Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[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 #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