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