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
);
682 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
683 DeviceInformation
->DeviceName
.Length
);
684 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
685 ReparseFile
.Buffer
, ReparseFile
.Length
);
686 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
688 InitializeObjectAttributes(&ObjectAttributes
,
690 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
694 /* Open reparse point directory */
695 HardwareErrors
= IoSetThreadHardErrorMode(FALSE
);
696 Status
= ZwOpenFile(&Handle
,
700 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
701 FILE_SYNCHRONOUS_IO_ALERT
);
702 IoSetThreadHardErrorMode(HardwareErrors
);
704 FreePool(ReparseFile
.Buffer
);
706 if (!NT_SUCCESS(Status
))
708 if (DatabaseHandle
!= 0)
710 TruncateRemoteDatabase(DatabaseHandle
, 0);
711 CloseRemoteDatabase(DatabaseHandle
);
713 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
717 /* Query reparse point information
718 * We only pay attention to mout point
720 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
721 FileName
.Buffer
= FileNameBuffer
;
722 FileName
.Length
= sizeof(FileNameBuffer
);
723 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
724 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
725 Status
= ZwQueryDirectoryFile(Handle
,
730 &ReparsePointInformation
,
731 sizeof(FILE_REPARSE_POINT_INFORMATION
),
732 FileReparsePointInformation
,
736 if (!NT_SUCCESS(Status
))
739 if (DatabaseHandle
!= 0)
741 TruncateRemoteDatabase(DatabaseHandle
, 0);
742 CloseRemoteDatabase(DatabaseHandle
);
744 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
748 /* If we failed to open the remote DB previously,
749 * retry this time allowing migration (and thus, creation if required)
751 if (DatabaseHandle
== 0)
753 DatabaseHandle
= OpenRemoteDatabase(DeviceInformation
, TRUE
);
754 if (DatabaseHandle
== 0)
756 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
761 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
763 /* Reset all the references to our DB entries */
767 DatabaseEntry
= GetRemoteDatabaseEntry(DatabaseHandle
, Offset
);
768 if (DatabaseEntry
== NULL
)
773 DatabaseEntry
->EntryReferences
= 0;
774 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
775 if (!NT_SUCCESS(Status
))
777 FreePool(DatabaseEntry
);
781 Offset
+= DatabaseEntry
->EntrySize
;
782 FreePool(DatabaseEntry
);
785 /* Init string for QueryVolumeName call */
786 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
787 SymbolicName
.Length
= 0;
788 SymbolicName
.Buffer
= SymbolicNameBuffer
;
791 /* Start looping on reparse points */
794 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
795 Status
= ZwQueryDirectoryFile(Handle
,
800 &ReparsePointInformation
,
801 sizeof(FILE_REPARSE_POINT_INFORMATION
),
802 FileReparsePointInformation
,
804 Restart
? &FileName
: NULL
,
806 /* Restart only once */
813 /* If we get the same one, we're done, bail out */
814 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
815 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
821 /* If querying failed, or if onloading, or if not returning mount points, bail out */
822 if (!NT_SUCCESS(Status
) || Unloading
|| ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
827 /* Get the volume name associated to the mount point */
828 Status
= QueryVolumeName(Handle
, &ReparsePointInformation
, 0, &SymbolicName
, &VolumeName
);
829 if (!NT_SUCCESS(Status
))
834 /* Browse the DB to find the name */
838 UNICODE_STRING DbName
;
840 DatabaseEntry
= GetRemoteDatabaseEntry(DatabaseHandle
, Offset
);
841 if (DatabaseEntry
== NULL
)
846 DbName
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
847 DbName
.Length
= DbName
.MaximumLength
;
848 DbName
.Buffer
= (PWSTR
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
849 /* Found, we're done! */
850 if (RtlEqualUnicodeString(&DbName
, &SymbolicName
, TRUE
))
855 Offset
+= DatabaseEntry
->EntrySize
;
856 FreePool(DatabaseEntry
);
859 /* If we found the mount point.... */
860 if (DatabaseEntry
!= NULL
)
862 /* If it was referenced, reference it once more and update to remote */
863 if (DatabaseEntry
->EntryReferences
)
865 ++DatabaseEntry
->EntryReferences
;
866 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
867 if (!NT_SUCCESS(Status
))
872 FreePool(DatabaseEntry
);
876 /* Query the Unique ID associated to that mount point in case it changed */
877 KeWaitForSingleObject(&DeviceExtension
->DeviceLock
, Executive
, KernelMode
, FALSE
, NULL
);
878 Status
= QueryUniqueIdFromMaster(DeviceExtension
, &SymbolicName
, &UniqueId
);
879 if (!NT_SUCCESS(Status
))
881 /* If we failed doing so, reuse the old Unique ID and push it to master */
882 Status
= WriteUniqueIdToMaster(DeviceExtension
, DatabaseEntry
);
883 if (!NT_SUCCESS(Status
))
885 goto ReleaseDeviceLock
;
888 /* And then, reference & write the entry */
889 ++DatabaseEntry
->EntryReferences
;
890 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
891 if (!NT_SUCCESS(Status
))
893 goto ReleaseDeviceLock
;
896 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
897 FreePool(DatabaseEntry
);
899 /* If the Unique ID didn't change */
900 else if (UniqueId
->UniqueIdLength
== DatabaseEntry
->UniqueIdLength
&&
901 RtlCompareMemory(UniqueId
->UniqueId
,
902 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
903 UniqueId
->UniqueIdLength
) == UniqueId
->UniqueIdLength
)
905 /* Reference the entry, and update to remote */
906 ++DatabaseEntry
->EntryReferences
;
907 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
908 if (!NT_SUCCESS(Status
))
914 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
915 FreePool(DatabaseEntry
);
917 /* Would, by chance, the Unique ID be present elsewhere? */
918 else if (IsUniqueIdPresent(DeviceExtension
, DatabaseEntry
))
920 /* Push the ID to master */
921 Status
= WriteUniqueIdToMaster(DeviceExtension
, DatabaseEntry
);
922 if (!NT_SUCCESS(Status
))
927 /* And then, reference & write the entry */
928 ++DatabaseEntry
->EntryReferences
;
929 Status
= WriteRemoteDatabaseEntry(DatabaseHandle
, Offset
, DatabaseEntry
);
930 if (!NT_SUCCESS(Status
))
936 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
937 FreePool(DatabaseEntry
);
941 /* OK, at that point, we're facing a totally unknown unique ID
942 * So, get rid of the old entry, and recreate a new one with
945 Status
= DeleteRemoteDatabaseEntry(DatabaseHandle
, Offset
);
946 if (!NT_SUCCESS(Status
))
951 FreePool(DatabaseEntry
);
952 /* Allocate a new entry big enough */
953 DatabaseEntry
= AllocatePool(UniqueId
->UniqueIdLength
+ SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
));
954 if (DatabaseEntry
== NULL
)
960 DatabaseEntry
->EntrySize
= UniqueId
->UniqueIdLength
+ SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
);
961 DatabaseEntry
->EntryReferences
= 1;
962 DatabaseEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
963 DatabaseEntry
->SymbolicNameLength
= SymbolicName
.Length
;
964 DatabaseEntry
->UniqueIdOffset
= SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
);
965 DatabaseEntry
->UniqueIdLength
= UniqueId
->UniqueIdLength
;
966 RtlCopyMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
), SymbolicName
.Buffer
, DatabaseEntry
->SymbolicNameLength
);
967 RtlCopyMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
), UniqueId
->UniqueId
, UniqueId
->UniqueIdLength
);
969 /* And write it remotely */
970 Status
= AddRemoteDatabaseEntry(DatabaseHandle
, DatabaseEntry
);
971 if (!NT_SUCCESS(Status
))
973 FreePool(DatabaseEntry
);
978 FreePool(DatabaseEntry
);
979 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
985 /* We failed finding it remotely
986 * So, let's allocate a new remote DB entry
988 KeWaitForSingleObject(&DeviceExtension
->DeviceLock
, Executive
, KernelMode
, FALSE
, NULL
);
989 /* To be able to do so, we need the device Unique ID, ask master */
990 Status
= QueryUniqueIdFromMaster(DeviceExtension
, &SymbolicName
, &UniqueId
);
991 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
992 if (NT_SUCCESS(Status
))
994 /* Allocate a new entry big enough */
995 DatabaseEntry
= AllocatePool(UniqueId
->UniqueIdLength
+ SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
));
996 if (DatabaseEntry
!= NULL
)
999 DatabaseEntry
->EntrySize
= UniqueId
->UniqueIdLength
+ SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
);
1000 DatabaseEntry
->EntryReferences
= 1;
1001 DatabaseEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1002 DatabaseEntry
->SymbolicNameLength
= SymbolicName
.Length
;
1003 DatabaseEntry
->UniqueIdOffset
= SymbolicName
.Length
+ sizeof(DATABASE_ENTRY
);
1004 DatabaseEntry
->UniqueIdLength
= UniqueId
->UniqueIdLength
;
1005 RtlCopyMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
), SymbolicName
.Buffer
, DatabaseEntry
->SymbolicNameLength
);
1006 RtlCopyMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
), UniqueId
->UniqueId
, UniqueId
->UniqueIdLength
);
1008 /* And write it remotely */
1009 Status
= AddRemoteDatabaseEntry(DatabaseHandle
, DatabaseEntry
);
1010 FreePool(DatabaseEntry
);
1013 if (!NT_SUCCESS(Status
))
1025 /* Find info about the device associated associated with the mount point */
1026 KeWaitForSingleObject(&DeviceExtension
->DeviceLock
, Executive
, KernelMode
, FALSE
, NULL
);
1027 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
, FALSE
, &ListDeviceInfo
);
1028 if (!NT_SUCCESS(Status
))
1030 FailedFinding
= TRUE
;
1031 FreePool(VolumeName
.Buffer
);
1035 /* Associate the device with the currrent DB */
1036 AssociatedDevice
= AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY
));
1037 if (AssociatedDevice
== NULL
)
1039 FreePool(VolumeName
.Buffer
);
1043 AssociatedDevice
->DeviceInformation
= DeviceInformation
;
1044 AssociatedDevice
->String
.Length
= VolumeName
.Length
;
1045 AssociatedDevice
->String
.MaximumLength
= VolumeName
.MaximumLength
;
1046 AssociatedDevice
->String
.Buffer
= VolumeName
.Buffer
;
1047 InsertTailList(&ListDeviceInfo
->AssociatedDevicesHead
, &AssociatedDevice
->AssociatedDevicesEntry
);
1050 /* If we don't have to skip notifications, notify */
1051 if (!ListDeviceInfo
->SkipNotifications
)
1053 PostOnlineNotification(DeviceExtension
, &ListDeviceInfo
->SymbolicName
);
1057 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1060 /* We don't need mount points any longer */
1063 /* Look for the DB again */
1064 KeWaitForSingleObject(&DeviceExtension
->DeviceLock
, Executive
, KernelMode
, FALSE
, NULL
);
1065 for (Entry
= DeviceExtension
->DeviceListHead
.Flink
;
1066 Entry
!= &DeviceExtension
->DeviceListHead
;
1067 Entry
= Entry
->Flink
)
1069 ListDeviceInfo
= CONTAINING_RECORD(Entry
, DEVICE_INFORMATION
, DeviceListEntry
);
1070 if (ListDeviceInfo
== DeviceInformation
)
1076 if (Entry
== &DeviceExtension
->DeviceListHead
)
1078 ListDeviceInfo
= NULL
;
1081 /* Start the pruning loop */
1086 DatabaseEntry
= GetRemoteDatabaseEntry(DatabaseHandle
, Offset
);
1087 if (DatabaseEntry
== NULL
)
1092 /* It's not referenced anylonger? Prune it */
1093 if (DatabaseEntry
->EntryReferences
== 0)
1095 Status
= DeleteRemoteDatabaseEntry(DatabaseHandle
, Offset
);
1096 if (!NT_SUCCESS(Status
))
1098 FreePool(DatabaseEntry
);
1099 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1103 /* Update the Unique IDs to reflect the changes we might have done previously */
1106 if (ListDeviceInfo
!= NULL
)
1108 UpdateReplicatedUniqueIds(ListDeviceInfo
, DatabaseEntry
);
1111 Offset
+= DatabaseEntry
->EntrySize
;
1114 FreePool(DatabaseEntry
);
1117 /* We do have a DB now :-) */
1118 if (ListDeviceInfo
!= NULL
&& !FailedFinding
)
1120 DeviceInformation
->NoDatabase
= FALSE
;
1123 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1130 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
1132 FreePool(DatabaseEntry
);
1134 FreePool(VolumeName
.Buffer
);
1138 CloseRemoteDatabase(DatabaseHandle
);
1140 ReleaseRemoteDatabaseSemaphore(DeviceExtension
);
1149 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
1158 LARGE_INTEGER Timeout
;
1159 PRECONCILE_WORK_ITEM WorkItem
;
1160 PDEVICE_EXTENSION DeviceExtension
;
1161 OBJECT_ATTRIBUTES ObjectAttributes
;
1163 UNREFERENCED_PARAMETER(DeviceObject
);
1165 InitializeObjectAttributes(&ObjectAttributes
,
1167 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1170 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1171 Timeout
.LowPart
= 0xFFFFFFFF;
1172 Timeout
.HighPart
= 0xFF676980;
1174 /* Try to wait as long as possible */
1175 for (i
= (Unloading
? 999 : 0); i
< 1000; i
++)
1177 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
1178 if (NT_SUCCESS(Status
))
1183 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
1190 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
1192 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
1197 DeviceExtension
= Context
;
1199 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
1201 /* Acquire workers lock */
1202 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
1204 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
1206 /* Ensure there are workers */
1207 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
1209 /* Unqueue a worker */
1210 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
1211 WorkItem
= CONTAINING_RECORD(Entry
,
1212 RECONCILE_WORK_ITEM
,
1213 WorkerQueueListEntry
);
1215 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
1218 WorkItem
->WorkerRoutine(WorkItem
->Context
);
1220 IoFreeWorkItem(WorkItem
->WorkItem
);
1223 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) == 0)
1228 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
1229 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
1231 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
1233 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
1236 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
1243 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
1244 IN PRECONCILE_WORK_ITEM WorkItem
,
1249 WorkItem
->Context
= Context
;
1251 /* When called, lock is already acquired */
1253 /* If noone, start to work */
1254 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
1256 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
1259 /* Otherwise queue worker for delayed execution */
1260 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
1261 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
1262 &(WorkItem
->WorkerQueueListEntry
));
1263 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
1265 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
1267 return STATUS_SUCCESS
;
1274 QueryVolumeName(IN HANDLE RootDirectory
,
1275 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation
,
1276 IN PUNICODE_STRING FileName OPTIONAL
,
1277 OUT PUNICODE_STRING SymbolicName
,
1278 OUT PUNICODE_STRING VolumeName
)
1283 IO_STATUS_BLOCK IoStatusBlock
;
1284 OBJECT_ATTRIBUTES ObjectAttributes
;
1285 PFILE_NAME_INFORMATION FileNameInfo
;
1286 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
1288 UNREFERENCED_PARAMETER(ReparsePointInformation
);
1292 InitializeObjectAttributes(&ObjectAttributes
,
1300 InitializeObjectAttributes(&ObjectAttributes
,
1302 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
1308 Status
= ZwOpenFile(&Handle
,
1309 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
1312 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1313 (FileName
) ? FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
:
1314 FILE_OPEN_BY_FILE_ID
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
);
1315 if (!NT_SUCCESS(Status
))
1320 /* Get the reparse point data */
1321 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
1322 if (!ReparseDataBuffer
)
1325 return STATUS_INSUFFICIENT_RESOURCES
;
1328 Status
= ZwFsControlFile(Handle
,
1333 FSCTL_GET_REPARSE_POINT
,
1337 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
1338 if (!NT_SUCCESS(Status
))
1340 FreePool(ReparseDataBuffer
);
1345 /* Check that name can fit in buffer */
1346 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
1348 FreePool(ReparseDataBuffer
);
1350 return STATUS_BUFFER_TOO_SMALL
;
1353 /* Copy symbolic name */
1354 SymbolicName
->Length
= ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
1355 RtlCopyMemory(SymbolicName
->Buffer
,
1356 (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
1357 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
),
1358 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
1360 FreePool(ReparseDataBuffer
);
1362 /* Name has to \ terminated */
1363 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
1366 return STATUS_INVALID_PARAMETER
;
1369 /* So that we can delete it, and match mountmgr requirements */
1370 SymbolicName
->Length
-= sizeof(WCHAR
);
1371 SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1373 /* Also ensure it's really a volume name... */
1374 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
1377 return STATUS_INVALID_PARAMETER
;
1380 /* Now prepare to really get the name */
1381 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
1385 return STATUS_INSUFFICIENT_RESOURCES
;
1388 Status
= ZwQueryInformationFile(Handle
,
1391 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
1392 FileNameInformation
);
1393 if (Status
== STATUS_BUFFER_OVERFLOW
)
1395 /* As expected... Reallocate with proper size */
1396 NeededLength
= FileNameInfo
->FileNameLength
;
1397 FreePool(FileNameInfo
);
1399 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
1403 return STATUS_INSUFFICIENT_RESOURCES
;
1406 /* And query name */
1407 Status
= ZwQueryInformationFile(Handle
,
1410 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
1411 FileNameInformation
);
1416 if (!NT_SUCCESS(Status
))
1421 /* Return the volume name */
1422 VolumeName
->Length
= (USHORT
)FileNameInfo
->FileNameLength
;
1423 VolumeName
->MaximumLength
= (USHORT
)FileNameInfo
->FileNameLength
+ sizeof(WCHAR
);
1424 VolumeName
->Buffer
= AllocatePool(VolumeName
->MaximumLength
);
1425 if (!VolumeName
->Buffer
)
1427 return STATUS_INSUFFICIENT_RESOURCES
;
1430 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
1431 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1433 FreePool(FileNameInfo
);
1435 return STATUS_SUCCESS
;
1442 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
1443 IN PDEVICE_INFORMATION DeviceInformation
)
1447 BOOLEAN RestartScan
;
1448 IO_STATUS_BLOCK IoStatusBlock
;
1449 OBJECT_ATTRIBUTES ObjectAttributes
;
1450 PDEVICE_INFORMATION VolumeDeviceInformation
;
1451 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[0x64];
1452 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
1453 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
1455 /* Removable devices don't have remote database on them */
1456 if (DeviceInformation
->Removable
)
1461 /* Prepare a string with reparse point index */
1462 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
1463 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
1464 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
1465 if (!ReparseFile
.Buffer
)
1470 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
1471 DeviceInformation
->DeviceName
.Length
);
1472 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
1473 ReparseFile
.Buffer
, ReparseFile
.Length
);
1474 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1476 InitializeObjectAttributes(&ObjectAttributes
,
1478 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
1482 /* Open reparse point */
1483 Status
= ZwOpenFile(&Handle
,
1487 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1488 FILE_SYNCHRONOUS_IO_ALERT
| FILE_OPEN_REPARSE_POINT
);
1489 FreePool(ReparseFile
.Buffer
);
1490 if (!NT_SUCCESS(Status
))
1492 DeviceInformation
->NoDatabase
= FALSE
;
1496 /* Query reparse point information
1497 * We only pay attention to mout point
1499 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
1500 FileName
.Buffer
= FileNameBuffer
;
1501 FileName
.Length
= sizeof(FileNameBuffer
);
1502 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
1503 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
1504 Status
= ZwQueryDirectoryFile(Handle
,
1509 &ReparsePointInformation
,
1510 sizeof(FILE_REPARSE_POINT_INFORMATION
),
1511 FileReparsePointInformation
,
1515 if (!NT_SUCCESS(Status
))
1523 /* Query mount points */
1526 SymbolicName
.Length
= 0;
1527 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
1528 SymbolicName
.Buffer
= SymbolicNameBuffer
;
1529 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
1531 Status
= ZwQueryDirectoryFile(Handle
,
1536 &ReparsePointInformation
,
1537 sizeof(FILE_REPARSE_POINT_INFORMATION
),
1538 FileReparsePointInformation
,
1540 (RestartScan
) ? &FileName
: NULL
,
1544 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
1545 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
1552 RestartScan
= FALSE
;
1555 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
1560 /* Get the volume name associated to the mount point */
1561 Status
= QueryVolumeName(Handle
,
1562 &ReparsePointInformation
,
1563 NULL
, &SymbolicName
,
1565 if (!NT_SUCCESS(Status
))
1570 FreePool(VolumeName
.Buffer
);
1572 /* Get its information */
1573 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
1574 FALSE
, &VolumeDeviceInformation
);
1575 if (!NT_SUCCESS(Status
))
1577 DeviceInformation
->NoDatabase
= TRUE
;
1581 /* If notification are enabled, mark it online */
1582 if (!DeviceInformation
->SkipNotifications
)
1584 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
1595 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1596 IN PDEVICE_INFORMATION DeviceInformation
)
1598 PRECONCILE_WORK_ITEM WorkItem
;
1600 /* Removable devices don't have remote database */
1601 if (DeviceInformation
->Removable
)
1606 /* Allocate a work item */
1607 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
1613 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
1614 if (!WorkItem
->WorkItem
)
1621 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
1622 WorkItem
->DeviceExtension
= DeviceExtension
;
1623 WorkItem
->DeviceInformation
= DeviceInformation
;
1624 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
1626 /* If there's no automount, and automatic letters
1627 * all volumes to find those online and notify there presence
1629 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
1630 DeviceExtension
->AutomaticDriveLetter
== 1 &&
1631 DeviceExtension
->NoAutoMount
== FALSE
)
1633 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
1641 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
1643 PLIST_ENTRY NextEntry
;
1644 PDEVICE_INFORMATION DeviceInformation
;
1646 /* Browse all the devices */
1647 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1648 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1649 NextEntry
= NextEntry
->Flink
)
1651 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1654 /* If it's not removable, then, it might have a database to sync */
1655 if (!DeviceInformation
->Removable
)
1657 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
1667 CreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
1671 HANDLE Database
= 0;
1672 UNICODE_STRING DatabaseName
;
1673 PMIGRATE_WORK_ITEM WorkItem
;
1674 IO_STATUS_BLOCK IoStatusBlock
;
1675 OBJECT_ATTRIBUTES ObjectAttributes
;
1676 PDEVICE_INFORMATION DeviceInformation
;
1678 UNREFERENCED_PARAMETER(DeviceObject
);
1680 /* Extract context */
1682 DeviceInformation
= WorkItem
->DeviceInformation
;
1684 /* Reconstruct appropriate string */
1685 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1686 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
1687 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
1688 if (DatabaseName
.Buffer
== NULL
)
1690 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1694 /* Create the required folder (in which the database will be stored
1695 * \System Volume Information at root of the volume
1697 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
1698 if (!NT_SUCCESS(Status
))
1703 /* Finish initiating strings */
1704 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1705 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1706 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1707 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1709 /* Create database */
1710 InitializeObjectAttributes(&ObjectAttributes
,
1712 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1716 Status
= IoCreateFile(&Database
,
1717 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1718 FILE_READ_ATTRIBUTES
| FILE_WRITE_EA
| FILE_READ_EA
|
1719 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1723 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1726 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1731 IO_STOP_ON_SYMLINK
| IO_NO_PARAMETER_CHECKING
);
1732 if (!NT_SUCCESS(Status
))
1734 if (Status
== STATUS_STOPPED_ON_SYMLINK
)
1736 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1744 if (DatabaseName
.Buffer
)
1746 FreePool(DatabaseName
.Buffer
);
1749 if (NT_SUCCESS(Status
))
1751 DeviceInformation
->Migrated
= 1;
1753 else if (Database
!= 0)
1758 IoFreeWorkItem(WorkItem
->WorkItem
);
1760 WorkItem
->WorkItem
= NULL
;
1761 WorkItem
->Status
= Status
;
1762 WorkItem
->Database
= Database
;
1764 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1771 CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1772 IN OUT PHANDLE Database
)
1776 PMIGRATE_WORK_ITEM WorkItem
;
1778 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1780 /* Allocate a work item dedicated to migration */
1781 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1785 return STATUS_INSUFFICIENT_RESOURCES
;
1788 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1789 WorkItem
->Event
= &Event
;
1790 WorkItem
->DeviceInformation
= DeviceInformation
;
1791 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1792 if (!WorkItem
->WorkItem
)
1796 return STATUS_INSUFFICIENT_RESOURCES
;
1800 IoQueueWorkItem(WorkItem
->WorkItem
,
1801 CreateRemoteDatabaseWorker
,
1805 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1806 Status
= WorkItem
->Status
;
1808 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1818 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1819 IN BOOLEAN MigrateDatabase
)
1823 BOOLEAN PreviousMode
;
1824 IO_STATUS_BLOCK IoStatusBlock
;
1825 OBJECT_ATTRIBUTES ObjectAttributes
;
1826 UNICODE_STRING DeviceRemoteDatabase
;
1830 /* Get database name */
1831 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1832 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1833 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1834 if (!DeviceRemoteDatabase
.Buffer
)
1839 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1840 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1841 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1842 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1845 InitializeObjectAttributes(&ObjectAttributes
,
1846 &DeviceRemoteDatabase
,
1847 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1851 /* Disable hard errors */
1852 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1854 Status
= IoCreateFile(&Database
,
1855 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1856 FILE_READ_ATTRIBUTES
| FILE_WRITE_EA
| FILE_READ_EA
|
1857 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1861 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1863 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1864 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1869 IO_STOP_ON_SYMLINK
| IO_NO_PARAMETER_CHECKING
);
1870 if (Status
== STATUS_STOPPED_ON_SYMLINK
)
1872 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1875 /* If base it to be migrated and was opened successfully, go ahead */
1876 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1878 CreateRemoteDatabase(DeviceInformation
, &Database
);
1881 IoSetThreadHardErrorMode(PreviousMode
);
1882 FreePool(DeviceRemoteDatabase
.Buffer
);
1891 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1892 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1893 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1897 PDATABASE_ENTRY Entry
, NewEntry
;
1898 NTSTATUS Status
= STATUS_SUCCESS
;
1900 /* Open the remote database */
1901 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1907 /* Get all the entries */
1910 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1916 /* Not the correct entry, skip it */
1917 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1919 Offset
+= Entry
->EntrySize
;
1924 /* Not the correct entry, skip it */
1925 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1926 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1927 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1929 Offset
+= Entry
->EntrySize
;
1934 /* Here, we have the correct entry */
1935 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1938 Offset
+= Entry
->EntrySize
;
1943 /* Recreate the entry from the previous one */
1944 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1945 NewEntry
->EntryReferences
= Entry
->EntryReferences
;
1946 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1947 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1948 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1949 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1950 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1951 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1952 NewEntry
->SymbolicNameLength
);
1953 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1954 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1956 /* Delete old entry */
1957 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1958 if (!NT_SUCCESS(Status
))
1965 /* And replace with new one */
1966 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1969 } while (NT_SUCCESS(Status
));
1971 CloseRemoteDatabase(Database
);
1981 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
1984 IN ULONG ValueLength
,
1986 IN PVOID EntryContext
)
1988 PMOUNTDEV_UNIQUE_ID UniqueId
;
1989 UNICODE_STRING RegistryEntry
;
1991 UNREFERENCED_PARAMETER(EntryContext
);
1993 if (ValueType
!= REG_BINARY
)
1995 return STATUS_SUCCESS
;
2000 /* First ensure we have the correct data */
2001 if (UniqueId
->UniqueIdLength
!= ValueLength
)
2003 return STATUS_SUCCESS
;
2006 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
2008 return STATUS_SUCCESS
;
2011 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
2013 /* Then, it's a drive letter, erase it */
2014 if (IsDriveLetter(&RegistryEntry
))
2016 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
2021 return STATUS_SUCCESS
;
2028 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
2030 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
2032 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
2033 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
2035 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
2047 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
2050 IN ULONG ValueLength
,
2052 IN PVOID EntryContext
)
2054 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
2056 UNREFERENCED_PARAMETER(EntryContext
);
2058 /* Ensure we have correct input */
2059 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
2060 UniqueId
->UniqueIdLength
!= ValueLength
)
2062 return STATUS_SUCCESS
;
2065 /* And then, if unique ID matching, delete entry */
2066 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
2068 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
2073 return STATUS_SUCCESS
;
2080 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
2082 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
2084 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
2085 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
2087 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,