Remove unnecessary executable bits
[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 #endif
54 #endif
55
56 HRESULT __stdcall BtrfsPropSheet::QueryInterface(REFIID riid, void **ppObj) {
57 if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
58 *ppObj = static_cast<IShellPropSheetExt*>(this);
59 AddRef();
60 return S_OK;
61 } else if (riid == IID_IShellExtInit) {
62 *ppObj = static_cast<IShellExtInit*>(this);
63 AddRef();
64 return S_OK;
65 }
66
67 *ppObj = NULL;
68 return E_NOINTERFACE;
69 }
70
71 void BtrfsPropSheet::add_to_search_list(WCHAR* fn) {
72 WCHAR* s;
73
74 s = (WCHAR*)malloc((wcslen(fn) + 1) * sizeof(WCHAR));
75 if (!s)
76 return;
77
78 memcpy(s, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
79
80 search_list.push_back(s);
81 }
82
83 void BtrfsPropSheet::do_search(WCHAR* fn) {
84 HANDLE h;
85 WCHAR* ss;
86 WIN32_FIND_DATAW ffd;
87
88 ss = (WCHAR*)malloc((wcslen(fn) + 3) * sizeof(WCHAR));
89 if (!ss)
90 return;
91
92 memcpy(ss, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
93 wcscat(ss, L"\\*");
94
95 h = FindFirstFileW(ss, &ffd);
96 if (h == INVALID_HANDLE_VALUE)
97 return;
98
99 do {
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));
102
103 memcpy(fn2, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
104 wcscat(fn2, L"\\");
105 wcscat(fn2, ffd.cFileName);
106
107 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
108 add_to_search_list(fn2);
109 } else {
110 HANDLE fh;
111
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);
114
115 if (fh != INVALID_HANDLE_VALUE) {
116 NTSTATUS Status;
117 IO_STATUS_BLOCK iosb;
118 btrfs_inode_info bii2;
119
120 Status = NtFsControlFile(fh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
121
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];
128 }
129
130 CloseHandle(fh);
131 }
132
133 free(fn2);
134 }
135 }
136 } while (FindNextFileW(h, &ffd));
137
138 FindClose(h);
139 }
140
141 DWORD BtrfsPropSheet::search_list_thread() {
142 while (!search_list.empty()) {
143 WCHAR* fn = search_list.front();
144
145 do_search(fn);
146
147 search_list.pop_front();
148 free(fn);
149 }
150
151 thread = NULL;
152
153 return 0;
154 }
155
156 static DWORD WINAPI global_search_list_thread(LPVOID lpParameter) {
157 BtrfsPropSheet* bps = (BtrfsPropSheet*)lpParameter;
158
159 return bps->search_list_thread();
160 }
161
162 HRESULT BtrfsPropSheet::check_file(std::wstring fn, UINT i, UINT num_files, UINT* sv) {
163 HANDLE h;
164 IO_STATUS_BLOCK iosb;
165 NTSTATUS Status;
166 FILE_ACCESS_INFORMATION fai;
167 BY_HANDLE_FILE_INFORMATION bhfi;
168 btrfs_inode_info bii2;
169
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);
172
173 if (h == INVALID_HANDLE_VALUE)
174 return E_FAIL;
175
176 Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
177 if (!NT_SUCCESS(Status)) {
178 CloseHandle(h);
179 return E_FAIL;
180 }
181
182 if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
183 can_change_perms = fai.AccessFlags & WRITE_DAC;
184
185 readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
186
187 if (!readonly && num_files == 1 && !can_change_perms)
188 show_admin_button = TRUE;
189
190 if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
191 add_to_search_list((WCHAR*)fn.c_str());
192
193 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
194
195 if (NT_SUCCESS(Status) && !bii2.top) {
196 int j;
197
198 LARGE_INTEGER filesize;
199
200 if (i == 0) {
201 subvol = bii2.subvol;
202 inode = bii2.inode;
203 type = bii2.type;
204 uid = bii2.st_uid;
205 gid = bii2.st_gid;
206 rdev = bii2.st_rdev;
207 } else {
208 if (subvol != bii2.subvol)
209 various_subvols = TRUE;
210
211 if (inode != bii2.inode)
212 various_inodes = TRUE;
213
214 if (type != bii2.type)
215 various_types = TRUE;
216
217 if (uid != bii2.st_uid)
218 various_uids = TRUE;
219
220 if (gid != bii2.st_gid)
221 various_gids = TRUE;
222 }
223
224 if (bii2.inline_length > 0) {
225 totalsize += bii2.inline_length;
226 sizes[0] += bii2.inline_length;
227 }
228
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];
233 }
234 }
235
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;
242
243 if (bii2.inode == SUBVOL_ROOT_INODE) {
244 BOOL ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
245
246 has_subvols = TRUE;
247
248 if (*sv == 0)
249 ro_subvol = ro;
250 else {
251 if (ro_subvol != ro)
252 various_ro = TRUE;
253 }
254
255 (*sv)++;
256 }
257
258 ignore = FALSE;
259
260 if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
261 if (filesize.QuadPart != 0)
262 can_change_nocow = FALSE;
263 }
264
265 CloseHandle(h);
266 } else {
267 CloseHandle(h);
268 return E_FAIL;
269 }
270
271 return S_OK;
272 }
273
274 HRESULT BtrfsPropSheet::load_file_list() {
275 UINT num_files, i, sv = 0;
276 WCHAR fn[MAX_PATH];
277
278 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
279
280 min_mode = 0;
281 max_mode = 0;
282 min_flags = 0;
283 max_flags = 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;
287
288 can_change_perms = TRUE;
289 can_change_nocow = TRUE;
290
291 sizes[0] = sizes[1] = sizes[2] = sizes[3] = 0;
292
293 for (i = 0; i < num_files; i++) {
294 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
295 HRESULT hr;
296
297 hr = check_file(fn, i, num_files, &sv);
298 if (FAILED(hr))
299 return hr;
300 } else
301 return E_FAIL;
302 }
303
304 min_mode = ~min_mode;
305 min_flags = ~min_flags;
306
307 mode = min_mode;
308 mode_set = ~(min_mode ^ max_mode);
309
310 flags = min_flags;
311 flags_set = ~(min_flags ^ max_flags);
312
313 return S_OK;
314 }
315
316 HRESULT __stdcall BtrfsPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
317 FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
318 HDROP hdrop;
319 HRESULT hr;
320
321 if (pidlFolder)
322 return E_FAIL;
323
324 if (!pdtobj)
325 return E_FAIL;
326
327 stgm.tymed = TYMED_HGLOBAL;
328
329 if (FAILED(pdtobj->GetData(&format, &stgm)))
330 return E_INVALIDARG;
331
332 stgm_set = TRUE;
333
334 hdrop = (HDROP)GlobalLock(stgm.hGlobal);
335
336 if (!hdrop) {
337 ReleaseStgMedium(&stgm);
338 stgm_set = FALSE;
339 return E_INVALIDARG;
340 }
341
342 hr = load_file_list();
343 if (FAILED(hr))
344 return hr;
345
346 if (search_list.size() > 0) {
347 thread = CreateThread(NULL, 0, global_search_list_thread, this, 0, NULL);
348
349 if (!thread)
350 ShowError(NULL, GetLastError());
351 }
352
353 GlobalUnlock(hdrop);
354
355 return S_OK;
356 }
357
358 void BtrfsPropSheet::set_cmdline(std::wstring cmdline) {
359 HANDLE h;
360 IO_STATUS_BLOCK iosb;
361 NTSTATUS Status;
362 UINT sv = 0;
363 BY_HANDLE_FILE_INFORMATION bhfi;
364 btrfs_inode_info bii2;
365 FILE_ACCESS_INFORMATION fai;
366
367 min_mode = 0;
368 max_mode = 0;
369 min_flags = 0;
370 max_flags = 0;
371 min_compression_type = 0xff;
372 max_compression_type = 0;
373 various_subvols = various_inodes = various_types = various_uids = various_gids = various_ro = FALSE;
374
375 can_change_perms = TRUE;
376 can_change_nocow = TRUE;
377
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);
380
381 if (h == INVALID_HANDLE_VALUE)
382 return;
383
384 Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
385 if (!NT_SUCCESS(Status)) {
386 CloseHandle(h);
387 return;
388 }
389
390 if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
391 can_change_perms = fai.AccessFlags & WRITE_DAC;
392
393 readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
394
395 if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
396 add_to_search_list((WCHAR*)cmdline.c_str());
397
398 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
399
400 if (NT_SUCCESS(Status) && !bii2.top) {
401 int j;
402
403 LARGE_INTEGER filesize;
404
405 subvol = bii2.subvol;
406 inode = bii2.inode;
407 type = bii2.type;
408 uid = bii2.st_uid;
409 gid = bii2.st_gid;
410 rdev = bii2.st_rdev;
411
412 if (bii2.inline_length > 0) {
413 totalsize += bii2.inline_length;
414 sizes[0] += bii2.inline_length;
415 }
416
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];
421 }
422 }
423
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;
430
431 if (bii2.inode == SUBVOL_ROOT_INODE) {
432 BOOL ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
433
434 has_subvols = TRUE;
435
436 if (sv == 0)
437 ro_subvol = ro;
438 else {
439 if (ro_subvol != ro)
440 various_ro = TRUE;
441 }
442
443 sv++;
444 }
445
446 ignore = FALSE;
447
448 if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
449 if (filesize.QuadPart != 0)
450 can_change_nocow = FALSE;
451 }
452
453 CloseHandle(h);
454 } else {
455 CloseHandle(h);
456 return;
457 }
458
459 min_mode = ~min_mode;
460 min_flags = ~min_flags;
461
462 mode = min_mode;
463 mode_set = ~(min_mode ^ max_mode);
464
465 flags = min_flags;
466 flags_set = ~(min_flags ^ max_flags);
467
468 if (search_list.size() > 0) {
469 thread = CreateThread(NULL, 0, global_search_list_thread, this, 0, NULL);
470
471 if (!thread)
472 ShowError(NULL, GetLastError());
473 }
474
475 this->filename = cmdline;
476 }
477
478 static ULONG inode_type_to_string_ref(UINT8 type) {
479 switch (type) {
480 case BTRFS_TYPE_FILE:
481 return IDS_INODE_FILE;
482
483 case BTRFS_TYPE_DIRECTORY:
484 return IDS_INODE_DIR;
485
486 case BTRFS_TYPE_CHARDEV:
487 return IDS_INODE_CHAR;
488
489 case BTRFS_TYPE_BLOCKDEV:
490 return IDS_INODE_BLOCK;
491
492 case BTRFS_TYPE_FIFO:
493 return IDS_INODE_FIFO;
494
495 case BTRFS_TYPE_SOCKET:
496 return IDS_INODE_SOCKET;
497
498 case BTRFS_TYPE_SYMLINK:
499 return IDS_INODE_SYMLINK;
500
501 default:
502 return IDS_INODE_UNKNOWN;
503 }
504 }
505
506 void BtrfsPropSheet::change_inode_flag(HWND hDlg, UINT64 flag, UINT state) {
507 if (flag & BTRFS_INODE_NODATACOW)
508 flag |= BTRFS_INODE_NODATASUM;
509
510 if (state == BST_CHECKED) {
511 flags |= flag;
512 flags_set |= flag;
513 } else if (state == BST_UNCHECKED) {
514 flags &= ~flag;
515 flags_set |= flag;
516 } else if (state == BST_INDETERMINATE) {
517 flags_set = ~flag;
518 }
519
520 flags_changed = TRUE;
521
522 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
523 }
524
525 void BtrfsPropSheet::apply_changes_file(HWND hDlg, std::wstring fn) {
526 HANDLE h;
527 IO_STATUS_BLOCK iosb;
528 NTSTATUS Status;
529 btrfs_set_inode_info bsii;
530 btrfs_inode_info bii2;
531 ULONG perms = FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
532
533 if (flags_changed || ro_changed)
534 perms |= FILE_WRITE_ATTRIBUTES;
535
536 if (perms_changed || gid_changed || uid_changed)
537 perms |= WRITE_DAC;
538
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;
541
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);
544
545 if (h == INVALID_HANDLE_VALUE) {
546 ShowError(hDlg, GetLastError());
547 return;
548 }
549
550 ZeroMemory(&bsii, sizeof(btrfs_set_inode_info));
551
552 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
553
554 if (!NT_SUCCESS(Status)) {
555 ShowNtStatusError(hDlg, Status);
556 CloseHandle(h);
557 return;
558 }
559
560 if (bii2.inode == SUBVOL_ROOT_INODE && ro_changed) {
561 BY_HANDLE_FILE_INFORMATION bhfi;
562 FILE_BASIC_INFO fbi;
563
564 if (!GetFileInformationByHandle(h, &bhfi)) {
565 ShowError(hDlg, GetLastError());
566 return;
567 }
568
569 memset(&fbi, 0, sizeof(fbi));
570 fbi.FileAttributes = bhfi.dwFileAttributes;
571
572 if (ro_subvol)
573 fbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
574 else
575 fbi.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
576
577 if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(fbi))) {
578 CloseHandle(h);
579 ShowError(hDlg, GetLastError());
580 return;
581 }
582 }
583
584 if (flags_changed || perms_changed || uid_changed || gid_changed || compress_type_changed) {
585 if (flags_changed) {
586 bsii.flags_changed = TRUE;
587 bsii.flags = (bii2.flags & ~flags_set) | (flags & flags_set);
588 }
589
590 if (perms_changed) {
591 bsii.mode_changed = TRUE;
592 bsii.st_mode = (bii2.st_mode & ~mode_set) | (mode & mode_set);
593 }
594
595 if (uid_changed) {
596 bsii.uid_changed = TRUE;
597 bsii.st_uid = uid;
598 }
599
600 if (gid_changed) {
601 bsii.gid_changed = TRUE;
602 bsii.st_gid = gid;
603 }
604
605 if (compress_type_changed) {
606 bsii.compression_type_changed = TRUE;
607 bsii.compression_type = compress_type;
608 }
609
610 Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
611
612 if (!NT_SUCCESS(Status)) {
613 ShowNtStatusError(hDlg, Status);
614 CloseHandle(h);
615 return;
616 }
617 }
618
619 CloseHandle(h);
620 }
621
622 void BtrfsPropSheet::apply_changes(HWND hDlg) {
623 UINT num_files, i;
624 WCHAR fn[MAX_PATH]; // FIXME - is this long enough?
625
626 if (various_uids)
627 uid_changed = FALSE;
628
629 if (various_gids)
630 gid_changed = FALSE;
631
632 if (!flags_changed && !perms_changed && !uid_changed && !gid_changed && !compress_type_changed && !ro_changed)
633 return;
634
635 if (filename[0] != 0)
636 apply_changes_file(hDlg, filename);
637 else {
638 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
639
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);
643 }
644 }
645 }
646
647 flags_changed = FALSE;
648 perms_changed = FALSE;
649 uid_changed = FALSE;
650 gid_changed = FALSE;
651 ro_changed = FALSE;
652 }
653
654 void BtrfsPropSheet::set_size_on_disk(HWND hwndDlg) {
655 WCHAR size_on_disk[1024], s[1024], old_text[1024];
656
657 format_size(totalsize, size_on_disk, sizeof(size_on_disk) / sizeof(WCHAR), TRUE);
658
659 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), size_format, size_on_disk) == STRSAFE_E_INSUFFICIENT_BUFFER) {
660 ShowError(hwndDlg, ERROR_INSUFFICIENT_BUFFER);
661 return;
662 }
663
664 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, old_text, sizeof(old_text) / sizeof(WCHAR));
665
666 if (wcscmp(s, old_text))
667 SetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, s);
668 }
669
670 void BtrfsPropSheet::change_perm_flag(HWND hDlg, ULONG flag, UINT state) {
671 if (state == BST_CHECKED) {
672 mode |= flag;
673 mode_set |= flag;
674 } else if (state == BST_UNCHECKED) {
675 mode &= ~flag;
676 mode_set |= flag;
677 } else if (state == BST_INDETERMINATE) {
678 mode_set = ~flag;
679 }
680
681 perms_changed = TRUE;
682
683 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
684 }
685
686 void BtrfsPropSheet::change_uid(HWND hDlg, UINT32 uid) {
687 if (this->uid != uid) {
688 this->uid = uid;
689 uid_changed = TRUE;
690
691 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
692 }
693 }
694
695 void BtrfsPropSheet::change_gid(HWND hDlg, UINT32 gid) {
696 if (this->gid != gid) {
697 this->gid = gid;
698 gid_changed = TRUE;
699
700 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
701 }
702 }
703
704 void BtrfsPropSheet::update_size_details_dialog(HWND hDlg) {
705 WCHAR size[1024], old_text[1024];
706 int i;
707 ULONG items[] = { IDC_SIZE_INLINE, IDC_SIZE_UNCOMPRESSED, IDC_SIZE_ZLIB, IDC_SIZE_LZO };
708
709 for (i = 0; i < 4; i++) {
710 format_size(sizes[i], size, sizeof(size) / sizeof(WCHAR), TRUE);
711
712 GetDlgItemTextW(hDlg, items[i], old_text, sizeof(old_text) / sizeof(WCHAR));
713
714 if (wcscmp(size, old_text))
715 SetDlgItemTextW(hDlg, items[i], size);
716 }
717 }
718
719 static INT_PTR CALLBACK SizeDetailsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
720 switch (uMsg) {
721 case WM_INITDIALOG:
722 {
723 BtrfsPropSheet* bps = (BtrfsPropSheet*)lParam;
724
725 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
726
727 bps->update_size_details_dialog(hwndDlg);
728
729 if (bps->thread)
730 SetTimer(hwndDlg, 1, 250, NULL);
731
732 return TRUE;
733 }
734
735 case WM_COMMAND:
736 if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
737 EndDialog(hwndDlg, 0);
738 return TRUE;
739 }
740 break;
741
742 case WM_TIMER:
743 {
744 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
745
746 if (bps) {
747 bps->update_size_details_dialog(hwndDlg);
748
749 if (!bps->thread)
750 KillTimer(hwndDlg, 1);
751 }
752
753 break;
754 }
755 }
756
757 return FALSE;
758 }
759
760 static void set_check_box(HWND hwndDlg, ULONG id, UINT64 min, UINT64 max) {
761 if (min && max) {
762 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_CHECKED, 0);
763 } else if (!min && !max) {
764 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_UNCHECKED, 0);
765 } else {
766 LONG_PTR style;
767
768 style = GetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE);
769 style &= ~BS_AUTOCHECKBOX;
770 style |= BS_AUTO3STATE;
771 SetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE, style);
772
773 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_INDETERMINATE, 0);
774 }
775 }
776
777 void BtrfsPropSheet::open_as_admin(HWND hwndDlg) {
778 ULONG num_files, i;
779 WCHAR fn[MAX_PATH];
780
781 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
782
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;
787
788 t[0] = '"';
789 GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
790 wcscat(t, L"\",ShowPropSheet ");
791 wcscat(t, fn);
792
793 RtlZeroMemory(&sei, sizeof(sei));
794
795 sei.cbSize = sizeof(sei);
796 sei.hwnd = hwndDlg;
797 sei.lpVerb = L"runas";
798 sei.lpFile = L"rundll32.exe";
799 sei.lpParameters = t;
800 sei.nShow = SW_SHOW;
801 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
802
803 if (!ShellExecuteExW(&sei)) {
804 ShowError(hwndDlg, GetLastError());
805 return;
806 }
807
808 WaitForSingleObject(sei.hProcess, INFINITE);
809 CloseHandle(sei.hProcess);
810
811 load_file_list();
812 init_propsheet(hwndDlg);
813 }
814 }
815 }
816
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))
820
821 void BtrfsPropSheet::init_propsheet(HWND hwndDlg) {
822 WCHAR s[255];
823 ULONG sr;
824 int i;
825 HWND comptype;
826
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 };
831
832 if (various_subvols) {
833 if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
834 ShowError(hwndDlg, GetLastError());
835 return;
836 }
837 } else {
838 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", subvol) == STRSAFE_E_INSUFFICIENT_BUFFER)
839 return;
840 }
841
842 SetDlgItemTextW(hwndDlg, IDC_SUBVOL, s);
843
844 if (various_inodes) {
845 if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
846 ShowError(hwndDlg, GetLastError());
847 return;
848 }
849 } else {
850 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", inode) == STRSAFE_E_INSUFFICIENT_BUFFER)
851 return;
852 }
853
854 SetDlgItemTextW(hwndDlg, IDC_INODE, s);
855
856 if (various_types)
857 sr = IDS_VARIOUS;
858 else
859 sr = inode_type_to_string_ref(type);
860
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;
866 }
867
868 if (sr == IDS_INODE_UNKNOWN) {
869 WCHAR t[255];
870
871 if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
872 ShowError(hwndDlg, GetLastError());
873 return;
874 }
875
876 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, type) == STRSAFE_E_INSUFFICIENT_BUFFER)
877 return;
878 } else if (sr == IDS_INODE_CHAR || sr == IDS_INODE_BLOCK) {
879 WCHAR t[255];
880
881 if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
882 ShowError(hwndDlg, GetLastError());
883 return;
884 }
885
886 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, major(rdev), minor(rdev)) == STRSAFE_E_INSUFFICIENT_BUFFER)
887 return;
888 } else {
889 if (!LoadStringW(module, sr, s, sizeof(s) / sizeof(WCHAR))) {
890 ShowError(hwndDlg, GetLastError());
891 return;
892 }
893 }
894
895 SetDlgItemTextW(hwndDlg, IDC_TYPE, s);
896
897 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, size_format, sizeof(size_format) / sizeof(WCHAR));
898 set_size_on_disk(hwndDlg);
899
900 if (thread)
901 SetTimer(hwndDlg, 1, 250, NULL);
902
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);
905
906 comptype = GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE);
907
908 if (min_compression_type != max_compression_type) {
909 SendMessage(comptype, CB_ADDSTRING, NULL, (LPARAM)L"");
910 SendMessage(comptype, CB_SETCURSEL, 0, 0);
911 }
912
913 i = 0;
914 while (comp_types[i] != 0) {
915 WCHAR t[255];
916
917 if (!LoadStringW(module, comp_types[i], t, sizeof(t) / sizeof(WCHAR))) {
918 ShowError(hwndDlg, GetLastError());
919 return;
920 }
921
922 SendMessage(comptype, CB_ADDSTRING, NULL, (LPARAM)t);
923
924 i++;
925 }
926
927 if (min_compression_type == max_compression_type) {
928 SendMessage(comptype, CB_SETCURSEL, min_compression_type, 0);
929 compress_type = min_compression_type;
930 }
931
932 EnableWindow(comptype, max_flags & BTRFS_INODE_COMPRESS);
933
934 i = 0;
935 while (perm_controls[i] != 0) {
936 set_check_box(hwndDlg, perm_controls[i], min_mode & perms[i], max_mode & perms[i]);
937 i++;
938 }
939
940 if (various_uids) {
941 if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
942 ShowError(hwndDlg, GetLastError());
943 return;
944 }
945
946 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
947 } else {
948 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", uid) == STRSAFE_E_INSUFFICIENT_BUFFER)
949 return;
950 }
951
952 SetDlgItemTextW(hwndDlg, IDC_UID, s);
953
954 if (various_gids) {
955 if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
956 ShowError(hwndDlg, GetLastError());
957 return;
958 }
959
960 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
961 } else {
962 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", gid) == STRSAFE_E_INSUFFICIENT_BUFFER)
963 return;
964 }
965
966 SetDlgItemTextW(hwndDlg, IDC_GID, s);
967
968 ShowWindow(GetDlgItem(hwndDlg, IDC_SUBVOL_RO), has_subvols);
969
970 if (has_subvols)
971 set_check_box(hwndDlg, IDC_SUBVOL_RO, ro_subvol, various_ro ? (!ro_subvol) : ro_subvol);
972
973 if (!can_change_nocow)
974 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
975
976 if (!can_change_perms) {
977 i = 0;
978 while (perm_controls[i] != 0) {
979 EnableWindow(GetDlgItem(hwndDlg, perm_controls[i]), 0);
980 i++;
981 }
982
983 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
984 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
985 EnableWindow(GetDlgItem(hwndDlg, IDC_SETUID), 0);
986 }
987
988 if (readonly) {
989 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
990 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), 0);
991 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), 0);
992 }
993
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);
997 } else
998 ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_HIDE);
999 }
1000
1001 static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1002 switch (uMsg) {
1003 case WM_INITDIALOG:
1004 {
1005 PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
1006 BtrfsPropSheet* bps = (BtrfsPropSheet*)psp->lParam;
1007
1008 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
1009
1010 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
1011
1012 bps->init_propsheet(hwndDlg);
1013
1014 return FALSE;
1015 }
1016
1017 case WM_COMMAND:
1018 {
1019 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1020
1021 if (bps && !bps->readonly) {
1022 switch (HIWORD(wParam)) {
1023 case BN_CLICKED: {
1024 switch (LOWORD(wParam)) {
1025 case IDC_NODATACOW:
1026 bps->change_inode_flag(hwndDlg, BTRFS_INODE_NODATACOW, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1027 break;
1028
1029 case IDC_COMPRESS:
1030 bps->change_inode_flag(hwndDlg, BTRFS_INODE_COMPRESS, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1031
1032 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) != BST_UNCHECKED);
1033 break;
1034
1035 case IDC_USERR:
1036 bps->change_perm_flag(hwndDlg, S_IRUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1037 break;
1038
1039 case IDC_USERW:
1040 bps->change_perm_flag(hwndDlg, S_IWUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1041 break;
1042
1043 case IDC_USERX:
1044 bps->change_perm_flag(hwndDlg, S_IXUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1045 break;
1046
1047 case IDC_GROUPR:
1048 bps->change_perm_flag(hwndDlg, S_IRGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1049 break;
1050
1051 case IDC_GROUPW:
1052 bps->change_perm_flag(hwndDlg, S_IWGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1053 break;
1054
1055 case IDC_GROUPX:
1056 bps->change_perm_flag(hwndDlg, S_IXGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1057 break;
1058
1059 case IDC_OTHERR:
1060 bps->change_perm_flag(hwndDlg, S_IROTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1061 break;
1062
1063 case IDC_OTHERW:
1064 bps->change_perm_flag(hwndDlg, S_IWOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1065 break;
1066
1067 case IDC_OTHERX:
1068 bps->change_perm_flag(hwndDlg, S_IXOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1069 break;
1070
1071 case IDC_SETUID:
1072 bps->change_perm_flag(hwndDlg, S_ISUID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1073 break;
1074
1075 case IDC_SETGID:
1076 bps->change_perm_flag(hwndDlg, S_ISGID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1077 break;
1078
1079 case IDC_STICKY:
1080 bps->change_perm_flag(hwndDlg, S_ISVTX, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1081 break;
1082
1083 case IDC_SUBVOL_RO:
1084 switch (IsDlgButtonChecked(hwndDlg, LOWORD(wParam))) {
1085 case BST_CHECKED:
1086 bps->ro_subvol = TRUE;
1087 bps->ro_changed = TRUE;
1088 break;
1089
1090 case BST_UNCHECKED:
1091 bps->ro_subvol = FALSE;
1092 bps->ro_changed = TRUE;
1093 break;
1094
1095 case BST_INDETERMINATE:
1096 bps->ro_changed = FALSE;
1097 break;
1098 }
1099
1100 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1101 break;
1102
1103 case IDC_OPEN_ADMIN:
1104 bps->open_as_admin(hwndDlg);
1105 break;
1106 }
1107
1108 break;
1109 }
1110
1111 case EN_CHANGE: {
1112 switch (LOWORD(wParam)) {
1113 case IDC_UID: {
1114 WCHAR s[255];
1115
1116 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1117
1118 bps->change_uid(hwndDlg, _wtoi(s));
1119 break;
1120 }
1121
1122 case IDC_GID: {
1123 WCHAR s[255];
1124
1125 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1126
1127 bps->change_gid(hwndDlg, _wtoi(s));
1128 break;
1129 }
1130 }
1131
1132 break;
1133 }
1134
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);
1139
1140 if (bps->min_compression_type != bps->max_compression_type) {
1141 if (sel == 0)
1142 bps->compress_type_changed = FALSE;
1143 else {
1144 bps->compress_type = sel - 1;
1145 bps->compress_type_changed = TRUE;
1146 }
1147 } else {
1148 bps->compress_type = sel;
1149 bps->compress_type_changed = TRUE;
1150 }
1151
1152 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1153
1154 break;
1155 }
1156 }
1157
1158 break;
1159 }
1160 }
1161 }
1162
1163 break;
1164 }
1165
1166 case WM_NOTIFY:
1167 {
1168 switch (((LPNMHDR)lParam)->code) {
1169 case PSN_KILLACTIVE:
1170 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, FALSE);
1171 break;
1172
1173 case PSN_APPLY: {
1174 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1175
1176 bps->apply_changes(hwndDlg);
1177 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
1178 break;
1179 }
1180
1181 case NM_CLICK:
1182 case NM_RETURN: {
1183 if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_SIZE_ON_DISK)) {
1184 PNMLINK pNMLink = (PNMLINK)lParam;
1185
1186 if (pNMLink->item.iLink == 0)
1187 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SIZE_DETAILS), hwndDlg, SizeDetailsDlgProc, GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
1188 }
1189 break;
1190 }
1191 }
1192 }
1193
1194 case WM_TIMER:
1195 {
1196 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1197
1198 if (bps) {
1199 bps->set_size_on_disk(hwndDlg);
1200
1201 if (!bps->thread)
1202 KillTimer(hwndDlg, 1);
1203 }
1204
1205 break;
1206 }
1207 }
1208
1209 return FALSE;
1210 }
1211
1212 HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) {
1213 PROPSHEETPAGE psp;
1214 HPROPSHEETPAGE hPage;
1215 INITCOMMONCONTROLSEX icex;
1216
1217 if (ignore)
1218 return S_OK;
1219
1220 icex.dwSize = sizeof(icex);
1221 icex.dwICC = ICC_LINK_CLASS;
1222
1223 if (!InitCommonControlsEx(&icex)) {
1224 MessageBoxW(NULL, L"InitCommonControlsEx failed", L"Error", MB_ICONERROR);
1225 }
1226
1227 psp.dwSize = sizeof(psp);
1228 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
1229 psp.hInstance = module;
1230 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROP_SHEET);
1231 psp.hIcon = 0;
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;
1237
1238 hPage = CreatePropertySheetPage(&psp);
1239
1240 if (hPage) {
1241 if (pfnAddPage(hPage, lParam)) {
1242 this->AddRef();
1243 return S_OK;
1244 } else
1245 DestroyPropertySheetPage(hPage);
1246 } else
1247 return E_OUTOFMEMORY;
1248
1249 return E_FAIL;
1250 }
1251
1252 HRESULT __stdcall BtrfsPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) {
1253 return S_OK;
1254 }
1255
1256 #ifdef __cplusplus
1257 extern "C" {
1258 #endif
1259
1260 void CALLBACK ShowPropSheetW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1261 BtrfsPropSheet* bps;
1262 PROPSHEETPAGEW psp;
1263 PROPSHEETHEADERW psh;
1264 WCHAR title[255];
1265
1266 set_dpi_aware();
1267
1268 LoadStringW(module, IDS_STANDALONE_PROPSHEET_TITLE, title, sizeof(title) / sizeof(WCHAR));
1269
1270 bps = new BtrfsPropSheet;
1271 bps->set_cmdline(lpszCmdLine);
1272
1273 psp.dwSize = sizeof(psp);
1274 psp.dwFlags = PSP_USETITLE;
1275 psp.hInstance = module;
1276 psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROP_SHEET);
1277 psp.hIcon = 0;
1278 psp.pszTitle = MAKEINTRESOURCEW(IDS_PROP_SHEET_TITLE);
1279 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
1280 psp.pfnCallback = NULL;
1281 psp.lParam = (LPARAM)bps;
1282
1283 memset(&psh, 0, sizeof(PROPSHEETHEADERW));
1284
1285 psh.dwSize = sizeof(PROPSHEETHEADERW);
1286 psh.dwFlags = PSH_PROPSHEETPAGE;
1287 psh.hwndParent = hwnd;
1288 psh.hInstance = psp.hInstance;
1289 psh.pszCaption = title;
1290 psh.nPages = 1;
1291 psh.ppsp = &psp;
1292
1293 PropertySheetW(&psh);
1294 }
1295
1296 #ifdef __cplusplus
1297 }
1298 #endif