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 307 if (!GetSaveFileNameW(&ofn)) 308 return; 309 310 SetDlgItemTextW(hwnd, IDC_STREAM_DEST, file); 311 } 312 313 void BtrfsSend::BrowseParent(HWND hwnd) { 314 BROWSEINFOW bi; 315 PIDLIST_ABSOLUTE root, pidl; 316 HRESULT hr; 317 WCHAR parent[MAX_PATH], volpathw[MAX_PATH]; 318 NTSTATUS Status; 319 IO_STATUS_BLOCK iosb; 320 btrfs_get_file_ids bgfi; 321 322 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 323 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 324 325 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0); 326 if (FAILED(hr)) 327 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED); 328 329 memset(&bi, 0, sizeof(BROWSEINFOW)); 330 331 bi.hwndOwner = hwnd; 332 bi.pidlRoot = root; 333 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON; 334 335 pidl = SHBrowseForFolderW(&bi); 336 337 if (!pidl) 338 return; 339 340 if (!SHGetPathFromIDListW(pidl, parent)) 341 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED); 342 343 { 344 win_handle h = CreateFileW(parent, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 345 if (h == INVALID_HANDLE_VALUE) 346 throw string_error(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str()); 347 348 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids)); 349 if (!NT_SUCCESS(Status)) 350 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str()); 351 } 352 353 if (bgfi.inode != 0x100 || bgfi.top) 354 throw string_error(IDS_NOT_SUBVOL); 355 356 SetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent); 357 } 358 359 void BtrfsSend::AddClone(HWND hwnd) { 360 BROWSEINFOW bi; 361 PIDLIST_ABSOLUTE root, pidl; 362 HRESULT hr; 363 WCHAR path[MAX_PATH], volpathw[MAX_PATH]; 364 NTSTATUS Status; 365 IO_STATUS_BLOCK iosb; 366 btrfs_get_file_ids bgfi; 367 368 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 369 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 370 371 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0); 372 if (FAILED(hr)) 373 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED); 374 375 memset(&bi, 0, sizeof(BROWSEINFOW)); 376 377 bi.hwndOwner = hwnd; 378 bi.pidlRoot = root; 379 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON; 380 381 pidl = SHBrowseForFolderW(&bi); 382 383 if (!pidl) 384 return; 385 386 if (!SHGetPathFromIDListW(pidl, path)) 387 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED); 388 389 { 390 win_handle h = CreateFileW(path, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 391 if (h == INVALID_HANDLE_VALUE) 392 throw string_error(IDS_SEND_CANT_OPEN_DIR, path, GetLastError(), format_message(GetLastError()).c_str()); 393 394 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids)); 395 if (!NT_SUCCESS(Status)) 396 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str()); 397 } 398 399 if (bgfi.inode != 0x100 || bgfi.top) 400 throw string_error(IDS_NOT_SUBVOL); 401 402 SendMessageW(GetDlgItem(hwnd, IDC_CLONE_LIST), LB_ADDSTRING, 0, (LPARAM)path); 403 } 404 405 void BtrfsSend::RemoveClone(HWND hwnd) { 406 LRESULT sel; 407 HWND cl = GetDlgItem(hwnd, IDC_CLONE_LIST); 408 409 sel = SendMessageW(cl, LB_GETCURSEL, 0, 0); 410 411 if (sel == LB_ERR) 412 return; 413 414 SendMessageW(cl, LB_DELETESTRING, sel, 0); 415 416 if (SendMessageW(cl, LB_GETCURSEL, 0, 0) == LB_ERR) 417 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), false); 418 } 419 420 INT_PTR BtrfsSend::SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 421 try { 422 switch (uMsg) { 423 case WM_INITDIALOG: 424 this->hwnd = hwndDlg; 425 426 GetDlgItemTextW(hwndDlg, IDCANCEL, closetext, sizeof(closetext) / sizeof(WCHAR)); 427 break; 428 429 case WM_COMMAND: 430 switch (HIWORD(wParam)) { 431 case BN_CLICKED: 432 switch (LOWORD(wParam)) { 433 case IDOK: 434 StartSend(hwndDlg); 435 return true; 436 437 case IDCANCEL: 438 if (started) { 439 TerminateThread(thread, 0); 440 441 if (stream != INVALID_HANDLE_VALUE) { 442 NTSTATUS Status; 443 FILE_DISPOSITION_INFO fdi; 444 IO_STATUS_BLOCK iosb; 445 446 fdi.DeleteFile = true; 447 448 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation); 449 450 CloseHandle(stream); 451 452 if (!NT_SUCCESS(Status)) 453 throw ntstatus_error(Status); 454 } 455 456 if (dirh != INVALID_HANDLE_VALUE) 457 CloseHandle(dirh); 458 459 started = false; 460 461 SetDlgItemTextW(hwndDlg, IDCANCEL, closetext); 462 463 EnableWindow(GetDlgItem(hwnd, IDOK), true); 464 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true); 465 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); 466 } else 467 EndDialog(hwndDlg, 1); 468 return true; 469 470 case IDC_BROWSE: 471 Browse(hwndDlg); 472 return true; 473 474 case IDC_INCREMENTAL: 475 incremental = IsDlgButtonChecked(hwndDlg, LOWORD(wParam)); 476 477 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_SUBVOL), incremental); 478 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_BROWSE), incremental); 479 return true; 480 481 case IDC_PARENT_BROWSE: 482 BrowseParent(hwndDlg); 483 return true; 484 485 case IDC_CLONE_ADD: 486 AddClone(hwndDlg); 487 return true; 488 489 case IDC_CLONE_REMOVE: 490 RemoveClone(hwndDlg); 491 return true; 492 } 493 break; 494 495 case LBN_SELCHANGE: 496 switch (LOWORD(wParam)) { 497 case IDC_CLONE_LIST: 498 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), true); 499 return true; 500 } 501 break; 502 } 503 break; 504 } 505 } catch (const exception& e) { 506 error_message(hwnd, e.what()); 507 } 508 509 return false; 510 } 511 512 static INT_PTR CALLBACK stub_SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 513 BtrfsSend* bs; 514 515 if (uMsg == WM_INITDIALOG) { 516 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 517 bs = (BtrfsSend*)lParam; 518 } else 519 bs = (BtrfsSend*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 520 521 if (bs) 522 return bs->SendDlgProc(hwndDlg, uMsg, wParam, lParam); 523 else 524 return false; 525 } 526 527 void BtrfsSend::Open(HWND hwnd, LPWSTR path) { 528 subvol = path; 529 530 if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SEND_SUBVOL), hwnd, stub_SendDlgProc, (LPARAM)this) <= 0) 531 throw last_error(GetLastError()); 532 } 533 534 #ifdef __REACTOS__ 535 extern "C" { 536 #endif 537 538 void CALLBACK SendSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 539 try { 540 win_handle token; 541 TOKEN_PRIVILEGES tp; 542 LUID luid; 543 544 set_dpi_aware(); 545 546 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 547 throw last_error(GetLastError()); 548 549 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 550 throw last_error(GetLastError()); 551 552 tp.PrivilegeCount = 1; 553 tp.Privileges[0].Luid = luid; 554 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 555 556 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 557 throw last_error(GetLastError()); 558 559 BtrfsSend bs; 560 561 bs.Open(hwnd, lpszCmdLine); 562 } catch (const exception& e) { 563 error_message(hwnd, e.what()); 564 } 565 } 566 567 #ifdef __REACTOS__ 568 } /* extern "C" */ 569 #endif 570 571 static void send_subvol(const wstring& subvol, const wstring& file, const wstring& parent, const vector<wstring>& clones) { 572 char* buf; 573 win_handle dirh, stream; 574 ULONG i; 575 btrfs_send_subvol* bss; 576 IO_STATUS_BLOCK iosb; 577 NTSTATUS Status; 578 btrfs_send_header header; 579 btrfs_send_command end; 580 581 buf = (char*)malloc(SEND_BUFFER_LEN); 582 583 try { 584 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); 585 if (dirh == INVALID_HANDLE_VALUE) 586 throw last_error(GetLastError()); 587 588 stream = CreateFileW(file.c_str(), FILE_WRITE_DATA | DELETE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 589 if (stream == INVALID_HANDLE_VALUE) 590 throw last_error(GetLastError()); 591 592 try { 593 size_t bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE)); 594 bss = (btrfs_send_subvol*)malloc(bss_size); 595 memset(bss, 0, bss_size); 596 597 if (parent != L"") { 598 HANDLE parenth; 599 600 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); 601 if (parenth == INVALID_HANDLE_VALUE) 602 throw last_error(GetLastError()); 603 604 bss->parent = parenth; 605 } else 606 bss->parent = nullptr; 607 608 bss->num_clones = (ULONG)clones.size(); 609 610 for (i = 0; i < bss->num_clones; i++) { 611 HANDLE h; 612 613 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); 614 if (h == INVALID_HANDLE_VALUE) { 615 auto le = GetLastError(); 616 ULONG j; 617 618 for (j = 0; j < i; j++) { 619 CloseHandle(bss->clones[j]); 620 } 621 622 if (bss->parent) CloseHandle(bss->parent); 623 624 throw last_error(le); 625 } 626 627 bss->clones[i] = h; 628 } 629 630 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, (ULONG)bss_size, nullptr, 0); 631 632 for (i = 0; i < bss->num_clones; i++) { 633 CloseHandle(bss->clones[i]); 634 } 635 636 if (bss->parent) CloseHandle(bss->parent); 637 638 if (!NT_SUCCESS(Status)) 639 throw ntstatus_error(Status); 640 641 memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic)); 642 header.version = 1; 643 644 if (!WriteFile(stream, &header, sizeof(header), nullptr, nullptr)) 645 throw last_error(GetLastError()); 646 647 do { 648 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, nullptr, 0, buf, SEND_BUFFER_LEN); 649 650 if (NT_SUCCESS(Status)) 651 WriteFile(stream, buf, (DWORD)iosb.Information, nullptr, nullptr); 652 } while (NT_SUCCESS(Status)); 653 654 if (Status != STATUS_END_OF_FILE) 655 throw ntstatus_error(Status); 656 657 end.length = 0; 658 end.cmd = BTRFS_SEND_CMD_END; 659 end.csum = 0x9dc96c50; 660 661 if (!WriteFile(stream, &end, sizeof(end), nullptr, nullptr)) 662 throw last_error(GetLastError()); 663 664 SetEndOfFile(stream); 665 } catch (...) { 666 FILE_DISPOSITION_INFO fdi; 667 668 fdi.DeleteFile = true; 669 670 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation); 671 if (!NT_SUCCESS(Status)) 672 throw ntstatus_error(Status); 673 674 throw; 675 } 676 } catch (...) { 677 free(buf); 678 throw; 679 } 680 681 free(buf); 682 } 683 684 #ifdef __REACTOS__ 685 extern "C" { 686 #endif 687 688 void CALLBACK SendSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 689 vector<wstring> args; 690 wstring subvol = L"", parent = L"", file = L""; 691 vector<wstring> clones; 692 693 command_line_to_args(lpszCmdLine, args); 694 695 if (args.size() >= 2) { 696 TOKEN_PRIVILEGES tp; 697 LUID luid; 698 699 { 700 win_handle token; 701 702 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 703 return; 704 705 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 706 return; 707 708 tp.PrivilegeCount = 1; 709 tp.Privileges[0].Luid = luid; 710 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 711 712 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 713 return; 714 } 715 716 for (unsigned int i = 0; i < args.size(); i++) { 717 if (args[i][0] == '-') { 718 if (args[i][2] == 0 && i < args.size() - 1) { 719 if (args[i][1] == 'p') { 720 parent = args[i+1]; 721 i++; 722 } else if (args[i][1] == 'c') { 723 clones.push_back(args[i+1]); 724 i++; 725 } 726 } 727 } else { 728 if (subvol == L"") 729 subvol = args[i]; 730 else if (file == L"") 731 file = args[i]; 732 } 733 } 734 735 if (subvol != L"" && file != L"") { 736 try { 737 send_subvol(subvol, file, parent, clones); 738 } catch (const exception& e) { 739 cerr << "Error: " << e.what() << endl; 740 } 741 } 742 } 743 } 744 745 #ifdef __REACTOS__ 746 } /* extern "C" */ 747 #endif 748