3 * Copyright (C) 2011-2012 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/mountmgr/mountmgr.c
22 * PURPOSE: Mount Manager - remote/local database handler
23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
31 PWSTR DatabasePath
= L
"\\Registry\\Machine\\System\\MountedDevices";
32 PWSTR OfflinePath
= L
"\\Registry\\Machine\\System\\MountedDevices\\Offline";
34 UNICODE_STRING RemoteDatabase
= RTL_CONSTANT_STRING(L
"\\System Volume Information\\MountPointManagerRemoteDatabase");
35 UNICODE_STRING RemoteDatabaseFile
= RTL_CONSTANT_STRING(L
"\\:$MountMgrRemoteDatabase");
41 GetRemoteDatabaseSize(IN HANDLE Database
)
44 IO_STATUS_BLOCK IoStatusBlock
;
45 FILE_STANDARD_INFORMATION StandardInfo
;
47 /* Just query the size */
48 Status
= ZwQueryInformationFile(Database
,
51 sizeof(FILE_STANDARD_INFORMATION
),
52 FileStandardInformation
);
53 if (NT_SUCCESS(Status
))
55 return StandardInfo
.EndOfFile
.LowPart
;
65 AddRemoteDatabaseEntry(IN HANDLE Database
,
66 IN PDATABASE_ENTRY Entry
)
69 IO_STATUS_BLOCK IoStatusBlock
;
71 /* Get size to append data */
72 Size
.QuadPart
= GetRemoteDatabaseSize(Database
);
74 return ZwWriteFile(Database
, 0, NULL
, NULL
,
75 &IoStatusBlock
, Entry
,
76 Entry
->EntrySize
, &Size
, NULL
);
83 CloseRemoteDatabase(IN HANDLE Database
)
85 return ZwClose(Database
);
92 TruncateRemoteDatabase(IN HANDLE Database
,
96 IO_STATUS_BLOCK IoStatusBlock
;
97 FILE_END_OF_FILE_INFORMATION EndOfFile
;
98 FILE_ALLOCATION_INFORMATION Allocation
;
100 EndOfFile
.EndOfFile
.QuadPart
= NewSize
;
101 Allocation
.AllocationSize
.QuadPart
= NewSize
;
104 Status
= ZwSetInformationFile(Database
,
107 sizeof(FILE_END_OF_FILE_INFORMATION
),
108 FileEndOfFileInformation
);
109 if (NT_SUCCESS(Status
))
111 /* And then, properly set allocation information */
112 Status
= ZwSetInformationFile(Database
,
115 sizeof(FILE_ALLOCATION_INFORMATION
),
116 FileAllocationInformation
);
126 GetRemoteDatabaseEntry(IN HANDLE Database
,
127 IN LONG StartingOffset
)
131 PDATABASE_ENTRY Entry
;
132 LARGE_INTEGER ByteOffset
;
133 IO_STATUS_BLOCK IoStatusBlock
;
135 /* Get the entry at the given position */
136 ByteOffset
.QuadPart
= StartingOffset
;
137 Status
= ZwReadFile(Database
,
146 if (!NT_SUCCESS(Status
))
151 /* If entry doesn't exist, truncate database */
154 TruncateRemoteDatabase(Database
, StartingOffset
);
158 /* Allocate the entry */
159 Entry
= AllocatePool(EntrySize
);
165 /* Effectively read the entry */
166 Status
= ZwReadFile(Database
,
175 /* If it fails or returns inconsistent data, drop it (= truncate) */
176 if (!NT_SUCCESS(Status
) ||
177 (IoStatusBlock
.Information
!= EntrySize
) ||
178 (EntrySize
< sizeof(DATABASE_ENTRY
)) )
180 TruncateRemoteDatabase(Database
, StartingOffset
);
186 if (MAX(Entry
->SymbolicNameOffset
+ Entry
->SymbolicNameLength
,
187 Entry
->UniqueIdOffset
+ Entry
->UniqueIdLength
) > (LONG
)EntrySize
)
189 TruncateRemoteDatabase(Database
, StartingOffset
);
201 DeleteRemoteDatabaseEntry(IN HANDLE Database
,
202 IN LONG StartingOffset
)
208 PDATABASE_ENTRY Entry
;
209 IO_STATUS_BLOCK IoStatusBlock
;
210 LARGE_INTEGER EndEntriesOffset
;
212 /* First, get database size */
213 DatabaseSize
= GetRemoteDatabaseSize(Database
);
216 return STATUS_INVALID_PARAMETER
;
219 /* Then, get the entry to remove */
220 Entry
= GetRemoteDatabaseEntry(Database
, StartingOffset
);
223 return STATUS_INVALID_PARAMETER
;
226 /* Validate parameters: ensure we won't get negative size */
227 if (Entry
->EntrySize
+ StartingOffset
> DatabaseSize
)
229 /* If we get invalid parameters, truncate the whole database
230 * starting the wrong entry. We can't rely on the rest
233 return TruncateRemoteDatabase(Database
, StartingOffset
);
236 /* Now, get the size of the remaining entries (those after the one to remove) */
237 EndSize
= DatabaseSize
- Entry
->EntrySize
- StartingOffset
;
238 /* Allocate a buffer big enough to hold them */
239 TmpBuffer
= AllocatePool(EndSize
);
243 return STATUS_INSUFFICIENT_RESOURCES
;
246 /* Get the offset of the entry right after the one to delete */
247 EndEntriesOffset
.QuadPart
= Entry
->EntrySize
+ StartingOffset
;
248 /* We don't need the entry any more */
251 /* Read the ending entries */
252 Status
= ZwReadFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
253 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
254 if (!NT_SUCCESS(Status
))
260 /* Ensure nothing went wrong - we don't want to corrupt the DB */
261 if (IoStatusBlock
.Information
!= EndSize
)
264 return STATUS_INVALID_PARAMETER
;
267 /* Remove the entry */
268 Status
= TruncateRemoteDatabase(Database
, StartingOffset
+ EndSize
);
269 if (!NT_SUCCESS(Status
))
275 /* Now, shift the ending entries to erase the entry */
276 EndEntriesOffset
.QuadPart
= StartingOffset
;
277 Status
= ZwWriteFile(Database
, NULL
, NULL
, NULL
, &IoStatusBlock
,
278 TmpBuffer
, EndSize
, &EndEntriesOffset
, NULL
);
290 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName
,
293 IN ULONG ValueLength
,
295 IN PVOID EntryContext
)
297 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
299 UNREFERENCED_PARAMETER(ValueType
);
300 UNREFERENCED_PARAMETER(EntryContext
);
302 /* Ensure it matches, and delete */
303 if ((UniqueId
->UniqueIdLength
== ValueLength
) &&
304 (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) ==
307 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
312 return STATUS_SUCCESS
;
319 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink
,
320 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
322 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
324 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
325 QueryTable
[0].QueryRoutine
= DeleteFromLocalDatabaseRoutine
;
326 QueryTable
[0].Name
= SymbolicLink
->Buffer
;
328 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
339 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
342 LARGE_INTEGER Timeout
;
344 /* Wait for 7 minutes */
345 Timeout
.QuadPart
= 0xFA0A1F00;
346 Status
= KeWaitForSingleObject(&(DeviceExtension
->RemoteDatabaseLock
), Executive
, KernelMode
, FALSE
, &Timeout
);
347 if (Status
!= STATUS_TIMEOUT
)
352 return STATUS_IO_TIMEOUT
;
359 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension
)
361 KeReleaseSemaphore(&(DeviceExtension
->RemoteDatabaseLock
), IO_NO_INCREMENT
, 1, FALSE
);
366 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter
)
368 UNREFERENCED_PARAMETER(Parameter
);
377 WorkerThread(IN PDEVICE_OBJECT DeviceObject
,
386 LARGE_INTEGER Timeout
;
387 PRECONCILE_WORK_ITEM WorkItem
;
388 PDEVICE_EXTENSION DeviceExtension
;
389 OBJECT_ATTRIBUTES ObjectAttributes
;
391 UNREFERENCED_PARAMETER(DeviceObject
);
393 InitializeObjectAttributes(&ObjectAttributes
,
395 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
398 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
399 Timeout
.LowPart
= 0xFFFFFFFF;
400 Timeout
.HighPart
= 0xFF676980;
402 /* Try to wait as long as possible */
403 for (i
= (Unloading
? 999 : 0); i
< 1000; i
++)
405 Status
= ZwOpenEvent(&SafeEvent
, EVENT_ALL_ACCESS
, &ObjectAttributes
);
406 if (NT_SUCCESS(Status
))
411 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &Timeout
);
418 Status
= ZwWaitForSingleObject(SafeEvent
, FALSE
, &Timeout
);
420 while (Status
== STATUS_TIMEOUT
&& !Unloading
);
425 DeviceExtension
= Context
;
427 InterlockedExchange(&(DeviceExtension
->WorkerThreadStatus
), 1);
429 /* Acquire workers lock */
430 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
432 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
434 /* Ensure there are workers */
435 while (!IsListEmpty(&(DeviceExtension
->WorkerQueueListHead
)))
437 /* Unqueue a worker */
438 Entry
= RemoveHeadList(&(DeviceExtension
->WorkerQueueListHead
));
439 WorkItem
= CONTAINING_RECORD(Entry
,
441 WorkerQueueListEntry
);
443 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
446 WorkItem
->WorkerRoutine(WorkItem
->Context
);
448 IoFreeWorkItem(WorkItem
->WorkItem
);
451 if (InterlockedDecrement(&(DeviceExtension
->WorkerReferences
)) == 0)
456 KeWaitForSingleObject(&(DeviceExtension
->WorkerSemaphore
), Executive
, KernelMode
, FALSE
, NULL
);
457 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
459 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
461 InterlockedDecrement(&(DeviceExtension
->WorkerReferences
));
464 KeSetEvent(&UnloadEvent
, IO_NO_INCREMENT
, FALSE
);
471 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension
,
472 IN PRECONCILE_WORK_ITEM WorkItem
,
477 WorkItem
->Context
= Context
;
479 /* When called, lock is already acquired */
481 /* If noone, start to work */
482 if (InterlockedIncrement(&(DeviceExtension
->WorkerReferences
)))
484 IoQueueWorkItem(WorkItem
->WorkItem
, WorkerThread
, DelayedWorkQueue
, DeviceExtension
);
487 /* Otherwise queue worker for delayed execution */
488 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
489 InsertTailList(&(DeviceExtension
->WorkerQueueListHead
),
490 &(WorkItem
->WorkerQueueListEntry
));
491 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
493 KeReleaseSemaphore(&(DeviceExtension
->WorkerSemaphore
), IO_NO_INCREMENT
, 1, FALSE
);
495 return STATUS_SUCCESS
;
502 QueryVolumeName(IN HANDLE RootDirectory
,
503 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation
,
504 IN PUNICODE_STRING FileName OPTIONAL
,
505 OUT PUNICODE_STRING SymbolicName
,
506 OUT PUNICODE_STRING VolumeName
)
511 IO_STATUS_BLOCK IoStatusBlock
;
512 OBJECT_ATTRIBUTES ObjectAttributes
;
513 PFILE_NAME_INFORMATION FileNameInfo
;
514 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
516 UNREFERENCED_PARAMETER(ReparsePointInformation
);
520 InitializeObjectAttributes(&ObjectAttributes
,
528 InitializeObjectAttributes(&ObjectAttributes
,
530 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
536 Status
= ZwOpenFile(&Handle
,
537 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
540 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
541 (FileName
) ? FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
:
542 FILE_OPEN_BY_FILE_ID
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_REPARSE_POINT
);
543 if (!NT_SUCCESS(Status
))
548 /* Get the reparse point data */
549 ReparseDataBuffer
= AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
550 if (!ReparseDataBuffer
)
553 return STATUS_INSUFFICIENT_RESOURCES
;
556 Status
= ZwFsControlFile(Handle
,
561 FSCTL_GET_REPARSE_POINT
,
565 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
566 if (!NT_SUCCESS(Status
))
568 FreePool(ReparseDataBuffer
);
573 /* Check that name can fit in buffer */
574 if (ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
) > SymbolicName
->MaximumLength
)
576 FreePool(ReparseDataBuffer
);
578 return STATUS_BUFFER_TOO_SMALL
;
581 /* Copy symoblic name */
582 SymbolicName
->Length
= ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
583 RtlCopyMemory(SymbolicName
->Buffer
,
584 (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
585 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
),
586 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
588 FreePool(ReparseDataBuffer
);
590 /* Name has to \ terminated */
591 if (SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
594 return STATUS_INVALID_PARAMETER
;
597 /* So that we can delete it, and match mountmgr requirements */
598 SymbolicName
->Length
-= sizeof(WCHAR
);
599 SymbolicName
->Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
601 /* Also ensure it's really a volume name... */
602 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName
))
605 return STATUS_INVALID_PARAMETER
;
608 /* Now prepare to really get the name */
609 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
));
613 return STATUS_INSUFFICIENT_RESOURCES
;
616 Status
= ZwQueryInformationFile(Handle
,
619 sizeof(FILE_NAME_INFORMATION
) + 2 * sizeof(WCHAR
),
620 FileNameInformation
);
621 if (Status
== STATUS_BUFFER_OVERFLOW
)
623 /* As expected... Reallocate with proper size */
624 NeededLength
= FileNameInfo
->FileNameLength
;
625 FreePool(FileNameInfo
);
627 FileNameInfo
= AllocatePool(sizeof(FILE_NAME_INFORMATION
) + NeededLength
);
631 return STATUS_INSUFFICIENT_RESOURCES
;
635 Status
= ZwQueryInformationFile(Handle
,
638 sizeof(FILE_NAME_INFORMATION
) + NeededLength
,
639 FileNameInformation
);
644 if (!NT_SUCCESS(Status
))
649 /* Return the volume name */
650 VolumeName
->Length
= (USHORT
)FileNameInfo
->FileNameLength
;
651 VolumeName
->MaximumLength
= (USHORT
)FileNameInfo
->FileNameLength
+ sizeof(WCHAR
);
652 VolumeName
->Buffer
= AllocatePool(VolumeName
->MaximumLength
);
653 if (!VolumeName
->Buffer
)
655 return STATUS_INSUFFICIENT_RESOURCES
;
658 RtlCopyMemory(VolumeName
->Buffer
, FileNameInfo
->FileName
, FileNameInfo
->FileNameLength
);
659 VolumeName
->Buffer
[FileNameInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
661 FreePool(FileNameInfo
);
663 return STATUS_SUCCESS
;
670 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension
,
671 IN PDEVICE_INFORMATION DeviceInformation
)
676 IO_STATUS_BLOCK IoStatusBlock
;
677 OBJECT_ATTRIBUTES ObjectAttributes
;
678 PDEVICE_INFORMATION VolumeDeviceInformation
;
679 WCHAR FileNameBuffer
[0x8], SymbolicNameBuffer
[0x64];
680 UNICODE_STRING ReparseFile
, FileName
, SymbolicName
, VolumeName
;
681 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation
, SavedReparsePointInformation
;
683 /* Removable devices don't have remote database on them */
684 if (DeviceInformation
->Removable
)
689 /* Prepare a string with reparse point index */
690 ReparseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ ReparseIndex
.Length
;
691 ReparseFile
.MaximumLength
= ReparseFile
.Length
+ sizeof(UNICODE_NULL
);
692 ReparseFile
.Buffer
= AllocatePool(ReparseFile
.MaximumLength
);
693 if (!ReparseFile
.Buffer
)
698 RtlCopyMemory(ReparseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
,
699 DeviceInformation
->DeviceName
.Length
);
700 RtlCopyMemory((PVOID
)((ULONG_PTR
)ReparseFile
.Buffer
+ DeviceInformation
->DeviceName
.Length
),
701 ReparseFile
.Buffer
, ReparseFile
.Length
);
702 ReparseFile
.Buffer
[ReparseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
704 InitializeObjectAttributes(&ObjectAttributes
,
706 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
710 /* Open reparse point */
711 Status
= ZwOpenFile(&Handle
,
715 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
716 FILE_SYNCHRONOUS_IO_ALERT
| FILE_OPEN_REPARSE_POINT
);
717 FreePool(ReparseFile
.Buffer
);
718 if (!NT_SUCCESS(Status
))
720 DeviceInformation
->NoDatabase
= FALSE
;
724 /* Query reparse point information
725 * We only pay attention to mout point
727 RtlZeroMemory(FileNameBuffer
, sizeof(FileNameBuffer
));
728 FileName
.Buffer
= FileNameBuffer
;
729 FileName
.Length
= sizeof(FileNameBuffer
);
730 FileName
.MaximumLength
= sizeof(FileNameBuffer
);
731 ((PULONG
)FileNameBuffer
)[0] = IO_REPARSE_TAG_MOUNT_POINT
;
732 Status
= ZwQueryDirectoryFile(Handle
,
737 &ReparsePointInformation
,
738 sizeof(FILE_REPARSE_POINT_INFORMATION
),
739 FileReparsePointInformation
,
743 if (!NT_SUCCESS(Status
))
751 /* Query mount points */
754 SymbolicName
.Length
= 0;
755 SymbolicName
.MaximumLength
= sizeof(SymbolicNameBuffer
);
756 SymbolicName
.Buffer
= SymbolicNameBuffer
;
757 RtlCopyMemory(&SavedReparsePointInformation
, &ReparsePointInformation
, sizeof(FILE_REPARSE_POINT_INFORMATION
));
759 Status
= ZwQueryDirectoryFile(Handle
,
764 &ReparsePointInformation
,
765 sizeof(FILE_REPARSE_POINT_INFORMATION
),
766 FileReparsePointInformation
,
768 (RestartScan
) ? &FileName
: NULL
,
772 if (ReparsePointInformation
.FileReference
== SavedReparsePointInformation
.FileReference
&&
773 ReparsePointInformation
.Tag
== SavedReparsePointInformation
.Tag
)
783 if (!NT_SUCCESS(Status
) || ReparsePointInformation
.Tag
!= IO_REPARSE_TAG_MOUNT_POINT
)
788 /* Get the volume name associated to the mount point */
789 Status
= QueryVolumeName(Handle
,
790 &ReparsePointInformation
,
793 if (!NT_SUCCESS(Status
))
798 FreePool(VolumeName
.Buffer
);
800 /* Get its information */
801 Status
= FindDeviceInfo(DeviceExtension
, &SymbolicName
,
802 FALSE
, &VolumeDeviceInformation
);
803 if (!NT_SUCCESS(Status
))
805 DeviceInformation
->NoDatabase
= TRUE
;
809 /* If notification are enabled, mark it online */
810 if (!DeviceInformation
->SkipNotifications
)
812 PostOnlineNotification(DeviceExtension
, &VolumeDeviceInformation
->SymbolicName
);
823 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension
,
824 IN PDEVICE_INFORMATION DeviceInformation
)
826 PRECONCILE_WORK_ITEM WorkItem
;
828 /* Removable devices don't have remote database */
829 if (DeviceInformation
->Removable
)
834 /* Allocate a work item */
835 WorkItem
= AllocatePool(sizeof(RECONCILE_WORK_ITEM
));
841 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
842 if (!WorkItem
->WorkItem
)
849 WorkItem
->WorkerRoutine
= ReconcileThisDatabaseWithMasterWorker
;
850 WorkItem
->DeviceExtension
= DeviceExtension
;
851 WorkItem
->DeviceInformation
= DeviceInformation
;
852 QueueWorkItem(DeviceExtension
, WorkItem
, &(WorkItem
->DeviceExtension
));
854 /* If there's no automount, and automatic letters
855 * all volumes to find those online and notify there presence
857 if (DeviceExtension
->WorkerThreadStatus
== 0 &&
858 DeviceExtension
->AutomaticDriveLetter
== 1 &&
859 DeviceExtension
->NoAutoMount
== FALSE
)
861 OnlineMountedVolumes(DeviceExtension
, DeviceInformation
);
869 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension
)
871 PLIST_ENTRY NextEntry
;
872 PDEVICE_INFORMATION DeviceInformation
;
874 /* Browse all the devices */
875 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
876 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
877 NextEntry
= NextEntry
->Flink
)
879 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
882 /* If it's not removable, then, it might have a database to sync */
883 if (!DeviceInformation
->Removable
)
885 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
895 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject
,
902 LARGE_INTEGER ByteOffset
;
903 PMIGRATE_WORK_ITEM WorkItem
;
904 IO_STATUS_BLOCK IoStatusBlock
;
905 HANDLE Migrate
= 0, Database
= 0;
906 PDEVICE_INFORMATION DeviceInformation
;
907 BOOLEAN PreviousMode
, Complete
= FALSE
;
908 UNICODE_STRING DatabaseName
, DatabaseFile
;
909 OBJECT_ATTRIBUTES ObjectAttributes
, MigrateAttributes
;
910 #define TEMP_BUFFER_SIZE 0x200
912 UNREFERENCED_PARAMETER(DeviceObject
);
914 /* Extract context */
916 DeviceInformation
= WorkItem
->DeviceInformation
;
918 /* Reconstruct appropriate string */
919 DatabaseName
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
920 DatabaseName
.MaximumLength
= DatabaseName
.Length
+ sizeof(WCHAR
);
922 DatabaseFile
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabaseFile
.Length
;
923 DatabaseFile
.MaximumLength
= DatabaseFile
.Length
+ sizeof(WCHAR
);
925 DatabaseName
.Buffer
= AllocatePool(DatabaseName
.MaximumLength
);
926 DatabaseFile
.Buffer
= AllocatePool(DatabaseFile
.MaximumLength
);
927 /* Allocate buffer that will be used to swap contents */
928 TmpBuffer
= AllocatePool(TEMP_BUFFER_SIZE
);
929 if (!DatabaseName
.Buffer
|| !DatabaseFile
.Buffer
|| !TmpBuffer
)
931 Status
= STATUS_INSUFFICIENT_RESOURCES
;
935 /* Create the required folder (in which the database will be stored
936 * \System Volume Information at root of the volume
938 Status
= RtlCreateSystemVolumeInformationFolder(&(DeviceInformation
->DeviceName
));
939 if (!NT_SUCCESS(Status
))
944 /* Finish initating strings */
945 RtlCopyMemory(DatabaseName
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
946 RtlCopyMemory(DatabaseFile
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
947 RtlCopyMemory(DatabaseName
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
948 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
949 RtlCopyMemory(DatabaseFile
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
950 RemoteDatabaseFile
.Buffer
, RemoteDatabaseFile
.Length
);
951 DatabaseName
.Buffer
[DatabaseName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
952 DatabaseFile
.Buffer
[DatabaseFile
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
954 /* Create database */
955 InitializeObjectAttributes(&ObjectAttributes
,
957 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
961 Status
= ZwCreateFile(&Database
,
962 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
963 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
964 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
968 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
971 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
974 if (!NT_SUCCESS(Status
))
980 InitializeObjectAttributes(&MigrateAttributes
,
982 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
986 /* Disable hard errors and open the database that will be copied */
987 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
988 Status
= ZwCreateFile(&Migrate
,
989 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
990 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
991 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
995 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
998 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1001 IoSetThreadHardErrorMode(PreviousMode
);
1002 if (!NT_SUCCESS(Status
))
1006 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1008 Status
= STATUS_SUCCESS
;
1011 if (!NT_SUCCESS(Status
) || Complete
)
1016 ByteOffset
.QuadPart
= 0LL;
1017 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1018 /* Now, loop as long it's possible */
1019 while (Status
== STATUS_SUCCESS
)
1021 /* Read data from existing database */
1022 Status
= ZwReadFile(Migrate
,
1031 if (!NT_SUCCESS(Status
))
1036 /* And write them into new database */
1037 Length
= (ULONG
)IoStatusBlock
.Information
;
1038 Status
= ZwWriteFile(Database
,
1047 ByteOffset
.QuadPart
+= Length
;
1049 IoSetThreadHardErrorMode(PreviousMode
);
1051 /* Delete old databse if it was well copied */
1052 if (Status
== STATUS_END_OF_FILE
)
1055 Status
= ZwSetInformationFile(Migrate
,
1058 sizeof(Disposition
),
1059 FileDispositionInformation
);
1062 /* Migration is over */
1067 FreePool(TmpBuffer
);
1070 if (DatabaseFile
.Buffer
)
1072 FreePool(DatabaseFile
.Buffer
);
1075 if (DatabaseName
.Buffer
)
1077 FreePool(DatabaseName
.Buffer
);
1085 if (NT_SUCCESS(Status
))
1087 DeviceInformation
->Migrated
= 1;
1094 IoFreeWorkItem(WorkItem
->WorkItem
);
1096 WorkItem
->WorkItem
= NULL
;
1097 WorkItem
->Status
= Status
;
1098 WorkItem
->Database
= Database
;
1100 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
1101 #undef TEMP_BUFFER_SIZE
1108 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1109 IN OUT PHANDLE Database
)
1113 PMIGRATE_WORK_ITEM WorkItem
;
1115 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1117 /* Allocate a work item dedicated to migration */
1118 WorkItem
= AllocatePool(sizeof(MIGRATE_WORK_ITEM
));
1122 return STATUS_INSUFFICIENT_RESOURCES
;
1125 RtlZeroMemory(WorkItem
, sizeof(MIGRATE_WORK_ITEM
));
1126 WorkItem
->Event
= &Event
;
1127 WorkItem
->DeviceInformation
= DeviceInformation
;
1128 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceInformation
->DeviceExtension
->DeviceObject
);
1129 if (!WorkItem
->WorkItem
)
1133 return STATUS_INSUFFICIENT_RESOURCES
;
1137 IoQueueWorkItem(WorkItem
->WorkItem
,
1138 MigrateRemoteDatabaseWorker
,
1142 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1143 Status
= WorkItem
->Status
;
1145 *Database
= (NT_SUCCESS(Status
) ? WorkItem
->Database
: 0);
1155 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation
,
1156 IN BOOLEAN MigrateDatabase
)
1160 BOOLEAN PreviousMode
;
1161 IO_STATUS_BLOCK IoStatusBlock
;
1162 OBJECT_ATTRIBUTES ObjectAttributes
;
1163 UNICODE_STRING DeviceRemoteDatabase
;
1167 /* Get database name */
1168 DeviceRemoteDatabase
.Length
= DeviceInformation
->DeviceName
.Length
+ RemoteDatabase
.Length
;
1169 DeviceRemoteDatabase
.MaximumLength
= DeviceRemoteDatabase
.Length
+ sizeof(WCHAR
);
1170 DeviceRemoteDatabase
.Buffer
= AllocatePool(DeviceRemoteDatabase
.MaximumLength
);
1171 if (!DeviceRemoteDatabase
.Buffer
)
1176 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
, DeviceInformation
->DeviceName
.Buffer
, DeviceInformation
->DeviceName
.Length
);
1177 RtlCopyMemory(DeviceRemoteDatabase
.Buffer
+ (DeviceInformation
->DeviceName
.Length
/ sizeof(WCHAR
)),
1178 RemoteDatabase
.Buffer
, RemoteDatabase
.Length
);
1179 DeviceRemoteDatabase
.Buffer
[DeviceRemoteDatabase
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1182 InitializeObjectAttributes(&ObjectAttributes
,
1183 &DeviceRemoteDatabase
,
1184 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1188 /* Disable hard errors */
1189 PreviousMode
= IoSetThreadHardErrorMode(FALSE
);
1191 Status
= ZwCreateFile(&Database
,
1192 SYNCHRONIZE
| READ_CONTROL
| FILE_WRITE_ATTRIBUTES
|
1193 FILE_READ_ATTRIBUTES
| FILE_WRITE_PROPERTIES
| FILE_READ_PROPERTIES
|
1194 FILE_APPEND_DATA
| FILE_WRITE_DATA
| FILE_READ_DATA
,
1198 FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
1200 (!MigrateDatabase
|| DeviceInformation
->Migrated
== 0) ? FILE_OPEN_IF
: FILE_OPEN
,
1201 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_ALERT
,
1205 /* If base it to be migrated and was opened successfully, go ahead */
1206 if (MigrateDatabase
&& NT_SUCCESS(Status
))
1208 MigrateRemoteDatabase(DeviceInformation
, &Database
);
1211 IoSetThreadHardErrorMode(PreviousMode
);
1212 FreePool(DeviceRemoteDatabase
.Buffer
);
1222 QueryUniqueIdQueryRoutine(IN PWSTR ValueName
,
1225 IN ULONG ValueLength
,
1227 IN PVOID EntryContext
)
1229 PMOUNTDEV_UNIQUE_ID IntUniqueId
;
1230 PMOUNTDEV_UNIQUE_ID
* UniqueId
;
1232 UNREFERENCED_PARAMETER(ValueName
);
1233 UNREFERENCED_PARAMETER(ValueType
);
1234 UNREFERENCED_PARAMETER(EntryContext
);
1237 if (ValueLength
>= 0x10000)
1239 return STATUS_SUCCESS
;
1242 /* Allocate the Unique ID */
1243 IntUniqueId
= AllocatePool(sizeof(UniqueId
) + ValueLength
);
1246 /* Copy data & return */
1247 IntUniqueId
->UniqueIdLength
= (USHORT
)ValueLength
;
1248 RtlCopyMemory(&(IntUniqueId
->UniqueId
), ValueData
, ValueLength
);
1251 *UniqueId
= IntUniqueId
;
1254 return STATUS_SUCCESS
;
1261 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1262 IN PUNICODE_STRING SymbolicName
,
1263 OUT PMOUNTDEV_UNIQUE_ID
* UniqueId
)
1266 PDEVICE_INFORMATION DeviceInformation
;
1267 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1269 /* Query the unique ID */
1270 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1271 QueryTable
[0].QueryRoutine
= QueryUniqueIdQueryRoutine
;
1272 QueryTable
[0].Name
= SymbolicName
->Buffer
;
1275 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1280 /* Unique ID found, no need to go farther */
1283 return STATUS_SUCCESS
;
1286 /* Otherwise, find associate device information */
1287 Status
= FindDeviceInfo(DeviceExtension
, SymbolicName
, FALSE
, &DeviceInformation
);
1288 if (!NT_SUCCESS(Status
))
1293 *UniqueId
= AllocatePool(DeviceInformation
->UniqueId
->UniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
1296 return STATUS_INSUFFICIENT_RESOURCES
;
1299 /* Return this unique ID (better than nothing) */
1300 (*UniqueId
)->UniqueIdLength
= DeviceInformation
->UniqueId
->UniqueIdLength
;
1301 RtlCopyMemory(&((*UniqueId
)->UniqueId
), &(DeviceInformation
->UniqueId
->UniqueId
), (*UniqueId
)->UniqueIdLength
);
1303 return STATUS_SUCCESS
;
1310 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension
,
1311 IN PDATABASE_ENTRY DatabaseEntry
)
1314 PWCHAR SymbolicName
;
1315 PLIST_ENTRY NextEntry
;
1316 UNICODE_STRING SymbolicString
;
1317 PDEVICE_INFORMATION DeviceInformation
;
1319 /* Create symbolic name from database entry */
1320 SymbolicName
= AllocatePool(DatabaseEntry
->SymbolicNameLength
+ sizeof(WCHAR
));
1323 return STATUS_INSUFFICIENT_RESOURCES
;
1326 RtlCopyMemory(SymbolicName
,
1327 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
),
1328 DatabaseEntry
->SymbolicNameLength
);
1329 SymbolicName
[DatabaseEntry
->SymbolicNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1331 /* Associate the unique ID with the name from remote database */
1332 Status
= RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1336 (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1337 DatabaseEntry
->UniqueIdLength
);
1338 FreePool(SymbolicName
);
1340 /* Reget symbolic name */
1341 SymbolicString
.Length
= DatabaseEntry
->SymbolicNameLength
;
1342 SymbolicString
.MaximumLength
= DatabaseEntry
->SymbolicNameLength
;
1343 SymbolicString
.Buffer
= (PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->SymbolicNameOffset
);
1345 /* Find the device using this unique ID */
1346 for (NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
1347 NextEntry
!= &(DeviceExtension
->DeviceListHead
);
1348 NextEntry
= NextEntry
->Flink
)
1350 DeviceInformation
= CONTAINING_RECORD(NextEntry
,
1354 if (DeviceInformation
->UniqueId
->UniqueIdLength
!= DatabaseEntry
->UniqueIdLength
)
1359 if (RtlCompareMemory((PVOID
)((ULONG_PTR
)DatabaseEntry
+ DatabaseEntry
->UniqueIdOffset
),
1360 DeviceInformation
->UniqueId
->UniqueId
,
1361 DatabaseEntry
->UniqueIdLength
) == DatabaseEntry
->UniqueIdLength
)
1367 /* If found, create a mount point */
1368 if (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
1370 MountMgrCreatePointWorker(DeviceExtension
, &SymbolicString
, &(DeviceInformation
->DeviceName
));
1380 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation
,
1381 IN PMOUNTDEV_UNIQUE_ID OldUniqueId
,
1382 IN PMOUNTDEV_UNIQUE_ID NewUniqueId
)
1386 PDATABASE_ENTRY Entry
, NewEntry
;
1387 NTSTATUS Status
= STATUS_SUCCESS
;
1389 /* Open the remote database */
1390 Database
= OpenRemoteDatabase(DeviceInformation
, FALSE
);
1396 /* Get all the entries */
1399 Entry
= GetRemoteDatabaseEntry(Database
, Offset
);
1405 /* Not the correct entry, skip it */
1406 if (Entry
->UniqueIdLength
!= OldUniqueId
->UniqueIdLength
)
1408 Offset
+= Entry
->EntrySize
;
1413 /* Not the correct entry, skip it */
1414 if (RtlCompareMemory(OldUniqueId
->UniqueId
,
1415 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->UniqueIdOffset
),
1416 Entry
->UniqueIdLength
) != Entry
->UniqueIdLength
)
1418 Offset
+= Entry
->EntrySize
;
1423 /* Here, we have the correct entry */
1424 NewEntry
= AllocatePool(Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
);
1427 Offset
+= Entry
->EntrySize
;
1432 /* Recreate the entry from the previous one */
1433 NewEntry
->EntrySize
= Entry
->EntrySize
+ NewUniqueId
->UniqueIdLength
- OldUniqueId
->UniqueIdLength
;
1434 NewEntry
->DatabaseOffset
= Entry
->DatabaseOffset
;
1435 NewEntry
->SymbolicNameOffset
= sizeof(DATABASE_ENTRY
);
1436 NewEntry
->SymbolicNameLength
= Entry
->SymbolicNameLength
;
1437 NewEntry
->UniqueIdOffset
= Entry
->SymbolicNameLength
+ sizeof(DATABASE_ENTRY
);
1438 NewEntry
->UniqueIdLength
= NewUniqueId
->UniqueIdLength
;
1439 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->SymbolicNameOffset
),
1440 (PVOID
)((ULONG_PTR
)Entry
+ Entry
->SymbolicNameOffset
),
1441 NewEntry
->SymbolicNameLength
);
1442 RtlCopyMemory((PVOID
)((ULONG_PTR
)NewEntry
+ NewEntry
->UniqueIdOffset
),
1443 NewUniqueId
->UniqueId
, NewEntry
->UniqueIdLength
);
1445 /* Delete old entry */
1446 Status
= DeleteRemoteDatabaseEntry(Database
, Offset
);
1447 if (!NT_SUCCESS(Status
))
1454 /* And replace with new one */
1455 Status
= AddRemoteDatabaseEntry(Database
, NewEntry
);
1458 } while (NT_SUCCESS(Status
));
1460 CloseRemoteDatabase(Database
);
1470 DeleteDriveLetterRoutine(IN PWSTR ValueName
,
1473 IN ULONG ValueLength
,
1475 IN PVOID EntryContext
)
1477 PMOUNTDEV_UNIQUE_ID UniqueId
;
1478 UNICODE_STRING RegistryEntry
;
1480 UNREFERENCED_PARAMETER(EntryContext
);
1482 if (ValueType
!= REG_BINARY
)
1484 return STATUS_SUCCESS
;
1489 /* First ensure we have the correct data */
1490 if (UniqueId
->UniqueIdLength
!= ValueLength
)
1492 return STATUS_SUCCESS
;
1495 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1497 return STATUS_SUCCESS
;
1500 RtlInitUnicodeString(&RegistryEntry
, ValueName
);
1502 /* Then, it's a drive letter, erase it */
1503 if (IsDriveLetter(&RegistryEntry
))
1505 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1510 return STATUS_SUCCESS
;
1517 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1519 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1521 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1522 QueryTable
[0].QueryRoutine
= DeleteDriveLetterRoutine
;
1524 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
1536 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName
,
1539 IN ULONG ValueLength
,
1541 IN PVOID EntryContext
)
1543 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
1545 UNREFERENCED_PARAMETER(EntryContext
);
1547 /* Ensure we have correct input */
1548 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
1549 UniqueId
->UniqueIdLength
!= ValueLength
)
1551 return STATUS_SUCCESS
;
1554 /* And then, if unique ID matching, delete entry */
1555 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
1557 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
1562 return STATUS_SUCCESS
;
1569 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId
)
1571 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
1573 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
1574 QueryTable
[0].QueryRoutine
= DeleteNoDriveLetterEntryRoutine
;
1576 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,