3 * Copyright (C) 2011-2012 ReactOS Team
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.
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.
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.
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)
31 PWSTR DatabasePath
= L
"\\Registry\\Machine\\System\\MountedDevices";
32 PWSTR OfflinePath
= L
"\\Registry\\Machine\\System\\MountedDevices\\Offline";
34 UNICODE_STRING RemoteDatabase
= RTL_CONSTANT_STRING(L
"\\System Volume Information\\MountPointManagerRemoteDatabase");
40 GetRemoteDatabaseSize(IN HANDLE Database
)
43 IO_STATUS_BLOCK IoStatusBlock
;
44 FILE_STANDARD_INFORMATION StandardInfo
;
46 /* Just query the size */
47 Status
= ZwQueryInformationFile(Database
,
50 sizeof(FILE_STANDARD_INFORMATION
),
51 FileStandardInformation
);
52 if (NT_SUCCESS(Status
))
54 return StandardInfo
.EndOfFile
.LowPart
;
64 AddRemoteDatabaseEntry(IN HANDLE Database
,
65 IN PDATABASE_ENTRY Entry
)
68 IO_STATUS_BLOCK IoStatusBlock
;
70 /* Get size to append data */
71 Size
.QuadPart
= GetRemoteDatabaseSize(Database
);
73 return ZwWriteFile(Database
, NULL
, NULL
, NULL
,
74 &IoStatusBlock
, Entry
,
75 Entry
->EntrySize
, &Size
, NULL
);
82 CloseRemoteDatabase(IN HANDLE Database
)
84 return ZwClose(Database
);
91 TruncateRemoteDatabase(IN HANDLE Database
,
95 IO_STATUS_BLOCK IoStatusBlock
;
96 FILE_END_OF_FILE_INFORMATION EndOfFile
;
97 FILE_ALLOCATION_INFORMATION Allocation
;
99 EndOfFile
.EndOfFile
.QuadPart
= NewSize
;
100 Allocation
.AllocationSize
.QuadPart
= NewSize
;
103 Status
= ZwSetInformationFile(Database
,
106 sizeof(FILE_END_OF_FILE_INFORMATION
),
107 FileEndOfFileInformation
);
108 if (NT_SUCCESS(Status
))
110 /* And then, properly set allocation information */
111 Status
= ZwSetInformationFile(Database
,
114 sizeof(FILE_ALLOCATION_INFORMATION
),
115 FileAllocationInformation
);
125 GetRemoteDatabaseEntry(IN HANDLE Database
,
126 IN LONG StartingOffset
)
130 PDATABASE_ENTRY Entry
;
131 LARGE_INTEGER ByteOffset
;
132 IO_STATUS_BLOCK IoStatusBlock
;
134 /* Get the entry at the given position */
135 ByteOffset
.QuadPart
= StartingOffset
;
136 Status
= ZwReadFile(Database
,
145 if (!NT_SUCCESS(Status
))
150 /* If entry doesn't exist, truncate database */
153 TruncateRemoteDatabase(Database
, StartingOffset
);
157 /* Allocate the entry */
158 Entry
= AllocatePool(EntrySize
);
164 /* Effectively read the entry */
165 Status
= ZwReadFile(Database
,
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
)) )
179 TruncateRemoteDatabase(Database
, StartingOffset
);
185 if (MAX(Entry
->SymbolicNameOffset
+ Entry
->SymbolicNameLength
,
186 Entry
->UniqueIdOffset
+ Entry
->UniqueIdLength
) > (LONG
)EntrySize
)
188 TruncateRemoteDatabase(Database
, StartingOffset
);
200 WriteRemoteDatabaseEntry(IN HANDLE Database
,
202 IN PDATABASE_ENTRY Entry
)
205 LARGE_INTEGER ByteOffset
;
206 IO_STATUS_BLOCK IoStatusBlock
;
208 ByteOffset
.QuadPart
= Offset
;
209 Status
= ZwWriteFile(Database
,
218 if (NT_SUCCESS(Status
))
220 if (IoStatusBlock
.Information
< Entry
->EntrySize
)
222 Status
= STATUS_INSUFFICIENT_RESOURCES
;
233 DeleteRemoteDatabaseEntry(IN HANDLE Database
,
234 IN LONG StartingOffset
)
240 PDATABASE_ENTRY Entry
;
241 IO_STATUS_BLOCK IoStatusBlock
;
242 LARGE_INTEGER EndEntriesOffset
;
244 /* First, get database size */
245 DatabaseSize
= GetRemoteDatabaseSize(Database
);
248 return STATUS_INVALID_PARAMETER
;
251 /* Then, get the entry to remove */
252 Entry
= GetRemoteDatabaseEntry(Database
, StartingOffset
);
255 return STATUS_INVALID_PARAMETER
;
258 /* Validate parameters: ensure we won't get negative size */
259 if (Entry
->EntrySize
+ StartingOffset
> DatabaseSize
)
261 /* If we get invalid parameters, truncate the whole database
262 * starting the wrong entry. We can't rely on the rest
265 return TruncateRemoteDatabase(Database
, StartingOffset
);
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
);
275 return STATUS_INSUFFICIENT_RESOURCES
;
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 */
283 /* Read the ending entries */
284 Status
= ZwReadFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
285 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
286 if (!NT_SUCCESS(Status
))
292 /* Ensure nothing went wrong - we don't want to corrupt the DB */
293 if (IoStatusBlock
.Information
!= EndSize
)
296 return STATUS_INVALID_PARAMETER
;
299 /* Remove the entry */
300 Status
= TruncateRemoteDatabase(Database
, StartingOffset
+ EndSize
);
301 if (!NT_SUCCESS(Status
))
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
);
322 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName
,
325 IN ULONG ValueLength
,
327 IN PVOID EntryContext
)
329 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
331 UNREFERENCED_PARAMETER(ValueType
);
332 UNREFERENCED_PARAMETER(EntryContext
);
334 /* Ensure it matches, and delete */
335 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
336 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
339 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
344 return STATUS_SUCCESS
;
351 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
352 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
354 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
356 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
357 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
358 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
360 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
371 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
374 LARGE_INTEGER Timeout
;
376 /* Wait for 7 minutes */
377 Timeout
.QuadPart
= 0xFA0A1F00;
378 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
379 if (Status
!= STATUS_TIMEOUT
)
384 return STATUS_IO_TIMEOUT
;
391 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
393 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
401 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
404 IN ULONG ValueLength
,
406 IN PVOID EntryContext
)
408 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
409 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
411 UNREFERENCED_PARAMETER(ValueName
);
412 UNREFERENCED_PARAMETER(ValueType
);
413 UNREFERENCED_PARAMETER(EntryContext
);
416 if (ValueLength
>= 0x10000)
418 return STATUS_SUCCESS
;
421 /* Allocate the Unique ID */
422 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
425 /* Copy data & return */
426 IntUniqueId
->UniqueIdLength
= (USHORT
)ValueLength
;
427 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
430 *UniqueId
= IntUniqueId
;
433 return STATUS_SUCCESS
;
440 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
441 IN PUNICODE_STRING SymbolicName
,
442 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
445 PDEVICE_INFORMATION DeviceInformation
;
446 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
448 /* Query the unique ID */
449 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
450 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
451 QueryTable
[0].Name
= SymbolicName
->Buffer
;
454 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
459 /* Unique ID found, no need to go farther */
462 return STATUS_SUCCESS
;
465 /* Otherwise, find associate device information */
466 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
467 if (!NT_SUCCESS(Status
))
472 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
475 return STATUS_INSUFFICIENT_RESOURCES
;
478 /* Return this unique ID (better than nothing) */
479 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
480 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
482 return STATUS_SUCCESS
;
489 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
490 IN PDATABASE_ENTRY DatabaseEntry
)
494 PLIST_ENTRY NextEntry
;
495 UNICODE_STRING SymbolicString
;
496 PDEVICE_INFORMATION DeviceInformation
;
498 /* Create symbolic name from database entry */
499 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
502 return STATUS_INSUFFICIENT_RESOURCES
;
505 RtlCopyMemory(SymbolicName
,
506 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
507 DatabaseEntry
->SymbolicNameLength
);
508 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
510 /* Associate the unique ID with the name from remote database */
511 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
515 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
516 DatabaseEntry
->UniqueIdLength
);
517 FreePool(SymbolicName
);
519 /* Reget symbolic name */
520 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
521 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
522 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
524 /* Find the device using this unique ID */
525 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
526 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
527 NextEntry
= NextEntry
->Flink
)
529 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
533 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
538 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
539 DeviceInformation
->UniqueId
->UniqueId
,
540 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
546 /* If found, create a mount point */
547 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
549 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
560 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
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
;
581 /* We're unloading, do nothing */
588 if (!NT_SUCCESS(WaitForRemoteDatabaseSemaphore(DeviceExtension
)))
593 /* Recheck for unloading */
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
)
605 ListDeviceInfo
= CONTAINING_RECORD(Entry
, DEVICE_INFORMATION
, DeviceListEntry
);
606 if (ListDeviceInfo
== DeviceInformation
)
612 /* If not found, or if removable, bail out */
613 if (Entry
== &DeviceExtension
->DeviceListHead
|| DeviceInformation
->Removable
)
615 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
619 /* Get our device object */
620 Status
= IoGetDeviceObjectPointer(&ListDeviceInfo
->DeviceName
, FILE_READ_ATTRIBUTES
, &FileObject
, &DeviceObject
);
621 if (!NT_SUCCESS(Status
))
623 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
627 /* Mark mounted only if not unloading */
628 if (!(DeviceObject
->Flags
& DO_UNLOAD_PENDING
))
630 InterlockedExchangeAdd(&ListDeviceInfo
->MountState
, 1);
633 ObDereferenceObject(FileObject
);
635 /* Force default: no DB, and need for reconcile */
636 DeviceInformation
->NeedsReconcile
= TRUE
;
637 DeviceInformation
->NoDatabase
= TRUE
;
638 FailedFinding
= FALSE
;
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
)
645 ListDeviceInfo
= CONTAINING_RECORD(Entry
, DEVICE_INFORMATION
, DeviceListEntry
);
647 EntryInfo
= ListDeviceInfo
->AssociatedDevicesHead
.Flink
;
648 while (EntryInfo
!= &ListDeviceInfo
->AssociatedDevicesHead
)
650 AssociatedDevice
= CONTAINING_RECORD(EntryInfo
, ASSOCIATED_DEVICE_ENTRY
, AssociatedDevicesEntry
);
651 NextEntry
= EntryInfo
->Flink
;
653 if (AssociatedDevice
->DeviceInformation
== DeviceInformation
)
655 RemoveEntryList(&AssociatedDevice
->AssociatedDevicesEntry
);
656 FreePool(AssociatedDevice
->String
.Buffer
);
657 FreePool(AssociatedDevice
);
660 EntryInfo
= NextEntry
;
664 /* Open the remote database */
665 DatabaseHandle
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
667 /* Prepare a string with reparse point index */
668 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
669 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
670 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
671 if (ReparseFile
.Buffer
== NULL
)
673 if (DatabaseHandle
!= 0)
675 CloseRemoteDatabase(DatabaseHandle
);
677 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
681 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
682 DeviceInformation
->DeviceName
.Length
);
683 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
684 ReparseIndex
.Buffer
, ReparseIndex
.Length
);
685 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
687 InitializeObjectAttributes(&ObjectAttributes
,
689 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
693 /* Open reparse point directory */
694 HardwareErrors
= IoSetThreadHardErrorMode(FALSE
);
695 Status
= ZwOpenFile(&Handle
,
699 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
700 FILE_SYNCHRONOUS_IO_ALERT
);
701 IoSetThreadHardErrorMode(HardwareErrors
);
703 FreePool(ReparseFile
.Buffer
);
705 if (!NT_SUCCESS(Status
))
707 if (DatabaseHandle
!= 0)
709 TruncateRemoteDatabase(DatabaseHandle
, 0);
710 CloseRemoteDatabase(DatabaseHandle
);
712 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
716 /* Query reparse point information
717 * We only pay attention to mout point
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
,
729 &ReparsePointInformation
,
730 sizeof(FILE_REPARSE_POINT_INFORMATION
),
731 FileReparsePointInformation
,
735 if (!NT_SUCCESS(Status
))
738 if (DatabaseHandle
!= 0)
740 TruncateRemoteDatabase(DatabaseHandle
, 0);
741 CloseRemoteDatabase(DatabaseHandle
);
743 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
747 /* If we failed to open the remote DB previously,
748 * retry this time allowing migration (and thus, creation if required)
750 if (DatabaseHandle
== 0)
752 DatabaseHandle
= OpenRemoteDatabase(DeviceInformation
, TRUE
);
753 if (DatabaseHandle
== 0)
755 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
760 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
762 /* Reset all the references to our DB entries */
766 DatabaseEntry
= GetRemoteDatabaseEntry(DatabaseHandle
, Offset
);
767 if (DatabaseEntry
== NULL
)
772 DatabaseEntry
->EntryReferences
= 0;
773 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
774 if (!NT_SUCCESS(Status
))
776 FreePool(DatabaseEntry
);
780 Offset
+= DatabaseEntry
->EntrySize
;
781 FreePool(DatabaseEntry
);
784 /* Init string for QueryVolumeName call */
785 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
786 SymbolicName
.Length
= 0;
787 SymbolicName
.Buffer
= SymbolicNameBuffer
;
790 /* Start looping on reparse points */
793 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
794 Status
= ZwQueryDirectoryFile(Handle
,
799 &ReparsePointInformation
,
800 sizeof(FILE_REPARSE_POINT_INFORMATION
),
801 FileReparsePointInformation
,
803 Restart
? &FileName
: NULL
,
805 /* Restart only once */
812 /* If we get the same one, we're done, bail out */
813 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
814 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
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
)
826 /* Get the volume name associated to the mount point */
827 Status
= QueryVolumeName(Handle
, &ReparsePointInformation
, 0, &SymbolicName
, &VolumeName
);
828 if (!NT_SUCCESS(Status
))
833 /* Browse the DB to find the name */
837 UNICODE_STRING DbName
;
839 DatabaseEntry
= GetRemoteDatabaseEntry(DatabaseHandle
, Offset
);
840 if (DatabaseEntry
== NULL
)
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
))
854 Offset
+= DatabaseEntry
->EntrySize
;
855 FreePool(DatabaseEntry
);
858 /* If we found the mount point.... */
859 if (DatabaseEntry
!= NULL
)
861 /* If it was referenced, reference it once more and update to remote */
862 if (DatabaseEntry
->EntryReferences
)
864 ++DatabaseEntry
->EntryReferences
;
865 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
866 if (!NT_SUCCESS(Status
))
871 FreePool(DatabaseEntry
);
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
))
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
))
884 goto ReleaseDeviceLock
;
887 /* And then, reference & write the entry */
888 ++DatabaseEntry
->EntryReferences
;
889 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
890 if (!NT_SUCCESS(Status
))
892 goto ReleaseDeviceLock
;
895 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
896 FreePool(DatabaseEntry
);
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
)
904 /* Reference the entry, and update to remote */
905 ++DatabaseEntry
->EntryReferences
;
906 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
907 if (!NT_SUCCESS(Status
))
913 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
914 FreePool(DatabaseEntry
);
916 /* Would, by chance, the Unique ID be present elsewhere? */
917 else if (IsUniqueIdPresent(DeviceExtension
, DatabaseEntry
))
919 /* Push the ID to master */
920 Status
= WriteUniqueIdToMaster(DeviceExtension
, DatabaseEntry
);
921 if (!NT_SUCCESS(Status
))
926 /* And then, reference & write the entry */
927 ++DatabaseEntry
->EntryReferences
;
928 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
929 if (!NT_SUCCESS(Status
))
935 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
936 FreePool(DatabaseEntry
);
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
944 Status
= DeleteRemoteDatabaseEntry(DatabaseHandle
, Offset
);
945 if (!NT_SUCCESS(Status
))
950 FreePool(DatabaseEntry
);
951 /* Allocate a new entry big enough */
952 DatabaseEntry
= AllocatePool(UniqueId
->UniqueIdLength
+ SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
));
953 if (DatabaseEntry
== NULL
)
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
);
968 /* And write it remotely */
969 Status
= AddRemoteDatabaseEntry(DatabaseHandle
, DatabaseEntry
);
970 if (!NT_SUCCESS(Status
))
972 FreePool(DatabaseEntry
);
977 FreePool(DatabaseEntry
);
978 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
984 /* We failed finding it remotely
985 * So, let's allocate a new remote DB entry
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
))
993 /* Allocate a new entry big enough */
994 DatabaseEntry
= AllocatePool(UniqueId
->UniqueIdLength
+ SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
));
995 if (DatabaseEntry
!= NULL
)
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
);
1007 /* And write it remotely */
1008 Status
= AddRemoteDatabaseEntry(DatabaseHandle
, DatabaseEntry
);
1009 FreePool(DatabaseEntry
);
1012 if (!NT_SUCCESS(Status
))
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
))
1029 FailedFinding
= TRUE
;
1030 FreePool(VolumeName
.Buffer
);
1034 /* Associate the device with the currrent DB */
1035 AssociatedDevice
= AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY
));
1036 if (AssociatedDevice
== NULL
)
1038 FreePool(VolumeName
.Buffer
);
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
);
1049 /* If we don't have to skip notifications, notify */
1050 if (!ListDeviceInfo
->SkipNotifications
)
1052 PostOnlineNotification(DeviceExtension
, &ListDeviceInfo
->SymbolicName
);
1056 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1059 /* We don't need mount points any longer */
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
)
1068 ListDeviceInfo
= CONTAINING_RECORD(Entry
, DEVICE_INFORMATION
, DeviceListEntry
);
1069 if (ListDeviceInfo
== DeviceInformation
)
1075 if (Entry
== &DeviceExtension
->DeviceListHead
)
1077 ListDeviceInfo
= NULL
;
1080 /* Start the pruning loop */
1085 DatabaseEntry
= GetRemoteDatabaseEntry(DatabaseHandle
, Offset
);
1086 if (DatabaseEntry
== NULL
)
1091 /* It's not referenced anylonger? Prune it */
1092 if (DatabaseEntry
->EntryReferences
== 0)
1094 Status
= DeleteRemoteDatabaseEntry(DatabaseHandle
, Offset
);
1095 if (!NT_SUCCESS(Status
))
1097 FreePool(DatabaseEntry
);
1098 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1102 /* Update the Unique IDs to reflect the changes we might have done previously */
1105 if (ListDeviceInfo
!= NULL
)
1107 UpdateReplicatedUniqueIds(ListDeviceInfo
, DatabaseEntry
);
1110 Offset
+= DatabaseEntry
->EntrySize
;
1113 FreePool(DatabaseEntry
);
1116 /* We do have a DB now :-) */
1117 if (ListDeviceInfo
!= NULL
&& !FailedFinding
)
1119 DeviceInformation
->NoDatabase
= FALSE
;
1122 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1129 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1131 FreePool(DatabaseEntry
);
1133 FreePool(VolumeName
.Buffer
);
1137 CloseRemoteDatabase(DatabaseHandle
);
1139 ReleaseRemoteDatabaseSemaphore(DeviceExtension
);
1148 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
1157 LARGE_INTEGER Timeout
;
1158 PRECONCILE_WORK_ITEM WorkItem
;
1159 PDEVICE_EXTENSION DeviceExtension
;
1160 OBJECT_ATTRIBUTES ObjectAttributes
;
1162 UNREFERENCED_PARAMETER(DeviceObject
);
1164 InitializeObjectAttributes(&ObjectAttributes
,
1166 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1169 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1170 Timeout
.QuadPart
= -10000000LL; /* Wait for 1 second */
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
1181 /* If we started to shutdown, stop waiting forever and jump to last attempt */
1188 /* Attempt to open the event */
1189 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
1190 if (NT_SUCCESS(Status
))
1195 /* Wait a bit to give SMSS a chance to create the event */
1196 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
1203 /* We managed to open the event, wait until autochk signals it */
1208 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
1210 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
1215 DeviceExtension
= Context
;
1217 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
1219 /* Acquire workers lock */
1220 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
1222 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
1224 /* Ensure there are workers */
1225 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
1227 /* Unqueue a worker */
1228 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
1229 WorkItem
= CONTAINING_RECORD(Entry
,
1230 RECONCILE_WORK_ITEM
,
1231 WorkerQueueListEntry
);
1233 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
1236 WorkItem
->WorkerRoutine(WorkItem
->Context
);
1238 IoFreeWorkItem(WorkItem
->WorkItem
);
1241 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) < 0)
1246 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
1247 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
1249 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
1251 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
1254 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
1261 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
1262 IN PRECONCILE_WORK_ITEM WorkItem
,
1267 WorkItem
->Context
= Context
;
1269 /* When called, lock is already acquired */
1271 /* If noone, start to work */
1272 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
1274 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
1277 /* Otherwise queue worker for delayed execution */
1278 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
1279 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
1280 &(WorkItem
->WorkerQueueListEntry
));
1281 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
1283 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
1285 return STATUS_SUCCESS
;
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
)
1301 IO_STATUS_BLOCK IoStatusBlock
;
1302 OBJECT_ATTRIBUTES ObjectAttributes
;
1303 PFILE_NAME_INFORMATION FileNameInfo
;
1304 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
1306 UNREFERENCED_PARAMETER(ReparsePointInformation
);
1310 InitializeObjectAttributes(&ObjectAttributes
,
1318 InitializeObjectAttributes(&ObjectAttributes
,
1320 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
1326 Status
= ZwOpenFile(&Handle
,
1327 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
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
))
1338 /* Get the reparse point data */
1339 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
1340 if (!ReparseDataBuffer
)
1343 return STATUS_INSUFFICIENT_RESOURCES
;
1346 Status
= ZwFsControlFile(Handle
,
1351 FSCTL_GET_REPARSE_POINT
,
1355 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
1356 if (!NT_SUCCESS(Status
))
1358 FreePool(ReparseDataBuffer
);
1363 /* Check that name can fit in buffer */
1364 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
1366 FreePool(ReparseDataBuffer
);
1368 return STATUS_BUFFER_TOO_SMALL
;
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
);
1378 FreePool(ReparseDataBuffer
);
1380 /* Name has to \ terminated */
1381 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
1384 return STATUS_INVALID_PARAMETER
;
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
;
1391 /* Also ensure it's really a volume name... */
1392 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
1395 return STATUS_INVALID_PARAMETER
;
1398 /* Now prepare to really get the name */
1399 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
1403 return STATUS_INSUFFICIENT_RESOURCES
;
1406 Status
= ZwQueryInformationFile(Handle
,
1409 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
1410 FileNameInformation
);
1411 if (Status
== STATUS_BUFFER_OVERFLOW
)
1413 /* As expected... Reallocate with proper size */
1414 NeededLength
= FileNameInfo
->FileNameLength
;
1415 FreePool(FileNameInfo
);
1417 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
1421 return STATUS_INSUFFICIENT_RESOURCES
;
1424 /* And query name */
1425 Status
= ZwQueryInformationFile(Handle
,
1428 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
1429 FileNameInformation
);
1434 if (!NT_SUCCESS(Status
))
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
)
1445 return STATUS_INSUFFICIENT_RESOURCES
;
1448 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
1449 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1451 FreePool(FileNameInfo
);
1453 return STATUS_SUCCESS
;
1460 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
1461 IN PDEVICE_INFORMATION DeviceInformation
)
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
;
1473 /* Removable devices don't have remote database on them */
1474 if (DeviceInformation
->Removable
)
1479 /* Prepare a string with reparse point index */
1480 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
1481 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
1482 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
1483 if (!ReparseFile
.Buffer
)
1488 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
1489 DeviceInformation
->DeviceName
.Length
);
1490 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
1491 ReparseFile
.Buffer
, ReparseFile
.Length
);
1492 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1494 InitializeObjectAttributes(&ObjectAttributes
,
1496 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
1500 /* Open reparse point */
1501 Status
= ZwOpenFile(&Handle
,
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
))
1510 DeviceInformation
->NoDatabase
= FALSE
;
1514 /* Query reparse point information
1515 * We only pay attention to mout point
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
,
1527 &ReparsePointInformation
,
1528 sizeof(FILE_REPARSE_POINT_INFORMATION
),
1529 FileReparsePointInformation
,
1533 if (!NT_SUCCESS(Status
))
1541 /* Query mount points */
1544 SymbolicName
.Length
= 0;
1545 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
1546 SymbolicName
.Buffer
= SymbolicNameBuffer
;
1547 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
1549 Status
= ZwQueryDirectoryFile(Handle
,
1554 &ReparsePointInformation
,
1555 sizeof(FILE_REPARSE_POINT_INFORMATION
),
1556 FileReparsePointInformation
,
1558 (RestartScan
) ? &FileName
: NULL
,
1562 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
1563 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
1570 RestartScan
= FALSE
;
1573 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
1578 /* Get the volume name associated to the mount point */
1579 Status
= QueryVolumeName(Handle
,
1580 &ReparsePointInformation
,
1581 NULL
, &SymbolicName
,
1583 if (!NT_SUCCESS(Status
))
1588 FreePool(VolumeName
.Buffer
);
1590 /* Get its information */
1591 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
1592 FALSE
, &VolumeDeviceInformation
);
1593 if (!NT_SUCCESS(Status
))
1595 DeviceInformation
->NoDatabase
= TRUE
;
1599 /* If notification are enabled, mark it online */
1600 if (!DeviceInformation
->SkipNotifications
)
1602 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
1613 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1614 IN PDEVICE_INFORMATION DeviceInformation
)
1616 PRECONCILE_WORK_ITEM WorkItem
;
1618 /* Removable devices don't have remote database */
1619 if (DeviceInformation
->Removable
)
1624 /* Allocate a work item */
1625 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
1631 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
1632 if (!WorkItem
->WorkItem
)
1639 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
1640 WorkItem
->DeviceExtension
= DeviceExtension
;
1641 WorkItem
->DeviceInformation
= DeviceInformation
;
1642 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
1644 /* If there's no automount, and automatic letters
1645 * all volumes to find those online and notify there presence
1647 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
1648 DeviceExtension
->AutomaticDriveLetter
== 1 &&
1649 DeviceExtension
->NoAutoMount
== FALSE
)
1651 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
1659 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
1661 PLIST_ENTRY NextEntry
;
1662 PDEVICE_INFORMATION DeviceInformation
;
1664 /* Browse all the devices */
1665 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1666 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1667 NextEntry
= NextEntry
->Flink
)
1669 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1672 /* If it's not removable, then, it might have a database to sync */
1673 if (!DeviceInformation
->Removable
)
1675 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
1685 CreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
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
;
1696 UNREFERENCED_PARAMETER(DeviceObject
);
1698 /* Extract context */
1700 DeviceInformation
= WorkItem
->DeviceInformation
;
1702 /* Reconstruct appropriate string */
1703 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1704 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
1705 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
1706 if (DatabaseName
.Buffer
== NULL
)
1708 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1712 /* Create the required folder (in which the database will be stored
1713 * \System Volume Information at root of the volume
1715 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
1716 if (!NT_SUCCESS(Status
))
1721 /* Finish initiating strings */
1722 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1723 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1724 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1725 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1727 /* Create database */
1728 InitializeObjectAttributes(&ObjectAttributes
,
1730 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1734 Status
= IoCreateFile(&Database
,
1735 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1736 FILE_READ_ATTRIBUTES
| FILE_WRITE_EA
| FILE_READ_EA
|
1737 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1741 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1744 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1749 IO_STOP_ON_SYMLINK
| IO_NO_PARAMETER_CHECKING
);
1750 if (!NT_SUCCESS(Status
))
1752 if (Status
== STATUS_STOPPED_ON_SYMLINK
)
1754 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1762 if (DatabaseName
.Buffer
)
1764 FreePool(DatabaseName
.Buffer
);
1767 if (NT_SUCCESS(Status
))
1769 DeviceInformation
->Migrated
= 1;
1771 else if (Database
!= 0)
1776 IoFreeWorkItem(WorkItem
->WorkItem
);
1778 WorkItem
->WorkItem
= NULL
;
1779 WorkItem
->Status
= Status
;
1780 WorkItem
->Database
= Database
;
1782 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1789 CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1790 IN OUT PHANDLE Database
)
1794 PMIGRATE_WORK_ITEM WorkItem
;
1796 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1798 /* Allocate a work item dedicated to migration */
1799 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1803 return STATUS_INSUFFICIENT_RESOURCES
;
1806 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1807 WorkItem
->Event
= &Event
;
1808 WorkItem
->DeviceInformation
= DeviceInformation
;
1809 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1810 if (!WorkItem
->WorkItem
)
1814 return STATUS_INSUFFICIENT_RESOURCES
;
1818 IoQueueWorkItem(WorkItem
->WorkItem
,
1819 CreateRemoteDatabaseWorker
,
1823 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1824 Status
= WorkItem
->Status
;
1826 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1836 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1837 IN BOOLEAN MigrateDatabase
)
1841 BOOLEAN PreviousMode
;
1842 IO_STATUS_BLOCK IoStatusBlock
;
1843 OBJECT_ATTRIBUTES ObjectAttributes
;
1844 UNICODE_STRING DeviceRemoteDatabase
;
1848 /* Get database name */
1849 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1850 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1851 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1852 if (!DeviceRemoteDatabase
.Buffer
)
1857 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1858 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1859 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1860 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1863 InitializeObjectAttributes(&ObjectAttributes
,
1864 &DeviceRemoteDatabase
,
1865 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1869 /* Disable hard errors */
1870 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1872 Status
= IoCreateFile(&Database
,
1873 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1874 FILE_READ_ATTRIBUTES
| FILE_WRITE_EA
| FILE_READ_EA
|
1875 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1879 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1881 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1882 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1887 IO_STOP_ON_SYMLINK
| IO_NO_PARAMETER_CHECKING
);
1888 if (Status
== STATUS_STOPPED_ON_SYMLINK
)
1890 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1893 /* If base it to be migrated and was opened successfully, go ahead */
1894 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1896 CreateRemoteDatabase(DeviceInformation
, &Database
);
1899 IoSetThreadHardErrorMode(PreviousMode
);
1900 FreePool(DeviceRemoteDatabase
.Buffer
);
1909 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1910 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1911 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1915 PDATABASE_ENTRY Entry
, NewEntry
;
1916 NTSTATUS Status
= STATUS_SUCCESS
;
1918 /* Open the remote database */
1919 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1925 /* Get all the entries */
1928 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1934 /* Not the correct entry, skip it */
1935 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1937 Offset
+= Entry
->EntrySize
;
1942 /* Not the correct entry, skip it */
1943 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1944 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1945 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1947 Offset
+= Entry
->EntrySize
;
1952 /* Here, we have the correct entry */
1953 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1956 Offset
+= Entry
->EntrySize
;
1961 /* Recreate the entry from the previous one */
1962 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1963 NewEntry
->EntryReferences
= Entry
->EntryReferences
;
1964 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1965 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1966 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1967 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1968 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1969 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1970 NewEntry
->SymbolicNameLength
);
1971 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1972 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1974 /* Delete old entry */
1975 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1976 if (!NT_SUCCESS(Status
))
1983 /* And replace with new one */
1984 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1987 } while (NT_SUCCESS(Status
));
1989 CloseRemoteDatabase(Database
);
1999 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
2002 IN ULONG ValueLength
,
2004 IN PVOID EntryContext
)
2006 PMOUNTDEV_UNIQUE_ID UniqueId
;
2007 UNICODE_STRING RegistryEntry
;
2009 UNREFERENCED_PARAMETER(EntryContext
);
2011 if (ValueType
!= REG_BINARY
)
2013 return STATUS_SUCCESS
;
2018 /* First ensure we have the correct data */
2019 if (UniqueId
->UniqueIdLength
!= ValueLength
)
2021 return STATUS_SUCCESS
;
2024 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
2026 return STATUS_SUCCESS
;
2029 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
2031 /* Then, it's a drive letter, erase it */
2032 if (IsDriveLetter(&RegistryEntry
))
2034 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
2039 return STATUS_SUCCESS
;
2046 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
2048 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
2050 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
2051 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
2053 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
2065 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
2068 IN ULONG ValueLength
,
2070 IN PVOID EntryContext
)
2072 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
2074 UNREFERENCED_PARAMETER(EntryContext
);
2076 /* Ensure we have correct input */
2077 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
2078 UniqueId
->UniqueIdLength
!= ValueLength
)
2080 return STATUS_SUCCESS
;
2083 /* And then, if unique ID matching, delete entry */
2084 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
2086 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
2091 return STATUS_SUCCESS
;
2098 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
2100 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
2102 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
2103 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
2105 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,