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