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