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