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 #include "btrfs_drv.h"
19
20 #include <ntddk.h>
21 #include <ntifs.h>
22 #include <mountmgr.h>
23 #include <windef.h>
24 #include <ntddstor.h>
25 #include <ntdddisk.h>
26 #include <ntddvol.h>
27
28 #include <initguid.h>
29 #include <wdmguid.h>
30 #include <ioevent.h>
31
32 extern ERESOURCE pdo_list_lock;
33 extern LIST_ENTRY pdo_list;
34 extern UNICODE_STRING registry_path;
35 extern KEVENT mountmgr_thread_event;
36 extern HANDLE mountmgr_thread_handle;
37 extern bool shutting_down;
38 extern PDEVICE_OBJECT busobj;
39 extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
40 extern ERESOURCE boot_lock;
41 extern PDRIVER_OBJECT drvobj;
42
43 typedef void (*pnp_callback)(PUNICODE_STRING devpath);
44
45 #ifndef __REACTOS__
46 // not in mingw yet
47 #ifndef _MSC_VER
48 DEFINE_GUID(GUID_IO_VOLUME_FVE_STATUS_CHANGE, 0x062998b2, 0xee1f, 0x4b6a, 0xb8, 0x57, 0xe7, 0x6c, 0xbb, 0xe9, 0xa6, 0xda);
49 #endif
50 #endif // __REACTOS__
51
52 extern PDEVICE_OBJECT master_devobj;
53
54 typedef struct {
55 LIST_ENTRY list_entry;
56 PDEVICE_OBJECT devobj;
57 void* notification_entry;
58 UNICODE_STRING devpath;
59 WCHAR buf[1];
60 } fve_data;
61
62 static LIST_ENTRY fve_data_list = { &fve_data_list, &fve_data_list };
63 KSPIN_LOCK fve_data_lock;
64
fs_ignored(BTRFS_UUID * uuid)65 static bool fs_ignored(BTRFS_UUID* uuid) {
66 UNICODE_STRING path, ignoreus;
67 NTSTATUS Status;
68 OBJECT_ATTRIBUTES oa;
69 KEY_VALUE_FULL_INFORMATION* kvfi;
70 ULONG dispos, retlen, kvfilen, i, j;
71 HANDLE h;
72 bool ret = false;
73
74 path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
75
76 path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
77 if (!path.Buffer) {
78 ERR("out of memory\n");
79 return false;
80 }
81
82 RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
83
84 i = registry_path.Length / sizeof(WCHAR);
85
86 path.Buffer[i] = '\\';
87 i++;
88
89 for (j = 0; j < 16; j++) {
90 path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
91 path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
92
93 i += 2;
94
95 if (j == 3 || j == 5 || j == 7 || j == 9) {
96 path.Buffer[i] = '-';
97 i++;
98 }
99 }
100
101 InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
102
103 Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
104
105 if (!NT_SUCCESS(Status)) {
106 TRACE("ZwCreateKey returned %08lx\n", Status);
107 ExFreePool(path.Buffer);
108 return false;
109 }
110
111 RtlInitUnicodeString(&ignoreus, L"Ignore");
112
113 kvfilen = (ULONG)offsetof(KEY_VALUE_FULL_INFORMATION, Name[0]) + (255 * sizeof(WCHAR));
114 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
115 if (!kvfi) {
116 ERR("out of memory\n");
117 ZwClose(h);
118 ExFreePool(path.Buffer);
119 return false;
120 }
121
122 Status = ZwQueryValueKey(h, &ignoreus, KeyValueFullInformation, kvfi, kvfilen, &retlen);
123 if (NT_SUCCESS(Status)) {
124 if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(uint32_t)) {
125 uint32_t* pr = (uint32_t*)((uint8_t*)kvfi + kvfi->DataOffset);
126
127 ret = *pr;
128 }
129 }
130
131 ZwClose(h);
132 ExFreePool(kvfi);
133 ExFreePool(path.Buffer);
134
135 return ret;
136 }
137
138 typedef struct {
139 PIO_WORKITEM work_item;
140 PFILE_OBJECT fileobj;
141 PDEVICE_OBJECT devobj;
142 UNICODE_STRING devpath;
143 WCHAR buf[1];
144 } fve_callback_context;
145
_Function_class_(IO_WORKITEM_ROUTINE)146 _Function_class_(IO_WORKITEM_ROUTINE)
147 static void __stdcall fve_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
148 fve_callback_context* ctx = con;
149
150 UNUSED(DeviceObject);
151
152 if (volume_arrival(&ctx->devpath, true)) {
153 KIRQL irql;
154 LIST_ENTRY* le;
155 fve_data* d = NULL;
156
157 // volume no longer locked - unregister notification
158
159 KeAcquireSpinLock(&fve_data_lock, &irql);
160
161 le = fve_data_list.Flink;
162 while (le != &fve_data_list) {
163 fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry);
164
165 if (d2->devobj == ctx->devobj) {
166 RemoveEntryList(&d2->list_entry);
167 d = d2;
168 break;
169 }
170
171 le = le->Flink;
172 }
173
174 KeReleaseSpinLock(&fve_data_lock, irql);
175
176 if (d) {
177 IoUnregisterPlugPlayNotification(d->notification_entry);
178 ExFreePool(d);
179 }
180 }
181
182 IoFreeWorkItem(ctx->work_item);
183 ExFreePool(ctx);
184 }
185
event_notification(PVOID NotificationStructure,PVOID Context)186 static NTSTATUS __stdcall event_notification(PVOID NotificationStructure, PVOID Context) {
187 TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = NotificationStructure;
188 PDEVICE_OBJECT devobj = Context;
189 PIO_WORKITEM work_item;
190 fve_callback_context* ctx;
191 LIST_ENTRY* le;
192 KIRQL irql;
193
194 if (RtlCompareMemory(&tdrn->Event, &GUID_IO_VOLUME_FVE_STATUS_CHANGE, sizeof(GUID)) != sizeof(GUID))
195 return STATUS_SUCCESS;
196
197 /* The FVE event has trailing data, presumably telling us whether the volume has
198 * been unlocked or whatever, but unfortunately it's undocumented! */
199
200 work_item = IoAllocateWorkItem(master_devobj);
201 if (!work_item) {
202 ERR("out of memory\n");
203 return STATUS_SUCCESS;
204 }
205
206 KeAcquireSpinLock(&fve_data_lock, &irql);
207
208 le = fve_data_list.Flink;
209 while (le != &fve_data_list) {
210 fve_data* d = CONTAINING_RECORD(le, fve_data, list_entry);
211
212 if (d->devobj == devobj) {
213 ctx = ExAllocatePoolWithTag(NonPagedPool, offsetof(fve_callback_context, buf) + d->devpath.Length,
214 ALLOC_TAG);
215
216 if (!ctx) {
217 KeReleaseSpinLock(&fve_data_lock, irql);
218 ERR("out of memory\n");
219 IoFreeWorkItem(work_item);
220 return STATUS_SUCCESS;
221 }
222
223 RtlCopyMemory(ctx->buf, d->devpath.Buffer, d->devpath.Length);
224 ctx->devpath.Length = ctx->devpath.MaximumLength = d->devpath.Length;
225
226 KeReleaseSpinLock(&fve_data_lock, irql);
227
228 ctx->devpath.Buffer = ctx->buf;
229
230 ctx->fileobj = tdrn->FileObject;
231 ctx->devobj = devobj;
232 ctx->work_item = work_item;
233
234 IoQueueWorkItem(work_item, fve_callback, DelayedWorkQueue, ctx);
235
236 return STATUS_SUCCESS;
237 }
238
239 le = le->Flink;
240 }
241
242 KeReleaseSpinLock(&fve_data_lock, irql);
243
244 IoFreeWorkItem(work_item);
245
246 return STATUS_SUCCESS;
247 }
248
register_fve_callback(PDEVICE_OBJECT devobj,PFILE_OBJECT fileobj,PUNICODE_STRING devpath)249 static void register_fve_callback(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj,
250 PUNICODE_STRING devpath) {
251 NTSTATUS Status;
252 KIRQL irql;
253 LIST_ENTRY* le;
254
255 fve_data* d = ExAllocatePoolWithTag(NonPagedPool, offsetof(fve_data, buf) + devpath->Length, ALLOC_TAG);
256 if (!d) {
257 ERR("out of memory\n");
258 return;
259 }
260
261 d->devpath.Buffer = d->buf;
262 d->devpath.Length = d->devpath.MaximumLength = devpath->Length;
263 RtlCopyMemory(d->devpath.Buffer, devpath->Buffer, devpath->Length);
264
265 KeAcquireSpinLock(&fve_data_lock, &irql);
266
267 le = fve_data_list.Flink;
268 while (le != &fve_data_list) {
269 fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry);
270
271 if (d2->devobj == devobj) {
272 KeReleaseSpinLock(&fve_data_lock, irql);
273 ExFreePool(d);
274 return;
275 }
276
277 le = le->Flink;
278 }
279
280 KeReleaseSpinLock(&fve_data_lock, irql);
281
282 Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileobj, drvobj, event_notification,
283 devobj, &d->notification_entry);
284 if (!NT_SUCCESS(Status)) {
285 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
286 return;
287 }
288
289 KeAcquireSpinLock(&fve_data_lock, &irql);
290
291 le = fve_data_list.Flink;
292 while (le != &fve_data_list) {
293 fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry);
294
295 if (d2->devobj == devobj) {
296 KeReleaseSpinLock(&fve_data_lock, irql);
297 IoUnregisterPlugPlayNotification(d->notification_entry);
298 ExFreePool(d);
299 return;
300 }
301
302 le = le->Flink;
303 }
304
305 d->devobj = devobj;
306 InsertTailList(&fve_data_list, &d->list_entry);
307
308 KeReleaseSpinLock(&fve_data_lock, irql);
309 }
310
test_vol(PDEVICE_OBJECT DeviceObject,PFILE_OBJECT FileObject,PUNICODE_STRING devpath,DWORD disk_num,DWORD part_num,uint64_t length,bool fve_callback)311 static bool test_vol(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
312 PUNICODE_STRING devpath, DWORD disk_num, DWORD part_num, uint64_t length,
313 bool fve_callback) {
314 NTSTATUS Status;
315 ULONG toread;
316 uint8_t* data = NULL;
317 uint32_t sector_size;
318 bool ret = true;
319
320 TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer);
321
322 sector_size = DeviceObject->SectorSize;
323
324 if (sector_size == 0) {
325 DISK_GEOMETRY geometry;
326 IO_STATUS_BLOCK iosb;
327
328 Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
329 &geometry, sizeof(DISK_GEOMETRY), true, &iosb);
330
331 if (!NT_SUCCESS(Status)) {
332 ERR("%.*S had a sector size of 0, and IOCTL_DISK_GET_DRIVE_GEOMETRY returned %08lx\n",
333 (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer, Status);
334 goto deref;
335 }
336
337 if (iosb.Information < sizeof(DISK_GEOMETRY)) {
338 ERR("%.*S: IOCTL_DISK_GET_DRIVE_GEOMETRY returned %Iu bytes, expected %Iu\n",
339 (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer, iosb.Information, sizeof(DISK_GEOMETRY));
340 }
341
342 sector_size = geometry.BytesPerSector;
343
344 if (sector_size == 0) {
345 ERR("%.*S had a sector size of 0\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer);
346 goto deref;
347 }
348 }
349
350 toread = (ULONG)sector_align(sizeof(superblock), sector_size);
351 data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
352 if (!data) {
353 ERR("out of memory\n");
354 goto deref;
355 }
356
357 Status = sync_read_phys(DeviceObject, FileObject, superblock_addrs[0], toread, data, true);
358
359 if (NT_SUCCESS(Status) && ((superblock*)data)->magic == BTRFS_MAGIC) {
360 superblock* sb = (superblock*)data;
361
362 if (check_superblock_checksum(sb)) {
363 TRACE("volume found\n");
364
365 if (length >= superblock_addrs[1] + toread) {
366 ULONG i = 1;
367
368 superblock* sb2 = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
369 if (!sb2) {
370 ERR("out of memory\n");
371 goto deref;
372 }
373
374 while (superblock_addrs[i] > 0 && length >= superblock_addrs[i] + toread) {
375 Status = sync_read_phys(DeviceObject, FileObject, superblock_addrs[i], toread, (PUCHAR)sb2, true);
376
377 if (NT_SUCCESS(Status) && sb2->magic == BTRFS_MAGIC) {
378 if (check_superblock_checksum(sb2) && sb2->generation > sb->generation)
379 RtlCopyMemory(sb, sb2, toread);
380 }
381
382 i++;
383 }
384
385 ExFreePool(sb2);
386 }
387
388 if (!fs_ignored(&sb->uuid)) {
389 DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
390 add_volume_device(sb, devpath, length, disk_num, part_num);
391 }
392 }
393 } else if (Status == STATUS_FVE_LOCKED_VOLUME) {
394 if (fve_callback)
395 ret = false;
396 else
397 register_fve_callback(DeviceObject, FileObject, devpath);
398 }
399
400 deref:
401 if (data)
402 ExFreePool(data);
403
404 return ret;
405 }
406
remove_drive_letter(PDEVICE_OBJECT mountmgr,PUNICODE_STRING devpath)407 NTSTATUS remove_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) {
408 NTSTATUS Status;
409 MOUNTMGR_MOUNT_POINT* mmp;
410 ULONG mmpsize;
411 MOUNTMGR_MOUNT_POINTS mmps1, *mmps2;
412
413 TRACE("removing drive letter\n");
414
415 mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + devpath->Length;
416
417 mmp = ExAllocatePoolWithTag(PagedPool, mmpsize, ALLOC_TAG);
418 if (!mmp) {
419 ERR("out of memory\n");
420 return STATUS_INSUFFICIENT_RESOURCES;
421 }
422
423 RtlZeroMemory(mmp, mmpsize);
424
425 mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
426 mmp->DeviceNameLength = devpath->Length;
427 RtlCopyMemory(&mmp[1], devpath->Buffer, devpath->Length);
428
429 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_DELETE_POINTS, mmp, mmpsize, &mmps1, sizeof(MOUNTMGR_MOUNT_POINTS), false, NULL);
430
431 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
432 ERR("IOCTL_MOUNTMGR_DELETE_POINTS 1 returned %08lx\n", Status);
433 ExFreePool(mmp);
434 return Status;
435 }
436
437 if (Status != STATUS_BUFFER_OVERFLOW || mmps1.Size == 0) {
438 ExFreePool(mmp);
439 return STATUS_NOT_FOUND;
440 }
441
442 mmps2 = ExAllocatePoolWithTag(PagedPool, mmps1.Size, ALLOC_TAG);
443 if (!mmps2) {
444 ERR("out of memory\n");
445 ExFreePool(mmp);
446 return STATUS_INSUFFICIENT_RESOURCES;
447 }
448
449 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_DELETE_POINTS, mmp, mmpsize, mmps2, mmps1.Size, false, NULL);
450
451 if (!NT_SUCCESS(Status))
452 ERR("IOCTL_MOUNTMGR_DELETE_POINTS 2 returned %08lx\n", Status);
453
454 ExFreePool(mmps2);
455 ExFreePool(mmp);
456
457 return Status;
458 }
459
disk_arrival(PUNICODE_STRING devpath)460 void disk_arrival(PUNICODE_STRING devpath) {
461 PFILE_OBJECT fileobj;
462 PDEVICE_OBJECT devobj;
463 NTSTATUS Status;
464 STORAGE_DEVICE_NUMBER sdn;
465 ULONG dlisize;
466 DRIVE_LAYOUT_INFORMATION_EX* dli = NULL;
467 IO_STATUS_BLOCK iosb;
468 GET_LENGTH_INFORMATION gli;
469
470 ExAcquireResourceSharedLite(&boot_lock, TRUE);
471
472 Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &fileobj, &devobj);
473 if (!NT_SUCCESS(Status)) {
474 ExReleaseResourceLite(&boot_lock);
475 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
476 return;
477 }
478
479 dlisize = 0;
480
481 do {
482 dlisize += 1024;
483
484 if (dli)
485 ExFreePool(dli);
486
487 dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
488 if (!dli) {
489 ERR("out of memory\n");
490 goto end;
491 }
492
493 Status = dev_ioctl(devobj, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0,
494 dli, dlisize, true, &iosb);
495 } while (Status == STATUS_BUFFER_TOO_SMALL);
496
497 // only consider disk as a potential filesystem if it has no partitions
498 if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
499 ExFreePool(dli);
500 goto end;
501 }
502
503 ExFreePool(dli);
504
505 Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
506 &gli, sizeof(gli), true, NULL);
507
508 if (!NT_SUCCESS(Status)) {
509 ERR("error reading length information: %08lx\n", Status);
510 goto end;
511 }
512
513 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
514 &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
515 if (!NT_SUCCESS(Status)) {
516 TRACE("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status);
517 sdn.DeviceNumber = 0xffffffff;
518 sdn.PartitionNumber = 0;
519 } else
520 TRACE("DeviceType = %lu, DeviceNumber = %lu, PartitionNumber = %lu\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber);
521
522 test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber,
523 gli.Length.QuadPart, false);
524
525 end:
526 ObDereferenceObject(fileobj);
527
528 ExReleaseResourceLite(&boot_lock);
529 }
530
531 void remove_volume_child(_Inout_ _Requires_exclusive_lock_held_(_Curr_->child_lock) _Releases_exclusive_lock_(_Curr_->child_lock) _In_ volume_device_extension* vde,
532 _In_ volume_child* vc, _In_ bool skip_dev) {
533 NTSTATUS Status;
534 pdo_device_extension* pdode = vde->pdode;
535 device_extension* Vcb = vde->mounted_device ? vde->mounted_device->DeviceExtension : NULL;
536
537 if (vc->notification_entry) {
538 if (fIoUnregisterPlugPlayNotificationEx)
539 fIoUnregisterPlugPlayNotificationEx(vc->notification_entry);
540 else
541 IoUnregisterPlugPlayNotification(vc->notification_entry);
542 }
543
544 if (vde->mounted_device && (!Vcb || !Vcb->options.allow_degraded)) {
545 Status = pnp_surprise_removal(vde->mounted_device, NULL);
546 if (!NT_SUCCESS(Status))
547 ERR("pnp_surprise_removal returned %08lx\n", Status);
548 }
549
550 if (!Vcb || !Vcb->options.allow_degraded) {
551 Status = IoSetDeviceInterfaceState(&vde->bus_name, false);
552 if (!NT_SUCCESS(Status))
553 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
554 }
555
556 if (pdode->children_loaded > 0) {
557 UNICODE_STRING mmdevpath;
558 PFILE_OBJECT FileObject;
559 PDEVICE_OBJECT mountmgr;
560 LIST_ENTRY* le;
561
562 if (!Vcb || !Vcb->options.allow_degraded) {
563 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
564 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
565 if (!NT_SUCCESS(Status))
566 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
567 else {
568 le = pdode->children.Flink;
569
570 while (le != &pdode->children) {
571 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
572
573 if (vc2->had_drive_letter) { // re-add entry to mountmgr
574 MOUNTDEV_NAME mdn;
575
576 Status = dev_ioctl(vc2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL);
577 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
578 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
579 else {
580 MOUNTDEV_NAME* mdn2;
581 ULONG mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
582
583 mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
584 if (!mdn2)
585 ERR("out of memory\n");
586 else {
587 Status = dev_ioctl(vc2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL);
588 if (!NT_SUCCESS(Status))
589 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
590 else {
591 UNICODE_STRING name;
592
593 name.Buffer = mdn2->Name;
594 name.Length = name.MaximumLength = mdn2->NameLength;
595
596 Status = mountmgr_add_drive_letter(mountmgr, &name);
597 if (!NT_SUCCESS(Status))
598 WARN("mountmgr_add_drive_letter returned %08lx\n", Status);
599 }
600
601 ExFreePool(mdn2);
602 }
603 }
604 }
605
606 le = le->Flink;
607 }
608
609 ObDereferenceObject(FileObject);
610 }
611 } else if (!skip_dev) {
612 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
613
614 le = Vcb->devices.Flink;
615 while (le != &Vcb->devices) {
616 device* dev = CONTAINING_RECORD(le, device, list_entry);
617
618 if (dev->devobj == vc->devobj) {
619 dev->devobj = NULL; // mark as missing
620 break;
621 }
622
623 le = le->Flink;
624 }
625
626 ExReleaseResourceLite(&Vcb->tree_lock);
627 }
628
629 if (vde->device->Characteristics & FILE_REMOVABLE_MEDIA) {
630 vde->device->Characteristics &= ~FILE_REMOVABLE_MEDIA;
631
632 le = pdode->children.Flink;
633 while (le != &pdode->children) {
634 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
635
636 if (vc2 != vc && vc2->devobj->Characteristics & FILE_REMOVABLE_MEDIA) {
637 vde->device->Characteristics |= FILE_REMOVABLE_MEDIA;
638 break;
639 }
640
641 le = le->Flink;
642 }
643 }
644 }
645
646 ObDereferenceObject(vc->fileobj);
647 ExFreePool(vc->pnp_name.Buffer);
648 RemoveEntryList(&vc->list_entry);
649 ExFreePool(vc);
650
651 pdode->children_loaded--;
652
653 if (pdode->children_loaded == 0) { // remove volume device
654 bool remove = false;
655
656 RemoveEntryList(&pdode->list_entry);
657
658 vde->removing = true;
659
660 Status = IoSetDeviceInterfaceState(&vde->bus_name, false);
661 if (!NT_SUCCESS(Status))
662 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
663
664 if (vde->pdo->AttachedDevice)
665 IoDetachDevice(vde->pdo);
666
667 if (vde->open_count == 0)
668 remove = true;
669
670 ExReleaseResourceLite(&pdode->child_lock);
671
672 if (!no_pnp) {
673 bus_device_extension* bde = busobj->DeviceExtension;
674
675 IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
676 }
677
678 if (remove) {
679 if (vde->name.Buffer)
680 ExFreePool(vde->name.Buffer);
681
682 if (Vcb)
683 Vcb->vde = NULL;
684
685 ExDeleteResourceLite(&pdode->child_lock);
686
687 IoDeleteDevice(vde->device);
688 }
689 } else
690 ExReleaseResourceLite(&pdode->child_lock);
691 }
692
volume_arrival(PUNICODE_STRING devpath,bool fve_callback)693 bool volume_arrival(PUNICODE_STRING devpath, bool fve_callback) {
694 STORAGE_DEVICE_NUMBER sdn;
695 PFILE_OBJECT fileobj;
696 PDEVICE_OBJECT devobj;
697 GET_LENGTH_INFORMATION gli;
698 NTSTATUS Status;
699 bool ret = true;
700
701 TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer);
702
703 ExAcquireResourceSharedLite(&boot_lock, TRUE);
704
705 Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &fileobj, &devobj);
706 if (!NT_SUCCESS(Status)) {
707 ExReleaseResourceLite(&boot_lock);
708 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
709 return false;
710 }
711
712 // make sure we're not processing devices we've created ourselves
713
714 if (devobj->DriverObject == drvobj)
715 goto end;
716
717 Status = dev_ioctl(devobj, IOCTL_VOLUME_ONLINE, NULL, 0, NULL, 0, true, NULL);
718 if (!NT_SUCCESS(Status))
719 TRACE("IOCTL_VOLUME_ONLINE returned %08lx\n", Status);
720
721 Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL);
722 if (!NT_SUCCESS(Status)) {
723 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08lx\n", Status);
724 goto end;
725 }
726
727 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
728 &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
729 if (!NT_SUCCESS(Status)) {
730 TRACE("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status);
731 sdn.DeviceNumber = 0xffffffff;
732 sdn.PartitionNumber = 0;
733 } else
734 TRACE("DeviceType = %lu, DeviceNumber = %lu, PartitionNumber = %lu\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber);
735
736 // If we've just added a partition to a whole-disk filesystem, unmount it
737 if (sdn.DeviceNumber != 0xffffffff && sdn.PartitionNumber != 0) {
738 LIST_ENTRY* le;
739
740 ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
741
742 le = pdo_list.Flink;
743 while (le != &pdo_list) {
744 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
745 LIST_ENTRY* le2;
746 bool changed = false;
747
748 if (pdode->vde) {
749 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
750
751 le2 = pdode->children.Flink;
752 while (le2 != &pdode->children) {
753 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
754 LIST_ENTRY* le3 = le2->Flink;
755
756 if (vc->disk_num == sdn.DeviceNumber && vc->part_num == 0) {
757 TRACE("removing device\n");
758
759 remove_volume_child(pdode->vde, vc, false);
760 changed = true;
761
762 break;
763 }
764
765 le2 = le3;
766 }
767
768 if (!changed)
769 ExReleaseResourceLite(&pdode->child_lock);
770 else
771 break;
772 }
773
774 le = le->Flink;
775 }
776
777 ExReleaseResourceLite(&pdo_list_lock);
778 }
779
780 ret = test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber,
781 gli.Length.QuadPart, fve_callback);
782
783 end:
784 ObDereferenceObject(fileobj);
785
786 ExReleaseResourceLite(&boot_lock);
787
788 return ret;
789 }
790
volume_arrival2(PUNICODE_STRING devpath)791 static void volume_arrival2(PUNICODE_STRING devpath) {
792 volume_arrival(devpath, false);
793 }
794
volume_removal(PUNICODE_STRING devpath)795 void volume_removal(PUNICODE_STRING devpath) {
796 LIST_ENTRY* le;
797 UNICODE_STRING devpath2;
798
799 TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer);
800
801 devpath2 = *devpath;
802
803 if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
804 devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') {
805 devpath2.Buffer = &devpath2.Buffer[3];
806 devpath2.Length -= 3 * sizeof(WCHAR);
807 devpath2.MaximumLength -= 3 * sizeof(WCHAR);
808 }
809
810 ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
811
812 le = pdo_list.Flink;
813 while (le != &pdo_list) {
814 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
815 LIST_ENTRY* le2;
816 bool changed = false;
817
818 if (pdode->vde) {
819 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
820
821 le2 = pdode->children.Flink;
822 while (le2 != &pdode->children) {
823 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
824 LIST_ENTRY* le3 = le2->Flink;
825
826 if (vc->pnp_name.Length == devpath2.Length && RtlCompareMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length) == devpath2.Length) {
827 TRACE("removing device\n");
828
829 if (!vc->boot_volume) {
830 remove_volume_child(pdode->vde, vc, false);
831 changed = true;
832 }
833
834 break;
835 }
836
837 le2 = le3;
838 }
839
840 if (!changed)
841 ExReleaseResourceLite(&pdode->child_lock);
842 else
843 break;
844 }
845
846 le = le->Flink;
847 }
848
849 ExReleaseResourceLite(&pdo_list_lock);
850 }
851
852 typedef struct {
853 UNICODE_STRING name;
854 pnp_callback func;
855 PIO_WORKITEM work_item;
856 } pnp_callback_context;
857
_Function_class_(IO_WORKITEM_ROUTINE)858 _Function_class_(IO_WORKITEM_ROUTINE)
859 static void __stdcall do_pnp_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
860 pnp_callback_context* context = con;
861
862 UNUSED(DeviceObject);
863
864 context->func(&context->name);
865
866 if (context->name.Buffer)
867 ExFreePool(context->name.Buffer);
868
869 IoFreeWorkItem(context->work_item);
870
871 ExFreePool(context);
872 }
873
enqueue_pnp_callback(PUNICODE_STRING name,pnp_callback func)874 static void enqueue_pnp_callback(PUNICODE_STRING name, pnp_callback func) {
875 PIO_WORKITEM work_item;
876 pnp_callback_context* context;
877
878 work_item = IoAllocateWorkItem(master_devobj);
879 if (!work_item) {
880 ERR("out of memory\n");
881 return;
882 }
883
884 context = ExAllocatePoolWithTag(PagedPool, sizeof(pnp_callback_context), ALLOC_TAG);
885
886 if (!context) {
887 ERR("out of memory\n");
888 IoFreeWorkItem(work_item);
889 return;
890 }
891
892 if (name->Length > 0) {
893 context->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
894 if (!context->name.Buffer) {
895 ERR("out of memory\n");
896 ExFreePool(context);
897 IoFreeWorkItem(work_item);
898 return;
899 }
900
901 RtlCopyMemory(context->name.Buffer, name->Buffer, name->Length);
902 context->name.Length = context->name.MaximumLength = name->Length;
903 } else {
904 context->name.Length = context->name.MaximumLength = 0;
905 context->name.Buffer = NULL;
906 }
907
908 context->func = func;
909 context->work_item = work_item;
910
911 IoQueueWorkItem(work_item, do_pnp_callback, DelayedWorkQueue, context);
912 }
913
_Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)914 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
915 NTSTATUS __stdcall volume_notification(PVOID NotificationStructure, PVOID Context) {
916 DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
917
918 UNUSED(Context);
919
920 if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
921 enqueue_pnp_callback(dicn->SymbolicLinkName, volume_arrival2);
922 else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
923 enqueue_pnp_callback(dicn->SymbolicLinkName, volume_removal);
924
925 return STATUS_SUCCESS;
926 }
927
_Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)928 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
929 NTSTATUS __stdcall pnp_notification(PVOID NotificationStructure, PVOID Context) {
930 DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
931
932 UNUSED(Context);
933
934 if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
935 enqueue_pnp_callback(dicn->SymbolicLinkName, disk_arrival);
936 else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
937 enqueue_pnp_callback(dicn->SymbolicLinkName, volume_removal);
938
939 return STATUS_SUCCESS;
940 }
941
mountmgr_process_drive(PDEVICE_OBJECT mountmgr,PUNICODE_STRING device_name)942 static void mountmgr_process_drive(PDEVICE_OBJECT mountmgr, PUNICODE_STRING device_name) {
943 NTSTATUS Status;
944 LIST_ENTRY* le;
945 bool need_remove = false;
946 volume_child* vc2 = NULL;
947
948 ExAcquireResourceSharedLite(&pdo_list_lock, true);
949
950 le = pdo_list.Flink;
951 while (le != &pdo_list) {
952 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
953 LIST_ENTRY* le2;
954
955 ExAcquireResourceSharedLite(&pdode->child_lock, true);
956
957 le2 = pdode->children.Flink;
958
959 while (le2 != &pdode->children) {
960 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
961
962 if (vc->devobj) {
963 MOUNTDEV_NAME mdn;
964
965 Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL);
966 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
967 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
968 else {
969 MOUNTDEV_NAME* mdn2;
970 ULONG mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
971
972 mdn2 = ExAllocatePoolWithTag(NonPagedPool, mdnsize, ALLOC_TAG);
973 if (!mdn2)
974 ERR("out of memory\n");
975 else {
976 Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL);
977 if (!NT_SUCCESS(Status))
978 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
979 else {
980 if (mdn2->NameLength == device_name->Length && RtlCompareMemory(mdn2->Name, device_name->Buffer, device_name->Length) == device_name->Length) {
981 vc2 = vc;
982 need_remove = true;
983 break;
984 }
985 }
986
987 ExFreePool(mdn2);
988 }
989 }
990 }
991
992 le2 = le2->Flink;
993 }
994
995 ExReleaseResourceLite(&pdode->child_lock);
996
997 if (need_remove)
998 break;
999
1000 le = le->Flink;
1001 }
1002
1003 ExReleaseResourceLite(&pdo_list_lock);
1004
1005 if (need_remove) {
1006 Status = remove_drive_letter(mountmgr, device_name);
1007 if (!NT_SUCCESS(Status))
1008 ERR("remove_drive_letter returned %08lx\n", Status);
1009 else
1010 vc2->had_drive_letter = true;
1011 }
1012 }
1013
mountmgr_updated(PDEVICE_OBJECT mountmgr,MOUNTMGR_MOUNT_POINTS * mmps)1014 static void mountmgr_updated(PDEVICE_OBJECT mountmgr, MOUNTMGR_MOUNT_POINTS* mmps) {
1015 ULONG i;
1016
1017 static const WCHAR pref[] = L"\\DosDevices\\";
1018
1019 for (i = 0; i < mmps->NumberOfMountPoints; i++) {
1020 UNICODE_STRING symlink, device_name;
1021
1022 if (mmps->MountPoints[i].SymbolicLinkNameOffset != 0) {
1023 symlink.Buffer = (WCHAR*)(((uint8_t*)mmps) + mmps->MountPoints[i].SymbolicLinkNameOffset);
1024 symlink.Length = symlink.MaximumLength = mmps->MountPoints[i].SymbolicLinkNameLength;
1025 } else {
1026 symlink.Buffer = NULL;
1027 symlink.Length = symlink.MaximumLength = 0;
1028 }
1029
1030 if (mmps->MountPoints[i].DeviceNameOffset != 0) {
1031 device_name.Buffer = (WCHAR*)(((uint8_t*)mmps) + mmps->MountPoints[i].DeviceNameOffset);
1032 device_name.Length = device_name.MaximumLength = mmps->MountPoints[i].DeviceNameLength;
1033 } else {
1034 device_name.Buffer = NULL;
1035 device_name.Length = device_name.MaximumLength = 0;
1036 }
1037
1038 if (symlink.Length > sizeof(pref) - sizeof(WCHAR) &&
1039 RtlCompareMemory(symlink.Buffer, pref, sizeof(pref) - sizeof(WCHAR)) == sizeof(pref) - sizeof(WCHAR))
1040 mountmgr_process_drive(mountmgr, &device_name);
1041 }
1042 }
1043
_Function_class_(KSTART_ROUTINE)1044 _Function_class_(KSTART_ROUTINE)
1045 void __stdcall mountmgr_thread(_In_ void* context) {
1046 UNICODE_STRING mmdevpath;
1047 NTSTATUS Status;
1048 PFILE_OBJECT FileObject;
1049 PDEVICE_OBJECT mountmgr;
1050 MOUNTMGR_CHANGE_NOTIFY_INFO mcni;
1051
1052 UNUSED(context);
1053
1054 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
1055 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
1056 if (!NT_SUCCESS(Status)) {
1057 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
1058 return;
1059 }
1060
1061 mcni.EpicNumber = 0;
1062
1063 while (true) {
1064 PIRP Irp;
1065 MOUNTMGR_MOUNT_POINT mmp;
1066 MOUNTMGR_MOUNT_POINTS mmps;
1067 IO_STATUS_BLOCK iosb;
1068
1069 KeClearEvent(&mountmgr_thread_event);
1070
1071 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_CHANGE_NOTIFY, mountmgr, &mcni, sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO),
1072 &mcni, sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO), false, &mountmgr_thread_event, &iosb);
1073
1074 if (!Irp) {
1075 ERR("out of memory\n");
1076 break;
1077 }
1078
1079 Status = IoCallDriver(mountmgr, Irp);
1080
1081 if (Status == STATUS_PENDING) {
1082 KeWaitForSingleObject(&mountmgr_thread_event, Executive, KernelMode, false, NULL);
1083 Status = iosb.Status;
1084 }
1085
1086 if (shutting_down)
1087 break;
1088
1089 if (!NT_SUCCESS(Status)) {
1090 ERR("IOCTL_MOUNTMGR_CHANGE_NOTIFY returned %08lx\n", Status);
1091 break;
1092 }
1093
1094 TRACE("mountmgr changed\n");
1095
1096 RtlZeroMemory(&mmp, sizeof(MOUNTMGR_MOUNT_POINT));
1097
1098 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(MOUNTMGR_MOUNT_POINT), &mmps, sizeof(MOUNTMGR_MOUNT_POINTS),
1099 false, NULL);
1100
1101 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
1102 ERR("IOCTL_MOUNTMGR_QUERY_POINTS 1 returned %08lx\n", Status);
1103 else if (mmps.Size > 0) {
1104 MOUNTMGR_MOUNT_POINTS* mmps2;
1105
1106 mmps2 = ExAllocatePoolWithTag(NonPagedPool, mmps.Size, ALLOC_TAG);
1107 if (!mmps2) {
1108 ERR("out of memory\n");
1109 break;
1110 }
1111
1112 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(MOUNTMGR_MOUNT_POINT), mmps2, mmps.Size,
1113 false, NULL);
1114 if (!NT_SUCCESS(Status))
1115 ERR("IOCTL_MOUNTMGR_QUERY_POINTS returned %08lx\n", Status);
1116 else
1117 mountmgr_updated(mountmgr, mmps2);
1118
1119 ExFreePool(mmps2);
1120 }
1121 }
1122
1123 ObDereferenceObject(FileObject);
1124
1125 mountmgr_thread_handle = NULL;
1126
1127 PsTerminateSystemThread(STATUS_SUCCESS);
1128 }
1129