1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
28 #define WIN32_NO_STATUS
34 #include <ndk/iofuncs.h>
41 #define NO_SHLWAPI_STRFCNS
44 #include "contextmenu.h"
47 #include "../btrfsioctl.h"
49 #include "btrfsioctl.h"
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"
65 USHORT ReparseDataLength
;
69 // FIXME - don't assume subvol's top inode is 0x100
71 HRESULT __stdcall
BtrfsContextMenu::QueryInterface(REFIID riid
, void **ppObj
) {
72 if (riid
== IID_IUnknown
|| riid
== IID_IContextMenu
) {
73 *ppObj
= static_cast<IContextMenu
*>(this);
76 } else if (riid
== IID_IShellExtInit
) {
77 *ppObj
= static_cast<IShellExtInit
*>(this);
86 HRESULT __stdcall
BtrfsContextMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder
, IDataObject
* pdtobj
, HKEY hkeyProgID
) {
89 btrfs_get_file_ids bgfi
;
93 FORMATETC format
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
101 stgm
.tymed
= TYMED_HGLOBAL
;
103 if (FAILED(pdtobj
->GetData(&format
, &stgm
)))
108 hdrop
= (HDROP
)GlobalLock(stgm
.hGlobal
);
111 ReleaseStgMedium(&stgm
);
116 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
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
);
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
));
125 if (NT_SUCCESS(Status
) && bgfi
.inode
== 0x100 && !bgfi
.top
) {
126 WCHAR parpath
[MAX_PATH
];
129 StringCchCopyW(parpath
, sizeof(parpath
) / sizeof(WCHAR
), fn
);
131 PathRemoveFileSpecW(parpath
);
133 h2
= CreateFileW(parpath
, FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
135 if (h2
!= INVALID_HANDLE_VALUE
)
136 allow_snapshot
= TRUE
;
158 if (!SHGetPathFromIDListW(pidlFolder
, path
))
161 // check we have permissions to create new subdirectory
163 h
= CreateFileW(path
, FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
165 if (h
== INVALID_HANDLE_VALUE
)
168 // check is Btrfs volume
170 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_FILE_IDS
, NULL
, 0, &bgfi
, sizeof(btrfs_get_file_ids
));
172 if (!NT_SUCCESS(Status
)) {
185 static BOOL
get_volume_path_parent(const WCHAR
* fn
, WCHAR
* volpath
, ULONG volpathlen
) {
189 f
= PathFindFileNameW(fn
);
192 return GetVolumePathNameW(fn
, volpath
, volpathlen
);
194 p
= (WCHAR
*)malloc((f
- fn
+ 1) * sizeof(WCHAR
));
195 memcpy(p
, fn
, (f
- fn
) * sizeof(WCHAR
));
198 b
= GetVolumePathNameW(p
, volpath
, volpathlen
);
205 static BOOL
show_reflink_paste(WCHAR
* path
) {
209 WCHAR fn
[MAX_PATH
], volpath1
[255], volpath2
[255];
211 if (!IsClipboardFormatAvailable(CF_HDROP
))
214 if (!GetVolumePathNameW(path
, volpath1
, sizeof(volpath1
) / sizeof(WCHAR
)))
217 if (!OpenClipboard(NULL
))
220 hdrop
= (HDROP
)GetClipboardData(CF_HDROP
);
227 lh
= GlobalLock(hdrop
);
234 num_files
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
236 if (num_files
== 0) {
242 if (!DragQueryFileW(hdrop
, 0, fn
, sizeof(fn
) / sizeof(WCHAR
))) {
248 if (!get_volume_path_parent(fn
, volpath2
, sizeof(volpath2
) / sizeof(WCHAR
))) {
258 return !wcscmp(volpath1
, volpath2
);
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
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
;
270 pbmi
->bmiHeader
.biWidth
= cx
;
271 pbmi
->bmiHeader
.biHeight
= cy
;
272 pbmi
->bmiHeader
.biBitCount
= bpp
;
275 static HRESULT
Create32BitHBITMAP(HDC hdc
, const SIZE
*psize
, void **ppvBits
, HBITMAP
* phBmp
) {
281 InitBitmapInfo(&bmi
, sizeof(bmi
), psize
->cx
, psize
->cy
, 32);
283 hdcUsed
= hdc
? hdc
: GetDC(NULL
);
286 *phBmp
= CreateDIBSection(hdcUsed
, &bmi
, DIB_RGB_COLORS
, ppvBits
, NULL
, 0);
288 ReleaseDC(NULL
, hdcUsed
);
291 return !*phBmp
? E_OUTOFMEMORY
: S_OK
;
294 void BtrfsContextMenu::get_uac_icon() {
295 IWICImagingFactory
* factory
= NULL
;
300 hr
= CoCreateInstance(CLSID_WICImagingFactory
, NULL
, CLSCTX_INPROC_SERVER
, IID_IWICImagingFactory
, (void **)&factory
);
302 hr
= CoCreateInstance(CLSID_WICImagingFactory
, NULL
, CLSCTX_INPROC_SERVER
, IID_PPV_ARGS(&factory
));
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
);
312 hr
= factory
->CreateBitmapFromHICON((HICON
)icon
, &bitmap
);
316 hr
= bitmap
->GetSize(&cx
, &cy
);
324 hr
= Create32BitHBITMAP(NULL
, &sz
, (void**)&buf
, &uacicon
);
326 UINT stride
= cx
* sizeof(DWORD
);
327 UINT buflen
= cy
* stride
;
328 bitmap
->CopyPixels(NULL
, stride
, buflen
, buf
);
339 HRESULT __stdcall
BtrfsContextMenu::QueryContextMenu(HMENU hmenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
) {
346 if (uFlags
& CMF_DEFAULTONLY
)
350 if (allow_snapshot
) {
351 if (LoadStringW(module
, IDS_CREATE_SNAPSHOT
, str
, sizeof(str
) / sizeof(WCHAR
)) == 0)
354 if (!InsertMenuW(hmenu
, indexMenu
, MF_BYPOSITION
, idCmdFirst
, str
))
360 if (idCmdFirst
+ entries
<= idCmdLast
) {
363 if (LoadStringW(module
, IDS_SEND_SUBVOL
, str
, sizeof(str
) / sizeof(WCHAR
)) == 0)
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
;
376 if (!InsertMenuItemW(hmenu
, indexMenu
+ entries
, TRUE
, &mii
))
382 if (LoadStringW(module
, IDS_NEW_SUBVOL
, str
, sizeof(str
) / sizeof(WCHAR
)) == 0)
385 if (!InsertMenuW(hmenu
, indexMenu
, MF_BYPOSITION
, idCmdFirst
, str
))
390 if (idCmdFirst
+ 1 <= idCmdLast
) {
393 if (LoadStringW(module
, IDS_RECV_SUBVOL
, str
, sizeof(str
) / sizeof(WCHAR
)) == 0)
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
;
406 if (!InsertMenuItemW(hmenu
, indexMenu
+ 1, TRUE
, &mii
))
412 if (idCmdFirst
+ 2 <= idCmdLast
&& show_reflink_paste(path
)) {
413 if (LoadStringW(module
, IDS_REFLINK_PASTE
, str
, sizeof(str
) / sizeof(WCHAR
)) == 0)
416 if (!InsertMenuW(hmenu
, indexMenu
+ 2, MF_BYPOSITION
, idCmdFirst
+ 2, str
))
423 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, entries
);
426 static void create_snapshot(HWND hwnd
, WCHAR
* fn
) {
429 IO_STATUS_BLOCK iosb
;
430 btrfs_get_file_ids bgfi
;
432 h
= CreateFileW(fn
, FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
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
));
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
];
440 btrfs_create_snapshot
* bcs
;
441 ULONG namelen
, pathend
;
442 WIN32_FIND_DATAW wfd
;
445 StringCchCopyW(parpath
, sizeof(parpath
) / sizeof(WCHAR
), fn
);
446 PathRemoveFileSpecW(parpath
);
448 StringCchCopyW(subvolname
, sizeof(subvolname
) / sizeof(WCHAR
), fn
);
449 PathStripPathW(subvolname
);
451 h2
= CreateFileW(parpath
, FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
453 if (h2
== INVALID_HANDLE_VALUE
) {
454 ShowError(hwnd
, GetLastError());
459 if (!LoadStringW(module
, IDS_SNAPSHOT_FILENAME
, templ
, MAX_PATH
)) {
460 ShowError(hwnd
, GetLastError());
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
);
475 StringCchCopyW(searchpath
, sizeof(searchpath
) / sizeof(WCHAR
), parpath
);
476 StringCchCatW(searchpath
, sizeof(searchpath
) / sizeof(WCHAR
), L
"\\");
477 pathend
= wcslen(searchpath
);
479 StringCchCatW(searchpath
, sizeof(searchpath
) / sizeof(WCHAR
), name
);
481 fff
= FindFirstFileW(searchpath
, &wfd
);
483 if (fff
!= INVALID_HANDLE_VALUE
) {
484 ULONG i
= wcslen(searchpath
), num
= 2;
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
);
497 fff
= FindFirstFileW(searchpath
, &wfd
);
499 } while (fff
!= INVALID_HANDLE_VALUE
);
502 namelen
= wcslen(&searchpath
[pathend
]) * sizeof(WCHAR
);
504 bcs
= (btrfs_create_snapshot
*)malloc(sizeof(btrfs_create_snapshot
) - 1 + namelen
);
505 bcs
->readonly
= FALSE
;
508 bcs
->namelen
= namelen
;
509 memcpy(bcs
->name
, &searchpath
[pathend
], namelen
);
511 Status
= NtFsControlFile(h2
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SNAPSHOT
, bcs
, sizeof(btrfs_create_snapshot
) - 1 + namelen
, NULL
, 0);
513 if (!NT_SUCCESS(Status
))
514 ShowNtStatusError(hwnd
, Status
);
521 ShowError(hwnd
, GetLastError());
524 static UINT64 __inline
sector_align(UINT64 n
, UINT64 a
) {
526 n
= (n
+ a
) & ~(a
- 1);
531 BOOL
BtrfsContextMenu::reflink_copy(HWND hwnd
, const WCHAR
* fn
, const WCHAR
* dir
) {
533 WCHAR
* name
, volpath1
[255], volpath2
[255];
534 std::wstring dirw
, newpath
;
537 FILETIME atime
, mtime
;
538 btrfs_inode_info bii
;
539 btrfs_set_inode_info bsii
;
542 IO_STATUS_BLOCK iosb
;
543 btrfs_set_xattr bsxa
;
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
548 name
= PathFindFileNameW(fn
);
552 if (dir
[0] != 0 && dir
[wcslen(dir
) - 1] != '\\')
558 if (!get_volume_path_parent(fn
, volpath1
, sizeof(volpath1
) / sizeof(WCHAR
))) {
559 ShowError(hwnd
, GetLastError());
563 if (!GetVolumePathNameW(dir
, volpath2
, sizeof(volpath2
) / sizeof(WCHAR
))) {
564 ShowError(hwnd
, GetLastError());
568 if (wcscmp(volpath1
, volpath2
)) // different filesystems
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());
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
);
584 // if subvol, do snapshot instead
585 if (bii
.inode
== SUBVOL_ROOT_INODE
) {
586 btrfs_create_snapshot
* bcs
;
588 std::wstring destname
, search
;
589 WIN32_FIND_DATAW wfd
;
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());
603 fff
= FindFirstFileW(search
.c_str(), &wfd
);
605 if (fff
!= INVALID_HANDLE_VALUE
) {
607 std::wstringstream ss
;
617 search
= dirw
+ destname
;
619 fff
= FindFirstFileW(search
.c_str(), &wfd
);
621 } while (fff
!= INVALID_HANDLE_VALUE
);
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
));
629 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SNAPSHOT
, bcs
, sizeof(btrfs_create_snapshot
) - sizeof(WCHAR
) + bcs
->namelen
, NULL
, 0);
633 if (!NT_SUCCESS(Status
)) {
634 ShowNtStatusError(hwnd
, Status
);
645 if (!GetFileInformationByHandleEx(source
, FileBasicInfo
, &fbi
, sizeof(FILE_BASIC_INFO
))) {
646 ShowError(hwnd
, GetLastError());
651 if (bii
.type
== BTRFS_TYPE_CHARDEV
|| bii
.type
== BTRFS_TYPE_BLOCKDEV
|| bii
.type
== BTRFS_TYPE_FIFO
|| bii
.type
== BTRFS_TYPE_SOCKET
) {
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());
663 bmnsize
= offsetof(btrfs_mknod
, name
[0]) + (wcslen(name
) * sizeof(WCHAR
));
664 bmn
= (btrfs_mknod
*)malloc(bmnsize
);
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
);
672 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_MKNOD
, bmn
, bmnsize
, NULL
, 0);
673 if (!NT_SUCCESS(Status
)) {
674 ShowNtStatusError(hwnd
, Status
);
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
);
690 dest
= INVALID_HANDLE_VALUE
;
692 dest
= CreateFileW(newpath
.c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, source
);
694 if (dest
== INVALID_HANDLE_VALUE
) {
697 if (GetLastError() != ERROR_FILE_EXISTS
&& GetLastError() != ERROR_ALREADY_EXISTS
&& wcscmp(fn
, newpath
.c_str())) {
698 ShowError(hwnd
, GetLastError());
705 std::wstringstream ss
;
707 ext
= PathFindExtensionW(fn
);
717 std::wstring namew
= name
;
719 ss
<< namew
.substr(0, ext
- name
);
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
);
731 dest
= INVALID_HANDLE_VALUE
;
733 dest
= CreateFileW(newpath
.c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, source
);
735 if (dest
== INVALID_HANDLE_VALUE
) {
736 if (GetLastError() != ERROR_FILE_EXISTS
&& GetLastError() != ERROR_ALREADY_EXISTS
) {
737 ShowError(hwnd
, GetLastError());
748 memset(&bsii
, 0, sizeof(btrfs_set_inode_info
));
750 bsii
.flags_changed
= TRUE
;
751 bsii
.flags
= bii
.flags
;
753 if (bii
.flags
& BTRFS_INODE_COMPRESS
) {
754 bsii
.compression_type_changed
= TRUE
;
755 bsii
.compression_type
= bii
.compression_type
;
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
);
764 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
765 if (!(fbi
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
767 WIN32_FIND_DATAW fff
;
773 h
= FindFirstFileW(qs
.c_str(), &fff
);
774 if (h
!= INVALID_HANDLE_VALUE
) {
778 if (fff
.cFileName
[0] == '.' && (fff
.cFileName
[1] == 0 || (fff
.cFileName
[1] == '.' && fff
.cFileName
[2] == 0)))
783 fn2
+= fff
.cFileName
;
785 if (!reflink_copy(hwnd
, fn2
.c_str(), newpath
.c_str()))
787 } while (FindNextFileW(h
, &fff
));
793 // CreateDirectoryExW also copies streams, no need to do it here
796 WIN32_FIND_STREAM_DATA fsd
;
798 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) {
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());
810 rplen
= sizeof(reparse_header
) + rh
.ReparseDataLength
;
811 rp
= (UINT8
*)malloc(rplen
);
813 if (!DeviceIoControl(source
, FSCTL_GET_REPARSE_POINT
, NULL
, 0, rp
, rplen
, &bytesret
, NULL
)) {
814 ShowError(hwnd
, GetLastError());
818 if (!DeviceIoControl(dest
, FSCTL_SET_REPARSE_POINT
, rp
, rplen
, NULL
, 0, &bytesret
, NULL
)) {
819 ShowError(hwnd
, GetLastError());
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
;
833 if (!GetFileInformationByHandleEx(source
, FileStandardInfo
, &fsi
, sizeof(FILE_STANDARD_INFO
))) {
834 ShowError(hwnd
, GetLastError());
838 if (!DeviceIoControl(source
, FSCTL_GET_INTEGRITY_INFORMATION
, NULL
, 0, &fgiib
, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
), &bytesret
, NULL
)) {
839 ShowError(hwnd
, GetLastError());
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());
850 fsiib
.ChecksumAlgorithm
= fgiib
.ChecksumAlgorithm
;
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());
858 feofi
.EndOfFile
= fsi
.EndOfFile
;
859 if (!SetFileInformationByHandle(dest
, FileEndOfFileInfo
, &feofi
, sizeof(FILE_END_OF_FILE_INFO
))){
860 ShowError(hwnd
, GetLastError());
864 ded
.FileHandle
= source
;
865 maxdup
= 0xffffffff - fgiib
.ClusterSizeInBytes
+ 1;
867 alloc_size
= sector_align(fsi
.EndOfFile
.QuadPart
, fgiib
.ClusterSizeInBytes
);
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());
878 offset
+= ded
.ByteCount
.QuadPart
;
882 h
= FindFirstStreamW(fn
, FindStreamInfoStandard
, &fsd
, 0);
883 if (h
!= INVALID_HANDLE_VALUE
) {
887 sn
= fsd
.cStreamName
;
889 if (sn
!= L
"::$DATA" && sn
.length() > 6 && sn
.substr(sn
.length() - 6, 6) == L
":$DATA") {
893 if (fsd
.StreamSize
.QuadPart
> 0) {
899 stream
= CreateFileW(fn2
.c_str(), GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
901 if (stream
== INVALID_HANDLE_VALUE
) {
902 ShowError(hwnd
, GetLastError());
906 // We can get away with this because our streams are guaranteed to be below 64 KB -
907 // don't do this on NTFS!
908 data
= (UINT8
*)malloc(fsd
.StreamSize
.QuadPart
);
910 if (!ReadFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
911 ShowError(hwnd
, GetLastError());
920 stream
= CreateFileW((newpath
+ sn
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
922 if (stream
== INVALID_HANDLE_VALUE
) {
923 ShowError(hwnd
, GetLastError());
925 if (data
) free(data
);
931 if (!WriteFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
932 ShowError(hwnd
, GetLastError());
943 } while (FindNextStreamW(h
, &fsd
));
949 atime
.dwLowDateTime
= fbi
.LastAccessTime
.LowPart
;
950 atime
.dwHighDateTime
= fbi
.LastAccessTime
.HighPart
;
951 mtime
.dwLowDateTime
= fbi
.LastWriteTime
.LowPart
;
952 mtime
.dwHighDateTime
= fbi
.LastWriteTime
.HighPart
;
953 SetFileTime(dest
, NULL
, &atime
, &mtime
);
955 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, &bsxa
, sizeof(btrfs_set_xattr
));
957 if (Status
== STATUS_BUFFER_OVERFLOW
|| (NT_SUCCESS(Status
) && bsxa
.valuelen
> 0)) {
959 btrfs_set_xattr
*xa
= NULL
, *xa2
;
965 xa
= (btrfs_set_xattr
*)malloc(xalen
);
967 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, xa
, xalen
);
968 } while (Status
== STATUS_BUFFER_OVERFLOW
);
970 if (!NT_SUCCESS(Status
)) {
972 ShowNtStatusError(hwnd
, Status
);
977 while (xa2
->valuelen
> 0) {
978 Status
= NtFsControlFile(dest
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_SET_XATTR
, xa2
,
979 offsetof(btrfs_set_xattr
, data
[0]) + xa2
->namelen
+ xa2
->valuelen
, NULL
, 0);
980 if (!NT_SUCCESS(Status
)) {
982 ShowNtStatusError(hwnd
, Status
);
985 xa2
= (btrfs_set_xattr
*)&xa2
->data
[xa2
->namelen
+ xa2
->valuelen
];
989 } else if (!NT_SUCCESS(Status
)) {
990 ShowNtStatusError(hwnd
, Status
);
998 FILE_DISPOSITION_INFO fdi
;
1000 fdi
.DeleteFile
= TRUE
;
1001 if (!SetFileInformationByHandle(dest
, FileDispositionInfo
, &fdi
, sizeof(FILE_DISPOSITION_INFO
)))
1002 ShowError(hwnd
, GetLastError());
1006 CloseHandle(source
);
1011 HRESULT __stdcall
BtrfsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO picia
) {
1012 LPCMINVOKECOMMANDINFOEX pici
= (LPCMINVOKECOMMANDINFOEX
)picia
;
1015 return E_INVALIDARG
;
1018 if ((IS_INTRESOURCE(pici
->lpVerb
) && allow_snapshot
&& pici
->lpVerb
== 0) || (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, SNAPSHOT_VERBA
))) {
1025 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
1030 for (i
= 0; i
< num_files
; i
++) {
1031 if (DragQueryFileW((HDROP
)stgm
.hGlobal
, i
, fn
, sizeof(fn
) / sizeof(WCHAR
))) {
1032 create_snapshot(pici
->hwnd
, fn
);
1037 } else if ((IS_INTRESOURCE(pici
->lpVerb
) && ((allow_snapshot
&& (ULONG_PTR
)pici
->lpVerb
== 1) || (!allow_snapshot
&& (ULONG_PTR
)pici
->lpVerb
== 0))) ||
1038 (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, SEND_VERBA
))) {
1040 WCHAR dll
[MAX_PATH
], fn
[MAX_PATH
];
1042 SHELLEXECUTEINFOW sei
;
1044 GetModuleFileNameW(module
, dll
, sizeof(dll
) / sizeof(WCHAR
));
1049 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
1054 for (i
= 0; i
< num_files
; i
++) {
1055 if (DragQueryFileW((HDROP
)stgm
.hGlobal
, i
, fn
, sizeof(fn
) / sizeof(WCHAR
))) {
1058 t
+= L
"\",SendSubvolGUI ";
1061 RtlZeroMemory(&sei
, sizeof(sei
));
1063 sei
.cbSize
= sizeof(sei
);
1064 sei
.hwnd
= pici
->hwnd
;
1065 sei
.lpVerb
= L
"runas";
1066 sei
.lpFile
= L
"rundll32.exe";
1067 sei
.lpParameters
= t
.c_str();
1068 sei
.nShow
= SW_SHOW
;
1069 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
1071 if (!ShellExecuteExW(&sei
)) {
1072 ShowError(pici
->hwnd
, GetLastError());
1076 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1077 CloseHandle(sei
.hProcess
);
1084 if ((IS_INTRESOURCE(pici
->lpVerb
) && (ULONG_PTR
)pici
->lpVerb
== 0) || (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, NEW_SUBVOL_VERBA
))) {
1086 IO_STATUS_BLOCK iosb
;
1088 ULONG pathlen
, searchpathlen
, pathend
, bcslen
;
1089 WCHAR name
[MAX_PATH
], *searchpath
;
1090 btrfs_create_subvol
* bcs
;
1092 WIN32_FIND_DATAW wfd
;
1094 if (!LoadStringW(module
, IDS_NEW_SUBVOL_FILENAME
, name
, MAX_PATH
)) {
1095 ShowError(pici
->hwnd
, GetLastError());
1099 h
= CreateFileW(path
, FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
1101 if (h
== INVALID_HANDLE_VALUE
) {
1102 ShowError(pici
->hwnd
, GetLastError());
1106 pathlen
= wcslen(path
);
1108 searchpathlen
= pathlen
+ wcslen(name
) + 10;
1109 searchpath
= (WCHAR
*)malloc(searchpathlen
* sizeof(WCHAR
));
1111 StringCchCopyW(searchpath
, searchpathlen
, path
);
1112 StringCchCatW(searchpath
, searchpathlen
, L
"\\");
1113 pathend
= wcslen(searchpath
);
1115 StringCchCatW(searchpath
, searchpathlen
, name
);
1117 fff
= FindFirstFileW(searchpath
, &wfd
);
1119 if (fff
!= INVALID_HANDLE_VALUE
) {
1120 ULONG i
= wcslen(searchpath
), num
= 2;
1126 if (StringCchPrintfW(searchpath
, searchpathlen
, L
"%s (%u)", searchpath
, num
) == STRSAFE_E_INSUFFICIENT_BUFFER
) {
1127 MessageBoxW(pici
->hwnd
, L
"Filename too long.\n", L
"Error", MB_ICONERROR
);
1132 fff
= FindFirstFileW(searchpath
, &wfd
);
1134 } while (fff
!= INVALID_HANDLE_VALUE
);
1137 bcslen
= offsetof(btrfs_create_subvol
, name
[0]) + (wcslen(&searchpath
[pathend
]) * sizeof(WCHAR
));
1138 bcs
= (btrfs_create_subvol
*)malloc(bcslen
);
1140 bcs
->readonly
= FALSE
;
1142 bcs
->namelen
= wcslen(&searchpath
[pathend
]) * sizeof(WCHAR
);
1143 memcpy(bcs
->name
, &searchpath
[pathend
], bcs
->namelen
);
1145 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SUBVOL
, bcs
, bcslen
, NULL
, 0);
1150 if (!NT_SUCCESS(Status
)) {
1152 ShowNtStatusError(pici
->hwnd
, Status
);
1159 } else if ((IS_INTRESOURCE(pici
->lpVerb
) && (ULONG_PTR
)pici
->lpVerb
== 1) || (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, RECV_VERBA
))) {
1160 WCHAR dll
[MAX_PATH
];
1162 SHELLEXECUTEINFOW sei
;
1164 GetModuleFileNameW(module
, dll
, sizeof(dll
) / sizeof(WCHAR
));
1168 t
+= L
"\",RecvSubvolGUI ";
1171 RtlZeroMemory(&sei
, sizeof(sei
));
1173 sei
.cbSize
= sizeof(sei
);
1174 sei
.hwnd
= pici
->hwnd
;
1175 sei
.lpVerb
= L
"runas";
1176 sei
.lpFile
= L
"rundll32.exe";
1177 sei
.lpParameters
= t
.c_str();
1178 sei
.nShow
= SW_SHOW
;
1179 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
1181 if (!ShellExecuteExW(&sei
)) {
1182 ShowError(pici
->hwnd
, GetLastError());
1186 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1187 CloseHandle(sei
.hProcess
);
1190 } else if ((IS_INTRESOURCE(pici
->lpVerb
) && (ULONG_PTR
)pici
->lpVerb
== 2) || (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, REFLINK_VERBA
))) {
1193 if (!IsClipboardFormatAvailable(CF_HDROP
))
1196 if (!OpenClipboard(pici
->hwnd
)) {
1197 ShowError(pici
->hwnd
, GetLastError());
1201 hdrop
= (HDROP
)GetClipboardData(CF_HDROP
);
1206 lh
= GlobalLock(hdrop
);
1212 num_files
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
1214 for (i
= 0; i
< num_files
; i
++) {
1215 if (DragQueryFileW(hdrop
, i
, fn
, sizeof(fn
) / sizeof(WCHAR
))) {
1216 if (!reflink_copy(pici
->hwnd
, fn
, pici
->lpDirectoryW
)) {
1237 HRESULT __stdcall
BtrfsContextMenu::GetCommandString(UINT_PTR idCmd
, UINT uFlags
, UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
) {
1239 return E_INVALIDARG
;
1242 return E_INVALIDARG
;
1248 if (LoadStringA(module
, IDS_CREATE_SNAPSHOT_HELP_TEXT
, pszName
, cchMax
))
1254 if (LoadStringW(module
, IDS_CREATE_SNAPSHOT_HELP_TEXT
, (LPWSTR
)pszName
, cchMax
))
1264 return StringCchCopyA(pszName
, cchMax
, SNAPSHOT_VERBA
);
1267 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, SNAPSHOT_VERBW
);
1270 return E_INVALIDARG
;
1272 } else if (idCmd
== 1) {
1275 if (LoadStringA(module
, IDS_SEND_SUBVOL_HELP
, pszName
, cchMax
))
1281 if (LoadStringW(module
, IDS_SEND_SUBVOL_HELP
, (LPWSTR
)pszName
, cchMax
))
1291 return StringCchCopyA(pszName
, cchMax
, SEND_VERBA
);
1294 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, SEND_VERBW
);
1297 return E_INVALIDARG
;
1300 return E_INVALIDARG
;
1305 if (LoadStringA(module
, IDS_NEW_SUBVOL_HELP_TEXT
, pszName
, cchMax
))
1311 if (LoadStringW(module
, IDS_NEW_SUBVOL_HELP_TEXT
, (LPWSTR
)pszName
, cchMax
))
1321 return StringCchCopyA(pszName
, cchMax
, NEW_SUBVOL_VERBA
);
1324 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, NEW_SUBVOL_VERBW
);
1327 return E_INVALIDARG
;
1329 } else if (idCmd
== 1) {
1332 if (LoadStringA(module
, IDS_RECV_SUBVOL_HELP
, pszName
, cchMax
))
1338 if (LoadStringW(module
, IDS_RECV_SUBVOL_HELP
, (LPWSTR
)pszName
, cchMax
))
1348 return StringCchCopyA(pszName
, cchMax
, RECV_VERBA
);
1351 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, RECV_VERBW
);
1354 return E_INVALIDARG
;
1356 } else if (idCmd
== 2) {
1359 if (LoadStringA(module
, IDS_REFLINK_PASTE_HELP
, pszName
, cchMax
))
1365 if (LoadStringW(module
, IDS_REFLINK_PASTE_HELP
, (LPWSTR
)pszName
, cchMax
))
1375 return StringCchCopyA(pszName
, cchMax
, REFLINK_VERBA
);
1378 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, REFLINK_VERBW
);
1381 return E_INVALIDARG
;
1384 return E_INVALIDARG
;
1388 static void reflink_copy2(std::wstring srcfn
, std::wstring destdir
, std::wstring destname
) {
1389 HANDLE source
, dest
;
1391 FILE_BASIC_INFO fbi
;
1392 FILETIME atime
, mtime
;
1393 btrfs_inode_info bii
;
1394 btrfs_set_inode_info bsii
;
1397 IO_STATUS_BLOCK iosb
;
1398 btrfs_set_xattr bsxa
;
1400 source
= CreateFileW(srcfn
.c_str(), GENERIC_READ
| FILE_TRAVERSE
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_OPEN_REPARSE_POINT
, NULL
);
1401 if (source
== INVALID_HANDLE_VALUE
)
1404 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_INODE_INFO
, NULL
, 0, &bii
, sizeof(btrfs_inode_info
));
1405 if (!NT_SUCCESS(Status
)) {
1406 CloseHandle(source
);
1410 // if subvol, do snapshot instead
1411 if (bii
.inode
== SUBVOL_ROOT_INODE
) {
1413 btrfs_create_snapshot
* bcs
;
1416 dirh
= CreateFileW(destdir
.c_str(), FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
1417 if (dirh
== INVALID_HANDLE_VALUE
) {
1418 CloseHandle(source
);
1422 bcslen
= offsetof(btrfs_create_snapshot
, name
[0]) + (destname
.length() * sizeof(WCHAR
));
1423 bcs
= (btrfs_create_snapshot
*)malloc(bcslen
);
1424 bcs
->subvol
= source
;
1425 bcs
->namelen
= destname
.length() * sizeof(WCHAR
);
1426 memcpy(bcs
->name
, destname
.c_str(), destname
.length() * sizeof(WCHAR
));
1428 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SNAPSHOT
, bcs
, bcslen
, NULL
, 0);
1432 CloseHandle(source
);
1438 if (!GetFileInformationByHandleEx(source
, FileBasicInfo
, &fbi
, sizeof(FILE_BASIC_INFO
))) {
1439 CloseHandle(source
);
1443 if (bii
.type
== BTRFS_TYPE_CHARDEV
|| bii
.type
== BTRFS_TYPE_BLOCKDEV
|| bii
.type
== BTRFS_TYPE_FIFO
|| bii
.type
== BTRFS_TYPE_SOCKET
) {
1448 dirh
= CreateFileW(destdir
.c_str(), FILE_ADD_FILE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
1449 if (dirh
== INVALID_HANDLE_VALUE
) {
1450 CloseHandle(source
);
1454 bmnsize
= offsetof(btrfs_mknod
, name
[0]) + (destname
.length() * sizeof(WCHAR
));
1455 bmn
= (btrfs_mknod
*)malloc(bmnsize
);
1458 bmn
->type
= bii
.type
;
1459 bmn
->st_rdev
= bii
.st_rdev
;
1460 bmn
->namelen
= destname
.length() * sizeof(WCHAR
);
1461 memcpy(bmn
->name
, destname
.c_str(), bmn
->namelen
);
1463 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_MKNOD
, bmn
, bmnsize
, NULL
, 0);
1464 if (!NT_SUCCESS(Status
)) {
1466 CloseHandle(source
);
1474 dest
= CreateFileW((destdir
+ destname
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
1475 } else if (fbi
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1476 if (CreateDirectoryExW(srcfn
.c_str(), (destdir
+ destname
).c_str(), NULL
))
1477 dest
= CreateFileW((destdir
+ destname
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1478 NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
1480 dest
= INVALID_HANDLE_VALUE
;
1482 dest
= CreateFileW((destdir
+ destname
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, source
);
1484 if (dest
== INVALID_HANDLE_VALUE
) {
1485 CloseHandle(source
);
1489 memset(&bsii
, 0, sizeof(btrfs_set_inode_info
));
1491 bsii
.flags_changed
= TRUE
;
1492 bsii
.flags
= bii
.flags
;
1494 if (bii
.flags
& BTRFS_INODE_COMPRESS
) {
1495 bsii
.compression_type_changed
= TRUE
;
1496 bsii
.compression_type
= bii
.compression_type
;
1499 Status
= NtFsControlFile(dest
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_SET_INODE_INFO
, &bsii
, sizeof(btrfs_set_inode_info
), NULL
, 0);
1500 if (!NT_SUCCESS(Status
))
1503 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1504 if (!(fbi
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
1506 WIN32_FIND_DATAW fff
;
1512 h
= FindFirstFileW(qs
.c_str(), &fff
);
1513 if (h
!= INVALID_HANDLE_VALUE
) {
1517 if (fff
.cFileName
[0] == '.' && (fff
.cFileName
[1] == 0 || (fff
.cFileName
[1] == '.' && fff
.cFileName
[2] == 0)))
1522 fn2
+= fff
.cFileName
;
1524 reflink_copy2(fn2
, destdir
+ destname
+ L
"\\", fff
.cFileName
);
1525 } while (FindNextFileW(h
, &fff
));
1531 // CreateDirectoryExW also copies streams, no need to do it here
1534 WIN32_FIND_STREAM_DATA fsd
;
1536 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) {
1541 if (!DeviceIoControl(source
, FSCTL_GET_REPARSE_POINT
, NULL
, 0, &rh
, sizeof(reparse_header
), &bytesret
, NULL
)) {
1542 if (GetLastError() != ERROR_MORE_DATA
)
1546 rplen
= sizeof(reparse_header
) + rh
.ReparseDataLength
;
1547 rp
= (UINT8
*)malloc(rplen
);
1549 if (!DeviceIoControl(source
, FSCTL_GET_REPARSE_POINT
, NULL
, 0, rp
, rplen
, &bytesret
, NULL
))
1552 if (!DeviceIoControl(dest
, FSCTL_SET_REPARSE_POINT
, rp
, rplen
, NULL
, 0, &bytesret
, NULL
))
1557 FILE_STANDARD_INFO fsi
;
1558 FILE_END_OF_FILE_INFO feofi
;
1559 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER fgiib
;
1560 FSCTL_SET_INTEGRITY_INFORMATION_BUFFER fsiib
;
1561 DUPLICATE_EXTENTS_DATA ded
;
1562 UINT64 offset
, alloc_size
;
1565 if (!GetFileInformationByHandleEx(source
, FileStandardInfo
, &fsi
, sizeof(FILE_STANDARD_INFO
)))
1568 if (!DeviceIoControl(source
, FSCTL_GET_INTEGRITY_INFORMATION
, NULL
, 0, &fgiib
, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
), &bytesret
, NULL
))
1571 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_SPARSE_FILE
) {
1572 if (!DeviceIoControl(dest
, FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0, &bytesret
, NULL
))
1576 fsiib
.ChecksumAlgorithm
= fgiib
.ChecksumAlgorithm
;
1578 fsiib
.Flags
= fgiib
.Flags
;
1579 if (!DeviceIoControl(dest
, FSCTL_SET_INTEGRITY_INFORMATION
, &fsiib
, sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER
), NULL
, 0, &bytesret
, NULL
))
1582 feofi
.EndOfFile
= fsi
.EndOfFile
;
1583 if (!SetFileInformationByHandle(dest
, FileEndOfFileInfo
, &feofi
, sizeof(FILE_END_OF_FILE_INFO
)))
1586 ded
.FileHandle
= source
;
1587 maxdup
= 0xffffffff - fgiib
.ClusterSizeInBytes
+ 1;
1589 alloc_size
= sector_align(fsi
.EndOfFile
.QuadPart
, fgiib
.ClusterSizeInBytes
);
1592 while (offset
< alloc_size
) {
1593 ded
.SourceFileOffset
.QuadPart
= ded
.TargetFileOffset
.QuadPart
= offset
;
1594 ded
.ByteCount
.QuadPart
= maxdup
< (alloc_size
- offset
) ? maxdup
: (alloc_size
- offset
);
1595 if (!DeviceIoControl(dest
, FSCTL_DUPLICATE_EXTENTS_TO_FILE
, &ded
, sizeof(DUPLICATE_EXTENTS_DATA
), NULL
, 0, &bytesret
, NULL
))
1598 offset
+= ded
.ByteCount
.QuadPart
;
1602 h
= FindFirstStreamW(srcfn
.c_str(), FindStreamInfoStandard
, &fsd
, 0);
1603 if (h
!= INVALID_HANDLE_VALUE
) {
1607 sn
= fsd
.cStreamName
;
1609 if (sn
!= L
"::$DATA" && sn
.length() > 6 && sn
.substr(sn
.length() - 6, 6) == L
":$DATA") {
1613 if (fsd
.StreamSize
.QuadPart
> 0) {
1619 stream
= CreateFileW(fn2
.c_str(), GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
1621 if (stream
== INVALID_HANDLE_VALUE
)
1624 // We can get away with this because our streams are guaranteed to be below 64 KB -
1625 // don't do this on NTFS!
1626 data
= (UINT8
*)malloc(fsd
.StreamSize
.QuadPart
);
1628 if (!ReadFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
1630 CloseHandle(stream
);
1634 CloseHandle(stream
);
1637 stream
= CreateFileW((destdir
+ destname
+ sn
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
1639 if (stream
== INVALID_HANDLE_VALUE
) {
1640 if (data
) free(data
);
1645 if (!WriteFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
1647 CloseHandle(stream
);
1654 CloseHandle(stream
);
1656 } while (FindNextStreamW(h
, &fsd
));
1662 atime
.dwLowDateTime
= fbi
.LastAccessTime
.LowPart
;
1663 atime
.dwHighDateTime
= fbi
.LastAccessTime
.HighPart
;
1664 mtime
.dwLowDateTime
= fbi
.LastWriteTime
.LowPart
;
1665 mtime
.dwHighDateTime
= fbi
.LastWriteTime
.HighPart
;
1666 SetFileTime(dest
, NULL
, &atime
, &mtime
);
1668 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, &bsxa
, sizeof(btrfs_set_xattr
));
1670 if (Status
== STATUS_BUFFER_OVERFLOW
|| (NT_SUCCESS(Status
) && bsxa
.valuelen
> 0)) {
1672 btrfs_set_xattr
*xa
= NULL
, *xa2
;
1678 xa
= (btrfs_set_xattr
*)malloc(xalen
);
1680 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, xa
, xalen
);
1681 } while (Status
== STATUS_BUFFER_OVERFLOW
);
1683 if (!NT_SUCCESS(Status
)) {
1689 while (xa2
->valuelen
> 0) {
1690 Status
= NtFsControlFile(dest
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_SET_XATTR
, xa2
,
1691 offsetof(btrfs_set_xattr
, data
[0]) + xa2
->namelen
+ xa2
->valuelen
, NULL
, 0);
1692 if (!NT_SUCCESS(Status
)) {
1696 xa2
= (btrfs_set_xattr
*)&xa2
->data
[xa2
->namelen
+ xa2
->valuelen
];
1700 } else if (!NT_SUCCESS(Status
))
1707 FILE_DISPOSITION_INFO fdi
;
1709 fdi
.DeleteFile
= TRUE
;
1710 SetFileInformationByHandle(dest
, FileDispositionInfo
, &fdi
, sizeof(FILE_DISPOSITION_INFO
));
1714 CloseHandle(source
);
1717 void CALLBACK
ReflinkCopyW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1721 args
= CommandLineToArgvW(lpszCmdLine
, &num_args
);
1726 if (num_args
>= 2) {
1728 BOOL dest_is_dir
= FALSE
;
1729 std::wstring dest
= args
[num_args
- 1], destdir
, destname
;
1730 WCHAR volpath2
[MAX_PATH
];
1733 destdirh
= CreateFileW(dest
.c_str(), FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
1734 if (destdirh
!= INVALID_HANDLE_VALUE
) {
1735 BY_HANDLE_FILE_INFORMATION bhfi
;
1737 if (GetFileInformationByHandle(destdirh
, &bhfi
) && bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1741 if (destdir
.substr(destdir
.length() - 1, 1) != L
"\\")
1744 CloseHandle(destdirh
);
1748 size_t found
= dest
.rfind(L
"\\");
1750 if (found
== std::wstring::npos
) {
1754 destdir
= dest
.substr(0, found
);
1755 destname
= dest
.substr(found
+ 1);
1759 if (!GetVolumePathNameW(dest
.c_str(), volpath2
, sizeof(volpath2
) / sizeof(WCHAR
)))
1762 for (i
= 0; i
< num_args
- 1; i
++) {
1763 WIN32_FIND_DATAW ffd
;
1766 h
= FindFirstFileW(args
[i
], &ffd
);
1767 if (h
!= INVALID_HANDLE_VALUE
) {
1768 WCHAR volpath1
[MAX_PATH
];
1769 std::wstring path
= args
[i
];
1770 size_t found
= path
.rfind(L
"\\");
1772 if (found
== std::wstring::npos
)
1775 path
= path
.substr(0, found
);
1779 if (get_volume_path_parent(path
.c_str(), volpath1
, sizeof(volpath1
) / sizeof(WCHAR
))) {
1780 if (!wcscmp(volpath1
, volpath2
)) {
1782 reflink_copy2(path
+ ffd
.cFileName
, destdir
, dest_is_dir
? ffd
.cFileName
: destname
);
1783 } while (FindNextFileW(h
, &ffd
));