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