xref: /reactos/dll/shellext/shellbtrfs/main.cpp (revision 4561998a)
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 
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 
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 
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 
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 
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 
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 
246 extern "C" STDAPI DllCanUnloadNow(void) {
247     return objs_loaded == 0 ? S_OK : S_FALSE;
248 }
249 
250 extern "C" 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 
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(), (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 
310 static void register_clsid(const GUID clsid, const WCHAR* description) {
311     WCHAR* clsidstring;
312     wstring inproc, progid, 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         progid = L"CLSID\\"s + clsidstring + L"\\ProgId"s;
321         clsidkeyname = L"CLSID\\"s + clsidstring;
322 #else
323         inproc = wstring(L"CLSID\\") + clsidstring + wstring(L"\\InprocServer32");
324         progid = wstring(L"CLSID\\") + clsidstring + wstring(L"\\ProgId");
325         clsidkeyname = wstring(L"CLSID\\") + clsidstring;
326 #endif
327 
328         write_reg_key(HKEY_CLASSES_ROOT, clsidkeyname, nullptr, description);
329 
330         GetModuleFileNameW(module, dllpath, sizeof(dllpath));
331 
332         write_reg_key(HKEY_CLASSES_ROOT, inproc, nullptr, dllpath);
333 
334         write_reg_key(HKEY_CLASSES_ROOT, inproc, L"ThreadingModel", L"Apartment");
335     } catch (...) {
336         CoTaskMemFree(clsidstring);
337         throw;
338     }
339 
340     CoTaskMemFree(clsidstring);
341 }
342 
343 static void unregister_clsid(const GUID clsid) {
344     WCHAR* clsidstring;
345 
346     StringFromCLSID(clsid, &clsidstring);
347 
348     try {
349         WCHAR clsidkeyname[MAX_PATH];
350 
351         wsprintfW(clsidkeyname, L"CLSID\\%s", clsidstring);
352 
353         LONG l = RegDeleteTreeW(HKEY_CLASSES_ROOT, clsidkeyname);
354 
355         if (l != ERROR_SUCCESS)
356             throw string_error(IDS_REGDELETETREE_FAILED, l);
357     } catch (...) {
358         CoTaskMemFree(clsidstring);
359         throw;
360     }
361 
362     CoTaskMemFree(clsidstring);
363 }
364 
365 static void reg_icon_overlay(const GUID clsid, const wstring& name) {
366     WCHAR* clsidstring;
367 
368     StringFromCLSID(clsid, &clsidstring);
369 
370     try {
371 #ifndef __REACTOS__
372         wstring path = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\"s + name;
373 #else
374         wstring path = wstring(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\") + name;
375 #endif
376 
377         write_reg_key(HKEY_LOCAL_MACHINE, path, nullptr, clsidstring);
378     } catch (...) {
379         CoTaskMemFree(clsidstring);
380         throw;
381     }
382 
383     CoTaskMemFree(clsidstring);
384 }
385 
386 static void unreg_icon_overlay(const wstring& name) {
387 #ifndef __REACTOS__
388     wstring path = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\"s + name;
389 #else
390     wstring path = wstring(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\") + name;
391 #endif
392 
393     LONG l = RegDeleteTreeW(HKEY_LOCAL_MACHINE, path.c_str());
394 
395     if (l != ERROR_SUCCESS)
396         throw string_error(IDS_REGDELETETREE_FAILED, l);
397 }
398 
399 static void reg_context_menu_handler(const GUID clsid, const wstring& filetype, const wstring& name) {
400     WCHAR* clsidstring;
401 
402     StringFromCLSID(clsid, &clsidstring);
403 
404     try {
405 #ifndef __REACTOS__
406         wstring path = filetype + L"\\ShellEx\\ContextMenuHandlers\\"s + name;
407 #else
408         wstring path = filetype + wstring(L"\\ShellEx\\ContextMenuHandlers\\") + name;
409 #endif
410 
411         write_reg_key(HKEY_CLASSES_ROOT, path, nullptr, clsidstring);
412     } catch (...) {
413         CoTaskMemFree(clsidstring);
414         throw;
415     }
416 }
417 
418 static void unreg_context_menu_handler(const wstring& filetype, const wstring& name) {
419 #ifndef __REACTOS__
420     wstring path = filetype + L"\\ShellEx\\ContextMenuHandlers\\"s + name;
421 #else
422     wstring path = filetype + wstring(L"\\ShellEx\\ContextMenuHandlers\\") + name;
423 #endif
424 
425     LONG l = RegDeleteTreeW(HKEY_CLASSES_ROOT, path.c_str());
426 
427     if (l != ERROR_SUCCESS)
428         throw string_error(IDS_REGDELETETREE_FAILED, l);
429 }
430 
431 static void reg_prop_sheet_handler(const GUID clsid, const wstring& filetype, const wstring& name) {
432     WCHAR* clsidstring;
433 
434     StringFromCLSID(clsid, &clsidstring);
435 
436     try {
437 #ifndef __REACTOS__
438         wstring path = filetype + L"\\ShellEx\\PropertySheetHandlers\\"s + name;
439 #else
440         wstring path = filetype + wstring(L"\\ShellEx\\PropertySheetHandlers\\") + name;
441 #endif
442 
443         write_reg_key(HKEY_CLASSES_ROOT, path, nullptr, clsidstring);
444     } catch (...) {
445         CoTaskMemFree(clsidstring);
446         throw;
447     }
448 }
449 
450 static void unreg_prop_sheet_handler(const wstring& filetype, const wstring& name) {
451 #ifndef __REACTOS__
452     wstring path = filetype + L"\\ShellEx\\PropertySheetHandlers\\"s + name;
453 #else
454     wstring path = filetype + wstring(L"\\ShellEx\\PropertySheetHandlers\\") + name;
455 #endif
456 
457     LONG l = RegDeleteTreeW(HKEY_CLASSES_ROOT, path.c_str());
458 
459     if (l != ERROR_SUCCESS)
460         throw string_error(IDS_REGDELETETREE_FAILED, l);
461 }
462 
463 extern "C" STDAPI DllRegisterServer(void) {
464     try {
465         register_clsid(CLSID_ShellBtrfsIconHandler, COM_DESCRIPTION_ICON_HANDLER);
466         register_clsid(CLSID_ShellBtrfsContextMenu, COM_DESCRIPTION_CONTEXT_MENU);
467         register_clsid(CLSID_ShellBtrfsPropSheet, COM_DESCRIPTION_PROP_SHEET);
468         register_clsid(CLSID_ShellBtrfsVolPropSheet, COM_DESCRIPTION_VOL_PROP_SHEET);
469 
470         reg_icon_overlay(CLSID_ShellBtrfsIconHandler, ICON_OVERLAY_NAME);
471 
472         reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Directory\\Background", ICON_OVERLAY_NAME);
473         reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Folder", ICON_OVERLAY_NAME);
474 
475         reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"Folder", ICON_OVERLAY_NAME);
476         reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"*", ICON_OVERLAY_NAME);
477         reg_prop_sheet_handler(CLSID_ShellBtrfsVolPropSheet, L"Drive", ICON_OVERLAY_NAME);
478     } catch (const exception& e) {
479         error_message(nullptr, e.what());
480         return E_FAIL;
481     }
482 
483     return S_OK;
484 }
485 
486 extern "C" STDAPI DllUnregisterServer(void) {
487     try {
488         unreg_prop_sheet_handler(L"Folder", ICON_OVERLAY_NAME);
489         unreg_prop_sheet_handler(L"*", ICON_OVERLAY_NAME);
490         unreg_prop_sheet_handler(L"Drive", ICON_OVERLAY_NAME);
491         unreg_context_menu_handler(L"Folder", ICON_OVERLAY_NAME);
492         unreg_context_menu_handler(L"Directory\\Background", ICON_OVERLAY_NAME);
493         unreg_icon_overlay(ICON_OVERLAY_NAME);
494 
495         unregister_clsid(CLSID_ShellBtrfsVolPropSheet);
496         unregister_clsid(CLSID_ShellBtrfsPropSheet);
497         unregister_clsid(CLSID_ShellBtrfsContextMenu);
498         unregister_clsid(CLSID_ShellBtrfsIconHandler);
499     } catch (const exception& e) {
500         error_message(nullptr, e.what());
501         return E_FAIL;
502     }
503 
504     return S_OK;
505 }
506 
507 extern "C" STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine) {
508     if (bInstall)
509         return DllRegisterServer();
510     else
511         return DllUnregisterServer();
512 }
513 
514 extern "C" BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) {
515     if (dwReason == DLL_PROCESS_ATTACH)
516         module = (HMODULE)hModule;
517 
518     return true;
519 }
520 
521 static void create_subvol(const wstring& fn) {
522     size_t found = fn.rfind(L"\\");
523     wstring path, file;
524     win_handle h;
525     ULONG bcslen;
526     btrfs_create_subvol* bcs;
527     IO_STATUS_BLOCK iosb;
528 
529     if (found == wstring::npos) {
530         path = L"";
531         file = fn;
532     } else {
533         path = fn.substr(0, found);
534         file = fn.substr(found + 1);
535     }
536     path += L"\\";
537 
538     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);
539 
540     if (h == INVALID_HANDLE_VALUE)
541         return;
542 
543     bcslen = offsetof(btrfs_create_subvol, name[0]) + (file.length() * sizeof(WCHAR));
544     bcs = (btrfs_create_subvol*)malloc(bcslen);
545 
546     bcs->readonly = false;
547     bcs->posix = false;
548     bcs->namelen = (uint16_t)(file.length() * sizeof(WCHAR));
549     memcpy(bcs->name, file.c_str(), bcs->namelen);
550 
551     NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, nullptr, 0);
552 }
553 
554 extern "C" void CALLBACK CreateSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
555     vector<wstring> args;
556 
557     command_line_to_args(lpszCmdLine, args);
558 
559     if (args.size() >= 1)
560         create_subvol(args[0]);
561 }
562 
563 static void create_snapshot2(const wstring& source, const wstring& fn) {
564     size_t found = fn.rfind(L"\\");
565     wstring path, file;
566     win_handle h, src;
567     ULONG bcslen;
568     btrfs_create_snapshot* bcs;
569     IO_STATUS_BLOCK iosb;
570 
571     if (found == wstring::npos) {
572         path = L"";
573         file = fn;
574     } else {
575         path = fn.substr(0, found);
576         file = fn.substr(found + 1);
577     }
578     path += L"\\";
579 
580     src = CreateFileW(source.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
581     if (src == INVALID_HANDLE_VALUE)
582         return;
583 
584     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);
585 
586     if (h == INVALID_HANDLE_VALUE)
587         return;
588 
589     bcslen = offsetof(btrfs_create_snapshot, name[0]) + (file.length() * sizeof(WCHAR));
590     bcs = (btrfs_create_snapshot*)malloc(bcslen);
591 
592     bcs->readonly = false;
593     bcs->posix = false;
594     bcs->namelen = (uint16_t)(file.length() * sizeof(WCHAR));
595     memcpy(bcs->name, file.c_str(), bcs->namelen);
596     bcs->subvol = src;
597 
598     NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, bcslen, nullptr, 0);
599 }
600 
601 extern "C" void CALLBACK CreateSnapshotW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
602     vector<wstring> args;
603 
604     command_line_to_args(lpszCmdLine, args);
605 
606     if (args.size() >= 2)
607         create_snapshot2(args[0], args[1]);
608 }
609 
610 void command_line_to_args(LPWSTR cmdline, vector<wstring> args) {
611     LPWSTR* l;
612     int num_args;
613 
614     args.clear();
615 
616     l = CommandLineToArgvW(cmdline, &num_args);
617 
618     if (!l)
619         return;
620 
621     try {
622         args.reserve(num_args);
623 
624         for (unsigned int i = 0; i < (unsigned int)num_args; i++) {
625             args.push_back(l[i]);
626         }
627     } catch (...) {
628         LocalFree(l);
629         throw;
630     }
631 
632     LocalFree(l);
633 }
634 
635 #ifdef _MSC_VER
636 #pragma warning(push)
637 #pragma warning(disable: 4996)
638 #endif
639 
640 string_error::string_error(int resno, ...) {
641     wstring fmt, s;
642     int len;
643     va_list args;
644 
645     if (!load_string(module, resno, fmt))
646         throw runtime_error("LoadString failed."); // FIXME
647 
648     va_start(args, resno);
649     len = _vsnwprintf(nullptr, 0, fmt.c_str(), args);
650 
651     if (len == 0)
652         s = L"";
653     else {
654         s.resize(len);
655         _vsnwprintf((wchar_t*)s.c_str(), len, fmt.c_str(), args);
656     }
657 
658     va_end(args);
659 
660     utf16_to_utf8(s, msg);
661 }
662 
663 #ifdef _MSC_VER
664 #pragma warning(pop)
665 #endif
666 
667 void utf8_to_utf16(const string& utf8, wstring& utf16) {
668     NTSTATUS Status;
669     ULONG utf16len;
670     WCHAR* buf;
671 
672     Status = RtlUTF8ToUnicodeN(nullptr, 0, &utf16len, utf8.c_str(), utf8.length());
673     if (!NT_SUCCESS(Status))
674         throw string_error(IDS_RECV_RTLUTF8TOUNICODEN_FAILED, Status, format_ntstatus(Status).c_str());
675 
676     buf = (WCHAR*)malloc(utf16len + sizeof(WCHAR));
677 
678     if (!buf)
679         throw string_error(IDS_OUT_OF_MEMORY);
680 
681     Status = RtlUTF8ToUnicodeN(buf, utf16len, &utf16len, utf8.c_str(), utf8.length());
682     if (!NT_SUCCESS(Status)) {
683         free(buf);
684         throw string_error(IDS_RECV_RTLUTF8TOUNICODEN_FAILED, Status, format_ntstatus(Status).c_str());
685     }
686 
687     buf[utf16len / sizeof(WCHAR)] = 0;
688 
689     utf16 = buf;
690 
691     free(buf);
692 }
693 
694 void utf16_to_utf8(const wstring& utf16, string& utf8) {
695     NTSTATUS Status;
696     ULONG utf8len;
697     char* buf;
698 
699     Status = RtlUnicodeToUTF8N(nullptr, 0, &utf8len, utf16.c_str(), utf16.length() * sizeof(WCHAR));
700     if (!NT_SUCCESS(Status))
701         throw string_error(IDS_RECV_RTLUNICODETOUTF8N_FAILED, Status, format_ntstatus(Status).c_str());
702 
703     buf = (char*)malloc(utf8len + sizeof(char));
704 
705     if (!buf)
706         throw string_error(IDS_OUT_OF_MEMORY);
707 
708     Status = RtlUnicodeToUTF8N(buf, utf8len, &utf8len, utf16.c_str(), utf16.length() * sizeof(WCHAR));
709     if (!NT_SUCCESS(Status)) {
710         free(buf);
711         throw string_error(IDS_RECV_RTLUNICODETOUTF8N_FAILED, Status, format_ntstatus(Status).c_str());
712     }
713 
714     buf[utf8len] = 0;
715 
716     utf8 = buf;
717 
718     free(buf);
719 }
720 
721 last_error::last_error(DWORD errnum) {
722     WCHAR* buf;
723 
724     if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
725         errnum, 0, (WCHAR*)&buf, 0, nullptr) == 0)
726         throw runtime_error("FormatMessage failed");
727 
728     try {
729         utf16_to_utf8(buf, msg);
730     } catch (...) {
731         LocalFree(buf);
732         throw;
733     }
734 
735     LocalFree(buf);
736 }
737 
738 void error_message(HWND hwnd, const char* msg) {
739     wstring title, wmsg;
740 
741     load_string(module, IDS_ERROR, title);
742 
743     utf8_to_utf16(msg, wmsg);
744 
745     MessageBoxW(hwnd, wmsg.c_str(), title.c_str(), MB_ICONERROR);
746 }
747 
748 ntstatus_error::ntstatus_error(NTSTATUS Status) {
749     _RtlNtStatusToDosError RtlNtStatusToDosError;
750     HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
751     WCHAR* buf;
752 
753     if (!ntdll)
754         throw runtime_error("Error loading ntdll.dll.");
755 
756     try {
757         RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(ntdll, "RtlNtStatusToDosError");
758 
759         if (!RtlNtStatusToDosError)
760             throw runtime_error("Error loading RtlNtStatusToDosError in ntdll.dll.");
761 
762         if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
763             RtlNtStatusToDosError(Status), 0, (WCHAR*)&buf, 0, nullptr) == 0)
764             throw runtime_error("FormatMessage failed");
765 
766         try {
767             utf16_to_utf8(buf, msg);
768         } catch (...) {
769             LocalFree(buf);
770             throw;
771         }
772 
773         LocalFree(buf);
774     } catch (...) {
775         FreeLibrary(ntdll);
776         throw;
777     }
778 
779     FreeLibrary(ntdll);
780 }
781 
782 #ifdef __REACTOS__
783 NTSTATUS NTAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
784                                  ULONG *utf8_bytes_written,
785                                  const WCHAR *uni_src, ULONG uni_bytes)
786 {
787     NTSTATUS status;
788     ULONG i;
789     ULONG written;
790     ULONG ch;
791     BYTE utf8_ch[4];
792     ULONG utf8_ch_len;
793 
794     if (!uni_src)
795         return STATUS_INVALID_PARAMETER_4;
796     if (!utf8_bytes_written)
797         return STATUS_INVALID_PARAMETER;
798     if (utf8_dest && uni_bytes % sizeof(WCHAR))
799         return STATUS_INVALID_PARAMETER_5;
800 
801     written = 0;
802     status = STATUS_SUCCESS;
803 
804     for (i = 0; i < uni_bytes / sizeof(WCHAR); i++)
805     {
806         /* decode UTF-16 into ch */
807         ch = uni_src[i];
808         if (ch >= 0xdc00 && ch <= 0xdfff)
809         {
810             ch = 0xfffd;
811             status = STATUS_SOME_NOT_MAPPED;
812         }
813         else if (ch >= 0xd800 && ch <= 0xdbff)
814         {
815             if (i + 1 < uni_bytes / sizeof(WCHAR))
816             {
817                 ch -= 0xd800;
818                 ch <<= 10;
819                 if (uni_src[i + 1] >= 0xdc00 && uni_src[i + 1] <= 0xdfff)
820                 {
821                     ch |= uni_src[i + 1] - 0xdc00;
822                     ch += 0x010000;
823                     i++;
824                 }
825                 else
826                 {
827                     ch = 0xfffd;
828                     status = STATUS_SOME_NOT_MAPPED;
829                 }
830             }
831             else
832             {
833                 ch = 0xfffd;
834                 status = STATUS_SOME_NOT_MAPPED;
835             }
836         }
837 
838         /* encode ch as UTF-8 */
839         if (ch < 0x80)
840         {
841             utf8_ch[0] = ch & 0x7f;
842             utf8_ch_len = 1;
843         }
844         else if (ch < 0x800)
845         {
846             utf8_ch[0] = 0xc0 | (ch >>  6 & 0x1f);
847             utf8_ch[1] = 0x80 | (ch >>  0 & 0x3f);
848             utf8_ch_len = 2;
849         }
850         else if (ch < 0x10000)
851         {
852             utf8_ch[0] = 0xe0 | (ch >> 12 & 0x0f);
853             utf8_ch[1] = 0x80 | (ch >>  6 & 0x3f);
854             utf8_ch[2] = 0x80 | (ch >>  0 & 0x3f);
855             utf8_ch_len = 3;
856         }
857         else if (ch < 0x200000)
858         {
859             utf8_ch[0] = 0xf0 | (ch >> 18 & 0x07);
860             utf8_ch[1] = 0x80 | (ch >> 12 & 0x3f);
861             utf8_ch[2] = 0x80 | (ch >>  6 & 0x3f);
862             utf8_ch[3] = 0x80 | (ch >>  0 & 0x3f);
863             utf8_ch_len = 4;
864         }
865 
866         if (!utf8_dest)
867         {
868             written += utf8_ch_len;
869             continue;
870         }
871 
872         if (utf8_bytes_max >= utf8_ch_len)
873         {
874             memcpy(utf8_dest, utf8_ch, utf8_ch_len);
875             utf8_dest += utf8_ch_len;
876             utf8_bytes_max -= utf8_ch_len;
877             written += utf8_ch_len;
878         }
879         else
880         {
881             utf8_bytes_max = 0;
882             status = STATUS_BUFFER_TOO_SMALL;
883         }
884     }
885 
886     *utf8_bytes_written = written;
887     return status;
888 }
889 #endif
890