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
1494