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
,
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
};
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
;
190 if (bd
->next_entry
> 0)
191 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
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());
212 wstring_sprintf(d
.name
, u
, bd
->device_number
);
214 if (!load_string(module
, IDS_DISK_PART_NUM
, u
))
215 throw last_error(GetLastError());
217 wstring_sprintf(d
.name
, u
, bd
->device_number
, bd
->partition_number
);
220 d
.dev_id
= bd
->dev_id
;
226 dev_size
+= bd
->size
;
228 if (bd
->next_entry
> 0)
229 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
235 data_size
= data_alloc
= 0;
236 metadata_size
= metadata_alloc
= 0;
240 for (uint64_t k
= 0; k
< bue
->num_devices
; k
++) {
241 dev_alloc
+= bue
->devices
[k
].alloc
;
243 if (bue
->type
& BLOCK_FLAG_DATA
) {
244 data_alloc
+= bue
->devices
[k
].alloc
;
247 if (bue
->type
& BLOCK_FLAG_METADATA
) {
248 metadata_alloc
+= bue
->devices
[k
].alloc
;
252 if (bue
->type
& BLOCK_FLAG_DATA
)
253 data_size
+= bue
->size
;
255 if (bue
->type
& BLOCK_FLAG_METADATA
)
256 metadata_size
+= bue
->size
;
258 if (bue
->next_entry
> 0)
259 bue
= (btrfs_usage
*)((uint8_t*)bue
+ bue
->next_entry
);
266 if (!load_string(module
, IDS_USAGE_DEV_SIZE
, u
))
267 throw last_error(GetLastError());
269 format_size(dev_size
, v
, false);
271 wstring_sprintf(t
, u
, v
.c_str());
277 if (!load_string(module
, IDS_USAGE_DEV_ALLOC
, u
))
278 throw last_error(GetLastError());
280 format_size(dev_alloc
, v
, false);
282 wstring_sprintf(t
, u
, v
.c_str());
290 // device unallocated
292 if (!load_string(module
, IDS_USAGE_DEV_UNALLOC
, u
))
293 throw last_error(GetLastError());
295 format_size(dev_size
- dev_alloc
, v
, false);
297 wstring_sprintf(t
, u
, v
.c_str());
307 if (data_alloc
> 0) {
308 if (!load_string(module
, IDS_USAGE_DATA_RATIO
, u
))
309 throw last_error(GetLastError());
311 wstring_sprintf(t
, u
, (float)data_alloc
/ (float)data_size
);
322 if (!load_string(module
, IDS_USAGE_METADATA_RATIO
, u
))
323 throw last_error(GetLastError());
325 wstring_sprintf(t
, u
, (float)metadata_alloc
/ (float)metadata_size
);
327 s
+= t
+ L
"\r\n\r\n";
329 for (i
= 0; i
< sizeof(types
) / sizeof(types
[0]); i
++) {
330 for (j
= 0; j
< sizeof(duptypes
) / sizeof(duptypes
[0]); j
++) {
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
;
337 if (bue
->type
& BLOCK_FLAG_DATA
&& bue
->type
& BLOCK_FLAG_METADATA
&& (types
[i
] == BLOCK_FLAG_DATA
|| types
[i
] == BLOCK_FLAG_METADATA
))
340 if (!load_string(module
, typestrings
[i
], typestring
))
341 throw last_error(GetLastError());
343 if (!load_string(module
, dupstrings
[j
], dupstring
))
344 throw last_error(GetLastError());
346 format_size(bue
->size
, sizestring
, false);
347 format_size(bue
->used
, usedstring
, false);
349 wstring_sprintf(t
, typestring
, dupstring
.c_str(), sizestring
.c_str(), usedstring
.c_str());
353 for (uint64_t k
= 0; k
< bue
->num_devices
; k
++) {
356 format_size(bue
->devices
[k
].alloc
, sizestring
, false);
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";
362 devs
[l
].alloc
+= bue
->devices
[k
].alloc
;
370 if (!load_string(module
, IDS_UNKNOWN_DEVICE
, typestring
))
371 throw last_error(GetLastError());
373 wstring_sprintf(t
, typestring
, bue
->devices
[k
].dev_id
);
376 s
+= t
+ L
"\t"s
+ sizestring
+ L
"\r\n"s
;
378 s
+= t
+ L
"\t" + sizestring
+ L
"\r\n";
388 if (bue
->next_entry
> 0)
389 bue
= (btrfs_usage
*)((uint8_t*)bue
+ bue
->next_entry
);
396 if (!load_string(module
, IDS_USAGE_UNALLOC
, t
))
397 throw last_error(GetLastError());
405 for (size_t k
= 0; k
< min((uint64_t)SIZE_MAX
, num_devs
); k
++) {
408 format_size(devs
[k
].size
- devs
[k
].alloc
, sizestring
, false);
410 s
+= devs
[k
].name
+ L
"\t" + sizestring
+ L
"\r\n";
414 void BtrfsVolPropSheet::RefreshUsage(HWND hwndDlg
) {
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);
421 if (h
!= INVALID_HANDLE_VALUE
) {
423 IO_STATUS_BLOCK iosb
;
424 ULONG devsize
, usagesize
, i
;
429 devices
= (btrfs_device
*)malloc(devsize
);
432 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_DEVICES
, nullptr, 0, devices
, devsize
);
433 if (Status
== STATUS_BUFFER_OVERFLOW
) {
438 devices
= (btrfs_device
*)malloc(devsize
);
447 if (!NT_SUCCESS(Status
))
453 usage
= (btrfs_usage
*)malloc(usagesize
);
456 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_USAGE
, nullptr, 0, usage
, usagesize
);
457 if (Status
== STATUS_BUFFER_OVERFLOW
) {
462 usage
= (btrfs_usage
*)malloc(usagesize
);
471 if (!NT_SUCCESS(Status
)) {
480 FormatUsage(hwndDlg
, s
, usage
);
482 SetDlgItemTextW(hwndDlg
, IDC_USAGE_BOX
, s
.c_str());
487 INT_PTR CALLBACK
BtrfsVolPropSheet::UsageDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
496 IO_STATUS_BLOCK iosb
;
498 EnableThemeDialogTexture(hwndDlg
, ETDT_ENABLETAB
);
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);
503 if (h
!= INVALID_HANDLE_VALUE
) {
509 usage
= (btrfs_usage
*)malloc(usagesize
);
512 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_USAGE
, nullptr, 0, usage
, usagesize
);
513 if (Status
== STATUS_BUFFER_OVERFLOW
) {
518 usage
= (btrfs_usage
*)malloc(usagesize
);
527 if (!NT_SUCCESS(Status
)) {
532 FormatUsage(hwndDlg
, s
, usage
);
534 SetDlgItemTextW(hwndDlg
, IDC_USAGE_BOX
, s
.c_str());
543 switch (HIWORD(wParam
)) {
545 switch (LOWORD(wParam
)) {
548 EndDialog(hwndDlg
, 0);
551 case IDC_USAGE_REFRESH
:
552 RefreshUsage(hwndDlg
);
559 } catch (const exception
& e
) {
560 error_message(hwndDlg
, e
.what());
566 static INT_PTR CALLBACK
stub_UsageDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
567 BtrfsVolPropSheet
* bvps
;
569 if (uMsg
== WM_INITDIALOG
) {
570 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
571 bvps
= (BtrfsVolPropSheet
*)lParam
;
573 bvps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
577 return bvps
->UsageDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
582 void BtrfsVolPropSheet::ShowUsage(HWND hwndDlg
) {
583 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_VOL_USAGE
), hwndDlg
, stub_UsageDlgProc
, (LPARAM
)this);
586 static void add_lv_column(HWND list
, int string
, int cx
) {
590 if (!load_string(module
, string
, s
))
591 throw last_error(GetLastError());
593 lvc
.mask
= LVCF_TEXT
|LVCF_WIDTH
;
594 lvc
.pszText
= (WCHAR
*)s
.c_str();
596 SendMessageW(list
, LVM_INSERTCOLUMNW
, 0, (LPARAM
)&lvc
);
599 static int CALLBACK
lv_sort(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
) {
600 if (lParam1
< lParam2
)
602 else if (lParam1
> lParam2
)
608 static uint64_t find_dev_alloc(uint64_t dev_id
, btrfs_usage
* usage
) {
618 for (k
= 0; k
< bue
->num_devices
; k
++) {
619 if (bue
->devices
[k
].dev_id
== dev_id
)
620 alloc
+= bue
->devices
[k
].alloc
;
623 if (bue
->next_entry
> 0)
624 bue
= (btrfs_usage
*)((uint8_t*)bue
+ bue
->next_entry
);
632 void BtrfsVolPropSheet::RefreshDevList(HWND devlist
) {
634 IO_STATUS_BLOCK iosb
;
635 ULONG usagesize
, devsize
;
639 uint64_t num_rw_devices
;
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);
644 if (h
== INVALID_HANDLE_VALUE
)
645 throw last_error(GetLastError());
653 devices
= (btrfs_device
*)malloc(devsize
);
656 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_DEVICES
, nullptr, 0, devices
, devsize
);
657 if (Status
== STATUS_BUFFER_OVERFLOW
) {
662 devices
= (btrfs_device
*)malloc(devsize
);
671 if (!NT_SUCCESS(Status
))
679 usage
= (btrfs_usage
*)malloc(usagesize
);
682 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_USAGE
, nullptr, 0, usage
, usagesize
);
683 if (Status
== STATUS_BUFFER_OVERFLOW
) {
688 usage
= (btrfs_usage
*)malloc(usagesize
);
699 if (!NT_SUCCESS(Status
)) {
705 SendMessageW(devlist
, LVM_DELETEALLITEMS
, 0, 0);
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
;
722 s
= to_wstring(bd
->dev_id
);
723 lvi
.pszText
= (LPWSTR
)s
.c_str();
725 SendMessageW(devlist
, LVM_INSERTITEMW
, 0, (LPARAM
)&lvi
);
729 lvi
.mask
= LVIF_TEXT
;
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());
741 wstring_sprintf(s
, u
, bd
->device_number
);
743 if (!load_string(module
, IDS_DISK_PART_NUM
, u
))
744 throw last_error(GetLastError());
746 wstring_sprintf(s
, u
, bd
->device_number
, bd
->partition_number
);
749 lvi
.pszText
= (LPWSTR
)s
.c_str();
751 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
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
);
766 format_size(bd
->size
, s
, false);
767 lvi
.pszText
= (LPWSTR
)s
.c_str();
768 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
772 alloc
= find_dev_alloc(bd
->dev_id
, usage
);
775 format_size(alloc
, s
, false);
776 lvi
.pszText
= (LPWSTR
)s
.c_str();
777 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
781 wstring_sprintf(s
, L
"%1.1f%%", (float)alloc
* 100.0f
/ (float)bd
->size
);
783 lvi
.pszText
= (LPWSTR
)s
.c_str();
784 SendMessageW(devlist
, LVM_SETITEMW
, 0, (LPARAM
)&lvi
);
788 if (bd
->next_entry
> 0)
789 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
796 SendMessageW(devlist
, LVM_SORTITEMS
, 0, (LPARAM
)lv_sort
);
798 EnableWindow(GetDlgItem(GetParent(devlist
), IDC_DEVICE_ADD
), num_rw_devices
> 0);
799 EnableWindow(GetDlgItem(GetParent(devlist
), IDC_DEVICE_REMOVE
), num_rw_devices
> 1);
802 void BtrfsVolPropSheet::ResetStats(HWND hwndDlg
) {
804 WCHAR modfn
[MAX_PATH
];
805 SHELLEXECUTEINFOW sei
;
807 sel
= to_wstring(stats_dev
);
809 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
812 t
= L
"\""s
+ modfn
+ L
"\",ResetStats " + fn
+ L
"|" + sel
;
814 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ResetStats ") + fn
+ wstring(L
"|") + sel
;
817 RtlZeroMemory(&sei
, sizeof(sei
));
819 sei
.cbSize
= sizeof(sei
);
821 sei
.lpVerb
= L
"runas";
822 sei
.lpFile
= L
"rundll32.exe";
823 sei
.lpParameters
= t
.c_str();
825 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
827 if (!ShellExecuteExW(&sei
))
828 throw last_error(GetLastError());
830 WaitForSingleObject(sei
.hProcess
, INFINITE
);
831 CloseHandle(sei
.hProcess
);
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);
836 if (h
!= INVALID_HANDLE_VALUE
) {
838 IO_STATUS_BLOCK iosb
;
845 devices
= (btrfs_device
*)malloc(devsize
);
848 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_GET_DEVICES
, nullptr, 0, devices
, devsize
);
849 if (Status
== STATUS_BUFFER_OVERFLOW
) {
854 devices
= (btrfs_device
*)malloc(devsize
);
864 EndDialog(hwndDlg
, 0);
867 INT_PTR CALLBACK
BtrfsVolPropSheet::StatsDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
874 btrfs_device
*bd
, *dev
= nullptr;
877 static int stat_ids
[] = { IDC_WRITE_ERRS
, IDC_READ_ERRS
, IDC_FLUSH_ERRS
, IDC_CORRUPTION_ERRS
, IDC_GENERATION_ERRS
};
882 if (bd
->dev_id
== stats_dev
) {
887 if (bd
->next_entry
> 0)
888 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
894 EndDialog(hwndDlg
, 0);
895 throw string_error(IDS_CANNOT_FIND_DEVICE
);
898 GetDlgItemTextW(hwndDlg
, IDC_DEVICE_ID
, s
, sizeof(s
) / sizeof(WCHAR
));
900 wstring_sprintf(t
, s
, dev
->dev_id
);
902 SetDlgItemTextW(hwndDlg
, IDC_DEVICE_ID
, t
.c_str());
904 for (i
= 0; i
< 5; i
++) {
905 GetDlgItemTextW(hwndDlg
, stat_ids
[i
], s
, sizeof(s
) / sizeof(WCHAR
));
907 wstring_sprintf(t
, s
, dev
->stats
[i
]);
909 SetDlgItemTextW(hwndDlg
, stat_ids
[i
], t
.c_str());
912 SendMessageW(GetDlgItem(hwndDlg
, IDC_RESET_STATS
), BCM_SETSHIELD
, 0, true);
913 EnableWindow(GetDlgItem(hwndDlg
, IDC_RESET_STATS
), !readonly
);
919 switch (HIWORD(wParam
)) {
921 switch (LOWORD(wParam
)) {
924 EndDialog(hwndDlg
, 0);
927 case IDC_RESET_STATS
:
935 } catch (const exception
& e
) {
936 error_message(hwndDlg
, e
.what());
942 static INT_PTR CALLBACK
stub_StatsDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
943 BtrfsVolPropSheet
* bvps
;
945 if (uMsg
== WM_INITDIALOG
) {
946 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
947 bvps
= (BtrfsVolPropSheet
*)lParam
;
949 bvps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
953 return bvps
->StatsDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
958 void BtrfsVolPropSheet::ShowStats(HWND hwndDlg
, uint64_t devid
) {
961 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DEVICE_STATS
), hwndDlg
, stub_StatsDlgProc
, (LPARAM
)this);
964 INT_PTR CALLBACK
BtrfsVolPropSheet::DeviceDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
973 EnableThemeDialogTexture(hwndDlg
, ETDT_ENABLETAB
);
975 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
977 GetClientRect(devlist
, &rect
);
978 w
= rect
.right
- rect
.left
;
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);
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);
991 RefreshDevList(devlist
);
997 switch (HIWORD(wParam
)) {
999 switch (LOWORD(wParam
)) {
1002 KillTimer(hwndDlg
, 1);
1003 EndDialog(hwndDlg
, 0);
1006 case IDC_DEVICE_ADD
:
1009 WCHAR modfn
[MAX_PATH
];
1010 SHELLEXECUTEINFOW sei
;
1012 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1015 t
= L
"\""s
+ modfn
+ L
"\",AddDevice "s
+ fn
;
1017 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",AddDevice ") + fn
;
1020 RtlZeroMemory(&sei
, sizeof(sei
));
1022 sei
.cbSize
= sizeof(sei
);
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
;
1030 if (!ShellExecuteExW(&sei
))
1031 throw last_error(GetLastError());
1033 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1034 CloseHandle(sei
.hProcess
);
1036 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1041 case IDC_DEVICE_REFRESH
:
1042 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1045 case IDC_DEVICE_SHOW_STATS
:
1047 WCHAR sel
[MAX_PATH
];
1051 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1053 auto index
= SendMessageW(devlist
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
1058 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1059 lvi
.mask
= LVIF_TEXT
;
1060 lvi
.iItem
= (int)index
;
1063 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1064 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1066 ShowStats(hwndDlg
, _wtoi(sel
));
1070 case IDC_DEVICE_REMOVE
:
1072 wstring t
, mess
, mess2
, title
;
1073 WCHAR modfn
[MAX_PATH
], sel
[MAX_PATH
], sel2
[MAX_PATH
];
1075 SHELLEXECUTEINFOW sei
;
1078 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1080 auto index
= SendMessageW(devlist
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
1085 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1086 lvi
.mask
= LVIF_TEXT
;
1087 lvi
.iItem
= (int)index
;
1090 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1091 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1095 lvi
.cchTextMax
= sizeof(sel2
) / sizeof(WCHAR
);
1096 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1098 if (!load_string(module
, IDS_REMOVE_DEVICE_CONFIRMATION
, mess
))
1099 throw last_error(GetLastError());
1101 wstring_sprintf(mess2
, mess
, sel
, sel2
);
1103 if (!load_string(module
, IDS_CONFIRMATION_TITLE
, title
))
1104 throw last_error(GetLastError());
1106 if (MessageBoxW(hwndDlg
, mess2
.c_str(), title
.c_str(), MB_YESNO
) != IDYES
)
1109 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1112 t
= L
"\""s
+ modfn
+ L
"\",RemoveDevice "s
+ fn
+ L
"|"s
+ sel
;
1114 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",RemoveDevice ") + fn
+ wstring(L
"|") + sel
;
1117 RtlZeroMemory(&sei
, sizeof(sei
));
1119 sei
.cbSize
= sizeof(sei
);
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
;
1127 if (!ShellExecuteExW(&sei
))
1128 throw last_error(GetLastError());
1130 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1131 CloseHandle(sei
.hProcess
);
1133 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1138 case IDC_DEVICE_RESIZE
:
1143 WCHAR modfn
[MAX_PATH
], sel
[100];
1144 SHELLEXECUTEINFOW sei
;
1146 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1148 auto index
= SendMessageW(devlist
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
1153 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1154 lvi
.mask
= LVIF_TEXT
;
1155 lvi
.iItem
= (int)index
;
1158 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1159 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1161 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1164 t
= L
"\""s
+ modfn
+ L
"\",ResizeDevice "s
+ fn
+ L
"|"s
+ sel
;
1166 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ResizeDevice ") + fn
+ wstring(L
"|") + sel
;
1169 RtlZeroMemory(&sei
, sizeof(sei
));
1171 sei
.cbSize
= sizeof(sei
);
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
;
1179 if (!ShellExecuteExW(&sei
))
1180 throw last_error(GetLastError());
1182 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1183 CloseHandle(sei
.hProcess
);
1185 RefreshDevList(GetDlgItem(hwndDlg
, IDC_DEVLIST
));
1193 switch (((LPNMHDR
)lParam
)->code
) {
1194 case LVN_ITEMCHANGED
:
1196 NMLISTVIEW
* nmv
= (NMLISTVIEW
*)lParam
;
1198 EnableWindow(GetDlgItem(hwndDlg
, IDC_DEVICE_SHOW_STATS
), nmv
->uNewState
& LVIS_SELECTED
);
1200 if (nmv
->uNewState
& LVIS_SELECTED
&& !readonly
) {
1203 bool device_readonly
= false;
1205 WCHAR sel
[MAX_PATH
];
1208 devlist
= GetDlgItem(hwndDlg
, IDC_DEVLIST
);
1210 RtlZeroMemory(&lvi
, sizeof(LVITEMW
));
1211 lvi
.mask
= LVIF_TEXT
;
1212 lvi
.iItem
= nmv
->iItem
;
1215 lvi
.cchTextMax
= sizeof(sel
) / sizeof(WCHAR
);
1216 SendMessageW(devlist
, LVM_GETITEMW
, 0, (LPARAM
)&lvi
);
1222 if (bd
->dev_id
== devid
) {
1223 device_readonly
= bd
->readonly
;
1227 if (bd
->next_entry
> 0)
1228 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
1233 EnableWindow(GetDlgItem(hwndDlg
, IDC_DEVICE_RESIZE
), !device_readonly
);
1235 EnableWindow(GetDlgItem(hwndDlg
, IDC_DEVICE_RESIZE
), false);
1242 } catch (const exception
& e
) {
1243 error_message(hwndDlg
, e
.what());
1249 static INT_PTR CALLBACK
stub_DeviceDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1250 BtrfsVolPropSheet
* bvps
;
1252 if (uMsg
== WM_INITDIALOG
) {
1253 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
1254 bvps
= (BtrfsVolPropSheet
*)lParam
;
1256 bvps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1260 return bvps
->DeviceDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
1265 void BtrfsVolPropSheet::ShowDevices(HWND hwndDlg
) {
1266 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DEVICES
), hwndDlg
, stub_DeviceDlgProc
, (LPARAM
)this);
1269 void BtrfsVolPropSheet::ShowScrub(HWND hwndDlg
) {
1271 WCHAR modfn
[MAX_PATH
];
1272 SHELLEXECUTEINFOW sei
;
1274 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1277 t
= L
"\""s
+ modfn
+ L
"\",ShowScrub "s
+ fn
;
1279 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ShowScrub ") + fn
;
1282 RtlZeroMemory(&sei
, sizeof(sei
));
1284 sei
.cbSize
= sizeof(sei
);
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
;
1292 if (!ShellExecuteExW(&sei
))
1293 throw last_error(GetLastError());
1295 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1296 CloseHandle(sei
.hProcess
);
1299 void BtrfsVolPropSheet::ShowChangeDriveLetter(HWND hwndDlg
) {
1301 WCHAR modfn
[MAX_PATH
];
1302 SHELLEXECUTEINFOW sei
;
1304 GetModuleFileNameW(module
, modfn
, sizeof(modfn
) / sizeof(WCHAR
));
1307 t
= L
"\""s
+ modfn
+ L
"\",ShowChangeDriveLetter "s
+ fn
;
1309 t
= wstring(L
"\"") + modfn
+ wstring(L
"\",ShowChangeDriveLetter ") + fn
;
1312 RtlZeroMemory(&sei
, sizeof(sei
));
1314 sei
.cbSize
= sizeof(sei
);
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
;
1322 if (!ShellExecuteExW(&sei
))
1323 throw last_error(GetLastError());
1325 WaitForSingleObject(sei
.hProcess
, INFINITE
);
1326 CloseHandle(sei
.hProcess
);
1329 static INT_PTR CALLBACK
PropSheetDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1334 PROPSHEETPAGE
* psp
= (PROPSHEETPAGE
*)lParam
;
1335 BtrfsVolPropSheet
* bps
= (BtrfsVolPropSheet
*)psp
->lParam
;
1338 EnableThemeDialogTexture(hwndDlg
, ETDT_ENABLETAB
);
1340 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)bps
);
1342 bps
->readonly
= true;
1346 if (!bd
->readonly
) {
1347 bps
->readonly
= false;
1351 if (bd
->next_entry
> 0)
1352 bd
= (btrfs_device
*)((uint8_t*)bd
+ bd
->next_entry
);
1357 if (bps
->uuid_set
) {
1361 GetDlgItemTextW(hwndDlg
, IDC_UUID
, s
, sizeof(s
) / sizeof(WCHAR
));
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]);
1367 SetDlgItemTextW(hwndDlg
, IDC_UUID
, t
.c_str());
1369 SetDlgItemTextW(hwndDlg
, IDC_UUID
, L
"");
1371 SendMessageW(GetDlgItem(hwndDlg
, IDC_VOL_SCRUB
), BCM_SETSHIELD
, 0, true);
1372 SendMessageW(GetDlgItem(hwndDlg
, IDC_VOL_CHANGE_DRIVE_LETTER
), BCM_SETSHIELD
, 0, true);
1379 switch (((LPNMHDR
)lParam
)->code
) {
1380 case PSN_KILLACTIVE
:
1381 SetWindowLongPtrW(hwndDlg
, DWLP_MSGRESULT
, false);
1389 BtrfsVolPropSheet
* bps
= (BtrfsVolPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1392 switch (HIWORD(wParam
)) {
1394 switch (LOWORD(wParam
)) {
1395 case IDC_VOL_SHOW_USAGE
:
1396 bps
->ShowUsage(hwndDlg
);
1399 case IDC_VOL_BALANCE
:
1400 bps
->balance
->ShowBalance(hwndDlg
);
1403 case IDC_VOL_DEVICES
:
1404 bps
->ShowDevices(hwndDlg
);
1408 bps
->ShowScrub(hwndDlg
);
1411 case IDC_VOL_CHANGE_DRIVE_LETTER
:
1412 bps
->ShowChangeDriveLetter(hwndDlg
);
1422 } catch (const exception
& e
) {
1423 error_message(hwndDlg
, e
.what());
1429 HRESULT __stdcall
BtrfsVolPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
) {
1432 HPROPSHEETPAGE hPage
;
1433 INITCOMMONCONTROLSEX icex
;
1438 icex
.dwSize
= sizeof(icex
);
1439 icex
.dwICC
= ICC_LINK_CLASS
;
1441 if (!InitCommonControlsEx(&icex
))
1442 throw string_error(IDS_INITCOMMONCONTROLSEX_FAILED
);
1444 psp
.dwSize
= sizeof(psp
);
1445 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
;
1446 psp
.hInstance
= module
;
1447 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_VOL_PROP_SHEET
);
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;
1455 hPage
= CreatePropertySheetPage(&psp
);
1458 if (pfnAddPage(hPage
, lParam
)) {
1462 DestroyPropertySheetPage(hPage
);
1464 return E_OUTOFMEMORY
;
1465 } catch (const exception
& e
) {
1466 error_message(nullptr, e
.what());
1472 HRESULT __stdcall
BtrfsVolPropSheet::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
) {
1476 void BtrfsChangeDriveLetter::do_change(HWND hwndDlg
) {
1477 unsigned int sel
= (unsigned int)SendDlgItemMessageW(hwndDlg
, IDC_DRIVE_LETTER_COMBO
, CB_GETCURSEL
, 0, 0);
1479 if (sel
>= 0 && sel
< letters
.size()) {
1482 if (fn
.length() == 3 && fn
[1] == L
':' && fn
[2] == L
'\\') {
1483 dd
= L
"\\DosDevices\\?:";
1488 throw runtime_error("Volume path was not root of drive.");
1490 error_message(nullptr, "Volume path was not root of drive.");
1497 auto v
= mm
.query_points(dd
);
1501 throw runtime_error("Error finding device name.");
1503 error_message(nullptr, "Error finding device name.");
1506 dev_name
= v
[0].device_name
;
1509 wstring new_dd
= L
"\\DosDevices\\?:";
1510 new_dd
[12] = letters
[sel
];
1512 mm
.delete_points(dd
);
1515 mm
.create_point(new_dd
, dev_name
);
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
);
1523 EndDialog(hwndDlg
, 1);
1526 INT_PTR
BtrfsChangeDriveLetter::DlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1531 HWND cb
= GetDlgItem(hwndDlg
, IDC_DRIVE_LETTER_COMBO
);
1533 SendMessageW(cb
, CB_RESETCONTENT
, 0, 0);
1538 drv
= L
"\\DosDevices\\?:";
1540 for (wchar_t l
= 'A'; l
<= 'Z'; l
++) {
1546 auto v
= mm
.query_points(drv
);
1550 } catch (const ntstatus_error
& ntstatus
) {
1551 if (ntstatus
.Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1558 wstring str
= L
"?:";
1561 letters
.push_back(l
);
1563 SendMessageW(cb
, CB_ADDSTRING
, 0, reinterpret_cast<LPARAM
>(str
.c_str()));
1571 switch (HIWORD(wParam
)) {
1573 switch (LOWORD(wParam
)) {
1579 EndDialog(hwndDlg
, 0);
1586 } catch (const exception
& e
) {
1587 error_message(hwndDlg
, e
.what());
1594 INT_PTR CALLBACK
VolPropSheetDlgproc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1596 BtrfsChangeDriveLetter
* bcdl
;
1598 if (uMsg
== WM_INITDIALOG
) {
1599 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
1600 bcdl
= (BtrfsChangeDriveLetter
*)lParam
;
1602 bcdl
= (BtrfsChangeDriveLetter
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1604 return bcdl
->DlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
1608 void BtrfsChangeDriveLetter::show() {
1610 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DRIVE_LETTER
), hwnd
, [](HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1611 BtrfsChangeDriveLetter
* bcdl
;
1613 if (uMsg
== WM_INITDIALOG
) {
1614 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
1615 bcdl
= (BtrfsChangeDriveLetter
*)lParam
;
1617 bcdl
= (BtrfsChangeDriveLetter
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1619 return bcdl
->DlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
1622 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_DRIVE_LETTER
), hwnd
, VolPropSheetDlgproc
, (LPARAM
)this);
1630 void CALLBACK
ResetStatsW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1634 TOKEN_PRIVILEGES tp
;
1637 wstring cmdline
, vol
, dev
;
1639 IO_STATUS_BLOCK iosb
;
1643 cmdline
= lpszCmdLine
;
1645 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
))
1646 throw last_error(GetLastError());
1648 if (!LookupPrivilegeValueW(nullptr, L
"SeManageVolumePrivilege", &luid
))
1649 throw last_error(GetLastError());
1651 tp
.PrivilegeCount
= 1;
1652 tp
.Privileges
[0].Luid
= luid
;
1653 tp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
1655 if (!AdjustTokenPrivileges(token
, false, &tp
, sizeof(TOKEN_PRIVILEGES
), nullptr, nullptr))
1656 throw last_error(GetLastError());
1658 pipe
= cmdline
.find(L
"|");
1660 if (pipe
== string::npos
)
1663 vol
= cmdline
.substr(0, pipe
);
1664 dev
= cmdline
.substr(pipe
+ 1);
1666 devid
= _wtoi(dev
.c_str());
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);
1673 if (h
== INVALID_HANDLE_VALUE
)
1674 throw last_error(GetLastError());
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());
1684 void CALLBACK
ShowChangeDriveLetterW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1685 BtrfsChangeDriveLetter
bcdl(hwnd
, lpszCmdLine
);