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