a025f7db3ec0bbaf87b4bd75ab56c6c8c88a86ce
[reactos.git] / drivers / filters / mountmgr / database.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2011-2012 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/mountmgr/mountmgr.c
22 * PURPOSE: Mount Manager - remote/local database handler
23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
24 */
25
26 #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 = DeviceInformation->DeviceName.Length + ReparseIndex.Length;
669 ReparseFile.MaximumLength = ReparseFile.Length + sizeof(UNICODE_NULL);
670 ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength);
671 if (ReparseFile.Buffer == NULL)
672 {
673 if (DatabaseHandle != 0)
674 {
675 CloseRemoteDatabase(DatabaseHandle);
676 }
677 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
678 goto ReleaseRDS;
679 }
680
681 RtlCopyMemory(ReparseFile.Buffer, DeviceInformation->DeviceName.Buffer,
682 DeviceInformation->DeviceName.Length);
683 RtlCopyMemory((PVOID)((ULONG_PTR)ReparseFile.Buffer + DeviceInformation->DeviceName.Length),
684 ReparseIndex.Buffer, ReparseIndex.Length);
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, start to work */
1272 if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)))
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 = DeviceInformation->DeviceName.Length + ReparseIndex.Length;
1481 ReparseFile.MaximumLength = ReparseFile.Length + sizeof(UNICODE_NULL);
1482 ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength);
1483 if (!ReparseFile.Buffer)
1484 {
1485 return;
1486 }
1487
1488 RtlCopyMemory(ReparseFile.Buffer, DeviceInformation->DeviceName.Buffer,
1489 DeviceInformation->DeviceName.Length);
1490 RtlCopyMemory((PVOID)((ULONG_PTR)ReparseFile.Buffer + DeviceInformation->DeviceName.Length),
1491 ReparseFile.Buffer, ReparseFile.Length);
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 there's no automount, and automatic letters
1645 * all volumes to find those online and notify there presence
1646 */
1647 if (DeviceExtension->WorkerThreadStatus == 0 &&
1648 DeviceExtension->AutomaticDriveLetter == 1 &&
1649 DeviceExtension->NoAutoMount == FALSE)
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 = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
1704 DatabaseName.MaximumLength = DatabaseName.Length + sizeof(WCHAR);
1705 DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength);
1706 if (DatabaseName.Buffer == NULL)
1707 {
1708 Status = STATUS_INSUFFICIENT_RESOURCES;
1709 goto Cleanup;
1710 }
1711
1712 /* Create the required folder (in which the database will be stored
1713 * \System Volume Information at root of the volume
1714 */
1715 Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName));
1716 if (!NT_SUCCESS(Status))
1717 {
1718 goto Cleanup;
1719 }
1720
1721 /* Finish initiating strings */
1722 RtlCopyMemory(DatabaseName.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
1723 RtlCopyMemory(DatabaseName.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
1724 RemoteDatabase.Buffer, RemoteDatabase.Length);
1725 DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1726
1727 /* Create database */
1728 InitializeObjectAttributes(&ObjectAttributes,
1729 &DatabaseName,
1730 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1731 NULL,
1732 NULL);
1733
1734 Status = IoCreateFile(&Database,
1735 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1736 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA |
1737 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1738 &ObjectAttributes,
1739 &IoStatusBlock,
1740 NULL,
1741 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1742 0,
1743 FILE_CREATE,
1744 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1745 NULL,
1746 0,
1747 CreateFileTypeNone,
1748 NULL,
1749 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING);
1750 if (!NT_SUCCESS(Status))
1751 {
1752 if (Status == STATUS_STOPPED_ON_SYMLINK)
1753 {
1754 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1755 }
1756
1757 Database = 0;
1758 goto Cleanup;
1759 }
1760
1761 Cleanup:
1762 if (DatabaseName.Buffer)
1763 {
1764 FreePool(DatabaseName.Buffer);
1765 }
1766
1767 if (NT_SUCCESS(Status))
1768 {
1769 DeviceInformation->Migrated = 1;
1770 }
1771 else if (Database != 0)
1772 {
1773 ZwClose(Database);
1774 }
1775
1776 IoFreeWorkItem(WorkItem->WorkItem);
1777
1778 WorkItem->WorkItem = NULL;
1779 WorkItem->Status = Status;
1780 WorkItem->Database = Database;
1781
1782 KeSetEvent(WorkItem->Event, 0, FALSE);
1783 }
1784
1785 /*
1786 * @implemented
1787 */
1788 NTSTATUS
1789 CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1790 IN OUT PHANDLE Database)
1791 {
1792 KEVENT Event;
1793 NTSTATUS Status;
1794 PMIGRATE_WORK_ITEM WorkItem;
1795
1796 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1797
1798 /* Allocate a work item dedicated to migration */
1799 WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM));
1800 if (!WorkItem)
1801 {
1802 *Database = 0;
1803 return STATUS_INSUFFICIENT_RESOURCES;
1804 }
1805
1806 RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM));
1807 WorkItem->Event = &Event;
1808 WorkItem->DeviceInformation = DeviceInformation;
1809 WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject);
1810 if (!WorkItem->WorkItem)
1811 {
1812 FreePool(WorkItem);
1813 *Database = 0;
1814 return STATUS_INSUFFICIENT_RESOURCES;
1815 }
1816
1817 /* And queue it */
1818 IoQueueWorkItem(WorkItem->WorkItem,
1819 CreateRemoteDatabaseWorker,
1820 DelayedWorkQueue,
1821 WorkItem);
1822
1823 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1824 Status = WorkItem->Status;
1825
1826 *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
1827
1828 FreePool(WorkItem);
1829 return Status;
1830 }
1831
1832 /*
1833 * @implemented
1834 */
1835 HANDLE
1836 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1837 IN BOOLEAN MigrateDatabase)
1838 {
1839 HANDLE Database;
1840 NTSTATUS Status;
1841 BOOLEAN PreviousMode;
1842 IO_STATUS_BLOCK IoStatusBlock;
1843 OBJECT_ATTRIBUTES ObjectAttributes;
1844 UNICODE_STRING DeviceRemoteDatabase;
1845
1846 Database = 0;
1847
1848 /* Get database name */
1849 DeviceRemoteDatabase.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
1850 DeviceRemoteDatabase.MaximumLength = DeviceRemoteDatabase.Length + sizeof(WCHAR);
1851 DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength);
1852 if (!DeviceRemoteDatabase.Buffer)
1853 {
1854 return 0;
1855 }
1856
1857 RtlCopyMemory(DeviceRemoteDatabase.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
1858 RtlCopyMemory(DeviceRemoteDatabase.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
1859 RemoteDatabase.Buffer, RemoteDatabase.Length);
1860 DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL;
1861
1862 /* Open database */
1863 InitializeObjectAttributes(&ObjectAttributes,
1864 &DeviceRemoteDatabase,
1865 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1866 NULL,
1867 NULL);
1868
1869 /* Disable hard errors */
1870 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1871
1872 Status = IoCreateFile(&Database,
1873 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1874 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA |
1875 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1876 &ObjectAttributes,
1877 &IoStatusBlock,
1878 NULL,
1879 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1880 0,
1881 (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN,
1882 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1883 NULL,
1884 0,
1885 CreateFileTypeNone,
1886 NULL,
1887 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING);
1888 if (Status == STATUS_STOPPED_ON_SYMLINK)
1889 {
1890 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1891 }
1892
1893 /* If base it to be migrated and was opened successfully, go ahead */
1894 if (MigrateDatabase && NT_SUCCESS(Status))
1895 {
1896 CreateRemoteDatabase(DeviceInformation, &Database);
1897 }
1898
1899 IoSetThreadHardErrorMode(PreviousMode);
1900 FreePool(DeviceRemoteDatabase.Buffer);
1901
1902 return Database;
1903 }
1904
1905 /*
1906 * @implemented
1907 */
1908 VOID
1909 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,
1910 IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
1911 IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
1912 {
1913 LONG Offset = 0;
1914 HANDLE Database;
1915 PDATABASE_ENTRY Entry, NewEntry;
1916 NTSTATUS Status = STATUS_SUCCESS;
1917
1918 /* Open the remote database */
1919 Database = OpenRemoteDatabase(DeviceInformation, FALSE);
1920 if (!Database)
1921 {
1922 return;
1923 }
1924
1925 /* Get all the entries */
1926 do
1927 {
1928 Entry = GetRemoteDatabaseEntry(Database, Offset);
1929 if (!Entry)
1930 {
1931 break;
1932 }
1933
1934 /* Not the correct entry, skip it */
1935 if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength)
1936 {
1937 Offset += Entry->EntrySize;
1938 FreePool(Entry);
1939 continue;
1940 }
1941
1942 /* Not the correct entry, skip it */
1943 if (RtlCompareMemory(OldUniqueId->UniqueId,
1944 (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset),
1945 Entry->UniqueIdLength) != Entry->UniqueIdLength)
1946 {
1947 Offset += Entry->EntrySize;
1948 FreePool(Entry);
1949 continue;
1950 }
1951
1952 /* Here, we have the correct entry */
1953 NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength);
1954 if (!NewEntry)
1955 {
1956 Offset += Entry->EntrySize;
1957 FreePool(Entry);
1958 continue;
1959 }
1960
1961 /* Recreate the entry from the previous one */
1962 NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength;
1963 NewEntry->EntryReferences = Entry->EntryReferences;
1964 NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
1965 NewEntry->SymbolicNameLength = Entry->SymbolicNameLength;
1966 NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY);
1967 NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength;
1968 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset),
1969 (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset),
1970 NewEntry->SymbolicNameLength);
1971 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset),
1972 NewUniqueId->UniqueId, NewEntry->UniqueIdLength);
1973
1974 /* Delete old entry */
1975 Status = DeleteRemoteDatabaseEntry(Database, Offset);
1976 if (!NT_SUCCESS(Status))
1977 {
1978 FreePool(Entry);
1979 FreePool(NewEntry);
1980 break;
1981 }
1982
1983 /* And replace with new one */
1984 Status = AddRemoteDatabaseEntry(Database, NewEntry);
1985 FreePool(Entry);
1986 FreePool(NewEntry);
1987 } while (NT_SUCCESS(Status));
1988
1989 CloseRemoteDatabase(Database);
1990
1991 return;
1992 }
1993
1994 /*
1995 * @implemented
1996 */
1997 NTSTATUS
1998 NTAPI
1999 DeleteDriveLetterRoutine(IN PWSTR ValueName,
2000 IN ULONG ValueType,
2001 IN PVOID ValueData,
2002 IN ULONG ValueLength,
2003 IN PVOID Context,
2004 IN PVOID EntryContext)
2005 {
2006 PMOUNTDEV_UNIQUE_ID UniqueId;
2007 UNICODE_STRING RegistryEntry;
2008
2009 UNREFERENCED_PARAMETER(EntryContext);
2010
2011 if (ValueType != REG_BINARY)
2012 {
2013 return STATUS_SUCCESS;
2014 }
2015
2016 UniqueId = Context;
2017
2018 /* First ensure we have the correct data */
2019 if (UniqueId->UniqueIdLength != ValueLength)
2020 {
2021 return STATUS_SUCCESS;
2022 }
2023
2024 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2025 {
2026 return STATUS_SUCCESS;
2027 }
2028
2029 RtlInitUnicodeString(&RegistryEntry, ValueName);
2030
2031 /* Then, it's a drive letter, erase it */
2032 if (IsDriveLetter(&RegistryEntry))
2033 {
2034 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2035 DatabasePath,
2036 ValueName);
2037 }
2038
2039 return STATUS_SUCCESS;
2040 }
2041
2042 /*
2043 * @implemented
2044 */
2045 VOID
2046 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2047 {
2048 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2049
2050 RtlZeroMemory(QueryTable, sizeof(QueryTable));
2051 QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
2052
2053 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2054 DatabasePath,
2055 QueryTable,
2056 UniqueId,
2057 NULL);
2058 }
2059
2060 /*
2061 * @implemented
2062 */
2063 NTSTATUS
2064 NTAPI
2065 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,
2066 IN ULONG ValueType,
2067 IN PVOID ValueData,
2068 IN ULONG ValueLength,
2069 IN PVOID Context,
2070 IN PVOID EntryContext)
2071 {
2072 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
2073
2074 UNREFERENCED_PARAMETER(EntryContext);
2075
2076 /* Ensure we have correct input */
2077 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
2078 UniqueId->UniqueIdLength != ValueLength)
2079 {
2080 return STATUS_SUCCESS;
2081 }
2082
2083 /* And then, if unique ID matching, delete entry */
2084 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2085 {
2086 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2087 DatabasePath,
2088 ValueName);
2089 }
2090
2091 return STATUS_SUCCESS;
2092 }
2093
2094 /*
2095 * @implemented
2096 */
2097 VOID
2098 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2099 {
2100 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2101
2102 RtlZeroMemory(QueryTable, sizeof(QueryTable));
2103 QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
2104
2105 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2106 DatabasePath,
2107 QueryTable,
2108 UniqueId,
2109 NULL);
2110 }