[USB]
[reactos.git] / reactos / drivers / filters / mountmgr / database.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2011-2012 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 *
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)
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #include "mntmgr.h"
29
30 #define NDEBUG
31 #include <debug.h>
32
33 PWSTR DatabasePath = L"\\Registry\\Machine\\System\\MountedDevices";
34 PWSTR OfflinePath = L"\\Registry\\Machine\\System\\MountedDevices\\Offline";
35
36 UNICODE_STRING RemoteDatabase = RTL_CONSTANT_STRING(L"\\System Volume Information\\MountPointManagerRemoteDatabase");
37 UNICODE_STRING RemoteDatabaseFile = RTL_CONSTANT_STRING(L"\\:$MountMgrRemoteDatabase");
38
39 /*
40 * @implemented
41 */
42 LONG
43 GetRemoteDatabaseSize(IN HANDLE Database)
44 {
45 NTSTATUS Status;
46 IO_STATUS_BLOCK IoStatusBlock;
47 FILE_STANDARD_INFORMATION StandardInfo;
48
49 /* Just query the size */
50 Status = ZwQueryInformationFile(Database,
51 &IoStatusBlock,
52 &StandardInfo,
53 sizeof(FILE_STANDARD_INFORMATION),
54 FileStandardInformation);
55 if (NT_SUCCESS(Status))
56 {
57 return StandardInfo.EndOfFile.LowPart;
58 }
59
60 return 0;
61 }
62
63 /*
64 * @implemented
65 */
66 NTSTATUS
67 AddRemoteDatabaseEntry(IN HANDLE Database,
68 IN PDATABASE_ENTRY Entry)
69 {
70 LARGE_INTEGER Size;
71 IO_STATUS_BLOCK IoStatusBlock;
72
73 /* Get size to append data */
74 Size.QuadPart = GetRemoteDatabaseSize(Database);
75
76 return ZwWriteFile(Database, 0, NULL, NULL,
77 &IoStatusBlock, Entry,
78 Entry->EntrySize, &Size, NULL);
79 }
80
81 /*
82 * @implemented
83 */
84 NTSTATUS
85 CloseRemoteDatabase(IN HANDLE Database)
86 {
87 return ZwClose(Database);
88 }
89
90 /*
91 * @implemented
92 */
93 NTSTATUS
94 TruncateRemoteDatabase(IN HANDLE Database,
95 IN LONG NewSize)
96 {
97 NTSTATUS Status;
98 IO_STATUS_BLOCK IoStatusBlock;
99 FILE_END_OF_FILE_INFORMATION EndOfFile;
100 FILE_ALLOCATION_INFORMATION Allocation;
101
102 EndOfFile.EndOfFile.QuadPart = NewSize;
103 Allocation.AllocationSize.QuadPart = NewSize;
104
105 /* First set EOF */
106 Status = ZwSetInformationFile(Database,
107 &IoStatusBlock,
108 &EndOfFile,
109 sizeof(FILE_END_OF_FILE_INFORMATION),
110 FileEndOfFileInformation);
111 if (NT_SUCCESS(Status))
112 {
113 /* And then, properly set allocation information */
114 Status = ZwSetInformationFile(Database,
115 &IoStatusBlock,
116 &Allocation,
117 sizeof(FILE_ALLOCATION_INFORMATION),
118 FileAllocationInformation);
119 }
120
121 return Status;
122 }
123
124 /*
125 * @implemented
126 */
127 PDATABASE_ENTRY
128 GetRemoteDatabaseEntry(IN HANDLE Database,
129 IN LONG StartingOffset)
130 {
131 NTSTATUS Status;
132 ULONG EntrySize;
133 PDATABASE_ENTRY Entry;
134 LARGE_INTEGER ByteOffset;
135 IO_STATUS_BLOCK IoStatusBlock;
136
137 /* Get the entry at the given position */
138 ByteOffset.QuadPart = StartingOffset;
139 Status = ZwReadFile(Database,
140 NULL,
141 NULL,
142 NULL,
143 &IoStatusBlock,
144 &EntrySize,
145 sizeof(EntrySize),
146 &ByteOffset,
147 NULL);
148 if (!NT_SUCCESS(Status))
149 {
150 return NULL;
151 }
152
153 /* If entry doesn't exist, truncate database */
154 if (!EntrySize)
155 {
156 TruncateRemoteDatabase(Database, StartingOffset);
157 return NULL;
158 }
159
160 /* Allocate the entry */
161 Entry = AllocatePool(EntrySize);
162 if (!Entry)
163 {
164 return NULL;
165 }
166
167 /* Effectively read the entry */
168 Status = ZwReadFile(Database,
169 NULL,
170 NULL,
171 NULL,
172 &IoStatusBlock,
173 Entry,
174 EntrySize,
175 &ByteOffset,
176 NULL);
177 /* If it fails or returns inconsistent data, drop it (= truncate) */
178 if (!NT_SUCCESS(Status) || IoStatusBlock.Information != EntrySize || EntrySize < sizeof(DATABASE_ENTRY))
179 {
180 TruncateRemoteDatabase(Database, StartingOffset);
181 FreePool(Entry);
182 return NULL;
183 }
184
185 /* Validate entry */
186 if (MAX(Entry->SymbolicNameOffset + Entry->SymbolicNameLength,
187 Entry->UniqueIdOffset + Entry->UniqueIdLength) > EntrySize)
188 {
189 TruncateRemoteDatabase(Database, StartingOffset);
190 FreePool(Entry);
191 return NULL;
192 }
193
194 return Entry;
195 }
196
197 /*
198 * @implemented
199 */
200 NTSTATUS
201 DeleteRemoteDatabaseEntry(IN HANDLE Database,
202 IN LONG StartingOffset)
203 {
204 LONG EndSize;
205 PVOID TmpBuffer;
206 NTSTATUS Status;
207 LONG DatabaseSize;
208 PDATABASE_ENTRY Entry;
209 IO_STATUS_BLOCK IoStatusBlock;
210 LARGE_INTEGER EndEntriesOffset;
211
212 /* First, get database size */
213 DatabaseSize = GetRemoteDatabaseSize(Database);
214 if (!DatabaseSize)
215 {
216 return STATUS_INVALID_PARAMETER;
217 }
218
219 /* Then, get the entry to remove */
220 Entry = GetRemoteDatabaseEntry(Database, StartingOffset);
221 if (!Entry)
222 {
223 return STATUS_INVALID_PARAMETER;
224 }
225
226 /* Validate parameters: ensure we won't get negative size */
227 if (Entry->EntrySize + StartingOffset > DatabaseSize)
228 {
229 /* If we get invalid parameters, truncate the whole database
230 * starting the wrong entry. We can't rely on the rest
231 */
232 FreePool(Entry);
233 return TruncateRemoteDatabase(Database, StartingOffset);
234 }
235
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);
240 if (!TmpBuffer)
241 {
242 FreePool(Entry);
243 return STATUS_INSUFFICIENT_RESOURCES;
244 }
245
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 */
249 FreePool(Entry);
250
251 /* Read the ending entries */
252 Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock,
253 TmpBuffer, EndSize, &EndEntriesOffset, NULL);
254 if (!NT_SUCCESS(Status))
255 {
256 FreePool(TmpBuffer);
257 return Status;
258 }
259
260 /* Ensure nothing went wrong - we don't want to corrupt the DB */
261 if (IoStatusBlock.Information != EndSize)
262 {
263 FreePool(TmpBuffer);
264 return STATUS_INVALID_PARAMETER;
265 }
266
267 /* Remove the entry */
268 Status = TruncateRemoteDatabase(Database, StartingOffset + EndSize);
269 if (!NT_SUCCESS(Status))
270 {
271 FreePool(TmpBuffer);
272 return Status;
273 }
274
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);
279
280 FreePool(TmpBuffer);
281
282 return Status;
283 }
284
285 /*
286 * @implemented
287 */
288 NTSTATUS
289 NTAPI
290 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName,
291 IN ULONG ValueType,
292 IN PVOID ValueData,
293 IN ULONG ValueLength,
294 IN PVOID Context,
295 IN PVOID EntryContext)
296 {
297 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
298
299 /* Ensure it matches, and delete */
300 if ((UniqueId->UniqueIdLength == ValueLength) &&
301 (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) ==
302 ValueLength))
303 {
304 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
305 DatabasePath,
306 ValueName);
307 }
308
309 return STATUS_SUCCESS;
310 }
311
312 /*
313 * @implemented
314 */
315 VOID
316 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink,
317 IN PMOUNTDEV_UNIQUE_ID UniqueId)
318 {
319 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
320
321 RtlZeroMemory(QueryTable, sizeof(QueryTable));
322 QueryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine;
323 QueryTable[0].Name = SymbolicLink->Buffer;
324
325 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
326 DatabasePath,
327 QueryTable,
328 UniqueId,
329 NULL);
330 }
331
332 /*
333 * @implemented
334 */
335 NTSTATUS
336 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
337 {
338 NTSTATUS Status;
339 LARGE_INTEGER Timeout;
340
341 /* Wait for 7 minutes */
342 Timeout.QuadPart = 0xFA0A1F00;
343 Status = KeWaitForSingleObject(&(DeviceExtension->RemoteDatabaseLock), Executive, KernelMode, FALSE, &Timeout);
344 if (Status != STATUS_TIMEOUT)
345 {
346 return Status;
347 }
348
349 return STATUS_IO_TIMEOUT;
350 }
351
352 /*
353 * @implemented
354 */
355 VOID
356 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
357 {
358 KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE);
359 }
360
361 VOID
362 NTAPI
363 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter)
364 {
365 return;
366 }
367
368 /*
369 * @implemented
370 */
371 VOID
372 NTAPI
373 WorkerThread(IN PDEVICE_OBJECT DeviceObject,
374 IN PVOID Context)
375 {
376 ULONG i;
377 KEVENT Event;
378 KIRQL OldIrql;
379 NTSTATUS Status;
380 HANDLE SafeEvent;
381 PLIST_ENTRY Entry;
382 LARGE_INTEGER Timeout;
383 PRECONCILE_WORK_ITEM WorkItem;
384 PDEVICE_EXTENSION DeviceExtension;
385 OBJECT_ATTRIBUTES ObjectAttributes;
386
387 InitializeObjectAttributes(&ObjectAttributes,
388 &SafeVolumes,
389 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
390 NULL,
391 NULL);
392 KeInitializeEvent(&Event, NotificationEvent, FALSE);
393 Timeout.LowPart = 0xFFFFFFFF;
394 Timeout.HighPart = 0xFF676980;
395
396 /* Try to wait as long as possible */
397 for (i = (Unloading ? 999 : 0); i < 1000; i++)
398 {
399 Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes);
400 if (NT_SUCCESS(Status))
401 {
402 break;
403 }
404
405 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
406 }
407
408 if (i < 1000)
409 {
410 do
411 {
412 Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout);
413 }
414 while (Status == STATUS_TIMEOUT && !Unloading);
415
416 ZwClose(SafeEvent);
417 }
418
419 DeviceExtension = Context;
420
421 InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1);
422
423 /* Acquire workers lock */
424 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
425
426 OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
427 /* Ensure there are workers */
428 while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead)))
429 {
430 /* Unqueue a worker */
431 Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead));
432 WorkItem = CONTAINING_RECORD(Entry,
433 RECONCILE_WORK_ITEM,
434 WorkerQueueListEntry);
435
436 KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
437
438 /* Call it */
439 WorkItem->WorkerRoutine(WorkItem->Context);
440
441 IoFreeWorkItem(WorkItem->WorkItem);
442 FreePool(WorkItem);
443
444 if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) == 0)
445 {
446 return;
447 }
448
449 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
450 OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
451 }
452 KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
453
454 InterlockedDecrement(&(DeviceExtension->WorkerReferences));
455
456 /* Reset event */
457 KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE);
458 }
459
460 /*
461 * @implemented
462 */
463 NTSTATUS
464 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension,
465 IN PRECONCILE_WORK_ITEM WorkItem,
466 IN PVOID Context)
467 {
468 KIRQL OldIrql;
469
470 WorkItem->Context = Context;
471
472 /* When called, lock is already acquired */
473
474 /* If noone, start to work */
475 if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)))
476 {
477 IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension);
478 }
479
480 /* Otherwise queue worker for delayed execution */
481 OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
482 InsertTailList(&(DeviceExtension->WorkerQueueListHead),
483 &(WorkItem->WorkerQueueListEntry));
484 KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
485
486 KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE);
487
488 return STATUS_SUCCESS;
489 }
490
491 /*
492 * @implemented
493 */
494 NTSTATUS
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)
500 {
501 HANDLE Handle;
502 NTSTATUS Status;
503 ULONG NeededLength;
504 IO_STATUS_BLOCK IoStatusBlock;
505 OBJECT_ATTRIBUTES ObjectAttributes;
506 PFILE_NAME_INFORMATION FileNameInfo;
507 PREPARSE_DATA_BUFFER ReparseDataBuffer;
508
509 if (!FileName)
510 {
511 InitializeObjectAttributes(&ObjectAttributes,
512 NULL,
513 OBJ_KERNEL_HANDLE,
514 RootDirectory,
515 NULL);
516 }
517 else
518 {
519 InitializeObjectAttributes(&ObjectAttributes,
520 FileName,
521 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
522 NULL,
523 NULL);
524 }
525
526 /* Open volume */
527 Status = ZwOpenFile(&Handle,
528 SYNCHRONIZE | FILE_READ_ATTRIBUTES,
529 &ObjectAttributes,
530 &IoStatusBlock,
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))
535 {
536 return Status;
537 }
538
539 /* Get the reparse point data */
540 ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
541 if (!ReparseDataBuffer)
542 {
543 ZwClose(Handle);
544 return STATUS_INSUFFICIENT_RESOURCES;
545 }
546
547 Status = ZwFsControlFile(Handle,
548 0,
549 NULL,
550 NULL,
551 &IoStatusBlock,
552 FSCTL_GET_REPARSE_POINT,
553 NULL,
554 0,
555 ReparseDataBuffer,
556 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
557 if (!NT_SUCCESS(Status))
558 {
559 FreePool(ReparseDataBuffer);
560 ZwClose(Handle);
561 return Status;
562 }
563
564 /* Check that name can fit in buffer */
565 if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength)
566 {
567 FreePool(ReparseDataBuffer);
568 ZwClose(Handle);
569 return STATUS_BUFFER_TOO_SMALL;
570 }
571
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);
578
579 FreePool(ReparseDataBuffer);
580
581 /* Name has to \ terminated */
582 if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\')
583 {
584 ZwClose(Handle);
585 return STATUS_INVALID_PARAMETER;
586 }
587
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;
591
592 /* Also ensure it's really a volume name... */
593 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName))
594 {
595 ZwClose(Handle);
596 return STATUS_INVALID_PARAMETER;
597 }
598
599 /* Now prepare to really get the name */
600 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR));
601 if (!FileNameInfo)
602 {
603 ZwClose(Handle);
604 return STATUS_INSUFFICIENT_RESOURCES;
605 }
606
607 Status = ZwQueryInformationFile(Handle,
608 &IoStatusBlock,
609 FileNameInfo,
610 sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR),
611 FileNameInformation);
612 if (Status == STATUS_BUFFER_OVERFLOW)
613 {
614 /* As expected... Reallocate with proper size */
615 NeededLength = FileNameInfo->FileNameLength;
616 FreePool(FileNameInfo);
617
618 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength);
619 if (!FileNameInfo)
620 {
621 ZwClose(Handle);
622 return STATUS_INSUFFICIENT_RESOURCES;
623 }
624
625 /* And query name */
626 Status = ZwQueryInformationFile(Handle,
627 &IoStatusBlock,
628 FileNameInfo,
629 sizeof(FILE_NAME_INFORMATION) + NeededLength,
630 FileNameInformation);
631 }
632
633 ZwClose(Handle);
634
635 if (!NT_SUCCESS(Status))
636 {
637 return Status;
638 }
639
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)
645 {
646 return STATUS_INSUFFICIENT_RESOURCES;
647 }
648
649 RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength);
650 VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
651
652 FreePool(FileNameInfo);
653
654 return STATUS_SUCCESS;
655 }
656
657 /*
658 * @implemented
659 */
660 VOID
661 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
662 IN PDEVICE_INFORMATION DeviceInformation)
663 {
664 HANDLE Handle;
665 NTSTATUS Status;
666 BOOLEAN RestartScan;
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;
673
674 /* Removable devices don't have remote database on them */
675 if (DeviceInformation->Removable)
676 {
677 return;
678 }
679
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)
685 {
686 return;
687 }
688
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;
694
695 InitializeObjectAttributes(&ObjectAttributes,
696 &ReparseFile,
697 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
698 NULL,
699 NULL);
700
701 /* Open reparse point */
702 Status = ZwOpenFile(&Handle,
703 FILE_GENERIC_READ,
704 &ObjectAttributes,
705 &IoStatusBlock,
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))
710 {
711 DeviceInformation->NoDatabase = FALSE;
712 return;
713 }
714
715 /* Query reparse point information
716 * We only pay attention to mout point
717 */
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,
724 NULL,
725 NULL,
726 NULL,
727 &IoStatusBlock,
728 &ReparsePointInformation,
729 sizeof(FILE_REPARSE_POINT_INFORMATION),
730 FileReparsePointInformation,
731 TRUE,
732 &FileName,
733 FALSE);
734 if (!NT_SUCCESS(Status))
735 {
736 ZwClose(Handle);
737 return;
738 }
739
740 /* Query mount points */
741 while (TRUE)
742 {
743 RestartScan = TRUE;
744 SymbolicName.Length = 0;
745 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
746 SymbolicName.Buffer = SymbolicNameBuffer;
747 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
748
749 Status = ZwQueryDirectoryFile(Handle,
750 NULL,
751 NULL,
752 NULL,
753 &IoStatusBlock,
754 &ReparsePointInformation,
755 sizeof(FILE_REPARSE_POINT_INFORMATION),
756 FileReparsePointInformation,
757 TRUE,
758 (RestartScan) ? &FileName : NULL,
759 RestartScan);
760 if (!RestartScan)
761 {
762 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
763 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
764 {
765 ZwClose(Handle);
766 return;
767 }
768 }
769 else
770 {
771 RestartScan = FALSE;
772 }
773
774 if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
775 {
776 ZwClose(Handle);
777 return;
778 }
779
780 /* Get the volume name associated to the mount point */
781 Status = QueryVolumeName(Handle,
782 &ReparsePointInformation,
783 NULL, &SymbolicName,
784 &VolumeName);
785 if (!NT_SUCCESS(Status))
786 {
787 continue;
788 }
789
790 FreePool(VolumeName.Buffer);
791
792 /* Get its information */
793 Status = FindDeviceInfo(DeviceExtension, &SymbolicName,
794 FALSE, &VolumeDeviceInformation);
795 if (!NT_SUCCESS(Status))
796 {
797 DeviceInformation->NoDatabase = TRUE;
798 continue;
799 }
800
801 /* If notification are enabled, mark it online */
802 if (!DeviceInformation->SkipNotifications)
803 {
804 PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName);
805 }
806 }
807 }
808
809 /*
810 * @implemented
811 */
812 VOID
813 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension,
814 IN PDEVICE_INFORMATION DeviceInformation)
815 {
816 PRECONCILE_WORK_ITEM WorkItem;
817
818 /* Removable devices don't have remote database */
819 if (DeviceInformation->Removable)
820 {
821 return;
822 }
823
824 /* Allocate a work item */
825 WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM));
826 if (!WorkItem)
827 {
828 return;
829 }
830
831 WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
832 if (!WorkItem->WorkItem)
833 {
834 FreePool(WorkItem);
835 return;
836 }
837
838 /* And queue it */
839 WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker;
840 WorkItem->DeviceExtension = DeviceExtension;
841 WorkItem->DeviceInformation = DeviceInformation;
842 QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension));
843
844 /* If there's no automount, and automatic letters
845 * all volumes to find those online and notify there presence
846 */
847 if (DeviceExtension->WorkerThreadStatus == 0 &&
848 DeviceExtension->AutomaticDriveLetter == 1 &&
849 DeviceExtension->NoAutoMount == FALSE)
850 {
851 OnlineMountedVolumes(DeviceExtension, DeviceInformation);
852 }
853 }
854
855 /*
856 * @implemented
857 */
858 VOID
859 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension)
860 {
861 PLIST_ENTRY NextEntry;
862 PDEVICE_INFORMATION DeviceInformation;
863
864 /* Browse all the devices */
865 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
866 NextEntry != &(DeviceExtension->DeviceListHead);
867 NextEntry = NextEntry->Flink)
868 {
869 DeviceInformation = CONTAINING_RECORD(NextEntry,
870 DEVICE_INFORMATION,
871 DeviceListEntry);
872 /* If it's not removable, then, it might have a database to sync */
873 if (!DeviceInformation->Removable)
874 {
875 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
876 }
877 }
878 }
879
880 /*
881 * @implemented
882 */
883 VOID
884 NTAPI
885 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject,
886 IN PVOID Context)
887 {
888 ULONG Length;
889 NTSTATUS Status;
890 PVOID TmpBuffer;
891 CHAR Disposition;
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
901
902 /* Extract context */
903 WorkItem = Context;
904 DeviceInformation = WorkItem->DeviceInformation;
905
906 /* Reconstruct appropriate string */
907 DatabaseName.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
908 DatabaseName.MaximumLength = DatabaseName.Length + sizeof(WCHAR);
909
910 DatabaseFile.Length = DeviceInformation->DeviceName.Length + RemoteDatabaseFile.Length;
911 DatabaseFile.MaximumLength = DatabaseFile.Length + sizeof(WCHAR);
912
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)
918 {
919 Status = STATUS_INSUFFICIENT_RESOURCES;
920 goto Cleanup;
921 }
922
923 /* Create the required folder (in which the database will be stored
924 * \System Volume Information at root of the volume
925 */
926 Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName));
927 if (!NT_SUCCESS(Status))
928 {
929 goto Cleanup;
930 }
931
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;
941
942 /* Create database */
943 InitializeObjectAttributes(&ObjectAttributes,
944 &DatabaseName,
945 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
946 NULL,
947 NULL);
948
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,
953 &ObjectAttributes,
954 &IoStatusBlock,
955 NULL,
956 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
957 0,
958 FILE_CREATE,
959 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
960 NULL,
961 0);
962 if (!NT_SUCCESS(Status))
963 {
964 Database = 0;
965 goto Cleanup;
966 }
967
968 InitializeObjectAttributes(&MigrateAttributes,
969 &DatabaseFile,
970 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
971 NULL,
972 NULL);
973
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,
980 &MigrateAttributes,
981 &IoStatusBlock,
982 NULL,
983 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
984 0,
985 FILE_OPEN,
986 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
987 NULL,
988 0);
989 IoSetThreadHardErrorMode(PreviousMode);
990 if (!NT_SUCCESS(Status))
991 {
992 Migrate = 0;
993 }
994 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
995 {
996 Status == STATUS_SUCCESS;
997 Complete = TRUE;
998 }
999 if (!NT_SUCCESS(Status) || Complete)
1000 {
1001 goto Cleanup;
1002 }
1003
1004 ByteOffset.QuadPart = 0LL;
1005 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1006 /* Now, loop as long it's possible */
1007 while (Status == STATUS_SUCCESS)
1008 {
1009 /* Read data from existing database */
1010 Status = ZwReadFile(Migrate,
1011 NULL,
1012 NULL,
1013 NULL,
1014 &IoStatusBlock,
1015 TmpBuffer,
1016 TEMP_BUFFER_SIZE,
1017 &ByteOffset,
1018 NULL);
1019 if (!NT_SUCCESS(Status))
1020 {
1021 break;
1022 }
1023
1024 /* And write them into new database */
1025 Length = IoStatusBlock.Information;
1026 Status = ZwWriteFile(Database,
1027 NULL,
1028 NULL,
1029 NULL,
1030 &IoStatusBlock,
1031 TmpBuffer,
1032 Length,
1033 &ByteOffset,
1034 NULL);
1035 ByteOffset.QuadPart += Length;
1036 }
1037 IoSetThreadHardErrorMode(PreviousMode);
1038
1039 /* Delete old databse if it was well copied */
1040 if (Status == STATUS_END_OF_FILE)
1041 {
1042 Disposition = 1;
1043 Status = ZwSetInformationFile(Migrate,
1044 &IoStatusBlock,
1045 &Disposition,
1046 sizeof(Disposition),
1047 FileDispositionInformation);
1048 }
1049
1050 /* Migration is over */
1051
1052 Cleanup:
1053 if (TmpBuffer)
1054 {
1055 FreePool(TmpBuffer);
1056 }
1057
1058 if (DatabaseFile.Buffer)
1059 {
1060 FreePool(DatabaseFile.Buffer);
1061 }
1062
1063 if (DatabaseName.Buffer)
1064 {
1065 FreePool(DatabaseName.Buffer);
1066 }
1067
1068 if (Migrate)
1069 {
1070 ZwClose(Migrate);
1071 }
1072
1073 if (NT_SUCCESS(Status))
1074 {
1075 DeviceInformation->Migrated = 1;
1076 }
1077 else if (Database)
1078 {
1079 ZwClose(Database);
1080 }
1081
1082 IoFreeWorkItem(WorkItem->WorkItem);
1083
1084 WorkItem->WorkItem = NULL;
1085 WorkItem->Status = Status;
1086 WorkItem->Database = Database;
1087
1088 KeSetEvent(WorkItem->Event, 0, FALSE);
1089 #undef TEMP_BUFFER_SIZE
1090 }
1091
1092 /*
1093 * @implemented
1094 */
1095 NTSTATUS
1096 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1097 IN OUT PHANDLE Database)
1098 {
1099 KEVENT Event;
1100 NTSTATUS Status;
1101 PMIGRATE_WORK_ITEM WorkItem;
1102
1103 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1104
1105 /* Allocate a work item dedicated to migration */
1106 WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM));
1107 if (!WorkItem)
1108 {
1109 *Database = 0;
1110 return STATUS_INSUFFICIENT_RESOURCES;
1111 }
1112
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)
1118 {
1119 FreePool(WorkItem);
1120 *Database = 0;
1121 return STATUS_INSUFFICIENT_RESOURCES;
1122 }
1123
1124 /* And queue it */
1125 IoQueueWorkItem(WorkItem->WorkItem,
1126 MigrateRemoteDatabaseWorker,
1127 DelayedWorkQueue,
1128 WorkItem);
1129
1130 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1131 Status = WorkItem->Status;
1132
1133 *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
1134
1135 FreePool(WorkItem);
1136 return Status;
1137 }
1138
1139 /*
1140 * @implemented
1141 */
1142 HANDLE
1143 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1144 IN BOOLEAN MigrateDatabase)
1145 {
1146 HANDLE Database;
1147 NTSTATUS Status;
1148 BOOLEAN PreviousMode;
1149 IO_STATUS_BLOCK IoStatusBlock;
1150 OBJECT_ATTRIBUTES ObjectAttributes;
1151 UNICODE_STRING DeviceRemoteDatabase;
1152
1153 Database = 0;
1154
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)
1160 {
1161 return 0;
1162 }
1163
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;
1168
1169 /* Open database */
1170 InitializeObjectAttributes(&ObjectAttributes,
1171 &DeviceRemoteDatabase,
1172 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1173 NULL,
1174 NULL);
1175
1176 /* Disable hard errors */
1177 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1178
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,
1183 &ObjectAttributes,
1184 &IoStatusBlock,
1185 NULL,
1186 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1187 0,
1188 (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN,
1189 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1190 NULL,
1191 0);
1192
1193 /* If base it to be migrated and was opened successfully, go ahead */
1194 if (MigrateDatabase && NT_SUCCESS(Status))
1195 {
1196 MigrateRemoteDatabase(DeviceInformation, &Database);
1197 }
1198
1199 IoSetThreadHardErrorMode(PreviousMode);
1200 FreePool(DeviceRemoteDatabase.Buffer);
1201
1202 return Database;
1203 }
1204
1205 /*
1206 * @implemented
1207 */
1208 NTSTATUS
1209 NTAPI
1210 QueryUniqueIdQueryRoutine(IN PWSTR ValueName,
1211 IN ULONG ValueType,
1212 IN PVOID ValueData,
1213 IN ULONG ValueLength,
1214 IN PVOID Context,
1215 IN PVOID EntryContext)
1216 {
1217 PMOUNTDEV_UNIQUE_ID IntUniqueId;
1218 PMOUNTDEV_UNIQUE_ID * UniqueId;
1219
1220 /* Sanity check */
1221 if (ValueLength >= 0x10000)
1222 {
1223 return STATUS_SUCCESS;
1224 }
1225
1226 /* Allocate the Unique ID */
1227 IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength);
1228 if (IntUniqueId)
1229 {
1230 /* Copy data & return */
1231 IntUniqueId->UniqueIdLength = ValueLength;
1232 RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength);
1233
1234 UniqueId = Context;
1235 *UniqueId = IntUniqueId;
1236 }
1237
1238 return STATUS_SUCCESS;
1239 }
1240
1241 /*
1242 * @implemented
1243 */
1244 NTSTATUS
1245 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension,
1246 IN PUNICODE_STRING SymbolicName,
1247 OUT PMOUNTDEV_UNIQUE_ID * UniqueId)
1248 {
1249 NTSTATUS Status;
1250 PDEVICE_INFORMATION DeviceInformation;
1251 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1252
1253 /* Query the unique ID */
1254 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1255 QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine;
1256 QueryTable[0].Name = SymbolicName->Buffer;
1257
1258 *UniqueId = NULL;
1259 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1260 DatabasePath,
1261 QueryTable,
1262 UniqueId,
1263 NULL);
1264 /* Unique ID found, no need to go farther */
1265 if (*UniqueId)
1266 {
1267 return STATUS_SUCCESS;
1268 }
1269
1270 /* Otherwise, find associate device information */
1271 Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation);
1272 if (!NT_SUCCESS(Status))
1273 {
1274 return Status;
1275 }
1276
1277 *UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1278 if (!*UniqueId)
1279 {
1280 return STATUS_INSUFFICIENT_RESOURCES;
1281 }
1282
1283 /* Return this unique ID (better than nothing) */
1284 (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
1285 RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength);
1286
1287 return STATUS_SUCCESS;
1288 }
1289
1290 /*
1291 * @implemented
1292 */
1293 NTSTATUS
1294 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension,
1295 IN PDATABASE_ENTRY DatabaseEntry)
1296 {
1297 NTSTATUS Status;
1298 PWCHAR SymbolicName;
1299 PLIST_ENTRY NextEntry;
1300 UNICODE_STRING SymbolicString;
1301 PDEVICE_INFORMATION DeviceInformation;
1302
1303 /* Create symbolic name from database entry */
1304 SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR));
1305 if (!SymbolicName)
1306 {
1307 return STATUS_INSUFFICIENT_RESOURCES;
1308 }
1309
1310 RtlCopyMemory(SymbolicName,
1311 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset),
1312 DatabaseEntry->SymbolicNameLength);
1313 SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL;
1314
1315 /* Associate the unique ID with the name from remote database */
1316 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1317 DatabasePath,
1318 SymbolicName,
1319 REG_BINARY,
1320 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
1321 DatabaseEntry->UniqueIdLength);
1322 FreePool(SymbolicName);
1323
1324 /* Reget symbolic name */
1325 SymbolicString.Length = DatabaseEntry->SymbolicNameLength;
1326 SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength;
1327 SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
1328
1329 /* Find the device using this unique ID */
1330 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1331 NextEntry != &(DeviceExtension->DeviceListHead);
1332 NextEntry = NextEntry->Flink)
1333 {
1334 DeviceInformation = CONTAINING_RECORD(NextEntry,
1335 DEVICE_INFORMATION,
1336 DeviceListEntry);
1337
1338 if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength)
1339 {
1340 continue;
1341 }
1342
1343 if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
1344 DeviceInformation->UniqueId->UniqueId,
1345 DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
1346 {
1347 break;
1348 }
1349 }
1350
1351 /* If found, create a mount point */
1352 if (NextEntry != &(DeviceExtension->DeviceListHead))
1353 {
1354 MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName));
1355 }
1356
1357 return Status;
1358 }
1359
1360 /*
1361 * @implemented
1362 */
1363 VOID
1364 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,
1365 IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
1366 IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
1367 {
1368 LONG Offset = 0;
1369 HANDLE Database;
1370 PDATABASE_ENTRY Entry, NewEntry;
1371 NTSTATUS Status = STATUS_SUCCESS;
1372
1373 /* Open the remote database */
1374 Database = OpenRemoteDatabase(DeviceInformation, FALSE);
1375 if (!Database)
1376 {
1377 return;
1378 }
1379
1380 /* Get all the entries */
1381 do
1382 {
1383 Entry = GetRemoteDatabaseEntry(Database, Offset);
1384 if (!Entry)
1385 {
1386 break;
1387 }
1388
1389 /* Not the correct entry, skip it */
1390 if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength)
1391 {
1392 Offset += Entry->EntrySize;
1393 FreePool(Entry);
1394 continue;
1395 }
1396
1397 /* Not the correct entry, skip it */
1398 if (RtlCompareMemory(OldUniqueId->UniqueId,
1399 (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset),
1400 Entry->UniqueIdLength) != Entry->UniqueIdLength)
1401 {
1402 Offset += Entry->EntrySize;
1403 FreePool(Entry);
1404 continue;
1405 }
1406
1407 /* Here, we have the correct entry */
1408 NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength);
1409 if (!NewEntry)
1410 {
1411 Offset += Entry->EntrySize;
1412 FreePool(Entry);
1413 continue;
1414 }
1415
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);
1428
1429 /* Delete old entry */
1430 Status = DeleteRemoteDatabaseEntry(Database, Offset);
1431 if (!NT_SUCCESS(Status))
1432 {
1433 FreePool(Entry);
1434 FreePool(NewEntry);
1435 break;
1436 }
1437
1438 /* And replace with new one */
1439 Status = AddRemoteDatabaseEntry(Database, NewEntry);
1440 FreePool(Entry);
1441 FreePool(NewEntry);
1442 } while (NT_SUCCESS(Status));
1443
1444 CloseRemoteDatabase(Database);
1445
1446 return;
1447 }
1448
1449 /*
1450 * @implemented
1451 */
1452 NTSTATUS
1453 NTAPI
1454 DeleteDriveLetterRoutine(IN PWSTR ValueName,
1455 IN ULONG ValueType,
1456 IN PVOID ValueData,
1457 IN ULONG ValueLength,
1458 IN PVOID Context,
1459 IN PVOID EntryContext)
1460 {
1461 PMOUNTDEV_UNIQUE_ID UniqueId;
1462 UNICODE_STRING RegistryEntry;
1463
1464 if (ValueType != REG_BINARY)
1465 {
1466 return STATUS_SUCCESS;
1467 }
1468
1469 UniqueId = Context;
1470
1471 /* First ensure we have the correct data */
1472 if (UniqueId->UniqueIdLength != ValueLength)
1473 {
1474 return STATUS_SUCCESS;
1475 }
1476
1477 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
1478 {
1479 return STATUS_SUCCESS;
1480 }
1481
1482 RtlInitUnicodeString(&RegistryEntry, ValueName);
1483
1484 /* Then, it's a drive letter, erase it */
1485 if (IsDriveLetter(&RegistryEntry))
1486 {
1487 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1488 DatabasePath,
1489 ValueName);
1490 }
1491
1492 return STATUS_SUCCESS;
1493 }
1494
1495 /*
1496 * @implemented
1497 */
1498 VOID
1499 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
1500 {
1501 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1502
1503 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1504 QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
1505
1506 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1507 DatabasePath,
1508 QueryTable,
1509 UniqueId,
1510 NULL);
1511 }
1512
1513 /*
1514 * @implemented
1515 */
1516 NTSTATUS
1517 NTAPI
1518 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,
1519 IN ULONG ValueType,
1520 IN PVOID ValueData,
1521 IN ULONG ValueLength,
1522 IN PVOID Context,
1523 IN PVOID EntryContext)
1524 {
1525 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
1526
1527 /* Ensure we have correct input */
1528 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
1529 UniqueId->UniqueIdLength != ValueLength)
1530 {
1531 return STATUS_SUCCESS;
1532 }
1533
1534 /* And then, if unique ID matching, delete entry */
1535 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
1536 {
1537 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1538 DatabasePath,
1539 ValueName);
1540 }
1541
1542 return STATUS_SUCCESS;
1543 }
1544
1545 /*
1546 * @implemented
1547 */
1548 VOID
1549 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
1550 {
1551 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1552
1553 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1554 QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
1555
1556 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1557 DatabasePath,
1558 QueryTable,
1559 UniqueId,
1560 NULL);
1561 }