xref: /reactos/drivers/storage/mountmgr/database.c (revision 0c2cdcae)
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 
36 /*
37  * @implemented
38  */
39 LONG
40 GetRemoteDatabaseSize(IN HANDLE Database)
41 {
42     NTSTATUS Status;
43     IO_STATUS_BLOCK IoStatusBlock;
44     FILE_STANDARD_INFORMATION StandardInfo;
45 
46     /* Just query the size */
47     Status = ZwQueryInformationFile(Database,
48                                     &IoStatusBlock,
49                                     &StandardInfo,
50                                     sizeof(FILE_STANDARD_INFORMATION),
51                                     FileStandardInformation);
52     if (NT_SUCCESS(Status))
53     {
54         return StandardInfo.EndOfFile.LowPart;
55     }
56 
57     return 0;
58 }
59 
60 /*
61  * @implemented
62  */
63 NTSTATUS
64 AddRemoteDatabaseEntry(IN HANDLE Database,
65                        IN PDATABASE_ENTRY Entry)
66 {
67     LARGE_INTEGER Size;
68     IO_STATUS_BLOCK IoStatusBlock;
69 
70     /* Get size to append data */
71     Size.QuadPart = GetRemoteDatabaseSize(Database);
72 
73     return ZwWriteFile(Database, NULL, NULL, NULL,
74                        &IoStatusBlock, Entry,
75                        Entry->EntrySize, &Size, NULL);
76 }
77 
78 /*
79  * @implemented
80  */
81 NTSTATUS
82 CloseRemoteDatabase(IN HANDLE Database)
83 {
84     return ZwClose(Database);
85 }
86 
87 /*
88  * @implemented
89  */
90 NTSTATUS
91 TruncateRemoteDatabase(IN HANDLE Database,
92                        IN LONG NewSize)
93 {
94     NTSTATUS Status;
95     IO_STATUS_BLOCK IoStatusBlock;
96     FILE_END_OF_FILE_INFORMATION EndOfFile;
97     FILE_ALLOCATION_INFORMATION Allocation;
98 
99     EndOfFile.EndOfFile.QuadPart = NewSize;
100     Allocation.AllocationSize.QuadPart = NewSize;
101 
102     /* First set EOF */
103     Status = ZwSetInformationFile(Database,
104                                   &IoStatusBlock,
105                                   &EndOfFile,
106                                   sizeof(FILE_END_OF_FILE_INFORMATION),
107                                   FileEndOfFileInformation);
108     if (NT_SUCCESS(Status))
109     {
110         /* And then, properly set allocation information */
111         Status = ZwSetInformationFile(Database,
112                                       &IoStatusBlock,
113                                       &Allocation,
114                                       sizeof(FILE_ALLOCATION_INFORMATION),
115                                       FileAllocationInformation);
116     }
117 
118     return Status;
119 }
120 
121 /*
122  * @implemented
123  */
124 PDATABASE_ENTRY
125 GetRemoteDatabaseEntry(IN HANDLE Database,
126                        IN LONG StartingOffset)
127 {
128     NTSTATUS Status;
129     ULONG EntrySize;
130     PDATABASE_ENTRY Entry;
131     LARGE_INTEGER ByteOffset;
132     IO_STATUS_BLOCK IoStatusBlock;
133 
134     /* Get the entry at the given position */
135     ByteOffset.QuadPart = StartingOffset;
136     Status = ZwReadFile(Database,
137                         NULL,
138                         NULL,
139                         NULL,
140                         &IoStatusBlock,
141                         &EntrySize,
142                         sizeof(EntrySize),
143                         &ByteOffset,
144                         NULL);
145     if (!NT_SUCCESS(Status))
146     {
147         return NULL;
148     }
149 
150     /* If entry doesn't exist, truncate database */
151     if (!EntrySize)
152     {
153         TruncateRemoteDatabase(Database, StartingOffset);
154         return NULL;
155     }
156 
157     /* Allocate the entry */
158     Entry = AllocatePool(EntrySize);
159     if (!Entry)
160     {
161         return NULL;
162     }
163 
164     /* Effectively read the entry */
165     Status = ZwReadFile(Database,
166                         NULL,
167                         NULL,
168                         NULL,
169                         &IoStatusBlock,
170                         Entry,
171                         EntrySize,
172                         &ByteOffset,
173                         NULL);
174     /* If it fails or returns inconsistent data, drop it (= truncate) */
175     if (!NT_SUCCESS(Status) ||
176         (IoStatusBlock.Information != EntrySize) ||
177         (EntrySize < sizeof(DATABASE_ENTRY)) )
178     {
179         TruncateRemoteDatabase(Database, StartingOffset);
180         FreePool(Entry);
181         return NULL;
182     }
183 
184     /* Validate entry */
185     if (MAX(Entry->SymbolicNameOffset + Entry->SymbolicNameLength,
186             Entry->UniqueIdOffset + Entry->UniqueIdLength) > (LONG)EntrySize)
187     {
188         TruncateRemoteDatabase(Database, StartingOffset);
189         FreePool(Entry);
190         return NULL;
191     }
192 
193     return Entry;
194 }
195 
196 /*
197  * @implemented
198  */
199 NTSTATUS
200 WriteRemoteDatabaseEntry(IN HANDLE Database,
201                          IN LONG Offset,
202                          IN PDATABASE_ENTRY Entry)
203 {
204     NTSTATUS Status;
205     LARGE_INTEGER ByteOffset;
206     IO_STATUS_BLOCK IoStatusBlock;
207 
208     ByteOffset.QuadPart = Offset;
209     Status = ZwWriteFile(Database,
210                          NULL,
211                          NULL,
212                          NULL,
213                          &IoStatusBlock,
214                          Entry,
215                          Entry->EntrySize,
216                          &ByteOffset,
217                          NULL);
218     if (NT_SUCCESS(Status))
219     {
220         if (IoStatusBlock.Information < Entry->EntrySize)
221         {
222             Status = STATUS_INSUFFICIENT_RESOURCES;
223         }
224     }
225 
226     return Status;
227 }
228 
229 /*
230  * @implemented
231  */
232 NTSTATUS
233 DeleteRemoteDatabaseEntry(IN HANDLE Database,
234                           IN LONG StartingOffset)
235 {
236     ULONG EndSize;
237     PVOID TmpBuffer;
238     NTSTATUS Status;
239     ULONG DatabaseSize;
240     PDATABASE_ENTRY Entry;
241     IO_STATUS_BLOCK IoStatusBlock;
242     LARGE_INTEGER EndEntriesOffset;
243 
244     /* First, get database size */
245     DatabaseSize = GetRemoteDatabaseSize(Database);
246     if (!DatabaseSize)
247     {
248         return STATUS_INVALID_PARAMETER;
249     }
250 
251     /* Then, get the entry to remove */
252     Entry = GetRemoteDatabaseEntry(Database, StartingOffset);
253     if (!Entry)
254     {
255         return STATUS_INVALID_PARAMETER;
256     }
257 
258     /* Validate parameters: ensure we won't get negative size */
259     if (Entry->EntrySize + StartingOffset > DatabaseSize)
260     {
261         /* If we get invalid parameters, truncate the whole database
262          * starting the wrong entry. We can't rely on the rest
263          */
264         FreePool(Entry);
265         return TruncateRemoteDatabase(Database, StartingOffset);
266     }
267 
268     /* Now, get the size of the remaining entries (those after the one to remove) */
269     EndSize = DatabaseSize - Entry->EntrySize - StartingOffset;
270     /* Allocate a buffer big enough to hold them */
271     TmpBuffer = AllocatePool(EndSize);
272     if (!TmpBuffer)
273     {
274         FreePool(Entry);
275         return STATUS_INSUFFICIENT_RESOURCES;
276     }
277 
278     /* Get the offset of the entry right after the one to delete */
279     EndEntriesOffset.QuadPart = Entry->EntrySize + StartingOffset;
280     /* We don't need the entry any more */
281     FreePool(Entry);
282 
283     /* Read the ending entries */
284     Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock,
285                         TmpBuffer, EndSize, &EndEntriesOffset, NULL);
286     if (!NT_SUCCESS(Status))
287     {
288         FreePool(TmpBuffer);
289         return Status;
290     }
291 
292     /* Ensure nothing went wrong - we don't want to corrupt the DB */
293     if (IoStatusBlock.Information != EndSize)
294     {
295         FreePool(TmpBuffer);
296         return STATUS_INVALID_PARAMETER;
297     }
298 
299     /* Remove the entry */
300     Status = TruncateRemoteDatabase(Database, StartingOffset + EndSize);
301     if (!NT_SUCCESS(Status))
302     {
303         FreePool(TmpBuffer);
304         return Status;
305     }
306 
307     /* Now, shift the ending entries to erase the entry */
308     EndEntriesOffset.QuadPart = StartingOffset;
309     Status = ZwWriteFile(Database, NULL, NULL, NULL, &IoStatusBlock,
310                          TmpBuffer, EndSize, &EndEntriesOffset, NULL);
311 
312     FreePool(TmpBuffer);
313 
314     return Status;
315 }
316 
317 /*
318  * @implemented
319  */
320 NTSTATUS
321 NTAPI
322 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName,
323                                IN ULONG ValueType,
324                                IN PVOID ValueData,
325                                IN ULONG ValueLength,
326                                IN PVOID Context,
327                                IN PVOID EntryContext)
328 {
329     PMOUNTDEV_UNIQUE_ID UniqueId = Context;
330 
331     UNREFERENCED_PARAMETER(ValueType);
332     UNREFERENCED_PARAMETER(EntryContext);
333 
334     /* Ensure it matches, and delete */
335     if ((UniqueId->UniqueIdLength == ValueLength) &&
336         (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) ==
337          ValueLength))
338     {
339         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
340                                DatabasePath,
341                                ValueName);
342     }
343 
344     return STATUS_SUCCESS;
345 }
346 
347 /*
348  * @implemented
349  */
350 VOID
351 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink,
352                         IN PMOUNTDEV_UNIQUE_ID UniqueId)
353 {
354     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
355 
356     RtlZeroMemory(QueryTable, sizeof(QueryTable));
357     QueryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine;
358     QueryTable[0].Name = SymbolicLink->Buffer;
359 
360     RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
361                            DatabasePath,
362                            QueryTable,
363                            UniqueId,
364                            NULL);
365 }
366 
367 /*
368  * @implemented
369  */
370 NTSTATUS
371 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
372 {
373     NTSTATUS Status;
374     LARGE_INTEGER Timeout;
375 
376     /* Wait for 7 minutes */
377     Timeout.QuadPart = 0xFA0A1F00;
378     Status = KeWaitForSingleObject(&(DeviceExtension->RemoteDatabaseLock), Executive, KernelMode, FALSE, &Timeout);
379     if (Status != STATUS_TIMEOUT)
380     {
381         return Status;
382     }
383 
384     return STATUS_IO_TIMEOUT;
385 }
386 
387 /*
388  * @implemented
389  */
390 VOID
391 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
392 {
393     KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE);
394 }
395 
396 /*
397  * @implemented
398  */
399 NTSTATUS
400 NTAPI
401 QueryUniqueIdQueryRoutine(IN PWSTR ValueName,
402                           IN ULONG ValueType,
403                           IN PVOID ValueData,
404                           IN ULONG ValueLength,
405                           IN PVOID Context,
406                           IN PVOID EntryContext)
407 {
408     PMOUNTDEV_UNIQUE_ID IntUniqueId;
409     PMOUNTDEV_UNIQUE_ID * UniqueId;
410 
411     UNREFERENCED_PARAMETER(ValueName);
412     UNREFERENCED_PARAMETER(ValueType);
413     UNREFERENCED_PARAMETER(EntryContext);
414 
415     /* Sanity check */
416     if (ValueLength >= 0x10000)
417     {
418         return STATUS_SUCCESS;
419     }
420 
421     /* Allocate the Unique ID */
422     IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength);
423     if (IntUniqueId)
424     {
425         /* Copy data & return */
426         IntUniqueId->UniqueIdLength = (USHORT)ValueLength;
427         RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength);
428 
429         UniqueId = Context;
430         *UniqueId = IntUniqueId;
431     }
432 
433     return STATUS_SUCCESS;
434 }
435 
436 /*
437  * @implemented
438  */
439 NTSTATUS
440 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension,
441                         IN PUNICODE_STRING SymbolicName,
442                         OUT PMOUNTDEV_UNIQUE_ID * UniqueId)
443 {
444     NTSTATUS Status;
445     PDEVICE_INFORMATION DeviceInformation;
446     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
447 
448     /* Query the unique ID */
449     RtlZeroMemory(QueryTable, sizeof(QueryTable));
450     QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine;
451     QueryTable[0].Name = SymbolicName->Buffer;
452 
453     *UniqueId = NULL;
454     RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
455                            DatabasePath,
456                            QueryTable,
457                            UniqueId,
458                            NULL);
459     /* Unique ID found, no need to go farther */
460     if (*UniqueId)
461     {
462         return STATUS_SUCCESS;
463     }
464 
465     /* Otherwise, find associate device information */
466     Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation);
467     if (!NT_SUCCESS(Status))
468     {
469         return Status;
470     }
471 
472     *UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
473     if (!*UniqueId)
474     {
475         return STATUS_INSUFFICIENT_RESOURCES;
476     }
477 
478     /* Return this unique ID (better than nothing) */
479     (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
480     RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength);
481 
482     return STATUS_SUCCESS;
483 }
484 
485 /*
486  * @implemented
487  */
488 NTSTATUS
489 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension,
490                       IN PDATABASE_ENTRY DatabaseEntry)
491 {
492     NTSTATUS Status;
493     PWCHAR SymbolicName;
494     PLIST_ENTRY NextEntry;
495     UNICODE_STRING SymbolicString;
496     PDEVICE_INFORMATION DeviceInformation;
497 
498     /* Create symbolic name from database entry */
499     SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR));
500     if (!SymbolicName)
501     {
502         return STATUS_INSUFFICIENT_RESOURCES;
503     }
504 
505     RtlCopyMemory(SymbolicName,
506                   (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset),
507                   DatabaseEntry->SymbolicNameLength);
508     SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL;
509 
510     /* Associate the unique ID with the name from remote database */
511     Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
512                                    DatabasePath,
513                                    SymbolicName,
514                                    REG_BINARY,
515                                    (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
516                                    DatabaseEntry->UniqueIdLength);
517     FreePool(SymbolicName);
518 
519     /* Reget symbolic name */
520     SymbolicString.Length = DatabaseEntry->SymbolicNameLength;
521     SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength;
522     SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
523 
524     /* Find the device using this unique ID */
525     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
526          NextEntry != &(DeviceExtension->DeviceListHead);
527          NextEntry = NextEntry->Flink)
528     {
529         DeviceInformation = CONTAINING_RECORD(NextEntry,
530                                               DEVICE_INFORMATION,
531                                               DeviceListEntry);
532 
533         if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength)
534         {
535             continue;
536         }
537 
538         if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
539                              DeviceInformation->UniqueId->UniqueId,
540                              DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
541         {
542             break;
543         }
544     }
545 
546     /* If found, create a mount point */
547     if (NextEntry != &(DeviceExtension->DeviceListHead))
548     {
549         MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName));
550     }
551 
552     return Status;
553 }
554 
555 /*
556  * @implemented
557  */
558 VOID
559 NTAPI
560 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter)
561 {
562     ULONG Offset;
563     NTSTATUS Status;
564     PFILE_OBJECT FileObject;
565     PDEVICE_OBJECT DeviceObject;
566     PMOUNTDEV_UNIQUE_ID UniqueId;
567     PDATABASE_ENTRY DatabaseEntry;
568     HANDLE DatabaseHandle, Handle;
569     IO_STATUS_BLOCK IoStatusBlock;
570     OBJECT_ATTRIBUTES ObjectAttributes;
571     PDEVICE_INFORMATION ListDeviceInfo;
572     PLIST_ENTRY Entry, EntryInfo, NextEntry;
573     PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
574     BOOLEAN HardwareErrors, Restart, FailedFinding;
575     WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[100];
576     UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName;
577     FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation;
578     PDEVICE_EXTENSION DeviceExtension = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceExtension;
579     PDEVICE_INFORMATION DeviceInformation = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceInformation;
580 
581     /* We're unloading, do nothing */
582     if (Unloading)
583     {
584         return;
585     }
586 
587     /* Lock remote DB */
588     if (!NT_SUCCESS(WaitForRemoteDatabaseSemaphore(DeviceExtension)))
589     {
590         return;
591     }
592 
593     /* Recheck for unloading */
594     if (Unloading)
595     {
596         goto ReleaseRDS;
597     }
598 
599     /* Find the DB to reconcile */
600     KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
601     for (Entry = DeviceExtension->DeviceListHead.Flink;
602          Entry != &DeviceExtension->DeviceListHead;
603          Entry = Entry->Flink)
604     {
605         ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
606         if (ListDeviceInfo == DeviceInformation)
607         {
608             break;
609         }
610     }
611 
612     /* If not found, or if removable, bail out */
613     if (Entry == &DeviceExtension->DeviceListHead || DeviceInformation->Removable)
614     {
615         KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
616         goto ReleaseRDS;
617     }
618 
619     /* Get our device object */
620     Status = IoGetDeviceObjectPointer(&ListDeviceInfo->DeviceName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
621     if (!NT_SUCCESS(Status))
622     {
623         KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
624         goto ReleaseRDS;
625     }
626 
627     /* Mark mounted only if not unloading */
628     if (!(DeviceObject->Flags & DO_UNLOAD_PENDING))
629     {
630         InterlockedExchangeAdd(&ListDeviceInfo->MountState, 1);
631     }
632 
633     ObDereferenceObject(FileObject);
634 
635     /* Force default: no DB, and need for reconcile */
636     DeviceInformation->NeedsReconcile = TRUE;
637     DeviceInformation->NoDatabase = TRUE;
638     FailedFinding = FALSE;
639 
640     /* Remove any associated device that refers to the DB to reconcile */
641     for (Entry = DeviceExtension->DeviceListHead.Flink;
642          Entry != &DeviceExtension->DeviceListHead;
643          Entry = Entry->Flink)
644     {
645         ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
646 
647         EntryInfo = ListDeviceInfo->AssociatedDevicesHead.Flink;
648         while (EntryInfo != &ListDeviceInfo->AssociatedDevicesHead)
649         {
650             AssociatedDevice = CONTAINING_RECORD(EntryInfo, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
651             NextEntry = EntryInfo->Flink;
652 
653             if (AssociatedDevice->DeviceInformation == DeviceInformation)
654             {
655                 RemoveEntryList(&AssociatedDevice->AssociatedDevicesEntry);
656                 FreePool(AssociatedDevice->String.Buffer);
657                 FreePool(AssociatedDevice);
658             }
659 
660             EntryInfo = NextEntry;
661         }
662     }
663 
664     /* Open the remote database */
665     DatabaseHandle = OpenRemoteDatabase(DeviceInformation, FALSE);
666 
667     /* Prepare a string with reparse point index */
668     ReparseFile.Length = 0;
669     ReparseFile.MaximumLength = DeviceInformation->DeviceName.Length
670                                 + ReparseIndex.Length
671                                 + sizeof(UNICODE_NULL);
672     ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength);
673     if (!ReparseFile.Buffer)
674     {
675         if (DatabaseHandle != 0)
676         {
677             CloseRemoteDatabase(DatabaseHandle);
678         }
679         KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
680         goto ReleaseRDS;
681     }
682 
683     RtlAppendUnicodeStringToString(&ReparseFile, &DeviceInformation->DeviceName);
684     RtlAppendUnicodeStringToString(&ReparseFile, &ReparseIndex);
685     ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
686 
687     InitializeObjectAttributes(&ObjectAttributes,
688                                &ReparseFile,
689                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
690                                NULL,
691                                NULL);
692 
693     /* Open reparse point directory */
694     HardwareErrors = IoSetThreadHardErrorMode(FALSE);
695     Status = ZwOpenFile(&Handle,
696                         FILE_GENERIC_READ,
697                         &ObjectAttributes,
698                         &IoStatusBlock,
699                         FILE_SHARE_READ | FILE_SHARE_WRITE,
700                         FILE_SYNCHRONOUS_IO_ALERT);
701     IoSetThreadHardErrorMode(HardwareErrors);
702 
703     FreePool(ReparseFile.Buffer);
704 
705     if (!NT_SUCCESS(Status))
706     {
707         if (DatabaseHandle != 0)
708         {
709             TruncateRemoteDatabase(DatabaseHandle, 0);
710             CloseRemoteDatabase(DatabaseHandle);
711         }
712         KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
713         goto ReleaseRDS;
714     }
715 
716     /* Query reparse point information
717      * We only pay attention to mout point
718      */
719     RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer));
720     FileName.Buffer = FileNameBuffer;
721     FileName.Length = sizeof(FileNameBuffer);
722     FileName.MaximumLength = sizeof(FileNameBuffer);
723     ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT;
724     Status = ZwQueryDirectoryFile(Handle,
725                                   NULL,
726                                   NULL,
727                                   NULL,
728                                   &IoStatusBlock,
729                                   &ReparsePointInformation,
730                                   sizeof(FILE_REPARSE_POINT_INFORMATION),
731                                   FileReparsePointInformation,
732                                   TRUE,
733                                   &FileName,
734                                   FALSE);
735     if (!NT_SUCCESS(Status))
736     {
737         ZwClose(Handle);
738         if (DatabaseHandle != 0)
739         {
740             TruncateRemoteDatabase(DatabaseHandle, 0);
741             CloseRemoteDatabase(DatabaseHandle);
742         }
743         KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
744         goto ReleaseRDS;
745     }
746 
747     /* If we failed to open the remote DB previously,
748      * retry this time allowing migration (and thus, creation if required)
749      */
750     if (DatabaseHandle == 0)
751     {
752         DatabaseHandle = OpenRemoteDatabase(DeviceInformation, TRUE);
753         if (DatabaseHandle == 0)
754         {
755             KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
756             goto ReleaseRDS;
757         }
758     }
759 
760     KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
761 
762     /* Reset all the references to our DB entries */
763     Offset = 0;
764     for (;;)
765     {
766         DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset);
767         if (DatabaseEntry == NULL)
768         {
769             break;
770         }
771 
772         DatabaseEntry->EntryReferences = 0;
773         Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
774         if (!NT_SUCCESS(Status))
775         {
776             FreePool(DatabaseEntry);
777             goto CloseReparse;
778         }
779 
780         Offset += DatabaseEntry->EntrySize;
781         FreePool(DatabaseEntry);
782     }
783 
784     /* Init string for QueryVolumeName call */
785     SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
786     SymbolicName.Length = 0;
787     SymbolicName.Buffer = SymbolicNameBuffer;
788     Restart = TRUE;
789 
790     /* Start looping on reparse points */
791     for (;;)
792     {
793         RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
794         Status = ZwQueryDirectoryFile(Handle,
795                                       NULL,
796                                       NULL,
797                                       NULL,
798                                       &IoStatusBlock,
799                                       &ReparsePointInformation,
800                                       sizeof(FILE_REPARSE_POINT_INFORMATION),
801                                       FileReparsePointInformation,
802                                       TRUE,
803                                       Restart ? &FileName : NULL,
804                                       Restart);
805         /* Restart only once */
806         if (Restart)
807         {
808             Restart = FALSE;
809         }
810         else
811         {
812             /* If we get the same one, we're done, bail out */
813             if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
814                 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
815             {
816                 break;
817             }
818         }
819 
820         /* If querying failed, or if onloading, or if not returning mount points, bail out */
821         if (!NT_SUCCESS(Status) || Unloading || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
822         {
823             break;
824         }
825 
826         /* Get the volume name associated to the mount point */
827         Status = QueryVolumeName(Handle, &ReparsePointInformation, 0, &SymbolicName, &VolumeName);
828         if (!NT_SUCCESS(Status))
829         {
830             continue;
831         }
832 
833         /* Browse the DB to find the name */
834         Offset = 0;
835         for (;;)
836         {
837             UNICODE_STRING DbName;
838 
839             DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset);
840             if (DatabaseEntry == NULL)
841             {
842                 break;
843             }
844 
845             DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
846             DbName.Length = DbName.MaximumLength;
847             DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
848             /* Found, we're done! */
849             if (RtlEqualUnicodeString(&DbName, &SymbolicName, TRUE))
850             {
851                 break;
852             }
853 
854             Offset += DatabaseEntry->EntrySize;
855             FreePool(DatabaseEntry);
856         }
857 
858         /* If we found the mount point.... */
859         if (DatabaseEntry != NULL)
860         {
861             /* If it was referenced, reference it once more and update to remote */
862             if (DatabaseEntry->EntryReferences)
863             {
864                 ++DatabaseEntry->EntryReferences;
865                 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
866                 if (!NT_SUCCESS(Status))
867                 {
868                     goto FreeDBEntry;
869                 }
870 
871                 FreePool(DatabaseEntry);
872             }
873             else
874             {
875                 /* Query the Unique ID associated to that mount point in case it changed */
876                 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
877                 Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId);
878                 if (!NT_SUCCESS(Status))
879                 {
880                     /* If we failed doing so, reuse the old Unique ID and push it to master */
881                     Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry);
882                     if (!NT_SUCCESS(Status))
883                     {
884                         goto ReleaseDeviceLock;
885                     }
886 
887                     /* And then, reference & write the entry */
888                     ++DatabaseEntry->EntryReferences;
889                     Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
890                     if (!NT_SUCCESS(Status))
891                     {
892                         goto ReleaseDeviceLock;
893                     }
894 
895                     KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
896                     FreePool(DatabaseEntry);
897                 }
898                 /* If the Unique ID didn't change */
899                 else if (UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
900                          RtlCompareMemory(UniqueId->UniqueId,
901                                           (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
902                                           UniqueId->UniqueIdLength) == UniqueId->UniqueIdLength)
903                 {
904                     /* Reference the entry, and update to remote */
905                     ++DatabaseEntry->EntryReferences;
906                     Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
907                     if (!NT_SUCCESS(Status))
908                     {
909                         goto FreeUniqueId;
910                     }
911 
912                     FreePool(UniqueId);
913                     KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
914                     FreePool(DatabaseEntry);
915                 }
916                 /* Would, by chance, the Unique ID be present elsewhere? */
917                 else if (IsUniqueIdPresent(DeviceExtension, DatabaseEntry))
918                 {
919                     /* Push the ID to master */
920                     Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry);
921                     if (!NT_SUCCESS(Status))
922                     {
923                         goto FreeUniqueId;
924                     }
925 
926                     /* And then, reference & write the entry */
927                     ++DatabaseEntry->EntryReferences;
928                     Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
929                     if (!NT_SUCCESS(Status))
930                     {
931                         goto FreeUniqueId;
932                     }
933 
934                     FreePool(UniqueId);
935                     KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
936                     FreePool(DatabaseEntry);
937                 }
938                 else
939                 {
940                     /* OK, at that point, we're facing a totally unknown unique ID
941                      * So, get rid of the old entry, and recreate a new one with
942                      * the know unique ID
943                      */
944                     Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset);
945                     if (!NT_SUCCESS(Status))
946                     {
947                         goto FreeUniqueId;
948                     }
949 
950                     FreePool(DatabaseEntry);
951                     /* Allocate a new entry big enough */
952                     DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY));
953                     if (DatabaseEntry == NULL)
954                     {
955                        goto FreeUniqueId;
956                     }
957 
958                     /* Configure it */
959                     DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY);
960                     DatabaseEntry->EntryReferences = 1;
961                     DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
962                     DatabaseEntry->SymbolicNameLength = SymbolicName.Length;
963                     DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY);
964                     DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
965                     RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength);
966                     RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
967 
968                     /* And write it remotely */
969                     Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry);
970                     if (!NT_SUCCESS(Status))
971                     {
972                        FreePool(DatabaseEntry);
973                        goto FreeUniqueId;
974                     }
975 
976                     FreePool(UniqueId);
977                     FreePool(DatabaseEntry);
978                     KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
979                 }
980             }
981         }
982         else
983         {
984             /* We failed finding it remotely
985              * So, let's allocate a new remote DB entry
986              */
987             KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
988             /* To be able to do so, we need the device Unique ID, ask master */
989             Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId);
990             KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
991             if (NT_SUCCESS(Status))
992             {
993                 /* Allocate a new entry big enough */
994                 DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY));
995                 if (DatabaseEntry != NULL)
996                 {
997                     /* Configure it */
998                     DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY);
999                     DatabaseEntry->EntryReferences = 1;
1000                     DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
1001                     DatabaseEntry->SymbolicNameLength = SymbolicName.Length;
1002                     DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY);
1003                     DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
1004                     RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength);
1005                     RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
1006 
1007                     /* And write it remotely */
1008                     Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry);
1009                     FreePool(DatabaseEntry);
1010                     FreePool(UniqueId);
1011 
1012                     if (!NT_SUCCESS(Status))
1013                     {
1014                         goto FreeVolume;
1015                     }
1016                 }
1017                 else
1018                 {
1019                     FreePool(UniqueId);
1020                 }
1021             }
1022         }
1023 
1024         /* Find info about the device associated associated with the mount point */
1025         KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1026         Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &ListDeviceInfo);
1027         if (!NT_SUCCESS(Status))
1028         {
1029             FailedFinding = TRUE;
1030             FreePool(VolumeName.Buffer);
1031         }
1032         else
1033         {
1034             /* Associate the device with the currrent DB */
1035             AssociatedDevice = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY));
1036             if (AssociatedDevice == NULL)
1037             {
1038                 FreePool(VolumeName.Buffer);
1039             }
1040             else
1041             {
1042                 AssociatedDevice->DeviceInformation = DeviceInformation;
1043                 AssociatedDevice->String.Length = VolumeName.Length;
1044                 AssociatedDevice->String.MaximumLength = VolumeName.MaximumLength;
1045                 AssociatedDevice->String.Buffer = VolumeName.Buffer;
1046                 InsertTailList(&ListDeviceInfo->AssociatedDevicesHead, &AssociatedDevice->AssociatedDevicesEntry);
1047             }
1048 
1049             /* If we don't have to skip notifications, notify */
1050             if (!ListDeviceInfo->SkipNotifications)
1051             {
1052                 PostOnlineNotification(DeviceExtension, &ListDeviceInfo->SymbolicName);
1053             }
1054         }
1055 
1056         KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1057     }
1058 
1059     /* We don't need mount points any longer */
1060     ZwClose(Handle);
1061 
1062     /* Look for the DB again */
1063     KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1064     for (Entry = DeviceExtension->DeviceListHead.Flink;
1065          Entry != &DeviceExtension->DeviceListHead;
1066          Entry = Entry->Flink)
1067     {
1068         ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
1069         if (ListDeviceInfo == DeviceInformation)
1070         {
1071             break;
1072         }
1073     }
1074 
1075     if (Entry == &DeviceExtension->DeviceListHead)
1076     {
1077         ListDeviceInfo = NULL;
1078     }
1079 
1080     /* Start the pruning loop */
1081     Offset = 0;
1082     for (;;)
1083     {
1084         /* Get the entry */
1085         DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset);
1086         if (DatabaseEntry == NULL)
1087         {
1088             break;
1089         }
1090 
1091         /* It's not referenced anylonger? Prune it */
1092         if (DatabaseEntry->EntryReferences == 0)
1093         {
1094             Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset);
1095             if (!NT_SUCCESS(Status))
1096             {
1097                 FreePool(DatabaseEntry);
1098                 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1099                 goto CloseRDB;
1100             }
1101         }
1102         /* Update the Unique IDs to reflect the changes we might have done previously */
1103         else
1104         {
1105             if (ListDeviceInfo != NULL)
1106             {
1107                 UpdateReplicatedUniqueIds(ListDeviceInfo, DatabaseEntry);
1108             }
1109 
1110             Offset += DatabaseEntry->EntrySize;
1111         }
1112 
1113         FreePool(DatabaseEntry);
1114     }
1115 
1116     /* We do have a DB now :-) */
1117     if (ListDeviceInfo != NULL && !FailedFinding)
1118     {
1119         DeviceInformation->NoDatabase = FALSE;
1120     }
1121 
1122     KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1123 
1124     goto CloseRDB;
1125 
1126 FreeUniqueId:
1127     FreePool(UniqueId);
1128 ReleaseDeviceLock:
1129     KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1130 FreeDBEntry:
1131     FreePool(DatabaseEntry);
1132 FreeVolume:
1133     FreePool(VolumeName.Buffer);
1134 CloseReparse:
1135     ZwClose(Handle);
1136 CloseRDB:
1137     CloseRemoteDatabase(DatabaseHandle);
1138 ReleaseRDS:
1139     ReleaseRemoteDatabaseSemaphore(DeviceExtension);
1140     return;
1141 }
1142 
1143 /*
1144  * @implemented
1145  */
1146 VOID
1147 NTAPI
1148 WorkerThread(IN PDEVICE_OBJECT DeviceObject,
1149              IN PVOID Context)
1150 {
1151     ULONG i;
1152     KEVENT Event;
1153     KIRQL OldIrql;
1154     NTSTATUS Status;
1155     HANDLE SafeEvent;
1156     PLIST_ENTRY Entry;
1157     LARGE_INTEGER Timeout;
1158     PRECONCILE_WORK_ITEM WorkItem;
1159     PDEVICE_EXTENSION DeviceExtension;
1160     OBJECT_ATTRIBUTES ObjectAttributes;
1161 
1162     UNREFERENCED_PARAMETER(DeviceObject);
1163 
1164     InitializeObjectAttributes(&ObjectAttributes,
1165                                &SafeVolumes,
1166                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1167                                NULL,
1168                                NULL);
1169     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1170     Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
1171 
1172     /* Wait as long as possible for clearance from autochk
1173      * We will write remote databases only if it is safe
1174      * to access volumes.
1175      * First, given we start before SMSS, wait for the
1176      * event creation.
1177      */
1178     i = 0;
1179     do
1180     {
1181         /* If we started to shutdown, stop waiting forever and jump to last attempt */
1182         if (Unloading)
1183         {
1184             i = 999;
1185         }
1186         else
1187         {
1188             /* Attempt to open the event */
1189             Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes);
1190             if (NT_SUCCESS(Status))
1191             {
1192                 break;
1193             }
1194 
1195             /* Wait a bit to give SMSS a chance to create the event */
1196             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
1197         }
1198 
1199         ++i;
1200     }
1201     while (i < 1000);
1202 
1203     /* We managed to open the event, wait until autochk signals it */
1204     if (i < 1000)
1205     {
1206         do
1207         {
1208             Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout);
1209         }
1210         while (Status == STATUS_TIMEOUT && !Unloading);
1211 
1212         ZwClose(SafeEvent);
1213     }
1214 
1215     DeviceExtension = Context;
1216 
1217     InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1);
1218 
1219     /* Acquire workers lock */
1220     KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
1221 
1222     KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1223 
1224     /* Ensure there are workers */
1225     while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead)))
1226     {
1227         /* Unqueue a worker */
1228         Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead));
1229         WorkItem = CONTAINING_RECORD(Entry,
1230                                      RECONCILE_WORK_ITEM,
1231                                      WorkerQueueListEntry);
1232 
1233         KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1234 
1235         /* Call it */
1236         WorkItem->WorkerRoutine(WorkItem->Context);
1237 
1238         IoFreeWorkItem(WorkItem->WorkItem);
1239         FreePool(WorkItem);
1240 
1241         if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) < 0)
1242         {
1243             return;
1244         }
1245 
1246         KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
1247         KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1248     }
1249     KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1250 
1251     InterlockedDecrement(&(DeviceExtension->WorkerReferences));
1252 
1253     /* Reset event */
1254     KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE);
1255 }
1256 
1257 /*
1258  * @implemented
1259  */
1260 NTSTATUS
1261 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension,
1262               IN PRECONCILE_WORK_ITEM WorkItem,
1263               IN PVOID Context)
1264 {
1265     KIRQL OldIrql;
1266 
1267     WorkItem->Context = Context;
1268 
1269     /* When called, lock is already acquired */
1270 
1271     /* If noone (-1 as references), start to work */
1272     if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)) == 0)
1273     {
1274         IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension);
1275     }
1276 
1277     /* Otherwise queue worker for delayed execution */
1278     KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1279     InsertTailList(&(DeviceExtension->WorkerQueueListHead),
1280                    &(WorkItem->WorkerQueueListEntry));
1281     KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1282 
1283     KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE);
1284 
1285     return STATUS_SUCCESS;
1286 }
1287 
1288 /*
1289  * @implemented
1290  */
1291 NTSTATUS
1292 QueryVolumeName(IN HANDLE RootDirectory,
1293                 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation,
1294                 IN PUNICODE_STRING FileName OPTIONAL,
1295                 OUT PUNICODE_STRING SymbolicName,
1296                 OUT PUNICODE_STRING VolumeName)
1297 {
1298     HANDLE Handle;
1299     NTSTATUS Status;
1300     ULONG NeededLength;
1301     IO_STATUS_BLOCK IoStatusBlock;
1302     OBJECT_ATTRIBUTES ObjectAttributes;
1303     PFILE_NAME_INFORMATION FileNameInfo;
1304     PREPARSE_DATA_BUFFER ReparseDataBuffer;
1305 
1306     UNREFERENCED_PARAMETER(ReparsePointInformation);
1307 
1308     if (!FileName)
1309     {
1310         InitializeObjectAttributes(&ObjectAttributes,
1311                                    NULL,
1312                                    OBJ_KERNEL_HANDLE,
1313                                    RootDirectory,
1314                                    NULL);
1315     }
1316     else
1317     {
1318         InitializeObjectAttributes(&ObjectAttributes,
1319                                    FileName,
1320                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1321                                    NULL,
1322                                    NULL);
1323     }
1324 
1325     /* Open volume */
1326     Status = ZwOpenFile(&Handle,
1327                         SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1328                         &ObjectAttributes,
1329                         &IoStatusBlock,
1330                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1331                         (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT :
1332                                      FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
1333     if (!NT_SUCCESS(Status))
1334     {
1335         return Status;
1336     }
1337 
1338     /* Get the reparse point data */
1339     ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1340     if (!ReparseDataBuffer)
1341     {
1342         ZwClose(Handle);
1343         return STATUS_INSUFFICIENT_RESOURCES;
1344     }
1345 
1346     Status = ZwFsControlFile(Handle,
1347                              0,
1348                              NULL,
1349                              NULL,
1350                              &IoStatusBlock,
1351                              FSCTL_GET_REPARSE_POINT,
1352                              NULL,
1353                              0,
1354                              ReparseDataBuffer,
1355                              MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1356     if (!NT_SUCCESS(Status))
1357     {
1358         FreePool(ReparseDataBuffer);
1359         ZwClose(Handle);
1360         return Status;
1361     }
1362 
1363     /* Check that name can fit in buffer */
1364     if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength)
1365     {
1366         FreePool(ReparseDataBuffer);
1367         ZwClose(Handle);
1368         return STATUS_BUFFER_TOO_SMALL;
1369     }
1370 
1371     /* Copy symbolic name */
1372     SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength;
1373     RtlCopyMemory(SymbolicName->Buffer,
1374                   (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer +
1375                                      ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset),
1376                   ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength);
1377 
1378     FreePool(ReparseDataBuffer);
1379 
1380     /* Name has to \ terminated */
1381     if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\')
1382     {
1383         ZwClose(Handle);
1384         return STATUS_INVALID_PARAMETER;
1385     }
1386 
1387     /* So that we can delete it, and match mountmgr requirements */
1388     SymbolicName->Length -= sizeof(WCHAR);
1389     SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1390 
1391     /* Also ensure it's really a volume name... */
1392     if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName))
1393     {
1394         ZwClose(Handle);
1395         return STATUS_INVALID_PARAMETER;
1396     }
1397 
1398     /* Now prepare to really get the name */
1399     FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR));
1400     if (!FileNameInfo)
1401     {
1402         ZwClose(Handle);
1403         return STATUS_INSUFFICIENT_RESOURCES;
1404     }
1405 
1406     Status = ZwQueryInformationFile(Handle,
1407                                     &IoStatusBlock,
1408                                     FileNameInfo,
1409                                     sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR),
1410                                     FileNameInformation);
1411     if (Status == STATUS_BUFFER_OVERFLOW)
1412     {
1413         /* As expected... Reallocate with proper size */
1414         NeededLength = FileNameInfo->FileNameLength;
1415         FreePool(FileNameInfo);
1416 
1417         FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength);
1418         if (!FileNameInfo)
1419         {
1420             ZwClose(Handle);
1421             return STATUS_INSUFFICIENT_RESOURCES;
1422         }
1423 
1424         /* And query name */
1425         Status = ZwQueryInformationFile(Handle,
1426                                         &IoStatusBlock,
1427                                         FileNameInfo,
1428                                         sizeof(FILE_NAME_INFORMATION) + NeededLength,
1429                                         FileNameInformation);
1430     }
1431 
1432     ZwClose(Handle);
1433 
1434     if (!NT_SUCCESS(Status))
1435     {
1436         return Status;
1437     }
1438 
1439     /* Return the volume name */
1440     VolumeName->Length = (USHORT)FileNameInfo->FileNameLength;
1441     VolumeName->MaximumLength = (USHORT)FileNameInfo->FileNameLength + sizeof(WCHAR);
1442     VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength);
1443     if (!VolumeName->Buffer)
1444     {
1445         return STATUS_INSUFFICIENT_RESOURCES;
1446     }
1447 
1448     RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength);
1449     VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
1450 
1451     FreePool(FileNameInfo);
1452 
1453     return STATUS_SUCCESS;
1454 }
1455 
1456 /*
1457  * @implemented
1458  */
1459 VOID
1460 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
1461                      IN PDEVICE_INFORMATION DeviceInformation)
1462 {
1463     HANDLE Handle;
1464     NTSTATUS Status;
1465     BOOLEAN RestartScan;
1466     IO_STATUS_BLOCK IoStatusBlock;
1467     OBJECT_ATTRIBUTES ObjectAttributes;
1468     PDEVICE_INFORMATION VolumeDeviceInformation;
1469     WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64];
1470     UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName;
1471     FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation;
1472 
1473     /* Removable devices don't have remote database on them */
1474     if (DeviceInformation->Removable)
1475     {
1476         return;
1477     }
1478 
1479     /* Prepare a string with reparse point index */
1480     ReparseFile.Length = 0;
1481     ReparseFile.MaximumLength = DeviceInformation->DeviceName.Length
1482                                 + ReparseIndex.Length
1483                                 + sizeof(UNICODE_NULL);
1484     ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength);
1485     if (!ReparseFile.Buffer)
1486     {
1487         return;
1488     }
1489 
1490     RtlAppendUnicodeStringToString(&ReparseFile, &DeviceInformation->DeviceName);
1491     RtlAppendUnicodeStringToString(&ReparseFile, &ReparseIndex);
1492     ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
1493 
1494     InitializeObjectAttributes(&ObjectAttributes,
1495                                &ReparseFile,
1496                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1497                                NULL,
1498                                NULL);
1499 
1500     /* Open reparse point */
1501     Status = ZwOpenFile(&Handle,
1502                         FILE_GENERIC_READ,
1503                         &ObjectAttributes,
1504                         &IoStatusBlock,
1505                         FILE_SHARE_READ | FILE_SHARE_WRITE,
1506                         FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT);
1507     FreePool(ReparseFile.Buffer);
1508     if (!NT_SUCCESS(Status))
1509     {
1510         DeviceInformation->NoDatabase = FALSE;
1511         return;
1512     }
1513 
1514     /* Query reparse point information
1515      * We only pay attention to mout point
1516      */
1517     RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer));
1518     FileName.Buffer = FileNameBuffer;
1519     FileName.Length = sizeof(FileNameBuffer);
1520     FileName.MaximumLength = sizeof(FileNameBuffer);
1521     ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT;
1522     Status = ZwQueryDirectoryFile(Handle,
1523                                   NULL,
1524                                   NULL,
1525                                   NULL,
1526                                   &IoStatusBlock,
1527                                   &ReparsePointInformation,
1528                                   sizeof(FILE_REPARSE_POINT_INFORMATION),
1529                                   FileReparsePointInformation,
1530                                   TRUE,
1531                                   &FileName,
1532                                   FALSE);
1533     if (!NT_SUCCESS(Status))
1534     {
1535         ZwClose(Handle);
1536         return;
1537     }
1538 
1539     RestartScan = TRUE;
1540 
1541     /* Query mount points */
1542     while (TRUE)
1543     {
1544         SymbolicName.Length = 0;
1545         SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
1546         SymbolicName.Buffer = SymbolicNameBuffer;
1547         RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
1548 
1549         Status = ZwQueryDirectoryFile(Handle,
1550                                       NULL,
1551                                       NULL,
1552                                       NULL,
1553                                       &IoStatusBlock,
1554                                       &ReparsePointInformation,
1555                                       sizeof(FILE_REPARSE_POINT_INFORMATION),
1556                                       FileReparsePointInformation,
1557                                       TRUE,
1558                                       (RestartScan) ? &FileName : NULL,
1559                                       RestartScan);
1560          if (!RestartScan)
1561          {
1562              if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
1563                  ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
1564              {
1565                  break;
1566              }
1567          }
1568          else
1569          {
1570              RestartScan = FALSE;
1571          }
1572 
1573          if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
1574          {
1575              break;
1576          }
1577 
1578          /* Get the volume name associated to the mount point */
1579          Status = QueryVolumeName(Handle,
1580                                   &ReparsePointInformation,
1581                                   NULL, &SymbolicName,
1582                                   &VolumeName);
1583          if (!NT_SUCCESS(Status))
1584          {
1585              continue;
1586          }
1587 
1588          FreePool(VolumeName.Buffer);
1589 
1590          /* Get its information */
1591          Status = FindDeviceInfo(DeviceExtension, &SymbolicName,
1592                                  FALSE, &VolumeDeviceInformation);
1593          if (!NT_SUCCESS(Status))
1594          {
1595              DeviceInformation->NoDatabase = TRUE;
1596              continue;
1597          }
1598 
1599          /* If notification are enabled, mark it online */
1600          if (!DeviceInformation->SkipNotifications)
1601          {
1602              PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName);
1603          }
1604     }
1605 
1606     ZwClose(Handle);
1607 }
1608 
1609 /*
1610  * @implemented
1611  */
1612 VOID
1613 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension,
1614                                 IN PDEVICE_INFORMATION DeviceInformation)
1615 {
1616     PRECONCILE_WORK_ITEM WorkItem;
1617 
1618     /* Removable devices don't have remote database */
1619     if (DeviceInformation->Removable)
1620     {
1621         return;
1622     }
1623 
1624     /* Allocate a work item */
1625     WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM));
1626     if (!WorkItem)
1627     {
1628         return;
1629     }
1630 
1631     WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
1632     if (!WorkItem->WorkItem)
1633     {
1634         FreePool(WorkItem);
1635         return;
1636     }
1637 
1638     /* And queue it */
1639     WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker;
1640     WorkItem->DeviceExtension = DeviceExtension;
1641     WorkItem->DeviceInformation = DeviceInformation;
1642     QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension));
1643 
1644     /* If the worker thread isn't started yet, automatic drive letter is
1645      * enabled but automount disabled, manually set mounted volumes online.
1646      * Otherwise, they will be set online during database reconciliation. */
1647     if (DeviceExtension->WorkerThreadStatus == 0 &&
1648         DeviceExtension->AutomaticDriveLetter &&
1649         DeviceExtension->NoAutoMount)
1650     {
1651         OnlineMountedVolumes(DeviceExtension, DeviceInformation);
1652     }
1653 }
1654 
1655 /*
1656  * @implemented
1657  */
1658 VOID
1659 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension)
1660 {
1661     PLIST_ENTRY NextEntry;
1662     PDEVICE_INFORMATION DeviceInformation;
1663 
1664     /* Browse all the devices */
1665     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1666          NextEntry != &(DeviceExtension->DeviceListHead);
1667          NextEntry = NextEntry->Flink)
1668     {
1669         DeviceInformation = CONTAINING_RECORD(NextEntry,
1670                                               DEVICE_INFORMATION,
1671                                               DeviceListEntry);
1672         /* If it's not removable, then, it might have a database to sync */
1673         if (!DeviceInformation->Removable)
1674         {
1675             ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
1676         }
1677     }
1678 }
1679 
1680 /*
1681  * @implemented
1682  */
1683 VOID
1684 NTAPI
1685 CreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject,
1686                            IN PVOID Context)
1687 {
1688     NTSTATUS Status;
1689     HANDLE Database = 0;
1690     UNICODE_STRING DatabaseName;
1691     PMIGRATE_WORK_ITEM WorkItem;
1692     IO_STATUS_BLOCK IoStatusBlock;
1693     OBJECT_ATTRIBUTES ObjectAttributes;
1694     PDEVICE_INFORMATION DeviceInformation;
1695 
1696     UNREFERENCED_PARAMETER(DeviceObject);
1697 
1698     /* Extract context */
1699     WorkItem = Context;
1700     DeviceInformation = WorkItem->DeviceInformation;
1701 
1702     /* Reconstruct appropriate string */
1703     DatabaseName.Length = 0;
1704     DatabaseName.MaximumLength = DeviceInformation->DeviceName.Length
1705                                  + RemoteDatabase.Length
1706                                  + sizeof(UNICODE_NULL);
1707     DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength);
1708     if (DatabaseName.Buffer == NULL)
1709     {
1710         Status = STATUS_INSUFFICIENT_RESOURCES;
1711         goto Cleanup;
1712     }
1713 
1714     /* Create the required folder (in which the database will be stored
1715      * \System Volume Information at root of the volume
1716      */
1717     Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName));
1718     if (!NT_SUCCESS(Status))
1719     {
1720         goto Cleanup;
1721     }
1722 
1723     /* Finish initiating strings */
1724     RtlAppendUnicodeStringToString(&DatabaseName, &DeviceInformation->DeviceName);
1725     RtlAppendUnicodeStringToString(&DatabaseName, &RemoteDatabase);
1726     DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1727 
1728     /* Create database */
1729     InitializeObjectAttributes(&ObjectAttributes,
1730                                &DatabaseName,
1731                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1732                                NULL,
1733                                NULL);
1734 
1735     Status = IoCreateFile(&Database,
1736                           SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1737                           FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA |
1738                           FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1739                           &ObjectAttributes,
1740                           &IoStatusBlock,
1741                           NULL,
1742                           FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1743                           0,
1744                           FILE_CREATE,
1745                           FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1746                           NULL,
1747                           0,
1748                           CreateFileTypeNone,
1749                           NULL,
1750                           IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING);
1751     if (!NT_SUCCESS(Status))
1752     {
1753         if (Status == STATUS_STOPPED_ON_SYMLINK)
1754         {
1755             DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1756         }
1757 
1758         Database = 0;
1759         goto Cleanup;
1760     }
1761 
1762 Cleanup:
1763     if (DatabaseName.Buffer)
1764     {
1765         FreePool(DatabaseName.Buffer);
1766     }
1767 
1768     if (NT_SUCCESS(Status))
1769     {
1770         DeviceInformation->Migrated = 1;
1771     }
1772     else if (Database != 0)
1773     {
1774         ZwClose(Database);
1775     }
1776 
1777     IoFreeWorkItem(WorkItem->WorkItem);
1778 
1779     WorkItem->WorkItem = NULL;
1780     WorkItem->Status = Status;
1781     WorkItem->Database = Database;
1782 
1783     KeSetEvent(WorkItem->Event, 0, FALSE);
1784 }
1785 
1786 /*
1787  * @implemented
1788  */
1789 NTSTATUS
1790 CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1791                      IN OUT PHANDLE Database)
1792 {
1793     KEVENT Event;
1794     NTSTATUS Status;
1795     PMIGRATE_WORK_ITEM WorkItem;
1796 
1797     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1798 
1799     /* Allocate a work item dedicated to migration */
1800     WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM));
1801     if (!WorkItem)
1802     {
1803         *Database = 0;
1804         return STATUS_INSUFFICIENT_RESOURCES;
1805     }
1806 
1807     RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM));
1808     WorkItem->Event = &Event;
1809     WorkItem->DeviceInformation = DeviceInformation;
1810     WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject);
1811     if (!WorkItem->WorkItem)
1812     {
1813         FreePool(WorkItem);
1814         *Database = 0;
1815         return STATUS_INSUFFICIENT_RESOURCES;
1816     }
1817 
1818     /* And queue it */
1819     IoQueueWorkItem(WorkItem->WorkItem,
1820                     CreateRemoteDatabaseWorker,
1821                     DelayedWorkQueue,
1822                     WorkItem);
1823 
1824     KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1825     Status = WorkItem->Status;
1826 
1827     *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
1828 
1829     FreePool(WorkItem);
1830     return Status;
1831 }
1832 
1833 /*
1834  * @implemented
1835  */
1836 HANDLE
1837 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1838                    IN BOOLEAN MigrateDatabase)
1839 {
1840     HANDLE Database;
1841     NTSTATUS Status;
1842     BOOLEAN PreviousMode;
1843     IO_STATUS_BLOCK IoStatusBlock;
1844     OBJECT_ATTRIBUTES ObjectAttributes;
1845     UNICODE_STRING DeviceRemoteDatabase;
1846 
1847     Database = 0;
1848 
1849     /* Get database name */
1850     DeviceRemoteDatabase.Length = 0;
1851     DeviceRemoteDatabase.MaximumLength = DeviceInformation->DeviceName.Length
1852                                          + RemoteDatabase.Length
1853                                          + sizeof(UNICODE_NULL);
1854     DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength);
1855     if (!DeviceRemoteDatabase.Buffer)
1856     {
1857         return 0;
1858     }
1859 
1860     RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &DeviceInformation->DeviceName);
1861     RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &RemoteDatabase);
1862     DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL;
1863 
1864     /* Open database */
1865     InitializeObjectAttributes(&ObjectAttributes,
1866                                &DeviceRemoteDatabase,
1867                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1868                                NULL,
1869                                NULL);
1870 
1871     /* Disable hard errors */
1872     PreviousMode = IoSetThreadHardErrorMode(FALSE);
1873 
1874     Status = IoCreateFile(&Database,
1875                           SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1876                           FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA |
1877                           FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1878                           &ObjectAttributes,
1879                           &IoStatusBlock,
1880                           NULL,
1881                           FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1882                           0,
1883                           (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN,
1884                           FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1885                           NULL,
1886                           0,
1887                           CreateFileTypeNone,
1888                           NULL,
1889                           IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING);
1890     if (Status == STATUS_STOPPED_ON_SYMLINK)
1891     {
1892         DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1893     }
1894 
1895     /* If base it to be migrated and was opened successfully, go ahead */
1896     if (MigrateDatabase && NT_SUCCESS(Status))
1897     {
1898         CreateRemoteDatabase(DeviceInformation, &Database);
1899     }
1900 
1901     IoSetThreadHardErrorMode(PreviousMode);
1902     FreePool(DeviceRemoteDatabase.Buffer);
1903 
1904     return Database;
1905 }
1906 
1907 /*
1908  * @implemented
1909  */
1910 VOID
1911 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,
1912                              IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
1913                              IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
1914 {
1915     LONG Offset = 0;
1916     HANDLE Database;
1917     PDATABASE_ENTRY Entry, NewEntry;
1918     NTSTATUS Status = STATUS_SUCCESS;
1919 
1920     /* Open the remote database */
1921     Database = OpenRemoteDatabase(DeviceInformation, FALSE);
1922     if (!Database)
1923     {
1924         return;
1925     }
1926 
1927     /* Get all the entries */
1928     do
1929     {
1930         Entry = GetRemoteDatabaseEntry(Database, Offset);
1931         if (!Entry)
1932         {
1933             break;
1934         }
1935 
1936         /* Not the correct entry, skip it */
1937         if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength)
1938         {
1939             Offset += Entry->EntrySize;
1940             FreePool(Entry);
1941             continue;
1942         }
1943 
1944         /* Not the correct entry, skip it */
1945         if (RtlCompareMemory(OldUniqueId->UniqueId,
1946                              (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset),
1947                              Entry->UniqueIdLength) != Entry->UniqueIdLength)
1948         {
1949             Offset += Entry->EntrySize;
1950             FreePool(Entry);
1951             continue;
1952         }
1953 
1954         /* Here, we have the correct entry */
1955         NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength);
1956         if (!NewEntry)
1957         {
1958             Offset += Entry->EntrySize;
1959             FreePool(Entry);
1960             continue;
1961         }
1962 
1963         /* Recreate the entry from the previous one */
1964         NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength;
1965         NewEntry->EntryReferences = Entry->EntryReferences;
1966         NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
1967         NewEntry->SymbolicNameLength = Entry->SymbolicNameLength;
1968         NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY);
1969         NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength;
1970         RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset),
1971                       (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset),
1972                       NewEntry->SymbolicNameLength);
1973         RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset),
1974                       NewUniqueId->UniqueId, NewEntry->UniqueIdLength);
1975 
1976         /* Delete old entry */
1977         Status = DeleteRemoteDatabaseEntry(Database, Offset);
1978         if (!NT_SUCCESS(Status))
1979         {
1980             FreePool(Entry);
1981             FreePool(NewEntry);
1982             break;
1983         }
1984 
1985         /* And replace with new one */
1986         Status = AddRemoteDatabaseEntry(Database, NewEntry);
1987         FreePool(Entry);
1988         FreePool(NewEntry);
1989     } while (NT_SUCCESS(Status));
1990 
1991     CloseRemoteDatabase(Database);
1992 
1993     return;
1994 }
1995 
1996 /*
1997  * @implemented
1998  */
1999 NTSTATUS
2000 NTAPI
2001 DeleteDriveLetterRoutine(IN PWSTR ValueName,
2002                          IN ULONG ValueType,
2003                          IN PVOID ValueData,
2004                          IN ULONG ValueLength,
2005                          IN PVOID Context,
2006                          IN PVOID EntryContext)
2007 {
2008     PMOUNTDEV_UNIQUE_ID UniqueId;
2009     UNICODE_STRING RegistryEntry;
2010 
2011     UNREFERENCED_PARAMETER(EntryContext);
2012 
2013     if (ValueType != REG_BINARY)
2014     {
2015         return STATUS_SUCCESS;
2016     }
2017 
2018     UniqueId = Context;
2019 
2020     /* First ensure we have the correct data */
2021     if (UniqueId->UniqueIdLength != ValueLength)
2022     {
2023         return STATUS_SUCCESS;
2024     }
2025 
2026     if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2027     {
2028         return STATUS_SUCCESS;
2029     }
2030 
2031     RtlInitUnicodeString(&RegistryEntry, ValueName);
2032 
2033     /* Then, it's a drive letter, erase it */
2034     if (IsDriveLetter(&RegistryEntry))
2035     {
2036         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2037                                DatabasePath,
2038                                ValueName);
2039     }
2040 
2041     return STATUS_SUCCESS;
2042 }
2043 
2044 /*
2045  * @implemented
2046  */
2047 VOID
2048 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2049 {
2050     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2051 
2052     RtlZeroMemory(QueryTable, sizeof(QueryTable));
2053     QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
2054 
2055     RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2056                            DatabasePath,
2057                            QueryTable,
2058                            UniqueId,
2059                            NULL);
2060 }
2061 
2062 /*
2063  * @implemented
2064  */
2065 NTSTATUS
2066 NTAPI
2067 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,
2068                                 IN ULONG ValueType,
2069                                 IN PVOID ValueData,
2070                                 IN ULONG ValueLength,
2071                                 IN PVOID Context,
2072                                 IN PVOID EntryContext)
2073 {
2074     PMOUNTDEV_UNIQUE_ID UniqueId = Context;
2075 
2076     UNREFERENCED_PARAMETER(EntryContext);
2077 
2078     /* Ensure we have correct input */
2079     if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
2080         UniqueId->UniqueIdLength != ValueLength)
2081     {
2082         return STATUS_SUCCESS;
2083     }
2084 
2085     /* And then, if unique ID matching, delete entry */
2086     if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2087     {
2088         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2089                                DatabasePath,
2090                                ValueName);
2091     }
2092 
2093     return STATUS_SUCCESS;
2094 }
2095 
2096 /*
2097  * @implemented
2098  */
2099 VOID
2100 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2101 {
2102     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2103 
2104     RtlZeroMemory(QueryTable, sizeof(QueryTable));
2105     QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
2106 
2107     RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2108                            DatabasePath,
2109                            QueryTable,
2110                            UniqueId,
2111                            NULL);
2112 }
2113