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