Reactos
at listview 2114 lines 69 kB view raw
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 31PWSTR DatabasePath = L"\\Registry\\Machine\\System\\MountedDevices"; 32PWSTR OfflinePath = L"\\Registry\\Machine\\System\\MountedDevices\\Offline"; 33 34UNICODE_STRING RemoteDatabase = RTL_CONSTANT_STRING(L"\\System Volume Information\\MountPointManagerRemoteDatabase"); 35 36/* 37 * @implemented 38 */ 39LONG 40GetRemoteDatabaseSize(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 */ 63NTSTATUS 64AddRemoteDatabaseEntry(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 */ 81NTSTATUS 82CloseRemoteDatabase(IN HANDLE Database) 83{ 84 return ZwClose(Database); 85} 86 87/* 88 * @implemented 89 */ 90NTSTATUS 91TruncateRemoteDatabase(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 */ 124PDATABASE_ENTRY 125GetRemoteDatabaseEntry(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 */ 199NTSTATUS 200WriteRemoteDatabaseEntry(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 */ 232NTSTATUS 233DeleteRemoteDatabaseEntry(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 zero or 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 */ 320NTSTATUS 321NTAPI 322DeleteFromLocalDatabaseRoutine(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 */ 350VOID 351DeleteFromLocalDatabase(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 */ 370NTSTATUS 371WaitForRemoteDatabaseSemaphore(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 */ 390VOID 391ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension) 392{ 393 KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE); 394} 395 396/* 397 * @implemented 398 */ 399NTSTATUS 400NTAPI 401QueryUniqueIdQueryRoutine(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 */ 439NTSTATUS 440QueryUniqueIdFromMaster(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 */ 488NTSTATUS 489WriteUniqueIdToMaster(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 */ 558VOID 559NTAPI 560ReconcileThisDatabaseWithMasterWorker(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 1126FreeUniqueId: 1127 FreePool(UniqueId); 1128ReleaseDeviceLock: 1129 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); 1130FreeDBEntry: 1131 FreePool(DatabaseEntry); 1132FreeVolume: 1133 FreePool(VolumeName.Buffer); 1134CloseReparse: 1135 ZwClose(Handle); 1136CloseRDB: 1137 CloseRemoteDatabase(DatabaseHandle); 1138ReleaseRDS: 1139 ReleaseRemoteDatabaseSemaphore(DeviceExtension); 1140 return; 1141} 1142 1143/* 1144 * @implemented 1145 */ 1146VOID 1147NTAPI 1148WorkerThread(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 */ 1260NTSTATUS 1261QueueWorkItem(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 */ 1291NTSTATUS 1292QueryVolumeName(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 if (!FileName) 1307 { 1308 UNICODE_STRING Reference; 1309 1310 Reference.Length = Reference.MaximumLength = sizeof(ReparsePointInformation->FileReference); 1311 Reference.Buffer = (PWSTR)&(ReparsePointInformation->FileReference); 1312 InitializeObjectAttributes(&ObjectAttributes, 1313 &Reference, 1314 OBJ_KERNEL_HANDLE, 1315 RootDirectory, 1316 NULL); 1317 } 1318 else 1319 { 1320 InitializeObjectAttributes(&ObjectAttributes, 1321 FileName, 1322 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 1323 NULL, 1324 NULL); 1325 } 1326 1327 /* Open volume */ 1328 Status = ZwOpenFile(&Handle, 1329 SYNCHRONIZE | FILE_READ_ATTRIBUTES, 1330 &ObjectAttributes, 1331 &IoStatusBlock, 1332 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1333 (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT : 1334 FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT); 1335 if (!NT_SUCCESS(Status)) 1336 { 1337 return Status; 1338 } 1339 1340 /* Get the reparse point data */ 1341 ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 1342 if (!ReparseDataBuffer) 1343 { 1344 ZwClose(Handle); 1345 return STATUS_INSUFFICIENT_RESOURCES; 1346 } 1347 1348 Status = ZwFsControlFile(Handle, 1349 0, 1350 NULL, 1351 NULL, 1352 &IoStatusBlock, 1353 FSCTL_GET_REPARSE_POINT, 1354 NULL, 1355 0, 1356 ReparseDataBuffer, 1357 MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 1358 if (!NT_SUCCESS(Status)) 1359 { 1360 FreePool(ReparseDataBuffer); 1361 ZwClose(Handle); 1362 return Status; 1363 } 1364 1365 /* Check that name can fit in buffer */ 1366 if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength) 1367 { 1368 FreePool(ReparseDataBuffer); 1369 ZwClose(Handle); 1370 return STATUS_BUFFER_TOO_SMALL; 1371 } 1372 1373 /* Copy symbolic name */ 1374 SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength; 1375 RtlCopyMemory(SymbolicName->Buffer, 1376 (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + 1377 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset), 1378 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength); 1379 1380 FreePool(ReparseDataBuffer); 1381 1382 /* Name has to \ terminated */ 1383 if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\') 1384 { 1385 ZwClose(Handle); 1386 return STATUS_INVALID_PARAMETER; 1387 } 1388 1389 /* So that we can delete it, and match mountmgr requirements */ 1390 SymbolicName->Length -= sizeof(WCHAR); 1391 SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL; 1392 1393 /* Also ensure it's really a volume name... */ 1394 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName)) 1395 { 1396 ZwClose(Handle); 1397 return STATUS_INVALID_PARAMETER; 1398 } 1399 1400 /* Now prepare to really get the name */ 1401 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR)); 1402 if (!FileNameInfo) 1403 { 1404 ZwClose(Handle); 1405 return STATUS_INSUFFICIENT_RESOURCES; 1406 } 1407 1408 Status = ZwQueryInformationFile(Handle, 1409 &IoStatusBlock, 1410 FileNameInfo, 1411 sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR), 1412 FileNameInformation); 1413 if (Status == STATUS_BUFFER_OVERFLOW) 1414 { 1415 /* As expected... Reallocate with proper size */ 1416 NeededLength = FileNameInfo->FileNameLength; 1417 FreePool(FileNameInfo); 1418 1419 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength); 1420 if (!FileNameInfo) 1421 { 1422 ZwClose(Handle); 1423 return STATUS_INSUFFICIENT_RESOURCES; 1424 } 1425 1426 /* And query name */ 1427 Status = ZwQueryInformationFile(Handle, 1428 &IoStatusBlock, 1429 FileNameInfo, 1430 sizeof(FILE_NAME_INFORMATION) + NeededLength, 1431 FileNameInformation); 1432 } 1433 1434 ZwClose(Handle); 1435 1436 if (!NT_SUCCESS(Status)) 1437 { 1438 return Status; 1439 } 1440 1441 /* Return the volume name */ 1442 VolumeName->Length = (USHORT)FileNameInfo->FileNameLength; 1443 VolumeName->MaximumLength = (USHORT)FileNameInfo->FileNameLength + sizeof(WCHAR); 1444 VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength); 1445 if (!VolumeName->Buffer) 1446 { 1447 return STATUS_INSUFFICIENT_RESOURCES; 1448 } 1449 1450 RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength); 1451 VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL; 1452 1453 FreePool(FileNameInfo); 1454 1455 return STATUS_SUCCESS; 1456} 1457 1458/* 1459 * @implemented 1460 */ 1461VOID 1462OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension, 1463 IN PDEVICE_INFORMATION DeviceInformation) 1464{ 1465 HANDLE Handle; 1466 NTSTATUS Status; 1467 BOOLEAN RestartScan; 1468 IO_STATUS_BLOCK IoStatusBlock; 1469 OBJECT_ATTRIBUTES ObjectAttributes; 1470 PDEVICE_INFORMATION VolumeDeviceInformation; 1471 WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64]; 1472 UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName; 1473 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation; 1474 1475 /* Removable devices don't have remote database on them */ 1476 if (DeviceInformation->Removable) 1477 { 1478 return; 1479 } 1480 1481 /* Prepare a string with reparse point index */ 1482 ReparseFile.Length = 0; 1483 ReparseFile.MaximumLength = DeviceInformation->DeviceName.Length 1484 + ReparseIndex.Length 1485 + sizeof(UNICODE_NULL); 1486 ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength); 1487 if (!ReparseFile.Buffer) 1488 { 1489 return; 1490 } 1491 1492 RtlAppendUnicodeStringToString(&ReparseFile, &DeviceInformation->DeviceName); 1493 RtlAppendUnicodeStringToString(&ReparseFile, &ReparseIndex); 1494 ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL; 1495 1496 InitializeObjectAttributes(&ObjectAttributes, 1497 &ReparseFile, 1498 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 1499 NULL, 1500 NULL); 1501 1502 /* Open reparse point */ 1503 Status = ZwOpenFile(&Handle, 1504 FILE_GENERIC_READ, 1505 &ObjectAttributes, 1506 &IoStatusBlock, 1507 FILE_SHARE_READ | FILE_SHARE_WRITE, 1508 FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT); 1509 FreePool(ReparseFile.Buffer); 1510 if (!NT_SUCCESS(Status)) 1511 { 1512 DeviceInformation->NoDatabase = FALSE; 1513 return; 1514 } 1515 1516 /* Query reparse point information 1517 * We only pay attention to mout point 1518 */ 1519 RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer)); 1520 FileName.Buffer = FileNameBuffer; 1521 FileName.Length = sizeof(FileNameBuffer); 1522 FileName.MaximumLength = sizeof(FileNameBuffer); 1523 ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT; 1524 Status = ZwQueryDirectoryFile(Handle, 1525 NULL, 1526 NULL, 1527 NULL, 1528 &IoStatusBlock, 1529 &ReparsePointInformation, 1530 sizeof(FILE_REPARSE_POINT_INFORMATION), 1531 FileReparsePointInformation, 1532 TRUE, 1533 &FileName, 1534 FALSE); 1535 if (!NT_SUCCESS(Status)) 1536 { 1537 ZwClose(Handle); 1538 return; 1539 } 1540 1541 RestartScan = TRUE; 1542 1543 /* Query mount points */ 1544 while (TRUE) 1545 { 1546 SymbolicName.Length = 0; 1547 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer); 1548 SymbolicName.Buffer = SymbolicNameBuffer; 1549 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION)); 1550 1551 Status = ZwQueryDirectoryFile(Handle, 1552 NULL, 1553 NULL, 1554 NULL, 1555 &IoStatusBlock, 1556 &ReparsePointInformation, 1557 sizeof(FILE_REPARSE_POINT_INFORMATION), 1558 FileReparsePointInformation, 1559 TRUE, 1560 (RestartScan) ? &FileName : NULL, 1561 RestartScan); 1562 if (!RestartScan) 1563 { 1564 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference && 1565 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag) 1566 { 1567 break; 1568 } 1569 } 1570 else 1571 { 1572 RestartScan = FALSE; 1573 } 1574 1575 if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT) 1576 { 1577 break; 1578 } 1579 1580 /* Get the volume name associated to the mount point */ 1581 Status = QueryVolumeName(Handle, 1582 &ReparsePointInformation, 1583 NULL, &SymbolicName, 1584 &VolumeName); 1585 if (!NT_SUCCESS(Status)) 1586 { 1587 continue; 1588 } 1589 1590 FreePool(VolumeName.Buffer); 1591 1592 /* Get its information */ 1593 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, 1594 FALSE, &VolumeDeviceInformation); 1595 if (!NT_SUCCESS(Status)) 1596 { 1597 DeviceInformation->NoDatabase = TRUE; 1598 continue; 1599 } 1600 1601 /* If notification are enabled, mark it online */ 1602 if (!DeviceInformation->SkipNotifications) 1603 { 1604 PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName); 1605 } 1606 } 1607 1608 ZwClose(Handle); 1609} 1610 1611/* 1612 * @implemented 1613 */ 1614VOID 1615ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension, 1616 IN PDEVICE_INFORMATION DeviceInformation) 1617{ 1618 PRECONCILE_WORK_ITEM WorkItem; 1619 1620 /* Removable devices don't have remote database */ 1621 if (DeviceInformation->Removable) 1622 { 1623 return; 1624 } 1625 1626 /* Allocate a work item */ 1627 WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM)); 1628 if (!WorkItem) 1629 { 1630 return; 1631 } 1632 1633 WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject); 1634 if (!WorkItem->WorkItem) 1635 { 1636 FreePool(WorkItem); 1637 return; 1638 } 1639 1640 /* And queue it */ 1641 WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker; 1642 WorkItem->DeviceExtension = DeviceExtension; 1643 WorkItem->DeviceInformation = DeviceInformation; 1644 QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension)); 1645 1646 /* If the worker thread isn't started yet, automatic drive letter is 1647 * enabled but automount disabled, manually set mounted volumes online. 1648 * Otherwise, they will be set online during database reconciliation. */ 1649 if (DeviceExtension->WorkerThreadStatus == 0 && 1650 DeviceExtension->AutomaticDriveLetter && 1651 DeviceExtension->NoAutoMount) 1652 { 1653 OnlineMountedVolumes(DeviceExtension, DeviceInformation); 1654 } 1655} 1656 1657/* 1658 * @implemented 1659 */ 1660VOID 1661ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension) 1662{ 1663 PLIST_ENTRY NextEntry; 1664 PDEVICE_INFORMATION DeviceInformation; 1665 1666 /* Browse all the devices */ 1667 for (NextEntry = DeviceExtension->DeviceListHead.Flink; 1668 NextEntry != &(DeviceExtension->DeviceListHead); 1669 NextEntry = NextEntry->Flink) 1670 { 1671 DeviceInformation = CONTAINING_RECORD(NextEntry, 1672 DEVICE_INFORMATION, 1673 DeviceListEntry); 1674 /* If it's not removable, then, it might have a database to sync */ 1675 if (!DeviceInformation->Removable) 1676 { 1677 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); 1678 } 1679 } 1680} 1681 1682/* 1683 * @implemented 1684 */ 1685VOID 1686NTAPI 1687CreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject, 1688 IN PVOID Context) 1689{ 1690 NTSTATUS Status; 1691 HANDLE Database = 0; 1692 UNICODE_STRING DatabaseName; 1693 PMIGRATE_WORK_ITEM WorkItem; 1694 IO_STATUS_BLOCK IoStatusBlock; 1695 OBJECT_ATTRIBUTES ObjectAttributes; 1696 PDEVICE_INFORMATION DeviceInformation; 1697 1698 UNREFERENCED_PARAMETER(DeviceObject); 1699 1700 /* Extract context */ 1701 WorkItem = Context; 1702 DeviceInformation = WorkItem->DeviceInformation; 1703 1704 /* Reconstruct appropriate string */ 1705 DatabaseName.Length = 0; 1706 DatabaseName.MaximumLength = DeviceInformation->DeviceName.Length 1707 + RemoteDatabase.Length 1708 + sizeof(UNICODE_NULL); 1709 DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength); 1710 if (DatabaseName.Buffer == NULL) 1711 { 1712 Status = STATUS_INSUFFICIENT_RESOURCES; 1713 goto Cleanup; 1714 } 1715 1716 /* Create the required folder (in which the database will be stored 1717 * \System Volume Information at root of the volume 1718 */ 1719 Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName)); 1720 if (!NT_SUCCESS(Status)) 1721 { 1722 goto Cleanup; 1723 } 1724 1725 /* Finish initiating strings */ 1726 RtlAppendUnicodeStringToString(&DatabaseName, &DeviceInformation->DeviceName); 1727 RtlAppendUnicodeStringToString(&DatabaseName, &RemoteDatabase); 1728 DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL; 1729 1730 /* Create database */ 1731 InitializeObjectAttributes(&ObjectAttributes, 1732 &DatabaseName, 1733 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 1734 NULL, 1735 NULL); 1736 1737 Status = IoCreateFile(&Database, 1738 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | 1739 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | 1740 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, 1741 &ObjectAttributes, 1742 &IoStatusBlock, 1743 NULL, 1744 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 1745 0, 1746 FILE_CREATE, 1747 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, 1748 NULL, 1749 0, 1750 CreateFileTypeNone, 1751 NULL, 1752 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING); 1753 if (!NT_SUCCESS(Status)) 1754 { 1755 if (Status == STATUS_STOPPED_ON_SYMLINK) 1756 { 1757 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n"); 1758 } 1759 1760 Database = 0; 1761 goto Cleanup; 1762 } 1763 1764Cleanup: 1765 if (DatabaseName.Buffer) 1766 { 1767 FreePool(DatabaseName.Buffer); 1768 } 1769 1770 if (NT_SUCCESS(Status)) 1771 { 1772 DeviceInformation->Migrated = 1; 1773 } 1774 else if (Database != 0) 1775 { 1776 ZwClose(Database); 1777 } 1778 1779 IoFreeWorkItem(WorkItem->WorkItem); 1780 1781 WorkItem->WorkItem = NULL; 1782 WorkItem->Status = Status; 1783 WorkItem->Database = Database; 1784 1785 KeSetEvent(WorkItem->Event, 0, FALSE); 1786} 1787 1788/* 1789 * @implemented 1790 */ 1791NTSTATUS 1792CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, 1793 IN OUT PHANDLE Database) 1794{ 1795 KEVENT Event; 1796 NTSTATUS Status; 1797 PMIGRATE_WORK_ITEM WorkItem; 1798 1799 KeInitializeEvent(&Event, NotificationEvent, FALSE); 1800 1801 /* Allocate a work item dedicated to migration */ 1802 WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM)); 1803 if (!WorkItem) 1804 { 1805 *Database = 0; 1806 return STATUS_INSUFFICIENT_RESOURCES; 1807 } 1808 1809 RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM)); 1810 WorkItem->Event = &Event; 1811 WorkItem->DeviceInformation = DeviceInformation; 1812 WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject); 1813 if (!WorkItem->WorkItem) 1814 { 1815 FreePool(WorkItem); 1816 *Database = 0; 1817 return STATUS_INSUFFICIENT_RESOURCES; 1818 } 1819 1820 /* And queue it */ 1821 IoQueueWorkItem(WorkItem->WorkItem, 1822 CreateRemoteDatabaseWorker, 1823 DelayedWorkQueue, 1824 WorkItem); 1825 1826 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 1827 Status = WorkItem->Status; 1828 1829 *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0); 1830 1831 FreePool(WorkItem); 1832 return Status; 1833} 1834 1835/* 1836 * @implemented 1837 */ 1838HANDLE 1839OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, 1840 IN BOOLEAN MigrateDatabase) 1841{ 1842 HANDLE Database; 1843 NTSTATUS Status; 1844 BOOLEAN PreviousMode; 1845 IO_STATUS_BLOCK IoStatusBlock; 1846 OBJECT_ATTRIBUTES ObjectAttributes; 1847 UNICODE_STRING DeviceRemoteDatabase; 1848 1849 Database = 0; 1850 1851 /* Get database name */ 1852 DeviceRemoteDatabase.Length = 0; 1853 DeviceRemoteDatabase.MaximumLength = DeviceInformation->DeviceName.Length 1854 + RemoteDatabase.Length 1855 + sizeof(UNICODE_NULL); 1856 DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength); 1857 if (!DeviceRemoteDatabase.Buffer) 1858 { 1859 return 0; 1860 } 1861 1862 RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &DeviceInformation->DeviceName); 1863 RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &RemoteDatabase); 1864 DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL; 1865 1866 /* Open database */ 1867 InitializeObjectAttributes(&ObjectAttributes, 1868 &DeviceRemoteDatabase, 1869 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 1870 NULL, 1871 NULL); 1872 1873 /* Disable hard errors */ 1874 PreviousMode = IoSetThreadHardErrorMode(FALSE); 1875 1876 Status = IoCreateFile(&Database, 1877 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | 1878 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | 1879 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, 1880 &ObjectAttributes, 1881 &IoStatusBlock, 1882 NULL, 1883 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 1884 0, 1885 (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN, 1886 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, 1887 NULL, 1888 0, 1889 CreateFileTypeNone, 1890 NULL, 1891 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING); 1892 if (Status == STATUS_STOPPED_ON_SYMLINK) 1893 { 1894 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n"); 1895 } 1896 1897 /* If base it to be migrated and was opened successfully, go ahead */ 1898 if (MigrateDatabase && NT_SUCCESS(Status)) 1899 { 1900 CreateRemoteDatabase(DeviceInformation, &Database); 1901 } 1902 1903 IoSetThreadHardErrorMode(PreviousMode); 1904 FreePool(DeviceRemoteDatabase.Buffer); 1905 1906 return Database; 1907} 1908 1909/* 1910 * @implemented 1911 */ 1912VOID 1913ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation, 1914 IN PMOUNTDEV_UNIQUE_ID OldUniqueId, 1915 IN PMOUNTDEV_UNIQUE_ID NewUniqueId) 1916{ 1917 LONG Offset = 0; 1918 HANDLE Database; 1919 PDATABASE_ENTRY Entry, NewEntry; 1920 NTSTATUS Status = STATUS_SUCCESS; 1921 1922 /* Open the remote database */ 1923 Database = OpenRemoteDatabase(DeviceInformation, FALSE); 1924 if (!Database) 1925 { 1926 return; 1927 } 1928 1929 /* Get all the entries */ 1930 do 1931 { 1932 Entry = GetRemoteDatabaseEntry(Database, Offset); 1933 if (!Entry) 1934 { 1935 break; 1936 } 1937 1938 /* Not the correct entry, skip it */ 1939 if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength) 1940 { 1941 Offset += Entry->EntrySize; 1942 FreePool(Entry); 1943 continue; 1944 } 1945 1946 /* Not the correct entry, skip it */ 1947 if (RtlCompareMemory(OldUniqueId->UniqueId, 1948 (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset), 1949 Entry->UniqueIdLength) != Entry->UniqueIdLength) 1950 { 1951 Offset += Entry->EntrySize; 1952 FreePool(Entry); 1953 continue; 1954 } 1955 1956 /* Here, we have the correct entry */ 1957 NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength); 1958 if (!NewEntry) 1959 { 1960 Offset += Entry->EntrySize; 1961 FreePool(Entry); 1962 continue; 1963 } 1964 1965 /* Recreate the entry from the previous one */ 1966 NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength; 1967 NewEntry->EntryReferences = Entry->EntryReferences; 1968 NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); 1969 NewEntry->SymbolicNameLength = Entry->SymbolicNameLength; 1970 NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY); 1971 NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength; 1972 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset), 1973 (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset), 1974 NewEntry->SymbolicNameLength); 1975 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset), 1976 NewUniqueId->UniqueId, NewEntry->UniqueIdLength); 1977 1978 /* Delete old entry */ 1979 Status = DeleteRemoteDatabaseEntry(Database, Offset); 1980 if (!NT_SUCCESS(Status)) 1981 { 1982 FreePool(Entry); 1983 FreePool(NewEntry); 1984 break; 1985 } 1986 1987 /* And replace with new one */ 1988 Status = AddRemoteDatabaseEntry(Database, NewEntry); 1989 FreePool(Entry); 1990 FreePool(NewEntry); 1991 } while (NT_SUCCESS(Status)); 1992 1993 CloseRemoteDatabase(Database); 1994 1995 return; 1996} 1997 1998/* 1999 * @implemented 2000 */ 2001NTSTATUS 2002NTAPI 2003DeleteDriveLetterRoutine(IN PWSTR ValueName, 2004 IN ULONG ValueType, 2005 IN PVOID ValueData, 2006 IN ULONG ValueLength, 2007 IN PVOID Context, 2008 IN PVOID EntryContext) 2009{ 2010 PMOUNTDEV_UNIQUE_ID UniqueId; 2011 UNICODE_STRING RegistryEntry; 2012 2013 UNREFERENCED_PARAMETER(EntryContext); 2014 2015 if (ValueType != REG_BINARY) 2016 { 2017 return STATUS_SUCCESS; 2018 } 2019 2020 UniqueId = Context; 2021 2022 /* First ensure we have the correct data */ 2023 if (UniqueId->UniqueIdLength != ValueLength) 2024 { 2025 return STATUS_SUCCESS; 2026 } 2027 2028 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) 2029 { 2030 return STATUS_SUCCESS; 2031 } 2032 2033 RtlInitUnicodeString(&RegistryEntry, ValueName); 2034 2035 /* Then, it's a drive letter, erase it */ 2036 if (IsDriveLetter(&RegistryEntry)) 2037 { 2038 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, 2039 DatabasePath, 2040 ValueName); 2041 } 2042 2043 return STATUS_SUCCESS; 2044} 2045 2046/* 2047 * @implemented 2048 */ 2049VOID 2050DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId) 2051{ 2052 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 2053 2054 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 2055 QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine; 2056 2057 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 2058 DatabasePath, 2059 QueryTable, 2060 UniqueId, 2061 NULL); 2062} 2063 2064/* 2065 * @implemented 2066 */ 2067NTSTATUS 2068NTAPI 2069DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName, 2070 IN ULONG ValueType, 2071 IN PVOID ValueData, 2072 IN ULONG ValueLength, 2073 IN PVOID Context, 2074 IN PVOID EntryContext) 2075{ 2076 PMOUNTDEV_UNIQUE_ID UniqueId = Context; 2077 2078 UNREFERENCED_PARAMETER(EntryContext); 2079 2080 /* Ensure we have correct input */ 2081 if (ValueName[0] != L'#' || ValueType != REG_BINARY || 2082 UniqueId->UniqueIdLength != ValueLength) 2083 { 2084 return STATUS_SUCCESS; 2085 } 2086 2087 /* And then, if unique ID matching, delete entry */ 2088 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) 2089 { 2090 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, 2091 DatabasePath, 2092 ValueName); 2093 } 2094 2095 return STATUS_SUCCESS; 2096} 2097 2098/* 2099 * @implemented 2100 */ 2101VOID 2102DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId) 2103{ 2104 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 2105 2106 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 2107 QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine; 2108 2109 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 2110 DatabasePath, 2111 QueryTable, 2112 UniqueId, 2113 NULL); 2114}