1 /* Copyright (c) Mark Harmstone 2017 2 * 3 * This file is part of WinBtrfs. 4 * 5 * WinBtrfs is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public Licence as published by 7 * the Free Software Foundation, either version 3 of the Licence, or 8 * (at your option) any later version. 9 * 10 * WinBtrfs is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public Licence for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public Licence 16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include "shellext.h" 19 #include "send.h" 20 #include "resource.h" 21 #include <stddef.h> 22 #include <shlobj.h> 23 #ifdef __REACTOS__ 24 #undef DeleteFile 25 #endif 26 #include <iostream> 27 28 #define SEND_BUFFER_LEN 1048576 29 30 DWORD BtrfsSend::Thread() { 31 try { 32 NTSTATUS Status; 33 IO_STATUS_BLOCK iosb; 34 btrfs_send_subvol* bss; 35 btrfs_send_header header; 36 btrfs_send_command end; 37 ULONG bss_size, i; 38 39 buf = (char*)malloc(SEND_BUFFER_LEN); 40 41 try { 42 dirh = CreateFileW(subvol.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 43 if (dirh == INVALID_HANDLE_VALUE) 44 throw string_error(IDS_SEND_CANT_OPEN_DIR, subvol.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 45 46 try { 47 bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE)); 48 bss = (btrfs_send_subvol*)malloc(bss_size); 49 memset(bss, 0, bss_size); 50 51 if (incremental) { 52 WCHAR parent[MAX_PATH]; 53 HANDLE parenth; 54 55 parent[0] = 0; 56 57 GetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent, sizeof(parent) / sizeof(WCHAR)); 58 59 parenth = CreateFileW(parent, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 60 if (parenth == INVALID_HANDLE_VALUE) 61 throw string_error(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str()); 62 63 bss->parent = parenth; 64 } else 65 bss->parent = nullptr; 66 67 bss->num_clones = clones.size(); 68 69 for (i = 0; i < bss->num_clones; i++) { 70 HANDLE h; 71 72 h = CreateFileW(clones[i].c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 73 if (h == INVALID_HANDLE_VALUE) { 74 auto le = GetLastError(); 75 ULONG j; 76 77 for (j = 0; j < i; j++) { 78 CloseHandle(bss->clones[j]); 79 } 80 81 if (bss->parent) CloseHandle(bss->parent); 82 83 throw string_error(IDS_SEND_CANT_OPEN_DIR, clones[i].c_str(), le, format_message(le).c_str()); 84 } 85 86 bss->clones[i] = h; 87 } 88 89 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, bss_size, nullptr, 0); 90 91 for (i = 0; i < bss->num_clones; i++) { 92 CloseHandle(bss->clones[i]); 93 } 94 95 if (!NT_SUCCESS(Status)) { 96 if (Status == (NTSTATUS)STATUS_INVALID_PARAMETER) { 97 BY_HANDLE_FILE_INFORMATION fileinfo; 98 if (!GetFileInformationByHandle(dirh, &fileinfo)) { 99 auto le = GetLastError(); 100 if (bss->parent) CloseHandle(bss->parent); 101 throw string_error(IDS_SEND_GET_FILE_INFO_FAILED, le, format_message(le).c_str()); 102 } 103 104 if (!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { 105 if (bss->parent) CloseHandle(bss->parent); 106 throw string_error(IDS_SEND_NOT_READONLY); 107 } 108 109 if (bss->parent) { 110 if (!GetFileInformationByHandle(bss->parent, &fileinfo)) { 111 auto le = GetLastError(); 112 CloseHandle(bss->parent); 113 throw string_error(IDS_SEND_GET_FILE_INFO_FAILED, le, format_message(le).c_str()); 114 } 115 116 if (!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { 117 CloseHandle(bss->parent); 118 throw string_error(IDS_SEND_PARENT_NOT_READONLY); 119 } 120 } 121 } 122 123 if (bss->parent) CloseHandle(bss->parent); 124 throw string_error(IDS_SEND_FSCTL_BTRFS_SEND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); 125 } 126 127 if (bss->parent) CloseHandle(bss->parent); 128 129 stream = CreateFileW(file, FILE_WRITE_DATA | DELETE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 130 if (stream == INVALID_HANDLE_VALUE) 131 throw string_error(IDS_SEND_CANT_OPEN_FILE, file, GetLastError(), format_message(GetLastError()).c_str()); 132 133 try { 134 memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic)); 135 header.version = 1; 136 137 if (!WriteFile(stream, &header, sizeof(header), nullptr, nullptr)) 138 throw string_error(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 139 140 do { 141 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, nullptr, 0, buf, SEND_BUFFER_LEN); 142 143 if (NT_SUCCESS(Status)) { 144 if (!WriteFile(stream, buf, iosb.Information, nullptr, nullptr)) 145 throw string_error(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 146 } 147 } while (NT_SUCCESS(Status)); 148 149 if (Status != STATUS_END_OF_FILE) 150 throw string_error(IDS_SEND_FSCTL_BTRFS_READ_SEND_BUFFER_FAILED, Status, format_ntstatus(Status).c_str()); 151 152 end.length = 0; 153 end.cmd = BTRFS_SEND_CMD_END; 154 end.csum = 0x9dc96c50; 155 156 if (!WriteFile(stream, &end, sizeof(end), nullptr, nullptr)) 157 throw string_error(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 158 159 SetEndOfFile(stream); 160 } catch (...) { 161 FILE_DISPOSITION_INFO fdi; 162 163 fdi.DeleteFile = true; 164 165 SetFileInformationByHandle(stream, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO)); 166 167 CloseHandle(stream); 168 stream = INVALID_HANDLE_VALUE; 169 170 throw; 171 } 172 173 CloseHandle(stream); 174 stream = INVALID_HANDLE_VALUE; 175 } catch (...) { 176 CloseHandle(dirh); 177 dirh = INVALID_HANDLE_VALUE; 178 179 throw; 180 } 181 182 CloseHandle(dirh); 183 dirh = INVALID_HANDLE_VALUE; 184 } catch (...) { 185 free(buf); 186 buf = nullptr; 187 188 started = false; 189 190 SetDlgItemTextW(hwnd, IDCANCEL, closetext); 191 EnableWindow(GetDlgItem(hwnd, IDOK), true); 192 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true); 193 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); 194 195 throw; 196 } 197 } catch (const exception& e) { 198 wstring msg; 199 200 utf8_to_utf16(e.what(), msg); 201 202 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, msg.c_str()); 203 return 0; 204 } 205 206 207 free(buf); 208 buf = nullptr; 209 210 started = false; 211 212 SetDlgItemTextW(hwnd, IDCANCEL, closetext); 213 EnableWindow(GetDlgItem(hwnd, IDOK), true); 214 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true); 215 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); 216 217 wstring success; 218 219 load_string(module, IDS_SEND_SUCCESS, success); 220 221 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, success.c_str()); 222 223 return 0; 224 } 225 226 static DWORD WINAPI send_thread(LPVOID lpParameter) { 227 BtrfsSend* bs = (BtrfsSend*)lpParameter; 228 229 return bs->Thread(); 230 } 231 232 void BtrfsSend::StartSend(HWND hwnd) { 233 wstring s; 234 HWND cl; 235 ULONG num_clones; 236 237 if (started) 238 return; 239 240 GetDlgItemTextW(hwnd, IDC_STREAM_DEST, file, sizeof(file) / sizeof(WCHAR)); 241 242 if (file[0] == 0) 243 return; 244 245 if (incremental) { 246 WCHAR parent[MAX_PATH]; 247 248 GetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent, sizeof(parent) / sizeof(WCHAR)); 249 250 if (parent[0] == 0) 251 return; 252 } 253 254 started = true; 255 256 wstring writing; 257 258 load_string(module, IDS_SEND_WRITING, writing); 259 260 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, writing.c_str()); 261 262 load_string(module, IDS_SEND_CANCEL, s); 263 SetDlgItemTextW(hwnd, IDCANCEL, s.c_str()); 264 265 EnableWindow(GetDlgItem(hwnd, IDOK), false); 266 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), false); 267 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), false); 268 269 clones.clear(); 270 271 cl = GetDlgItem(hwnd, IDC_CLONE_LIST); 272 num_clones = SendMessageW(cl, LB_GETCOUNT, 0, 0); 273 274 if ((LRESULT)num_clones != LB_ERR) { 275 ULONG i; 276 277 for (i = 0; i < num_clones; i++) { 278 WCHAR* t; 279 ULONG len; 280 281 len = SendMessageW(cl, LB_GETTEXTLEN, i, 0); 282 t = (WCHAR*)malloc((len + 1) * sizeof(WCHAR)); 283 284 SendMessageW(cl, LB_GETTEXT, i, (LPARAM)t); 285 286 clones.push_back(t); 287 288 free(t); 289 } 290 } 291 292 thread = CreateThread(nullptr, 0, send_thread, this, 0, nullptr); 293 294 if (!thread) 295 throw last_error(GetLastError()); 296 } 297 298 void BtrfsSend::Browse(HWND hwnd) { 299 OPENFILENAMEW ofn; 300 301 file[0] = 0; 302 303 memset(&ofn, 0, sizeof(OPENFILENAMEW)); 304 ofn.lStructSize = sizeof(OPENFILENAMEW); 305 ofn.hwndOwner = hwnd; 306 ofn.hInstance = module; 307 ofn.lpstrFile = file; 308 ofn.nMaxFile = sizeof(file) / sizeof(WCHAR); 309 310 if (!GetSaveFileNameW(&ofn)) 311 return; 312 313 SetDlgItemTextW(hwnd, IDC_STREAM_DEST, file); 314 } 315 316 void BtrfsSend::BrowseParent(HWND hwnd) { 317 BROWSEINFOW bi; 318 PIDLIST_ABSOLUTE root, pidl; 319 HRESULT hr; 320 WCHAR parent[MAX_PATH], volpathw[MAX_PATH]; 321 NTSTATUS Status; 322 IO_STATUS_BLOCK iosb; 323 btrfs_get_file_ids bgfi; 324 325 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 326 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 327 328 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0); 329 if (FAILED(hr)) 330 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED); 331 332 memset(&bi, 0, sizeof(BROWSEINFOW)); 333 334 bi.hwndOwner = hwnd; 335 bi.pidlRoot = root; 336 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON; 337 338 pidl = SHBrowseForFolderW(&bi); 339 340 if (!pidl) 341 return; 342 343 if (!SHGetPathFromIDListW(pidl, parent)) 344 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED); 345 346 { 347 win_handle h = CreateFileW(parent, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 348 if (h == INVALID_HANDLE_VALUE) 349 throw string_error(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str()); 350 351 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids)); 352 if (!NT_SUCCESS(Status)) 353 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str()); 354 } 355 356 if (bgfi.inode != 0x100 || bgfi.top) 357 throw string_error(IDS_NOT_SUBVOL); 358 359 SetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent); 360 } 361 362 void BtrfsSend::AddClone(HWND hwnd) { 363 BROWSEINFOW bi; 364 PIDLIST_ABSOLUTE root, pidl; 365 HRESULT hr; 366 WCHAR path[MAX_PATH], volpathw[MAX_PATH]; 367 NTSTATUS Status; 368 IO_STATUS_BLOCK iosb; 369 btrfs_get_file_ids bgfi; 370 371 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 372 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 373 374 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0); 375 if (FAILED(hr)) 376 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED); 377 378 memset(&bi, 0, sizeof(BROWSEINFOW)); 379 380 bi.hwndOwner = hwnd; 381 bi.pidlRoot = root; 382 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON; 383 384 pidl = SHBrowseForFolderW(&bi); 385 386 if (!pidl) 387 return; 388 389 if (!SHGetPathFromIDListW(pidl, path)) 390 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED); 391 392 { 393 win_handle h = CreateFileW(path, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 394 if (h == INVALID_HANDLE_VALUE) 395 throw string_error(IDS_SEND_CANT_OPEN_DIR, path, GetLastError(), format_message(GetLastError()).c_str()); 396 397 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids)); 398 if (!NT_SUCCESS(Status)) 399 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str()); 400 } 401 402 if (bgfi.inode != 0x100 || bgfi.top) 403 throw string_error(IDS_NOT_SUBVOL); 404 405 SendMessageW(GetDlgItem(hwnd, IDC_CLONE_LIST), LB_ADDSTRING, 0, (LPARAM)path); 406 } 407 408 void BtrfsSend::RemoveClone(HWND hwnd) { 409 LRESULT sel; 410 HWND cl = GetDlgItem(hwnd, IDC_CLONE_LIST); 411 412 sel = SendMessageW(cl, LB_GETCURSEL, 0, 0); 413 414 if (sel == LB_ERR) 415 return; 416 417 SendMessageW(cl, LB_DELETESTRING, sel, 0); 418 419 if (SendMessageW(cl, LB_GETCURSEL, 0, 0) == LB_ERR) 420 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), false); 421 } 422 423 INT_PTR BtrfsSend::SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 424 try { 425 switch (uMsg) { 426 case WM_INITDIALOG: 427 this->hwnd = hwndDlg; 428 429 GetDlgItemTextW(hwndDlg, IDCANCEL, closetext, sizeof(closetext) / sizeof(WCHAR)); 430 break; 431 432 case WM_COMMAND: 433 switch (HIWORD(wParam)) { 434 case BN_CLICKED: 435 switch (LOWORD(wParam)) { 436 case IDOK: 437 StartSend(hwndDlg); 438 return true; 439 440 case IDCANCEL: 441 if (started) { 442 TerminateThread(thread, 0); 443 444 if (stream != INVALID_HANDLE_VALUE) { 445 FILE_DISPOSITION_INFO fdi; 446 447 fdi.DeleteFile = true; 448 449 SetFileInformationByHandle(stream, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO)); 450 CloseHandle(stream); 451 } 452 453 if (dirh != INVALID_HANDLE_VALUE) 454 CloseHandle(dirh); 455 456 started = false; 457 458 SetDlgItemTextW(hwndDlg, IDCANCEL, closetext); 459 460 EnableWindow(GetDlgItem(hwnd, IDOK), true); 461 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true); 462 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); 463 } else 464 EndDialog(hwndDlg, 1); 465 return true; 466 467 case IDC_BROWSE: 468 Browse(hwndDlg); 469 return true; 470 471 case IDC_INCREMENTAL: 472 incremental = IsDlgButtonChecked(hwndDlg, LOWORD(wParam)); 473 474 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_SUBVOL), incremental); 475 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_BROWSE), incremental); 476 return true; 477 478 case IDC_PARENT_BROWSE: 479 BrowseParent(hwndDlg); 480 return true; 481 482 case IDC_CLONE_ADD: 483 AddClone(hwndDlg); 484 return true; 485 486 case IDC_CLONE_REMOVE: 487 RemoveClone(hwndDlg); 488 return true; 489 } 490 break; 491 492 case LBN_SELCHANGE: 493 switch (LOWORD(wParam)) { 494 case IDC_CLONE_LIST: 495 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), true); 496 return true; 497 } 498 break; 499 } 500 break; 501 } 502 } catch (const exception& e) { 503 error_message(hwnd, e.what()); 504 } 505 506 return false; 507 } 508 509 static INT_PTR CALLBACK stub_SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 510 BtrfsSend* bs; 511 512 if (uMsg == WM_INITDIALOG) { 513 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 514 bs = (BtrfsSend*)lParam; 515 } else 516 bs = (BtrfsSend*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 517 518 if (bs) 519 return bs->SendDlgProc(hwndDlg, uMsg, wParam, lParam); 520 else 521 return false; 522 } 523 524 void BtrfsSend::Open(HWND hwnd, LPWSTR path) { 525 subvol = path; 526 527 if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SEND_SUBVOL), hwnd, stub_SendDlgProc, (LPARAM)this) <= 0) 528 throw last_error(GetLastError()); 529 } 530 531 #ifdef __REACTOS__ 532 extern "C" { 533 #endif 534 535 void CALLBACK SendSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 536 try { 537 win_handle token; 538 TOKEN_PRIVILEGES tp; 539 LUID luid; 540 541 set_dpi_aware(); 542 543 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 544 throw last_error(GetLastError()); 545 546 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 547 throw last_error(GetLastError()); 548 549 tp.PrivilegeCount = 1; 550 tp.Privileges[0].Luid = luid; 551 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 552 553 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 554 throw last_error(GetLastError()); 555 556 BtrfsSend bs; 557 558 bs.Open(hwnd, lpszCmdLine); 559 } catch (const exception& e) { 560 error_message(hwnd, e.what()); 561 } 562 } 563 564 #ifdef __REACTOS__ 565 } /* extern "C" */ 566 #endif 567 568 static void send_subvol(const wstring& subvol, const wstring& file, const wstring& parent, const vector<wstring>& clones) { 569 char* buf; 570 win_handle dirh, stream; 571 ULONG bss_size, i; 572 btrfs_send_subvol* bss; 573 IO_STATUS_BLOCK iosb; 574 NTSTATUS Status; 575 btrfs_send_header header; 576 btrfs_send_command end; 577 578 buf = (char*)malloc(SEND_BUFFER_LEN); 579 580 try { 581 dirh = CreateFileW(subvol.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 582 if (dirh == INVALID_HANDLE_VALUE) 583 throw last_error(GetLastError()); 584 585 stream = CreateFileW(file.c_str(), FILE_WRITE_DATA | DELETE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 586 if (stream == INVALID_HANDLE_VALUE) 587 throw last_error(GetLastError()); 588 589 try { 590 bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE)); 591 bss = (btrfs_send_subvol*)malloc(bss_size); 592 memset(bss, 0, bss_size); 593 594 if (parent != L"") { 595 HANDLE parenth; 596 597 parenth = CreateFileW(parent.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 598 if (parenth == INVALID_HANDLE_VALUE) 599 throw last_error(GetLastError()); 600 601 bss->parent = parenth; 602 } else 603 bss->parent = nullptr; 604 605 bss->num_clones = clones.size(); 606 607 for (i = 0; i < bss->num_clones; i++) { 608 HANDLE h; 609 610 h = CreateFileW(clones[i].c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 611 if (h == INVALID_HANDLE_VALUE) { 612 auto le = GetLastError(); 613 ULONG j; 614 615 for (j = 0; j < i; j++) { 616 CloseHandle(bss->clones[j]); 617 } 618 619 if (bss->parent) CloseHandle(bss->parent); 620 621 throw last_error(le); 622 } 623 624 bss->clones[i] = h; 625 } 626 627 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, bss_size, nullptr, 0); 628 629 for (i = 0; i < bss->num_clones; i++) { 630 CloseHandle(bss->clones[i]); 631 } 632 633 if (bss->parent) CloseHandle(bss->parent); 634 635 if (!NT_SUCCESS(Status)) 636 throw ntstatus_error(Status); 637 638 memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic)); 639 header.version = 1; 640 641 if (!WriteFile(stream, &header, sizeof(header), nullptr, nullptr)) 642 throw last_error(GetLastError()); 643 644 do { 645 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, nullptr, 0, buf, SEND_BUFFER_LEN); 646 647 if (NT_SUCCESS(Status)) 648 WriteFile(stream, buf, iosb.Information, nullptr, nullptr); 649 } while (NT_SUCCESS(Status)); 650 651 if (Status != STATUS_END_OF_FILE) 652 throw ntstatus_error(Status); 653 654 end.length = 0; 655 end.cmd = BTRFS_SEND_CMD_END; 656 end.csum = 0x9dc96c50; 657 658 if (!WriteFile(stream, &end, sizeof(end), nullptr, nullptr)) 659 throw last_error(GetLastError()); 660 661 SetEndOfFile(stream); 662 } catch (...) { 663 FILE_DISPOSITION_INFO fdi; 664 665 fdi.DeleteFile = true; 666 667 SetFileInformationByHandle(stream, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO)); 668 669 throw; 670 } 671 } catch (...) { 672 free(buf); 673 throw; 674 } 675 676 free(buf); 677 } 678 679 #ifdef __REACTOS__ 680 extern "C" { 681 #endif 682 683 void CALLBACK SendSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 684 vector<wstring> args; 685 wstring subvol = L"", parent = L"", file = L""; 686 vector<wstring> clones; 687 688 command_line_to_args(lpszCmdLine, args); 689 690 if (args.size() >= 2) { 691 TOKEN_PRIVILEGES tp; 692 LUID luid; 693 694 { 695 win_handle token; 696 697 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 698 return; 699 700 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 701 return; 702 703 tp.PrivilegeCount = 1; 704 tp.Privileges[0].Luid = luid; 705 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 706 707 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 708 return; 709 } 710 711 for (unsigned int i = 0; i < args.size(); i++) { 712 if (args[i][0] == '-') { 713 if (args[i][2] == 0 && i < args.size() - 1) { 714 if (args[i][1] == 'p') { 715 parent = args[i+1]; 716 i++; 717 } else if (args[i][1] == 'c') { 718 clones.push_back(args[i+1]); 719 i++; 720 } 721 } 722 } else { 723 if (subvol == L"") 724 subvol = args[i]; 725 else if (file == L"") 726 file = args[i]; 727 } 728 } 729 730 if (subvol != L"" && file != L"") { 731 try { 732 send_subvol(subvol, file, parent, clones); 733 } catch (const exception& e) { 734 cerr << "Error: " << e.what() << endl; 735 } 736 } 737 } 738 } 739 740 #ifdef __REACTOS__ 741 } /* extern "C" */ 742 #endif 743