1 /* Copyright (c) Mark Harmstone 2016-17 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 #ifndef __REACTOS__ 19 #define UNICODE 20 #endif 21 #include "shellext.h" 22 #ifndef __REACTOS__ 23 #include <windows.h> 24 #include <strsafe.h> 25 #include <stddef.h> 26 #include <winternl.h> 27 #else 28 #define WIN32_NO_STATUS 29 #include <windef.h> 30 #include <winbase.h> 31 #include <strsafe.h> 32 #include <shellapi.h> 33 #include <winioctl.h> 34 #include <ndk/iofuncs.h> 35 #undef DeleteFile 36 #endif 37 #include <wincodec.h> 38 #include <string> 39 #include <sstream> 40 41 #define NO_SHLWAPI_STRFCNS 42 #include <shlwapi.h> 43 44 #include "contextmenu.h" 45 #include "resource.h" 46 #ifndef __REACTOS__ 47 #include "../btrfsioctl.h" 48 #else 49 #include "btrfsioctl.h" 50 #endif 51 52 #define NEW_SUBVOL_VERBA "newsubvol" 53 #define NEW_SUBVOL_VERBW L"newsubvol" 54 #define SNAPSHOT_VERBA "snapshot" 55 #define SNAPSHOT_VERBW L"snapshot" 56 #define REFLINK_VERBA "reflink" 57 #define REFLINK_VERBW L"reflink" 58 #define RECV_VERBA "recvsubvol" 59 #define RECV_VERBW L"recvsubvol" 60 #define SEND_VERBA "sendsubvol" 61 #define SEND_VERBW L"sendsubvol" 62 63 typedef struct { 64 ULONG ReparseTag; 65 USHORT ReparseDataLength; 66 USHORT Reserved; 67 } reparse_header; 68 69 // FIXME - don't assume subvol's top inode is 0x100 70 71 HRESULT __stdcall BtrfsContextMenu::QueryInterface(REFIID riid, void **ppObj) { 72 if (riid == IID_IUnknown || riid == IID_IContextMenu) { 73 *ppObj = static_cast<IContextMenu*>(this); 74 AddRef(); 75 return S_OK; 76 } else if (riid == IID_IShellExtInit) { 77 *ppObj = static_cast<IShellExtInit*>(this); 78 AddRef(); 79 return S_OK; 80 } 81 82 *ppObj = NULL; 83 return E_NOINTERFACE; 84 } 85 86 HRESULT __stdcall BtrfsContextMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) { 87 HANDLE h; 88 IO_STATUS_BLOCK iosb; 89 btrfs_get_file_ids bgfi; 90 NTSTATUS Status; 91 92 if (!pidlFolder) { 93 FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 94 UINT num_files, i; 95 WCHAR fn[MAX_PATH]; 96 HDROP hdrop; 97 98 if (!pdtobj) 99 return E_FAIL; 100 101 stgm.tymed = TYMED_HGLOBAL; 102 103 if (FAILED(pdtobj->GetData(&format, &stgm))) 104 return E_INVALIDARG; 105 106 stgm_set = TRUE; 107 108 hdrop = (HDROP)GlobalLock(stgm.hGlobal); 109 110 if (!hdrop) { 111 ReleaseStgMedium(&stgm); 112 stgm_set = FALSE; 113 return E_INVALIDARG; 114 } 115 116 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0); 117 118 for (i = 0; i < num_files; i++) { 119 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) { 120 h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 121 122 if (h != INVALID_HANDLE_VALUE) { 123 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids)); 124 125 if (NT_SUCCESS(Status) && bgfi.inode == 0x100 && !bgfi.top) { 126 WCHAR parpath[MAX_PATH]; 127 HANDLE h2; 128 129 StringCchCopyW(parpath, sizeof(parpath) / sizeof(WCHAR), fn); 130 131 PathRemoveFileSpecW(parpath); 132 133 h2 = CreateFileW(parpath, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 134 135 if (h2 != INVALID_HANDLE_VALUE) 136 allow_snapshot = TRUE; 137 138 CloseHandle(h2); 139 140 ignore = FALSE; 141 bg = FALSE; 142 143 CloseHandle(h); 144 GlobalUnlock(hdrop); 145 return S_OK; 146 } 147 148 CloseHandle(h); 149 } 150 } 151 } 152 153 GlobalUnlock(hdrop); 154 155 return S_OK; 156 } 157 158 if (!SHGetPathFromIDListW(pidlFolder, path)) 159 return E_FAIL; 160 161 // check we have permissions to create new subdirectory 162 163 h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 164 165 if (h == INVALID_HANDLE_VALUE) 166 return E_FAIL; 167 168 // check is Btrfs volume 169 170 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids)); 171 172 if (!NT_SUCCESS(Status)) { 173 CloseHandle(h); 174 return E_FAIL; 175 } 176 177 CloseHandle(h); 178 179 ignore = FALSE; 180 bg = TRUE; 181 182 return S_OK; 183 } 184 185 static BOOL get_volume_path_parent(const WCHAR* fn, WCHAR* volpath, ULONG volpathlen) { 186 WCHAR *f, *p; 187 BOOL b; 188 189 f = PathFindFileNameW(fn); 190 191 if (f == fn) 192 return GetVolumePathNameW(fn, volpath, volpathlen); 193 194 p = (WCHAR*)malloc((f - fn + 1) * sizeof(WCHAR)); 195 memcpy(p, fn, (f - fn) * sizeof(WCHAR)); 196 p[f - fn] = 0; 197 198 b = GetVolumePathNameW(p, volpath, volpathlen); 199 200 free(p); 201 202 return b; 203 } 204 205 static BOOL show_reflink_paste(WCHAR* path) { 206 HDROP hdrop; 207 HANDLE lh; 208 ULONG num_files; 209 WCHAR fn[MAX_PATH], volpath1[255], volpath2[255]; 210 211 if (!IsClipboardFormatAvailable(CF_HDROP)) 212 return FALSE; 213 214 if (!GetVolumePathNameW(path, volpath1, sizeof(volpath1) / sizeof(WCHAR))) 215 return FALSE; 216 217 if (!OpenClipboard(NULL)) 218 return FALSE; 219 220 hdrop = (HDROP)GetClipboardData(CF_HDROP); 221 222 if (!hdrop) { 223 CloseClipboard(); 224 return FALSE; 225 } 226 227 lh = GlobalLock(hdrop); 228 229 if (!lh) { 230 CloseClipboard(); 231 return FALSE; 232 } 233 234 num_files = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0); 235 236 if (num_files == 0) { 237 GlobalUnlock(lh); 238 CloseClipboard(); 239 return FALSE; 240 } 241 242 if (!DragQueryFileW(hdrop, 0, fn, sizeof(fn) / sizeof(WCHAR))) { 243 GlobalUnlock(lh); 244 CloseClipboard(); 245 return FALSE; 246 } 247 248 if (!get_volume_path_parent(fn, volpath2, sizeof(volpath2) / sizeof(WCHAR))) { 249 GlobalUnlock(lh); 250 CloseClipboard(); 251 return FALSE; 252 } 253 254 GlobalUnlock(lh); 255 256 CloseClipboard(); 257 258 return !wcscmp(volpath1, volpath2); 259 } 260 261 // The code for putting an icon against a menu item comes from: 262 // http://web.archive.org/web/20070208005514/http://shellrevealed.com/blogs/shellblog/archive/2007/02/06/Vista-Style-Menus_2C00_-Part-1-_2D00_-Adding-icons-to-standard-menus.aspx 263 264 static void InitBitmapInfo(BITMAPINFO* pbmi, ULONG cbInfo, LONG cx, LONG cy, WORD bpp) { 265 ZeroMemory(pbmi, cbInfo); 266 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 267 pbmi->bmiHeader.biPlanes = 1; 268 pbmi->bmiHeader.biCompression = BI_RGB; 269 270 pbmi->bmiHeader.biWidth = cx; 271 pbmi->bmiHeader.biHeight = cy; 272 pbmi->bmiHeader.biBitCount = bpp; 273 } 274 275 static HRESULT Create32BitHBITMAP(HDC hdc, const SIZE *psize, void **ppvBits, HBITMAP* phBmp) { 276 BITMAPINFO bmi; 277 HDC hdcUsed; 278 279 *phBmp = NULL; 280 281 InitBitmapInfo(&bmi, sizeof(bmi), psize->cx, psize->cy, 32); 282 283 hdcUsed = hdc ? hdc : GetDC(NULL); 284 285 if (hdcUsed) { 286 *phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0); 287 if (hdc != hdcUsed) 288 ReleaseDC(NULL, hdcUsed); 289 } 290 291 return !*phBmp ? E_OUTOFMEMORY : S_OK; 292 } 293 294 void BtrfsContextMenu::get_uac_icon() { 295 IWICImagingFactory* factory = NULL; 296 IWICBitmap* bitmap; 297 HRESULT hr; 298 299 #ifdef __REACTOS__ 300 hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (void **)&factory); 301 #else 302 hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)); 303 #endif 304 305 if (SUCCEEDED(hr)) { 306 HANDLE icon; 307 308 // We can't use IDI_SHIELD, as that will only give us the full-size icon 309 icon = LoadImageW(GetModuleHandleW(L"user32.dll"), MAKEINTRESOURCEW(106)/* UAC shield */, IMAGE_ICON, 310 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); 311 312 hr = factory->CreateBitmapFromHICON((HICON)icon, &bitmap); 313 if (SUCCEEDED(hr)) { 314 UINT cx, cy; 315 316 hr = bitmap->GetSize(&cx, &cy); 317 if (SUCCEEDED(hr)) { 318 SIZE sz; 319 BYTE* buf; 320 321 sz.cx = (int)cx; 322 sz.cy = -(int)cy; 323 324 hr = Create32BitHBITMAP(NULL, &sz, (void**)&buf, &uacicon); 325 if (SUCCEEDED(hr)) { 326 UINT stride = cx * sizeof(DWORD); 327 UINT buflen = cy * stride; 328 bitmap->CopyPixels(NULL, stride, buflen, buf); 329 } 330 } 331 332 bitmap->Release(); 333 } 334 335 factory->Release(); 336 } 337 } 338 339 HRESULT __stdcall BtrfsContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { 340 WCHAR str[256]; 341 ULONG entries = 0; 342 343 if (ignore) 344 return E_INVALIDARG; 345 346 if (uFlags & CMF_DEFAULTONLY) 347 return S_OK; 348 349 if (!bg) { 350 if (allow_snapshot) { 351 if (LoadStringW(module, IDS_CREATE_SNAPSHOT, str, sizeof(str) / sizeof(WCHAR)) == 0) 352 return E_FAIL; 353 354 if (!InsertMenuW(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, str)) 355 return E_FAIL; 356 357 entries = 1; 358 } 359 360 if (idCmdFirst + entries <= idCmdLast) { 361 MENUITEMINFOW mii; 362 363 if (LoadStringW(module, IDS_SEND_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0) 364 return E_FAIL; 365 366 if (!uacicon) 367 get_uac_icon(); 368 369 memset(&mii, 0, sizeof(MENUITEMINFOW)); 370 mii.cbSize = sizeof(MENUITEMINFOW); 371 mii.fMask = MIIM_STRING | MIIM_ID | MIIM_BITMAP; 372 mii.dwTypeData = str; 373 mii.wID = idCmdFirst + entries; 374 mii.hbmpItem = uacicon; 375 376 if (!InsertMenuItemW(hmenu, indexMenu + entries, TRUE, &mii)) 377 return E_FAIL; 378 379 entries++; 380 } 381 } else { 382 if (LoadStringW(module, IDS_NEW_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0) 383 return E_FAIL; 384 385 if (!InsertMenuW(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, str)) 386 return E_FAIL; 387 388 entries = 1; 389 390 if (idCmdFirst + 1 <= idCmdLast) { 391 MENUITEMINFOW mii; 392 393 if (LoadStringW(module, IDS_RECV_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0) 394 return E_FAIL; 395 396 if (!uacicon) 397 get_uac_icon(); 398 399 memset(&mii, 0, sizeof(MENUITEMINFOW)); 400 mii.cbSize = sizeof(MENUITEMINFOW); 401 mii.fMask = MIIM_STRING | MIIM_ID | MIIM_BITMAP; 402 mii.dwTypeData = str; 403 mii.wID = idCmdFirst + 1; 404 mii.hbmpItem = uacicon; 405 406 if (!InsertMenuItemW(hmenu, indexMenu + 1, TRUE, &mii)) 407 return E_FAIL; 408 409 entries++; 410 } 411 412 if (idCmdFirst + 2 <= idCmdLast && show_reflink_paste(path)) { 413 if (LoadStringW(module, IDS_REFLINK_PASTE, str, sizeof(str) / sizeof(WCHAR)) == 0) 414 return E_FAIL; 415 416 if (!InsertMenuW(hmenu, indexMenu + 2, MF_BYPOSITION, idCmdFirst + 2, str)) 417 return E_FAIL; 418 419 entries++; 420 } 421 } 422 423 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, entries); 424 } 425 426 static void create_snapshot(HWND hwnd, WCHAR* fn) { 427 HANDLE h; 428 NTSTATUS Status; 429 IO_STATUS_BLOCK iosb; 430 btrfs_get_file_ids bgfi; 431 432 h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 433 434 if (h != INVALID_HANDLE_VALUE) { 435 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids)); 436 437 if (NT_SUCCESS(Status) && bgfi.inode == 0x100 && !bgfi.top) { 438 WCHAR parpath[MAX_PATH], subvolname[MAX_PATH], templ[MAX_PATH], name[MAX_PATH], searchpath[MAX_PATH]; 439 HANDLE h2, fff; 440 btrfs_create_snapshot* bcs; 441 ULONG namelen, pathend; 442 WIN32_FIND_DATAW wfd; 443 SYSTEMTIME time; 444 445 StringCchCopyW(parpath, sizeof(parpath) / sizeof(WCHAR), fn); 446 PathRemoveFileSpecW(parpath); 447 448 StringCchCopyW(subvolname, sizeof(subvolname) / sizeof(WCHAR), fn); 449 PathStripPathW(subvolname); 450 451 h2 = CreateFileW(parpath, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 452 453 if (h2 == INVALID_HANDLE_VALUE) { 454 ShowError(hwnd, GetLastError()); 455 CloseHandle(h); 456 return; 457 } 458 459 if (!LoadStringW(module, IDS_SNAPSHOT_FILENAME, templ, MAX_PATH)) { 460 ShowError(hwnd, GetLastError()); 461 CloseHandle(h); 462 CloseHandle(h2); 463 return; 464 } 465 466 GetLocalTime(&time); 467 468 if (StringCchPrintfW(name, sizeof(name) / sizeof(WCHAR), templ, subvolname, time.wYear, time.wMonth, time.wDay) == STRSAFE_E_INSUFFICIENT_BUFFER) { 469 MessageBoxW(hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR); 470 CloseHandle(h); 471 CloseHandle(h2); 472 return; 473 } 474 475 StringCchCopyW(searchpath, sizeof(searchpath) / sizeof(WCHAR), parpath); 476 StringCchCatW(searchpath, sizeof(searchpath) / sizeof(WCHAR), L"\\"); 477 pathend = wcslen(searchpath); 478 479 StringCchCatW(searchpath, sizeof(searchpath) / sizeof(WCHAR), name); 480 481 fff = FindFirstFileW(searchpath, &wfd); 482 483 if (fff != INVALID_HANDLE_VALUE) { 484 ULONG i = wcslen(searchpath), num = 2; 485 486 do { 487 FindClose(fff); 488 489 searchpath[i] = 0; 490 if (StringCchPrintfW(searchpath, sizeof(searchpath) / sizeof(WCHAR), L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) { 491 MessageBoxW(hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR); 492 CloseHandle(h); 493 CloseHandle(h2); 494 return; 495 } 496 497 fff = FindFirstFileW(searchpath, &wfd); 498 num++; 499 } while (fff != INVALID_HANDLE_VALUE); 500 } 501 502 namelen = wcslen(&searchpath[pathend]) * sizeof(WCHAR); 503 504 bcs = (btrfs_create_snapshot*)malloc(sizeof(btrfs_create_snapshot) - 1 + namelen); 505 bcs->readonly = FALSE; 506 bcs->posix = FALSE; 507 bcs->subvol = h; 508 bcs->namelen = namelen; 509 memcpy(bcs->name, &searchpath[pathend], namelen); 510 511 Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, sizeof(btrfs_create_snapshot) - 1 + namelen, NULL, 0); 512 513 if (!NT_SUCCESS(Status)) 514 ShowNtStatusError(hwnd, Status); 515 516 CloseHandle(h2); 517 } 518 519 CloseHandle(h); 520 } else 521 ShowError(hwnd, GetLastError()); 522 } 523 524 static UINT64 __inline sector_align(UINT64 n, UINT64 a) { 525 if (n & (a - 1)) 526 n = (n + a) & ~(a - 1); 527 528 return n; 529 } 530 531 BOOL BtrfsContextMenu::reflink_copy(HWND hwnd, const WCHAR* fn, const WCHAR* dir) { 532 HANDLE source, dest; 533 WCHAR* name, volpath1[255], volpath2[255]; 534 std::wstring dirw, newpath; 535 BOOL ret = FALSE; 536 FILE_BASIC_INFO fbi; 537 FILETIME atime, mtime; 538 btrfs_inode_info bii; 539 btrfs_set_inode_info bsii; 540 ULONG bytesret; 541 NTSTATUS Status; 542 IO_STATUS_BLOCK iosb; 543 btrfs_set_xattr bsxa; 544 545 // Thanks to 0xbadfca11, whose version of reflink for Windows provided a few pointers on what 546 // to do here - https://github.com/0xbadfca11/reflink 547 548 name = PathFindFileNameW(fn); 549 550 dirw = dir; 551 552 if (dir[0] != 0 && dir[wcslen(dir) - 1] != '\\') 553 dirw += L"\\"; 554 555 newpath = dirw; 556 newpath += name; 557 558 if (!get_volume_path_parent(fn, volpath1, sizeof(volpath1) / sizeof(WCHAR))) { 559 ShowError(hwnd, GetLastError()); 560 return FALSE; 561 } 562 563 if (!GetVolumePathNameW(dir, volpath2, sizeof(volpath2) / sizeof(WCHAR))) { 564 ShowError(hwnd, GetLastError()); 565 return FALSE; 566 } 567 568 if (wcscmp(volpath1, volpath2)) // different filesystems 569 return FALSE; 570 571 source = CreateFileW(fn, GENERIC_READ | FILE_TRAVERSE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL); 572 if (source == INVALID_HANDLE_VALUE) { 573 ShowError(hwnd, GetLastError()); 574 return FALSE; 575 } 576 577 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii, sizeof(btrfs_inode_info)); 578 if (!NT_SUCCESS(Status)) { 579 ShowNtStatusError(hwnd, Status); 580 CloseHandle(source); 581 return FALSE; 582 } 583 584 // if subvol, do snapshot instead 585 if (bii.inode == SUBVOL_ROOT_INODE) { 586 btrfs_create_snapshot* bcs; 587 HANDLE dirh, fff; 588 std::wstring destname, search; 589 WIN32_FIND_DATAW wfd; 590 int num = 2; 591 592 dirh = CreateFileW(dir, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 593 if (dirh == INVALID_HANDLE_VALUE) { 594 ShowError(hwnd, GetLastError()); 595 CloseHandle(source); 596 return FALSE; 597 } 598 599 search = dirw; 600 search += name; 601 destname = name; 602 603 fff = FindFirstFileW(search.c_str(), &wfd); 604 605 if (fff != INVALID_HANDLE_VALUE) { 606 do { 607 std::wstringstream ss; 608 609 FindClose(fff); 610 611 ss << name; 612 ss << L" ("; 613 ss << num; 614 ss << L")"; 615 destname = ss.str(); 616 617 search = dirw + destname; 618 619 fff = FindFirstFileW(search.c_str(), &wfd); 620 num++; 621 } while (fff != INVALID_HANDLE_VALUE); 622 } 623 624 bcs = (btrfs_create_snapshot*)malloc(sizeof(btrfs_create_snapshot) - sizeof(WCHAR) + (destname.length() * sizeof(WCHAR))); 625 bcs->subvol = source; 626 bcs->namelen = destname.length() * sizeof(WCHAR); 627 memcpy(bcs->name, destname.c_str(), destname.length() * sizeof(WCHAR)); 628 629 Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, sizeof(btrfs_create_snapshot) - sizeof(WCHAR) + bcs->namelen, NULL, 0); 630 631 free(bcs); 632 633 if (!NT_SUCCESS(Status)) { 634 ShowNtStatusError(hwnd, Status); 635 CloseHandle(source); 636 CloseHandle(dirh); 637 return FALSE; 638 } 639 640 CloseHandle(source); 641 CloseHandle(dirh); 642 return TRUE; 643 } 644 645 if (!GetFileInformationByHandleEx(source, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) { 646 ShowError(hwnd, GetLastError()); 647 CloseHandle(source); 648 return FALSE; 649 } 650 651 if (bii.type == BTRFS_TYPE_CHARDEV || bii.type == BTRFS_TYPE_BLOCKDEV || bii.type == BTRFS_TYPE_FIFO || bii.type == BTRFS_TYPE_SOCKET) { 652 HANDLE dirh; 653 ULONG bmnsize; 654 btrfs_mknod* bmn; 655 656 dirh = CreateFileW(dir, FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 657 if (dirh == INVALID_HANDLE_VALUE) { 658 ShowError(hwnd, GetLastError()); 659 CloseHandle(source); 660 return FALSE; 661 } 662 663 bmnsize = offsetof(btrfs_mknod, name[0]) + (wcslen(name) * sizeof(WCHAR)); 664 bmn = (btrfs_mknod*)malloc(bmnsize); 665 666 bmn->inode = 0; 667 bmn->type = bii.type; 668 bmn->st_rdev = bii.st_rdev; 669 bmn->namelen = wcslen(name) * sizeof(WCHAR); 670 memcpy(bmn->name, name, bmn->namelen); 671 672 Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_MKNOD, bmn, bmnsize, NULL, 0); 673 if (!NT_SUCCESS(Status)) { 674 ShowNtStatusError(hwnd, Status); 675 CloseHandle(dirh); 676 CloseHandle(source); 677 free(bmn); 678 return FALSE; 679 } 680 681 CloseHandle(dirh); 682 free(bmn); 683 684 dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, 0, NULL); 685 } else if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 686 if (CreateDirectoryExW(fn, newpath.c_str(), NULL)) 687 dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 688 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 689 else 690 dest = INVALID_HANDLE_VALUE; 691 } else 692 dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, source); 693 694 if (dest == INVALID_HANDLE_VALUE) { 695 int num = 2; 696 697 if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_ALREADY_EXISTS && wcscmp(fn, newpath.c_str())) { 698 ShowError(hwnd, GetLastError()); 699 CloseHandle(source); 700 return FALSE; 701 } 702 703 do { 704 WCHAR* ext; 705 std::wstringstream ss; 706 707 ext = PathFindExtensionW(fn); 708 709 ss << dirw; 710 711 if (*ext == 0) { 712 ss << name; 713 ss << L" ("; 714 ss << num; 715 ss << L")"; 716 } else { 717 std::wstring namew = name; 718 719 ss << namew.substr(0, ext - name); 720 ss << L" ("; 721 ss << num; 722 ss << L")"; 723 ss << ext; 724 } 725 726 newpath = ss.str(); 727 if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 728 if (CreateDirectoryExW(fn, newpath.c_str(), NULL)) 729 dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 730 else 731 dest = INVALID_HANDLE_VALUE; 732 } else 733 dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, source); 734 735 if (dest == INVALID_HANDLE_VALUE) { 736 if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_ALREADY_EXISTS) { 737 ShowError(hwnd, GetLastError()); 738 CloseHandle(source); 739 return FALSE; 740 } 741 742 num++; 743 } else 744 break; 745 } while (TRUE); 746 } 747 748 memset(&bsii, 0, sizeof(btrfs_set_inode_info)); 749 750 bsii.flags_changed = TRUE; 751 bsii.flags = bii.flags; 752 753 if (bii.flags & BTRFS_INODE_COMPRESS) { 754 bsii.compression_type_changed = TRUE; 755 bsii.compression_type = bii.compression_type; 756 } 757 758 Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0); 759 if (!NT_SUCCESS(Status)) { 760 ShowNtStatusError(hwnd, Status); 761 goto end; 762 } 763 764 if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 765 if (!(fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { 766 HANDLE h; 767 WIN32_FIND_DATAW fff; 768 std::wstring qs; 769 770 qs = fn; 771 qs += L"\\*"; 772 773 h = FindFirstFileW(qs.c_str(), &fff); 774 if (h != INVALID_HANDLE_VALUE) { 775 do { 776 std::wstring fn2; 777 778 if (fff.cFileName[0] == '.' && (fff.cFileName[1] == 0 || (fff.cFileName[1] == '.' && fff.cFileName[2] == 0))) 779 continue; 780 781 fn2 = fn; 782 fn2 += L"\\"; 783 fn2 += fff.cFileName; 784 785 if (!reflink_copy(hwnd, fn2.c_str(), newpath.c_str())) 786 goto end; 787 } while (FindNextFileW(h, &fff)); 788 789 FindClose(h); 790 } 791 } 792 793 // CreateDirectoryExW also copies streams, no need to do it here 794 } else { 795 HANDLE h; 796 WIN32_FIND_STREAM_DATA fsd; 797 798 if (fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { 799 reparse_header rh; 800 ULONG rplen; 801 UINT8* rp; 802 803 if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, &rh, sizeof(reparse_header), &bytesret, NULL)) { 804 if (GetLastError() != ERROR_MORE_DATA) { 805 ShowError(hwnd, GetLastError()); 806 goto end; 807 } 808 } 809 810 rplen = sizeof(reparse_header) + rh.ReparseDataLength; 811 rp = (UINT8*)malloc(rplen); 812 813 if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, rp, rplen, &bytesret, NULL)) { 814 ShowError(hwnd, GetLastError()); 815 goto end; 816 } 817 818 if (!DeviceIoControl(dest, FSCTL_SET_REPARSE_POINT, rp, rplen, NULL, 0, &bytesret, NULL)) { 819 ShowError(hwnd, GetLastError()); 820 goto end; 821 } 822 823 free(rp); 824 } else { 825 FILE_STANDARD_INFO fsi; 826 FILE_END_OF_FILE_INFO feofi; 827 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER fgiib; 828 FSCTL_SET_INTEGRITY_INFORMATION_BUFFER fsiib; 829 DUPLICATE_EXTENTS_DATA ded; 830 UINT64 offset, alloc_size; 831 ULONG maxdup; 832 833 if (!GetFileInformationByHandleEx(source, FileStandardInfo, &fsi, sizeof(FILE_STANDARD_INFO))) { 834 ShowError(hwnd, GetLastError()); 835 goto end; 836 } 837 838 if (!DeviceIoControl(source, FSCTL_GET_INTEGRITY_INFORMATION, NULL, 0, &fgiib, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER), &bytesret, NULL)) { 839 ShowError(hwnd, GetLastError()); 840 goto end; 841 } 842 843 if (fbi.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) { 844 if (!DeviceIoControl(dest, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesret, NULL)) { 845 ShowError(hwnd, GetLastError()); 846 goto end; 847 } 848 } 849 850 fsiib.ChecksumAlgorithm = fgiib.ChecksumAlgorithm; 851 fsiib.Reserved = 0; 852 fsiib.Flags = fgiib.Flags; 853 if (!DeviceIoControl(dest, FSCTL_SET_INTEGRITY_INFORMATION, &fsiib, sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER), NULL, 0, &bytesret, NULL)) { 854 ShowError(hwnd, GetLastError()); 855 goto end; 856 } 857 858 feofi.EndOfFile = fsi.EndOfFile; 859 if (!SetFileInformationByHandle(dest, FileEndOfFileInfo, &feofi, sizeof(FILE_END_OF_FILE_INFO))){ 860 ShowError(hwnd, GetLastError()); 861 goto end; 862 } 863 864 ded.FileHandle = source; 865 maxdup = 0xffffffff - fgiib.ClusterSizeInBytes + 1; 866 867 alloc_size = sector_align(fsi.EndOfFile.QuadPart, fgiib.ClusterSizeInBytes); 868 869 offset = 0; 870 while (offset < alloc_size) { 871 ded.SourceFileOffset.QuadPart = ded.TargetFileOffset.QuadPart = offset; 872 ded.ByteCount.QuadPart = maxdup < (alloc_size - offset) ? maxdup : (alloc_size - offset); 873 if (!DeviceIoControl(dest, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA), NULL, 0, &bytesret, NULL)) { 874 ShowError(hwnd, GetLastError()); 875 goto end; 876 } 877 878 offset += ded.ByteCount.QuadPart; 879 } 880 } 881 882 h = FindFirstStreamW(fn, FindStreamInfoStandard, &fsd, 0); 883 if (h != INVALID_HANDLE_VALUE) { 884 do { 885 std::wstring sn; 886 887 sn = fsd.cStreamName; 888 889 if (sn != L"::$DATA" && sn.length() > 6 && sn.substr(sn.length() - 6, 6) == L":$DATA") { 890 HANDLE stream; 891 UINT8* data = NULL; 892 893 if (fsd.StreamSize.QuadPart > 0) { 894 std::wstring fn2; 895 896 fn2 = fn; 897 fn2 += sn; 898 899 stream = CreateFileW(fn2.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); 900 901 if (stream == INVALID_HANDLE_VALUE) { 902 ShowError(hwnd, GetLastError()); 903 FindClose(h); 904 goto end; 905 } 906 907 // We can get away with this because our streams are guaranteed to be below 64 KB - 908 // don't do this on NTFS! 909 data = (UINT8*)malloc(fsd.StreamSize.QuadPart); 910 911 if (!ReadFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) { 912 ShowError(hwnd, GetLastError()); 913 FindClose(h); 914 free(data); 915 CloseHandle(stream); 916 goto end; 917 } 918 919 CloseHandle(stream); 920 } 921 922 stream = CreateFileW((newpath + sn).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, NULL); 923 924 if (stream == INVALID_HANDLE_VALUE) { 925 ShowError(hwnd, GetLastError()); 926 927 FindClose(h); 928 if (data) free(data); 929 930 goto end; 931 } 932 933 if (data) { 934 if (!WriteFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) { 935 ShowError(hwnd, GetLastError()); 936 FindClose(h); 937 free(data); 938 CloseHandle(stream); 939 goto end; 940 } 941 942 free(data); 943 } 944 945 CloseHandle(stream); 946 } 947 } while (FindNextStreamW(h, &fsd)); 948 949 FindClose(h); 950 } 951 } 952 953 atime.dwLowDateTime = fbi.LastAccessTime.LowPart; 954 atime.dwHighDateTime = fbi.LastAccessTime.HighPart; 955 mtime.dwLowDateTime = fbi.LastWriteTime.LowPart; 956 mtime.dwHighDateTime = fbi.LastWriteTime.HighPart; 957 SetFileTime(dest, NULL, &atime, &mtime); 958 959 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, &bsxa, sizeof(btrfs_set_xattr)); 960 961 if (Status == STATUS_BUFFER_OVERFLOW || (NT_SUCCESS(Status) && bsxa.valuelen > 0)) { 962 ULONG xalen = 0; 963 btrfs_set_xattr *xa = NULL, *xa2; 964 965 do { 966 xalen += 1024; 967 968 if (xa) free(xa); 969 xa = (btrfs_set_xattr*)malloc(xalen); 970 971 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, xa, xalen); 972 } while (Status == STATUS_BUFFER_OVERFLOW); 973 974 if (!NT_SUCCESS(Status)) { 975 free(xa); 976 ShowNtStatusError(hwnd, Status); 977 goto end; 978 } 979 980 xa2 = xa; 981 while (xa2->valuelen > 0) { 982 Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, xa2, 983 offsetof(btrfs_set_xattr, data[0]) + xa2->namelen + xa2->valuelen, NULL, 0); 984 if (!NT_SUCCESS(Status)) { 985 free(xa); 986 ShowNtStatusError(hwnd, Status); 987 goto end; 988 } 989 xa2 = (btrfs_set_xattr*)&xa2->data[xa2->namelen + xa2->valuelen]; 990 } 991 992 free(xa); 993 } else if (!NT_SUCCESS(Status)) { 994 ShowNtStatusError(hwnd, Status); 995 goto end; 996 } 997 998 ret = TRUE; 999 1000 end: 1001 if (!ret) { 1002 FILE_DISPOSITION_INFO fdi; 1003 1004 fdi.DeleteFile = TRUE; 1005 if (!SetFileInformationByHandle(dest, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO))) 1006 ShowError(hwnd, GetLastError()); 1007 } 1008 1009 CloseHandle(dest); 1010 CloseHandle(source); 1011 1012 return ret; 1013 } 1014 1015 HRESULT __stdcall BtrfsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO picia) { 1016 LPCMINVOKECOMMANDINFOEX pici = (LPCMINVOKECOMMANDINFOEX)picia; 1017 1018 if (ignore) 1019 return E_INVALIDARG; 1020 1021 if (!bg) { 1022 if ((IS_INTRESOURCE(pici->lpVerb) && allow_snapshot && pici->lpVerb == 0) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, SNAPSHOT_VERBA))) { 1023 UINT num_files, i; 1024 WCHAR fn[MAX_PATH]; 1025 1026 if (!stgm_set) 1027 return E_FAIL; 1028 1029 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0); 1030 1031 if (num_files == 0) 1032 return E_FAIL; 1033 1034 for (i = 0; i < num_files; i++) { 1035 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) { 1036 create_snapshot(pici->hwnd, fn); 1037 } 1038 } 1039 1040 return S_OK; 1041 } else if ((IS_INTRESOURCE(pici->lpVerb) && ((allow_snapshot && (ULONG_PTR)pici->lpVerb == 1) || (!allow_snapshot && (ULONG_PTR)pici->lpVerb == 0))) || 1042 (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, SEND_VERBA))) { 1043 UINT num_files, i; 1044 WCHAR dll[MAX_PATH], fn[MAX_PATH]; 1045 std::wstring t; 1046 SHELLEXECUTEINFOW sei; 1047 1048 GetModuleFileNameW(module, dll, sizeof(dll) / sizeof(WCHAR)); 1049 1050 if (!stgm_set) 1051 return E_FAIL; 1052 1053 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0); 1054 1055 if (num_files == 0) 1056 return E_FAIL; 1057 1058 for (i = 0; i < num_files; i++) { 1059 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) { 1060 t = L"\""; 1061 t += dll; 1062 t += L"\",SendSubvolGUI "; 1063 t += fn; 1064 1065 RtlZeroMemory(&sei, sizeof(sei)); 1066 1067 sei.cbSize = sizeof(sei); 1068 sei.hwnd = pici->hwnd; 1069 sei.lpVerb = L"runas"; 1070 sei.lpFile = L"rundll32.exe"; 1071 sei.lpParameters = t.c_str(); 1072 sei.nShow = SW_SHOW; 1073 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 1074 1075 if (!ShellExecuteExW(&sei)) { 1076 ShowError(pici->hwnd, GetLastError()); 1077 return E_FAIL; 1078 } 1079 1080 WaitForSingleObject(sei.hProcess, INFINITE); 1081 CloseHandle(sei.hProcess); 1082 } 1083 } 1084 1085 return S_OK; 1086 } 1087 } else { 1088 if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 0) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, NEW_SUBVOL_VERBA))) { 1089 HANDLE h; 1090 IO_STATUS_BLOCK iosb; 1091 NTSTATUS Status; 1092 ULONG pathlen, searchpathlen, pathend, bcslen; 1093 WCHAR name[MAX_PATH], *searchpath; 1094 btrfs_create_subvol* bcs; 1095 HANDLE fff; 1096 WIN32_FIND_DATAW wfd; 1097 1098 if (!LoadStringW(module, IDS_NEW_SUBVOL_FILENAME, name, MAX_PATH)) { 1099 ShowError(pici->hwnd, GetLastError()); 1100 return E_FAIL; 1101 } 1102 1103 h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 1104 1105 if (h == INVALID_HANDLE_VALUE) { 1106 ShowError(pici->hwnd, GetLastError()); 1107 return E_FAIL; 1108 } 1109 1110 pathlen = wcslen(path); 1111 1112 searchpathlen = pathlen + wcslen(name) + 10; 1113 searchpath = (WCHAR*)malloc(searchpathlen * sizeof(WCHAR)); 1114 1115 StringCchCopyW(searchpath, searchpathlen, path); 1116 StringCchCatW(searchpath, searchpathlen, L"\\"); 1117 pathend = wcslen(searchpath); 1118 1119 StringCchCatW(searchpath, searchpathlen, name); 1120 1121 fff = FindFirstFileW(searchpath, &wfd); 1122 1123 if (fff != INVALID_HANDLE_VALUE) { 1124 ULONG i = wcslen(searchpath), num = 2; 1125 1126 do { 1127 FindClose(fff); 1128 1129 searchpath[i] = 0; 1130 if (StringCchPrintfW(searchpath, searchpathlen, L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) { 1131 MessageBoxW(pici->hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR); 1132 CloseHandle(h); 1133 return E_FAIL; 1134 } 1135 1136 fff = FindFirstFileW(searchpath, &wfd); 1137 num++; 1138 } while (fff != INVALID_HANDLE_VALUE); 1139 } 1140 1141 bcslen = offsetof(btrfs_create_subvol, name[0]) + (wcslen(&searchpath[pathend]) * sizeof(WCHAR)); 1142 bcs = (btrfs_create_subvol*)malloc(bcslen); 1143 1144 bcs->readonly = FALSE; 1145 bcs->posix = FALSE; 1146 bcs->namelen = wcslen(&searchpath[pathend]) * sizeof(WCHAR); 1147 memcpy(bcs->name, &searchpath[pathend], bcs->namelen); 1148 1149 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, NULL, 0); 1150 1151 free(searchpath); 1152 free(bcs); 1153 1154 if (!NT_SUCCESS(Status)) { 1155 CloseHandle(h); 1156 ShowNtStatusError(pici->hwnd, Status); 1157 return E_FAIL; 1158 } 1159 1160 CloseHandle(h); 1161 1162 return S_OK; 1163 } else if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 1) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, RECV_VERBA))) { 1164 WCHAR dll[MAX_PATH]; 1165 std::wstring t; 1166 SHELLEXECUTEINFOW sei; 1167 1168 GetModuleFileNameW(module, dll, sizeof(dll) / sizeof(WCHAR)); 1169 1170 t = L"\""; 1171 t += dll; 1172 t += L"\",RecvSubvolGUI "; 1173 t += path; 1174 1175 RtlZeroMemory(&sei, sizeof(sei)); 1176 1177 sei.cbSize = sizeof(sei); 1178 sei.hwnd = pici->hwnd; 1179 sei.lpVerb = L"runas"; 1180 sei.lpFile = L"rundll32.exe"; 1181 sei.lpParameters = t.c_str(); 1182 sei.nShow = SW_SHOW; 1183 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 1184 1185 if (!ShellExecuteExW(&sei)) { 1186 ShowError(pici->hwnd, GetLastError()); 1187 return E_FAIL; 1188 } 1189 1190 WaitForSingleObject(sei.hProcess, INFINITE); 1191 CloseHandle(sei.hProcess); 1192 1193 return S_OK; 1194 } else if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 2) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, REFLINK_VERBA))) { 1195 HDROP hdrop; 1196 1197 if (!IsClipboardFormatAvailable(CF_HDROP)) 1198 return S_OK; 1199 1200 if (!OpenClipboard(pici->hwnd)) { 1201 ShowError(pici->hwnd, GetLastError()); 1202 return E_FAIL; 1203 } 1204 1205 hdrop = (HDROP)GetClipboardData(CF_HDROP); 1206 1207 if (hdrop) { 1208 HANDLE lh; 1209 1210 lh = GlobalLock(hdrop); 1211 1212 if (lh) { 1213 ULONG num_files, i; 1214 WCHAR fn[MAX_PATH]; 1215 1216 num_files = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0); 1217 1218 for (i = 0; i < num_files; i++) { 1219 if (DragQueryFileW(hdrop, i, fn, sizeof(fn) / sizeof(WCHAR))) { 1220 if (!reflink_copy(pici->hwnd, fn, pici->lpDirectoryW)) { 1221 GlobalUnlock(lh); 1222 CloseClipboard(); 1223 return E_FAIL; 1224 } 1225 } 1226 } 1227 1228 GlobalUnlock(lh); 1229 } 1230 } 1231 1232 CloseClipboard(); 1233 1234 return S_OK; 1235 } 1236 } 1237 1238 return E_FAIL; 1239 } 1240 1241 HRESULT __stdcall BtrfsContextMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax) { 1242 if (ignore) 1243 return E_INVALIDARG; 1244 1245 if (idCmd != 0) 1246 return E_INVALIDARG; 1247 1248 if (!bg) { 1249 if (idCmd == 0) { 1250 switch (uFlags) { 1251 case GCS_HELPTEXTA: 1252 if (LoadStringA(module, IDS_CREATE_SNAPSHOT_HELP_TEXT, pszName, cchMax)) 1253 return S_OK; 1254 else 1255 return E_FAIL; 1256 1257 case GCS_HELPTEXTW: 1258 if (LoadStringW(module, IDS_CREATE_SNAPSHOT_HELP_TEXT, (LPWSTR)pszName, cchMax)) 1259 return S_OK; 1260 else 1261 return E_FAIL; 1262 1263 case GCS_VALIDATEA: 1264 case GCS_VALIDATEW: 1265 return S_OK; 1266 1267 case GCS_VERBA: 1268 return StringCchCopyA(pszName, cchMax, SNAPSHOT_VERBA); 1269 1270 case GCS_VERBW: 1271 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, SNAPSHOT_VERBW); 1272 1273 default: 1274 return E_INVALIDARG; 1275 } 1276 } else if (idCmd == 1) { 1277 switch (uFlags) { 1278 case GCS_HELPTEXTA: 1279 if (LoadStringA(module, IDS_SEND_SUBVOL_HELP, pszName, cchMax)) 1280 return S_OK; 1281 else 1282 return E_FAIL; 1283 1284 case GCS_HELPTEXTW: 1285 if (LoadStringW(module, IDS_SEND_SUBVOL_HELP, (LPWSTR)pszName, cchMax)) 1286 return S_OK; 1287 else 1288 return E_FAIL; 1289 1290 case GCS_VALIDATEA: 1291 case GCS_VALIDATEW: 1292 return S_OK; 1293 1294 case GCS_VERBA: 1295 return StringCchCopyA(pszName, cchMax, SEND_VERBA); 1296 1297 case GCS_VERBW: 1298 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, SEND_VERBW); 1299 1300 default: 1301 return E_INVALIDARG; 1302 } 1303 } else 1304 return E_INVALIDARG; 1305 } else { 1306 if (idCmd == 0) { 1307 switch (uFlags) { 1308 case GCS_HELPTEXTA: 1309 if (LoadStringA(module, IDS_NEW_SUBVOL_HELP_TEXT, pszName, cchMax)) 1310 return S_OK; 1311 else 1312 return E_FAIL; 1313 1314 case GCS_HELPTEXTW: 1315 if (LoadStringW(module, IDS_NEW_SUBVOL_HELP_TEXT, (LPWSTR)pszName, cchMax)) 1316 return S_OK; 1317 else 1318 return E_FAIL; 1319 1320 case GCS_VALIDATEA: 1321 case GCS_VALIDATEW: 1322 return S_OK; 1323 1324 case GCS_VERBA: 1325 return StringCchCopyA(pszName, cchMax, NEW_SUBVOL_VERBA); 1326 1327 case GCS_VERBW: 1328 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, NEW_SUBVOL_VERBW); 1329 1330 default: 1331 return E_INVALIDARG; 1332 } 1333 } else if (idCmd == 1) { 1334 switch (uFlags) { 1335 case GCS_HELPTEXTA: 1336 if (LoadStringA(module, IDS_RECV_SUBVOL_HELP, pszName, cchMax)) 1337 return S_OK; 1338 else 1339 return E_FAIL; 1340 1341 case GCS_HELPTEXTW: 1342 if (LoadStringW(module, IDS_RECV_SUBVOL_HELP, (LPWSTR)pszName, cchMax)) 1343 return S_OK; 1344 else 1345 return E_FAIL; 1346 1347 case GCS_VALIDATEA: 1348 case GCS_VALIDATEW: 1349 return S_OK; 1350 1351 case GCS_VERBA: 1352 return StringCchCopyA(pszName, cchMax, RECV_VERBA); 1353 1354 case GCS_VERBW: 1355 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, RECV_VERBW); 1356 1357 default: 1358 return E_INVALIDARG; 1359 } 1360 } else if (idCmd == 2) { 1361 switch (uFlags) { 1362 case GCS_HELPTEXTA: 1363 if (LoadStringA(module, IDS_REFLINK_PASTE_HELP, pszName, cchMax)) 1364 return S_OK; 1365 else 1366 return E_FAIL; 1367 1368 case GCS_HELPTEXTW: 1369 if (LoadStringW(module, IDS_REFLINK_PASTE_HELP, (LPWSTR)pszName, cchMax)) 1370 return S_OK; 1371 else 1372 return E_FAIL; 1373 1374 case GCS_VALIDATEA: 1375 case GCS_VALIDATEW: 1376 return S_OK; 1377 1378 case GCS_VERBA: 1379 return StringCchCopyA(pszName, cchMax, REFLINK_VERBA); 1380 1381 case GCS_VERBW: 1382 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, REFLINK_VERBW); 1383 1384 default: 1385 return E_INVALIDARG; 1386 } 1387 } else 1388 return E_INVALIDARG; 1389 } 1390 } 1391 1392 static void reflink_copy2(std::wstring srcfn, std::wstring destdir, std::wstring destname) { 1393 HANDLE source, dest; 1394 BOOL ret = FALSE; 1395 FILE_BASIC_INFO fbi; 1396 FILETIME atime, mtime; 1397 btrfs_inode_info bii; 1398 btrfs_set_inode_info bsii; 1399 ULONG bytesret; 1400 NTSTATUS Status; 1401 IO_STATUS_BLOCK iosb; 1402 btrfs_set_xattr bsxa; 1403 1404 source = CreateFileW(srcfn.c_str(), GENERIC_READ | FILE_TRAVERSE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL); 1405 if (source == INVALID_HANDLE_VALUE) 1406 return; 1407 1408 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii, sizeof(btrfs_inode_info)); 1409 if (!NT_SUCCESS(Status)) { 1410 CloseHandle(source); 1411 return; 1412 } 1413 1414 // if subvol, do snapshot instead 1415 if (bii.inode == SUBVOL_ROOT_INODE) { 1416 ULONG bcslen; 1417 btrfs_create_snapshot* bcs; 1418 HANDLE dirh; 1419 1420 dirh = CreateFileW(destdir.c_str(), FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 1421 if (dirh == INVALID_HANDLE_VALUE) { 1422 CloseHandle(source); 1423 return; 1424 } 1425 1426 bcslen = offsetof(btrfs_create_snapshot, name[0]) + (destname.length() * sizeof(WCHAR)); 1427 bcs = (btrfs_create_snapshot*)malloc(bcslen); 1428 bcs->subvol = source; 1429 bcs->namelen = destname.length() * sizeof(WCHAR); 1430 memcpy(bcs->name, destname.c_str(), destname.length() * sizeof(WCHAR)); 1431 1432 Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, bcslen, NULL, 0); 1433 1434 free(bcs); 1435 1436 CloseHandle(source); 1437 CloseHandle(dirh); 1438 1439 return; 1440 } 1441 1442 if (!GetFileInformationByHandleEx(source, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) { 1443 CloseHandle(source); 1444 return; 1445 } 1446 1447 if (bii.type == BTRFS_TYPE_CHARDEV || bii.type == BTRFS_TYPE_BLOCKDEV || bii.type == BTRFS_TYPE_FIFO || bii.type == BTRFS_TYPE_SOCKET) { 1448 HANDLE dirh; 1449 ULONG bmnsize; 1450 btrfs_mknod* bmn; 1451 1452 dirh = CreateFileW(destdir.c_str(), FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 1453 if (dirh == INVALID_HANDLE_VALUE) { 1454 CloseHandle(source); 1455 return; 1456 } 1457 1458 bmnsize = offsetof(btrfs_mknod, name[0]) + (destname.length() * sizeof(WCHAR)); 1459 bmn = (btrfs_mknod*)malloc(bmnsize); 1460 1461 bmn->inode = 0; 1462 bmn->type = bii.type; 1463 bmn->st_rdev = bii.st_rdev; 1464 bmn->namelen = destname.length() * sizeof(WCHAR); 1465 memcpy(bmn->name, destname.c_str(), bmn->namelen); 1466 1467 Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_MKNOD, bmn, bmnsize, NULL, 0); 1468 if (!NT_SUCCESS(Status)) { 1469 CloseHandle(dirh); 1470 CloseHandle(source); 1471 free(bmn); 1472 return; 1473 } 1474 1475 CloseHandle(dirh); 1476 free(bmn); 1477 1478 dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, 0, NULL); 1479 } else if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 1480 if (CreateDirectoryExW(srcfn.c_str(), (destdir + destname).c_str(), NULL)) 1481 dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1482 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 1483 else 1484 dest = INVALID_HANDLE_VALUE; 1485 } else 1486 dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, source); 1487 1488 if (dest == INVALID_HANDLE_VALUE) { 1489 CloseHandle(source); 1490 return; 1491 } 1492 1493 memset(&bsii, 0, sizeof(btrfs_set_inode_info)); 1494 1495 bsii.flags_changed = TRUE; 1496 bsii.flags = bii.flags; 1497 1498 if (bii.flags & BTRFS_INODE_COMPRESS) { 1499 bsii.compression_type_changed = TRUE; 1500 bsii.compression_type = bii.compression_type; 1501 } 1502 1503 Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0); 1504 if (!NT_SUCCESS(Status)) 1505 goto end; 1506 1507 if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 1508 if (!(fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { 1509 HANDLE h; 1510 WIN32_FIND_DATAW fff; 1511 std::wstring qs; 1512 1513 qs = srcfn; 1514 qs += L"\\*"; 1515 1516 h = FindFirstFileW(qs.c_str(), &fff); 1517 if (h != INVALID_HANDLE_VALUE) { 1518 do { 1519 std::wstring fn2; 1520 1521 if (fff.cFileName[0] == '.' && (fff.cFileName[1] == 0 || (fff.cFileName[1] == '.' && fff.cFileName[2] == 0))) 1522 continue; 1523 1524 fn2 = srcfn; 1525 fn2 += L"\\"; 1526 fn2 += fff.cFileName; 1527 1528 reflink_copy2(fn2, destdir + destname + L"\\", fff.cFileName); 1529 } while (FindNextFileW(h, &fff)); 1530 1531 FindClose(h); 1532 } 1533 } 1534 1535 // CreateDirectoryExW also copies streams, no need to do it here 1536 } else { 1537 HANDLE h; 1538 WIN32_FIND_STREAM_DATA fsd; 1539 1540 if (fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { 1541 reparse_header rh; 1542 ULONG rplen; 1543 UINT8* rp; 1544 1545 if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, &rh, sizeof(reparse_header), &bytesret, NULL)) { 1546 if (GetLastError() != ERROR_MORE_DATA) 1547 goto end; 1548 } 1549 1550 rplen = sizeof(reparse_header) + rh.ReparseDataLength; 1551 rp = (UINT8*)malloc(rplen); 1552 1553 if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, rp, rplen, &bytesret, NULL)) 1554 goto end; 1555 1556 if (!DeviceIoControl(dest, FSCTL_SET_REPARSE_POINT, rp, rplen, NULL, 0, &bytesret, NULL)) 1557 goto end; 1558 1559 free(rp); 1560 } else { 1561 FILE_STANDARD_INFO fsi; 1562 FILE_END_OF_FILE_INFO feofi; 1563 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER fgiib; 1564 FSCTL_SET_INTEGRITY_INFORMATION_BUFFER fsiib; 1565 DUPLICATE_EXTENTS_DATA ded; 1566 UINT64 offset, alloc_size; 1567 ULONG maxdup; 1568 1569 if (!GetFileInformationByHandleEx(source, FileStandardInfo, &fsi, sizeof(FILE_STANDARD_INFO))) 1570 goto end; 1571 1572 if (!DeviceIoControl(source, FSCTL_GET_INTEGRITY_INFORMATION, NULL, 0, &fgiib, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER), &bytesret, NULL)) 1573 goto end; 1574 1575 if (fbi.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) { 1576 if (!DeviceIoControl(dest, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesret, NULL)) 1577 goto end; 1578 } 1579 1580 fsiib.ChecksumAlgorithm = fgiib.ChecksumAlgorithm; 1581 fsiib.Reserved = 0; 1582 fsiib.Flags = fgiib.Flags; 1583 if (!DeviceIoControl(dest, FSCTL_SET_INTEGRITY_INFORMATION, &fsiib, sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER), NULL, 0, &bytesret, NULL)) 1584 goto end; 1585 1586 feofi.EndOfFile = fsi.EndOfFile; 1587 if (!SetFileInformationByHandle(dest, FileEndOfFileInfo, &feofi, sizeof(FILE_END_OF_FILE_INFO))) 1588 goto end; 1589 1590 ded.FileHandle = source; 1591 maxdup = 0xffffffff - fgiib.ClusterSizeInBytes + 1; 1592 1593 alloc_size = sector_align(fsi.EndOfFile.QuadPart, fgiib.ClusterSizeInBytes); 1594 1595 offset = 0; 1596 while (offset < alloc_size) { 1597 ded.SourceFileOffset.QuadPart = ded.TargetFileOffset.QuadPart = offset; 1598 ded.ByteCount.QuadPart = maxdup < (alloc_size - offset) ? maxdup : (alloc_size - offset); 1599 if (!DeviceIoControl(dest, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA), NULL, 0, &bytesret, NULL)) 1600 goto end; 1601 1602 offset += ded.ByteCount.QuadPart; 1603 } 1604 } 1605 1606 h = FindFirstStreamW(srcfn.c_str(), FindStreamInfoStandard, &fsd, 0); 1607 if (h != INVALID_HANDLE_VALUE) { 1608 do { 1609 std::wstring sn; 1610 1611 sn = fsd.cStreamName; 1612 1613 if (sn != L"::$DATA" && sn.length() > 6 && sn.substr(sn.length() - 6, 6) == L":$DATA") { 1614 HANDLE stream; 1615 UINT8* data = NULL; 1616 1617 if (fsd.StreamSize.QuadPart > 0) { 1618 std::wstring fn2; 1619 1620 fn2 = srcfn; 1621 fn2 += sn; 1622 1623 stream = CreateFileW(fn2.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); 1624 1625 if (stream == INVALID_HANDLE_VALUE) 1626 goto end; 1627 1628 // We can get away with this because our streams are guaranteed to be below 64 KB - 1629 // don't do this on NTFS! 1630 data = (UINT8*)malloc(fsd.StreamSize.QuadPart); 1631 1632 if (!ReadFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) { 1633 free(data); 1634 CloseHandle(stream); 1635 goto end; 1636 } 1637 1638 CloseHandle(stream); 1639 } 1640 1641 stream = CreateFileW((destdir + destname + sn).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, NULL); 1642 1643 if (stream == INVALID_HANDLE_VALUE) { 1644 if (data) free(data); 1645 goto end; 1646 } 1647 1648 if (data) { 1649 if (!WriteFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) { 1650 free(data); 1651 CloseHandle(stream); 1652 goto end; 1653 } 1654 1655 free(data); 1656 } 1657 1658 CloseHandle(stream); 1659 } 1660 } while (FindNextStreamW(h, &fsd)); 1661 1662 FindClose(h); 1663 } 1664 } 1665 1666 atime.dwLowDateTime = fbi.LastAccessTime.LowPart; 1667 atime.dwHighDateTime = fbi.LastAccessTime.HighPart; 1668 mtime.dwLowDateTime = fbi.LastWriteTime.LowPart; 1669 mtime.dwHighDateTime = fbi.LastWriteTime.HighPart; 1670 SetFileTime(dest, NULL, &atime, &mtime); 1671 1672 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, &bsxa, sizeof(btrfs_set_xattr)); 1673 1674 if (Status == STATUS_BUFFER_OVERFLOW || (NT_SUCCESS(Status) && bsxa.valuelen > 0)) { 1675 ULONG xalen = 0; 1676 btrfs_set_xattr *xa = NULL, *xa2; 1677 1678 do { 1679 xalen += 1024; 1680 1681 if (xa) free(xa); 1682 xa = (btrfs_set_xattr*)malloc(xalen); 1683 1684 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, xa, xalen); 1685 } while (Status == STATUS_BUFFER_OVERFLOW); 1686 1687 if (!NT_SUCCESS(Status)) { 1688 free(xa); 1689 goto end; 1690 } 1691 1692 xa2 = xa; 1693 while (xa2->valuelen > 0) { 1694 Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, xa2, 1695 offsetof(btrfs_set_xattr, data[0]) + xa2->namelen + xa2->valuelen, NULL, 0); 1696 if (!NT_SUCCESS(Status)) { 1697 free(xa); 1698 goto end; 1699 } 1700 xa2 = (btrfs_set_xattr*)&xa2->data[xa2->namelen + xa2->valuelen]; 1701 } 1702 1703 free(xa); 1704 } else if (!NT_SUCCESS(Status)) 1705 goto end; 1706 1707 ret = TRUE; 1708 1709 end: 1710 if (!ret) { 1711 FILE_DISPOSITION_INFO fdi; 1712 1713 fdi.DeleteFile = TRUE; 1714 SetFileInformationByHandle(dest, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO)); 1715 } 1716 1717 CloseHandle(dest); 1718 CloseHandle(source); 1719 } 1720 1721 #ifdef __REACTOS__ 1722 extern "C" { 1723 #endif 1724 1725 void CALLBACK ReflinkCopyW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1726 LPWSTR* args; 1727 int num_args; 1728 1729 args = CommandLineToArgvW(lpszCmdLine, &num_args); 1730 1731 if (!args) 1732 return; 1733 1734 if (num_args >= 2) { 1735 HANDLE destdirh; 1736 BOOL dest_is_dir = FALSE; 1737 std::wstring dest = args[num_args - 1], destdir, destname; 1738 WCHAR volpath2[MAX_PATH]; 1739 int i; 1740 1741 destdirh = CreateFileW(dest.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 1742 if (destdirh != INVALID_HANDLE_VALUE) { 1743 BY_HANDLE_FILE_INFORMATION bhfi; 1744 1745 if (GetFileInformationByHandle(destdirh, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 1746 dest_is_dir = TRUE; 1747 1748 destdir = dest; 1749 if (destdir.substr(destdir.length() - 1, 1) != L"\\") 1750 destdir += L"\\"; 1751 } 1752 CloseHandle(destdirh); 1753 } 1754 1755 if (!dest_is_dir) { 1756 size_t found = dest.rfind(L"\\"); 1757 1758 if (found == std::wstring::npos) { 1759 destdir = L""; 1760 destname = dest; 1761 } else { 1762 destdir = dest.substr(0, found); 1763 destname = dest.substr(found + 1); 1764 } 1765 } 1766 1767 if (!GetVolumePathNameW(dest.c_str(), volpath2, sizeof(volpath2) / sizeof(WCHAR))) 1768 goto end; 1769 1770 for (i = 0; i < num_args - 1; i++) { 1771 WIN32_FIND_DATAW ffd; 1772 HANDLE h; 1773 1774 h = FindFirstFileW(args[i], &ffd); 1775 if (h != INVALID_HANDLE_VALUE) { 1776 WCHAR volpath1[MAX_PATH]; 1777 std::wstring path = args[i]; 1778 size_t found = path.rfind(L"\\"); 1779 1780 if (found == std::wstring::npos) 1781 path = L""; 1782 else 1783 path = path.substr(0, found); 1784 1785 path += L"\\"; 1786 1787 if (get_volume_path_parent(path.c_str(), volpath1, sizeof(volpath1) / sizeof(WCHAR))) { 1788 if (!wcscmp(volpath1, volpath2)) { 1789 do { 1790 reflink_copy2(path + ffd.cFileName, destdir, dest_is_dir ? ffd.cFileName : destname); 1791 } while (FindNextFileW(h, &ffd)); 1792 } 1793 } 1794 1795 FindClose(h); 1796 } 1797 } 1798 } 1799 1800 end: 1801 LocalFree(args); 1802 } 1803 1804 #ifdef __REACTOS__ 1805 } /* extern "C" */ 1806 #endif 1807