xref: /reactos/drivers/filesystems/btrfs/search.c (revision 84ccccab)
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             PDEVICE_OBJECT pdo;
487 
488             if (vde->name.Buffer)
489                 ExFreePool(vde->name.Buffer);
490 
491             if (Vcb)
492                 Vcb->vde = NULL;
493 
494             ExDeleteResourceLite(&pdode->child_lock);
495 
496             pdo = vde->pdo;
497             IoDeleteDevice(vde->device);
498 
499             if (no_pnp)
500                 IoDeleteDevice(pdo);
501         }
502     } else
503         ExReleaseResourceLite(&pdode->child_lock);
504 }
505 
506 void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
507     STORAGE_DEVICE_NUMBER sdn;
508     PFILE_OBJECT FileObject, mountmgrfo;
509     UNICODE_STRING mmdevpath;
510     PDEVICE_OBJECT devobj, mountmgr;
511     GET_LENGTH_INFORMATION gli;
512     NTSTATUS Status;
513 
514     TRACE("%.*S\n", devpath->Length / sizeof(WCHAR), devpath->Buffer);
515 
516     Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &devobj);
517     if (!NT_SUCCESS(Status)) {
518         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
519         return;
520     }
521 
522     // make sure we're not processing devices we've created ourselves
523 
524     if (devobj->DriverObject == DriverObject)
525         goto end;
526 
527     Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), TRUE, NULL);
528     if (!NT_SUCCESS(Status)) {
529         ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status);
530         goto end;
531     }
532 
533     Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
534                        &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
535     if (!NT_SUCCESS(Status)) {
536         TRACE("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
537         sdn.DeviceNumber = 0xffffffff;
538         sdn.PartitionNumber = 0;
539     } else
540         TRACE("DeviceType = %u, DeviceNumber = %u, PartitionNumber = %u\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber);
541 
542     // If we've just added a partition to a whole-disk filesystem, unmount it
543     if (sdn.DeviceNumber != 0xffffffff) {
544         LIST_ENTRY* le;
545 
546         ExAcquireResourceExclusiveLite(&pdo_list_lock, TRUE);
547 
548         le = pdo_list.Flink;
549         while (le != &pdo_list) {
550             pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
551             LIST_ENTRY* le2;
552             BOOL changed = FALSE;
553 
554             if (pdode->vde) {
555                 ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
556 
557                 le2 = pdode->children.Flink;
558                 while (le2 != &pdode->children) {
559                     volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
560                     LIST_ENTRY* le3 = le2->Flink;
561 
562                     if (vc->disk_num == sdn.DeviceNumber && vc->part_num == 0) {
563                         TRACE("removing device\n");
564 
565                         remove_volume_child(pdode->vde, vc, FALSE);
566                         changed = TRUE;
567 
568                         break;
569                     }
570 
571                     le2 = le3;
572                 }
573 
574                 if (!changed)
575                     ExReleaseResourceLite(&pdode->child_lock);
576                 else
577                     break;
578             }
579 
580             le = le->Flink;
581         }
582 
583         ExReleaseResourceLite(&pdo_list_lock);
584     }
585 
586     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
587     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
588     if (!NT_SUCCESS(Status)) {
589         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
590         goto end;
591     }
592 
593     test_vol(mountmgr, devobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber, gli.Length.QuadPart);
594 
595     ObDereferenceObject(mountmgrfo);
596 
597 end:
598     ObDereferenceObject(FileObject);
599 }
600 
601 void volume_removal(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
602     LIST_ENTRY* le;
603     UNICODE_STRING devpath2;
604 
605     TRACE("%.*S\n", devpath->Length / sizeof(WCHAR), devpath->Buffer);
606 
607     UNUSED(DriverObject);
608 
609     devpath2 = *devpath;
610 
611     if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
612         devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') {
613         devpath2.Buffer = &devpath2.Buffer[3];
614         devpath2.Length -= 3 * sizeof(WCHAR);
615         devpath2.MaximumLength -= 3 * sizeof(WCHAR);
616     }
617 
618     ExAcquireResourceExclusiveLite(&pdo_list_lock, TRUE);
619 
620     le = pdo_list.Flink;
621     while (le != &pdo_list) {
622         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
623         LIST_ENTRY* le2;
624         BOOL changed = FALSE;
625 
626         if (pdode->vde) {
627             ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
628 
629             le2 = pdode->children.Flink;
630             while (le2 != &pdode->children) {
631                 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
632                 LIST_ENTRY* le3 = le2->Flink;
633 
634                 if (vc->pnp_name.Length == devpath2.Length && RtlCompareMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length) == devpath2.Length) {
635                     TRACE("removing device\n");
636 
637                     remove_volume_child(pdode->vde, vc, FALSE);
638                     changed = TRUE;
639 
640                     break;
641                 }
642 
643                 le2 = le3;
644             }
645 
646             if (!changed)
647                 ExReleaseResourceLite(&pdode->child_lock);
648             else
649                 break;
650         }
651 
652         le = le->Flink;
653     }
654 
655     ExReleaseResourceLite(&pdo_list_lock);
656 }
657 
658 typedef struct {
659     PDRIVER_OBJECT DriverObject;
660     UNICODE_STRING name;
661     pnp_callback func;
662     PIO_WORKITEM work_item;
663 } pnp_callback_context;
664 
665 _Function_class_(IO_WORKITEM_ROUTINE)
666 #ifdef __REACTOS__
667 static void NTAPI do_pnp_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
668 #else
669 static void do_pnp_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
670 #endif
671     pnp_callback_context* context = con;
672 
673     UNUSED(DeviceObject);
674 
675     context->func(context->DriverObject, &context->name);
676 
677     if (context->name.Buffer)
678         ExFreePool(context->name.Buffer);
679 
680     IoFreeWorkItem(context->work_item);
681 }
682 
683 static void enqueue_pnp_callback(PDRIVER_OBJECT DriverObject, PUNICODE_STRING name, pnp_callback func) {
684     PIO_WORKITEM work_item;
685     pnp_callback_context* context;
686 
687     work_item = IoAllocateWorkItem(master_devobj);
688 
689     context = ExAllocatePoolWithTag(PagedPool, sizeof(pnp_callback_context), ALLOC_TAG);
690 
691     if (!context) {
692         ERR("out of memory\n");
693         IoFreeWorkItem(work_item);
694         return;
695     }
696 
697     context->DriverObject = DriverObject;
698 
699     if (name->Length > 0) {
700         context->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
701         if (!context->name.Buffer) {
702             ERR("out of memory\n");
703             ExFreePool(context);
704             IoFreeWorkItem(work_item);
705             return;
706         }
707 
708         RtlCopyMemory(context->name.Buffer, name->Buffer, name->Length);
709         context->name.Length = context->name.MaximumLength = name->Length;
710     } else {
711         context->name.Length = context->name.MaximumLength = 0;
712         context->name.Buffer = NULL;
713     }
714 
715     context->func = func;
716     context->work_item = work_item;
717 
718     IoQueueWorkItem(work_item, do_pnp_callback, DelayedWorkQueue, context);
719 }
720 
721 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
722 #ifdef __REACTOS__
723 NTSTATUS NTAPI volume_notification(PVOID NotificationStructure, PVOID Context) {
724 #else
725 NTSTATUS volume_notification(PVOID NotificationStructure, PVOID Context) {
726 #endif
727     DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
728     PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context;
729 
730     if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
731         enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_arrival);
732     else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
733         enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_removal);
734 
735     return STATUS_SUCCESS;
736 }
737 
738 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
739 #ifdef __REACTOS__
740 NTSTATUS NTAPI pnp_notification(PVOID NotificationStructure, PVOID Context) {
741 #else
742 NTSTATUS pnp_notification(PVOID NotificationStructure, PVOID Context) {
743 #endif
744     DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
745     PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context;
746 
747     if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
748         enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, disk_arrival);
749     else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
750         enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_removal);
751 
752     return STATUS_SUCCESS;
753 }
754 
755 static void mountmgr_process_drive(PDEVICE_OBJECT mountmgr, PUNICODE_STRING device_name) {
756     NTSTATUS Status;
757     LIST_ENTRY* le;
758     BOOL done = FALSE;
759 
760     ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
761 
762     le = pdo_list.Flink;
763     while (le != &pdo_list) {
764         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
765         LIST_ENTRY* le2;
766 
767         ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
768 
769         le2 = pdode->children.Flink;
770 
771         while (le2 != &pdode->children) {
772             volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
773 
774             if (vc->devobj) {
775                 MOUNTDEV_NAME mdn;
776 
777                 Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
778                 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
779                     ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
780                 else {
781                     MOUNTDEV_NAME* mdn2;
782                     ULONG mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
783 
784                     mdn2 = ExAllocatePoolWithTag(NonPagedPool, mdnsize, ALLOC_TAG);
785                     if (!mdn2)
786                         ERR("out of memory\n");
787                     else {
788                         Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
789                         if (!NT_SUCCESS(Status))
790                             ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
791                         else {
792                             if (mdn2->NameLength == device_name->Length && RtlCompareMemory(mdn2->Name, device_name->Buffer, device_name->Length) == device_name->Length) {
793                                 Status = remove_drive_letter(mountmgr, device_name);
794                                 if (!NT_SUCCESS(Status))
795                                     ERR("remove_drive_letter returned %08x\n", Status);
796                                 else
797                                     vc->had_drive_letter = TRUE;
798 
799                                 done = TRUE;
800                                 break;
801                             }
802                         }
803 
804                         ExFreePool(mdn2);
805                     }
806                 }
807             }
808 
809             le2 = le2->Flink;
810         }
811 
812         ExReleaseResourceLite(&pdode->child_lock);
813 
814         if (done)
815             break;
816 
817         le = le->Flink;
818     }
819 
820     ExReleaseResourceLite(&pdo_list_lock);
821 }
822 
823 static void mountmgr_updated(PDEVICE_OBJECT mountmgr, MOUNTMGR_MOUNT_POINTS* mmps) {
824     ULONG i;
825 
826     static WCHAR pref[] = L"\\DosDevices\\";
827 
828     for (i = 0; i < mmps->NumberOfMountPoints; i++) {
829         UNICODE_STRING symlink, device_name;
830 
831         if (mmps->MountPoints[i].SymbolicLinkNameOffset != 0) {
832             symlink.Buffer = (WCHAR*)(((UINT8*)mmps) + mmps->MountPoints[i].SymbolicLinkNameOffset);
833             symlink.Length = symlink.MaximumLength = mmps->MountPoints[i].SymbolicLinkNameLength;
834         } else {
835             symlink.Buffer = NULL;
836             symlink.Length = symlink.MaximumLength = 0;
837         }
838 
839         if (mmps->MountPoints[i].DeviceNameOffset != 0) {
840             device_name.Buffer = (WCHAR*)(((UINT8*)mmps) + mmps->MountPoints[i].DeviceNameOffset);
841             device_name.Length = device_name.MaximumLength = mmps->MountPoints[i].DeviceNameLength;
842         } else {
843             device_name.Buffer = NULL;
844             device_name.Length = device_name.MaximumLength = 0;
845         }
846 
847         if (symlink.Length > wcslen(pref) * sizeof(WCHAR) &&
848             RtlCompareMemory(symlink.Buffer, pref, wcslen(pref) * sizeof(WCHAR)) == wcslen(pref) * sizeof(WCHAR))
849             mountmgr_process_drive(mountmgr, &device_name);
850     }
851 }
852 
853 _Function_class_(KSTART_ROUTINE)
854 #ifdef __REACTOS__
855 void NTAPI mountmgr_thread(_In_ void* context) {
856 #else
857 void mountmgr_thread(_In_ void* context) {
858 #endif
859     UNICODE_STRING mmdevpath;
860     NTSTATUS Status;
861     PFILE_OBJECT FileObject;
862     PDEVICE_OBJECT mountmgr;
863     MOUNTMGR_CHANGE_NOTIFY_INFO mcni;
864 
865     UNUSED(context);
866 
867     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
868     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
869     if (!NT_SUCCESS(Status)) {
870         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
871         return;
872     }
873 
874     mcni.EpicNumber = 0;
875 
876     while (TRUE) {
877         PIRP Irp;
878         MOUNTMGR_MOUNT_POINT mmp;
879         MOUNTMGR_MOUNT_POINTS mmps;
880         IO_STATUS_BLOCK iosb;
881 
882         KeClearEvent(&mountmgr_thread_event);
883 
884         Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_CHANGE_NOTIFY, mountmgr, &mcni, sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO),
885                                             &mcni, sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO), FALSE, &mountmgr_thread_event, &iosb);
886 
887         if (!Irp) {
888             ERR("out of memory\n");
889             break;
890         }
891 
892         Status = IoCallDriver(mountmgr, Irp);
893 
894         if (Status == STATUS_PENDING) {
895             KeWaitForSingleObject(&mountmgr_thread_event, Executive, KernelMode, FALSE, NULL);
896             Status = iosb.Status;
897         }
898 
899         if (shutting_down)
900             break;
901 
902         if (!NT_SUCCESS(Status)) {
903             ERR("IOCTL_MOUNTMGR_CHANGE_NOTIFY returned %08x\n", Status);
904             break;
905         }
906 
907         TRACE("mountmgr changed\n");
908 
909         RtlZeroMemory(&mmp, sizeof(MOUNTMGR_MOUNT_POINT));
910 
911         Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(MOUNTMGR_MOUNT_POINT), &mmps, sizeof(MOUNTMGR_MOUNT_POINTS),
912                            FALSE, NULL);
913 
914         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
915             ERR("IOCTL_MOUNTMGR_QUERY_POINTS 1 returned %08x\n", Status);
916 
917         if (mmps.Size > 0) {
918             MOUNTMGR_MOUNT_POINTS* mmps2;
919 
920             mmps2 = ExAllocatePoolWithTag(NonPagedPool, mmps.Size, ALLOC_TAG);
921             if (!mmps2) {
922                 ERR("out of memory\n");
923                 break;
924             }
925 
926             Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(MOUNTMGR_MOUNT_POINTS), mmps2, mmps.Size,
927                                FALSE, NULL);
928             if (!NT_SUCCESS(Status))
929                 ERR("IOCTL_MOUNTMGR_QUERY_POINTS returned %08x\n", Status);
930             else
931                 mountmgr_updated(mountmgr, mmps2);
932 
933             ExFreePool(mmps2);
934         }
935     }
936 
937     ObDereferenceObject(FileObject);
938 
939     mountmgr_thread_handle = NULL;
940 
941     PsTerminateSystemThread(STATUS_SUCCESS);
942 }
943