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 UNREFERENCED_PARAMETER(ValueType
);
302 UNREFERENCED_PARAMETER(EntryContext
);
304 /* Ensure it matches, and delete */
305 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
306 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
309 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
314 return STATUS_SUCCESS
;
321 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
322 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
324 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
326 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
327 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
328 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
330 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
341 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
344 LARGE_INTEGER Timeout
;
346 /* Wait for 7 minutes */
347 Timeout
.QuadPart
= 0xFA0A1F00;
348 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
349 if (Status
!= STATUS_TIMEOUT
)
354 return STATUS_IO_TIMEOUT
;
361 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
363 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
368 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
370 UNREFERENCED_PARAMETER(Parameter
);
379 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
388 LARGE_INTEGER Timeout
;
389 PRECONCILE_WORK_ITEM WorkItem
;
390 PDEVICE_EXTENSION DeviceExtension
;
391 OBJECT_ATTRIBUTES ObjectAttributes
;
393 UNREFERENCED_PARAMETER(DeviceObject
);
395 InitializeObjectAttributes(&ObjectAttributes
,
397 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
400 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
401 Timeout
.LowPart
= 0xFFFFFFFF;
402 Timeout
.HighPart
= 0xFF676980;
404 /* Try to wait as long as possible */
405 for (i
= (Unloading
? 999 : 0); i
< 1000; i
++)
407 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
408 if (NT_SUCCESS(Status
))
413 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
420 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
422 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
427 DeviceExtension
= Context
;
429 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
431 /* Acquire workers lock */
432 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
434 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
436 /* Ensure there are workers */
437 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
439 /* Unqueue a worker */
440 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
441 WorkItem
= CONTAINING_RECORD(Entry
,
443 WorkerQueueListEntry
);
445 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
448 WorkItem
->WorkerRoutine(WorkItem
->Context
);
450 IoFreeWorkItem(WorkItem
->WorkItem
);
453 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) == 0)
458 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
459 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
461 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
463 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
466 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
473 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
474 IN PRECONCILE_WORK_ITEM WorkItem
,
479 WorkItem
->Context
= Context
;
481 /* When called, lock is already acquired */
483 /* If noone, start to work */
484 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
486 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
489 /* Otherwise queue worker for delayed execution */
490 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
491 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
492 &(WorkItem
->WorkerQueueListEntry
));
493 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
495 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
497 return STATUS_SUCCESS
;
504 QueryVolumeName(IN HANDLE RootDirectory
,
505 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation
,
506 IN PUNICODE_STRING FileName OPTIONAL
,
507 OUT PUNICODE_STRING SymbolicName
,
508 OUT PUNICODE_STRING VolumeName
)
513 IO_STATUS_BLOCK IoStatusBlock
;
514 OBJECT_ATTRIBUTES ObjectAttributes
;
515 PFILE_NAME_INFORMATION FileNameInfo
;
516 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
518 UNREFERENCED_PARAMETER(ReparsePointInformation
);
522 InitializeObjectAttributes(&ObjectAttributes
,
530 InitializeObjectAttributes(&ObjectAttributes
,
532 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
538 Status
= ZwOpenFile(&Handle
,
539 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
542 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
543 (FileName
) ? FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
:
544 FILE_OPEN_BY_FILE_ID
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
);
545 if (!NT_SUCCESS(Status
))
550 /* Get the reparse point data */
551 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
552 if (!ReparseDataBuffer
)
555 return STATUS_INSUFFICIENT_RESOURCES
;
558 Status
= ZwFsControlFile(Handle
,
563 FSCTL_GET_REPARSE_POINT
,
567 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
568 if (!NT_SUCCESS(Status
))
570 FreePool(ReparseDataBuffer
);
575 /* Check that name can fit in buffer */
576 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
578 FreePool(ReparseDataBuffer
);
580 return STATUS_BUFFER_TOO_SMALL
;
583 /* Copy symoblic name */
584 SymbolicName
->Length
= ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
585 RtlCopyMemory(SymbolicName
->Buffer
,
586 (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
587 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
),
588 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
590 FreePool(ReparseDataBuffer
);
592 /* Name has to \ terminated */
593 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
596 return STATUS_INVALID_PARAMETER
;
599 /* So that we can delete it, and match mountmgr requirements */
600 SymbolicName
->Length
-= sizeof(WCHAR
);
601 SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
603 /* Also ensure it's really a volume name... */
604 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
607 return STATUS_INVALID_PARAMETER
;
610 /* Now prepare to really get the name */
611 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
615 return STATUS_INSUFFICIENT_RESOURCES
;
618 Status
= ZwQueryInformationFile(Handle
,
621 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
622 FileNameInformation
);
623 if (Status
== STATUS_BUFFER_OVERFLOW
)
625 /* As expected... Reallocate with proper size */
626 NeededLength
= FileNameInfo
->FileNameLength
;
627 FreePool(FileNameInfo
);
629 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
633 return STATUS_INSUFFICIENT_RESOURCES
;
637 Status
= ZwQueryInformationFile(Handle
,
640 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
641 FileNameInformation
);
646 if (!NT_SUCCESS(Status
))
651 /* Return the volume name */
652 VolumeName
->Length
= (USHORT
)FileNameInfo
->FileNameLength
;
653 VolumeName
->MaximumLength
= (USHORT
)FileNameInfo
->FileNameLength
+ sizeof(WCHAR
);
654 VolumeName
->Buffer
= AllocatePool(VolumeName
->MaximumLength
);
655 if (!VolumeName
->Buffer
)
657 return STATUS_INSUFFICIENT_RESOURCES
;
660 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
661 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
663 FreePool(FileNameInfo
);
665 return STATUS_SUCCESS
;
672 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
673 IN PDEVICE_INFORMATION DeviceInformation
)
678 IO_STATUS_BLOCK IoStatusBlock
;
679 OBJECT_ATTRIBUTES ObjectAttributes
;
680 PDEVICE_INFORMATION VolumeDeviceInformation
;
681 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[0x64];
682 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
683 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
685 /* Removable devices don't have remote database on them */
686 if (DeviceInformation
->Removable
)
691 /* Prepare a string with reparse point index */
692 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
693 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
694 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
695 if (!ReparseFile
.Buffer
)
700 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
701 DeviceInformation
->DeviceName
.Length
);
702 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
703 ReparseFile
.Buffer
, ReparseFile
.Length
);
704 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
706 InitializeObjectAttributes(&ObjectAttributes
,
708 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
712 /* Open reparse point */
713 Status
= ZwOpenFile(&Handle
,
717 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
718 FILE_SYNCHRONOUS_IO_ALERT
| FILE_OPEN_REPARSE_POINT
);
719 FreePool(ReparseFile
.Buffer
);
720 if (!NT_SUCCESS(Status
))
722 DeviceInformation
->NoDatabase
= FALSE
;
726 /* Query reparse point information
727 * We only pay attention to mout point
729 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
730 FileName
.Buffer
= FileNameBuffer
;
731 FileName
.Length
= sizeof(FileNameBuffer
);
732 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
733 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
734 Status
= ZwQueryDirectoryFile(Handle
,
739 &ReparsePointInformation
,
740 sizeof(FILE_REPARSE_POINT_INFORMATION
),
741 FileReparsePointInformation
,
745 if (!NT_SUCCESS(Status
))
751 /* Query mount points */
755 SymbolicName
.Length
= 0;
756 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
757 SymbolicName
.Buffer
= SymbolicNameBuffer
;
758 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
760 Status
= ZwQueryDirectoryFile(Handle
,
765 &ReparsePointInformation
,
766 sizeof(FILE_REPARSE_POINT_INFORMATION
),
767 FileReparsePointInformation
,
769 (RestartScan
) ? &FileName
: NULL
,
773 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
774 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
785 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
791 /* Get the volume name associated to the mount point */
792 Status
= QueryVolumeName(Handle
,
793 &ReparsePointInformation
,
796 if (!NT_SUCCESS(Status
))
801 FreePool(VolumeName
.Buffer
);
803 /* Get its information */
804 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
805 FALSE
, &VolumeDeviceInformation
);
806 if (!NT_SUCCESS(Status
))
808 DeviceInformation
->NoDatabase
= TRUE
;
812 /* If notification are enabled, mark it online */
813 if (!DeviceInformation
->SkipNotifications
)
815 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
824 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
825 IN PDEVICE_INFORMATION DeviceInformation
)
827 PRECONCILE_WORK_ITEM WorkItem
;
829 /* Removable devices don't have remote database */
830 if (DeviceInformation
->Removable
)
835 /* Allocate a work item */
836 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
842 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
843 if (!WorkItem
->WorkItem
)
850 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
851 WorkItem
->DeviceExtension
= DeviceExtension
;
852 WorkItem
->DeviceInformation
= DeviceInformation
;
853 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
855 /* If there's no automount, and automatic letters
856 * all volumes to find those online and notify there presence
858 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
859 DeviceExtension
->AutomaticDriveLetter
== 1 &&
860 DeviceExtension
->NoAutoMount
== FALSE
)
862 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
870 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
872 PLIST_ENTRY NextEntry
;
873 PDEVICE_INFORMATION DeviceInformation
;
875 /* Browse all the devices */
876 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
877 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
878 NextEntry
= NextEntry
->Flink
)
880 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
883 /* If it's not removable, then, it might have a database to sync */
884 if (!DeviceInformation
->Removable
)
886 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
896 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
903 LARGE_INTEGER ByteOffset
;
904 PMIGRATE_WORK_ITEM WorkItem
;
905 IO_STATUS_BLOCK IoStatusBlock
;
906 HANDLE Migrate
= 0, Database
= 0;
907 PDEVICE_INFORMATION DeviceInformation
;
908 BOOLEAN PreviousMode
, Complete
= FALSE
;
909 UNICODE_STRING DatabaseName
, DatabaseFile
;
910 OBJECT_ATTRIBUTES ObjectAttributes
, MigrateAttributes
;
911 #define TEMP_BUFFER_SIZE 0x200
913 UNREFERENCED_PARAMETER(DeviceObject
);
915 /* Extract context */
917 DeviceInformation
= WorkItem
->DeviceInformation
;
919 /* Reconstruct appropriate string */
920 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
921 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
923 DatabaseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabaseFile
.Length
;
924 DatabaseFile
.MaximumLength
= DatabaseFile
.Length
+ sizeof(WCHAR
);
926 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
927 DatabaseFile
.Buffer
= AllocatePool(DatabaseFile
.MaximumLength
);
928 /* Allocate buffer that will be used to swap contents */
929 TmpBuffer
= AllocatePool(TEMP_BUFFER_SIZE
);
930 if (!DatabaseName
.Buffer
|| !DatabaseFile
.Buffer
|| !TmpBuffer
)
932 Status
= STATUS_INSUFFICIENT_RESOURCES
;
936 /* Create the required folder (in which the database will be stored
937 * \System Volume Information at root of the volume
939 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
940 if (!NT_SUCCESS(Status
))
945 /* Finish initating strings */
946 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
947 RtlCopyMemory(DatabaseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
948 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
949 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
950 RtlCopyMemory(DatabaseFile
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
951 RemoteDatabaseFile
.Buffer
, RemoteDatabaseFile
.Length
);
952 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
953 DatabaseFile
.Buffer
[DatabaseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
955 /* Create database */
956 InitializeObjectAttributes(&ObjectAttributes
,
958 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
962 Status
= ZwCreateFile(&Database
,
963 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
964 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
965 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
969 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
972 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
975 if (!NT_SUCCESS(Status
))
981 InitializeObjectAttributes(&MigrateAttributes
,
983 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
987 /* Disable hard errors and open the database that will be copied */
988 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
989 Status
= ZwCreateFile(&Migrate
,
990 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
991 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
992 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
996 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
999 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1002 IoSetThreadHardErrorMode(PreviousMode
);
1003 if (!NT_SUCCESS(Status
))
1007 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1009 Status
= STATUS_SUCCESS
;
1012 if (!NT_SUCCESS(Status
) || Complete
)
1017 ByteOffset
.QuadPart
= 0LL;
1018 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1019 /* Now, loop as long it's possible */
1020 while (Status
== STATUS_SUCCESS
)
1022 /* Read data from existing database */
1023 Status
= ZwReadFile(Migrate
,
1032 if (!NT_SUCCESS(Status
))
1037 /* And write them into new database */
1038 Length
= (ULONG
)IoStatusBlock
.Information
;
1039 Status
= ZwWriteFile(Database
,
1048 ByteOffset
.QuadPart
+= Length
;
1050 IoSetThreadHardErrorMode(PreviousMode
);
1052 /* Delete old databse if it was well copied */
1053 if (Status
== STATUS_END_OF_FILE
)
1056 Status
= ZwSetInformationFile(Migrate
,
1059 sizeof(Disposition
),
1060 FileDispositionInformation
);
1063 /* Migration is over */
1068 FreePool(TmpBuffer
);
1071 if (DatabaseFile
.Buffer
)
1073 FreePool(DatabaseFile
.Buffer
);
1076 if (DatabaseName
.Buffer
)
1078 FreePool(DatabaseName
.Buffer
);
1086 if (NT_SUCCESS(Status
))
1088 DeviceInformation
->Migrated
= 1;
1095 IoFreeWorkItem(WorkItem
->WorkItem
);
1097 WorkItem
->WorkItem
= NULL
;
1098 WorkItem
->Status
= Status
;
1099 WorkItem
->Database
= Database
;
1101 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1102 #undef TEMP_BUFFER_SIZE
1109 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1110 IN OUT PHANDLE Database
)
1114 PMIGRATE_WORK_ITEM WorkItem
;
1116 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1118 /* Allocate a work item dedicated to migration */
1119 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1123 return STATUS_INSUFFICIENT_RESOURCES
;
1126 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1127 WorkItem
->Event
= &Event
;
1128 WorkItem
->DeviceInformation
= DeviceInformation
;
1129 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1130 if (!WorkItem
->WorkItem
)
1134 return STATUS_INSUFFICIENT_RESOURCES
;
1138 IoQueueWorkItem(WorkItem
->WorkItem
,
1139 MigrateRemoteDatabaseWorker
,
1143 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1144 Status
= WorkItem
->Status
;
1146 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1156 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1157 IN BOOLEAN MigrateDatabase
)
1161 BOOLEAN PreviousMode
;
1162 IO_STATUS_BLOCK IoStatusBlock
;
1163 OBJECT_ATTRIBUTES ObjectAttributes
;
1164 UNICODE_STRING DeviceRemoteDatabase
;
1168 /* Get database name */
1169 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1170 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1171 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1172 if (!DeviceRemoteDatabase
.Buffer
)
1177 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1178 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1179 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1180 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1183 InitializeObjectAttributes(&ObjectAttributes
,
1184 &DeviceRemoteDatabase
,
1185 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1189 /* Disable hard errors */
1190 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1192 Status
= ZwCreateFile(&Database
,
1193 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1194 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1195 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1199 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1201 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1202 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1206 /* If base it to be migrated and was opened successfully, go ahead */
1207 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1209 MigrateRemoteDatabase(DeviceInformation
, &Database
);
1212 IoSetThreadHardErrorMode(PreviousMode
);
1213 FreePool(DeviceRemoteDatabase
.Buffer
);
1223 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
1226 IN ULONG ValueLength
,
1228 IN PVOID EntryContext
)
1230 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
1231 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
1233 UNREFERENCED_PARAMETER(ValueName
);
1234 UNREFERENCED_PARAMETER(ValueType
);
1235 UNREFERENCED_PARAMETER(EntryContext
);
1238 if (ValueLength
>= 0x10000)
1240 return STATUS_SUCCESS
;
1243 /* Allocate the Unique ID */
1244 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
1247 /* Copy data & return */
1248 IntUniqueId
->UniqueIdLength
= (USHORT
)ValueLength
;
1249 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
1252 *UniqueId
= IntUniqueId
;
1255 return STATUS_SUCCESS
;
1262 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1263 IN PUNICODE_STRING SymbolicName
,
1264 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
1267 PDEVICE_INFORMATION DeviceInformation
;
1268 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1270 /* Query the unique ID */
1271 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1272 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
1273 QueryTable
[0].Name
= SymbolicName
->Buffer
;
1276 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1281 /* Unique ID found, no need to go farther */
1284 return STATUS_SUCCESS
;
1287 /* Otherwise, find associate device information */
1288 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
1289 if (!NT_SUCCESS(Status
))
1294 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
1297 return STATUS_INSUFFICIENT_RESOURCES
;
1300 /* Return this unique ID (better than nothing) */
1301 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
1302 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
1304 return STATUS_SUCCESS
;
1311 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1312 IN PDATABASE_ENTRY DatabaseEntry
)
1315 PWCHAR SymbolicName
;
1316 PLIST_ENTRY NextEntry
;
1317 UNICODE_STRING SymbolicString
;
1318 PDEVICE_INFORMATION DeviceInformation
;
1320 /* Create symbolic name from database entry */
1321 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
1324 return STATUS_INSUFFICIENT_RESOURCES
;
1327 RtlCopyMemory(SymbolicName
,
1328 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
1329 DatabaseEntry
->SymbolicNameLength
);
1330 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1332 /* Associate the unique ID with the name from remote database */
1333 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1337 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1338 DatabaseEntry
->UniqueIdLength
);
1339 FreePool(SymbolicName
);
1341 /* Reget symbolic name */
1342 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
1343 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
1344 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
1346 /* Find the device using this unique ID */
1347 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1348 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1349 NextEntry
= NextEntry
->Flink
)
1351 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1355 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
1360 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1361 DeviceInformation
->UniqueId
->UniqueId
,
1362 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
1368 /* If found, create a mount point */
1369 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
1371 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
1381 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1382 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1383 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1387 PDATABASE_ENTRY Entry
, NewEntry
;
1388 NTSTATUS Status
= STATUS_SUCCESS
;
1390 /* Open the remote database */
1391 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1397 /* Get all the entries */
1400 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1406 /* Not the correct entry, skip it */
1407 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1409 Offset
+= Entry
->EntrySize
;
1414 /* Not the correct entry, skip it */
1415 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1416 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1417 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1419 Offset
+= Entry
->EntrySize
;
1424 /* Here, we have the correct entry */
1425 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1428 Offset
+= Entry
->EntrySize
;
1433 /* Recreate the entry from the previous one */
1434 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1435 NewEntry
->DatabaseOffset
= Entry
->DatabaseOffset
;
1436 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1437 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1438 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1439 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1440 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1441 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1442 NewEntry
->SymbolicNameLength
);
1443 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1444 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1446 /* Delete old entry */
1447 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1448 if (!NT_SUCCESS(Status
))
1455 /* And replace with new one */
1456 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1459 } while (NT_SUCCESS(Status
));
1461 CloseRemoteDatabase(Database
);
1471 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
1474 IN ULONG ValueLength
,
1476 IN PVOID EntryContext
)
1478 PMOUNTDEV_UNIQUE_ID UniqueId
;
1479 UNICODE_STRING RegistryEntry
;
1481 UNREFERENCED_PARAMETER(EntryContext
);
1483 if (ValueType
!= REG_BINARY
)
1485 return STATUS_SUCCESS
;
1490 /* First ensure we have the correct data */
1491 if (UniqueId
->UniqueIdLength
!= ValueLength
)
1493 return STATUS_SUCCESS
;
1496 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1498 return STATUS_SUCCESS
;
1501 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
1503 /* Then, it's a drive letter, erase it */
1504 if (IsDriveLetter(&RegistryEntry
))
1506 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1511 return STATUS_SUCCESS
;
1518 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1520 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1522 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1523 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
1525 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1537 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
1540 IN ULONG ValueLength
,
1542 IN PVOID EntryContext
)
1544 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
1546 UNREFERENCED_PARAMETER(EntryContext
);
1548 /* Ensure we have correct input */
1549 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
1550 UniqueId
->UniqueIdLength
!= ValueLength
)
1552 return STATUS_SUCCESS
;
1555 /* And then, if unique ID matching, delete entry */
1556 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1558 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1563 return STATUS_SUCCESS
;
1570 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1572 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1574 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1575 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
1577 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,