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/>. */
26 static const GUID CLSID_ShellBtrfsIconHandler
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf0 } };
27 static const GUID CLSID_ShellBtrfsContextMenu
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf1 } };
28 static const GUID CLSID_ShellBtrfsPropSheet
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf2 } };
29 static const GUID CLSID_ShellBtrfsVolPropSheet
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf3 } };
31 #define COM_DESCRIPTION_ICON_HANDLER L"WinBtrfs shell extension (icon handler)"
32 #define COM_DESCRIPTION_CONTEXT_MENU L"WinBtrfs shell extension (context menu)"
33 #define COM_DESCRIPTION_PROP_SHEET L"WinBtrfs shell extension (property sheet)"
34 #define COM_DESCRIPTION_VOL_PROP_SHEET L"WinBtrfs shell extension (volume property sheet)"
35 #define ICON_OVERLAY_NAME L"WinBtrfs"
37 typedef enum _PROCESS_DPI_AWARENESS
{
39 PROCESS_SYSTEM_DPI_AWARE
,
40 PROCESS_PER_MONITOR_DPI_AWARE
41 } PROCESS_DPI_AWARENESS
;
43 typedef ULONG (WINAPI
*_RtlNtStatusToDosError
)(NTSTATUS Status
);
44 typedef HRESULT (WINAPI
*_SetProcessDpiAwareness
)(PROCESS_DPI_AWARENESS value
);
49 void ShowError(HWND hwnd
, ULONG err
) {
52 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
,
53 err
, 0, (WCHAR
*)&buf
, 0, NULL
) == 0) {
54 MessageBoxW(hwnd
, L
"FormatMessage failed", L
"Error", MB_ICONERROR
);
58 MessageBoxW(hwnd
, buf
, L
"Error", MB_ICONERROR
);
63 void ShowStringError(HWND hwndDlg
, int num
, ...) {
64 WCHAR title
[255], s
[1024], t
[1024];
67 if (!LoadStringW(module
, IDS_ERROR
, title
, sizeof(title
) / sizeof(WCHAR
))) {
68 ShowError(hwndDlg
, GetLastError());
72 if (!LoadStringW(module
, num
, s
, sizeof(s
) / sizeof(WCHAR
))) {
73 ShowError(hwndDlg
, GetLastError());
79 vswprintf(t
, sizeof(t
) / sizeof(WCHAR
), s
, ap
);
81 vsnwprintf(t
, sizeof(t
) / sizeof(WCHAR
), s
, ap
);
84 MessageBoxW(hwndDlg
, t
, title
, MB_ICONERROR
);
89 void ShowNtStatusError(HWND hwnd
, NTSTATUS Status
) {
90 _RtlNtStatusToDosError RtlNtStatusToDosError
;
91 HMODULE ntdll
= LoadLibraryW(L
"ntdll.dll");
94 MessageBoxW(hwnd
, L
"Error loading ntdll.dll", L
"Error", MB_ICONERROR
);
98 RtlNtStatusToDosError
= (_RtlNtStatusToDosError
)GetProcAddress(ntdll
, "RtlNtStatusToDosError");
100 if (!RtlNtStatusToDosError
) {
101 MessageBoxW(hwnd
, L
"Error loading RtlNtStatusToDosError in ntdll.dll", L
"Error", MB_ICONERROR
);
106 ShowError(hwnd
, RtlNtStatusToDosError(Status
));
111 void set_dpi_aware() {
112 _SetProcessDpiAwareness SetProcessDpiAwareness
;
113 HMODULE shcore
= LoadLibraryW(L
"shcore.dll");
118 SetProcessDpiAwareness
= (_SetProcessDpiAwareness
)GetProcAddress(shcore
, "SetProcessDpiAwareness");
120 if (!SetProcessDpiAwareness
)
123 SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE
);
126 void format_size(UINT64 size
, WCHAR
* s
, ULONG len
, BOOL show_bytes
) {
127 WCHAR nb
[255], nb2
[255], t
[255], bytes
[255];
132 WCHAR thou
[4], grouping
[64], *c
;
134 _i64tow(size
, nb
, 10);
136 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, thou
, sizeof(thou
) / sizeof(WCHAR
));
140 fmt
.lpDecimalSep
= (LPWSTR
)L
"."; // not used
141 fmt
.lpThousandSep
= thou
;
142 fmt
.NegativeOrder
= 0;
144 // Grouping code copied from dlls/shlwapi/string.c in Wine - thank you
147 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SGROUPING
, grouping
, sizeof(grouping
) / sizeof(WCHAR
));
151 if (*c
>= '0' && *c
< '9') {
153 fmt
.Grouping
+= *c
- '0';
159 if (fmt
.Grouping
% 10 == 0)
164 GetNumberFormatW(LOCALE_USER_DEFAULT
, 0, nb
, &fmt
, nb2
, sizeof(nb2
) / sizeof(WCHAR
));
167 if (!LoadStringW(module
, size
== 1 ? IDS_SIZE_BYTE
: IDS_SIZE_BYTES
, t
, sizeof(t
) / sizeof(WCHAR
))) {
168 ShowError(NULL
, GetLastError());
172 if (StringCchPrintfW(s
, len
, t
, nb2
) == STRSAFE_E_INSUFFICIENT_BUFFER
) {
173 ShowError(NULL
, ERROR_INSUFFICIENT_BUFFER
);
181 if (!LoadStringW(module
, IDS_SIZE_BYTES
, t
, sizeof(t
) / sizeof(WCHAR
))) {
182 ShowError(NULL
, GetLastError());
186 if (StringCchPrintfW(bytes
, sizeof(bytes
) / sizeof(WCHAR
), t
, nb2
) == STRSAFE_E_INSUFFICIENT_BUFFER
) {
187 ShowError(NULL
, ERROR_INSUFFICIENT_BUFFER
);
192 if (size
>= 1152921504606846976) {
194 f
= (float)size
/ 1152921504606846976.0f
;
195 } else if (size
>= 1125899906842624) {
197 f
= (float)size
/ 1125899906842624.0f
;
198 } else if (size
>= 1099511627776) {
200 f
= (float)size
/ 1099511627776.0f
;
201 } else if (size
>= 1073741824) {
203 f
= (float)size
/ 1073741824.0f
;
204 } else if (size
>= 1048576) {
206 f
= (float)size
/ 1048576.0f
;
209 f
= (float)size
/ 1024.0f
;
212 if (!LoadStringW(module
, sr
, t
, sizeof(t
) / sizeof(WCHAR
))) {
213 ShowError(NULL
, GetLastError());
218 if (StringCchPrintfW(kb
, sizeof(kb
) / sizeof(WCHAR
), t
, f
) == STRSAFE_E_INSUFFICIENT_BUFFER
) {
219 ShowError(NULL
, ERROR_INSUFFICIENT_BUFFER
);
223 if (!LoadStringW(module
, IDS_SIZE_LARGE
, t
, sizeof(t
) / sizeof(WCHAR
))) {
224 ShowError(NULL
, GetLastError());
228 if (StringCchPrintfW(s
, len
, t
, kb
, bytes
) == STRSAFE_E_INSUFFICIENT_BUFFER
) {
229 ShowError(NULL
, ERROR_INSUFFICIENT_BUFFER
);
233 if (StringCchPrintfW(s
, len
, t
, f
) == STRSAFE_E_INSUFFICIENT_BUFFER
) {
234 ShowError(NULL
, ERROR_INSUFFICIENT_BUFFER
);
240 std::wstring
format_message(ULONG last_error
) {
244 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
,
245 last_error
, 0, (WCHAR
*)&buf
, 0, NULL
) == 0) {
246 return L
"(error retrieving message)";
253 // remove trailing newline
254 while (s
.length() > 0 && (s
.substr(s
.length() - 1, 1) == L
"\r" || s
.substr(s
.length() - 1, 1) == L
"\n"))
255 s
= s
.substr(0, s
.length() - 1);
260 std::wstring
format_ntstatus(NTSTATUS Status
) {
261 _RtlNtStatusToDosError RtlNtStatusToDosError
;
263 HMODULE ntdll
= LoadLibraryW(L
"ntdll.dll");
266 return L
"(error loading ntdll.dll)";
268 RtlNtStatusToDosError
= (_RtlNtStatusToDosError
)GetProcAddress(ntdll
, "RtlNtStatusToDosError");
270 if (!RtlNtStatusToDosError
) {
272 return L
"(error loading RtlNtStatusToDosError)";
275 s
= format_message(RtlNtStatusToDosError(Status
));
286 STDAPI
DllCanUnloadNow(void) {
287 return objs_loaded
== 0 ? S_OK
: S_FALSE
;
290 STDAPI
DllGetClassObject(REFCLSID rclsid
, REFIID riid
, LPVOID
* ppv
) {
291 if (rclsid
== CLSID_ShellBtrfsIconHandler
) {
292 Factory
* fact
= new Factory
;
294 return E_OUTOFMEMORY
;
296 fact
->type
= FactoryIconHandler
;
298 return fact
->QueryInterface(riid
, ppv
);
300 } else if (rclsid
== CLSID_ShellBtrfsContextMenu
) {
301 Factory
* fact
= new Factory
;
303 return E_OUTOFMEMORY
;
305 fact
->type
= FactoryContextMenu
;
307 return fact
->QueryInterface(riid
, ppv
);
309 } else if (rclsid
== CLSID_ShellBtrfsPropSheet
) {
310 Factory
* fact
= new Factory
;
312 return E_OUTOFMEMORY
;
314 fact
->type
= FactoryPropSheet
;
316 return fact
->QueryInterface(riid
, ppv
);
318 } else if (rclsid
== CLSID_ShellBtrfsVolPropSheet
) {
319 Factory
* fact
= new Factory
;
321 return E_OUTOFMEMORY
;
323 fact
->type
= FactoryVolPropSheet
;
325 return fact
->QueryInterface(riid
, ppv
);
329 return CLASS_E_CLASSNOTAVAILABLE
;
332 static BOOL
write_reg_key(HKEY root
, const WCHAR
* keyname
, const WCHAR
* val
, DWORD type
, const BYTE
* data
, DWORD datasize
) {
337 l
= RegCreateKeyExW(root
, keyname
, 0, NULL
, 0, KEY_ALL_ACCESS
, NULL
, &hk
, &dispos
);
338 if (l
!= ERROR_SUCCESS
) {
340 wsprintfW(s
, L
"RegCreateKey returned %08x", l
);
341 MessageBoxW(0, s
, NULL
, MB_ICONERROR
);
346 l
= RegSetValueExW(hk
, val
, 0, type
, data
, datasize
);
347 if (l
!= ERROR_SUCCESS
) {
349 wsprintfW(s
, L
"RegSetValueEx returned %08x", l
);
350 MessageBoxW(0, s
, NULL
, MB_ICONERROR
);
356 if (l
!= ERROR_SUCCESS
) {
358 wsprintfW(s
, L
"RegCloseKey returned %08x", l
);
359 MessageBoxW(0, s
, NULL
, MB_ICONERROR
);
367 static BOOL
register_clsid(const GUID clsid
, const WCHAR
* description
) {
369 WCHAR inproc
[MAX_PATH
], progid
[MAX_PATH
], clsidkeyname
[MAX_PATH
], dllpath
[MAX_PATH
];
372 StringFromCLSID(clsid
, &clsidstring
);
374 wsprintfW(inproc
, L
"CLSID\\%s\\InprocServer32", clsidstring
);
375 wsprintfW(progid
, L
"CLSID\\%s\\ProgId", clsidstring
);
376 wsprintfW(clsidkeyname
, L
"CLSID\\%s", clsidstring
);
378 if (!write_reg_key(HKEY_CLASSES_ROOT
, clsidkeyname
, NULL
, REG_SZ
, (BYTE
*)description
, (wcslen(description
) + 1) * sizeof(WCHAR
)))
381 GetModuleFileNameW(module
, dllpath
, sizeof(dllpath
));
383 if (!write_reg_key(HKEY_CLASSES_ROOT
, inproc
, NULL
, REG_SZ
, (BYTE
*)dllpath
, (wcslen(dllpath
) + 1) * sizeof(WCHAR
)))
386 if (!write_reg_key(HKEY_CLASSES_ROOT
, inproc
, L
"ThreadingModel", REG_SZ
, (BYTE
*)L
"Apartment", (wcslen(L
"Apartment") + 1) * sizeof(WCHAR
)))
392 CoTaskMemFree(clsidstring
);
397 static BOOL
unregister_clsid(const GUID clsid
) {
399 WCHAR clsidkeyname
[MAX_PATH
];
403 StringFromCLSID(clsid
, &clsidstring
);
404 wsprintfW(clsidkeyname
, L
"CLSID\\%s", clsidstring
);
406 l
= RegDeleteTreeW(HKEY_CLASSES_ROOT
, clsidkeyname
);
408 if (l
!= ERROR_SUCCESS
) {
410 wsprintfW(s
, L
"RegDeleteTree returned %08x", l
);
411 MessageBoxW(0, s
, NULL
, MB_ICONERROR
);
417 CoTaskMemFree(clsidstring
);
422 static BOOL
reg_icon_overlay(const GUID clsid
, const WCHAR
* name
) {
423 WCHAR path
[MAX_PATH
];
427 StringFromCLSID(clsid
, &clsidstring
);
429 wcscpy(path
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\");
432 if (!write_reg_key(HKEY_LOCAL_MACHINE
, path
, NULL
, REG_SZ
, (BYTE
*)clsidstring
, (wcslen(clsidstring
) + 1) * sizeof(WCHAR
)))
438 CoTaskMemFree(clsidstring
);
443 static BOOL
unreg_icon_overlay(const WCHAR
* name
) {
444 WCHAR path
[MAX_PATH
];
447 wcscpy(path
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\");
450 l
= RegDeleteTreeW(HKEY_LOCAL_MACHINE
, path
);
452 if (l
!= ERROR_SUCCESS
) {
454 wsprintfW(s
, L
"RegDeleteTree returned %08x", l
);
455 MessageBoxW(0, s
, NULL
, MB_ICONERROR
);
462 static BOOL
reg_context_menu_handler(const GUID clsid
, const WCHAR
* filetype
, const WCHAR
* name
) {
463 WCHAR path
[MAX_PATH
];
467 StringFromCLSID(clsid
, &clsidstring
);
469 wcscpy(path
, filetype
);
470 wcscat(path
, L
"\\ShellEx\\ContextMenuHandlers\\");
473 if (!write_reg_key(HKEY_CLASSES_ROOT
, path
, NULL
, REG_SZ
, (BYTE
*)clsidstring
, (wcslen(clsidstring
) + 1) * sizeof(WCHAR
)))
479 CoTaskMemFree(clsidstring
);
484 static BOOL
unreg_context_menu_handler(const WCHAR
* filetype
, const WCHAR
* name
) {
485 WCHAR path
[MAX_PATH
];
488 wcscpy(path
, filetype
);
489 wcscat(path
, L
"\\ShellEx\\ContextMenuHandlers\\");
492 l
= RegDeleteTreeW(HKEY_CLASSES_ROOT
, path
);
494 if (l
!= ERROR_SUCCESS
) {
496 wsprintfW(s
, L
"RegDeleteTree returned %08x", l
);
497 MessageBoxW(0, s
, NULL
, MB_ICONERROR
);
504 static BOOL
reg_prop_sheet_handler(const GUID clsid
, const WCHAR
* filetype
, const WCHAR
* name
) {
505 WCHAR path
[MAX_PATH
];
509 StringFromCLSID(clsid
, &clsidstring
);
511 wcscpy(path
, filetype
);
512 wcscat(path
, L
"\\ShellEx\\PropertySheetHandlers\\");
515 if (!write_reg_key(HKEY_CLASSES_ROOT
, path
, NULL
, REG_SZ
, (BYTE
*)clsidstring
, (wcslen(clsidstring
) + 1) * sizeof(WCHAR
)))
521 CoTaskMemFree(clsidstring
);
526 static BOOL
unreg_prop_sheet_handler(const WCHAR
* filetype
, const WCHAR
* name
) {
527 WCHAR path
[MAX_PATH
];
530 wcscpy(path
, filetype
);
531 wcscat(path
, L
"\\ShellEx\\PropertySheetHandlers\\");
534 l
= RegDeleteTreeW(HKEY_CLASSES_ROOT
, path
);
536 if (l
!= ERROR_SUCCESS
) {
538 wsprintfW(s
, L
"RegDeleteTree returned %08x", l
);
539 MessageBoxW(0, s
, NULL
, MB_ICONERROR
);
546 STDAPI
DllRegisterServer(void) {
547 if (!register_clsid(CLSID_ShellBtrfsIconHandler
, COM_DESCRIPTION_ICON_HANDLER
))
550 if (!register_clsid(CLSID_ShellBtrfsContextMenu
, COM_DESCRIPTION_CONTEXT_MENU
))
553 if (!register_clsid(CLSID_ShellBtrfsPropSheet
, COM_DESCRIPTION_PROP_SHEET
))
556 if (!register_clsid(CLSID_ShellBtrfsVolPropSheet
, COM_DESCRIPTION_VOL_PROP_SHEET
))
559 if (!reg_icon_overlay(CLSID_ShellBtrfsIconHandler
, ICON_OVERLAY_NAME
)) {
560 MessageBoxW(0, L
"Failed to register icon overlay.", NULL
, MB_ICONERROR
);
564 if (!reg_context_menu_handler(CLSID_ShellBtrfsContextMenu
, L
"Directory\\Background", ICON_OVERLAY_NAME
)) {
565 MessageBoxW(0, L
"Failed to register context menu handler.", NULL
, MB_ICONERROR
);
569 if (!reg_context_menu_handler(CLSID_ShellBtrfsContextMenu
, L
"Folder", ICON_OVERLAY_NAME
)) {
570 MessageBoxW(0, L
"Failed to register context menu handler.", NULL
, MB_ICONERROR
);
574 if (!reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet
, L
"Folder", ICON_OVERLAY_NAME
)) {
575 MessageBoxW(0, L
"Failed to register property sheet handler.", NULL
, MB_ICONERROR
);
579 if (!reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet
, L
"*", ICON_OVERLAY_NAME
)) {
580 MessageBoxW(0, L
"Failed to register property sheet handler.", NULL
, MB_ICONERROR
);
584 if (!reg_prop_sheet_handler(CLSID_ShellBtrfsVolPropSheet
, L
"Drive", ICON_OVERLAY_NAME
)) {
585 MessageBoxW(0, L
"Failed to register volume property sheet handler.", NULL
, MB_ICONERROR
);
592 STDAPI
DllUnregisterServer(void) {
593 unreg_prop_sheet_handler(L
"Folder", ICON_OVERLAY_NAME
);
594 unreg_prop_sheet_handler(L
"*", ICON_OVERLAY_NAME
);
595 unreg_prop_sheet_handler(L
"Drive", ICON_OVERLAY_NAME
);
596 unreg_context_menu_handler(L
"Folder", ICON_OVERLAY_NAME
);
597 unreg_context_menu_handler(L
"Directory\\Background", ICON_OVERLAY_NAME
);
598 unreg_icon_overlay(ICON_OVERLAY_NAME
);
600 if (!unregister_clsid(CLSID_ShellBtrfsVolPropSheet
))
603 if (!unregister_clsid(CLSID_ShellBtrfsPropSheet
))
606 if (!unregister_clsid(CLSID_ShellBtrfsContextMenu
))
609 if (!unregister_clsid(CLSID_ShellBtrfsIconHandler
))
615 STDAPI
DllInstall(BOOL bInstall
, LPCWSTR pszCmdLine
) {
617 return DllRegisterServer();
619 return DllUnregisterServer();
622 BOOL APIENTRY
DllMain(HANDLE hModule
, DWORD dwReason
, void* lpReserved
) {
623 if (dwReason
== DLL_PROCESS_ATTACH
)
624 module
= (HMODULE
)hModule
;
629 static void create_subvol(std::wstring fn
) {
630 size_t found
= fn
.rfind(L
"\\");
631 std::wstring path
, file
;
634 btrfs_create_subvol
* bcs
;
635 IO_STATUS_BLOCK iosb
;
637 if (found
== std::wstring::npos
) {
641 path
= fn
.substr(0, found
);
642 file
= fn
.substr(found
+ 1);
646 h
= CreateFileW(path
.c_str(), FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
648 if (h
== INVALID_HANDLE_VALUE
)
651 bcslen
= offsetof(btrfs_create_subvol
, name
[0]) + (file
.length() * sizeof(WCHAR
));
652 bcs
= (btrfs_create_subvol
*)malloc(bcslen
);
654 bcs
->readonly
= FALSE
;
656 bcs
->namelen
= file
.length() * sizeof(WCHAR
);
657 memcpy(bcs
->name
, file
.c_str(), bcs
->namelen
);
659 NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SUBVOL
, bcs
, bcslen
, NULL
, 0);
664 void CALLBACK
CreateSubvolW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
668 args
= CommandLineToArgvW(lpszCmdLine
, &num_args
);
674 create_subvol(args
[0]);
679 static void create_snapshot2(std::wstring source
, std::wstring fn
) {
680 size_t found
= fn
.rfind(L
"\\");
681 std::wstring path
, file
;
684 btrfs_create_snapshot
* bcs
;
685 IO_STATUS_BLOCK iosb
;
687 if (found
== std::wstring::npos
) {
691 path
= fn
.substr(0, found
);
692 file
= fn
.substr(found
+ 1);
696 src
= CreateFileW(source
.c_str(), FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
697 if (src
== INVALID_HANDLE_VALUE
)
700 h
= CreateFileW(path
.c_str(), FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
702 if (h
== INVALID_HANDLE_VALUE
) {
707 bcslen
= offsetof(btrfs_create_snapshot
, name
[0]) + (file
.length() * sizeof(WCHAR
));
708 bcs
= (btrfs_create_snapshot
*)malloc(bcslen
);
710 bcs
->readonly
= FALSE
;
712 bcs
->namelen
= file
.length() * sizeof(WCHAR
);
713 memcpy(bcs
->name
, file
.c_str(), bcs
->namelen
);
716 NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_CREATE_SNAPSHOT
, bcs
, bcslen
, NULL
, 0);
722 void CALLBACK
CreateSnapshotW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
726 args
= CommandLineToArgvW(lpszCmdLine
, &num_args
);
732 create_snapshot2(args
[0], args
[1]);