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