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