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