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