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());
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
);
911 if (!ReadFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
912 ShowError(hwnd
, GetLastError());
922 stream
= CreateFileW((newpath
+ sn
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
924 if (stream
== INVALID_HANDLE_VALUE
) {
925 ShowError(hwnd
, GetLastError());
928 if (data
) free(data
);
934 if (!WriteFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
935 ShowError(hwnd
, GetLastError());
947 } while (FindNextStreamW(h
, &fsd
));
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
);
959 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, &bsxa
, sizeof(btrfs_set_xattr
));
961 if (Status
== STATUS_BUFFER_OVERFLOW
|| (NT_SUCCESS(Status
) && bsxa
.valuelen
> 0)) {
963 btrfs_set_xattr
*xa
= NULL
, *xa2
;
969 xa
= (btrfs_set_xattr
*)malloc(xalen
);
971 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, xa
, xalen
);
972 } while (Status
== STATUS_BUFFER_OVERFLOW
);
974 if (!NT_SUCCESS(Status
)) {
976 ShowNtStatusError(hwnd
, Status
);
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
)) {
986 ShowNtStatusError(hwnd
, Status
);
989 xa2
= (btrfs_set_xattr
*)&xa2
->data
[xa2
->namelen
+ xa2
->valuelen
];
993 } else if (!NT_SUCCESS(Status
)) {
994 ShowNtStatusError(hwnd
, Status
);
1002 FILE_DISPOSITION_INFO fdi
;
1004 fdi
.DeleteFile
= TRUE
;
1005 if (!SetFileInformationByHandle(dest
, FileDispositionInfo
, &fdi
, sizeof(FILE_DISPOSITION_INFO
)))
1006 ShowError(hwnd
, GetLastError());
1010 CloseHandle(source
);
1015 HRESULT __stdcall
BtrfsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO picia
) {
1016 LPCMINVOKECOMMANDINFOEX pici
= (LPCMINVOKECOMMANDINFOEX
)picia
;
1019 return E_INVALIDARG
;
1022 if ((IS_INTRESOURCE(pici
->lpVerb
) && allow_snapshot
&& pici
->lpVerb
== 0) || (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, SNAPSHOT_VERBA
))) {
1029 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
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
);
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
))) {
1044 WCHAR dll
[MAX_PATH
], fn
[MAX_PATH
];
1046 SHELLEXECUTEINFOW sei
;
1048 GetModuleFileNameW(module
, dll
, sizeof(dll
) / sizeof(WCHAR
));
1053 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
1058 for (i
= 0; i
< num_files
; i
++) {
1059 if (DragQueryFileW((HDROP
)stgm
.hGlobal
, i
, fn
, sizeof(fn
) / sizeof(WCHAR
))) {
1062 t
+= L
"\",SendSubvolGUI ";
1065 RtlZeroMemory(&sei
, sizeof(sei
));
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
;
1075 if (!ShellExecuteExW(&sei
)) {
1076 ShowError(pici
->hwnd
, GetLastError());
1080 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1081 CloseHandle(sei
.hProcess
);
1088 if ((IS_INTRESOURCE(pici
->lpVerb
) && (ULONG_PTR
)pici
->lpVerb
== 0) || (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, NEW_SUBVOL_VERBA
))) {
1090 IO_STATUS_BLOCK iosb
;
1092 ULONG pathlen
, searchpathlen
, pathend
, bcslen
;
1093 WCHAR name
[MAX_PATH
], *searchpath
;
1094 btrfs_create_subvol
* bcs
;
1096 WIN32_FIND_DATAW wfd
;
1098 if (!LoadStringW(module
, IDS_NEW_SUBVOL_FILENAME
, name
, MAX_PATH
)) {
1099 ShowError(pici
->hwnd
, GetLastError());
1103 h
= CreateFileW(path
, FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
1105 if (h
== INVALID_HANDLE_VALUE
) {
1106 ShowError(pici
->hwnd
, GetLastError());
1110 pathlen
= wcslen(path
);
1112 searchpathlen
= pathlen
+ wcslen(name
) + 10;
1113 searchpath
= (WCHAR
*)malloc(searchpathlen
* sizeof(WCHAR
));
1115 StringCchCopyW(searchpath
, searchpathlen
, path
);
1116 StringCchCatW(searchpath
, searchpathlen
, L
"\\");
1117 pathend
= wcslen(searchpath
);
1119 StringCchCatW(searchpath
, searchpathlen
, name
);
1121 fff
= FindFirstFileW(searchpath
, &wfd
);
1123 if (fff
!= INVALID_HANDLE_VALUE
) {
1124 ULONG i
= wcslen(searchpath
), num
= 2;
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
);
1136 fff
= FindFirstFileW(searchpath
, &wfd
);
1138 } while (fff
!= INVALID_HANDLE_VALUE
);
1141 bcslen
= offsetof(btrfs_create_subvol
, name
[0]) + (wcslen(&searchpath
[pathend
]) * sizeof(WCHAR
));
1142 bcs
= (btrfs_create_subvol
*)malloc(bcslen
);
1144 bcs
->readonly
= FALSE
;
1146 bcs
->namelen
= wcslen(&searchpath
[pathend
]) * sizeof(WCHAR
);
1147 memcpy(bcs
->name
, &searchpath
[pathend
], bcs
->namelen
);
1149 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SUBVOL
, bcs
, bcslen
, NULL
, 0);
1154 if (!NT_SUCCESS(Status
)) {
1156 ShowNtStatusError(pici
->hwnd
, Status
);
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
];
1166 SHELLEXECUTEINFOW sei
;
1168 GetModuleFileNameW(module
, dll
, sizeof(dll
) / sizeof(WCHAR
));
1172 t
+= L
"\",RecvSubvolGUI ";
1175 RtlZeroMemory(&sei
, sizeof(sei
));
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
;
1185 if (!ShellExecuteExW(&sei
)) {
1186 ShowError(pici
->hwnd
, GetLastError());
1190 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1191 CloseHandle(sei
.hProcess
);
1194 } else if ((IS_INTRESOURCE(pici
->lpVerb
) && (ULONG_PTR
)pici
->lpVerb
== 2) || (!IS_INTRESOURCE(pici
->lpVerb
) && !strcmp(pici
->lpVerb
, REFLINK_VERBA
))) {
1197 if (!IsClipboardFormatAvailable(CF_HDROP
))
1200 if (!OpenClipboard(pici
->hwnd
)) {
1201 ShowError(pici
->hwnd
, GetLastError());
1205 hdrop
= (HDROP
)GetClipboardData(CF_HDROP
);
1210 lh
= GlobalLock(hdrop
);
1216 num_files
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
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
)) {
1241 HRESULT __stdcall
BtrfsContextMenu::GetCommandString(UINT_PTR idCmd
, UINT uFlags
, UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
) {
1243 return E_INVALIDARG
;
1246 return E_INVALIDARG
;
1252 if (LoadStringA(module
, IDS_CREATE_SNAPSHOT_HELP_TEXT
, pszName
, cchMax
))
1258 if (LoadStringW(module
, IDS_CREATE_SNAPSHOT_HELP_TEXT
, (LPWSTR
)pszName
, cchMax
))
1268 return StringCchCopyA(pszName
, cchMax
, SNAPSHOT_VERBA
);
1271 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, SNAPSHOT_VERBW
);
1274 return E_INVALIDARG
;
1276 } else if (idCmd
== 1) {
1279 if (LoadStringA(module
, IDS_SEND_SUBVOL_HELP
, pszName
, cchMax
))
1285 if (LoadStringW(module
, IDS_SEND_SUBVOL_HELP
, (LPWSTR
)pszName
, cchMax
))
1295 return StringCchCopyA(pszName
, cchMax
, SEND_VERBA
);
1298 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, SEND_VERBW
);
1301 return E_INVALIDARG
;
1304 return E_INVALIDARG
;
1309 if (LoadStringA(module
, IDS_NEW_SUBVOL_HELP_TEXT
, pszName
, cchMax
))
1315 if (LoadStringW(module
, IDS_NEW_SUBVOL_HELP_TEXT
, (LPWSTR
)pszName
, cchMax
))
1325 return StringCchCopyA(pszName
, cchMax
, NEW_SUBVOL_VERBA
);
1328 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, NEW_SUBVOL_VERBW
);
1331 return E_INVALIDARG
;
1333 } else if (idCmd
== 1) {
1336 if (LoadStringA(module
, IDS_RECV_SUBVOL_HELP
, pszName
, cchMax
))
1342 if (LoadStringW(module
, IDS_RECV_SUBVOL_HELP
, (LPWSTR
)pszName
, cchMax
))
1352 return StringCchCopyA(pszName
, cchMax
, RECV_VERBA
);
1355 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, RECV_VERBW
);
1358 return E_INVALIDARG
;
1360 } else if (idCmd
== 2) {
1363 if (LoadStringA(module
, IDS_REFLINK_PASTE_HELP
, pszName
, cchMax
))
1369 if (LoadStringW(module
, IDS_REFLINK_PASTE_HELP
, (LPWSTR
)pszName
, cchMax
))
1379 return StringCchCopyA(pszName
, cchMax
, REFLINK_VERBA
);
1382 return StringCchCopyW((STRSAFE_LPWSTR
)pszName
, cchMax
, REFLINK_VERBW
);
1385 return E_INVALIDARG
;
1388 return E_INVALIDARG
;
1392 static void reflink_copy2(std::wstring srcfn
, std::wstring destdir
, std::wstring destname
) {
1393 HANDLE source
, dest
;
1395 FILE_BASIC_INFO fbi
;
1396 FILETIME atime
, mtime
;
1397 btrfs_inode_info bii
;
1398 btrfs_set_inode_info bsii
;
1401 IO_STATUS_BLOCK iosb
;
1402 btrfs_set_xattr bsxa
;
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
)
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
);
1414 // if subvol, do snapshot instead
1415 if (bii
.inode
== SUBVOL_ROOT_INODE
) {
1417 btrfs_create_snapshot
* bcs
;
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
);
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
));
1432 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SNAPSHOT
, bcs
, bcslen
, NULL
, 0);
1436 CloseHandle(source
);
1442 if (!GetFileInformationByHandleEx(source
, FileBasicInfo
, &fbi
, sizeof(FILE_BASIC_INFO
))) {
1443 CloseHandle(source
);
1447 if (bii
.type
== BTRFS_TYPE_CHARDEV
|| bii
.type
== BTRFS_TYPE_BLOCKDEV
|| bii
.type
== BTRFS_TYPE_FIFO
|| bii
.type
== BTRFS_TYPE_SOCKET
) {
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
);
1458 bmnsize
= offsetof(btrfs_mknod
, name
[0]) + (destname
.length() * sizeof(WCHAR
));
1459 bmn
= (btrfs_mknod
*)malloc(bmnsize
);
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
);
1467 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_MKNOD
, bmn
, bmnsize
, NULL
, 0);
1468 if (!NT_SUCCESS(Status
)) {
1470 CloseHandle(source
);
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
);
1484 dest
= INVALID_HANDLE_VALUE
;
1486 dest
= CreateFileW((destdir
+ destname
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, source
);
1488 if (dest
== INVALID_HANDLE_VALUE
) {
1489 CloseHandle(source
);
1493 memset(&bsii
, 0, sizeof(btrfs_set_inode_info
));
1495 bsii
.flags_changed
= TRUE
;
1496 bsii
.flags
= bii
.flags
;
1498 if (bii
.flags
& BTRFS_INODE_COMPRESS
) {
1499 bsii
.compression_type_changed
= TRUE
;
1500 bsii
.compression_type
= bii
.compression_type
;
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
))
1507 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1508 if (!(fbi
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
1510 WIN32_FIND_DATAW fff
;
1516 h
= FindFirstFileW(qs
.c_str(), &fff
);
1517 if (h
!= INVALID_HANDLE_VALUE
) {
1521 if (fff
.cFileName
[0] == '.' && (fff
.cFileName
[1] == 0 || (fff
.cFileName
[1] == '.' && fff
.cFileName
[2] == 0)))
1526 fn2
+= fff
.cFileName
;
1528 reflink_copy2(fn2
, destdir
+ destname
+ L
"\\", fff
.cFileName
);
1529 } while (FindNextFileW(h
, &fff
));
1535 // CreateDirectoryExW also copies streams, no need to do it here
1538 WIN32_FIND_STREAM_DATA fsd
;
1540 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) {
1545 if (!DeviceIoControl(source
, FSCTL_GET_REPARSE_POINT
, NULL
, 0, &rh
, sizeof(reparse_header
), &bytesret
, NULL
)) {
1546 if (GetLastError() != ERROR_MORE_DATA
)
1550 rplen
= sizeof(reparse_header
) + rh
.ReparseDataLength
;
1551 rp
= (UINT8
*)malloc(rplen
);
1553 if (!DeviceIoControl(source
, FSCTL_GET_REPARSE_POINT
, NULL
, 0, rp
, rplen
, &bytesret
, NULL
))
1556 if (!DeviceIoControl(dest
, FSCTL_SET_REPARSE_POINT
, rp
, rplen
, NULL
, 0, &bytesret
, NULL
))
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
;
1569 if (!GetFileInformationByHandleEx(source
, FileStandardInfo
, &fsi
, sizeof(FILE_STANDARD_INFO
)))
1572 if (!DeviceIoControl(source
, FSCTL_GET_INTEGRITY_INFORMATION
, NULL
, 0, &fgiib
, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
), &bytesret
, NULL
))
1575 if (fbi
.FileAttributes
& FILE_ATTRIBUTE_SPARSE_FILE
) {
1576 if (!DeviceIoControl(dest
, FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0, &bytesret
, NULL
))
1580 fsiib
.ChecksumAlgorithm
= fgiib
.ChecksumAlgorithm
;
1582 fsiib
.Flags
= fgiib
.Flags
;
1583 if (!DeviceIoControl(dest
, FSCTL_SET_INTEGRITY_INFORMATION
, &fsiib
, sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER
), NULL
, 0, &bytesret
, NULL
))
1586 feofi
.EndOfFile
= fsi
.EndOfFile
;
1587 if (!SetFileInformationByHandle(dest
, FileEndOfFileInfo
, &feofi
, sizeof(FILE_END_OF_FILE_INFO
)))
1590 ded
.FileHandle
= source
;
1591 maxdup
= 0xffffffff - fgiib
.ClusterSizeInBytes
+ 1;
1593 alloc_size
= sector_align(fsi
.EndOfFile
.QuadPart
, fgiib
.ClusterSizeInBytes
);
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
))
1602 offset
+= ded
.ByteCount
.QuadPart
;
1606 h
= FindFirstStreamW(srcfn
.c_str(), FindStreamInfoStandard
, &fsd
, 0);
1607 if (h
!= INVALID_HANDLE_VALUE
) {
1611 sn
= fsd
.cStreamName
;
1613 if (sn
!= L
"::$DATA" && sn
.length() > 6 && sn
.substr(sn
.length() - 6, 6) == L
":$DATA") {
1617 if (fsd
.StreamSize
.QuadPart
> 0) {
1623 stream
= CreateFileW(fn2
.c_str(), GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
1625 if (stream
== INVALID_HANDLE_VALUE
)
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
);
1632 if (!ReadFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
1634 CloseHandle(stream
);
1638 CloseHandle(stream
);
1641 stream
= CreateFileW((destdir
+ destname
+ sn
).c_str(), GENERIC_READ
| GENERIC_WRITE
| DELETE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
1643 if (stream
== INVALID_HANDLE_VALUE
) {
1644 if (data
) free(data
);
1649 if (!WriteFile(stream
, data
, fsd
.StreamSize
.QuadPart
, &bytesret
, NULL
)) {
1651 CloseHandle(stream
);
1658 CloseHandle(stream
);
1660 } while (FindNextStreamW(h
, &fsd
));
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
);
1672 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, &bsxa
, sizeof(btrfs_set_xattr
));
1674 if (Status
== STATUS_BUFFER_OVERFLOW
|| (NT_SUCCESS(Status
) && bsxa
.valuelen
> 0)) {
1676 btrfs_set_xattr
*xa
= NULL
, *xa2
;
1682 xa
= (btrfs_set_xattr
*)malloc(xalen
);
1684 Status
= NtFsControlFile(source
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_XATTRS
, NULL
, 0, xa
, xalen
);
1685 } while (Status
== STATUS_BUFFER_OVERFLOW
);
1687 if (!NT_SUCCESS(Status
)) {
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
)) {
1700 xa2
= (btrfs_set_xattr
*)&xa2
->data
[xa2
->namelen
+ xa2
->valuelen
];
1704 } else if (!NT_SUCCESS(Status
))
1711 FILE_DISPOSITION_INFO fdi
;
1713 fdi
.DeleteFile
= TRUE
;
1714 SetFileInformationByHandle(dest
, FileDispositionInfo
, &fdi
, sizeof(FILE_DISPOSITION_INFO
));
1718 CloseHandle(source
);
1725 void CALLBACK
ReflinkCopyW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1729 args
= CommandLineToArgvW(lpszCmdLine
, &num_args
);
1734 if (num_args
>= 2) {
1736 BOOL dest_is_dir
= FALSE
;
1737 std::wstring dest
= args
[num_args
- 1], destdir
, destname
;
1738 WCHAR volpath2
[MAX_PATH
];
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
;
1745 if (GetFileInformationByHandle(destdirh
, &bhfi
) && bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1749 if (destdir
.substr(destdir
.length() - 1, 1) != L
"\\")
1752 CloseHandle(destdirh
);
1756 size_t found
= dest
.rfind(L
"\\");
1758 if (found
== std::wstring::npos
) {
1762 destdir
= dest
.substr(0, found
);
1763 destname
= dest
.substr(found
+ 1);
1767 if (!GetVolumePathNameW(dest
.c_str(), volpath2
, sizeof(volpath2
) / sizeof(WCHAR
)))
1770 for (i
= 0; i
< num_args
- 1; i
++) {
1771 WIN32_FIND_DATAW ffd
;
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
"\\");
1780 if (found
== std::wstring::npos
)
1783 path
= path
.substr(0, found
);
1787 if (get_volume_path_parent(path
.c_str(), volpath1
, sizeof(volpath1
) / sizeof(WCHAR
))) {
1788 if (!wcscmp(volpath1
, volpath2
)) {
1790 reflink_copy2(path
+ ffd
.cFileName
, destdir
, dest_is_dir
? ffd
.cFileName
: destname
);
1791 } while (FindNextFileW(h
, &ffd
));