694c98a25faea78155ba6eb85a6dbdb3ca008ef9
[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
682 RtlCopyMemory(ReparseFile.Buffer, DeviceInformation->DeviceName.Buffer,
683 DeviceInformation->DeviceName.Length);
684 RtlCopyMemory((PVOID)((ULONG_PTR)ReparseFile.Buffer + DeviceInformation->DeviceName.Length),
685 ReparseFile.Buffer, ReparseFile.Length);
686 ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
687
688 InitializeObjectAttributes(&ObjectAttributes,
689 &ReparseFile,
690 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
691 NULL,
692 NULL);
693
694 /* Open reparse point directory */
695 HardwareErrors = IoSetThreadHardErrorMode(FALSE);
696 Status = ZwOpenFile(&Handle,
697 FILE_GENERIC_READ,
698 &ObjectAttributes,
699 &IoStatusBlock,
700 FILE_SHARE_READ | FILE_SHARE_WRITE,
701 FILE_SYNCHRONOUS_IO_ALERT);
702 IoSetThreadHardErrorMode(HardwareErrors);
703
704 FreePool(ReparseFile.Buffer);
705
706 if (!NT_SUCCESS(Status))
707 {
708 if (DatabaseHandle != 0)
709 {
710 TruncateRemoteDatabase(DatabaseHandle, 0);
711 CloseRemoteDatabase(DatabaseHandle);
712 }
713 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
714 goto ReleaseRDS;
715 }
716
717 /* Query reparse point information
718 * We only pay attention to mout point
719 */
720 RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer));
721 FileName.Buffer = FileNameBuffer;
722 FileName.Length = sizeof(FileNameBuffer);
723 FileName.MaximumLength = sizeof(FileNameBuffer);
724 ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT;
725 Status = ZwQueryDirectoryFile(Handle,
726 NULL,
727 NULL,
728 NULL,
729 &IoStatusBlock,
730 &ReparsePointInformation,
731 sizeof(FILE_REPARSE_POINT_INFORMATION),
732 FileReparsePointInformation,
733 TRUE,
734 &FileName,
735 FALSE);
736 if (!NT_SUCCESS(Status))
737 {
738 ZwClose(Handle);
739 if (DatabaseHandle != 0)
740 {
741 TruncateRemoteDatabase(DatabaseHandle, 0);
742 CloseRemoteDatabase(DatabaseHandle);
743 }
744 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
745 goto ReleaseRDS;
746 }
747
748 /* If we failed to open the remote DB previously,
749 * retry this time allowing migration (and thus, creation if required)
750 */
751 if (DatabaseHandle == 0)
752 {
753 DatabaseHandle = OpenRemoteDatabase(DeviceInformation, TRUE);
754 if (DatabaseHandle == 0)
755 {
756 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
757 goto ReleaseRDS;
758 }
759 }
760
761 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
762
763 /* Reset all the references to our DB entries */
764 Offset = 0;
765 for (;;)
766 {
767 DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset);
768 if (DatabaseEntry == NULL)
769 {
770 break;
771 }
772
773 DatabaseEntry->EntryReferences = 0;
774 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
775 if (!NT_SUCCESS(Status))
776 {
777 FreePool(DatabaseEntry);
778 goto CloseReparse;
779 }
780
781 Offset += DatabaseEntry->EntrySize;
782 FreePool(DatabaseEntry);
783 }
784
785 /* Init string for QueryVolumeName call */
786 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
787 SymbolicName.Length = 0;
788 SymbolicName.Buffer = SymbolicNameBuffer;
789 Restart = TRUE;
790
791 /* Start looping on reparse points */
792 for (;;)
793 {
794 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
795 Status = ZwQueryDirectoryFile(Handle,
796 NULL,
797 NULL,
798 NULL,
799 &IoStatusBlock,
800 &ReparsePointInformation,
801 sizeof(FILE_REPARSE_POINT_INFORMATION),
802 FileReparsePointInformation,
803 TRUE,
804 Restart ? &FileName : NULL,
805 Restart);
806 /* Restart only once */
807 if (Restart)
808 {
809 Restart = FALSE;
810 }
811 else
812 {
813 /* If we get the same one, we're done, bail out */
814 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
815 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
816 {
817 break;
818 }
819 }
820
821 /* If querying failed, or if onloading, or if not returning mount points, bail out */
822 if (!NT_SUCCESS(Status) || Unloading || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
823 {
824 break;
825 }
826
827 /* Get the volume name associated to the mount point */
828 Status = QueryVolumeName(Handle, &ReparsePointInformation, 0, &SymbolicName, &VolumeName);
829 if (!NT_SUCCESS(Status))
830 {
831 continue;
832 }
833
834 /* Browse the DB to find the name */
835 Offset = 0;
836 for (;;)
837 {
838 UNICODE_STRING DbName;
839
840 DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset);
841 if (DatabaseEntry == NULL)
842 {
843 break;
844 }
845
846 DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
847 DbName.Length = DbName.MaximumLength;
848 DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
849 /* Found, we're done! */
850 if (RtlEqualUnicodeString(&DbName, &SymbolicName, TRUE))
851 {
852 break;
853 }
854
855 Offset += DatabaseEntry->EntrySize;
856 FreePool(DatabaseEntry);
857 }
858
859 /* If we found the mount point.... */
860 if (DatabaseEntry != NULL)
861 {
862 /* If it was referenced, reference it once more and update to remote */
863 if (DatabaseEntry->EntryReferences)
864 {
865 ++DatabaseEntry->EntryReferences;
866 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
867 if (!NT_SUCCESS(Status))
868 {
869 goto FreeDBEntry;
870 }
871
872 FreePool(DatabaseEntry);
873 }
874 else
875 {
876 /* Query the Unique ID associated to that mount point in case it changed */
877 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
878 Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId);
879 if (!NT_SUCCESS(Status))
880 {
881 /* If we failed doing so, reuse the old Unique ID and push it to master */
882 Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry);
883 if (!NT_SUCCESS(Status))
884 {
885 goto ReleaseDeviceLock;
886 }
887
888 /* And then, reference & write the entry */
889 ++DatabaseEntry->EntryReferences;
890 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
891 if (!NT_SUCCESS(Status))
892 {
893 goto ReleaseDeviceLock;
894 }
895
896 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
897 FreePool(DatabaseEntry);
898 }
899 /* If the Unique ID didn't change */
900 else if (UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
901 RtlCompareMemory(UniqueId->UniqueId,
902 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
903 UniqueId->UniqueIdLength) == UniqueId->UniqueIdLength)
904 {
905 /* Reference the entry, and update to remote */
906 ++DatabaseEntry->EntryReferences;
907 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
908 if (!NT_SUCCESS(Status))
909 {
910 goto FreeUniqueId;
911 }
912
913 FreePool(UniqueId);
914 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
915 FreePool(DatabaseEntry);
916 }
917 /* Would, by chance, the Unique ID be present elsewhere? */
918 else if (IsUniqueIdPresent(DeviceExtension, DatabaseEntry))
919 {
920 /* Push the ID to master */
921 Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry);
922 if (!NT_SUCCESS(Status))
923 {
924 goto FreeUniqueId;
925 }
926
927 /* And then, reference & write the entry */
928 ++DatabaseEntry->EntryReferences;
929 Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry);
930 if (!NT_SUCCESS(Status))
931 {
932 goto FreeUniqueId;
933 }
934
935 FreePool(UniqueId);
936 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
937 FreePool(DatabaseEntry);
938 }
939 else
940 {
941 /* OK, at that point, we're facing a totally unknown unique ID
942 * So, get rid of the old entry, and recreate a new one with
943 * the know unique ID
944 */
945 Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset);
946 if (!NT_SUCCESS(Status))
947 {
948 goto FreeUniqueId;
949 }
950
951 FreePool(DatabaseEntry);
952 /* Allocate a new entry big enough */
953 DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY));
954 if (DatabaseEntry == NULL)
955 {
956 goto FreeUniqueId;
957 }
958
959 /* Configure it */
960 DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY);
961 DatabaseEntry->EntryReferences = 1;
962 DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
963 DatabaseEntry->SymbolicNameLength = SymbolicName.Length;
964 DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY);
965 DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
966 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength);
967 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
968
969 /* And write it remotely */
970 Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry);
971 if (!NT_SUCCESS(Status))
972 {
973 FreePool(DatabaseEntry);
974 goto FreeUniqueId;
975 }
976
977 FreePool(UniqueId);
978 FreePool(DatabaseEntry);
979 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
980 }
981 }
982 }
983 else
984 {
985 /* We failed finding it remotely
986 * So, let's allocate a new remote DB entry
987 */
988 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
989 /* To be able to do so, we need the device Unique ID, ask master */
990 Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId);
991 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
992 if (NT_SUCCESS(Status))
993 {
994 /* Allocate a new entry big enough */
995 DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY));
996 if (DatabaseEntry != NULL)
997 {
998 /* Configure it */
999 DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY);
1000 DatabaseEntry->EntryReferences = 1;
1001 DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
1002 DatabaseEntry->SymbolicNameLength = SymbolicName.Length;
1003 DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY);
1004 DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
1005 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength);
1006 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
1007
1008 /* And write it remotely */
1009 Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry);
1010 FreePool(DatabaseEntry);
1011 FreePool(UniqueId);
1012
1013 if (!NT_SUCCESS(Status))
1014 {
1015 goto FreeVolume;
1016 }
1017 }
1018 else
1019 {
1020 FreePool(UniqueId);
1021 }
1022 }
1023 }
1024
1025 /* Find info about the device associated associated with the mount point */
1026 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1027 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &ListDeviceInfo);
1028 if (!NT_SUCCESS(Status))
1029 {
1030 FailedFinding = TRUE;
1031 FreePool(VolumeName.Buffer);
1032 }
1033 else
1034 {
1035 /* Associate the device with the currrent DB */
1036 AssociatedDevice = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY));
1037 if (AssociatedDevice == NULL)
1038 {
1039 FreePool(VolumeName.Buffer);
1040 }
1041 else
1042 {
1043 AssociatedDevice->DeviceInformation = DeviceInformation;
1044 AssociatedDevice->String.Length = VolumeName.Length;
1045 AssociatedDevice->String.MaximumLength = VolumeName.MaximumLength;
1046 AssociatedDevice->String.Buffer = VolumeName.Buffer;
1047 InsertTailList(&ListDeviceInfo->AssociatedDevicesHead, &AssociatedDevice->AssociatedDevicesEntry);
1048 }
1049
1050 /* If we don't have to skip notifications, notify */
1051 if (!ListDeviceInfo->SkipNotifications)
1052 {
1053 PostOnlineNotification(DeviceExtension, &ListDeviceInfo->SymbolicName);
1054 }
1055 }
1056
1057 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1058 }
1059
1060 /* We don't need mount points any longer */
1061 ZwClose(Handle);
1062
1063 /* Look for the DB again */
1064 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1065 for (Entry = DeviceExtension->DeviceListHead.Flink;
1066 Entry != &DeviceExtension->DeviceListHead;
1067 Entry = Entry->Flink)
1068 {
1069 ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
1070 if (ListDeviceInfo == DeviceInformation)
1071 {
1072 break;
1073 }
1074 }
1075
1076 if (Entry == &DeviceExtension->DeviceListHead)
1077 {
1078 ListDeviceInfo = NULL;
1079 }
1080
1081 /* Start the pruning loop */
1082 Offset = 0;
1083 for (;;)
1084 {
1085 /* Get the entry */
1086 DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset);
1087 if (DatabaseEntry == NULL)
1088 {
1089 break;
1090 }
1091
1092 /* It's not referenced anylonger? Prune it */
1093 if (DatabaseEntry->EntryReferences == 0)
1094 {
1095 Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset);
1096 if (!NT_SUCCESS(Status))
1097 {
1098 FreePool(DatabaseEntry);
1099 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1100 goto CloseRDB;
1101 }
1102 }
1103 /* Update the Unique IDs to reflect the changes we might have done previously */
1104 else
1105 {
1106 if (ListDeviceInfo != NULL)
1107 {
1108 UpdateReplicatedUniqueIds(ListDeviceInfo, DatabaseEntry);
1109 }
1110
1111 Offset += DatabaseEntry->EntrySize;
1112 }
1113
1114 FreePool(DatabaseEntry);
1115 }
1116
1117 /* We do have a DB now :-) */
1118 if (ListDeviceInfo != NULL && !FailedFinding)
1119 {
1120 DeviceInformation->NoDatabase = FALSE;
1121 }
1122
1123 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1124
1125 goto CloseRDB;
1126
1127 FreeUniqueId:
1128 FreePool(UniqueId);
1129 ReleaseDeviceLock:
1130 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1131 FreeDBEntry:
1132 FreePool(DatabaseEntry);
1133 FreeVolume:
1134 FreePool(VolumeName.Buffer);
1135 CloseReparse:
1136 ZwClose(Handle);
1137 CloseRDB:
1138 CloseRemoteDatabase(DatabaseHandle);
1139 ReleaseRDS:
1140 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
1141 return;
1142 }
1143
1144 /*
1145 * @implemented
1146 */
1147 VOID
1148 NTAPI
1149 WorkerThread(IN PDEVICE_OBJECT DeviceObject,
1150 IN PVOID Context)
1151 {
1152 ULONG i;
1153 KEVENT Event;
1154 KIRQL OldIrql;
1155 NTSTATUS Status;
1156 HANDLE SafeEvent;
1157 PLIST_ENTRY Entry;
1158 LARGE_INTEGER Timeout;
1159 PRECONCILE_WORK_ITEM WorkItem;
1160 PDEVICE_EXTENSION DeviceExtension;
1161 OBJECT_ATTRIBUTES ObjectAttributes;
1162
1163 UNREFERENCED_PARAMETER(DeviceObject);
1164
1165 InitializeObjectAttributes(&ObjectAttributes,
1166 &SafeVolumes,
1167 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1168 NULL,
1169 NULL);
1170 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1171 Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
1172
1173 /* Wait as long as possible for clearance from autochk
1174 * We will write remote databases only if it is safe
1175 * to access volumes.
1176 * First, given we start before SMSS, wait for the
1177 * event creation.
1178 */
1179 i = 0;
1180 do
1181 {
1182 /* If we started to shutdown, stop waiting forever and jump to last attempt */
1183 if (Unloading)
1184 {
1185 i = 999;
1186 }
1187 else
1188 {
1189 /* Attempt to open the event */
1190 Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes);
1191 if (NT_SUCCESS(Status))
1192 {
1193 break;
1194 }
1195
1196 /* Wait a bit to give SMSS a chance to create the event */
1197 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
1198 }
1199
1200 ++i;
1201 }
1202 while (i < 1000);
1203
1204 /* We managed to open the event, wait until autochk signals it */
1205 if (i < 1000)
1206 {
1207 do
1208 {
1209 Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout);
1210 }
1211 while (Status == STATUS_TIMEOUT && !Unloading);
1212
1213 ZwClose(SafeEvent);
1214 }
1215
1216 DeviceExtension = Context;
1217
1218 InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1);
1219
1220 /* Acquire workers lock */
1221 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
1222
1223 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1224
1225 /* Ensure there are workers */
1226 while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead)))
1227 {
1228 /* Unqueue a worker */
1229 Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead));
1230 WorkItem = CONTAINING_RECORD(Entry,
1231 RECONCILE_WORK_ITEM,
1232 WorkerQueueListEntry);
1233
1234 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1235
1236 /* Call it */
1237 WorkItem->WorkerRoutine(WorkItem->Context);
1238
1239 IoFreeWorkItem(WorkItem->WorkItem);
1240 FreePool(WorkItem);
1241
1242 if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) < 0)
1243 {
1244 return;
1245 }
1246
1247 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
1248 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1249 }
1250 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1251
1252 InterlockedDecrement(&(DeviceExtension->WorkerReferences));
1253
1254 /* Reset event */
1255 KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE);
1256 }
1257
1258 /*
1259 * @implemented
1260 */
1261 NTSTATUS
1262 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension,
1263 IN PRECONCILE_WORK_ITEM WorkItem,
1264 IN PVOID Context)
1265 {
1266 KIRQL OldIrql;
1267
1268 WorkItem->Context = Context;
1269
1270 /* When called, lock is already acquired */
1271
1272 /* If noone, start to work */
1273 if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)))
1274 {
1275 IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension);
1276 }
1277
1278 /* Otherwise queue worker for delayed execution */
1279 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1280 InsertTailList(&(DeviceExtension->WorkerQueueListHead),
1281 &(WorkItem->WorkerQueueListEntry));
1282 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1283
1284 KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE);
1285
1286 return STATUS_SUCCESS;
1287 }
1288
1289 /*
1290 * @implemented
1291 */
1292 NTSTATUS
1293 QueryVolumeName(IN HANDLE RootDirectory,
1294 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation,
1295 IN PUNICODE_STRING FileName OPTIONAL,
1296 OUT PUNICODE_STRING SymbolicName,
1297 OUT PUNICODE_STRING VolumeName)
1298 {
1299 HANDLE Handle;
1300 NTSTATUS Status;
1301 ULONG NeededLength;
1302 IO_STATUS_BLOCK IoStatusBlock;
1303 OBJECT_ATTRIBUTES ObjectAttributes;
1304 PFILE_NAME_INFORMATION FileNameInfo;
1305 PREPARSE_DATA_BUFFER ReparseDataBuffer;
1306
1307 UNREFERENCED_PARAMETER(ReparsePointInformation);
1308
1309 if (!FileName)
1310 {
1311 InitializeObjectAttributes(&ObjectAttributes,
1312 NULL,
1313 OBJ_KERNEL_HANDLE,
1314 RootDirectory,
1315 NULL);
1316 }
1317 else
1318 {
1319 InitializeObjectAttributes(&ObjectAttributes,
1320 FileName,
1321 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1322 NULL,
1323 NULL);
1324 }
1325
1326 /* Open volume */
1327 Status = ZwOpenFile(&Handle,
1328 SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1329 &ObjectAttributes,
1330 &IoStatusBlock,
1331 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1332 (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT :
1333 FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
1334 if (!NT_SUCCESS(Status))
1335 {
1336 return Status;
1337 }
1338
1339 /* Get the reparse point data */
1340 ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1341 if (!ReparseDataBuffer)
1342 {
1343 ZwClose(Handle);
1344 return STATUS_INSUFFICIENT_RESOURCES;
1345 }
1346
1347 Status = ZwFsControlFile(Handle,
1348 0,
1349 NULL,
1350 NULL,
1351 &IoStatusBlock,
1352 FSCTL_GET_REPARSE_POINT,
1353 NULL,
1354 0,
1355 ReparseDataBuffer,
1356 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1357 if (!NT_SUCCESS(Status))
1358 {
1359 FreePool(ReparseDataBuffer);
1360 ZwClose(Handle);
1361 return Status;
1362 }
1363
1364 /* Check that name can fit in buffer */
1365 if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength)
1366 {
1367 FreePool(ReparseDataBuffer);
1368 ZwClose(Handle);
1369 return STATUS_BUFFER_TOO_SMALL;
1370 }
1371
1372 /* Copy symbolic name */
1373 SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength;
1374 RtlCopyMemory(SymbolicName->Buffer,
1375 (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer +
1376 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset),
1377 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength);
1378
1379 FreePool(ReparseDataBuffer);
1380
1381 /* Name has to \ terminated */
1382 if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\')
1383 {
1384 ZwClose(Handle);
1385 return STATUS_INVALID_PARAMETER;
1386 }
1387
1388 /* So that we can delete it, and match mountmgr requirements */
1389 SymbolicName->Length -= sizeof(WCHAR);
1390 SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1391
1392 /* Also ensure it's really a volume name... */
1393 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName))
1394 {
1395 ZwClose(Handle);
1396 return STATUS_INVALID_PARAMETER;
1397 }
1398
1399 /* Now prepare to really get the name */
1400 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR));
1401 if (!FileNameInfo)
1402 {
1403 ZwClose(Handle);
1404 return STATUS_INSUFFICIENT_RESOURCES;
1405 }
1406
1407 Status = ZwQueryInformationFile(Handle,
1408 &IoStatusBlock,
1409 FileNameInfo,
1410 sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR),
1411 FileNameInformation);
1412 if (Status == STATUS_BUFFER_OVERFLOW)
1413 {
1414 /* As expected... Reallocate with proper size */
1415 NeededLength = FileNameInfo->FileNameLength;
1416 FreePool(FileNameInfo);
1417
1418 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength);
1419 if (!FileNameInfo)
1420 {
1421 ZwClose(Handle);
1422 return STATUS_INSUFFICIENT_RESOURCES;
1423 }
1424
1425 /* And query name */
1426 Status = ZwQueryInformationFile(Handle,
1427 &IoStatusBlock,
1428 FileNameInfo,
1429 sizeof(FILE_NAME_INFORMATION) + NeededLength,
1430 FileNameInformation);
1431 }
1432
1433 ZwClose(Handle);
1434
1435 if (!NT_SUCCESS(Status))
1436 {
1437 return Status;
1438 }
1439
1440 /* Return the volume name */
1441 VolumeName->Length = (USHORT)FileNameInfo->FileNameLength;
1442 VolumeName->MaximumLength = (USHORT)FileNameInfo->FileNameLength + sizeof(WCHAR);
1443 VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength);
1444 if (!VolumeName->Buffer)
1445 {
1446 return STATUS_INSUFFICIENT_RESOURCES;
1447 }
1448
1449 RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength);
1450 VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
1451
1452 FreePool(FileNameInfo);
1453
1454 return STATUS_SUCCESS;
1455 }
1456
1457 /*
1458 * @implemented
1459 */
1460 VOID
1461 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
1462 IN PDEVICE_INFORMATION DeviceInformation)
1463 {
1464 HANDLE Handle;
1465 NTSTATUS Status;
1466 BOOLEAN RestartScan;
1467 IO_STATUS_BLOCK IoStatusBlock;
1468 OBJECT_ATTRIBUTES ObjectAttributes;
1469 PDEVICE_INFORMATION VolumeDeviceInformation;
1470 WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64];
1471 UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName;
1472 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation;
1473
1474 /* Removable devices don't have remote database on them */
1475 if (DeviceInformation->Removable)
1476 {
1477 return;
1478 }
1479
1480 /* Prepare a string with reparse point index */
1481 ReparseFile.Length = DeviceInformation->DeviceName.Length + ReparseIndex.Length;
1482 ReparseFile.MaximumLength = ReparseFile.Length + sizeof(UNICODE_NULL);
1483 ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength);
1484 if (!ReparseFile.Buffer)
1485 {
1486 return;
1487 }
1488
1489 RtlCopyMemory(ReparseFile.Buffer, DeviceInformation->DeviceName.Buffer,
1490 DeviceInformation->DeviceName.Length);
1491 RtlCopyMemory((PVOID)((ULONG_PTR)ReparseFile.Buffer + DeviceInformation->DeviceName.Length),
1492 ReparseFile.Buffer, ReparseFile.Length);
1493 ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
1494
1495 InitializeObjectAttributes(&ObjectAttributes,
1496 &ReparseFile,
1497 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1498 NULL,
1499 NULL);
1500
1501 /* Open reparse point */
1502 Status = ZwOpenFile(&Handle,
1503 FILE_GENERIC_READ,
1504 &ObjectAttributes,
1505 &IoStatusBlock,
1506 FILE_SHARE_READ | FILE_SHARE_WRITE,
1507 FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT);
1508 FreePool(ReparseFile.Buffer);
1509 if (!NT_SUCCESS(Status))
1510 {
1511 DeviceInformation->NoDatabase = FALSE;
1512 return;
1513 }
1514
1515 /* Query reparse point information
1516 * We only pay attention to mout point
1517 */
1518 RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer));
1519 FileName.Buffer = FileNameBuffer;
1520 FileName.Length = sizeof(FileNameBuffer);
1521 FileName.MaximumLength = sizeof(FileNameBuffer);
1522 ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT;
1523 Status = ZwQueryDirectoryFile(Handle,
1524 NULL,
1525 NULL,
1526 NULL,
1527 &IoStatusBlock,
1528 &ReparsePointInformation,
1529 sizeof(FILE_REPARSE_POINT_INFORMATION),
1530 FileReparsePointInformation,
1531 TRUE,
1532 &FileName,
1533 FALSE);
1534 if (!NT_SUCCESS(Status))
1535 {
1536 ZwClose(Handle);
1537 return;
1538 }
1539
1540 RestartScan = TRUE;
1541
1542 /* Query mount points */
1543 while (TRUE)
1544 {
1545 SymbolicName.Length = 0;
1546 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
1547 SymbolicName.Buffer = SymbolicNameBuffer;
1548 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
1549
1550 Status = ZwQueryDirectoryFile(Handle,
1551 NULL,
1552 NULL,
1553 NULL,
1554 &IoStatusBlock,
1555 &ReparsePointInformation,
1556 sizeof(FILE_REPARSE_POINT_INFORMATION),
1557 FileReparsePointInformation,
1558 TRUE,
1559 (RestartScan) ? &FileName : NULL,
1560 RestartScan);
1561 if (!RestartScan)
1562 {
1563 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
1564 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
1565 {
1566 break;
1567 }
1568 }
1569 else
1570 {
1571 RestartScan = FALSE;
1572 }
1573
1574 if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
1575 {
1576 break;
1577 }
1578
1579 /* Get the volume name associated to the mount point */
1580 Status = QueryVolumeName(Handle,
1581 &ReparsePointInformation,
1582 NULL, &SymbolicName,
1583 &VolumeName);
1584 if (!NT_SUCCESS(Status))
1585 {
1586 continue;
1587 }
1588
1589 FreePool(VolumeName.Buffer);
1590
1591 /* Get its information */
1592 Status = FindDeviceInfo(DeviceExtension, &SymbolicName,
1593 FALSE, &VolumeDeviceInformation);
1594 if (!NT_SUCCESS(Status))
1595 {
1596 DeviceInformation->NoDatabase = TRUE;
1597 continue;
1598 }
1599
1600 /* If notification are enabled, mark it online */
1601 if (!DeviceInformation->SkipNotifications)
1602 {
1603 PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName);
1604 }
1605 }
1606
1607 ZwClose(Handle);
1608 }
1609
1610 /*
1611 * @implemented
1612 */
1613 VOID
1614 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension,
1615 IN PDEVICE_INFORMATION DeviceInformation)
1616 {
1617 PRECONCILE_WORK_ITEM WorkItem;
1618
1619 /* Removable devices don't have remote database */
1620 if (DeviceInformation->Removable)
1621 {
1622 return;
1623 }
1624
1625 UNIMPLEMENTED;
1626 return;
1627
1628 /* Allocate a work item */
1629 WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM));
1630 if (!WorkItem)
1631 {
1632 return;
1633 }
1634
1635 WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
1636 if (!WorkItem->WorkItem)
1637 {
1638 FreePool(WorkItem);
1639 return;
1640 }
1641
1642 /* And queue it */
1643 WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker;
1644 WorkItem->DeviceExtension = DeviceExtension;
1645 WorkItem->DeviceInformation = DeviceInformation;
1646 QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension));
1647
1648 /* If there's no automount, and automatic letters
1649 * all volumes to find those online and notify there presence
1650 */
1651 if (DeviceExtension->WorkerThreadStatus == 0 &&
1652 DeviceExtension->AutomaticDriveLetter == 1 &&
1653 DeviceExtension->NoAutoMount == FALSE)
1654 {
1655 OnlineMountedVolumes(DeviceExtension, DeviceInformation);
1656 }
1657 }
1658
1659 /*
1660 * @implemented
1661 */
1662 VOID
1663 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension)
1664 {
1665 PLIST_ENTRY NextEntry;
1666 PDEVICE_INFORMATION DeviceInformation;
1667
1668 /* Browse all the devices */
1669 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1670 NextEntry != &(DeviceExtension->DeviceListHead);
1671 NextEntry = NextEntry->Flink)
1672 {
1673 DeviceInformation = CONTAINING_RECORD(NextEntry,
1674 DEVICE_INFORMATION,
1675 DeviceListEntry);
1676 /* If it's not removable, then, it might have a database to sync */
1677 if (!DeviceInformation->Removable)
1678 {
1679 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
1680 }
1681 }
1682 }
1683
1684 /*
1685 * @implemented
1686 */
1687 VOID
1688 NTAPI
1689 CreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject,
1690 IN PVOID Context)
1691 {
1692 NTSTATUS Status;
1693 HANDLE Database = 0;
1694 UNICODE_STRING DatabaseName;
1695 PMIGRATE_WORK_ITEM WorkItem;
1696 IO_STATUS_BLOCK IoStatusBlock;
1697 OBJECT_ATTRIBUTES ObjectAttributes;
1698 PDEVICE_INFORMATION DeviceInformation;
1699
1700 UNREFERENCED_PARAMETER(DeviceObject);
1701
1702 /* Extract context */
1703 WorkItem = Context;
1704 DeviceInformation = WorkItem->DeviceInformation;
1705
1706 /* Reconstruct appropriate string */
1707 DatabaseName.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
1708 DatabaseName.MaximumLength = DatabaseName.Length + sizeof(WCHAR);
1709 DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength);
1710 if (DatabaseName.Buffer == NULL)
1711 {
1712 Status = STATUS_INSUFFICIENT_RESOURCES;
1713 goto Cleanup;
1714 }
1715
1716 /* Create the required folder (in which the database will be stored
1717 * \System Volume Information at root of the volume
1718 */
1719 Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName));
1720 if (!NT_SUCCESS(Status))
1721 {
1722 goto Cleanup;
1723 }
1724
1725 /* Finish initiating strings */
1726 RtlCopyMemory(DatabaseName.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
1727 RtlCopyMemory(DatabaseName.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
1728 RemoteDatabase.Buffer, RemoteDatabase.Length);
1729 DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1730
1731 /* Create database */
1732 InitializeObjectAttributes(&ObjectAttributes,
1733 &DatabaseName,
1734 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1735 NULL,
1736 NULL);
1737
1738 Status = IoCreateFile(&Database,
1739 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1740 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA |
1741 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1742 &ObjectAttributes,
1743 &IoStatusBlock,
1744 NULL,
1745 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1746 0,
1747 FILE_CREATE,
1748 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1749 NULL,
1750 0,
1751 CreateFileTypeNone,
1752 NULL,
1753 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING);
1754 if (!NT_SUCCESS(Status))
1755 {
1756 if (Status == STATUS_STOPPED_ON_SYMLINK)
1757 {
1758 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1759 }
1760
1761 Database = 0;
1762 goto Cleanup;
1763 }
1764
1765 Cleanup:
1766 if (DatabaseName.Buffer)
1767 {
1768 FreePool(DatabaseName.Buffer);
1769 }
1770
1771 if (NT_SUCCESS(Status))
1772 {
1773 DeviceInformation->Migrated = 1;
1774 }
1775 else if (Database != 0)
1776 {
1777 ZwClose(Database);
1778 }
1779
1780 IoFreeWorkItem(WorkItem->WorkItem);
1781
1782 WorkItem->WorkItem = NULL;
1783 WorkItem->Status = Status;
1784 WorkItem->Database = Database;
1785
1786 KeSetEvent(WorkItem->Event, 0, FALSE);
1787 }
1788
1789 /*
1790 * @implemented
1791 */
1792 NTSTATUS
1793 CreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1794 IN OUT PHANDLE Database)
1795 {
1796 KEVENT Event;
1797 NTSTATUS Status;
1798 PMIGRATE_WORK_ITEM WorkItem;
1799
1800 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1801
1802 /* Allocate a work item dedicated to migration */
1803 WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM));
1804 if (!WorkItem)
1805 {
1806 *Database = 0;
1807 return STATUS_INSUFFICIENT_RESOURCES;
1808 }
1809
1810 RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM));
1811 WorkItem->Event = &Event;
1812 WorkItem->DeviceInformation = DeviceInformation;
1813 WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject);
1814 if (!WorkItem->WorkItem)
1815 {
1816 FreePool(WorkItem);
1817 *Database = 0;
1818 return STATUS_INSUFFICIENT_RESOURCES;
1819 }
1820
1821 /* And queue it */
1822 IoQueueWorkItem(WorkItem->WorkItem,
1823 CreateRemoteDatabaseWorker,
1824 DelayedWorkQueue,
1825 WorkItem);
1826
1827 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1828 Status = WorkItem->Status;
1829
1830 *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
1831
1832 FreePool(WorkItem);
1833 return Status;
1834 }
1835
1836 /*
1837 * @implemented
1838 */
1839 HANDLE
1840 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1841 IN BOOLEAN MigrateDatabase)
1842 {
1843 HANDLE Database;
1844 NTSTATUS Status;
1845 BOOLEAN PreviousMode;
1846 IO_STATUS_BLOCK IoStatusBlock;
1847 OBJECT_ATTRIBUTES ObjectAttributes;
1848 UNICODE_STRING DeviceRemoteDatabase;
1849
1850 Database = 0;
1851
1852 /* Get database name */
1853 DeviceRemoteDatabase.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
1854 DeviceRemoteDatabase.MaximumLength = DeviceRemoteDatabase.Length + sizeof(WCHAR);
1855 DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength);
1856 if (!DeviceRemoteDatabase.Buffer)
1857 {
1858 return 0;
1859 }
1860
1861 RtlCopyMemory(DeviceRemoteDatabase.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
1862 RtlCopyMemory(DeviceRemoteDatabase.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
1863 RemoteDatabase.Buffer, RemoteDatabase.Length);
1864 DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL;
1865
1866 /* Open database */
1867 InitializeObjectAttributes(&ObjectAttributes,
1868 &DeviceRemoteDatabase,
1869 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1870 NULL,
1871 NULL);
1872
1873 /* Disable hard errors */
1874 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1875
1876 Status = IoCreateFile(&Database,
1877 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1878 FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA |
1879 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1880 &ObjectAttributes,
1881 &IoStatusBlock,
1882 NULL,
1883 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1884 0,
1885 (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN,
1886 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1887 NULL,
1888 0,
1889 CreateFileTypeNone,
1890 NULL,
1891 IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING);
1892 if (Status == STATUS_STOPPED_ON_SYMLINK)
1893 {
1894 DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n");
1895 }
1896
1897 /* If base it to be migrated and was opened successfully, go ahead */
1898 if (MigrateDatabase && NT_SUCCESS(Status))
1899 {
1900 CreateRemoteDatabase(DeviceInformation, &Database);
1901 }
1902
1903 IoSetThreadHardErrorMode(PreviousMode);
1904 FreePool(DeviceRemoteDatabase.Buffer);
1905
1906 return Database;
1907 }
1908
1909 /*
1910 * @implemented
1911 */
1912 VOID
1913 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,
1914 IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
1915 IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
1916 {
1917 LONG Offset = 0;
1918 HANDLE Database;
1919 PDATABASE_ENTRY Entry, NewEntry;
1920 NTSTATUS Status = STATUS_SUCCESS;
1921
1922 /* Open the remote database */
1923 Database = OpenRemoteDatabase(DeviceInformation, FALSE);
1924 if (!Database)
1925 {
1926 return;
1927 }
1928
1929 /* Get all the entries */
1930 do
1931 {
1932 Entry = GetRemoteDatabaseEntry(Database, Offset);
1933 if (!Entry)
1934 {
1935 break;
1936 }
1937
1938 /* Not the correct entry, skip it */
1939 if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength)
1940 {
1941 Offset += Entry->EntrySize;
1942 FreePool(Entry);
1943 continue;
1944 }
1945
1946 /* Not the correct entry, skip it */
1947 if (RtlCompareMemory(OldUniqueId->UniqueId,
1948 (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset),
1949 Entry->UniqueIdLength) != Entry->UniqueIdLength)
1950 {
1951 Offset += Entry->EntrySize;
1952 FreePool(Entry);
1953 continue;
1954 }
1955
1956 /* Here, we have the correct entry */
1957 NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength);
1958 if (!NewEntry)
1959 {
1960 Offset += Entry->EntrySize;
1961 FreePool(Entry);
1962 continue;
1963 }
1964
1965 /* Recreate the entry from the previous one */
1966 NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength;
1967 NewEntry->EntryReferences = Entry->EntryReferences;
1968 NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
1969 NewEntry->SymbolicNameLength = Entry->SymbolicNameLength;
1970 NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY);
1971 NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength;
1972 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset),
1973 (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset),
1974 NewEntry->SymbolicNameLength);
1975 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset),
1976 NewUniqueId->UniqueId, NewEntry->UniqueIdLength);
1977
1978 /* Delete old entry */
1979 Status = DeleteRemoteDatabaseEntry(Database, Offset);
1980 if (!NT_SUCCESS(Status))
1981 {
1982 FreePool(Entry);
1983 FreePool(NewEntry);
1984 break;
1985 }
1986
1987 /* And replace with new one */
1988 Status = AddRemoteDatabaseEntry(Database, NewEntry);
1989 FreePool(Entry);
1990 FreePool(NewEntry);
1991 } while (NT_SUCCESS(Status));
1992
1993 CloseRemoteDatabase(Database);
1994
1995 return;
1996 }
1997
1998 /*
1999 * @implemented
2000 */
2001 NTSTATUS
2002 NTAPI
2003 DeleteDriveLetterRoutine(IN PWSTR ValueName,
2004 IN ULONG ValueType,
2005 IN PVOID ValueData,
2006 IN ULONG ValueLength,
2007 IN PVOID Context,
2008 IN PVOID EntryContext)
2009 {
2010 PMOUNTDEV_UNIQUE_ID UniqueId;
2011 UNICODE_STRING RegistryEntry;
2012
2013 UNREFERENCED_PARAMETER(EntryContext);
2014
2015 if (ValueType != REG_BINARY)
2016 {
2017 return STATUS_SUCCESS;
2018 }
2019
2020 UniqueId = Context;
2021
2022 /* First ensure we have the correct data */
2023 if (UniqueId->UniqueIdLength != ValueLength)
2024 {
2025 return STATUS_SUCCESS;
2026 }
2027
2028 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2029 {
2030 return STATUS_SUCCESS;
2031 }
2032
2033 RtlInitUnicodeString(&RegistryEntry, ValueName);
2034
2035 /* Then, it's a drive letter, erase it */
2036 if (IsDriveLetter(&RegistryEntry))
2037 {
2038 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2039 DatabasePath,
2040 ValueName);
2041 }
2042
2043 return STATUS_SUCCESS;
2044 }
2045
2046 /*
2047 * @implemented
2048 */
2049 VOID
2050 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2051 {
2052 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2053
2054 RtlZeroMemory(QueryTable, sizeof(QueryTable));
2055 QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
2056
2057 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2058 DatabasePath,
2059 QueryTable,
2060 UniqueId,
2061 NULL);
2062 }
2063
2064 /*
2065 * @implemented
2066 */
2067 NTSTATUS
2068 NTAPI
2069 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,
2070 IN ULONG ValueType,
2071 IN PVOID ValueData,
2072 IN ULONG ValueLength,
2073 IN PVOID Context,
2074 IN PVOID EntryContext)
2075 {
2076 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
2077
2078 UNREFERENCED_PARAMETER(EntryContext);
2079
2080 /* Ensure we have correct input */
2081 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
2082 UniqueId->UniqueIdLength != ValueLength)
2083 {
2084 return STATUS_SUCCESS;
2085 }
2086
2087 /* And then, if unique ID matching, delete entry */
2088 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2089 {
2090 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2091 DatabasePath,
2092 ValueName);
2093 }
2094
2095 return STATUS_SUCCESS;
2096 }
2097
2098 /*
2099 * @implemented
2100 */
2101 VOID
2102 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2103 {
2104 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2105
2106 RtlZeroMemory(QueryTable, sizeof(QueryTable));
2107 QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
2108
2109 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2110 DatabasePath,
2111 QueryTable,
2112 UniqueId,
2113 NULL);
2114 }