[NTOSKRNL] Reference the file object before issuing the unlock all IRP
[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 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