xref: /reactos/drivers/storage/mountmgr/database.c (revision 5f263560)
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
GetRemoteDatabaseSize(IN HANDLE Database)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
AddRemoteDatabaseEntry(IN HANDLE Database,IN PDATABASE_ENTRY Entry)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
CloseRemoteDatabase(IN HANDLE Database)82 CloseRemoteDatabase(IN HANDLE Database)
83 {
84     return ZwClose(Database);
85 }
86 
87 /*
88  * @implemented
89  */
90 NTSTATUS
TruncateRemoteDatabase(IN HANDLE Database,IN LONG NewSize)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
GetRemoteDatabaseEntry(IN HANDLE Database,IN LONG StartingOffset)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
WriteRemoteDatabaseEntry(IN HANDLE Database,IN LONG Offset,IN PDATABASE_ENTRY Entry)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
DeleteRemoteDatabaseEntry(IN HANDLE Database,IN LONG StartingOffset)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
DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink,IN PMOUNTDEV_UNIQUE_ID UniqueId)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
WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)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
ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)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
QueryUniqueIdQueryRoutine(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension,IN PUNICODE_STRING SymbolicName,OUT PMOUNTDEV_UNIQUE_ID * UniqueId)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
WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension,IN PDATABASE_ENTRY DatabaseEntry)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
ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter)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
WorkerThread(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)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
QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension,IN PRECONCILE_WORK_ITEM WorkItem,IN PVOID Context)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
QueryVolumeName(IN HANDLE RootDirectory,IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation,IN PUNICODE_STRING FileName OPTIONAL,OUT PUNICODE_STRING SymbolicName,OUT PUNICODE_STRING VolumeName)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
OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension,IN PDEVICE_INFORMATION DeviceInformation)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
ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension,IN PDEVICE_INFORMATION DeviceInformation)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
ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension)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
CreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)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
CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,IN OUT PHANDLE Database)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
OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,IN BOOLEAN MigrateDatabase)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
ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,IN PMOUNTDEV_UNIQUE_ID OldUniqueId,IN PMOUNTDEV_UNIQUE_ID NewUniqueId)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
DeleteDriveLetterRoutine(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)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
DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)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