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 STDAPI DllCanUnloadNow(void) { 247 return objs_loaded == 0 ? S_OK : S_FALSE; 248 } 249 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 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 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 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 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 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 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 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 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 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 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 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 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 544 STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine) { 545 if (bInstall) 546 return DllRegisterServer(); 547 else 548 return DllUnregisterServer(); 549 } 550 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 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 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 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 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 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 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 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 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 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 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 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