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 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 size_t 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 = (ULONG)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, (ULONG)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, (DWORD)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 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation); 166 167 CloseHandle(stream); 168 stream = INVALID_HANDLE_VALUE; 169 170 if (!NT_SUCCESS(Status)) 171 throw ntstatus_error(Status); 172 173 throw; 174 } 175 176 CloseHandle(stream); 177 stream = INVALID_HANDLE_VALUE; 178 } catch (...) { 179 CloseHandle(dirh); 180 dirh = INVALID_HANDLE_VALUE; 181 182 throw; 183 } 184 185 CloseHandle(dirh); 186 dirh = INVALID_HANDLE_VALUE; 187 } catch (...) { 188 free(buf); 189 buf = nullptr; 190 191 started = false; 192 193 SetDlgItemTextW(hwnd, IDCANCEL, closetext); 194 EnableWindow(GetDlgItem(hwnd, IDOK), true); 195 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true); 196 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); 197 198 throw; 199 } 200 } catch (const exception& e) { 201 auto msg = utf8_to_utf16(e.what()); 202 203 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, msg.c_str()); 204 return 0; 205 } 206 207 208 free(buf); 209 buf = nullptr; 210 211 started = false; 212 213 SetDlgItemTextW(hwnd, IDCANCEL, closetext); 214 EnableWindow(GetDlgItem(hwnd, IDOK), true); 215 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true); 216 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); 217 218 wstring success; 219 220 load_string(module, IDS_SEND_SUCCESS, success); 221 222 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, success.c_str()); 223 224 return 0; 225 } 226 227 static DWORD WINAPI send_thread(LPVOID lpParameter) { 228 BtrfsSend* bs = (BtrfsSend*)lpParameter; 229 230 return bs->Thread(); 231 } 232 233 void BtrfsSend::StartSend(HWND hwnd) { 234 wstring s; 235 HWND cl; 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 auto num_clones = SendMessageW(cl, LB_GETCOUNT, 0, 0); 273 274 if (num_clones != LB_ERR) { 275 for (unsigned int i = 0; i < (unsigned int)num_clones; i++) { 276 WCHAR* t; 277 278 auto len = SendMessageW(cl, LB_GETTEXTLEN, i, 0); 279 t = (WCHAR*)malloc((len + 1) * sizeof(WCHAR)); 280 281 SendMessageW(cl, LB_GETTEXT, i, (LPARAM)t); 282 283 clones.push_back(t); 284 285 free(t); 286 } 287 } 288 289 thread = CreateThread(nullptr, 0, send_thread, this, 0, nullptr); 290 291 if (!thread) 292 throw last_error(GetLastError()); 293 } 294 295 void BtrfsSend::Browse(HWND hwnd) { 296 OPENFILENAMEW ofn; 297 298 file[0] = 0; 299 300 memset(&ofn, 0, sizeof(OPENFILENAMEW)); 301 ofn.lStructSize = sizeof(OPENFILENAMEW); 302 ofn.hwndOwner = hwnd; 303 ofn.hInstance = module; 304 ofn.lpstrFile = file; 305 ofn.nMaxFile = sizeof(file) / sizeof(WCHAR); 306 ofn.Flags = OFN_EXPLORER; 307 308 if (!GetSaveFileNameW(&ofn)) 309 return; 310 311 SetDlgItemTextW(hwnd, IDC_STREAM_DEST, file); 312 } 313 314 void BtrfsSend::BrowseParent(HWND hwnd) { 315 BROWSEINFOW bi; 316 PIDLIST_ABSOLUTE root, pidl; 317 HRESULT hr; 318 WCHAR parent[MAX_PATH], volpathw[MAX_PATH]; 319 NTSTATUS Status; 320 IO_STATUS_BLOCK iosb; 321 btrfs_get_file_ids bgfi; 322 323 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 324 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 325 326 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0); 327 if (FAILED(hr)) 328 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED); 329 330 memset(&bi, 0, sizeof(BROWSEINFOW)); 331 332 bi.hwndOwner = hwnd; 333 bi.pidlRoot = root; 334 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON; 335 336 pidl = SHBrowseForFolderW(&bi); 337 338 if (!pidl) 339 return; 340 341 if (!SHGetPathFromIDListW(pidl, parent)) 342 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED); 343 344 { 345 win_handle h = CreateFileW(parent, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 346 if (h == INVALID_HANDLE_VALUE) 347 throw string_error(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str()); 348 349 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids)); 350 if (!NT_SUCCESS(Status)) 351 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str()); 352 } 353 354 if (bgfi.inode != 0x100 || bgfi.top) 355 throw string_error(IDS_NOT_SUBVOL); 356 357 SetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent); 358 } 359 360 void BtrfsSend::AddClone(HWND hwnd) { 361 BROWSEINFOW bi; 362 PIDLIST_ABSOLUTE root, pidl; 363 HRESULT hr; 364 WCHAR path[MAX_PATH], volpathw[MAX_PATH]; 365 NTSTATUS Status; 366 IO_STATUS_BLOCK iosb; 367 btrfs_get_file_ids bgfi; 368 369 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 370 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 371 372 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0); 373 if (FAILED(hr)) 374 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED); 375 376 memset(&bi, 0, sizeof(BROWSEINFOW)); 377 378 bi.hwndOwner = hwnd; 379 bi.pidlRoot = root; 380 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON; 381 382 pidl = SHBrowseForFolderW(&bi); 383 384 if (!pidl) 385 return; 386 387 if (!SHGetPathFromIDListW(pidl, path)) 388 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED); 389 390 { 391 win_handle h = CreateFileW(path, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 392 if (h == INVALID_HANDLE_VALUE) 393 throw string_error(IDS_SEND_CANT_OPEN_DIR, path, GetLastError(), format_message(GetLastError()).c_str()); 394 395 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids)); 396 if (!NT_SUCCESS(Status)) 397 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str()); 398 } 399 400 if (bgfi.inode != 0x100 || bgfi.top) 401 throw string_error(IDS_NOT_SUBVOL); 402 403 SendMessageW(GetDlgItem(hwnd, IDC_CLONE_LIST), LB_ADDSTRING, 0, (LPARAM)path); 404 } 405 406 void BtrfsSend::RemoveClone(HWND hwnd) { 407 LRESULT sel; 408 HWND cl = GetDlgItem(hwnd, IDC_CLONE_LIST); 409 410 sel = SendMessageW(cl, LB_GETCURSEL, 0, 0); 411 412 if (sel == LB_ERR) 413 return; 414 415 SendMessageW(cl, LB_DELETESTRING, sel, 0); 416 417 if (SendMessageW(cl, LB_GETCURSEL, 0, 0) == LB_ERR) 418 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), false); 419 } 420 421 INT_PTR BtrfsSend::SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 422 try { 423 switch (uMsg) { 424 case WM_INITDIALOG: 425 this->hwnd = hwndDlg; 426 427 GetDlgItemTextW(hwndDlg, IDCANCEL, closetext, sizeof(closetext) / sizeof(WCHAR)); 428 break; 429 430 case WM_COMMAND: 431 switch (HIWORD(wParam)) { 432 case BN_CLICKED: 433 switch (LOWORD(wParam)) { 434 case IDOK: 435 StartSend(hwndDlg); 436 return true; 437 438 case IDCANCEL: 439 if (started) { 440 TerminateThread(thread, 0); 441 442 if (stream != INVALID_HANDLE_VALUE) { 443 NTSTATUS Status; 444 FILE_DISPOSITION_INFO fdi; 445 IO_STATUS_BLOCK iosb; 446 447 fdi.DeleteFile = true; 448 449 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation); 450 451 CloseHandle(stream); 452 453 if (!NT_SUCCESS(Status)) 454 throw ntstatus_error(Status); 455 } 456 457 if (dirh != INVALID_HANDLE_VALUE) 458 CloseHandle(dirh); 459 460 started = false; 461 462 SetDlgItemTextW(hwndDlg, IDCANCEL, closetext); 463 464 EnableWindow(GetDlgItem(hwnd, IDOK), true); 465 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true); 466 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); 467 } else 468 EndDialog(hwndDlg, 1); 469 return true; 470 471 case IDC_BROWSE: 472 Browse(hwndDlg); 473 return true; 474 475 case IDC_INCREMENTAL: 476 incremental = IsDlgButtonChecked(hwndDlg, LOWORD(wParam)); 477 478 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_SUBVOL), incremental); 479 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_BROWSE), incremental); 480 return true; 481 482 case IDC_PARENT_BROWSE: 483 BrowseParent(hwndDlg); 484 return true; 485 486 case IDC_CLONE_ADD: 487 AddClone(hwndDlg); 488 return true; 489 490 case IDC_CLONE_REMOVE: 491 RemoveClone(hwndDlg); 492 return true; 493 } 494 break; 495 496 case LBN_SELCHANGE: 497 switch (LOWORD(wParam)) { 498 case IDC_CLONE_LIST: 499 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), true); 500 return true; 501 } 502 break; 503 } 504 break; 505 } 506 } catch (const exception& e) { 507 error_message(hwnd, e.what()); 508 } 509 510 return false; 511 } 512 513 static INT_PTR CALLBACK stub_SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 514 BtrfsSend* bs; 515 516 if (uMsg == WM_INITDIALOG) { 517 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 518 bs = (BtrfsSend*)lParam; 519 } else 520 bs = (BtrfsSend*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 521 522 if (bs) 523 return bs->SendDlgProc(hwndDlg, uMsg, wParam, lParam); 524 else 525 return false; 526 } 527 528 void BtrfsSend::Open(HWND hwnd, LPWSTR path) { 529 subvol = path; 530 531 if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SEND_SUBVOL), hwnd, stub_SendDlgProc, (LPARAM)this) <= 0) 532 throw last_error(GetLastError()); 533 } 534 535 extern "C" 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 static void send_subvol(const wstring& subvol, const wstring& file, const wstring& parent, const vector<wstring>& clones) { 565 char* buf; 566 win_handle dirh, stream; 567 ULONG i; 568 btrfs_send_subvol* bss; 569 IO_STATUS_BLOCK iosb; 570 NTSTATUS Status; 571 btrfs_send_header header; 572 btrfs_send_command end; 573 574 buf = (char*)malloc(SEND_BUFFER_LEN); 575 576 try { 577 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); 578 if (dirh == INVALID_HANDLE_VALUE) 579 throw last_error(GetLastError()); 580 581 stream = CreateFileW(file.c_str(), FILE_WRITE_DATA | DELETE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 582 if (stream == INVALID_HANDLE_VALUE) 583 throw last_error(GetLastError()); 584 585 try { 586 size_t bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE)); 587 bss = (btrfs_send_subvol*)malloc(bss_size); 588 memset(bss, 0, bss_size); 589 590 if (parent != L"") { 591 HANDLE parenth; 592 593 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); 594 if (parenth == INVALID_HANDLE_VALUE) 595 throw last_error(GetLastError()); 596 597 bss->parent = parenth; 598 } else 599 bss->parent = nullptr; 600 601 bss->num_clones = (ULONG)clones.size(); 602 603 for (i = 0; i < bss->num_clones; i++) { 604 HANDLE h; 605 606 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); 607 if (h == INVALID_HANDLE_VALUE) { 608 auto le = GetLastError(); 609 ULONG j; 610 611 for (j = 0; j < i; j++) { 612 CloseHandle(bss->clones[j]); 613 } 614 615 if (bss->parent) CloseHandle(bss->parent); 616 617 throw last_error(le); 618 } 619 620 bss->clones[i] = h; 621 } 622 623 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, (ULONG)bss_size, nullptr, 0); 624 625 for (i = 0; i < bss->num_clones; i++) { 626 CloseHandle(bss->clones[i]); 627 } 628 629 if (bss->parent) CloseHandle(bss->parent); 630 631 if (!NT_SUCCESS(Status)) 632 throw ntstatus_error(Status); 633 634 memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic)); 635 header.version = 1; 636 637 if (!WriteFile(stream, &header, sizeof(header), nullptr, nullptr)) 638 throw last_error(GetLastError()); 639 640 do { 641 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, nullptr, 0, buf, SEND_BUFFER_LEN); 642 643 if (NT_SUCCESS(Status)) 644 WriteFile(stream, buf, (DWORD)iosb.Information, nullptr, nullptr); 645 } while (NT_SUCCESS(Status)); 646 647 if (Status != STATUS_END_OF_FILE) 648 throw ntstatus_error(Status); 649 650 end.length = 0; 651 end.cmd = BTRFS_SEND_CMD_END; 652 end.csum = 0x9dc96c50; 653 654 if (!WriteFile(stream, &end, sizeof(end), nullptr, nullptr)) 655 throw last_error(GetLastError()); 656 657 SetEndOfFile(stream); 658 } catch (...) { 659 FILE_DISPOSITION_INFO fdi; 660 661 fdi.DeleteFile = true; 662 663 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation); 664 if (!NT_SUCCESS(Status)) 665 throw ntstatus_error(Status); 666 667 throw; 668 } 669 } catch (...) { 670 free(buf); 671 throw; 672 } 673 674 free(buf); 675 } 676 677 extern "C" void CALLBACK SendSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 678 vector<wstring> args; 679 wstring subvol = L"", parent = L"", file = L""; 680 vector<wstring> clones; 681 682 command_line_to_args(lpszCmdLine, args); 683 684 if (args.size() >= 2) { 685 TOKEN_PRIVILEGES tp; 686 LUID luid; 687 688 { 689 win_handle token; 690 691 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 692 return; 693 694 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 695 return; 696 697 tp.PrivilegeCount = 1; 698 tp.Privileges[0].Luid = luid; 699 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 700 701 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 702 return; 703 } 704 705 for (unsigned int i = 0; i < args.size(); i++) { 706 if (args[i][0] == '-') { 707 if (args[i][2] == 0 && i < args.size() - 1) { 708 if (args[i][1] == 'p') { 709 parent = args[i+1]; 710 i++; 711 } else if (args[i][1] == 'c') { 712 clones.push_back(args[i+1]); 713 i++; 714 } 715 } 716 } else { 717 if (subvol == L"") 718 subvol = args[i]; 719 else if (file == L"") 720 file = args[i]; 721 } 722 } 723 724 if (subvol != L"" && file != L"") { 725 try { 726 send_subvol(subvol, file, parent, clones); 727 } catch (const exception& e) { 728 cerr << "Error: " << e.what() << endl; 729 } 730 } 731 } 732 } 733