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");
35 UNICODE_STRING RemoteDatabaseFile
= RTL_CONSTANT_STRING(L
"\\:$MountMgrRemoteDatabase");
41 GetRemoteDatabaseSize(IN HANDLE Database
)
44 IO_STATUS_BLOCK IoStatusBlock
;
45 FILE_STANDARD_INFORMATION StandardInfo
;
47 /* Just query the size */
48 Status
= ZwQueryInformationFile(Database
,
51 sizeof(FILE_STANDARD_INFORMATION
),
52 FileStandardInformation
);
53 if (NT_SUCCESS(Status
))
55 return StandardInfo
.EndOfFile
.LowPart
;
65 AddRemoteDatabaseEntry(IN HANDLE Database
,
66 IN PDATABASE_ENTRY Entry
)
69 IO_STATUS_BLOCK IoStatusBlock
;
71 /* Get size to append data */
72 Size
.QuadPart
= GetRemoteDatabaseSize(Database
);
74 return ZwWriteFile(Database
, 0, NULL
, NULL
,
75 &IoStatusBlock
, Entry
,
76 Entry
->EntrySize
, &Size
, NULL
);
83 CloseRemoteDatabase(IN HANDLE Database
)
85 return ZwClose(Database
);
92 TruncateRemoteDatabase(IN HANDLE Database
,
96 IO_STATUS_BLOCK IoStatusBlock
;
97 FILE_END_OF_FILE_INFORMATION EndOfFile
;
98 FILE_ALLOCATION_INFORMATION Allocation
;
100 EndOfFile
.EndOfFile
.QuadPart
= NewSize
;
101 Allocation
.AllocationSize
.QuadPart
= NewSize
;
104 Status
= ZwSetInformationFile(Database
,
107 sizeof(FILE_END_OF_FILE_INFORMATION
),
108 FileEndOfFileInformation
);
109 if (NT_SUCCESS(Status
))
111 /* And then, properly set allocation information */
112 Status
= ZwSetInformationFile(Database
,
115 sizeof(FILE_ALLOCATION_INFORMATION
),
116 FileAllocationInformation
);
126 GetRemoteDatabaseEntry(IN HANDLE Database
,
127 IN LONG StartingOffset
)
131 PDATABASE_ENTRY Entry
;
132 LARGE_INTEGER ByteOffset
;
133 IO_STATUS_BLOCK IoStatusBlock
;
135 /* Get the entry at the given position */
136 ByteOffset
.QuadPart
= StartingOffset
;
137 Status
= ZwReadFile(Database
,
146 if (!NT_SUCCESS(Status
))
151 /* If entry doesn't exist, truncate database */
154 TruncateRemoteDatabase(Database
, StartingOffset
);
158 /* Allocate the entry */
159 Entry
= AllocatePool(EntrySize
);
165 /* Effectively read the entry */
166 Status
= ZwReadFile(Database
,
175 /* If it fails or returns inconsistent data, drop it (= truncate) */
176 if (!NT_SUCCESS(Status
) ||
177 (IoStatusBlock
.Information
!= EntrySize
) ||
178 (EntrySize
< sizeof(DATABASE_ENTRY
)) )
180 TruncateRemoteDatabase(Database
, StartingOffset
);
186 if (MAX(Entry
->SymbolicNameOffset
+ Entry
->SymbolicNameLength
,
187 Entry
->UniqueIdOffset
+ Entry
->UniqueIdLength
) > (LONG
)EntrySize
)
189 TruncateRemoteDatabase(Database
, StartingOffset
);
201 WriteRemoteDatabaseEntry(IN HANDLE Database
,
203 IN PDATABASE_ENTRY Entry
)
206 LARGE_INTEGER ByteOffset
;
207 IO_STATUS_BLOCK IoStatusBlock
;
209 ByteOffset
.QuadPart
= Offset
;
210 Status
= ZwWriteFile(Database
,
219 if (NT_SUCCESS(Status
))
221 if (IoStatusBlock
.Information
< Entry
->EntrySize
)
223 Status
= STATUS_INSUFFICIENT_RESOURCES
;
234 DeleteRemoteDatabaseEntry(IN HANDLE Database
,
235 IN LONG StartingOffset
)
241 PDATABASE_ENTRY Entry
;
242 IO_STATUS_BLOCK IoStatusBlock
;
243 LARGE_INTEGER EndEntriesOffset
;
245 /* First, get database size */
246 DatabaseSize
= GetRemoteDatabaseSize(Database
);
249 return STATUS_INVALID_PARAMETER
;
252 /* Then, get the entry to remove */
253 Entry
= GetRemoteDatabaseEntry(Database
, StartingOffset
);
256 return STATUS_INVALID_PARAMETER
;
259 /* Validate parameters: ensure we won't get negative size */
260 if (Entry
->EntrySize
+ StartingOffset
> DatabaseSize
)
262 /* If we get invalid parameters, truncate the whole database
263 * starting the wrong entry. We can't rely on the rest
266 return TruncateRemoteDatabase(Database
, StartingOffset
);
269 /* Now, get the size of the remaining entries (those after the one to remove) */
270 EndSize
= DatabaseSize
- Entry
->EntrySize
- StartingOffset
;
271 /* Allocate a buffer big enough to hold them */
272 TmpBuffer
= AllocatePool(EndSize
);
276 return STATUS_INSUFFICIENT_RESOURCES
;
279 /* Get the offset of the entry right after the one to delete */
280 EndEntriesOffset
.QuadPart
= Entry
->EntrySize
+ StartingOffset
;
281 /* We don't need the entry any more */
284 /* Read the ending entries */
285 Status
= ZwReadFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
286 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
287 if (!NT_SUCCESS(Status
))
293 /* Ensure nothing went wrong - we don't want to corrupt the DB */
294 if (IoStatusBlock
.Information
!= EndSize
)
297 return STATUS_INVALID_PARAMETER
;
300 /* Remove the entry */
301 Status
= TruncateRemoteDatabase(Database
, StartingOffset
+ EndSize
);
302 if (!NT_SUCCESS(Status
))
308 /* Now, shift the ending entries to erase the entry */
309 EndEntriesOffset
.QuadPart
= StartingOffset
;
310 Status
= ZwWriteFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
311 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
323 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName
,
326 IN ULONG ValueLength
,
328 IN PVOID EntryContext
)
330 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
332 UNREFERENCED_PARAMETER(ValueType
);
333 UNREFERENCED_PARAMETER(EntryContext
);
335 /* Ensure it matches, and delete */
336 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
337 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
340 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
345 return STATUS_SUCCESS
;
352 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
353 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
355 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
357 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
358 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
359 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
361 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
372 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
375 LARGE_INTEGER Timeout
;
377 /* Wait for 7 minutes */
378 Timeout
.QuadPart
= 0xFA0A1F00;
379 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
380 if (Status
!= STATUS_TIMEOUT
)
385 return STATUS_IO_TIMEOUT
;
392 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
394 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
402 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
405 IN ULONG ValueLength
,
407 IN PVOID EntryContext
)
409 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
410 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
412 UNREFERENCED_PARAMETER(ValueName
);
413 UNREFERENCED_PARAMETER(ValueType
);
414 UNREFERENCED_PARAMETER(EntryContext
);
417 if (ValueLength
>= 0x10000)
419 return STATUS_SUCCESS
;
422 /* Allocate the Unique ID */
423 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
426 /* Copy data & return */
427 IntUniqueId
->UniqueIdLength
= (USHORT
)ValueLength
;
428 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
431 *UniqueId
= IntUniqueId
;
434 return STATUS_SUCCESS
;
441 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
442 IN PUNICODE_STRING SymbolicName
,
443 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
446 PDEVICE_INFORMATION DeviceInformation
;
447 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
449 /* Query the unique ID */
450 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
451 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
452 QueryTable
[0].Name
= SymbolicName
->Buffer
;
455 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
460 /* Unique ID found, no need to go farther */
463 return STATUS_SUCCESS
;
466 /* Otherwise, find associate device information */
467 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
468 if (!NT_SUCCESS(Status
))
473 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
476 return STATUS_INSUFFICIENT_RESOURCES
;
479 /* Return this unique ID (better than nothing) */
480 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
481 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
483 return STATUS_SUCCESS
;
490 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
491 IN PDATABASE_ENTRY DatabaseEntry
)
495 PLIST_ENTRY NextEntry
;
496 UNICODE_STRING SymbolicString
;
497 PDEVICE_INFORMATION DeviceInformation
;
499 /* Create symbolic name from database entry */
500 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
503 return STATUS_INSUFFICIENT_RESOURCES
;
506 RtlCopyMemory(SymbolicName
,
507 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
508 DatabaseEntry
->SymbolicNameLength
);
509 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
511 /* Associate the unique ID with the name from remote database */
512 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
516 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
517 DatabaseEntry
->UniqueIdLength
);
518 FreePool(SymbolicName
);
520 /* Reget symbolic name */
521 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
522 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
523 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
525 /* Find the device using this unique ID */
526 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
527 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
528 NextEntry
= NextEntry
->Flink
)
530 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
534 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
539 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
540 DeviceInformation
->UniqueId
->UniqueId
,
541 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
547 /* If found, create a mount point */
548 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
550 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
561 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
565 PFILE_OBJECT FileObject
;
566 PDEVICE_OBJECT DeviceObject
;
567 PMOUNTDEV_UNIQUE_ID UniqueId
;
568 PDATABASE_ENTRY DatabaseEntry
;
569 HANDLE DatabaseHandle
, Handle
;
570 IO_STATUS_BLOCK IoStatusBlock
;
571 OBJECT_ATTRIBUTES ObjectAttributes
;
572 PDEVICE_INFORMATION ListDeviceInfo
;
573 PLIST_ENTRY Entry
, EntryInfo
, NextEntry
;
574 PASSOCIATED_DEVICE_ENTRY AssociatedDevice
;
575 BOOLEAN HardwareErrors
, Restart
, FailedFinding
;
576 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[100];
577 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
578 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
579 PDEVICE_EXTENSION DeviceExtension
= ((PRECONCILE_WORK_ITEM_CONTEXT
)Parameter
)->DeviceExtension
;
580 PDEVICE_INFORMATION DeviceInformation
= ((PRECONCILE_WORK_ITEM_CONTEXT
)Parameter
)->DeviceInformation
;
582 /* We're unloading, do nothing */
589 if (!NT_SUCCESS(WaitForRemoteDatabaseSemaphore(DeviceExtension
)))
594 /* Recheck for unloading */
600 /* Find the DB to reconcile */
601 KeWaitForSingleObject(&DeviceExtension
->DeviceLock
, Executive
, KernelMode
, FALSE
, NULL
);
602 for (Entry
= DeviceExtension
->DeviceListHead
.Flink
;
603 Entry
!= &DeviceExtension
->DeviceListHead
;
604 Entry
= Entry
->Flink
)
606 ListDeviceInfo
= CONTAINING_RECORD(Entry
, DEVICE_INFORMATION
, DeviceListEntry
);
607 if (ListDeviceInfo
== DeviceInformation
)
613 /* If not found, or if removable, bail out */
614 if (Entry
== &DeviceExtension
->DeviceListHead
|| DeviceInformation
->Removable
)
616 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
620 /* Get our device object */
621 Status
= IoGetDeviceObjectPointer(&ListDeviceInfo
->DeviceName
, FILE_READ_ATTRIBUTES
, &FileObject
, &DeviceObject
);
622 if (!NT_SUCCESS(Status
))
624 KeReleaseSemaphore(&DeviceExtension
->DeviceLock
, IO_NO_INCREMENT
, 1, FALSE
);
628 if (DeviceObject
->Flags
& 1)
630 _InterlockedExchangeAdd(&ListDeviceInfo
->MountState
, 1u);
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 symoblic 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 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
1674 LARGE_INTEGER ByteOffset
;
1675 PMIGRATE_WORK_ITEM WorkItem
;
1676 IO_STATUS_BLOCK IoStatusBlock
;
1677 HANDLE Migrate
= 0, Database
= 0;
1678 PDEVICE_INFORMATION DeviceInformation
;
1679 BOOLEAN PreviousMode
, Complete
= FALSE
;
1680 UNICODE_STRING DatabaseName
, DatabaseFile
;
1681 OBJECT_ATTRIBUTES ObjectAttributes
, MigrateAttributes
;
1682 #define TEMP_BUFFER_SIZE 0x200
1684 UNREFERENCED_PARAMETER(DeviceObject
);
1686 /* Extract context */
1688 DeviceInformation
= WorkItem
->DeviceInformation
;
1690 /* Reconstruct appropriate string */
1691 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1692 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
1694 DatabaseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabaseFile
.Length
;
1695 DatabaseFile
.MaximumLength
= DatabaseFile
.Length
+ sizeof(WCHAR
);
1697 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
1698 DatabaseFile
.Buffer
= AllocatePool(DatabaseFile
.MaximumLength
);
1699 /* Allocate buffer that will be used to swap contents */
1700 TmpBuffer
= AllocatePool(TEMP_BUFFER_SIZE
);
1701 if (!DatabaseName
.Buffer
|| !DatabaseFile
.Buffer
|| !TmpBuffer
)
1703 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1707 /* Create the required folder (in which the database will be stored
1708 * \System Volume Information at root of the volume
1710 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
1711 if (!NT_SUCCESS(Status
))
1716 /* Finish initating strings */
1717 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1718 RtlCopyMemory(DatabaseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1719 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1720 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1721 RtlCopyMemory(DatabaseFile
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1722 RemoteDatabaseFile
.Buffer
, RemoteDatabaseFile
.Length
);
1723 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1724 DatabaseFile
.Buffer
[DatabaseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1726 /* Create database */
1727 InitializeObjectAttributes(&ObjectAttributes
,
1729 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1733 Status
= ZwCreateFile(&Database
,
1734 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1735 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1736 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1740 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1743 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1746 if (!NT_SUCCESS(Status
))
1752 InitializeObjectAttributes(&MigrateAttributes
,
1754 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1758 /* Disable hard errors and open the database that will be copied */
1759 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1760 Status
= ZwCreateFile(&Migrate
,
1761 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1762 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1763 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1767 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1770 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1773 IoSetThreadHardErrorMode(PreviousMode
);
1774 if (!NT_SUCCESS(Status
))
1778 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1780 Status
= STATUS_SUCCESS
;
1783 if (!NT_SUCCESS(Status
) || Complete
)
1788 ByteOffset
.QuadPart
= 0LL;
1789 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1790 /* Now, loop as long it's possible */
1791 while (Status
== STATUS_SUCCESS
)
1793 /* Read data from existing database */
1794 Status
= ZwReadFile(Migrate
,
1803 if (!NT_SUCCESS(Status
))
1808 /* And write them into new database */
1809 Length
= (ULONG
)IoStatusBlock
.Information
;
1810 Status
= ZwWriteFile(Database
,
1819 ByteOffset
.QuadPart
+= Length
;
1821 IoSetThreadHardErrorMode(PreviousMode
);
1823 /* Delete old databse if it was well copied */
1824 if (Status
== STATUS_END_OF_FILE
)
1827 Status
= ZwSetInformationFile(Migrate
,
1830 sizeof(Disposition
),
1831 FileDispositionInformation
);
1834 /* Migration is over */
1839 FreePool(TmpBuffer
);
1842 if (DatabaseFile
.Buffer
)
1844 FreePool(DatabaseFile
.Buffer
);
1847 if (DatabaseName
.Buffer
)
1849 FreePool(DatabaseName
.Buffer
);
1857 if (NT_SUCCESS(Status
))
1859 DeviceInformation
->Migrated
= 1;
1866 IoFreeWorkItem(WorkItem
->WorkItem
);
1868 WorkItem
->WorkItem
= NULL
;
1869 WorkItem
->Status
= Status
;
1870 WorkItem
->Database
= Database
;
1872 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1873 #undef TEMP_BUFFER_SIZE
1880 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1881 IN OUT PHANDLE Database
)
1885 PMIGRATE_WORK_ITEM WorkItem
;
1887 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1889 /* Allocate a work item dedicated to migration */
1890 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1894 return STATUS_INSUFFICIENT_RESOURCES
;
1897 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1898 WorkItem
->Event
= &Event
;
1899 WorkItem
->DeviceInformation
= DeviceInformation
;
1900 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1901 if (!WorkItem
->WorkItem
)
1905 return STATUS_INSUFFICIENT_RESOURCES
;
1909 IoQueueWorkItem(WorkItem
->WorkItem
,
1910 MigrateRemoteDatabaseWorker
,
1914 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1915 Status
= WorkItem
->Status
;
1917 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1927 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1928 IN BOOLEAN MigrateDatabase
)
1932 BOOLEAN PreviousMode
;
1933 IO_STATUS_BLOCK IoStatusBlock
;
1934 OBJECT_ATTRIBUTES ObjectAttributes
;
1935 UNICODE_STRING DeviceRemoteDatabase
;
1939 /* Get database name */
1940 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1941 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1942 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1943 if (!DeviceRemoteDatabase
.Buffer
)
1948 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1949 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1950 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1951 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1954 InitializeObjectAttributes(&ObjectAttributes
,
1955 &DeviceRemoteDatabase
,
1956 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1960 /* Disable hard errors */
1961 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1963 Status
= ZwCreateFile(&Database
,
1964 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1965 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1966 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1970 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1972 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1973 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1977 /* If base it to be migrated and was opened successfully, go ahead */
1978 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1980 MigrateRemoteDatabase(DeviceInformation
, &Database
);
1983 IoSetThreadHardErrorMode(PreviousMode
);
1984 FreePool(DeviceRemoteDatabase
.Buffer
);
1993 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1994 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1995 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1999 PDATABASE_ENTRY Entry
, NewEntry
;
2000 NTSTATUS Status
= STATUS_SUCCESS
;
2002 /* Open the remote database */
2003 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
2009 /* Get all the entries */
2012 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
2018 /* Not the correct entry, skip it */
2019 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
2021 Offset
+= Entry
->EntrySize
;
2026 /* Not the correct entry, skip it */
2027 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
2028 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
2029 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
2031 Offset
+= Entry
->EntrySize
;
2036 /* Here, we have the correct entry */
2037 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
2040 Offset
+= Entry
->EntrySize
;
2045 /* Recreate the entry from the previous one */
2046 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
2047 NewEntry
->EntryReferences
= Entry
->EntryReferences
;
2048 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
2049 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
2050 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
2051 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
2052 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
2053 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
2054 NewEntry
->SymbolicNameLength
);
2055 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
2056 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
2058 /* Delete old entry */
2059 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
2060 if (!NT_SUCCESS(Status
))
2067 /* And replace with new one */
2068 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
2071 } while (NT_SUCCESS(Status
));
2073 CloseRemoteDatabase(Database
);
2083 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
2086 IN ULONG ValueLength
,
2088 IN PVOID EntryContext
)
2090 PMOUNTDEV_UNIQUE_ID UniqueId
;
2091 UNICODE_STRING RegistryEntry
;
2093 UNREFERENCED_PARAMETER(EntryContext
);
2095 if (ValueType
!= REG_BINARY
)
2097 return STATUS_SUCCESS
;
2102 /* First ensure we have the correct data */
2103 if (UniqueId
->UniqueIdLength
!= ValueLength
)
2105 return STATUS_SUCCESS
;
2108 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
2110 return STATUS_SUCCESS
;
2113 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
2115 /* Then, it's a drive letter, erase it */
2116 if (IsDriveLetter(&RegistryEntry
))
2118 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
2123 return STATUS_SUCCESS
;
2130 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
2132 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
2134 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
2135 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
2137 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
2149 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
2152 IN ULONG ValueLength
,
2154 IN PVOID EntryContext
)
2156 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
2158 UNREFERENCED_PARAMETER(EntryContext
);
2160 /* Ensure we have correct input */
2161 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
2162 UniqueId
->UniqueIdLength
!= ValueLength
)
2164 return STATUS_SUCCESS
;
2167 /* And then, if unique ID matching, delete entry */
2168 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
2170 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
2175 return STATUS_SUCCESS
;
2182 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
2184 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
2186 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
2187 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
2189 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,