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
);
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 /* Ensure it matches, and delete */
300 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
301 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
304 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
309 return STATUS_SUCCESS
;
316 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
317 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
319 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
321 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
322 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
323 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
325 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
336 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
339 LARGE_INTEGER Timeout
;
341 /* Wait for 7 minutes */
342 Timeout
.QuadPart
= 0xFA0A1F00;
343 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
344 if (Status
!= STATUS_TIMEOUT
)
349 return STATUS_IO_TIMEOUT
;
356 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
358 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
363 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
373 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
382 LARGE_INTEGER Timeout
;
383 PRECONCILE_WORK_ITEM WorkItem
;
384 PDEVICE_EXTENSION DeviceExtension
;
385 OBJECT_ATTRIBUTES ObjectAttributes
;
387 InitializeObjectAttributes(&ObjectAttributes
,
389 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
392 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
393 Timeout
.LowPart
= 0xFFFFFFFF;
394 Timeout
.HighPart
= 0xFF676980;
396 /* Try to wait as long as possible */
397 for (i
= (Unloading
? 999 : 0); i
< 1000; i
++)
399 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
400 if (NT_SUCCESS(Status
))
405 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
412 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
414 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
419 DeviceExtension
= Context
;
421 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
423 /* Acquire workers lock */
424 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
426 OldIrql
= KfAcquireSpinLock(&(DeviceExtension
->WorkerLock
));
427 /* Ensure there are workers */
428 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
430 /* Unqueue a worker */
431 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
432 WorkItem
= CONTAINING_RECORD(Entry
,
434 WorkerQueueListEntry
);
436 KfReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
439 WorkItem
->WorkerRoutine(WorkItem
->Context
);
441 IoFreeWorkItem(WorkItem
->WorkItem
);
444 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) == 0)
449 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
450 OldIrql
= KfAcquireSpinLock(&(DeviceExtension
->WorkerLock
));
452 KfReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
454 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
457 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
464 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
465 IN PRECONCILE_WORK_ITEM WorkItem
,
470 WorkItem
->Context
= Context
;
472 /* When called, lock is already acquired */
474 /* If noone, start to work */
475 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
477 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
480 /* Otherwise queue worker for delayed execution */
481 OldIrql
= KfAcquireSpinLock(&(DeviceExtension
->WorkerLock
));
482 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
483 &(WorkItem
->WorkerQueueListEntry
));
484 KfReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
486 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
488 return STATUS_SUCCESS
;
495 QueryVolumeName(IN HANDLE RootDirectory
,
496 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation
,
497 IN PUNICODE_STRING FileName OPTIONAL
,
498 OUT PUNICODE_STRING SymbolicName
,
499 OUT PUNICODE_STRING VolumeName
)
504 IO_STATUS_BLOCK IoStatusBlock
;
505 OBJECT_ATTRIBUTES ObjectAttributes
;
506 PFILE_NAME_INFORMATION FileNameInfo
;
507 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
511 InitializeObjectAttributes(&ObjectAttributes
,
519 InitializeObjectAttributes(&ObjectAttributes
,
521 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
527 Status
= ZwOpenFile(&Handle
,
528 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
531 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
532 (FileName
) ? FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
:
533 FILE_OPEN_BY_FILE_ID
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
);
534 if (!NT_SUCCESS(Status
))
539 /* Get the reparse point data */
540 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
541 if (!ReparseDataBuffer
)
544 return STATUS_INSUFFICIENT_RESOURCES
;
547 Status
= ZwFsControlFile(Handle
,
552 FSCTL_GET_REPARSE_POINT
,
556 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
557 if (!NT_SUCCESS(Status
))
559 FreePool(ReparseDataBuffer
);
564 /* Check that name can fit in buffer */
565 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
567 FreePool(ReparseDataBuffer
);
569 return STATUS_BUFFER_TOO_SMALL
;
572 /* Copy symoblic name */
573 SymbolicName
->Length
= ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
574 RtlCopyMemory(SymbolicName
->Buffer
,
575 (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
576 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
),
577 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
579 FreePool(ReparseDataBuffer
);
581 /* Name has to \ terminated */
582 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
585 return STATUS_INVALID_PARAMETER
;
588 /* So that we can delete it, and match mountmgr requirements */
589 SymbolicName
->Length
-= sizeof(WCHAR
);
590 SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
592 /* Also ensure it's really a volume name... */
593 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
596 return STATUS_INVALID_PARAMETER
;
599 /* Now prepare to really get the name */
600 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
604 return STATUS_INSUFFICIENT_RESOURCES
;
607 Status
= ZwQueryInformationFile(Handle
,
610 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
611 FileNameInformation
);
612 if (Status
== STATUS_BUFFER_OVERFLOW
)
614 /* As expected... Reallocate with proper size */
615 NeededLength
= FileNameInfo
->FileNameLength
;
616 FreePool(FileNameInfo
);
618 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
622 return STATUS_INSUFFICIENT_RESOURCES
;
626 Status
= ZwQueryInformationFile(Handle
,
629 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
630 FileNameInformation
);
635 if (!NT_SUCCESS(Status
))
640 /* Return the volume name */
641 VolumeName
->Length
= FileNameInfo
->FileNameLength
;
642 VolumeName
->MaximumLength
= FileNameInfo
->FileNameLength
+ sizeof(WCHAR
);
643 VolumeName
->Buffer
= AllocatePool(VolumeName
->MaximumLength
);
644 if (!VolumeName
->Buffer
)
646 return STATUS_INSUFFICIENT_RESOURCES
;
649 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
650 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
652 FreePool(FileNameInfo
);
654 return STATUS_SUCCESS
;
661 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
662 IN PDEVICE_INFORMATION DeviceInformation
)
667 IO_STATUS_BLOCK IoStatusBlock
;
668 OBJECT_ATTRIBUTES ObjectAttributes
;
669 PDEVICE_INFORMATION VolumeDeviceInformation
;
670 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[0x64];
671 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
672 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
674 /* Removable devices don't have remote database on them */
675 if (DeviceInformation
->Removable
)
680 /* Prepare a string with reparse point index */
681 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
682 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
683 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
684 if (!ReparseFile
.Buffer
)
689 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
690 DeviceInformation
->DeviceName
.Length
);
691 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
692 ReparseFile
.Buffer
, ReparseFile
.Length
);
693 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
695 InitializeObjectAttributes(&ObjectAttributes
,
697 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
701 /* Open reparse point */
702 Status
= ZwOpenFile(&Handle
,
706 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
707 FILE_SYNCHRONOUS_IO_ALERT
| FILE_OPEN_REPARSE_POINT
);
708 FreePool(ReparseFile
.Buffer
);
709 if (!NT_SUCCESS(Status
))
711 DeviceInformation
->NoDatabase
= FALSE
;
715 /* Query reparse point information
716 * We only pay attention to mout point
718 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
719 FileName
.Buffer
= FileNameBuffer
;
720 FileName
.Length
= sizeof(FileNameBuffer
);
721 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
722 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
723 Status
= ZwQueryDirectoryFile(Handle
,
728 &ReparsePointInformation
,
729 sizeof(FILE_REPARSE_POINT_INFORMATION
),
730 FileReparsePointInformation
,
734 if (!NT_SUCCESS(Status
))
740 /* Query mount points */
744 SymbolicName
.Length
= 0;
745 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
746 SymbolicName
.Buffer
= SymbolicNameBuffer
;
747 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
749 Status
= ZwQueryDirectoryFile(Handle
,
754 &ReparsePointInformation
,
755 sizeof(FILE_REPARSE_POINT_INFORMATION
),
756 FileReparsePointInformation
,
758 (RestartScan
) ? &FileName
: NULL
,
762 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
763 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
774 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
780 /* Get the volume name associated to the mount point */
781 Status
= QueryVolumeName(Handle
,
782 &ReparsePointInformation
,
785 if (!NT_SUCCESS(Status
))
790 FreePool(VolumeName
.Buffer
);
792 /* Get its information */
793 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
794 FALSE
, &VolumeDeviceInformation
);
795 if (!NT_SUCCESS(Status
))
797 DeviceInformation
->NoDatabase
= TRUE
;
801 /* If notification are enabled, mark it online */
802 if (!DeviceInformation
->SkipNotifications
)
804 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
813 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
814 IN PDEVICE_INFORMATION DeviceInformation
)
816 PRECONCILE_WORK_ITEM WorkItem
;
818 /* Removable devices don't have remote database */
819 if (DeviceInformation
->Removable
)
824 /* Allocate a work item */
825 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
831 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
832 if (!WorkItem
->WorkItem
)
839 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
840 WorkItem
->DeviceExtension
= DeviceExtension
;
841 WorkItem
->DeviceInformation
= DeviceInformation
;
842 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
844 /* If there's no automount, and automatic letters
845 * all volumes to find those online and notify there presence
847 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
848 DeviceExtension
->AutomaticDriveLetter
== 1 &&
849 DeviceExtension
->NoAutoMount
== FALSE
)
851 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
859 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
861 PLIST_ENTRY NextEntry
;
862 PDEVICE_INFORMATION DeviceInformation
;
864 /* Browse all the devices */
865 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
866 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
867 NextEntry
= NextEntry
->Flink
)
869 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
872 /* If it's not removable, then, it might have a database to sync */
873 if (!DeviceInformation
->Removable
)
875 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
885 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
892 LARGE_INTEGER ByteOffset
;
893 PMIGRATE_WORK_ITEM WorkItem
;
894 IO_STATUS_BLOCK IoStatusBlock
;
895 HANDLE Migrate
= 0, Database
= 0;
896 PDEVICE_INFORMATION DeviceInformation
;
897 BOOLEAN PreviousMode
, Complete
= FALSE
;
898 UNICODE_STRING DatabaseName
, DatabaseFile
;
899 OBJECT_ATTRIBUTES ObjectAttributes
, MigrateAttributes
;
900 #define TEMP_BUFFER_SIZE 0x200
902 /* Extract context */
904 DeviceInformation
= WorkItem
->DeviceInformation
;
906 /* Reconstruct appropriate string */
907 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
908 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
910 DatabaseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabaseFile
.Length
;
911 DatabaseFile
.MaximumLength
= DatabaseFile
.Length
+ sizeof(WCHAR
);
913 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
914 DatabaseFile
.Buffer
= AllocatePool(DatabaseFile
.MaximumLength
);
915 /* Allocate buffer that will be used to swap contents */
916 TmpBuffer
= AllocatePool(TEMP_BUFFER_SIZE
);
917 if (!DatabaseName
.Buffer
|| !DatabaseFile
.Buffer
|| !TmpBuffer
)
919 Status
= STATUS_INSUFFICIENT_RESOURCES
;
923 /* Create the required folder (in which the database will be stored
924 * \System Volume Information at root of the volume
926 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
927 if (!NT_SUCCESS(Status
))
932 /* Finish initating strings */
933 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
934 RtlCopyMemory(DatabaseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
935 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
936 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
937 RtlCopyMemory(DatabaseFile
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
938 RemoteDatabaseFile
.Buffer
, RemoteDatabaseFile
.Length
);
939 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
940 DatabaseFile
.Buffer
[DatabaseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
942 /* Create database */
943 InitializeObjectAttributes(&ObjectAttributes
,
945 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
949 Status
= ZwCreateFile(&Database
,
950 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
951 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
952 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
956 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
959 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
962 if (!NT_SUCCESS(Status
))
968 InitializeObjectAttributes(&MigrateAttributes
,
970 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
974 /* Disable hard errors and open the database that will be copied */
975 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
976 Status
= ZwCreateFile(&Migrate
,
977 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
978 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
979 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
983 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
986 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
989 IoSetThreadHardErrorMode(PreviousMode
);
990 if (!NT_SUCCESS(Status
))
994 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
996 Status
== STATUS_SUCCESS
;
999 if (!NT_SUCCESS(Status
) || Complete
)
1004 ByteOffset
.QuadPart
= 0LL;
1005 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1006 /* Now, loop as long it's possible */
1007 while (Status
== STATUS_SUCCESS
)
1009 /* Read data from existing database */
1010 Status
= ZwReadFile(Migrate
,
1019 if (!NT_SUCCESS(Status
))
1024 /* And write them into new database */
1025 Length
= IoStatusBlock
.Information
;
1026 Status
= ZwWriteFile(Database
,
1035 ByteOffset
.QuadPart
+= Length
;
1037 IoSetThreadHardErrorMode(PreviousMode
);
1039 /* Delete old databse if it was well copied */
1040 if (Status
== STATUS_END_OF_FILE
)
1043 Status
= ZwSetInformationFile(Migrate
,
1046 sizeof(Disposition
),
1047 FileDispositionInformation
);
1050 /* Migration is over */
1055 FreePool(TmpBuffer
);
1058 if (DatabaseFile
.Buffer
)
1060 FreePool(DatabaseFile
.Buffer
);
1063 if (DatabaseName
.Buffer
)
1065 FreePool(DatabaseName
.Buffer
);
1073 if (NT_SUCCESS(Status
))
1075 DeviceInformation
->Migrated
= 1;
1082 IoFreeWorkItem(WorkItem
->WorkItem
);
1084 WorkItem
->WorkItem
= NULL
;
1085 WorkItem
->Status
= Status
;
1086 WorkItem
->Database
= Database
;
1088 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1089 #undef TEMP_BUFFER_SIZE
1096 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1097 IN OUT PHANDLE Database
)
1101 PMIGRATE_WORK_ITEM WorkItem
;
1103 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1105 /* Allocate a work item dedicated to migration */
1106 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1110 return STATUS_INSUFFICIENT_RESOURCES
;
1113 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1114 WorkItem
->Event
= &Event
;
1115 WorkItem
->DeviceInformation
= DeviceInformation
;
1116 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1117 if (!WorkItem
->WorkItem
)
1121 return STATUS_INSUFFICIENT_RESOURCES
;
1125 IoQueueWorkItem(WorkItem
->WorkItem
,
1126 MigrateRemoteDatabaseWorker
,
1130 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1131 Status
= WorkItem
->Status
;
1133 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1143 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1144 IN BOOLEAN MigrateDatabase
)
1148 BOOLEAN PreviousMode
;
1149 IO_STATUS_BLOCK IoStatusBlock
;
1150 OBJECT_ATTRIBUTES ObjectAttributes
;
1151 UNICODE_STRING DeviceRemoteDatabase
;
1155 /* Get database name */
1156 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1157 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1158 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1159 if (!DeviceRemoteDatabase
.Buffer
)
1164 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1165 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1166 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1167 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1170 InitializeObjectAttributes(&ObjectAttributes
,
1171 &DeviceRemoteDatabase
,
1172 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1176 /* Disable hard errors */
1177 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1179 Status
= ZwCreateFile(&Database
,
1180 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1181 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1182 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1186 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1188 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1189 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1193 /* If base it to be migrated and was opened successfully, go ahead */
1194 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1196 MigrateRemoteDatabase(DeviceInformation
, &Database
);
1199 IoSetThreadHardErrorMode(PreviousMode
);
1200 FreePool(DeviceRemoteDatabase
.Buffer
);
1210 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
1213 IN ULONG ValueLength
,
1215 IN PVOID EntryContext
)
1217 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
1218 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
1221 if (ValueLength
>= 0x10000)
1223 return STATUS_SUCCESS
;
1226 /* Allocate the Unique ID */
1227 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
1230 /* Copy data & return */
1231 IntUniqueId
->UniqueIdLength
= ValueLength
;
1232 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
1235 *UniqueId
= IntUniqueId
;
1238 return STATUS_SUCCESS
;
1245 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1246 IN PUNICODE_STRING SymbolicName
,
1247 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
1250 PDEVICE_INFORMATION DeviceInformation
;
1251 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1253 /* Query the unique ID */
1254 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1255 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
1256 QueryTable
[0].Name
= SymbolicName
->Buffer
;
1259 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1264 /* Unique ID found, no need to go farther */
1267 return STATUS_SUCCESS
;
1270 /* Otherwise, find associate device information */
1271 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
1272 if (!NT_SUCCESS(Status
))
1277 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
1280 return STATUS_INSUFFICIENT_RESOURCES
;
1283 /* Return this unique ID (better than nothing) */
1284 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
1285 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
1287 return STATUS_SUCCESS
;
1294 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1295 IN PDATABASE_ENTRY DatabaseEntry
)
1298 PWCHAR SymbolicName
;
1299 PLIST_ENTRY NextEntry
;
1300 UNICODE_STRING SymbolicString
;
1301 PDEVICE_INFORMATION DeviceInformation
;
1303 /* Create symbolic name from database entry */
1304 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
1307 return STATUS_INSUFFICIENT_RESOURCES
;
1310 RtlCopyMemory(SymbolicName
,
1311 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
1312 DatabaseEntry
->SymbolicNameLength
);
1313 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1315 /* Associate the unique ID with the name from remote database */
1316 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1320 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1321 DatabaseEntry
->UniqueIdLength
);
1322 FreePool(SymbolicName
);
1324 /* Reget symbolic name */
1325 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
1326 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
1327 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
1329 /* Find the device using this unique ID */
1330 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1331 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1332 NextEntry
= NextEntry
->Flink
)
1334 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1338 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
1343 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1344 DeviceInformation
->UniqueId
->UniqueId
,
1345 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
1351 /* If found, create a mount point */
1352 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
1354 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
1364 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1365 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1366 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1370 PDATABASE_ENTRY Entry
, NewEntry
;
1371 NTSTATUS Status
= STATUS_SUCCESS
;
1373 /* Open the remote database */
1374 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1380 /* Get all the entries */
1383 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1389 /* Not the correct entry, skip it */
1390 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1392 Offset
+= Entry
->EntrySize
;
1397 /* Not the correct entry, skip it */
1398 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1399 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1400 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1402 Offset
+= Entry
->EntrySize
;
1407 /* Here, we have the correct entry */
1408 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1411 Offset
+= Entry
->EntrySize
;
1416 /* Recreate the entry from the previous one */
1417 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1418 NewEntry
->DatabaseOffset
= Entry
->DatabaseOffset
;
1419 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1420 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1421 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1422 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1423 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1424 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1425 NewEntry
->SymbolicNameLength
);
1426 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1427 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1429 /* Delete old entry */
1430 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1431 if (!NT_SUCCESS(Status
))
1438 /* And replace with new one */
1439 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1442 } while (NT_SUCCESS(Status
));
1444 CloseRemoteDatabase(Database
);
1454 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
1457 IN ULONG ValueLength
,
1459 IN PVOID EntryContext
)
1461 PMOUNTDEV_UNIQUE_ID UniqueId
;
1462 UNICODE_STRING RegistryEntry
;
1464 if (ValueType
!= REG_BINARY
)
1466 return STATUS_SUCCESS
;
1471 /* First ensure we have the correct data */
1472 if (UniqueId
->UniqueIdLength
!= ValueLength
)
1474 return STATUS_SUCCESS
;
1477 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1479 return STATUS_SUCCESS
;
1482 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
1484 /* Then, it's a drive letter, erase it */
1485 if (IsDriveLetter(&RegistryEntry
))
1487 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1492 return STATUS_SUCCESS
;
1499 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1501 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1503 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1504 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
1506 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1518 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
1521 IN ULONG ValueLength
,
1523 IN PVOID EntryContext
)
1525 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
1527 /* Ensure we have correct input */
1528 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
1529 UniqueId
->UniqueIdLength
!= ValueLength
)
1531 return STATUS_SUCCESS
;
1534 /* And then, if unique ID matching, delete entry */
1535 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1537 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1542 return STATUS_SUCCESS
;
1549 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1551 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1553 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1554 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
1556 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,