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