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