xref: /reactos/dll/shellext/shellbtrfs/devices.cpp (revision e08ae510)
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 #include "devices.h"
23 #include "resource.h"
24 #include "balance.h"
25 #include <stddef.h>
26 #include <uxtheme.h>
27 #include <setupapi.h>
28 #include <strsafe.h>
29 #include <mountmgr.h>
30 #include <algorithm>
31 #ifndef __REACTOS__
32 #include "../btrfs.h"
33 #else
34 #include <ntddstor.h>
35 #include <ndk/rtlfuncs.h>
36 #include <ndk/obfuncs.h>
37 #include "btrfs.h"
38 #endif
39 
40 DEFINE_GUID(GUID_DEVINTERFACE_HIDDEN_VOLUME, 0x7f108a28L, 0x9833, 0x4b3b, 0xb7, 0x80, 0x2c, 0x6b, 0x5f, 0xa5, 0xc0, 0x62);
41 
42 static std::wstring get_mountdev_name(HANDLE h) {
43     NTSTATUS Status;
44     IO_STATUS_BLOCK iosb;
45     MOUNTDEV_NAME mdn, *mdn2;
46     ULONG mdnsize;
47     std::wstring name;
48 
49     Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
50                                    NULL, 0, &mdn, sizeof(MOUNTDEV_NAME));
51     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
52         return L"";
53 
54     mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
55 
56     mdn2 = (MOUNTDEV_NAME*)malloc(mdnsize);
57 
58     Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
59                                    NULL, 0, mdn2, mdnsize);
60     if (!NT_SUCCESS(Status)) {
61         free(mdn2);
62         return L"";
63     }
64 
65     name = std::wstring(mdn2->Name, mdn2->NameLength / sizeof(WCHAR));
66 
67     free(mdn2);
68 
69     return name;
70 }
71 
72 static void find_devices(HWND hwnd, const GUID* guid, HANDLE mountmgr, std::vector<device>* device_list) {
73     HDEVINFO h;
74 
75     static WCHAR dosdevices[] = L"\\DosDevices\\";
76 
77     h = SetupDiGetClassDevs(guid, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
78 
79     if (h != INVALID_HANDLE_VALUE) {
80         DWORD index = 0;
81         SP_DEVICE_INTERFACE_DATA did;
82 
83         did.cbSize = sizeof(did);
84 
85         if (!SetupDiEnumDeviceInterfaces(h, NULL, guid, index, &did))
86             return;
87 
88         do {
89             SP_DEVINFO_DATA dd;
90             SP_DEVICE_INTERFACE_DETAIL_DATA_W* detail;
91             DWORD size;
92 
93             dd.cbSize = sizeof(dd);
94 
95             SetupDiGetDeviceInterfaceDetailW(h, &did, NULL, 0, &size, NULL);
96 
97             detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(size);
98             memset(detail, 0, size);
99 
100             detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
101 
102             if (SetupDiGetDeviceInterfaceDetailW(h, &did, detail, size, &size, &dd)) {
103                 NTSTATUS Status;
104                 HANDLE file;
105                 device dev;
106                 STORAGE_DEVICE_NUMBER sdn;
107                 IO_STATUS_BLOCK iosb;
108                 UNICODE_STRING path;
109                 OBJECT_ATTRIBUTES attr;
110                 GET_LENGTH_INFORMATION gli;
111                 ULONG i;
112                 UINT8 sb[4096];
113 
114                 path.Buffer = detail->DevicePath;
115                 path.Length = path.MaximumLength = wcslen(detail->DevicePath) * sizeof(WCHAR);
116 
117                 if (path.Length > 4 * sizeof(WCHAR) && path.Buffer[0] == '\\' && path.Buffer[1] == '\\'  && path.Buffer[2] == '?'  && path.Buffer[3] == '\\')
118                     path.Buffer[1] = '?';
119 
120                 InitializeObjectAttributes(&attr, &path, 0, NULL, NULL);
121 
122                 Status = NtOpenFile(&file, FILE_GENERIC_READ, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_ALERT);
123 
124                 if (!NT_SUCCESS(Status))
125                     goto nextitem2;
126 
127                 dev.pnp_name = detail->DevicePath;
128 
129                 Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(GET_LENGTH_INFORMATION));
130                 if (!NT_SUCCESS(Status))
131                     goto nextitem;
132 
133                 dev.size = gli.Length.QuadPart;
134 
135                 Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(STORAGE_DEVICE_NUMBER));
136                 if (!NT_SUCCESS(Status)) {
137                     dev.disk_num = 0xffffffff;
138                     dev.part_num = 0xffffffff;
139                 } else {
140                     dev.disk_num = sdn.DeviceNumber;
141                     dev.part_num = sdn.PartitionNumber;
142                 }
143 
144                 dev.friendly_name = L"";
145                 dev.drive = L"";
146                 dev.fstype = L"";
147                 dev.has_parts = FALSE;
148                 dev.ignore = FALSE;
149                 dev.multi_device = FALSE;
150 
151                 dev.is_disk = RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID);
152 
153                 if (dev.is_disk) {
154                     STORAGE_PROPERTY_QUERY spq;
155                     STORAGE_DEVICE_DESCRIPTOR sdd, *sdd2;
156                     ULONG dlisize;
157                     DRIVE_LAYOUT_INFORMATION_EX* dli;
158 
159                     spq.PropertyId = StorageDeviceProperty;
160                     spq.QueryType = PropertyStandardQuery;
161                     spq.AdditionalParameters[0] = 0;
162 
163                     Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_QUERY_PROPERTY,
164                                                    &spq, sizeof(STORAGE_PROPERTY_QUERY), &sdd, sizeof(STORAGE_DEVICE_DESCRIPTOR));
165 
166                     if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW) {
167                         sdd2 = (STORAGE_DEVICE_DESCRIPTOR*)malloc(sdd.Size);
168 
169                         Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_QUERY_PROPERTY,
170                                                     &spq, sizeof(STORAGE_PROPERTY_QUERY), sdd2, sdd.Size);
171                         if (NT_SUCCESS(Status)) {
172                             std::string desc2;
173 
174                             desc2 = "";
175 
176                             if (sdd2->VendorIdOffset != 0) {
177                                 desc2 += (char*)((UINT8*)sdd2 + sdd2->VendorIdOffset);
178 
179                                 while (desc2.length() > 0 && desc2[desc2.length() - 1] == ' ')
180                                     desc2 = desc2.substr(0, desc2.length() - 1);
181                             }
182 
183                             if (sdd2->ProductIdOffset != 0) {
184                                 if (sdd2->VendorIdOffset != 0 && desc2.length() != 0 && desc2[desc2.length() - 1] != ' ')
185                                     desc2 += " ";
186 
187                                 desc2 += (char*)((UINT8*)sdd2 + sdd2->ProductIdOffset);
188 
189                                 while (desc2.length() > 0 && desc2[desc2.length() - 1] == ' ')
190                                     desc2 = desc2.substr(0, desc2.length() - 1);
191                             }
192 
193                             if (sdd2->VendorIdOffset != 0 || sdd2->ProductIdOffset != 0) {
194                                 ULONG ss;
195 
196                                 ss = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, desc2.c_str(), -1, NULL, 0);
197 
198                                 if (ss > 0) {
199                                     WCHAR* desc3 = (WCHAR*)malloc(ss * sizeof(WCHAR));
200 
201                                     if (MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, desc2.c_str(), -1, desc3, ss * sizeof(WCHAR)))
202                                         dev.friendly_name = desc3;
203 
204                                     free(desc3);
205                                 }
206                             }
207                         }
208 
209                         free(sdd2);
210                     }
211 
212                     dlisize = 0;
213                     dli = NULL;
214 
215                     do {
216                         dlisize += 1024;
217 
218                         if (dli)
219                             free(dli);
220 
221                         dli = (DRIVE_LAYOUT_INFORMATION_EX*)malloc(dlisize);
222 
223                         Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
224                                                        NULL, 0, dli, dlisize);
225                     } while (Status == STATUS_BUFFER_TOO_SMALL);
226 
227                     if (NT_SUCCESS(Status) && dli->PartitionCount > 0)
228                         dev.has_parts = TRUE;
229 
230                     free(dli);
231                 } else {
232                     ULONG mmpsize;
233                     MOUNTMGR_MOUNT_POINT* mmp;
234                     MOUNTMGR_MOUNT_POINTS mmps;
235 
236                     mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + path.Length;
237 
238                     mmp = (MOUNTMGR_MOUNT_POINT*)malloc(mmpsize);
239 
240                     RtlZeroMemory(mmp, sizeof(MOUNTMGR_MOUNT_POINT));
241                     mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
242                     mmp->DeviceNameLength = path.Length;
243                     RtlCopyMemory(&mmp[1], path.Buffer, path.Length);
244 
245                     Status = NtDeviceIoControlFile(mountmgr, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_QUERY_POINTS,
246                                                    mmp, mmpsize, &mmps, sizeof(MOUNTMGR_MOUNT_POINTS));
247                     if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW) {
248                         MOUNTMGR_MOUNT_POINTS* mmps2;
249 
250                         mmps2 = (MOUNTMGR_MOUNT_POINTS*)malloc(mmps.Size);
251 
252                         Status = NtDeviceIoControlFile(mountmgr, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_QUERY_POINTS,
253                                                     mmp, mmpsize, mmps2, mmps.Size);
254 
255                         if (NT_SUCCESS(Status)) {
256                             ULONG i;
257 
258                             for (i = 0; i < mmps2->NumberOfMountPoints; i++) {
259                                 WCHAR* symlink = (WCHAR*)((UINT8*)mmps2 + mmps2->MountPoints[i].SymbolicLinkNameOffset);
260 
261                                 if (mmps2->MountPoints[i].SymbolicLinkNameLength == 0x1c &&
262                                     RtlCompareMemory(symlink, dosdevices, wcslen(dosdevices) * sizeof(WCHAR)) == wcslen(dosdevices) * sizeof(WCHAR) &&
263                                     symlink[13] == ':'
264                                 ) {
265                                     WCHAR dr[3];
266 
267                                     dr[0] = symlink[12];
268                                     dr[1] = ':';
269                                     dr[2] = 0;
270 
271                                     dev.drive = dr;
272                                     break;
273                                 }
274                             }
275                         }
276                     }
277 
278                     free(mmp);
279                 }
280 
281                 if (!dev.is_disk || !dev.has_parts) {
282                     i = 0;
283                     while (fs_ident[i].name) {
284                         if (i == 0 || fs_ident[i].kboff != fs_ident[i-1].kboff) {
285                             LARGE_INTEGER off;
286 
287                             off.QuadPart = fs_ident[i].kboff * 1024;
288                             Status = NtReadFile(file, NULL, NULL, NULL, &iosb, sb, sizeof(sb), &off, NULL);
289                         }
290 
291                         if (NT_SUCCESS(Status)) {
292                             if (RtlCompareMemory(sb + fs_ident[i].sboff, fs_ident[i].magic, fs_ident[i].magiclen) == fs_ident[i].magiclen) {
293                                 dev.fstype = fs_ident[i].name;
294 
295                                 if (dev.fstype == L"Btrfs") {
296                                     superblock* bsb = (superblock*)sb;
297 
298                                     RtlCopyMemory(&dev.fs_uuid, &bsb->uuid, sizeof(BTRFS_UUID));
299                                     RtlCopyMemory(&dev.dev_uuid, &bsb->dev_item.device_uuid, sizeof(BTRFS_UUID));
300                                 }
301 
302                                 break;
303                             }
304                         }
305 
306                         i++;
307                     }
308 
309                     if (dev.fstype == L"Btrfs" && RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) != sizeof(GUID)) {
310                         std::wstring name;
311                         std::wstring pref = L"\\Device\\Btrfs{";
312 
313                         name = get_mountdev_name(file);
314 
315                         if (name.length() > pref.length() && RtlCompareMemory(name.c_str(), pref.c_str(), pref.length() * sizeof(WCHAR)) == pref.length() * sizeof(WCHAR))
316                             dev.ignore = TRUE;
317                     }
318                 }
319 
320                 device_list->push_back(dev);
321 
322 nextitem:
323                 NtClose(file);
324             }
325 
326 nextitem2:
327             free(detail);
328 
329             index++;
330         } while (SetupDiEnumDeviceInterfaces(h, NULL, guid, index, &did));
331 
332         SetupDiDestroyDeviceInfoList(h);
333     } else {
334         ShowError(hwnd, GetLastError());
335         return;
336     }
337 }
338 
339 static bool sort_devices(device i, device j) {
340     if (i.disk_num < j.disk_num)
341         return true;
342 
343     if (i.disk_num == j.disk_num && i.part_num < j.part_num)
344         return true;
345 
346     return false;
347 }
348 
349 void BtrfsDeviceAdd::populate_device_tree(HWND tree) {
350     HWND hwnd = GetParent(tree);
351     unsigned int i;
352     ULONG last_disk_num = 0xffffffff;
353     HTREEITEM diskitem;
354     NTSTATUS Status;
355     OBJECT_ATTRIBUTES attr;
356     UNICODE_STRING us;
357     IO_STATUS_BLOCK iosb;
358     HANDLE mountmgr, btrfsh;
359     btrfs_filesystem* bfs = NULL;
360 
361     static WCHAR btrfs[] = L"\\Btrfs";
362 
363     device_list.clear();
364 
365     RtlInitUnicodeString(&us, MOUNTMGR_DEVICE_NAME);
366     InitializeObjectAttributes(&attr, &us, 0, NULL, NULL);
367 
368     Status = NtOpenFile(&mountmgr, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb,
369                         FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT);
370     if (!NT_SUCCESS(Status)) {
371         MessageBoxW(hwnd, L"Could not get handle to mount manager.", L"Error", MB_ICONERROR);
372         return;
373     }
374 
375     us.Length = us.MaximumLength = wcslen(btrfs) * sizeof(WCHAR);
376     us.Buffer = btrfs;
377 
378     InitializeObjectAttributes(&attr, &us, 0, NULL, NULL);
379 
380     Status = NtOpenFile(&btrfsh, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &attr, &iosb,
381                         FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
382     if (NT_SUCCESS(Status)) {
383         ULONG bfssize = 0;
384 
385         do {
386             bfssize += 1024;
387 
388             if (bfs) free(bfs);
389             bfs = (btrfs_filesystem*)malloc(bfssize);
390 
391             Status = NtDeviceIoControlFile(btrfsh, NULL, NULL, NULL, &iosb, IOCTL_BTRFS_QUERY_FILESYSTEMS, NULL, 0, bfs, bfssize);
392             if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
393                 free(bfs);
394                 bfs = NULL;
395                 break;
396             }
397         } while (Status == STATUS_BUFFER_OVERFLOW);
398 
399         if (bfs && bfs->num_devices == 0) { // no mounted filesystems found
400             free(bfs);
401             bfs = NULL;
402         }
403     }
404     NtClose(btrfsh);
405 
406     find_devices(hwnd, &GUID_DEVINTERFACE_DISK, mountmgr, &device_list);
407     find_devices(hwnd, &GUID_DEVINTERFACE_VOLUME, mountmgr, &device_list);
408     find_devices(hwnd, &GUID_DEVINTERFACE_HIDDEN_VOLUME, mountmgr, &device_list);
409 
410     NtClose(mountmgr);
411 
412     std::sort(device_list.begin(), device_list.end(), sort_devices);
413 
414     for (i = 0; i < device_list.size(); i++) {
415         if (!device_list[i].ignore) {
416             TVINSERTSTRUCTW tis;
417             HTREEITEM item;
418             std::wstring name;
419             WCHAR size[255];
420 
421             if (device_list[i].disk_num != 0xffffffff && device_list[i].disk_num == last_disk_num)
422                 tis.hParent = diskitem;
423             else
424                 tis.hParent = TVI_ROOT;
425 
426             tis.hInsertAfter = TVI_LAST;
427             tis.itemex.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
428             tis.itemex.state = TVIS_EXPANDED;
429             tis.itemex.stateMask = TVIS_EXPANDED;
430 
431             if (device_list[i].disk_num != 0xffffffff) {
432                 WCHAR t[255], u[255];
433 
434                 if (!LoadStringW(module, device_list[i].part_num != 0 ? IDS_PARTITION : IDS_DISK_NUM, t, sizeof(t) / sizeof(WCHAR))) {
435                     ShowError(hwnd, GetLastError());
436                     return;
437                 }
438 
439                 if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, device_list[i].part_num != 0 ? device_list[i].part_num : device_list[i].disk_num) == STRSAFE_E_INSUFFICIENT_BUFFER)
440                     return;
441 
442                 name = u;
443             } else
444                 name = device_list[i].pnp_name;
445 
446             // match child Btrfs devices to their parent
447             if (bfs && device_list[i].drive == L"" && device_list[i].fstype == L"Btrfs") {
448                 btrfs_filesystem* bfs2 = bfs;
449 
450                 while (TRUE) {
451                     if (RtlCompareMemory(&bfs2->uuid, &device_list[i].fs_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
452                         ULONG j, k;
453                         btrfs_filesystem_device* dev;
454 
455                         for (j = 0; j < bfs2->num_devices; j++) {
456                             if (j == 0)
457                                 dev = &bfs2->device;
458                             else
459                                 dev = (btrfs_filesystem_device*)((UINT8*)dev + offsetof(btrfs_filesystem_device, name[0]) + dev->name_length);
460 
461                             if (RtlCompareMemory(&device_list[i].dev_uuid, &device_list[i].dev_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
462                                 for (k = 0; k < device_list.size(); k++) {
463                                     if (k != i && device_list[k].fstype == L"Btrfs" && device_list[k].drive != L"" &&
464                                         RtlCompareMemory(&device_list[k].fs_uuid, &device_list[i].fs_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
465                                         device_list[i].drive = device_list[k].drive;
466                                         break;
467                                     }
468                                 }
469 
470                                 device_list[i].multi_device = bfs2->num_devices > 1;
471 
472                                 break;
473                             }
474                         }
475 
476                         break;
477                     }
478 
479                     if (bfs2->next_entry != 0)
480                         bfs2 = (btrfs_filesystem*)((UINT8*)bfs2 + bfs2->next_entry);
481                     else
482                         break;
483                 }
484             }
485 
486             name += L" (";
487 
488             if (device_list[i].friendly_name != L"") {
489                 name += device_list[i].friendly_name;
490                 name += L", ";
491             }
492 
493             if (device_list[i].drive != L"") {
494                 name += device_list[i].drive;
495                 name += L", ";
496             }
497 
498             if (device_list[i].fstype != L"") {
499                 name += device_list[i].fstype;
500                 name += L", ";
501             }
502 
503             format_size(device_list[i].size, size, sizeof(size) / sizeof(WCHAR), FALSE);
504             name += size;
505 
506             name += L")";
507 
508             tis.itemex.pszText = (WCHAR*)name.c_str();
509             tis.itemex.cchTextMax = name.length();
510             tis.itemex.lParam = (LPARAM)&device_list[i];
511 
512             item = (HTREEITEM)SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tis);
513             if (!item) {
514                 MessageBoxW(hwnd, L"TVM_INSERTITEM failed", L"Error", MB_ICONERROR);
515                 return;
516             }
517 
518             if (device_list[i].part_num == 0) {
519                 diskitem = item;
520                 last_disk_num = device_list[i].disk_num;
521             }
522         }
523     }
524 }
525 
526 void BtrfsDeviceAdd::AddDevice(HWND hwndDlg) {
527     WCHAR mess[255], title[255];
528     NTSTATUS Status;
529     UNICODE_STRING vn;
530     OBJECT_ATTRIBUTES attr;
531     IO_STATUS_BLOCK iosb;
532     HANDLE h, h2;
533 
534     if (!sel) {
535         EndDialog(hwndDlg, 0);
536         return;
537     }
538 
539     if (sel->fstype != L"") {
540         WCHAR s[255];
541 
542         if (!LoadStringW(module, IDS_ADD_DEVICE_CONFIRMATION_FS, s, sizeof(s) / sizeof(WCHAR))) {
543             ShowError(hwndDlg, GetLastError());
544             return;
545         }
546 
547         if (StringCchPrintfW(mess, sizeof(mess) / sizeof(WCHAR), s, sel->fstype.c_str()) == STRSAFE_E_INSUFFICIENT_BUFFER)
548             return;
549     } else {
550         if (!LoadStringW(module, IDS_ADD_DEVICE_CONFIRMATION, mess, sizeof(mess) / sizeof(WCHAR))) {
551             ShowError(hwndDlg, GetLastError());
552             return;
553         }
554     }
555 
556     if (!LoadStringW(module, IDS_CONFIRMATION_TITLE, title, sizeof(title) / sizeof(WCHAR))) {
557         ShowError(hwndDlg, GetLastError());
558         return;
559     }
560 
561     if (MessageBoxW(hwndDlg, mess, title, MB_YESNO) != IDYES)
562         return;
563 
564     h = CreateFileW(cmdline, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
565                     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
566 
567     if (h == INVALID_HANDLE_VALUE) {
568         ShowError(hwndDlg, GetLastError());
569         return;
570     }
571 
572     vn.Length = vn.MaximumLength = sel->pnp_name.length() * sizeof(WCHAR);
573     vn.Buffer = (WCHAR*)sel->pnp_name.c_str();
574 
575     InitializeObjectAttributes(&attr, &vn, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
576 
577     Status = NtOpenFile(&h2, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT);
578     if (!NT_SUCCESS(Status)) {
579         ShowNtStatusError(hwndDlg, Status);
580         CloseHandle(h);
581         return;
582     }
583 
584     if (!sel->is_disk) {
585         Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0);
586         if (!NT_SUCCESS(Status)) {
587             WCHAR t[255], u[255];
588 
589             if (!LoadStringW(module, IDS_LOCK_FAILED, t, sizeof(t) / sizeof(WCHAR))) {
590                 ShowError(hwnd, GetLastError());
591                 return;
592             }
593 
594             if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, Status) == STRSAFE_E_INSUFFICIENT_BUFFER)
595                 return;
596 
597             if (!LoadStringW(module, IDS_ERROR, title, sizeof(title) / sizeof(WCHAR))) {
598                 ShowError(hwndDlg, GetLastError());
599                 return;
600             }
601 
602             MessageBoxW(hwndDlg, u, title, MB_ICONERROR);
603 
604             NtClose(h2);
605             CloseHandle(h);
606             return;
607         }
608     }
609 
610     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_ADD_DEVICE, &h2, sizeof(HANDLE), NULL, 0);
611     if (!NT_SUCCESS(Status)) {
612         ShowNtStatusError(hwndDlg, Status);
613         NtClose(h2);
614         CloseHandle(h);
615         return;
616     }
617 
618     if (!sel->is_disk) {
619         Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0);
620         if (!NT_SUCCESS(Status))
621             ShowNtStatusError(hwndDlg, Status);
622 
623         Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0);
624         if (!NT_SUCCESS(Status))
625             ShowNtStatusError(hwndDlg, Status);
626     }
627 
628     NtClose(h2);
629     CloseHandle(h);
630 
631     EndDialog(hwndDlg, 0);
632 }
633 
634 INT_PTR CALLBACK BtrfsDeviceAdd::DeviceAddDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
635     switch (uMsg) {
636         case WM_INITDIALOG:
637         {
638             EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
639             populate_device_tree(GetDlgItem(hwndDlg, IDC_DEVICE_TREE));
640             EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
641             break;
642         }
643 
644         case WM_COMMAND:
645             switch (HIWORD(wParam)) {
646                 case BN_CLICKED:
647                     switch (LOWORD(wParam)) {
648                         case IDOK:
649                             AddDevice(hwndDlg);
650                         return TRUE;
651 
652                         case IDCANCEL:
653                             EndDialog(hwndDlg, 0);
654                         return TRUE;
655                     }
656                 break;
657             }
658         break;
659 
660         case WM_NOTIFY:
661             switch (((LPNMHDR)lParam)->code) {
662                 case TVN_SELCHANGEDW:
663                 {
664                     NMTREEVIEWW* nmtv = (NMTREEVIEWW*)lParam;
665                     TVITEMW tvi;
666                     BOOL enable = FALSE;
667 
668                     RtlZeroMemory(&tvi, sizeof(TVITEMW));
669                     tvi.hItem = nmtv->itemNew.hItem;
670                     tvi.mask = TVIF_PARAM | TVIF_HANDLE;
671 
672                     if (SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_TREE), TVM_GETITEMW, 0, (LPARAM)&tvi))
673                         sel = tvi.lParam == 0 ? NULL : (device*)tvi.lParam;
674                     else
675                         sel = NULL;
676 
677                     if (sel)
678                         enable = (!sel->is_disk || !sel->has_parts) && !sel->multi_device;
679 
680                     EnableWindow(GetDlgItem(hwndDlg, IDOK), enable);
681                     break;
682                 }
683             }
684         break;
685     }
686 
687     return FALSE;
688 }
689 
690 static INT_PTR CALLBACK stub_DeviceAddDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
691     BtrfsDeviceAdd* bda;
692 
693     if (uMsg == WM_INITDIALOG) {
694         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
695         bda = (BtrfsDeviceAdd*)lParam;
696     } else {
697         bda = (BtrfsDeviceAdd*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
698     }
699 
700     if (bda)
701         return bda->DeviceAddDlgProc(hwndDlg, uMsg, wParam, lParam);
702     else
703         return FALSE;
704 }
705 
706 void BtrfsDeviceAdd::ShowDialog() {
707     DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DEVICE_ADD), hwnd, stub_DeviceAddDlgProc, (LPARAM)this);
708 }
709 
710 BtrfsDeviceAdd::BtrfsDeviceAdd(HINSTANCE hinst, HWND hwnd, WCHAR* cmdline) {
711     this->hinst = hinst;
712     this->hwnd = hwnd;
713     this->cmdline = cmdline;
714 
715     sel = NULL;
716 }
717 
718 void BtrfsDeviceResize::do_resize(HWND hwndDlg) {
719     HANDLE h;
720     NTSTATUS Status;
721     IO_STATUS_BLOCK iosb;
722     btrfs_resize br;
723 
724     h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
725                     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
726 
727     if (h == INVALID_HANDLE_VALUE) {
728         ShowError(hwndDlg, GetLastError());
729         return;
730     }
731 
732     br.device = dev_id;
733     br.size = new_size;
734 
735     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESIZE, &br, sizeof(btrfs_resize), NULL, 0);
736 
737     if (Status != STATUS_MORE_PROCESSING_REQUIRED && !NT_SUCCESS(Status)) {
738         ShowNtStatusError(hwndDlg, Status);
739         CloseHandle(h);
740         return;
741     }
742 
743     CloseHandle(h);
744 
745     if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
746         WCHAR s[255], t[255], u[255];
747 
748         LoadStringW(module, IDS_RESIZE_SUCCESSFUL, s, sizeof(s) / sizeof(WCHAR));
749         format_size(new_size, u, sizeof(u) / sizeof(WCHAR), TRUE);
750         StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, dev_id, u);
751         MessageBoxW(hwndDlg, t, L"", MB_OK);
752 
753         EndDialog(hwndDlg, 0);
754     } else {
755         BtrfsBalance* bb;
756         HWND par;
757 
758         par = GetParent(hwndDlg);
759         EndDialog(hwndDlg, 0);
760 
761         bb = new BtrfsBalance(fn, FALSE, TRUE);
762 
763         bb->ShowBalance(par);
764 
765         delete bb;
766     }
767 }
768 
769 INT_PTR CALLBACK BtrfsDeviceResize::DeviceResizeDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
770     switch (uMsg) {
771         case WM_INITDIALOG:
772         {
773             HANDLE h;
774             WCHAR s[255], t[255], u[255];
775 
776             EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
777 
778             GetDlgItemTextW(hwndDlg, IDC_RESIZE_DEVICE_ID, s, sizeof(s) / sizeof(WCHAR));
779             StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, dev_id);
780             SetDlgItemTextW(hwndDlg, IDC_RESIZE_DEVICE_ID, t);
781 
782             h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
783                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
784 
785             if (h != INVALID_HANDLE_VALUE) {
786                 NTSTATUS Status;
787                 IO_STATUS_BLOCK iosb;
788                 btrfs_device *devices, *bd;
789                 ULONG devsize;
790                 BOOL found = FALSE;
791                 HWND slider;
792 
793                 devsize = 1024;
794                 devices = (btrfs_device*)malloc(devsize);
795 
796                 while (TRUE) {
797                     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_DEVICES, NULL, 0, devices, devsize);
798                     if (Status == STATUS_BUFFER_OVERFLOW) {
799                         devsize += 1024;
800 
801                         free(devices);
802                         devices = (btrfs_device*)malloc(devsize);
803                     } else
804                         break;
805                 }
806 
807                 if (!NT_SUCCESS(Status)) {
808                     free(devices);
809                     CloseHandle(h);
810                     return FALSE;
811                 }
812 
813                 bd = devices;
814 
815                 while (TRUE) {
816                     if (bd->dev_id == dev_id) {
817                         memcpy(&dev_info, bd, sizeof(btrfs_device));
818                         found = TRUE;
819                         break;
820                     }
821 
822                     if (bd->next_entry > 0)
823                         bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
824                     else
825                         break;
826                 }
827 
828                 if (!found) {
829                     free(devices);
830                     CloseHandle(h);
831                     return FALSE;
832                 }
833 
834                 free(devices);
835                 CloseHandle(h);
836 
837                 GetDlgItemTextW(hwndDlg, IDC_RESIZE_CURSIZE, s, sizeof(s) / sizeof(WCHAR));
838                 format_size(dev_info.size, u, sizeof(u) / sizeof(WCHAR), TRUE);
839                 StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, u);
840                 SetDlgItemTextW(hwndDlg, IDC_RESIZE_CURSIZE, t);
841 
842                 new_size = dev_info.size;
843 
844                 GetDlgItemTextW(hwndDlg, IDC_RESIZE_NEWSIZE, new_size_text, sizeof(new_size_text) / sizeof(WCHAR));
845                 StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), new_size_text, u);
846                 SetDlgItemTextW(hwndDlg, IDC_RESIZE_NEWSIZE, t);
847 
848                 slider = GetDlgItem(hwndDlg, IDC_RESIZE_SLIDER);
849                 SendMessageW(slider, TBM_SETRANGEMIN, FALSE, 0);
850                 SendMessageW(slider, TBM_SETRANGEMAX, FALSE, dev_info.max_size / 1048576);
851                 SendMessageW(slider, TBM_SETPOS, TRUE, new_size / 1048576);
852             } else
853                 return FALSE;
854 
855             break;
856         }
857 
858         case WM_COMMAND:
859             switch (HIWORD(wParam)) {
860                 case BN_CLICKED:
861                     switch (LOWORD(wParam)) {
862                         case IDOK:
863                             do_resize(hwndDlg);
864                             return TRUE;
865 
866                         case IDCANCEL:
867                             EndDialog(hwndDlg, 0);
868                             return TRUE;
869                     }
870                 break;
871             }
872         break;
873 
874         case WM_HSCROLL:
875         {
876             WCHAR t[255], u[255];
877 
878             new_size = UInt32x32To64(SendMessageW(GetDlgItem(hwndDlg, IDC_RESIZE_SLIDER), TBM_GETPOS, 0, 0), 1048576);
879 
880             format_size(new_size, u, sizeof(u) / sizeof(WCHAR), TRUE);
881             StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), new_size_text, u);
882             SetDlgItemTextW(hwndDlg, IDC_RESIZE_NEWSIZE, t);
883 
884             EnableWindow(GetDlgItem(hwndDlg, IDOK), new_size > 0 ? TRUE : FALSE);
885 
886             break;
887         }
888     }
889 
890     return FALSE;
891 }
892 
893 static INT_PTR CALLBACK stub_DeviceResizeDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
894     BtrfsDeviceResize* bdr;
895 
896     if (uMsg == WM_INITDIALOG) {
897         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
898         bdr = (BtrfsDeviceResize*)lParam;
899     } else
900         bdr = (BtrfsDeviceResize*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
901 
902     if (bdr)
903         return bdr->DeviceResizeDlgProc(hwndDlg, uMsg, wParam, lParam);
904     else
905         return FALSE;
906 }
907 
908 void BtrfsDeviceResize::ShowDialog(HWND hwnd, WCHAR* fn, UINT64 dev_id) {
909     this->dev_id = dev_id;
910     wcscpy(this->fn, fn);
911 
912     DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RESIZE), hwnd, stub_DeviceResizeDlgProc, (LPARAM)this);
913 }
914 
915 #ifdef __cplusplus
916 extern "C" {
917 #endif
918 
919 void CALLBACK AddDeviceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
920     HANDLE token;
921     TOKEN_PRIVILEGES tp;
922     LUID luid;
923     BtrfsDeviceAdd* bda;
924 
925     set_dpi_aware();
926 
927     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
928         ShowError(hwnd, GetLastError());
929         return;
930     }
931 
932     if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
933         ShowError(hwnd, GetLastError());
934         goto end;
935     }
936 
937     tp.PrivilegeCount = 1;
938     tp.Privileges[0].Luid = luid;
939     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
940 
941     if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
942         ShowError(hwnd, GetLastError());
943         goto end;
944     }
945 
946     bda = new BtrfsDeviceAdd(hinst, hwnd, lpszCmdLine);
947     bda->ShowDialog();
948     delete bda;
949 
950 end:
951     CloseHandle(token);
952 }
953 
954 void CALLBACK RemoveDeviceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
955     WCHAR *s, *vol, *dev;
956     UINT64 devid;
957     HANDLE h, token;
958     TOKEN_PRIVILEGES tp;
959     LUID luid;
960     NTSTATUS Status;
961     IO_STATUS_BLOCK iosb;
962     BtrfsBalance* bb;
963 
964     set_dpi_aware();
965 
966     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
967         ShowError(hwnd, GetLastError());
968         return;
969     }
970 
971     if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
972         ShowError(hwnd, GetLastError());
973         goto end;
974     }
975 
976     tp.PrivilegeCount = 1;
977     tp.Privileges[0].Luid = luid;
978     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
979 
980     if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
981         ShowError(hwnd, GetLastError());
982         goto end;
983     }
984 
985     s = wcsstr(lpszCmdLine, L"|");
986     if (!s)
987         goto end;
988 
989     s[0] = 0;
990 
991     vol = lpszCmdLine;
992     dev = &s[1];
993 
994     devid = _wtoi(dev);
995     if (devid == 0)
996         goto end;
997 
998     h = CreateFileW(vol, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
999                     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
1000 
1001     if (h == INVALID_HANDLE_VALUE) {
1002         ShowError(hwnd, GetLastError());
1003         goto end;
1004     }
1005 
1006     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_REMOVE_DEVICE, &devid, sizeof(UINT64), NULL, 0);
1007     if (!NT_SUCCESS(Status)) {
1008         if (Status == STATUS_CANNOT_DELETE)
1009             ShowStringError(hwnd, IDS_CANNOT_REMOVE_RAID);
1010         else
1011             ShowNtStatusError(hwnd, Status);
1012 
1013         CloseHandle(h);
1014         goto end;
1015     }
1016 
1017     CloseHandle(h);
1018 
1019     bb = new BtrfsBalance(vol, TRUE);
1020 
1021     bb->ShowBalance(hwnd);
1022 
1023     delete bb;
1024 
1025 end:
1026     CloseHandle(token);
1027 }
1028 
1029 void CALLBACK ResizeDeviceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1030     WCHAR *s, *vol, *dev;
1031     UINT64 devid;
1032     HANDLE token;
1033     TOKEN_PRIVILEGES tp;
1034     LUID luid;
1035     BtrfsDeviceResize* bdr;
1036 
1037     set_dpi_aware();
1038 
1039     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
1040         ShowError(hwnd, GetLastError());
1041         return;
1042     }
1043 
1044     if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
1045         ShowError(hwnd, GetLastError());
1046         goto end;
1047     }
1048 
1049     tp.PrivilegeCount = 1;
1050     tp.Privileges[0].Luid = luid;
1051     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1052 
1053     if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
1054         ShowError(hwnd, GetLastError());
1055         goto end;
1056     }
1057 
1058     s = wcsstr(lpszCmdLine, L"|");
1059     if (!s)
1060         goto end;
1061 
1062     s[0] = 0;
1063 
1064     vol = lpszCmdLine;
1065     dev = &s[1];
1066 
1067     devid = _wtoi(dev);
1068     if (devid == 0)
1069         goto end;
1070 
1071     bdr = new BtrfsDeviceResize;
1072     bdr->ShowDialog(hwnd, vol, devid);
1073     delete bdr;
1074 
1075 end:
1076     CloseHandle(token);
1077 }
1078 
1079 #ifdef __cplusplus
1080 }
1081 #endif
1082