xref: /reactos/drivers/filesystems/btrfs/volume.c (revision 53221834)
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 #include <mountdev.h>
20 #include <ntddvol.h>
21 #include <ntddstor.h>
22 #include <ntdddisk.h>
23 #include <wdmguid.h>
24 
25 #define IOCTL_VOLUME_IS_DYNAMIC     CTL_CODE(IOCTL_VOLUME_BASE, 18, METHOD_BUFFERED, FILE_ANY_ACCESS)
26 #define IOCTL_VOLUME_POST_ONLINE    CTL_CODE(IOCTL_VOLUME_BASE, 25, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
27 
28 extern PDRIVER_OBJECT drvobj;
29 extern PDEVICE_OBJECT master_devobj;
30 extern PDEVICE_OBJECT busobj;
31 extern ERESOURCE pdo_list_lock;
32 extern LIST_ENTRY pdo_list;
33 extern UNICODE_STRING registry_path;
34 extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
35 
36 NTSTATUS vol_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
37     volume_device_extension* vde = DeviceObject->DeviceExtension;
38 
39     TRACE("(%p, %p)\n", DeviceObject, Irp);
40 
41     if (vde->removing)
42         return STATUS_DEVICE_NOT_READY;
43 
44     Irp->IoStatus.Information = FILE_OPENED;
45     InterlockedIncrement(&vde->open_count);
46 
47     return STATUS_SUCCESS;
48 }
49 
50 void free_vol(volume_device_extension* vde) {
51     PDEVICE_OBJECT pdo;
52 
53     vde->dead = true;
54 
55     if (vde->mounted_device) {
56         device_extension* Vcb = vde->mounted_device->DeviceExtension;
57 
58         Vcb->vde = NULL;
59     }
60 
61     if (vde->name.Buffer)
62         ExFreePool(vde->name.Buffer);
63 
64     ExDeleteResourceLite(&vde->pdode->child_lock);
65 
66     if (vde->pdo->AttachedDevice)
67         IoDetachDevice(vde->pdo);
68 
69     while (!IsListEmpty(&vde->pdode->children)) {
70         volume_child* vc = CONTAINING_RECORD(RemoveHeadList(&vde->pdode->children), volume_child, list_entry);
71 
72         if (vc->notification_entry) {
73             if (fIoUnregisterPlugPlayNotificationEx)
74                 fIoUnregisterPlugPlayNotificationEx(vc->notification_entry);
75             else
76                 IoUnregisterPlugPlayNotification(vc->notification_entry);
77         }
78 
79         if (vc->pnp_name.Buffer)
80             ExFreePool(vc->pnp_name.Buffer);
81 
82         ExFreePool(vc);
83     }
84 
85     if (no_pnp)
86         ExFreePool(vde->pdode);
87 
88     pdo = vde->pdo;
89     IoDeleteDevice(vde->device);
90 
91     if (!no_pnp)
92         IoDeleteDevice(pdo);
93 }
94 
95 NTSTATUS vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
96     volume_device_extension* vde = DeviceObject->DeviceExtension;
97     pdo_device_extension* pdode = vde->pdode;
98 
99     TRACE("(%p, %p)\n", DeviceObject, Irp);
100 
101     Irp->IoStatus.Information = 0;
102 
103     if (vde->dead)
104         return STATUS_SUCCESS;
105 
106     ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
107 
108     if (vde->dead) {
109         ExReleaseResourceLite(&pdo_list_lock);
110         return STATUS_SUCCESS;
111     }
112 
113     ExAcquireResourceSharedLite(&pdode->child_lock, true);
114 
115     if (InterlockedDecrement(&vde->open_count) == 0 && vde->removing) {
116         ExReleaseResourceLite(&pdode->child_lock);
117 
118         free_vol(vde);
119     } else
120         ExReleaseResourceLite(&pdode->child_lock);
121 
122     ExReleaseResourceLite(&pdo_list_lock);
123 
124     return STATUS_SUCCESS;
125 }
126 
127 typedef struct {
128     IO_STATUS_BLOCK iosb;
129     KEVENT Event;
130 } vol_read_context;
131 
132 _Function_class_(IO_COMPLETION_ROUTINE)
133 static NTSTATUS __stdcall vol_read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
134     vol_read_context* context = conptr;
135 
136     UNUSED(DeviceObject);
137 
138     context->iosb = Irp->IoStatus;
139     KeSetEvent(&context->Event, 0, false);
140 
141     return STATUS_MORE_PROCESSING_REQUIRED;
142 }
143 
144 NTSTATUS vol_read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
145     volume_device_extension* vde = DeviceObject->DeviceExtension;
146     pdo_device_extension* pdode = vde->pdode;
147     volume_child* vc;
148     NTSTATUS Status;
149     PIRP Irp2;
150     vol_read_context context;
151     PIO_STACK_LOCATION IrpSp, IrpSp2;
152 
153     TRACE("(%p, %p)\n", DeviceObject, Irp);
154 
155     ExAcquireResourceSharedLite(&pdode->child_lock, true);
156 
157     if (IsListEmpty(&pdode->children)) {
158         ExReleaseResourceLite(&pdode->child_lock);
159         Status = STATUS_INVALID_DEVICE_REQUEST;
160         goto end;
161     }
162 
163     vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
164 
165     // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack
166 
167     Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
168 
169     if (!Irp2) {
170         ERR("IoAllocateIrp failed\n");
171         ExReleaseResourceLite(&pdode->child_lock);
172         Status = STATUS_INSUFFICIENT_RESOURCES;
173         goto end;
174     }
175 
176     IrpSp = IoGetCurrentIrpStackLocation(Irp);
177     IrpSp2 = IoGetNextIrpStackLocation(Irp2);
178 
179     IrpSp2->MajorFunction = IRP_MJ_READ;
180     IrpSp2->FileObject = vc->fileobj;
181 
182     if (vc->devobj->Flags & DO_BUFFERED_IO) {
183         Irp2->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.Read.Length, ALLOC_TAG);
184         if (!Irp2->AssociatedIrp.SystemBuffer) {
185             ERR("out of memory\n");
186             ExReleaseResourceLite(&pdode->child_lock);
187             Status = STATUS_INSUFFICIENT_RESOURCES;
188             goto end;
189         }
190 
191         Irp2->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
192 
193         Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
194     } else if (vc->devobj->Flags & DO_DIRECT_IO)
195         Irp2->MdlAddress = Irp->MdlAddress;
196     else
197         Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
198 
199     IrpSp2->Parameters.Read.Length = IrpSp->Parameters.Read.Length;
200     IrpSp2->Parameters.Read.ByteOffset.QuadPart = IrpSp->Parameters.Read.ByteOffset.QuadPart;
201 
202     KeInitializeEvent(&context.Event, NotificationEvent, false);
203     Irp2->UserIosb = &context.iosb;
204 
205     IoSetCompletionRoutine(Irp2, vol_read_completion, &context, true, true, true);
206 
207     Status = IoCallDriver(vc->devobj, Irp2);
208 
209     if (Status == STATUS_PENDING) {
210         KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
211         Status = context.iosb.Status;
212     }
213 
214     ExReleaseResourceLite(&pdode->child_lock);
215 
216     Irp->IoStatus.Information = context.iosb.Information;
217 
218 end:
219     Irp->IoStatus.Status = Status;
220     IoCompleteRequest(Irp, IO_NO_INCREMENT);
221 
222     return Status;
223 }
224 
225 NTSTATUS vol_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
226     volume_device_extension* vde = DeviceObject->DeviceExtension;
227     pdo_device_extension* pdode = vde->pdode;
228     volume_child* vc;
229     NTSTATUS Status;
230     PIRP Irp2;
231     vol_read_context context;
232     PIO_STACK_LOCATION IrpSp, IrpSp2;
233 
234     TRACE("(%p, %p)\n", DeviceObject, Irp);
235 
236     ExAcquireResourceSharedLite(&pdode->child_lock, true);
237 
238     if (IsListEmpty(&pdode->children)) {
239         ExReleaseResourceLite(&pdode->child_lock);
240         Status = STATUS_INVALID_DEVICE_REQUEST;
241         goto end;
242     }
243 
244     vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
245 
246     if (vc->list_entry.Flink != &pdode->children) { // more than once device
247         ExReleaseResourceLite(&pdode->child_lock);
248         Status = STATUS_ACCESS_DENIED;
249         goto end;
250     }
251 
252     // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack
253 
254     Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
255 
256     if (!Irp2) {
257         ERR("IoAllocateIrp failed\n");
258         ExReleaseResourceLite(&pdode->child_lock);
259         Status = STATUS_INSUFFICIENT_RESOURCES;
260         goto end;
261     }
262 
263     IrpSp = IoGetCurrentIrpStackLocation(Irp);
264     IrpSp2 = IoGetNextIrpStackLocation(Irp2);
265 
266     IrpSp2->MajorFunction = IRP_MJ_WRITE;
267     IrpSp2->FileObject = vc->fileobj;
268 
269     if (vc->devobj->Flags & DO_BUFFERED_IO) {
270         Irp2->AssociatedIrp.SystemBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
271 
272         Irp2->Flags |= IRP_BUFFERED_IO;
273 
274         Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
275     } else if (vc->devobj->Flags & DO_DIRECT_IO)
276         Irp2->MdlAddress = Irp->MdlAddress;
277     else
278         Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
279 
280     IrpSp2->Parameters.Write.Length = IrpSp->Parameters.Write.Length;
281     IrpSp2->Parameters.Write.ByteOffset.QuadPart = IrpSp->Parameters.Write.ByteOffset.QuadPart;
282 
283     KeInitializeEvent(&context.Event, NotificationEvent, false);
284     Irp2->UserIosb = &context.iosb;
285 
286     IoSetCompletionRoutine(Irp2, vol_read_completion, &context, true, true, true);
287 
288     Status = IoCallDriver(vc->devobj, Irp2);
289 
290     if (Status == STATUS_PENDING) {
291         KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
292         Status = context.iosb.Status;
293     }
294 
295     ExReleaseResourceLite(&pdode->child_lock);
296 
297     Irp->IoStatus.Information = context.iosb.Information;
298 
299 end:
300     Irp->IoStatus.Status = Status;
301     IoCompleteRequest(Irp, IO_NO_INCREMENT);
302 
303     return Status;
304 }
305 
306 NTSTATUS vol_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
307     TRACE("(%p, %p)\n", DeviceObject, Irp);
308 
309     return STATUS_INVALID_DEVICE_REQUEST;
310 }
311 
312 NTSTATUS vol_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
313     TRACE("(%p, %p)\n", DeviceObject, Irp);
314 
315     return STATUS_INVALID_DEVICE_REQUEST;
316 }
317 
318 NTSTATUS vol_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
319     TRACE("(%p, %p)\n", DeviceObject, Irp);
320 
321     return STATUS_INVALID_DEVICE_REQUEST;
322 }
323 
324 NTSTATUS vol_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
325     TRACE("(%p, %p)\n", DeviceObject, Irp);
326 
327     return STATUS_INVALID_DEVICE_REQUEST;
328 }
329 
330 NTSTATUS vol_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
331     TRACE("(%p, %p)\n", DeviceObject, Irp);
332 
333     return STATUS_SUCCESS;
334 }
335 
336 NTSTATUS vol_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
337     TRACE("(%p, %p)\n", DeviceObject, Irp);
338 
339     return STATUS_INVALID_DEVICE_REQUEST;
340 }
341 
342 NTSTATUS vol_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
343     TRACE("(%p, %p)\n", DeviceObject, Irp);
344 
345     return STATUS_INVALID_DEVICE_REQUEST;
346 }
347 
348 NTSTATUS vol_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
349     TRACE("(%p, %p)\n", DeviceObject, Irp);
350 
351     Irp->IoStatus.Information = 0;
352 
353     return STATUS_SUCCESS;
354 }
355 
356 NTSTATUS vol_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
357     TRACE("(%p, %p)\n", DeviceObject, Irp);
358 
359     return STATUS_INVALID_DEVICE_REQUEST;
360 }
361 
362 NTSTATUS vol_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
363     TRACE("(%p, %p)\n", DeviceObject, Irp);
364 
365     return STATUS_INVALID_DEVICE_REQUEST;
366 }
367 
368 NTSTATUS vol_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
369     TRACE("(%p, %p)\n", DeviceObject, Irp);
370 
371     return STATUS_INVALID_DEVICE_REQUEST;
372 }
373 
374 static NTSTATUS vol_query_device_name(volume_device_extension* vde, PIRP Irp) {
375     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
376     PMOUNTDEV_NAME name;
377 
378     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
379         Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
380         return STATUS_BUFFER_TOO_SMALL;
381     }
382 
383     name = Irp->AssociatedIrp.SystemBuffer;
384     name->NameLength = vde->name.Length;
385 
386     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) {
387         Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
388         return STATUS_BUFFER_OVERFLOW;
389     }
390 
391     RtlCopyMemory(name->Name, vde->name.Buffer, vde->name.Length);
392 
393     Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength;
394 
395     return STATUS_SUCCESS;
396 }
397 
398 static NTSTATUS vol_query_unique_id(volume_device_extension* vde, PIRP Irp) {
399     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
400     MOUNTDEV_UNIQUE_ID* mduid;
401     pdo_device_extension* pdode;
402 
403     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
404         Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
405         return STATUS_BUFFER_TOO_SMALL;
406     }
407 
408     mduid = Irp->AssociatedIrp.SystemBuffer;
409     mduid->UniqueIdLength = sizeof(BTRFS_UUID);
410 
411     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength) {
412         Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
413         return STATUS_BUFFER_OVERFLOW;
414     }
415 
416     if (!vde->pdo)
417         return STATUS_INVALID_PARAMETER;
418 
419     pdode = vde->pdode;
420 
421     RtlCopyMemory(mduid->UniqueId, &pdode->uuid, sizeof(BTRFS_UUID));
422 
423     Irp->IoStatus.Information = offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength;
424 
425     return STATUS_SUCCESS;
426 }
427 
428 static NTSTATUS vol_is_dynamic(PIRP Irp) {
429     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
430     uint8_t* buf;
431 
432     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == 0 || !Irp->AssociatedIrp.SystemBuffer)
433         return STATUS_INVALID_PARAMETER;
434 
435     buf = (uint8_t*)Irp->AssociatedIrp.SystemBuffer;
436 
437     *buf = 1;
438 
439     Irp->IoStatus.Information = 1;
440 
441     return STATUS_SUCCESS;
442 }
443 
444 static NTSTATUS vol_check_verify(volume_device_extension* vde) {
445     pdo_device_extension* pdode = vde->pdode;
446     NTSTATUS Status;
447     LIST_ENTRY* le;
448 
449     ExAcquireResourceSharedLite(&pdode->child_lock, true);
450 
451     le = pdode->children.Flink;
452     while (le != &pdode->children) {
453         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
454 
455         Status = dev_ioctl(vc->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, false, NULL);
456         if (!NT_SUCCESS(Status))
457             goto end;
458 
459         le = le->Flink;
460     }
461 
462     Status = STATUS_SUCCESS;
463 
464 end:
465     ExReleaseResourceLite(&pdode->child_lock);
466 
467     return Status;
468 }
469 
470 static NTSTATUS vol_get_disk_extents(volume_device_extension* vde, PIRP Irp) {
471     pdo_device_extension* pdode = vde->pdode;
472     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
473     LIST_ENTRY* le;
474     ULONG num_extents = 0, i, max_extents = 1;
475     NTSTATUS Status;
476     VOLUME_DISK_EXTENTS *ext, *ext3;
477 
478     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS))
479         return STATUS_BUFFER_TOO_SMALL;
480 
481     ExAcquireResourceSharedLite(&pdode->child_lock, true);
482 
483     le = pdode->children.Flink;
484     while (le != &pdode->children) {
485         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
486         VOLUME_DISK_EXTENTS ext2;
487 
488         Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &ext2, sizeof(VOLUME_DISK_EXTENTS), false, NULL);
489         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
490             ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status);
491             goto end;
492         }
493 
494         num_extents += ext2.NumberOfDiskExtents;
495 
496         if (ext2.NumberOfDiskExtents > max_extents)
497             max_extents = ext2.NumberOfDiskExtents;
498 
499         le = le->Flink;
500     }
501 
502     ext = Irp->AssociatedIrp.SystemBuffer;
503 
504     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (num_extents * sizeof(DISK_EXTENT))) {
505         Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]);
506         ext->NumberOfDiskExtents = num_extents;
507         Status = STATUS_BUFFER_OVERFLOW;
508         goto end;
509     }
510 
511     ext3 = ExAllocatePoolWithTag(PagedPool, offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), ALLOC_TAG);
512     if (!ext3) {
513         ERR("out of memory\n");
514         Status = STATUS_INSUFFICIENT_RESOURCES;
515         goto end;
516     }
517 
518     i = 0;
519     ext->NumberOfDiskExtents = 0;
520 
521     le = pdode->children.Flink;
522     while (le != &pdode->children) {
523         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
524 
525         Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, ext3,
526                            (ULONG)offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), false, NULL);
527         if (!NT_SUCCESS(Status)) {
528             ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status);
529             ExFreePool(ext3);
530             goto end;
531         }
532 
533         if (i + ext3->NumberOfDiskExtents > num_extents) {
534             Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]);
535             ext->NumberOfDiskExtents = i + ext3->NumberOfDiskExtents;
536             Status = STATUS_BUFFER_OVERFLOW;
537             ExFreePool(ext3);
538             goto end;
539         }
540 
541         RtlCopyMemory(&ext->Extents[i], ext3->Extents, sizeof(DISK_EXTENT) * ext3->NumberOfDiskExtents);
542         i += ext3->NumberOfDiskExtents;
543 
544         le = le->Flink;
545     }
546 
547     ExFreePool(ext3);
548 
549     Status = STATUS_SUCCESS;
550 
551     ext->NumberOfDiskExtents = i;
552     Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (i * sizeof(DISK_EXTENT));
553 
554 end:
555     ExReleaseResourceLite(&pdode->child_lock);
556 
557     return Status;
558 }
559 
560 static NTSTATUS vol_is_writable(volume_device_extension* vde) {
561     pdo_device_extension* pdode = vde->pdode;
562     NTSTATUS Status;
563     LIST_ENTRY* le;
564     bool writable = false;
565 
566     ExAcquireResourceSharedLite(&pdode->child_lock, true);
567 
568     le = pdode->children.Flink;
569     while (le != &pdode->children) {
570         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
571 
572         Status = dev_ioctl(vc->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, true, NULL);
573 
574         if (NT_SUCCESS(Status)) {
575             writable = true;
576             break;
577         } else if (Status != STATUS_MEDIA_WRITE_PROTECTED)
578             goto end;
579 
580         le = le->Flink;
581     }
582 
583     Status = writable ? STATUS_SUCCESS : STATUS_MEDIA_WRITE_PROTECTED;
584 
585 end:
586 ExReleaseResourceLite(&pdode->child_lock);
587 
588     return STATUS_SUCCESS;
589 }
590 
591 static NTSTATUS vol_get_length(volume_device_extension* vde, PIRP Irp) {
592     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
593     pdo_device_extension* pdode = vde->pdode;
594     GET_LENGTH_INFORMATION* gli;
595     LIST_ENTRY* le;
596 
597     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION))
598         return STATUS_BUFFER_TOO_SMALL;
599 
600     gli = (GET_LENGTH_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
601 
602     gli->Length.QuadPart = 0;
603 
604     ExAcquireResourceSharedLite(&pdode->child_lock, true);
605 
606     le = pdode->children.Flink;
607     while (le != &pdode->children) {
608         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
609 
610         gli->Length.QuadPart += vc->size;
611 
612         le = le->Flink;
613     }
614 
615     ExReleaseResourceLite(&pdode->child_lock);
616 
617     Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
618 
619     return STATUS_SUCCESS;
620 }
621 
622 static NTSTATUS vol_get_drive_geometry(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
623     volume_device_extension* vde = DeviceObject->DeviceExtension;
624     pdo_device_extension* pdode = vde->pdode;
625     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
626     DISK_GEOMETRY* geom;
627     uint64_t length;
628     LIST_ENTRY* le;
629 
630     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
631         return STATUS_BUFFER_TOO_SMALL;
632 
633     length = 0;
634 
635     ExAcquireResourceSharedLite(&pdode->child_lock, true);
636 
637     le = pdode->children.Flink;
638     while (le != &pdode->children) {
639         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
640 
641         length += vc->size;
642 
643         le = le->Flink;
644     }
645 
646     ExReleaseResourceLite(&pdode->child_lock);
647 
648     geom = (DISK_GEOMETRY*)Irp->AssociatedIrp.SystemBuffer;
649     geom->BytesPerSector = DeviceObject->SectorSize == 0 ? 0x200 : DeviceObject->SectorSize;
650     geom->SectorsPerTrack = 0x3f;
651     geom->TracksPerCylinder = 0xff;
652     geom->Cylinders.QuadPart = length / (UInt32x32To64(geom->TracksPerCylinder, geom->SectorsPerTrack) * geom->BytesPerSector);
653     geom->MediaType = DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA ? RemovableMedia : FixedMedia;
654 
655     Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
656 
657     return STATUS_SUCCESS;
658 }
659 
660 static NTSTATUS vol_get_gpt_attributes(PIRP Irp) {
661     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
662     VOLUME_GET_GPT_ATTRIBUTES_INFORMATION* vggai;
663 
664     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION))
665         return STATUS_BUFFER_TOO_SMALL;
666 
667     vggai = (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
668 
669     vggai->GptAttributes = 0;
670 
671     Irp->IoStatus.Information = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION);
672 
673     return STATUS_SUCCESS;
674 }
675 
676 static NTSTATUS vol_get_device_number(volume_device_extension* vde, PIRP Irp) {
677     pdo_device_extension* pdode = vde->pdode;
678     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
679     volume_child* vc;
680     STORAGE_DEVICE_NUMBER* sdn;
681 
682     // If only one device, return its disk number. This is needed for ejection to work.
683 
684     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DEVICE_NUMBER))
685         return STATUS_BUFFER_TOO_SMALL;
686 
687     ExAcquireResourceSharedLite(&pdode->child_lock, true);
688 
689     if (IsListEmpty(&pdode->children) || pdode->num_children > 1) {
690         ExReleaseResourceLite(&pdode->child_lock);
691         return STATUS_INVALID_DEVICE_REQUEST;
692     }
693 
694     vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
695 
696     if (vc->disk_num == 0xffffffff) {
697         ExReleaseResourceLite(&pdode->child_lock);
698         return STATUS_INVALID_DEVICE_REQUEST;
699     }
700 
701     sdn = (STORAGE_DEVICE_NUMBER*)Irp->AssociatedIrp.SystemBuffer;
702 
703     sdn->DeviceType = FILE_DEVICE_DISK;
704     sdn->DeviceNumber = vc->disk_num;
705     sdn->PartitionNumber = vc->part_num;
706 
707     ExReleaseResourceLite(&pdode->child_lock);
708 
709     Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
710 
711     return STATUS_SUCCESS;
712 }
713 
714 _Function_class_(IO_COMPLETION_ROUTINE)
715 static NTSTATUS __stdcall vol_ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
716     KEVENT* event = conptr;
717 
718     UNUSED(DeviceObject);
719     UNUSED(Irp);
720 
721     KeSetEvent(event, 0, false);
722 
723     return STATUS_MORE_PROCESSING_REQUIRED;
724 }
725 
726 static NTSTATUS vol_ioctl_passthrough(volume_device_extension* vde, PIRP Irp) {
727     NTSTATUS Status;
728     volume_child* vc;
729     PIRP Irp2;
730     PIO_STACK_LOCATION IrpSp, IrpSp2;
731     KEVENT Event;
732     pdo_device_extension* pdode = vde->pdode;
733 
734     TRACE("(%p, %p)\n", vde, Irp);
735 
736     ExAcquireResourceSharedLite(&pdode->child_lock, true);
737 
738     if (IsListEmpty(&pdode->children)) {
739         ExReleaseResourceLite(&pdode->child_lock);
740         return STATUS_INVALID_DEVICE_REQUEST;
741     }
742 
743     vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
744 
745     if (vc->list_entry.Flink != &pdode->children) { // more than one device
746         ExReleaseResourceLite(&pdode->child_lock);
747         return STATUS_INVALID_DEVICE_REQUEST;
748     }
749 
750     Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
751 
752     if (!Irp2) {
753         ERR("IoAllocateIrp failed\n");
754         ExReleaseResourceLite(&pdode->child_lock);
755         return STATUS_INSUFFICIENT_RESOURCES;
756     }
757 
758     IrpSp = IoGetCurrentIrpStackLocation(Irp);
759     IrpSp2 = IoGetNextIrpStackLocation(Irp2);
760 
761     IrpSp2->MajorFunction = IrpSp->MajorFunction;
762     IrpSp2->MinorFunction = IrpSp->MinorFunction;
763     IrpSp2->FileObject = vc->fileobj;
764 
765     IrpSp2->Parameters.DeviceIoControl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
766     IrpSp2->Parameters.DeviceIoControl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
767     IrpSp2->Parameters.DeviceIoControl.IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
768     IrpSp2->Parameters.DeviceIoControl.Type3InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
769 
770     Irp2->AssociatedIrp.SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
771     Irp2->MdlAddress = Irp->MdlAddress;
772     Irp2->UserBuffer = Irp->UserBuffer;
773     Irp2->Flags = Irp->Flags;
774 
775     KeInitializeEvent(&Event, NotificationEvent, false);
776 
777     IoSetCompletionRoutine(Irp2, vol_ioctl_completion, &Event, true, true, true);
778 
779     Status = IoCallDriver(vc->devobj, Irp2);
780 
781     if (Status == STATUS_PENDING) {
782         KeWaitForSingleObject(&Event, Executive, KernelMode, false, NULL);
783         Status = Irp2->IoStatus.Status;
784     }
785 
786     Irp->IoStatus.Status = Irp2->IoStatus.Status;
787     Irp->IoStatus.Information = Irp2->IoStatus.Information;
788 
789     ExReleaseResourceLite(&pdode->child_lock);
790 
791     IoFreeIrp(Irp2);
792 
793     return Status;
794 }
795 
796 static NTSTATUS vol_query_stable_guid(volume_device_extension* vde, PIRP Irp) {
797     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
798     MOUNTDEV_STABLE_GUID* mdsg;
799     pdo_device_extension* pdode;
800 
801     if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID)) {
802         Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
803         return STATUS_BUFFER_TOO_SMALL;
804     }
805 
806     mdsg = Irp->AssociatedIrp.SystemBuffer;
807 
808     if (!vde->pdo)
809         return STATUS_INVALID_PARAMETER;
810 
811     pdode = vde->pdode;
812 
813     RtlCopyMemory(&mdsg->StableGuid, &pdode->uuid, sizeof(BTRFS_UUID));
814 
815     Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
816 
817     return STATUS_SUCCESS;
818 }
819 
820 NTSTATUS vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
821     volume_device_extension* vde = DeviceObject->DeviceExtension;
822     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
823 
824     TRACE("(%p, %p)\n", DeviceObject, Irp);
825 
826     Irp->IoStatus.Information = 0;
827 
828     switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
829         case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
830             return vol_query_device_name(vde, Irp);
831 
832         case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
833             return vol_query_unique_id(vde, Irp);
834 
835         case IOCTL_STORAGE_GET_DEVICE_NUMBER:
836             return vol_get_device_number(vde, Irp);
837 
838         case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
839             TRACE("unhandled control code IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME\n");
840             break;
841 
842         case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
843             return vol_query_stable_guid(vde, Irp);
844 
845         case IOCTL_MOUNTDEV_LINK_CREATED:
846             TRACE("unhandled control code IOCTL_MOUNTDEV_LINK_CREATED\n");
847             break;
848 
849         case IOCTL_VOLUME_GET_GPT_ATTRIBUTES:
850             return vol_get_gpt_attributes(Irp);
851 
852         case IOCTL_VOLUME_IS_DYNAMIC:
853             return vol_is_dynamic(Irp);
854 
855         case IOCTL_VOLUME_ONLINE:
856             Irp->IoStatus.Information = 0;
857             return STATUS_SUCCESS;
858 
859         case IOCTL_VOLUME_POST_ONLINE:
860             Irp->IoStatus.Information = 0;
861             return STATUS_SUCCESS;
862 
863         case IOCTL_DISK_GET_DRIVE_GEOMETRY:
864             return vol_get_drive_geometry(DeviceObject, Irp);
865 
866         case IOCTL_DISK_IS_WRITABLE:
867             return vol_is_writable(vde);
868 
869         case IOCTL_DISK_GET_LENGTH_INFO:
870             return vol_get_length(vde, Irp);
871 
872         case IOCTL_STORAGE_CHECK_VERIFY:
873         case IOCTL_DISK_CHECK_VERIFY:
874             return vol_check_verify(vde);
875 
876         case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
877             return vol_get_disk_extents(vde, Irp);
878 
879         default: { // pass ioctl through if only one child device
880             ULONG code = IrpSp->Parameters.DeviceIoControl.IoControlCode;
881             NTSTATUS Status = vol_ioctl_passthrough(vde, Irp);
882 
883 #ifdef __REACTOS__
884             &code;
885 #endif
886 
887             if (NT_SUCCESS(Status))
888                 TRACE("passing through ioctl %lx (returning %08lx)\n", code, Status);
889             else
890                 WARN("passing through ioctl %lx (returning %08lx)\n", code, Status);
891 
892             return Status;
893         }
894     }
895 
896     return STATUS_INVALID_DEVICE_REQUEST;
897 }
898 
899 NTSTATUS vol_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
900     TRACE("(%p, %p)\n", DeviceObject, Irp);
901 
902     return STATUS_INVALID_DEVICE_REQUEST;
903 }
904 
905 NTSTATUS vol_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
906     TRACE("(%p, %p)\n", DeviceObject, Irp);
907 
908     return STATUS_INVALID_DEVICE_REQUEST;
909 }
910 
911 NTSTATUS vol_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
912     TRACE("(%p, %p)\n", DeviceObject, Irp);
913 
914     return STATUS_INVALID_DEVICE_REQUEST;
915 }
916 
917 NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) {
918     NTSTATUS Status;
919     ULONG mmdltsize;
920     MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
921     MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
922 
923     mmdltsize = (ULONG)offsetof(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName[0]) + devpath->Length;
924 
925     mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
926     if (!mmdlt) {
927         ERR("out of memory\n");
928         return STATUS_INSUFFICIENT_RESOURCES;
929     }
930 
931     mmdlt->DeviceNameLength = devpath->Length;
932     RtlCopyMemory(&mmdlt->DeviceName, devpath->Buffer, devpath->Length);
933     TRACE("mmdlt = %.*S\n", (int)(mmdlt->DeviceNameLength / sizeof(WCHAR)), mmdlt->DeviceName);
934 
935     Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER, mmdlt, mmdltsize, &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), false, NULL);
936 
937     if (!NT_SUCCESS(Status))
938         ERR("IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER returned %08lx\n", Status);
939     else
940         TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
941 
942     ExFreePool(mmdlt);
943 
944     return Status;
945 }
946 
947 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
948 NTSTATUS __stdcall pnp_removal(PVOID NotificationStructure, PVOID Context) {
949     TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = (TARGET_DEVICE_REMOVAL_NOTIFICATION*)NotificationStructure;
950     pdo_device_extension* pdode = (pdo_device_extension*)Context;
951 
952     if (RtlCompareMemory(&tdrn->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE, sizeof(GUID)) == sizeof(GUID)) {
953         TRACE("GUID_TARGET_DEVICE_QUERY_REMOVE\n");
954 
955         if (pdode->vde && pdode->vde->mounted_device)
956             return pnp_query_remove_device(pdode->vde->mounted_device, NULL);
957     }
958 
959     return STATUS_SUCCESS;
960 }
961 
962 static bool allow_degraded_mount(BTRFS_UUID* uuid) {
963     HANDLE h;
964     NTSTATUS Status;
965     OBJECT_ATTRIBUTES oa;
966     UNICODE_STRING path, adus;
967     uint32_t degraded = mount_allow_degraded;
968     ULONG i, j, kvfilen, retlen;
969     KEY_VALUE_FULL_INFORMATION* kvfi;
970 
971     path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
972     path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
973 
974     if (!path.Buffer) {
975         ERR("out of memory\n");
976         return false;
977     }
978 
979     RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
980     i = registry_path.Length / sizeof(WCHAR);
981 
982     path.Buffer[i] = '\\';
983     i++;
984 
985     for (j = 0; j < 16; j++) {
986         path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
987         path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
988 
989         i += 2;
990 
991         if (j == 3 || j == 5 || j == 7 || j == 9) {
992             path.Buffer[i] = '-';
993             i++;
994         }
995     }
996 
997     InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
998 
999     kvfilen = (ULONG)offsetof(KEY_VALUE_FULL_INFORMATION, Name[0]) + (255 * sizeof(WCHAR));
1000     kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
1001     if (!kvfi) {
1002         ERR("out of memory\n");
1003         ExFreePool(path.Buffer);
1004         return false;
1005     }
1006 
1007     Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa);
1008     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1009         goto end;
1010     else if (!NT_SUCCESS(Status)) {
1011         ERR("ZwOpenKey returned %08lx\n", Status);
1012         goto end;
1013     }
1014 
1015     adus.Buffer = L"AllowDegraded";
1016     adus.Length = adus.MaximumLength = sizeof(adus.Buffer) - sizeof(WCHAR);
1017 
1018     if (NT_SUCCESS(ZwQueryValueKey(h, &adus, KeyValueFullInformation, kvfi, kvfilen, &retlen))) {
1019         if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(uint32_t)) {
1020             uint32_t* val = (uint32_t*)((uint8_t*)kvfi + kvfi->DataOffset);
1021 
1022             degraded = *val;
1023         }
1024     }
1025 
1026     ZwClose(h);
1027 
1028 end:
1029     ExFreePool(kvfi);
1030 
1031     ExFreePool(path.Buffer);
1032 
1033     return degraded;
1034 }
1035 
1036 typedef struct {
1037     LIST_ENTRY list_entry;
1038     UNICODE_STRING name;
1039     NTSTATUS Status;
1040     BTRFS_UUID uuid;
1041 } drive_letter_removal;
1042 
1043 static void drive_letter_callback2(pdo_device_extension* pdode, PDEVICE_OBJECT mountmgr) {
1044     LIST_ENTRY* le;
1045     LIST_ENTRY dlrlist;
1046 
1047     InitializeListHead(&dlrlist);
1048 
1049     ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1050 
1051     le = pdode->children.Flink;
1052 
1053     while (le != &pdode->children) {
1054         drive_letter_removal* dlr;
1055 
1056         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
1057 
1058         dlr = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_removal), ALLOC_TAG);
1059         if (!dlr) {
1060             ERR("out of memory\n");
1061 
1062             while (!IsListEmpty(&dlrlist)) {
1063                 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
1064 
1065                 ExFreePool(dlr->name.Buffer);
1066                 ExFreePool(dlr);
1067             }
1068 
1069             ExReleaseResourceLite(&pdode->child_lock);
1070             return;
1071         }
1072 
1073         dlr->name.Length = dlr->name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR));
1074         dlr->name.Buffer = ExAllocatePoolWithTag(PagedPool, dlr->name.Length, ALLOC_TAG);
1075 
1076         if (!dlr->name.Buffer) {
1077             ERR("out of memory\n");
1078 
1079             ExFreePool(dlr);
1080 
1081             while (!IsListEmpty(&dlrlist)) {
1082                 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
1083 
1084                 ExFreePool(dlr->name.Buffer);
1085                 ExFreePool(dlr);
1086             }
1087 
1088             ExReleaseResourceLite(&pdode->child_lock);
1089             return;
1090         }
1091 
1092         RtlCopyMemory(dlr->name.Buffer, L"\\??", 3 * sizeof(WCHAR));
1093         RtlCopyMemory(&dlr->name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length);
1094 
1095         dlr->uuid = vc->uuid;
1096 
1097         InsertTailList(&dlrlist, &dlr->list_entry);
1098 
1099         le = le->Flink;
1100     }
1101 
1102     ExReleaseResourceLite(&pdode->child_lock);
1103 
1104     le = dlrlist.Flink;
1105     while (le != &dlrlist) {
1106         drive_letter_removal* dlr = CONTAINING_RECORD(le, drive_letter_removal, list_entry);
1107 
1108         dlr->Status = remove_drive_letter(mountmgr, &dlr->name);
1109 
1110         if (!NT_SUCCESS(dlr->Status) && dlr->Status != STATUS_NOT_FOUND)
1111             WARN("remove_drive_letter returned %08lx\n", dlr->Status);
1112 
1113         le = le->Flink;
1114     }
1115 
1116     // set vc->had_drive_letter
1117 
1118     ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1119 
1120     while (!IsListEmpty(&dlrlist)) {
1121         drive_letter_removal* dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
1122 
1123         le = pdode->children.Flink;
1124 
1125         while (le != &pdode->children) {
1126             volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
1127 
1128             if (RtlCompareMemory(&vc->uuid, &dlr->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1129                 vc->had_drive_letter = NT_SUCCESS(dlr->Status);
1130                 break;
1131             }
1132 
1133             le = le->Flink;
1134         }
1135 
1136         ExFreePool(dlr->name.Buffer);
1137         ExFreePool(dlr);
1138     }
1139 
1140     ExReleaseResourceLite(&pdode->child_lock);
1141 }
1142 
1143 _Function_class_(IO_WORKITEM_ROUTINE)
1144 static void __stdcall drive_letter_callback(pdo_device_extension* pdode) {
1145     NTSTATUS Status;
1146     UNICODE_STRING mmdevpath;
1147     PDEVICE_OBJECT mountmgr;
1148     PFILE_OBJECT mountmgrfo;
1149 
1150     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
1151     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
1152     if (!NT_SUCCESS(Status)) {
1153         ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
1154         return;
1155     }
1156 
1157     drive_letter_callback2(pdode, mountmgr);
1158 
1159     ObDereferenceObject(mountmgrfo);
1160 }
1161 
1162 void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length, ULONG disk_num, ULONG part_num) {
1163     NTSTATUS Status;
1164     LIST_ENTRY* le;
1165     PDEVICE_OBJECT DeviceObject;
1166     volume_child* vc;
1167     PFILE_OBJECT FileObject;
1168     UNICODE_STRING devpath2;
1169     bool inserted = false, new_pdo = false;
1170     pdo_device_extension* pdode = NULL;
1171     PDEVICE_OBJECT pdo = NULL;
1172     bool process_drive_letters = false;
1173 
1174     if (devpath->Length == 0)
1175         return;
1176 
1177     ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
1178 
1179     le = pdo_list.Flink;
1180     while (le != &pdo_list) {
1181         pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
1182 
1183         if (RtlCompareMemory(&pdode2->uuid, &sb->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1184             pdode = pdode2;
1185             break;
1186         }
1187 
1188         le = le->Flink;
1189     }
1190 
1191     Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
1192     if (!NT_SUCCESS(Status)) {
1193         ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
1194         ExReleaseResourceLite(&pdo_list_lock);
1195         return;
1196     }
1197 
1198     if (!pdode) {
1199         if (no_pnp) {
1200             Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF, NULL, NULL, 0, &pdo);
1201 
1202             if (!NT_SUCCESS(Status)) {
1203                 ERR("IoReportDetectedDevice returned %08lx\n", Status);
1204                 ExReleaseResourceLite(&pdo_list_lock);
1205                 return;
1206             }
1207 
1208             pdode = ExAllocatePoolWithTag(NonPagedPool, sizeof(pdo_device_extension), ALLOC_TAG);
1209 
1210             if (!pdode) {
1211                 ERR("out of memory\n");
1212                 ExReleaseResourceLite(&pdo_list_lock);
1213                 return;
1214             }
1215         } else {
1216             Status = IoCreateDevice(drvobj, sizeof(pdo_device_extension), NULL, FILE_DEVICE_DISK,
1217                                     FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, false, &pdo);
1218             if (!NT_SUCCESS(Status)) {
1219                 ERR("IoCreateDevice returned %08lx\n", Status);
1220                 ExReleaseResourceLite(&pdo_list_lock);
1221                 goto fail;
1222             }
1223 
1224             pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
1225 
1226             pdode = pdo->DeviceExtension;
1227         }
1228 
1229         RtlZeroMemory(pdode, sizeof(pdo_device_extension));
1230 
1231         pdode->type = VCB_TYPE_PDO;
1232         pdode->pdo = pdo;
1233         pdode->uuid = sb->uuid;
1234 
1235         ExInitializeResourceLite(&pdode->child_lock);
1236         InitializeListHead(&pdode->children);
1237         pdode->num_children = sb->num_devices;
1238         pdode->children_loaded = 0;
1239 
1240         pdo->Flags &= ~DO_DEVICE_INITIALIZING;
1241         pdo->SectorSize = (USHORT)sb->sector_size;
1242 
1243         ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1244 
1245         new_pdo = true;
1246     } else {
1247         ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1248         ExConvertExclusiveToSharedLite(&pdo_list_lock);
1249 
1250         le = pdode->children.Flink;
1251         while (le != &pdode->children) {
1252             volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
1253 
1254             if (RtlCompareMemory(&vc2->uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1255                 // duplicate, ignore
1256                 ExReleaseResourceLite(&pdode->child_lock);
1257                 ExReleaseResourceLite(&pdo_list_lock);
1258                 goto fail;
1259             }
1260 
1261             le = le->Flink;
1262         }
1263     }
1264 
1265     vc = ExAllocatePoolWithTag(PagedPool, sizeof(volume_child), ALLOC_TAG);
1266     if (!vc) {
1267         ERR("out of memory\n");
1268 
1269         ExReleaseResourceLite(&pdode->child_lock);
1270         ExReleaseResourceLite(&pdo_list_lock);
1271 
1272         goto fail;
1273     }
1274 
1275     vc->uuid = sb->dev_item.device_uuid;
1276     vc->devid = sb->dev_item.dev_id;
1277     vc->generation = sb->generation;
1278     vc->notification_entry = NULL;
1279     vc->boot_volume = false;
1280 
1281     Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, FileObject,
1282                                             drvobj, pnp_removal, pdode, &vc->notification_entry);
1283     if (!NT_SUCCESS(Status))
1284         WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status);
1285 
1286     vc->devobj = DeviceObject;
1287     vc->fileobj = FileObject;
1288 
1289     devpath2 = *devpath;
1290 
1291     // The PNP path sometimes begins \\?\ and sometimes \??\. We need to remove this prefix
1292     // so we can compare properly if the device is removed.
1293     if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
1294         devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') {
1295         devpath2.Buffer = &devpath2.Buffer[3];
1296         devpath2.Length -= 3 * sizeof(WCHAR);
1297         devpath2.MaximumLength -= 3 * sizeof(WCHAR);
1298     }
1299 
1300     vc->pnp_name.Length = vc->pnp_name.MaximumLength = devpath2.Length;
1301     vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, devpath2.Length, ALLOC_TAG);
1302 
1303     if (vc->pnp_name.Buffer)
1304         RtlCopyMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length);
1305     else {
1306         ERR("out of memory\n");
1307         vc->pnp_name.Length = vc->pnp_name.MaximumLength = 0;
1308     }
1309 
1310     vc->size = length;
1311     vc->seeding = sb->flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? true : false;
1312     vc->disk_num = disk_num;
1313     vc->part_num = part_num;
1314     vc->had_drive_letter = false;
1315 
1316     le = pdode->children.Flink;
1317     while (le != &pdode->children) {
1318         volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
1319 
1320         if (vc2->generation < vc->generation) {
1321             if (le == pdode->children.Flink)
1322                 pdode->num_children = sb->num_devices;
1323 
1324             InsertHeadList(vc2->list_entry.Blink, &vc->list_entry);
1325             inserted = true;
1326             break;
1327         }
1328 
1329         le = le->Flink;
1330     }
1331 
1332     if (!inserted)
1333         InsertTailList(&pdode->children, &vc->list_entry);
1334 
1335     pdode->children_loaded++;
1336 
1337     if (pdode->vde && pdode->vde->mounted_device) {
1338         device_extension* Vcb = pdode->vde->mounted_device->DeviceExtension;
1339 
1340         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1341 
1342         le = Vcb->devices.Flink;
1343         while (le != &Vcb->devices) {
1344             device* dev = CONTAINING_RECORD(le, device, list_entry);
1345 
1346             if (!dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1347                 dev->devobj = DeviceObject;
1348                 dev->disk_num = disk_num;
1349                 dev->part_num = part_num;
1350                 init_device(Vcb, dev, false);
1351                 break;
1352             }
1353 
1354             le = le->Flink;
1355         }
1356 
1357         ExReleaseResourceLite(&Vcb->tree_lock);
1358     }
1359 
1360     if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1361         pdode->removable = true;
1362 
1363         if (pdode->vde && pdode->vde->device)
1364             pdode->vde->device->Characteristics |= FILE_REMOVABLE_MEDIA;
1365     }
1366 
1367     if (pdode->num_children == pdode->children_loaded || (pdode->children_loaded == 1 && allow_degraded_mount(&sb->uuid))) {
1368         if ((!new_pdo || !no_pnp) && pdode->vde) {
1369             Status = IoSetDeviceInterfaceState(&pdode->vde->bus_name, true);
1370             if (!NT_SUCCESS(Status))
1371                 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
1372         }
1373 
1374         process_drive_letters = true;
1375     }
1376 
1377     ExReleaseResourceLite(&pdode->child_lock);
1378 
1379     if (new_pdo)
1380         InsertTailList(&pdo_list, &pdode->list_entry);
1381 
1382     ExReleaseResourceLite(&pdo_list_lock);
1383 
1384     if (process_drive_letters)
1385         drive_letter_callback(pdode);
1386 
1387     if (new_pdo) {
1388         if (RtlCompareMemory(&sb->uuid, &boot_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID))
1389             boot_add_device(pdo);
1390         else if (no_pnp)
1391             AddDevice(drvobj, pdo);
1392         else {
1393             bus_device_extension* bde = busobj->DeviceExtension;
1394             IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
1395         }
1396     }
1397 
1398     return;
1399 
1400 fail:
1401     ObDereferenceObject(FileObject);
1402 }
1403