[MOUNTMGR]
[reactos.git] / reactos / 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 UNICODE_STRING RemoteDatabaseFile = RTL_CONSTANT_STRING(L"\\:$MountMgrRemoteDatabase");
36
37 /*
38 * @implemented
39 */
40 LONG
41 GetRemoteDatabaseSize(IN HANDLE Database)
42 {
43 NTSTATUS Status;
44 IO_STATUS_BLOCK IoStatusBlock;
45 FILE_STANDARD_INFORMATION StandardInfo;
46
47 /* Just query the size */
48 Status = ZwQueryInformationFile(Database,
49 &IoStatusBlock,
50 &StandardInfo,
51 sizeof(FILE_STANDARD_INFORMATION),
52 FileStandardInformation);
53 if (NT_SUCCESS(Status))
54 {
55 return StandardInfo.EndOfFile.LowPart;
56 }
57
58 return 0;
59 }
60
61 /*
62 * @implemented
63 */
64 NTSTATUS
65 AddRemoteDatabaseEntry(IN HANDLE Database,
66 IN PDATABASE_ENTRY Entry)
67 {
68 LARGE_INTEGER Size;
69 IO_STATUS_BLOCK IoStatusBlock;
70
71 /* Get size to append data */
72 Size.QuadPart = GetRemoteDatabaseSize(Database);
73
74 return ZwWriteFile(Database, 0, NULL, NULL,
75 &IoStatusBlock, Entry,
76 Entry->EntrySize, &Size, NULL);
77 }
78
79 /*
80 * @implemented
81 */
82 NTSTATUS
83 CloseRemoteDatabase(IN HANDLE Database)
84 {
85 return ZwClose(Database);
86 }
87
88 /*
89 * @implemented
90 */
91 NTSTATUS
92 TruncateRemoteDatabase(IN HANDLE Database,
93 IN LONG NewSize)
94 {
95 NTSTATUS Status;
96 IO_STATUS_BLOCK IoStatusBlock;
97 FILE_END_OF_FILE_INFORMATION EndOfFile;
98 FILE_ALLOCATION_INFORMATION Allocation;
99
100 EndOfFile.EndOfFile.QuadPart = NewSize;
101 Allocation.AllocationSize.QuadPart = NewSize;
102
103 /* First set EOF */
104 Status = ZwSetInformationFile(Database,
105 &IoStatusBlock,
106 &EndOfFile,
107 sizeof(FILE_END_OF_FILE_INFORMATION),
108 FileEndOfFileInformation);
109 if (NT_SUCCESS(Status))
110 {
111 /* And then, properly set allocation information */
112 Status = ZwSetInformationFile(Database,
113 &IoStatusBlock,
114 &Allocation,
115 sizeof(FILE_ALLOCATION_INFORMATION),
116 FileAllocationInformation);
117 }
118
119 return Status;
120 }
121
122 /*
123 * @implemented
124 */
125 PDATABASE_ENTRY
126 GetRemoteDatabaseEntry(IN HANDLE Database,
127 IN LONG StartingOffset)
128 {
129 NTSTATUS Status;
130 ULONG EntrySize;
131 PDATABASE_ENTRY Entry;
132 LARGE_INTEGER ByteOffset;
133 IO_STATUS_BLOCK IoStatusBlock;
134
135 /* Get the entry at the given position */
136 ByteOffset.QuadPart = StartingOffset;
137 Status = ZwReadFile(Database,
138 NULL,
139 NULL,
140 NULL,
141 &IoStatusBlock,
142 &EntrySize,
143 sizeof(EntrySize),
144 &ByteOffset,
145 NULL);
146 if (!NT_SUCCESS(Status))
147 {
148 return NULL;
149 }
150
151 /* If entry doesn't exist, truncate database */
152 if (!EntrySize)
153 {
154 TruncateRemoteDatabase(Database, StartingOffset);
155 return NULL;
156 }
157
158 /* Allocate the entry */
159 Entry = AllocatePool(EntrySize);
160 if (!Entry)
161 {
162 return NULL;
163 }
164
165 /* Effectively read the entry */
166 Status = ZwReadFile(Database,
167 NULL,
168 NULL,
169 NULL,
170 &IoStatusBlock,
171 Entry,
172 EntrySize,
173 &ByteOffset,
174 NULL);
175 /* If it fails or returns inconsistent data, drop it (= truncate) */
176 if (!NT_SUCCESS(Status) ||
177 (IoStatusBlock.Information != EntrySize) ||
178 (EntrySize < sizeof(DATABASE_ENTRY)) )
179 {
180 TruncateRemoteDatabase(Database, StartingOffset);
181 FreePool(Entry);
182 return NULL;
183 }
184
185 /* Validate entry */
186 if (MAX(Entry->SymbolicNameOffset + Entry->SymbolicNameLength,
187 Entry->UniqueIdOffset + Entry->UniqueIdLength) > (LONG)EntrySize)
188 {
189 TruncateRemoteDatabase(Database, StartingOffset);
190 FreePool(Entry);
191 return NULL;
192 }
193
194 return Entry;
195 }
196
197 /*
198 * @implemented
199 */
200 NTSTATUS
201 WriteRemoteDatabaseEntry(IN HANDLE Database,
202 IN LONG Offset,
203 IN PDATABASE_ENTRY Entry)
204 {
205 NTSTATUS Status;
206 LARGE_INTEGER ByteOffset;
207 IO_STATUS_BLOCK IoStatusBlock;
208
209 ByteOffset.QuadPart = Offset;
210 Status = ZwWriteFile(Database,
211 NULL,
212 NULL,
213 NULL,
214 &IoStatusBlock,
215 Entry,
216 Entry->EntrySize,
217 &ByteOffset,
218 NULL);
219 if (NT_SUCCESS(Status))
220 {
221 if (IoStatusBlock.Information < Entry->EntrySize)
222 {
223 Status = STATUS_INSUFFICIENT_RESOURCES;
224 }
225 }
226
227 return Status;
228 }
229
230 /*
231 * @implemented
232 */
233 NTSTATUS
234 DeleteRemoteDatabaseEntry(IN HANDLE Database,
235 IN LONG StartingOffset)
236 {
237 ULONG EndSize;
238 PVOID TmpBuffer;
239 NTSTATUS Status;
240 ULONG DatabaseSize;
241 PDATABASE_ENTRY Entry;
242 IO_STATUS_BLOCK IoStatusBlock;
243 LARGE_INTEGER EndEntriesOffset;
244
245 /* First, get database size */
246 DatabaseSize = GetRemoteDatabaseSize(Database);
247 if (!DatabaseSize)
248 {
249 return STATUS_INVALID_PARAMETER;
250 }
251
252 /* Then, get the entry to remove */
253 Entry = GetRemoteDatabaseEntry(Database, StartingOffset);
254 if (!Entry)
255 {
256 return STATUS_INVALID_PARAMETER;
257 }
258
259 /* Validate parameters: ensure we won't get negative size */
260 if (Entry->EntrySize + StartingOffset > DatabaseSize)
261 {
262 /* If we get invalid parameters, truncate the whole database
263 * starting the wrong entry. We can't rely on the rest
264 */
265 FreePool(Entry);
266 return TruncateRemoteDatabase(Database, StartingOffset);
267 }
268
269 /* Now, get the size of the remaining entries (those after the one to remove) */
270 EndSize = DatabaseSize - Entry->EntrySize - StartingOffset;
271 /* Allocate a buffer big enough to hold them */
272 TmpBuffer = AllocatePool(EndSize);
273 if (!TmpBuffer)
274 {
275 FreePool(Entry);
276 return STATUS_INSUFFICIENT_RESOURCES;
277 }
278
279 /* Get the offset of the entry right after the one to delete */
280 EndEntriesOffset.QuadPart = Entry->EntrySize + StartingOffset;
281 /* We don't need the entry any more */
282 FreePool(Entry);
283
284 /* Read the ending entries */
285 Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock,
286 TmpBuffer, EndSize, &EndEntriesOffset, NULL);
287 if (!NT_SUCCESS(Status))
288 {
289 FreePool(TmpBuffer);
290 return Status;
291 }
292
293 /* Ensure nothing went wrong - we don't want to corrupt the DB */
294 if (IoStatusBlock.Information != EndSize)
295 {
296 FreePool(TmpBuffer);
297 return STATUS_INVALID_PARAMETER;
298 }
299
300 /* Remove the entry */
301 Status = TruncateRemoteDatabase(Database, StartingOffset + EndSize);
302 if (!NT_SUCCESS(Status))
303 {
304 FreePool(TmpBuffer);
305 return Status;
306 }
307
308 /* Now, shift the ending entries to erase the entry */
309 EndEntriesOffset.QuadPart = StartingOffset;
310 Status = ZwWriteFile(Database, NULL, NULL, NULL, &IoStatusBlock,
311 TmpBuffer, EndSize, &EndEntriesOffset, NULL);
312
313 FreePool(TmpBuffer);
314
315 return Status;
316 }
317
318 /*
319 * @implemented
320 */
321 NTSTATUS
322 NTAPI
323 DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName,
324 IN ULONG ValueType,
325 IN PVOID ValueData,
326 IN ULONG ValueLength,
327 IN PVOID Context,
328 IN PVOID EntryContext)
329 {
330 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
331
332 UNREFERENCED_PARAMETER(ValueType);
333 UNREFERENCED_PARAMETER(EntryContext);
334
335 /* Ensure it matches, and delete */
336 if ((UniqueId->UniqueIdLength == ValueLength) &&
337 (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) ==
338 ValueLength))
339 {
340 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
341 DatabasePath,
342 ValueName);
343 }
344
345 return STATUS_SUCCESS;
346 }
347
348 /*
349 * @implemented
350 */
351 VOID
352 DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink,
353 IN PMOUNTDEV_UNIQUE_ID UniqueId)
354 {
355 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
356
357 RtlZeroMemory(QueryTable, sizeof(QueryTable));
358 QueryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine;
359 QueryTable[0].Name = SymbolicLink->Buffer;
360
361 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
362 DatabasePath,
363 QueryTable,
364 UniqueId,
365 NULL);
366 }
367
368 /*
369 * @implemented
370 */
371 NTSTATUS
372 WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
373 {
374 NTSTATUS Status;
375 LARGE_INTEGER Timeout;
376
377 /* Wait for 7 minutes */
378 Timeout.QuadPart = 0xFA0A1F00;
379 Status = KeWaitForSingleObject(&(DeviceExtension->RemoteDatabaseLock), Executive, KernelMode, FALSE, &Timeout);
380 if (Status != STATUS_TIMEOUT)
381 {
382 return Status;
383 }
384
385 return STATUS_IO_TIMEOUT;
386 }
387
388 /*
389 * @implemented
390 */
391 VOID
392 ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
393 {
394 KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE);
395 }
396
397 /*
398 * @implemented
399 */
400 NTSTATUS
401 NTAPI
402 QueryUniqueIdQueryRoutine(IN PWSTR ValueName,
403 IN ULONG ValueType,
404 IN PVOID ValueData,
405 IN ULONG ValueLength,
406 IN PVOID Context,
407 IN PVOID EntryContext)
408 {
409 PMOUNTDEV_UNIQUE_ID IntUniqueId;
410 PMOUNTDEV_UNIQUE_ID * UniqueId;
411
412 UNREFERENCED_PARAMETER(ValueName);
413 UNREFERENCED_PARAMETER(ValueType);
414 UNREFERENCED_PARAMETER(EntryContext);
415
416 /* Sanity check */
417 if (ValueLength >= 0x10000)
418 {
419 return STATUS_SUCCESS;
420 }
421
422 /* Allocate the Unique ID */
423 IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength);
424 if (IntUniqueId)
425 {
426 /* Copy data & return */
427 IntUniqueId->UniqueIdLength = (USHORT)ValueLength;
428 RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength);
429
430 UniqueId = Context;
431 *UniqueId = IntUniqueId;
432 }
433
434 return STATUS_SUCCESS;
435 }
436
437 /*
438 * @implemented
439 */
440 NTSTATUS
441 QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension,
442 IN PUNICODE_STRING SymbolicName,
443 OUT PMOUNTDEV_UNIQUE_ID * UniqueId)
444 {
445 NTSTATUS Status;
446 PDEVICE_INFORMATION DeviceInformation;
447 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
448
449 /* Query the unique ID */
450 RtlZeroMemory(QueryTable, sizeof(QueryTable));
451 QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine;
452 QueryTable[0].Name = SymbolicName->Buffer;
453
454 *UniqueId = NULL;
455 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
456 DatabasePath,
457 QueryTable,
458 UniqueId,
459 NULL);
460 /* Unique ID found, no need to go farther */
461 if (*UniqueId)
462 {
463 return STATUS_SUCCESS;
464 }
465
466 /* Otherwise, find associate device information */
467 Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation);
468 if (!NT_SUCCESS(Status))
469 {
470 return Status;
471 }
472
473 *UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
474 if (!*UniqueId)
475 {
476 return STATUS_INSUFFICIENT_RESOURCES;
477 }
478
479 /* Return this unique ID (better than nothing) */
480 (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
481 RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength);
482
483 return STATUS_SUCCESS;
484 }
485
486 /*
487 * @implemented
488 */
489 NTSTATUS
490 WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension,
491 IN PDATABASE_ENTRY DatabaseEntry)
492 {
493 NTSTATUS Status;
494 PWCHAR SymbolicName;
495 PLIST_ENTRY NextEntry;
496 UNICODE_STRING SymbolicString;
497 PDEVICE_INFORMATION DeviceInformation;
498
499 /* Create symbolic name from database entry */
500 SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR));
501 if (!SymbolicName)
502 {
503 return STATUS_INSUFFICIENT_RESOURCES;
504 }
505
506 RtlCopyMemory(SymbolicName,
507 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset),
508 DatabaseEntry->SymbolicNameLength);
509 SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL;
510
511 /* Associate the unique ID with the name from remote database */
512 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
513 DatabasePath,
514 SymbolicName,
515 REG_BINARY,
516 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
517 DatabaseEntry->UniqueIdLength);
518 FreePool(SymbolicName);
519
520 /* Reget symbolic name */
521 SymbolicString.Length = DatabaseEntry->SymbolicNameLength;
522 SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength;
523 SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
524
525 /* Find the device using this unique ID */
526 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
527 NextEntry != &(DeviceExtension->DeviceListHead);
528 NextEntry = NextEntry->Flink)
529 {
530 DeviceInformation = CONTAINING_RECORD(NextEntry,
531 DEVICE_INFORMATION,
532 DeviceListEntry);
533
534 if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength)
535 {
536 continue;
537 }
538
539 if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
540 DeviceInformation->UniqueId->UniqueId,
541 DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
542 {
543 break;
544 }
545 }
546
547 /* If found, create a mount point */
548 if (NextEntry != &(DeviceExtension->DeviceListHead))
549 {
550 MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName));
551 }
552
553 return Status;
554 }
555
556 /*
557 * @implemented
558 */
559 VOID
560 NTAPI
561 ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter)
562 {
563 ULONG Offset;
564 NTSTATUS Status;
565 PFILE_OBJECT FileObject;
566 PDEVICE_OBJECT DeviceObject;
567 PMOUNTDEV_UNIQUE_ID UniqueId;
568 PDATABASE_ENTRY DatabaseEntry;
569 HANDLE DatabaseHandle, Handle;
570 IO_STATUS_BLOCK IoStatusBlock;
571 OBJECT_ATTRIBUTES ObjectAttributes;
572 PDEVICE_INFORMATION ListDeviceInfo;
573 PLIST_ENTRY Entry, EntryInfo, NextEntry;
574 PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
575 BOOLEAN HardwareErrors, Restart, FailedFinding;
576 WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[100];
577 UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName;
578 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation;
579 PDEVICE_EXTENSION DeviceExtension = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceExtension;
580 PDEVICE_INFORMATION DeviceInformation = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceInformation;
581
582 /* We're unloading, do nothing */
583 if (Unloading)
584 {
585 return;
586 }
587
588 /* Lock remote DB */
589 if (!NT_SUCCESS(WaitForRemoteDatabaseSemaphore(DeviceExtension)))
590 {
591 return;
592 }
593
594 /* Recheck for unloading */
595 if (Unloading)
596 {
597 goto ReleaseRDS;
598 }
599
600 /* Find the DB to reconcile */
601 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
602 for (Entry = DeviceExtension->DeviceListHead.Flink;
603 Entry != &DeviceExtension->DeviceListHead;
604 Entry = Entry->Flink)
605 {
606 ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
607 if (ListDeviceInfo == DeviceInformation)
608 {
609 break;
610 }
611 }
612
613 /* If not found, or if removable, bail out */
614 if (Entry == &DeviceExtension->DeviceListHead || DeviceInformation->Removable)
615 {
616 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
617 goto ReleaseRDS;
618 }
619
620 /* Get our device object */
621 Status = IoGetDeviceObjectPointer(&ListDeviceInfo->DeviceName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
622 if (!NT_SUCCESS(Status))
623 {
624 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
625 goto ReleaseRDS;
626 }
627
628 if (DeviceObject->Flags & 1)
629 {
630 _InterlockedExchangeAdd(&ListDeviceInfo->MountState, 1u);
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.LowPart = 0xFFFFFFFF;
1172 Timeout.HighPart = 0xFF676980;
1173
1174 /* Try to wait as long as possible */
1175 for (i = (Unloading ? 999 : 0); i < 1000; i++)
1176 {
1177 Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes);
1178 if (NT_SUCCESS(Status))
1179 {
1180 break;
1181 }
1182
1183 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
1184 }
1185
1186 if (i < 1000)
1187 {
1188 do
1189 {
1190 Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout);
1191 }
1192 while (Status == STATUS_TIMEOUT && !Unloading);
1193
1194 ZwClose(SafeEvent);
1195 }
1196
1197 DeviceExtension = Context;
1198
1199 InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1);
1200
1201 /* Acquire workers lock */
1202 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
1203
1204 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1205
1206 /* Ensure there are workers */
1207 while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead)))
1208 {
1209 /* Unqueue a worker */
1210 Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead));
1211 WorkItem = CONTAINING_RECORD(Entry,
1212 RECONCILE_WORK_ITEM,
1213 WorkerQueueListEntry);
1214
1215 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1216
1217 /* Call it */
1218 WorkItem->WorkerRoutine(WorkItem->Context);
1219
1220 IoFreeWorkItem(WorkItem->WorkItem);
1221 FreePool(WorkItem);
1222
1223 if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) == 0)
1224 {
1225 return;
1226 }
1227
1228 KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
1229 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1230 }
1231 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1232
1233 InterlockedDecrement(&(DeviceExtension->WorkerReferences));
1234
1235 /* Reset event */
1236 KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE);
1237 }
1238
1239 /*
1240 * @implemented
1241 */
1242 NTSTATUS
1243 QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension,
1244 IN PRECONCILE_WORK_ITEM WorkItem,
1245 IN PVOID Context)
1246 {
1247 KIRQL OldIrql;
1248
1249 WorkItem->Context = Context;
1250
1251 /* When called, lock is already acquired */
1252
1253 /* If noone, start to work */
1254 if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)))
1255 {
1256 IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension);
1257 }
1258
1259 /* Otherwise queue worker for delayed execution */
1260 KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
1261 InsertTailList(&(DeviceExtension->WorkerQueueListHead),
1262 &(WorkItem->WorkerQueueListEntry));
1263 KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
1264
1265 KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE);
1266
1267 return STATUS_SUCCESS;
1268 }
1269
1270 /*
1271 * @implemented
1272 */
1273 NTSTATUS
1274 QueryVolumeName(IN HANDLE RootDirectory,
1275 IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation,
1276 IN PUNICODE_STRING FileName OPTIONAL,
1277 OUT PUNICODE_STRING SymbolicName,
1278 OUT PUNICODE_STRING VolumeName)
1279 {
1280 HANDLE Handle;
1281 NTSTATUS Status;
1282 ULONG NeededLength;
1283 IO_STATUS_BLOCK IoStatusBlock;
1284 OBJECT_ATTRIBUTES ObjectAttributes;
1285 PFILE_NAME_INFORMATION FileNameInfo;
1286 PREPARSE_DATA_BUFFER ReparseDataBuffer;
1287
1288 UNREFERENCED_PARAMETER(ReparsePointInformation);
1289
1290 if (!FileName)
1291 {
1292 InitializeObjectAttributes(&ObjectAttributes,
1293 NULL,
1294 OBJ_KERNEL_HANDLE,
1295 RootDirectory,
1296 NULL);
1297 }
1298 else
1299 {
1300 InitializeObjectAttributes(&ObjectAttributes,
1301 FileName,
1302 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1303 NULL,
1304 NULL);
1305 }
1306
1307 /* Open volume */
1308 Status = ZwOpenFile(&Handle,
1309 SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1310 &ObjectAttributes,
1311 &IoStatusBlock,
1312 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1313 (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT :
1314 FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
1315 if (!NT_SUCCESS(Status))
1316 {
1317 return Status;
1318 }
1319
1320 /* Get the reparse point data */
1321 ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1322 if (!ReparseDataBuffer)
1323 {
1324 ZwClose(Handle);
1325 return STATUS_INSUFFICIENT_RESOURCES;
1326 }
1327
1328 Status = ZwFsControlFile(Handle,
1329 0,
1330 NULL,
1331 NULL,
1332 &IoStatusBlock,
1333 FSCTL_GET_REPARSE_POINT,
1334 NULL,
1335 0,
1336 ReparseDataBuffer,
1337 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1338 if (!NT_SUCCESS(Status))
1339 {
1340 FreePool(ReparseDataBuffer);
1341 ZwClose(Handle);
1342 return Status;
1343 }
1344
1345 /* Check that name can fit in buffer */
1346 if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength)
1347 {
1348 FreePool(ReparseDataBuffer);
1349 ZwClose(Handle);
1350 return STATUS_BUFFER_TOO_SMALL;
1351 }
1352
1353 /* Copy symoblic name */
1354 SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength;
1355 RtlCopyMemory(SymbolicName->Buffer,
1356 (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer +
1357 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset),
1358 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength);
1359
1360 FreePool(ReparseDataBuffer);
1361
1362 /* Name has to \ terminated */
1363 if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\')
1364 {
1365 ZwClose(Handle);
1366 return STATUS_INVALID_PARAMETER;
1367 }
1368
1369 /* So that we can delete it, and match mountmgr requirements */
1370 SymbolicName->Length -= sizeof(WCHAR);
1371 SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1372
1373 /* Also ensure it's really a volume name... */
1374 if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName))
1375 {
1376 ZwClose(Handle);
1377 return STATUS_INVALID_PARAMETER;
1378 }
1379
1380 /* Now prepare to really get the name */
1381 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR));
1382 if (!FileNameInfo)
1383 {
1384 ZwClose(Handle);
1385 return STATUS_INSUFFICIENT_RESOURCES;
1386 }
1387
1388 Status = ZwQueryInformationFile(Handle,
1389 &IoStatusBlock,
1390 FileNameInfo,
1391 sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR),
1392 FileNameInformation);
1393 if (Status == STATUS_BUFFER_OVERFLOW)
1394 {
1395 /* As expected... Reallocate with proper size */
1396 NeededLength = FileNameInfo->FileNameLength;
1397 FreePool(FileNameInfo);
1398
1399 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength);
1400 if (!FileNameInfo)
1401 {
1402 ZwClose(Handle);
1403 return STATUS_INSUFFICIENT_RESOURCES;
1404 }
1405
1406 /* And query name */
1407 Status = ZwQueryInformationFile(Handle,
1408 &IoStatusBlock,
1409 FileNameInfo,
1410 sizeof(FILE_NAME_INFORMATION) + NeededLength,
1411 FileNameInformation);
1412 }
1413
1414 ZwClose(Handle);
1415
1416 if (!NT_SUCCESS(Status))
1417 {
1418 return Status;
1419 }
1420
1421 /* Return the volume name */
1422 VolumeName->Length = (USHORT)FileNameInfo->FileNameLength;
1423 VolumeName->MaximumLength = (USHORT)FileNameInfo->FileNameLength + sizeof(WCHAR);
1424 VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength);
1425 if (!VolumeName->Buffer)
1426 {
1427 return STATUS_INSUFFICIENT_RESOURCES;
1428 }
1429
1430 RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength);
1431 VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
1432
1433 FreePool(FileNameInfo);
1434
1435 return STATUS_SUCCESS;
1436 }
1437
1438 /*
1439 * @implemented
1440 */
1441 VOID
1442 OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
1443 IN PDEVICE_INFORMATION DeviceInformation)
1444 {
1445 HANDLE Handle;
1446 NTSTATUS Status;
1447 BOOLEAN RestartScan;
1448 IO_STATUS_BLOCK IoStatusBlock;
1449 OBJECT_ATTRIBUTES ObjectAttributes;
1450 PDEVICE_INFORMATION VolumeDeviceInformation;
1451 WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64];
1452 UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName;
1453 FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation;
1454
1455 /* Removable devices don't have remote database on them */
1456 if (DeviceInformation->Removable)
1457 {
1458 return;
1459 }
1460
1461 /* Prepare a string with reparse point index */
1462 ReparseFile.Length = DeviceInformation->DeviceName.Length + ReparseIndex.Length;
1463 ReparseFile.MaximumLength = ReparseFile.Length + sizeof(UNICODE_NULL);
1464 ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength);
1465 if (!ReparseFile.Buffer)
1466 {
1467 return;
1468 }
1469
1470 RtlCopyMemory(ReparseFile.Buffer, DeviceInformation->DeviceName.Buffer,
1471 DeviceInformation->DeviceName.Length);
1472 RtlCopyMemory((PVOID)((ULONG_PTR)ReparseFile.Buffer + DeviceInformation->DeviceName.Length),
1473 ReparseFile.Buffer, ReparseFile.Length);
1474 ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
1475
1476 InitializeObjectAttributes(&ObjectAttributes,
1477 &ReparseFile,
1478 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1479 NULL,
1480 NULL);
1481
1482 /* Open reparse point */
1483 Status = ZwOpenFile(&Handle,
1484 FILE_GENERIC_READ,
1485 &ObjectAttributes,
1486 &IoStatusBlock,
1487 FILE_SHARE_READ | FILE_SHARE_WRITE,
1488 FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT);
1489 FreePool(ReparseFile.Buffer);
1490 if (!NT_SUCCESS(Status))
1491 {
1492 DeviceInformation->NoDatabase = FALSE;
1493 return;
1494 }
1495
1496 /* Query reparse point information
1497 * We only pay attention to mout point
1498 */
1499 RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer));
1500 FileName.Buffer = FileNameBuffer;
1501 FileName.Length = sizeof(FileNameBuffer);
1502 FileName.MaximumLength = sizeof(FileNameBuffer);
1503 ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT;
1504 Status = ZwQueryDirectoryFile(Handle,
1505 NULL,
1506 NULL,
1507 NULL,
1508 &IoStatusBlock,
1509 &ReparsePointInformation,
1510 sizeof(FILE_REPARSE_POINT_INFORMATION),
1511 FileReparsePointInformation,
1512 TRUE,
1513 &FileName,
1514 FALSE);
1515 if (!NT_SUCCESS(Status))
1516 {
1517 ZwClose(Handle);
1518 return;
1519 }
1520
1521 RestartScan = TRUE;
1522
1523 /* Query mount points */
1524 while (TRUE)
1525 {
1526 SymbolicName.Length = 0;
1527 SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
1528 SymbolicName.Buffer = SymbolicNameBuffer;
1529 RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
1530
1531 Status = ZwQueryDirectoryFile(Handle,
1532 NULL,
1533 NULL,
1534 NULL,
1535 &IoStatusBlock,
1536 &ReparsePointInformation,
1537 sizeof(FILE_REPARSE_POINT_INFORMATION),
1538 FileReparsePointInformation,
1539 TRUE,
1540 (RestartScan) ? &FileName : NULL,
1541 RestartScan);
1542 if (!RestartScan)
1543 {
1544 if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
1545 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
1546 {
1547 break;
1548 }
1549 }
1550 else
1551 {
1552 RestartScan = FALSE;
1553 }
1554
1555 if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
1556 {
1557 break;
1558 }
1559
1560 /* Get the volume name associated to the mount point */
1561 Status = QueryVolumeName(Handle,
1562 &ReparsePointInformation,
1563 NULL, &SymbolicName,
1564 &VolumeName);
1565 if (!NT_SUCCESS(Status))
1566 {
1567 continue;
1568 }
1569
1570 FreePool(VolumeName.Buffer);
1571
1572 /* Get its information */
1573 Status = FindDeviceInfo(DeviceExtension, &SymbolicName,
1574 FALSE, &VolumeDeviceInformation);
1575 if (!NT_SUCCESS(Status))
1576 {
1577 DeviceInformation->NoDatabase = TRUE;
1578 continue;
1579 }
1580
1581 /* If notification are enabled, mark it online */
1582 if (!DeviceInformation->SkipNotifications)
1583 {
1584 PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName);
1585 }
1586 }
1587
1588 ZwClose(Handle);
1589 }
1590
1591 /*
1592 * @implemented
1593 */
1594 VOID
1595 ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension,
1596 IN PDEVICE_INFORMATION DeviceInformation)
1597 {
1598 PRECONCILE_WORK_ITEM WorkItem;
1599
1600 /* Removable devices don't have remote database */
1601 if (DeviceInformation->Removable)
1602 {
1603 return;
1604 }
1605
1606 /* Allocate a work item */
1607 WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM));
1608 if (!WorkItem)
1609 {
1610 return;
1611 }
1612
1613 WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
1614 if (!WorkItem->WorkItem)
1615 {
1616 FreePool(WorkItem);
1617 return;
1618 }
1619
1620 /* And queue it */
1621 WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker;
1622 WorkItem->DeviceExtension = DeviceExtension;
1623 WorkItem->DeviceInformation = DeviceInformation;
1624 QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension));
1625
1626 /* If there's no automount, and automatic letters
1627 * all volumes to find those online and notify there presence
1628 */
1629 if (DeviceExtension->WorkerThreadStatus == 0 &&
1630 DeviceExtension->AutomaticDriveLetter == 1 &&
1631 DeviceExtension->NoAutoMount == FALSE)
1632 {
1633 OnlineMountedVolumes(DeviceExtension, DeviceInformation);
1634 }
1635 }
1636
1637 /*
1638 * @implemented
1639 */
1640 VOID
1641 ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension)
1642 {
1643 PLIST_ENTRY NextEntry;
1644 PDEVICE_INFORMATION DeviceInformation;
1645
1646 /* Browse all the devices */
1647 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1648 NextEntry != &(DeviceExtension->DeviceListHead);
1649 NextEntry = NextEntry->Flink)
1650 {
1651 DeviceInformation = CONTAINING_RECORD(NextEntry,
1652 DEVICE_INFORMATION,
1653 DeviceListEntry);
1654 /* If it's not removable, then, it might have a database to sync */
1655 if (!DeviceInformation->Removable)
1656 {
1657 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
1658 }
1659 }
1660 }
1661
1662 /*
1663 * @implemented
1664 */
1665 VOID
1666 NTAPI
1667 MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject,
1668 IN PVOID Context)
1669 {
1670 ULONG Length;
1671 NTSTATUS Status;
1672 PVOID TmpBuffer;
1673 CHAR Disposition;
1674 LARGE_INTEGER ByteOffset;
1675 PMIGRATE_WORK_ITEM WorkItem;
1676 IO_STATUS_BLOCK IoStatusBlock;
1677 HANDLE Migrate = 0, Database = 0;
1678 PDEVICE_INFORMATION DeviceInformation;
1679 BOOLEAN PreviousMode, Complete = FALSE;
1680 UNICODE_STRING DatabaseName, DatabaseFile;
1681 OBJECT_ATTRIBUTES ObjectAttributes, MigrateAttributes;
1682 #define TEMP_BUFFER_SIZE 0x200
1683
1684 UNREFERENCED_PARAMETER(DeviceObject);
1685
1686 /* Extract context */
1687 WorkItem = Context;
1688 DeviceInformation = WorkItem->DeviceInformation;
1689
1690 /* Reconstruct appropriate string */
1691 DatabaseName.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
1692 DatabaseName.MaximumLength = DatabaseName.Length + sizeof(WCHAR);
1693
1694 DatabaseFile.Length = DeviceInformation->DeviceName.Length + RemoteDatabaseFile.Length;
1695 DatabaseFile.MaximumLength = DatabaseFile.Length + sizeof(WCHAR);
1696
1697 DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength);
1698 DatabaseFile.Buffer = AllocatePool(DatabaseFile.MaximumLength);
1699 /* Allocate buffer that will be used to swap contents */
1700 TmpBuffer = AllocatePool(TEMP_BUFFER_SIZE);
1701 if (!DatabaseName.Buffer || !DatabaseFile.Buffer || !TmpBuffer)
1702 {
1703 Status = STATUS_INSUFFICIENT_RESOURCES;
1704 goto Cleanup;
1705 }
1706
1707 /* Create the required folder (in which the database will be stored
1708 * \System Volume Information at root of the volume
1709 */
1710 Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName));
1711 if (!NT_SUCCESS(Status))
1712 {
1713 goto Cleanup;
1714 }
1715
1716 /* Finish initating strings */
1717 RtlCopyMemory(DatabaseName.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
1718 RtlCopyMemory(DatabaseFile.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
1719 RtlCopyMemory(DatabaseName.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
1720 RemoteDatabase.Buffer, RemoteDatabase.Length);
1721 RtlCopyMemory(DatabaseFile.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
1722 RemoteDatabaseFile.Buffer, RemoteDatabaseFile.Length);
1723 DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1724 DatabaseFile.Buffer[DatabaseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
1725
1726 /* Create database */
1727 InitializeObjectAttributes(&ObjectAttributes,
1728 &DatabaseName,
1729 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1730 NULL,
1731 NULL);
1732
1733 Status = ZwCreateFile(&Database,
1734 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1735 FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES |
1736 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1737 &ObjectAttributes,
1738 &IoStatusBlock,
1739 NULL,
1740 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1741 0,
1742 FILE_CREATE,
1743 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1744 NULL,
1745 0);
1746 if (!NT_SUCCESS(Status))
1747 {
1748 Database = 0;
1749 goto Cleanup;
1750 }
1751
1752 InitializeObjectAttributes(&MigrateAttributes,
1753 &DatabaseFile,
1754 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1755 NULL,
1756 NULL);
1757
1758 /* Disable hard errors and open the database that will be copied */
1759 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1760 Status = ZwCreateFile(&Migrate,
1761 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1762 FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES |
1763 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1764 &MigrateAttributes,
1765 &IoStatusBlock,
1766 NULL,
1767 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1768 0,
1769 FILE_OPEN,
1770 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1771 NULL,
1772 0);
1773 IoSetThreadHardErrorMode(PreviousMode);
1774 if (!NT_SUCCESS(Status))
1775 {
1776 Migrate = 0;
1777 }
1778 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1779 {
1780 Status = STATUS_SUCCESS;
1781 Complete = TRUE;
1782 }
1783 if (!NT_SUCCESS(Status) || Complete)
1784 {
1785 goto Cleanup;
1786 }
1787
1788 ByteOffset.QuadPart = 0LL;
1789 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1790 /* Now, loop as long it's possible */
1791 while (Status == STATUS_SUCCESS)
1792 {
1793 /* Read data from existing database */
1794 Status = ZwReadFile(Migrate,
1795 NULL,
1796 NULL,
1797 NULL,
1798 &IoStatusBlock,
1799 TmpBuffer,
1800 TEMP_BUFFER_SIZE,
1801 &ByteOffset,
1802 NULL);
1803 if (!NT_SUCCESS(Status))
1804 {
1805 break;
1806 }
1807
1808 /* And write them into new database */
1809 Length = (ULONG)IoStatusBlock.Information;
1810 Status = ZwWriteFile(Database,
1811 NULL,
1812 NULL,
1813 NULL,
1814 &IoStatusBlock,
1815 TmpBuffer,
1816 Length,
1817 &ByteOffset,
1818 NULL);
1819 ByteOffset.QuadPart += Length;
1820 }
1821 IoSetThreadHardErrorMode(PreviousMode);
1822
1823 /* Delete old databse if it was well copied */
1824 if (Status == STATUS_END_OF_FILE)
1825 {
1826 Disposition = 1;
1827 Status = ZwSetInformationFile(Migrate,
1828 &IoStatusBlock,
1829 &Disposition,
1830 sizeof(Disposition),
1831 FileDispositionInformation);
1832 }
1833
1834 /* Migration is over */
1835
1836 Cleanup:
1837 if (TmpBuffer)
1838 {
1839 FreePool(TmpBuffer);
1840 }
1841
1842 if (DatabaseFile.Buffer)
1843 {
1844 FreePool(DatabaseFile.Buffer);
1845 }
1846
1847 if (DatabaseName.Buffer)
1848 {
1849 FreePool(DatabaseName.Buffer);
1850 }
1851
1852 if (Migrate)
1853 {
1854 ZwClose(Migrate);
1855 }
1856
1857 if (NT_SUCCESS(Status))
1858 {
1859 DeviceInformation->Migrated = 1;
1860 }
1861 else if (Database)
1862 {
1863 ZwClose(Database);
1864 }
1865
1866 IoFreeWorkItem(WorkItem->WorkItem);
1867
1868 WorkItem->WorkItem = NULL;
1869 WorkItem->Status = Status;
1870 WorkItem->Database = Database;
1871
1872 KeSetEvent(WorkItem->Event, 0, FALSE);
1873 #undef TEMP_BUFFER_SIZE
1874 }
1875
1876 /*
1877 * @implemented
1878 */
1879 NTSTATUS
1880 MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1881 IN OUT PHANDLE Database)
1882 {
1883 KEVENT Event;
1884 NTSTATUS Status;
1885 PMIGRATE_WORK_ITEM WorkItem;
1886
1887 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1888
1889 /* Allocate a work item dedicated to migration */
1890 WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM));
1891 if (!WorkItem)
1892 {
1893 *Database = 0;
1894 return STATUS_INSUFFICIENT_RESOURCES;
1895 }
1896
1897 RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM));
1898 WorkItem->Event = &Event;
1899 WorkItem->DeviceInformation = DeviceInformation;
1900 WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject);
1901 if (!WorkItem->WorkItem)
1902 {
1903 FreePool(WorkItem);
1904 *Database = 0;
1905 return STATUS_INSUFFICIENT_RESOURCES;
1906 }
1907
1908 /* And queue it */
1909 IoQueueWorkItem(WorkItem->WorkItem,
1910 MigrateRemoteDatabaseWorker,
1911 DelayedWorkQueue,
1912 WorkItem);
1913
1914 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1915 Status = WorkItem->Status;
1916
1917 *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
1918
1919 FreePool(WorkItem);
1920 return Status;
1921 }
1922
1923 /*
1924 * @implemented
1925 */
1926 HANDLE
1927 OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
1928 IN BOOLEAN MigrateDatabase)
1929 {
1930 HANDLE Database;
1931 NTSTATUS Status;
1932 BOOLEAN PreviousMode;
1933 IO_STATUS_BLOCK IoStatusBlock;
1934 OBJECT_ATTRIBUTES ObjectAttributes;
1935 UNICODE_STRING DeviceRemoteDatabase;
1936
1937 Database = 0;
1938
1939 /* Get database name */
1940 DeviceRemoteDatabase.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
1941 DeviceRemoteDatabase.MaximumLength = DeviceRemoteDatabase.Length + sizeof(WCHAR);
1942 DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength);
1943 if (!DeviceRemoteDatabase.Buffer)
1944 {
1945 return 0;
1946 }
1947
1948 RtlCopyMemory(DeviceRemoteDatabase.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
1949 RtlCopyMemory(DeviceRemoteDatabase.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
1950 RemoteDatabase.Buffer, RemoteDatabase.Length);
1951 DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL;
1952
1953 /* Open database */
1954 InitializeObjectAttributes(&ObjectAttributes,
1955 &DeviceRemoteDatabase,
1956 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1957 NULL,
1958 NULL);
1959
1960 /* Disable hard errors */
1961 PreviousMode = IoSetThreadHardErrorMode(FALSE);
1962
1963 Status = ZwCreateFile(&Database,
1964 SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
1965 FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES |
1966 FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
1967 &ObjectAttributes,
1968 &IoStatusBlock,
1969 NULL,
1970 FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1971 0,
1972 (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN,
1973 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
1974 NULL,
1975 0);
1976
1977 /* If base it to be migrated and was opened successfully, go ahead */
1978 if (MigrateDatabase && NT_SUCCESS(Status))
1979 {
1980 MigrateRemoteDatabase(DeviceInformation, &Database);
1981 }
1982
1983 IoSetThreadHardErrorMode(PreviousMode);
1984 FreePool(DeviceRemoteDatabase.Buffer);
1985
1986 return Database;
1987 }
1988
1989 /*
1990 * @implemented
1991 */
1992 VOID
1993 ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,
1994 IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
1995 IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
1996 {
1997 LONG Offset = 0;
1998 HANDLE Database;
1999 PDATABASE_ENTRY Entry, NewEntry;
2000 NTSTATUS Status = STATUS_SUCCESS;
2001
2002 /* Open the remote database */
2003 Database = OpenRemoteDatabase(DeviceInformation, FALSE);
2004 if (!Database)
2005 {
2006 return;
2007 }
2008
2009 /* Get all the entries */
2010 do
2011 {
2012 Entry = GetRemoteDatabaseEntry(Database, Offset);
2013 if (!Entry)
2014 {
2015 break;
2016 }
2017
2018 /* Not the correct entry, skip it */
2019 if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength)
2020 {
2021 Offset += Entry->EntrySize;
2022 FreePool(Entry);
2023 continue;
2024 }
2025
2026 /* Not the correct entry, skip it */
2027 if (RtlCompareMemory(OldUniqueId->UniqueId,
2028 (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset),
2029 Entry->UniqueIdLength) != Entry->UniqueIdLength)
2030 {
2031 Offset += Entry->EntrySize;
2032 FreePool(Entry);
2033 continue;
2034 }
2035
2036 /* Here, we have the correct entry */
2037 NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength);
2038 if (!NewEntry)
2039 {
2040 Offset += Entry->EntrySize;
2041 FreePool(Entry);
2042 continue;
2043 }
2044
2045 /* Recreate the entry from the previous one */
2046 NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength;
2047 NewEntry->EntryReferences = Entry->EntryReferences;
2048 NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
2049 NewEntry->SymbolicNameLength = Entry->SymbolicNameLength;
2050 NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY);
2051 NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength;
2052 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset),
2053 (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset),
2054 NewEntry->SymbolicNameLength);
2055 RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset),
2056 NewUniqueId->UniqueId, NewEntry->UniqueIdLength);
2057
2058 /* Delete old entry */
2059 Status = DeleteRemoteDatabaseEntry(Database, Offset);
2060 if (!NT_SUCCESS(Status))
2061 {
2062 FreePool(Entry);
2063 FreePool(NewEntry);
2064 break;
2065 }
2066
2067 /* And replace with new one */
2068 Status = AddRemoteDatabaseEntry(Database, NewEntry);
2069 FreePool(Entry);
2070 FreePool(NewEntry);
2071 } while (NT_SUCCESS(Status));
2072
2073 CloseRemoteDatabase(Database);
2074
2075 return;
2076 }
2077
2078 /*
2079 * @implemented
2080 */
2081 NTSTATUS
2082 NTAPI
2083 DeleteDriveLetterRoutine(IN PWSTR ValueName,
2084 IN ULONG ValueType,
2085 IN PVOID ValueData,
2086 IN ULONG ValueLength,
2087 IN PVOID Context,
2088 IN PVOID EntryContext)
2089 {
2090 PMOUNTDEV_UNIQUE_ID UniqueId;
2091 UNICODE_STRING RegistryEntry;
2092
2093 UNREFERENCED_PARAMETER(EntryContext);
2094
2095 if (ValueType != REG_BINARY)
2096 {
2097 return STATUS_SUCCESS;
2098 }
2099
2100 UniqueId = Context;
2101
2102 /* First ensure we have the correct data */
2103 if (UniqueId->UniqueIdLength != ValueLength)
2104 {
2105 return STATUS_SUCCESS;
2106 }
2107
2108 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2109 {
2110 return STATUS_SUCCESS;
2111 }
2112
2113 RtlInitUnicodeString(&RegistryEntry, ValueName);
2114
2115 /* Then, it's a drive letter, erase it */
2116 if (IsDriveLetter(&RegistryEntry))
2117 {
2118 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2119 DatabasePath,
2120 ValueName);
2121 }
2122
2123 return STATUS_SUCCESS;
2124 }
2125
2126 /*
2127 * @implemented
2128 */
2129 VOID
2130 DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2131 {
2132 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2133
2134 RtlZeroMemory(QueryTable, sizeof(QueryTable));
2135 QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
2136
2137 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2138 DatabasePath,
2139 QueryTable,
2140 UniqueId,
2141 NULL);
2142 }
2143
2144 /*
2145 * @implemented
2146 */
2147 NTSTATUS
2148 NTAPI
2149 DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,
2150 IN ULONG ValueType,
2151 IN PVOID ValueData,
2152 IN ULONG ValueLength,
2153 IN PVOID Context,
2154 IN PVOID EntryContext)
2155 {
2156 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
2157
2158 UNREFERENCED_PARAMETER(EntryContext);
2159
2160 /* Ensure we have correct input */
2161 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
2162 UniqueId->UniqueIdLength != ValueLength)
2163 {
2164 return STATUS_SUCCESS;
2165 }
2166
2167 /* And then, if unique ID matching, delete entry */
2168 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
2169 {
2170 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
2171 DatabasePath,
2172 ValueName);
2173 }
2174
2175 return STATUS_SUCCESS;
2176 }
2177
2178 /*
2179 * @implemented
2180 */
2181 VOID
2182 DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
2183 {
2184 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
2185
2186 RtlZeroMemory(QueryTable, sizeof(QueryTable));
2187 QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
2188
2189 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2190 DatabasePath,
2191 QueryTable,
2192 UniqueId,
2193 NULL);
2194 }