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 DeleteRemoteDatabaseEntry(IN HANDLE Database
,
202 IN LONG StartingOffset
)
208 PDATABASE_ENTRY Entry
;
209 IO_STATUS_BLOCK IoStatusBlock
;
210 LARGE_INTEGER EndEntriesOffset
;
212 /* First, get database size */
213 DatabaseSize
= GetRemoteDatabaseSize(Database
);
216 return STATUS_INVALID_PARAMETER
;
219 /* Then, get the entry to remove */
220 Entry
= GetRemoteDatabaseEntry(Database
, StartingOffset
);
223 return STATUS_INVALID_PARAMETER
;
226 /* Validate parameters: ensure we won't get negative size */
227 if (Entry
->EntrySize
+ StartingOffset
> DatabaseSize
)
229 /* If we get invalid parameters, truncate the whole database
230 * starting the wrong entry. We can't rely on the rest
233 return TruncateRemoteDatabase(Database
, StartingOffset
);
236 /* Now, get the size of the remaining entries (those after the one to remove) */
237 EndSize
= DatabaseSize
- Entry
->EntrySize
- StartingOffset
;
238 /* Allocate a buffer big enough to hold them */
239 TmpBuffer
= AllocatePool(EndSize
);
243 return STATUS_INSUFFICIENT_RESOURCES
;
246 /* Get the offset of the entry right after the one to delete */
247 EndEntriesOffset
.QuadPart
= Entry
->EntrySize
+ StartingOffset
;
248 /* We don't need the entry any more */
251 /* Read the ending entries */
252 Status
= ZwReadFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
253 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
254 if (!NT_SUCCESS(Status
))
260 /* Ensure nothing went wrong - we don't want to corrupt the DB */
261 if (IoStatusBlock
.Information
!= EndSize
)
264 return STATUS_INVALID_PARAMETER
;
267 /* Remove the entry */
268 Status
= TruncateRemoteDatabase(Database
, StartingOffset
+ EndSize
);
269 if (!NT_SUCCESS(Status
))
275 /* Now, shift the ending entries to erase the entry */
276 EndEntriesOffset
.QuadPart
= StartingOffset
;
277 Status
= ZwWriteFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
278 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
290 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName
,
293 IN ULONG ValueLength
,
295 IN PVOID EntryContext
)
297 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
299 UNREFERENCED_PARAMETER(ValueType
);
300 UNREFERENCED_PARAMETER(EntryContext
);
302 /* Ensure it matches, and delete */
303 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
304 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
307 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
312 return STATUS_SUCCESS
;
319 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
320 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
322 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
324 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
325 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
326 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
328 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
339 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
342 LARGE_INTEGER Timeout
;
344 /* Wait for 7 minutes */
345 Timeout
.QuadPart
= 0xFA0A1F00;
346 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
347 if (Status
!= STATUS_TIMEOUT
)
352 return STATUS_IO_TIMEOUT
;
359 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
361 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
366 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
368 UNREFERENCED_PARAMETER(Parameter
);
377 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
386 LARGE_INTEGER Timeout
;
387 PRECONCILE_WORK_ITEM WorkItem
;
388 PDEVICE_EXTENSION DeviceExtension
;
389 OBJECT_ATTRIBUTES ObjectAttributes
;
391 UNREFERENCED_PARAMETER(DeviceObject
);
393 InitializeObjectAttributes(&ObjectAttributes
,
395 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
398 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
399 Timeout
.LowPart
= 0xFFFFFFFF;
400 Timeout
.HighPart
= 0xFF676980;
402 /* Try to wait as long as possible */
403 for (i
= (Unloading
? 999 : 0); i
< 1000; i
++)
405 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
406 if (NT_SUCCESS(Status
))
411 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
418 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
420 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
425 DeviceExtension
= Context
;
427 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
429 /* Acquire workers lock */
430 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
432 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
434 /* Ensure there are workers */
435 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
437 /* Unqueue a worker */
438 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
439 WorkItem
= CONTAINING_RECORD(Entry
,
441 WorkerQueueListEntry
);
443 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
446 WorkItem
->WorkerRoutine(WorkItem
->Context
);
448 IoFreeWorkItem(WorkItem
->WorkItem
);
451 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) == 0)
456 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
457 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
459 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
461 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
464 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
471 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
472 IN PRECONCILE_WORK_ITEM WorkItem
,
477 WorkItem
->Context
= Context
;
479 /* When called, lock is already acquired */
481 /* If noone, start to work */
482 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
484 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
487 /* Otherwise queue worker for delayed execution */
488 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
489 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
490 &(WorkItem
->WorkerQueueListEntry
));
491 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
493 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
495 return STATUS_SUCCESS
;
502 QueryVolumeName(IN HANDLE RootDirectory
,
503 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation
,
504 IN PUNICODE_STRING FileName OPTIONAL
,
505 OUT PUNICODE_STRING SymbolicName
,
506 OUT PUNICODE_STRING VolumeName
)
511 IO_STATUS_BLOCK IoStatusBlock
;
512 OBJECT_ATTRIBUTES ObjectAttributes
;
513 PFILE_NAME_INFORMATION FileNameInfo
;
514 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
516 UNREFERENCED_PARAMETER(ReparsePointInformation
);
520 InitializeObjectAttributes(&ObjectAttributes
,
528 InitializeObjectAttributes(&ObjectAttributes
,
530 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
536 Status
= ZwOpenFile(&Handle
,
537 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
540 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
541 (FileName
) ? FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
:
542 FILE_OPEN_BY_FILE_ID
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
);
543 if (!NT_SUCCESS(Status
))
548 /* Get the reparse point data */
549 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
550 if (!ReparseDataBuffer
)
553 return STATUS_INSUFFICIENT_RESOURCES
;
556 Status
= ZwFsControlFile(Handle
,
561 FSCTL_GET_REPARSE_POINT
,
565 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
566 if (!NT_SUCCESS(Status
))
568 FreePool(ReparseDataBuffer
);
573 /* Check that name can fit in buffer */
574 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
576 FreePool(ReparseDataBuffer
);
578 return STATUS_BUFFER_TOO_SMALL
;
581 /* Copy symoblic name */
582 SymbolicName
->Length
= ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
583 RtlCopyMemory(SymbolicName
->Buffer
,
584 (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
585 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
),
586 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
588 FreePool(ReparseDataBuffer
);
590 /* Name has to \ terminated */
591 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
594 return STATUS_INVALID_PARAMETER
;
597 /* So that we can delete it, and match mountmgr requirements */
598 SymbolicName
->Length
-= sizeof(WCHAR
);
599 SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
601 /* Also ensure it's really a volume name... */
602 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
605 return STATUS_INVALID_PARAMETER
;
608 /* Now prepare to really get the name */
609 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
613 return STATUS_INSUFFICIENT_RESOURCES
;
616 Status
= ZwQueryInformationFile(Handle
,
619 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
620 FileNameInformation
);
621 if (Status
== STATUS_BUFFER_OVERFLOW
)
623 /* As expected... Reallocate with proper size */
624 NeededLength
= FileNameInfo
->FileNameLength
;
625 FreePool(FileNameInfo
);
627 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
631 return STATUS_INSUFFICIENT_RESOURCES
;
635 Status
= ZwQueryInformationFile(Handle
,
638 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
639 FileNameInformation
);
644 if (!NT_SUCCESS(Status
))
649 /* Return the volume name */
650 VolumeName
->Length
= (USHORT
)FileNameInfo
->FileNameLength
;
651 VolumeName
->MaximumLength
= (USHORT
)FileNameInfo
->FileNameLength
+ sizeof(WCHAR
);
652 VolumeName
->Buffer
= AllocatePool(VolumeName
->MaximumLength
);
653 if (!VolumeName
->Buffer
)
655 return STATUS_INSUFFICIENT_RESOURCES
;
658 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
659 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
661 FreePool(FileNameInfo
);
663 return STATUS_SUCCESS
;
670 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
671 IN PDEVICE_INFORMATION DeviceInformation
)
676 IO_STATUS_BLOCK IoStatusBlock
;
677 OBJECT_ATTRIBUTES ObjectAttributes
;
678 PDEVICE_INFORMATION VolumeDeviceInformation
;
679 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[0x64];
680 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
681 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
683 /* Removable devices don't have remote database on them */
684 if (DeviceInformation
->Removable
)
689 /* Prepare a string with reparse point index */
690 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
691 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
692 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
693 if (!ReparseFile
.Buffer
)
698 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
699 DeviceInformation
->DeviceName
.Length
);
700 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
701 ReparseFile
.Buffer
, ReparseFile
.Length
);
702 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
704 InitializeObjectAttributes(&ObjectAttributes
,
706 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
710 /* Open reparse point */
711 Status
= ZwOpenFile(&Handle
,
715 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
716 FILE_SYNCHRONOUS_IO_ALERT
| FILE_OPEN_REPARSE_POINT
);
717 FreePool(ReparseFile
.Buffer
);
718 if (!NT_SUCCESS(Status
))
720 DeviceInformation
->NoDatabase
= FALSE
;
724 /* Query reparse point information
725 * We only pay attention to mout point
727 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
728 FileName
.Buffer
= FileNameBuffer
;
729 FileName
.Length
= sizeof(FileNameBuffer
);
730 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
731 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
732 Status
= ZwQueryDirectoryFile(Handle
,
737 &ReparsePointInformation
,
738 sizeof(FILE_REPARSE_POINT_INFORMATION
),
739 FileReparsePointInformation
,
743 if (!NT_SUCCESS(Status
))
749 /* Query mount points */
753 SymbolicName
.Length
= 0;
754 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
755 SymbolicName
.Buffer
= SymbolicNameBuffer
;
756 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
758 Status
= ZwQueryDirectoryFile(Handle
,
763 &ReparsePointInformation
,
764 sizeof(FILE_REPARSE_POINT_INFORMATION
),
765 FileReparsePointInformation
,
767 (RestartScan
) ? &FileName
: NULL
,
771 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
772 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
783 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
789 /* Get the volume name associated to the mount point */
790 Status
= QueryVolumeName(Handle
,
791 &ReparsePointInformation
,
794 if (!NT_SUCCESS(Status
))
799 FreePool(VolumeName
.Buffer
);
801 /* Get its information */
802 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
803 FALSE
, &VolumeDeviceInformation
);
804 if (!NT_SUCCESS(Status
))
806 DeviceInformation
->NoDatabase
= TRUE
;
810 /* If notification are enabled, mark it online */
811 if (!DeviceInformation
->SkipNotifications
)
813 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
822 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
823 IN PDEVICE_INFORMATION DeviceInformation
)
825 PRECONCILE_WORK_ITEM WorkItem
;
827 /* Removable devices don't have remote database */
828 if (DeviceInformation
->Removable
)
833 /* Allocate a work item */
834 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
840 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
841 if (!WorkItem
->WorkItem
)
848 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
849 WorkItem
->DeviceExtension
= DeviceExtension
;
850 WorkItem
->DeviceInformation
= DeviceInformation
;
851 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
853 /* If there's no automount, and automatic letters
854 * all volumes to find those online and notify there presence
856 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
857 DeviceExtension
->AutomaticDriveLetter
== 1 &&
858 DeviceExtension
->NoAutoMount
== FALSE
)
860 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
868 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
870 PLIST_ENTRY NextEntry
;
871 PDEVICE_INFORMATION DeviceInformation
;
873 /* Browse all the devices */
874 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
875 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
876 NextEntry
= NextEntry
->Flink
)
878 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
881 /* If it's not removable, then, it might have a database to sync */
882 if (!DeviceInformation
->Removable
)
884 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
894 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
901 LARGE_INTEGER ByteOffset
;
902 PMIGRATE_WORK_ITEM WorkItem
;
903 IO_STATUS_BLOCK IoStatusBlock
;
904 HANDLE Migrate
= 0, Database
= 0;
905 PDEVICE_INFORMATION DeviceInformation
;
906 BOOLEAN PreviousMode
, Complete
= FALSE
;
907 UNICODE_STRING DatabaseName
, DatabaseFile
;
908 OBJECT_ATTRIBUTES ObjectAttributes
, MigrateAttributes
;
909 #define TEMP_BUFFER_SIZE 0x200
911 UNREFERENCED_PARAMETER(DeviceObject
);
913 /* Extract context */
915 DeviceInformation
= WorkItem
->DeviceInformation
;
917 /* Reconstruct appropriate string */
918 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
919 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
921 DatabaseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabaseFile
.Length
;
922 DatabaseFile
.MaximumLength
= DatabaseFile
.Length
+ sizeof(WCHAR
);
924 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
925 DatabaseFile
.Buffer
= AllocatePool(DatabaseFile
.MaximumLength
);
926 /* Allocate buffer that will be used to swap contents */
927 TmpBuffer
= AllocatePool(TEMP_BUFFER_SIZE
);
928 if (!DatabaseName
.Buffer
|| !DatabaseFile
.Buffer
|| !TmpBuffer
)
930 Status
= STATUS_INSUFFICIENT_RESOURCES
;
934 /* Create the required folder (in which the database will be stored
935 * \System Volume Information at root of the volume
937 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
938 if (!NT_SUCCESS(Status
))
943 /* Finish initating strings */
944 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
945 RtlCopyMemory(DatabaseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
946 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
947 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
948 RtlCopyMemory(DatabaseFile
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
949 RemoteDatabaseFile
.Buffer
, RemoteDatabaseFile
.Length
);
950 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
951 DatabaseFile
.Buffer
[DatabaseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
953 /* Create database */
954 InitializeObjectAttributes(&ObjectAttributes
,
956 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
960 Status
= ZwCreateFile(&Database
,
961 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
962 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
963 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
967 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
970 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
973 if (!NT_SUCCESS(Status
))
979 InitializeObjectAttributes(&MigrateAttributes
,
981 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
985 /* Disable hard errors and open the database that will be copied */
986 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
987 Status
= ZwCreateFile(&Migrate
,
988 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
989 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
990 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
994 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
997 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1000 IoSetThreadHardErrorMode(PreviousMode
);
1001 if (!NT_SUCCESS(Status
))
1005 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1007 Status
= STATUS_SUCCESS
;
1010 if (!NT_SUCCESS(Status
) || Complete
)
1015 ByteOffset
.QuadPart
= 0LL;
1016 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1017 /* Now, loop as long it's possible */
1018 while (Status
== STATUS_SUCCESS
)
1020 /* Read data from existing database */
1021 Status
= ZwReadFile(Migrate
,
1030 if (!NT_SUCCESS(Status
))
1035 /* And write them into new database */
1036 Length
= (ULONG
)IoStatusBlock
.Information
;
1037 Status
= ZwWriteFile(Database
,
1046 ByteOffset
.QuadPart
+= Length
;
1048 IoSetThreadHardErrorMode(PreviousMode
);
1050 /* Delete old databse if it was well copied */
1051 if (Status
== STATUS_END_OF_FILE
)
1054 Status
= ZwSetInformationFile(Migrate
,
1057 sizeof(Disposition
),
1058 FileDispositionInformation
);
1061 /* Migration is over */
1066 FreePool(TmpBuffer
);
1069 if (DatabaseFile
.Buffer
)
1071 FreePool(DatabaseFile
.Buffer
);
1074 if (DatabaseName
.Buffer
)
1076 FreePool(DatabaseName
.Buffer
);
1084 if (NT_SUCCESS(Status
))
1086 DeviceInformation
->Migrated
= 1;
1093 IoFreeWorkItem(WorkItem
->WorkItem
);
1095 WorkItem
->WorkItem
= NULL
;
1096 WorkItem
->Status
= Status
;
1097 WorkItem
->Database
= Database
;
1099 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1100 #undef TEMP_BUFFER_SIZE
1107 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1108 IN OUT PHANDLE Database
)
1112 PMIGRATE_WORK_ITEM WorkItem
;
1114 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1116 /* Allocate a work item dedicated to migration */
1117 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1121 return STATUS_INSUFFICIENT_RESOURCES
;
1124 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1125 WorkItem
->Event
= &Event
;
1126 WorkItem
->DeviceInformation
= DeviceInformation
;
1127 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1128 if (!WorkItem
->WorkItem
)
1132 return STATUS_INSUFFICIENT_RESOURCES
;
1136 IoQueueWorkItem(WorkItem
->WorkItem
,
1137 MigrateRemoteDatabaseWorker
,
1141 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1142 Status
= WorkItem
->Status
;
1144 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1154 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1155 IN BOOLEAN MigrateDatabase
)
1159 BOOLEAN PreviousMode
;
1160 IO_STATUS_BLOCK IoStatusBlock
;
1161 OBJECT_ATTRIBUTES ObjectAttributes
;
1162 UNICODE_STRING DeviceRemoteDatabase
;
1166 /* Get database name */
1167 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1168 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1169 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1170 if (!DeviceRemoteDatabase
.Buffer
)
1175 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1176 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1177 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1178 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1181 InitializeObjectAttributes(&ObjectAttributes
,
1182 &DeviceRemoteDatabase
,
1183 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1187 /* Disable hard errors */
1188 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1190 Status
= ZwCreateFile(&Database
,
1191 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1192 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1193 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1197 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1199 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1200 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1204 /* If base it to be migrated and was opened successfully, go ahead */
1205 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1207 MigrateRemoteDatabase(DeviceInformation
, &Database
);
1210 IoSetThreadHardErrorMode(PreviousMode
);
1211 FreePool(DeviceRemoteDatabase
.Buffer
);
1221 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
1224 IN ULONG ValueLength
,
1226 IN PVOID EntryContext
)
1228 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
1229 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
1231 UNREFERENCED_PARAMETER(ValueName
);
1232 UNREFERENCED_PARAMETER(ValueType
);
1233 UNREFERENCED_PARAMETER(EntryContext
);
1236 if (ValueLength
>= 0x10000)
1238 return STATUS_SUCCESS
;
1241 /* Allocate the Unique ID */
1242 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
1245 /* Copy data & return */
1246 IntUniqueId
->UniqueIdLength
= (USHORT
)ValueLength
;
1247 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
1250 *UniqueId
= IntUniqueId
;
1253 return STATUS_SUCCESS
;
1260 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1261 IN PUNICODE_STRING SymbolicName
,
1262 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
1265 PDEVICE_INFORMATION DeviceInformation
;
1266 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1268 /* Query the unique ID */
1269 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1270 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
1271 QueryTable
[0].Name
= SymbolicName
->Buffer
;
1274 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1279 /* Unique ID found, no need to go farther */
1282 return STATUS_SUCCESS
;
1285 /* Otherwise, find associate device information */
1286 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
1287 if (!NT_SUCCESS(Status
))
1292 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
1295 return STATUS_INSUFFICIENT_RESOURCES
;
1298 /* Return this unique ID (better than nothing) */
1299 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
1300 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
1302 return STATUS_SUCCESS
;
1309 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1310 IN PDATABASE_ENTRY DatabaseEntry
)
1313 PWCHAR SymbolicName
;
1314 PLIST_ENTRY NextEntry
;
1315 UNICODE_STRING SymbolicString
;
1316 PDEVICE_INFORMATION DeviceInformation
;
1318 /* Create symbolic name from database entry */
1319 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
1322 return STATUS_INSUFFICIENT_RESOURCES
;
1325 RtlCopyMemory(SymbolicName
,
1326 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
1327 DatabaseEntry
->SymbolicNameLength
);
1328 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1330 /* Associate the unique ID with the name from remote database */
1331 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1335 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1336 DatabaseEntry
->UniqueIdLength
);
1337 FreePool(SymbolicName
);
1339 /* Reget symbolic name */
1340 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
1341 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
1342 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
1344 /* Find the device using this unique ID */
1345 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1346 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1347 NextEntry
= NextEntry
->Flink
)
1349 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1353 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
1358 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1359 DeviceInformation
->UniqueId
->UniqueId
,
1360 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
1366 /* If found, create a mount point */
1367 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
1369 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
1379 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1380 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1381 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1385 PDATABASE_ENTRY Entry
, NewEntry
;
1386 NTSTATUS Status
= STATUS_SUCCESS
;
1388 /* Open the remote database */
1389 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1395 /* Get all the entries */
1398 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1404 /* Not the correct entry, skip it */
1405 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1407 Offset
+= Entry
->EntrySize
;
1412 /* Not the correct entry, skip it */
1413 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1414 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1415 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1417 Offset
+= Entry
->EntrySize
;
1422 /* Here, we have the correct entry */
1423 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1426 Offset
+= Entry
->EntrySize
;
1431 /* Recreate the entry from the previous one */
1432 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1433 NewEntry
->DatabaseOffset
= Entry
->DatabaseOffset
;
1434 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1435 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1436 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1437 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1438 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1439 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1440 NewEntry
->SymbolicNameLength
);
1441 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1442 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1444 /* Delete old entry */
1445 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1446 if (!NT_SUCCESS(Status
))
1453 /* And replace with new one */
1454 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1457 } while (NT_SUCCESS(Status
));
1459 CloseRemoteDatabase(Database
);
1469 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
1472 IN ULONG ValueLength
,
1474 IN PVOID EntryContext
)
1476 PMOUNTDEV_UNIQUE_ID UniqueId
;
1477 UNICODE_STRING RegistryEntry
;
1479 UNREFERENCED_PARAMETER(EntryContext
);
1481 if (ValueType
!= REG_BINARY
)
1483 return STATUS_SUCCESS
;
1488 /* First ensure we have the correct data */
1489 if (UniqueId
->UniqueIdLength
!= ValueLength
)
1491 return STATUS_SUCCESS
;
1494 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1496 return STATUS_SUCCESS
;
1499 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
1501 /* Then, it's a drive letter, erase it */
1502 if (IsDriveLetter(&RegistryEntry
))
1504 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1509 return STATUS_SUCCESS
;
1516 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1518 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1520 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1521 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
1523 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1535 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
1538 IN ULONG ValueLength
,
1540 IN PVOID EntryContext
)
1542 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
1544 UNREFERENCED_PARAMETER(EntryContext
);
1546 /* Ensure we have correct input */
1547 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
1548 UniqueId
->UniqueIdLength
!= ValueLength
)
1550 return STATUS_SUCCESS
;
1553 /* And then, if unique ID matching, delete entry */
1554 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1556 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1561 return STATUS_SUCCESS
;
1568 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1570 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1572 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1573 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
1575 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,