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