xref: /reactos/drivers/filesystems/btrfs/volume.c (revision 36873c49)
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 %08x\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 %08x\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             return vol_ioctl_passthrough(vde, Irp);
881     }
882 
883     return STATUS_INVALID_DEVICE_REQUEST;
884 }
885 
886 NTSTATUS vol_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
887     TRACE("(%p, %p)\n", DeviceObject, Irp);
888 
889     return STATUS_INVALID_DEVICE_REQUEST;
890 }
891 
892 NTSTATUS vol_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
893     TRACE("(%p, %p)\n", DeviceObject, Irp);
894 
895     return STATUS_INVALID_DEVICE_REQUEST;
896 }
897 
898 NTSTATUS vol_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
899     TRACE("(%p, %p)\n", DeviceObject, Irp);
900 
901     return STATUS_INVALID_DEVICE_REQUEST;
902 }
903 
904 NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) {
905     NTSTATUS Status;
906     ULONG mmdltsize;
907     MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
908     MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
909 
910     mmdltsize = (ULONG)offsetof(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName[0]) + devpath->Length;
911 
912     mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
913     if (!mmdlt) {
914         ERR("out of memory\n");
915         return STATUS_INSUFFICIENT_RESOURCES;
916     }
917 
918     mmdlt->DeviceNameLength = devpath->Length;
919     RtlCopyMemory(&mmdlt->DeviceName, devpath->Buffer, devpath->Length);
920     TRACE("mmdlt = %.*S\n", mmdlt->DeviceNameLength / sizeof(WCHAR), mmdlt->DeviceName);
921 
922     Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER, mmdlt, mmdltsize, &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), false, NULL);
923 
924     if (!NT_SUCCESS(Status))
925         ERR("IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER returned %08x\n", Status);
926     else
927         TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
928 
929     ExFreePool(mmdlt);
930 
931     return Status;
932 }
933 
934 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
935 NTSTATUS __stdcall pnp_removal(PVOID NotificationStructure, PVOID Context) {
936     TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = (TARGET_DEVICE_REMOVAL_NOTIFICATION*)NotificationStructure;
937     pdo_device_extension* pdode = (pdo_device_extension*)Context;
938 
939     if (RtlCompareMemory(&tdrn->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE, sizeof(GUID)) == sizeof(GUID)) {
940         TRACE("GUID_TARGET_DEVICE_QUERY_REMOVE\n");
941 
942         if (pdode->vde && pdode->vde->mounted_device)
943             return pnp_query_remove_device(pdode->vde->mounted_device, NULL);
944     }
945 
946     return STATUS_SUCCESS;
947 }
948 
949 static bool allow_degraded_mount(BTRFS_UUID* uuid) {
950     HANDLE h;
951     NTSTATUS Status;
952     OBJECT_ATTRIBUTES oa;
953     UNICODE_STRING path, adus;
954     uint32_t degraded = mount_allow_degraded;
955     ULONG i, j, kvfilen, retlen;
956     KEY_VALUE_FULL_INFORMATION* kvfi;
957 
958     path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
959     path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
960 
961     if (!path.Buffer) {
962         ERR("out of memory\n");
963         return false;
964     }
965 
966     RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
967     i = registry_path.Length / sizeof(WCHAR);
968 
969     path.Buffer[i] = '\\';
970     i++;
971 
972     for (j = 0; j < 16; j++) {
973         path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
974         path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
975 
976         i += 2;
977 
978         if (j == 3 || j == 5 || j == 7 || j == 9) {
979             path.Buffer[i] = '-';
980             i++;
981         }
982     }
983 
984     InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
985 
986     kvfilen = (ULONG)offsetof(KEY_VALUE_FULL_INFORMATION, Name[0]) + (255 * sizeof(WCHAR));
987     kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
988     if (!kvfi) {
989         ERR("out of memory\n");
990         ExFreePool(path.Buffer);
991         return false;
992     }
993 
994     Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa);
995     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
996         goto end;
997     else if (!NT_SUCCESS(Status)) {
998         ERR("ZwOpenKey returned %08x\n", Status);
999         goto end;
1000     }
1001 
1002     adus.Buffer = L"AllowDegraded";
1003     adus.Length = adus.MaximumLength = sizeof(adus.Buffer) - sizeof(WCHAR);
1004 
1005     if (NT_SUCCESS(ZwQueryValueKey(h, &adus, KeyValueFullInformation, kvfi, kvfilen, &retlen))) {
1006         if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(uint32_t)) {
1007             uint32_t* val = (uint32_t*)((uint8_t*)kvfi + kvfi->DataOffset);
1008 
1009             degraded = *val;
1010         }
1011     }
1012 
1013     ZwClose(h);
1014 
1015 end:
1016     ExFreePool(kvfi);
1017 
1018     ExFreePool(path.Buffer);
1019 
1020     return degraded;
1021 }
1022 
1023 typedef struct {
1024     LIST_ENTRY list_entry;
1025     UNICODE_STRING name;
1026     NTSTATUS Status;
1027     BTRFS_UUID uuid;
1028 } drive_letter_removal;
1029 
1030 static void drive_letter_callback2(pdo_device_extension* pdode, PDEVICE_OBJECT mountmgr) {
1031     LIST_ENTRY* le;
1032     LIST_ENTRY dlrlist;
1033 
1034     InitializeListHead(&dlrlist);
1035 
1036     ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1037 
1038     le = pdode->children.Flink;
1039 
1040     while (le != &pdode->children) {
1041         drive_letter_removal* dlr;
1042 
1043         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
1044 
1045         dlr = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_removal), ALLOC_TAG);
1046         if (!dlr) {
1047             ERR("out of memory\n");
1048 
1049             while (!IsListEmpty(&dlrlist)) {
1050                 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
1051 
1052                 ExFreePool(dlr->name.Buffer);
1053                 ExFreePool(dlr);
1054             }
1055 
1056             ExReleaseResourceLite(&pdode->child_lock);
1057             return;
1058         }
1059 
1060         dlr->name.Length = dlr->name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR));
1061         dlr->name.Buffer = ExAllocatePoolWithTag(PagedPool, dlr->name.Length, ALLOC_TAG);
1062 
1063         if (!dlr->name.Buffer) {
1064             ERR("out of memory\n");
1065 
1066             ExFreePool(dlr);
1067 
1068             while (!IsListEmpty(&dlrlist)) {
1069                 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
1070 
1071                 ExFreePool(dlr->name.Buffer);
1072                 ExFreePool(dlr);
1073             }
1074 
1075             ExReleaseResourceLite(&pdode->child_lock);
1076             return;
1077         }
1078 
1079         RtlCopyMemory(dlr->name.Buffer, L"\\??", 3 * sizeof(WCHAR));
1080         RtlCopyMemory(&dlr->name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length);
1081 
1082         dlr->uuid = vc->uuid;
1083 
1084         InsertTailList(&dlrlist, &dlr->list_entry);
1085 
1086         le = le->Flink;
1087     }
1088 
1089     ExReleaseResourceLite(&pdode->child_lock);
1090 
1091     le = dlrlist.Flink;
1092     while (le != &dlrlist) {
1093         drive_letter_removal* dlr = CONTAINING_RECORD(le, drive_letter_removal, list_entry);
1094 
1095         dlr->Status = remove_drive_letter(mountmgr, &dlr->name);
1096 
1097         if (!NT_SUCCESS(dlr->Status) && dlr->Status != STATUS_NOT_FOUND)
1098             WARN("remove_drive_letter returned %08x\n", dlr->Status);
1099 
1100         le = le->Flink;
1101     }
1102 
1103     // set vc->had_drive_letter
1104 
1105     ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1106 
1107     while (!IsListEmpty(&dlrlist)) {
1108         drive_letter_removal* dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
1109 
1110         le = pdode->children.Flink;
1111 
1112         while (le != &pdode->children) {
1113             volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
1114 
1115             if (RtlCompareMemory(&vc->uuid, &dlr->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1116                 vc->had_drive_letter = NT_SUCCESS(dlr->Status);
1117                 break;
1118             }
1119 
1120             le = le->Flink;
1121         }
1122 
1123         ExFreePool(dlr->name.Buffer);
1124         ExFreePool(dlr);
1125     }
1126 
1127     ExReleaseResourceLite(&pdode->child_lock);
1128 }
1129 
1130 _Function_class_(IO_WORKITEM_ROUTINE)
1131 static void __stdcall drive_letter_callback(pdo_device_extension* pdode) {
1132     NTSTATUS Status;
1133     UNICODE_STRING mmdevpath;
1134     PDEVICE_OBJECT mountmgr;
1135     PFILE_OBJECT mountmgrfo;
1136 
1137     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
1138     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
1139     if (!NT_SUCCESS(Status)) {
1140         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
1141         return;
1142     }
1143 
1144     drive_letter_callback2(pdode, mountmgr);
1145 
1146     ObDereferenceObject(mountmgrfo);
1147 }
1148 
1149 void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length, ULONG disk_num, ULONG part_num) {
1150     NTSTATUS Status;
1151     LIST_ENTRY* le;
1152     PDEVICE_OBJECT DeviceObject;
1153     volume_child* vc;
1154     PFILE_OBJECT FileObject;
1155     UNICODE_STRING devpath2;
1156     bool inserted = false, new_pdo = false;
1157     pdo_device_extension* pdode = NULL;
1158     PDEVICE_OBJECT pdo = NULL;
1159     bool process_drive_letters = false;
1160 
1161     if (devpath->Length == 0)
1162         return;
1163 
1164     ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
1165 
1166     le = pdo_list.Flink;
1167     while (le != &pdo_list) {
1168         pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
1169 
1170         if (RtlCompareMemory(&pdode2->uuid, &sb->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1171             pdode = pdode2;
1172             break;
1173         }
1174 
1175         le = le->Flink;
1176     }
1177 
1178     Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
1179     if (!NT_SUCCESS(Status)) {
1180         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
1181         ExReleaseResourceLite(&pdo_list_lock);
1182         return;
1183     }
1184 
1185     if (!pdode) {
1186         if (no_pnp) {
1187             Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF, NULL, NULL, 0, &pdo);
1188 
1189             if (!NT_SUCCESS(Status)) {
1190                 ERR("IoReportDetectedDevice returned %08x\n", Status);
1191                 ExReleaseResourceLite(&pdo_list_lock);
1192                 return;
1193             }
1194 
1195             pdode = ExAllocatePoolWithTag(NonPagedPool, sizeof(pdo_device_extension), ALLOC_TAG);
1196 
1197             if (!pdode) {
1198                 ERR("out of memory\n");
1199                 ExReleaseResourceLite(&pdo_list_lock);
1200                 return;
1201             }
1202         } else {
1203             Status = IoCreateDevice(drvobj, sizeof(pdo_device_extension), NULL, FILE_DEVICE_DISK,
1204                                     FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, false, &pdo);
1205             if (!NT_SUCCESS(Status)) {
1206                 ERR("IoCreateDevice returned %08x\n", Status);
1207                 ExReleaseResourceLite(&pdo_list_lock);
1208                 goto fail;
1209             }
1210 
1211             pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
1212 
1213             pdode = pdo->DeviceExtension;
1214         }
1215 
1216         RtlZeroMemory(pdode, sizeof(pdo_device_extension));
1217 
1218         pdode->type = VCB_TYPE_PDO;
1219         pdode->pdo = pdo;
1220         pdode->uuid = sb->uuid;
1221 
1222         ExInitializeResourceLite(&pdode->child_lock);
1223         InitializeListHead(&pdode->children);
1224         pdode->num_children = sb->num_devices;
1225         pdode->children_loaded = 0;
1226 
1227         pdo->Flags &= ~DO_DEVICE_INITIALIZING;
1228         pdo->SectorSize = (USHORT)sb->sector_size;
1229 
1230         ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1231 
1232         new_pdo = true;
1233     } else {
1234         ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1235         ExConvertExclusiveToSharedLite(&pdo_list_lock);
1236 
1237         le = pdode->children.Flink;
1238         while (le != &pdode->children) {
1239             volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
1240 
1241             if (RtlCompareMemory(&vc2->uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1242                 // duplicate, ignore
1243                 ExReleaseResourceLite(&pdode->child_lock);
1244                 ExReleaseResourceLite(&pdo_list_lock);
1245                 goto fail;
1246             }
1247 
1248             le = le->Flink;
1249         }
1250     }
1251 
1252     vc = ExAllocatePoolWithTag(PagedPool, sizeof(volume_child), ALLOC_TAG);
1253     if (!vc) {
1254         ERR("out of memory\n");
1255 
1256         ExReleaseResourceLite(&pdode->child_lock);
1257         ExReleaseResourceLite(&pdo_list_lock);
1258 
1259         goto fail;
1260     }
1261 
1262     vc->uuid = sb->dev_item.device_uuid;
1263     vc->devid = sb->dev_item.dev_id;
1264     vc->generation = sb->generation;
1265     vc->notification_entry = NULL;
1266 
1267     Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, FileObject,
1268                                             drvobj, pnp_removal, pdode, &vc->notification_entry);
1269     if (!NT_SUCCESS(Status))
1270         WARN("IoRegisterPlugPlayNotification returned %08x\n", Status);
1271 
1272     vc->devobj = DeviceObject;
1273     vc->fileobj = FileObject;
1274 
1275     devpath2 = *devpath;
1276 
1277     // The PNP path sometimes begins \\?\ and sometimes \??\. We need to remove this prefix
1278     // so we can compare properly if the device is removed.
1279     if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
1280         devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') {
1281         devpath2.Buffer = &devpath2.Buffer[3];
1282         devpath2.Length -= 3 * sizeof(WCHAR);
1283         devpath2.MaximumLength -= 3 * sizeof(WCHAR);
1284     }
1285 
1286     vc->pnp_name.Length = vc->pnp_name.MaximumLength = devpath2.Length;
1287     vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, devpath2.Length, ALLOC_TAG);
1288 
1289     if (vc->pnp_name.Buffer)
1290         RtlCopyMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length);
1291     else {
1292         ERR("out of memory\n");
1293         vc->pnp_name.Length = vc->pnp_name.MaximumLength = 0;
1294     }
1295 
1296     vc->size = length;
1297     vc->seeding = sb->flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? true : false;
1298     vc->disk_num = disk_num;
1299     vc->part_num = part_num;
1300     vc->had_drive_letter = false;
1301 
1302     le = pdode->children.Flink;
1303     while (le != &pdode->children) {
1304         volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
1305 
1306         if (vc2->generation < vc->generation) {
1307             if (le == pdode->children.Flink)
1308                 pdode->num_children = sb->num_devices;
1309 
1310             InsertHeadList(vc2->list_entry.Blink, &vc->list_entry);
1311             inserted = true;
1312             break;
1313         }
1314 
1315         le = le->Flink;
1316     }
1317 
1318     if (!inserted)
1319         InsertTailList(&pdode->children, &vc->list_entry);
1320 
1321     pdode->children_loaded++;
1322 
1323     if (pdode->vde && pdode->vde->mounted_device) {
1324         device_extension* Vcb = pdode->vde->mounted_device->DeviceExtension;
1325 
1326         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1327 
1328         le = Vcb->devices.Flink;
1329         while (le != &Vcb->devices) {
1330             device* dev = CONTAINING_RECORD(le, device, list_entry);
1331 
1332             if (!dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1333                 dev->devobj = DeviceObject;
1334                 dev->disk_num = disk_num;
1335                 dev->part_num = part_num;
1336                 init_device(Vcb, dev, false);
1337                 break;
1338             }
1339 
1340             le = le->Flink;
1341         }
1342 
1343         ExReleaseResourceLite(&Vcb->tree_lock);
1344     }
1345 
1346     if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1347         pdode->removable = true;
1348 
1349         if (pdode->vde && pdode->vde->device)
1350             pdode->vde->device->Characteristics |= FILE_REMOVABLE_MEDIA;
1351     }
1352 
1353     if (pdode->num_children == pdode->children_loaded || (pdode->children_loaded == 1 && allow_degraded_mount(&sb->uuid))) {
1354         if ((!new_pdo || !no_pnp) && pdode->vde) {
1355             Status = IoSetDeviceInterfaceState(&pdode->vde->bus_name, true);
1356             if (!NT_SUCCESS(Status))
1357                 WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
1358         }
1359 
1360         process_drive_letters = true;
1361     }
1362 
1363     ExReleaseResourceLite(&pdode->child_lock);
1364 
1365     if (new_pdo)
1366         InsertTailList(&pdo_list, &pdode->list_entry);
1367 
1368     ExReleaseResourceLite(&pdo_list_lock);
1369 
1370     if (process_drive_letters)
1371         drive_letter_callback(pdode);
1372 
1373     if (new_pdo) {
1374         if (no_pnp)
1375             AddDevice(drvobj, pdo);
1376         else {
1377             bus_device_extension* bde = busobj->DeviceExtension;
1378             IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
1379         }
1380     }
1381 
1382     return;
1383 
1384 fail:
1385     ObDereferenceObject(FileObject);
1386 }
1387