1 /* 2 * ReactOS kernel 3 * Copyright (C) 2011-2012 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program 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 General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 * 19 * COPYRIGHT: See COPYING in the top level directory 20 * PROJECT: ReactOS kernel 21 * FILE: drivers/filesystem/mountmgr/mountmgr.c 22 * PURPOSE: Mount Manager - remote/local database handler 23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) 24 */ 25 26 #include "mntmgr.h" 27 28 #define NDEBUG 29 #include <debug.h> 30 31 PWSTR DatabasePath = L"\\Registry\\Machine\\System\\MountedDevices"; 32 PWSTR OfflinePath = L"\\Registry\\Machine\\System\\MountedDevices\\Offline"; 33 34 UNICODE_STRING RemoteDatabase = RTL_CONSTANT_STRING(L"\\System Volume Information\\MountPointManagerRemoteDatabase"); 35 36 /* 37 * @implemented 38 */ 39 LONG 40 GetRemoteDatabaseSize(IN HANDLE Database) 41 { 42 NTSTATUS Status; 43 IO_STATUS_BLOCK IoStatusBlock; 44 FILE_STANDARD_INFORMATION StandardInfo; 45 46 /* Just query the size */ 47 Status = ZwQueryInformationFile(Database, 48 &IoStatusBlock, 49 &StandardInfo, 50 sizeof(FILE_STANDARD_INFORMATION), 51 FileStandardInformation); 52 if (NT_SUCCESS(Status)) 53 { 54 return StandardInfo.EndOfFile.LowPart; 55 } 56 57 return 0; 58 } 59 60 /* 61 * @implemented 62 */ 63 NTSTATUS 64 AddRemoteDatabaseEntry(IN HANDLE Database, 65 IN PDATABASE_ENTRY Entry) 66 { 67 LARGE_INTEGER Size; 68 IO_STATUS_BLOCK IoStatusBlock; 69 70 /* Get size to append data */ 71 Size.QuadPart = GetRemoteDatabaseSize(Database); 72 73 return ZwWriteFile(Database, NULL, NULL, NULL, 74 &IoStatusBlock, Entry, 75 Entry->EntrySize, &Size, NULL); 76 } 77 78 /* 79 * @implemented 80 */ 81 NTSTATUS 82 CloseRemoteDatabase(IN HANDLE Database) 83 { 84 return ZwClose(Database); 85 } 86 87 /* 88 * @implemented 89 */ 90 NTSTATUS 91 TruncateRemoteDatabase(IN HANDLE Database, 92 IN LONG NewSize) 93 { 94 NTSTATUS Status; 95 IO_STATUS_BLOCK IoStatusBlock; 96 FILE_END_OF_FILE_INFORMATION EndOfFile; 97 FILE_ALLOCATION_INFORMATION Allocation; 98 99 EndOfFile.EndOfFile.QuadPart = NewSize; 100 Allocation.AllocationSize.QuadPart = NewSize; 101 102 /* First set EOF */ 103 Status = ZwSetInformationFile(Database, 104 &IoStatusBlock, 105 &EndOfFile, 106 sizeof(FILE_END_OF_FILE_INFORMATION), 107 FileEndOfFileInformation); 108 if (NT_SUCCESS(Status)) 109 { 110 /* And then, properly set allocation information */ 111 Status = ZwSetInformationFile(Database, 112 &IoStatusBlock, 113 &Allocation, 114 sizeof(FILE_ALLOCATION_INFORMATION), 115 FileAllocationInformation); 116 } 117 118 return Status; 119 } 120 121 /* 122 * @implemented 123 */ 124 PDATABASE_ENTRY 125 GetRemoteDatabaseEntry(IN HANDLE Database, 126 IN LONG StartingOffset) 127 { 128 NTSTATUS Status; 129 ULONG EntrySize; 130 PDATABASE_ENTRY Entry; 131 LARGE_INTEGER ByteOffset; 132 IO_STATUS_BLOCK IoStatusBlock; 133 134 /* Get the entry at the given position */ 135 ByteOffset.QuadPart = StartingOffset; 136 Status = ZwReadFile(Database, 137 NULL, 138 NULL, 139 NULL, 140 &IoStatusBlock, 141 &EntrySize, 142 sizeof(EntrySize), 143 &ByteOffset, 144 NULL); 145 if (!NT_SUCCESS(Status)) 146 { 147 return NULL; 148 } 149 150 /* If entry doesn't exist, truncate database */ 151 if (!EntrySize) 152 { 153 TruncateRemoteDatabase(Database, StartingOffset); 154 return NULL; 155 } 156 157 /* Allocate the entry */ 158 Entry = AllocatePool(EntrySize); 159 if (!Entry) 160 { 161 return NULL; 162 } 163 164 /* Effectively read the entry */ 165 Status = ZwReadFile(Database, 166 NULL, 167 NULL, 168 NULL, 169 &IoStatusBlock, 170 Entry, 171 EntrySize, 172 &ByteOffset, 173 NULL); 174 /* If it fails or returns inconsistent data, drop it (= truncate) */ 175 if (!NT_SUCCESS(Status) || 176 (IoStatusBlock.Information != EntrySize) || 177 (EntrySize < sizeof(DATABASE_ENTRY)) ) 178 { 179 TruncateRemoteDatabase(Database, StartingOffset); 180 FreePool(Entry); 181 return NULL; 182 } 183 184 /* Validate entry */ 185 if (MAX(Entry->SymbolicNameOffset + Entry->SymbolicNameLength, 186 Entry->UniqueIdOffset + Entry->UniqueIdLength) > (LONG)EntrySize) 187 { 188 TruncateRemoteDatabase(Database, StartingOffset); 189 FreePool(Entry); 190 return NULL; 191 } 192 193 return Entry; 194 } 195 196 /* 197 * @implemented 198 */ 199 NTSTATUS 200 WriteRemoteDatabaseEntry(IN HANDLE Database, 201 IN LONG Offset, 202 IN PDATABASE_ENTRY Entry) 203 { 204 NTSTATUS Status; 205 LARGE_INTEGER ByteOffset; 206 IO_STATUS_BLOCK IoStatusBlock; 207 208 ByteOffset.QuadPart = Offset; 209 Status = ZwWriteFile(Database, 210 NULL, 211 NULL, 212 NULL, 213 &IoStatusBlock, 214 Entry, 215 Entry->EntrySize, 216 &ByteOffset, 217 NULL); 218 if (NT_SUCCESS(Status)) 219 { 220 if (IoStatusBlock.Information < Entry->EntrySize) 221 { 222 Status = STATUS_INSUFFICIENT_RESOURCES; 223 } 224 } 225 226 return Status; 227 } 228 229 /* 230 * @implemented 231 */ 232 NTSTATUS 233 DeleteRemoteDatabaseEntry(IN HANDLE Database, 234 IN LONG StartingOffset) 235 { 236 ULONG EndSize; 237 PVOID TmpBuffer; 238 NTSTATUS Status; 239 ULONG DatabaseSize; 240 PDATABASE_ENTRY Entry; 241 IO_STATUS_BLOCK IoStatusBlock; 242 LARGE_INTEGER EndEntriesOffset; 243 244 /* First, get database size */ 245 DatabaseSize = GetRemoteDatabaseSize(Database); 246 if (!DatabaseSize) 247 { 248 return STATUS_INVALID_PARAMETER; 249 } 250 251 /* Then, get the entry to remove */ 252 Entry = GetRemoteDatabaseEntry(Database, StartingOffset); 253 if (!Entry) 254 { 255 return STATUS_INVALID_PARAMETER; 256 } 257 258 /* Validate parameters: ensure we won't get negative size */ 259 if (Entry->EntrySize + StartingOffset > DatabaseSize) 260 { 261 /* If we get invalid parameters, truncate the whole database 262 * starting the wrong entry. We can't rely on the rest 263 */ 264 FreePool(Entry); 265 return TruncateRemoteDatabase(Database, StartingOffset); 266 } 267 268 /* Now, get the size of the remaining entries (those after the one to remove) */ 269 EndSize = DatabaseSize - Entry->EntrySize - StartingOffset; 270 /* Allocate a buffer big enough to hold them */ 271 TmpBuffer = AllocatePool(EndSize); 272 if (!TmpBuffer) 273 { 274 FreePool(Entry); 275 return STATUS_INSUFFICIENT_RESOURCES; 276 } 277 278 /* Get the offset of the entry right after the one to delete */ 279 EndEntriesOffset.QuadPart = Entry->EntrySize + StartingOffset; 280 /* We don't need the entry any more */ 281 FreePool(Entry); 282 283 /* Read the ending entries */ 284 Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock, 285 TmpBuffer, EndSize, &EndEntriesOffset, NULL); 286 if (!NT_SUCCESS(Status)) 287 { 288 FreePool(TmpBuffer); 289 return Status; 290 } 291 292 /* Ensure nothing went wrong - we don't want to corrupt the DB */ 293 if (IoStatusBlock.Information != EndSize) 294 { 295 FreePool(TmpBuffer); 296 return STATUS_INVALID_PARAMETER; 297 } 298 299 /* Remove the entry */ 300 Status = TruncateRemoteDatabase(Database, StartingOffset + EndSize); 301 if (!NT_SUCCESS(Status)) 302 { 303 FreePool(TmpBuffer); 304 return Status; 305 } 306 307 /* Now, shift the ending entries to erase the entry */ 308 EndEntriesOffset.QuadPart = StartingOffset; 309 Status = ZwWriteFile(Database, NULL, NULL, NULL, &IoStatusBlock, 310 TmpBuffer, EndSize, &EndEntriesOffset, NULL); 311 312 FreePool(TmpBuffer); 313 314 return Status; 315 } 316 317 /* 318 * @implemented 319 */ 320 NTSTATUS 321 NTAPI 322 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName, 323 IN ULONG ValueType, 324 IN PVOID ValueData, 325 IN ULONG ValueLength, 326 IN PVOID Context, 327 IN PVOID EntryContext) 328 { 329 PMOUNTDEV_UNIQUE_ID UniqueId = Context; 330 331 UNREFERENCED_PARAMETER(ValueType); 332 UNREFERENCED_PARAMETER(EntryContext); 333 334 /* Ensure it matches, and delete */ 335 if ((UniqueId->UniqueIdLength == ValueLength) && 336 (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) == 337 ValueLength)) 338 { 339 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, 340 DatabasePath, 341 ValueName); 342 } 343 344 return STATUS_SUCCESS; 345 } 346 347 /* 348 * @implemented 349 */ 350 VOID 351 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink, 352 IN PMOUNTDEV_UNIQUE_ID UniqueId) 353 { 354 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 355 356 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 357 QueryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine; 358 QueryTable[0].Name = SymbolicLink->Buffer; 359 360 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 361 DatabasePath, 362 QueryTable, 363 UniqueId, 364 NULL); 365 } 366 367 /* 368 * @implemented 369 */ 370 NTSTATUS 371 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension) 372 { 373 NTSTATUS Status; 374 LARGE_INTEGER Timeout; 375 376 /* Wait for 7 minutes */ 377 Timeout.QuadPart = 0xFA0A1F00; 378 Status = KeWaitForSingleObject(&(DeviceExtension->RemoteDatabaseLock), Executive, KernelMode, FALSE, &Timeout); 379 if (Status != STATUS_TIMEOUT) 380 { 381 return Status; 382 } 383 384 return STATUS_IO_TIMEOUT; 385 } 386 387 /* 388 * @implemented 389 */ 390 VOID 391 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension) 392 { 393 KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE); 394 } 395 396 /* 397 * @implemented 398 */ 399 NTSTATUS 400 NTAPI 401 QueryUniqueIdQueryRoutine(IN PWSTR ValueName, 402 IN ULONG ValueType, 403 IN PVOID ValueData, 404 IN ULONG ValueLength, 405 IN PVOID Context, 406 IN PVOID EntryContext) 407 { 408 PMOUNTDEV_UNIQUE_ID IntUniqueId; 409 PMOUNTDEV_UNIQUE_ID * UniqueId; 410 411 UNREFERENCED_PARAMETER(ValueName); 412 UNREFERENCED_PARAMETER(ValueType); 413 UNREFERENCED_PARAMETER(EntryContext); 414 415 /* Sanity check */ 416 if (ValueLength >= 0x10000) 417 { 418 return STATUS_SUCCESS; 419 } 420 421 /* Allocate the Unique ID */ 422 IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength); 423 if (IntUniqueId) 424 { 425 /* Copy data & return */ 426 IntUniqueId->UniqueIdLength = (USHORT)ValueLength; 427 RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength); 428 429 UniqueId = Context; 430 *UniqueId = IntUniqueId; 431 } 432 433 return STATUS_SUCCESS; 434 } 435 436 /* 437 * @implemented 438 */ 439 NTSTATUS 440 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension, 441 IN PUNICODE_STRING SymbolicName, 442 OUT PMOUNTDEV_UNIQUE_ID * UniqueId) 443 { 444 NTSTATUS Status; 445 PDEVICE_INFORMATION DeviceInformation; 446 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 447 448 /* Query the unique ID */ 449 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 450 QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine; 451 QueryTable[0].Name = SymbolicName->Buffer; 452 453 *UniqueId = NULL; 454 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 455 DatabasePath, 456 QueryTable, 457 UniqueId, 458 NULL); 459 /* Unique ID found, no need to go farther */ 460 if (*UniqueId) 461 { 462 return STATUS_SUCCESS; 463 } 464 465 /* Otherwise, find associate device information */ 466 Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation); 467 if (!NT_SUCCESS(Status)) 468 { 469 return Status; 470 } 471 472 *UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); 473 if (!*UniqueId) 474 { 475 return STATUS_INSUFFICIENT_RESOURCES; 476 } 477 478 /* Return this unique ID (better than nothing) */ 479 (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength; 480 RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength); 481 482 return STATUS_SUCCESS; 483 } 484 485 /* 486 * @implemented 487 */ 488 NTSTATUS 489 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension, 490 IN PDATABASE_ENTRY DatabaseEntry) 491 { 492 NTSTATUS Status; 493 PWCHAR SymbolicName; 494 PLIST_ENTRY NextEntry; 495 UNICODE_STRING SymbolicString; 496 PDEVICE_INFORMATION DeviceInformation; 497 498 /* Create symbolic name from database entry */ 499 SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR)); 500 if (!SymbolicName) 501 { 502 return STATUS_INSUFFICIENT_RESOURCES; 503 } 504 505 RtlCopyMemory(SymbolicName, 506 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), 507 DatabaseEntry->SymbolicNameLength); 508 SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL; 509 510 /* Associate the unique ID with the name from remote database */ 511 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, 512 DatabasePath, 513 SymbolicName, 514 REG_BINARY, 515 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), 516 DatabaseEntry->UniqueIdLength); 517 FreePool(SymbolicName); 518 519 /* Reget symbolic name */ 520 SymbolicString.Length = DatabaseEntry->SymbolicNameLength; 521 SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength; 522 SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset); 523 524 /* Find the device using this unique ID */ 525 for (NextEntry = DeviceExtension->DeviceListHead.Flink; 526 NextEntry != &(DeviceExtension->DeviceListHead); 527 NextEntry = NextEntry->Flink) 528 { 529 DeviceInformation = CONTAINING_RECORD(NextEntry, 530 DEVICE_INFORMATION, 531 DeviceListEntry); 532 533 if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength) 534 { 535 continue; 536 } 537 538 if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), 539 DeviceInformation->UniqueId->UniqueId, 540 DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength) 541 { 542 break; 543 } 544 } 545 546 /* If found, create a mount point */ 547 if (NextEntry != &(DeviceExtension->DeviceListHead)) 548 { 549 MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName)); 550 } 551 552 return Status; 553 } 554 555 /* 556 * @implemented 557 */ 558 VOID 559 NTAPI 560 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter) 561 { 562 ULONG Offset; 563 NTSTATUS Status; 564 PFILE_OBJECT FileObject; 565 PDEVICE_OBJECT DeviceObject; 566 PMOUNTDEV_UNIQUE_ID UniqueId; 567 PDATABASE_ENTRY DatabaseEntry; 568 HANDLE DatabaseHandle, Handle; 569 IO_STATUS_BLOCK IoStatusBlock; 570 OBJECT_ATTRIBUTES ObjectAttributes; 571 PDEVICE_INFORMATION ListDeviceInfo; 572 PLIST_ENTRY Entry, EntryInfo, NextEntry; 573 PASSOCIATED_DEVICE_ENTRY AssociatedDevice; 574 BOOLEAN HardwareErrors, Restart, FailedFinding; 575 WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[100]; 576 UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName; 577 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation; 578 PDEVICE_EXTENSION DeviceExtension = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceExtension; 579 PDEVICE_INFORMATION DeviceInformation = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceInformation; 580 581 /* We're unloading, do nothing */ 582 if (Unloading) 583 { 584 return; 585 } 586 587 /* Lock remote DB */ 588 if (!NT_SUCCESS(WaitForRemoteDatabaseSemaphore(DeviceExtension))) 589 { 590 return; 591 } 592 593 /* Recheck for unloading */ 594 if (Unloading) 595 { 596 goto ReleaseRDS; 597 } 598 599 /* Find the DB to reconcile */ 600 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); 601 for (Entry = DeviceExtension->DeviceListHead.Flink; 602 Entry != &DeviceExtension->DeviceListHead; 603 Entry = Entry->Flink) 604 { 605 ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry); 606 if (ListDeviceInfo == DeviceInformation) 607 { 608 break; 609 } 610 } 611 612 /* If not found, or if removable, bail out */ 613 if (Entry == &DeviceExtension->DeviceListHead || DeviceInformation->Removable) 614 { 615 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 616 goto ReleaseRDS; 617 } 618 619 /* Get our device object */ 620 Status = IoGetDeviceObjectPointer(&ListDeviceInfo->DeviceName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); 621 if (!NT_SUCCESS(Status)) 622 { 623 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 624 goto ReleaseRDS; 625 } 626 627 /* Mark mounted only if not unloading */ 628 if (!(DeviceObject->Flags & DO_UNLOAD_PENDING)) 629 { 630 InterlockedExchangeAdd(&ListDeviceInfo->MountState, 1); 631 } 632 633 ObDereferenceObject(FileObject); 634 635 /* Force default: no DB, and need for reconcile */ 636 DeviceInformation->NeedsReconcile = TRUE; 637 DeviceInformation->NoDatabase = TRUE; 638 FailedFinding = FALSE; 639 640 /* Remove any associated device that refers to the DB to reconcile */ 641 for (Entry = DeviceExtension->DeviceListHead.Flink; 642 Entry != &DeviceExtension->DeviceListHead; 643 Entry = Entry->Flink) 644 { 645 ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry); 646 647 EntryInfo = ListDeviceInfo->AssociatedDevicesHead.Flink; 648 while (EntryInfo != &ListDeviceInfo->AssociatedDevicesHead) 649 { 650 AssociatedDevice = CONTAINING_RECORD(EntryInfo, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); 651 NextEntry = EntryInfo->Flink; 652 653 if (AssociatedDevice->DeviceInformation == DeviceInformation) 654 { 655 RemoveEntryList(&AssociatedDevice->AssociatedDevicesEntry); 656 FreePool(AssociatedDevice->String.Buffer); 657 FreePool(AssociatedDevice); 658 } 659 660 EntryInfo = NextEntry; 661 } 662 } 663 664 /* Open the remote database */ 665 DatabaseHandle = OpenRemoteDatabase(DeviceInformation, FALSE); 666 667 /* Prepare a string with reparse point index */ 668 ReparseFile.Length = 0; 669 ReparseFile.MaximumLength = DeviceInformation->DeviceName.Length 670 + ReparseIndex.Length 671 + sizeof(UNICODE_NULL); 672 ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength); 673 if (!ReparseFile.Buffer) 674 { 675 if (DatabaseHandle != 0) 676 { 677 CloseRemoteDatabase(DatabaseHandle); 678 } 679 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 680 goto ReleaseRDS; 681 } 682 683 RtlAppendUnicodeStringToString(&ReparseFile, &DeviceInformation->DeviceName); 684 RtlAppendUnicodeStringToString(&ReparseFile, &ReparseIndex); 685 ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL; 686 687 InitializeObjectAttributes(&ObjectAttributes, 688 &ReparseFile, 689 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 690 NULL, 691 NULL); 692 693 /* Open reparse point directory */ 694 HardwareErrors = IoSetThreadHardErrorMode(FALSE); 695 Status = ZwOpenFile(&Handle, 696 FILE_GENERIC_READ, 697 &ObjectAttributes, 698 &IoStatusBlock, 699 FILE_SHARE_READ | FILE_SHARE_WRITE, 700 FILE_SYNCHRONOUS_IO_ALERT); 701 IoSetThreadHardErrorMode(HardwareErrors); 702 703 FreePool(ReparseFile.Buffer); 704 705 if (!NT_SUCCESS(Status)) 706 { 707 if (DatabaseHandle != 0) 708 { 709 TruncateRemoteDatabase(DatabaseHandle, 0); 710 CloseRemoteDatabase(DatabaseHandle); 711 } 712 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 713 goto ReleaseRDS; 714 } 715 716 /* Query reparse point information 717 * We only pay attention to mout point 718 */ 719 RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer)); 720 FileName.Buffer = FileNameBuffer; 721 FileName.Length = sizeof(FileNameBuffer); 722 FileName.MaximumLength = sizeof(FileNameBuffer); 723 ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT; 724 Status = ZwQueryDirectoryFile(Handle, 725 NULL, 726 NULL, 727 NULL, 728 &IoStatusBlock, 729 &ReparsePointInformation, 730 sizeof(FILE_REPARSE_POINT_INFORMATION), 731 FileReparsePointInformation, 732 TRUE, 733 &FileName, 734 FALSE); 735 if (!NT_SUCCESS(Status)) 736 { 737 ZwClose(Handle); 738 if (DatabaseHandle != 0) 739 { 740 TruncateRemoteDatabase(DatabaseHandle, 0); 741 CloseRemoteDatabase(DatabaseHandle); 742 } 743 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 744 goto ReleaseRDS; 745 } 746 747 /* If we failed to open the remote DB previously, 748 * retry this time allowing migration (and thus, creation if required) 749 */ 750 if (DatabaseHandle == 0) 751 { 752 DatabaseHandle = OpenRemoteDatabase(DeviceInformation, TRUE); 753 if (DatabaseHandle == 0) 754 { 755 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 756 goto ReleaseRDS; 757 } 758 } 759 760 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 761 762 /* Reset all the references to our DB entries */ 763 Offset = 0; 764 for (;;) 765 { 766 DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset); 767 if (DatabaseEntry == NULL) 768 { 769 break; 770 } 771 772 DatabaseEntry->EntryReferences = 0; 773 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); 774 if (!NT_SUCCESS(Status)) 775 { 776 FreePool(DatabaseEntry); 777 goto CloseReparse; 778 } 779 780 Offset += DatabaseEntry->EntrySize; 781 FreePool(DatabaseEntry); 782 } 783 784 /* Init string for QueryVolumeName call */ 785 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer); 786 SymbolicName.Length = 0; 787 SymbolicName.Buffer = SymbolicNameBuffer; 788 Restart = TRUE; 789 790 /* Start looping on reparse points */ 791 for (;;) 792 { 793 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION)); 794 Status = ZwQueryDirectoryFile(Handle, 795 NULL, 796 NULL, 797 NULL, 798 &IoStatusBlock, 799 &ReparsePointInformation, 800 sizeof(FILE_REPARSE_POINT_INFORMATION), 801 FileReparsePointInformation, 802 TRUE, 803 Restart ? &FileName : NULL, 804 Restart); 805 /* Restart only once */ 806 if (Restart) 807 { 808 Restart = FALSE; 809 } 810 else 811 { 812 /* If we get the same one, we're done, bail out */ 813 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference && 814 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag) 815 { 816 break; 817 } 818 } 819 820 /* If querying failed, or if onloading, or if not returning mount points, bail out */ 821 if (!NT_SUCCESS(Status) || Unloading || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT) 822 { 823 break; 824 } 825 826 /* Get the volume name associated to the mount point */ 827 Status = QueryVolumeName(Handle, &ReparsePointInformation, 0, &SymbolicName, &VolumeName); 828 if (!NT_SUCCESS(Status)) 829 { 830 continue; 831 } 832 833 /* Browse the DB to find the name */ 834 Offset = 0; 835 for (;;) 836 { 837 UNICODE_STRING DbName; 838 839 DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset); 840 if (DatabaseEntry == NULL) 841 { 842 break; 843 } 844 845 DbName.MaximumLength = DatabaseEntry->SymbolicNameLength; 846 DbName.Length = DbName.MaximumLength; 847 DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset); 848 /* Found, we're done! */ 849 if (RtlEqualUnicodeString(&DbName, &SymbolicName, TRUE)) 850 { 851 break; 852 } 853 854 Offset += DatabaseEntry->EntrySize; 855 FreePool(DatabaseEntry); 856 } 857 858 /* If we found the mount point.... */ 859 if (DatabaseEntry != NULL) 860 { 861 /* If it was referenced, reference it once more and update to remote */ 862 if (DatabaseEntry->EntryReferences) 863 { 864 ++DatabaseEntry->EntryReferences; 865 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); 866 if (!NT_SUCCESS(Status)) 867 { 868 goto FreeDBEntry; 869 } 870 871 FreePool(DatabaseEntry); 872 } 873 else 874 { 875 /* Query the Unique ID associated to that mount point in case it changed */ 876 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); 877 Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId); 878 if (!NT_SUCCESS(Status)) 879 { 880 /* If we failed doing so, reuse the old Unique ID and push it to master */ 881 Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry); 882 if (!NT_SUCCESS(Status)) 883 { 884 goto ReleaseDeviceLock; 885 } 886 887 /* And then, reference & write the entry */ 888 ++DatabaseEntry->EntryReferences; 889 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); 890 if (!NT_SUCCESS(Status)) 891 { 892 goto ReleaseDeviceLock; 893 } 894 895 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 896 FreePool(DatabaseEntry); 897 } 898 /* If the Unique ID didn't change */ 899 else if (UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength && 900 RtlCompareMemory(UniqueId->UniqueId, 901 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), 902 UniqueId->UniqueIdLength) == UniqueId->UniqueIdLength) 903 { 904 /* Reference the entry, and update to remote */ 905 ++DatabaseEntry->EntryReferences; 906 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); 907 if (!NT_SUCCESS(Status)) 908 { 909 goto FreeUniqueId; 910 } 911 912 FreePool(UniqueId); 913 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 914 FreePool(DatabaseEntry); 915 } 916 /* Would, by chance, the Unique ID be present elsewhere? */ 917 else if (IsUniqueIdPresent(DeviceExtension, DatabaseEntry)) 918 { 919 /* Push the ID to master */ 920 Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry); 921 if (!NT_SUCCESS(Status)) 922 { 923 goto FreeUniqueId; 924 } 925 926 /* And then, reference & write the entry */ 927 ++DatabaseEntry->EntryReferences; 928 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); 929 if (!NT_SUCCESS(Status)) 930 { 931 goto FreeUniqueId; 932 } 933 934 FreePool(UniqueId); 935 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 936 FreePool(DatabaseEntry); 937 } 938 else 939 { 940 /* OK, at that point, we're facing a totally unknown unique ID 941 * So, get rid of the old entry, and recreate a new one with 942 * the know unique ID 943 */ 944 Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset); 945 if (!NT_SUCCESS(Status)) 946 { 947 goto FreeUniqueId; 948 } 949 950 FreePool(DatabaseEntry); 951 /* Allocate a new entry big enough */ 952 DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY)); 953 if (DatabaseEntry == NULL) 954 { 955 goto FreeUniqueId; 956 } 957 958 /* Configure it */ 959 DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY); 960 DatabaseEntry->EntryReferences = 1; 961 DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); 962 DatabaseEntry->SymbolicNameLength = SymbolicName.Length; 963 DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY); 964 DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength; 965 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength); 966 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength); 967 968 /* And write it remotely */ 969 Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry); 970 if (!NT_SUCCESS(Status)) 971 { 972 FreePool(DatabaseEntry); 973 goto FreeUniqueId; 974 } 975 976 FreePool(UniqueId); 977 FreePool(DatabaseEntry); 978 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 979 } 980 } 981 } 982 else 983 { 984 /* We failed finding it remotely 985 * So, let's allocate a new remote DB entry 986 */ 987 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); 988 /* To be able to do so, we need the device Unique ID, ask master */ 989 Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId); 990 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 991 if (NT_SUCCESS(Status)) 992 { 993 /* Allocate a new entry big enough */ 994 DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY)); 995 if (DatabaseEntry != NULL) 996 { 997 /* Configure it */ 998 DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY); 999 DatabaseEntry->EntryReferences = 1; 1000 DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); 1001 DatabaseEntry->SymbolicNameLength = SymbolicName.Length; 1002 DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY); 1003 DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength; 1004 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength); 1005 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength); 1006 1007 /* And write it remotely */ 1008 Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry); 1009 FreePool(DatabaseEntry); 1010 FreePool(UniqueId); 1011 1012 if (!NT_SUCCESS(Status)) 1013 { 1014 goto FreeVolume; 1015 } 1016 } 1017 else 1018 { 1019 FreePool(UniqueId); 1020 } 1021 } 1022 } 1023 1024 /* Find info about the device associated associated with the mount point */ 1025 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); 1026 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &ListDeviceInfo); 1027 if (!NT_SUCCESS(Status)) 1028 { 1029 FailedFinding = TRUE; 1030 FreePool(VolumeName.Buffer); 1031 } 1032 else 1033 { 1034 /* Associate the device with the currrent DB */ 1035 AssociatedDevice = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY)); 1036 if (AssociatedDevice == NULL) 1037 { 1038 FreePool(VolumeName.Buffer); 1039 } 1040 else 1041 { 1042 AssociatedDevice->DeviceInformation = DeviceInformation; 1043 AssociatedDevice->String.Length = VolumeName.Length; 1044 AssociatedDevice->String.MaximumLength = VolumeName.MaximumLength; 1045 AssociatedDevice->String.Buffer = VolumeName.Buffer; 1046 InsertTailList(&ListDeviceInfo->AssociatedDevicesHead, &AssociatedDevice->AssociatedDevicesEntry); 1047 } 1048 1049 /* If we don't have to skip notifications, notify */ 1050 if (!ListDeviceInfo->SkipNotifications) 1051 { 1052 PostOnlineNotification(DeviceExtension, &ListDeviceInfo->SymbolicName); 1053 } 1054 } 1055 1056 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 1057 } 1058 1059 /* We don't need mount points any longer */ 1060 ZwClose(Handle); 1061 1062 /* Look for the DB again */ 1063 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); 1064 for (Entry = DeviceExtension->DeviceListHead.Flink; 1065 Entry != &DeviceExtension->DeviceListHead; 1066 Entry = Entry->Flink) 1067 { 1068 ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry); 1069 if (ListDeviceInfo == DeviceInformation) 1070 { 1071 break; 1072 } 1073 } 1074 1075 if (Entry == &DeviceExtension->DeviceListHead) 1076 { 1077 ListDeviceInfo = NULL; 1078 } 1079 1080 /* Start the pruning loop */ 1081 Offset = 0; 1082 for (;;) 1083 { 1084 /* Get the entry */ 1085 DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset); 1086 if (DatabaseEntry == NULL) 1087 { 1088 break; 1089 } 1090 1091 /* It's not referenced anylonger? Prune it */ 1092 if (DatabaseEntry->EntryReferences == 0) 1093 { 1094 Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset); 1095 if (!NT_SUCCESS(Status)) 1096 { 1097 FreePool(DatabaseEntry); 1098 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 1099 goto CloseRDB; 1100 } 1101 } 1102 /* Update the Unique IDs to reflect the changes we might have done previously */ 1103 else 1104 { 1105 if (ListDeviceInfo != NULL) 1106 { 1107 UpdateReplicatedUniqueIds(ListDeviceInfo, DatabaseEntry); 1108 } 1109 1110 Offset += DatabaseEntry->EntrySize; 1111 } 1112 1113 FreePool(DatabaseEntry); 1114 } 1115 1116 /* We do have a DB now :-) */ 1117 if (ListDeviceInfo != NULL && !FailedFinding) 1118 { 1119 DeviceInformation->NoDatabase = FALSE; 1120 } 1121 1122 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 1123 1124 goto CloseRDB; 1125 1126 FreeUniqueId: 1127 FreePool(UniqueId); 1128 ReleaseDeviceLock: 1129 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 1130 FreeDBEntry: 1131 FreePool(DatabaseEntry); 1132 FreeVolume: 1133 FreePool(VolumeName.Buffer); 1134 CloseReparse: 1135 ZwClose(Handle); 1136 CloseRDB: 1137 CloseRemoteDatabase(DatabaseHandle); 1138 ReleaseRDS: 1139 ReleaseRemoteDatabaseSemaphore(DeviceExtension); 1140 return; 1141 } 1142 1143 /* 1144 * @implemented 1145 */ 1146 VOID 1147 NTAPI 1148 WorkerThread(IN PDEVICE_OBJECT DeviceObject, 1149 IN PVOID Context) 1150 { 1151 ULONG i; 1152 KEVENT Event; 1153 KIRQL OldIrql; 1154 NTSTATUS Status; 1155 HANDLE SafeEvent; 1156 PLIST_ENTRY Entry; 1157 LARGE_INTEGER Timeout; 1158 PRECONCILE_WORK_ITEM WorkItem; 1159 PDEVICE_EXTENSION DeviceExtension; 1160 OBJECT_ATTRIBUTES ObjectAttributes; 1161 1162 UNREFERENCED_PARAMETER(DeviceObject); 1163 1164 InitializeObjectAttributes(&ObjectAttributes, 1165 &SafeVolumes, 1166 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 1167 NULL, 1168 NULL); 1169 KeInitializeEvent(&Event, NotificationEvent, FALSE); 1170 Timeout.QuadPart = -10000000LL; /* Wait for 1 second */ 1171 1172 /* Wait as long as possible for clearance from autochk 1173 * We will write remote databases only if it is safe 1174 * to access volumes. 1175 * First, given we start before SMSS, wait for the 1176 * event creation. 1177 */ 1178 i = 0; 1179 do 1180 { 1181 /* If we started to shutdown, stop waiting forever and jump to last attempt */ 1182 if (Unloading) 1183 { 1184 i = 999; 1185 } 1186 else 1187 { 1188 /* Attempt to open the event */ 1189 Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes); 1190 if (NT_SUCCESS(Status)) 1191 { 1192 break; 1193 } 1194 1195 /* Wait a bit to give SMSS a chance to create the event */ 1196 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout); 1197 } 1198 1199 ++i; 1200 } 1201 while (i < 1000); 1202 1203 /* We managed to open the event, wait until autochk signals it */ 1204 if (i < 1000) 1205 { 1206 do 1207 { 1208 Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout); 1209 } 1210 while (Status == STATUS_TIMEOUT && !Unloading); 1211 1212 ZwClose(SafeEvent); 1213 } 1214 1215 DeviceExtension = Context; 1216 1217 InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1); 1218 1219 /* Acquire workers lock */ 1220 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL); 1221 1222 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql); 1223 1224 /* Ensure there are workers */ 1225 while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead))) 1226 { 1227 /* Unqueue a worker */ 1228 Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead)); 1229 WorkItem = CONTAINING_RECORD(Entry, 1230 RECONCILE_WORK_ITEM, 1231 WorkerQueueListEntry); 1232 1233 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); 1234 1235 /* Call it */ 1236 WorkItem->WorkerRoutine(WorkItem->Context); 1237 1238 IoFreeWorkItem(WorkItem->WorkItem); 1239 FreePool(WorkItem); 1240 1241 if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) < 0) 1242 { 1243 return; 1244 } 1245 1246 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL); 1247 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql); 1248 } 1249 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); 1250 1251 InterlockedDecrement(&(DeviceExtension->WorkerReferences)); 1252 1253 /* Reset event */ 1254 KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE); 1255 } 1256 1257 /* 1258 * @implemented 1259 */ 1260 NTSTATUS 1261 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension, 1262 IN PRECONCILE_WORK_ITEM WorkItem, 1263 IN PVOID Context) 1264 { 1265 KIRQL OldIrql; 1266 1267 WorkItem->Context = Context; 1268 1269 /* When called, lock is already acquired */ 1270 1271 /* If noone (-1 as references), start to work */ 1272 if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)) == 0) 1273 { 1274 IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension); 1275 } 1276 1277 /* Otherwise queue worker for delayed execution */ 1278 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql); 1279 InsertTailList(&(DeviceExtension->WorkerQueueListHead), 1280 &(WorkItem->WorkerQueueListEntry)); 1281 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); 1282 1283 KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE); 1284 1285 return STATUS_SUCCESS; 1286 } 1287 1288 /* 1289 * @implemented 1290 */ 1291 NTSTATUS 1292 QueryVolumeName(IN HANDLE RootDirectory, 1293 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation, 1294 IN PUNICODE_STRING FileName OPTIONAL, 1295 OUT PUNICODE_STRING SymbolicName, 1296 OUT PUNICODE_STRING VolumeName) 1297 { 1298 HANDLE Handle; 1299 NTSTATUS Status; 1300 ULONG NeededLength; 1301 IO_STATUS_BLOCK IoStatusBlock; 1302 OBJECT_ATTRIBUTES ObjectAttributes; 1303 PFILE_NAME_INFORMATION FileNameInfo; 1304 PREPARSE_DATA_BUFFER ReparseDataBuffer; 1305 1306 UNREFERENCED_PARAMETER(ReparsePointInformation); 1307 1308 if (!FileName) 1309 { 1310 InitializeObjectAttributes(&ObjectAttributes, 1311 NULL, 1312 OBJ_KERNEL_HANDLE, 1313 RootDirectory, 1314 NULL); 1315 } 1316 else 1317 { 1318 InitializeObjectAttributes(&ObjectAttributes, 1319 FileName, 1320 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 1321 NULL, 1322 NULL); 1323 } 1324 1325 /* Open volume */ 1326 Status = ZwOpenFile(&Handle, 1327 SYNCHRONIZE | FILE_READ_ATTRIBUTES, 1328 &ObjectAttributes, 1329 &IoStatusBlock, 1330 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1331 (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT : 1332 FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT); 1333 if (!NT_SUCCESS(Status)) 1334 { 1335 return Status; 1336 } 1337 1338 /* Get the reparse point data */ 1339 ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 1340 if (!ReparseDataBuffer) 1341 { 1342 ZwClose(Handle); 1343 return STATUS_INSUFFICIENT_RESOURCES; 1344 } 1345 1346 Status = ZwFsControlFile(Handle, 1347 0, 1348 NULL, 1349 NULL, 1350 &IoStatusBlock, 1351 FSCTL_GET_REPARSE_POINT, 1352 NULL, 1353 0, 1354 ReparseDataBuffer, 1355 MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 1356 if (!NT_SUCCESS(Status)) 1357 { 1358 FreePool(ReparseDataBuffer); 1359 ZwClose(Handle); 1360 return Status; 1361 } 1362 1363 /* Check that name can fit in buffer */ 1364 if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength) 1365 { 1366 FreePool(ReparseDataBuffer); 1367 ZwClose(Handle); 1368 return STATUS_BUFFER_TOO_SMALL; 1369 } 1370 1371 /* Copy symbolic name */ 1372 SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength; 1373 RtlCopyMemory(SymbolicName->Buffer, 1374 (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + 1375 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset), 1376 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength); 1377 1378 FreePool(ReparseDataBuffer); 1379 1380 /* Name has to \ terminated */ 1381 if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\') 1382 { 1383 ZwClose(Handle); 1384 return STATUS_INVALID_PARAMETER; 1385 } 1386 1387 /* So that we can delete it, and match mountmgr requirements */ 1388 SymbolicName->Length -= sizeof(WCHAR); 1389 SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL; 1390 1391 /* Also ensure it's really a volume name... */ 1392 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName)) 1393 { 1394 ZwClose(Handle); 1395 return STATUS_INVALID_PARAMETER; 1396 } 1397 1398 /* Now prepare to really get the name */ 1399 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR)); 1400 if (!FileNameInfo) 1401 { 1402 ZwClose(Handle); 1403 return STATUS_INSUFFICIENT_RESOURCES; 1404 } 1405 1406 Status = ZwQueryInformationFile(Handle, 1407 &IoStatusBlock, 1408 FileNameInfo, 1409 sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR), 1410 FileNameInformation); 1411 if (Status == STATUS_BUFFER_OVERFLOW) 1412 { 1413 /* As expected... Reallocate with proper size */ 1414 NeededLength = FileNameInfo->FileNameLength; 1415 FreePool(FileNameInfo); 1416 1417 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength); 1418 if (!FileNameInfo) 1419 { 1420 ZwClose(Handle); 1421 return STATUS_INSUFFICIENT_RESOURCES; 1422 } 1423 1424 /* And query name */ 1425 Status = ZwQueryInformationFile(Handle, 1426 &IoStatusBlock, 1427 FileNameInfo, 1428 sizeof(FILE_NAME_INFORMATION) + NeededLength, 1429 FileNameInformation); 1430 } 1431 1432 ZwClose(Handle); 1433 1434 if (!NT_SUCCESS(Status)) 1435 { 1436 return Status; 1437 } 1438 1439 /* Return the volume name */ 1440 VolumeName->Length = (USHORT)FileNameInfo->FileNameLength; 1441 VolumeName->MaximumLength = (USHORT)FileNameInfo->FileNameLength + sizeof(WCHAR); 1442 VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength); 1443 if (!VolumeName->Buffer) 1444 { 1445 return STATUS_INSUFFICIENT_RESOURCES; 1446 } 1447 1448 RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength); 1449 VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL; 1450 1451 FreePool(FileNameInfo); 1452 1453 return STATUS_SUCCESS; 1454 } 1455 1456 /* 1457 * @implemented 1458 */ 1459 VOID 1460 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension, 1461 IN PDEVICE_INFORMATION DeviceInformation) 1462 { 1463 HANDLE Handle; 1464 NTSTATUS Status; 1465 BOOLEAN RestartScan; 1466 IO_STATUS_BLOCK IoStatusBlock; 1467 OBJECT_ATTRIBUTES ObjectAttributes; 1468 PDEVICE_INFORMATION VolumeDeviceInformation; 1469 WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64]; 1470 UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName; 1471 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation; 1472 1473 /* Removable devices don't have remote database on them */ 1474 if (DeviceInformation->Removable) 1475 { 1476 return; 1477 } 1478 1479 /* Prepare a string with reparse point index */ 1480 ReparseFile.Length = 0; 1481 ReparseFile.MaximumLength = DeviceInformation->DeviceName.Length 1482 + ReparseIndex.Length 1483 + sizeof(UNICODE_NULL); 1484 ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength); 1485 if (!ReparseFile.Buffer) 1486 { 1487 return; 1488 } 1489 1490 RtlAppendUnicodeStringToString(&ReparseFile, &DeviceInformation->DeviceName); 1491 RtlAppendUnicodeStringToString(&ReparseFile, &ReparseIndex); 1492 ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL; 1493 1494 InitializeObjectAttributes(&ObjectAttributes, 1495 &ReparseFile, 1496 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 1497 NULL, 1498 NULL); 1499 1500 /* Open reparse point */ 1501 Status = ZwOpenFile(&Handle, 1502 FILE_GENERIC_READ, 1503 &ObjectAttributes, 1504 &IoStatusBlock, 1505 FILE_SHARE_READ | FILE_SHARE_WRITE, 1506 FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT); 1507 FreePool(ReparseFile.Buffer); 1508 if (!NT_SUCCESS(Status)) 1509 { 1510 DeviceInformation->NoDatabase = FALSE; 1511 return; 1512 } 1513 1514 /* Query reparse point information 1515 * We only pay attention to mout point 1516 */ 1517 RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer)); 1518 FileName.Buffer = FileNameBuffer; 1519 FileName.Length = sizeof(FileNameBuffer); 1520 FileName.MaximumLength = sizeof(FileNameBuffer); 1521 ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT; 1522 Status = ZwQueryDirectoryFile(Handle, 1523 NULL, 1524 NULL, 1525 NULL, 1526 &IoStatusBlock, 1527 &ReparsePointInformation, 1528 sizeof(FILE_REPARSE_POINT_INFORMATION), 1529 FileReparsePointInformation, 1530 TRUE, 1531 &FileName, 1532 FALSE); 1533 if (!NT_SUCCESS(Status)) 1534 { 1535 ZwClose(Handle); 1536 return; 1537 } 1538 1539 RestartScan = TRUE; 1540 1541 /* Query mount points */ 1542 while (TRUE) 1543 { 1544 SymbolicName.Length = 0; 1545 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer); 1546 SymbolicName.Buffer = SymbolicNameBuffer; 1547 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION)); 1548 1549 Status = ZwQueryDirectoryFile(Handle, 1550 NULL, 1551 NULL, 1552 NULL, 1553 &IoStatusBlock, 1554 &ReparsePointInformation, 1555 sizeof(FILE_REPARSE_POINT_INFORMATION), 1556 FileReparsePointInformation, 1557 TRUE, 1558 (RestartScan) ? &FileName : NULL, 1559 RestartScan); 1560 if (!RestartScan) 1561 { 1562 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference && 1563 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag) 1564 { 1565 break; 1566 } 1567 } 1568 else 1569 { 1570 RestartScan = FALSE; 1571 } 1572 1573 if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT) 1574 { 1575 break; 1576 } 1577 1578 /* Get the volume name associated to the mount point */ 1579 Status = QueryVolumeName(Handle, 1580 &ReparsePointInformation, 1581 NULL, &SymbolicName, 1582 &VolumeName); 1583 if (!NT_SUCCESS(Status)) 1584 { 1585 continue; 1586 } 1587 1588 FreePool(VolumeName.Buffer); 1589 1590 /* Get its information */ 1591 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, 1592 FALSE, &VolumeDeviceInformation); 1593 if (!NT_SUCCESS(Status)) 1594 { 1595 DeviceInformation->NoDatabase = TRUE; 1596 continue; 1597 } 1598 1599 /* If notification are enabled, mark it online */ 1600 if (!DeviceInformation->SkipNotifications) 1601 { 1602 PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName); 1603 } 1604 } 1605 1606 ZwClose(Handle); 1607 } 1608 1609 /* 1610 * @implemented 1611 */ 1612 VOID 1613 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension, 1614 IN PDEVICE_INFORMATION DeviceInformation) 1615 { 1616 PRECONCILE_WORK_ITEM WorkItem; 1617 1618 /* Removable devices don't have remote database */ 1619 if (DeviceInformation->Removable) 1620 { 1621 return; 1622 } 1623 1624 /* Allocate a work item */ 1625 WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM)); 1626 if (!WorkItem) 1627 { 1628 return; 1629 } 1630 1631 WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject); 1632 if (!WorkItem->WorkItem) 1633 { 1634 FreePool(WorkItem); 1635 return; 1636 } 1637 1638 /* And queue it */ 1639 WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker; 1640 WorkItem->DeviceExtension = DeviceExtension; 1641 WorkItem->DeviceInformation = DeviceInformation; 1642 QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension)); 1643 1644 /* If the worker thread isn't started yet, automatic drive letter is 1645 * enabled but automount disabled, manually set mounted volumes online. 1646 * Otherwise, they will be set online during database reconciliation. */ 1647 if (DeviceExtension->WorkerThreadStatus == 0 && 1648 DeviceExtension->AutomaticDriveLetter && 1649 DeviceExtension->NoAutoMount) 1650 { 1651 OnlineMountedVolumes(DeviceExtension, DeviceInformation); 1652 } 1653 } 1654 1655 /* 1656 * @implemented 1657 */ 1658 VOID 1659 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension) 1660 { 1661 PLIST_ENTRY NextEntry; 1662 PDEVICE_INFORMATION DeviceInformation; 1663 1664 /* Browse all the devices */ 1665 for (NextEntry = DeviceExtension->DeviceListHead.Flink; 1666 NextEntry != &(DeviceExtension->DeviceListHead); 1667 NextEntry = NextEntry->Flink) 1668 { 1669 DeviceInformation = CONTAINING_RECORD(NextEntry, 1670 DEVICE_INFORMATION, 1671 DeviceListEntry); 1672 /* If it's not removable, then, it might have a database to sync */ 1673 if (!DeviceInformation->Removable) 1674 { 1675 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); 1676 } 1677 } 1678 } 1679 1680 /* 1681 * @implemented 1682 */ 1683 VOID 1684 NTAPI 1685 CreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject, 1686 IN PVOID Context) 1687 { 1688 NTSTATUS Status; 1689 HANDLE Database = 0; 1690 UNICODE_STRING DatabaseName; 1691 PMIGRATE_WORK_ITEM WorkItem; 1692 IO_STATUS_BLOCK IoStatusBlock; 1693 OBJECT_ATTRIBUTES ObjectAttributes; 1694 PDEVICE_INFORMATION DeviceInformation; 1695 1696 UNREFERENCED_PARAMETER(DeviceObject); 1697 1698 /* Extract context */ 1699 WorkItem = Context; 1700 DeviceInformation = WorkItem->DeviceInformation; 1701 1702 /* Reconstruct appropriate string */ 1703 DatabaseName.Length = 0; 1704 DatabaseName.MaximumLength = DeviceInformation->DeviceName.Length 1705 + RemoteDatabase.Length 1706 + sizeof(UNICODE_NULL); 1707 DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength); 1708 if (DatabaseName.Buffer == NULL) 1709 { 1710 Status = STATUS_INSUFFICIENT_RESOURCES; 1711 goto Cleanup; 1712 } 1713 1714 /* Create the required folder (in which the database will be stored 1715 * \System Volume Information at root of the volume 1716 */ 1717 Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName)); 1718 if (!NT_SUCCESS(Status)) 1719 { 1720 goto Cleanup; 1721 } 1722 1723 /* Finish initiating strings */ 1724 RtlAppendUnicodeStringToString(&DatabaseName, &DeviceInformation->DeviceName); 1725 RtlAppendUnicodeStringToString(&DatabaseName, &RemoteDatabase); 1726 DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL; 1727 1728 /* Create database */ 1729 InitializeObjectAttributes(&ObjectAttributes, 1730 &DatabaseName, 1731 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 1732 NULL, 1733 NULL); 1734 1735 Status = IoCreateFile(&Database, 1736 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | 1737 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | 1738 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, 1739 &ObjectAttributes, 1740 &IoStatusBlock, 1741 NULL, 1742 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 1743 0, 1744 FILE_CREATE, 1745 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, 1746 NULL, 1747 0, 1748 CreateFileTypeNone, 1749 NULL, 1750 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING); 1751 if (!NT_SUCCESS(Status)) 1752 { 1753 if (Status == STATUS_STOPPED_ON_SYMLINK) 1754 { 1755 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n"); 1756 } 1757 1758 Database = 0; 1759 goto Cleanup; 1760 } 1761 1762 Cleanup: 1763 if (DatabaseName.Buffer) 1764 { 1765 FreePool(DatabaseName.Buffer); 1766 } 1767 1768 if (NT_SUCCESS(Status)) 1769 { 1770 DeviceInformation->Migrated = 1; 1771 } 1772 else if (Database != 0) 1773 { 1774 ZwClose(Database); 1775 } 1776 1777 IoFreeWorkItem(WorkItem->WorkItem); 1778 1779 WorkItem->WorkItem = NULL; 1780 WorkItem->Status = Status; 1781 WorkItem->Database = Database; 1782 1783 KeSetEvent(WorkItem->Event, 0, FALSE); 1784 } 1785 1786 /* 1787 * @implemented 1788 */ 1789 NTSTATUS 1790 CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, 1791 IN OUT PHANDLE Database) 1792 { 1793 KEVENT Event; 1794 NTSTATUS Status; 1795 PMIGRATE_WORK_ITEM WorkItem; 1796 1797 KeInitializeEvent(&Event, NotificationEvent, FALSE); 1798 1799 /* Allocate a work item dedicated to migration */ 1800 WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM)); 1801 if (!WorkItem) 1802 { 1803 *Database = 0; 1804 return STATUS_INSUFFICIENT_RESOURCES; 1805 } 1806 1807 RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM)); 1808 WorkItem->Event = &Event; 1809 WorkItem->DeviceInformation = DeviceInformation; 1810 WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject); 1811 if (!WorkItem->WorkItem) 1812 { 1813 FreePool(WorkItem); 1814 *Database = 0; 1815 return STATUS_INSUFFICIENT_RESOURCES; 1816 } 1817 1818 /* And queue it */ 1819 IoQueueWorkItem(WorkItem->WorkItem, 1820 CreateRemoteDatabaseWorker, 1821 DelayedWorkQueue, 1822 WorkItem); 1823 1824 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 1825 Status = WorkItem->Status; 1826 1827 *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0); 1828 1829 FreePool(WorkItem); 1830 return Status; 1831 } 1832 1833 /* 1834 * @implemented 1835 */ 1836 HANDLE 1837 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, 1838 IN BOOLEAN MigrateDatabase) 1839 { 1840 HANDLE Database; 1841 NTSTATUS Status; 1842 BOOLEAN PreviousMode; 1843 IO_STATUS_BLOCK IoStatusBlock; 1844 OBJECT_ATTRIBUTES ObjectAttributes; 1845 UNICODE_STRING DeviceRemoteDatabase; 1846 1847 Database = 0; 1848 1849 /* Get database name */ 1850 DeviceRemoteDatabase.Length = 0; 1851 DeviceRemoteDatabase.MaximumLength = DeviceInformation->DeviceName.Length 1852 + RemoteDatabase.Length 1853 + sizeof(UNICODE_NULL); 1854 DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength); 1855 if (!DeviceRemoteDatabase.Buffer) 1856 { 1857 return 0; 1858 } 1859 1860 RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &DeviceInformation->DeviceName); 1861 RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &RemoteDatabase); 1862 DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL; 1863 1864 /* Open database */ 1865 InitializeObjectAttributes(&ObjectAttributes, 1866 &DeviceRemoteDatabase, 1867 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 1868 NULL, 1869 NULL); 1870 1871 /* Disable hard errors */ 1872 PreviousMode = IoSetThreadHardErrorMode(FALSE); 1873 1874 Status = IoCreateFile(&Database, 1875 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | 1876 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | 1877 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, 1878 &ObjectAttributes, 1879 &IoStatusBlock, 1880 NULL, 1881 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 1882 0, 1883 (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN, 1884 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, 1885 NULL, 1886 0, 1887 CreateFileTypeNone, 1888 NULL, 1889 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING); 1890 if (Status == STATUS_STOPPED_ON_SYMLINK) 1891 { 1892 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n"); 1893 } 1894 1895 /* If base it to be migrated and was opened successfully, go ahead */ 1896 if (MigrateDatabase && NT_SUCCESS(Status)) 1897 { 1898 CreateRemoteDatabase(DeviceInformation, &Database); 1899 } 1900 1901 IoSetThreadHardErrorMode(PreviousMode); 1902 FreePool(DeviceRemoteDatabase.Buffer); 1903 1904 return Database; 1905 } 1906 1907 /* 1908 * @implemented 1909 */ 1910 VOID 1911 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation, 1912 IN PMOUNTDEV_UNIQUE_ID OldUniqueId, 1913 IN PMOUNTDEV_UNIQUE_ID NewUniqueId) 1914 { 1915 LONG Offset = 0; 1916 HANDLE Database; 1917 PDATABASE_ENTRY Entry, NewEntry; 1918 NTSTATUS Status = STATUS_SUCCESS; 1919 1920 /* Open the remote database */ 1921 Database = OpenRemoteDatabase(DeviceInformation, FALSE); 1922 if (!Database) 1923 { 1924 return; 1925 } 1926 1927 /* Get all the entries */ 1928 do 1929 { 1930 Entry = GetRemoteDatabaseEntry(Database, Offset); 1931 if (!Entry) 1932 { 1933 break; 1934 } 1935 1936 /* Not the correct entry, skip it */ 1937 if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength) 1938 { 1939 Offset += Entry->EntrySize; 1940 FreePool(Entry); 1941 continue; 1942 } 1943 1944 /* Not the correct entry, skip it */ 1945 if (RtlCompareMemory(OldUniqueId->UniqueId, 1946 (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset), 1947 Entry->UniqueIdLength) != Entry->UniqueIdLength) 1948 { 1949 Offset += Entry->EntrySize; 1950 FreePool(Entry); 1951 continue; 1952 } 1953 1954 /* Here, we have the correct entry */ 1955 NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength); 1956 if (!NewEntry) 1957 { 1958 Offset += Entry->EntrySize; 1959 FreePool(Entry); 1960 continue; 1961 } 1962 1963 /* Recreate the entry from the previous one */ 1964 NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength; 1965 NewEntry->EntryReferences = Entry->EntryReferences; 1966 NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); 1967 NewEntry->SymbolicNameLength = Entry->SymbolicNameLength; 1968 NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY); 1969 NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength; 1970 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset), 1971 (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset), 1972 NewEntry->SymbolicNameLength); 1973 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset), 1974 NewUniqueId->UniqueId, NewEntry->UniqueIdLength); 1975 1976 /* Delete old entry */ 1977 Status = DeleteRemoteDatabaseEntry(Database, Offset); 1978 if (!NT_SUCCESS(Status)) 1979 { 1980 FreePool(Entry); 1981 FreePool(NewEntry); 1982 break; 1983 } 1984 1985 /* And replace with new one */ 1986 Status = AddRemoteDatabaseEntry(Database, NewEntry); 1987 FreePool(Entry); 1988 FreePool(NewEntry); 1989 } while (NT_SUCCESS(Status)); 1990 1991 CloseRemoteDatabase(Database); 1992 1993 return; 1994 } 1995 1996 /* 1997 * @implemented 1998 */ 1999 NTSTATUS 2000 NTAPI 2001 DeleteDriveLetterRoutine(IN PWSTR ValueName, 2002 IN ULONG ValueType, 2003 IN PVOID ValueData, 2004 IN ULONG ValueLength, 2005 IN PVOID Context, 2006 IN PVOID EntryContext) 2007 { 2008 PMOUNTDEV_UNIQUE_ID UniqueId; 2009 UNICODE_STRING RegistryEntry; 2010 2011 UNREFERENCED_PARAMETER(EntryContext); 2012 2013 if (ValueType != REG_BINARY) 2014 { 2015 return STATUS_SUCCESS; 2016 } 2017 2018 UniqueId = Context; 2019 2020 /* First ensure we have the correct data */ 2021 if (UniqueId->UniqueIdLength != ValueLength) 2022 { 2023 return STATUS_SUCCESS; 2024 } 2025 2026 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) 2027 { 2028 return STATUS_SUCCESS; 2029 } 2030 2031 RtlInitUnicodeString(&RegistryEntry, ValueName); 2032 2033 /* Then, it's a drive letter, erase it */ 2034 if (IsDriveLetter(&RegistryEntry)) 2035 { 2036 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, 2037 DatabasePath, 2038 ValueName); 2039 } 2040 2041 return STATUS_SUCCESS; 2042 } 2043 2044 /* 2045 * @implemented 2046 */ 2047 VOID 2048 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId) 2049 { 2050 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 2051 2052 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 2053 QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine; 2054 2055 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 2056 DatabasePath, 2057 QueryTable, 2058 UniqueId, 2059 NULL); 2060 } 2061 2062 /* 2063 * @implemented 2064 */ 2065 NTSTATUS 2066 NTAPI 2067 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName, 2068 IN ULONG ValueType, 2069 IN PVOID ValueData, 2070 IN ULONG ValueLength, 2071 IN PVOID Context, 2072 IN PVOID EntryContext) 2073 { 2074 PMOUNTDEV_UNIQUE_ID UniqueId = Context; 2075 2076 UNREFERENCED_PARAMETER(EntryContext); 2077 2078 /* Ensure we have correct input */ 2079 if (ValueName[0] != L'#' || ValueType != REG_BINARY || 2080 UniqueId->UniqueIdLength != ValueLength) 2081 { 2082 return STATUS_SUCCESS; 2083 } 2084 2085 /* And then, if unique ID matching, delete entry */ 2086 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) 2087 { 2088 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, 2089 DatabasePath, 2090 ValueName); 2091 } 2092 2093 return STATUS_SUCCESS; 2094 } 2095 2096 /* 2097 * @implemented 2098 */ 2099 VOID 2100 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId) 2101 { 2102 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 2103 2104 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 2105 QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine; 2106 2107 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 2108 DatabasePath, 2109 QueryTable, 2110 UniqueId, 2111 NULL); 2112 } 2113