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