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
) ||
179 (IoStatusBlock
.Information
!= EntrySize
) ||
180 (EntrySize
< sizeof(DATABASE_ENTRY
)) )
182 TruncateRemoteDatabase(Database
, StartingOffset
);
188 if (MAX(Entry
->SymbolicNameOffset
+ Entry
->SymbolicNameLength
,
189 Entry
->UniqueIdOffset
+ Entry
->UniqueIdLength
) > (LONG
)EntrySize
)
191 TruncateRemoteDatabase(Database
, StartingOffset
);
203 DeleteRemoteDatabaseEntry(IN HANDLE Database
,
204 IN LONG StartingOffset
)
210 PDATABASE_ENTRY Entry
;
211 IO_STATUS_BLOCK IoStatusBlock
;
212 LARGE_INTEGER EndEntriesOffset
;
214 /* First, get database size */
215 DatabaseSize
= GetRemoteDatabaseSize(Database
);
218 return STATUS_INVALID_PARAMETER
;
221 /* Then, get the entry to remove */
222 Entry
= GetRemoteDatabaseEntry(Database
, StartingOffset
);
225 return STATUS_INVALID_PARAMETER
;
228 /* Validate parameters: ensure we won't get negative size */
229 if (Entry
->EntrySize
+ StartingOffset
> DatabaseSize
)
231 /* If we get invalid parameters, truncate the whole database
232 * starting the wrong entry. We can't rely on the rest
235 return TruncateRemoteDatabase(Database
, StartingOffset
);
238 /* Now, get the size of the remaining entries (those after the one to remove) */
239 EndSize
= DatabaseSize
- Entry
->EntrySize
- StartingOffset
;
240 /* Allocate a buffer big enough to hold them */
241 TmpBuffer
= AllocatePool(EndSize
);
245 return STATUS_INSUFFICIENT_RESOURCES
;
248 /* Get the offset of the entry right after the one to delete */
249 EndEntriesOffset
.QuadPart
= Entry
->EntrySize
+ StartingOffset
;
250 /* We don't need the entry any more */
253 /* Read the ending entries */
254 Status
= ZwReadFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
255 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
256 if (!NT_SUCCESS(Status
))
262 /* Ensure nothing went wrong - we don't want to corrupt the DB */
263 if (IoStatusBlock
.Information
!= EndSize
)
266 return STATUS_INVALID_PARAMETER
;
269 /* Remove the entry */
270 Status
= TruncateRemoteDatabase(Database
, StartingOffset
+ EndSize
);
271 if (!NT_SUCCESS(Status
))
277 /* Now, shift the ending entries to erase the entry */
278 EndEntriesOffset
.QuadPart
= StartingOffset
;
279 Status
= ZwWriteFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
280 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
292 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName
,
295 IN ULONG ValueLength
,
297 IN PVOID EntryContext
)
299 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
301 /* Ensure it matches, and delete */
302 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
303 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
306 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
311 return STATUS_SUCCESS
;
318 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
319 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
321 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
323 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
324 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
325 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
327 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
338 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
341 LARGE_INTEGER Timeout
;
343 /* Wait for 7 minutes */
344 Timeout
.QuadPart
= 0xFA0A1F00;
345 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
346 if (Status
!= STATUS_TIMEOUT
)
351 return STATUS_IO_TIMEOUT
;
358 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
360 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
365 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
375 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
384 LARGE_INTEGER Timeout
;
385 PRECONCILE_WORK_ITEM WorkItem
;
386 PDEVICE_EXTENSION DeviceExtension
;
387 OBJECT_ATTRIBUTES ObjectAttributes
;
389 InitializeObjectAttributes(&ObjectAttributes
,
391 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
394 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
395 Timeout
.LowPart
= 0xFFFFFFFF;
396 Timeout
.HighPart
= 0xFF676980;
398 /* Try to wait as long as possible */
399 for (i
= (Unloading
? 999 : 0); i
< 1000; i
++)
401 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
402 if (NT_SUCCESS(Status
))
407 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
414 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
416 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
421 DeviceExtension
= Context
;
423 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
425 /* Acquire workers lock */
426 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
428 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
430 /* Ensure there are workers */
431 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
433 /* Unqueue a worker */
434 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
435 WorkItem
= CONTAINING_RECORD(Entry
,
437 WorkerQueueListEntry
);
439 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
442 WorkItem
->WorkerRoutine(WorkItem
->Context
);
444 IoFreeWorkItem(WorkItem
->WorkItem
);
447 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) == 0)
452 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
453 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
455 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
457 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
460 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
467 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
468 IN PRECONCILE_WORK_ITEM WorkItem
,
473 WorkItem
->Context
= Context
;
475 /* When called, lock is already acquired */
477 /* If noone, start to work */
478 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
480 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
483 /* Otherwise queue worker for delayed execution */
484 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
485 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
486 &(WorkItem
->WorkerQueueListEntry
));
487 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
489 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
491 return STATUS_SUCCESS
;
498 QueryVolumeName(IN HANDLE RootDirectory
,
499 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation
,
500 IN PUNICODE_STRING FileName OPTIONAL
,
501 OUT PUNICODE_STRING SymbolicName
,
502 OUT PUNICODE_STRING VolumeName
)
507 IO_STATUS_BLOCK IoStatusBlock
;
508 OBJECT_ATTRIBUTES ObjectAttributes
;
509 PFILE_NAME_INFORMATION FileNameInfo
;
510 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
514 InitializeObjectAttributes(&ObjectAttributes
,
522 InitializeObjectAttributes(&ObjectAttributes
,
524 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
530 Status
= ZwOpenFile(&Handle
,
531 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
534 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
535 (FileName
) ? FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
:
536 FILE_OPEN_BY_FILE_ID
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
);
537 if (!NT_SUCCESS(Status
))
542 /* Get the reparse point data */
543 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
544 if (!ReparseDataBuffer
)
547 return STATUS_INSUFFICIENT_RESOURCES
;
550 Status
= ZwFsControlFile(Handle
,
555 FSCTL_GET_REPARSE_POINT
,
559 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
560 if (!NT_SUCCESS(Status
))
562 FreePool(ReparseDataBuffer
);
567 /* Check that name can fit in buffer */
568 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
570 FreePool(ReparseDataBuffer
);
572 return STATUS_BUFFER_TOO_SMALL
;
575 /* Copy symoblic name */
576 SymbolicName
->Length
= ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
577 RtlCopyMemory(SymbolicName
->Buffer
,
578 (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
579 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
),
580 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
582 FreePool(ReparseDataBuffer
);
584 /* Name has to \ terminated */
585 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
588 return STATUS_INVALID_PARAMETER
;
591 /* So that we can delete it, and match mountmgr requirements */
592 SymbolicName
->Length
-= sizeof(WCHAR
);
593 SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
595 /* Also ensure it's really a volume name... */
596 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
599 return STATUS_INVALID_PARAMETER
;
602 /* Now prepare to really get the name */
603 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
607 return STATUS_INSUFFICIENT_RESOURCES
;
610 Status
= ZwQueryInformationFile(Handle
,
613 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
614 FileNameInformation
);
615 if (Status
== STATUS_BUFFER_OVERFLOW
)
617 /* As expected... Reallocate with proper size */
618 NeededLength
= FileNameInfo
->FileNameLength
;
619 FreePool(FileNameInfo
);
621 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
625 return STATUS_INSUFFICIENT_RESOURCES
;
629 Status
= ZwQueryInformationFile(Handle
,
632 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
633 FileNameInformation
);
638 if (!NT_SUCCESS(Status
))
643 /* Return the volume name */
644 VolumeName
->Length
= (USHORT
)FileNameInfo
->FileNameLength
;
645 VolumeName
->MaximumLength
= (USHORT
)FileNameInfo
->FileNameLength
+ sizeof(WCHAR
);
646 VolumeName
->Buffer
= AllocatePool(VolumeName
->MaximumLength
);
647 if (!VolumeName
->Buffer
)
649 return STATUS_INSUFFICIENT_RESOURCES
;
652 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
653 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
655 FreePool(FileNameInfo
);
657 return STATUS_SUCCESS
;
664 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
665 IN PDEVICE_INFORMATION DeviceInformation
)
670 IO_STATUS_BLOCK IoStatusBlock
;
671 OBJECT_ATTRIBUTES ObjectAttributes
;
672 PDEVICE_INFORMATION VolumeDeviceInformation
;
673 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[0x64];
674 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
675 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
677 /* Removable devices don't have remote database on them */
678 if (DeviceInformation
->Removable
)
683 /* Prepare a string with reparse point index */
684 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
685 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
686 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
687 if (!ReparseFile
.Buffer
)
692 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
693 DeviceInformation
->DeviceName
.Length
);
694 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
695 ReparseFile
.Buffer
, ReparseFile
.Length
);
696 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
698 InitializeObjectAttributes(&ObjectAttributes
,
700 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
704 /* Open reparse point */
705 Status
= ZwOpenFile(&Handle
,
709 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
710 FILE_SYNCHRONOUS_IO_ALERT
| FILE_OPEN_REPARSE_POINT
);
711 FreePool(ReparseFile
.Buffer
);
712 if (!NT_SUCCESS(Status
))
714 DeviceInformation
->NoDatabase
= FALSE
;
718 /* Query reparse point information
719 * We only pay attention to mout point
721 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
722 FileName
.Buffer
= FileNameBuffer
;
723 FileName
.Length
= sizeof(FileNameBuffer
);
724 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
725 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
726 Status
= ZwQueryDirectoryFile(Handle
,
731 &ReparsePointInformation
,
732 sizeof(FILE_REPARSE_POINT_INFORMATION
),
733 FileReparsePointInformation
,
737 if (!NT_SUCCESS(Status
))
743 /* Query mount points */
747 SymbolicName
.Length
= 0;
748 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
749 SymbolicName
.Buffer
= SymbolicNameBuffer
;
750 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
752 Status
= ZwQueryDirectoryFile(Handle
,
757 &ReparsePointInformation
,
758 sizeof(FILE_REPARSE_POINT_INFORMATION
),
759 FileReparsePointInformation
,
761 (RestartScan
) ? &FileName
: NULL
,
765 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
766 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
777 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
783 /* Get the volume name associated to the mount point */
784 Status
= QueryVolumeName(Handle
,
785 &ReparsePointInformation
,
788 if (!NT_SUCCESS(Status
))
793 FreePool(VolumeName
.Buffer
);
795 /* Get its information */
796 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
797 FALSE
, &VolumeDeviceInformation
);
798 if (!NT_SUCCESS(Status
))
800 DeviceInformation
->NoDatabase
= TRUE
;
804 /* If notification are enabled, mark it online */
805 if (!DeviceInformation
->SkipNotifications
)
807 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
816 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
817 IN PDEVICE_INFORMATION DeviceInformation
)
819 PRECONCILE_WORK_ITEM WorkItem
;
821 /* Removable devices don't have remote database */
822 if (DeviceInformation
->Removable
)
827 /* Allocate a work item */
828 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
834 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
835 if (!WorkItem
->WorkItem
)
842 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
843 WorkItem
->DeviceExtension
= DeviceExtension
;
844 WorkItem
->DeviceInformation
= DeviceInformation
;
845 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
847 /* If there's no automount, and automatic letters
848 * all volumes to find those online and notify there presence
850 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
851 DeviceExtension
->AutomaticDriveLetter
== 1 &&
852 DeviceExtension
->NoAutoMount
== FALSE
)
854 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
862 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
864 PLIST_ENTRY NextEntry
;
865 PDEVICE_INFORMATION DeviceInformation
;
867 /* Browse all the devices */
868 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
869 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
870 NextEntry
= NextEntry
->Flink
)
872 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
875 /* If it's not removable, then, it might have a database to sync */
876 if (!DeviceInformation
->Removable
)
878 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
888 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
895 LARGE_INTEGER ByteOffset
;
896 PMIGRATE_WORK_ITEM WorkItem
;
897 IO_STATUS_BLOCK IoStatusBlock
;
898 HANDLE Migrate
= 0, Database
= 0;
899 PDEVICE_INFORMATION DeviceInformation
;
900 BOOLEAN PreviousMode
, Complete
= FALSE
;
901 UNICODE_STRING DatabaseName
, DatabaseFile
;
902 OBJECT_ATTRIBUTES ObjectAttributes
, MigrateAttributes
;
903 #define TEMP_BUFFER_SIZE 0x200
905 /* Extract context */
907 DeviceInformation
= WorkItem
->DeviceInformation
;
909 /* Reconstruct appropriate string */
910 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
911 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
913 DatabaseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabaseFile
.Length
;
914 DatabaseFile
.MaximumLength
= DatabaseFile
.Length
+ sizeof(WCHAR
);
916 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
917 DatabaseFile
.Buffer
= AllocatePool(DatabaseFile
.MaximumLength
);
918 /* Allocate buffer that will be used to swap contents */
919 TmpBuffer
= AllocatePool(TEMP_BUFFER_SIZE
);
920 if (!DatabaseName
.Buffer
|| !DatabaseFile
.Buffer
|| !TmpBuffer
)
922 Status
= STATUS_INSUFFICIENT_RESOURCES
;
926 /* Create the required folder (in which the database will be stored
927 * \System Volume Information at root of the volume
929 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
930 if (!NT_SUCCESS(Status
))
935 /* Finish initating strings */
936 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
937 RtlCopyMemory(DatabaseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
938 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
939 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
940 RtlCopyMemory(DatabaseFile
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
941 RemoteDatabaseFile
.Buffer
, RemoteDatabaseFile
.Length
);
942 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
943 DatabaseFile
.Buffer
[DatabaseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
945 /* Create database */
946 InitializeObjectAttributes(&ObjectAttributes
,
948 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
952 Status
= ZwCreateFile(&Database
,
953 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
954 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
955 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
959 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
962 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
965 if (!NT_SUCCESS(Status
))
971 InitializeObjectAttributes(&MigrateAttributes
,
973 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
977 /* Disable hard errors and open the database that will be copied */
978 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
979 Status
= ZwCreateFile(&Migrate
,
980 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
981 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
982 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
986 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
989 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
992 IoSetThreadHardErrorMode(PreviousMode
);
993 if (!NT_SUCCESS(Status
))
997 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
999 Status
= STATUS_SUCCESS
;
1002 if (!NT_SUCCESS(Status
) || Complete
)
1007 ByteOffset
.QuadPart
= 0LL;
1008 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1009 /* Now, loop as long it's possible */
1010 while (Status
== STATUS_SUCCESS
)
1012 /* Read data from existing database */
1013 Status
= ZwReadFile(Migrate
,
1022 if (!NT_SUCCESS(Status
))
1027 /* And write them into new database */
1028 Length
= (ULONG
)IoStatusBlock
.Information
;
1029 Status
= ZwWriteFile(Database
,
1038 ByteOffset
.QuadPart
+= Length
;
1040 IoSetThreadHardErrorMode(PreviousMode
);
1042 /* Delete old databse if it was well copied */
1043 if (Status
== STATUS_END_OF_FILE
)
1046 Status
= ZwSetInformationFile(Migrate
,
1049 sizeof(Disposition
),
1050 FileDispositionInformation
);
1053 /* Migration is over */
1058 FreePool(TmpBuffer
);
1061 if (DatabaseFile
.Buffer
)
1063 FreePool(DatabaseFile
.Buffer
);
1066 if (DatabaseName
.Buffer
)
1068 FreePool(DatabaseName
.Buffer
);
1076 if (NT_SUCCESS(Status
))
1078 DeviceInformation
->Migrated
= 1;
1085 IoFreeWorkItem(WorkItem
->WorkItem
);
1087 WorkItem
->WorkItem
= NULL
;
1088 WorkItem
->Status
= Status
;
1089 WorkItem
->Database
= Database
;
1091 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1092 #undef TEMP_BUFFER_SIZE
1099 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1100 IN OUT PHANDLE Database
)
1104 PMIGRATE_WORK_ITEM WorkItem
;
1106 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1108 /* Allocate a work item dedicated to migration */
1109 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1113 return STATUS_INSUFFICIENT_RESOURCES
;
1116 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1117 WorkItem
->Event
= &Event
;
1118 WorkItem
->DeviceInformation
= DeviceInformation
;
1119 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1120 if (!WorkItem
->WorkItem
)
1124 return STATUS_INSUFFICIENT_RESOURCES
;
1128 IoQueueWorkItem(WorkItem
->WorkItem
,
1129 MigrateRemoteDatabaseWorker
,
1133 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1134 Status
= WorkItem
->Status
;
1136 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1146 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1147 IN BOOLEAN MigrateDatabase
)
1151 BOOLEAN PreviousMode
;
1152 IO_STATUS_BLOCK IoStatusBlock
;
1153 OBJECT_ATTRIBUTES ObjectAttributes
;
1154 UNICODE_STRING DeviceRemoteDatabase
;
1158 /* Get database name */
1159 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1160 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1161 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1162 if (!DeviceRemoteDatabase
.Buffer
)
1167 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1168 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1169 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1170 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1173 InitializeObjectAttributes(&ObjectAttributes
,
1174 &DeviceRemoteDatabase
,
1175 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1179 /* Disable hard errors */
1180 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1182 Status
= ZwCreateFile(&Database
,
1183 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1184 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1185 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1189 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1191 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1192 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1196 /* If base it to be migrated and was opened successfully, go ahead */
1197 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1199 MigrateRemoteDatabase(DeviceInformation
, &Database
);
1202 IoSetThreadHardErrorMode(PreviousMode
);
1203 FreePool(DeviceRemoteDatabase
.Buffer
);
1213 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
1216 IN ULONG ValueLength
,
1218 IN PVOID EntryContext
)
1220 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
1221 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
1224 if (ValueLength
>= 0x10000)
1226 return STATUS_SUCCESS
;
1229 /* Allocate the Unique ID */
1230 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
1233 /* Copy data & return */
1234 IntUniqueId
->UniqueIdLength
= (USHORT
)ValueLength
;
1235 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
1238 *UniqueId
= IntUniqueId
;
1241 return STATUS_SUCCESS
;
1248 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1249 IN PUNICODE_STRING SymbolicName
,
1250 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
1253 PDEVICE_INFORMATION DeviceInformation
;
1254 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1256 /* Query the unique ID */
1257 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1258 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
1259 QueryTable
[0].Name
= SymbolicName
->Buffer
;
1262 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1267 /* Unique ID found, no need to go farther */
1270 return STATUS_SUCCESS
;
1273 /* Otherwise, find associate device information */
1274 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
1275 if (!NT_SUCCESS(Status
))
1280 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
1283 return STATUS_INSUFFICIENT_RESOURCES
;
1286 /* Return this unique ID (better than nothing) */
1287 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
1288 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
1290 return STATUS_SUCCESS
;
1297 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1298 IN PDATABASE_ENTRY DatabaseEntry
)
1301 PWCHAR SymbolicName
;
1302 PLIST_ENTRY NextEntry
;
1303 UNICODE_STRING SymbolicString
;
1304 PDEVICE_INFORMATION DeviceInformation
;
1306 /* Create symbolic name from database entry */
1307 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
1310 return STATUS_INSUFFICIENT_RESOURCES
;
1313 RtlCopyMemory(SymbolicName
,
1314 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
1315 DatabaseEntry
->SymbolicNameLength
);
1316 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1318 /* Associate the unique ID with the name from remote database */
1319 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1323 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1324 DatabaseEntry
->UniqueIdLength
);
1325 FreePool(SymbolicName
);
1327 /* Reget symbolic name */
1328 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
1329 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
1330 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
1332 /* Find the device using this unique ID */
1333 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1334 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1335 NextEntry
= NextEntry
->Flink
)
1337 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1341 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
1346 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1347 DeviceInformation
->UniqueId
->UniqueId
,
1348 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
1354 /* If found, create a mount point */
1355 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
1357 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
1367 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1368 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1369 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1373 PDATABASE_ENTRY Entry
, NewEntry
;
1374 NTSTATUS Status
= STATUS_SUCCESS
;
1376 /* Open the remote database */
1377 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1383 /* Get all the entries */
1386 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1392 /* Not the correct entry, skip it */
1393 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1395 Offset
+= Entry
->EntrySize
;
1400 /* Not the correct entry, skip it */
1401 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1402 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1403 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1405 Offset
+= Entry
->EntrySize
;
1410 /* Here, we have the correct entry */
1411 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1414 Offset
+= Entry
->EntrySize
;
1419 /* Recreate the entry from the previous one */
1420 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1421 NewEntry
->DatabaseOffset
= Entry
->DatabaseOffset
;
1422 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1423 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1424 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1425 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1426 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1427 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1428 NewEntry
->SymbolicNameLength
);
1429 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1430 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1432 /* Delete old entry */
1433 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1434 if (!NT_SUCCESS(Status
))
1441 /* And replace with new one */
1442 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1445 } while (NT_SUCCESS(Status
));
1447 CloseRemoteDatabase(Database
);
1457 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
1460 IN ULONG ValueLength
,
1462 IN PVOID EntryContext
)
1464 PMOUNTDEV_UNIQUE_ID UniqueId
;
1465 UNICODE_STRING RegistryEntry
;
1467 if (ValueType
!= REG_BINARY
)
1469 return STATUS_SUCCESS
;
1474 /* First ensure we have the correct data */
1475 if (UniqueId
->UniqueIdLength
!= ValueLength
)
1477 return STATUS_SUCCESS
;
1480 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1482 return STATUS_SUCCESS
;
1485 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
1487 /* Then, it's a drive letter, erase it */
1488 if (IsDriveLetter(&RegistryEntry
))
1490 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1495 return STATUS_SUCCESS
;
1502 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1504 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1506 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1507 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
1509 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1521 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
1524 IN ULONG ValueLength
,
1526 IN PVOID EntryContext
)
1528 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
1530 /* Ensure we have correct input */
1531 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
1532 UniqueId
->UniqueIdLength
!= ValueLength
)
1534 return STATUS_SUCCESS
;
1537 /* And then, if unique ID matching, delete entry */
1538 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1540 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1545 return STATUS_SUCCESS
;
1552 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1554 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1556 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1557 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
1559 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,