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 20 #include <ntddk.h> 21 #include <ntifs.h> 22 #include <mountmgr.h> 23 #include <windef.h> 24 #include <ntddstor.h> 25 #include <ntdddisk.h> 26 #include <ntddvol.h> 27 28 #include <initguid.h> 29 #include <wdmguid.h> 30 #include <ioevent.h> 31 32 extern ERESOURCE pdo_list_lock; 33 extern LIST_ENTRY pdo_list; 34 extern UNICODE_STRING registry_path; 35 extern KEVENT mountmgr_thread_event; 36 extern HANDLE mountmgr_thread_handle; 37 extern bool shutting_down; 38 extern PDEVICE_OBJECT busobj; 39 extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx; 40 extern ERESOURCE boot_lock; 41 extern PDRIVER_OBJECT drvobj; 42 43 typedef void (*pnp_callback)(PUNICODE_STRING devpath); 44 45 #ifndef __REACTOS__ 46 // not in mingw yet 47 #ifndef _MSC_VER 48 DEFINE_GUID(GUID_IO_VOLUME_FVE_STATUS_CHANGE, 0x062998b2, 0xee1f, 0x4b6a, 0xb8, 0x57, 0xe7, 0x6c, 0xbb, 0xe9, 0xa6, 0xda); 49 #endif 50 #endif // __REACTOS__ 51 52 extern PDEVICE_OBJECT master_devobj; 53 54 typedef struct { 55 LIST_ENTRY list_entry; 56 PDEVICE_OBJECT devobj; 57 void* notification_entry; 58 UNICODE_STRING devpath; 59 WCHAR buf[1]; 60 } fve_data; 61 62 static LIST_ENTRY fve_data_list = { &fve_data_list, &fve_data_list }; 63 KSPIN_LOCK fve_data_lock; 64 65 static bool fs_ignored(BTRFS_UUID* uuid) { 66 UNICODE_STRING path, ignoreus; 67 NTSTATUS Status; 68 OBJECT_ATTRIBUTES oa; 69 KEY_VALUE_FULL_INFORMATION* kvfi; 70 ULONG dispos, retlen, kvfilen, i, j; 71 HANDLE h; 72 bool ret = false; 73 74 path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR)); 75 76 path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); 77 if (!path.Buffer) { 78 ERR("out of memory\n"); 79 return false; 80 } 81 82 RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length); 83 84 i = registry_path.Length / sizeof(WCHAR); 85 86 path.Buffer[i] = '\\'; 87 i++; 88 89 for (j = 0; j < 16; j++) { 90 path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4); 91 path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF); 92 93 i += 2; 94 95 if (j == 3 || j == 5 || j == 7 || j == 9) { 96 path.Buffer[i] = '-'; 97 i++; 98 } 99 } 100 101 InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 102 103 Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); 104 105 if (!NT_SUCCESS(Status)) { 106 TRACE("ZwCreateKey returned %08lx\n", Status); 107 ExFreePool(path.Buffer); 108 return false; 109 } 110 111 RtlInitUnicodeString(&ignoreus, L"Ignore"); 112 113 kvfilen = (ULONG)offsetof(KEY_VALUE_FULL_INFORMATION, Name[0]) + (255 * sizeof(WCHAR)); 114 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); 115 if (!kvfi) { 116 ERR("out of memory\n"); 117 ZwClose(h); 118 ExFreePool(path.Buffer); 119 return false; 120 } 121 122 Status = ZwQueryValueKey(h, &ignoreus, KeyValueFullInformation, kvfi, kvfilen, &retlen); 123 if (NT_SUCCESS(Status)) { 124 if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(uint32_t)) { 125 uint32_t* pr = (uint32_t*)((uint8_t*)kvfi + kvfi->DataOffset); 126 127 ret = *pr; 128 } 129 } 130 131 ZwClose(h); 132 ExFreePool(kvfi); 133 ExFreePool(path.Buffer); 134 135 return ret; 136 } 137 138 typedef struct { 139 PIO_WORKITEM work_item; 140 PFILE_OBJECT fileobj; 141 PDEVICE_OBJECT devobj; 142 UNICODE_STRING devpath; 143 WCHAR buf[1]; 144 } fve_callback_context; 145 146 _Function_class_(IO_WORKITEM_ROUTINE) 147 static void __stdcall fve_callback(PDEVICE_OBJECT DeviceObject, PVOID con) { 148 fve_callback_context* ctx = con; 149 150 UNUSED(DeviceObject); 151 152 if (volume_arrival(&ctx->devpath, true)) { 153 KIRQL irql; 154 LIST_ENTRY* le; 155 fve_data* d = NULL; 156 157 // volume no longer locked - unregister notification 158 159 KeAcquireSpinLock(&fve_data_lock, &irql); 160 161 le = fve_data_list.Flink; 162 while (le != &fve_data_list) { 163 fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry); 164 165 if (d2->devobj == ctx->devobj) { 166 RemoveEntryList(&d2->list_entry); 167 d = d2; 168 break; 169 } 170 171 le = le->Flink; 172 } 173 174 KeReleaseSpinLock(&fve_data_lock, irql); 175 176 if (d) { 177 IoUnregisterPlugPlayNotification(d->notification_entry); 178 ExFreePool(d); 179 } 180 } 181 182 IoFreeWorkItem(ctx->work_item); 183 ExFreePool(ctx); 184 } 185 186 static NTSTATUS __stdcall event_notification(PVOID NotificationStructure, PVOID Context) { 187 TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = NotificationStructure; 188 PDEVICE_OBJECT devobj = Context; 189 PIO_WORKITEM work_item; 190 fve_callback_context* ctx; 191 LIST_ENTRY* le; 192 KIRQL irql; 193 194 if (RtlCompareMemory(&tdrn->Event, &GUID_IO_VOLUME_FVE_STATUS_CHANGE, sizeof(GUID)) != sizeof(GUID)) 195 return STATUS_SUCCESS; 196 197 /* The FVE event has trailing data, presumably telling us whether the volume has 198 * been unlocked or whatever, but unfortunately it's undocumented! */ 199 200 work_item = IoAllocateWorkItem(master_devobj); 201 if (!work_item) { 202 ERR("out of memory\n"); 203 return STATUS_SUCCESS; 204 } 205 206 KeAcquireSpinLock(&fve_data_lock, &irql); 207 208 le = fve_data_list.Flink; 209 while (le != &fve_data_list) { 210 fve_data* d = CONTAINING_RECORD(le, fve_data, list_entry); 211 212 if (d->devobj == devobj) { 213 ctx = ExAllocatePoolWithTag(NonPagedPool, offsetof(fve_callback_context, buf) + d->devpath.Length, 214 ALLOC_TAG); 215 216 if (!ctx) { 217 KeReleaseSpinLock(&fve_data_lock, irql); 218 ERR("out of memory\n"); 219 IoFreeWorkItem(work_item); 220 return STATUS_SUCCESS; 221 } 222 223 RtlCopyMemory(ctx->buf, d->devpath.Buffer, d->devpath.Length); 224 ctx->devpath.Length = ctx->devpath.MaximumLength = d->devpath.Length; 225 226 KeReleaseSpinLock(&fve_data_lock, irql); 227 228 ctx->devpath.Buffer = ctx->buf; 229 230 ctx->fileobj = tdrn->FileObject; 231 ctx->devobj = devobj; 232 ctx->work_item = work_item; 233 234 IoQueueWorkItem(work_item, fve_callback, DelayedWorkQueue, ctx); 235 236 return STATUS_SUCCESS; 237 } 238 239 le = le->Flink; 240 } 241 242 KeReleaseSpinLock(&fve_data_lock, irql); 243 244 IoFreeWorkItem(work_item); 245 246 return STATUS_SUCCESS; 247 } 248 249 static void register_fve_callback(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj, 250 PUNICODE_STRING devpath) { 251 NTSTATUS Status; 252 KIRQL irql; 253 LIST_ENTRY* le; 254 255 fve_data* d = ExAllocatePoolWithTag(NonPagedPool, offsetof(fve_data, buf) + devpath->Length, ALLOC_TAG); 256 if (!d) { 257 ERR("out of memory\n"); 258 return; 259 } 260 261 d->devpath.Buffer = d->buf; 262 d->devpath.Length = d->devpath.MaximumLength = devpath->Length; 263 RtlCopyMemory(d->devpath.Buffer, devpath->Buffer, devpath->Length); 264 265 KeAcquireSpinLock(&fve_data_lock, &irql); 266 267 le = fve_data_list.Flink; 268 while (le != &fve_data_list) { 269 fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry); 270 271 if (d2->devobj == devobj) { 272 KeReleaseSpinLock(&fve_data_lock, irql); 273 ExFreePool(d); 274 return; 275 } 276 277 le = le->Flink; 278 } 279 280 KeReleaseSpinLock(&fve_data_lock, irql); 281 282 Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileobj, drvobj, event_notification, 283 devobj, &d->notification_entry); 284 if (!NT_SUCCESS(Status)) { 285 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status); 286 return; 287 } 288 289 KeAcquireSpinLock(&fve_data_lock, &irql); 290 291 le = fve_data_list.Flink; 292 while (le != &fve_data_list) { 293 fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry); 294 295 if (d2->devobj == devobj) { 296 KeReleaseSpinLock(&fve_data_lock, irql); 297 IoUnregisterPlugPlayNotification(d->notification_entry); 298 ExFreePool(d); 299 return; 300 } 301 302 le = le->Flink; 303 } 304 305 d->devobj = devobj; 306 InsertTailList(&fve_data_list, &d->list_entry); 307 308 KeReleaseSpinLock(&fve_data_lock, irql); 309 } 310 311 static bool test_vol(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, 312 PUNICODE_STRING devpath, DWORD disk_num, DWORD part_num, uint64_t length, 313 bool fve_callback) { 314 NTSTATUS Status; 315 ULONG toread; 316 uint8_t* data = NULL; 317 uint32_t sector_size; 318 bool ret = true; 319 320 TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer); 321 322 sector_size = DeviceObject->SectorSize; 323 324 if (sector_size == 0) { 325 DISK_GEOMETRY geometry; 326 IO_STATUS_BLOCK iosb; 327 328 Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, 329 &geometry, sizeof(DISK_GEOMETRY), true, &iosb); 330 331 if (!NT_SUCCESS(Status)) { 332 ERR("%.*S had a sector size of 0, and IOCTL_DISK_GET_DRIVE_GEOMETRY returned %08lx\n", 333 (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer, Status); 334 goto deref; 335 } 336 337 if (iosb.Information < sizeof(DISK_GEOMETRY)) { 338 ERR("%.*S: IOCTL_DISK_GET_DRIVE_GEOMETRY returned %Iu bytes, expected %Iu\n", 339 (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer, iosb.Information, sizeof(DISK_GEOMETRY)); 340 } 341 342 sector_size = geometry.BytesPerSector; 343 344 if (sector_size == 0) { 345 ERR("%.*S had a sector size of 0\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer); 346 goto deref; 347 } 348 } 349 350 toread = (ULONG)sector_align(sizeof(superblock), sector_size); 351 data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG); 352 if (!data) { 353 ERR("out of memory\n"); 354 goto deref; 355 } 356 357 Status = sync_read_phys(DeviceObject, FileObject, superblock_addrs[0], toread, data, true); 358 359 if (NT_SUCCESS(Status) && ((superblock*)data)->magic == BTRFS_MAGIC) { 360 superblock* sb = (superblock*)data; 361 362 if (check_superblock_checksum(sb)) { 363 TRACE("volume found\n"); 364 365 if (length >= superblock_addrs[1] + toread) { 366 ULONG i = 1; 367 368 superblock* sb2 = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG); 369 if (!sb2) { 370 ERR("out of memory\n"); 371 goto deref; 372 } 373 374 while (superblock_addrs[i] > 0 && length >= superblock_addrs[i] + toread) { 375 Status = sync_read_phys(DeviceObject, FileObject, superblock_addrs[i], toread, (PUCHAR)sb2, true); 376 377 if (NT_SUCCESS(Status) && sb2->magic == BTRFS_MAGIC) { 378 if (check_superblock_checksum(sb2) && sb2->generation > sb->generation) 379 RtlCopyMemory(sb, sb2, toread); 380 } 381 382 i++; 383 } 384 385 ExFreePool(sb2); 386 } 387 388 if (!fs_ignored(&sb->uuid)) { 389 DeviceObject->Flags &= ~DO_VERIFY_VOLUME; 390 add_volume_device(sb, devpath, length, disk_num, part_num); 391 } 392 } 393 } else if (Status == STATUS_FVE_LOCKED_VOLUME) { 394 if (fve_callback) 395 ret = false; 396 else 397 register_fve_callback(DeviceObject, FileObject, devpath); 398 } 399 400 deref: 401 if (data) 402 ExFreePool(data); 403 404 return ret; 405 } 406 407 NTSTATUS remove_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) { 408 NTSTATUS Status; 409 MOUNTMGR_MOUNT_POINT* mmp; 410 ULONG mmpsize; 411 MOUNTMGR_MOUNT_POINTS mmps1, *mmps2; 412 413 TRACE("removing drive letter\n"); 414 415 mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + devpath->Length; 416 417 mmp = ExAllocatePoolWithTag(PagedPool, mmpsize, ALLOC_TAG); 418 if (!mmp) { 419 ERR("out of memory\n"); 420 return STATUS_INSUFFICIENT_RESOURCES; 421 } 422 423 RtlZeroMemory(mmp, mmpsize); 424 425 mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); 426 mmp->DeviceNameLength = devpath->Length; 427 RtlCopyMemory(&mmp[1], devpath->Buffer, devpath->Length); 428 429 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_DELETE_POINTS, mmp, mmpsize, &mmps1, sizeof(MOUNTMGR_MOUNT_POINTS), false, NULL); 430 431 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) { 432 ERR("IOCTL_MOUNTMGR_DELETE_POINTS 1 returned %08lx\n", Status); 433 ExFreePool(mmp); 434 return Status; 435 } 436 437 if (Status != STATUS_BUFFER_OVERFLOW || mmps1.Size == 0) { 438 ExFreePool(mmp); 439 return STATUS_NOT_FOUND; 440 } 441 442 mmps2 = ExAllocatePoolWithTag(PagedPool, mmps1.Size, ALLOC_TAG); 443 if (!mmps2) { 444 ERR("out of memory\n"); 445 ExFreePool(mmp); 446 return STATUS_INSUFFICIENT_RESOURCES; 447 } 448 449 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_DELETE_POINTS, mmp, mmpsize, mmps2, mmps1.Size, false, NULL); 450 451 if (!NT_SUCCESS(Status)) 452 ERR("IOCTL_MOUNTMGR_DELETE_POINTS 2 returned %08lx\n", Status); 453 454 ExFreePool(mmps2); 455 ExFreePool(mmp); 456 457 return Status; 458 } 459 460 void disk_arrival(PUNICODE_STRING devpath) { 461 PFILE_OBJECT fileobj; 462 PDEVICE_OBJECT devobj; 463 NTSTATUS Status; 464 STORAGE_DEVICE_NUMBER sdn; 465 ULONG dlisize; 466 DRIVE_LAYOUT_INFORMATION_EX* dli = NULL; 467 IO_STATUS_BLOCK iosb; 468 GET_LENGTH_INFORMATION gli; 469 470 ExAcquireResourceSharedLite(&boot_lock, TRUE); 471 472 Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &fileobj, &devobj); 473 if (!NT_SUCCESS(Status)) { 474 ExReleaseResourceLite(&boot_lock); 475 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); 476 return; 477 } 478 479 dlisize = 0; 480 481 do { 482 dlisize += 1024; 483 484 if (dli) 485 ExFreePool(dli); 486 487 dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG); 488 if (!dli) { 489 ERR("out of memory\n"); 490 goto end; 491 } 492 493 Status = dev_ioctl(devobj, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, 494 dli, dlisize, true, &iosb); 495 } while (Status == STATUS_BUFFER_TOO_SMALL); 496 497 // only consider disk as a potential filesystem if it has no partitions 498 if (NT_SUCCESS(Status) && dli->PartitionCount > 0) { 499 ExFreePool(dli); 500 goto end; 501 } 502 503 ExFreePool(dli); 504 505 Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, 506 &gli, sizeof(gli), true, NULL); 507 508 if (!NT_SUCCESS(Status)) { 509 ERR("error reading length information: %08lx\n", Status); 510 goto end; 511 } 512 513 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, 514 &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL); 515 if (!NT_SUCCESS(Status)) { 516 TRACE("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status); 517 sdn.DeviceNumber = 0xffffffff; 518 sdn.PartitionNumber = 0; 519 } else 520 TRACE("DeviceType = %lu, DeviceNumber = %lu, PartitionNumber = %lu\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber); 521 522 test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber, 523 gli.Length.QuadPart, false); 524 525 end: 526 ObDereferenceObject(fileobj); 527 528 ExReleaseResourceLite(&boot_lock); 529 } 530 531 void remove_volume_child(_Inout_ _Requires_exclusive_lock_held_(_Curr_->child_lock) _Releases_exclusive_lock_(_Curr_->child_lock) _In_ volume_device_extension* vde, 532 _In_ volume_child* vc, _In_ bool skip_dev) { 533 NTSTATUS Status; 534 pdo_device_extension* pdode = vde->pdode; 535 device_extension* Vcb = vde->mounted_device ? vde->mounted_device->DeviceExtension : NULL; 536 537 if (vc->notification_entry) { 538 if (fIoUnregisterPlugPlayNotificationEx) 539 fIoUnregisterPlugPlayNotificationEx(vc->notification_entry); 540 else 541 IoUnregisterPlugPlayNotification(vc->notification_entry); 542 } 543 544 if (vde->mounted_device && (!Vcb || !Vcb->options.allow_degraded)) { 545 Status = pnp_surprise_removal(vde->mounted_device, NULL); 546 if (!NT_SUCCESS(Status)) 547 ERR("pnp_surprise_removal returned %08lx\n", Status); 548 } 549 550 if (!Vcb || !Vcb->options.allow_degraded) { 551 Status = IoSetDeviceInterfaceState(&vde->bus_name, false); 552 if (!NT_SUCCESS(Status)) 553 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status); 554 } 555 556 if (pdode->children_loaded > 0) { 557 UNICODE_STRING mmdevpath; 558 PFILE_OBJECT FileObject; 559 PDEVICE_OBJECT mountmgr; 560 LIST_ENTRY* le; 561 562 if (!Vcb || !Vcb->options.allow_degraded) { 563 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); 564 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr); 565 if (!NT_SUCCESS(Status)) 566 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); 567 else { 568 le = pdode->children.Flink; 569 570 while (le != &pdode->children) { 571 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry); 572 573 if (vc2->had_drive_letter) { // re-add entry to mountmgr 574 MOUNTDEV_NAME mdn; 575 576 Status = dev_ioctl(vc2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL); 577 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) 578 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status); 579 else { 580 MOUNTDEV_NAME* mdn2; 581 ULONG mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength; 582 583 mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG); 584 if (!mdn2) 585 ERR("out of memory\n"); 586 else { 587 Status = dev_ioctl(vc2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL); 588 if (!NT_SUCCESS(Status)) 589 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status); 590 else { 591 UNICODE_STRING name; 592 593 name.Buffer = mdn2->Name; 594 name.Length = name.MaximumLength = mdn2->NameLength; 595 596 Status = mountmgr_add_drive_letter(mountmgr, &name); 597 if (!NT_SUCCESS(Status)) 598 WARN("mountmgr_add_drive_letter returned %08lx\n", Status); 599 } 600 601 ExFreePool(mdn2); 602 } 603 } 604 } 605 606 le = le->Flink; 607 } 608 609 ObDereferenceObject(FileObject); 610 } 611 } else if (!skip_dev) { 612 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 613 614 le = Vcb->devices.Flink; 615 while (le != &Vcb->devices) { 616 device* dev = CONTAINING_RECORD(le, device, list_entry); 617 618 if (dev->devobj == vc->devobj) { 619 dev->devobj = NULL; // mark as missing 620 break; 621 } 622 623 le = le->Flink; 624 } 625 626 ExReleaseResourceLite(&Vcb->tree_lock); 627 } 628 629 if (vde->device->Characteristics & FILE_REMOVABLE_MEDIA) { 630 vde->device->Characteristics &= ~FILE_REMOVABLE_MEDIA; 631 632 le = pdode->children.Flink; 633 while (le != &pdode->children) { 634 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry); 635 636 if (vc2 != vc && vc2->devobj->Characteristics & FILE_REMOVABLE_MEDIA) { 637 vde->device->Characteristics |= FILE_REMOVABLE_MEDIA; 638 break; 639 } 640 641 le = le->Flink; 642 } 643 } 644 } 645 646 ObDereferenceObject(vc->fileobj); 647 ExFreePool(vc->pnp_name.Buffer); 648 RemoveEntryList(&vc->list_entry); 649 ExFreePool(vc); 650 651 pdode->children_loaded--; 652 653 if (pdode->children_loaded == 0) { // remove volume device 654 bool remove = false; 655 656 RemoveEntryList(&pdode->list_entry); 657 658 vde->removing = true; 659 660 Status = IoSetDeviceInterfaceState(&vde->bus_name, false); 661 if (!NT_SUCCESS(Status)) 662 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status); 663 664 if (vde->pdo->AttachedDevice) 665 IoDetachDevice(vde->pdo); 666 667 if (vde->open_count == 0) 668 remove = true; 669 670 ExReleaseResourceLite(&pdode->child_lock); 671 672 if (!no_pnp) { 673 bus_device_extension* bde = busobj->DeviceExtension; 674 675 IoInvalidateDeviceRelations(bde->buspdo, BusRelations); 676 } 677 678 if (remove) { 679 if (vde->name.Buffer) 680 ExFreePool(vde->name.Buffer); 681 682 if (Vcb) 683 Vcb->vde = NULL; 684 685 ExDeleteResourceLite(&pdode->child_lock); 686 687 IoDeleteDevice(vde->device); 688 } 689 } else 690 ExReleaseResourceLite(&pdode->child_lock); 691 } 692 693 bool volume_arrival(PUNICODE_STRING devpath, bool fve_callback) { 694 STORAGE_DEVICE_NUMBER sdn; 695 PFILE_OBJECT fileobj; 696 PDEVICE_OBJECT devobj; 697 GET_LENGTH_INFORMATION gli; 698 NTSTATUS Status; 699 bool ret = true; 700 701 TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer); 702 703 ExAcquireResourceSharedLite(&boot_lock, TRUE); 704 705 Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &fileobj, &devobj); 706 if (!NT_SUCCESS(Status)) { 707 ExReleaseResourceLite(&boot_lock); 708 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); 709 return false; 710 } 711 712 // make sure we're not processing devices we've created ourselves 713 714 if (devobj->DriverObject == drvobj) 715 goto end; 716 717 Status = dev_ioctl(devobj, IOCTL_VOLUME_ONLINE, NULL, 0, NULL, 0, true, NULL); 718 if (!NT_SUCCESS(Status)) 719 TRACE("IOCTL_VOLUME_ONLINE returned %08lx\n", Status); 720 721 Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL); 722 if (!NT_SUCCESS(Status)) { 723 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08lx\n", Status); 724 goto end; 725 } 726 727 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, 728 &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL); 729 if (!NT_SUCCESS(Status)) { 730 TRACE("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status); 731 sdn.DeviceNumber = 0xffffffff; 732 sdn.PartitionNumber = 0; 733 } else 734 TRACE("DeviceType = %lu, DeviceNumber = %lu, PartitionNumber = %lu\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber); 735 736 // If we've just added a partition to a whole-disk filesystem, unmount it 737 if (sdn.DeviceNumber != 0xffffffff && sdn.PartitionNumber != 0) { 738 LIST_ENTRY* le; 739 740 ExAcquireResourceExclusiveLite(&pdo_list_lock, true); 741 742 le = pdo_list.Flink; 743 while (le != &pdo_list) { 744 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry); 745 LIST_ENTRY* le2; 746 bool changed = false; 747 748 if (pdode->vde) { 749 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 750 751 le2 = pdode->children.Flink; 752 while (le2 != &pdode->children) { 753 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry); 754 LIST_ENTRY* le3 = le2->Flink; 755 756 if (vc->disk_num == sdn.DeviceNumber && vc->part_num == 0) { 757 TRACE("removing device\n"); 758 759 remove_volume_child(pdode->vde, vc, false); 760 changed = true; 761 762 break; 763 } 764 765 le2 = le3; 766 } 767 768 if (!changed) 769 ExReleaseResourceLite(&pdode->child_lock); 770 else 771 break; 772 } 773 774 le = le->Flink; 775 } 776 777 ExReleaseResourceLite(&pdo_list_lock); 778 } 779 780 ret = test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber, 781 gli.Length.QuadPart, fve_callback); 782 783 end: 784 ObDereferenceObject(fileobj); 785 786 ExReleaseResourceLite(&boot_lock); 787 788 return ret; 789 } 790 791 static void volume_arrival2(PUNICODE_STRING devpath) { 792 volume_arrival(devpath, false); 793 } 794 795 void volume_removal(PUNICODE_STRING devpath) { 796 LIST_ENTRY* le; 797 UNICODE_STRING devpath2; 798 799 TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer); 800 801 devpath2 = *devpath; 802 803 if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') && 804 devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') { 805 devpath2.Buffer = &devpath2.Buffer[3]; 806 devpath2.Length -= 3 * sizeof(WCHAR); 807 devpath2.MaximumLength -= 3 * sizeof(WCHAR); 808 } 809 810 ExAcquireResourceExclusiveLite(&pdo_list_lock, true); 811 812 le = pdo_list.Flink; 813 while (le != &pdo_list) { 814 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry); 815 LIST_ENTRY* le2; 816 bool changed = false; 817 818 if (pdode->vde) { 819 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 820 821 le2 = pdode->children.Flink; 822 while (le2 != &pdode->children) { 823 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry); 824 LIST_ENTRY* le3 = le2->Flink; 825 826 if (vc->pnp_name.Length == devpath2.Length && RtlCompareMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length) == devpath2.Length) { 827 TRACE("removing device\n"); 828 829 if (!vc->boot_volume) { 830 remove_volume_child(pdode->vde, vc, false); 831 changed = true; 832 } 833 834 break; 835 } 836 837 le2 = le3; 838 } 839 840 if (!changed) 841 ExReleaseResourceLite(&pdode->child_lock); 842 else 843 break; 844 } 845 846 le = le->Flink; 847 } 848 849 ExReleaseResourceLite(&pdo_list_lock); 850 } 851 852 typedef struct { 853 UNICODE_STRING name; 854 pnp_callback func; 855 PIO_WORKITEM work_item; 856 } pnp_callback_context; 857 858 _Function_class_(IO_WORKITEM_ROUTINE) 859 static void __stdcall do_pnp_callback(PDEVICE_OBJECT DeviceObject, PVOID con) { 860 pnp_callback_context* context = con; 861 862 UNUSED(DeviceObject); 863 864 context->func(&context->name); 865 866 if (context->name.Buffer) 867 ExFreePool(context->name.Buffer); 868 869 IoFreeWorkItem(context->work_item); 870 871 ExFreePool(context); 872 } 873 874 static void enqueue_pnp_callback(PUNICODE_STRING name, pnp_callback func) { 875 PIO_WORKITEM work_item; 876 pnp_callback_context* context; 877 878 work_item = IoAllocateWorkItem(master_devobj); 879 if (!work_item) { 880 ERR("out of memory\n"); 881 return; 882 } 883 884 context = ExAllocatePoolWithTag(PagedPool, sizeof(pnp_callback_context), ALLOC_TAG); 885 886 if (!context) { 887 ERR("out of memory\n"); 888 IoFreeWorkItem(work_item); 889 return; 890 } 891 892 if (name->Length > 0) { 893 context->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG); 894 if (!context->name.Buffer) { 895 ERR("out of memory\n"); 896 ExFreePool(context); 897 IoFreeWorkItem(work_item); 898 return; 899 } 900 901 RtlCopyMemory(context->name.Buffer, name->Buffer, name->Length); 902 context->name.Length = context->name.MaximumLength = name->Length; 903 } else { 904 context->name.Length = context->name.MaximumLength = 0; 905 context->name.Buffer = NULL; 906 } 907 908 context->func = func; 909 context->work_item = work_item; 910 911 IoQueueWorkItem(work_item, do_pnp_callback, DelayedWorkQueue, context); 912 } 913 914 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE) 915 NTSTATUS __stdcall volume_notification(PVOID NotificationStructure, PVOID Context) { 916 DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure; 917 918 UNUSED(Context); 919 920 if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID)) 921 enqueue_pnp_callback(dicn->SymbolicLinkName, volume_arrival2); 922 else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID)) 923 enqueue_pnp_callback(dicn->SymbolicLinkName, volume_removal); 924 925 return STATUS_SUCCESS; 926 } 927 928 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE) 929 NTSTATUS __stdcall pnp_notification(PVOID NotificationStructure, PVOID Context) { 930 DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure; 931 932 UNUSED(Context); 933 934 if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID)) 935 enqueue_pnp_callback(dicn->SymbolicLinkName, disk_arrival); 936 else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID)) 937 enqueue_pnp_callback(dicn->SymbolicLinkName, volume_removal); 938 939 return STATUS_SUCCESS; 940 } 941 942 static void mountmgr_process_drive(PDEVICE_OBJECT mountmgr, PUNICODE_STRING device_name) { 943 NTSTATUS Status; 944 LIST_ENTRY* le; 945 bool need_remove = false; 946 volume_child* vc2 = NULL; 947 948 ExAcquireResourceSharedLite(&pdo_list_lock, true); 949 950 le = pdo_list.Flink; 951 while (le != &pdo_list) { 952 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry); 953 LIST_ENTRY* le2; 954 955 ExAcquireResourceSharedLite(&pdode->child_lock, true); 956 957 le2 = pdode->children.Flink; 958 959 while (le2 != &pdode->children) { 960 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry); 961 962 if (vc->devobj) { 963 MOUNTDEV_NAME mdn; 964 965 Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL); 966 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) 967 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status); 968 else { 969 MOUNTDEV_NAME* mdn2; 970 ULONG mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength; 971 972 mdn2 = ExAllocatePoolWithTag(NonPagedPool, mdnsize, ALLOC_TAG); 973 if (!mdn2) 974 ERR("out of memory\n"); 975 else { 976 Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL); 977 if (!NT_SUCCESS(Status)) 978 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status); 979 else { 980 if (mdn2->NameLength == device_name->Length && RtlCompareMemory(mdn2->Name, device_name->Buffer, device_name->Length) == device_name->Length) { 981 vc2 = vc; 982 need_remove = true; 983 break; 984 } 985 } 986 987 ExFreePool(mdn2); 988 } 989 } 990 } 991 992 le2 = le2->Flink; 993 } 994 995 ExReleaseResourceLite(&pdode->child_lock); 996 997 if (need_remove) 998 break; 999 1000 le = le->Flink; 1001 } 1002 1003 ExReleaseResourceLite(&pdo_list_lock); 1004 1005 if (need_remove) { 1006 Status = remove_drive_letter(mountmgr, device_name); 1007 if (!NT_SUCCESS(Status)) 1008 ERR("remove_drive_letter returned %08lx\n", Status); 1009 else 1010 vc2->had_drive_letter = true; 1011 } 1012 } 1013 1014 static void mountmgr_updated(PDEVICE_OBJECT mountmgr, MOUNTMGR_MOUNT_POINTS* mmps) { 1015 ULONG i; 1016 1017 static const WCHAR pref[] = L"\\DosDevices\\"; 1018 1019 for (i = 0; i < mmps->NumberOfMountPoints; i++) { 1020 UNICODE_STRING symlink, device_name; 1021 1022 if (mmps->MountPoints[i].SymbolicLinkNameOffset != 0) { 1023 symlink.Buffer = (WCHAR*)(((uint8_t*)mmps) + mmps->MountPoints[i].SymbolicLinkNameOffset); 1024 symlink.Length = symlink.MaximumLength = mmps->MountPoints[i].SymbolicLinkNameLength; 1025 } else { 1026 symlink.Buffer = NULL; 1027 symlink.Length = symlink.MaximumLength = 0; 1028 } 1029 1030 if (mmps->MountPoints[i].DeviceNameOffset != 0) { 1031 device_name.Buffer = (WCHAR*)(((uint8_t*)mmps) + mmps->MountPoints[i].DeviceNameOffset); 1032 device_name.Length = device_name.MaximumLength = mmps->MountPoints[i].DeviceNameLength; 1033 } else { 1034 device_name.Buffer = NULL; 1035 device_name.Length = device_name.MaximumLength = 0; 1036 } 1037 1038 if (symlink.Length > sizeof(pref) - sizeof(WCHAR) && 1039 RtlCompareMemory(symlink.Buffer, pref, sizeof(pref) - sizeof(WCHAR)) == sizeof(pref) - sizeof(WCHAR)) 1040 mountmgr_process_drive(mountmgr, &device_name); 1041 } 1042 } 1043 1044 _Function_class_(KSTART_ROUTINE) 1045 void __stdcall mountmgr_thread(_In_ void* context) { 1046 UNICODE_STRING mmdevpath; 1047 NTSTATUS Status; 1048 PFILE_OBJECT FileObject; 1049 PDEVICE_OBJECT mountmgr; 1050 MOUNTMGR_CHANGE_NOTIFY_INFO mcni; 1051 1052 UNUSED(context); 1053 1054 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); 1055 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr); 1056 if (!NT_SUCCESS(Status)) { 1057 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); 1058 return; 1059 } 1060 1061 mcni.EpicNumber = 0; 1062 1063 while (true) { 1064 PIRP Irp; 1065 MOUNTMGR_MOUNT_POINT mmp; 1066 MOUNTMGR_MOUNT_POINTS mmps; 1067 IO_STATUS_BLOCK iosb; 1068 1069 KeClearEvent(&mountmgr_thread_event); 1070 1071 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_CHANGE_NOTIFY, mountmgr, &mcni, sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO), 1072 &mcni, sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO), false, &mountmgr_thread_event, &iosb); 1073 1074 if (!Irp) { 1075 ERR("out of memory\n"); 1076 break; 1077 } 1078 1079 Status = IoCallDriver(mountmgr, Irp); 1080 1081 if (Status == STATUS_PENDING) { 1082 KeWaitForSingleObject(&mountmgr_thread_event, Executive, KernelMode, false, NULL); 1083 Status = iosb.Status; 1084 } 1085 1086 if (shutting_down) 1087 break; 1088 1089 if (!NT_SUCCESS(Status)) { 1090 ERR("IOCTL_MOUNTMGR_CHANGE_NOTIFY returned %08lx\n", Status); 1091 break; 1092 } 1093 1094 TRACE("mountmgr changed\n"); 1095 1096 RtlZeroMemory(&mmp, sizeof(MOUNTMGR_MOUNT_POINT)); 1097 1098 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(MOUNTMGR_MOUNT_POINT), &mmps, sizeof(MOUNTMGR_MOUNT_POINTS), 1099 false, NULL); 1100 1101 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) 1102 ERR("IOCTL_MOUNTMGR_QUERY_POINTS 1 returned %08lx\n", Status); 1103 else if (mmps.Size > 0) { 1104 MOUNTMGR_MOUNT_POINTS* mmps2; 1105 1106 mmps2 = ExAllocatePoolWithTag(NonPagedPool, mmps.Size, ALLOC_TAG); 1107 if (!mmps2) { 1108 ERR("out of memory\n"); 1109 break; 1110 } 1111 1112 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(MOUNTMGR_MOUNT_POINT), mmps2, mmps.Size, 1113 false, NULL); 1114 if (!NT_SUCCESS(Status)) 1115 ERR("IOCTL_MOUNTMGR_QUERY_POINTS returned %08lx\n", Status); 1116 else 1117 mountmgr_updated(mountmgr, mmps2); 1118 1119 ExFreePool(mmps2); 1120 } 1121 } 1122 1123 ObDereferenceObject(FileObject); 1124 1125 mountmgr_thread_handle = NULL; 1126 1127 PsTerminateSystemThread(STATUS_SUCCESS); 1128 } 1129