[NTDLL] Allow shimdata to override the process manifest.
[reactos.git] / dll / shellext / shellbtrfs / contextmenu.cpp
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 goto end;
904 }
905
906 // We can get away with this because our streams are guaranteed to be below 64 KB -
907 // don't do this on NTFS!
908 data = (UINT8*)malloc(fsd.StreamSize.QuadPart);
909
910 if (!ReadFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
911 ShowError(hwnd, GetLastError());
912 free(data);
913 CloseHandle(stream);
914 goto end;
915 }
916
917 CloseHandle(stream);
918 }
919
920 stream = CreateFileW((newpath + sn).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, NULL);
921
922 if (stream == INVALID_HANDLE_VALUE) {
923 ShowError(hwnd, GetLastError());
924
925 if (data) free(data);
926
927 goto end;
928 }
929
930 if (data) {
931 if (!WriteFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
932 ShowError(hwnd, GetLastError());
933 free(data);
934 CloseHandle(stream);
935 goto end;
936 }
937
938 free(data);
939 }
940
941 CloseHandle(stream);
942 }
943 } while (FindNextStreamW(h, &fsd));
944
945 FindClose(h);
946 }
947 }
948
949 atime.dwLowDateTime = fbi.LastAccessTime.LowPart;
950 atime.dwHighDateTime = fbi.LastAccessTime.HighPart;
951 mtime.dwLowDateTime = fbi.LastWriteTime.LowPart;
952 mtime.dwHighDateTime = fbi.LastWriteTime.HighPart;
953 SetFileTime(dest, NULL, &atime, &mtime);
954
955 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, &bsxa, sizeof(btrfs_set_xattr));
956
957 if (Status == STATUS_BUFFER_OVERFLOW || (NT_SUCCESS(Status) && bsxa.valuelen > 0)) {
958 ULONG xalen = 0;
959 btrfs_set_xattr *xa = NULL, *xa2;
960
961 do {
962 xalen += 1024;
963
964 if (xa) free(xa);
965 xa = (btrfs_set_xattr*)malloc(xalen);
966
967 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, xa, xalen);
968 } while (Status == STATUS_BUFFER_OVERFLOW);
969
970 if (!NT_SUCCESS(Status)) {
971 free(xa);
972 ShowNtStatusError(hwnd, Status);
973 goto end;
974 }
975
976 xa2 = xa;
977 while (xa2->valuelen > 0) {
978 Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, xa2,
979 offsetof(btrfs_set_xattr, data[0]) + xa2->namelen + xa2->valuelen, NULL, 0);
980 if (!NT_SUCCESS(Status)) {
981 free(xa);
982 ShowNtStatusError(hwnd, Status);
983 goto end;
984 }
985 xa2 = (btrfs_set_xattr*)&xa2->data[xa2->namelen + xa2->valuelen];
986 }
987
988 free(xa);
989 } else if (!NT_SUCCESS(Status)) {
990 ShowNtStatusError(hwnd, Status);
991 goto end;
992 }
993
994 ret = TRUE;
995
996 end:
997 if (!ret) {
998 FILE_DISPOSITION_INFO fdi;
999
1000 fdi.DeleteFile = TRUE;
1001 if (!SetFileInformationByHandle(dest, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO)))
1002 ShowError(hwnd, GetLastError());
1003 }
1004
1005 CloseHandle(dest);
1006 CloseHandle(source);
1007
1008 return ret;
1009 }
1010
1011 HRESULT __stdcall BtrfsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO picia) {
1012 LPCMINVOKECOMMANDINFOEX pici = (LPCMINVOKECOMMANDINFOEX)picia;
1013
1014 if (ignore)
1015 return E_INVALIDARG;
1016
1017 if (!bg) {
1018 if ((IS_INTRESOURCE(pici->lpVerb) && allow_snapshot && pici->lpVerb == 0) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, SNAPSHOT_VERBA))) {
1019 UINT num_files, i;
1020 WCHAR fn[MAX_PATH];
1021
1022 if (!stgm_set)
1023 return E_FAIL;
1024
1025 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
1026
1027 if (num_files == 0)
1028 return E_FAIL;
1029
1030 for (i = 0; i < num_files; i++) {
1031 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
1032 create_snapshot(pici->hwnd, fn);
1033 }
1034 }
1035
1036 return S_OK;
1037 } else if ((IS_INTRESOURCE(pici->lpVerb) && ((allow_snapshot && (ULONG_PTR)pici->lpVerb == 1) || (!allow_snapshot && (ULONG_PTR)pici->lpVerb == 0))) ||
1038 (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, SEND_VERBA))) {
1039 UINT num_files, i;
1040 WCHAR dll[MAX_PATH], fn[MAX_PATH];
1041 std::wstring t;
1042 SHELLEXECUTEINFOW sei;
1043
1044 GetModuleFileNameW(module, dll, sizeof(dll) / sizeof(WCHAR));
1045
1046 if (!stgm_set)
1047 return E_FAIL;
1048
1049 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
1050
1051 if (num_files == 0)
1052 return E_FAIL;
1053
1054 for (i = 0; i < num_files; i++) {
1055 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
1056 t = L"\"";
1057 t += dll;
1058 t += L"\",SendSubvolGUI ";
1059 t += fn;
1060
1061 RtlZeroMemory(&sei, sizeof(sei));
1062
1063 sei.cbSize = sizeof(sei);
1064 sei.hwnd = pici->hwnd;
1065 sei.lpVerb = L"runas";
1066 sei.lpFile = L"rundll32.exe";
1067 sei.lpParameters = t.c_str();
1068 sei.nShow = SW_SHOW;
1069 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
1070
1071 if (!ShellExecuteExW(&sei)) {
1072 ShowError(pici->hwnd, GetLastError());
1073 return E_FAIL;
1074 }
1075
1076 WaitForSingleObject(sei.hProcess, INFINITE);
1077 CloseHandle(sei.hProcess);
1078 }
1079 }
1080
1081 return S_OK;
1082 }
1083 } else {
1084 if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 0) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, NEW_SUBVOL_VERBA))) {
1085 HANDLE h;
1086 IO_STATUS_BLOCK iosb;
1087 NTSTATUS Status;
1088 ULONG pathlen, searchpathlen, pathend, bcslen;
1089 WCHAR name[MAX_PATH], *searchpath;
1090 btrfs_create_subvol* bcs;
1091 HANDLE fff;
1092 WIN32_FIND_DATAW wfd;
1093
1094 if (!LoadStringW(module, IDS_NEW_SUBVOL_FILENAME, name, MAX_PATH)) {
1095 ShowError(pici->hwnd, GetLastError());
1096 return E_FAIL;
1097 }
1098
1099 h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1100
1101 if (h == INVALID_HANDLE_VALUE) {
1102 ShowError(pici->hwnd, GetLastError());
1103 return E_FAIL;
1104 }
1105
1106 pathlen = wcslen(path);
1107
1108 searchpathlen = pathlen + wcslen(name) + 10;
1109 searchpath = (WCHAR*)malloc(searchpathlen * sizeof(WCHAR));
1110
1111 StringCchCopyW(searchpath, searchpathlen, path);
1112 StringCchCatW(searchpath, searchpathlen, L"\\");
1113 pathend = wcslen(searchpath);
1114
1115 StringCchCatW(searchpath, searchpathlen, name);
1116
1117 fff = FindFirstFileW(searchpath, &wfd);
1118
1119 if (fff != INVALID_HANDLE_VALUE) {
1120 ULONG i = wcslen(searchpath), num = 2;
1121
1122 do {
1123 FindClose(fff);
1124
1125 searchpath[i] = 0;
1126 if (StringCchPrintfW(searchpath, searchpathlen, L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) {
1127 MessageBoxW(pici->hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
1128 CloseHandle(h);
1129 return E_FAIL;
1130 }
1131
1132 fff = FindFirstFileW(searchpath, &wfd);
1133 num++;
1134 } while (fff != INVALID_HANDLE_VALUE);
1135 }
1136
1137 bcslen = offsetof(btrfs_create_subvol, name[0]) + (wcslen(&searchpath[pathend]) * sizeof(WCHAR));
1138 bcs = (btrfs_create_subvol*)malloc(bcslen);
1139
1140 bcs->readonly = FALSE;
1141 bcs->posix = FALSE;
1142 bcs->namelen = wcslen(&searchpath[pathend]) * sizeof(WCHAR);
1143 memcpy(bcs->name, &searchpath[pathend], bcs->namelen);
1144
1145 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, NULL, 0);
1146
1147 free(searchpath);
1148 free(bcs);
1149
1150 if (!NT_SUCCESS(Status)) {
1151 CloseHandle(h);
1152 ShowNtStatusError(pici->hwnd, Status);
1153 return E_FAIL;
1154 }
1155
1156 CloseHandle(h);
1157
1158 return S_OK;
1159 } else if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 1) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, RECV_VERBA))) {
1160 WCHAR dll[MAX_PATH];
1161 std::wstring t;
1162 SHELLEXECUTEINFOW sei;
1163
1164 GetModuleFileNameW(module, dll, sizeof(dll) / sizeof(WCHAR));
1165
1166 t = L"\"";
1167 t += dll;
1168 t += L"\",RecvSubvolGUI ";
1169 t += path;
1170
1171 RtlZeroMemory(&sei, sizeof(sei));
1172
1173 sei.cbSize = sizeof(sei);
1174 sei.hwnd = pici->hwnd;
1175 sei.lpVerb = L"runas";
1176 sei.lpFile = L"rundll32.exe";
1177 sei.lpParameters = t.c_str();
1178 sei.nShow = SW_SHOW;
1179 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
1180
1181 if (!ShellExecuteExW(&sei)) {
1182 ShowError(pici->hwnd, GetLastError());
1183 return E_FAIL;
1184 }
1185
1186 WaitForSingleObject(sei.hProcess, INFINITE);
1187 CloseHandle(sei.hProcess);
1188
1189 return S_OK;
1190 } else if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 2) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, REFLINK_VERBA))) {
1191 HDROP hdrop;
1192
1193 if (!IsClipboardFormatAvailable(CF_HDROP))
1194 return S_OK;
1195
1196 if (!OpenClipboard(pici->hwnd)) {
1197 ShowError(pici->hwnd, GetLastError());
1198 return E_FAIL;
1199 }
1200
1201 hdrop = (HDROP)GetClipboardData(CF_HDROP);
1202
1203 if (hdrop) {
1204 HANDLE lh;
1205
1206 lh = GlobalLock(hdrop);
1207
1208 if (lh) {
1209 ULONG num_files, i;
1210 WCHAR fn[MAX_PATH];
1211
1212 num_files = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
1213
1214 for (i = 0; i < num_files; i++) {
1215 if (DragQueryFileW(hdrop, i, fn, sizeof(fn) / sizeof(WCHAR))) {
1216 if (!reflink_copy(pici->hwnd, fn, pici->lpDirectoryW)) {
1217 GlobalUnlock(lh);
1218 CloseClipboard();
1219 return E_FAIL;
1220 }
1221 }
1222 }
1223
1224 GlobalUnlock(lh);
1225 }
1226 }
1227
1228 CloseClipboard();
1229
1230 return S_OK;
1231 }
1232 }
1233
1234 return E_FAIL;
1235 }
1236
1237 HRESULT __stdcall BtrfsContextMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax) {
1238 if (ignore)
1239 return E_INVALIDARG;
1240
1241 if (idCmd != 0)
1242 return E_INVALIDARG;
1243
1244 if (!bg) {
1245 if (idCmd == 0) {
1246 switch (uFlags) {
1247 case GCS_HELPTEXTA:
1248 if (LoadStringA(module, IDS_CREATE_SNAPSHOT_HELP_TEXT, pszName, cchMax))
1249 return S_OK;
1250 else
1251 return E_FAIL;
1252
1253 case GCS_HELPTEXTW:
1254 if (LoadStringW(module, IDS_CREATE_SNAPSHOT_HELP_TEXT, (LPWSTR)pszName, cchMax))
1255 return S_OK;
1256 else
1257 return E_FAIL;
1258
1259 case GCS_VALIDATEA:
1260 case GCS_VALIDATEW:
1261 return S_OK;
1262
1263 case GCS_VERBA:
1264 return StringCchCopyA(pszName, cchMax, SNAPSHOT_VERBA);
1265
1266 case GCS_VERBW:
1267 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, SNAPSHOT_VERBW);
1268
1269 default:
1270 return E_INVALIDARG;
1271 }
1272 } else if (idCmd == 1) {
1273 switch (uFlags) {
1274 case GCS_HELPTEXTA:
1275 if (LoadStringA(module, IDS_SEND_SUBVOL_HELP, pszName, cchMax))
1276 return S_OK;
1277 else
1278 return E_FAIL;
1279
1280 case GCS_HELPTEXTW:
1281 if (LoadStringW(module, IDS_SEND_SUBVOL_HELP, (LPWSTR)pszName, cchMax))
1282 return S_OK;
1283 else
1284 return E_FAIL;
1285
1286 case GCS_VALIDATEA:
1287 case GCS_VALIDATEW:
1288 return S_OK;
1289
1290 case GCS_VERBA:
1291 return StringCchCopyA(pszName, cchMax, SEND_VERBA);
1292
1293 case GCS_VERBW:
1294 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, SEND_VERBW);
1295
1296 default:
1297 return E_INVALIDARG;
1298 }
1299 } else
1300 return E_INVALIDARG;
1301 } else {
1302 if (idCmd == 0) {
1303 switch (uFlags) {
1304 case GCS_HELPTEXTA:
1305 if (LoadStringA(module, IDS_NEW_SUBVOL_HELP_TEXT, pszName, cchMax))
1306 return S_OK;
1307 else
1308 return E_FAIL;
1309
1310 case GCS_HELPTEXTW:
1311 if (LoadStringW(module, IDS_NEW_SUBVOL_HELP_TEXT, (LPWSTR)pszName, cchMax))
1312 return S_OK;
1313 else
1314 return E_FAIL;
1315
1316 case GCS_VALIDATEA:
1317 case GCS_VALIDATEW:
1318 return S_OK;
1319
1320 case GCS_VERBA:
1321 return StringCchCopyA(pszName, cchMax, NEW_SUBVOL_VERBA);
1322
1323 case GCS_VERBW:
1324 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, NEW_SUBVOL_VERBW);
1325
1326 default:
1327 return E_INVALIDARG;
1328 }
1329 } else if (idCmd == 1) {
1330 switch (uFlags) {
1331 case GCS_HELPTEXTA:
1332 if (LoadStringA(module, IDS_RECV_SUBVOL_HELP, pszName, cchMax))
1333 return S_OK;
1334 else
1335 return E_FAIL;
1336
1337 case GCS_HELPTEXTW:
1338 if (LoadStringW(module, IDS_RECV_SUBVOL_HELP, (LPWSTR)pszName, cchMax))
1339 return S_OK;
1340 else
1341 return E_FAIL;
1342
1343 case GCS_VALIDATEA:
1344 case GCS_VALIDATEW:
1345 return S_OK;
1346
1347 case GCS_VERBA:
1348 return StringCchCopyA(pszName, cchMax, RECV_VERBA);
1349
1350 case GCS_VERBW:
1351 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, RECV_VERBW);
1352
1353 default:
1354 return E_INVALIDARG;
1355 }
1356 } else if (idCmd == 2) {
1357 switch (uFlags) {
1358 case GCS_HELPTEXTA:
1359 if (LoadStringA(module, IDS_REFLINK_PASTE_HELP, pszName, cchMax))
1360 return S_OK;
1361 else
1362 return E_FAIL;
1363
1364 case GCS_HELPTEXTW:
1365 if (LoadStringW(module, IDS_REFLINK_PASTE_HELP, (LPWSTR)pszName, cchMax))
1366 return S_OK;
1367 else
1368 return E_FAIL;
1369
1370 case GCS_VALIDATEA:
1371 case GCS_VALIDATEW:
1372 return S_OK;
1373
1374 case GCS_VERBA:
1375 return StringCchCopyA(pszName, cchMax, REFLINK_VERBA);
1376
1377 case GCS_VERBW:
1378 return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, REFLINK_VERBW);
1379
1380 default:
1381 return E_INVALIDARG;
1382 }
1383 } else
1384 return E_INVALIDARG;
1385 }
1386 }
1387
1388 static void reflink_copy2(std::wstring srcfn, std::wstring destdir, std::wstring destname) {
1389 HANDLE source, dest;
1390 BOOL ret = FALSE;
1391 FILE_BASIC_INFO fbi;
1392 FILETIME atime, mtime;
1393 btrfs_inode_info bii;
1394 btrfs_set_inode_info bsii;
1395 ULONG bytesret;
1396 NTSTATUS Status;
1397 IO_STATUS_BLOCK iosb;
1398 btrfs_set_xattr bsxa;
1399
1400 source = CreateFileW(srcfn.c_str(), GENERIC_READ | FILE_TRAVERSE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);
1401 if (source == INVALID_HANDLE_VALUE)
1402 return;
1403
1404 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii, sizeof(btrfs_inode_info));
1405 if (!NT_SUCCESS(Status)) {
1406 CloseHandle(source);
1407 return;
1408 }
1409
1410 // if subvol, do snapshot instead
1411 if (bii.inode == SUBVOL_ROOT_INODE) {
1412 ULONG bcslen;
1413 btrfs_create_snapshot* bcs;
1414 HANDLE dirh;
1415
1416 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);
1417 if (dirh == INVALID_HANDLE_VALUE) {
1418 CloseHandle(source);
1419 return;
1420 }
1421
1422 bcslen = offsetof(btrfs_create_snapshot, name[0]) + (destname.length() * sizeof(WCHAR));
1423 bcs = (btrfs_create_snapshot*)malloc(bcslen);
1424 bcs->subvol = source;
1425 bcs->namelen = destname.length() * sizeof(WCHAR);
1426 memcpy(bcs->name, destname.c_str(), destname.length() * sizeof(WCHAR));
1427
1428 Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, bcslen, NULL, 0);
1429
1430 free(bcs);
1431
1432 CloseHandle(source);
1433 CloseHandle(dirh);
1434
1435 return;
1436 }
1437
1438 if (!GetFileInformationByHandleEx(source, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) {
1439 CloseHandle(source);
1440 return;
1441 }
1442
1443 if (bii.type == BTRFS_TYPE_CHARDEV || bii.type == BTRFS_TYPE_BLOCKDEV || bii.type == BTRFS_TYPE_FIFO || bii.type == BTRFS_TYPE_SOCKET) {
1444 HANDLE dirh;
1445 ULONG bmnsize;
1446 btrfs_mknod* bmn;
1447
1448 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);
1449 if (dirh == INVALID_HANDLE_VALUE) {
1450 CloseHandle(source);
1451 return;
1452 }
1453
1454 bmnsize = offsetof(btrfs_mknod, name[0]) + (destname.length() * sizeof(WCHAR));
1455 bmn = (btrfs_mknod*)malloc(bmnsize);
1456
1457 bmn->inode = 0;
1458 bmn->type = bii.type;
1459 bmn->st_rdev = bii.st_rdev;
1460 bmn->namelen = destname.length() * sizeof(WCHAR);
1461 memcpy(bmn->name, destname.c_str(), bmn->namelen);
1462
1463 Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_MKNOD, bmn, bmnsize, NULL, 0);
1464 if (!NT_SUCCESS(Status)) {
1465 CloseHandle(dirh);
1466 CloseHandle(source);
1467 free(bmn);
1468 return;
1469 }
1470
1471 CloseHandle(dirh);
1472 free(bmn);
1473
1474 dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, 0, NULL);
1475 } else if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1476 if (CreateDirectoryExW(srcfn.c_str(), (destdir + destname).c_str(), NULL))
1477 dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1478 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1479 else
1480 dest = INVALID_HANDLE_VALUE;
1481 } else
1482 dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, source);
1483
1484 if (dest == INVALID_HANDLE_VALUE) {
1485 CloseHandle(source);
1486 return;
1487 }
1488
1489 memset(&bsii, 0, sizeof(btrfs_set_inode_info));
1490
1491 bsii.flags_changed = TRUE;
1492 bsii.flags = bii.flags;
1493
1494 if (bii.flags & BTRFS_INODE_COMPRESS) {
1495 bsii.compression_type_changed = TRUE;
1496 bsii.compression_type = bii.compression_type;
1497 }
1498
1499 Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
1500 if (!NT_SUCCESS(Status))
1501 goto end;
1502
1503 if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1504 if (!(fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1505 HANDLE h;
1506 WIN32_FIND_DATAW fff;
1507 std::wstring qs;
1508
1509 qs = srcfn;
1510 qs += L"\\*";
1511
1512 h = FindFirstFileW(qs.c_str(), &fff);
1513 if (h != INVALID_HANDLE_VALUE) {
1514 do {
1515 std::wstring fn2;
1516
1517 if (fff.cFileName[0] == '.' && (fff.cFileName[1] == 0 || (fff.cFileName[1] == '.' && fff.cFileName[2] == 0)))
1518 continue;
1519
1520 fn2 = srcfn;
1521 fn2 += L"\\";
1522 fn2 += fff.cFileName;
1523
1524 reflink_copy2(fn2, destdir + destname + L"\\", fff.cFileName);
1525 } while (FindNextFileW(h, &fff));
1526
1527 FindClose(h);
1528 }
1529 }
1530
1531 // CreateDirectoryExW also copies streams, no need to do it here
1532 } else {
1533 HANDLE h;
1534 WIN32_FIND_STREAM_DATA fsd;
1535
1536 if (fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1537 reparse_header rh;
1538 ULONG rplen;
1539 UINT8* rp;
1540
1541 if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, &rh, sizeof(reparse_header), &bytesret, NULL)) {
1542 if (GetLastError() != ERROR_MORE_DATA)
1543 goto end;
1544 }
1545
1546 rplen = sizeof(reparse_header) + rh.ReparseDataLength;
1547 rp = (UINT8*)malloc(rplen);
1548
1549 if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, rp, rplen, &bytesret, NULL))
1550 goto end;
1551
1552 if (!DeviceIoControl(dest, FSCTL_SET_REPARSE_POINT, rp, rplen, NULL, 0, &bytesret, NULL))
1553 goto end;
1554
1555 free(rp);
1556 } else {
1557 FILE_STANDARD_INFO fsi;
1558 FILE_END_OF_FILE_INFO feofi;
1559 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER fgiib;
1560 FSCTL_SET_INTEGRITY_INFORMATION_BUFFER fsiib;
1561 DUPLICATE_EXTENTS_DATA ded;
1562 UINT64 offset, alloc_size;
1563 ULONG maxdup;
1564
1565 if (!GetFileInformationByHandleEx(source, FileStandardInfo, &fsi, sizeof(FILE_STANDARD_INFO)))
1566 goto end;
1567
1568 if (!DeviceIoControl(source, FSCTL_GET_INTEGRITY_INFORMATION, NULL, 0, &fgiib, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER), &bytesret, NULL))
1569 goto end;
1570
1571 if (fbi.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) {
1572 if (!DeviceIoControl(dest, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesret, NULL))
1573 goto end;
1574 }
1575
1576 fsiib.ChecksumAlgorithm = fgiib.ChecksumAlgorithm;
1577 fsiib.Reserved = 0;
1578 fsiib.Flags = fgiib.Flags;
1579 if (!DeviceIoControl(dest, FSCTL_SET_INTEGRITY_INFORMATION, &fsiib, sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER), NULL, 0, &bytesret, NULL))
1580 goto end;
1581
1582 feofi.EndOfFile = fsi.EndOfFile;
1583 if (!SetFileInformationByHandle(dest, FileEndOfFileInfo, &feofi, sizeof(FILE_END_OF_FILE_INFO)))
1584 goto end;
1585
1586 ded.FileHandle = source;
1587 maxdup = 0xffffffff - fgiib.ClusterSizeInBytes + 1;
1588
1589 alloc_size = sector_align(fsi.EndOfFile.QuadPart, fgiib.ClusterSizeInBytes);
1590
1591 offset = 0;
1592 while (offset < alloc_size) {
1593 ded.SourceFileOffset.QuadPart = ded.TargetFileOffset.QuadPart = offset;
1594 ded.ByteCount.QuadPart = maxdup < (alloc_size - offset) ? maxdup : (alloc_size - offset);
1595 if (!DeviceIoControl(dest, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA), NULL, 0, &bytesret, NULL))
1596 goto end;
1597
1598 offset += ded.ByteCount.QuadPart;
1599 }
1600 }
1601
1602 h = FindFirstStreamW(srcfn.c_str(), FindStreamInfoStandard, &fsd, 0);
1603 if (h != INVALID_HANDLE_VALUE) {
1604 do {
1605 std::wstring sn;
1606
1607 sn = fsd.cStreamName;
1608
1609 if (sn != L"::$DATA" && sn.length() > 6 && sn.substr(sn.length() - 6, 6) == L":$DATA") {
1610 HANDLE stream;
1611 UINT8* data = NULL;
1612
1613 if (fsd.StreamSize.QuadPart > 0) {
1614 std::wstring fn2;
1615
1616 fn2 = srcfn;
1617 fn2 += sn;
1618
1619 stream = CreateFileW(fn2.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1620
1621 if (stream == INVALID_HANDLE_VALUE)
1622 goto end;
1623
1624 // We can get away with this because our streams are guaranteed to be below 64 KB -
1625 // don't do this on NTFS!
1626 data = (UINT8*)malloc(fsd.StreamSize.QuadPart);
1627
1628 if (!ReadFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
1629 free(data);
1630 CloseHandle(stream);
1631 goto end;
1632 }
1633
1634 CloseHandle(stream);
1635 }
1636
1637 stream = CreateFileW((destdir + destname + sn).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, NULL);
1638
1639 if (stream == INVALID_HANDLE_VALUE) {
1640 if (data) free(data);
1641 goto end;
1642 }
1643
1644 if (data) {
1645 if (!WriteFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
1646 free(data);
1647 CloseHandle(stream);
1648 goto end;
1649 }
1650
1651 free(data);
1652 }
1653
1654 CloseHandle(stream);
1655 }
1656 } while (FindNextStreamW(h, &fsd));
1657
1658 FindClose(h);
1659 }
1660 }
1661
1662 atime.dwLowDateTime = fbi.LastAccessTime.LowPart;
1663 atime.dwHighDateTime = fbi.LastAccessTime.HighPart;
1664 mtime.dwLowDateTime = fbi.LastWriteTime.LowPart;
1665 mtime.dwHighDateTime = fbi.LastWriteTime.HighPart;
1666 SetFileTime(dest, NULL, &atime, &mtime);
1667
1668 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, &bsxa, sizeof(btrfs_set_xattr));
1669
1670 if (Status == STATUS_BUFFER_OVERFLOW || (NT_SUCCESS(Status) && bsxa.valuelen > 0)) {
1671 ULONG xalen = 0;
1672 btrfs_set_xattr *xa = NULL, *xa2;
1673
1674 do {
1675 xalen += 1024;
1676
1677 if (xa) free(xa);
1678 xa = (btrfs_set_xattr*)malloc(xalen);
1679
1680 Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, xa, xalen);
1681 } while (Status == STATUS_BUFFER_OVERFLOW);
1682
1683 if (!NT_SUCCESS(Status)) {
1684 free(xa);
1685 goto end;
1686 }
1687
1688 xa2 = xa;
1689 while (xa2->valuelen > 0) {
1690 Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, xa2,
1691 offsetof(btrfs_set_xattr, data[0]) + xa2->namelen + xa2->valuelen, NULL, 0);
1692 if (!NT_SUCCESS(Status)) {
1693 free(xa);
1694 goto end;
1695 }
1696 xa2 = (btrfs_set_xattr*)&xa2->data[xa2->namelen + xa2->valuelen];
1697 }
1698
1699 free(xa);
1700 } else if (!NT_SUCCESS(Status))
1701 goto end;
1702
1703 ret = TRUE;
1704
1705 end:
1706 if (!ret) {
1707 FILE_DISPOSITION_INFO fdi;
1708
1709 fdi.DeleteFile = TRUE;
1710 SetFileInformationByHandle(dest, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO));
1711 }
1712
1713 CloseHandle(dest);
1714 CloseHandle(source);
1715 }
1716
1717 void CALLBACK ReflinkCopyW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1718 LPWSTR* args;
1719 int num_args;
1720
1721 args = CommandLineToArgvW(lpszCmdLine, &num_args);
1722
1723 if (!args)
1724 return;
1725
1726 if (num_args >= 2) {
1727 HANDLE destdirh;
1728 BOOL dest_is_dir = FALSE;
1729 std::wstring dest = args[num_args - 1], destdir, destname;
1730 WCHAR volpath2[MAX_PATH];
1731 int i;
1732
1733 destdirh = CreateFileW(dest.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1734 if (destdirh != INVALID_HANDLE_VALUE) {
1735 BY_HANDLE_FILE_INFORMATION bhfi;
1736
1737 if (GetFileInformationByHandle(destdirh, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1738 dest_is_dir = TRUE;
1739
1740 destdir = dest;
1741 if (destdir.substr(destdir.length() - 1, 1) != L"\\")
1742 destdir += L"\\";
1743 }
1744 CloseHandle(destdirh);
1745 }
1746
1747 if (!dest_is_dir) {
1748 size_t found = dest.rfind(L"\\");
1749
1750 if (found == std::wstring::npos) {
1751 destdir = L"";
1752 destname = dest;
1753 } else {
1754 destdir = dest.substr(0, found);
1755 destname = dest.substr(found + 1);
1756 }
1757 }
1758
1759 if (!GetVolumePathNameW(dest.c_str(), volpath2, sizeof(volpath2) / sizeof(WCHAR)))
1760 goto end;
1761
1762 for (i = 0; i < num_args - 1; i++) {
1763 WIN32_FIND_DATAW ffd;
1764 HANDLE h;
1765
1766 h = FindFirstFileW(args[i], &ffd);
1767 if (h != INVALID_HANDLE_VALUE) {
1768 WCHAR volpath1[MAX_PATH];
1769 std::wstring path = args[i];
1770 size_t found = path.rfind(L"\\");
1771
1772 if (found == std::wstring::npos)
1773 path = L"";
1774 else
1775 path = path.substr(0, found);
1776
1777 path += L"\\";
1778
1779 if (get_volume_path_parent(path.c_str(), volpath1, sizeof(volpath1) / sizeof(WCHAR))) {
1780 if (!wcscmp(volpath1, volpath2)) {
1781 do {
1782 reflink_copy2(path + ffd.cFileName, destdir, dest_is_dir ? ffd.cFileName : destname);
1783 } while (FindNextFileW(h, &ffd));
1784 }
1785 }
1786
1787 FindClose(h);
1788 }
1789 }
1790 }
1791
1792 end:
1793 LocalFree(args);
1794 }