/* Copyright (c) Mark Harmstone 2016-17 * * This file is part of WinBtrfs. * * WinBtrfs is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public Licence as published by * the Free Software Foundation, either version 3 of the Licence, or * (at your option) any later version. * * WinBtrfs is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public Licence for more details. * * You should have received a copy of the GNU Lesser General Public Licence * along with WinBtrfs. If not, see . */ #include "btrfs_drv.h" #include #include #include #include #include #define IOCTL_VOLUME_IS_DYNAMIC CTL_CODE(IOCTL_VOLUME_BASE, 18, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_VOLUME_POST_ONLINE CTL_CODE(IOCTL_VOLUME_BASE, 25, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) extern PDRIVER_OBJECT drvobj; extern PDEVICE_OBJECT master_devobj; extern PDEVICE_OBJECT busobj; extern ERESOURCE pdo_list_lock; extern LIST_ENTRY pdo_list; extern UNICODE_STRING registry_path; extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx; NTSTATUS vol_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { volume_device_extension* vde = DeviceObject->DeviceExtension; TRACE("(%p, %p)\n", DeviceObject, Irp); if (vde->removing) return STATUS_DEVICE_NOT_READY; Irp->IoStatus.Information = FILE_OPENED; InterlockedIncrement(&vde->open_count); return STATUS_SUCCESS; } void free_vol(volume_device_extension* vde) { PDEVICE_OBJECT pdo; vde->dead = true; if (vde->mounted_device) { device_extension* Vcb = vde->mounted_device->DeviceExtension; Vcb->vde = NULL; } if (vde->name.Buffer) ExFreePool(vde->name.Buffer); ExDeleteResourceLite(&vde->pdode->child_lock); if (vde->pdo->AttachedDevice) IoDetachDevice(vde->pdo); while (!IsListEmpty(&vde->pdode->children)) { volume_child* vc = CONTAINING_RECORD(RemoveHeadList(&vde->pdode->children), volume_child, list_entry); if (vc->notification_entry) { if (fIoUnregisterPlugPlayNotificationEx) fIoUnregisterPlugPlayNotificationEx(vc->notification_entry); else IoUnregisterPlugPlayNotification(vc->notification_entry); } if (vc->pnp_name.Buffer) ExFreePool(vc->pnp_name.Buffer); ExFreePool(vc); } if (no_pnp) ExFreePool(vde->pdode); pdo = vde->pdo; IoDeleteDevice(vde->device); if (!no_pnp) IoDeleteDevice(pdo); } NTSTATUS vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { volume_device_extension* vde = DeviceObject->DeviceExtension; pdo_device_extension* pdode = vde->pdode; TRACE("(%p, %p)\n", DeviceObject, Irp); Irp->IoStatus.Information = 0; if (vde->dead) return STATUS_SUCCESS; ExAcquireResourceExclusiveLite(&pdo_list_lock, true); if (vde->dead) { ExReleaseResourceLite(&pdo_list_lock); return STATUS_SUCCESS; } ExAcquireResourceSharedLite(&pdode->child_lock, true); if (InterlockedDecrement(&vde->open_count) == 0 && vde->removing) { ExReleaseResourceLite(&pdode->child_lock); free_vol(vde); } else ExReleaseResourceLite(&pdode->child_lock); ExReleaseResourceLite(&pdo_list_lock); return STATUS_SUCCESS; } typedef struct { IO_STATUS_BLOCK iosb; KEVENT Event; } vol_read_context; _Function_class_(IO_COMPLETION_ROUTINE) static NTSTATUS __stdcall vol_read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { vol_read_context* context = conptr; UNUSED(DeviceObject); context->iosb = Irp->IoStatus; KeSetEvent(&context->Event, 0, false); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS vol_read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { volume_device_extension* vde = DeviceObject->DeviceExtension; pdo_device_extension* pdode = vde->pdode; volume_child* vc; NTSTATUS Status; PIRP Irp2; vol_read_context context; PIO_STACK_LOCATION IrpSp, IrpSp2; TRACE("(%p, %p)\n", DeviceObject, Irp); ExAcquireResourceSharedLite(&pdode->child_lock, true); if (IsListEmpty(&pdode->children)) { ExReleaseResourceLite(&pdode->child_lock); Status = STATUS_INVALID_DEVICE_REQUEST; goto end; } vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry); // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack Irp2 = IoAllocateIrp(vc->devobj->StackSize, false); if (!Irp2) { ERR("IoAllocateIrp failed\n"); ExReleaseResourceLite(&pdode->child_lock); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } IrpSp = IoGetCurrentIrpStackLocation(Irp); IrpSp2 = IoGetNextIrpStackLocation(Irp2); IrpSp2->MajorFunction = IRP_MJ_READ; IrpSp2->FileObject = vc->fileobj; if (vc->devobj->Flags & DO_BUFFERED_IO) { Irp2->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.Read.Length, ALLOC_TAG); if (!Irp2->AssociatedIrp.SystemBuffer) { ERR("out of memory\n"); ExReleaseResourceLite(&pdode->child_lock); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } Irp2->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION; Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); } else if (vc->devobj->Flags & DO_DIRECT_IO) Irp2->MdlAddress = Irp->MdlAddress; else Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); IrpSp2->Parameters.Read.Length = IrpSp->Parameters.Read.Length; IrpSp2->Parameters.Read.ByteOffset.QuadPart = IrpSp->Parameters.Read.ByteOffset.QuadPart; KeInitializeEvent(&context.Event, NotificationEvent, false); Irp2->UserIosb = &context.iosb; IoSetCompletionRoutine(Irp2, vol_read_completion, &context, true, true, true); Status = IoCallDriver(vc->devobj, Irp2); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL); Status = context.iosb.Status; } ExReleaseResourceLite(&pdode->child_lock); Irp->IoStatus.Information = context.iosb.Information; end: Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS vol_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { volume_device_extension* vde = DeviceObject->DeviceExtension; pdo_device_extension* pdode = vde->pdode; volume_child* vc; NTSTATUS Status; PIRP Irp2; vol_read_context context; PIO_STACK_LOCATION IrpSp, IrpSp2; TRACE("(%p, %p)\n", DeviceObject, Irp); ExAcquireResourceSharedLite(&pdode->child_lock, true); if (IsListEmpty(&pdode->children)) { ExReleaseResourceLite(&pdode->child_lock); Status = STATUS_INVALID_DEVICE_REQUEST; goto end; } vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry); if (vc->list_entry.Flink != &pdode->children) { // more than once device ExReleaseResourceLite(&pdode->child_lock); Status = STATUS_ACCESS_DENIED; goto end; } // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack Irp2 = IoAllocateIrp(vc->devobj->StackSize, false); if (!Irp2) { ERR("IoAllocateIrp failed\n"); ExReleaseResourceLite(&pdode->child_lock); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } IrpSp = IoGetCurrentIrpStackLocation(Irp); IrpSp2 = IoGetNextIrpStackLocation(Irp2); IrpSp2->MajorFunction = IRP_MJ_WRITE; IrpSp2->FileObject = vc->fileobj; if (vc->devobj->Flags & DO_BUFFERED_IO) { Irp2->AssociatedIrp.SystemBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); Irp2->Flags |= IRP_BUFFERED_IO; Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); } else if (vc->devobj->Flags & DO_DIRECT_IO) Irp2->MdlAddress = Irp->MdlAddress; else Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); IrpSp2->Parameters.Write.Length = IrpSp->Parameters.Write.Length; IrpSp2->Parameters.Write.ByteOffset.QuadPart = IrpSp->Parameters.Write.ByteOffset.QuadPart; KeInitializeEvent(&context.Event, NotificationEvent, false); Irp2->UserIosb = &context.iosb; IoSetCompletionRoutine(Irp2, vol_read_completion, &context, true, true, true); Status = IoCallDriver(vc->devobj, Irp2); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL); Status = context.iosb.Status; } ExReleaseResourceLite(&pdode->child_lock); Irp->IoStatus.Information = context.iosb.Information; end: Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS vol_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_SUCCESS; } NTSTATUS vol_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); Irp->IoStatus.Information = 0; return STATUS_SUCCESS; } NTSTATUS vol_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } static NTSTATUS vol_query_device_name(volume_device_extension* vde, PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PMOUNTDEV_NAME name; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) { Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); return STATUS_BUFFER_TOO_SMALL; } name = Irp->AssociatedIrp.SystemBuffer; name->NameLength = vde->name.Length; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) { Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); return STATUS_BUFFER_OVERFLOW; } RtlCopyMemory(name->Name, vde->name.Buffer, vde->name.Length); Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength; return STATUS_SUCCESS; } static NTSTATUS vol_query_unique_id(volume_device_extension* vde, PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); MOUNTDEV_UNIQUE_ID* mduid; pdo_device_extension* pdode; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) { Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); return STATUS_BUFFER_TOO_SMALL; } mduid = Irp->AssociatedIrp.SystemBuffer; mduid->UniqueIdLength = sizeof(BTRFS_UUID); if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength) { Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); return STATUS_BUFFER_OVERFLOW; } if (!vde->pdo) return STATUS_INVALID_PARAMETER; pdode = vde->pdode; RtlCopyMemory(mduid->UniqueId, &pdode->uuid, sizeof(BTRFS_UUID)); Irp->IoStatus.Information = offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength; return STATUS_SUCCESS; } static NTSTATUS vol_is_dynamic(PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); uint8_t* buf; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == 0 || !Irp->AssociatedIrp.SystemBuffer) return STATUS_INVALID_PARAMETER; buf = (uint8_t*)Irp->AssociatedIrp.SystemBuffer; *buf = 1; Irp->IoStatus.Information = 1; return STATUS_SUCCESS; } static NTSTATUS vol_check_verify(volume_device_extension* vde) { pdo_device_extension* pdode = vde->pdode; NTSTATUS Status; LIST_ENTRY* le; ExAcquireResourceSharedLite(&pdode->child_lock, true); le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); Status = dev_ioctl(vc->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, false, NULL); if (!NT_SUCCESS(Status)) goto end; le = le->Flink; } Status = STATUS_SUCCESS; end: ExReleaseResourceLite(&pdode->child_lock); return Status; } static NTSTATUS vol_get_disk_extents(volume_device_extension* vde, PIRP Irp) { pdo_device_extension* pdode = vde->pdode; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); LIST_ENTRY* le; ULONG num_extents = 0, i, max_extents = 1; NTSTATUS Status; VOLUME_DISK_EXTENTS *ext, *ext3; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS)) return STATUS_BUFFER_TOO_SMALL; ExAcquireResourceSharedLite(&pdode->child_lock, true); le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); VOLUME_DISK_EXTENTS ext2; Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &ext2, sizeof(VOLUME_DISK_EXTENTS), false, NULL); if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) { ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status); goto end; } num_extents += ext2.NumberOfDiskExtents; if (ext2.NumberOfDiskExtents > max_extents) max_extents = ext2.NumberOfDiskExtents; le = le->Flink; } ext = Irp->AssociatedIrp.SystemBuffer; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (num_extents * sizeof(DISK_EXTENT))) { Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]); ext->NumberOfDiskExtents = num_extents; Status = STATUS_BUFFER_OVERFLOW; goto end; } ext3 = ExAllocatePoolWithTag(PagedPool, offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), ALLOC_TAG); if (!ext3) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } i = 0; ext->NumberOfDiskExtents = 0; le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, ext3, (ULONG)offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), false, NULL); if (!NT_SUCCESS(Status)) { ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status); ExFreePool(ext3); goto end; } if (i + ext3->NumberOfDiskExtents > num_extents) { Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]); ext->NumberOfDiskExtents = i + ext3->NumberOfDiskExtents; Status = STATUS_BUFFER_OVERFLOW; ExFreePool(ext3); goto end; } RtlCopyMemory(&ext->Extents[i], ext3->Extents, sizeof(DISK_EXTENT) * ext3->NumberOfDiskExtents); i += ext3->NumberOfDiskExtents; le = le->Flink; } ExFreePool(ext3); Status = STATUS_SUCCESS; ext->NumberOfDiskExtents = i; Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (i * sizeof(DISK_EXTENT)); end: ExReleaseResourceLite(&pdode->child_lock); return Status; } static NTSTATUS vol_is_writable(volume_device_extension* vde) { pdo_device_extension* pdode = vde->pdode; NTSTATUS Status; LIST_ENTRY* le; bool writable = false; ExAcquireResourceSharedLite(&pdode->child_lock, true); le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); Status = dev_ioctl(vc->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, true, NULL); if (NT_SUCCESS(Status)) { writable = true; break; } else if (Status != STATUS_MEDIA_WRITE_PROTECTED) goto end; le = le->Flink; } Status = writable ? STATUS_SUCCESS : STATUS_MEDIA_WRITE_PROTECTED; end: ExReleaseResourceLite(&pdode->child_lock); return STATUS_SUCCESS; } static NTSTATUS vol_get_length(volume_device_extension* vde, PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); pdo_device_extension* pdode = vde->pdode; GET_LENGTH_INFORMATION* gli; LIST_ENTRY* le; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; gli = (GET_LENGTH_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; gli->Length.QuadPart = 0; ExAcquireResourceSharedLite(&pdode->child_lock, true); le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); gli->Length.QuadPart += vc->size; le = le->Flink; } ExReleaseResourceLite(&pdode->child_lock); Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); return STATUS_SUCCESS; } static NTSTATUS vol_get_drive_geometry(PDEVICE_OBJECT DeviceObject, PIRP Irp) { volume_device_extension* vde = DeviceObject->DeviceExtension; pdo_device_extension* pdode = vde->pdode; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); DISK_GEOMETRY* geom; uint64_t length; LIST_ENTRY* le; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) return STATUS_BUFFER_TOO_SMALL; length = 0; ExAcquireResourceSharedLite(&pdode->child_lock, true); le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); length += vc->size; le = le->Flink; } ExReleaseResourceLite(&pdode->child_lock); geom = (DISK_GEOMETRY*)Irp->AssociatedIrp.SystemBuffer; geom->BytesPerSector = DeviceObject->SectorSize == 0 ? 0x200 : DeviceObject->SectorSize; geom->SectorsPerTrack = 0x3f; geom->TracksPerCylinder = 0xff; geom->Cylinders.QuadPart = length / (UInt32x32To64(geom->TracksPerCylinder, geom->SectorsPerTrack) * geom->BytesPerSector); geom->MediaType = DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA ? RemovableMedia : FixedMedia; Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); return STATUS_SUCCESS; } static NTSTATUS vol_get_gpt_attributes(PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); VOLUME_GET_GPT_ATTRIBUTES_INFORMATION* vggai; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; vggai = (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; vggai->GptAttributes = 0; Irp->IoStatus.Information = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION); return STATUS_SUCCESS; } static NTSTATUS vol_get_device_number(volume_device_extension* vde, PIRP Irp) { pdo_device_extension* pdode = vde->pdode; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); volume_child* vc; STORAGE_DEVICE_NUMBER* sdn; // If only one device, return its disk number. This is needed for ejection to work. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DEVICE_NUMBER)) return STATUS_BUFFER_TOO_SMALL; ExAcquireResourceSharedLite(&pdode->child_lock, true); if (IsListEmpty(&pdode->children) || pdode->num_children > 1) { ExReleaseResourceLite(&pdode->child_lock); return STATUS_INVALID_DEVICE_REQUEST; } vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry); if (vc->disk_num == 0xffffffff) { ExReleaseResourceLite(&pdode->child_lock); return STATUS_INVALID_DEVICE_REQUEST; } sdn = (STORAGE_DEVICE_NUMBER*)Irp->AssociatedIrp.SystemBuffer; sdn->DeviceType = FILE_DEVICE_DISK; sdn->DeviceNumber = vc->disk_num; sdn->PartitionNumber = vc->part_num; ExReleaseResourceLite(&pdode->child_lock); Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); return STATUS_SUCCESS; } _Function_class_(IO_COMPLETION_ROUTINE) static NTSTATUS __stdcall vol_ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { KEVENT* event = conptr; UNUSED(DeviceObject); UNUSED(Irp); KeSetEvent(event, 0, false); return STATUS_MORE_PROCESSING_REQUIRED; } static NTSTATUS vol_ioctl_passthrough(volume_device_extension* vde, PIRP Irp) { NTSTATUS Status; volume_child* vc; PIRP Irp2; PIO_STACK_LOCATION IrpSp, IrpSp2; KEVENT Event; pdo_device_extension* pdode = vde->pdode; TRACE("(%p, %p)\n", vde, Irp); ExAcquireResourceSharedLite(&pdode->child_lock, true); if (IsListEmpty(&pdode->children)) { ExReleaseResourceLite(&pdode->child_lock); return STATUS_INVALID_DEVICE_REQUEST; } vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry); if (vc->list_entry.Flink != &pdode->children) { // more than one device ExReleaseResourceLite(&pdode->child_lock); return STATUS_INVALID_DEVICE_REQUEST; } Irp2 = IoAllocateIrp(vc->devobj->StackSize, false); if (!Irp2) { ERR("IoAllocateIrp failed\n"); ExReleaseResourceLite(&pdode->child_lock); return STATUS_INSUFFICIENT_RESOURCES; } IrpSp = IoGetCurrentIrpStackLocation(Irp); IrpSp2 = IoGetNextIrpStackLocation(Irp2); IrpSp2->MajorFunction = IrpSp->MajorFunction; IrpSp2->MinorFunction = IrpSp->MinorFunction; IrpSp2->FileObject = vc->fileobj; IrpSp2->Parameters.DeviceIoControl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; IrpSp2->Parameters.DeviceIoControl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; IrpSp2->Parameters.DeviceIoControl.IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; IrpSp2->Parameters.DeviceIoControl.Type3InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; Irp2->AssociatedIrp.SystemBuffer = Irp->AssociatedIrp.SystemBuffer; Irp2->MdlAddress = Irp->MdlAddress; Irp2->UserBuffer = Irp->UserBuffer; Irp2->Flags = Irp->Flags; KeInitializeEvent(&Event, NotificationEvent, false); IoSetCompletionRoutine(Irp2, vol_ioctl_completion, &Event, true, true, true); Status = IoCallDriver(vc->devobj, Irp2); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, false, NULL); Status = Irp2->IoStatus.Status; } Irp->IoStatus.Status = Irp2->IoStatus.Status; Irp->IoStatus.Information = Irp2->IoStatus.Information; ExReleaseResourceLite(&pdode->child_lock); IoFreeIrp(Irp2); return Status; } static NTSTATUS vol_query_stable_guid(volume_device_extension* vde, PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); MOUNTDEV_STABLE_GUID* mdsg; pdo_device_extension* pdode; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID)) { Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID); return STATUS_BUFFER_TOO_SMALL; } mdsg = Irp->AssociatedIrp.SystemBuffer; if (!vde->pdo) return STATUS_INVALID_PARAMETER; pdode = vde->pdode; RtlCopyMemory(&mdsg->StableGuid, &pdode->uuid, sizeof(BTRFS_UUID)); Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID); return STATUS_SUCCESS; } NTSTATUS vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { volume_device_extension* vde = DeviceObject->DeviceExtension; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); TRACE("(%p, %p)\n", DeviceObject, Irp); Irp->IoStatus.Information = 0; switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: return vol_query_device_name(vde, Irp); case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: return vol_query_unique_id(vde, Irp); case IOCTL_STORAGE_GET_DEVICE_NUMBER: return vol_get_device_number(vde, Irp); case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: TRACE("unhandled control code IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME\n"); break; case IOCTL_MOUNTDEV_QUERY_STABLE_GUID: return vol_query_stable_guid(vde, Irp); case IOCTL_MOUNTDEV_LINK_CREATED: TRACE("unhandled control code IOCTL_MOUNTDEV_LINK_CREATED\n"); break; case IOCTL_VOLUME_GET_GPT_ATTRIBUTES: return vol_get_gpt_attributes(Irp); case IOCTL_VOLUME_IS_DYNAMIC: return vol_is_dynamic(Irp); case IOCTL_VOLUME_ONLINE: Irp->IoStatus.Information = 0; return STATUS_SUCCESS; case IOCTL_VOLUME_POST_ONLINE: Irp->IoStatus.Information = 0; return STATUS_SUCCESS; case IOCTL_DISK_GET_DRIVE_GEOMETRY: return vol_get_drive_geometry(DeviceObject, Irp); case IOCTL_DISK_IS_WRITABLE: return vol_is_writable(vde); case IOCTL_DISK_GET_LENGTH_INFO: return vol_get_length(vde, Irp); case IOCTL_STORAGE_CHECK_VERIFY: case IOCTL_DISK_CHECK_VERIFY: return vol_check_verify(vde); case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS: return vol_get_disk_extents(vde, Irp); default: { // pass ioctl through if only one child device ULONG code = IrpSp->Parameters.DeviceIoControl.IoControlCode; NTSTATUS Status = vol_ioctl_passthrough(vde, Irp); #ifdef __REACTOS__ &code; #endif if (NT_SUCCESS(Status)) TRACE("passing through ioctl %lx (returning %08lx)\n", code, Status); else WARN("passing through ioctl %lx (returning %08lx)\n", code, Status); return Status; } } return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS vol_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { TRACE("(%p, %p)\n", DeviceObject, Irp); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) { NTSTATUS Status; ULONG mmdltsize; MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt; MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli; mmdltsize = (ULONG)offsetof(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName[0]) + devpath->Length; mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG); if (!mmdlt) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } mmdlt->DeviceNameLength = devpath->Length; RtlCopyMemory(&mmdlt->DeviceName, devpath->Buffer, devpath->Length); TRACE("mmdlt = %.*S\n", (int)(mmdlt->DeviceNameLength / sizeof(WCHAR)), mmdlt->DeviceName); Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER, mmdlt, mmdltsize, &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), false, NULL); if (!NT_SUCCESS(Status)) ERR("IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER returned %08lx\n", Status); else TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter); ExFreePool(mmdlt); return Status; } _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE) NTSTATUS __stdcall pnp_removal(PVOID NotificationStructure, PVOID Context) { TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = (TARGET_DEVICE_REMOVAL_NOTIFICATION*)NotificationStructure; pdo_device_extension* pdode = (pdo_device_extension*)Context; if (RtlCompareMemory(&tdrn->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE, sizeof(GUID)) == sizeof(GUID)) { TRACE("GUID_TARGET_DEVICE_QUERY_REMOVE\n"); if (pdode->vde && pdode->vde->mounted_device) return pnp_query_remove_device(pdode->vde->mounted_device, NULL); } return STATUS_SUCCESS; } static bool allow_degraded_mount(BTRFS_UUID* uuid) { HANDLE h; NTSTATUS Status; OBJECT_ATTRIBUTES oa; UNICODE_STRING path, adus; uint32_t degraded = mount_allow_degraded; ULONG i, j, kvfilen, retlen; KEY_VALUE_FULL_INFORMATION* kvfi; path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR)); path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); if (!path.Buffer) { ERR("out of memory\n"); return false; } RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length); i = registry_path.Length / sizeof(WCHAR); path.Buffer[i] = '\\'; i++; for (j = 0; j < 16; j++) { path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4); path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF); i += 2; if (j == 3 || j == 5 || j == 7 || j == 9) { path.Buffer[i] = '-'; i++; } } InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); kvfilen = (ULONG)offsetof(KEY_VALUE_FULL_INFORMATION, Name[0]) + (255 * sizeof(WCHAR)); kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); if (!kvfi) { ERR("out of memory\n"); ExFreePool(path.Buffer); return false; } Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) goto end; else if (!NT_SUCCESS(Status)) { ERR("ZwOpenKey returned %08lx\n", Status); goto end; } adus.Buffer = L"AllowDegraded"; adus.Length = adus.MaximumLength = sizeof(adus.Buffer) - sizeof(WCHAR); if (NT_SUCCESS(ZwQueryValueKey(h, &adus, KeyValueFullInformation, kvfi, kvfilen, &retlen))) { if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(uint32_t)) { uint32_t* val = (uint32_t*)((uint8_t*)kvfi + kvfi->DataOffset); degraded = *val; } } ZwClose(h); end: ExFreePool(kvfi); ExFreePool(path.Buffer); return degraded; } typedef struct { LIST_ENTRY list_entry; UNICODE_STRING name; NTSTATUS Status; BTRFS_UUID uuid; } drive_letter_removal; static void drive_letter_callback2(pdo_device_extension* pdode, PDEVICE_OBJECT mountmgr) { LIST_ENTRY* le; LIST_ENTRY dlrlist; InitializeListHead(&dlrlist); ExAcquireResourceExclusiveLite(&pdode->child_lock, true); le = pdode->children.Flink; while (le != &pdode->children) { drive_letter_removal* dlr; volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); dlr = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_removal), ALLOC_TAG); if (!dlr) { ERR("out of memory\n"); while (!IsListEmpty(&dlrlist)) { dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); ExFreePool(dlr->name.Buffer); ExFreePool(dlr); } ExReleaseResourceLite(&pdode->child_lock); return; } dlr->name.Length = dlr->name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR)); dlr->name.Buffer = ExAllocatePoolWithTag(PagedPool, dlr->name.Length, ALLOC_TAG); if (!dlr->name.Buffer) { ERR("out of memory\n"); ExFreePool(dlr); while (!IsListEmpty(&dlrlist)) { dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); ExFreePool(dlr->name.Buffer); ExFreePool(dlr); } ExReleaseResourceLite(&pdode->child_lock); return; } RtlCopyMemory(dlr->name.Buffer, L"\\??", 3 * sizeof(WCHAR)); RtlCopyMemory(&dlr->name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length); dlr->uuid = vc->uuid; InsertTailList(&dlrlist, &dlr->list_entry); le = le->Flink; } ExReleaseResourceLite(&pdode->child_lock); le = dlrlist.Flink; while (le != &dlrlist) { drive_letter_removal* dlr = CONTAINING_RECORD(le, drive_letter_removal, list_entry); dlr->Status = remove_drive_letter(mountmgr, &dlr->name); if (!NT_SUCCESS(dlr->Status) && dlr->Status != STATUS_NOT_FOUND) WARN("remove_drive_letter returned %08lx\n", dlr->Status); le = le->Flink; } // set vc->had_drive_letter ExAcquireResourceExclusiveLite(&pdode->child_lock, true); while (!IsListEmpty(&dlrlist)) { drive_letter_removal* dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); if (RtlCompareMemory(&vc->uuid, &dlr->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { vc->had_drive_letter = NT_SUCCESS(dlr->Status); break; } le = le->Flink; } ExFreePool(dlr->name.Buffer); ExFreePool(dlr); } ExReleaseResourceLite(&pdode->child_lock); } _Function_class_(IO_WORKITEM_ROUTINE) static void __stdcall drive_letter_callback(pdo_device_extension* pdode) { NTSTATUS Status; UNICODE_STRING mmdevpath; PDEVICE_OBJECT mountmgr; PFILE_OBJECT mountmgrfo; RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr); if (!NT_SUCCESS(Status)) { ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); return; } drive_letter_callback2(pdode, mountmgr); ObDereferenceObject(mountmgrfo); } void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length, ULONG disk_num, ULONG part_num) { NTSTATUS Status; LIST_ENTRY* le; PDEVICE_OBJECT DeviceObject; volume_child* vc; PFILE_OBJECT FileObject; UNICODE_STRING devpath2; bool inserted = false, new_pdo = false; pdo_device_extension* pdode = NULL; PDEVICE_OBJECT pdo = NULL; bool process_drive_letters = false; if (devpath->Length == 0) return; ExAcquireResourceExclusiveLite(&pdo_list_lock, true); le = pdo_list.Flink; while (le != &pdo_list) { pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry); if (RtlCompareMemory(&pdode2->uuid, &sb->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { pdode = pdode2; break; } le = le->Flink; } Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); if (!NT_SUCCESS(Status)) { ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); ExReleaseResourceLite(&pdo_list_lock); return; } if (!pdode) { if (no_pnp) { Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF, NULL, NULL, 0, &pdo); if (!NT_SUCCESS(Status)) { ERR("IoReportDetectedDevice returned %08lx\n", Status); ExReleaseResourceLite(&pdo_list_lock); return; } pdode = ExAllocatePoolWithTag(NonPagedPool, sizeof(pdo_device_extension), ALLOC_TAG); if (!pdode) { ERR("out of memory\n"); ExReleaseResourceLite(&pdo_list_lock); return; } } else { Status = IoCreateDevice(drvobj, sizeof(pdo_device_extension), NULL, FILE_DEVICE_DISK, FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, false, &pdo); if (!NT_SUCCESS(Status)) { ERR("IoCreateDevice returned %08lx\n", Status); ExReleaseResourceLite(&pdo_list_lock); goto fail; } pdo->Flags |= DO_BUS_ENUMERATED_DEVICE; pdode = pdo->DeviceExtension; } RtlZeroMemory(pdode, sizeof(pdo_device_extension)); pdode->type = VCB_TYPE_PDO; pdode->pdo = pdo; pdode->uuid = sb->uuid; ExInitializeResourceLite(&pdode->child_lock); InitializeListHead(&pdode->children); pdode->num_children = sb->num_devices; pdode->children_loaded = 0; pdo->Flags &= ~DO_DEVICE_INITIALIZING; pdo->SectorSize = (USHORT)sb->sector_size; ExAcquireResourceExclusiveLite(&pdode->child_lock, true); new_pdo = true; } else { ExAcquireResourceExclusiveLite(&pdode->child_lock, true); ExConvertExclusiveToSharedLite(&pdo_list_lock); le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry); if (RtlCompareMemory(&vc2->uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { // duplicate, ignore ExReleaseResourceLite(&pdode->child_lock); ExReleaseResourceLite(&pdo_list_lock); goto fail; } le = le->Flink; } } vc = ExAllocatePoolWithTag(PagedPool, sizeof(volume_child), ALLOC_TAG); if (!vc) { ERR("out of memory\n"); ExReleaseResourceLite(&pdode->child_lock); ExReleaseResourceLite(&pdo_list_lock); goto fail; } vc->uuid = sb->dev_item.device_uuid; vc->devid = sb->dev_item.dev_id; vc->generation = sb->generation; vc->notification_entry = NULL; vc->boot_volume = false; Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, FileObject, drvobj, pnp_removal, pdode, &vc->notification_entry); if (!NT_SUCCESS(Status)) WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status); vc->devobj = DeviceObject; vc->fileobj = FileObject; devpath2 = *devpath; // The PNP path sometimes begins \\?\ and sometimes \??\. We need to remove this prefix // so we can compare properly if the device is removed. if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') && devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') { devpath2.Buffer = &devpath2.Buffer[3]; devpath2.Length -= 3 * sizeof(WCHAR); devpath2.MaximumLength -= 3 * sizeof(WCHAR); } vc->pnp_name.Length = vc->pnp_name.MaximumLength = devpath2.Length; vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, devpath2.Length, ALLOC_TAG); if (vc->pnp_name.Buffer) RtlCopyMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length); else { ERR("out of memory\n"); vc->pnp_name.Length = vc->pnp_name.MaximumLength = 0; } vc->size = length; vc->seeding = sb->flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? true : false; vc->disk_num = disk_num; vc->part_num = part_num; vc->had_drive_letter = false; le = pdode->children.Flink; while (le != &pdode->children) { volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry); if (vc2->generation < vc->generation) { if (le == pdode->children.Flink) pdode->num_children = sb->num_devices; InsertHeadList(vc2->list_entry.Blink, &vc->list_entry); inserted = true; break; } le = le->Flink; } if (!inserted) InsertTailList(&pdode->children, &vc->list_entry); pdode->children_loaded++; if (pdode->vde && pdode->vde->mounted_device) { device_extension* Vcb = pdode->vde->mounted_device->DeviceExtension; ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); le = Vcb->devices.Flink; while (le != &Vcb->devices) { device* dev = CONTAINING_RECORD(le, device, list_entry); if (!dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { dev->devobj = DeviceObject; dev->disk_num = disk_num; dev->part_num = part_num; init_device(Vcb, dev, false); break; } le = le->Flink; } ExReleaseResourceLite(&Vcb->tree_lock); } if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { pdode->removable = true; if (pdode->vde && pdode->vde->device) pdode->vde->device->Characteristics |= FILE_REMOVABLE_MEDIA; } if (pdode->num_children == pdode->children_loaded || (pdode->children_loaded == 1 && allow_degraded_mount(&sb->uuid))) { if ((!new_pdo || !no_pnp) && pdode->vde) { Status = IoSetDeviceInterfaceState(&pdode->vde->bus_name, true); if (!NT_SUCCESS(Status)) WARN("IoSetDeviceInterfaceState returned %08lx\n", Status); } process_drive_letters = true; } ExReleaseResourceLite(&pdode->child_lock); if (new_pdo) InsertTailList(&pdo_list, &pdode->list_entry); ExReleaseResourceLite(&pdo_list_lock); if (process_drive_letters) drive_letter_callback(pdode); if (new_pdo) { if (RtlCompareMemory(&sb->uuid, &boot_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) boot_add_device(pdo); else if (no_pnp) AddDevice(drvobj, pdo); else { bus_device_extension* bde = busobj->DeviceExtension; IoInvalidateDeviceRelations(bde->buspdo, BusRelations); } } return; fail: ObDereferenceObject(FileObject); }