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 #define ISOLATION_AWARE_ENABLED 1 19 #define STRSAFE_NO_DEPRECATE 20 21 #include "shellext.h" 22 #ifndef __REACTOS__ 23 #include <windows.h> 24 #include <strsafe.h> 25 #include <winternl.h> 26 #else 27 #define WIN32_NO_STATUS 28 #include <windef.h> 29 #include <winbase.h> 30 #include <strsafe.h> 31 #include <ndk/iofuncs.h> 32 #include <ndk/iotypes.h> 33 #endif 34 35 #define NO_SHLWAPI_STRFCNS 36 #include <shlwapi.h> 37 #include <uxtheme.h> 38 39 #include "volpropsheet.h" 40 #include "resource.h" 41 #ifndef __REACTOS__ 42 #include "mountmgr.h" 43 #else 44 #include "mountmgr_local.h" 45 #endif 46 47 #ifndef __REACTOS__ 48 static const NTSTATUS STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034; 49 #endif 50 51 HRESULT __stdcall BtrfsVolPropSheet::QueryInterface(REFIID riid, void **ppObj) { 52 if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) { 53 *ppObj = static_cast<IShellPropSheetExt*>(this); 54 AddRef(); 55 return S_OK; 56 } else if (riid == IID_IShellExtInit) { 57 *ppObj = static_cast<IShellExtInit*>(this); 58 AddRef(); 59 return S_OK; 60 } 61 62 *ppObj = nullptr; 63 return E_NOINTERFACE; 64 } 65 66 HRESULT __stdcall BtrfsVolPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) { 67 ULONG num_files; 68 FORMATETC format = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 69 HDROP hdrop; 70 WCHAR fnbuf[MAX_PATH]; 71 72 if (pidlFolder) 73 return E_FAIL; 74 75 if (!pdtobj) 76 return E_FAIL; 77 78 stgm.tymed = TYMED_HGLOBAL; 79 80 if (FAILED(pdtobj->GetData(&format, &stgm))) 81 return E_INVALIDARG; 82 83 stgm_set = true; 84 85 hdrop = (HDROP)GlobalLock(stgm.hGlobal); 86 87 if (!hdrop) { 88 ReleaseStgMedium(&stgm); 89 stgm_set = false; 90 return E_INVALIDARG; 91 } 92 93 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0); 94 95 if (num_files > 1) { 96 GlobalUnlock(hdrop); 97 return E_FAIL; 98 } 99 100 if (DragQueryFileW((HDROP)stgm.hGlobal, 0, fnbuf, sizeof(fnbuf) / sizeof(WCHAR))) { 101 fn = fnbuf; 102 103 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 104 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 105 106 if (h != INVALID_HANDLE_VALUE) { 107 NTSTATUS Status; 108 IO_STATUS_BLOCK iosb; 109 ULONG devsize, i; 110 111 i = 0; 112 devsize = 1024; 113 114 devices = (btrfs_device*)malloc(devsize); 115 116 while (true) { 117 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize); 118 if (Status == STATUS_BUFFER_OVERFLOW) { 119 if (i < 8) { 120 devsize += 1024; 121 122 free(devices); 123 devices = (btrfs_device*)malloc(devsize); 124 125 i++; 126 } else { 127 GlobalUnlock(hdrop); 128 return E_FAIL; 129 } 130 } else 131 break; 132 } 133 134 if (!NT_SUCCESS(Status)) { 135 GlobalUnlock(hdrop); 136 return E_FAIL; 137 } 138 139 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_UUID, nullptr, 0, &uuid, sizeof(BTRFS_UUID)); 140 uuid_set = NT_SUCCESS(Status); 141 142 ignore = false; 143 balance = new BtrfsBalance(fn); 144 } else { 145 GlobalUnlock(hdrop); 146 return E_FAIL; 147 } 148 } else { 149 GlobalUnlock(hdrop); 150 return E_FAIL; 151 } 152 153 GlobalUnlock(hdrop); 154 155 return S_OK; 156 } 157 158 typedef struct { 159 uint64_t dev_id; 160 wstring name; 161 uint64_t alloc; 162 uint64_t size; 163 } dev; 164 165 void BtrfsVolPropSheet::FormatUsage(HWND hwndDlg, wstring& s, btrfs_usage* usage) { 166 uint8_t i, j; 167 uint64_t num_devs, dev_size, dev_alloc, data_size, data_alloc, metadata_size, metadata_alloc; 168 btrfs_device* bd; 169 vector<dev> devs; 170 btrfs_usage* bue; 171 wstring t, u, v; 172 173 static const uint64_t types[] = { BLOCK_FLAG_DATA, BLOCK_FLAG_DATA | BLOCK_FLAG_METADATA, BLOCK_FLAG_METADATA, BLOCK_FLAG_SYSTEM }; 174 static const ULONG typestrings[] = { IDS_USAGE_DATA, IDS_USAGE_MIXED, IDS_USAGE_METADATA, IDS_USAGE_SYSTEM }; 175 static const uint64_t duptypes[] = { 0, BLOCK_FLAG_DUPLICATE, BLOCK_FLAG_RAID0, BLOCK_FLAG_RAID1, BLOCK_FLAG_RAID10, BLOCK_FLAG_RAID5, 176 BLOCK_FLAG_RAID6, BLOCK_FLAG_RAID1C3, BLOCK_FLAG_RAID1C4 }; 177 static const ULONG dupstrings[] = { IDS_SINGLE, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID10, IDS_RAID5, IDS_RAID6, IDS_RAID1C3, IDS_RAID1C4 }; 178 179 static const uint64_t raid_types = BLOCK_FLAG_DUPLICATE | BLOCK_FLAG_RAID0 | BLOCK_FLAG_RAID1 | BLOCK_FLAG_RAID10 | BLOCK_FLAG_RAID5 | 180 BLOCK_FLAG_RAID6 | BLOCK_FLAG_RAID1C3 | BLOCK_FLAG_RAID1C4; 181 182 s = L""; 183 184 num_devs = 0; 185 bd = devices; 186 187 while (true) { 188 num_devs++; 189 190 if (bd->next_entry > 0) 191 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 192 else 193 break; 194 } 195 196 bd = devices; 197 198 dev_size = 0; 199 200 while (true) { 201 dev d; 202 203 if (bd->missing) { 204 if (!load_string(module, IDS_MISSING, d.name)) 205 throw last_error(GetLastError()); 206 } else if (bd->device_number == 0xffffffff) 207 d.name = wstring(bd->name, bd->namelen / sizeof(WCHAR)); 208 else if (bd->partition_number == 0) { 209 if (!load_string(module, IDS_DISK_NUM, u)) 210 throw last_error(GetLastError()); 211 212 wstring_sprintf(d.name, u, bd->device_number); 213 } else { 214 if (!load_string(module, IDS_DISK_PART_NUM, u)) 215 throw last_error(GetLastError()); 216 217 wstring_sprintf(d.name, u, bd->device_number, bd->partition_number); 218 } 219 220 d.dev_id = bd->dev_id; 221 d.alloc = 0; 222 d.size = bd->size; 223 224 devs.push_back(d); 225 226 dev_size += bd->size; 227 228 if (bd->next_entry > 0) 229 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 230 else 231 break; 232 } 233 234 dev_alloc = 0; 235 data_size = data_alloc = 0; 236 metadata_size = metadata_alloc = 0; 237 238 bue = usage; 239 while (true) { 240 for (uint64_t k = 0; k < bue->num_devices; k++) { 241 dev_alloc += bue->devices[k].alloc; 242 243 if (bue->type & BLOCK_FLAG_DATA) { 244 data_alloc += bue->devices[k].alloc; 245 } 246 247 if (bue->type & BLOCK_FLAG_METADATA) { 248 metadata_alloc += bue->devices[k].alloc; 249 } 250 } 251 252 if (bue->type & BLOCK_FLAG_DATA) 253 data_size += bue->size; 254 255 if (bue->type & BLOCK_FLAG_METADATA) 256 metadata_size += bue->size; 257 258 if (bue->next_entry > 0) 259 bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry); 260 else 261 break; 262 } 263 264 // device size 265 266 if (!load_string(module, IDS_USAGE_DEV_SIZE, u)) 267 throw last_error(GetLastError()); 268 269 format_size(dev_size, v, false); 270 271 wstring_sprintf(t, u, v.c_str()); 272 273 s += t + L"\r\n"; 274 275 // device allocated 276 277 if (!load_string(module, IDS_USAGE_DEV_ALLOC, u)) 278 throw last_error(GetLastError()); 279 280 format_size(dev_alloc, v, false); 281 282 wstring_sprintf(t, u, v.c_str()); 283 284 #ifndef __REACTOS__ 285 s += t + L"\r\n"s; 286 #else 287 s += t + L"\r\n"; 288 #endif 289 290 // device unallocated 291 292 if (!load_string(module, IDS_USAGE_DEV_UNALLOC, u)) 293 throw last_error(GetLastError()); 294 295 format_size(dev_size - dev_alloc, v, false); 296 297 wstring_sprintf(t, u, v.c_str()); 298 299 #ifndef __REACTOS__ 300 s += t + L"\r\n"s; 301 #else 302 s += t + L"\r\n"; 303 #endif 304 305 // data ratio 306 307 if (data_alloc > 0) { 308 if (!load_string(module, IDS_USAGE_DATA_RATIO, u)) 309 throw last_error(GetLastError()); 310 311 wstring_sprintf(t, u, (float)data_alloc / (float)data_size); 312 313 #ifndef __REACTOS__ 314 s += t + L"\r\n"s; 315 #else 316 s += t + L"\r\n"; 317 #endif 318 } 319 320 // metadata ratio 321 322 if (!load_string(module, IDS_USAGE_METADATA_RATIO, u)) 323 throw last_error(GetLastError()); 324 325 wstring_sprintf(t, u, (float)metadata_alloc / (float)metadata_size); 326 327 s += t + L"\r\n\r\n"; 328 329 for (i = 0; i < sizeof(types) / sizeof(types[0]); i++) { 330 for (j = 0; j < sizeof(duptypes) / sizeof(duptypes[0]); j++) { 331 bue = usage; 332 333 while (true) { 334 if ((bue->type & types[i]) == types[i] && ((duptypes[j] == 0 && (bue->type & raid_types) == 0) || bue->type & duptypes[j])) { 335 wstring sizestring, usedstring, typestring, dupstring; 336 337 if (bue->type & BLOCK_FLAG_DATA && bue->type & BLOCK_FLAG_METADATA && (types[i] == BLOCK_FLAG_DATA || types[i] == BLOCK_FLAG_METADATA)) 338 break; 339 340 if (!load_string(module, typestrings[i], typestring)) 341 throw last_error(GetLastError()); 342 343 if (!load_string(module, dupstrings[j], dupstring)) 344 throw last_error(GetLastError()); 345 346 format_size(bue->size, sizestring, false); 347 format_size(bue->used, usedstring, false); 348 349 wstring_sprintf(t, typestring, dupstring.c_str(), sizestring.c_str(), usedstring.c_str()); 350 351 s += t + L"\r\n"; 352 353 for (uint64_t k = 0; k < bue->num_devices; k++) { 354 bool found = false; 355 356 format_size(bue->devices[k].alloc, sizestring, false); 357 358 for (size_t l = 0; l < min((uint64_t)SIZE_MAX, num_devs); l++) { 359 if (devs[l].dev_id == bue->devices[k].dev_id) { 360 s += devs[l].name + L"\t" + sizestring + L"\r\n"; 361 362 devs[l].alloc += bue->devices[k].alloc; 363 364 found = true; 365 break; 366 } 367 } 368 369 if (!found) { 370 if (!load_string(module, IDS_UNKNOWN_DEVICE, typestring)) 371 throw last_error(GetLastError()); 372 373 wstring_sprintf(t, typestring, bue->devices[k].dev_id); 374 375 #ifndef __REACTOS__ 376 s += t + L"\t"s + sizestring + L"\r\n"s; 377 #else 378 s += t + L"\t" + sizestring + L"\r\n"; 379 #endif 380 } 381 } 382 383 s += L"\r\n"; 384 385 break; 386 } 387 388 if (bue->next_entry > 0) 389 bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry); 390 else 391 break; 392 } 393 } 394 } 395 396 if (!load_string(module, IDS_USAGE_UNALLOC, t)) 397 throw last_error(GetLastError()); 398 399 #ifndef __REACTOS__ 400 s += t + L"\r\n"s; 401 #else 402 s += t + L"\r\n"; 403 #endif 404 405 for (size_t k = 0; k < min((uint64_t)SIZE_MAX, num_devs); k++) { 406 wstring sizestring; 407 408 format_size(devs[k].size - devs[k].alloc, sizestring, false); 409 410 s += devs[k].name + L"\t" + sizestring + L"\r\n"; 411 } 412 } 413 414 void BtrfsVolPropSheet::RefreshUsage(HWND hwndDlg) { 415 wstring s; 416 btrfs_usage* usage; 417 418 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 419 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 420 421 if (h != INVALID_HANDLE_VALUE) { 422 NTSTATUS Status; 423 IO_STATUS_BLOCK iosb; 424 ULONG devsize, usagesize, i; 425 426 i = 0; 427 devsize = 1024; 428 429 devices = (btrfs_device*)malloc(devsize); 430 431 while (true) { 432 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize); 433 if (Status == STATUS_BUFFER_OVERFLOW) { 434 if (i < 8) { 435 devsize += 1024; 436 437 free(devices); 438 devices = (btrfs_device*)malloc(devsize); 439 440 i++; 441 } else 442 return; 443 } else 444 break; 445 } 446 447 if (!NT_SUCCESS(Status)) 448 return; 449 450 i = 0; 451 usagesize = 1024; 452 453 usage = (btrfs_usage*)malloc(usagesize); 454 455 while (true) { 456 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_USAGE, nullptr, 0, usage, usagesize); 457 if (Status == STATUS_BUFFER_OVERFLOW) { 458 if (i < 8) { 459 usagesize += 1024; 460 461 free(usage); 462 usage = (btrfs_usage*)malloc(usagesize); 463 464 i++; 465 } else 466 return; 467 } else 468 break; 469 } 470 471 if (!NT_SUCCESS(Status)) { 472 free(usage); 473 return; 474 } 475 476 ignore = false; 477 } else 478 return; 479 480 FormatUsage(hwndDlg, s, usage); 481 482 SetDlgItemTextW(hwndDlg, IDC_USAGE_BOX, s.c_str()); 483 484 free(usage); 485 } 486 487 INT_PTR CALLBACK BtrfsVolPropSheet::UsageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 488 try { 489 switch (uMsg) { 490 case WM_INITDIALOG: 491 { 492 wstring s; 493 int i; 494 ULONG usagesize; 495 NTSTATUS Status; 496 IO_STATUS_BLOCK iosb; 497 498 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); 499 500 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 501 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 502 503 if (h != INVALID_HANDLE_VALUE) { 504 btrfs_usage* usage; 505 506 i = 0; 507 usagesize = 1024; 508 509 usage = (btrfs_usage*)malloc(usagesize); 510 511 while (true) { 512 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_USAGE, nullptr, 0, usage, usagesize); 513 if (Status == STATUS_BUFFER_OVERFLOW) { 514 if (i < 8) { 515 usagesize += 1024; 516 517 free(usage); 518 usage = (btrfs_usage*)malloc(usagesize); 519 520 i++; 521 } else 522 break; 523 } else 524 break; 525 } 526 527 if (!NT_SUCCESS(Status)) { 528 free(usage); 529 break; 530 } 531 532 FormatUsage(hwndDlg, s, usage); 533 534 SetDlgItemTextW(hwndDlg, IDC_USAGE_BOX, s.c_str()); 535 536 free(usage); 537 } 538 539 break; 540 } 541 542 case WM_COMMAND: 543 switch (HIWORD(wParam)) { 544 case BN_CLICKED: 545 switch (LOWORD(wParam)) { 546 case IDOK: 547 case IDCANCEL: 548 EndDialog(hwndDlg, 0); 549 return true; 550 551 case IDC_USAGE_REFRESH: 552 RefreshUsage(hwndDlg); 553 return true; 554 } 555 break; 556 } 557 break; 558 } 559 } catch (const exception& e) { 560 error_message(hwndDlg, e.what()); 561 } 562 563 return false; 564 } 565 566 static INT_PTR CALLBACK stub_UsageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 567 BtrfsVolPropSheet* bvps; 568 569 if (uMsg == WM_INITDIALOG) { 570 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 571 bvps = (BtrfsVolPropSheet*)lParam; 572 } else { 573 bvps = (BtrfsVolPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 574 } 575 576 if (bvps) 577 return bvps->UsageDlgProc(hwndDlg, uMsg, wParam, lParam); 578 else 579 return false; 580 } 581 582 void BtrfsVolPropSheet::ShowUsage(HWND hwndDlg) { 583 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_VOL_USAGE), hwndDlg, stub_UsageDlgProc, (LPARAM)this); 584 } 585 586 static void add_lv_column(HWND list, int string, int cx) { 587 LVCOLUMNW lvc; 588 wstring s; 589 590 if (!load_string(module, string, s)) 591 throw last_error(GetLastError()); 592 593 lvc.mask = LVCF_TEXT|LVCF_WIDTH; 594 lvc.pszText = (WCHAR*)s.c_str(); 595 lvc.cx = cx; 596 SendMessageW(list, LVM_INSERTCOLUMNW, 0, (LPARAM)&lvc); 597 } 598 599 static int CALLBACK lv_sort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { 600 if (lParam1 < lParam2) 601 return -1; 602 else if (lParam1 > lParam2) 603 return 1; 604 else 605 return 0; 606 } 607 608 static uint64_t find_dev_alloc(uint64_t dev_id, btrfs_usage* usage) { 609 btrfs_usage* bue; 610 uint64_t alloc; 611 612 alloc = 0; 613 614 bue = usage; 615 while (true) { 616 uint64_t k; 617 618 for (k = 0; k < bue->num_devices; k++) { 619 if (bue->devices[k].dev_id == dev_id) 620 alloc += bue->devices[k].alloc; 621 } 622 623 if (bue->next_entry > 0) 624 bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry); 625 else 626 break; 627 } 628 629 return alloc; 630 } 631 632 void BtrfsVolPropSheet::RefreshDevList(HWND devlist) { 633 NTSTATUS Status; 634 IO_STATUS_BLOCK iosb; 635 ULONG usagesize, devsize; 636 btrfs_usage* usage; 637 btrfs_device* bd; 638 int i; 639 uint64_t num_rw_devices; 640 { 641 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 642 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 643 644 if (h == INVALID_HANDLE_VALUE) 645 throw last_error(GetLastError()); 646 647 i = 0; 648 devsize = 1024; 649 650 if (devices) 651 free(devices); 652 653 devices = (btrfs_device*)malloc(devsize); 654 655 while (true) { 656 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize); 657 if (Status == STATUS_BUFFER_OVERFLOW) { 658 if (i < 8) { 659 devsize += 1024; 660 661 free(devices); 662 devices = (btrfs_device*)malloc(devsize); 663 664 i++; 665 } else 666 return; 667 } else 668 break; 669 } 670 671 if (!NT_SUCCESS(Status)) 672 return; 673 674 bd = devices; 675 676 i = 0; 677 usagesize = 1024; 678 679 usage = (btrfs_usage*)malloc(usagesize); 680 681 while (true) { 682 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_USAGE, nullptr, 0, usage, usagesize); 683 if (Status == STATUS_BUFFER_OVERFLOW) { 684 if (i < 8) { 685 usagesize += 1024; 686 687 free(usage); 688 usage = (btrfs_usage*)malloc(usagesize); 689 690 i++; 691 } else { 692 free(usage); 693 return; 694 } 695 } else 696 break; 697 } 698 699 if (!NT_SUCCESS(Status)) { 700 free(usage); 701 return; 702 } 703 } 704 705 SendMessageW(devlist, LVM_DELETEALLITEMS, 0, 0); 706 707 num_rw_devices = 0; 708 709 i = 0; 710 while (true) { 711 LVITEMW lvi; 712 wstring s, u; 713 uint64_t alloc; 714 715 // ID 716 717 RtlZeroMemory(&lvi, sizeof(LVITEMW)); 718 lvi.mask = LVIF_TEXT | LVIF_PARAM; 719 lvi.iItem = (int)SendMessageW(devlist, LVM_GETITEMCOUNT, 0, 0); 720 lvi.lParam = (LPARAM)bd->dev_id; 721 722 s = to_wstring(bd->dev_id); 723 lvi.pszText = (LPWSTR)s.c_str(); 724 725 SendMessageW(devlist, LVM_INSERTITEMW, 0, (LPARAM)&lvi); 726 727 // description 728 729 lvi.mask = LVIF_TEXT; 730 lvi.iSubItem = 1; 731 732 if (bd->missing) { 733 if (!load_string(module, IDS_MISSING, s)) 734 throw last_error(GetLastError()); 735 } else if (bd->device_number == 0xffffffff) 736 s = wstring(bd->name, bd->namelen / sizeof(WCHAR)); 737 else if (bd->partition_number == 0) { 738 if (!load_string(module, IDS_DISK_NUM, u)) 739 throw last_error(GetLastError()); 740 741 wstring_sprintf(s, u, bd->device_number); 742 } else { 743 if (!load_string(module, IDS_DISK_PART_NUM, u)) 744 throw last_error(GetLastError()); 745 746 wstring_sprintf(s, u, bd->device_number, bd->partition_number); 747 } 748 749 lvi.pszText = (LPWSTR)s.c_str(); 750 751 SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi); 752 753 // readonly 754 755 lvi.iSubItem = 2; 756 load_string(module, bd->readonly ? IDS_DEVLIST_READONLY_YES : IDS_DEVLIST_READONLY_NO, s); 757 lvi.pszText = (LPWSTR)s.c_str(); 758 SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi); 759 760 if (!bd->readonly) 761 num_rw_devices++; 762 763 // size 764 765 lvi.iSubItem = 3; 766 format_size(bd->size, s, false); 767 lvi.pszText = (LPWSTR)s.c_str(); 768 SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi); 769 770 // alloc 771 772 alloc = find_dev_alloc(bd->dev_id, usage); 773 774 lvi.iSubItem = 4; 775 format_size(alloc, s, false); 776 lvi.pszText = (LPWSTR)s.c_str(); 777 SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi); 778 779 // alloc % 780 781 wstring_sprintf(s, L"%1.1f%%", (float)alloc * 100.0f / (float)bd->size); 782 lvi.iSubItem = 5; 783 lvi.pszText = (LPWSTR)s.c_str(); 784 SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi); 785 786 i++; 787 788 if (bd->next_entry > 0) 789 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 790 else 791 break; 792 } 793 794 free(usage); 795 796 SendMessageW(devlist, LVM_SORTITEMS, 0, (LPARAM)lv_sort); 797 798 EnableWindow(GetDlgItem(GetParent(devlist), IDC_DEVICE_ADD), num_rw_devices > 0); 799 EnableWindow(GetDlgItem(GetParent(devlist), IDC_DEVICE_REMOVE), num_rw_devices > 1); 800 } 801 802 void BtrfsVolPropSheet::ResetStats(HWND hwndDlg) { 803 wstring t, sel; 804 WCHAR modfn[MAX_PATH]; 805 SHELLEXECUTEINFOW sei; 806 807 sel = to_wstring(stats_dev); 808 809 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 810 811 #ifndef __REACTOS__ 812 t = L"\""s + modfn + L"\",ResetStats " + fn + L"|" + sel; 813 #else 814 t = wstring(L"\"") + modfn + wstring(L"\",ResetStats ") + fn + wstring(L"|") + sel; 815 #endif 816 817 RtlZeroMemory(&sei, sizeof(sei)); 818 819 sei.cbSize = sizeof(sei); 820 sei.hwnd = hwndDlg; 821 sei.lpVerb = L"runas"; 822 sei.lpFile = L"rundll32.exe"; 823 sei.lpParameters = t.c_str(); 824 sei.nShow = SW_SHOW; 825 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 826 827 if (!ShellExecuteExW(&sei)) 828 throw last_error(GetLastError()); 829 830 WaitForSingleObject(sei.hProcess, INFINITE); 831 CloseHandle(sei.hProcess); 832 833 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 834 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 835 836 if (h != INVALID_HANDLE_VALUE) { 837 NTSTATUS Status; 838 IO_STATUS_BLOCK iosb; 839 ULONG devsize, i; 840 841 i = 0; 842 devsize = 1024; 843 844 free(devices); 845 devices = (btrfs_device*)malloc(devsize); 846 847 while (true) { 848 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize); 849 if (Status == STATUS_BUFFER_OVERFLOW) { 850 if (i < 8) { 851 devsize += 1024; 852 853 free(devices); 854 devices = (btrfs_device*)malloc(devsize); 855 856 i++; 857 } else 858 break; 859 } else 860 break; 861 } 862 } 863 864 EndDialog(hwndDlg, 0); 865 } 866 867 INT_PTR CALLBACK BtrfsVolPropSheet::StatsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 868 try { 869 switch (uMsg) { 870 case WM_INITDIALOG: 871 { 872 WCHAR s[255]; 873 wstring t; 874 btrfs_device *bd, *dev = nullptr; 875 int i; 876 877 static int stat_ids[] = { IDC_WRITE_ERRS, IDC_READ_ERRS, IDC_FLUSH_ERRS, IDC_CORRUPTION_ERRS, IDC_GENERATION_ERRS }; 878 879 bd = devices; 880 881 while (true) { 882 if (bd->dev_id == stats_dev) { 883 dev = bd; 884 break; 885 } 886 887 if (bd->next_entry > 0) 888 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 889 else 890 break; 891 } 892 893 if (!dev) { 894 EndDialog(hwndDlg, 0); 895 throw string_error(IDS_CANNOT_FIND_DEVICE); 896 } 897 898 GetDlgItemTextW(hwndDlg, IDC_DEVICE_ID, s, sizeof(s) / sizeof(WCHAR)); 899 900 wstring_sprintf(t, s, dev->dev_id); 901 902 SetDlgItemTextW(hwndDlg, IDC_DEVICE_ID, t.c_str()); 903 904 for (i = 0; i < 5; i++) { 905 GetDlgItemTextW(hwndDlg, stat_ids[i], s, sizeof(s) / sizeof(WCHAR)); 906 907 wstring_sprintf(t, s, dev->stats[i]); 908 909 SetDlgItemTextW(hwndDlg, stat_ids[i], t.c_str()); 910 } 911 912 SendMessageW(GetDlgItem(hwndDlg, IDC_RESET_STATS), BCM_SETSHIELD, 0, true); 913 EnableWindow(GetDlgItem(hwndDlg, IDC_RESET_STATS), !readonly); 914 915 break; 916 } 917 918 case WM_COMMAND: 919 switch (HIWORD(wParam)) { 920 case BN_CLICKED: 921 switch (LOWORD(wParam)) { 922 case IDOK: 923 case IDCANCEL: 924 EndDialog(hwndDlg, 0); 925 return true; 926 927 case IDC_RESET_STATS: 928 ResetStats(hwndDlg); 929 return true; 930 } 931 break; 932 } 933 break; 934 } 935 } catch (const exception& e) { 936 error_message(hwndDlg, e.what()); 937 } 938 939 return false; 940 } 941 942 static INT_PTR CALLBACK stub_StatsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 943 BtrfsVolPropSheet* bvps; 944 945 if (uMsg == WM_INITDIALOG) { 946 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 947 bvps = (BtrfsVolPropSheet*)lParam; 948 } else { 949 bvps = (BtrfsVolPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 950 } 951 952 if (bvps) 953 return bvps->StatsDlgProc(hwndDlg, uMsg, wParam, lParam); 954 else 955 return false; 956 } 957 958 void BtrfsVolPropSheet::ShowStats(HWND hwndDlg, uint64_t devid) { 959 stats_dev = devid; 960 961 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DEVICE_STATS), hwndDlg, stub_StatsDlgProc, (LPARAM)this); 962 } 963 964 INT_PTR CALLBACK BtrfsVolPropSheet::DeviceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 965 try { 966 switch (uMsg) { 967 case WM_INITDIALOG: 968 { 969 HWND devlist; 970 RECT rect; 971 ULONG w; 972 973 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); 974 975 devlist = GetDlgItem(hwndDlg, IDC_DEVLIST); 976 977 GetClientRect(devlist, &rect); 978 w = rect.right - rect.left; 979 980 add_lv_column(devlist, IDS_DEVLIST_ALLOC_PC, w * 5 / 44); 981 add_lv_column(devlist, IDS_DEVLIST_ALLOC, w * 6 / 44); 982 add_lv_column(devlist, IDS_DEVLIST_SIZE, w * 6 / 44); 983 add_lv_column(devlist, IDS_DEVLIST_READONLY, w * 7 / 44); 984 add_lv_column(devlist, IDS_DEVLIST_DESC, w * 16 / 44); 985 add_lv_column(devlist, IDS_DEVLIST_ID, w * 4 / 44); 986 987 SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_ADD), BCM_SETSHIELD, 0, true); 988 SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_REMOVE), BCM_SETSHIELD, 0, true); 989 SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_RESIZE), BCM_SETSHIELD, 0, true); 990 991 RefreshDevList(devlist); 992 993 break; 994 } 995 996 case WM_COMMAND: 997 switch (HIWORD(wParam)) { 998 case BN_CLICKED: 999 switch (LOWORD(wParam)) { 1000 case IDOK: 1001 case IDCANCEL: 1002 KillTimer(hwndDlg, 1); 1003 EndDialog(hwndDlg, 0); 1004 return true; 1005 1006 case IDC_DEVICE_ADD: 1007 { 1008 wstring t; 1009 WCHAR modfn[MAX_PATH]; 1010 SHELLEXECUTEINFOW sei; 1011 1012 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 1013 1014 #ifndef __REACTOS__ 1015 t = L"\""s + modfn + L"\",AddDevice "s + fn; 1016 #else 1017 t = wstring(L"\"") + modfn + wstring(L"\",AddDevice ") + fn; 1018 #endif 1019 1020 RtlZeroMemory(&sei, sizeof(sei)); 1021 1022 sei.cbSize = sizeof(sei); 1023 sei.hwnd = hwndDlg; 1024 sei.lpVerb = L"runas"; 1025 sei.lpFile = L"rundll32.exe"; 1026 sei.lpParameters = t.c_str(); 1027 sei.nShow = SW_SHOW; 1028 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 1029 1030 if (!ShellExecuteExW(&sei)) 1031 throw last_error(GetLastError()); 1032 1033 WaitForSingleObject(sei.hProcess, INFINITE); 1034 CloseHandle(sei.hProcess); 1035 1036 RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST)); 1037 1038 return true; 1039 } 1040 1041 case IDC_DEVICE_REFRESH: 1042 RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST)); 1043 return true; 1044 1045 case IDC_DEVICE_SHOW_STATS: 1046 { 1047 WCHAR sel[MAX_PATH]; 1048 HWND devlist; 1049 LVITEMW lvi; 1050 1051 devlist = GetDlgItem(hwndDlg, IDC_DEVLIST); 1052 1053 auto index = SendMessageW(devlist, LVM_GETNEXTITEM, -1, LVNI_SELECTED); 1054 1055 if (index == -1) 1056 return true; 1057 1058 RtlZeroMemory(&lvi, sizeof(LVITEMW)); 1059 lvi.mask = LVIF_TEXT; 1060 lvi.iItem = (int)index; 1061 lvi.iSubItem = 0; 1062 lvi.pszText = sel; 1063 lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR); 1064 SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi); 1065 1066 ShowStats(hwndDlg, _wtoi(sel)); 1067 return true; 1068 } 1069 1070 case IDC_DEVICE_REMOVE: 1071 { 1072 wstring t, mess, mess2, title; 1073 WCHAR modfn[MAX_PATH], sel[MAX_PATH], sel2[MAX_PATH]; 1074 HWND devlist; 1075 SHELLEXECUTEINFOW sei; 1076 LVITEMW lvi; 1077 1078 devlist = GetDlgItem(hwndDlg, IDC_DEVLIST); 1079 1080 auto index = SendMessageW(devlist, LVM_GETNEXTITEM, -1, LVNI_SELECTED); 1081 1082 if (index == -1) 1083 return true; 1084 1085 RtlZeroMemory(&lvi, sizeof(LVITEMW)); 1086 lvi.mask = LVIF_TEXT; 1087 lvi.iItem = (int)index; 1088 lvi.iSubItem = 0; 1089 lvi.pszText = sel; 1090 lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR); 1091 SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi); 1092 1093 lvi.iSubItem = 1; 1094 lvi.pszText = sel2; 1095 lvi.cchTextMax = sizeof(sel2) / sizeof(WCHAR); 1096 SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi); 1097 1098 if (!load_string(module, IDS_REMOVE_DEVICE_CONFIRMATION, mess)) 1099 throw last_error(GetLastError()); 1100 1101 wstring_sprintf(mess2, mess, sel, sel2); 1102 1103 if (!load_string(module, IDS_CONFIRMATION_TITLE, title)) 1104 throw last_error(GetLastError()); 1105 1106 if (MessageBoxW(hwndDlg, mess2.c_str(), title.c_str(), MB_YESNO) != IDYES) 1107 return true; 1108 1109 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 1110 1111 #ifndef __REACTOS__ 1112 t = L"\""s + modfn + L"\",RemoveDevice "s + fn + L"|"s + sel; 1113 #else 1114 t = wstring(L"\"") + modfn + wstring(L"\",RemoveDevice ") + fn + wstring(L"|") + sel; 1115 #endif 1116 1117 RtlZeroMemory(&sei, sizeof(sei)); 1118 1119 sei.cbSize = sizeof(sei); 1120 sei.hwnd = hwndDlg; 1121 sei.lpVerb = L"runas"; 1122 sei.lpFile = L"rundll32.exe"; 1123 sei.lpParameters = t.c_str(); 1124 sei.nShow = SW_SHOW; 1125 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 1126 1127 if (!ShellExecuteExW(&sei)) 1128 throw last_error(GetLastError()); 1129 1130 WaitForSingleObject(sei.hProcess, INFINITE); 1131 CloseHandle(sei.hProcess); 1132 1133 RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST)); 1134 1135 return true; 1136 } 1137 1138 case IDC_DEVICE_RESIZE: 1139 { 1140 HWND devlist; 1141 LVITEMW lvi; 1142 wstring t; 1143 WCHAR modfn[MAX_PATH], sel[100]; 1144 SHELLEXECUTEINFOW sei; 1145 1146 devlist = GetDlgItem(hwndDlg, IDC_DEVLIST); 1147 1148 auto index = SendMessageW(devlist, LVM_GETNEXTITEM, -1, LVNI_SELECTED); 1149 1150 if (index == -1) 1151 return true; 1152 1153 RtlZeroMemory(&lvi, sizeof(LVITEMW)); 1154 lvi.mask = LVIF_TEXT; 1155 lvi.iItem = (int)index; 1156 lvi.iSubItem = 0; 1157 lvi.pszText = sel; 1158 lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR); 1159 SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi); 1160 1161 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 1162 1163 #ifndef __REACTOS__ 1164 t = L"\""s + modfn + L"\",ResizeDevice "s + fn + L"|"s + sel; 1165 #else 1166 t = wstring(L"\"") + modfn + wstring(L"\",ResizeDevice ") + fn + wstring(L"|") + sel; 1167 #endif 1168 1169 RtlZeroMemory(&sei, sizeof(sei)); 1170 1171 sei.cbSize = sizeof(sei); 1172 sei.hwnd = hwndDlg; 1173 sei.lpVerb = L"runas"; 1174 sei.lpFile = L"rundll32.exe"; 1175 sei.lpParameters = t.c_str(); 1176 sei.nShow = SW_SHOW; 1177 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 1178 1179 if (!ShellExecuteExW(&sei)) 1180 throw last_error(GetLastError()); 1181 1182 WaitForSingleObject(sei.hProcess, INFINITE); 1183 CloseHandle(sei.hProcess); 1184 1185 RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST)); 1186 } 1187 } 1188 break; 1189 } 1190 break; 1191 1192 case WM_NOTIFY: 1193 switch (((LPNMHDR)lParam)->code) { 1194 case LVN_ITEMCHANGED: 1195 { 1196 NMLISTVIEW* nmv = (NMLISTVIEW*)lParam; 1197 1198 EnableWindow(GetDlgItem(hwndDlg, IDC_DEVICE_SHOW_STATS), nmv->uNewState & LVIS_SELECTED); 1199 1200 if (nmv->uNewState & LVIS_SELECTED && !readonly) { 1201 HWND devlist; 1202 btrfs_device* bd; 1203 bool device_readonly = false; 1204 LVITEMW lvi; 1205 WCHAR sel[MAX_PATH]; 1206 uint64_t devid; 1207 1208 devlist = GetDlgItem(hwndDlg, IDC_DEVLIST); 1209 1210 RtlZeroMemory(&lvi, sizeof(LVITEMW)); 1211 lvi.mask = LVIF_TEXT; 1212 lvi.iItem = nmv->iItem; 1213 lvi.iSubItem = 0; 1214 lvi.pszText = sel; 1215 lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR); 1216 SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi); 1217 devid = _wtoi(sel); 1218 1219 bd = devices; 1220 1221 while (true) { 1222 if (bd->dev_id == devid) { 1223 device_readonly = bd->readonly; 1224 break; 1225 } 1226 1227 if (bd->next_entry > 0) 1228 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 1229 else 1230 break; 1231 } 1232 1233 EnableWindow(GetDlgItem(hwndDlg, IDC_DEVICE_RESIZE), !device_readonly); 1234 } else 1235 EnableWindow(GetDlgItem(hwndDlg, IDC_DEVICE_RESIZE), false); 1236 1237 break; 1238 } 1239 } 1240 break; 1241 } 1242 } catch (const exception& e) { 1243 error_message(hwndDlg, e.what()); 1244 } 1245 1246 return false; 1247 } 1248 1249 static INT_PTR CALLBACK stub_DeviceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 1250 BtrfsVolPropSheet* bvps; 1251 1252 if (uMsg == WM_INITDIALOG) { 1253 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 1254 bvps = (BtrfsVolPropSheet*)lParam; 1255 } else { 1256 bvps = (BtrfsVolPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 1257 } 1258 1259 if (bvps) 1260 return bvps->DeviceDlgProc(hwndDlg, uMsg, wParam, lParam); 1261 else 1262 return false; 1263 } 1264 1265 void BtrfsVolPropSheet::ShowDevices(HWND hwndDlg) { 1266 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DEVICES), hwndDlg, stub_DeviceDlgProc, (LPARAM)this); 1267 } 1268 1269 void BtrfsVolPropSheet::ShowScrub(HWND hwndDlg) { 1270 wstring t; 1271 WCHAR modfn[MAX_PATH]; 1272 SHELLEXECUTEINFOW sei; 1273 1274 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 1275 1276 #ifndef __REACTOS__ 1277 t = L"\""s + modfn + L"\",ShowScrub "s + fn; 1278 #else 1279 t = wstring(L"\"") + modfn + wstring(L"\",ShowScrub ") + fn; 1280 #endif 1281 1282 RtlZeroMemory(&sei, sizeof(sei)); 1283 1284 sei.cbSize = sizeof(sei); 1285 sei.hwnd = hwndDlg; 1286 sei.lpVerb = L"runas"; 1287 sei.lpFile = L"rundll32.exe"; 1288 sei.lpParameters = t.c_str(); 1289 sei.nShow = SW_SHOW; 1290 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 1291 1292 if (!ShellExecuteExW(&sei)) 1293 throw last_error(GetLastError()); 1294 1295 WaitForSingleObject(sei.hProcess, INFINITE); 1296 CloseHandle(sei.hProcess); 1297 } 1298 1299 void BtrfsVolPropSheet::ShowChangeDriveLetter(HWND hwndDlg) { 1300 wstring t; 1301 WCHAR modfn[MAX_PATH]; 1302 SHELLEXECUTEINFOW sei; 1303 1304 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 1305 1306 #ifndef __REACTOS__ 1307 t = L"\""s + modfn + L"\",ShowChangeDriveLetter "s + fn; 1308 #else 1309 t = wstring(L"\"") + modfn + wstring(L"\",ShowChangeDriveLetter ") + fn; 1310 #endif 1311 1312 RtlZeroMemory(&sei, sizeof(sei)); 1313 1314 sei.cbSize = sizeof(sei); 1315 sei.hwnd = hwndDlg; 1316 sei.lpVerb = L"runas"; 1317 sei.lpFile = L"rundll32.exe"; 1318 sei.lpParameters = t.c_str(); 1319 sei.nShow = SW_SHOW; 1320 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 1321 1322 if (!ShellExecuteExW(&sei)) 1323 throw last_error(GetLastError()); 1324 1325 WaitForSingleObject(sei.hProcess, INFINITE); 1326 CloseHandle(sei.hProcess); 1327 } 1328 1329 static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 1330 try { 1331 switch (uMsg) { 1332 case WM_INITDIALOG: 1333 { 1334 PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)lParam; 1335 BtrfsVolPropSheet* bps = (BtrfsVolPropSheet*)psp->lParam; 1336 btrfs_device* bd; 1337 1338 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); 1339 1340 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps); 1341 1342 bps->readonly = true; 1343 bd = bps->devices; 1344 1345 while (true) { 1346 if (!bd->readonly) { 1347 bps->readonly = false; 1348 break; 1349 } 1350 1351 if (bd->next_entry > 0) 1352 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 1353 else 1354 break; 1355 } 1356 1357 if (bps->uuid_set) { 1358 WCHAR s[255]; 1359 wstring t; 1360 1361 GetDlgItemTextW(hwndDlg, IDC_UUID, s, sizeof(s) / sizeof(WCHAR)); 1362 1363 wstring_sprintf(t, s, bps->uuid.uuid[0], bps->uuid.uuid[1], bps->uuid.uuid[2], bps->uuid.uuid[3], bps->uuid.uuid[4], bps->uuid.uuid[5], 1364 bps->uuid.uuid[6], bps->uuid.uuid[7], bps->uuid.uuid[8], bps->uuid.uuid[9], bps->uuid.uuid[10], bps->uuid.uuid[11], 1365 bps->uuid.uuid[12], bps->uuid.uuid[13], bps->uuid.uuid[14], bps->uuid.uuid[15]); 1366 1367 SetDlgItemTextW(hwndDlg, IDC_UUID, t.c_str()); 1368 } else 1369 SetDlgItemTextW(hwndDlg, IDC_UUID, L""); 1370 1371 SendMessageW(GetDlgItem(hwndDlg, IDC_VOL_SCRUB), BCM_SETSHIELD, 0, true); 1372 SendMessageW(GetDlgItem(hwndDlg, IDC_VOL_CHANGE_DRIVE_LETTER), BCM_SETSHIELD, 0, true); 1373 1374 return false; 1375 } 1376 1377 case WM_NOTIFY: 1378 { 1379 switch (((LPNMHDR)lParam)->code) { 1380 case PSN_KILLACTIVE: 1381 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, false); 1382 break; 1383 } 1384 break; 1385 } 1386 1387 case WM_COMMAND: 1388 { 1389 BtrfsVolPropSheet* bps = (BtrfsVolPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 1390 1391 if (bps) { 1392 switch (HIWORD(wParam)) { 1393 case BN_CLICKED: { 1394 switch (LOWORD(wParam)) { 1395 case IDC_VOL_SHOW_USAGE: 1396 bps->ShowUsage(hwndDlg); 1397 break; 1398 1399 case IDC_VOL_BALANCE: 1400 bps->balance->ShowBalance(hwndDlg); 1401 break; 1402 1403 case IDC_VOL_DEVICES: 1404 bps->ShowDevices(hwndDlg); 1405 break; 1406 1407 case IDC_VOL_SCRUB: 1408 bps->ShowScrub(hwndDlg); 1409 break; 1410 1411 case IDC_VOL_CHANGE_DRIVE_LETTER: 1412 bps->ShowChangeDriveLetter(hwndDlg); 1413 break; 1414 } 1415 } 1416 } 1417 } 1418 1419 break; 1420 } 1421 } 1422 } catch (const exception& e) { 1423 error_message(hwndDlg, e.what()); 1424 } 1425 1426 return false; 1427 } 1428 1429 HRESULT __stdcall BtrfsVolPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) { 1430 try { 1431 PROPSHEETPAGEW psp; 1432 HPROPSHEETPAGE hPage; 1433 INITCOMMONCONTROLSEX icex; 1434 1435 if (ignore) 1436 return S_OK; 1437 1438 icex.dwSize = sizeof(icex); 1439 icex.dwICC = ICC_LINK_CLASS; 1440 1441 if (!InitCommonControlsEx(&icex)) 1442 throw string_error(IDS_INITCOMMONCONTROLSEX_FAILED); 1443 1444 psp.dwSize = sizeof(psp); 1445 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE; 1446 psp.hInstance = module; 1447 psp.pszTemplate = MAKEINTRESOURCEW(IDD_VOL_PROP_SHEET); 1448 psp.hIcon = 0; 1449 psp.pszTitle = MAKEINTRESOURCEW(IDS_VOL_PROP_SHEET_TITLE); 1450 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc; 1451 psp.pcRefParent = (UINT*)&objs_loaded; 1452 psp.pfnCallback = nullptr; 1453 psp.lParam = (LPARAM)this; 1454 1455 hPage = CreatePropertySheetPageW(&psp); 1456 1457 if (hPage) { 1458 if (pfnAddPage(hPage, lParam)) { 1459 this->AddRef(); 1460 return S_OK; 1461 } else 1462 DestroyPropertySheetPage(hPage); 1463 } else 1464 return E_OUTOFMEMORY; 1465 } catch (const exception& e) { 1466 error_message(nullptr, e.what()); 1467 } 1468 1469 return E_FAIL; 1470 } 1471 1472 HRESULT __stdcall BtrfsVolPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) { 1473 return S_OK; 1474 } 1475 1476 void BtrfsChangeDriveLetter::do_change(HWND hwndDlg) { 1477 unsigned int sel = (unsigned int)SendDlgItemMessageW(hwndDlg, IDC_DRIVE_LETTER_COMBO, CB_GETCURSEL, 0, 0); 1478 1479 if (sel < letters.size()) { 1480 wstring dd; 1481 1482 if (fn.length() == 3 && fn[1] == L':' && fn[2] == L'\\') { 1483 dd = L"\\DosDevices\\?:"; 1484 1485 dd[12] = fn[0]; 1486 } else 1487 #ifndef __REACTOS__ 1488 throw runtime_error("Volume path was not root of drive."); 1489 #else 1490 error_message(nullptr, "Volume path was not root of drive."); 1491 #endif 1492 1493 mountmgr mm; 1494 wstring dev_name; 1495 1496 { 1497 auto v = mm.query_points(dd); 1498 1499 if (v.empty()) 1500 #ifndef __REACTOS__ 1501 throw runtime_error("Error finding device name."); 1502 #else 1503 error_message(nullptr, "Error finding device name."); 1504 #endif 1505 1506 dev_name = v[0].device_name; 1507 } 1508 1509 wstring new_dd = L"\\DosDevices\\?:"; 1510 new_dd[12] = letters[sel]; 1511 1512 mm.delete_points(dd); 1513 1514 try { 1515 mm.create_point(new_dd, dev_name); 1516 } catch (...) { 1517 // if fails, try to recreate old symlink, so we're not left with no drive letter at all 1518 mm.create_point(dd, dev_name); 1519 throw; 1520 } 1521 } 1522 1523 EndDialog(hwndDlg, 1); 1524 } 1525 1526 INT_PTR BtrfsChangeDriveLetter::DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 1527 try { 1528 switch (uMsg) { 1529 case WM_INITDIALOG: 1530 { 1531 HWND cb = GetDlgItem(hwndDlg, IDC_DRIVE_LETTER_COMBO); 1532 1533 SendMessageW(cb, CB_RESETCONTENT, 0, 0); 1534 1535 mountmgr mm; 1536 wstring drv; 1537 1538 drv = L"\\DosDevices\\?:"; 1539 1540 for (wchar_t l = 'A'; l <= 'Z'; l++) { 1541 bool found = true; 1542 1543 drv[12] = l; 1544 1545 try { 1546 auto v = mm.query_points(drv); 1547 1548 if (v.empty()) 1549 found = false; 1550 } catch (const ntstatus_error& ntstatus) { 1551 if (ntstatus.Status == STATUS_OBJECT_NAME_NOT_FOUND) 1552 found = false; 1553 else 1554 throw; 1555 } 1556 1557 if (!found) { 1558 wstring str = L"?:"; 1559 1560 str[0] = l; 1561 letters.push_back(l); 1562 1563 SendMessageW(cb, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(str.c_str())); 1564 } 1565 } 1566 1567 break; 1568 } 1569 1570 case WM_COMMAND: 1571 switch (HIWORD(wParam)) { 1572 case BN_CLICKED: 1573 switch (LOWORD(wParam)) { 1574 case IDOK: 1575 do_change(hwndDlg); 1576 return true; 1577 1578 case IDCANCEL: 1579 EndDialog(hwndDlg, 0); 1580 return true; 1581 } 1582 break; 1583 } 1584 break; 1585 } 1586 } catch (const exception& e) { 1587 error_message(hwndDlg, e.what()); 1588 } 1589 1590 return false; 1591 } 1592 1593 static INT_PTR __stdcall dlg_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 1594 BtrfsChangeDriveLetter* bcdl; 1595 1596 if (uMsg == WM_INITDIALOG) { 1597 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 1598 bcdl = (BtrfsChangeDriveLetter*)lParam; 1599 } else 1600 bcdl = (BtrfsChangeDriveLetter*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 1601 1602 return bcdl->DlgProc(hwndDlg, uMsg, wParam, lParam); 1603 } 1604 1605 void BtrfsChangeDriveLetter::show() { 1606 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DRIVE_LETTER), hwnd, dlg_proc, (LPARAM)this); 1607 } 1608 1609 #ifdef __cplusplus 1610 extern "C" { 1611 #endif 1612 1613 void CALLBACK ResetStatsW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1614 try { 1615 win_handle token; 1616 NTSTATUS Status; 1617 TOKEN_PRIVILEGES tp; 1618 LUID luid; 1619 uint64_t devid; 1620 wstring cmdline, vol, dev; 1621 size_t pipe; 1622 IO_STATUS_BLOCK iosb; 1623 1624 set_dpi_aware(); 1625 1626 cmdline = lpszCmdLine; 1627 1628 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 1629 throw last_error(GetLastError()); 1630 1631 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 1632 throw last_error(GetLastError()); 1633 1634 tp.PrivilegeCount = 1; 1635 tp.Privileges[0].Luid = luid; 1636 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1637 1638 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 1639 throw last_error(GetLastError()); 1640 1641 pipe = cmdline.find(L"|"); 1642 1643 if (pipe == string::npos) 1644 return; 1645 1646 vol = cmdline.substr(0, pipe); 1647 dev = cmdline.substr(pipe + 1); 1648 1649 devid = _wtoi(dev.c_str()); 1650 if (devid == 0) 1651 return; 1652 1653 win_handle h = CreateFileW(vol.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 1654 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 1655 1656 if (h == INVALID_HANDLE_VALUE) 1657 throw last_error(GetLastError()); 1658 1659 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESET_STATS, &devid, sizeof(uint64_t), nullptr, 0); 1660 if (!NT_SUCCESS(Status)) 1661 throw ntstatus_error(Status); 1662 } catch (const exception& e) { 1663 error_message(hwnd, e.what()); 1664 } 1665 } 1666 1667 void CALLBACK ShowChangeDriveLetterW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1668 BtrfsChangeDriveLetter bcdl(hwnd, lpszCmdLine); 1669 1670 bcdl.show(); 1671 } 1672 1673 #ifdef __cplusplus 1674 } 1675 #endif 1676