1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
18 #define ISOLATION_AWARE_ENABLED 1
19 #define STRSAFE_NO_DEPRECATE
27 #define WIN32_NO_STATUS
31 #include <ndk/iofuncs.h>
32 #include <ndk/iotypes.h>
35 #define NO_SHLWAPI_STRFCNS
39 #include "volpropsheet.h"
44 #include "mountmgr_local.h"
48 static const NTSTATUS STATUS_OBJECT_NAME_NOT_FOUND
= 0xC0000034;
51 HRESULT __stdcall
BtrfsVolPropSheet::QueryInterface(REFIID riid
, void **ppObj
) {
52 if (riid
== IID_IUnknown
|| riid
== IID_IShellPropSheetExt
) {
53 *ppObj
= static_cast<IShellPropSheetExt
*>(this);
56 } else if (riid
== IID_IShellExtInit
) {
57 *ppObj
= static_cast<IShellExtInit
*>(this);
66 HRESULT __stdcall
BtrfsVolPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder
, IDataObject
* pdtobj
, HKEY hkeyProgID
) {
68 FORMATETC format
= { CF_HDROP
, nullptr, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
70 WCHAR fnbuf
[MAX_PATH
];
78 stgm
.tymed
= TYMED_HGLOBAL
;
80 if (FAILED(pdtobj
->GetData(&format
, &stgm
)))
85 hdrop
= (HDROP
)GlobalLock(stgm
.hGlobal
);
88 ReleaseStgMedium(&stgm
);
93 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, nullptr, 0);
100 if (DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, fnbuf
, sizeof(fnbuf
) / sizeof(MAX_PATH
))) {
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);
106 if (h
!= INVALID_HANDLE_VALUE
) {
108 IO_STATUS_BLOCK iosb
;
114 devices
= (btrfs_device
*)malloc(devsize
);
117 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_DEVICES
, nullptr, 0, devices
, devsize
);
118 if (Status
== STATUS_BUFFER_OVERFLOW
) {
123 devices
= (btrfs_device
*)malloc(devsize
);
134 if (!NT_SUCCESS(Status
)) {
139 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_UUID
, nullptr, 0, &uuid
, sizeof(BTRFS_UUID
));
140 uuid_set
= NT_SUCCESS(Status
);
143 balance
= new BtrfsBalance(fn
);
165 void BtrfsVolPropSheet::FormatUsage(HWND hwndDlg
, wstring
& s
, btrfs_usage
* usage
) {
167 uint64_t num_devs
, dev_size
, dev_alloc
, data_size
, data_alloc
, metadata_size
, metadata_alloc
;
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
, BLOCK_FLAG_RAID6
};
176 static const ULONG dupstrings
[] = { IDS_SINGLE
, IDS_DUP
, IDS_RAID0
, IDS_RAID1
, IDS_RAID10
, IDS_RAID5
, IDS_RAID6
};
186 if (bd
->next_entry
> 0)
187 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
200 if (!load_string(module
, IDS_MISSING
, d
.name
))
201 throw last_error(GetLastError());
202 } else if (bd
->device_number
== 0xffffffff)
203 d
.name
= wstring(bd
->name
, bd
->namelen
/ sizeof(WCHAR
));
204 else if (bd
->partition_number
== 0) {
205 if (!load_string(module
, IDS_DISK_NUM
, u
))
206 throw last_error(GetLastError());
208 wstring_sprintf(d
.name
, u
, bd
->device_number
);
210 if (!load_string(module
, IDS_DISK_PART_NUM
, u
))
211 throw last_error(GetLastError());
213 wstring_sprintf(d
.name
, u
, bd
->device_number
, bd
->partition_number
);
216 d
.dev_id
= bd
->dev_id
;
222 dev_size
+= bd
->size
;
224 if (bd
->next_entry
> 0)
225 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
231 data_size
= data_alloc
= 0;
232 metadata_size
= metadata_alloc
= 0;
236 for (uint64_t k
= 0; k
< bue
->num_devices
; k
++) {
237 dev_alloc
+= bue
->devices
[k
].alloc
;
239 if (bue
->type
& BLOCK_FLAG_DATA
) {
240 data_alloc
+= bue
->devices
[k
].alloc
;
243 if (bue
->type
& BLOCK_FLAG_METADATA
) {
244 metadata_alloc
+= bue
->devices
[k
].alloc
;
248 if (bue
->type
& BLOCK_FLAG_DATA
)
249 data_size
+= bue
->size
;
251 if (bue
->type
& BLOCK_FLAG_METADATA
)
252 metadata_size
+= bue
->size
;
254 if (bue
->next_entry
> 0)
255 bue
= (btrfs_usage
*)((uint8_t*)bue
+ bue
->next_entry
);
262 if (!load_string(module
, IDS_USAGE_DEV_SIZE
, u
))
263 throw last_error(GetLastError());
265 format_size(dev_size
, v
, false);
267 wstring_sprintf(t
, u
, v
.c_str());
273 if (!load_string(module
, IDS_USAGE_DEV_ALLOC
, u
))
274 throw last_error(GetLastError());
276 format_size(dev_alloc
, v
, false);
278 wstring_sprintf(t
, u
, v
.c_str());
286 // device unallocated
288 if (!load_string(module
, IDS_USAGE_DEV_UNALLOC
, u
))
289 throw last_error(GetLastError());
291 format_size(dev_size
- dev_alloc
, v
, false);
293 wstring_sprintf(t
, u
, v
.c_str());
303 if (data_alloc
> 0) {
304 if (!load_string(module
, IDS_USAGE_DATA_RATIO
, u
))
305 throw last_error(GetLastError());
307 wstring_sprintf(t
, u
, (float)data_alloc
/ (float)data_size
);
318 if (!load_string(module
, IDS_USAGE_METADATA_RATIO
, u
))
319 throw last_error(GetLastError());
321 wstring_sprintf(t
, u
, (float)metadata_alloc
/ (float)metadata_size
);
323 s
+= t
+ L
"\r\n\r\n";
325 for (i
= 0; i
< sizeof(types
) / sizeof(types
[0]); i
++) {
326 for (j
= 0; j
< sizeof(duptypes
) / sizeof(duptypes
[0]); j
++) {
330 if ((bue
->type
& types
[i
]) == types
[i
] &&
331 ((duptypes
[j
] == 0 && (bue
->type
& (BLOCK_FLAG_DUPLICATE
| BLOCK_FLAG_RAID0
| BLOCK_FLAG_RAID1
| BLOCK_FLAG_RAID10
| BLOCK_FLAG_RAID5
| BLOCK_FLAG_RAID6
)) == 0)
332 || bue
->type
& duptypes
[j
])) {
333 wstring sizestring
, usedstring
, typestring
, dupstring
;
335 if (bue
->type
& BLOCK_FLAG_DATA
&& bue
->type
& BLOCK_FLAG_METADATA
&& (types
[i
] == BLOCK_FLAG_DATA
|| types
[i
] == BLOCK_FLAG_METADATA
))
338 if (!load_string(module
, typestrings
[i
], typestring
))
339 throw last_error(GetLastError());
341 if (!load_string(module
, dupstrings
[j
], dupstring
))
342 throw last_error(GetLastError());
344 format_size(bue
->size
, sizestring
, false);
345 format_size(bue
->used
, usedstring
, false);
347 wstring_sprintf(t
, typestring
, dupstring
.c_str(), sizestring
.c_str(), usedstring
.c_str());
351 for (uint64_t k
= 0; k
< bue
->num_devices
; k
++) {
354 format_size(bue
->devices
[k
].alloc
, sizestring
, false);
356 for (size_t l
= 0; l
< min((uint64_t)SIZE_MAX
, num_devs
); l
++) {
357 if (devs
[l
].dev_id
== bue
->devices
[k
].dev_id
) {
358 s
+= devs
[l
].name
+ L
"\t" + sizestring
+ L
"\r\n";
360 devs
[l
].alloc
+= bue
->devices
[k
].alloc
;
368 if (!load_string(module
, IDS_UNKNOWN_DEVICE
, typestring
))
369 throw last_error(GetLastError());
371 wstring_sprintf(t
, typestring
, bue
->devices
[k
].dev_id
);
374 s
+= t
+ L
"\t"s
+ sizestring
+ L
"\r\n"s
;
376 s
+= t
+ L
"\t" + sizestring
+ L
"\r\n";
386 if (bue
->next_entry
> 0)
387 bue
= (btrfs_usage
*)((uint8_t*)bue
+ bue
->next_entry
);
394 if (!load_string(module
, IDS_USAGE_UNALLOC
, t
))
395 throw last_error(GetLastError());
403 for (size_t k
= 0; k
< min((uint64_t)SIZE_MAX
, num_devs
); k
++) {
406 format_size(devs
[k
].size
- devs
[k
].alloc
, sizestring
, false);
408 s
+= devs
[k
].name
+ L
"\t" + sizestring
+ L
"\r\n";
412 void BtrfsVolPropSheet::RefreshUsage(HWND hwndDlg
) {
416 win_handle h
= CreateFileW(fn
.c_str(), FILE_TRAVERSE
| FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr,
417 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, nullptr);
419 if (h
!= INVALID_HANDLE_VALUE
) {
421 IO_STATUS_BLOCK iosb
;
422 ULONG devsize
, usagesize
, i
;
427 devices
= (btrfs_device
*)malloc(devsize
);
430 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_DEVICES
, nullptr, 0, devices
, devsize
);
431 if (Status
== STATUS_BUFFER_OVERFLOW
) {
436 devices
= (btrfs_device
*)malloc(devsize
);
445 if (!NT_SUCCESS(Status
))
451 usage
= (btrfs_usage
*)malloc(usagesize
);
454 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_USAGE
, nullptr, 0, usage
, usagesize
);
455 if (Status
== STATUS_BUFFER_OVERFLOW
) {
460 usage
= (btrfs_usage
*)malloc(usagesize
);
469 if (!NT_SUCCESS(Status
)) {
478 FormatUsage(hwndDlg
, s
, usage
);
480 SetDlgItemTextW(hwndDlg
, IDC_USAGE_BOX
, s
.c_str());
485 INT_PTR CALLBACK
BtrfsVolPropSheet::UsageDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
494 IO_STATUS_BLOCK iosb
;
496 EnableThemeDialogTexture(hwndDlg
, ETDT_ENABLETAB
);
498 win_handle h
= CreateFileW(fn
.c_str(), FILE_TRAVERSE
| FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr,
499 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, nullptr);
501 if (h
!= INVALID_HANDLE_VALUE
) {
507 usage
= (btrfs_usage
*)malloc(usagesize
);
510 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_USAGE
, nullptr, 0, usage
, usagesize
);
511 if (Status
== STATUS_BUFFER_OVERFLOW
) {
516 usage
= (btrfs_usage
*)malloc(usagesize
);
525 if (!NT_SUCCESS(Status
)) {
530 FormatUsage(hwndDlg
, s
, usage
);
532 SetDlgItemTextW(hwndDlg
, IDC_USAGE_BOX
, s
.c_str());
541 switch (HIWORD(wParam
)) {
543 switch (LOWORD(wParam
)) {
546 EndDialog(hwndDlg
, 0);
549 case IDC_USAGE_REFRESH
:
550 RefreshUsage(hwndDlg
);
557 } catch (const exception
& e
) {
558 error_message(hwndDlg
, e
.what());
564 static INT_PTR CALLBACK
stub_UsageDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
565 BtrfsVolPropSheet
* bvps
;
567 if (uMsg
== WM_INITDIALOG
) {
568 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
569 bvps
= (BtrfsVolPropSheet
*)lParam
;
571 bvps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
575 return bvps
->UsageDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
580 void BtrfsVolPropSheet::ShowUsage(HWND hwndDlg
) {
581 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_VOL_USAGE
), hwndDlg
, stub_UsageDlgProc
, (LPARAM
)this);
584 static void add_lv_column(HWND list
, int string
, int cx
) {
588 if (!load_string(module
, string
, s
))
589 throw last_error(GetLastError());
591 lvc
.mask
= LVCF_TEXT
|LVCF_WIDTH
;
592 lvc
.pszText
= (WCHAR
*)s
.c_str();
594 SendMessageW(list
, LVM_INSERTCOLUMNW
, 0, (LPARAM
)&lvc
);
597 static int CALLBACK
lv_sort(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
) {
598 if (lParam1
< lParam2
)
600 else if (lParam1
> lParam2
)
606 static uint64_t find_dev_alloc(uint64_t dev_id
, btrfs_usage
* usage
) {
616 for (k
= 0; k
< bue
->num_devices
; k
++) {
617 if (bue
->devices
[k
].dev_id
== dev_id
)
618 alloc
+= bue
->devices
[k
].alloc
;
621 if (bue
->next_entry
> 0)
622 bue
= (btrfs_usage
*)((uint8_t*)bue
+ bue
->next_entry
);
630 void BtrfsVolPropSheet::RefreshDevList(HWND devlist
) {
632 IO_STATUS_BLOCK iosb
;
633 ULONG usagesize
, devsize
;
637 uint64_t num_rw_devices
;
639 win_handle h
= CreateFileW(fn
.c_str(), FILE_TRAVERSE
| FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr,
640 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, nullptr);
642 if (h
== INVALID_HANDLE_VALUE
)
643 throw last_error(GetLastError());
651 devices
= (btrfs_device
*)malloc(devsize
);
654 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_DEVICES
, nullptr, 0, devices
, devsize
);
655 if (Status
== STATUS_BUFFER_OVERFLOW
) {
660 devices
= (btrfs_device
*)malloc(devsize
);
669 if (!NT_SUCCESS(Status
))
677 usage
= (btrfs_usage
*)malloc(usagesize
);
680 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_USAGE
, nullptr, 0, usage
, usagesize
);
681 if (Status
== STATUS_BUFFER_OVERFLOW
) {
686 usage
= (btrfs_usage
*)malloc(usagesize
);
697 if (!NT_SUCCESS(Status
)) {
703 SendMessageW(devlist
, LVM_DELETEALLITEMS
, 0, 0);
715 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
716 lvi
.mask
= LVIF_TEXT
| LVIF_PARAM
;
717 lvi
.iItem
= (int)SendMessageW(devlist
, LVM_GETITEMCOUNT
, 0, 0);
718 lvi
.lParam
= (LPARAM
)bd
->dev_id
;
720 s
= to_wstring(bd
->dev_id
);
721 lvi
.pszText
= (LPWSTR
)s
.c_str();
723 SendMessageW(devlist
, LVM_INSERTITEMW
, 0, (LPARAM
)&lvi
);
727 lvi
.mask
= LVIF_TEXT
;
731 if (!load_string(module
, IDS_MISSING
, s
))
732 throw last_error(GetLastError());
733 } else if (bd
->device_number
== 0xffffffff)
734 s
= wstring(bd
->name
, bd
->namelen
/ sizeof(WCHAR
));
735 else if (bd
->partition_number
== 0) {
736 if (!load_string(module
, IDS_DISK_NUM
, u
))
737 throw last_error(GetLastError());
739 wstring_sprintf(s
, u
, bd
->device_number
);
741 if (!load_string(module
, IDS_DISK_PART_NUM
, u
))
742 throw last_error(GetLastError());
744 wstring_sprintf(s
, u
, bd
->device_number
, bd
->partition_number
);
747 lvi
.pszText
= (LPWSTR
)s
.c_str();
749 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
754 load_string(module
, bd
->readonly
? IDS_DEVLIST_READONLY_YES
: IDS_DEVLIST_READONLY_NO
, s
);
755 lvi
.pszText
= (LPWSTR
)s
.c_str();
756 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
764 format_size(bd
->size
, s
, false);
765 lvi
.pszText
= (LPWSTR
)s
.c_str();
766 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
770 alloc
= find_dev_alloc(bd
->dev_id
, usage
);
773 format_size(alloc
, s
, false);
774 lvi
.pszText
= (LPWSTR
)s
.c_str();
775 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
779 wstring_sprintf(s
, L
"%1.1f%%", (float)alloc
* 100.0f
/ (float)bd
->size
);
781 lvi
.pszText
= (LPWSTR
)s
.c_str();
782 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
786 if (bd
->next_entry
> 0)
787 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
794 SendMessageW(devlist
, LVM_SORTITEMS
, 0, (LPARAM
)lv_sort
);
796 EnableWindow(GetDlgItem(GetParent(devlist
), IDC_DEVICE_ADD
), num_rw_devices
> 0);
797 EnableWindow(GetDlgItem(GetParent(devlist
), IDC_DEVICE_REMOVE
), num_rw_devices
> 1);
800 void BtrfsVolPropSheet::ResetStats(HWND hwndDlg
) {
802 WCHAR modfn
[MAX_PATH
];
803 SHELLEXECUTEINFOW sei
;
805 sel
= to_wstring(stats_dev
);
807 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
810 t
= L
"\""s
+ modfn
+ L
"\",ResetStats " + fn
+ L
"|" + sel
;
812 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ResetStats ") + fn
+ wstring(L
"|") + sel
;
815 RtlZeroMemory(&sei
, sizeof(sei
));
817 sei
.cbSize
= sizeof(sei
);
819 sei
.lpVerb
= L
"runas";
820 sei
.lpFile
= L
"rundll32.exe";
821 sei
.lpParameters
= t
.c_str();
823 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
825 if (!ShellExecuteExW(&sei
))
826 throw last_error(GetLastError());
828 WaitForSingleObject(sei
.hProcess
, INFINITE
);
829 CloseHandle(sei
.hProcess
);
831 win_handle h
= CreateFileW(fn
.c_str(), FILE_TRAVERSE
| FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr,
832 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, nullptr);
834 if (h
!= INVALID_HANDLE_VALUE
) {
836 IO_STATUS_BLOCK iosb
;
843 devices
= (btrfs_device
*)malloc(devsize
);
846 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_DEVICES
, nullptr, 0, devices
, devsize
);
847 if (Status
== STATUS_BUFFER_OVERFLOW
) {
852 devices
= (btrfs_device
*)malloc(devsize
);
862 EndDialog(hwndDlg
, 0);
865 INT_PTR CALLBACK
BtrfsVolPropSheet::StatsDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
872 btrfs_device
*bd
, *dev
= nullptr;
875 static int stat_ids
[] = { IDC_WRITE_ERRS
, IDC_READ_ERRS
, IDC_FLUSH_ERRS
, IDC_CORRUPTION_ERRS
, IDC_GENERATION_ERRS
};
880 if (bd
->dev_id
== stats_dev
) {
885 if (bd
->next_entry
> 0)
886 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
892 EndDialog(hwndDlg
, 0);
893 throw string_error(IDS_CANNOT_FIND_DEVICE
);
896 GetDlgItemTextW(hwndDlg
, IDC_DEVICE_ID
, s
, sizeof(s
) / sizeof(WCHAR
));
898 wstring_sprintf(t
, s
, dev
->dev_id
);
900 SetDlgItemTextW(hwndDlg
, IDC_DEVICE_ID
, t
.c_str());
902 for (i
= 0; i
< 5; i
++) {
903 GetDlgItemTextW(hwndDlg
, stat_ids
[i
], s
, sizeof(s
) / sizeof(WCHAR
));
905 wstring_sprintf(t
, s
, dev
->stats
[i
]);
907 SetDlgItemTextW(hwndDlg
, stat_ids
[i
], t
.c_str());
910 SendMessageW(GetDlgItem(hwndDlg
, IDC_RESET_STATS
), BCM_SETSHIELD
, 0, true);
911 EnableWindow(GetDlgItem(hwndDlg
, IDC_RESET_STATS
), !readonly
);
917 switch (HIWORD(wParam
)) {
919 switch (LOWORD(wParam
)) {
922 EndDialog(hwndDlg
, 0);
925 case IDC_RESET_STATS
:
933 } catch (const exception
& e
) {
934 error_message(hwndDlg
, e
.what());
940 static INT_PTR CALLBACK
stub_StatsDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
941 BtrfsVolPropSheet
* bvps
;
943 if (uMsg
== WM_INITDIALOG
) {
944 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
945 bvps
= (BtrfsVolPropSheet
*)lParam
;
947 bvps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
951 return bvps
->StatsDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
956 void BtrfsVolPropSheet::ShowStats(HWND hwndDlg
, uint64_t devid
) {
959 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DEVICE_STATS
), hwndDlg
, stub_StatsDlgProc
, (LPARAM
)this);
962 INT_PTR CALLBACK
BtrfsVolPropSheet::DeviceDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
971 EnableThemeDialogTexture(hwndDlg
, ETDT_ENABLETAB
);
973 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
975 GetClientRect(devlist
, &rect
);
976 w
= rect
.right
- rect
.left
;
978 add_lv_column(devlist
, IDS_DEVLIST_ALLOC_PC
, w
* 5 / 44);
979 add_lv_column(devlist
, IDS_DEVLIST_ALLOC
, w
* 6 / 44);
980 add_lv_column(devlist
, IDS_DEVLIST_SIZE
, w
* 6 / 44);
981 add_lv_column(devlist
, IDS_DEVLIST_READONLY
, w
* 7 / 44);
982 add_lv_column(devlist
, IDS_DEVLIST_DESC
, w
* 16 / 44);
983 add_lv_column(devlist
, IDS_DEVLIST_ID
, w
* 4 / 44);
985 SendMessageW(GetDlgItem(hwndDlg
, IDC_DEVICE_ADD
), BCM_SETSHIELD
, 0, true);
986 SendMessageW(GetDlgItem(hwndDlg
, IDC_DEVICE_REMOVE
), BCM_SETSHIELD
, 0, true);
987 SendMessageW(GetDlgItem(hwndDlg
, IDC_DEVICE_RESIZE
), BCM_SETSHIELD
, 0, true);
989 RefreshDevList(devlist
);
995 switch (HIWORD(wParam
)) {
997 switch (LOWORD(wParam
)) {
1000 KillTimer(hwndDlg
, 1);
1001 EndDialog(hwndDlg
, 0);
1004 case IDC_DEVICE_ADD
:
1007 WCHAR modfn
[MAX_PATH
];
1008 SHELLEXECUTEINFOW sei
;
1010 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1013 t
= L
"\""s
+ modfn
+ L
"\",AddDevice "s
+ fn
;
1015 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",AddDevice ") + fn
;
1018 RtlZeroMemory(&sei
, sizeof(sei
));
1020 sei
.cbSize
= sizeof(sei
);
1022 sei
.lpVerb
= L
"runas";
1023 sei
.lpFile
= L
"rundll32.exe";
1024 sei
.lpParameters
= t
.c_str();
1025 sei
.nShow
= SW_SHOW
;
1026 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
1028 if (!ShellExecuteExW(&sei
))
1029 throw last_error(GetLastError());
1031 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1032 CloseHandle(sei
.hProcess
);
1034 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1039 case IDC_DEVICE_REFRESH
:
1040 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1043 case IDC_DEVICE_SHOW_STATS
:
1045 WCHAR sel
[MAX_PATH
];
1049 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1051 auto index
= SendMessageW(devlist
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
1056 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1057 lvi
.mask
= LVIF_TEXT
;
1058 lvi
.iItem
= (int)index
;
1061 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1062 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1064 ShowStats(hwndDlg
, _wtoi(sel
));
1068 case IDC_DEVICE_REMOVE
:
1070 wstring t
, mess
, mess2
, title
;
1071 WCHAR modfn
[MAX_PATH
], sel
[MAX_PATH
], sel2
[MAX_PATH
];
1073 SHELLEXECUTEINFOW sei
;
1076 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1078 auto index
= SendMessageW(devlist
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
1083 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1084 lvi
.mask
= LVIF_TEXT
;
1085 lvi
.iItem
= (int)index
;
1088 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1089 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1093 lvi
.cchTextMax
= sizeof(sel2
) / sizeof(WCHAR
);
1094 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1096 if (!load_string(module
, IDS_REMOVE_DEVICE_CONFIRMATION
, mess
))
1097 throw last_error(GetLastError());
1099 wstring_sprintf(mess2
, mess
, sel
, sel2
);
1101 if (!load_string(module
, IDS_CONFIRMATION_TITLE
, title
))
1102 throw last_error(GetLastError());
1104 if (MessageBoxW(hwndDlg
, mess2
.c_str(), title
.c_str(), MB_YESNO
) != IDYES
)
1107 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1110 t
= L
"\""s
+ modfn
+ L
"\",RemoveDevice "s
+ fn
+ L
"|"s
+ sel
;
1112 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",RemoveDevice ") + fn
+ wstring(L
"|") + sel
;
1115 RtlZeroMemory(&sei
, sizeof(sei
));
1117 sei
.cbSize
= sizeof(sei
);
1119 sei
.lpVerb
= L
"runas";
1120 sei
.lpFile
= L
"rundll32.exe";
1121 sei
.lpParameters
= t
.c_str();
1122 sei
.nShow
= SW_SHOW
;
1123 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
1125 if (!ShellExecuteExW(&sei
))
1126 throw last_error(GetLastError());
1128 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1129 CloseHandle(sei
.hProcess
);
1131 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1136 case IDC_DEVICE_RESIZE
:
1141 WCHAR modfn
[MAX_PATH
], sel
[100];
1142 SHELLEXECUTEINFOW sei
;
1144 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1146 auto index
= SendMessageW(devlist
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
1151 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1152 lvi
.mask
= LVIF_TEXT
;
1153 lvi
.iItem
= (int)index
;
1156 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1157 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1159 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1162 t
= L
"\""s
+ modfn
+ L
"\",ResizeDevice "s
+ fn
+ L
"|"s
+ sel
;
1164 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ResizeDevice ") + fn
+ wstring(L
"|") + sel
;
1167 RtlZeroMemory(&sei
, sizeof(sei
));
1169 sei
.cbSize
= sizeof(sei
);
1171 sei
.lpVerb
= L
"runas";
1172 sei
.lpFile
= L
"rundll32.exe";
1173 sei
.lpParameters
= t
.c_str();
1174 sei
.nShow
= SW_SHOW
;
1175 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
1177 if (!ShellExecuteExW(&sei
))
1178 throw last_error(GetLastError());
1180 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1181 CloseHandle(sei
.hProcess
);
1183 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1191 switch (((LPNMHDR
)lParam
)->code
) {
1192 case LVN_ITEMCHANGED
:
1194 NMLISTVIEW
* nmv
= (NMLISTVIEW
*)lParam
;
1196 EnableWindow(GetDlgItem(hwndDlg
, IDC_DEVICE_SHOW_STATS
), nmv
->uNewState
& LVIS_SELECTED
);
1198 if (nmv
->uNewState
& LVIS_SELECTED
&& !readonly
) {
1201 bool device_readonly
= false;
1203 WCHAR sel
[MAX_PATH
];
1206 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1208 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1209 lvi
.mask
= LVIF_TEXT
;
1210 lvi
.iItem
= nmv
->iItem
;
1213 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1214 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1220 if (bd
->dev_id
== devid
) {
1221 device_readonly
= bd
->readonly
;
1225 if (bd
->next_entry
> 0)
1226 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
1231 EnableWindow(GetDlgItem(hwndDlg
, IDC_DEVICE_RESIZE
), !device_readonly
);
1233 EnableWindow(GetDlgItem(hwndDlg
, IDC_DEVICE_RESIZE
), false);
1240 } catch (const exception
& e
) {
1241 error_message(hwndDlg
, e
.what());
1247 static INT_PTR CALLBACK
stub_DeviceDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1248 BtrfsVolPropSheet
* bvps
;
1250 if (uMsg
== WM_INITDIALOG
) {
1251 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
1252 bvps
= (BtrfsVolPropSheet
*)lParam
;
1254 bvps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1258 return bvps
->DeviceDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
1263 void BtrfsVolPropSheet::ShowDevices(HWND hwndDlg
) {
1264 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DEVICES
), hwndDlg
, stub_DeviceDlgProc
, (LPARAM
)this);
1267 void BtrfsVolPropSheet::ShowScrub(HWND hwndDlg
) {
1269 WCHAR modfn
[MAX_PATH
];
1270 SHELLEXECUTEINFOW sei
;
1272 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1275 t
= L
"\""s
+ modfn
+ L
"\",ShowScrub "s
+ fn
;
1277 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ShowScrub ") + fn
;
1280 RtlZeroMemory(&sei
, sizeof(sei
));
1282 sei
.cbSize
= sizeof(sei
);
1284 sei
.lpVerb
= L
"runas";
1285 sei
.lpFile
= L
"rundll32.exe";
1286 sei
.lpParameters
= t
.c_str();
1287 sei
.nShow
= SW_SHOW
;
1288 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
1290 if (!ShellExecuteExW(&sei
))
1291 throw last_error(GetLastError());
1293 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1294 CloseHandle(sei
.hProcess
);
1297 void BtrfsVolPropSheet::ShowChangeDriveLetter(HWND hwndDlg
) {
1299 WCHAR modfn
[MAX_PATH
];
1300 SHELLEXECUTEINFOW sei
;
1302 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1305 t
= L
"\""s
+ modfn
+ L
"\",ShowChangeDriveLetter "s
+ fn
;
1307 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ShowChangeDriveLetter ") + fn
;
1310 RtlZeroMemory(&sei
, sizeof(sei
));
1312 sei
.cbSize
= sizeof(sei
);
1314 sei
.lpVerb
= L
"runas";
1315 sei
.lpFile
= L
"rundll32.exe";
1316 sei
.lpParameters
= t
.c_str();
1317 sei
.nShow
= SW_SHOW
;
1318 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
1320 if (!ShellExecuteExW(&sei
))
1321 throw last_error(GetLastError());
1323 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1324 CloseHandle(sei
.hProcess
);
1327 static INT_PTR CALLBACK
PropSheetDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1332 PROPSHEETPAGE
* psp
= (PROPSHEETPAGE
*)lParam
;
1333 BtrfsVolPropSheet
* bps
= (BtrfsVolPropSheet
*)psp
->lParam
;
1336 EnableThemeDialogTexture(hwndDlg
, ETDT_ENABLETAB
);
1338 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)bps
);
1340 bps
->readonly
= true;
1344 if (!bd
->readonly
) {
1345 bps
->readonly
= false;
1349 if (bd
->next_entry
> 0)
1350 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
1355 if (bps
->uuid_set
) {
1359 GetDlgItemTextW(hwndDlg
, IDC_UUID
, s
, sizeof(s
) / sizeof(WCHAR
));
1361 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],
1362 bps
->uuid
.uuid
[6], bps
->uuid
.uuid
[7], bps
->uuid
.uuid
[8], bps
->uuid
.uuid
[9], bps
->uuid
.uuid
[10], bps
->uuid
.uuid
[11],
1363 bps
->uuid
.uuid
[12], bps
->uuid
.uuid
[13], bps
->uuid
.uuid
[14], bps
->uuid
.uuid
[15]);
1365 SetDlgItemTextW(hwndDlg
, IDC_UUID
, t
.c_str());
1367 SetDlgItemTextW(hwndDlg
, IDC_UUID
, L
"");
1369 SendMessageW(GetDlgItem(hwndDlg
, IDC_VOL_SCRUB
), BCM_SETSHIELD
, 0, true);
1370 SendMessageW(GetDlgItem(hwndDlg
, IDC_VOL_CHANGE_DRIVE_LETTER
), BCM_SETSHIELD
, 0, true);
1377 switch (((LPNMHDR
)lParam
)->code
) {
1378 case PSN_KILLACTIVE
:
1379 SetWindowLongPtrW(hwndDlg
, DWLP_MSGRESULT
, false);
1387 BtrfsVolPropSheet
* bps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1390 switch (HIWORD(wParam
)) {
1392 switch (LOWORD(wParam
)) {
1393 case IDC_VOL_SHOW_USAGE
:
1394 bps
->ShowUsage(hwndDlg
);
1397 case IDC_VOL_BALANCE
:
1398 bps
->balance
->ShowBalance(hwndDlg
);
1401 case IDC_VOL_DEVICES
:
1402 bps
->ShowDevices(hwndDlg
);
1406 bps
->ShowScrub(hwndDlg
);
1409 case IDC_VOL_CHANGE_DRIVE_LETTER
:
1410 bps
->ShowChangeDriveLetter(hwndDlg
);
1420 } catch (const exception
& e
) {
1421 error_message(hwndDlg
, e
.what());
1427 HRESULT __stdcall
BtrfsVolPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
) {
1430 HPROPSHEETPAGE hPage
;
1431 INITCOMMONCONTROLSEX icex
;
1436 icex
.dwSize
= sizeof(icex
);
1437 icex
.dwICC
= ICC_LINK_CLASS
;
1439 if (!InitCommonControlsEx(&icex
))
1440 throw string_error(IDS_INITCOMMONCONTROLSEX_FAILED
);
1442 psp
.dwSize
= sizeof(psp
);
1443 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
;
1444 psp
.hInstance
= module
;
1445 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_VOL_PROP_SHEET
);
1447 psp
.pszTitle
= MAKEINTRESOURCE(IDS_VOL_PROP_SHEET_TITLE
);
1448 psp
.pfnDlgProc
= (DLGPROC
)PropSheetDlgProc
;
1449 psp
.pcRefParent
= (UINT
*)&objs_loaded
;
1450 psp
.pfnCallback
= nullptr;
1451 psp
.lParam
= (LPARAM
)this;
1453 hPage
= CreatePropertySheetPage(&psp
);
1456 if (pfnAddPage(hPage
, lParam
)) {
1460 DestroyPropertySheetPage(hPage
);
1462 return E_OUTOFMEMORY
;
1463 } catch (const exception
& e
) {
1464 error_message(nullptr, e
.what());
1470 HRESULT __stdcall
BtrfsVolPropSheet::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
) {
1474 void BtrfsChangeDriveLetter::do_change(HWND hwndDlg
) {
1475 unsigned int sel
= (unsigned int)SendDlgItemMessageW(hwndDlg
, IDC_DRIVE_LETTER_COMBO
, CB_GETCURSEL
, 0, 0);
1477 if (sel
>= 0 && sel
< letters
.size()) {
1480 if (fn
.length() == 3 && fn
[1] == L
':' && fn
[2] == L
'\\') {
1481 dd
= L
"\\DosDevices\\?:";
1486 throw runtime_error("Volume path was not root of drive.");
1488 error_message(nullptr, "Volume path was not root of drive.");
1495 auto v
= mm
.query_points(dd
);
1499 throw runtime_error("Error finding device name.");
1501 error_message(nullptr, "Error finding device name.");
1504 dev_name
= v
[0].device_name
;
1507 wstring new_dd
= L
"\\DosDevices\\?:";
1508 new_dd
[12] = letters
[sel
];
1510 mm
.delete_points(dd
);
1513 mm
.create_point(new_dd
, dev_name
);
1515 // if fails, try to recreate old symlink, so we're not left with no drive letter at all
1516 mm
.create_point(dd
, dev_name
);
1521 EndDialog(hwndDlg
, 1);
1524 INT_PTR
BtrfsChangeDriveLetter::DlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1529 HWND cb
= GetDlgItem(hwndDlg
, IDC_DRIVE_LETTER_COMBO
);
1531 SendMessageW(cb
, CB_RESETCONTENT
, 0, 0);
1536 drv
= L
"\\DosDevices\\?:";
1538 for (wchar_t l
= 'A'; l
<= 'Z'; l
++) {
1544 auto v
= mm
.query_points(drv
);
1548 } catch (const ntstatus_error
& ntstatus
) {
1549 if (ntstatus
.Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1556 wstring str
= L
"?:";
1559 letters
.push_back(l
);
1561 SendMessageW(cb
, CB_ADDSTRING
, 0, reinterpret_cast<LPARAM
>(str
.c_str()));
1569 switch (HIWORD(wParam
)) {
1571 switch (LOWORD(wParam
)) {
1577 EndDialog(hwndDlg
, 0);
1584 } catch (const exception
& e
) {
1585 error_message(hwndDlg
, e
.what());
1592 INT_PTR CALLBACK
VolPropSheetDlgproc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1594 BtrfsChangeDriveLetter
* bcdl
;
1596 if (uMsg
== WM_INITDIALOG
) {
1597 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
1598 bcdl
= (BtrfsChangeDriveLetter
*)lParam
;
1600 bcdl
= (BtrfsChangeDriveLetter
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1602 return bcdl
->DlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
1606 void BtrfsChangeDriveLetter::show() {
1608 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DRIVE_LETTER
), hwnd
, [](HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1609 BtrfsChangeDriveLetter
* bcdl
;
1611 if (uMsg
== WM_INITDIALOG
) {
1612 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
1613 bcdl
= (BtrfsChangeDriveLetter
*)lParam
;
1615 bcdl
= (BtrfsChangeDriveLetter
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1617 return bcdl
->DlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
1620 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DRIVE_LETTER
), hwnd
, VolPropSheetDlgproc
, (LPARAM
)this);
1628 void CALLBACK
ResetStatsW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1632 TOKEN_PRIVILEGES tp
;
1635 wstring cmdline
, vol
, dev
;
1637 IO_STATUS_BLOCK iosb
;
1641 cmdline
= lpszCmdLine
;
1643 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
))
1644 throw last_error(GetLastError());
1646 if (!LookupPrivilegeValueW(nullptr, L
"SeManageVolumePrivilege", &luid
))
1647 throw last_error(GetLastError());
1649 tp
.PrivilegeCount
= 1;
1650 tp
.Privileges
[0].Luid
= luid
;
1651 tp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
1653 if (!AdjustTokenPrivileges(token
, false, &tp
, sizeof(TOKEN_PRIVILEGES
), nullptr, nullptr))
1654 throw last_error(GetLastError());
1656 pipe
= cmdline
.find(L
"|");
1658 if (pipe
== string::npos
)
1661 vol
= cmdline
.substr(0, pipe
);
1662 dev
= cmdline
.substr(pipe
+ 1);
1664 devid
= _wtoi(dev
.c_str());
1668 win_handle h
= CreateFileW(vol
.c_str(), FILE_TRAVERSE
| FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr,
1669 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, nullptr);
1671 if (h
== INVALID_HANDLE_VALUE
)
1672 throw last_error(GetLastError());
1674 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_RESET_STATS
, &devid
, sizeof(uint64_t), nullptr, 0);
1675 if (!NT_SUCCESS(Status
))
1676 throw ntstatus_error(Status
);
1677 } catch (const exception
& e
) {
1678 error_message(hwnd
, e
.what());
1682 void CALLBACK
ShowChangeDriveLetterW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1683 BtrfsChangeDriveLetter
bcdl(hwnd
, lpszCmdLine
);