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 "propsheet.h"
42 #define SUBVOL_ROOT_INODE 0x100
45 #ifndef __MINGW32__ // in winternl.h in mingw
47 typedef struct _FILE_ACCESS_INFORMATION
{
48 ACCESS_MASK AccessFlags
;
49 } FILE_ACCESS_INFORMATION
, *PFILE_ACCESS_INFORMATION
;
51 #define FileAccessInformation (FILE_INFORMATION_CLASS)8
56 HRESULT __stdcall
BtrfsPropSheet::QueryInterface(REFIID riid
, void **ppObj
) {
57 if (riid
== IID_IUnknown
|| riid
== IID_IShellPropSheetExt
) {
58 *ppObj
= static_cast<IShellPropSheetExt
*>(this);
61 } else if (riid
== IID_IShellExtInit
) {
62 *ppObj
= static_cast<IShellExtInit
*>(this);
71 void BtrfsPropSheet::add_to_search_list(WCHAR
* fn
) {
74 s
= (WCHAR
*)malloc((wcslen(fn
) + 1) * sizeof(WCHAR
));
78 memcpy(s
, fn
, (wcslen(fn
) + 1) * sizeof(WCHAR
));
80 search_list
.push_back(s
);
83 void BtrfsPropSheet::do_search(WCHAR
* fn
) {
88 ss
= (WCHAR
*)malloc((wcslen(fn
) + 3) * sizeof(WCHAR
));
92 memcpy(ss
, fn
, (wcslen(fn
) + 1) * sizeof(WCHAR
));
95 h
= FindFirstFileW(ss
, &ffd
);
96 if (h
== INVALID_HANDLE_VALUE
)
100 if (ffd
.cFileName
[0] != '.' || ((ffd
.cFileName
[1] != 0) && (ffd
.cFileName
[1] != '.' || ffd
.cFileName
[2] != 0))) {
101 WCHAR
* fn2
= (WCHAR
*)malloc((wcslen(fn
) + 1 + wcslen(ffd
.cFileName
) + 1) * sizeof(WCHAR
));
103 memcpy(fn2
, fn
, (wcslen(fn
) + 1) * sizeof(WCHAR
));
105 wcscat(fn2
, ffd
.cFileName
);
107 if (ffd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
108 add_to_search_list(fn2
);
112 fh
= CreateFileW(fn2
, FILE_TRAVERSE
| FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
113 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, NULL
);
115 if (fh
!= INVALID_HANDLE_VALUE
) {
117 IO_STATUS_BLOCK iosb
;
118 btrfs_inode_info bii2
;
120 Status
= NtFsControlFile(fh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_INODE_INFO
, NULL
, 0, &bii2
, sizeof(btrfs_inode_info
));
122 if (NT_SUCCESS(Status
)) {
123 sizes
[0] += bii2
.inline_length
;
124 sizes
[1] += bii2
.disk_size
[0];
125 sizes
[2] += bii2
.disk_size
[1];
126 sizes
[3] += bii2
.disk_size
[2];
127 totalsize
+= bii2
.inline_length
+ bii2
.disk_size
[0] + bii2
.disk_size
[1] + bii2
.disk_size
[2];
136 } while (FindNextFileW(h
, &ffd
));
141 DWORD
BtrfsPropSheet::search_list_thread() {
142 while (!search_list
.empty()) {
143 WCHAR
* fn
= search_list
.front();
147 search_list
.pop_front();
156 static DWORD WINAPI
global_search_list_thread(LPVOID lpParameter
) {
157 BtrfsPropSheet
* bps
= (BtrfsPropSheet
*)lpParameter
;
159 return bps
->search_list_thread();
162 HRESULT
BtrfsPropSheet::check_file(std::wstring fn
, UINT i
, UINT num_files
, UINT
* sv
) {
164 IO_STATUS_BLOCK iosb
;
166 FILE_ACCESS_INFORMATION fai
;
167 BY_HANDLE_FILE_INFORMATION bhfi
;
168 btrfs_inode_info bii2
;
170 h
= CreateFileW(fn
.c_str(), MAXIMUM_ALLOWED
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
171 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, NULL
);
173 if (h
== INVALID_HANDLE_VALUE
)
176 Status
= NtQueryInformationFile(h
, &iosb
, &fai
, sizeof(FILE_ACCESS_INFORMATION
), FileAccessInformation
);
177 if (!NT_SUCCESS(Status
)) {
182 if (fai
.AccessFlags
& FILE_READ_ATTRIBUTES
)
183 can_change_perms
= fai
.AccessFlags
& WRITE_DAC
;
185 readonly
= !(fai
.AccessFlags
& FILE_WRITE_ATTRIBUTES
);
187 if (!readonly
&& num_files
== 1 && !can_change_perms
)
188 show_admin_button
= TRUE
;
190 if (GetFileInformationByHandle(h
, &bhfi
) && bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
191 add_to_search_list((WCHAR
*)fn
.c_str());
193 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_INODE_INFO
, NULL
, 0, &bii2
, sizeof(btrfs_inode_info
));
195 if (NT_SUCCESS(Status
) && !bii2
.top
) {
198 LARGE_INTEGER filesize
;
201 subvol
= bii2
.subvol
;
208 if (subvol
!= bii2
.subvol
)
209 various_subvols
= TRUE
;
211 if (inode
!= bii2
.inode
)
212 various_inodes
= TRUE
;
214 if (type
!= bii2
.type
)
215 various_types
= TRUE
;
217 if (uid
!= bii2
.st_uid
)
220 if (gid
!= bii2
.st_gid
)
224 if (bii2
.inline_length
> 0) {
225 totalsize
+= bii2
.inline_length
;
226 sizes
[0] += bii2
.inline_length
;
229 for (j
= 0; j
< 3; j
++) {
230 if (bii2
.disk_size
[j
] > 0) {
231 totalsize
+= bii2
.disk_size
[j
];
232 sizes
[j
+ 1] += bii2
.disk_size
[j
];
236 min_mode
|= ~bii2
.st_mode
;
237 max_mode
|= bii2
.st_mode
;
238 min_flags
|= ~bii2
.flags
;
239 max_flags
|= bii2
.flags
;
240 min_compression_type
= bii2
.compression_type
< min_compression_type
? bii2
.compression_type
: min_compression_type
;
241 max_compression_type
= bii2
.compression_type
> max_compression_type
? bii2
.compression_type
: max_compression_type
;
243 if (bii2
.inode
== SUBVOL_ROOT_INODE
) {
244 BOOL ro
= bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
;
260 if (bii2
.type
!= BTRFS_TYPE_DIRECTORY
&& GetFileSizeEx(h
, &filesize
)) {
261 if (filesize
.QuadPart
!= 0)
262 can_change_nocow
= FALSE
;
274 HRESULT
BtrfsPropSheet::load_file_list() {
275 UINT num_files
, i
, sv
= 0;
278 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
284 min_compression_type
= 0xff;
285 max_compression_type
= 0;
286 various_subvols
= various_inodes
= various_types
= various_uids
= various_gids
= various_ro
= FALSE
;
288 can_change_perms
= TRUE
;
289 can_change_nocow
= TRUE
;
291 sizes
[0] = sizes
[1] = sizes
[2] = sizes
[3] = 0;
293 for (i
= 0; i
< num_files
; i
++) {
294 if (DragQueryFileW((HDROP
)stgm
.hGlobal
, i
, fn
, sizeof(fn
) / sizeof(MAX_PATH
))) {
297 hr
= check_file(fn
, i
, num_files
, &sv
);
304 min_mode
= ~min_mode
;
305 min_flags
= ~min_flags
;
308 mode_set
= ~(min_mode
^ max_mode
);
311 flags_set
= ~(min_flags
^ max_flags
);
316 HRESULT __stdcall
BtrfsPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder
, IDataObject
* pdtobj
, HKEY hkeyProgID
) {
317 FORMATETC format
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
327 stgm
.tymed
= TYMED_HGLOBAL
;
329 if (FAILED(pdtobj
->GetData(&format
, &stgm
)))
334 hdrop
= (HDROP
)GlobalLock(stgm
.hGlobal
);
337 ReleaseStgMedium(&stgm
);
342 hr
= load_file_list();
346 if (search_list
.size() > 0) {
347 thread
= CreateThread(NULL
, 0, global_search_list_thread
, this, 0, NULL
);
350 ShowError(NULL
, GetLastError());
358 void BtrfsPropSheet::set_cmdline(std::wstring cmdline
) {
360 IO_STATUS_BLOCK iosb
;
363 BY_HANDLE_FILE_INFORMATION bhfi
;
364 btrfs_inode_info bii2
;
365 FILE_ACCESS_INFORMATION fai
;
371 min_compression_type
= 0xff;
372 max_compression_type
= 0;
373 various_subvols
= various_inodes
= various_types
= various_uids
= various_gids
= various_ro
= FALSE
;
375 can_change_perms
= TRUE
;
376 can_change_nocow
= TRUE
;
378 h
= CreateFileW(cmdline
.c_str(), MAXIMUM_ALLOWED
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
379 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, NULL
);
381 if (h
== INVALID_HANDLE_VALUE
)
384 Status
= NtQueryInformationFile(h
, &iosb
, &fai
, sizeof(FILE_ACCESS_INFORMATION
), FileAccessInformation
);
385 if (!NT_SUCCESS(Status
)) {
390 if (fai
.AccessFlags
& FILE_READ_ATTRIBUTES
)
391 can_change_perms
= fai
.AccessFlags
& WRITE_DAC
;
393 readonly
= !(fai
.AccessFlags
& FILE_WRITE_ATTRIBUTES
);
395 if (GetFileInformationByHandle(h
, &bhfi
) && bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
396 add_to_search_list((WCHAR
*)cmdline
.c_str());
398 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_INODE_INFO
, NULL
, 0, &bii2
, sizeof(btrfs_inode_info
));
400 if (NT_SUCCESS(Status
) && !bii2
.top
) {
403 LARGE_INTEGER filesize
;
405 subvol
= bii2
.subvol
;
412 if (bii2
.inline_length
> 0) {
413 totalsize
+= bii2
.inline_length
;
414 sizes
[0] += bii2
.inline_length
;
417 for (j
= 0; j
< 3; j
++) {
418 if (bii2
.disk_size
[j
] > 0) {
419 totalsize
+= bii2
.disk_size
[j
];
420 sizes
[j
+ 1] += bii2
.disk_size
[j
];
424 min_mode
|= ~bii2
.st_mode
;
425 max_mode
|= bii2
.st_mode
;
426 min_flags
|= ~bii2
.flags
;
427 max_flags
|= bii2
.flags
;
428 min_compression_type
= bii2
.compression_type
< min_compression_type
? bii2
.compression_type
: min_compression_type
;
429 max_compression_type
= bii2
.compression_type
> max_compression_type
? bii2
.compression_type
: max_compression_type
;
431 if (bii2
.inode
== SUBVOL_ROOT_INODE
) {
432 BOOL ro
= bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
;
448 if (bii2
.type
!= BTRFS_TYPE_DIRECTORY
&& GetFileSizeEx(h
, &filesize
)) {
449 if (filesize
.QuadPart
!= 0)
450 can_change_nocow
= FALSE
;
459 min_mode
= ~min_mode
;
460 min_flags
= ~min_flags
;
463 mode_set
= ~(min_mode
^ max_mode
);
466 flags_set
= ~(min_flags
^ max_flags
);
468 if (search_list
.size() > 0) {
469 thread
= CreateThread(NULL
, 0, global_search_list_thread
, this, 0, NULL
);
472 ShowError(NULL
, GetLastError());
475 this->filename
= cmdline
;
478 static ULONG
inode_type_to_string_ref(UINT8 type
) {
480 case BTRFS_TYPE_FILE
:
481 return IDS_INODE_FILE
;
483 case BTRFS_TYPE_DIRECTORY
:
484 return IDS_INODE_DIR
;
486 case BTRFS_TYPE_CHARDEV
:
487 return IDS_INODE_CHAR
;
489 case BTRFS_TYPE_BLOCKDEV
:
490 return IDS_INODE_BLOCK
;
492 case BTRFS_TYPE_FIFO
:
493 return IDS_INODE_FIFO
;
495 case BTRFS_TYPE_SOCKET
:
496 return IDS_INODE_SOCKET
;
498 case BTRFS_TYPE_SYMLINK
:
499 return IDS_INODE_SYMLINK
;
502 return IDS_INODE_UNKNOWN
;
506 void BtrfsPropSheet::change_inode_flag(HWND hDlg
, UINT64 flag
, UINT state
) {
507 if (flag
& BTRFS_INODE_NODATACOW
)
508 flag
|= BTRFS_INODE_NODATASUM
;
510 if (state
== BST_CHECKED
) {
513 } else if (state
== BST_UNCHECKED
) {
516 } else if (state
== BST_INDETERMINATE
) {
520 flags_changed
= TRUE
;
522 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, (WPARAM
)hDlg
, 0);
525 void BtrfsPropSheet::apply_changes_file(HWND hDlg
, std::wstring fn
) {
527 IO_STATUS_BLOCK iosb
;
529 btrfs_set_inode_info bsii
;
530 btrfs_inode_info bii2
;
531 ULONG perms
= FILE_TRAVERSE
| FILE_READ_ATTRIBUTES
;
533 if (flags_changed
|| ro_changed
)
534 perms
|= FILE_WRITE_ATTRIBUTES
;
536 if (perms_changed
|| gid_changed
|| uid_changed
)
539 if (mode_set
& S_ISUID
&& (((min_mode
& S_ISUID
) != (max_mode
& S_ISUID
)) || ((min_mode
& S_ISUID
) != (mode
& S_ISUID
))))
540 perms
|= WRITE_OWNER
;
542 h
= CreateFileW(fn
.c_str(), perms
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
543 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, NULL
);
545 if (h
== INVALID_HANDLE_VALUE
) {
546 ShowError(hDlg
, GetLastError());
550 ZeroMemory(&bsii
, sizeof(btrfs_set_inode_info
));
552 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_INODE_INFO
, NULL
, 0, &bii2
, sizeof(btrfs_inode_info
));
554 if (!NT_SUCCESS(Status
)) {
555 ShowNtStatusError(hDlg
, Status
);
560 if (bii2
.inode
== SUBVOL_ROOT_INODE
&& ro_changed
) {
561 BY_HANDLE_FILE_INFORMATION bhfi
;
564 if (!GetFileInformationByHandle(h
, &bhfi
)) {
565 ShowError(hDlg
, GetLastError());
569 memset(&fbi
, 0, sizeof(fbi
));
570 fbi
.FileAttributes
= bhfi
.dwFileAttributes
;
573 fbi
.FileAttributes
|= FILE_ATTRIBUTE_READONLY
;
575 fbi
.FileAttributes
&= ~FILE_ATTRIBUTE_READONLY
;
577 if (!SetFileInformationByHandle(h
, FileBasicInfo
, &fbi
, sizeof(fbi
))) {
579 ShowError(hDlg
, GetLastError());
584 if (flags_changed
|| perms_changed
|| uid_changed
|| gid_changed
|| compress_type_changed
) {
586 bsii
.flags_changed
= TRUE
;
587 bsii
.flags
= (bii2
.flags
& ~flags_set
) | (flags
& flags_set
);
591 bsii
.mode_changed
= TRUE
;
592 bsii
.st_mode
= (bii2
.st_mode
& ~mode_set
) | (mode
& mode_set
);
596 bsii
.uid_changed
= TRUE
;
601 bsii
.gid_changed
= TRUE
;
605 if (compress_type_changed
) {
606 bsii
.compression_type_changed
= TRUE
;
607 bsii
.compression_type
= compress_type
;
610 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_SET_INODE_INFO
, &bsii
, sizeof(btrfs_set_inode_info
), NULL
, 0);
612 if (!NT_SUCCESS(Status
)) {
613 ShowNtStatusError(hDlg
, Status
);
622 void BtrfsPropSheet::apply_changes(HWND hDlg
) {
624 WCHAR fn
[MAX_PATH
]; // FIXME - is this long enough?
632 if (!flags_changed
&& !perms_changed
&& !uid_changed
&& !gid_changed
&& !compress_type_changed
&& !ro_changed
)
635 if (filename
[0] != 0)
636 apply_changes_file(hDlg
, filename
);
638 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
640 for (i
= 0; i
< num_files
; i
++) {
641 if (DragQueryFileW((HDROP
)stgm
.hGlobal
, i
, fn
, sizeof(fn
) / sizeof(MAX_PATH
))) {
642 apply_changes_file(hDlg
, fn
);
647 flags_changed
= FALSE
;
648 perms_changed
= FALSE
;
654 void BtrfsPropSheet::set_size_on_disk(HWND hwndDlg
) {
655 WCHAR size_on_disk
[1024], s
[1024], old_text
[1024];
657 format_size(totalsize
, size_on_disk
, sizeof(size_on_disk
) / sizeof(WCHAR
), TRUE
);
659 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), size_format
, size_on_disk
) == STRSAFE_E_INSUFFICIENT_BUFFER
) {
660 ShowError(hwndDlg
, ERROR_INSUFFICIENT_BUFFER
);
664 GetDlgItemTextW(hwndDlg
, IDC_SIZE_ON_DISK
, old_text
, sizeof(old_text
) / sizeof(WCHAR
));
666 if (wcscmp(s
, old_text
))
667 SetDlgItemTextW(hwndDlg
, IDC_SIZE_ON_DISK
, s
);
670 void BtrfsPropSheet::change_perm_flag(HWND hDlg
, ULONG flag
, UINT state
) {
671 if (state
== BST_CHECKED
) {
674 } else if (state
== BST_UNCHECKED
) {
677 } else if (state
== BST_INDETERMINATE
) {
681 perms_changed
= TRUE
;
683 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, (WPARAM
)hDlg
, 0);
686 void BtrfsPropSheet::change_uid(HWND hDlg
, UINT32 uid
) {
687 if (this->uid
!= uid
) {
691 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, (WPARAM
)hDlg
, 0);
695 void BtrfsPropSheet::change_gid(HWND hDlg
, UINT32 gid
) {
696 if (this->gid
!= gid
) {
700 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, (WPARAM
)hDlg
, 0);
704 void BtrfsPropSheet::update_size_details_dialog(HWND hDlg
) {
705 WCHAR size
[1024], old_text
[1024];
707 ULONG items
[] = { IDC_SIZE_INLINE
, IDC_SIZE_UNCOMPRESSED
, IDC_SIZE_ZLIB
, IDC_SIZE_LZO
};
709 for (i
= 0; i
< 4; i
++) {
710 format_size(sizes
[i
], size
, sizeof(size
) / sizeof(WCHAR
), TRUE
);
712 GetDlgItemTextW(hDlg
, items
[i
], old_text
, sizeof(old_text
) / sizeof(WCHAR
));
714 if (wcscmp(size
, old_text
))
715 SetDlgItemTextW(hDlg
, items
[i
], size
);
719 static INT_PTR CALLBACK
SizeDetailsDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
723 BtrfsPropSheet
* bps
= (BtrfsPropSheet
*)lParam
;
725 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)bps
);
727 bps
->update_size_details_dialog(hwndDlg
);
730 SetTimer(hwndDlg
, 1, 250, NULL
);
736 if (HIWORD(wParam
) == BN_CLICKED
&& (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
)) {
737 EndDialog(hwndDlg
, 0);
744 BtrfsPropSheet
* bps
= (BtrfsPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
747 bps
->update_size_details_dialog(hwndDlg
);
750 KillTimer(hwndDlg
, 1);
760 static void set_check_box(HWND hwndDlg
, ULONG id
, UINT64 min
, UINT64 max
) {
762 SendDlgItemMessage(hwndDlg
, id
, BM_SETCHECK
, BST_CHECKED
, 0);
763 } else if (!min
&& !max
) {
764 SendDlgItemMessage(hwndDlg
, id
, BM_SETCHECK
, BST_UNCHECKED
, 0);
768 style
= GetWindowLongPtr(GetDlgItem(hwndDlg
, id
), GWL_STYLE
);
769 style
&= ~BS_AUTOCHECKBOX
;
770 style
|= BS_AUTO3STATE
;
771 SetWindowLongPtr(GetDlgItem(hwndDlg
, id
), GWL_STYLE
, style
);
773 SendDlgItemMessage(hwndDlg
, id
, BM_SETCHECK
, BST_INDETERMINATE
, 0);
777 void BtrfsPropSheet::open_as_admin(HWND hwndDlg
) {
781 num_files
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0xFFFFFFFF, NULL
, 0);
783 for (i
= 0; i
< num_files
; i
++) {
784 if (DragQueryFileW((HDROP
)stgm
.hGlobal
, i
, fn
, sizeof(fn
) / sizeof(MAX_PATH
))) {
785 WCHAR t
[MAX_PATH
+ 100];
786 SHELLEXECUTEINFOW sei
;
789 GetModuleFileNameW(module
, t
+ 1, (sizeof(t
) / sizeof(WCHAR
)) - 1);
790 wcscat(t
, L
"\",ShowPropSheet ");
793 RtlZeroMemory(&sei
, sizeof(sei
));
795 sei
.cbSize
= sizeof(sei
);
797 sei
.lpVerb
= L
"runas";
798 sei
.lpFile
= L
"rundll32.exe";
799 sei
.lpParameters
= t
;
801 sei
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
803 if (!ShellExecuteExW(&sei
)) {
804 ShowError(hwndDlg
, GetLastError());
808 WaitForSingleObject(sei
.hProcess
, INFINITE
);
809 CloseHandle(sei
.hProcess
);
812 init_propsheet(hwndDlg
);
817 // based on functions in sys/sysmacros.h
818 #define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((UINT32)((rdev) >> 32) & ~0xFFF))
819 #define minor(rdev) (((rdev) & 0xFF) | ((UINT32)((rdev) >> 12) & ~0xFF))
821 void BtrfsPropSheet::init_propsheet(HWND hwndDlg
) {
827 static ULONG perm_controls
[] = { IDC_USERR
, IDC_USERW
, IDC_USERX
, IDC_GROUPR
, IDC_GROUPW
, IDC_GROUPX
, IDC_OTHERR
, IDC_OTHERW
, IDC_OTHERX
,
828 IDC_SETUID
, IDC_SETGID
, IDC_STICKY
, 0 };
829 static ULONG perms
[] = { S_IRUSR
, S_IWUSR
, S_IXUSR
, S_IRGRP
, S_IWGRP
, S_IXGRP
, S_IROTH
, S_IWOTH
, S_IXOTH
, S_ISUID
, S_ISGID
, S_ISVTX
, 0 };
830 static ULONG comp_types
[] = { IDS_COMPRESS_ANY
, IDS_COMPRESS_ZLIB
, IDS_COMPRESS_LZO
, 0 };
832 if (various_subvols
) {
833 if (!LoadStringW(module
, IDS_VARIOUS
, s
, sizeof(s
) / sizeof(WCHAR
))) {
834 ShowError(hwndDlg
, GetLastError());
838 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), L
"%llx", subvol
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
842 SetDlgItemTextW(hwndDlg
, IDC_SUBVOL
, s
);
844 if (various_inodes
) {
845 if (!LoadStringW(module
, IDS_VARIOUS
, s
, sizeof(s
) / sizeof(WCHAR
))) {
846 ShowError(hwndDlg
, GetLastError());
850 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), L
"%llx", inode
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
854 SetDlgItemTextW(hwndDlg
, IDC_INODE
, s
);
859 sr
= inode_type_to_string_ref(type
);
861 if (various_inodes
) {
862 if (sr
== IDS_INODE_CHAR
)
863 sr
= IDS_INODE_CHAR_SIMPLE
;
864 else if (sr
== IDS_INODE_BLOCK
)
865 sr
= IDS_INODE_BLOCK_SIMPLE
;
868 if (sr
== IDS_INODE_UNKNOWN
) {
871 if (!LoadStringW(module
, sr
, t
, sizeof(t
) / sizeof(WCHAR
))) {
872 ShowError(hwndDlg
, GetLastError());
876 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), t
, type
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
878 } else if (sr
== IDS_INODE_CHAR
|| sr
== IDS_INODE_BLOCK
) {
881 if (!LoadStringW(module
, sr
, t
, sizeof(t
) / sizeof(WCHAR
))) {
882 ShowError(hwndDlg
, GetLastError());
886 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), t
, major(rdev
), minor(rdev
)) == STRSAFE_E_INSUFFICIENT_BUFFER
)
889 if (!LoadStringW(module
, sr
, s
, sizeof(s
) / sizeof(WCHAR
))) {
890 ShowError(hwndDlg
, GetLastError());
895 SetDlgItemTextW(hwndDlg
, IDC_TYPE
, s
);
897 GetDlgItemTextW(hwndDlg
, IDC_SIZE_ON_DISK
, size_format
, sizeof(size_format
) / sizeof(WCHAR
));
898 set_size_on_disk(hwndDlg
);
901 SetTimer(hwndDlg
, 1, 250, NULL
);
903 set_check_box(hwndDlg
, IDC_NODATACOW
, min_flags
& BTRFS_INODE_NODATACOW
, max_flags
& BTRFS_INODE_NODATACOW
);
904 set_check_box(hwndDlg
, IDC_COMPRESS
, min_flags
& BTRFS_INODE_COMPRESS
, max_flags
& BTRFS_INODE_COMPRESS
);
906 comptype
= GetDlgItem(hwndDlg
, IDC_COMPRESS_TYPE
);
908 if (min_compression_type
!= max_compression_type
) {
909 SendMessage(comptype
, CB_ADDSTRING
, NULL
, (LPARAM
)L
"");
910 SendMessage(comptype
, CB_SETCURSEL
, 0, 0);
914 while (comp_types
[i
] != 0) {
917 if (!LoadStringW(module
, comp_types
[i
], t
, sizeof(t
) / sizeof(WCHAR
))) {
918 ShowError(hwndDlg
, GetLastError());
922 SendMessage(comptype
, CB_ADDSTRING
, NULL
, (LPARAM
)t
);
927 if (min_compression_type
== max_compression_type
) {
928 SendMessage(comptype
, CB_SETCURSEL
, min_compression_type
, 0);
929 compress_type
= min_compression_type
;
932 EnableWindow(comptype
, max_flags
& BTRFS_INODE_COMPRESS
);
935 while (perm_controls
[i
] != 0) {
936 set_check_box(hwndDlg
, perm_controls
[i
], min_mode
& perms
[i
], max_mode
& perms
[i
]);
941 if (!LoadStringW(module
, IDS_VARIOUS
, s
, sizeof(s
) / sizeof(WCHAR
))) {
942 ShowError(hwndDlg
, GetLastError());
946 EnableWindow(GetDlgItem(hwndDlg
, IDC_UID
), 0);
948 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), L
"%u", uid
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
952 SetDlgItemTextW(hwndDlg
, IDC_UID
, s
);
955 if (!LoadStringW(module
, IDS_VARIOUS
, s
, sizeof(s
) / sizeof(WCHAR
))) {
956 ShowError(hwndDlg
, GetLastError());
960 EnableWindow(GetDlgItem(hwndDlg
, IDC_GID
), 0);
962 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), L
"%u", gid
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
966 SetDlgItemTextW(hwndDlg
, IDC_GID
, s
);
968 ShowWindow(GetDlgItem(hwndDlg
, IDC_SUBVOL_RO
), has_subvols
);
971 set_check_box(hwndDlg
, IDC_SUBVOL_RO
, ro_subvol
, various_ro
? (!ro_subvol
) : ro_subvol
);
973 if (!can_change_nocow
)
974 EnableWindow(GetDlgItem(hwndDlg
, IDC_NODATACOW
), 0);
976 if (!can_change_perms
) {
978 while (perm_controls
[i
] != 0) {
979 EnableWindow(GetDlgItem(hwndDlg
, perm_controls
[i
]), 0);
983 EnableWindow(GetDlgItem(hwndDlg
, IDC_UID
), 0);
984 EnableWindow(GetDlgItem(hwndDlg
, IDC_GID
), 0);
985 EnableWindow(GetDlgItem(hwndDlg
, IDC_SETUID
), 0);
989 EnableWindow(GetDlgItem(hwndDlg
, IDC_NODATACOW
), 0);
990 EnableWindow(GetDlgItem(hwndDlg
, IDC_COMPRESS
), 0);
991 EnableWindow(GetDlgItem(hwndDlg
, IDC_COMPRESS_TYPE
), 0);
994 if (show_admin_button
) {
995 SendMessageW(GetDlgItem(hwndDlg
, IDC_OPEN_ADMIN
), BCM_SETSHIELD
, 0, TRUE
);
996 ShowWindow(GetDlgItem(hwndDlg
, IDC_OPEN_ADMIN
), SW_SHOW
);
998 ShowWindow(GetDlgItem(hwndDlg
, IDC_OPEN_ADMIN
), SW_HIDE
);
1001 static INT_PTR CALLBACK
PropSheetDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1005 PROPSHEETPAGE
* psp
= (PROPSHEETPAGE
*)lParam
;
1006 BtrfsPropSheet
* bps
= (BtrfsPropSheet
*)psp
->lParam
;
1008 EnableThemeDialogTexture(hwndDlg
, ETDT_ENABLETAB
);
1010 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)bps
);
1012 bps
->init_propsheet(hwndDlg
);
1019 BtrfsPropSheet
* bps
= (BtrfsPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1021 if (bps
&& !bps
->readonly
) {
1022 switch (HIWORD(wParam
)) {
1024 switch (LOWORD(wParam
)) {
1026 bps
->change_inode_flag(hwndDlg
, BTRFS_INODE_NODATACOW
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1030 bps
->change_inode_flag(hwndDlg
, BTRFS_INODE_COMPRESS
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1032 EnableWindow(GetDlgItem(hwndDlg
, IDC_COMPRESS_TYPE
), IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)) != BST_UNCHECKED
);
1036 bps
->change_perm_flag(hwndDlg
, S_IRUSR
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1040 bps
->change_perm_flag(hwndDlg
, S_IWUSR
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1044 bps
->change_perm_flag(hwndDlg
, S_IXUSR
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1048 bps
->change_perm_flag(hwndDlg
, S_IRGRP
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1052 bps
->change_perm_flag(hwndDlg
, S_IWGRP
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1056 bps
->change_perm_flag(hwndDlg
, S_IXGRP
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1060 bps
->change_perm_flag(hwndDlg
, S_IROTH
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1064 bps
->change_perm_flag(hwndDlg
, S_IWOTH
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1068 bps
->change_perm_flag(hwndDlg
, S_IXOTH
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1072 bps
->change_perm_flag(hwndDlg
, S_ISUID
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1076 bps
->change_perm_flag(hwndDlg
, S_ISGID
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1080 bps
->change_perm_flag(hwndDlg
, S_ISVTX
, IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
)));
1084 switch (IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
))) {
1086 bps
->ro_subvol
= TRUE
;
1087 bps
->ro_changed
= TRUE
;
1091 bps
->ro_subvol
= FALSE
;
1092 bps
->ro_changed
= TRUE
;
1095 case BST_INDETERMINATE
:
1096 bps
->ro_changed
= FALSE
;
1100 SendMessageW(GetParent(hwndDlg
), PSM_CHANGED
, (WPARAM
)hwndDlg
, 0);
1103 case IDC_OPEN_ADMIN
:
1104 bps
->open_as_admin(hwndDlg
);
1112 switch (LOWORD(wParam
)) {
1116 GetDlgItemTextW(hwndDlg
, LOWORD(wParam
), s
, sizeof(s
) / sizeof(WCHAR
));
1118 bps
->change_uid(hwndDlg
, _wtoi(s
));
1125 GetDlgItemTextW(hwndDlg
, LOWORD(wParam
), s
, sizeof(s
) / sizeof(WCHAR
));
1127 bps
->change_gid(hwndDlg
, _wtoi(s
));
1135 case CBN_SELCHANGE
: {
1136 switch (LOWORD(wParam
)) {
1137 case IDC_COMPRESS_TYPE
: {
1138 int sel
= SendMessageW(GetDlgItem(hwndDlg
, LOWORD(wParam
)), CB_GETCURSEL
, 0, 0);
1140 if (bps
->min_compression_type
!= bps
->max_compression_type
) {
1142 bps
->compress_type_changed
= FALSE
;
1144 bps
->compress_type
= sel
- 1;
1145 bps
->compress_type_changed
= TRUE
;
1148 bps
->compress_type
= sel
;
1149 bps
->compress_type_changed
= TRUE
;
1152 SendMessageW(GetParent(hwndDlg
), PSM_CHANGED
, (WPARAM
)hwndDlg
, 0);
1168 switch (((LPNMHDR
)lParam
)->code
) {
1169 case PSN_KILLACTIVE
:
1170 SetWindowLongPtrW(hwndDlg
, DWLP_MSGRESULT
, FALSE
);
1174 BtrfsPropSheet
* bps
= (BtrfsPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1176 bps
->apply_changes(hwndDlg
);
1177 SetWindowLongPtrW(hwndDlg
, DWLP_MSGRESULT
, PSNRET_NOERROR
);
1183 if (((LPNMHDR
)lParam
)->hwndFrom
== GetDlgItem(hwndDlg
, IDC_SIZE_ON_DISK
)) {
1184 PNMLINK pNMLink
= (PNMLINK
)lParam
;
1186 if (pNMLink
->item
.iLink
== 0)
1187 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_SIZE_DETAILS
), hwndDlg
, SizeDetailsDlgProc
, GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
));
1196 BtrfsPropSheet
* bps
= (BtrfsPropSheet
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1199 bps
->set_size_on_disk(hwndDlg
);
1202 KillTimer(hwndDlg
, 1);
1212 HRESULT __stdcall
BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
) {
1214 HPROPSHEETPAGE hPage
;
1215 INITCOMMONCONTROLSEX icex
;
1220 icex
.dwSize
= sizeof(icex
);
1221 icex
.dwICC
= ICC_LINK_CLASS
;
1223 if (!InitCommonControlsEx(&icex
)) {
1224 MessageBoxW(NULL
, L
"InitCommonControlsEx failed", L
"Error", MB_ICONERROR
);
1227 psp
.dwSize
= sizeof(psp
);
1228 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
;
1229 psp
.hInstance
= module
;
1230 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_PROP_SHEET
);
1232 psp
.pszTitle
= MAKEINTRESOURCE(IDS_PROP_SHEET_TITLE
);
1233 psp
.pfnDlgProc
= (DLGPROC
)PropSheetDlgProc
;
1234 psp
.pcRefParent
= (UINT
*)&objs_loaded
;
1235 psp
.pfnCallback
= NULL
;
1236 psp
.lParam
= (LPARAM
)this;
1238 hPage
= CreatePropertySheetPage(&psp
);
1241 if (pfnAddPage(hPage
, lParam
)) {
1245 DestroyPropertySheetPage(hPage
);
1247 return E_OUTOFMEMORY
;
1252 HRESULT __stdcall
BtrfsPropSheet::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
) {
1260 void CALLBACK
ShowPropSheetW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1261 BtrfsPropSheet
* bps
;
1263 PROPSHEETHEADERW psh
;
1268 LoadStringW(module
, IDS_STANDALONE_PROPSHEET_TITLE
, title
, sizeof(title
) / sizeof(WCHAR
));
1270 bps
= new BtrfsPropSheet
;
1271 bps
->set_cmdline(lpszCmdLine
);
1273 psp
.dwSize
= sizeof(psp
);
1274 psp
.dwFlags
= PSP_USETITLE
;
1275 psp
.hInstance
= module
;
1276 psp
.pszTemplate
= MAKEINTRESOURCEW(IDD_PROP_SHEET
);
1278 psp
.pszTitle
= MAKEINTRESOURCEW(IDS_PROP_SHEET_TITLE
);
1279 psp
.pfnDlgProc
= (DLGPROC
)PropSheetDlgProc
;
1280 psp
.pfnCallback
= NULL
;
1281 psp
.lParam
= (LPARAM
)bps
;
1283 memset(&psh
, 0, sizeof(PROPSHEETHEADERW
));
1285 psh
.dwSize
= sizeof(PROPSHEETHEADERW
);
1286 psh
.dwFlags
= PSH_PROPSHEETPAGE
;
1287 psh
.hwndParent
= hwnd
;
1288 psh
.hInstance
= psp
.hInstance
;
1289 psh
.pszCaption
= title
;
1293 PropertySheetW(&psh
);