1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #define ISOLATION_AWARE_ENABLED 1
19 #define STRSAFE_NO_DEPRECATE
20
21 #include "shellext.h"
22 #ifndef __REACTOS__
23 #include <windows.h>
24 #include <strsafe.h>
25 #include <winternl.h>
26 #else
27 #define WIN32_NO_STATUS
28 #include <windef.h>
29 #include <winbase.h>
30 #include <strsafe.h>
31 #include <ndk/iofuncs.h>
32 #include <ndk/iotypes.h>
33 #endif
34
35 #define NO_SHLWAPI_STRFCNS
36 #include <shlwapi.h>
37 #include <uxtheme.h>
38
39 #include "propsheet.h"
40 #include "resource.h"
41
42 #define SUBVOL_ROOT_INODE 0x100
43
44 #ifndef __REACTOS__
45 #ifndef __MINGW32__ // in winternl.h in mingw
46
47 typedef struct _FILE_ACCESS_INFORMATION {
48 ACCESS_MASK AccessFlags;
49 } FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
50
51 #define FileAccessInformation (FILE_INFORMATION_CLASS)8
52
53 typedef struct _FILE_STANDARD_INFORMATION {
54 LARGE_INTEGER AllocationSize;
55 LARGE_INTEGER EndOfFile;
56 ULONG NumberOfLinks;
57 BOOLEAN DeletePending;
58 BOOLEAN Directory;
59 } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
60
61 #define FileStandardInformation (FILE_INFORMATION_CLASS)5
62
63 typedef struct _FILE_FS_SIZE_INFORMATION {
64 LARGE_INTEGER TotalAllocationUnits;
65 LARGE_INTEGER AvailableAllocationUnits;
66 ULONG SectorsPerAllocationUnit;
67 ULONG BytesPerSector;
68 } FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
69
70 #endif
71 #endif
72
QueryInterface(REFIID riid,void ** ppObj)73 HRESULT __stdcall BtrfsPropSheet::QueryInterface(REFIID riid, void **ppObj) {
74 if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
75 *ppObj = static_cast<IShellPropSheetExt*>(this);
76 AddRef();
77 return S_OK;
78 } else if (riid == IID_IShellExtInit) {
79 *ppObj = static_cast<IShellExtInit*>(this);
80 AddRef();
81 return S_OK;
82 }
83
84 *ppObj = nullptr;
85 return E_NOINTERFACE;
86 }
87
do_search(const wstring & fn)88 void BtrfsPropSheet::do_search(const wstring& fn) {
89 wstring ss;
90 WIN32_FIND_DATAW ffd;
91
92 #ifndef __REACTOS__
93 ss = fn + L"\\*"s;
94 #else
95 ss = fn + wstring(L"\\*");
96 #endif
97
98 fff_handle h = FindFirstFileW(ss.c_str(), &ffd);
99 if (h == INVALID_HANDLE_VALUE)
100 return;
101
102 do {
103 if (ffd.cFileName[0] != '.' || ((ffd.cFileName[1] != 0) && (ffd.cFileName[1] != '.' || ffd.cFileName[2] != 0))) {
104 wstring fn2;
105
106 #ifndef __REACTOS__
107 fn2 = fn + L"\\"s + ffd.cFileName;
108 #else
109 fn2 = fn + wstring(L"\\") + ffd.cFileName;
110 #endif
111
112 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
113 search_list.push_back(fn2);
114 else {
115 win_handle fh = CreateFileW(fn2.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
116 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
117
118 if (fh != INVALID_HANDLE_VALUE) {
119 NTSTATUS Status;
120 IO_STATUS_BLOCK iosb;
121 btrfs_inode_info bii2;
122
123 memset(&bii2, 0, sizeof(bii2));
124
125 Status = NtFsControlFile(fh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
126
127 if (NT_SUCCESS(Status)) {
128 sizes[0] += bii2.inline_length;
129 sizes[1] += bii2.disk_size_uncompressed;
130 sizes[2] += bii2.disk_size_zlib;
131 sizes[3] += bii2.disk_size_lzo;
132 sizes[4] += bii2.disk_size_zstd;
133 totalsize += bii2.inline_length + bii2.disk_size_uncompressed + bii2.disk_size_zlib + bii2.disk_size_lzo + bii2.disk_size_zstd;
134 sparsesize += bii2.sparse_size;
135 num_extents += bii2.num_extents == 0 ? 0 : (bii2.num_extents - 1);
136 }
137
138 FILE_STANDARD_INFORMATION fsi;
139
140 Status = NtQueryInformationFile(fh, &iosb, &fsi, sizeof(fsi), FileStandardInformation);
141
142 if (NT_SUCCESS(Status)) {
143 if (bii2.inline_length > 0)
144 allocsize += fsi.EndOfFile.QuadPart;
145 else
146 allocsize += fsi.AllocationSize.QuadPart;
147 }
148 }
149 }
150 }
151 } while (FindNextFileW(h, &ffd));
152 }
153
search_list_thread()154 DWORD BtrfsPropSheet::search_list_thread() {
155 while (!search_list.empty()) {
156 do_search(search_list.front());
157
158 search_list.pop_front();
159 }
160
161 thread = nullptr;
162
163 return 0;
164 }
165
global_search_list_thread(LPVOID lpParameter)166 static DWORD WINAPI global_search_list_thread(LPVOID lpParameter) {
167 BtrfsPropSheet* bps = (BtrfsPropSheet*)lpParameter;
168
169 return bps->search_list_thread();
170 }
171
check_file(const wstring & fn,UINT i,UINT num_files,UINT * sv)172 HRESULT BtrfsPropSheet::check_file(const wstring& fn, UINT i, UINT num_files, UINT* sv) {
173 win_handle h;
174 IO_STATUS_BLOCK iosb;
175 NTSTATUS Status;
176 FILE_ACCESS_INFORMATION fai;
177 BY_HANDLE_FILE_INFORMATION bhfi;
178 btrfs_inode_info bii2;
179
180 h = CreateFileW(fn.c_str(), MAXIMUM_ALLOWED, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
181 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
182
183 if (h == INVALID_HANDLE_VALUE)
184 return E_FAIL;
185
186 Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
187 if (!NT_SUCCESS(Status))
188 return E_FAIL;
189
190 if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
191 can_change_perms = fai.AccessFlags & WRITE_DAC;
192
193 readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
194
195 if (!readonly && num_files == 1 && !can_change_perms)
196 show_admin_button = true;
197
198 if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
199 search_list.push_back(fn);
200
201 memset(&bii2, 0, sizeof(bii2));
202
203 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
204
205 if (NT_SUCCESS(Status) && !bii2.top) {
206 LARGE_INTEGER filesize;
207
208 if (i == 0) {
209 subvol = bii2.subvol;
210 inode = bii2.inode;
211 type = bii2.type;
212 uid = bii2.st_uid;
213 gid = bii2.st_gid;
214 rdev = bii2.st_rdev;
215 } else {
216 if (subvol != bii2.subvol)
217 various_subvols = true;
218
219 if (inode != bii2.inode)
220 various_inodes = true;
221
222 if (type != bii2.type)
223 various_types = true;
224
225 if (uid != bii2.st_uid)
226 various_uids = true;
227
228 if (gid != bii2.st_gid)
229 various_gids = true;
230 }
231
232 if (bii2.inline_length > 0) {
233 totalsize += bii2.inline_length;
234 sizes[0] += bii2.inline_length;
235 }
236
237 if (bii2.disk_size_uncompressed > 0) {
238 totalsize += bii2.disk_size_uncompressed;
239 sizes[1] += bii2.disk_size_uncompressed;
240 }
241
242 if (bii2.disk_size_zlib > 0) {
243 totalsize += bii2.disk_size_zlib;
244 sizes[2] += bii2.disk_size_zlib;
245 }
246
247 if (bii2.disk_size_lzo > 0) {
248 totalsize += bii2.disk_size_lzo;
249 sizes[3] += bii2.disk_size_lzo;
250 }
251
252 if (bii2.disk_size_zstd > 0) {
253 totalsize += bii2.disk_size_zstd;
254 sizes[4] += bii2.disk_size_zstd;
255 }
256
257 sparsesize += bii2.sparse_size;
258 num_extents += bii2.num_extents == 0 ? 0 : (bii2.num_extents - 1);
259
260 FILE_STANDARD_INFORMATION fsi;
261
262 Status = NtQueryInformationFile(h, &iosb, &fsi, sizeof(fsi), FileStandardInformation);
263
264 if (!NT_SUCCESS(Status))
265 throw ntstatus_error(Status);
266
267 if (bii2.inline_length > 0)
268 allocsize += fsi.EndOfFile.QuadPart;
269 else
270 allocsize += fsi.AllocationSize.QuadPart;
271
272 min_mode |= ~bii2.st_mode;
273 max_mode |= bii2.st_mode;
274 min_flags |= ~bii2.flags;
275 max_flags |= bii2.flags;
276 min_compression_type = bii2.compression_type < min_compression_type ? bii2.compression_type : min_compression_type;
277 max_compression_type = bii2.compression_type > max_compression_type ? bii2.compression_type : max_compression_type;
278
279 if (bii2.inode == SUBVOL_ROOT_INODE) {
280 bool ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
281
282 has_subvols = true;
283
284 if (*sv == 0)
285 ro_subvol = ro;
286 else {
287 if (ro_subvol != ro)
288 various_ro = true;
289 }
290
291 (*sv)++;
292 }
293
294 ignore = false;
295
296 if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
297 if (filesize.QuadPart != 0)
298 can_change_nocow = false;
299 }
300
301 {
302 FILE_FS_SIZE_INFORMATION ffsi;
303
304 Status = NtQueryVolumeInformationFile(h, &iosb, &ffsi, sizeof(ffsi), FileFsSizeInformation);
305
306 if (NT_SUCCESS(Status))
307 sector_size = ffsi.BytesPerSector;
308
309 if (sector_size == 0)
310 sector_size = 4096;
311 }
312 } else
313 return E_FAIL;
314
315 return S_OK;
316 }
317
load_file_list()318 HRESULT BtrfsPropSheet::load_file_list() {
319 UINT num_files, i, sv = 0;
320 WCHAR fn[MAX_PATH];
321
322 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
323
324 min_mode = 0;
325 max_mode = 0;
326 min_flags = 0;
327 max_flags = 0;
328 min_compression_type = 0xff;
329 max_compression_type = 0;
330 various_subvols = various_inodes = various_types = various_uids = various_gids = various_ro = false;
331
332 can_change_perms = true;
333 can_change_nocow = true;
334
335 sizes[0] = sizes[1] = sizes[2] = sizes[3] = sizes[4] = 0;
336 totalsize = allocsize = sparsesize = 0;
337
338 for (i = 0; i < num_files; i++) {
339 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
340 HRESULT hr;
341
342 hr = check_file(fn, i, num_files, &sv);
343 if (FAILED(hr))
344 return hr;
345 } else
346 return E_FAIL;
347 }
348
349 min_mode = ~min_mode;
350 min_flags = ~min_flags;
351
352 mode = min_mode;
353 mode_set = ~(min_mode ^ max_mode);
354
355 flags = min_flags;
356 flags_set = ~(min_flags ^ max_flags);
357
358 return S_OK;
359 }
360
Initialize(PCIDLIST_ABSOLUTE pidlFolder,IDataObject * pdtobj,HKEY hkeyProgID)361 HRESULT __stdcall BtrfsPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
362 try {
363 FORMATETC format = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
364 HDROP hdrop;
365 HRESULT hr;
366
367 if (pidlFolder)
368 return E_FAIL;
369
370 if (!pdtobj)
371 return E_FAIL;
372
373 stgm.tymed = TYMED_HGLOBAL;
374
375 if (FAILED(pdtobj->GetData(&format, &stgm)))
376 return E_INVALIDARG;
377
378 stgm_set = true;
379
380 hdrop = (HDROP)GlobalLock(stgm.hGlobal);
381
382 if (!hdrop) {
383 ReleaseStgMedium(&stgm);
384 stgm_set = false;
385 return E_INVALIDARG;
386 }
387
388 try {
389 hr = load_file_list();
390 if (FAILED(hr))
391 return hr;
392
393 if (search_list.size() > 0) {
394 thread = CreateThread(nullptr, 0, global_search_list_thread, this, 0, nullptr);
395
396 if (!thread)
397 throw last_error(GetLastError());
398 }
399 } catch (...) {
400 GlobalUnlock(hdrop);
401 throw;
402 }
403
404 GlobalUnlock(hdrop);
405 } catch (const exception& e) {
406 error_message(nullptr, e.what());
407
408 return E_FAIL;
409 }
410
411 return S_OK;
412 }
413
set_cmdline(const wstring & cmdline)414 void BtrfsPropSheet::set_cmdline(const wstring& cmdline) {
415 win_handle h;
416 IO_STATUS_BLOCK iosb;
417 NTSTATUS Status;
418 UINT sv = 0;
419 BY_HANDLE_FILE_INFORMATION bhfi;
420 btrfs_inode_info bii2;
421 FILE_ACCESS_INFORMATION fai;
422
423 min_mode = 0;
424 max_mode = 0;
425 min_flags = 0;
426 max_flags = 0;
427 min_compression_type = 0xff;
428 max_compression_type = 0;
429 various_subvols = various_inodes = various_types = various_uids = various_gids = various_ro = false;
430
431 can_change_perms = true;
432 can_change_nocow = true;
433
434 h = CreateFileW(cmdline.c_str(), MAXIMUM_ALLOWED, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
435 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
436
437 if (h == INVALID_HANDLE_VALUE)
438 throw last_error(GetLastError());
439
440 Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
441 if (!NT_SUCCESS(Status))
442 throw ntstatus_error(Status);
443
444 if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
445 can_change_perms = fai.AccessFlags & WRITE_DAC;
446
447 readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
448
449 if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
450 search_list.push_back(cmdline);
451
452 memset(&bii2, 0, sizeof(bii2));
453
454 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
455
456 if (!NT_SUCCESS(Status))
457 throw ntstatus_error(Status);
458
459 if (!bii2.top) {
460 LARGE_INTEGER filesize;
461
462 subvol = bii2.subvol;
463 inode = bii2.inode;
464 type = bii2.type;
465 uid = bii2.st_uid;
466 gid = bii2.st_gid;
467 rdev = bii2.st_rdev;
468
469 if (bii2.inline_length > 0) {
470 totalsize += bii2.inline_length;
471 sizes[0] += bii2.inline_length;
472 }
473
474 if (bii2.disk_size_uncompressed > 0) {
475 totalsize += bii2.disk_size_uncompressed;
476 sizes[1] += bii2.disk_size_uncompressed;
477 }
478
479 if (bii2.disk_size_zlib > 0) {
480 totalsize += bii2.disk_size_zlib;
481 sizes[2] += bii2.disk_size_zlib;
482 }
483
484 if (bii2.disk_size_lzo > 0) {
485 totalsize += bii2.disk_size_lzo;
486 sizes[3] += bii2.disk_size_lzo;
487 }
488
489 if (bii2.disk_size_zstd > 0) {
490 totalsize += bii2.disk_size_zstd;
491 sizes[4] += bii2.disk_size_zstd;
492 }
493
494 sparsesize += bii2.sparse_size;
495
496 FILE_STANDARD_INFORMATION fsi;
497
498 Status = NtQueryInformationFile(h, &iosb, &fsi, sizeof(fsi), FileStandardInformation);
499
500 if (!NT_SUCCESS(Status))
501 throw ntstatus_error(Status);
502
503 if (bii2.inline_length > 0)
504 allocsize += fsi.EndOfFile.QuadPart;
505 else
506 allocsize += fsi.AllocationSize.QuadPart;
507
508 min_mode |= ~bii2.st_mode;
509 max_mode |= bii2.st_mode;
510 min_flags |= ~bii2.flags;
511 max_flags |= bii2.flags;
512 min_compression_type = bii2.compression_type < min_compression_type ? bii2.compression_type : min_compression_type;
513 max_compression_type = bii2.compression_type > max_compression_type ? bii2.compression_type : max_compression_type;
514
515 if (bii2.inode == SUBVOL_ROOT_INODE) {
516 bool ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
517
518 has_subvols = true;
519
520 if (sv == 0)
521 ro_subvol = ro;
522 else {
523 if (ro_subvol != ro)
524 various_ro = true;
525 }
526
527 sv++;
528 }
529
530 ignore = false;
531
532 if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
533 if (filesize.QuadPart != 0)
534 can_change_nocow = false;
535 }
536 } else
537 return;
538
539 min_mode = ~min_mode;
540 min_flags = ~min_flags;
541
542 mode = min_mode;
543 mode_set = ~(min_mode ^ max_mode);
544
545 flags = min_flags;
546 flags_set = ~(min_flags ^ max_flags);
547
548 if (search_list.size() > 0) {
549 thread = CreateThread(nullptr, 0, global_search_list_thread, this, 0, nullptr);
550
551 if (!thread)
552 throw last_error(GetLastError());
553 }
554
555 this->filename = cmdline;
556 }
557
inode_type_to_string_ref(uint8_t type)558 static ULONG inode_type_to_string_ref(uint8_t type) {
559 switch (type) {
560 case BTRFS_TYPE_FILE:
561 return IDS_INODE_FILE;
562
563 case BTRFS_TYPE_DIRECTORY:
564 return IDS_INODE_DIR;
565
566 case BTRFS_TYPE_CHARDEV:
567 return IDS_INODE_CHAR;
568
569 case BTRFS_TYPE_BLOCKDEV:
570 return IDS_INODE_BLOCK;
571
572 case BTRFS_TYPE_FIFO:
573 return IDS_INODE_FIFO;
574
575 case BTRFS_TYPE_SOCKET:
576 return IDS_INODE_SOCKET;
577
578 case BTRFS_TYPE_SYMLINK:
579 return IDS_INODE_SYMLINK;
580
581 default:
582 return IDS_INODE_UNKNOWN;
583 }
584 }
585
change_inode_flag(HWND hDlg,uint64_t flag,UINT state)586 void BtrfsPropSheet::change_inode_flag(HWND hDlg, uint64_t flag, UINT state) {
587 if (flag & BTRFS_INODE_NODATACOW)
588 flag |= BTRFS_INODE_NODATASUM;
589
590 if (state == BST_CHECKED) {
591 flags |= flag;
592 flags_set |= flag;
593 } else if (state == BST_UNCHECKED) {
594 flags &= ~flag;
595 flags_set |= flag;
596 } else if (state == BST_INDETERMINATE) {
597 flags_set = ~flag;
598 }
599
600 if (flags & BTRFS_INODE_NODATACOW && flags_set & BTRFS_INODE_NODATACOW) {
601 EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS), false);
602 EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS_TYPE), false);
603 } else {
604 EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS), true);
605 EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS_TYPE), flags & BTRFS_INODE_COMPRESS && flags_set & BTRFS_INODE_COMPRESS);
606 }
607
608 EnableWindow(GetDlgItem(hDlg, IDC_NODATACOW), !(flags & BTRFS_INODE_COMPRESS) || !(flags_set & BTRFS_INODE_COMPRESS));
609
610 flags_changed = true;
611
612 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
613 }
614
apply_changes_file(HWND hDlg,const wstring & fn)615 void BtrfsPropSheet::apply_changes_file(HWND hDlg, const wstring& fn) {
616 win_handle h;
617 IO_STATUS_BLOCK iosb;
618 NTSTATUS Status;
619 btrfs_set_inode_info bsii;
620 btrfs_inode_info bii2;
621 ULONG perms = FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
622
623 if (flags_changed || ro_changed)
624 perms |= FILE_WRITE_ATTRIBUTES;
625
626 if (perms_changed || gid_changed || uid_changed)
627 perms |= WRITE_DAC;
628
629 if (mode_set & S_ISUID && (((min_mode & S_ISUID) != (max_mode & S_ISUID)) || ((min_mode & S_ISUID) != (mode & S_ISUID))))
630 perms |= WRITE_OWNER;
631
632 h = CreateFileW(fn.c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
633 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
634
635 if (h == INVALID_HANDLE_VALUE)
636 throw last_error(GetLastError());
637
638 ZeroMemory(&bsii, sizeof(btrfs_set_inode_info));
639
640 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
641
642 if (!NT_SUCCESS(Status))
643 throw ntstatus_error(Status);
644
645 if (bii2.inode == SUBVOL_ROOT_INODE && ro_changed) {
646 BY_HANDLE_FILE_INFORMATION bhfi;
647 FILE_BASIC_INFO fbi;
648
649 if (!GetFileInformationByHandle(h, &bhfi))
650 throw last_error(GetLastError());
651
652 memset(&fbi, 0, sizeof(fbi));
653 fbi.FileAttributes = bhfi.dwFileAttributes;
654
655 if (ro_subvol)
656 fbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
657 else
658 fbi.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
659
660 Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation);
661 if (!NT_SUCCESS(Status))
662 throw ntstatus_error(Status);
663 }
664
665 if (flags_changed || perms_changed || uid_changed || gid_changed || compress_type_changed) {
666 if (flags_changed) {
667 bsii.flags_changed = true;
668 bsii.flags = (bii2.flags & ~flags_set) | (flags & flags_set);
669 }
670
671 if (perms_changed) {
672 bsii.mode_changed = true;
673 bsii.st_mode = (bii2.st_mode & ~mode_set) | (mode & mode_set);
674 }
675
676 if (uid_changed) {
677 bsii.uid_changed = true;
678 bsii.st_uid = uid;
679 }
680
681 if (gid_changed) {
682 bsii.gid_changed = true;
683 bsii.st_gid = gid;
684 }
685
686 if (compress_type_changed) {
687 bsii.compression_type_changed = true;
688 bsii.compression_type = compress_type;
689 }
690
691 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
692
693 if (!NT_SUCCESS(Status))
694 throw ntstatus_error(Status);
695 }
696 }
697
apply_changes(HWND hDlg)698 void BtrfsPropSheet::apply_changes(HWND hDlg) {
699 UINT num_files, i;
700 WCHAR fn[MAX_PATH]; // FIXME - is this long enough?
701
702 if (various_uids)
703 uid_changed = false;
704
705 if (various_gids)
706 gid_changed = false;
707
708 if (!flags_changed && !perms_changed && !uid_changed && !gid_changed && !compress_type_changed && !ro_changed)
709 return;
710
711 if (filename[0] != 0)
712 apply_changes_file(hDlg, filename);
713 else {
714 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
715
716 for (i = 0; i < num_files; i++) {
717 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
718 apply_changes_file(hDlg, fn);
719 }
720 }
721 }
722
723 flags_changed = false;
724 perms_changed = false;
725 uid_changed = false;
726 gid_changed = false;
727 ro_changed = false;
728 }
729
set_size_on_disk(HWND hwndDlg)730 void BtrfsPropSheet::set_size_on_disk(HWND hwndDlg) {
731 wstring s, size_on_disk, cr, frag;
732 WCHAR old_text[1024];
733 float ratio;
734
735 format_size(totalsize, size_on_disk, true);
736
737 wstring_sprintf(s, size_format, size_on_disk.c_str());
738
739 if (allocsize == sparsesize || totalsize == 0)
740 ratio = 0.0f;
741 else
742 ratio = 100.0f * (1.0f - ((float)totalsize / (float)(allocsize - sparsesize)));
743
744 wstring_sprintf(cr, cr_format, ratio);
745
746 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, old_text, sizeof(old_text) / sizeof(WCHAR));
747
748 if (s != old_text)
749 SetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, s.c_str());
750
751 GetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, old_text, sizeof(old_text) / sizeof(WCHAR));
752
753 if (cr != old_text)
754 SetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, cr.c_str());
755
756 uint64_t extent_size = (allocsize - sparsesize - sizes[0]) / (sector_size == 0 ? 4096 : sector_size);
757
758 if (num_extents == 0 || extent_size <= 1)
759 ratio = 0.0f;
760 else
761 ratio = 100.0f * ((float)num_extents / (float)(extent_size - 1));
762
763 wstring_sprintf(frag, frag_format, ratio);
764
765 GetDlgItemTextW(hwndDlg, IDC_FRAGMENTATION, old_text, sizeof(old_text) / sizeof(WCHAR));
766
767 if (frag != old_text)
768 SetDlgItemTextW(hwndDlg, IDC_FRAGMENTATION, frag.c_str());
769 }
770
change_perm_flag(HWND hDlg,ULONG flag,UINT state)771 void BtrfsPropSheet::change_perm_flag(HWND hDlg, ULONG flag, UINT state) {
772 if (state == BST_CHECKED) {
773 mode |= flag;
774 mode_set |= flag;
775 } else if (state == BST_UNCHECKED) {
776 mode &= ~flag;
777 mode_set |= flag;
778 } else if (state == BST_INDETERMINATE) {
779 mode_set = ~flag;
780 }
781
782 perms_changed = true;
783
784 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
785 }
786
change_uid(HWND hDlg,uint32_t uid)787 void BtrfsPropSheet::change_uid(HWND hDlg, uint32_t uid) {
788 if (this->uid != uid) {
789 this->uid = uid;
790 uid_changed = true;
791
792 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
793 }
794 }
795
change_gid(HWND hDlg,uint32_t gid)796 void BtrfsPropSheet::change_gid(HWND hDlg, uint32_t gid) {
797 if (this->gid != gid) {
798 this->gid = gid;
799 gid_changed = true;
800
801 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
802 }
803 }
804
update_size_details_dialog(HWND hDlg)805 void BtrfsPropSheet::update_size_details_dialog(HWND hDlg) {
806 wstring size;
807 WCHAR old_text[1024];
808 int i;
809 ULONG items[] = { IDC_SIZE_INLINE, IDC_SIZE_UNCOMPRESSED, IDC_SIZE_ZLIB, IDC_SIZE_LZO, IDC_SIZE_ZSTD };
810
811 for (i = 0; i < 5; i++) {
812 format_size(sizes[i], size, true);
813
814 GetDlgItemTextW(hDlg, items[i], old_text, sizeof(old_text) / sizeof(WCHAR));
815
816 if (size != old_text)
817 SetDlgItemTextW(hDlg, items[i], size.c_str());
818 }
819 }
820
SizeDetailsDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)821 static INT_PTR CALLBACK SizeDetailsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
822 try {
823 switch (uMsg) {
824 case WM_INITDIALOG:
825 {
826 BtrfsPropSheet* bps = (BtrfsPropSheet*)lParam;
827
828 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
829
830 bps->update_size_details_dialog(hwndDlg);
831
832 if (bps->thread)
833 SetTimer(hwndDlg, 1, 250, nullptr);
834
835 return true;
836 }
837
838 case WM_COMMAND:
839 if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
840 EndDialog(hwndDlg, 0);
841 return true;
842 }
843 break;
844
845 case WM_TIMER:
846 {
847 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
848
849 if (bps) {
850 bps->update_size_details_dialog(hwndDlg);
851
852 if (!bps->thread)
853 KillTimer(hwndDlg, 1);
854 }
855
856 break;
857 }
858 }
859 } catch (const exception& e) {
860 error_message(hwndDlg, e.what());
861 }
862
863 return false;
864 }
865
set_check_box(HWND hwndDlg,ULONG id,uint64_t min,uint64_t max)866 static void set_check_box(HWND hwndDlg, ULONG id, uint64_t min, uint64_t max) {
867 if (min && max) {
868 SendDlgItemMessageW(hwndDlg, id, BM_SETCHECK, BST_CHECKED, 0);
869 } else if (!min && !max) {
870 SendDlgItemMessageW(hwndDlg, id, BM_SETCHECK, BST_UNCHECKED, 0);
871 } else {
872 LONG_PTR style;
873
874 style = GetWindowLongPtrW(GetDlgItem(hwndDlg, id), GWL_STYLE);
875 style &= ~BS_AUTOCHECKBOX;
876 style |= BS_AUTO3STATE;
877 SetWindowLongPtrW(GetDlgItem(hwndDlg, id), GWL_STYLE, style);
878
879 SendDlgItemMessageW(hwndDlg, id, BM_SETCHECK, BST_INDETERMINATE, 0);
880 }
881 }
882
open_as_admin(HWND hwndDlg)883 void BtrfsPropSheet::open_as_admin(HWND hwndDlg) {
884 ULONG num_files, i;
885 WCHAR fn[MAX_PATH], modfn[MAX_PATH];
886
887 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
888
889 if (num_files == 0)
890 return;
891
892 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR));
893
894 for (i = 0; i < num_files; i++) {
895 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
896 wstring t;
897 SHELLEXECUTEINFOW sei;
898
899 #ifndef __REACTOS__
900 t = L"\""s + modfn + L"\",ShowPropSheet "s + fn;
901 #else
902 t = wstring(L"\"") + modfn + wstring(L"\",ShowPropSheet ") + fn;
903 #endif
904
905 RtlZeroMemory(&sei, sizeof(sei));
906
907 sei.cbSize = sizeof(sei);
908 sei.hwnd = hwndDlg;
909 sei.lpVerb = L"runas";
910 sei.lpFile = L"rundll32.exe";
911 sei.lpParameters = t.c_str();
912 sei.nShow = SW_SHOW;
913 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
914
915 if (!ShellExecuteExW(&sei))
916 throw last_error(GetLastError());
917
918 WaitForSingleObject(sei.hProcess, INFINITE);
919 CloseHandle(sei.hProcess);
920
921 load_file_list();
922 init_propsheet(hwndDlg);
923 }
924 }
925 }
926
927 // based on functions in sys/sysmacros.h
928 #define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((uint32_t)((rdev) >> 32) & ~0xFFF))
929 #define minor(rdev) (((rdev) & 0xFF) | ((uint32_t)((rdev) >> 12) & ~0xFF))
930
init_propsheet(HWND hwndDlg)931 void BtrfsPropSheet::init_propsheet(HWND hwndDlg) {
932 wstring s;
933 ULONG sr;
934 int i;
935 HWND comptype;
936
937 static ULONG perm_controls[] = { IDC_USERR, IDC_USERW, IDC_USERX, IDC_GROUPR, IDC_GROUPW, IDC_GROUPX, IDC_OTHERR, IDC_OTHERW, IDC_OTHERX,
938 IDC_SETUID, IDC_SETGID, IDC_STICKY, 0 };
939 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 };
940 static ULONG comp_types[] = { IDS_COMPRESS_ANY, IDS_COMPRESS_ZLIB, IDS_COMPRESS_LZO, IDS_COMPRESS_ZSTD, 0 };
941
942 if (various_subvols) {
943 if (!load_string(module, IDS_VARIOUS, s))
944 throw last_error(GetLastError());
945 } else
946 wstring_sprintf(s, L"%llx", subvol);
947
948 SetDlgItemTextW(hwndDlg, IDC_SUBVOL, s.c_str());
949
950 if (various_inodes) {
951 if (!load_string(module, IDS_VARIOUS, s))
952 throw last_error(GetLastError());
953 } else
954 wstring_sprintf(s, L"%llx", inode);
955
956 SetDlgItemTextW(hwndDlg, IDC_INODE, s.c_str());
957
958 if (various_types)
959 sr = IDS_VARIOUS;
960 else
961 sr = inode_type_to_string_ref(type);
962
963 if (various_inodes) {
964 if (sr == IDS_INODE_CHAR)
965 sr = IDS_INODE_CHAR_SIMPLE;
966 else if (sr == IDS_INODE_BLOCK)
967 sr = IDS_INODE_BLOCK_SIMPLE;
968 }
969
970 if (sr == IDS_INODE_UNKNOWN) {
971 wstring t;
972
973 if (!load_string(module, sr, t))
974 throw last_error(GetLastError());
975
976 wstring_sprintf(s, t, type);
977 } else if (sr == IDS_INODE_CHAR || sr == IDS_INODE_BLOCK) {
978 wstring t;
979
980 if (!load_string(module, sr, t))
981 throw last_error(GetLastError());
982
983 wstring_sprintf(s, t, major(rdev), minor(rdev));
984 } else {
985 if (!load_string(module, sr, s))
986 throw last_error(GetLastError());
987 }
988
989 SetDlgItemTextW(hwndDlg, IDC_TYPE, s.c_str());
990
991 if (size_format[0] == 0)
992 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, size_format, sizeof(size_format) / sizeof(WCHAR));
993
994 if (cr_format[0] == 0)
995 GetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, cr_format, sizeof(cr_format) / sizeof(WCHAR));
996
997 if (frag_format[0] == 0)
998 GetDlgItemTextW(hwndDlg, IDC_FRAGMENTATION, frag_format, sizeof(frag_format) / sizeof(WCHAR));
999
1000 set_size_on_disk(hwndDlg);
1001
1002 if (thread)
1003 SetTimer(hwndDlg, 1, 250, nullptr);
1004
1005 set_check_box(hwndDlg, IDC_NODATACOW, min_flags & BTRFS_INODE_NODATACOW, max_flags & BTRFS_INODE_NODATACOW);
1006 set_check_box(hwndDlg, IDC_COMPRESS, min_flags & BTRFS_INODE_COMPRESS, max_flags & BTRFS_INODE_COMPRESS);
1007
1008 if (min_flags & BTRFS_INODE_NODATACOW || max_flags & BTRFS_INODE_NODATACOW)
1009 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), false);
1010 else if (min_flags & BTRFS_INODE_COMPRESS || max_flags & BTRFS_INODE_COMPRESS)
1011 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), false);
1012
1013 comptype = GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE);
1014
1015 while (SendMessageW(comptype, CB_GETCOUNT, 0, 0) > 0) {
1016 SendMessageW(comptype, CB_DELETESTRING, 0, 0);
1017 }
1018
1019 if (min_compression_type != max_compression_type) {
1020 SendMessageW(comptype, CB_ADDSTRING, 0, (LPARAM)L"");
1021 SendMessageW(comptype, CB_SETCURSEL, 0, 0);
1022 }
1023
1024 i = 0;
1025 while (comp_types[i] != 0) {
1026 wstring t;
1027
1028 if (!load_string(module, comp_types[i], t))
1029 throw last_error(GetLastError());
1030
1031 SendMessageW(comptype, CB_ADDSTRING, 0, (LPARAM)t.c_str());
1032
1033 i++;
1034 }
1035
1036 if (min_compression_type == max_compression_type) {
1037 SendMessageW(comptype, CB_SETCURSEL, min_compression_type, 0);
1038 compress_type = min_compression_type;
1039 }
1040
1041 EnableWindow(comptype, max_flags & BTRFS_INODE_COMPRESS);
1042
1043 i = 0;
1044 while (perm_controls[i] != 0) {
1045 set_check_box(hwndDlg, perm_controls[i], min_mode & perms[i], max_mode & perms[i]);
1046 i++;
1047 }
1048
1049 if (various_uids) {
1050 if (!load_string(module, IDS_VARIOUS, s))
1051 throw last_error(GetLastError());
1052
1053 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
1054 } else
1055 s = to_wstring(uid);
1056
1057 SetDlgItemTextW(hwndDlg, IDC_UID, s.c_str());
1058
1059 if (various_gids) {
1060 if (!load_string(module, IDS_VARIOUS, s))
1061 throw last_error(GetLastError());
1062
1063 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
1064 } else
1065 s = to_wstring(gid);
1066
1067 SetDlgItemTextW(hwndDlg, IDC_GID, s.c_str());
1068
1069 ShowWindow(GetDlgItem(hwndDlg, IDC_SUBVOL_RO), has_subvols);
1070
1071 if (has_subvols)
1072 set_check_box(hwndDlg, IDC_SUBVOL_RO, ro_subvol, various_ro ? (!ro_subvol) : ro_subvol);
1073
1074 if (!can_change_nocow)
1075 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
1076
1077 if (!can_change_perms) {
1078 i = 0;
1079 while (perm_controls[i] != 0) {
1080 EnableWindow(GetDlgItem(hwndDlg, perm_controls[i]), 0);
1081 i++;
1082 }
1083
1084 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
1085 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
1086 EnableWindow(GetDlgItem(hwndDlg, IDC_SETUID), 0);
1087 }
1088
1089 if (readonly) {
1090 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
1091 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), 0);
1092 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), 0);
1093 }
1094
1095 if (show_admin_button) {
1096 SendMessageW(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), BCM_SETSHIELD, 0, true);
1097 ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_SHOW);
1098 } else
1099 ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_HIDE);
1100 }
1101
PropSheetDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1102 static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1103 try {
1104 switch (uMsg) {
1105 case WM_INITDIALOG:
1106 {
1107 PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)lParam;
1108 BtrfsPropSheet* bps = (BtrfsPropSheet*)psp->lParam;
1109
1110 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
1111
1112 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
1113
1114 bps->init_propsheet(hwndDlg);
1115
1116 return false;
1117 }
1118
1119 case WM_COMMAND:
1120 {
1121 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
1122
1123 if (bps && !bps->readonly) {
1124 switch (HIWORD(wParam)) {
1125 case BN_CLICKED: {
1126 switch (LOWORD(wParam)) {
1127 case IDC_NODATACOW:
1128 bps->change_inode_flag(hwndDlg, BTRFS_INODE_NODATACOW, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1129 break;
1130
1131 case IDC_COMPRESS:
1132 bps->change_inode_flag(hwndDlg, BTRFS_INODE_COMPRESS, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1133
1134 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) != BST_UNCHECKED);
1135 break;
1136
1137 case IDC_USERR:
1138 bps->change_perm_flag(hwndDlg, S_IRUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1139 break;
1140
1141 case IDC_USERW:
1142 bps->change_perm_flag(hwndDlg, S_IWUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1143 break;
1144
1145 case IDC_USERX:
1146 bps->change_perm_flag(hwndDlg, S_IXUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1147 break;
1148
1149 case IDC_GROUPR:
1150 bps->change_perm_flag(hwndDlg, S_IRGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1151 break;
1152
1153 case IDC_GROUPW:
1154 bps->change_perm_flag(hwndDlg, S_IWGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1155 break;
1156
1157 case IDC_GROUPX:
1158 bps->change_perm_flag(hwndDlg, S_IXGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1159 break;
1160
1161 case IDC_OTHERR:
1162 bps->change_perm_flag(hwndDlg, S_IROTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1163 break;
1164
1165 case IDC_OTHERW:
1166 bps->change_perm_flag(hwndDlg, S_IWOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1167 break;
1168
1169 case IDC_OTHERX:
1170 bps->change_perm_flag(hwndDlg, S_IXOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1171 break;
1172
1173 case IDC_SETUID:
1174 bps->change_perm_flag(hwndDlg, S_ISUID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1175 break;
1176
1177 case IDC_SETGID:
1178 bps->change_perm_flag(hwndDlg, S_ISGID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1179 break;
1180
1181 case IDC_STICKY:
1182 bps->change_perm_flag(hwndDlg, S_ISVTX, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1183 break;
1184
1185 case IDC_SUBVOL_RO:
1186 switch (IsDlgButtonChecked(hwndDlg, LOWORD(wParam))) {
1187 case BST_CHECKED:
1188 bps->ro_subvol = true;
1189 bps->ro_changed = true;
1190 break;
1191
1192 case BST_UNCHECKED:
1193 bps->ro_subvol = false;
1194 bps->ro_changed = true;
1195 break;
1196
1197 case BST_INDETERMINATE:
1198 bps->ro_changed = false;
1199 break;
1200 }
1201
1202 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1203 break;
1204
1205 case IDC_OPEN_ADMIN:
1206 bps->open_as_admin(hwndDlg);
1207 break;
1208 }
1209
1210 break;
1211 }
1212
1213 case EN_CHANGE: {
1214 switch (LOWORD(wParam)) {
1215 case IDC_UID: {
1216 WCHAR s[255];
1217
1218 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1219
1220 bps->change_uid(hwndDlg, _wtoi(s));
1221 break;
1222 }
1223
1224 case IDC_GID: {
1225 WCHAR s[255];
1226
1227 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1228
1229 bps->change_gid(hwndDlg, _wtoi(s));
1230 break;
1231 }
1232 }
1233
1234 break;
1235 }
1236
1237 case CBN_SELCHANGE: {
1238 switch (LOWORD(wParam)) {
1239 case IDC_COMPRESS_TYPE: {
1240 auto sel = SendMessageW(GetDlgItem(hwndDlg, LOWORD(wParam)), CB_GETCURSEL, 0, 0);
1241
1242 if (bps->min_compression_type != bps->max_compression_type) {
1243 if (sel == 0)
1244 bps->compress_type_changed = false;
1245 else {
1246 bps->compress_type = (uint8_t)(sel - 1);
1247 bps->compress_type_changed = true;
1248 }
1249 } else {
1250 bps->compress_type = (uint8_t)sel;
1251 bps->compress_type_changed = true;
1252 }
1253
1254 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1255
1256 break;
1257 }
1258 }
1259
1260 break;
1261 }
1262 }
1263 }
1264
1265 break;
1266 }
1267
1268 case WM_NOTIFY:
1269 {
1270 switch (((LPNMHDR)lParam)->code) {
1271 case PSN_KILLACTIVE:
1272 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, false);
1273 break;
1274
1275 case PSN_APPLY: {
1276 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
1277
1278 bps->apply_changes(hwndDlg);
1279 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
1280 break;
1281 }
1282
1283 case NM_CLICK:
1284 case NM_RETURN: {
1285 if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_SIZE_ON_DISK)) {
1286 PNMLINK pNMLink = (PNMLINK)lParam;
1287
1288 if (pNMLink->item.iLink == 0)
1289 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SIZE_DETAILS), hwndDlg, SizeDetailsDlgProc, GetWindowLongPtrW(hwndDlg, GWLP_USERDATA));
1290 }
1291 break;
1292 }
1293 }
1294 }
1295
1296 case WM_TIMER:
1297 {
1298 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
1299
1300 if (bps) {
1301 bps->set_size_on_disk(hwndDlg);
1302
1303 if (!bps->thread)
1304 KillTimer(hwndDlg, 1);
1305 }
1306
1307 break;
1308 }
1309 }
1310 } catch (const exception& e) {
1311 error_message(hwndDlg, e.what());
1312 }
1313
1314 return false;
1315 }
1316
AddPages(LPFNADDPROPSHEETPAGE pfnAddPage,LPARAM lParam)1317 HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) {
1318 try {
1319 PROPSHEETPAGEW psp;
1320 HPROPSHEETPAGE hPage;
1321 INITCOMMONCONTROLSEX icex;
1322
1323 if (ignore)
1324 return S_OK;
1325
1326 icex.dwSize = sizeof(icex);
1327 icex.dwICC = ICC_LINK_CLASS;
1328
1329 if (!InitCommonControlsEx(&icex))
1330 throw string_error(IDS_INITCOMMONCONTROLSEX_FAILED);
1331
1332 psp.dwSize = sizeof(psp);
1333 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
1334 psp.hInstance = module;
1335 psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROP_SHEET);
1336 psp.hIcon = 0;
1337 psp.pszTitle = MAKEINTRESOURCEW(IDS_PROP_SHEET_TITLE);
1338 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
1339 psp.pcRefParent = (UINT*)&objs_loaded;
1340 psp.pfnCallback = nullptr;
1341 psp.lParam = (LPARAM)this;
1342
1343 hPage = CreatePropertySheetPageW(&psp);
1344
1345 if (hPage) {
1346 if (pfnAddPage(hPage, lParam)) {
1347 this->AddRef();
1348 return S_OK;
1349 } else
1350 DestroyPropertySheetPage(hPage);
1351 } else
1352 return E_OUTOFMEMORY;
1353 } catch (const exception& e) {
1354 error_message(nullptr, e.what());
1355 }
1356
1357 return E_FAIL;
1358 }
1359
ReplacePage(UINT uPageID,LPFNADDPROPSHEETPAGE pfnReplacePage,LPARAM lParam)1360 HRESULT __stdcall BtrfsPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) {
1361 return S_OK;
1362 }
1363
1364 #ifdef __cplusplus
1365 extern "C" {
1366 #endif
1367
ShowPropSheetW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)1368 void CALLBACK ShowPropSheetW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1369 try {
1370 BtrfsPropSheet bps;
1371 PROPSHEETPAGEW psp;
1372 PROPSHEETHEADERW psh;
1373 INITCOMMONCONTROLSEX icex;
1374 wstring title;
1375
1376 set_dpi_aware();
1377
1378 load_string(module, IDS_STANDALONE_PROPSHEET_TITLE, title);
1379
1380 bps.set_cmdline(lpszCmdLine);
1381
1382 icex.dwSize = sizeof(icex);
1383 icex.dwICC = ICC_LINK_CLASS;
1384
1385 if (!InitCommonControlsEx(&icex))
1386 throw string_error(IDS_INITCOMMONCONTROLSEX_FAILED);
1387
1388 psp.dwSize = sizeof(psp);
1389 psp.dwFlags = PSP_USETITLE;
1390 psp.hInstance = module;
1391 psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROP_SHEET);
1392 psp.hIcon = 0;
1393 psp.pszTitle = MAKEINTRESOURCEW(IDS_PROP_SHEET_TITLE);
1394 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
1395 psp.pfnCallback = nullptr;
1396 psp.lParam = (LPARAM)&bps;
1397
1398 memset(&psh, 0, sizeof(PROPSHEETHEADERW));
1399
1400 psh.dwSize = sizeof(PROPSHEETHEADERW);
1401 psh.dwFlags = PSH_PROPSHEETPAGE;
1402 psh.hwndParent = hwnd;
1403 psh.hInstance = psp.hInstance;
1404 psh.pszCaption = title.c_str();
1405 psh.nPages = 1;
1406 psh.ppsp = &psp;
1407
1408 if (PropertySheetW(&psh) < 0)
1409 throw last_error(GetLastError());
1410 } catch (const exception& e) {
1411 error_message(hwnd, e.what());
1412 }
1413 }
1414
1415 #ifdef __cplusplus
1416 }
1417 #endif
1418