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)
26 /* INCLUDES *****************************************************************/
33 PWSTR DatabasePath
= L
"\\Registry\\Machine\\System\\MountedDevices";
34 PWSTR OfflinePath
= L
"\\Registry\\Machine\\System\\MountedDevices\\Offline";
36 UNICODE_STRING RemoteDatabase
= RTL_CONSTANT_STRING(L
"\\System Volume Information\\MountPointManagerRemoteDatabase");
37 UNICODE_STRING RemoteDatabaseFile
= RTL_CONSTANT_STRING(L
"\\:$MountMgrRemoteDatabase");
43 GetRemoteDatabaseSize(IN HANDLE Database
)
46 IO_STATUS_BLOCK IoStatusBlock
;
47 FILE_STANDARD_INFORMATION StandardInfo
;
49 /* Just query the size */
50 Status
= ZwQueryInformationFile(Database
,
53 sizeof(FILE_STANDARD_INFORMATION
),
54 FileStandardInformation
);
55 if (NT_SUCCESS(Status
))
57 return StandardInfo
.EndOfFile
.LowPart
;
67 AddRemoteDatabaseEntry(IN HANDLE Database
,
68 IN PDATABASE_ENTRY Entry
)
71 IO_STATUS_BLOCK IoStatusBlock
;
73 /* Get size to append data */
74 Size
.QuadPart
= GetRemoteDatabaseSize(Database
);
76 return ZwWriteFile(Database
, 0, NULL
, NULL
,
77 &IoStatusBlock
, Entry
,
78 Entry
->EntrySize
, &Size
, NULL
);
85 CloseRemoteDatabase(IN HANDLE Database
)
87 return ZwClose(Database
);
94 TruncateRemoteDatabase(IN HANDLE Database
,
98 IO_STATUS_BLOCK IoStatusBlock
;
99 FILE_END_OF_FILE_INFORMATION EndOfFile
;
100 FILE_ALLOCATION_INFORMATION Allocation
;
102 EndOfFile
.EndOfFile
.QuadPart
= NewSize
;
103 Allocation
.AllocationSize
.QuadPart
= NewSize
;
106 Status
= ZwSetInformationFile(Database
,
109 sizeof(FILE_END_OF_FILE_INFORMATION
),
110 FileEndOfFileInformation
);
111 if (NT_SUCCESS(Status
))
113 /* And then, properly set allocation information */
114 Status
= ZwSetInformationFile(Database
,
117 sizeof(FILE_ALLOCATION_INFORMATION
),
118 FileAllocationInformation
);
128 GetRemoteDatabaseEntry(IN HANDLE Database
,
129 IN LONG StartingOffset
)
133 PDATABASE_ENTRY Entry
;
134 LARGE_INTEGER ByteOffset
;
135 IO_STATUS_BLOCK IoStatusBlock
;
137 /* Get the entry at the given position */
138 ByteOffset
.QuadPart
= StartingOffset
;
139 Status
= ZwReadFile(Database
,
148 if (!NT_SUCCESS(Status
))
153 /* If entry doesn't exist, truncate database */
156 TruncateRemoteDatabase(Database
, StartingOffset
);
160 /* Allocate the entry */
161 Entry
= AllocatePool(EntrySize
);
167 /* Effectively read the entry */
168 Status
= ZwReadFile(Database
,
177 /* If it fails or returns inconsistent data, drop it (= truncate) */
178 if (!NT_SUCCESS(Status
) || IoStatusBlock
.Information
!= EntrySize
|| EntrySize
< sizeof(DATABASE_ENTRY
))
180 TruncateRemoteDatabase(Database
, StartingOffset
);
186 if (MAX(Entry
->SymbolicNameOffset
+ Entry
->SymbolicNameLength
,
187 Entry
->UniqueIdOffset
+ Entry
->UniqueIdLength
) > EntrySize
)
189 TruncateRemoteDatabase(Database
, StartingOffset
);
198 DeleteRemoteDatabaseEntry(IN HANDLE Database
,
199 IN LONG StartingOffset
)
201 return STATUS_NOT_IMPLEMENTED
;
209 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName
,
212 IN ULONG ValueLength
,
214 IN PVOID EntryContext
)
216 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
218 /* Ensure it matches, and delete */
219 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
220 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
223 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
228 return STATUS_SUCCESS
;
235 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
236 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
238 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
240 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
241 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
242 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
244 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
255 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
258 LARGE_INTEGER Timeout
;
260 /* Wait for 7 minutes */
261 Timeout
.QuadPart
= 0xFA0A1F00;
262 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
263 if (Status
!= STATUS_TIMEOUT
)
268 return STATUS_IO_TIMEOUT
;
275 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
277 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
282 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
292 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
301 LARGE_INTEGER Timeout
;
302 PRECONCILE_WORK_ITEM WorkItem
;
303 PDEVICE_EXTENSION DeviceExtension
;
304 OBJECT_ATTRIBUTES ObjectAttributes
;
306 InitializeObjectAttributes(&ObjectAttributes
,
308 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
311 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
312 Timeout
.LowPart
= 0xFFFFFFFF;
313 Timeout
.HighPart
= 0xFF676980;
315 /* Try to wait as long as possible */
316 for (i
= (Unloading
? 999 : 0); i
< 1000; i
++)
318 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
319 if (NT_SUCCESS(Status
))
324 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
331 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
333 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
338 DeviceExtension
= Context
;
340 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
342 /* Acquire workers lock */
343 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
345 OldIrql
= KfAcquireSpinLock(&(DeviceExtension
->WorkerLock
));
346 /* Ensure there are workers */
347 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
349 /* Unqueue a worker */
350 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
351 WorkItem
= CONTAINING_RECORD(Entry
,
353 WorkerQueueListEntry
);
355 KfReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
358 WorkItem
->WorkerRoutine(WorkItem
->Context
);
360 IoFreeWorkItem(WorkItem
->WorkItem
);
363 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) == 0)
368 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
369 OldIrql
= KfAcquireSpinLock(&(DeviceExtension
->WorkerLock
));
371 KfReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
373 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
376 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
383 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
384 IN PRECONCILE_WORK_ITEM WorkItem
,
389 WorkItem
->Context
= Context
;
391 /* When called, lock is already acquired */
393 /* If noone, start to work */
394 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
396 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
399 /* Otherwise queue worker for delayed execution */
400 OldIrql
= KfAcquireSpinLock(&(DeviceExtension
->WorkerLock
));
401 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
402 &(WorkItem
->WorkerQueueListEntry
));
403 KfReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
405 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
407 return STATUS_SUCCESS
;
414 QueryVolumeName(IN HANDLE RootDirectory
,
415 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation
,
416 IN PUNICODE_STRING FileName OPTIONAL
,
417 OUT PUNICODE_STRING SymbolicName
,
418 OUT PUNICODE_STRING VolumeName
)
423 IO_STATUS_BLOCK IoStatusBlock
;
424 OBJECT_ATTRIBUTES ObjectAttributes
;
425 PFILE_NAME_INFORMATION FileNameInfo
;
426 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
430 InitializeObjectAttributes(&ObjectAttributes
,
438 InitializeObjectAttributes(&ObjectAttributes
,
440 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
446 Status
= ZwOpenFile(&Handle
,
447 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
450 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
451 (FileName
) ? FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
:
452 FILE_OPEN_BY_FILE_ID
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
);
453 if (!NT_SUCCESS(Status
))
458 /* Get the reparse point data */
459 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
460 if (!ReparseDataBuffer
)
463 return STATUS_INSUFFICIENT_RESOURCES
;
466 Status
= ZwFsControlFile(Handle
,
471 FSCTL_GET_REPARSE_POINT
,
475 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
476 if (!NT_SUCCESS(Status
))
478 FreePool(ReparseDataBuffer
);
483 /* Check that name can fit in buffer */
484 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
486 FreePool(ReparseDataBuffer
);
488 return STATUS_BUFFER_TOO_SMALL
;
491 /* Copy symoblic name */
492 SymbolicName
->Length
= ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
493 RtlCopyMemory(SymbolicName
->Buffer
,
494 (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
495 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
),
496 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
498 FreePool(ReparseDataBuffer
);
500 /* Name has to \ terminated */
501 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
504 return STATUS_INVALID_PARAMETER
;
507 /* So that we can delete it, and match mountmgr requirements */
508 SymbolicName
->Length
-= sizeof(WCHAR
);
509 SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
511 /* Also ensure it's really a volume name... */
512 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
515 return STATUS_INVALID_PARAMETER
;
518 /* Now prepare to really get the name */
519 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
523 return STATUS_INSUFFICIENT_RESOURCES
;
526 Status
= ZwQueryInformationFile(Handle
,
529 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
530 FileNameInformation
);
531 if (Status
== STATUS_BUFFER_OVERFLOW
)
533 /* As expected... Reallocate with proper size */
534 NeededLength
= FileNameInfo
->FileNameLength
;
535 FreePool(FileNameInfo
);
537 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
541 return STATUS_INSUFFICIENT_RESOURCES
;
545 Status
= ZwQueryInformationFile(Handle
,
548 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
549 FileNameInformation
);
554 if (!NT_SUCCESS(Status
))
559 /* Return the volume name */
560 VolumeName
->Length
= FileNameInfo
->FileNameLength
;
561 VolumeName
->MaximumLength
= FileNameInfo
->FileNameLength
+ sizeof(WCHAR
);
562 VolumeName
->Buffer
= AllocatePool(VolumeName
->MaximumLength
);
563 if (!VolumeName
->Buffer
)
565 return STATUS_INSUFFICIENT_RESOURCES
;
568 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
569 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
571 FreePool(FileNameInfo
);
573 return STATUS_SUCCESS
;
580 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
581 IN PDEVICE_INFORMATION DeviceInformation
)
586 IO_STATUS_BLOCK IoStatusBlock
;
587 OBJECT_ATTRIBUTES ObjectAttributes
;
588 PDEVICE_INFORMATION VolumeDeviceInformation
;
589 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[0x64];
590 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
591 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
593 /* Removable devices don't have remote database on them */
594 if (DeviceInformation
->Removable
)
599 /* Prepare a string with reparse point index */
600 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
601 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
602 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
603 if (!ReparseFile
.Buffer
)
608 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
609 DeviceInformation
->DeviceName
.Length
);
610 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
611 ReparseFile
.Buffer
, ReparseFile
.Length
);
612 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
614 InitializeObjectAttributes(&ObjectAttributes
,
616 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
620 /* Open reparse point */
621 Status
= ZwOpenFile(&Handle
,
625 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
626 FILE_SYNCHRONOUS_IO_ALERT
| FILE_OPEN_REPARSE_POINT
);
627 FreePool(ReparseFile
.Buffer
);
628 if (!NT_SUCCESS(Status
))
630 DeviceInformation
->NoDatabase
= FALSE
;
634 /* Query reparse point information
635 * We only pay attention to mout point
637 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
638 FileName
.Buffer
= FileNameBuffer
;
639 FileName
.Length
= sizeof(FileNameBuffer
);
640 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
641 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
642 Status
= ZwQueryDirectoryFile(Handle
,
647 &ReparsePointInformation
,
648 sizeof(FILE_REPARSE_POINT_INFORMATION
),
649 FileReparsePointInformation
,
653 if (!NT_SUCCESS(Status
))
659 /* Query mount points */
663 SymbolicName
.Length
= 0;
664 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
665 SymbolicName
.Buffer
= SymbolicNameBuffer
;
666 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
668 Status
= ZwQueryDirectoryFile(Handle
,
673 &ReparsePointInformation
,
674 sizeof(FILE_REPARSE_POINT_INFORMATION
),
675 FileReparsePointInformation
,
677 (RestartScan
) ? &FileName
: NULL
,
681 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
682 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
693 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
699 /* Get the volume name associated to the mount point */
700 Status
= QueryVolumeName(Handle
,
701 &ReparsePointInformation
,
704 if (!NT_SUCCESS(Status
))
709 FreePool(VolumeName
.Buffer
);
711 /* Get its information */
712 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
713 FALSE
, &VolumeDeviceInformation
);
714 if (!NT_SUCCESS(Status
))
716 DeviceInformation
->NoDatabase
= TRUE
;
720 /* If notification are enabled, mark it online */
721 if (!DeviceInformation
->SkipNotifications
)
723 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
732 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
733 IN PDEVICE_INFORMATION DeviceInformation
)
735 PRECONCILE_WORK_ITEM WorkItem
;
737 /* Removable devices don't have remote database */
738 if (DeviceInformation
->Removable
)
743 /* Allocate a work item */
744 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
750 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
751 if (!WorkItem
->WorkItem
)
758 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
759 WorkItem
->DeviceExtension
= DeviceExtension
;
760 WorkItem
->DeviceInformation
= DeviceInformation
;
761 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
763 /* If there's no automount, and automatic letters
764 * all volumes to find those online and notify there presence
766 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
767 DeviceExtension
->AutomaticDriveLetter
== 1 &&
768 DeviceExtension
->NoAutoMount
== FALSE
)
770 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
778 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
780 PLIST_ENTRY NextEntry
;
781 PDEVICE_INFORMATION DeviceInformation
;
783 /* Browse all the devices */
784 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
785 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
786 NextEntry
= NextEntry
->Flink
)
788 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
791 /* If it's not removable, then, it might have a database to sync */
792 if (!DeviceInformation
->Removable
)
794 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
804 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
811 LARGE_INTEGER ByteOffset
;
812 PMIGRATE_WORK_ITEM WorkItem
;
813 IO_STATUS_BLOCK IoStatusBlock
;
814 HANDLE Migrate
= 0, Database
= 0;
815 PDEVICE_INFORMATION DeviceInformation
;
816 BOOLEAN PreviousMode
, Complete
= FALSE
;
817 UNICODE_STRING DatabaseName
, DatabaseFile
;
818 OBJECT_ATTRIBUTES ObjectAttributes
, MigrateAttributes
;
819 #define TEMP_BUFFER_SIZE 0x200
821 /* Extract context */
823 DeviceInformation
= WorkItem
->DeviceInformation
;
825 /* Reconstruct appropriate string */
826 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
827 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
829 DatabaseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabaseFile
.Length
;
830 DatabaseFile
.MaximumLength
= DatabaseFile
.Length
+ sizeof(WCHAR
);
832 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
833 DatabaseFile
.Buffer
= AllocatePool(DatabaseFile
.MaximumLength
);
834 /* Allocate buffer that will be used to swap contents */
835 TmpBuffer
= AllocatePool(TEMP_BUFFER_SIZE
);
836 if (!DatabaseName
.Buffer
|| !DatabaseFile
.Buffer
|| !TmpBuffer
)
838 Status
= STATUS_INSUFFICIENT_RESOURCES
;
842 /* Create the required folder (in which the database will be stored
843 * \System Volume Information at root of the volume
845 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
846 if (!NT_SUCCESS(Status
))
851 /* Finish initating strings */
852 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
853 RtlCopyMemory(DatabaseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
854 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
855 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
856 RtlCopyMemory(DatabaseFile
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
857 RemoteDatabaseFile
.Buffer
, RemoteDatabaseFile
.Length
);
858 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
859 DatabaseFile
.Buffer
[DatabaseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
861 /* Create database */
862 InitializeObjectAttributes(&ObjectAttributes
,
864 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
868 Status
= ZwCreateFile(&Database
,
869 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
870 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
871 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
875 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
878 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
881 if (!NT_SUCCESS(Status
))
887 InitializeObjectAttributes(&MigrateAttributes
,
889 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
893 /* Disable hard errors and open the database that will be copied */
894 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
895 Status
= ZwCreateFile(&Migrate
,
896 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
897 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
898 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
902 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
905 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
908 IoSetThreadHardErrorMode(PreviousMode
);
909 if (!NT_SUCCESS(Status
))
913 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
915 Status
== STATUS_SUCCESS
;
918 if (!NT_SUCCESS(Status
) || Complete
)
923 ByteOffset
.QuadPart
= 0LL;
924 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
925 /* Now, loop as long it's possible */
926 while (Status
== STATUS_SUCCESS
)
928 /* Read data from existing database */
929 Status
= ZwReadFile(Migrate
,
938 if (!NT_SUCCESS(Status
))
943 /* And write them into new database */
944 Length
= IoStatusBlock
.Information
;
945 Status
= ZwWriteFile(Database
,
954 ByteOffset
.QuadPart
+= Length
;
956 IoSetThreadHardErrorMode(PreviousMode
);
958 /* Delete old databse if it was well copied */
959 if (Status
== STATUS_END_OF_FILE
)
962 Status
= ZwSetInformationFile(Migrate
,
966 FileDispositionInformation
);
969 /* Migration is over */
977 if (DatabaseFile
.Buffer
)
979 FreePool(DatabaseFile
.Buffer
);
982 if (DatabaseName
.Buffer
)
984 FreePool(DatabaseName
.Buffer
);
992 if (NT_SUCCESS(Status
))
994 DeviceInformation
->Migrated
= 1;
1001 IoFreeWorkItem(WorkItem
->WorkItem
);
1003 WorkItem
->WorkItem
= NULL
;
1004 WorkItem
->Status
= Status
;
1005 WorkItem
->Database
= Database
;
1007 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1008 #undef TEMP_BUFFER_SIZE
1015 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1016 IN OUT PHANDLE Database
)
1020 PMIGRATE_WORK_ITEM WorkItem
;
1022 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1024 /* Allocate a work item dedicated to migration */
1025 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1029 return STATUS_INSUFFICIENT_RESOURCES
;
1032 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1033 WorkItem
->Event
= &Event
;
1034 WorkItem
->DeviceInformation
= DeviceInformation
;
1035 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1036 if (!WorkItem
->WorkItem
)
1040 return STATUS_INSUFFICIENT_RESOURCES
;
1044 IoQueueWorkItem(WorkItem
->WorkItem
,
1045 MigrateRemoteDatabaseWorker
,
1049 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1050 Status
= WorkItem
->Status
;
1052 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1062 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1063 IN BOOLEAN MigrateDatabase
)
1067 BOOLEAN PreviousMode
;
1068 IO_STATUS_BLOCK IoStatusBlock
;
1069 OBJECT_ATTRIBUTES ObjectAttributes
;
1070 UNICODE_STRING DeviceRemoteDatabase
;
1074 /* Get database name */
1075 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1076 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1077 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1078 if (!DeviceRemoteDatabase
.Buffer
)
1083 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1084 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1085 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1086 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1089 InitializeObjectAttributes(&ObjectAttributes
,
1090 &DeviceRemoteDatabase
,
1091 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1095 /* Disable hard errors */
1096 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1098 Status
= ZwCreateFile(&Database
,
1099 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1100 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1101 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1105 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1107 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1108 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1112 /* If base it to be migrated and was opened successfully, go ahead */
1113 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1115 MigrateRemoteDatabase(DeviceInformation
, &Database
);
1118 IoSetThreadHardErrorMode(PreviousMode
);
1119 FreePool(DeviceRemoteDatabase
.Buffer
);
1129 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
1132 IN ULONG ValueLength
,
1134 IN PVOID EntryContext
)
1136 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
1137 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
1140 if (ValueLength
>= 0x10000)
1142 return STATUS_SUCCESS
;
1145 /* Allocate the Unique ID */
1146 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
1149 /* Copy data & return */
1150 IntUniqueId
->UniqueIdLength
= ValueLength
;
1151 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
1154 *UniqueId
= IntUniqueId
;
1157 return STATUS_SUCCESS
;
1164 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1165 IN PUNICODE_STRING SymbolicName
,
1166 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
1169 PDEVICE_INFORMATION DeviceInformation
;
1170 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1172 /* Query the unique ID */
1173 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1174 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
1175 QueryTable
[0].Name
= SymbolicName
->Buffer
;
1178 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1183 /* Unique ID found, no need to go farther */
1186 return STATUS_SUCCESS
;
1189 /* Otherwise, find associate device information */
1190 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
1191 if (!NT_SUCCESS(Status
))
1196 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
1199 return STATUS_INSUFFICIENT_RESOURCES
;
1202 /* Return this unique ID (better than nothing) */
1203 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
1204 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
1206 return STATUS_SUCCESS
;
1213 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1214 IN PDATABASE_ENTRY DatabaseEntry
)
1217 PWCHAR SymbolicName
;
1218 PLIST_ENTRY NextEntry
;
1219 UNICODE_STRING SymbolicString
;
1220 PDEVICE_INFORMATION DeviceInformation
;
1222 /* Create symbolic name from database entry */
1223 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
1226 return STATUS_INSUFFICIENT_RESOURCES
;
1229 RtlCopyMemory(SymbolicName
,
1230 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
1231 DatabaseEntry
->SymbolicNameLength
);
1232 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1234 /* Associate the unique ID with the name from remote database */
1235 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1239 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1240 DatabaseEntry
->UniqueIdLength
);
1241 FreePool(SymbolicName
);
1243 /* Reget symbolic name */
1244 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
1245 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
1246 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
1248 /* Find the device using this unique ID */
1249 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1250 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1251 NextEntry
= NextEntry
->Flink
)
1253 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1257 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
1262 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1263 DeviceInformation
->UniqueId
->UniqueId
,
1264 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
1270 /* If found, create a mount point */
1271 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
1273 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
1283 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1284 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1285 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1289 PDATABASE_ENTRY Entry
, NewEntry
;
1290 NTSTATUS Status
= STATUS_SUCCESS
;
1292 /* Open the remote database */
1293 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1299 /* Get all the entries */
1302 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1308 /* Not the correct entry, skip it */
1309 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1311 Offset
+= Entry
->EntrySize
;
1316 /* Not the correct entry, skip it */
1317 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1318 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1319 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1321 Offset
+= Entry
->EntrySize
;
1326 /* Here, we have the correct entry */
1327 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1330 Offset
+= Entry
->EntrySize
;
1335 /* Recreate the entry from the previous one */
1336 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1337 NewEntry
->DatabaseOffset
= Entry
->DatabaseOffset
;
1338 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1339 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1340 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1341 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1342 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1343 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1344 NewEntry
->SymbolicNameLength
);
1345 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1346 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1348 /* Delete old entry */
1349 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1350 if (!NT_SUCCESS(Status
))
1357 /* And replace with new one */
1358 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1361 } while (NT_SUCCESS(Status
));
1363 CloseRemoteDatabase(Database
);
1373 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
1376 IN ULONG ValueLength
,
1378 IN PVOID EntryContext
)
1380 PMOUNTDEV_UNIQUE_ID UniqueId
;
1381 UNICODE_STRING RegistryEntry
;
1383 if (ValueType
!= REG_BINARY
)
1385 return STATUS_SUCCESS
;
1390 /* First ensure we have the correct data */
1391 if (UniqueId
->UniqueIdLength
!= ValueLength
)
1393 return STATUS_SUCCESS
;
1396 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1398 return STATUS_SUCCESS
;
1401 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
1403 /* Then, it's a drive letter, erase it */
1404 if (IsDriveLetter(&RegistryEntry
))
1406 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1411 return STATUS_SUCCESS
;
1418 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1420 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1422 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1423 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
1425 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1437 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
1440 IN ULONG ValueLength
,
1442 IN PVOID EntryContext
)
1444 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
1446 /* Ensure we have correct input */
1447 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
1448 UniqueId
->UniqueIdLength
!= ValueLength
)
1450 return STATUS_SUCCESS
;
1453 /* And then, if unique ID matching, delete entry */
1454 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1456 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1461 return STATUS_SUCCESS
;
1468 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1470 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1472 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1473 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
1475 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,