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