xref: /reactos/drivers/storage/mountmgr/device.c (revision 7f26a396)
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/device.c
22  * PURPOSE:          Mount Manager - Device Control
23  * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
24  */
25 
26 #include "mntmgr.h"
27 
28 #define MAX_DEVICES 0x3E8 /* Matches 1000 devices */
29 
30 #define NDEBUG
31 #include <debug.h>
32 
33 /*
34  * @implemented
35  */
36 NTSTATUS
37 MountMgrChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
38                      IN PIRP Irp)
39 {
40     KIRQL OldIrql;
41     NTSTATUS Status;
42     PIO_STACK_LOCATION Stack;
43     PMOUNTMGR_CHANGE_NOTIFY_INFO ChangeNotify;
44 
45     /* Get the I/O buffer */
46     Stack = IoGetCurrentIrpStackLocation(Irp);
47     ChangeNotify = (PMOUNTMGR_CHANGE_NOTIFY_INFO)Irp->AssociatedIrp.SystemBuffer;
48 
49     /* Validate it */
50     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO) ||
51         Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO))
52     {
53         return STATUS_INVALID_PARAMETER;
54     }
55 
56     /* If epic number doesn't match, just return now one */
57     if (DeviceExtension->EpicNumber != ChangeNotify->EpicNumber)
58     {
59         ChangeNotify->EpicNumber = DeviceExtension->EpicNumber;
60         Irp->IoStatus.Information = sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO);
61         return STATUS_SUCCESS;
62     }
63 
64     /* If IRP is to be canceled, forget about that */
65     IoAcquireCancelSpinLock(&OldIrql);
66     if (Irp->Cancel)
67     {
68         Status = STATUS_CANCELLED;
69     }
70     /* Otherwise queue the IRP to be notified with the next epic number change */
71     else
72     {
73         InsertTailList(&(DeviceExtension->IrpListHead), &(Irp->Tail.Overlay.ListEntry));
74         IoMarkIrpPending(Irp);
75         IoSetCancelRoutine(Irp, MountMgrCancel);
76         Status = STATUS_PENDING;
77     }
78     IoReleaseCancelSpinLock(OldIrql);
79 
80     return Status;
81 }
82 
83 /*
84  * @implemented
85  */
86 NTSTATUS
87 MountmgrWriteNoAutoMount(IN PDEVICE_EXTENSION DeviceExtension)
88 {
89     ULONG Value = DeviceExtension->NoAutoMount;
90 
91     return RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
92                                  DeviceExtension->RegistryPath.Buffer,
93                                  L"NoAutoMount",
94                                  REG_DWORD,
95                                  &Value,
96                                  sizeof(Value));
97 }
98 
99 /*
100  * @implemented
101  */
102 NTSTATUS
103 MountMgrSetAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
104                      IN PIRP Irp)
105 {
106     PIO_STACK_LOCATION Stack;
107     PMOUNTMGR_SET_AUTO_MOUNT SetState;
108 
109     Stack = IoGetCurrentIrpStackLocation(Irp);
110 
111     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_SET_AUTO_MOUNT))
112     {
113         Irp->IoStatus.Information = 0;
114         return STATUS_INVALID_PARAMETER;
115     }
116 
117     /* Change the state only if there is a real difference
118      * with the user-provided NewState (normalized) */
119     SetState = (PMOUNTMGR_SET_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
120     if ((SetState->NewState != Enabled) == DeviceExtension->NoAutoMount)
121     {
122         Irp->IoStatus.Information = 0;
123         return STATUS_SUCCESS;
124     }
125 
126     /* Set new state */
127     DeviceExtension->NoAutoMount = (SetState->NewState != Enabled);
128     Irp->IoStatus.Information = 0;
129     return MountmgrWriteNoAutoMount(DeviceExtension);
130 }
131 
132 /*
133  * @implemented
134  */
135 NTSTATUS
136 MountMgrQueryAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
137                        IN PIRP Irp)
138 {
139     PIO_STACK_LOCATION Stack;
140     PMOUNTMGR_QUERY_AUTO_MOUNT QueryState;
141 
142     Stack = IoGetCurrentIrpStackLocation(Irp);
143 
144     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_QUERY_AUTO_MOUNT))
145     {
146         Irp->IoStatus.Information = 0;
147         return STATUS_INVALID_PARAMETER;
148     }
149 
150     QueryState = (PMOUNTMGR_QUERY_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
151     QueryState->CurrentState = !DeviceExtension->NoAutoMount;
152     Irp->IoStatus.Information = sizeof(MOUNTMGR_QUERY_AUTO_MOUNT);
153 
154     return STATUS_SUCCESS;
155 }
156 
157 /*
158  * @implemented
159  */
160 NTSTATUS
161 NTAPI
162 ScrubRegistryRoutine(IN PWSTR ValueName,
163                      IN ULONG ValueType,
164                      IN PVOID ValueData,
165                      IN ULONG ValueLength,
166                      IN PVOID Context,
167                      IN PVOID EntryContext)
168 {
169     NTSTATUS Status;
170     PLIST_ENTRY NextEntry;
171     PDEVICE_INFORMATION DeviceInfo;
172     PBOOLEAN Continue = EntryContext;
173     PDEVICE_EXTENSION DeviceExtension = Context;
174 
175     if (ValueType != REG_BINARY)
176     {
177         return STATUS_SUCCESS;
178     }
179 
180     /* Delete values for devices that don't have the matching unique ID */
181     if (!IsListEmpty(&(DeviceExtension->DeviceListHead)))
182     {
183         for (NextEntry = DeviceExtension->DeviceListHead.Flink;
184              NextEntry != &(DeviceExtension->DeviceListHead);
185              NextEntry = NextEntry->Flink)
186         {
187             DeviceInfo = CONTAINING_RECORD(NextEntry,
188                                            DEVICE_INFORMATION,
189                                            DeviceListEntry);
190 
191              if (!DeviceInfo->UniqueId || DeviceInfo->UniqueId->UniqueIdLength != ValueLength)
192              {
193                  continue;
194              }
195 
196              if (RtlCompareMemory(DeviceInfo->UniqueId->UniqueId, ValueData, ValueLength) == ValueLength)
197              {
198                  return STATUS_SUCCESS;
199              }
200         }
201     }
202 
203     /* Wrong unique ID, scrub it */
204     Status = RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
205                                     DatabasePath,
206                                     ValueName);
207     if (!NT_SUCCESS(Status))
208     {
209         *Continue = TRUE;
210         return STATUS_UNSUCCESSFUL;
211     }
212 
213     *Continue = FALSE;
214     return Status;
215 }
216 
217 /*
218  * @implemented
219  */
220 NTSTATUS
221 MountMgrScrubRegistry(IN PDEVICE_EXTENSION DeviceExtension)
222 {
223     NTSTATUS Status;
224     BOOLEAN Continue;
225     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
226 
227     do
228     {
229         RtlZeroMemory(QueryTable, sizeof(QueryTable));
230         QueryTable[0].QueryRoutine = ScrubRegistryRoutine;
231         QueryTable[0].EntryContext = &Continue;
232         Continue = FALSE;
233 
234         Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
235                                         DatabasePath,
236                                         QueryTable,
237                                         DeviceExtension,
238                                         NULL);
239     }
240     while (Continue);
241 
242     return Status;
243 }
244 
245 /*
246  * @implemented
247  */
248 NTSTATUS
249 MountMgrCreatePoint(IN PDEVICE_EXTENSION DeviceExtension,
250                     IN PIRP Irp)
251 {
252     ULONG MaxLength;
253     PIO_STACK_LOCATION Stack;
254     PMOUNTMGR_CREATE_POINT_INPUT Point;
255     UNICODE_STRING DeviceName, SymbolicName;
256 
257     Stack = IoGetCurrentIrpStackLocation(Irp);
258 
259     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CREATE_POINT_INPUT))
260     {
261         return STATUS_INVALID_PARAMETER;
262     }
263 
264     Point = (PMOUNTMGR_CREATE_POINT_INPUT)Irp->AssociatedIrp.SystemBuffer;
265 
266     MaxLength = MAX((Point->DeviceNameOffset + Point->DeviceNameLength),
267                     (Point->SymbolicLinkNameLength + Point->SymbolicLinkNameOffset));
268     if (MaxLength > Stack->Parameters.DeviceIoControl.InputBufferLength)
269     {
270         return STATUS_INVALID_PARAMETER;
271     }
272 
273     /* Get all the strings and call the worker */
274     SymbolicName.Length = Point->SymbolicLinkNameLength;
275     SymbolicName.MaximumLength = Point->SymbolicLinkNameLength;
276     DeviceName.Length = Point->DeviceNameLength;
277     DeviceName.MaximumLength = Point->DeviceNameLength;
278     SymbolicName.Buffer = (PVOID)((ULONG_PTR)Point + Point->SymbolicLinkNameOffset);
279     DeviceName.Buffer = (PVOID)((ULONG_PTR)Point + Point->DeviceNameOffset);
280 
281     return MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &DeviceName);
282 }
283 
284 /*
285  * @implemented
286  */
287 NTSTATUS
288 MountMgrCheckUnprocessedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
289                                 IN PIRP Irp)
290 {
291     PLIST_ENTRY NextEntry;
292     PDEVICE_INFORMATION DeviceInformation;
293     NTSTATUS ArrivalStatus, Status = STATUS_SUCCESS;
294 
295     UNREFERENCED_PARAMETER(Irp);
296 
297     /* No offline volumes, nothing more to do */
298     if (IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
299     {
300         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
301         return STATUS_SUCCESS;
302     }
303 
304     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
305 
306     /* Reactivate all the offline volumes */
307     while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
308     {
309         NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead));
310         DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
311 
312         ArrivalStatus = MountMgrMountedDeviceArrival(DeviceExtension,
313                                                      &(DeviceInformation->SymbolicName),
314                                                      DeviceInformation->ManuallyRegistered);
315         /* Then, remove them dead information */
316         MountMgrFreeDeadDeviceInfo(DeviceInformation);
317 
318         if (NT_SUCCESS(Status))
319         {
320             Status = ArrivalStatus;
321         }
322     }
323 
324     return Status;
325 }
326 
327 /*
328  * @implemented
329  */
330 BOOLEAN
331 IsFtVolume(IN PUNICODE_STRING SymbolicName)
332 {
333     NTSTATUS Status;
334     PFILE_OBJECT FileObject;
335     PARTITION_INFORMATION PartitionInfo;
336     PDEVICE_OBJECT DeviceObject, FileDeviceObject;
337 
338     /* Get device object */
339     Status = IoGetDeviceObjectPointer(SymbolicName,
340                                       FILE_READ_ATTRIBUTES,
341                                       &FileObject,
342                                       &DeviceObject);
343     if (!NT_SUCCESS(Status))
344         return FALSE;
345 
346     /* Get attached device */
347     FileDeviceObject = FileObject->DeviceObject;
348     DeviceObject = IoGetAttachedDeviceReference(FileDeviceObject);
349 
350     /* FT volume can't be removable */
351     if (FileDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
352     {
353         ObDereferenceObject(DeviceObject);
354         ObDereferenceObject(FileObject);
355         return FALSE;
356     }
357 
358     ObDereferenceObject(FileObject);
359 
360     /* Get partition information */
361     Status = MountMgrSendSyncDeviceIoCtl(IOCTL_DISK_GET_PARTITION_INFO,
362                                          DeviceObject,
363                                          NULL,
364                                          0,
365                                          &PartitionInfo,
366                                          sizeof(PartitionInfo),
367                                          NULL);
368 
369     ObDereferenceObject(DeviceObject);
370     if (!NT_SUCCESS(Status))
371         return FALSE;
372 
373     /* Check if this is a FT volume */
374     return IsFTPartition(PartitionInfo.PartitionType);
375 }
376 
377 /*
378  * @implemented
379  */
380 VOID
381 ProcessSuggestedDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
382 {
383     WCHAR NameBuffer[DRIVE_LETTER_LENGTH / sizeof(WCHAR)];
384     PLIST_ENTRY NextEntry;
385     UNICODE_STRING SymbolicName;
386     PDEVICE_INFORMATION DeviceInformation;
387 
388     /* No devices? Nothing to do! */
389     if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
390     {
391         return;
392     }
393 
394     /* For all the devices */
395     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
396          NextEntry != &(DeviceExtension->DeviceListHead);
397          NextEntry = NextEntry->Flink)
398     {
399         DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
400 
401         /* If no drive letter */
402         if (DeviceInformation->SuggestedDriveLetter == (UCHAR)-1)
403         {
404             /* Ensure it has no entry yet */
405             if (!HasDriveLetter(DeviceInformation) &&
406                 !HasNoDriveLetterEntry(DeviceInformation->UniqueId))
407             {
408                 /* And create one */
409                 CreateNoDriveLetterEntry(DeviceInformation->UniqueId);
410             }
411 
412             DeviceInformation->SuggestedDriveLetter = 0;
413         }
414         /* Suggested letter & no entry */
415         else if (DeviceInformation->SuggestedDriveLetter &&
416                  !HasNoDriveLetterEntry(DeviceInformation->UniqueId))
417         {
418             /* Just create a mount point */
419             SymbolicName.Buffer = NameBuffer;
420             RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
421             NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
422             NameBuffer[COLON_POSITION] = L':';
423             SymbolicName.Length =
424             SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
425 
426             MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &(DeviceInformation->DeviceName));
427         }
428     }
429 }
430 
431 /*
432  * @implemented
433  */
434 NTSTATUS
435 MountMgrNextDriveLetterWorker(IN PDEVICE_EXTENSION DeviceExtension,
436                               IN PUNICODE_STRING DeviceName,
437                               OUT PMOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInfo)
438 {
439     NTSTATUS Status;
440     UCHAR DriveLetter;
441     PLIST_ENTRY NextEntry;
442     PMOUNTDEV_UNIQUE_ID UniqueId;
443     BOOLEAN Removable, GptDriveLetter;
444     PDEVICE_INFORMATION DeviceInformation;
445     WCHAR NameBuffer[DRIVE_LETTER_LENGTH];
446     PSYMLINK_INFORMATION SymlinkInformation;
447     UNICODE_STRING TargetDeviceName, SymbolicName;
448 
449     /* First, process suggested letters */
450     if (!DeviceExtension->ProcessedSuggestions)
451     {
452         ProcessSuggestedDriveLetters(DeviceExtension);
453         DeviceExtension->ProcessedSuggestions = TRUE;
454     }
455 
456     /* Then, get information about the device */
457     Status = QueryDeviceInformation(DeviceName, &TargetDeviceName, NULL, &Removable, &GptDriveLetter, NULL, NULL, NULL);
458     if (!NT_SUCCESS(Status))
459     {
460         return Status;
461     }
462 
463     /* Ensure we have such device */
464     NextEntry = DeviceExtension->DeviceListHead.Flink;
465     while (NextEntry != &(DeviceExtension->DeviceListHead))
466     {
467         DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
468 
469         if (RtlCompareUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE) == 0)
470         {
471             break;
472         }
473 
474         NextEntry = NextEntry->Flink;
475     }
476 
477     if (NextEntry == &(DeviceExtension->DeviceListHead))
478     {
479         FreePool(TargetDeviceName.Buffer);
480         return STATUS_OBJECT_NAME_NOT_FOUND;
481     }
482 
483     /* Now, assume we will assign a letter */
484     DeviceInformation->LetterAssigned =
485     DriveLetterInfo->DriveLetterWasAssigned = TRUE;
486 
487     /* Browse all the symlinks to check if there is already a drive letter */
488     NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
489     while (NextEntry != &(DeviceInformation->SymbolicLinksListHead))
490     {
491         SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
492 
493         /* If this is a drive letter and it is online, forget about new drive letter */
494         if (IsDriveLetter(&(SymlinkInformation->Name)) && SymlinkInformation->Online)
495         {
496             DriveLetterInfo->DriveLetterWasAssigned = FALSE;
497             DriveLetterInfo->CurrentDriveLetter = (CHAR)SymlinkInformation->Name.Buffer[LETTER_POSITION];
498             break;
499         }
500 
501         NextEntry = NextEntry->Flink;
502     }
503 
504     /* If we didn't find a drive letter online, ensure this is not
505      * a no-drive entry by querying GPT attributes & database */
506     if (NextEntry == &(DeviceInformation->SymbolicLinksListHead))
507     {
508         if (!GptDriveLetter || HasNoDriveLetterEntry(DeviceInformation->UniqueId))
509         {
510             DriveLetterInfo->DriveLetterWasAssigned = FALSE;
511             DriveLetterInfo->CurrentDriveLetter = 0;
512             goto Release;
513         }
514     }
515 
516     /* If automount is disabled, and the device is not removable
517      * but needs a drive letter, don't assign one and bail out */
518     if (DeviceExtension->NoAutoMount && !Removable)
519     {
520         if (DriveLetterInfo->DriveLetterWasAssigned)
521         {
522             DriveLetterInfo->DriveLetterWasAssigned = FALSE;
523             DriveLetterInfo->CurrentDriveLetter = 0;
524             goto Release;
525         }
526     }
527 
528     /* Stop now if we don't need to assign the drive a letter */
529     if (!DriveLetterInfo->DriveLetterWasAssigned)
530         goto Release;
531 
532     /* Now everything is fine, begin drive letter assignment */
533 
534     if (RtlPrefixUnicodeString(&DeviceFloppy, &TargetDeviceName, TRUE))
535     {
536         /* If the device is a floppy, start with letter A */
537         DriveLetter = 'A';
538     }
539     else if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE))
540     {
541         /* If the device is a CD-ROM, start with letter D */
542         DriveLetter = 'D';
543     }
544     else
545     {
546         /* Finally, if it's a disk, use C */
547         DriveLetter = 'C';
548     }
549 
550     /* We cannot set NO drive letter */
551     ASSERT(DeviceInformation->SuggestedDriveLetter != (UCHAR)-1);
552 
553     /* If we don't have suggested letter but it's a FT volume, fail */
554     if (!DeviceInformation->SuggestedDriveLetter && IsFtVolume(&(DeviceInformation->DeviceName)))
555     {
556         DriveLetterInfo->DriveLetterWasAssigned = FALSE;
557         DriveLetterInfo->CurrentDriveLetter = 0;
558         goto Release;
559     }
560 
561     /* Prepare buffer */
562     RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
563     NameBuffer[COLON_POSITION] = L':';
564     SymbolicName.Buffer = NameBuffer;
565     SymbolicName.Length =
566     SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
567 
568     /* It's all prepared, create mount point */
569     if (DeviceInformation->SuggestedDriveLetter)
570     {
571         DriveLetterInfo->CurrentDriveLetter = DeviceInformation->SuggestedDriveLetter;
572         NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
573 
574         Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
575         if (NT_SUCCESS(Status))
576         {
577             goto Release;
578         }
579     }
580 
581     /* It failed with this letter... Try another one! */
582     for (DriveLetterInfo->CurrentDriveLetter = DriveLetter;
583          DriveLetterInfo->CurrentDriveLetter <= L'Z';
584          DriveLetterInfo->CurrentDriveLetter++)
585     {
586         NameBuffer[LETTER_POSITION] = DriveLetterInfo->CurrentDriveLetter;
587 
588         Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
589         if (NT_SUCCESS(Status))
590         {
591             break;
592         }
593     }
594 
595     /* We failed setting a letter */
596     if (DriveLetterInfo->CurrentDriveLetter > L'Z')
597     {
598         DriveLetterInfo->DriveLetterWasAssigned = FALSE;
599         DriveLetterInfo->CurrentDriveLetter = 0;
600 
601         /* Try at least to add a no drive letter entry */
602         Status = QueryDeviceInformation(&TargetDeviceName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
603         if (NT_SUCCESS(Status))
604         {
605             CreateNoDriveLetterEntry(UniqueId);
606             FreePool(UniqueId);
607         }
608     }
609 
610 Release:
611     FreePool(TargetDeviceName.Buffer);
612 
613     return STATUS_SUCCESS;
614 }
615 
616 
617 /*
618  * @implemented
619  */
620 NTSTATUS
621 MountMgrNextDriveLetter(IN PDEVICE_EXTENSION DeviceExtension,
622                         IN PIRP Irp)
623 {
624     NTSTATUS Status;
625     PIO_STACK_LOCATION Stack;
626     UNICODE_STRING DeviceName;
627     PMOUNTMGR_DRIVE_LETTER_TARGET DriveLetterTarget;
628     MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
629 
630     Stack = IoGetCurrentIrpStackLocation(Irp);
631 
632     /* Validate input */
633     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) ||
634         Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION))
635     {
636         return STATUS_INVALID_PARAMETER;
637     }
638 
639     DriveLetterTarget = (PMOUNTMGR_DRIVE_LETTER_TARGET)Irp->AssociatedIrp.SystemBuffer;
640     if (DriveLetterTarget->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
641     {
642         return STATUS_INVALID_PARAMETER;
643     }
644 
645     /* Call the worker */
646     DeviceName.Buffer = DriveLetterTarget->DeviceName;
647     DeviceName.Length =
648     DeviceName.MaximumLength = DriveLetterTarget->DeviceNameLength;
649 
650     Status = MountMgrNextDriveLetterWorker(DeviceExtension, &DeviceName,
651                                            &DriveLetterInformation);
652     if (NT_SUCCESS(Status))
653     {
654         *(PMOUNTMGR_DRIVE_LETTER_INFORMATION)Irp->AssociatedIrp.SystemBuffer =
655             DriveLetterInformation;
656         Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
657     }
658 
659     return Status;
660 }
661 
662 /*
663  * @implemented
664  */
665 NTSTATUS
666 NTAPI
667 MountMgrQuerySystemVolumeNameQueryRoutine(IN PWSTR ValueName,
668                                           IN ULONG ValueType,
669                                           IN PVOID ValueData,
670                                           IN ULONG ValueLength,
671                                           IN PVOID Context,
672                                           IN PVOID EntryContext)
673 {
674     UNICODE_STRING ValueString;
675     PUNICODE_STRING SystemVolumeName;
676 
677     UNREFERENCED_PARAMETER(ValueName);
678     UNREFERENCED_PARAMETER(ValueLength);
679     UNREFERENCED_PARAMETER(EntryContext);
680 
681     if (ValueType != REG_SZ)
682     {
683         return STATUS_SUCCESS;
684     }
685 
686     RtlInitUnicodeString(&ValueString, ValueData);
687     SystemVolumeName = Context;
688 
689     /* Return a string containing system volume name */
690     SystemVolumeName->Length = ValueString.Length;
691     SystemVolumeName->MaximumLength = ValueString.Length + sizeof(WCHAR);
692     SystemVolumeName->Buffer = AllocatePool(SystemVolumeName->MaximumLength);
693     if (SystemVolumeName->Buffer)
694     {
695         RtlCopyMemory(SystemVolumeName->Buffer, ValueData, ValueString.Length);
696         SystemVolumeName->Buffer[ValueString.Length / sizeof(WCHAR)] = UNICODE_NULL;
697     }
698 
699     return STATUS_SUCCESS;
700 
701 }
702 
703 /*
704  * @implemented
705  */
706 NTSTATUS
707 MountMgrQuerySystemVolumeName(OUT PUNICODE_STRING SystemVolumeName)
708 {
709     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
710 
711     RtlZeroMemory(QueryTable, sizeof(QueryTable));
712     QueryTable[0].QueryRoutine = MountMgrQuerySystemVolumeNameQueryRoutine;
713     QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
714     QueryTable[0].Name = L"SystemPartition";
715 
716     SystemVolumeName->Buffer = NULL;
717 
718     RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
719                            L"\\Registry\\Machine\\System\\Setup",
720                            QueryTable,
721                            SystemVolumeName,
722                            NULL);
723 
724     if (SystemVolumeName->Buffer)
725     {
726         return STATUS_SUCCESS;
727     }
728 
729     return STATUS_UNSUCCESSFUL;
730 }
731 
732 /*
733  * @implemented
734  */
735 VOID
736 MountMgrAssignDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
737 {
738     NTSTATUS Status;
739     PLIST_ENTRY NextEntry;
740     UNICODE_STRING SystemVolumeName;
741     PDEVICE_INFORMATION DeviceInformation;
742     MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
743 
744     /* First, get system volume name */
745     Status = MountMgrQuerySystemVolumeName(&SystemVolumeName);
746 
747     /* If there are no device, it's all done */
748     if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
749     {
750         if (NT_SUCCESS(Status))
751         {
752             FreePool(SystemVolumeName.Buffer);
753         }
754 
755         return;
756     }
757 
758     /* Now, for all the devices... */
759     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
760          NextEntry != &(DeviceExtension->DeviceListHead);
761          NextEntry = NextEntry->Flink)
762     {
763         DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
764 
765         /* If the device doesn't have a letter assigned, do it! */
766         if (!DeviceInformation->LetterAssigned)
767         {
768             MountMgrNextDriveLetterWorker(DeviceExtension,
769                                           &(DeviceInformation->DeviceName),
770                                           &DriveLetterInformation);
771         }
772 
773         /* If it's the system volume */
774         if (NT_SUCCESS(Status) && RtlEqualUnicodeString(&SystemVolumeName, &(DeviceInformation->DeviceName), TRUE))
775         {
776             /* Keep track of it */
777             DeviceExtension->DriveLetterData = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength +
778                                                             sizeof(MOUNTDEV_UNIQUE_ID));
779             if (DeviceExtension->DriveLetterData)
780             {
781                 RtlCopyMemory(DeviceExtension->DriveLetterData,
782                               DeviceInformation->UniqueId,
783                               DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
784             }
785 
786             /* Ensure it gets mounted if automount is disabled */
787             if (DeviceExtension->NoAutoMount)
788             {
789                 /* Temporarily re-enable automount for the
790                  * worker to mount and set a drive letter */
791                 DeviceExtension->NoAutoMount = FALSE;
792 
793                 MountMgrNextDriveLetterWorker(DeviceExtension,
794                                               &(DeviceInformation->DeviceName),
795                                               &DriveLetterInformation);
796 
797                 /* And re-disable automount */
798                 DeviceExtension->NoAutoMount = TRUE;
799             }
800         }
801     }
802 
803     if (NT_SUCCESS(Status))
804     {
805         FreePool(SystemVolumeName.Buffer);
806     }
807 }
808 
809 /*
810  * @implemented
811  */
812 NTSTATUS
813 MountMgrQueryDosVolumePath(IN PDEVICE_EXTENSION DeviceExtension,
814                            IN PIRP Irp)
815 {
816     NTSTATUS Status;
817     ULONG DevicesFound;
818     PIO_STACK_LOCATION Stack;
819     PLIST_ENTRY SymlinksEntry;
820     UNICODE_STRING SymbolicName;
821     PMOUNTMGR_TARGET_NAME Target;
822     PWSTR DeviceString, OldBuffer;
823     USHORT DeviceLength, OldLength;
824     PDEVICE_INFORMATION DeviceInformation;
825     PSYMLINK_INFORMATION SymlinkInformation;
826     PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
827 
828     Stack = IoGetCurrentIrpStackLocation(Irp);
829 
830     /* Validate input size */
831     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
832     {
833         return STATUS_INVALID_PARAMETER;
834     }
835 
836     /* Ensure we have received UNICODE_STRING */
837     Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
838     if (Target->DeviceNameLength & 1)
839     {
840         return STATUS_INVALID_PARAMETER;
841     }
842 
843     /* Validate the entry structure size */
844     if ((FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceNameLength) + Target->DeviceNameLength) >
845         Stack->Parameters.DeviceIoControl.InputBufferLength)
846     {
847         return STATUS_INVALID_PARAMETER;
848     }
849 
850     /* Ensure we can at least return needed size */
851     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
852     {
853         return STATUS_INVALID_PARAMETER;
854     }
855 
856     /* Construct string for query */
857     SymbolicName.Length = Target->DeviceNameLength;
858     SymbolicName.MaximumLength = Target->DeviceNameLength;
859     SymbolicName.Buffer = Target->DeviceName;
860 
861     /* Find device with our info */
862     Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
863     if (!NT_SUCCESS(Status))
864     {
865         return Status;
866     }
867 
868     DeviceLength = 0;
869     DeviceString = NULL;
870     DevicesFound = 0;
871 
872     /* Try to find associated device info */
873     while (TRUE)
874     {
875         for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
876              SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
877              SymlinksEntry = SymlinksEntry->Flink)
878         {
879             SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
880 
881             /* Try to find with drive letter */
882             if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
883             {
884                 break;
885             }
886         }
887 
888         /* We didn't find, break */
889         if (SymlinksEntry == &(DeviceInformation->SymbolicLinksListHead))
890         {
891             return STATUS_NOT_FOUND;
892         }
893 
894         /* It doesn't have associated device, go to fallback method */
895         if (IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
896         {
897             goto TryWithVolumeName;
898         }
899 
900         /* Create a string with the information about the device */
901         AssociatedDevice = CONTAINING_RECORD(&(DeviceInformation->SymbolicLinksListHead), ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
902         OldLength = DeviceLength;
903         OldBuffer = DeviceString;
904         DeviceLength += AssociatedDevice->String.Length;
905         DeviceString = AllocatePool(DeviceLength);
906         if (!DeviceString)
907         {
908             if (OldBuffer)
909             {
910                 FreePool(OldBuffer);
911             }
912 
913             return STATUS_INSUFFICIENT_RESOURCES;
914         }
915 
916         /* Store our info and previous if any */
917         RtlCopyMemory(DeviceString, AssociatedDevice->String.Buffer, AssociatedDevice->String.Length);
918         if (OldBuffer)
919         {
920             RtlCopyMemory(&DeviceString[AssociatedDevice->String.Length / sizeof(WCHAR)], OldBuffer, OldLength);
921             FreePool(OldBuffer);
922         }
923 
924         /* Count and continue looking */
925         ++DevicesFound;
926         DeviceInformation = AssociatedDevice->DeviceInformation;
927 
928         /* If too many devices, try another way */
929         if (DevicesFound > MAX_DEVICES) /*  1000 */
930         {
931             goto TryWithVolumeName;
932         }
933     }
934 
935     /* Reallocate our string, so that we can prepend disk letter */
936     OldBuffer = DeviceString;
937     OldLength = DeviceLength;
938     DeviceLength += 2 * sizeof(WCHAR);
939     DeviceString = AllocatePool(DeviceLength);
940     if (!DeviceString)
941     {
942         if (OldBuffer)
943         {
944             FreePool(OldBuffer);
945         }
946 
947         return STATUS_INSUFFICIENT_RESOURCES;
948     }
949 
950     /* Get the letter */
951     DeviceString[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
952     DeviceString[1] = L':';
953 
954     /* And copy the rest */
955     if (OldBuffer)
956     {
957         RtlCopyMemory(&DeviceString[2], OldBuffer, OldLength);
958         FreePool(OldBuffer);
959     }
960 
961 TryWithVolumeName:
962     /* If we didn't find anything, try differently */
963     if (DeviceLength < 2 * sizeof(WCHAR) || DeviceString[1] != L':')
964     {
965         if (DeviceString)
966         {
967             FreePool(DeviceString);
968             DeviceLength = 0;
969         }
970 
971         /* Try to find a volume name matching */
972         for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
973              SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
974              SymlinksEntry = SymlinksEntry->Flink)
975         {
976             SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
977 
978             if (MOUNTMGR_IS_VOLUME_NAME(&SymlinkInformation->Name))
979             {
980                 break;
981             }
982         }
983 
984         /* If found copy */
985         if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead))
986         {
987             DeviceLength = SymlinkInformation->Name.Length;
988             DeviceString = AllocatePool(DeviceLength);
989             if (!DeviceString)
990             {
991                 return STATUS_INSUFFICIENT_RESOURCES;
992             }
993 
994             RtlCopyMemory(DeviceString, SymlinkInformation->Name.Buffer, DeviceLength);
995             /* Ensure we are in the right namespace; [1] can be ? */
996             DeviceString[1] = L'\\';
997         }
998     }
999 
1000     /* If we found something */
1001     if (DeviceString)
1002     {
1003         /* At least, we will return our length */
1004         ((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSzLength = DeviceLength;
1005         /* MOUNTMGR_VOLUME_PATHS is a string + a ULONG */
1006         Irp->IoStatus.Information = DeviceLength + sizeof(ULONG);
1007 
1008         /* If we have enough room for copying the string */
1009         if (sizeof(ULONG) + DeviceLength <= Stack->Parameters.DeviceIoControl.OutputBufferLength)
1010         {
1011             /* Copy it */
1012             if (DeviceLength)
1013             {
1014                 RtlCopyMemory(((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz, DeviceString, DeviceLength);
1015             }
1016 
1017             /* And double zero at its end - this is needed in case of multiple paths which are separated by a single 0 */
1018             FreePool(DeviceString);
1019             ((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz[DeviceLength / sizeof(WCHAR)] = 0;
1020             ((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz[DeviceLength / sizeof(WCHAR) + 1] = 0;
1021 
1022             return STATUS_SUCCESS;
1023         }
1024         else
1025         {
1026             /* Just return appropriate size and leave */
1027             FreePool(DeviceString);
1028             Irp->IoStatus.Information = sizeof(ULONG);
1029             return STATUS_BUFFER_OVERFLOW;
1030         }
1031     }
1032 
1033     /* Fail */
1034     return STATUS_NOT_FOUND;
1035 }
1036 
1037 /*
1038  * @implemented
1039  */
1040 NTSTATUS
1041 MountMgrValidateBackPointer(IN PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry,
1042                             IN PDEVICE_INFORMATION DeviceInformation,
1043                             OUT PBOOLEAN Invalid)
1044 {
1045     HANDLE Handle;
1046     NTSTATUS Status;
1047     PLIST_ENTRY SymlinksEntry;
1048     IO_STATUS_BLOCK IoStatusBlock;
1049     PREPARSE_DATA_BUFFER ReparseData;
1050     OBJECT_ATTRIBUTES ObjectAttributes;
1051     UNICODE_STRING FullName, SubstituteName;
1052     PSYMLINK_INFORMATION SymlinkInformation;
1053 
1054     /* Initialize & allocate a string big enough to contain our complete mount point name */
1055     FullName.Length = 0;
1056     FullName.MaximumLength = AssociatedDeviceEntry->String.Length
1057                              + AssociatedDeviceEntry->DeviceInformation->DeviceName.Length
1058                              + sizeof(WCHAR)
1059                              + sizeof(UNICODE_NULL);
1060     FullName.Buffer = AllocatePool(FullName.MaximumLength);
1061     if (!FullName.Buffer)
1062     {
1063         return STATUS_INSUFFICIENT_RESOURCES;
1064     }
1065 
1066     /* Create the path */
1067     RtlAppendUnicodeStringToString(&FullName, &AssociatedDeviceEntry->DeviceInformation->DeviceName);
1068     FullName.Buffer[FullName.Length / sizeof(WCHAR)] = L'\\';
1069     RtlAppendUnicodeStringToString(&FullName, &AssociatedDeviceEntry->String);
1070     FullName.Buffer[FullName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1071 
1072     /* Open it to query the reparse point */
1073     InitializeObjectAttributes(&ObjectAttributes,
1074                                &FullName,
1075                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1076                                NULL,
1077                                NULL);
1078     Status = ZwOpenFile(&Handle,
1079                         SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1080                         &ObjectAttributes, &IoStatusBlock,
1081                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1082                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
1083     FreePool(FullName.Buffer);
1084 
1085     if (!NT_SUCCESS(Status))
1086     {
1087         *Invalid = TRUE;
1088         return STATUS_SUCCESS;
1089     }
1090 
1091     /* Allocate a buffer big enough to read reparse data */
1092     ReparseData = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1093     if (ReparseData == NULL)
1094     {
1095         ZwClose(Handle);
1096         return STATUS_INSUFFICIENT_RESOURCES;
1097     }
1098 
1099     /* Query reparse data */
1100     Status = ZwFsControlFile(Handle,
1101                              NULL, NULL, NULL,
1102                              &IoStatusBlock,
1103                              FSCTL_GET_REPARSE_POINT,
1104                              NULL, 0,
1105                              ReparseData, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1106     ZwClose(Handle);
1107 
1108     if (!NT_SUCCESS(Status))
1109     {
1110         FreePool(ReparseData);
1111         *Invalid = TRUE;
1112         return STATUS_SUCCESS;
1113     }
1114 
1115     /* Create a string with the substitute name */
1116     SubstituteName.Length = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
1117     SubstituteName.MaximumLength = SubstituteName.Length;
1118     SubstituteName.Buffer = (PWSTR)((ULONG_PTR)ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset);
1119 
1120     /* If that's a volume name that matches our associated device, that's a success! */
1121     if (MOUNTMGR_IS_VOLUME_NAME(&SubstituteName))
1122     {
1123         if (SubstituteName.Length == 98 && SubstituteName.Buffer[1] == L'?')
1124         {
1125             for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
1126                  SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
1127                  SymlinksEntry = SymlinksEntry->Flink)
1128             {
1129                 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1130 
1131                 if (RtlEqualUnicodeString(&SubstituteName, &SymlinkInformation->Name, TRUE))
1132                 {
1133                     FreePool(ReparseData);
1134                     return STATUS_SUCCESS;
1135                 }
1136             }
1137         }
1138     }
1139 
1140     FreePool(ReparseData);
1141     *Invalid = TRUE;
1142     return STATUS_SUCCESS;
1143 }
1144 
1145 /*
1146  * @implemented
1147  */
1148 NTSTATUS
1149 MountMgrQueryVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1150                          IN PDEVICE_INFORMATION DeviceInformation,
1151                          IN PLIST_ENTRY DeviceInfoList,
1152                          OUT PMOUNTMGR_VOLUME_PATHS * VolumePaths,
1153                          OUT PDEVICE_INFORMATION *FailedDevice)
1154 {
1155     ULONG Written;
1156     NTSTATUS Status;
1157     PLIST_ENTRY Entry;
1158     PSYMLINK_INFORMATION SymlinkInformation;
1159     PDEVICE_INFORMATION_ENTRY DeviceInfoEntry;
1160     PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry;
1161     PMOUNTMGR_VOLUME_PATHS * Paths = NULL, * CurrentPath;
1162     ULONG OutputPathLength, NumberOfPaths, ReturnedPaths;
1163 
1164     /* We return at least null char */
1165     OutputPathLength = sizeof(UNICODE_NULL);
1166 
1167     for (Entry = DeviceInformation->SymbolicLinksListHead.Flink;
1168          Entry != &(DeviceInformation->SymbolicLinksListHead);
1169          Entry = Entry->Flink)
1170     {
1171         SymlinkInformation = CONTAINING_RECORD(Entry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1172 
1173         /* Try to find the drive letter (ie, DOS device) */
1174         if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
1175         {
1176             /* We'll return the letter */
1177             OutputPathLength = 4 * sizeof(WCHAR);
1178             break;
1179         }
1180     }
1181 
1182     /* We didn't find any */
1183     if (Entry == &(DeviceInformation->SymbolicLinksListHead))
1184     {
1185         SymlinkInformation = NULL;
1186     }
1187 
1188     /* Do we have any device info to return? */
1189     for (Entry = DeviceInfoList->Flink; Entry != DeviceInfoList; Entry = Entry->Flink)
1190     {
1191         DeviceInfoEntry = CONTAINING_RECORD(Entry, DEVICE_INFORMATION_ENTRY, DeviceInformationEntry);
1192 
1193         /* Matching current device */
1194         if (DeviceInfoEntry->DeviceInformation == DeviceInformation)
1195         {
1196             /* Allocate the output buffer */
1197             *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1198             if (*VolumePaths == NULL)
1199             {
1200                 return STATUS_INSUFFICIENT_RESOURCES;
1201             }
1202 
1203             /* Set size */
1204             (*VolumePaths)->MultiSzLength = OutputPathLength;
1205             /* If we have a drive letter, return it */
1206             if (SymlinkInformation != NULL)
1207             {
1208                 (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1209                 (*VolumePaths)->MultiSz[1] = L':';
1210                 (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1211                 (*VolumePaths)->MultiSz[3] = UNICODE_NULL;
1212             }
1213             else
1214             {
1215                 (*VolumePaths)->MultiSz[0] = UNICODE_NULL;
1216             }
1217 
1218             return STATUS_SUCCESS;
1219         }
1220     }
1221 
1222     /* Allocate a new device entry */
1223     DeviceInfoEntry = AllocatePool(sizeof(DEVICE_INFORMATION_ENTRY));
1224     if (DeviceInfoEntry == NULL)
1225     {
1226         return STATUS_INSUFFICIENT_RESOURCES;
1227     }
1228 
1229     /* Add it to the list */
1230     DeviceInfoEntry->DeviceInformation = DeviceInformation;
1231     InsertTailList(DeviceInfoList, &DeviceInfoEntry->DeviceInformationEntry);
1232 
1233     NumberOfPaths = 0;
1234     /* Count the amount of devices we will have to handle */
1235     if (!IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
1236     {
1237         for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1238                      Entry != &DeviceInformation->AssociatedDevicesHead;
1239                      Entry = Entry->Flink)
1240         {
1241             ++NumberOfPaths;
1242         }
1243 
1244         ASSERT(NumberOfPaths != 0);
1245         /* And allocate a big enough buffer */
1246         Paths = AllocatePool(NumberOfPaths * sizeof(PMOUNTMGR_VOLUME_PATHS));
1247         if (Paths == NULL)
1248         {
1249             RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1250             FreePool(DeviceInfoEntry);
1251             return STATUS_INSUFFICIENT_RESOURCES;
1252         }
1253     }
1254 
1255     /* Start the hot loop to gather all the paths and be able to compute total output length! */
1256     ReturnedPaths = 0;
1257     CurrentPath = Paths;
1258     for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1259          Entry != &DeviceInformation->AssociatedDevicesHead;
1260          Entry = Entry->Flink)
1261     {
1262         USHORT InnerStrings;
1263         BOOLEAN Invalid = FALSE;
1264 
1265         AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1266 
1267         /* Validate the fact its a mount point by query reparse data */
1268         Status = MountMgrValidateBackPointer(AssociatedDeviceEntry, DeviceInformation, &Invalid);
1269 
1270         /* If we found an invalid device, that's a failure */
1271         if (Invalid)
1272         {
1273             *FailedDevice = AssociatedDeviceEntry->DeviceInformation;
1274             Status = STATUS_UNSUCCESSFUL;
1275         }
1276 
1277         /* Check whether we failed, if so, bail out */
1278         if (!NT_SUCCESS(Status))
1279         {
1280             ULONG i;
1281 
1282             for (i = 0; i < ReturnedPaths; ++i)
1283             {
1284                 FreePool(Paths[i]);
1285             }
1286 
1287             if (Paths != NULL)
1288             {
1289                 FreePool(Paths);
1290             }
1291             RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1292             FreePool(DeviceInfoEntry);
1293             return Status;
1294         }
1295 
1296         /* Query associated paths (hello ourselves :-)) */
1297         Status = MountMgrQueryVolumePaths(DeviceExtension,
1298                                           AssociatedDeviceEntry->DeviceInformation,
1299                                           DeviceInfoList,
1300                                           CurrentPath,
1301                                           FailedDevice);
1302         if (!NT_SUCCESS(Status))
1303         {
1304             ULONG i;
1305 
1306             for (i = 0; i < ReturnedPaths; ++i)
1307             {
1308                 FreePool(Paths[i]);
1309             }
1310 
1311             if (Paths != NULL)
1312             {
1313                 FreePool(Paths);
1314             }
1315             RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1316             FreePool(DeviceInfoEntry);
1317             return Status;
1318         }
1319 
1320         /* Count the number of strings we have in the multi string buffer */
1321         InnerStrings = 0;
1322         if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1323         {
1324             ULONG i;
1325             PWSTR MultiSz = (*CurrentPath)->MultiSz;
1326 
1327             for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR); ++i, ++MultiSz)
1328             {
1329                 if (*MultiSz == UNICODE_NULL)
1330                 {
1331                     ++InnerStrings;
1332                 }
1333             }
1334         }
1335 
1336         /* We returned one more path (ie, one more allocated buffer) */
1337         ++ReturnedPaths;
1338         /* Move the next pointer to use in the array */
1339         ++CurrentPath;
1340         /* Multiply String.Length by the number of found paths, we always add it after a path */
1341         OutputPathLength += (*CurrentPath)->MultiSzLength + InnerStrings * AssociatedDeviceEntry->String.Length - sizeof(UNICODE_NULL);
1342     }
1343 
1344     /* Allocate the output buffer */
1345     *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1346     if (*VolumePaths == NULL)
1347     {
1348         ULONG i;
1349 
1350         for (i = 0; i < ReturnedPaths; ++i)
1351         {
1352             FreePool(Paths[i]);
1353         }
1354 
1355         if (Paths != NULL)
1356         {
1357             FreePool(Paths);
1358         }
1359         RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1360         FreePool(DeviceInfoEntry);
1361         return STATUS_INSUFFICIENT_RESOURCES;
1362     }
1363 
1364     Written = 0;
1365     /* If we had found a DOS letter, that's the first thing we return */
1366     (*VolumePaths)->MultiSzLength = OutputPathLength;
1367     if (SymlinkInformation != NULL)
1368     {
1369         (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1370         (*VolumePaths)->MultiSz[1] = L':';
1371         (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1372         Written = 3;
1373     }
1374 
1375     /* Now, browse again all our paths to return them */
1376     CurrentPath = Paths;
1377     for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1378          Entry != &DeviceInformation->AssociatedDevicesHead;
1379          Entry = Entry->Flink)
1380     {
1381         AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1382 
1383         /* If we had a path... */
1384         if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1385         {
1386             ULONG i, Offset;
1387             PWSTR MultiSz;
1388 
1389             /* This offset is used to "jump" into MultiSz, so, start with the string begin (ie, skip MultiSzLength) */
1390             Offset = sizeof(ULONG);
1391             /* Browse every single letter, and skip last UNICODE_NULL */
1392             for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR) - 1; ++i)
1393             {
1394                 /* Get the letter */
1395                 MultiSz = (PWSTR)((ULONG_PTR)(*CurrentPath) + Offset);
1396                 /* If it was part of the path, just return it */
1397                 if (*MultiSz != UNICODE_NULL)
1398                 {
1399                     (*VolumePaths)->MultiSz[Written] = *MultiSz;
1400                 }
1401                 else
1402                 {
1403                     /* Otherwise, as planed, return our whole associated device name */
1404                     RtlCopyMemory(&(*VolumePaths)->MultiSz[Written],
1405                                   AssociatedDeviceEntry->String.Buffer,
1406                                   AssociatedDeviceEntry->String.Length);
1407                     Written += AssociatedDeviceEntry->String.Length / sizeof(WCHAR);
1408                     /* And don't forget to nullify */
1409                     (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1410                 }
1411 
1412                 /* We at least return a letter or a null char */
1413                 ++Written;
1414                 /* Move to the next letter */
1415                 Offset += sizeof(WCHAR);
1416             }
1417         }
1418 
1419         FreePool(*CurrentPath);
1420         ++CurrentPath;
1421     }
1422 
1423     /* MultiSz: don't forget last null char */
1424     (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1425     /* Cleanup everything and return success! */
1426     if (Paths != NULL)
1427     {
1428         FreePool(Paths);
1429     }
1430     RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1431     FreePool(DeviceInfoEntry);
1432     return STATUS_SUCCESS;
1433 }
1434 
1435 /*
1436  * @implemented
1437  */
1438 NTSTATUS
1439 MountMgrQueryDosVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1440                             IN PIRP Irp)
1441 {
1442     NTSTATUS Status;
1443     PLIST_ENTRY Entry;
1444     LIST_ENTRY Devices;
1445     BOOLEAN NeedNotification;
1446     PIO_STACK_LOCATION Stack;
1447     UNICODE_STRING SymbolicName;
1448     ULONG Attempts, OutputLength;
1449     PMOUNTMGR_TARGET_NAME Target;
1450     PMOUNTMGR_VOLUME_PATHS Paths, Output;
1451     RECONCILE_WORK_ITEM_CONTEXT ReconcileContext;
1452     PDEVICE_INFORMATION DeviceInformation, ListDeviceInfo, FailedDevice;
1453 
1454     Stack = IoGetCurrentIrpStackLocation(Irp);
1455 
1456     /* Validate input size */
1457     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1458     {
1459         return STATUS_INVALID_PARAMETER;
1460     }
1461 
1462     /* Ensure we have received UNICODE_STRING */
1463     Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1464     if (Target->DeviceNameLength & 1)
1465     {
1466         return STATUS_INVALID_PARAMETER;
1467     }
1468 
1469     /* Validate the entry structure size */
1470     if (Target->DeviceNameLength + FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1471     {
1472         return STATUS_INVALID_PARAMETER;
1473     }
1474 
1475     /* Ensure we can at least return needed size */
1476     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
1477     {
1478         return STATUS_INVALID_PARAMETER;
1479     }
1480 
1481     /* Construct string for query */
1482     SymbolicName.Length = Target->DeviceNameLength;
1483     SymbolicName.MaximumLength = Target->DeviceNameLength + sizeof(UNICODE_NULL);
1484     SymbolicName.Buffer = Target->DeviceName;
1485 
1486     /* Find device with our info */
1487     Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
1488     if (!NT_SUCCESS(Status))
1489     {
1490         return Status;
1491     }
1492 
1493     NeedNotification = FALSE;
1494     Attempts = 0;
1495     for (;;)
1496     {
1497         FailedDevice = NULL;
1498         InitializeListHead(&Devices);
1499 
1500         /* Query paths */
1501         Status = MountMgrQueryVolumePaths(DeviceExtension, DeviceInformation, &Devices, &Paths, &FailedDevice);
1502         if (NT_SUCCESS(Status))
1503         {
1504             break;
1505         }
1506 
1507         /* If it failed for generic reason (memory, whatever), bail out (ie, FailedDevice not set) */
1508         if (FailedDevice == NULL)
1509         {
1510             return Status;
1511         }
1512 
1513         /* If PnP, let's notify in case of success */
1514         if (!DeviceInformation->ManuallyRegistered)
1515         {
1516             NeedNotification = TRUE;
1517         }
1518 
1519         /* Reconcile database */
1520         ReconcileContext.DeviceExtension = DeviceExtension;
1521         ReconcileContext.DeviceInformation = FailedDevice;
1522         KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1523         ReconcileThisDatabaseWithMasterWorker(&ReconcileContext);
1524         KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1525 
1526         /* Look for our device, to check it's online */
1527         for (Entry = DeviceExtension->DeviceListHead.Flink;
1528              Entry != &DeviceExtension->DeviceListHead;
1529              Entry = Entry->Flink)
1530         {
1531             ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
1532             /* It's online, it's OK! */
1533             if (ListDeviceInfo == DeviceInformation)
1534             {
1535                 break;
1536             }
1537         }
1538 
1539         /* It's not online, it's not good */
1540         if (Entry == &DeviceExtension->DeviceListHead)
1541         {
1542             return STATUS_OBJECT_NAME_NOT_FOUND;
1543         }
1544 
1545         /* Increase attempts count */
1546         ++Attempts;
1547         /* Don't look forever and fail if we get out of attempts */
1548         if (Attempts >= 1000)
1549         {
1550             return Status;
1551         }
1552     }
1553 
1554     /* We need to notify? Go ahead */
1555     if (NeedNotification)
1556     {
1557         MountMgrNotifyNameChange(DeviceExtension, &SymbolicName, FALSE);
1558     }
1559 
1560     /* Get output buffer */
1561     Output = (PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer;
1562 
1563     /* Set required size */
1564     Output->MultiSzLength = Paths->MultiSzLength;
1565 
1566     /* Compute total length */
1567     OutputLength = Output->MultiSzLength + sizeof(ULONG);
1568 
1569     /* If it cannot fit, just return need size and quit */
1570     if (OutputLength > Stack->Parameters.DeviceIoControl.OutputBufferLength)
1571     {
1572         Irp->IoStatus.Information = sizeof(ULONG);
1573         FreePool(Paths);
1574         return STATUS_BUFFER_OVERFLOW;
1575     }
1576 
1577     /* Copy data and quit */
1578     Irp->IoStatus.Information = OutputLength;
1579     RtlCopyMemory(Output->MultiSz, Paths->MultiSz, Output->MultiSzLength);
1580     FreePool(Paths);
1581     return STATUS_SUCCESS;
1582 }
1583 
1584 /*
1585  * @implemented
1586  */
1587 NTSTATUS
1588 MountMgrKeepLinksWhenOffline(IN PDEVICE_EXTENSION DeviceExtension,
1589                              IN PIRP Irp)
1590 {
1591     NTSTATUS Status;
1592     PIO_STACK_LOCATION Stack;
1593     UNICODE_STRING SymbolicName;
1594     PMOUNTMGR_TARGET_NAME Target;
1595     PDEVICE_INFORMATION DeviceInformation;
1596 
1597     Stack = IoGetCurrentIrpStackLocation(Irp);
1598 
1599     /* Validate input */
1600     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1601     {
1602         return STATUS_INVALID_PARAMETER;
1603     }
1604 
1605     Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1606     if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1607     {
1608         return STATUS_INVALID_PARAMETER;
1609     }
1610 
1611     SymbolicName.Length =
1612     SymbolicName.MaximumLength = Target->DeviceNameLength;
1613     SymbolicName.Buffer = Target->DeviceName;
1614 
1615     /* Find the associated device */
1616     Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
1617     if (!NT_SUCCESS(Status))
1618     {
1619         return Status;
1620     }
1621 
1622     /* Mark we want to keep links */
1623     DeviceInformation->KeepLinks = TRUE;
1624 
1625     return STATUS_SUCCESS;
1626 }
1627 
1628 /*
1629  * @implemented
1630  */
1631 NTSTATUS
1632 MountMgrVolumeArrivalNotification(IN PDEVICE_EXTENSION DeviceExtension,
1633                                   IN PIRP Irp)
1634 {
1635     NTSTATUS Status;
1636     BOOLEAN OldState;
1637     PIO_STACK_LOCATION Stack;
1638     UNICODE_STRING SymbolicName;
1639     PMOUNTMGR_TARGET_NAME Target;
1640 
1641     Stack = IoGetCurrentIrpStackLocation(Irp);
1642 
1643     /* Validate input */
1644     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1645     {
1646         return STATUS_INVALID_PARAMETER;
1647     }
1648 
1649     Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1650     if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1651     {
1652         return STATUS_INVALID_PARAMETER;
1653     }
1654 
1655     SymbolicName.Length =
1656     SymbolicName.MaximumLength = Target->DeviceNameLength;
1657     SymbolicName.Buffer = Target->DeviceName;
1658 
1659     /* Disable hard errors */
1660     OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
1661     PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
1662 
1663     /* Call real worker */
1664     Status = MountMgrMountedDeviceArrival(DeviceExtension, &SymbolicName, TRUE);
1665 
1666     PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
1667 
1668     return Status;
1669 }
1670 
1671 /*
1672  * @implemented
1673  */
1674 NTSTATUS
1675 MountMgrQueryPoints(IN PDEVICE_EXTENSION DeviceExtension,
1676                     IN PIRP Irp)
1677 {
1678     NTSTATUS Status;
1679     PIO_STACK_LOCATION Stack;
1680     PMOUNTDEV_UNIQUE_ID UniqueId;
1681     PMOUNTMGR_MOUNT_POINT MountPoint;
1682     UNICODE_STRING SymbolicName, DeviceName;
1683 
1684     Stack = IoGetCurrentIrpStackLocation(Irp);
1685 
1686     /* Validate input... */
1687     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1688     {
1689         return STATUS_INVALID_PARAMETER;
1690     }
1691 
1692     MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1693     if (!MountPoint->SymbolicLinkNameLength)
1694     {
1695         MountPoint->SymbolicLinkNameOffset = 0;
1696     }
1697 
1698     if (!MountPoint->UniqueIdLength)
1699     {
1700         MountPoint->UniqueIdOffset = 0;
1701     }
1702 
1703     if (!MountPoint->DeviceNameLength)
1704     {
1705         MountPoint->DeviceNameOffset = 0;
1706     }
1707 
1708     /* Addresses can't be odd */
1709     if ((MountPoint->SymbolicLinkNameOffset & 1) ||
1710         (MountPoint->SymbolicLinkNameLength & 1))
1711     {
1712         return STATUS_INVALID_PARAMETER;
1713     }
1714 
1715     if ((MountPoint->UniqueIdOffset & 1) ||
1716         (MountPoint->UniqueIdLength & 1))
1717     {
1718         return STATUS_INVALID_PARAMETER;
1719     }
1720 
1721     if ((MountPoint->DeviceNameOffset & 1) ||
1722         (MountPoint->DeviceNameLength & 1))
1723     {
1724         return STATUS_INVALID_PARAMETER;
1725     }
1726 
1727     /* We can't go beyond */
1728     if (((ULONG)MountPoint->SymbolicLinkNameLength + MountPoint->UniqueIdLength +
1729         MountPoint->DeviceNameLength) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1730     {
1731         return STATUS_INVALID_PARAMETER;
1732     }
1733 
1734     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS))
1735     {
1736         return STATUS_INVALID_PARAMETER;
1737     }
1738 
1739     /* If caller provided a Symlink, use it */
1740     if (MountPoint->SymbolicLinkNameLength != 0)
1741     {
1742         if (MountPoint->SymbolicLinkNameLength > MAXSHORT)
1743         {
1744             return STATUS_INVALID_PARAMETER;
1745         }
1746 
1747         SymbolicName.Length = MountPoint->SymbolicLinkNameLength;
1748         SymbolicName.MaximumLength = MountPoint->SymbolicLinkNameLength + sizeof(WCHAR);
1749         SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1750         if (!SymbolicName.Buffer)
1751         {
1752             return STATUS_INSUFFICIENT_RESOURCES;
1753         }
1754 
1755         RtlCopyMemory(SymbolicName.Buffer,
1756                       (PWSTR)((ULONG_PTR)MountPoint + MountPoint->SymbolicLinkNameOffset),
1757                       SymbolicName.Length);
1758         SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1759 
1760         /* Query links using it */
1761         Status = QueryPointsFromSymbolicLinkName(DeviceExtension, &SymbolicName, Irp);
1762         FreePool(SymbolicName.Buffer);
1763     }
1764     /* If user provided an unique ID */
1765     else if (MountPoint->UniqueIdLength != 0)
1766     {
1767         UniqueId = AllocatePool(MountPoint->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1768         if (!UniqueId)
1769         {
1770             return STATUS_INSUFFICIENT_RESOURCES;
1771         }
1772 
1773         UniqueId->UniqueIdLength = MountPoint->UniqueIdLength;
1774         RtlCopyMemory(UniqueId->UniqueId,
1775                       (PVOID)((ULONG_PTR)MountPoint + MountPoint->UniqueIdOffset),
1776                       MountPoint->UniqueIdLength);
1777 
1778          /* Query links using it */
1779          Status = QueryPointsFromMemory(DeviceExtension, Irp, UniqueId, NULL);
1780          FreePool(UniqueId);
1781     }
1782     /* If caller provided a device name */
1783     else if (MountPoint->DeviceNameLength != 0)
1784     {
1785         if (MountPoint->DeviceNameLength > MAXSHORT)
1786         {
1787             return STATUS_INVALID_PARAMETER;
1788         }
1789 
1790         DeviceName.Length = MountPoint->DeviceNameLength;
1791         DeviceName.MaximumLength = MountPoint->DeviceNameLength + sizeof(WCHAR);
1792         DeviceName.Buffer = AllocatePool(DeviceName.MaximumLength);
1793         if (!DeviceName.Buffer)
1794         {
1795             return STATUS_INSUFFICIENT_RESOURCES;
1796         }
1797 
1798         RtlCopyMemory(DeviceName.Buffer,
1799                       (PWSTR)((ULONG_PTR)MountPoint + MountPoint->DeviceNameOffset),
1800                       DeviceName.Length);
1801         DeviceName.Buffer[DeviceName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1802 
1803          /* Query links using it */
1804         Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, &DeviceName);
1805         FreePool(DeviceName.Buffer);
1806     }
1807     else
1808     {
1809         /* Otherwise, query all links */
1810         Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, NULL);
1811     }
1812 
1813     return Status;
1814 }
1815 
1816 /*
1817  * @implemented
1818  */
1819 NTSTATUS
1820 MountMgrDeletePoints(IN PDEVICE_EXTENSION DeviceExtension,
1821                      IN PIRP Irp)
1822 {
1823     ULONG Link;
1824     NTSTATUS Status;
1825     BOOLEAN CreateNoDrive;
1826     PIO_STACK_LOCATION Stack;
1827     PMOUNTDEV_UNIQUE_ID UniqueId;
1828     PMOUNTMGR_MOUNT_POINT MountPoint;
1829     PMOUNTMGR_MOUNT_POINTS MountPoints;
1830     UNICODE_STRING SymbolicName, DeviceName;
1831 
1832     Stack = IoGetCurrentIrpStackLocation(Irp);
1833 
1834     /* Validate input */
1835     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1836     {
1837         return STATUS_INVALID_PARAMETER;
1838     }
1839 
1840     /* Query points */
1841     MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1842     CreateNoDrive = (MountPoint->SymbolicLinkNameOffset && MountPoint->SymbolicLinkNameLength);
1843 
1844     Status = MountMgrQueryPoints(DeviceExtension, Irp);
1845     if (!NT_SUCCESS(Status))
1846     {
1847         return Status;
1848     }
1849 
1850     /* For all the points matching the request */
1851     MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1852     for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1853     {
1854         SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1855         SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1856         SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1857         if (!SymbolicName.Buffer)
1858         {
1859             return STATUS_INSUFFICIENT_RESOURCES;
1860         }
1861 
1862         RtlCopyMemory(SymbolicName.Buffer,
1863                       (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1864                       SymbolicName.Length);
1865         SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1866 
1867         /* Create a no drive entry for the drive letters */
1868         if (CreateNoDrive && IsDriveLetter(&SymbolicName))
1869         {
1870             UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1871             if (UniqueId)
1872             {
1873                 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1874                 RtlCopyMemory(UniqueId->UniqueId,
1875                               (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1876                               MountPoints->MountPoints[Link].UniqueIdLength);
1877 
1878                 CreateNoDriveLetterEntry(UniqueId);
1879                 FreePool(UniqueId);
1880             }
1881         }
1882 
1883         /* If there are no link any more, and no need to create a no drive entry */
1884         if (Link == 0 && !CreateNoDrive)
1885         {
1886             /* Then, delete everything */
1887             UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength);
1888             if (UniqueId)
1889             {
1890                 RtlCopyMemory(UniqueId,
1891                               (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1892                               MountPoints->MountPoints[Link].UniqueIdLength);
1893 
1894                 DeleteNoDriveLetterEntry(UniqueId);
1895                 FreePool(UniqueId);
1896             }
1897         }
1898 
1899         /* Delete all the information about the mount point */
1900         GlobalDeleteSymbolicLink(&SymbolicName);
1901         DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, FALSE);
1902         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1903         FreePool(SymbolicName.Buffer);
1904 
1905         /* Notify the change */
1906         DeviceName.Length = DeviceName.MaximumLength =
1907         MountPoints->MountPoints[Link].DeviceNameLength;
1908         DeviceName.Buffer = (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].DeviceNameOffset);
1909         MountMgrNotifyNameChange(DeviceExtension, &DeviceName, TRUE);
1910     }
1911 
1912     MountMgrNotify(DeviceExtension);
1913 
1914     return Status;
1915 }
1916 
1917 /*
1918  * @implemented
1919  */
1920 NTSTATUS
1921 MountMgrDeletePointsDbOnly(IN PDEVICE_EXTENSION DeviceExtension,
1922                            IN PIRP Irp)
1923 {
1924     ULONG Link;
1925     NTSTATUS Status;
1926     UNICODE_STRING SymbolicName;
1927     PMOUNTDEV_UNIQUE_ID UniqueId;
1928     PMOUNTMGR_MOUNT_POINTS MountPoints;
1929 
1930     /* Query points */
1931     Status = MountMgrQueryPoints(DeviceExtension, Irp);
1932     if (!NT_SUCCESS(Status))
1933     {
1934         return Status;
1935     }
1936 
1937     MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1938     if (MountPoints->NumberOfMountPoints == 0)
1939     {
1940         return Status;
1941     }
1942 
1943     /* For all the mount points */
1944     for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1945     {
1946         SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1947         SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1948         SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1949         if (!SymbolicName.Buffer)
1950         {
1951             return STATUS_INSUFFICIENT_RESOURCES;
1952         }
1953 
1954         RtlCopyMemory(SymbolicName.Buffer,
1955                       (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1956                       SymbolicName.Length);
1957         SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1958 
1959         /* If the only mount point is a drive letter, then create a no letter drive entry */
1960         if (MountPoints->NumberOfMountPoints == 1 && IsDriveLetter(&SymbolicName))
1961         {
1962             UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1963             if (UniqueId)
1964             {
1965                 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1966                 RtlCopyMemory(UniqueId->UniqueId,
1967                               (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1968                               MountPoints->MountPoints[Link].UniqueIdLength);
1969 
1970                 CreateNoDriveLetterEntry(UniqueId);
1971                 FreePool(UniqueId);
1972             }
1973         }
1974 
1975         /* Simply delete mount point from DB */
1976         DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, TRUE);
1977         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1978         FreePool(SymbolicName.Buffer);
1979     }
1980 
1981     return Status;
1982 }
1983 
1984 /*
1985  * @implemented
1986  */
1987 NTSTATUS
1988 MountMgrVolumeMountPointChanged(IN PDEVICE_EXTENSION DeviceExtension,
1989                                 IN PIRP Irp,
1990                                 IN NTSTATUS LockStatus,
1991                                 OUT PUNICODE_STRING SourceDeviceName,
1992                                 OUT PUNICODE_STRING SourceSymbolicName,
1993                                 OUT PUNICODE_STRING TargetVolumeName)
1994 {
1995     HANDLE Handle;
1996     NTSTATUS Status;
1997     PFILE_OBJECT FileObject;
1998     PIO_STACK_LOCATION Stack;
1999     ULONG Length, SavedLength;
2000     BOOLEAN FOReferenced = FALSE;
2001     IO_STATUS_BLOCK IoStatusBlock;
2002     OBJECT_ATTRIBUTES ObjectAttributes;
2003     PDEVICE_INFORMATION DeviceInformation;
2004     OBJECT_NAME_INFORMATION ObjectNameInfo;
2005     FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
2006     PFILE_NAME_INFORMATION FileNameInfo = NULL;
2007     PMOUNTMGR_VOLUME_MOUNT_POINT VolumeMountPoint;
2008     POBJECT_NAME_INFORMATION ObjectNameInfoPtr = NULL;
2009     UNICODE_STRING SourceVolumeName, TargetDeviceName;
2010 
2011     Stack = IoGetCurrentIrpStackLocation(Irp);
2012 
2013     /* Validate input */
2014     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT))
2015     {
2016         return STATUS_INVALID_PARAMETER;
2017     }
2018 
2019     VolumeMountPoint = (PMOUNTMGR_VOLUME_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
2020 
2021     if (((ULONG)VolumeMountPoint->SourceVolumeNameLength + VolumeMountPoint->TargetVolumeNameLength) <
2022         Stack->Parameters.DeviceIoControl.InputBufferLength)
2023     {
2024         return STATUS_INVALID_PARAMETER;
2025     }
2026 
2027     /* Get source volume name */
2028     SourceVolumeName.Length =
2029     SourceVolumeName.MaximumLength = VolumeMountPoint->SourceVolumeNameLength;
2030     SourceVolumeName.Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset);
2031 
2032     InitializeObjectAttributes(&ObjectAttributes,
2033                                &SourceVolumeName,
2034                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
2035                                NULL,
2036                                NULL);
2037 
2038     /* Open it */
2039     Status = ZwOpenFile(&Handle,
2040                         SYNCHRONIZE | FILE_READ_ATTRIBUTES,
2041                         &ObjectAttributes,
2042                         &IoStatusBlock,
2043                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2044                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
2045     if (!NT_SUCCESS(Status))
2046     {
2047         return Status;
2048     }
2049 
2050     TargetDeviceName.Buffer = NULL;
2051 
2052     /* Query its attributes */
2053     Status = ZwQueryVolumeInformationFile(Handle,
2054                                           &IoStatusBlock,
2055                                           &FsDeviceInfo,
2056                                           sizeof(FsDeviceInfo),
2057                                           FileFsDeviceInformation);
2058     if (!NT_SUCCESS(Status))
2059     {
2060         goto Cleanup;
2061     }
2062 
2063     if (FsDeviceInfo.DeviceType != FILE_DEVICE_DISK && FsDeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK)
2064     {
2065         goto Cleanup;
2066     }
2067 
2068     if (FsDeviceInfo.Characteristics != (FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA))
2069     {
2070         goto Cleanup;
2071     }
2072 
2073     /* Reference it */
2074     Status = ObReferenceObjectByHandle(Handle, 0, *IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL);
2075     if (!NT_SUCCESS(Status))
2076     {
2077         goto Cleanup;
2078     }
2079     FOReferenced = TRUE;
2080 
2081     /* Get file name */
2082     FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION));
2083     if (!FileNameInfo)
2084     {
2085         Status = STATUS_INSUFFICIENT_RESOURCES;
2086         goto Cleanup;
2087     }
2088 
2089     Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2090                                     sizeof(FILE_NAME_INFORMATION),
2091                                     FileNameInformation);
2092     if (Status == STATUS_BUFFER_OVERFLOW)
2093     {
2094         /* Now we have real length, use it */
2095         Length = FileNameInfo->FileNameLength;
2096         FreePool(FileNameInfo);
2097 
2098         FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + Length);
2099         if (!FileNameInfo)
2100         {
2101             Status = STATUS_INSUFFICIENT_RESOURCES;
2102             goto Cleanup;
2103         }
2104 
2105         /* Really query file name */
2106         Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2107                                         sizeof(FILE_NAME_INFORMATION) + Length,
2108                                         FileNameInformation);
2109     }
2110 
2111     if (!NT_SUCCESS(Status))
2112     {
2113         goto Cleanup;
2114     }
2115 
2116     /* Get symbolic name */
2117     ObjectNameInfoPtr = &ObjectNameInfo;
2118     SavedLength = sizeof(OBJECT_NAME_INFORMATION);
2119     Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, sizeof(OBJECT_NAME_INFORMATION), &Length);
2120     if (Status == STATUS_INFO_LENGTH_MISMATCH)
2121     {
2122         /* Once again, with proper size, it works better */
2123         ObjectNameInfoPtr = AllocatePool(Length);
2124         if (!ObjectNameInfoPtr)
2125         {
2126             Status = STATUS_INSUFFICIENT_RESOURCES;
2127             goto Cleanup;
2128         }
2129 
2130         SavedLength = Length;
2131         Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, SavedLength, &Length);
2132     }
2133 
2134     if (!NT_SUCCESS(Status))
2135     {
2136         goto Cleanup;
2137     }
2138 
2139     /* Now, query the device name */
2140     Status = QueryDeviceInformation(&ObjectNameInfoPtr->Name, SourceDeviceName,
2141                                     NULL, NULL, NULL, NULL, NULL, NULL);
2142     if (!NT_SUCCESS(Status))
2143     {
2144         goto Cleanup;
2145     }
2146 
2147     /* For target volume name, use input */
2148     TargetVolumeName->Length =
2149     TargetVolumeName->MaximumLength = VolumeMountPoint->TargetVolumeNameLength;
2150     TargetVolumeName->Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset);
2151 
2152     /* Query its device name */
2153     Status = QueryDeviceInformation(TargetVolumeName, &TargetDeviceName,
2154                                     NULL, NULL, NULL, NULL, NULL, NULL);
2155     if (!NT_SUCCESS(Status))
2156     {
2157         goto Cleanup;
2158     }
2159 
2160     /* Return symbolic name */
2161     SourceSymbolicName->Length =
2162     SourceSymbolicName->MaximumLength = (USHORT)FileNameInfo->FileNameLength;
2163     SourceSymbolicName->Buffer = (PWSTR)FileNameInfo;
2164     /* memmove allows memory overlap */
2165     RtlMoveMemory(SourceSymbolicName->Buffer, FileNameInfo->FileName, SourceSymbolicName->Length);
2166     FileNameInfo = NULL;
2167 
2168     /* Notify the change */
2169     MountMgrNotify(DeviceExtension);
2170     MountMgrNotifyNameChange(DeviceExtension, &TargetDeviceName, TRUE);
2171 
2172     /* If we are locked, sync databases if possible */
2173     if (NT_SUCCESS(LockStatus))
2174     {
2175         Status = FindDeviceInfo(DeviceExtension, SourceDeviceName, FALSE, &DeviceInformation);
2176         if (NT_SUCCESS(Status))
2177         {
2178             ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
2179         }
2180         else
2181         {
2182             Status = STATUS_PENDING;
2183         }
2184     }
2185 
2186 Cleanup:
2187     if (TargetDeviceName.Buffer)
2188     {
2189         FreePool(TargetDeviceName.Buffer);
2190     }
2191 
2192     if (ObjectNameInfoPtr && ObjectNameInfoPtr != &ObjectNameInfo)
2193     {
2194         FreePool(ObjectNameInfoPtr);
2195     }
2196 
2197     if (FileNameInfo)
2198     {
2199         FreePool(FileNameInfo);
2200     }
2201 
2202     if (FOReferenced)
2203     {
2204         ObDereferenceObject(FileObject);
2205     }
2206 
2207     return Status;
2208 }
2209 
2210 /*
2211  * @implemented
2212  */
2213 NTSTATUS
2214 MountMgrVolumeMountPointCreated(IN PDEVICE_EXTENSION DeviceExtension,
2215                                 IN PIRP Irp,
2216                                 IN NTSTATUS LockStatus)
2217 {
2218     LONG Offset;
2219     BOOLEAN Found;
2220     NTSTATUS Status;
2221     HANDLE RemoteDatabase;
2222     PMOUNTDEV_UNIQUE_ID UniqueId;
2223     PDATABASE_ENTRY DatabaseEntry;
2224     PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2225     PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2226     UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2227 
2228     /* Initialize string */
2229     LinkTarget.Length = 0;
2230     LinkTarget.MaximumLength = 0xC8;
2231     LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2232     if (LinkTarget.Buffer == NULL)
2233     {
2234         return STATUS_INSUFFICIENT_RESOURCES;
2235     }
2236 
2237     /* If the mount point was created, then, it changed!
2238      * Also use it to query some information
2239      */
2240     Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2241     /* Pending means DB are under synchronization, bail out */
2242     if (Status == STATUS_PENDING)
2243     {
2244         FreePool(LinkTarget.Buffer);
2245         FreePool(SourceDeviceName.Buffer);
2246         FreePool(SourceSymbolicName.Buffer);
2247         return STATUS_SUCCESS;
2248     }
2249     else if (!NT_SUCCESS(Status))
2250     {
2251         FreePool(LinkTarget.Buffer);
2252         return Status;
2253     }
2254 
2255     /* Query the device information */
2256     Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2257     if (!NT_SUCCESS(Status))
2258     {
2259         /* If it failed, first try to get volume name */
2260         Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2261         if (!NT_SUCCESS(Status))
2262         {
2263             /* Then, try to read the symlink */
2264             Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2265             if (!NT_SUCCESS(Status))
2266             {
2267                 FreePool(LinkTarget.Buffer);
2268                 FreePool(SourceDeviceName.Buffer);
2269                 FreePool(SourceSymbolicName.Buffer);
2270                 return Status;
2271             }
2272         }
2273         else
2274         {
2275             FreePool(VolumeName.Buffer);
2276         }
2277 
2278         FreePool(SourceDeviceName.Buffer);
2279 
2280         SourceDeviceName.Length = LinkTarget.Length;
2281         SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2282         SourceDeviceName.Buffer = LinkTarget.Buffer;
2283 
2284         /* Now that we have the correct source, reattempt to query information */
2285         Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2286         if (!NT_SUCCESS(Status))
2287         {
2288             FreePool(SourceDeviceName.Buffer);
2289             FreePool(SourceSymbolicName.Buffer);
2290             return Status;
2291         }
2292     }
2293 
2294     FreePool(SourceDeviceName.Buffer);
2295 
2296     /* Get information about target device */
2297     Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2298     if (!NT_SUCCESS(Status))
2299     {
2300         FreePool(SourceSymbolicName.Buffer);
2301         return Status;
2302     }
2303 
2304     /* Notify if not disabled */
2305     if (!TargetDeviceInformation->SkipNotifications)
2306     {
2307         PostOnlineNotification(DeviceExtension, &TargetDeviceInformation->SymbolicName);
2308     }
2309 
2310     /* Open the remote database */
2311     RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2312     if (RemoteDatabase == 0)
2313     {
2314         FreePool(SourceSymbolicName.Buffer);
2315         return STATUS_INSUFFICIENT_RESOURCES;
2316     }
2317 
2318     /* Browse all the entries */
2319     Offset = 0;
2320     Found = FALSE;
2321     for (;;)
2322     {
2323         DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2324         if (DatabaseEntry == NULL)
2325         {
2326             break;
2327         }
2328 
2329         /* Try to find ourselves */
2330         DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2331         DbName.Length = DbName.MaximumLength;
2332         DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2333         if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2334         {
2335             /* Reference ourselves and update the entry */
2336             ++DatabaseEntry->EntryReferences;
2337             Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2338             FreePool(DatabaseEntry);
2339             Found = TRUE;
2340             break;
2341         }
2342 
2343         Offset += DatabaseEntry->EntrySize;
2344         FreePool(DatabaseEntry);
2345     }
2346 
2347     /* We couldn't find ourselves, we'll have to add ourselves */
2348     if (!Found)
2349     {
2350         ULONG EntrySize;
2351         PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2352 
2353         /* Query the device unique ID */
2354         Status = QueryDeviceInformation(&TargetVolumeName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
2355         if (!NT_SUCCESS(Status))
2356         {
2357             FreePool(SourceSymbolicName.Buffer);
2358             CloseRemoteDatabase(RemoteDatabase);
2359             return Status;
2360         }
2361 
2362         /* Allocate a database entry */
2363         EntrySize = UniqueId->UniqueIdLength + TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2364         DatabaseEntry = AllocatePool(EntrySize);
2365         if (DatabaseEntry == NULL)
2366         {
2367             FreePool(UniqueId);
2368             FreePool(SourceSymbolicName.Buffer);
2369             CloseRemoteDatabase(RemoteDatabase);
2370             return STATUS_INSUFFICIENT_RESOURCES;
2371         }
2372 
2373         /* Fill it in */
2374         DatabaseEntry->EntrySize = EntrySize;
2375         DatabaseEntry->EntryReferences = 1;
2376         DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
2377         DatabaseEntry->SymbolicNameLength = TargetVolumeName.Length;
2378         DatabaseEntry->UniqueIdOffset = TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2379         DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
2380         RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + sizeof(DATABASE_ENTRY)), TargetVolumeName.Buffer, DatabaseEntry->SymbolicNameLength);
2381         RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
2382 
2383         /* And write it down */
2384         Status = AddRemoteDatabaseEntry(RemoteDatabase, DatabaseEntry);
2385         FreePool(DatabaseEntry);
2386         if (!NT_SUCCESS(Status))
2387         {
2388             FreePool(UniqueId);
2389             FreePool(SourceSymbolicName.Buffer);
2390             CloseRemoteDatabase(RemoteDatabase);
2391             return Status;
2392         }
2393 
2394         /* And now, allocate an Unique ID item */
2395         UniqueIdReplicate = AllocatePool(sizeof(UNIQUE_ID_REPLICATE));
2396         if (UniqueIdReplicate == NULL)
2397         {
2398             FreePool(UniqueId);
2399             FreePool(SourceSymbolicName.Buffer);
2400             CloseRemoteDatabase(RemoteDatabase);
2401             return Status;
2402         }
2403 
2404         /* To associate it with the device */
2405         UniqueIdReplicate->UniqueId = UniqueId;
2406         InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2407     }
2408 
2409     /* We're done with the remote database */
2410     CloseRemoteDatabase(RemoteDatabase);
2411 
2412     /* Check we were find writing the entry */
2413     if (!NT_SUCCESS(Status))
2414     {
2415         FreePool(SourceSymbolicName.Buffer);
2416         return Status;
2417     }
2418 
2419     /* This is the end, allocate an associated entry */
2420     AssociatedEntry = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY));
2421     if (AssociatedEntry == NULL)
2422     {
2423         FreePool(SourceSymbolicName.Buffer);
2424         return STATUS_INSUFFICIENT_RESOURCES;
2425     }
2426 
2427     /* Initialize its source name string */
2428     AssociatedEntry->String.Length = SourceSymbolicName.Length;
2429     AssociatedEntry->String.MaximumLength = AssociatedEntry->String.Length + sizeof(UNICODE_NULL);
2430     AssociatedEntry->String.Buffer = AllocatePool(AssociatedEntry->String.MaximumLength);
2431     if (AssociatedEntry->String.Buffer == NULL)
2432     {
2433         FreePool(AssociatedEntry);
2434         FreePool(SourceSymbolicName.Buffer);
2435         return STATUS_INSUFFICIENT_RESOURCES;
2436     }
2437 
2438     /* Copy data & insert in list */
2439     RtlCopyMemory(AssociatedEntry->String.Buffer, SourceSymbolicName.Buffer, SourceSymbolicName.Length);
2440     AssociatedEntry->String.Buffer[SourceSymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
2441     AssociatedEntry->DeviceInformation = DeviceInformation;
2442     InsertTailList(&TargetDeviceInformation->AssociatedDevicesHead, &AssociatedEntry->AssociatedDevicesEntry);
2443 
2444     /* We're done! */
2445     FreePool(SourceSymbolicName.Buffer);
2446     return STATUS_SUCCESS;
2447 }
2448 
2449 /*
2450  * @implemented
2451  */
2452 NTSTATUS
2453 MountMgrVolumeMountPointDeleted(IN PDEVICE_EXTENSION DeviceExtension,
2454                                 IN PIRP Irp,
2455                                 IN NTSTATUS LockStatus)
2456 {
2457     LONG Offset;
2458     NTSTATUS Status;
2459     PLIST_ENTRY Entry;
2460     HANDLE RemoteDatabase;
2461     PDATABASE_ENTRY DatabaseEntry;
2462     PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2463     PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2464     PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2465     UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2466 
2467     /* Initialize string */
2468     LinkTarget.Length = 0;
2469     LinkTarget.MaximumLength = 0xC8;
2470     LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2471     if (LinkTarget.Buffer == NULL)
2472     {
2473         return STATUS_INSUFFICIENT_RESOURCES;
2474     }
2475 
2476     /* If the mount point was deleted, then, it changed!
2477      * Also use it to query some information
2478      */
2479     Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2480     /* Pending means DB are under synchronization, bail out */
2481     if (Status == STATUS_PENDING)
2482     {
2483         FreePool(LinkTarget.Buffer);
2484         FreePool(SourceDeviceName.Buffer);
2485         FreePool(SourceSymbolicName.Buffer);
2486         return STATUS_SUCCESS;
2487     }
2488     else if (!NT_SUCCESS(Status))
2489     {
2490         FreePool(LinkTarget.Buffer);
2491         return Status;
2492     }
2493 
2494     /* Query the device information */
2495     Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2496     if (!NT_SUCCESS(Status))
2497     {
2498         /* If it failed, first try to get volume name */
2499         Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2500         if (!NT_SUCCESS(Status))
2501         {
2502             /* Then, try to read the symlink */
2503             Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2504             if (!NT_SUCCESS(Status))
2505             {
2506                 FreePool(LinkTarget.Buffer);
2507                 FreePool(SourceDeviceName.Buffer);
2508                 FreePool(SourceSymbolicName.Buffer);
2509                 return Status;
2510             }
2511         }
2512         else
2513         {
2514             FreePool(VolumeName.Buffer);
2515         }
2516 
2517         FreePool(SourceDeviceName.Buffer);
2518 
2519         SourceDeviceName.Length = LinkTarget.Length;
2520         SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2521         SourceDeviceName.Buffer = LinkTarget.Buffer;
2522 
2523         /* Now that we have the correct source, reattempt to query information */
2524         Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2525         if (!NT_SUCCESS(Status))
2526         {
2527             FreePool(SourceDeviceName.Buffer);
2528             FreePool(SourceSymbolicName.Buffer);
2529             return Status;
2530         }
2531     }
2532 
2533     FreePool(SourceDeviceName.Buffer);
2534 
2535     /* Get information about target device */
2536     Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2537     if (!NT_SUCCESS(Status))
2538     {
2539         FreePool(SourceSymbolicName.Buffer);
2540         return Status;
2541     }
2542 
2543     /* Open the remote database */
2544     RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2545     if (RemoteDatabase == 0)
2546     {
2547         FreePool(SourceSymbolicName.Buffer);
2548         return STATUS_INSUFFICIENT_RESOURCES;
2549     }
2550 
2551     /* Browse all the entries */
2552     Offset = 0;
2553     for (;;)
2554     {
2555         DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2556         if (DatabaseEntry == NULL)
2557         {
2558             /* We didn't find ourselves, that's infortunate! */
2559             FreePool(SourceSymbolicName.Buffer);
2560             CloseRemoteDatabase(RemoteDatabase);
2561             return STATUS_INVALID_PARAMETER;
2562         }
2563 
2564         /* Try to find ourselves */
2565         DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2566         DbName.Length = DbName.MaximumLength;
2567         DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2568         if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2569         {
2570             break;
2571         }
2572 
2573         Offset += DatabaseEntry->EntrySize;
2574         FreePool(DatabaseEntry);
2575     }
2576 
2577     /* Dereference ourselves */
2578     DatabaseEntry->EntryReferences--;
2579     if (DatabaseEntry->EntryReferences == 0)
2580     {
2581         /* If we're still referenced, just update the entry */
2582         Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2583     }
2584     else
2585     {
2586         /* Otherwise, delete the entry */
2587         Status = DeleteRemoteDatabaseEntry(RemoteDatabase, Offset);
2588         if (!NT_SUCCESS(Status))
2589         {
2590             FreePool(DatabaseEntry);
2591             FreePool(SourceSymbolicName.Buffer);
2592             CloseRemoteDatabase(RemoteDatabase);
2593             return Status;
2594         }
2595 
2596         /* Also, delete our unique ID replicated record */
2597         for (Entry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink;
2598              Entry != &DeviceInformation->ReplicatedUniqueIdsListHead;
2599              Entry = Entry->Flink)
2600         {
2601             UniqueIdReplicate = CONTAINING_RECORD(Entry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
2602 
2603             if (UniqueIdReplicate->UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
2604                 RtlCompareMemory(UniqueIdReplicate->UniqueId->UniqueId,
2605                                  (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
2606                                  DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
2607             {
2608                 break;
2609             }
2610         }
2611 
2612         /* It has to exist! */
2613         if (Entry == &DeviceInformation->ReplicatedUniqueIdsListHead)
2614         {
2615             FreePool(DatabaseEntry);
2616             FreePool(SourceSymbolicName.Buffer);
2617             CloseRemoteDatabase(RemoteDatabase);
2618             return STATUS_UNSUCCESSFUL;
2619         }
2620 
2621         /* Remove it and free it */
2622         RemoveEntryList(&UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2623         FreePool(UniqueIdReplicate->UniqueId);
2624         FreePool(UniqueIdReplicate);
2625     }
2626 
2627     /* We're done with the remote database */
2628     FreePool(DatabaseEntry);
2629     CloseRemoteDatabase(RemoteDatabase);
2630 
2631     /* Check write operation succeed */
2632     if (!NT_SUCCESS(Status))
2633     {
2634         FreePool(SourceSymbolicName.Buffer);
2635         return Status;
2636     }
2637 
2638     /* Try to find our associated device entry */
2639     for (Entry = TargetDeviceInformation->AssociatedDevicesHead.Flink;
2640          Entry != &TargetDeviceInformation->AssociatedDevicesHead;
2641          Entry = Entry->Flink)
2642     {
2643         AssociatedEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
2644 
2645         /* If found, delete it */
2646         if (AssociatedEntry->DeviceInformation == DeviceInformation &&
2647             RtlEqualUnicodeString(&AssociatedEntry->String, &SourceSymbolicName, TRUE))
2648         {
2649             RemoveEntryList(&AssociatedEntry->AssociatedDevicesEntry);
2650             FreePool(AssociatedEntry->String.Buffer);
2651             FreePool(AssociatedEntry);
2652             break;
2653         }
2654     }
2655 
2656     /* We're done! */
2657     FreePool(SourceSymbolicName.Buffer);
2658     return STATUS_SUCCESS;
2659 }
2660 
2661 /*
2662  * @implemented
2663  */
2664 NTSTATUS
2665 NTAPI
2666 MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject,
2667                       IN PIRP Irp)
2668 {
2669     PIO_STACK_LOCATION Stack;
2670     NTSTATUS Status, LockStatus;
2671     PDEVICE_EXTENSION DeviceExtension;
2672 
2673     Stack = IoGetCurrentIrpStackLocation(Irp);
2674     DeviceExtension = DeviceObject->DeviceExtension;
2675 
2676     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2677 
2678     switch (Stack->Parameters.DeviceIoControl.IoControlCode)
2679     {
2680         case IOCTL_MOUNTMGR_CREATE_POINT:
2681             Status = MountMgrCreatePoint(DeviceExtension, Irp);
2682             break;
2683 
2684         case IOCTL_MOUNTMGR_DELETE_POINTS:
2685             Status = MountMgrDeletePoints(DeviceExtension, Irp);
2686             break;
2687 
2688         case IOCTL_MOUNTMGR_QUERY_POINTS:
2689             Status = MountMgrQueryPoints(DeviceExtension, Irp);
2690             break;
2691 
2692         case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY:
2693             Status = MountMgrDeletePointsDbOnly(DeviceExtension, Irp);
2694             break;
2695 
2696         case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER:
2697             Status = MountMgrNextDriveLetter(DeviceExtension, Irp);
2698             break;
2699 
2700         case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS:
2701         // NOTE: On Win7+, this is handled during driver re-initialization.
2702             DeviceExtension->AutomaticDriveLetter = TRUE;
2703             MountMgrAssignDriveLetters(DeviceExtension);
2704             ReconcileAllDatabasesWithMaster(DeviceExtension);
2705             WaitForOnlinesToComplete(DeviceExtension);
2706             Status = STATUS_SUCCESS;
2707             break;
2708 
2709         case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED:
2710             KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2711 
2712             LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2713             KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2714             Status = MountMgrVolumeMountPointCreated(DeviceExtension, Irp, LockStatus);
2715             if (NT_SUCCESS(LockStatus))
2716             {
2717                 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2718             }
2719 
2720             break;
2721 
2722         case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:
2723             KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2724 
2725             LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2726             KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2727             Status = MountMgrVolumeMountPointDeleted(DeviceExtension, Irp, LockStatus);
2728             if (NT_SUCCESS(LockStatus))
2729             {
2730                 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2731             }
2732 
2733             break;
2734 
2735         case IOCTL_MOUNTMGR_CHANGE_NOTIFY:
2736             Status = MountMgrChangeNotify(DeviceExtension, Irp);
2737             break;
2738 
2739         case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE:
2740             Status = MountMgrKeepLinksWhenOffline(DeviceExtension, Irp);
2741             break;
2742 
2743         case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES:
2744             Status = MountMgrCheckUnprocessedVolumes(DeviceExtension, Irp);
2745             goto Complete;
2746 
2747         case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION:
2748             KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2749             Status = MountMgrVolumeArrivalNotification(DeviceExtension, Irp);
2750             goto Complete;
2751 
2752         case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH:
2753             Status = MountMgrQueryDosVolumePath(DeviceExtension, Irp);
2754             break;
2755 
2756         case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS:
2757             Status = MountMgrQueryDosVolumePaths(DeviceExtension, Irp);
2758             break;
2759 
2760         case IOCTL_MOUNTMGR_SCRUB_REGISTRY:
2761             Status = MountMgrScrubRegistry(DeviceExtension);
2762             break;
2763 
2764         case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT:
2765             Status = MountMgrQueryAutoMount(DeviceExtension, Irp);
2766             break;
2767 
2768         case IOCTL_MOUNTMGR_SET_AUTO_MOUNT:
2769             Status = MountMgrSetAutoMount(DeviceExtension, Irp);
2770             break;
2771 
2772         default:
2773             Status = STATUS_INVALID_DEVICE_REQUEST;
2774     }
2775 
2776     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2777 
2778     if (Status != STATUS_PENDING)
2779     {
2780         goto Complete;
2781     }
2782 
2783     return Status;
2784 
2785 Complete:
2786     Irp->IoStatus.Status = Status;
2787     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2788 
2789     return Status;
2790 }
2791