Sync with trunk r63831.
[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 RestartScan = TRUE;
750
751 /* Query mount points */
752 while (TRUE)
753 {
754 SymbolicName.Length = 0;
755 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
756 SymbolicName.Buffer = SymbolicNameBuffer;
757 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
758
759 Status = ZwQueryDirectoryFile(Handle,
760 NULL,
761 NULL,
762 NULL,
763 &IoStatusBlock,
764 &ReparsePointInformation,
765 sizeof(FILE_REPARSE_POINT_INFORMATION),
766 FileReparsePointInformation,
767 TRUE,
768 (RestartScan) ? &FileName : NULL,
769 RestartScan);
770 if (!RestartScan)
771 {
772 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
773 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
774 {
775 break;
776 }
777 }
778 else
779 {
780 RestartScan = FALSE;
781 }
782
783 if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
784 {
785 break;
786 }
787
788 /* Get the volume name associated to the mount point */
789 Status = QueryVolumeName(Handle,
790 &ReparsePointInformation,
791 NULL, &SymbolicName,
792 &VolumeName);
793 if (!NT_SUCCESS(Status))
794 {
795 continue;
796 }
797
798 FreePool(VolumeName.Buffer);
799
800 /* Get its information */
801 Status = FindDeviceInfo(DeviceExtension, &SymbolicName,
802 FALSE, &VolumeDeviceInformation);
803 if (!NT_SUCCESS(Status))
804 {
805 DeviceInformation->NoDatabase = TRUE;
806 continue;
807 }
808
809 /* If notification are enabled, mark it online */
810 if (!DeviceInformation->SkipNotifications)
811 {
812 PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName);
813 }
814 }
815
816 ZwClose(Handle);
817 }
818
819 /*
820 * @implemented
821 */
822 VOID
823 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension,
824 IN PDEVICE_INFORMATION DeviceInformation)
825 {
826 PRECONCILE_WORK_ITEM WorkItem;
827
828 /* Removable devices don't have remote database */
829 if (DeviceInformation->Removable)
830 {
831 return;
832 }
833
834 /* Allocate a work item */
835 WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM));
836 if (!WorkItem)
837 {
838 return;
839 }
840
841 WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
842 if (!WorkItem->WorkItem)
843 {
844 FreePool(WorkItem);
845 return;
846 }
847
848 /* And queue it */
849 WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker;
850 WorkItem->DeviceExtension = DeviceExtension;
851 WorkItem->DeviceInformation = DeviceInformation;
852 QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension));
853
854 /* If there's no automount, and automatic letters
855 * all volumes to find those online and notify there presence
856 */
857 if (DeviceExtension->WorkerThreadStatus == 0 &&
858 DeviceExtension->AutomaticDriveLetter == 1 &&
859 DeviceExtension->NoAutoMount == FALSE)
860 {
861 OnlineMountedVolumes(DeviceExtension, DeviceInformation);
862 }
863 }
864
865 /*
866 * @implemented
867 */
868 VOID
869 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension)
870 {
871 PLIST_ENTRY NextEntry;
872 PDEVICE_INFORMATION DeviceInformation;
873
874 /* Browse all the devices */
875 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
876 NextEntry != &(DeviceExtension->DeviceListHead);
877 NextEntry = NextEntry->Flink)
878 {
879 DeviceInformation = CONTAINING_RECORD(NextEntry,
880 DEVICE_INFORMATION,
881 DeviceListEntry);
882 /* If it's not removable, then, it might have a database to sync */
883 if (!DeviceInformation->Removable)
884 {
885 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
886 }
887 }
888 }
889
890 /*
891 * @implemented
892 */
893 VOID
894 NTAPI
895 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject,
896 IN PVOID Context)
897 {
898 ULONG Length;
899 NTSTATUS Status;
900 PVOID TmpBuffer;
901 CHAR Disposition;
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
911
912 UNREFERENCED_PARAMETER(DeviceObject);
913
914 /* Extract context */
915 WorkItem = Context;
916 DeviceInformation = WorkItem->DeviceInformation;
917
918 /* Reconstruct appropriate string */
919 DatabaseName.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
920 DatabaseName.MaximumLength = DatabaseName.Length + sizeof(WCHAR);
921
922 DatabaseFile.Length = DeviceInformation->DeviceName.Length + RemoteDatabaseFile.Length;
923 DatabaseFile.MaximumLength = DatabaseFile.Length + sizeof(WCHAR);
924
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)
930 {
931 Status = STATUS_INSUFFICIENT_RESOURCES;
932 goto Cleanup;
933 }
934
935 /* Create the required folder (in which the database will be stored
936 * \System Volume Information at root of the volume
937 */
938 Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName));
939 if (!NT_SUCCESS(Status))
940 {
941 goto Cleanup;
942 }
943
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;
953
954 /* Create database */
955 InitializeObjectAttributes(&ObjectAttributes,
956 &DatabaseName,
957 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
958 NULL,
959 NULL);
960
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,
965 &ObjectAttributes,
966 &IoStatusBlock,
967 NULL,
968 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
969 0,
970 FILE_CREATE,
971 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
972 NULL,
973 0);
974 if (!NT_SUCCESS(Status))
975 {
976 Database = 0;
977 goto Cleanup;
978 }
979
980 InitializeObjectAttributes(&MigrateAttributes,
981 &DatabaseFile,
982 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
983 NULL,
984 NULL);
985
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,
992 &MigrateAttributes,
993 &IoStatusBlock,
994 NULL,
995 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
996 0,
997 FILE_OPEN,
998 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
999 NULL,
1000 0);
1001 IoSetThreadHardErrorMode(PreviousMode);
1002 if (!NT_SUCCESS(Status))
1003 {
1004 Migrate = 0;
1005 }
1006 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1007 {
1008 Status = STATUS_SUCCESS;
1009 Complete = TRUE;
1010 }
1011 if (!NT_SUCCESS(Status) || Complete)
1012 {
1013 goto Cleanup;
1014 }
1015
1016 ByteOffset.QuadPart = 0LL;
1017 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1018 /* Now, loop as long it's possible */
1019 while (Status == STATUS_SUCCESS)
1020 {
1021 /* Read data from existing database */
1022 Status = ZwReadFile(Migrate,
1023 NULL,
1024 NULL,
1025 NULL,
1026 &IoStatusBlock,
1027 TmpBuffer,
1028 TEMP_BUFFER_SIZE,
1029 &ByteOffset,
1030 NULL);
1031 if (!NT_SUCCESS(Status))
1032 {
1033 break;
1034 }
1035
1036 /* And write them into new database */
1037 Length = (ULONG)IoStatusBlock.Information;
1038 Status = ZwWriteFile(Database,
1039 NULL,
1040 NULL,
1041 NULL,
1042 &IoStatusBlock,
1043 TmpBuffer,
1044 Length,
1045 &ByteOffset,
1046 NULL);
1047 ByteOffset.QuadPart += Length;
1048 }
1049 IoSetThreadHardErrorMode(PreviousMode);
1050
1051 /* Delete old databse if it was well copied */
1052 if (Status == STATUS_END_OF_FILE)
1053 {
1054 Disposition = 1;
1055 Status = ZwSetInformationFile(Migrate,
1056 &IoStatusBlock,
1057 &Disposition,
1058 sizeof(Disposition),
1059 FileDispositionInformation);
1060 }
1061
1062 /* Migration is over */
1063
1064 Cleanup:
1065 if (TmpBuffer)
1066 {
1067 FreePool(TmpBuffer);
1068 }
1069
1070 if (DatabaseFile.Buffer)
1071 {
1072 FreePool(DatabaseFile.Buffer);
1073 }
1074
1075 if (DatabaseName.Buffer)
1076 {
1077 FreePool(DatabaseName.Buffer);
1078 }
1079
1080 if (Migrate)
1081 {
1082 ZwClose(Migrate);
1083 }
1084
1085 if (NT_SUCCESS(Status))
1086 {
1087 DeviceInformation->Migrated = 1;
1088 }
1089 else if (Database)
1090 {
1091 ZwClose(Database);
1092 }
1093
1094 IoFreeWorkItem(WorkItem->WorkItem);
1095
1096 WorkItem->WorkItem = NULL;
1097 WorkItem->Status = Status;
1098 WorkItem->Database = Database;
1099
1100 KeSetEvent(WorkItem->Event, 0, FALSE);
1101 #undef TEMP_BUFFER_SIZE
1102 }
1103
1104 /*
1105 * @implemented
1106 */
1107 NTSTATUS
1108 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1109 IN OUT PHANDLE Database)
1110 {
1111 KEVENT Event;
1112 NTSTATUS Status;
1113 PMIGRATE_WORK_ITEM WorkItem;
1114
1115 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1116
1117 /* Allocate a work item dedicated to migration */
1118 WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM));
1119 if (!WorkItem)
1120 {
1121 *Database = 0;
1122 return STATUS_INSUFFICIENT_RESOURCES;
1123 }
1124
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)
1130 {
1131 FreePool(WorkItem);
1132 *Database = 0;
1133 return STATUS_INSUFFICIENT_RESOURCES;
1134 }
1135
1136 /* And queue it */
1137 IoQueueWorkItem(WorkItem->WorkItem,
1138 MigrateRemoteDatabaseWorker,
1139 DelayedWorkQueue,
1140 WorkItem);
1141
1142 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1143 Status = WorkItem->Status;
1144
1145 *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
1146
1147 FreePool(WorkItem);
1148 return Status;
1149 }
1150
1151 /*
1152 * @implemented
1153 */
1154 HANDLE
1155 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1156 IN BOOLEAN MigrateDatabase)
1157 {
1158 HANDLE Database;
1159 NTSTATUS Status;
1160 BOOLEAN PreviousMode;
1161 IO_STATUS_BLOCK IoStatusBlock;
1162 OBJECT_ATTRIBUTES ObjectAttributes;
1163 UNICODE_STRING DeviceRemoteDatabase;
1164
1165 Database = 0;
1166
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)
1172 {
1173 return 0;
1174 }
1175
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;
1180
1181 /* Open database */
1182 InitializeObjectAttributes(&ObjectAttributes,
1183 &DeviceRemoteDatabase,
1184 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1185 NULL,
1186 NULL);
1187
1188 /* Disable hard errors */
1189 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1190
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,
1195 &ObjectAttributes,
1196 &IoStatusBlock,
1197 NULL,
1198 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1199 0,
1200 (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN,
1201 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1202 NULL,
1203 0);
1204
1205 /* If base it to be migrated and was opened successfully, go ahead */
1206 if (MigrateDatabase && NT_SUCCESS(Status))
1207 {
1208 MigrateRemoteDatabase(DeviceInformation, &Database);
1209 }
1210
1211 IoSetThreadHardErrorMode(PreviousMode);
1212 FreePool(DeviceRemoteDatabase.Buffer);
1213
1214 return Database;
1215 }
1216
1217 /*
1218 * @implemented
1219 */
1220 NTSTATUS
1221 NTAPI
1222 QueryUniqueIdQueryRoutine(IN PWSTR ValueName,
1223 IN ULONG ValueType,
1224 IN PVOID ValueData,
1225 IN ULONG ValueLength,
1226 IN PVOID Context,
1227 IN PVOID EntryContext)
1228 {
1229 PMOUNTDEV_UNIQUE_ID IntUniqueId;
1230 PMOUNTDEV_UNIQUE_ID * UniqueId;
1231
1232 UNREFERENCED_PARAMETER(ValueName);
1233 UNREFERENCED_PARAMETER(ValueType);
1234 UNREFERENCED_PARAMETER(EntryContext);
1235
1236 /* Sanity check */
1237 if (ValueLength >= 0x10000)
1238 {
1239 return STATUS_SUCCESS;
1240 }
1241
1242 /* Allocate the Unique ID */
1243 IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength);
1244 if (IntUniqueId)
1245 {
1246 /* Copy data & return */
1247 IntUniqueId->UniqueIdLength = (USHORT)ValueLength;
1248 RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength);
1249
1250 UniqueId = Context;
1251 *UniqueId = IntUniqueId;
1252 }
1253
1254 return STATUS_SUCCESS;
1255 }
1256
1257 /*
1258 * @implemented
1259 */
1260 NTSTATUS
1261 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension,
1262 IN PUNICODE_STRING SymbolicName,
1263 OUT PMOUNTDEV_UNIQUE_ID * UniqueId)
1264 {
1265 NTSTATUS Status;
1266 PDEVICE_INFORMATION DeviceInformation;
1267 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1268
1269 /* Query the unique ID */
1270 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1271 QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine;
1272 QueryTable[0].Name = SymbolicName->Buffer;
1273
1274 *UniqueId = NULL;
1275 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1276 DatabasePath,
1277 QueryTable,
1278 UniqueId,
1279 NULL);
1280 /* Unique ID found, no need to go farther */
1281 if (*UniqueId)
1282 {
1283 return STATUS_SUCCESS;
1284 }
1285
1286 /* Otherwise, find associate device information */
1287 Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation);
1288 if (!NT_SUCCESS(Status))
1289 {
1290 return Status;
1291 }
1292
1293 *UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1294 if (!*UniqueId)
1295 {
1296 return STATUS_INSUFFICIENT_RESOURCES;
1297 }
1298
1299 /* Return this unique ID (better than nothing) */
1300 (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
1301 RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength);
1302
1303 return STATUS_SUCCESS;
1304 }
1305
1306 /*
1307 * @implemented
1308 */
1309 NTSTATUS
1310 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension,
1311 IN PDATABASE_ENTRY DatabaseEntry)
1312 {
1313 NTSTATUS Status;
1314 PWCHAR SymbolicName;
1315 PLIST_ENTRY NextEntry;
1316 UNICODE_STRING SymbolicString;
1317 PDEVICE_INFORMATION DeviceInformation;
1318
1319 /* Create symbolic name from database entry */
1320 SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR));
1321 if (!SymbolicName)
1322 {
1323 return STATUS_INSUFFICIENT_RESOURCES;
1324 }
1325
1326 RtlCopyMemory(SymbolicName,
1327 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset),
1328 DatabaseEntry->SymbolicNameLength);
1329 SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL;
1330
1331 /* Associate the unique ID with the name from remote database */
1332 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1333 DatabasePath,
1334 SymbolicName,
1335 REG_BINARY,
1336 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
1337 DatabaseEntry->UniqueIdLength);
1338 FreePool(SymbolicName);
1339
1340 /* Reget symbolic name */
1341 SymbolicString.Length = DatabaseEntry->SymbolicNameLength;
1342 SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength;
1343 SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
1344
1345 /* Find the device using this unique ID */
1346 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1347 NextEntry != &(DeviceExtension->DeviceListHead);
1348 NextEntry = NextEntry->Flink)
1349 {
1350 DeviceInformation = CONTAINING_RECORD(NextEntry,
1351 DEVICE_INFORMATION,
1352 DeviceListEntry);
1353
1354 if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength)
1355 {
1356 continue;
1357 }
1358
1359 if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
1360 DeviceInformation->UniqueId->UniqueId,
1361 DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
1362 {
1363 break;
1364 }
1365 }
1366
1367 /* If found, create a mount point */
1368 if (NextEntry != &(DeviceExtension->DeviceListHead))
1369 {
1370 MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName));
1371 }
1372
1373 return Status;
1374 }
1375
1376 /*
1377 * @implemented
1378 */
1379 VOID
1380 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,
1381 IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
1382 IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
1383 {
1384 LONG Offset = 0;
1385 HANDLE Database;
1386 PDATABASE_ENTRY Entry, NewEntry;
1387 NTSTATUS Status = STATUS_SUCCESS;
1388
1389 /* Open the remote database */
1390 Database = OpenRemoteDatabase(DeviceInformation, FALSE);
1391 if (!Database)
1392 {
1393 return;
1394 }
1395
1396 /* Get all the entries */
1397 do
1398 {
1399 Entry = GetRemoteDatabaseEntry(Database, Offset);
1400 if (!Entry)
1401 {
1402 break;
1403 }
1404
1405 /* Not the correct entry, skip it */
1406 if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength)
1407 {
1408 Offset += Entry->EntrySize;
1409 FreePool(Entry);
1410 continue;
1411 }
1412
1413 /* Not the correct entry, skip it */
1414 if (RtlCompareMemory(OldUniqueId->UniqueId,
1415 (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset),
1416 Entry->UniqueIdLength) != Entry->UniqueIdLength)
1417 {
1418 Offset += Entry->EntrySize;
1419 FreePool(Entry);
1420 continue;
1421 }
1422
1423 /* Here, we have the correct entry */
1424 NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength);
1425 if (!NewEntry)
1426 {
1427 Offset += Entry->EntrySize;
1428 FreePool(Entry);
1429 continue;
1430 }
1431
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);
1444
1445 /* Delete old entry */
1446 Status = DeleteRemoteDatabaseEntry(Database, Offset);
1447 if (!NT_SUCCESS(Status))
1448 {
1449 FreePool(Entry);
1450 FreePool(NewEntry);
1451 break;
1452 }
1453
1454 /* And replace with new one */
1455 Status = AddRemoteDatabaseEntry(Database, NewEntry);
1456 FreePool(Entry);
1457 FreePool(NewEntry);
1458 } while (NT_SUCCESS(Status));
1459
1460 CloseRemoteDatabase(Database);
1461
1462 return;
1463 }
1464
1465 /*
1466 * @implemented
1467 */
1468 NTSTATUS
1469 NTAPI
1470 DeleteDriveLetterRoutine(IN PWSTR ValueName,
1471 IN ULONG ValueType,
1472 IN PVOID ValueData,
1473 IN ULONG ValueLength,
1474 IN PVOID Context,
1475 IN PVOID EntryContext)
1476 {
1477 PMOUNTDEV_UNIQUE_ID UniqueId;
1478 UNICODE_STRING RegistryEntry;
1479
1480 UNREFERENCED_PARAMETER(EntryContext);
1481
1482 if (ValueType != REG_BINARY)
1483 {
1484 return STATUS_SUCCESS;
1485 }
1486
1487 UniqueId = Context;
1488
1489 /* First ensure we have the correct data */
1490 if (UniqueId->UniqueIdLength != ValueLength)
1491 {
1492 return STATUS_SUCCESS;
1493 }
1494
1495 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
1496 {
1497 return STATUS_SUCCESS;
1498 }
1499
1500 RtlInitUnicodeString(&RegistryEntry, ValueName);
1501
1502 /* Then, it's a drive letter, erase it */
1503 if (IsDriveLetter(&RegistryEntry))
1504 {
1505 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1506 DatabasePath,
1507 ValueName);
1508 }
1509
1510 return STATUS_SUCCESS;
1511 }
1512
1513 /*
1514 * @implemented
1515 */
1516 VOID
1517 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
1518 {
1519 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1520
1521 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1522 QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
1523
1524 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1525 DatabasePath,
1526 QueryTable,
1527 UniqueId,
1528 NULL);
1529 }
1530
1531 /*
1532 * @implemented
1533 */
1534 NTSTATUS
1535 NTAPI
1536 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,
1537 IN ULONG ValueType,
1538 IN PVOID ValueData,
1539 IN ULONG ValueLength,
1540 IN PVOID Context,
1541 IN PVOID EntryContext)
1542 {
1543 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
1544
1545 UNREFERENCED_PARAMETER(EntryContext);
1546
1547 /* Ensure we have correct input */
1548 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
1549 UniqueId->UniqueIdLength != ValueLength)
1550 {
1551 return STATUS_SUCCESS;
1552 }
1553
1554 /* And then, if unique ID matching, delete entry */
1555 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
1556 {
1557 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1558 DatabasePath,
1559 ValueName);
1560 }
1561
1562 return STATUS_SUCCESS;
1563 }
1564
1565 /*
1566 * @implemented
1567 */
1568 VOID
1569 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
1570 {
1571 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1572
1573 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1574 QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
1575
1576 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1577 DatabasePath,
1578 QueryTable,
1579 UniqueId,
1580 NULL);
1581 }