[BTRFS][UBTRFS][SHELLBTRFS] Upgrade to 1.7.2
[reactos.git] / dll / shellext / shellbtrfs / volpropsheet.cpp
1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #define ISOLATION_AWARE_ENABLED 1
19 #define STRSAFE_NO_DEPRECATE
20
21 #include "shellext.h"
22 #ifndef __REACTOS__
23 #include <windows.h>
24 #include <strsafe.h>
25 #include <winternl.h>
26 #else
27 #define WIN32_NO_STATUS
28 #include <windef.h>
29 #include <winbase.h>
30 #include <strsafe.h>
31 #include <ndk/iofuncs.h>
32 #include <ndk/iotypes.h>
33 #endif
34
35 #define NO_SHLWAPI_STRFCNS
36 #include <shlwapi.h>
37 #include <uxtheme.h>
38
39 #include "volpropsheet.h"
40 #include "resource.h"
41 #ifndef __REACTOS__
42 #include "mountmgr.h"
43 #else
44 #include "mountmgr_local.h"
45 #endif
46
47 #ifndef __REACTOS__
48 static const NTSTATUS STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034;
49 #endif
50
51 HRESULT __stdcall BtrfsVolPropSheet::QueryInterface(REFIID riid, void **ppObj) {
52 if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
53 *ppObj = static_cast<IShellPropSheetExt*>(this);
54 AddRef();
55 return S_OK;
56 } else if (riid == IID_IShellExtInit) {
57 *ppObj = static_cast<IShellExtInit*>(this);
58 AddRef();
59 return S_OK;
60 }
61
62 *ppObj = nullptr;
63 return E_NOINTERFACE;
64 }
65
66 HRESULT __stdcall BtrfsVolPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
67 ULONG num_files;
68 FORMATETC format = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
69 HDROP hdrop;
70 WCHAR fnbuf[MAX_PATH];
71
72 if (pidlFolder)
73 return E_FAIL;
74
75 if (!pdtobj)
76 return E_FAIL;
77
78 stgm.tymed = TYMED_HGLOBAL;
79
80 if (FAILED(pdtobj->GetData(&format, &stgm)))
81 return E_INVALIDARG;
82
83 stgm_set = true;
84
85 hdrop = (HDROP)GlobalLock(stgm.hGlobal);
86
87 if (!hdrop) {
88 ReleaseStgMedium(&stgm);
89 stgm_set = false;
90 return E_INVALIDARG;
91 }
92
93 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
94
95 if (num_files > 1) {
96 GlobalUnlock(hdrop);
97 return E_FAIL;
98 }
99
100 if (DragQueryFileW((HDROP)stgm.hGlobal, 0, fnbuf, sizeof(fnbuf) / sizeof(MAX_PATH))) {
101 fn = fnbuf;
102
103 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
104 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
105
106 if (h != INVALID_HANDLE_VALUE) {
107 NTSTATUS Status;
108 IO_STATUS_BLOCK iosb;
109 ULONG devsize, i;
110
111 i = 0;
112 devsize = 1024;
113
114 devices = (btrfs_device*)malloc(devsize);
115
116 while (true) {
117 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize);
118 if (Status == STATUS_BUFFER_OVERFLOW) {
119 if (i < 8) {
120 devsize += 1024;
121
122 free(devices);
123 devices = (btrfs_device*)malloc(devsize);
124
125 i++;
126 } else {
127 GlobalUnlock(hdrop);
128 return E_FAIL;
129 }
130 } else
131 break;
132 }
133
134 if (!NT_SUCCESS(Status)) {
135 GlobalUnlock(hdrop);
136 return E_FAIL;
137 }
138
139 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_UUID, nullptr, 0, &uuid, sizeof(BTRFS_UUID));
140 uuid_set = NT_SUCCESS(Status);
141
142 ignore = false;
143 balance = new BtrfsBalance(fn);
144 } else {
145 GlobalUnlock(hdrop);
146 return E_FAIL;
147 }
148 } else {
149 GlobalUnlock(hdrop);
150 return E_FAIL;
151 }
152
153 GlobalUnlock(hdrop);
154
155 return S_OK;
156 }
157
158 typedef struct {
159 uint64_t dev_id;
160 wstring name;
161 uint64_t alloc;
162 uint64_t size;
163 } dev;
164
165 void BtrfsVolPropSheet::FormatUsage(HWND hwndDlg, wstring& s, btrfs_usage* usage) {
166 uint8_t i, j;
167 uint64_t num_devs, dev_size, dev_alloc, data_size, data_alloc, metadata_size, metadata_alloc;
168 btrfs_device* bd;
169 vector<dev> devs;
170 btrfs_usage* bue;
171 wstring t, u, v;
172
173 static const uint64_t types[] = { BLOCK_FLAG_DATA, BLOCK_FLAG_DATA | BLOCK_FLAG_METADATA, BLOCK_FLAG_METADATA, BLOCK_FLAG_SYSTEM };
174 static const ULONG typestrings[] = { IDS_USAGE_DATA, IDS_USAGE_MIXED, IDS_USAGE_METADATA, IDS_USAGE_SYSTEM };
175 static const uint64_t duptypes[] = { 0, BLOCK_FLAG_DUPLICATE, BLOCK_FLAG_RAID0, BLOCK_FLAG_RAID1, BLOCK_FLAG_RAID10, BLOCK_FLAG_RAID5,
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 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
571 bvps = (BtrfsVolPropSheet*)lParam;
572 } else {
573 bvps = (BtrfsVolPropSheet*)GetWindowLongPtr(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 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
947 bvps = (BtrfsVolPropSheet*)lParam;
948 } else {
949 bvps = (BtrfsVolPropSheet*)GetWindowLongPtr(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 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
1254 bvps = (BtrfsVolPropSheet*)lParam;
1255 } else {
1256 bvps = (BtrfsVolPropSheet*)GetWindowLongPtr(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 PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
1335 BtrfsVolPropSheet* bps = (BtrfsVolPropSheet*)psp->lParam;
1336 btrfs_device* bd;
1337
1338 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
1339
1340 SetWindowLongPtr(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*)GetWindowLongPtr(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 PROPSHEETPAGE 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 = MAKEINTRESOURCE(IDD_VOL_PROP_SHEET);
1448 psp.hIcon = 0;
1449 psp.pszTitle = MAKEINTRESOURCE(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 = CreatePropertySheetPage(&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 >= 0 && 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 #ifdef __REACTOS__
1594 INT_PTR CALLBACK VolPropSheetDlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1595 {
1596 BtrfsChangeDriveLetter* bcdl;
1597
1598 if (uMsg == WM_INITDIALOG) {
1599 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
1600 bcdl = (BtrfsChangeDriveLetter*)lParam;
1601 } else
1602 bcdl = (BtrfsChangeDriveLetter*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1603
1604 return bcdl->DlgProc(hwndDlg, uMsg, wParam, lParam);
1605 }
1606 #endif
1607
1608 void BtrfsChangeDriveLetter::show() {
1609 #ifndef __REACTOS__
1610 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DRIVE_LETTER), hwnd, [](HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1611 BtrfsChangeDriveLetter* bcdl;
1612
1613 if (uMsg == WM_INITDIALOG) {
1614 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
1615 bcdl = (BtrfsChangeDriveLetter*)lParam;
1616 } else
1617 bcdl = (BtrfsChangeDriveLetter*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1618
1619 return bcdl->DlgProc(hwndDlg, uMsg, wParam, lParam);
1620 }, (LPARAM)this);
1621 #else
1622 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DRIVE_LETTER), hwnd, VolPropSheetDlgproc, (LPARAM)this);
1623 #endif
1624 }
1625
1626 #ifdef __cplusplus
1627 extern "C" {
1628 #endif
1629
1630 void CALLBACK ResetStatsW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1631 try {
1632 win_handle token;
1633 NTSTATUS Status;
1634 TOKEN_PRIVILEGES tp;
1635 LUID luid;
1636 uint64_t devid;
1637 wstring cmdline, vol, dev;
1638 size_t pipe;
1639 IO_STATUS_BLOCK iosb;
1640
1641 set_dpi_aware();
1642
1643 cmdline = lpszCmdLine;
1644
1645 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1646 throw last_error(GetLastError());
1647
1648 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
1649 throw last_error(GetLastError());
1650
1651 tp.PrivilegeCount = 1;
1652 tp.Privileges[0].Luid = luid;
1653 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1654
1655 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
1656 throw last_error(GetLastError());
1657
1658 pipe = cmdline.find(L"|");
1659
1660 if (pipe == string::npos)
1661 return;
1662
1663 vol = cmdline.substr(0, pipe);
1664 dev = cmdline.substr(pipe + 1);
1665
1666 devid = _wtoi(dev.c_str());
1667 if (devid == 0)
1668 return;
1669
1670 win_handle h = CreateFileW(vol.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1671 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1672
1673 if (h == INVALID_HANDLE_VALUE)
1674 throw last_error(GetLastError());
1675
1676 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESET_STATS, &devid, sizeof(uint64_t), nullptr, 0);
1677 if (!NT_SUCCESS(Status))
1678 throw ntstatus_error(Status);
1679 } catch (const exception& e) {
1680 error_message(hwnd, e.what());
1681 }
1682 }
1683
1684 void CALLBACK ShowChangeDriveLetterW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1685 BtrfsChangeDriveLetter bcdl(hwnd, lpszCmdLine);
1686
1687 bcdl.show();
1688 }
1689
1690 #ifdef __cplusplus
1691 }
1692 #endif