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 static NTSTATUS vol_query_device_name(volume_device_extension* vde, PIRP Irp) { 307 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 308 PMOUNTDEV_NAME name; 309 310 if (IrpSp->FileObject && IrpSp->FileObject->FsContext) 311 return STATUS_INVALID_PARAMETER; 312 313 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) { 314 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); 315 return STATUS_BUFFER_TOO_SMALL; 316 } 317 318 name = Irp->AssociatedIrp.SystemBuffer; 319 name->NameLength = vde->name.Length; 320 321 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) { 322 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); 323 return STATUS_BUFFER_OVERFLOW; 324 } 325 326 RtlCopyMemory(name->Name, vde->name.Buffer, vde->name.Length); 327 328 Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength; 329 330 return STATUS_SUCCESS; 331 } 332 333 static NTSTATUS vol_query_unique_id(volume_device_extension* vde, PIRP Irp) { 334 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 335 MOUNTDEV_UNIQUE_ID* mduid; 336 pdo_device_extension* pdode; 337 338 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) { 339 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); 340 return STATUS_BUFFER_TOO_SMALL; 341 } 342 343 mduid = Irp->AssociatedIrp.SystemBuffer; 344 mduid->UniqueIdLength = sizeof(BTRFS_UUID); 345 346 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength) { 347 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); 348 return STATUS_BUFFER_OVERFLOW; 349 } 350 351 if (!vde->pdo) 352 return STATUS_INVALID_PARAMETER; 353 354 pdode = vde->pdode; 355 356 RtlCopyMemory(mduid->UniqueId, &pdode->uuid, sizeof(BTRFS_UUID)); 357 358 Irp->IoStatus.Information = offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength; 359 360 return STATUS_SUCCESS; 361 } 362 363 static NTSTATUS vol_is_dynamic(PIRP Irp) { 364 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 365 uint8_t* buf; 366 367 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == 0 || !Irp->AssociatedIrp.SystemBuffer) 368 return STATUS_INVALID_PARAMETER; 369 370 buf = (uint8_t*)Irp->AssociatedIrp.SystemBuffer; 371 372 *buf = 1; 373 374 Irp->IoStatus.Information = 1; 375 376 return STATUS_SUCCESS; 377 } 378 379 static NTSTATUS vol_check_verify(volume_device_extension* vde) { 380 pdo_device_extension* pdode = vde->pdode; 381 NTSTATUS Status; 382 LIST_ENTRY* le; 383 384 ExAcquireResourceSharedLite(&pdode->child_lock, true); 385 386 le = pdode->children.Flink; 387 while (le != &pdode->children) { 388 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 389 390 Status = dev_ioctl(vc->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, false, NULL); 391 if (!NT_SUCCESS(Status)) 392 goto end; 393 394 le = le->Flink; 395 } 396 397 Status = STATUS_SUCCESS; 398 399 end: 400 ExReleaseResourceLite(&pdode->child_lock); 401 402 return Status; 403 } 404 405 static NTSTATUS vol_get_disk_extents(volume_device_extension* vde, PIRP Irp) { 406 pdo_device_extension* pdode = vde->pdode; 407 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 408 LIST_ENTRY* le; 409 ULONG num_extents = 0, i, max_extents = 1; 410 NTSTATUS Status; 411 VOLUME_DISK_EXTENTS *ext, *ext3; 412 413 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS)) 414 return STATUS_BUFFER_TOO_SMALL; 415 416 ExAcquireResourceSharedLite(&pdode->child_lock, true); 417 418 le = pdode->children.Flink; 419 while (le != &pdode->children) { 420 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 421 VOLUME_DISK_EXTENTS ext2; 422 423 Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &ext2, sizeof(VOLUME_DISK_EXTENTS), false, NULL); 424 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) { 425 ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status); 426 goto end; 427 } 428 429 num_extents += ext2.NumberOfDiskExtents; 430 431 if (ext2.NumberOfDiskExtents > max_extents) 432 max_extents = ext2.NumberOfDiskExtents; 433 434 le = le->Flink; 435 } 436 437 ext = Irp->AssociatedIrp.SystemBuffer; 438 439 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (num_extents * sizeof(DISK_EXTENT))) { 440 Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]); 441 ext->NumberOfDiskExtents = num_extents; 442 Status = STATUS_BUFFER_OVERFLOW; 443 goto end; 444 } 445 446 ext3 = ExAllocatePoolWithTag(PagedPool, offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), ALLOC_TAG); 447 if (!ext3) { 448 ERR("out of memory\n"); 449 Status = STATUS_INSUFFICIENT_RESOURCES; 450 goto end; 451 } 452 453 i = 0; 454 ext->NumberOfDiskExtents = 0; 455 456 le = pdode->children.Flink; 457 while (le != &pdode->children) { 458 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 459 460 Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, ext3, 461 (ULONG)offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), false, NULL); 462 if (!NT_SUCCESS(Status)) { 463 ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status); 464 ExFreePool(ext3); 465 goto end; 466 } 467 468 if (i + ext3->NumberOfDiskExtents > num_extents) { 469 Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]); 470 ext->NumberOfDiskExtents = i + ext3->NumberOfDiskExtents; 471 Status = STATUS_BUFFER_OVERFLOW; 472 ExFreePool(ext3); 473 goto end; 474 } 475 476 RtlCopyMemory(&ext->Extents[i], ext3->Extents, sizeof(DISK_EXTENT) * ext3->NumberOfDiskExtents); 477 i += ext3->NumberOfDiskExtents; 478 479 le = le->Flink; 480 } 481 482 ExFreePool(ext3); 483 484 Status = STATUS_SUCCESS; 485 486 ext->NumberOfDiskExtents = i; 487 Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (i * sizeof(DISK_EXTENT)); 488 489 end: 490 ExReleaseResourceLite(&pdode->child_lock); 491 492 return Status; 493 } 494 495 static NTSTATUS vol_is_writable(volume_device_extension* vde) { 496 pdo_device_extension* pdode = vde->pdode; 497 NTSTATUS Status; 498 LIST_ENTRY* le; 499 bool writable = false; 500 501 ExAcquireResourceSharedLite(&pdode->child_lock, true); 502 503 le = pdode->children.Flink; 504 while (le != &pdode->children) { 505 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 506 507 Status = dev_ioctl(vc->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, true, NULL); 508 509 if (NT_SUCCESS(Status)) { 510 writable = true; 511 break; 512 } else if (Status != STATUS_MEDIA_WRITE_PROTECTED) 513 goto end; 514 515 le = le->Flink; 516 } 517 518 Status = writable ? STATUS_SUCCESS : STATUS_MEDIA_WRITE_PROTECTED; 519 520 end: 521 ExReleaseResourceLite(&pdode->child_lock); 522 523 return STATUS_SUCCESS; 524 } 525 526 static NTSTATUS vol_get_length(volume_device_extension* vde, PIRP Irp) { 527 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 528 pdo_device_extension* pdode = vde->pdode; 529 GET_LENGTH_INFORMATION* gli; 530 LIST_ENTRY* le; 531 532 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION)) 533 return STATUS_BUFFER_TOO_SMALL; 534 535 gli = (GET_LENGTH_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; 536 537 gli->Length.QuadPart = 0; 538 539 ExAcquireResourceSharedLite(&pdode->child_lock, true); 540 541 le = pdode->children.Flink; 542 while (le != &pdode->children) { 543 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 544 545 gli->Length.QuadPart += vc->size; 546 547 le = le->Flink; 548 } 549 550 ExReleaseResourceLite(&pdode->child_lock); 551 552 Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); 553 554 return STATUS_SUCCESS; 555 } 556 557 static NTSTATUS vol_get_drive_geometry(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 558 volume_device_extension* vde = DeviceObject->DeviceExtension; 559 pdo_device_extension* pdode = vde->pdode; 560 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 561 DISK_GEOMETRY* geom; 562 uint64_t length; 563 LIST_ENTRY* le; 564 565 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) 566 return STATUS_BUFFER_TOO_SMALL; 567 568 length = 0; 569 570 ExAcquireResourceSharedLite(&pdode->child_lock, true); 571 572 le = pdode->children.Flink; 573 while (le != &pdode->children) { 574 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 575 576 length += vc->size; 577 578 le = le->Flink; 579 } 580 581 ExReleaseResourceLite(&pdode->child_lock); 582 583 geom = (DISK_GEOMETRY*)Irp->AssociatedIrp.SystemBuffer; 584 geom->BytesPerSector = DeviceObject->SectorSize == 0 ? 0x200 : DeviceObject->SectorSize; 585 geom->SectorsPerTrack = 0x3f; 586 geom->TracksPerCylinder = 0xff; 587 geom->Cylinders.QuadPart = length / (UInt32x32To64(geom->TracksPerCylinder, geom->SectorsPerTrack) * geom->BytesPerSector); 588 geom->MediaType = DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA ? RemovableMedia : FixedMedia; 589 590 Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); 591 592 return STATUS_SUCCESS; 593 } 594 595 static NTSTATUS vol_get_gpt_attributes(PIRP Irp) { 596 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 597 VOLUME_GET_GPT_ATTRIBUTES_INFORMATION* vggai; 598 599 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION)) 600 return STATUS_BUFFER_TOO_SMALL; 601 602 vggai = (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; 603 604 vggai->GptAttributes = 0; 605 606 Irp->IoStatus.Information = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION); 607 608 return STATUS_SUCCESS; 609 } 610 611 static NTSTATUS vol_get_device_number(volume_device_extension* vde, PIRP Irp) { 612 pdo_device_extension* pdode = vde->pdode; 613 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 614 volume_child* vc; 615 STORAGE_DEVICE_NUMBER* sdn; 616 617 // If only one device, return its disk number. This is needed for ejection to work. 618 619 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DEVICE_NUMBER)) 620 return STATUS_BUFFER_TOO_SMALL; 621 622 ExAcquireResourceSharedLite(&pdode->child_lock, true); 623 624 if (IsListEmpty(&pdode->children) || pdode->num_children > 1) { 625 ExReleaseResourceLite(&pdode->child_lock); 626 return STATUS_INVALID_DEVICE_REQUEST; 627 } 628 629 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry); 630 631 if (vc->disk_num == 0xffffffff) { 632 ExReleaseResourceLite(&pdode->child_lock); 633 return STATUS_INVALID_DEVICE_REQUEST; 634 } 635 636 sdn = (STORAGE_DEVICE_NUMBER*)Irp->AssociatedIrp.SystemBuffer; 637 638 sdn->DeviceType = FILE_DEVICE_DISK; 639 sdn->DeviceNumber = vc->disk_num; 640 sdn->PartitionNumber = vc->part_num; 641 642 ExReleaseResourceLite(&pdode->child_lock); 643 644 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); 645 646 return STATUS_SUCCESS; 647 } 648 649 _Function_class_(IO_COMPLETION_ROUTINE) 650 static NTSTATUS __stdcall vol_ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { 651 KEVENT* event = conptr; 652 653 UNUSED(DeviceObject); 654 UNUSED(Irp); 655 656 KeSetEvent(event, 0, false); 657 658 return STATUS_MORE_PROCESSING_REQUIRED; 659 } 660 661 static NTSTATUS vol_ioctl_passthrough(volume_device_extension* vde, PIRP Irp) { 662 NTSTATUS Status; 663 volume_child* vc; 664 PIRP Irp2; 665 PIO_STACK_LOCATION IrpSp, IrpSp2; 666 KEVENT Event; 667 pdo_device_extension* pdode = vde->pdode; 668 669 TRACE("(%p, %p)\n", vde, Irp); 670 671 ExAcquireResourceSharedLite(&pdode->child_lock, true); 672 673 if (IsListEmpty(&pdode->children)) { 674 ExReleaseResourceLite(&pdode->child_lock); 675 return STATUS_INVALID_DEVICE_REQUEST; 676 } 677 678 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry); 679 680 if (vc->list_entry.Flink != &pdode->children) { // more than one device 681 ExReleaseResourceLite(&pdode->child_lock); 682 return STATUS_INVALID_DEVICE_REQUEST; 683 } 684 685 Irp2 = IoAllocateIrp(vc->devobj->StackSize, false); 686 687 if (!Irp2) { 688 ERR("IoAllocateIrp failed\n"); 689 ExReleaseResourceLite(&pdode->child_lock); 690 return STATUS_INSUFFICIENT_RESOURCES; 691 } 692 693 IrpSp = IoGetCurrentIrpStackLocation(Irp); 694 IrpSp2 = IoGetNextIrpStackLocation(Irp2); 695 696 IrpSp2->MajorFunction = IrpSp->MajorFunction; 697 IrpSp2->MinorFunction = IrpSp->MinorFunction; 698 IrpSp2->FileObject = vc->fileobj; 699 700 IrpSp2->Parameters.DeviceIoControl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; 701 IrpSp2->Parameters.DeviceIoControl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; 702 IrpSp2->Parameters.DeviceIoControl.IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; 703 IrpSp2->Parameters.DeviceIoControl.Type3InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; 704 705 Irp2->AssociatedIrp.SystemBuffer = Irp->AssociatedIrp.SystemBuffer; 706 Irp2->MdlAddress = Irp->MdlAddress; 707 Irp2->UserBuffer = Irp->UserBuffer; 708 Irp2->Flags = Irp->Flags; 709 710 KeInitializeEvent(&Event, NotificationEvent, false); 711 712 IoSetCompletionRoutine(Irp2, vol_ioctl_completion, &Event, true, true, true); 713 714 Status = IoCallDriver(vc->devobj, Irp2); 715 716 if (Status == STATUS_PENDING) { 717 KeWaitForSingleObject(&Event, Executive, KernelMode, false, NULL); 718 Status = Irp2->IoStatus.Status; 719 } 720 721 Irp->IoStatus.Status = Irp2->IoStatus.Status; 722 Irp->IoStatus.Information = Irp2->IoStatus.Information; 723 724 ExReleaseResourceLite(&pdode->child_lock); 725 726 IoFreeIrp(Irp2); 727 728 return Status; 729 } 730 731 static NTSTATUS vol_query_stable_guid(volume_device_extension* vde, PIRP Irp) { 732 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 733 MOUNTDEV_STABLE_GUID* mdsg; 734 pdo_device_extension* pdode; 735 736 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID)) { 737 Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID); 738 return STATUS_BUFFER_TOO_SMALL; 739 } 740 741 mdsg = Irp->AssociatedIrp.SystemBuffer; 742 743 if (!vde->pdo) 744 return STATUS_INVALID_PARAMETER; 745 746 pdode = vde->pdode; 747 748 RtlCopyMemory(&mdsg->StableGuid, &pdode->uuid, sizeof(BTRFS_UUID)); 749 750 Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID); 751 752 return STATUS_SUCCESS; 753 } 754 755 NTSTATUS vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 756 volume_device_extension* vde = DeviceObject->DeviceExtension; 757 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 758 759 TRACE("(%p, %p)\n", DeviceObject, Irp); 760 761 Irp->IoStatus.Information = 0; 762 763 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { 764 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: 765 return vol_query_device_name(vde, Irp); 766 767 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: 768 return vol_query_unique_id(vde, Irp); 769 770 case IOCTL_STORAGE_GET_DEVICE_NUMBER: 771 return vol_get_device_number(vde, Irp); 772 773 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: 774 TRACE("unhandled control code IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME\n"); 775 break; 776 777 case IOCTL_MOUNTDEV_QUERY_STABLE_GUID: 778 return vol_query_stable_guid(vde, Irp); 779 780 case IOCTL_MOUNTDEV_LINK_CREATED: 781 TRACE("unhandled control code IOCTL_MOUNTDEV_LINK_CREATED\n"); 782 break; 783 784 case IOCTL_VOLUME_GET_GPT_ATTRIBUTES: 785 return vol_get_gpt_attributes(Irp); 786 787 case IOCTL_VOLUME_IS_DYNAMIC: 788 return vol_is_dynamic(Irp); 789 790 case IOCTL_VOLUME_ONLINE: 791 Irp->IoStatus.Information = 0; 792 return STATUS_SUCCESS; 793 794 case IOCTL_VOLUME_POST_ONLINE: 795 Irp->IoStatus.Information = 0; 796 return STATUS_SUCCESS; 797 798 case IOCTL_DISK_GET_DRIVE_GEOMETRY: 799 return vol_get_drive_geometry(DeviceObject, Irp); 800 801 case IOCTL_DISK_IS_WRITABLE: 802 return vol_is_writable(vde); 803 804 case IOCTL_DISK_GET_LENGTH_INFO: 805 return vol_get_length(vde, Irp); 806 807 case IOCTL_STORAGE_CHECK_VERIFY: 808 case IOCTL_DISK_CHECK_VERIFY: 809 return vol_check_verify(vde); 810 811 case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS: 812 return vol_get_disk_extents(vde, Irp); 813 814 default: { // pass ioctl through if only one child device 815 NTSTATUS Status = vol_ioctl_passthrough(vde, Irp); 816 #ifdef _DEBUG 817 ULONG code = IrpSp->Parameters.DeviceIoControl.IoControlCode; 818 819 if (NT_SUCCESS(Status)) 820 TRACE("passing through ioctl %lx (returning %08lx)\n", code, Status); 821 else 822 WARN("passing through ioctl %lx (returning %08lx)\n", code, Status); 823 #endif 824 825 return Status; 826 } 827 } 828 829 return STATUS_INVALID_DEVICE_REQUEST; 830 } 831 832 NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) { 833 NTSTATUS Status; 834 ULONG mmdltsize; 835 MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt; 836 MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli; 837 838 mmdltsize = (ULONG)offsetof(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName[0]) + devpath->Length; 839 840 mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG); 841 if (!mmdlt) { 842 ERR("out of memory\n"); 843 return STATUS_INSUFFICIENT_RESOURCES; 844 } 845 846 mmdlt->DeviceNameLength = devpath->Length; 847 RtlCopyMemory(&mmdlt->DeviceName, devpath->Buffer, devpath->Length); 848 TRACE("mmdlt = %.*S\n", (int)(mmdlt->DeviceNameLength / sizeof(WCHAR)), mmdlt->DeviceName); 849 850 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER, mmdlt, mmdltsize, &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), false, NULL); 851 852 if (!NT_SUCCESS(Status)) 853 ERR("IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER returned %08lx\n", Status); 854 else 855 TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter); 856 857 ExFreePool(mmdlt); 858 859 return Status; 860 } 861 862 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE) 863 NTSTATUS __stdcall pnp_removal(PVOID NotificationStructure, PVOID Context) { 864 TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = (TARGET_DEVICE_REMOVAL_NOTIFICATION*)NotificationStructure; 865 pdo_device_extension* pdode = (pdo_device_extension*)Context; 866 867 if (RtlCompareMemory(&tdrn->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE, sizeof(GUID)) == sizeof(GUID)) { 868 TRACE("GUID_TARGET_DEVICE_QUERY_REMOVE\n"); 869 870 if (pdode->vde && pdode->vde->mounted_device) 871 pnp_query_remove_device(pdode->vde->mounted_device, NULL); 872 } 873 874 return STATUS_SUCCESS; 875 } 876 877 static bool allow_degraded_mount(BTRFS_UUID* uuid) { 878 HANDLE h; 879 NTSTATUS Status; 880 OBJECT_ATTRIBUTES oa; 881 UNICODE_STRING path, adus; 882 uint32_t degraded = mount_allow_degraded; 883 ULONG i, j, kvfilen, retlen; 884 KEY_VALUE_FULL_INFORMATION* kvfi; 885 886 path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR)); 887 path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); 888 889 if (!path.Buffer) { 890 ERR("out of memory\n"); 891 return false; 892 } 893 894 RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length); 895 i = registry_path.Length / sizeof(WCHAR); 896 897 path.Buffer[i] = '\\'; 898 i++; 899 900 for (j = 0; j < 16; j++) { 901 path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4); 902 path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF); 903 904 i += 2; 905 906 if (j == 3 || j == 5 || j == 7 || j == 9) { 907 path.Buffer[i] = '-'; 908 i++; 909 } 910 } 911 912 InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 913 914 kvfilen = (ULONG)offsetof(KEY_VALUE_FULL_INFORMATION, Name[0]) + (255 * sizeof(WCHAR)); 915 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); 916 if (!kvfi) { 917 ERR("out of memory\n"); 918 ExFreePool(path.Buffer); 919 return false; 920 } 921 922 Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa); 923 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 924 goto end; 925 else if (!NT_SUCCESS(Status)) { 926 ERR("ZwOpenKey returned %08lx\n", Status); 927 goto end; 928 } 929 930 adus.Buffer = L"AllowDegraded"; 931 adus.Length = adus.MaximumLength = sizeof(adus.Buffer) - sizeof(WCHAR); 932 933 if (NT_SUCCESS(ZwQueryValueKey(h, &adus, KeyValueFullInformation, kvfi, kvfilen, &retlen))) { 934 if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(uint32_t)) { 935 uint32_t* val = (uint32_t*)((uint8_t*)kvfi + kvfi->DataOffset); 936 937 degraded = *val; 938 } 939 } 940 941 ZwClose(h); 942 943 end: 944 ExFreePool(kvfi); 945 946 ExFreePool(path.Buffer); 947 948 return degraded; 949 } 950 951 typedef struct { 952 LIST_ENTRY list_entry; 953 UNICODE_STRING name; 954 NTSTATUS Status; 955 BTRFS_UUID uuid; 956 } drive_letter_removal; 957 958 static void drive_letter_callback2(pdo_device_extension* pdode, PDEVICE_OBJECT mountmgr) { 959 LIST_ENTRY* le; 960 LIST_ENTRY dlrlist; 961 962 InitializeListHead(&dlrlist); 963 964 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 965 966 le = pdode->children.Flink; 967 968 while (le != &pdode->children) { 969 drive_letter_removal* dlr; 970 971 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 972 973 dlr = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_removal), ALLOC_TAG); 974 if (!dlr) { 975 ERR("out of memory\n"); 976 977 while (!IsListEmpty(&dlrlist)) { 978 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); 979 980 ExFreePool(dlr->name.Buffer); 981 ExFreePool(dlr); 982 } 983 984 ExReleaseResourceLite(&pdode->child_lock); 985 return; 986 } 987 988 dlr->name.Length = dlr->name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR)); 989 dlr->name.Buffer = ExAllocatePoolWithTag(PagedPool, dlr->name.Length, ALLOC_TAG); 990 991 if (!dlr->name.Buffer) { 992 ERR("out of memory\n"); 993 994 ExFreePool(dlr); 995 996 while (!IsListEmpty(&dlrlist)) { 997 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); 998 999 ExFreePool(dlr->name.Buffer); 1000 ExFreePool(dlr); 1001 } 1002 1003 ExReleaseResourceLite(&pdode->child_lock); 1004 return; 1005 } 1006 1007 RtlCopyMemory(dlr->name.Buffer, L"\\??", 3 * sizeof(WCHAR)); 1008 RtlCopyMemory(&dlr->name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length); 1009 1010 dlr->uuid = vc->uuid; 1011 1012 InsertTailList(&dlrlist, &dlr->list_entry); 1013 1014 le = le->Flink; 1015 } 1016 1017 ExReleaseResourceLite(&pdode->child_lock); 1018 1019 le = dlrlist.Flink; 1020 while (le != &dlrlist) { 1021 drive_letter_removal* dlr = CONTAINING_RECORD(le, drive_letter_removal, list_entry); 1022 1023 dlr->Status = remove_drive_letter(mountmgr, &dlr->name); 1024 1025 if (!NT_SUCCESS(dlr->Status) && dlr->Status != STATUS_NOT_FOUND) 1026 WARN("remove_drive_letter returned %08lx\n", dlr->Status); 1027 1028 le = le->Flink; 1029 } 1030 1031 // set vc->had_drive_letter 1032 1033 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 1034 1035 while (!IsListEmpty(&dlrlist)) { 1036 drive_letter_removal* dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); 1037 1038 le = pdode->children.Flink; 1039 1040 while (le != &pdode->children) { 1041 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 1042 1043 if (RtlCompareMemory(&vc->uuid, &dlr->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 1044 vc->had_drive_letter = NT_SUCCESS(dlr->Status); 1045 break; 1046 } 1047 1048 le = le->Flink; 1049 } 1050 1051 ExFreePool(dlr->name.Buffer); 1052 ExFreePool(dlr); 1053 } 1054 1055 ExReleaseResourceLite(&pdode->child_lock); 1056 } 1057 1058 _Function_class_(IO_WORKITEM_ROUTINE) 1059 static void __stdcall drive_letter_callback(pdo_device_extension* pdode) { 1060 NTSTATUS Status; 1061 UNICODE_STRING mmdevpath; 1062 PDEVICE_OBJECT mountmgr; 1063 PFILE_OBJECT mountmgrfo; 1064 1065 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); 1066 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr); 1067 if (!NT_SUCCESS(Status)) { 1068 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); 1069 return; 1070 } 1071 1072 drive_letter_callback2(pdode, mountmgr); 1073 1074 ObDereferenceObject(mountmgrfo); 1075 } 1076 1077 void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length, ULONG disk_num, ULONG part_num) { 1078 NTSTATUS Status; 1079 LIST_ENTRY* le; 1080 PDEVICE_OBJECT DeviceObject; 1081 volume_child* vc; 1082 PFILE_OBJECT FileObject; 1083 UNICODE_STRING devpath2; 1084 bool inserted = false, new_pdo = false; 1085 pdo_device_extension* pdode = NULL; 1086 PDEVICE_OBJECT pdo = NULL; 1087 bool process_drive_letters = false; 1088 1089 if (devpath->Length == 0) 1090 return; 1091 1092 ExAcquireResourceExclusiveLite(&pdo_list_lock, true); 1093 1094 le = pdo_list.Flink; 1095 while (le != &pdo_list) { 1096 pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry); 1097 1098 if (RtlCompareMemory(&pdode2->uuid, &sb->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 1099 pdode = pdode2; 1100 break; 1101 } 1102 1103 le = le->Flink; 1104 } 1105 1106 Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); 1107 if (!NT_SUCCESS(Status)) { 1108 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); 1109 ExReleaseResourceLite(&pdo_list_lock); 1110 return; 1111 } 1112 1113 if (!pdode) { 1114 if (no_pnp) { 1115 Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF, NULL, NULL, 0, &pdo); 1116 1117 if (!NT_SUCCESS(Status)) { 1118 ERR("IoReportDetectedDevice returned %08lx\n", Status); 1119 ExReleaseResourceLite(&pdo_list_lock); 1120 return; 1121 } 1122 1123 pdode = ExAllocatePoolWithTag(NonPagedPool, sizeof(pdo_device_extension), ALLOC_TAG); 1124 1125 if (!pdode) { 1126 ERR("out of memory\n"); 1127 ExReleaseResourceLite(&pdo_list_lock); 1128 return; 1129 } 1130 } else { 1131 Status = IoCreateDevice(drvobj, sizeof(pdo_device_extension), NULL, FILE_DEVICE_DISK, 1132 FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, false, &pdo); 1133 if (!NT_SUCCESS(Status)) { 1134 ERR("IoCreateDevice returned %08lx\n", Status); 1135 ExReleaseResourceLite(&pdo_list_lock); 1136 goto fail; 1137 } 1138 1139 pdo->Flags |= DO_BUS_ENUMERATED_DEVICE; 1140 1141 pdode = pdo->DeviceExtension; 1142 } 1143 1144 RtlZeroMemory(pdode, sizeof(pdo_device_extension)); 1145 1146 pdode->type = VCB_TYPE_PDO; 1147 pdode->pdo = pdo; 1148 pdode->uuid = sb->uuid; 1149 1150 ExInitializeResourceLite(&pdode->child_lock); 1151 InitializeListHead(&pdode->children); 1152 pdode->num_children = sb->num_devices; 1153 pdode->children_loaded = 0; 1154 1155 pdo->Flags &= ~DO_DEVICE_INITIALIZING; 1156 pdo->SectorSize = (USHORT)sb->sector_size; 1157 1158 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 1159 1160 new_pdo = true; 1161 } else { 1162 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 1163 ExConvertExclusiveToSharedLite(&pdo_list_lock); 1164 1165 le = pdode->children.Flink; 1166 while (le != &pdode->children) { 1167 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry); 1168 1169 if (RtlCompareMemory(&vc2->uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 1170 // duplicate, ignore 1171 ExReleaseResourceLite(&pdode->child_lock); 1172 ExReleaseResourceLite(&pdo_list_lock); 1173 goto fail; 1174 } 1175 1176 le = le->Flink; 1177 } 1178 } 1179 1180 vc = ExAllocatePoolWithTag(PagedPool, sizeof(volume_child), ALLOC_TAG); 1181 if (!vc) { 1182 ERR("out of memory\n"); 1183 1184 ExReleaseResourceLite(&pdode->child_lock); 1185 ExReleaseResourceLite(&pdo_list_lock); 1186 1187 goto fail; 1188 } 1189 1190 vc->uuid = sb->dev_item.device_uuid; 1191 vc->devid = sb->dev_item.dev_id; 1192 vc->generation = sb->generation; 1193 vc->notification_entry = NULL; 1194 vc->boot_volume = false; 1195 1196 Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, FileObject, 1197 drvobj, pnp_removal, pdode, &vc->notification_entry); 1198 if (!NT_SUCCESS(Status)) 1199 WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status); 1200 1201 vc->devobj = DeviceObject; 1202 vc->fileobj = FileObject; 1203 1204 devpath2 = *devpath; 1205 1206 // The PNP path sometimes begins \\?\ and sometimes \??\. We need to remove this prefix 1207 // so we can compare properly if the device is removed. 1208 if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') && 1209 devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') { 1210 devpath2.Buffer = &devpath2.Buffer[3]; 1211 devpath2.Length -= 3 * sizeof(WCHAR); 1212 devpath2.MaximumLength -= 3 * sizeof(WCHAR); 1213 } 1214 1215 vc->pnp_name.Length = vc->pnp_name.MaximumLength = devpath2.Length; 1216 vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, devpath2.Length, ALLOC_TAG); 1217 1218 if (vc->pnp_name.Buffer) 1219 RtlCopyMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length); 1220 else { 1221 ERR("out of memory\n"); 1222 vc->pnp_name.Length = vc->pnp_name.MaximumLength = 0; 1223 } 1224 1225 vc->size = length; 1226 vc->seeding = sb->flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? true : false; 1227 vc->disk_num = disk_num; 1228 vc->part_num = part_num; 1229 vc->had_drive_letter = false; 1230 1231 le = pdode->children.Flink; 1232 while (le != &pdode->children) { 1233 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry); 1234 1235 if (vc2->generation < vc->generation) { 1236 if (le == pdode->children.Flink) 1237 pdode->num_children = sb->num_devices; 1238 1239 InsertHeadList(vc2->list_entry.Blink, &vc->list_entry); 1240 inserted = true; 1241 break; 1242 } 1243 1244 le = le->Flink; 1245 } 1246 1247 if (!inserted) 1248 InsertTailList(&pdode->children, &vc->list_entry); 1249 1250 pdode->children_loaded++; 1251 1252 if (pdode->vde && pdode->vde->mounted_device) { 1253 device_extension* Vcb = pdode->vde->mounted_device->DeviceExtension; 1254 1255 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 1256 1257 le = Vcb->devices.Flink; 1258 while (le != &Vcb->devices) { 1259 device* dev = CONTAINING_RECORD(le, device, list_entry); 1260 1261 if (!dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 1262 dev->devobj = DeviceObject; 1263 dev->disk_num = disk_num; 1264 dev->part_num = part_num; 1265 init_device(Vcb, dev, false); 1266 break; 1267 } 1268 1269 le = le->Flink; 1270 } 1271 1272 ExReleaseResourceLite(&Vcb->tree_lock); 1273 } 1274 1275 if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { 1276 pdode->removable = true; 1277 1278 if (pdode->vde && pdode->vde->device) 1279 pdode->vde->device->Characteristics |= FILE_REMOVABLE_MEDIA; 1280 } 1281 1282 if (pdode->num_children == pdode->children_loaded || (pdode->children_loaded == 1 && allow_degraded_mount(&sb->uuid))) { 1283 if ((!new_pdo || !no_pnp) && pdode->vde) { 1284 Status = IoSetDeviceInterfaceState(&pdode->vde->bus_name, true); 1285 if (!NT_SUCCESS(Status)) 1286 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status); 1287 } 1288 1289 process_drive_letters = true; 1290 } 1291 1292 ExReleaseResourceLite(&pdode->child_lock); 1293 1294 if (new_pdo) 1295 InsertTailList(&pdo_list, &pdode->list_entry); 1296 1297 ExReleaseResourceLite(&pdo_list_lock); 1298 1299 if (process_drive_letters) 1300 drive_letter_callback(pdode); 1301 1302 if (new_pdo) { 1303 if (RtlCompareMemory(&sb->uuid, &boot_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) 1304 boot_add_device(pdo); 1305 else if (no_pnp) 1306 AddDevice(drvobj, pdo); 1307 else { 1308 bus_device_extension* bde = busobj->DeviceExtension; 1309 IoInvalidateDeviceRelations(bde->buspdo, BusRelations); 1310 } 1311 } 1312 1313 return; 1314 1315 fail: 1316 ObDereferenceObject(FileObject); 1317 } 1318