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