1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include "shellext.h"
19 #include <windows.h>
20 #include <commctrl.h>
21 #include <strsafe.h>
22 #include <stddef.h>
23 #include <stdexcept>
24 #include "factory.h"
25 #include "resource.h"
26
27 static const GUID CLSID_ShellBtrfsIconHandler = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf0 } };
28 static const GUID CLSID_ShellBtrfsContextMenu = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf1 } };
29 static const GUID CLSID_ShellBtrfsPropSheet = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf2 } };
30 static const GUID CLSID_ShellBtrfsVolPropSheet = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf3 } };
31
32 #define COM_DESCRIPTION_ICON_HANDLER L"WinBtrfs shell extension (icon handler)"
33 #define COM_DESCRIPTION_CONTEXT_MENU L"WinBtrfs shell extension (context menu)"
34 #define COM_DESCRIPTION_PROP_SHEET L"WinBtrfs shell extension (property sheet)"
35 #define COM_DESCRIPTION_VOL_PROP_SHEET L"WinBtrfs shell extension (volume property sheet)"
36 #define ICON_OVERLAY_NAME L"WinBtrfs"
37
38 typedef enum _PROCESS_DPI_AWARENESS {
39 PROCESS_DPI_UNAWARE,
40 PROCESS_SYSTEM_DPI_AWARE,
41 PROCESS_PER_MONITOR_DPI_AWARE
42 } PROCESS_DPI_AWARENESS;
43
44 typedef ULONG (WINAPI *_RtlNtStatusToDosError)(NTSTATUS Status);
45 typedef HRESULT (WINAPI *_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS value);
46
47 HMODULE module;
48 LONG objs_loaded = 0;
49
set_dpi_aware()50 void set_dpi_aware() {
51 _SetProcessDpiAwareness SetProcessDpiAwareness;
52 HMODULE shcore = LoadLibraryW(L"shcore.dll");
53
54 if (!shcore)
55 return;
56
57 SetProcessDpiAwareness = (_SetProcessDpiAwareness)GetProcAddress(shcore, "SetProcessDpiAwareness");
58
59 if (!SetProcessDpiAwareness)
60 return;
61
62 SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
63 }
64
format_size(uint64_t size,wstring & s,bool show_bytes)65 void format_size(uint64_t size, wstring& s, bool show_bytes) {
66 wstring t, bytes, kb, nb;
67 WCHAR nb2[255];
68 ULONG sr;
69 float f;
70 NUMBERFMTW fmt;
71 WCHAR dec[2], thou[4], grouping[64], *c;
72 #ifdef __REACTOS__
73 WCHAR buffer[64];
74 #endif
75
76 #ifndef __REACTOS__
77 nb = to_wstring(size);
78 #else
79 swprintf(buffer, L"%I64d", size);
80 nb = wstring(buffer);
81 #endif
82
83 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thou, sizeof(thou) / sizeof(WCHAR));
84
85 dec[0] = '.'; dec[1] = 0; // not used, but silences gcc warning
86
87 fmt.NumDigits = 0;
88 fmt.LeadingZero = 1;
89 fmt.lpDecimalSep = dec;
90 fmt.lpThousandSep = thou;
91 fmt.NegativeOrder = 0;
92
93 // Grouping code copied from dlls/shlwapi/string.c in Wine - thank you
94
95 fmt.Grouping = 0;
96 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping) / sizeof(WCHAR));
97
98 c = grouping;
99 while (*c) {
100 if (*c >= '0' && *c < '9') {
101 fmt.Grouping *= 10;
102 fmt.Grouping += *c - '0';
103 }
104
105 c++;
106 }
107
108 if (fmt.Grouping % 10 == 0)
109 fmt.Grouping /= 10;
110 else
111 fmt.Grouping *= 10;
112
113 GetNumberFormatW(LOCALE_USER_DEFAULT, 0, nb.c_str(), &fmt, nb2, sizeof(nb2) / sizeof(WCHAR));
114
115 if (size < 1024) {
116 if (!load_string(module, size == 1 ? IDS_SIZE_BYTE : IDS_SIZE_BYTES, t))
117 throw last_error(GetLastError());
118
119 wstring_sprintf(s, t, nb2);
120 return;
121 }
122
123 if (show_bytes) {
124 if (!load_string(module, IDS_SIZE_BYTES, t))
125 throw last_error(GetLastError());
126
127 wstring_sprintf(bytes, t, nb2);
128 }
129
130 if (size >= 1152921504606846976) {
131 sr = IDS_SIZE_EB;
132 f = (float)size / 1152921504606846976.0f;
133 } else if (size >= 1125899906842624) {
134 sr = IDS_SIZE_PB;
135 f = (float)size / 1125899906842624.0f;
136 } else if (size >= 1099511627776) {
137 sr = IDS_SIZE_TB;
138 f = (float)size / 1099511627776.0f;
139 } else if (size >= 1073741824) {
140 sr = IDS_SIZE_GB;
141 f = (float)size / 1073741824.0f;
142 } else if (size >= 1048576) {
143 sr = IDS_SIZE_MB;
144 f = (float)size / 1048576.0f;
145 } else {
146 sr = IDS_SIZE_KB;
147 f = (float)size / 1024.0f;
148 }
149
150 if (!load_string(module, sr, t))
151 throw last_error(GetLastError());
152
153 if (show_bytes) {
154 wstring_sprintf(kb, t, f);
155
156 if (!load_string(module, IDS_SIZE_LARGE, t))
157 throw last_error(GetLastError());
158
159 wstring_sprintf(s, t, kb.c_str(), bytes.c_str());
160 } else
161 wstring_sprintf(s, t, f);
162 }
163
format_message(ULONG last_error)164 wstring format_message(ULONG last_error) {
165 WCHAR* buf;
166 wstring s;
167
168 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
169 last_error, 0, (WCHAR*)&buf, 0, nullptr) == 0) {
170 return L"(error retrieving message)";
171 }
172
173 s = buf;
174
175 LocalFree(buf);
176
177 // remove trailing newline
178 while (s.length() > 0 && (s.substr(s.length() - 1, 1) == L"\r" || s.substr(s.length() - 1, 1) == L"\n"))
179 s = s.substr(0, s.length() - 1);
180
181 return s;
182 }
183
format_ntstatus(NTSTATUS Status)184 wstring format_ntstatus(NTSTATUS Status) {
185 _RtlNtStatusToDosError RtlNtStatusToDosError;
186 wstring s;
187 HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
188
189 if (!ntdll)
190 return L"(error loading ntdll.dll)";
191
192 RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(ntdll, "RtlNtStatusToDosError");
193
194 if (!RtlNtStatusToDosError) {
195 FreeLibrary(ntdll);
196 return L"(error loading RtlNtStatusToDosError)";
197 }
198
199 s = format_message(RtlNtStatusToDosError(Status));
200
201 FreeLibrary(ntdll);
202
203 return s;
204 }
205
load_string(HMODULE module,UINT id,wstring & s)206 bool load_string(HMODULE module, UINT id, wstring& s) {
207 int len;
208 LPWSTR retstr = nullptr;
209
210 len = LoadStringW(module, id, (LPWSTR)&retstr, 0);
211
212 if (len == 0)
213 return false;
214
215 s = wstring(retstr, len);
216
217 return true;
218 }
219
220 #ifdef _MSC_VER
221 #pragma warning(push)
222 #pragma warning(disable: 4996)
223 #endif
224
wstring_sprintf(wstring & s,wstring fmt,...)225 void wstring_sprintf(wstring& s, wstring fmt, ...) {
226 int len;
227 va_list args;
228
229 va_start(args, fmt);
230 len = _vsnwprintf(nullptr, 0, fmt.c_str(), args);
231
232 if (len == 0)
233 s = L"";
234 else {
235 s.resize(len);
236 _vsnwprintf((wchar_t*)s.c_str(), len, fmt.c_str(), args);
237 }
238
239 va_end(args);
240 }
241
242 #ifdef _MSC_VER
243 #pragma warning(pop)
244 #endif
245
DllCanUnloadNow(void)246 STDAPI DllCanUnloadNow(void) {
247 return objs_loaded == 0 ? S_OK : S_FALSE;
248 }
249
DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID * ppv)250 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
251 if (rclsid == CLSID_ShellBtrfsIconHandler) {
252 Factory* fact = new Factory;
253 if (!fact)
254 return E_OUTOFMEMORY;
255 else {
256 fact->type = FactoryIconHandler;
257
258 return fact->QueryInterface(riid, ppv);
259 }
260 } else if (rclsid == CLSID_ShellBtrfsContextMenu) {
261 Factory* fact = new Factory;
262 if (!fact)
263 return E_OUTOFMEMORY;
264 else {
265 fact->type = FactoryContextMenu;
266
267 return fact->QueryInterface(riid, ppv);
268 }
269 } else if (rclsid == CLSID_ShellBtrfsPropSheet) {
270 Factory* fact = new Factory;
271 if (!fact)
272 return E_OUTOFMEMORY;
273 else {
274 fact->type = FactoryPropSheet;
275
276 return fact->QueryInterface(riid, ppv);
277 }
278 } else if (rclsid == CLSID_ShellBtrfsVolPropSheet) {
279 Factory* fact = new Factory;
280 if (!fact)
281 return E_OUTOFMEMORY;
282 else {
283 fact->type = FactoryVolPropSheet;
284
285 return fact->QueryInterface(riid, ppv);
286 }
287 }
288
289 return CLASS_E_CLASSNOTAVAILABLE;
290 }
291
write_reg_key(HKEY root,const wstring & keyname,const WCHAR * val,const wstring & data)292 static void write_reg_key(HKEY root, const wstring& keyname, const WCHAR* val, const wstring& data) {
293 LONG l;
294 HKEY hk;
295 DWORD dispos;
296
297 l = RegCreateKeyExW(root, keyname.c_str(), 0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &hk, &dispos);
298 if (l != ERROR_SUCCESS)
299 throw string_error(IDS_REGCREATEKEY_FAILED, l);
300
301 l = RegSetValueExW(hk, val, 0, REG_SZ, (const BYTE*)data.c_str(), (DWORD)((data.length() + 1) * sizeof(WCHAR)));
302 if (l != ERROR_SUCCESS)
303 throw string_error(IDS_REGSETVALUEEX_FAILED, l);
304
305 l = RegCloseKey(hk);
306 if (l != ERROR_SUCCESS)
307 throw string_error(IDS_REGCLOSEKEY_FAILED, l);
308 }
309
register_clsid(const GUID clsid,const WCHAR * description)310 static void register_clsid(const GUID clsid, const WCHAR* description) {
311 WCHAR* clsidstring;
312 wstring inproc, clsidkeyname;
313 WCHAR dllpath[MAX_PATH];
314
315 StringFromCLSID(clsid, &clsidstring);
316
317 try {
318 #ifndef __REACTOS__
319 inproc = L"CLSID\\"s + clsidstring + L"\\InprocServer32"s;
320 clsidkeyname = L"CLSID\\"s + clsidstring;
321 #else
322 inproc = wstring(L"CLSID\\") + clsidstring + wstring(L"\\InprocServer32");
323 clsidkeyname = wstring(L"CLSID\\") + clsidstring;
324 #endif
325
326 write_reg_key(HKEY_CLASSES_ROOT, clsidkeyname, nullptr, description);
327
328 GetModuleFileNameW(module, dllpath, sizeof(dllpath) / sizeof(WCHAR));
329
330 write_reg_key(HKEY_CLASSES_ROOT, inproc, nullptr, dllpath);
331
332 write_reg_key(HKEY_CLASSES_ROOT, inproc, L"ThreadingModel", L"Apartment");
333 } catch (...) {
334 CoTaskMemFree(clsidstring);
335 throw;
336 }
337
338 CoTaskMemFree(clsidstring);
339 }
340
341 // implementation of RegDeleteTreeW, only available from Vista on
reg_delete_tree(HKEY hkey,const wstring & keyname)342 static void reg_delete_tree(HKEY hkey, const wstring& keyname) {
343 HKEY k;
344 LSTATUS ret;
345
346 ret = RegOpenKeyExW(hkey, keyname.c_str(), 0, KEY_READ, &k);
347
348 if (ret != ERROR_SUCCESS)
349 throw last_error(ret);
350
351 try {
352 WCHAR* buf;
353 ULONG bufsize;
354
355 ret = RegQueryInfoKeyW(k, nullptr, nullptr, nullptr, nullptr, &bufsize, nullptr,
356 nullptr, nullptr, nullptr, nullptr, nullptr);
357 if (ret != ERROR_SUCCESS)
358 throw last_error(ret);
359
360 bufsize++;
361 buf = new WCHAR[bufsize];
362
363 try {
364 do {
365 ULONG size = bufsize;
366
367 ret = RegEnumKeyExW(k, 0, buf, &size, nullptr, nullptr, nullptr, nullptr);
368
369 if (ret == ERROR_NO_MORE_ITEMS)
370 break;
371 else if (ret != ERROR_SUCCESS)
372 throw last_error(ret);
373
374 reg_delete_tree(k, buf);
375 } while (true);
376
377 ret = RegDeleteKeyW(hkey, keyname.c_str());
378 if (ret != ERROR_SUCCESS)
379 throw last_error(ret);
380 } catch (...) {
381 delete[] buf;
382 throw;
383 }
384
385 delete[] buf;
386 } catch (...) {
387 RegCloseKey(k);
388 throw;
389 }
390
391 RegCloseKey(k);
392 }
393
unregister_clsid(const GUID clsid)394 static void unregister_clsid(const GUID clsid) {
395 WCHAR* clsidstring;
396
397 StringFromCLSID(clsid, &clsidstring);
398
399 try {
400 #ifndef __REACTOS__
401 reg_delete_tree(HKEY_CLASSES_ROOT, L"CLSID\\"s + clsidstring);
402 #else
403 wstring path = wstring(L"CLSID\\") + clsidstring;
404 reg_delete_tree(HKEY_CLASSES_ROOT, path);
405 #endif
406 } catch (...) {
407 CoTaskMemFree(clsidstring);
408 throw;
409 }
410
411 CoTaskMemFree(clsidstring);
412 }
413
reg_icon_overlay(const GUID clsid,const wstring & name)414 static void reg_icon_overlay(const GUID clsid, const wstring& name) {
415 WCHAR* clsidstring;
416
417 StringFromCLSID(clsid, &clsidstring);
418
419 try {
420 #ifndef __REACTOS__
421 wstring path = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\"s + name;
422 #else
423 wstring path = wstring(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\") + name;
424 #endif
425
426 write_reg_key(HKEY_LOCAL_MACHINE, path, nullptr, clsidstring);
427 } catch (...) {
428 CoTaskMemFree(clsidstring);
429 throw;
430 }
431
432 CoTaskMemFree(clsidstring);
433 }
434
unreg_icon_overlay(const wstring & name)435 static void unreg_icon_overlay(const wstring& name) {
436 #ifndef __REACTOS__
437 reg_delete_tree(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\"s + name);
438 #else
439 wstring path = wstring(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\") + name;
440 reg_delete_tree(HKEY_LOCAL_MACHINE, path);
441 #endif
442 }
443
reg_context_menu_handler(const GUID clsid,const wstring & filetype,const wstring & name)444 static void reg_context_menu_handler(const GUID clsid, const wstring& filetype, const wstring& name) {
445 WCHAR* clsidstring;
446
447 StringFromCLSID(clsid, &clsidstring);
448
449 try {
450 #ifndef __REACTOS__
451 wstring path = filetype + L"\\ShellEx\\ContextMenuHandlers\\"s + name;
452 #else
453 wstring path = filetype + wstring(L"\\ShellEx\\ContextMenuHandlers\\") + name;
454 #endif
455
456 write_reg_key(HKEY_CLASSES_ROOT, path, nullptr, clsidstring);
457 } catch (...) {
458 CoTaskMemFree(clsidstring);
459 throw;
460 }
461 }
462
unreg_context_menu_handler(const wstring & filetype,const wstring & name)463 static void unreg_context_menu_handler(const wstring& filetype, const wstring& name) {
464 #ifndef __REACTOS__
465 reg_delete_tree(HKEY_CLASSES_ROOT, filetype + L"\\ShellEx\\ContextMenuHandlers\\"s + name);
466 #else
467 wstring path = filetype + wstring(L"\\ShellEx\\ContextMenuHandlers\\") + name;
468 reg_delete_tree(HKEY_CLASSES_ROOT, path);
469 #endif
470 }
471
reg_prop_sheet_handler(const GUID clsid,const wstring & filetype,const wstring & name)472 static void reg_prop_sheet_handler(const GUID clsid, const wstring& filetype, const wstring& name) {
473 WCHAR* clsidstring;
474
475 StringFromCLSID(clsid, &clsidstring);
476
477 try {
478 #ifndef __REACTOS__
479 wstring path = filetype + L"\\ShellEx\\PropertySheetHandlers\\"s + name;
480 #else
481 wstring path = filetype + wstring(L"\\ShellEx\\PropertySheetHandlers\\") + name;
482 #endif
483
484 write_reg_key(HKEY_CLASSES_ROOT, path, nullptr, clsidstring);
485 } catch (...) {
486 CoTaskMemFree(clsidstring);
487 throw;
488 }
489 }
490
unreg_prop_sheet_handler(const wstring & filetype,const wstring & name)491 static void unreg_prop_sheet_handler(const wstring& filetype, const wstring& name) {
492 #ifndef __REACTOS__
493 reg_delete_tree(HKEY_CLASSES_ROOT, filetype + L"\\ShellEx\\PropertySheetHandlers\\"s + name);
494 #else
495 wstring path = filetype + wstring(L"\\ShellEx\\PropertySheetHandlers\\") + name;
496 reg_delete_tree(HKEY_CLASSES_ROOT, path);
497 #endif
498 }
499
DllRegisterServer(void)500 STDAPI DllRegisterServer(void) {
501 try {
502 register_clsid(CLSID_ShellBtrfsIconHandler, COM_DESCRIPTION_ICON_HANDLER);
503 register_clsid(CLSID_ShellBtrfsContextMenu, COM_DESCRIPTION_CONTEXT_MENU);
504 register_clsid(CLSID_ShellBtrfsPropSheet, COM_DESCRIPTION_PROP_SHEET);
505 register_clsid(CLSID_ShellBtrfsVolPropSheet, COM_DESCRIPTION_VOL_PROP_SHEET);
506
507 reg_icon_overlay(CLSID_ShellBtrfsIconHandler, ICON_OVERLAY_NAME);
508
509 reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Directory\\Background", ICON_OVERLAY_NAME);
510 reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Folder", ICON_OVERLAY_NAME);
511
512 reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"Folder", ICON_OVERLAY_NAME);
513 reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"*", ICON_OVERLAY_NAME);
514 reg_prop_sheet_handler(CLSID_ShellBtrfsVolPropSheet, L"Drive", ICON_OVERLAY_NAME);
515 } catch (const exception& e) {
516 error_message(nullptr, e.what());
517 return E_FAIL;
518 }
519
520 return S_OK;
521 }
522
DllUnregisterServer(void)523 STDAPI DllUnregisterServer(void) {
524 try {
525 unreg_prop_sheet_handler(L"Folder", ICON_OVERLAY_NAME);
526 unreg_prop_sheet_handler(L"*", ICON_OVERLAY_NAME);
527 unreg_prop_sheet_handler(L"Drive", ICON_OVERLAY_NAME);
528 unreg_context_menu_handler(L"Folder", ICON_OVERLAY_NAME);
529 unreg_context_menu_handler(L"Directory\\Background", ICON_OVERLAY_NAME);
530 unreg_icon_overlay(ICON_OVERLAY_NAME);
531
532 unregister_clsid(CLSID_ShellBtrfsVolPropSheet);
533 unregister_clsid(CLSID_ShellBtrfsPropSheet);
534 unregister_clsid(CLSID_ShellBtrfsContextMenu);
535 unregister_clsid(CLSID_ShellBtrfsIconHandler);
536 } catch (const exception& e) {
537 error_message(nullptr, e.what());
538 return E_FAIL;
539 }
540
541 return S_OK;
542 }
543
DllInstall(BOOL bInstall,LPCWSTR pszCmdLine)544 STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine) {
545 if (bInstall)
546 return DllRegisterServer();
547 else
548 return DllUnregisterServer();
549 }
550
DllMain(HANDLE hModule,DWORD dwReason,void * lpReserved)551 extern "C" BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) {
552 if (dwReason == DLL_PROCESS_ATTACH)
553 module = (HMODULE)hModule;
554
555 return true;
556 }
557
create_subvol(const wstring & fn)558 static void create_subvol(const wstring& fn) {
559 size_t found = fn.rfind(L"\\");
560 wstring path, file;
561 win_handle h;
562 btrfs_create_subvol* bcs;
563 IO_STATUS_BLOCK iosb;
564
565 if (found == wstring::npos) {
566 path = L"";
567 file = fn;
568 } else {
569 path = fn.substr(0, found);
570 file = fn.substr(found + 1);
571 }
572 path += L"\\";
573
574 h = CreateFileW(path.c_str(), FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
575
576 if (h == INVALID_HANDLE_VALUE)
577 return;
578
579 size_t bcslen = offsetof(btrfs_create_subvol, name[0]) + (file.length() * sizeof(WCHAR));
580 bcs = (btrfs_create_subvol*)malloc(bcslen);
581
582 bcs->readonly = false;
583 bcs->posix = false;
584 bcs->namelen = (uint16_t)(file.length() * sizeof(WCHAR));
585 memcpy(bcs->name, file.c_str(), bcs->namelen);
586
587 NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0);
588 }
589
CreateSubvolW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)590 extern "C" void CALLBACK CreateSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
591 vector<wstring> args;
592
593 command_line_to_args(lpszCmdLine, args);
594
595 if (args.size() >= 1)
596 create_subvol(args[0]);
597 }
598
create_snapshot2(const wstring & source,const wstring & fn)599 static void create_snapshot2(const wstring& source, const wstring& fn) {
600 size_t found = fn.rfind(L"\\");
601 wstring path, file;
602 win_handle h, src;
603 btrfs_create_snapshot* bcs;
604 IO_STATUS_BLOCK iosb;
605
606 if (found == wstring::npos) {
607 path = L"";
608 file = fn;
609 } else {
610 path = fn.substr(0, found);
611 file = fn.substr(found + 1);
612 }
613 path += L"\\";
614
615 src = CreateFileW(source.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
616 if (src == INVALID_HANDLE_VALUE)
617 return;
618
619 h = CreateFileW(path.c_str(), FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
620
621 if (h == INVALID_HANDLE_VALUE)
622 return;
623
624 size_t bcslen = offsetof(btrfs_create_snapshot, name[0]) + (file.length() * sizeof(WCHAR));
625 bcs = (btrfs_create_snapshot*)malloc(bcslen);
626
627 bcs->readonly = false;
628 bcs->posix = false;
629 bcs->namelen = (uint16_t)(file.length() * sizeof(WCHAR));
630 memcpy(bcs->name, file.c_str(), bcs->namelen);
631 bcs->subvol = src;
632
633 NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, (ULONG)bcslen, nullptr, 0);
634 }
635
CreateSnapshotW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)636 extern "C" void CALLBACK CreateSnapshotW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
637 vector<wstring> args;
638
639 command_line_to_args(lpszCmdLine, args);
640
641 if (args.size() >= 2)
642 create_snapshot2(args[0], args[1]);
643 }
644
command_line_to_args(LPWSTR cmdline,vector<wstring> & args)645 void command_line_to_args(LPWSTR cmdline, vector<wstring>& args) {
646 LPWSTR* l;
647 int num_args;
648
649 args.clear();
650
651 l = CommandLineToArgvW(cmdline, &num_args);
652
653 if (!l)
654 return;
655
656 try {
657 args.reserve(num_args);
658
659 for (unsigned int i = 0; i < (unsigned int)num_args; i++) {
660 args.push_back(l[i]);
661 }
662 } catch (...) {
663 LocalFree(l);
664 throw;
665 }
666
667 LocalFree(l);
668 }
669
utf16_to_utf8(const wstring_view & utf16)670 static string utf16_to_utf8(const wstring_view& utf16) {
671 string utf8;
672 char* buf;
673
674 if (utf16.empty())
675 return "";
676
677 auto utf8len = WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast<int>(utf16.length()), nullptr, 0, nullptr, nullptr);
678
679 if (utf8len == 0)
680 throw last_error(GetLastError());
681
682 buf = (char*)malloc(utf8len + sizeof(char));
683
684 if (!buf)
685 throw string_error(IDS_OUT_OF_MEMORY);
686
687 if (WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast<int>(utf16.length()), buf, utf8len, nullptr, nullptr) == 0) {
688 auto le = GetLastError();
689 free(buf);
690 throw last_error(le);
691 }
692
693 buf[utf8len] = 0;
694
695 utf8 = buf;
696
697 free(buf);
698
699 return utf8;
700 }
701
702 #ifdef _MSC_VER
703 #pragma warning(push)
704 #pragma warning(disable: 4996)
705 #endif
706
string_error(int resno,...)707 string_error::string_error(int resno, ...) {
708 wstring fmt, s;
709 int len;
710 va_list args;
711
712 if (!load_string(module, resno, fmt))
713 throw runtime_error("LoadString failed."); // FIXME
714
715 va_start(args, resno);
716 len = _vsnwprintf(nullptr, 0, fmt.c_str(), args);
717
718 if (len == 0)
719 s = L"";
720 else {
721 s.resize(len);
722 _vsnwprintf((wchar_t*)s.c_str(), len, fmt.c_str(), args);
723 }
724
725 va_end(args);
726
727 msg = utf16_to_utf8(s);
728 }
729
730 #ifdef _MSC_VER
731 #pragma warning(pop)
732 #endif
733
utf8_to_utf16(const string_view & utf8)734 wstring utf8_to_utf16(const string_view& utf8) {
735 wstring ret;
736 WCHAR* buf;
737
738 if (utf8.empty())
739 return L"";
740
741 auto utf16len = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), (int)utf8.length(), nullptr, 0);
742
743 if (utf16len == 0)
744 throw last_error(GetLastError());
745
746 buf = (WCHAR*)malloc((utf16len + 1) * sizeof(WCHAR));
747
748 if (!buf)
749 throw string_error(IDS_OUT_OF_MEMORY);
750
751 if (MultiByteToWideChar(CP_UTF8, 0, utf8.data(), (int)utf8.length(), buf, utf16len) == 0) {
752 auto le = GetLastError();
753 free(buf);
754 throw last_error(le);
755 }
756
757 buf[utf16len] = 0;
758
759 ret = buf;
760
761 free(buf);
762
763 return ret;
764 }
765
last_error(DWORD errnum)766 last_error::last_error(DWORD errnum) {
767 WCHAR* buf;
768
769 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
770 errnum, 0, (WCHAR*)&buf, 0, nullptr) == 0)
771 throw runtime_error("FormatMessage failed");
772
773 try {
774 msg = utf16_to_utf8(buf);
775 } catch (...) {
776 LocalFree(buf);
777 throw;
778 }
779
780 LocalFree(buf);
781 }
782
error_message(HWND hwnd,const char * msg)783 void error_message(HWND hwnd, const char* msg) {
784 wstring title;
785
786 load_string(module, IDS_ERROR, title);
787
788 auto wmsg = utf8_to_utf16(msg);
789
790 MessageBoxW(hwnd, wmsg.c_str(), title.c_str(), MB_ICONERROR);
791 }
792
ntstatus_error(NTSTATUS Status)793 ntstatus_error::ntstatus_error(NTSTATUS Status) : Status(Status) {
794 _RtlNtStatusToDosError RtlNtStatusToDosError;
795 HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
796 WCHAR* buf;
797
798 if (!ntdll)
799 throw runtime_error("Error loading ntdll.dll.");
800
801 try {
802 RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(ntdll, "RtlNtStatusToDosError");
803
804 if (!RtlNtStatusToDosError)
805 throw runtime_error("Error loading RtlNtStatusToDosError in ntdll.dll.");
806
807 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
808 RtlNtStatusToDosError(Status), 0, (WCHAR*)&buf, 0, nullptr) == 0)
809 throw runtime_error("FormatMessage failed");
810
811 try {
812 msg = utf16_to_utf8(buf);
813 } catch (...) {
814 LocalFree(buf);
815 throw;
816 }
817
818 LocalFree(buf);
819 } catch (...) {
820 FreeLibrary(ntdll);
821 throw;
822 }
823
824 FreeLibrary(ntdll);
825 }
826