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