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