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