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