[SHELLBTRFS] Upgrade to 1.5
[reactos.git] / dll / shellext / shellbtrfs / propsheet.cpp
1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #define ISOLATION_AWARE_ENABLED 1
19 #define STRSAFE_NO_DEPRECATE
20
21 #include "shellext.h"
22 #ifndef __REACTOS__
23 #include <windows.h>
24 #include <strsafe.h>
25 #include <winternl.h>
26 #else
27 #define WIN32_NO_STATUS
28 #include <windef.h>
29 #include <winbase.h>
30 #include <strsafe.h>
31 #include <ndk/iofuncs.h>
32 #include <ndk/iotypes.h>
33 #endif
34
35 #define NO_SHLWAPI_STRFCNS
36 #include <shlwapi.h>
37 #include <uxtheme.h>
38
39 #include "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
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
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
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
166 static DWORD WINAPI global_search_list_thread(LPVOID lpParameter) {
167 BtrfsPropSheet* bps = (BtrfsPropSheet*)lpParameter;
168
169 return bps->search_list_thread();
170 }
171
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
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(MAX_PATH))) {
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
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
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 return;
439
440 Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
441 if (!NT_SUCCESS(Status))
442 return;
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) && !bii2.top) {
457 LARGE_INTEGER filesize;
458
459 subvol = bii2.subvol;
460 inode = bii2.inode;
461 type = bii2.type;
462 uid = bii2.st_uid;
463 gid = bii2.st_gid;
464 rdev = bii2.st_rdev;
465
466 if (bii2.inline_length > 0) {
467 totalsize += bii2.inline_length;
468 sizes[0] += bii2.inline_length;
469 }
470
471 if (bii2.disk_size_uncompressed > 0) {
472 totalsize += bii2.disk_size_uncompressed;
473 sizes[1] += bii2.disk_size_uncompressed;
474 }
475
476 if (bii2.disk_size_zlib > 0) {
477 totalsize += bii2.disk_size_zlib;
478 sizes[2] += bii2.disk_size_zlib;
479 }
480
481 if (bii2.disk_size_lzo > 0) {
482 totalsize += bii2.disk_size_lzo;
483 sizes[3] += bii2.disk_size_lzo;
484 }
485
486 if (bii2.disk_size_zstd > 0) {
487 totalsize += bii2.disk_size_zstd;
488 sizes[4] += bii2.disk_size_zstd;
489 }
490
491 sparsesize += bii2.sparse_size;
492
493 FILE_STANDARD_INFORMATION fsi;
494
495 Status = NtQueryInformationFile(h, &iosb, &fsi, sizeof(fsi), FileStandardInformation);
496
497 if (!NT_SUCCESS(Status))
498 throw ntstatus_error(Status);
499
500 if (bii2.inline_length > 0)
501 allocsize += fsi.EndOfFile.QuadPart;
502 else
503 allocsize += fsi.AllocationSize.QuadPart;
504
505 min_mode |= ~bii2.st_mode;
506 max_mode |= bii2.st_mode;
507 min_flags |= ~bii2.flags;
508 max_flags |= bii2.flags;
509 min_compression_type = bii2.compression_type < min_compression_type ? bii2.compression_type : min_compression_type;
510 max_compression_type = bii2.compression_type > max_compression_type ? bii2.compression_type : max_compression_type;
511
512 if (bii2.inode == SUBVOL_ROOT_INODE) {
513 bool ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
514
515 has_subvols = true;
516
517 if (sv == 0)
518 ro_subvol = ro;
519 else {
520 if (ro_subvol != ro)
521 various_ro = true;
522 }
523
524 sv++;
525 }
526
527 ignore = false;
528
529 if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
530 if (filesize.QuadPart != 0)
531 can_change_nocow = false;
532 }
533 } else
534 return;
535
536 min_mode = ~min_mode;
537 min_flags = ~min_flags;
538
539 mode = min_mode;
540 mode_set = ~(min_mode ^ max_mode);
541
542 flags = min_flags;
543 flags_set = ~(min_flags ^ max_flags);
544
545 if (search_list.size() > 0) {
546 thread = CreateThread(nullptr, 0, global_search_list_thread, this, 0, nullptr);
547
548 if (!thread)
549 throw last_error(GetLastError());
550 }
551
552 this->filename = cmdline;
553 }
554
555 static ULONG inode_type_to_string_ref(uint8_t type) {
556 switch (type) {
557 case BTRFS_TYPE_FILE:
558 return IDS_INODE_FILE;
559
560 case BTRFS_TYPE_DIRECTORY:
561 return IDS_INODE_DIR;
562
563 case BTRFS_TYPE_CHARDEV:
564 return IDS_INODE_CHAR;
565
566 case BTRFS_TYPE_BLOCKDEV:
567 return IDS_INODE_BLOCK;
568
569 case BTRFS_TYPE_FIFO:
570 return IDS_INODE_FIFO;
571
572 case BTRFS_TYPE_SOCKET:
573 return IDS_INODE_SOCKET;
574
575 case BTRFS_TYPE_SYMLINK:
576 return IDS_INODE_SYMLINK;
577
578 default:
579 return IDS_INODE_UNKNOWN;
580 }
581 }
582
583 void BtrfsPropSheet::change_inode_flag(HWND hDlg, uint64_t flag, UINT state) {
584 if (flag & BTRFS_INODE_NODATACOW)
585 flag |= BTRFS_INODE_NODATASUM;
586
587 if (state == BST_CHECKED) {
588 flags |= flag;
589 flags_set |= flag;
590 } else if (state == BST_UNCHECKED) {
591 flags &= ~flag;
592 flags_set |= flag;
593 } else if (state == BST_INDETERMINATE) {
594 flags_set = ~flag;
595 }
596
597 flags_changed = true;
598
599 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
600 }
601
602 void BtrfsPropSheet::apply_changes_file(HWND hDlg, const wstring& fn) {
603 win_handle h;
604 IO_STATUS_BLOCK iosb;
605 NTSTATUS Status;
606 btrfs_set_inode_info bsii;
607 btrfs_inode_info bii2;
608 ULONG perms = FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
609
610 if (flags_changed || ro_changed)
611 perms |= FILE_WRITE_ATTRIBUTES;
612
613 if (perms_changed || gid_changed || uid_changed)
614 perms |= WRITE_DAC;
615
616 if (mode_set & S_ISUID && (((min_mode & S_ISUID) != (max_mode & S_ISUID)) || ((min_mode & S_ISUID) != (mode & S_ISUID))))
617 perms |= WRITE_OWNER;
618
619 h = CreateFileW(fn.c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
620 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
621
622 if (h == INVALID_HANDLE_VALUE)
623 throw last_error(GetLastError());
624
625 ZeroMemory(&bsii, sizeof(btrfs_set_inode_info));
626
627 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
628
629 if (!NT_SUCCESS(Status))
630 throw ntstatus_error(Status);
631
632 if (bii2.inode == SUBVOL_ROOT_INODE && ro_changed) {
633 BY_HANDLE_FILE_INFORMATION bhfi;
634 FILE_BASIC_INFO fbi;
635
636 if (!GetFileInformationByHandle(h, &bhfi))
637 throw last_error(GetLastError());
638
639 memset(&fbi, 0, sizeof(fbi));
640 fbi.FileAttributes = bhfi.dwFileAttributes;
641
642 if (ro_subvol)
643 fbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
644 else
645 fbi.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
646
647 Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation);
648 if (!NT_SUCCESS(Status))
649 throw ntstatus_error(Status);
650 }
651
652 if (flags_changed || perms_changed || uid_changed || gid_changed || compress_type_changed) {
653 if (flags_changed) {
654 bsii.flags_changed = true;
655 bsii.flags = (bii2.flags & ~flags_set) | (flags & flags_set);
656 }
657
658 if (perms_changed) {
659 bsii.mode_changed = true;
660 bsii.st_mode = (bii2.st_mode & ~mode_set) | (mode & mode_set);
661 }
662
663 if (uid_changed) {
664 bsii.uid_changed = true;
665 bsii.st_uid = uid;
666 }
667
668 if (gid_changed) {
669 bsii.gid_changed = true;
670 bsii.st_gid = gid;
671 }
672
673 if (compress_type_changed) {
674 bsii.compression_type_changed = true;
675 bsii.compression_type = compress_type;
676 }
677
678 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
679
680 if (!NT_SUCCESS(Status))
681 throw ntstatus_error(Status);
682 }
683 }
684
685 void BtrfsPropSheet::apply_changes(HWND hDlg) {
686 UINT num_files, i;
687 WCHAR fn[MAX_PATH]; // FIXME - is this long enough?
688
689 if (various_uids)
690 uid_changed = false;
691
692 if (various_gids)
693 gid_changed = false;
694
695 if (!flags_changed && !perms_changed && !uid_changed && !gid_changed && !compress_type_changed && !ro_changed)
696 return;
697
698 if (filename[0] != 0)
699 apply_changes_file(hDlg, filename);
700 else {
701 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
702
703 for (i = 0; i < num_files; i++) {
704 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
705 apply_changes_file(hDlg, fn);
706 }
707 }
708 }
709
710 flags_changed = false;
711 perms_changed = false;
712 uid_changed = false;
713 gid_changed = false;
714 ro_changed = false;
715 }
716
717 void BtrfsPropSheet::set_size_on_disk(HWND hwndDlg) {
718 wstring s, size_on_disk, cr, frag;
719 WCHAR old_text[1024];
720 float ratio;
721
722 format_size(totalsize, size_on_disk, true);
723
724 wstring_sprintf(s, size_format, size_on_disk.c_str());
725
726 if (allocsize == sparsesize || totalsize == 0)
727 ratio = 0.0f;
728 else
729 ratio = 100.0f * (1.0f - ((float)totalsize / (float)(allocsize - sparsesize)));
730
731 wstring_sprintf(cr, cr_format, ratio);
732
733 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, old_text, sizeof(old_text) / sizeof(WCHAR));
734
735 if (s != old_text)
736 SetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, s.c_str());
737
738 GetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, old_text, sizeof(old_text) / sizeof(WCHAR));
739
740 if (cr != old_text)
741 SetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, cr.c_str());
742
743 uint64_t extent_size = (allocsize - sparsesize - sizes[0]) / sector_size;
744
745 if (num_extents == 0 || extent_size <= 1)
746 ratio = 0.0f;
747 else
748 ratio = 100.0f * ((float)num_extents / (float)(extent_size - 1));
749
750 wstring_sprintf(frag, frag_format, ratio);
751
752 GetDlgItemTextW(hwndDlg, IDC_FRAGMENTATION, old_text, sizeof(old_text) / sizeof(WCHAR));
753
754
755 if (frag != old_text)
756 SetDlgItemTextW(hwndDlg, IDC_FRAGMENTATION, frag.c_str());
757 }
758
759 void BtrfsPropSheet::change_perm_flag(HWND hDlg, ULONG flag, UINT state) {
760 if (state == BST_CHECKED) {
761 mode |= flag;
762 mode_set |= flag;
763 } else if (state == BST_UNCHECKED) {
764 mode &= ~flag;
765 mode_set |= flag;
766 } else if (state == BST_INDETERMINATE) {
767 mode_set = ~flag;
768 }
769
770 perms_changed = true;
771
772 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
773 }
774
775 void BtrfsPropSheet::change_uid(HWND hDlg, uint32_t uid) {
776 if (this->uid != uid) {
777 this->uid = uid;
778 uid_changed = true;
779
780 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
781 }
782 }
783
784 void BtrfsPropSheet::change_gid(HWND hDlg, uint32_t gid) {
785 if (this->gid != gid) {
786 this->gid = gid;
787 gid_changed = true;
788
789 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
790 }
791 }
792
793 void BtrfsPropSheet::update_size_details_dialog(HWND hDlg) {
794 wstring size;
795 WCHAR old_text[1024];
796 int i;
797 ULONG items[] = { IDC_SIZE_INLINE, IDC_SIZE_UNCOMPRESSED, IDC_SIZE_ZLIB, IDC_SIZE_LZO, IDC_SIZE_ZSTD };
798
799 for (i = 0; i < 5; i++) {
800 format_size(sizes[i], size, true);
801
802 GetDlgItemTextW(hDlg, items[i], old_text, sizeof(old_text) / sizeof(WCHAR));
803
804 if (size != old_text)
805 SetDlgItemTextW(hDlg, items[i], size.c_str());
806 }
807 }
808
809 static INT_PTR CALLBACK SizeDetailsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
810 try {
811 switch (uMsg) {
812 case WM_INITDIALOG:
813 {
814 BtrfsPropSheet* bps = (BtrfsPropSheet*)lParam;
815
816 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
817
818 bps->update_size_details_dialog(hwndDlg);
819
820 if (bps->thread)
821 SetTimer(hwndDlg, 1, 250, nullptr);
822
823 return true;
824 }
825
826 case WM_COMMAND:
827 if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
828 EndDialog(hwndDlg, 0);
829 return true;
830 }
831 break;
832
833 case WM_TIMER:
834 {
835 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
836
837 if (bps) {
838 bps->update_size_details_dialog(hwndDlg);
839
840 if (!bps->thread)
841 KillTimer(hwndDlg, 1);
842 }
843
844 break;
845 }
846 }
847 } catch (const exception& e) {
848 error_message(hwndDlg, e.what());
849 }
850
851 return false;
852 }
853
854 static void set_check_box(HWND hwndDlg, ULONG id, uint64_t min, uint64_t max) {
855 if (min && max) {
856 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_CHECKED, 0);
857 } else if (!min && !max) {
858 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_UNCHECKED, 0);
859 } else {
860 LONG_PTR style;
861
862 style = GetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE);
863 style &= ~BS_AUTOCHECKBOX;
864 style |= BS_AUTO3STATE;
865 SetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE, style);
866
867 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_INDETERMINATE, 0);
868 }
869 }
870
871 void BtrfsPropSheet::open_as_admin(HWND hwndDlg) {
872 ULONG num_files, i;
873 WCHAR fn[MAX_PATH], modfn[MAX_PATH];
874
875 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
876
877 if (num_files == 0)
878 return;
879
880 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR));
881
882 for (i = 0; i < num_files; i++) {
883 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
884 wstring t;
885 SHELLEXECUTEINFOW sei;
886
887 #ifndef __REACTOS__
888 t = L"\""s + modfn + L"\",ShowPropSheet "s + fn;
889 #else
890 t = wstring(L"\"") + modfn + wstring(L"\",ShowPropSheet ") + fn;
891 #endif
892
893 RtlZeroMemory(&sei, sizeof(sei));
894
895 sei.cbSize = sizeof(sei);
896 sei.hwnd = hwndDlg;
897 sei.lpVerb = L"runas";
898 sei.lpFile = L"rundll32.exe";
899 sei.lpParameters = t.c_str();
900 sei.nShow = SW_SHOW;
901 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
902
903 if (!ShellExecuteExW(&sei))
904 throw last_error(GetLastError());
905
906 WaitForSingleObject(sei.hProcess, INFINITE);
907 CloseHandle(sei.hProcess);
908
909 load_file_list();
910 init_propsheet(hwndDlg);
911 }
912 }
913 }
914
915 // based on functions in sys/sysmacros.h
916 #define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((uint32_t)((rdev) >> 32) & ~0xFFF))
917 #define minor(rdev) (((rdev) & 0xFF) | ((uint32_t)((rdev) >> 12) & ~0xFF))
918
919 void BtrfsPropSheet::init_propsheet(HWND hwndDlg) {
920 wstring s;
921 ULONG sr;
922 int i;
923 HWND comptype;
924
925 static ULONG perm_controls[] = { IDC_USERR, IDC_USERW, IDC_USERX, IDC_GROUPR, IDC_GROUPW, IDC_GROUPX, IDC_OTHERR, IDC_OTHERW, IDC_OTHERX,
926 IDC_SETUID, IDC_SETGID, IDC_STICKY, 0 };
927 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 };
928 static ULONG comp_types[] = { IDS_COMPRESS_ANY, IDS_COMPRESS_ZLIB, IDS_COMPRESS_LZO, IDS_COMPRESS_ZSTD, 0 };
929
930 if (various_subvols) {
931 if (!load_string(module, IDS_VARIOUS, s))
932 throw last_error(GetLastError());
933 } else
934 wstring_sprintf(s, L"%llx", subvol);
935
936 SetDlgItemTextW(hwndDlg, IDC_SUBVOL, s.c_str());
937
938 if (various_inodes) {
939 if (!load_string(module, IDS_VARIOUS, s))
940 throw last_error(GetLastError());
941 } else
942 wstring_sprintf(s, L"%llx", inode);
943
944 SetDlgItemTextW(hwndDlg, IDC_INODE, s.c_str());
945
946 if (various_types)
947 sr = IDS_VARIOUS;
948 else
949 sr = inode_type_to_string_ref(type);
950
951 if (various_inodes) {
952 if (sr == IDS_INODE_CHAR)
953 sr = IDS_INODE_CHAR_SIMPLE;
954 else if (sr == IDS_INODE_BLOCK)
955 sr = IDS_INODE_BLOCK_SIMPLE;
956 }
957
958 if (sr == IDS_INODE_UNKNOWN) {
959 wstring t;
960
961 if (!load_string(module, sr, t))
962 throw last_error(GetLastError());
963
964 wstring_sprintf(s, t, type);
965 } else if (sr == IDS_INODE_CHAR || sr == IDS_INODE_BLOCK) {
966 wstring t;
967
968 if (!load_string(module, sr, t))
969 throw last_error(GetLastError());
970
971 wstring_sprintf(s, t, major(rdev), minor(rdev));
972 } else {
973 if (!load_string(module, sr, s))
974 throw last_error(GetLastError());
975 }
976
977 SetDlgItemTextW(hwndDlg, IDC_TYPE, s.c_str());
978
979 if (size_format[0] == 0)
980 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, size_format, sizeof(size_format) / sizeof(WCHAR));
981
982 if (cr_format[0] == 0)
983 GetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, cr_format, sizeof(cr_format) / sizeof(WCHAR));
984
985 if (frag_format[0] == 0)
986 GetDlgItemTextW(hwndDlg, IDC_FRAGMENTATION, frag_format, sizeof(frag_format) / sizeof(WCHAR));
987
988 set_size_on_disk(hwndDlg);
989
990 if (thread)
991 SetTimer(hwndDlg, 1, 250, nullptr);
992
993 set_check_box(hwndDlg, IDC_NODATACOW, min_flags & BTRFS_INODE_NODATACOW, max_flags & BTRFS_INODE_NODATACOW);
994 set_check_box(hwndDlg, IDC_COMPRESS, min_flags & BTRFS_INODE_COMPRESS, max_flags & BTRFS_INODE_COMPRESS);
995
996 comptype = GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE);
997
998 while (SendMessage(comptype, CB_GETCOUNT, 0, 0) > 0) {
999 SendMessage(comptype, CB_DELETESTRING, 0, 0);
1000 }
1001
1002 if (min_compression_type != max_compression_type) {
1003 SendMessage(comptype, CB_ADDSTRING, 0, (LPARAM)L"");
1004 SendMessage(comptype, CB_SETCURSEL, 0, 0);
1005 }
1006
1007 i = 0;
1008 while (comp_types[i] != 0) {
1009 wstring t;
1010
1011 if (!load_string(module, comp_types[i], t))
1012 throw last_error(GetLastError());
1013
1014 SendMessage(comptype, CB_ADDSTRING, 0, (LPARAM)t.c_str());
1015
1016 i++;
1017 }
1018
1019 if (min_compression_type == max_compression_type) {
1020 SendMessage(comptype, CB_SETCURSEL, min_compression_type, 0);
1021 compress_type = min_compression_type;
1022 }
1023
1024 EnableWindow(comptype, max_flags & BTRFS_INODE_COMPRESS);
1025
1026 i = 0;
1027 while (perm_controls[i] != 0) {
1028 set_check_box(hwndDlg, perm_controls[i], min_mode & perms[i], max_mode & perms[i]);
1029 i++;
1030 }
1031
1032 if (various_uids) {
1033 if (!load_string(module, IDS_VARIOUS, s))
1034 throw last_error(GetLastError());
1035
1036 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
1037 } else
1038 s = to_wstring(uid);
1039
1040 SetDlgItemTextW(hwndDlg, IDC_UID, s.c_str());
1041
1042 if (various_gids) {
1043 if (!load_string(module, IDS_VARIOUS, s))
1044 throw last_error(GetLastError());
1045
1046 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
1047 } else
1048 s = to_wstring(gid);
1049
1050 SetDlgItemTextW(hwndDlg, IDC_GID, s.c_str());
1051
1052 ShowWindow(GetDlgItem(hwndDlg, IDC_SUBVOL_RO), has_subvols);
1053
1054 if (has_subvols)
1055 set_check_box(hwndDlg, IDC_SUBVOL_RO, ro_subvol, various_ro ? (!ro_subvol) : ro_subvol);
1056
1057 if (!can_change_nocow)
1058 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
1059
1060 if (!can_change_perms) {
1061 i = 0;
1062 while (perm_controls[i] != 0) {
1063 EnableWindow(GetDlgItem(hwndDlg, perm_controls[i]), 0);
1064 i++;
1065 }
1066
1067 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
1068 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
1069 EnableWindow(GetDlgItem(hwndDlg, IDC_SETUID), 0);
1070 }
1071
1072 if (readonly) {
1073 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
1074 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), 0);
1075 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), 0);
1076 }
1077
1078 if (show_admin_button) {
1079 SendMessageW(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), BCM_SETSHIELD, 0, true);
1080 ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_SHOW);
1081 } else
1082 ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_HIDE);
1083 }
1084
1085 static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1086 try {
1087 switch (uMsg) {
1088 case WM_INITDIALOG:
1089 {
1090 PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
1091 BtrfsPropSheet* bps = (BtrfsPropSheet*)psp->lParam;
1092
1093 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
1094
1095 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
1096
1097 bps->init_propsheet(hwndDlg);
1098
1099 return false;
1100 }
1101
1102 case WM_COMMAND:
1103 {
1104 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1105
1106 if (bps && !bps->readonly) {
1107 switch (HIWORD(wParam)) {
1108 case BN_CLICKED: {
1109 switch (LOWORD(wParam)) {
1110 case IDC_NODATACOW:
1111 bps->change_inode_flag(hwndDlg, BTRFS_INODE_NODATACOW, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1112 break;
1113
1114 case IDC_COMPRESS:
1115 bps->change_inode_flag(hwndDlg, BTRFS_INODE_COMPRESS, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1116
1117 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) != BST_UNCHECKED);
1118 break;
1119
1120 case IDC_USERR:
1121 bps->change_perm_flag(hwndDlg, S_IRUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1122 break;
1123
1124 case IDC_USERW:
1125 bps->change_perm_flag(hwndDlg, S_IWUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1126 break;
1127
1128 case IDC_USERX:
1129 bps->change_perm_flag(hwndDlg, S_IXUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1130 break;
1131
1132 case IDC_GROUPR:
1133 bps->change_perm_flag(hwndDlg, S_IRGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1134 break;
1135
1136 case IDC_GROUPW:
1137 bps->change_perm_flag(hwndDlg, S_IWGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1138 break;
1139
1140 case IDC_GROUPX:
1141 bps->change_perm_flag(hwndDlg, S_IXGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1142 break;
1143
1144 case IDC_OTHERR:
1145 bps->change_perm_flag(hwndDlg, S_IROTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1146 break;
1147
1148 case IDC_OTHERW:
1149 bps->change_perm_flag(hwndDlg, S_IWOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1150 break;
1151
1152 case IDC_OTHERX:
1153 bps->change_perm_flag(hwndDlg, S_IXOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1154 break;
1155
1156 case IDC_SETUID:
1157 bps->change_perm_flag(hwndDlg, S_ISUID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1158 break;
1159
1160 case IDC_SETGID:
1161 bps->change_perm_flag(hwndDlg, S_ISGID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1162 break;
1163
1164 case IDC_STICKY:
1165 bps->change_perm_flag(hwndDlg, S_ISVTX, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1166 break;
1167
1168 case IDC_SUBVOL_RO:
1169 switch (IsDlgButtonChecked(hwndDlg, LOWORD(wParam))) {
1170 case BST_CHECKED:
1171 bps->ro_subvol = true;
1172 bps->ro_changed = true;
1173 break;
1174
1175 case BST_UNCHECKED:
1176 bps->ro_subvol = false;
1177 bps->ro_changed = true;
1178 break;
1179
1180 case BST_INDETERMINATE:
1181 bps->ro_changed = false;
1182 break;
1183 }
1184
1185 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1186 break;
1187
1188 case IDC_OPEN_ADMIN:
1189 bps->open_as_admin(hwndDlg);
1190 break;
1191 }
1192
1193 break;
1194 }
1195
1196 case EN_CHANGE: {
1197 switch (LOWORD(wParam)) {
1198 case IDC_UID: {
1199 WCHAR s[255];
1200
1201 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1202
1203 bps->change_uid(hwndDlg, _wtoi(s));
1204 break;
1205 }
1206
1207 case IDC_GID: {
1208 WCHAR s[255];
1209
1210 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1211
1212 bps->change_gid(hwndDlg, _wtoi(s));
1213 break;
1214 }
1215 }
1216
1217 break;
1218 }
1219
1220 case CBN_SELCHANGE: {
1221 switch (LOWORD(wParam)) {
1222 case IDC_COMPRESS_TYPE: {
1223 auto sel = SendMessageW(GetDlgItem(hwndDlg, LOWORD(wParam)), CB_GETCURSEL, 0, 0);
1224
1225 if (bps->min_compression_type != bps->max_compression_type) {
1226 if (sel == 0)
1227 bps->compress_type_changed = false;
1228 else {
1229 bps->compress_type = (uint8_t)(sel - 1);
1230 bps->compress_type_changed = true;
1231 }
1232 } else {
1233 bps->compress_type = (uint8_t)sel;
1234 bps->compress_type_changed = true;
1235 }
1236
1237 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1238
1239 break;
1240 }
1241 }
1242
1243 break;
1244 }
1245 }
1246 }
1247
1248 break;
1249 }
1250
1251 case WM_NOTIFY:
1252 {
1253 switch (((LPNMHDR)lParam)->code) {
1254 case PSN_KILLACTIVE:
1255 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, false);
1256 break;
1257
1258 case PSN_APPLY: {
1259 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1260
1261 bps->apply_changes(hwndDlg);
1262 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
1263 break;
1264 }
1265
1266 case NM_CLICK:
1267 case NM_RETURN: {
1268 if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_SIZE_ON_DISK)) {
1269 PNMLINK pNMLink = (PNMLINK)lParam;
1270
1271 if (pNMLink->item.iLink == 0)
1272 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SIZE_DETAILS), hwndDlg, SizeDetailsDlgProc, GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
1273 }
1274 break;
1275 }
1276 }
1277 }
1278
1279 case WM_TIMER:
1280 {
1281 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1282
1283 if (bps) {
1284 bps->set_size_on_disk(hwndDlg);
1285
1286 if (!bps->thread)
1287 KillTimer(hwndDlg, 1);
1288 }
1289
1290 break;
1291 }
1292 }
1293 } catch (const exception& e) {
1294 error_message(hwndDlg, e.what());
1295 }
1296
1297 return false;
1298 }
1299
1300 HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) {
1301 try {
1302 PROPSHEETPAGE psp;
1303 HPROPSHEETPAGE hPage;
1304 INITCOMMONCONTROLSEX icex;
1305
1306 if (ignore)
1307 return S_OK;
1308
1309 icex.dwSize = sizeof(icex);
1310 icex.dwICC = ICC_LINK_CLASS;
1311
1312 if (!InitCommonControlsEx(&icex))
1313 throw string_error(IDS_INITCOMMONCONTROLSEX_FAILED);
1314
1315 psp.dwSize = sizeof(psp);
1316 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
1317 psp.hInstance = module;
1318 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROP_SHEET);
1319 psp.hIcon = 0;
1320 psp.pszTitle = MAKEINTRESOURCE(IDS_PROP_SHEET_TITLE);
1321 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
1322 psp.pcRefParent = (UINT*)&objs_loaded;
1323 psp.pfnCallback = nullptr;
1324 psp.lParam = (LPARAM)this;
1325
1326 hPage = CreatePropertySheetPage(&psp);
1327
1328 if (hPage) {
1329 if (pfnAddPage(hPage, lParam)) {
1330 this->AddRef();
1331 return S_OK;
1332 } else
1333 DestroyPropertySheetPage(hPage);
1334 } else
1335 return E_OUTOFMEMORY;
1336 } catch (const exception& e) {
1337 error_message(nullptr, e.what());
1338 }
1339
1340 return E_FAIL;
1341 }
1342
1343 HRESULT __stdcall BtrfsPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) {
1344 return S_OK;
1345 }
1346
1347 #ifdef __cplusplus
1348 extern "C" {
1349 #endif
1350
1351 void CALLBACK ShowPropSheetW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1352 try {
1353 BtrfsPropSheet bps;
1354 PROPSHEETPAGEW psp;
1355 PROPSHEETHEADERW psh;
1356 wstring title;
1357
1358 set_dpi_aware();
1359
1360 load_string(module, IDS_STANDALONE_PROPSHEET_TITLE, title);
1361
1362 bps.set_cmdline(lpszCmdLine);
1363
1364 psp.dwSize = sizeof(psp);
1365 psp.dwFlags = PSP_USETITLE;
1366 psp.hInstance = module;
1367 psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROP_SHEET);
1368 psp.hIcon = 0;
1369 psp.pszTitle = MAKEINTRESOURCEW(IDS_PROP_SHEET_TITLE);
1370 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
1371 psp.pfnCallback = nullptr;
1372 psp.lParam = (LPARAM)&bps;
1373
1374 memset(&psh, 0, sizeof(PROPSHEETHEADERW));
1375
1376 psh.dwSize = sizeof(PROPSHEETHEADERW);
1377 psh.dwFlags = PSH_PROPSHEETPAGE;
1378 psh.hwndParent = hwnd;
1379 psh.hInstance = psp.hInstance;
1380 psh.pszCaption = title.c_str();
1381 psh.nPages = 1;
1382 psh.ppsp = &psp;
1383
1384 PropertySheetW(&psh);
1385 } catch (const exception& e) {
1386 error_message(hwnd, e.what());
1387 }
1388 }
1389
1390 #ifdef __cplusplus
1391 }
1392 #endif