xref: /reactos/drivers/storage/mountmgr/device.c (revision a17d32a3)
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 (FIELD_OFFSET(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName) + DriveLetterTarget->DeviceNameLength >
641         Stack->Parameters.DeviceIoControl.InputBufferLength)
642     {
643         return STATUS_INVALID_PARAMETER;
644     }
645 
646     /* Call the worker */
647     DeviceName.Buffer = DriveLetterTarget->DeviceName;
648     DeviceName.Length =
649     DeviceName.MaximumLength = DriveLetterTarget->DeviceNameLength;
650 
651     Status = MountMgrNextDriveLetterWorker(DeviceExtension, &DeviceName,
652                                            &DriveLetterInformation);
653     if (NT_SUCCESS(Status))
654     {
655         *(PMOUNTMGR_DRIVE_LETTER_INFORMATION)Irp->AssociatedIrp.SystemBuffer =
656             DriveLetterInformation;
657         Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
658     }
659 
660     return Status;
661 }
662 
663 /*
664  * @implemented
665  */
666 NTSTATUS
667 NTAPI
668 MountMgrQuerySystemVolumeNameQueryRoutine(IN PWSTR ValueName,
669                                           IN ULONG ValueType,
670                                           IN PVOID ValueData,
671                                           IN ULONG ValueLength,
672                                           IN PVOID Context,
673                                           IN PVOID EntryContext)
674 {
675     UNICODE_STRING ValueString;
676     PUNICODE_STRING SystemVolumeName;
677 
678     UNREFERENCED_PARAMETER(ValueName);
679     UNREFERENCED_PARAMETER(ValueLength);
680     UNREFERENCED_PARAMETER(EntryContext);
681 
682     if (ValueType != REG_SZ)
683     {
684         return STATUS_SUCCESS;
685     }
686 
687     RtlInitUnicodeString(&ValueString, ValueData);
688     SystemVolumeName = Context;
689 
690     /* Return a string containing system volume name */
691     SystemVolumeName->Length = ValueString.Length;
692     SystemVolumeName->MaximumLength = ValueString.Length + sizeof(WCHAR);
693     SystemVolumeName->Buffer = AllocatePool(SystemVolumeName->MaximumLength);
694     if (SystemVolumeName->Buffer)
695     {
696         RtlCopyMemory(SystemVolumeName->Buffer, ValueData, ValueString.Length);
697         SystemVolumeName->Buffer[ValueString.Length / sizeof(WCHAR)] = UNICODE_NULL;
698     }
699 
700     return STATUS_SUCCESS;
701 
702 }
703 
704 /*
705  * @implemented
706  */
707 NTSTATUS
708 MountMgrQuerySystemVolumeName(OUT PUNICODE_STRING SystemVolumeName)
709 {
710     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
711 
712     RtlZeroMemory(QueryTable, sizeof(QueryTable));
713     QueryTable[0].QueryRoutine = MountMgrQuerySystemVolumeNameQueryRoutine;
714     QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
715     QueryTable[0].Name = L"SystemPartition";
716 
717     SystemVolumeName->Buffer = NULL;
718 
719     RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
720                            L"\\Registry\\Machine\\System\\Setup",
721                            QueryTable,
722                            SystemVolumeName,
723                            NULL);
724 
725     if (SystemVolumeName->Buffer)
726     {
727         return STATUS_SUCCESS;
728     }
729 
730     return STATUS_UNSUCCESSFUL;
731 }
732 
733 /*
734  * @implemented
735  */
736 VOID
737 MountMgrAssignDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
738 {
739     NTSTATUS Status;
740     PLIST_ENTRY NextEntry;
741     UNICODE_STRING SystemVolumeName;
742     PDEVICE_INFORMATION DeviceInformation;
743     MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
744 
745     /* First, get system volume name */
746     Status = MountMgrQuerySystemVolumeName(&SystemVolumeName);
747 
748     /* If there are no device, it's all done */
749     if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
750     {
751         if (NT_SUCCESS(Status))
752         {
753             FreePool(SystemVolumeName.Buffer);
754         }
755 
756         return;
757     }
758 
759     /* Now, for all the devices... */
760     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
761          NextEntry != &(DeviceExtension->DeviceListHead);
762          NextEntry = NextEntry->Flink)
763     {
764         DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
765 
766         /* If the device doesn't have a letter assigned, do it! */
767         if (!DeviceInformation->LetterAssigned)
768         {
769             MountMgrNextDriveLetterWorker(DeviceExtension,
770                                           &(DeviceInformation->DeviceName),
771                                           &DriveLetterInformation);
772         }
773 
774         /* If it's the system volume */
775         if (NT_SUCCESS(Status) && RtlEqualUnicodeString(&SystemVolumeName, &(DeviceInformation->DeviceName), TRUE))
776         {
777             /* Keep track of it */
778             DeviceExtension->DriveLetterData = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength +
779                                                             sizeof(MOUNTDEV_UNIQUE_ID));
780             if (DeviceExtension->DriveLetterData)
781             {
782                 RtlCopyMemory(DeviceExtension->DriveLetterData,
783                               DeviceInformation->UniqueId,
784                               DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
785             }
786 
787             /* Ensure it gets mounted if automount is disabled */
788             if (DeviceExtension->NoAutoMount)
789             {
790                 /* Temporarily re-enable automount for the
791                  * worker to mount and set a drive letter */
792                 DeviceExtension->NoAutoMount = FALSE;
793 
794                 MountMgrNextDriveLetterWorker(DeviceExtension,
795                                               &(DeviceInformation->DeviceName),
796                                               &DriveLetterInformation);
797 
798                 /* And re-disable automount */
799                 DeviceExtension->NoAutoMount = TRUE;
800             }
801         }
802     }
803 
804     if (NT_SUCCESS(Status))
805     {
806         FreePool(SystemVolumeName.Buffer);
807     }
808 }
809 
810 /*
811  * @implemented
812  */
813 NTSTATUS
814 MountMgrQueryDosVolumePath(IN PDEVICE_EXTENSION DeviceExtension,
815                            IN PIRP Irp)
816 {
817     NTSTATUS Status;
818     ULONG DevicesFound;
819     PIO_STACK_LOCATION Stack;
820     PLIST_ENTRY SymlinksEntry;
821     UNICODE_STRING SymbolicName;
822     PMOUNTMGR_TARGET_NAME Target;
823     PMOUNTMGR_VOLUME_PATHS Output;
824     PWSTR DeviceString, OldBuffer;
825     USHORT DeviceLength, OldLength;
826     PDEVICE_INFORMATION DeviceInformation;
827     PSYMLINK_INFORMATION SymlinkInformation;
828     PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
829 
830     Stack = IoGetCurrentIrpStackLocation(Irp);
831 
832     /* Validate input size */
833     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
834     {
835         return STATUS_INVALID_PARAMETER;
836     }
837 
838     /* Ensure we have received UNICODE_STRING */
839     Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
840     if (Target->DeviceNameLength & 1)
841     {
842         return STATUS_INVALID_PARAMETER;
843     }
844 
845     /* Validate the entry structure size */
846     if (FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + Target->DeviceNameLength >
847         Stack->Parameters.DeviceIoControl.InputBufferLength)
848     {
849         return STATUS_INVALID_PARAMETER;
850     }
851 
852     /* Ensure we can at least return needed size */
853     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz))
854     {
855         return STATUS_INVALID_PARAMETER;
856     }
857 
858     /* Construct string for query */
859     SymbolicName.Length =
860     SymbolicName.MaximumLength = Target->DeviceNameLength;
861     SymbolicName.Buffer = Target->DeviceName;
862 
863     /* Find device with our info */
864     Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
865     if (!NT_SUCCESS(Status))
866     {
867         return Status;
868     }
869 
870     DeviceLength = 0;
871     DeviceString = NULL;
872     DevicesFound = 0;
873 
874     /* Try to find associated device info */
875     while (TRUE)
876     {
877         for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
878              SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
879              SymlinksEntry = SymlinksEntry->Flink)
880         {
881             SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
882 
883             /* Try to find with drive letter */
884             if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
885             {
886                 break;
887             }
888         }
889 
890         /* If we've found a device via drive letter, do default processing */
891         if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead))
892             break;
893 
894         /* If it doesn't have an associated device, go to fallback method */
895         if (IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
896             goto TryWithVolumeName;
897 
898         /* Create a string with the information about the device */
899         AssociatedDevice = CONTAINING_RECORD(&(DeviceInformation->AssociatedDevicesHead), ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
900         OldLength = DeviceLength;
901         OldBuffer = DeviceString;
902         DeviceLength += AssociatedDevice->String.Length;
903         DeviceString = AllocatePool(DeviceLength);
904         if (!DeviceString)
905         {
906             if (OldBuffer)
907             {
908                 FreePool(OldBuffer);
909             }
910 
911             return STATUS_INSUFFICIENT_RESOURCES;
912         }
913 
914         /* Store our info and previous if any */
915         RtlCopyMemory(DeviceString, AssociatedDevice->String.Buffer, AssociatedDevice->String.Length);
916         if (OldBuffer)
917         {
918             RtlCopyMemory(&DeviceString[AssociatedDevice->String.Length / sizeof(WCHAR)], OldBuffer, OldLength);
919             FreePool(OldBuffer);
920         }
921 
922         /* Count and continue looking */
923         ++DevicesFound;
924         DeviceInformation = AssociatedDevice->DeviceInformation;
925 
926         /* If too many devices, try another way */
927         if (DevicesFound > MAX_DEVICES) /*  1000 */
928         {
929             goto TryWithVolumeName;
930         }
931     }
932 
933     /* Reallocate our string, so that we can prepend disk letter */
934     OldBuffer = DeviceString;
935     OldLength = DeviceLength;
936     DeviceLength += 2 * sizeof(WCHAR);
937     DeviceString = AllocatePool(DeviceLength);
938     if (!DeviceString)
939     {
940         if (OldBuffer)
941         {
942             FreePool(OldBuffer);
943         }
944 
945         return STATUS_INSUFFICIENT_RESOURCES;
946     }
947 
948     /* Get the letter */
949     DeviceString[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
950     DeviceString[1] = L':';
951 
952     /* And copy the rest */
953     if (OldBuffer)
954     {
955         RtlCopyMemory(&DeviceString[2], OldBuffer, OldLength);
956         FreePool(OldBuffer);
957     }
958 
959 TryWithVolumeName:
960     /* If we didn't find anything, try differently */
961     if (DeviceLength < 2 * sizeof(WCHAR) || DeviceString[1] != L':')
962     {
963         if (DeviceString)
964         {
965             FreePool(DeviceString);
966             DeviceString = NULL;
967             DeviceLength = 0;
968         }
969 
970         /* Try to find a volume name matching */
971         for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
972              SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
973              SymlinksEntry = SymlinksEntry->Flink)
974         {
975             SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
976 
977             if (MOUNTMGR_IS_VOLUME_NAME(&SymlinkInformation->Name))
978             {
979                 break;
980             }
981         }
982 
983         /* If found copy */
984         if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead))
985         {
986             DeviceLength = SymlinkInformation->Name.Length;
987             DeviceString = AllocatePool(DeviceLength);
988             if (!DeviceString)
989             {
990                 return STATUS_INSUFFICIENT_RESOURCES;
991             }
992 
993             RtlCopyMemory(DeviceString, SymlinkInformation->Name.Buffer, DeviceLength);
994             /* Ensure we are in the Win32 namespace; [1] can be '?' */
995             DeviceString[1] = L'\\';
996         }
997     }
998 
999     /* If we didn't find something, fail */
1000     if (!DeviceString)
1001         return STATUS_NOT_FOUND;
1002 
1003     /* Get the output buffer */
1004     Output = (PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer;
1005 
1006     /* At least, we will return our length */
1007     Output->MultiSzLength = DeviceLength + 2 * sizeof(UNICODE_NULL);
1008     Irp->IoStatus.Information = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) + Output->MultiSzLength;
1009 
1010     /* If we have enough room for copying the string */
1011     if (Irp->IoStatus.Information <= Stack->Parameters.DeviceIoControl.OutputBufferLength)
1012     {
1013         /* Copy it */
1014         if (DeviceLength)
1015         {
1016             RtlCopyMemory(Output->MultiSz, DeviceString, DeviceLength);
1017         }
1018 
1019         /* And double-NUL at its end - this is needed in case of
1020          * multiple paths which are separated by a single NUL */
1021         FreePool(DeviceString);
1022         Output->MultiSz[DeviceLength / sizeof(WCHAR)] = UNICODE_NULL;
1023         Output->MultiSz[DeviceLength / sizeof(WCHAR) + 1] = UNICODE_NULL;
1024 
1025         return STATUS_SUCCESS;
1026     }
1027     else
1028     {
1029         /* Just return the size needed and leave */
1030         FreePool(DeviceString);
1031         Irp->IoStatus.Information = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz);
1032         return STATUS_BUFFER_OVERFLOW;
1033     }
1034 }
1035 
1036 /*
1037  * @implemented
1038  */
1039 NTSTATUS
1040 MountMgrValidateBackPointer(IN PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry,
1041                             IN PDEVICE_INFORMATION DeviceInformation,
1042                             OUT PBOOLEAN Invalid)
1043 {
1044     HANDLE Handle;
1045     NTSTATUS Status;
1046     PLIST_ENTRY SymlinksEntry;
1047     IO_STATUS_BLOCK IoStatusBlock;
1048     PREPARSE_DATA_BUFFER ReparseData;
1049     OBJECT_ATTRIBUTES ObjectAttributes;
1050     UNICODE_STRING FullName, SubstituteName;
1051     PSYMLINK_INFORMATION SymlinkInformation;
1052 
1053     /* Initialize & allocate a string big enough to contain our complete mount point name */
1054     FullName.Length = 0;
1055     FullName.MaximumLength = AssociatedDeviceEntry->String.Length
1056                              + AssociatedDeviceEntry->DeviceInformation->DeviceName.Length
1057                              + sizeof(WCHAR)
1058                              + sizeof(UNICODE_NULL);
1059     FullName.Buffer = AllocatePool(FullName.MaximumLength);
1060     if (!FullName.Buffer)
1061     {
1062         return STATUS_INSUFFICIENT_RESOURCES;
1063     }
1064 
1065     /* Create the path */
1066     RtlAppendUnicodeStringToString(&FullName, &AssociatedDeviceEntry->DeviceInformation->DeviceName);
1067     FullName.Buffer[FullName.Length / sizeof(WCHAR)] = L'\\';
1068     RtlAppendUnicodeStringToString(&FullName, &AssociatedDeviceEntry->String);
1069     FullName.Buffer[FullName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1070 
1071     /* Open it to query the reparse point */
1072     InitializeObjectAttributes(&ObjectAttributes,
1073                                &FullName,
1074                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1075                                NULL,
1076                                NULL);
1077     Status = ZwOpenFile(&Handle,
1078                         SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1079                         &ObjectAttributes, &IoStatusBlock,
1080                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1081                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
1082     FreePool(FullName.Buffer);
1083 
1084     if (!NT_SUCCESS(Status))
1085     {
1086         *Invalid = TRUE;
1087         return STATUS_SUCCESS;
1088     }
1089 
1090     /* Allocate a buffer big enough to read reparse data */
1091     ReparseData = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1092     if (ReparseData == NULL)
1093     {
1094         ZwClose(Handle);
1095         return STATUS_INSUFFICIENT_RESOURCES;
1096     }
1097 
1098     /* Query reparse data */
1099     Status = ZwFsControlFile(Handle,
1100                              NULL, NULL, NULL,
1101                              &IoStatusBlock,
1102                              FSCTL_GET_REPARSE_POINT,
1103                              NULL, 0,
1104                              ReparseData, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1105     ZwClose(Handle);
1106 
1107     if (!NT_SUCCESS(Status))
1108     {
1109         FreePool(ReparseData);
1110         *Invalid = TRUE;
1111         return STATUS_SUCCESS;
1112     }
1113 
1114     /* Create a string with the substitute name */
1115     SubstituteName.Length = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
1116     SubstituteName.MaximumLength = SubstituteName.Length;
1117     SubstituteName.Buffer = (PWSTR)((ULONG_PTR)ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset);
1118 
1119     /* If that's a volume name that matches our associated device, that's a success! */
1120     if (MOUNTMGR_IS_VOLUME_NAME(&SubstituteName))
1121     {
1122         if (SubstituteName.Length == 98 && SubstituteName.Buffer[1] == L'?')
1123         {
1124             for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
1125                  SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
1126                  SymlinksEntry = SymlinksEntry->Flink)
1127             {
1128                 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1129 
1130                 if (RtlEqualUnicodeString(&SubstituteName, &SymlinkInformation->Name, TRUE))
1131                 {
1132                     FreePool(ReparseData);
1133                     return STATUS_SUCCESS;
1134                 }
1135             }
1136         }
1137     }
1138 
1139     FreePool(ReparseData);
1140     *Invalid = TRUE;
1141     return STATUS_SUCCESS;
1142 }
1143 
1144 /*
1145  * @implemented
1146  */
1147 NTSTATUS
1148 MountMgrQueryVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1149                          IN PDEVICE_INFORMATION DeviceInformation,
1150                          IN PLIST_ENTRY DeviceInfoList,
1151                          OUT PMOUNTMGR_VOLUME_PATHS * VolumePaths,
1152                          OUT PDEVICE_INFORMATION *FailedDevice)
1153 {
1154     ULONG Written;
1155     NTSTATUS Status;
1156     PLIST_ENTRY Entry;
1157     PSYMLINK_INFORMATION SymlinkInformation;
1158     PDEVICE_INFORMATION_ENTRY DeviceInfoEntry;
1159     PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry;
1160     PMOUNTMGR_VOLUME_PATHS * Paths = NULL, * CurrentPath;
1161     ULONG OutputPathLength, NumberOfPaths, ReturnedPaths;
1162 
1163     /* We return at least null char */
1164     OutputPathLength = sizeof(UNICODE_NULL);
1165 
1166     for (Entry = DeviceInformation->SymbolicLinksListHead.Flink;
1167          Entry != &(DeviceInformation->SymbolicLinksListHead);
1168          Entry = Entry->Flink)
1169     {
1170         SymlinkInformation = CONTAINING_RECORD(Entry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1171 
1172         /* Try to find the drive letter (ie, DOS device) */
1173         if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
1174         {
1175             /* We'll return the letter */
1176             OutputPathLength = 4 * sizeof(WCHAR);
1177             break;
1178         }
1179     }
1180 
1181     /* We didn't find any */
1182     if (Entry == &(DeviceInformation->SymbolicLinksListHead))
1183     {
1184         SymlinkInformation = NULL;
1185     }
1186 
1187     /* Do we have any device info to return? */
1188     for (Entry = DeviceInfoList->Flink; Entry != DeviceInfoList; Entry = Entry->Flink)
1189     {
1190         DeviceInfoEntry = CONTAINING_RECORD(Entry, DEVICE_INFORMATION_ENTRY, DeviceInformationEntry);
1191 
1192         /* Matching current device */
1193         if (DeviceInfoEntry->DeviceInformation == DeviceInformation)
1194         {
1195             /* Allocate the output buffer */
1196             *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1197             if (*VolumePaths == NULL)
1198             {
1199                 return STATUS_INSUFFICIENT_RESOURCES;
1200             }
1201 
1202             /* Set size */
1203             (*VolumePaths)->MultiSzLength = OutputPathLength;
1204             /* If we have a drive letter, return it */
1205             if (SymlinkInformation != NULL)
1206             {
1207                 (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1208                 (*VolumePaths)->MultiSz[1] = L':';
1209                 (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1210                 (*VolumePaths)->MultiSz[3] = UNICODE_NULL;
1211             }
1212             else
1213             {
1214                 (*VolumePaths)->MultiSz[0] = UNICODE_NULL;
1215             }
1216 
1217             return STATUS_SUCCESS;
1218         }
1219     }
1220 
1221     /* Allocate a new device entry */
1222     DeviceInfoEntry = AllocatePool(sizeof(DEVICE_INFORMATION_ENTRY));
1223     if (DeviceInfoEntry == NULL)
1224     {
1225         return STATUS_INSUFFICIENT_RESOURCES;
1226     }
1227 
1228     /* Add it to the list */
1229     DeviceInfoEntry->DeviceInformation = DeviceInformation;
1230     InsertTailList(DeviceInfoList, &DeviceInfoEntry->DeviceInformationEntry);
1231 
1232     NumberOfPaths = 0;
1233     /* Count the amount of devices we will have to handle */
1234     if (!IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
1235     {
1236         for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1237                      Entry != &DeviceInformation->AssociatedDevicesHead;
1238                      Entry = Entry->Flink)
1239         {
1240             ++NumberOfPaths;
1241         }
1242 
1243         ASSERT(NumberOfPaths != 0);
1244         /* And allocate a big enough buffer */
1245         Paths = AllocatePool(NumberOfPaths * sizeof(PMOUNTMGR_VOLUME_PATHS));
1246         if (Paths == NULL)
1247         {
1248             RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1249             FreePool(DeviceInfoEntry);
1250             return STATUS_INSUFFICIENT_RESOURCES;
1251         }
1252     }
1253 
1254     /* Start the hot loop to gather all the paths and be able to compute total output length! */
1255     ReturnedPaths = 0;
1256     CurrentPath = Paths;
1257     for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1258          Entry != &DeviceInformation->AssociatedDevicesHead;
1259          Entry = Entry->Flink)
1260     {
1261         USHORT InnerStrings;
1262         BOOLEAN Invalid = FALSE;
1263 
1264         AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1265 
1266         /* Validate the fact its a mount point by query reparse data */
1267         Status = MountMgrValidateBackPointer(AssociatedDeviceEntry, DeviceInformation, &Invalid);
1268 
1269         /* If we found an invalid device, that's a failure */
1270         if (Invalid)
1271         {
1272             *FailedDevice = AssociatedDeviceEntry->DeviceInformation;
1273             Status = STATUS_UNSUCCESSFUL;
1274         }
1275 
1276         /* Check whether we failed, if so, bail out */
1277         if (!NT_SUCCESS(Status))
1278         {
1279             ULONG i;
1280 
1281             for (i = 0; i < ReturnedPaths; ++i)
1282             {
1283                 FreePool(Paths[i]);
1284             }
1285 
1286             if (Paths != NULL)
1287             {
1288                 FreePool(Paths);
1289             }
1290             RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1291             FreePool(DeviceInfoEntry);
1292             return Status;
1293         }
1294 
1295         /* Query associated paths (hello ourselves :-)) */
1296         Status = MountMgrQueryVolumePaths(DeviceExtension,
1297                                           AssociatedDeviceEntry->DeviceInformation,
1298                                           DeviceInfoList,
1299                                           CurrentPath,
1300                                           FailedDevice);
1301         if (!NT_SUCCESS(Status))
1302         {
1303             ULONG i;
1304 
1305             for (i = 0; i < ReturnedPaths; ++i)
1306             {
1307                 FreePool(Paths[i]);
1308             }
1309 
1310             if (Paths != NULL)
1311             {
1312                 FreePool(Paths);
1313             }
1314             RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1315             FreePool(DeviceInfoEntry);
1316             return Status;
1317         }
1318 
1319         /* Count the number of strings we have in the multi string buffer */
1320         InnerStrings = 0;
1321         if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1322         {
1323             ULONG i;
1324             PWSTR MultiSz = (*CurrentPath)->MultiSz;
1325 
1326             for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR); ++i, ++MultiSz)
1327             {
1328                 if (*MultiSz == UNICODE_NULL)
1329                 {
1330                     ++InnerStrings;
1331                 }
1332             }
1333         }
1334 
1335         /* We returned one more path (ie, one more allocated buffer) */
1336         ++ReturnedPaths;
1337         /* Move the next pointer to use in the array */
1338         ++CurrentPath;
1339         /* Multiply String.Length by the number of found paths, we always add it after a path */
1340         OutputPathLength += (*CurrentPath)->MultiSzLength + InnerStrings * AssociatedDeviceEntry->String.Length - sizeof(UNICODE_NULL);
1341     }
1342 
1343     /* Allocate the output buffer */
1344     *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1345     if (*VolumePaths == NULL)
1346     {
1347         ULONG i;
1348 
1349         for (i = 0; i < ReturnedPaths; ++i)
1350         {
1351             FreePool(Paths[i]);
1352         }
1353 
1354         if (Paths != NULL)
1355         {
1356             FreePool(Paths);
1357         }
1358         RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1359         FreePool(DeviceInfoEntry);
1360         return STATUS_INSUFFICIENT_RESOURCES;
1361     }
1362 
1363     Written = 0;
1364     /* If we had found a DOS letter, that's the first thing we return */
1365     (*VolumePaths)->MultiSzLength = OutputPathLength;
1366     if (SymlinkInformation != NULL)
1367     {
1368         (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1369         (*VolumePaths)->MultiSz[1] = L':';
1370         (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1371         Written = 3;
1372     }
1373 
1374     /* Now, browse again all our paths to return them */
1375     CurrentPath = Paths;
1376     for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1377          Entry != &DeviceInformation->AssociatedDevicesHead;
1378          Entry = Entry->Flink)
1379     {
1380         AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1381 
1382         /* If we had a path... */
1383         if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1384         {
1385             ULONG i, Offset;
1386             PWSTR MultiSz;
1387 
1388             /* This offset is used to "jump" into MultiSz, so, start with the string begin (ie, skip MultiSzLength) */
1389             Offset = sizeof(ULONG);
1390             /* Browse every single letter, and skip last UNICODE_NULL */
1391             for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR) - 1; ++i)
1392             {
1393                 /* Get the letter */
1394                 MultiSz = (PWSTR)((ULONG_PTR)(*CurrentPath) + Offset);
1395                 /* If it was part of the path, just return it */
1396                 if (*MultiSz != UNICODE_NULL)
1397                 {
1398                     (*VolumePaths)->MultiSz[Written] = *MultiSz;
1399                 }
1400                 else
1401                 {
1402                     /* Otherwise, as planed, return our whole associated device name */
1403                     RtlCopyMemory(&(*VolumePaths)->MultiSz[Written],
1404                                   AssociatedDeviceEntry->String.Buffer,
1405                                   AssociatedDeviceEntry->String.Length);
1406                     Written += AssociatedDeviceEntry->String.Length / sizeof(WCHAR);
1407                     /* And don't forget to nullify */
1408                     (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1409                 }
1410 
1411                 /* We at least return a letter or a null char */
1412                 ++Written;
1413                 /* Move to the next letter */
1414                 Offset += sizeof(WCHAR);
1415             }
1416         }
1417 
1418         FreePool(*CurrentPath);
1419         ++CurrentPath;
1420     }
1421 
1422     /* MultiSz: don't forget last null char */
1423     (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1424     /* Cleanup everything and return success! */
1425     if (Paths != NULL)
1426     {
1427         FreePool(Paths);
1428     }
1429     RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1430     FreePool(DeviceInfoEntry);
1431     return STATUS_SUCCESS;
1432 }
1433 
1434 /*
1435  * @implemented
1436  */
1437 NTSTATUS
1438 MountMgrQueryDosVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1439                             IN PIRP Irp)
1440 {
1441     NTSTATUS Status;
1442     PLIST_ENTRY Entry;
1443     LIST_ENTRY Devices;
1444     BOOLEAN NeedNotification;
1445     PIO_STACK_LOCATION Stack;
1446     UNICODE_STRING SymbolicName;
1447     ULONG Attempts, OutputLength;
1448     PMOUNTMGR_TARGET_NAME Target;
1449     PMOUNTMGR_VOLUME_PATHS Paths, Output;
1450     RECONCILE_WORK_ITEM_CONTEXT ReconcileContext;
1451     PDEVICE_INFORMATION DeviceInformation, ListDeviceInfo, FailedDevice;
1452 
1453     Stack = IoGetCurrentIrpStackLocation(Irp);
1454 
1455     /* Validate input size */
1456     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1457     {
1458         return STATUS_INVALID_PARAMETER;
1459     }
1460 
1461     /* Ensure we have received UNICODE_STRING */
1462     Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1463     if (Target->DeviceNameLength & 1)
1464     {
1465         return STATUS_INVALID_PARAMETER;
1466     }
1467 
1468     /* Validate the entry structure size */
1469     if (FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + Target->DeviceNameLength >
1470         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 < FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz))
1477     {
1478         return STATUS_INVALID_PARAMETER;
1479     }
1480 
1481     /* Construct string for query */
1482     SymbolicName.Length =
1483     SymbolicName.MaximumLength = Target->DeviceNameLength;
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 the 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 = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) + Output->MultiSzLength;
1568 
1569     /* If it cannot fit, just return the size needed and leave */
1570     if (OutputLength > Stack->Parameters.DeviceIoControl.OutputBufferLength)
1571     {
1572         Irp->IoStatus.Information = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz);
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 (FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + Target->DeviceNameLength >
1607         Stack->Parameters.DeviceIoControl.InputBufferLength)
1608     {
1609         return STATUS_INVALID_PARAMETER;
1610     }
1611 
1612     SymbolicName.Length =
1613     SymbolicName.MaximumLength = Target->DeviceNameLength;
1614     SymbolicName.Buffer = Target->DeviceName;
1615 
1616     /* Find the associated device */
1617     Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
1618     if (!NT_SUCCESS(Status))
1619     {
1620         return Status;
1621     }
1622 
1623     /* Mark we want to keep links */
1624     DeviceInformation->KeepLinks = TRUE;
1625 
1626     return STATUS_SUCCESS;
1627 }
1628 
1629 /*
1630  * @implemented
1631  */
1632 NTSTATUS
1633 MountMgrVolumeArrivalNotification(IN PDEVICE_EXTENSION DeviceExtension,
1634                                   IN PIRP Irp)
1635 {
1636     NTSTATUS Status;
1637     BOOLEAN OldState;
1638     PIO_STACK_LOCATION Stack;
1639     UNICODE_STRING SymbolicName;
1640     PMOUNTMGR_TARGET_NAME Target;
1641 
1642     Stack = IoGetCurrentIrpStackLocation(Irp);
1643 
1644     /* Validate input */
1645     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1646     {
1647         return STATUS_INVALID_PARAMETER;
1648     }
1649 
1650     Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1651     if (FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + Target->DeviceNameLength >
1652         Stack->Parameters.DeviceIoControl.InputBufferLength)
1653     {
1654         return STATUS_INVALID_PARAMETER;
1655     }
1656 
1657     SymbolicName.Length =
1658     SymbolicName.MaximumLength = Target->DeviceNameLength;
1659     SymbolicName.Buffer = Target->DeviceName;
1660 
1661     /* Disable hard errors */
1662     OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
1663     PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
1664 
1665     /* Call real worker */
1666     Status = MountMgrMountedDeviceArrival(DeviceExtension, &SymbolicName, TRUE);
1667 
1668     PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
1669 
1670     return Status;
1671 }
1672 
1673 /*
1674  * @implemented
1675  */
1676 NTSTATUS
1677 MountMgrQueryPoints(IN PDEVICE_EXTENSION DeviceExtension,
1678                     IN PIRP Irp)
1679 {
1680     NTSTATUS Status;
1681     PIO_STACK_LOCATION Stack;
1682     PMOUNTDEV_UNIQUE_ID UniqueId;
1683     PMOUNTMGR_MOUNT_POINT MountPoint;
1684     UNICODE_STRING SymbolicName, DeviceName;
1685 
1686     Stack = IoGetCurrentIrpStackLocation(Irp);
1687 
1688     /* Validate input... */
1689     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1690     {
1691         return STATUS_INVALID_PARAMETER;
1692     }
1693 
1694     MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1695     if (!MountPoint->SymbolicLinkNameLength)
1696     {
1697         MountPoint->SymbolicLinkNameOffset = 0;
1698     }
1699 
1700     if (!MountPoint->UniqueIdLength)
1701     {
1702         MountPoint->UniqueIdOffset = 0;
1703     }
1704 
1705     if (!MountPoint->DeviceNameLength)
1706     {
1707         MountPoint->DeviceNameOffset = 0;
1708     }
1709 
1710     /* Addresses can't be odd */
1711     if ((MountPoint->SymbolicLinkNameOffset & 1) ||
1712         (MountPoint->SymbolicLinkNameLength & 1))
1713     {
1714         return STATUS_INVALID_PARAMETER;
1715     }
1716 
1717     if ((MountPoint->UniqueIdOffset & 1) ||
1718         (MountPoint->UniqueIdLength & 1))
1719     {
1720         return STATUS_INVALID_PARAMETER;
1721     }
1722 
1723     if ((MountPoint->DeviceNameOffset & 1) ||
1724         (MountPoint->DeviceNameLength & 1))
1725     {
1726         return STATUS_INVALID_PARAMETER;
1727     }
1728 
1729     /* We can't go beyond */
1730     if (((ULONG)MountPoint->SymbolicLinkNameLength + MountPoint->UniqueIdLength +
1731         MountPoint->DeviceNameLength) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1732     {
1733         return STATUS_INVALID_PARAMETER;
1734     }
1735 
1736     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS))
1737     {
1738         return STATUS_INVALID_PARAMETER;
1739     }
1740 
1741     /* If caller provided a Symlink, use it */
1742     if (MountPoint->SymbolicLinkNameLength != 0)
1743     {
1744         if (MountPoint->SymbolicLinkNameLength > MAXSHORT)
1745         {
1746             return STATUS_INVALID_PARAMETER;
1747         }
1748 
1749         SymbolicName.Length = MountPoint->SymbolicLinkNameLength;
1750         SymbolicName.MaximumLength = MountPoint->SymbolicLinkNameLength + sizeof(WCHAR);
1751         SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1752         if (!SymbolicName.Buffer)
1753         {
1754             return STATUS_INSUFFICIENT_RESOURCES;
1755         }
1756 
1757         RtlCopyMemory(SymbolicName.Buffer,
1758                       (PWSTR)((ULONG_PTR)MountPoint + MountPoint->SymbolicLinkNameOffset),
1759                       SymbolicName.Length);
1760         SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1761 
1762         /* Query links using it */
1763         Status = QueryPointsFromSymbolicLinkName(DeviceExtension, &SymbolicName, Irp);
1764         FreePool(SymbolicName.Buffer);
1765     }
1766     /* If user provided an unique ID */
1767     else if (MountPoint->UniqueIdLength != 0)
1768     {
1769         UniqueId = AllocatePool(MountPoint->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1770         if (!UniqueId)
1771         {
1772             return STATUS_INSUFFICIENT_RESOURCES;
1773         }
1774 
1775         UniqueId->UniqueIdLength = MountPoint->UniqueIdLength;
1776         RtlCopyMemory(UniqueId->UniqueId,
1777                       (PVOID)((ULONG_PTR)MountPoint + MountPoint->UniqueIdOffset),
1778                       MountPoint->UniqueIdLength);
1779 
1780          /* Query links using it */
1781          Status = QueryPointsFromMemory(DeviceExtension, Irp, UniqueId, NULL);
1782          FreePool(UniqueId);
1783     }
1784     /* If caller provided a device name */
1785     else if (MountPoint->DeviceNameLength != 0)
1786     {
1787         if (MountPoint->DeviceNameLength > MAXSHORT)
1788         {
1789             return STATUS_INVALID_PARAMETER;
1790         }
1791 
1792         DeviceName.Length = MountPoint->DeviceNameLength;
1793         DeviceName.MaximumLength = MountPoint->DeviceNameLength + sizeof(WCHAR);
1794         DeviceName.Buffer = AllocatePool(DeviceName.MaximumLength);
1795         if (!DeviceName.Buffer)
1796         {
1797             return STATUS_INSUFFICIENT_RESOURCES;
1798         }
1799 
1800         RtlCopyMemory(DeviceName.Buffer,
1801                       (PWSTR)((ULONG_PTR)MountPoint + MountPoint->DeviceNameOffset),
1802                       DeviceName.Length);
1803         DeviceName.Buffer[DeviceName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1804 
1805          /* Query links using it */
1806         Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, &DeviceName);
1807         FreePool(DeviceName.Buffer);
1808     }
1809     else
1810     {
1811         /* Otherwise, query all links */
1812         Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, NULL);
1813     }
1814 
1815     return Status;
1816 }
1817 
1818 /*
1819  * @implemented
1820  */
1821 NTSTATUS
1822 MountMgrDeletePoints(IN PDEVICE_EXTENSION DeviceExtension,
1823                      IN PIRP Irp)
1824 {
1825     ULONG Link;
1826     NTSTATUS Status;
1827     BOOLEAN CreateNoDrive;
1828     PIO_STACK_LOCATION Stack;
1829     PMOUNTDEV_UNIQUE_ID UniqueId;
1830     PMOUNTMGR_MOUNT_POINT MountPoint;
1831     PMOUNTMGR_MOUNT_POINTS MountPoints;
1832     UNICODE_STRING SymbolicName, DeviceName;
1833 
1834     Stack = IoGetCurrentIrpStackLocation(Irp);
1835 
1836     /* Validate input */
1837     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1838     {
1839         return STATUS_INVALID_PARAMETER;
1840     }
1841 
1842     /* Query points */
1843     MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1844     CreateNoDrive = (MountPoint->SymbolicLinkNameOffset && MountPoint->SymbolicLinkNameLength);
1845 
1846     Status = MountMgrQueryPoints(DeviceExtension, Irp);
1847     if (!NT_SUCCESS(Status))
1848     {
1849         return Status;
1850     }
1851 
1852     /* For all the points matching the request */
1853     MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1854     for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1855     {
1856         SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1857         SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1858         SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1859         if (!SymbolicName.Buffer)
1860         {
1861             return STATUS_INSUFFICIENT_RESOURCES;
1862         }
1863 
1864         RtlCopyMemory(SymbolicName.Buffer,
1865                       (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1866                       SymbolicName.Length);
1867         SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1868 
1869         /* Create a no drive entry for the drive letters */
1870         if (CreateNoDrive && IsDriveLetter(&SymbolicName))
1871         {
1872             UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1873             if (UniqueId)
1874             {
1875                 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1876                 RtlCopyMemory(UniqueId->UniqueId,
1877                               (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1878                               MountPoints->MountPoints[Link].UniqueIdLength);
1879 
1880                 CreateNoDriveLetterEntry(UniqueId);
1881                 FreePool(UniqueId);
1882             }
1883         }
1884 
1885         /* If there are no link any more, and no need to create a no drive entry */
1886         if (Link == 0 && !CreateNoDrive)
1887         {
1888             /* Then, delete everything */
1889             UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength);
1890             if (UniqueId)
1891             {
1892                 RtlCopyMemory(UniqueId,
1893                               (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1894                               MountPoints->MountPoints[Link].UniqueIdLength);
1895 
1896                 DeleteNoDriveLetterEntry(UniqueId);
1897                 FreePool(UniqueId);
1898             }
1899         }
1900 
1901         /* Delete all the information about the mount point */
1902         GlobalDeleteSymbolicLink(&SymbolicName);
1903         DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, FALSE);
1904         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1905         FreePool(SymbolicName.Buffer);
1906 
1907         /* Notify the change */
1908         DeviceName.Length = DeviceName.MaximumLength =
1909         MountPoints->MountPoints[Link].DeviceNameLength;
1910         DeviceName.Buffer = (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].DeviceNameOffset);
1911         MountMgrNotifyNameChange(DeviceExtension, &DeviceName, TRUE);
1912     }
1913 
1914     MountMgrNotify(DeviceExtension);
1915 
1916     return Status;
1917 }
1918 
1919 /*
1920  * @implemented
1921  */
1922 NTSTATUS
1923 MountMgrDeletePointsDbOnly(IN PDEVICE_EXTENSION DeviceExtension,
1924                            IN PIRP Irp)
1925 {
1926     ULONG Link;
1927     NTSTATUS Status;
1928     UNICODE_STRING SymbolicName;
1929     PMOUNTDEV_UNIQUE_ID UniqueId;
1930     PMOUNTMGR_MOUNT_POINTS MountPoints;
1931 
1932     /* Query points */
1933     Status = MountMgrQueryPoints(DeviceExtension, Irp);
1934     if (!NT_SUCCESS(Status))
1935     {
1936         return Status;
1937     }
1938 
1939     MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1940     if (MountPoints->NumberOfMountPoints == 0)
1941     {
1942         return Status;
1943     }
1944 
1945     /* For all the mount points */
1946     for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1947     {
1948         SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1949         SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1950         SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1951         if (!SymbolicName.Buffer)
1952         {
1953             return STATUS_INSUFFICIENT_RESOURCES;
1954         }
1955 
1956         RtlCopyMemory(SymbolicName.Buffer,
1957                       (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1958                       SymbolicName.Length);
1959         SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1960 
1961         /* If the only mount point is a drive letter, then create a no letter drive entry */
1962         if (MountPoints->NumberOfMountPoints == 1 && IsDriveLetter(&SymbolicName))
1963         {
1964             UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1965             if (UniqueId)
1966             {
1967                 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1968                 RtlCopyMemory(UniqueId->UniqueId,
1969                               (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1970                               MountPoints->MountPoints[Link].UniqueIdLength);
1971 
1972                 CreateNoDriveLetterEntry(UniqueId);
1973                 FreePool(UniqueId);
1974             }
1975         }
1976 
1977         /* Simply delete mount point from DB */
1978         DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, TRUE);
1979         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1980         FreePool(SymbolicName.Buffer);
1981     }
1982 
1983     return Status;
1984 }
1985 
1986 /*
1987  * @implemented
1988  */
1989 NTSTATUS
1990 MountMgrVolumeMountPointChanged(IN PDEVICE_EXTENSION DeviceExtension,
1991                                 IN PIRP Irp,
1992                                 IN NTSTATUS LockStatus,
1993                                 OUT PUNICODE_STRING SourceDeviceName,
1994                                 OUT PUNICODE_STRING SourceSymbolicName,
1995                                 OUT PUNICODE_STRING TargetVolumeName)
1996 {
1997     HANDLE Handle;
1998     NTSTATUS Status;
1999     PFILE_OBJECT FileObject;
2000     PIO_STACK_LOCATION Stack;
2001     ULONG Length, SavedLength;
2002     BOOLEAN FOReferenced = FALSE;
2003     IO_STATUS_BLOCK IoStatusBlock;
2004     OBJECT_ATTRIBUTES ObjectAttributes;
2005     PDEVICE_INFORMATION DeviceInformation;
2006     OBJECT_NAME_INFORMATION ObjectNameInfo;
2007     FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
2008     PFILE_NAME_INFORMATION FileNameInfo = NULL;
2009     PMOUNTMGR_VOLUME_MOUNT_POINT VolumeMountPoint;
2010     POBJECT_NAME_INFORMATION ObjectNameInfoPtr = NULL;
2011     UNICODE_STRING SourceVolumeName, TargetDeviceName;
2012 
2013     Stack = IoGetCurrentIrpStackLocation(Irp);
2014 
2015     /* Validate input */
2016     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT))
2017     {
2018         return STATUS_INVALID_PARAMETER;
2019     }
2020 
2021     VolumeMountPoint = (PMOUNTMGR_VOLUME_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
2022 
2023     if (((ULONG)VolumeMountPoint->SourceVolumeNameLength + VolumeMountPoint->TargetVolumeNameLength) <
2024         Stack->Parameters.DeviceIoControl.InputBufferLength)
2025     {
2026         return STATUS_INVALID_PARAMETER;
2027     }
2028 
2029     /* Get source volume name */
2030     SourceVolumeName.Length =
2031     SourceVolumeName.MaximumLength = VolumeMountPoint->SourceVolumeNameLength;
2032     SourceVolumeName.Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset);
2033 
2034     InitializeObjectAttributes(&ObjectAttributes,
2035                                &SourceVolumeName,
2036                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
2037                                NULL,
2038                                NULL);
2039 
2040     /* Open it */
2041     Status = ZwOpenFile(&Handle,
2042                         SYNCHRONIZE | FILE_READ_ATTRIBUTES,
2043                         &ObjectAttributes,
2044                         &IoStatusBlock,
2045                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2046                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
2047     if (!NT_SUCCESS(Status))
2048     {
2049         return Status;
2050     }
2051 
2052     TargetDeviceName.Buffer = NULL;
2053 
2054     /* Query its attributes */
2055     Status = ZwQueryVolumeInformationFile(Handle,
2056                                           &IoStatusBlock,
2057                                           &FsDeviceInfo,
2058                                           sizeof(FsDeviceInfo),
2059                                           FileFsDeviceInformation);
2060     if (!NT_SUCCESS(Status))
2061     {
2062         goto Cleanup;
2063     }
2064 
2065     if (FsDeviceInfo.DeviceType != FILE_DEVICE_DISK && FsDeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK)
2066     {
2067         goto Cleanup;
2068     }
2069 
2070     if (FsDeviceInfo.Characteristics != (FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA))
2071     {
2072         goto Cleanup;
2073     }
2074 
2075     /* Reference it */
2076     Status = ObReferenceObjectByHandle(Handle, 0, *IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL);
2077     if (!NT_SUCCESS(Status))
2078     {
2079         goto Cleanup;
2080     }
2081     FOReferenced = TRUE;
2082 
2083     /* Get file name */
2084     FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION));
2085     if (!FileNameInfo)
2086     {
2087         Status = STATUS_INSUFFICIENT_RESOURCES;
2088         goto Cleanup;
2089     }
2090 
2091     Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2092                                     sizeof(FILE_NAME_INFORMATION),
2093                                     FileNameInformation);
2094     if (Status == STATUS_BUFFER_OVERFLOW)
2095     {
2096         /* Now we have real length, use it */
2097         Length = FileNameInfo->FileNameLength;
2098         FreePool(FileNameInfo);
2099 
2100         FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + Length);
2101         if (!FileNameInfo)
2102         {
2103             Status = STATUS_INSUFFICIENT_RESOURCES;
2104             goto Cleanup;
2105         }
2106 
2107         /* Really query file name */
2108         Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2109                                         sizeof(FILE_NAME_INFORMATION) + Length,
2110                                         FileNameInformation);
2111     }
2112 
2113     if (!NT_SUCCESS(Status))
2114     {
2115         goto Cleanup;
2116     }
2117 
2118     /* Get symbolic name */
2119     ObjectNameInfoPtr = &ObjectNameInfo;
2120     SavedLength = sizeof(OBJECT_NAME_INFORMATION);
2121     Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, sizeof(OBJECT_NAME_INFORMATION), &Length);
2122     if (Status == STATUS_INFO_LENGTH_MISMATCH)
2123     {
2124         /* Once again, with proper size, it works better */
2125         ObjectNameInfoPtr = AllocatePool(Length);
2126         if (!ObjectNameInfoPtr)
2127         {
2128             Status = STATUS_INSUFFICIENT_RESOURCES;
2129             goto Cleanup;
2130         }
2131 
2132         SavedLength = Length;
2133         Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, SavedLength, &Length);
2134     }
2135 
2136     if (!NT_SUCCESS(Status))
2137     {
2138         goto Cleanup;
2139     }
2140 
2141     /* Now, query the device name */
2142     Status = QueryDeviceInformation(&ObjectNameInfoPtr->Name, SourceDeviceName,
2143                                     NULL, NULL, NULL, NULL, NULL, NULL);
2144     if (!NT_SUCCESS(Status))
2145     {
2146         goto Cleanup;
2147     }
2148 
2149     /* For target volume name, use input */
2150     TargetVolumeName->Length =
2151     TargetVolumeName->MaximumLength = VolumeMountPoint->TargetVolumeNameLength;
2152     TargetVolumeName->Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset);
2153 
2154     /* Query its device name */
2155     Status = QueryDeviceInformation(TargetVolumeName, &TargetDeviceName,
2156                                     NULL, NULL, NULL, NULL, NULL, NULL);
2157     if (!NT_SUCCESS(Status))
2158     {
2159         goto Cleanup;
2160     }
2161 
2162     /* Return symbolic name */
2163     SourceSymbolicName->Length =
2164     SourceSymbolicName->MaximumLength = (USHORT)FileNameInfo->FileNameLength;
2165     SourceSymbolicName->Buffer = (PWSTR)FileNameInfo;
2166     /* memmove allows memory overlap */
2167     RtlMoveMemory(SourceSymbolicName->Buffer, FileNameInfo->FileName, SourceSymbolicName->Length);
2168     FileNameInfo = NULL;
2169 
2170     /* Notify the change */
2171     MountMgrNotify(DeviceExtension);
2172     MountMgrNotifyNameChange(DeviceExtension, &TargetDeviceName, TRUE);
2173 
2174     /* If we are locked, sync databases if possible */
2175     if (NT_SUCCESS(LockStatus))
2176     {
2177         Status = FindDeviceInfo(DeviceExtension, SourceDeviceName, FALSE, &DeviceInformation);
2178         if (NT_SUCCESS(Status))
2179         {
2180             ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
2181         }
2182         else
2183         {
2184             Status = STATUS_PENDING;
2185         }
2186     }
2187 
2188 Cleanup:
2189     if (TargetDeviceName.Buffer)
2190     {
2191         FreePool(TargetDeviceName.Buffer);
2192     }
2193 
2194     if (ObjectNameInfoPtr && ObjectNameInfoPtr != &ObjectNameInfo)
2195     {
2196         FreePool(ObjectNameInfoPtr);
2197     }
2198 
2199     if (FileNameInfo)
2200     {
2201         FreePool(FileNameInfo);
2202     }
2203 
2204     if (FOReferenced)
2205     {
2206         ObDereferenceObject(FileObject);
2207     }
2208 
2209     return Status;
2210 }
2211 
2212 /*
2213  * @implemented
2214  */
2215 NTSTATUS
2216 MountMgrVolumeMountPointCreated(IN PDEVICE_EXTENSION DeviceExtension,
2217                                 IN PIRP Irp,
2218                                 IN NTSTATUS LockStatus)
2219 {
2220     LONG Offset;
2221     BOOLEAN Found;
2222     NTSTATUS Status;
2223     HANDLE RemoteDatabase;
2224     PMOUNTDEV_UNIQUE_ID UniqueId;
2225     PDATABASE_ENTRY DatabaseEntry;
2226     PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2227     PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2228     UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2229 
2230     /* Initialize string */
2231     LinkTarget.Length = 0;
2232     LinkTarget.MaximumLength = 0xC8;
2233     LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2234     if (LinkTarget.Buffer == NULL)
2235     {
2236         return STATUS_INSUFFICIENT_RESOURCES;
2237     }
2238 
2239     /* If the mount point was created, then, it changed!
2240      * Also use it to query some information
2241      */
2242     Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2243     /* Pending means DB are under synchronization, bail out */
2244     if (Status == STATUS_PENDING)
2245     {
2246         FreePool(LinkTarget.Buffer);
2247         FreePool(SourceDeviceName.Buffer);
2248         FreePool(SourceSymbolicName.Buffer);
2249         return STATUS_SUCCESS;
2250     }
2251     else if (!NT_SUCCESS(Status))
2252     {
2253         FreePool(LinkTarget.Buffer);
2254         return Status;
2255     }
2256 
2257     /* Query the device information */
2258     Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2259     if (!NT_SUCCESS(Status))
2260     {
2261         /* If it failed, first try to get volume name */
2262         Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2263         if (!NT_SUCCESS(Status))
2264         {
2265             /* Then, try to read the symlink */
2266             Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2267             if (!NT_SUCCESS(Status))
2268             {
2269                 FreePool(LinkTarget.Buffer);
2270                 FreePool(SourceDeviceName.Buffer);
2271                 FreePool(SourceSymbolicName.Buffer);
2272                 return Status;
2273             }
2274         }
2275         else
2276         {
2277             FreePool(VolumeName.Buffer);
2278         }
2279 
2280         FreePool(SourceDeviceName.Buffer);
2281 
2282         SourceDeviceName.Length = LinkTarget.Length;
2283         SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2284         SourceDeviceName.Buffer = LinkTarget.Buffer;
2285 
2286         /* Now that we have the correct source, reattempt to query information */
2287         Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2288         if (!NT_SUCCESS(Status))
2289         {
2290             FreePool(SourceDeviceName.Buffer);
2291             FreePool(SourceSymbolicName.Buffer);
2292             return Status;
2293         }
2294     }
2295 
2296     FreePool(SourceDeviceName.Buffer);
2297 
2298     /* Get information about target device */
2299     Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2300     if (!NT_SUCCESS(Status))
2301     {
2302         FreePool(SourceSymbolicName.Buffer);
2303         return Status;
2304     }
2305 
2306     /* Notify if not disabled */
2307     if (!TargetDeviceInformation->SkipNotifications)
2308     {
2309         PostOnlineNotification(DeviceExtension, &TargetDeviceInformation->SymbolicName);
2310     }
2311 
2312     /* Open the remote database */
2313     RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2314     if (RemoteDatabase == 0)
2315     {
2316         FreePool(SourceSymbolicName.Buffer);
2317         return STATUS_INSUFFICIENT_RESOURCES;
2318     }
2319 
2320     /* Browse all the entries */
2321     Offset = 0;
2322     Found = FALSE;
2323     for (;;)
2324     {
2325         DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2326         if (DatabaseEntry == NULL)
2327         {
2328             break;
2329         }
2330 
2331         /* Try to find ourselves */
2332         DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2333         DbName.Length = DbName.MaximumLength;
2334         DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2335         if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2336         {
2337             /* Reference ourselves and update the entry */
2338             ++DatabaseEntry->EntryReferences;
2339             Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2340             FreePool(DatabaseEntry);
2341             Found = TRUE;
2342             break;
2343         }
2344 
2345         Offset += DatabaseEntry->EntrySize;
2346         FreePool(DatabaseEntry);
2347     }
2348 
2349     /* We couldn't find ourselves, we'll have to add ourselves */
2350     if (!Found)
2351     {
2352         ULONG EntrySize;
2353         PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2354 
2355         /* Query the device unique ID */
2356         Status = QueryDeviceInformation(&TargetVolumeName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
2357         if (!NT_SUCCESS(Status))
2358         {
2359             FreePool(SourceSymbolicName.Buffer);
2360             CloseRemoteDatabase(RemoteDatabase);
2361             return Status;
2362         }
2363 
2364         /* Allocate a database entry */
2365         EntrySize = UniqueId->UniqueIdLength + TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2366         DatabaseEntry = AllocatePool(EntrySize);
2367         if (DatabaseEntry == NULL)
2368         {
2369             FreePool(UniqueId);
2370             FreePool(SourceSymbolicName.Buffer);
2371             CloseRemoteDatabase(RemoteDatabase);
2372             return STATUS_INSUFFICIENT_RESOURCES;
2373         }
2374 
2375         /* Fill it in */
2376         DatabaseEntry->EntrySize = EntrySize;
2377         DatabaseEntry->EntryReferences = 1;
2378         DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
2379         DatabaseEntry->SymbolicNameLength = TargetVolumeName.Length;
2380         DatabaseEntry->UniqueIdOffset = TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2381         DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
2382         RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + sizeof(DATABASE_ENTRY)), TargetVolumeName.Buffer, DatabaseEntry->SymbolicNameLength);
2383         RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
2384 
2385         /* And write it down */
2386         Status = AddRemoteDatabaseEntry(RemoteDatabase, DatabaseEntry);
2387         FreePool(DatabaseEntry);
2388         if (!NT_SUCCESS(Status))
2389         {
2390             FreePool(UniqueId);
2391             FreePool(SourceSymbolicName.Buffer);
2392             CloseRemoteDatabase(RemoteDatabase);
2393             return Status;
2394         }
2395 
2396         /* And now, allocate an Unique ID item */
2397         UniqueIdReplicate = AllocatePool(sizeof(UNIQUE_ID_REPLICATE));
2398         if (UniqueIdReplicate == NULL)
2399         {
2400             FreePool(UniqueId);
2401             FreePool(SourceSymbolicName.Buffer);
2402             CloseRemoteDatabase(RemoteDatabase);
2403             return Status;
2404         }
2405 
2406         /* To associate it with the device */
2407         UniqueIdReplicate->UniqueId = UniqueId;
2408         InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2409     }
2410 
2411     /* We're done with the remote database */
2412     CloseRemoteDatabase(RemoteDatabase);
2413 
2414     /* Check we were find writing the entry */
2415     if (!NT_SUCCESS(Status))
2416     {
2417         FreePool(SourceSymbolicName.Buffer);
2418         return Status;
2419     }
2420 
2421     /* This is the end, allocate an associated entry */
2422     AssociatedEntry = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY));
2423     if (AssociatedEntry == NULL)
2424     {
2425         FreePool(SourceSymbolicName.Buffer);
2426         return STATUS_INSUFFICIENT_RESOURCES;
2427     }
2428 
2429     /* Initialize its source name string */
2430     AssociatedEntry->String.Length = SourceSymbolicName.Length;
2431     AssociatedEntry->String.MaximumLength = AssociatedEntry->String.Length + sizeof(UNICODE_NULL);
2432     AssociatedEntry->String.Buffer = AllocatePool(AssociatedEntry->String.MaximumLength);
2433     if (AssociatedEntry->String.Buffer == NULL)
2434     {
2435         FreePool(AssociatedEntry);
2436         FreePool(SourceSymbolicName.Buffer);
2437         return STATUS_INSUFFICIENT_RESOURCES;
2438     }
2439 
2440     /* Copy data & insert in list */
2441     RtlCopyMemory(AssociatedEntry->String.Buffer, SourceSymbolicName.Buffer, SourceSymbolicName.Length);
2442     AssociatedEntry->String.Buffer[SourceSymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
2443     AssociatedEntry->DeviceInformation = DeviceInformation;
2444     InsertTailList(&TargetDeviceInformation->AssociatedDevicesHead, &AssociatedEntry->AssociatedDevicesEntry);
2445 
2446     /* We're done! */
2447     FreePool(SourceSymbolicName.Buffer);
2448     return STATUS_SUCCESS;
2449 }
2450 
2451 /*
2452  * @implemented
2453  */
2454 NTSTATUS
2455 MountMgrVolumeMountPointDeleted(IN PDEVICE_EXTENSION DeviceExtension,
2456                                 IN PIRP Irp,
2457                                 IN NTSTATUS LockStatus)
2458 {
2459     LONG Offset;
2460     NTSTATUS Status;
2461     PLIST_ENTRY Entry;
2462     HANDLE RemoteDatabase;
2463     PDATABASE_ENTRY DatabaseEntry;
2464     PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2465     PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2466     PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2467     UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2468 
2469     /* Initialize string */
2470     LinkTarget.Length = 0;
2471     LinkTarget.MaximumLength = 0xC8;
2472     LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2473     if (LinkTarget.Buffer == NULL)
2474     {
2475         return STATUS_INSUFFICIENT_RESOURCES;
2476     }
2477 
2478     /* If the mount point was deleted, then, it changed!
2479      * Also use it to query some information
2480      */
2481     Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2482     /* Pending means DB are under synchronization, bail out */
2483     if (Status == STATUS_PENDING)
2484     {
2485         FreePool(LinkTarget.Buffer);
2486         FreePool(SourceDeviceName.Buffer);
2487         FreePool(SourceSymbolicName.Buffer);
2488         return STATUS_SUCCESS;
2489     }
2490     else if (!NT_SUCCESS(Status))
2491     {
2492         FreePool(LinkTarget.Buffer);
2493         return Status;
2494     }
2495 
2496     /* Query the device information */
2497     Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2498     if (!NT_SUCCESS(Status))
2499     {
2500         /* If it failed, first try to get volume name */
2501         Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2502         if (!NT_SUCCESS(Status))
2503         {
2504             /* Then, try to read the symlink */
2505             Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2506             if (!NT_SUCCESS(Status))
2507             {
2508                 FreePool(LinkTarget.Buffer);
2509                 FreePool(SourceDeviceName.Buffer);
2510                 FreePool(SourceSymbolicName.Buffer);
2511                 return Status;
2512             }
2513         }
2514         else
2515         {
2516             FreePool(VolumeName.Buffer);
2517         }
2518 
2519         FreePool(SourceDeviceName.Buffer);
2520 
2521         SourceDeviceName.Length = LinkTarget.Length;
2522         SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2523         SourceDeviceName.Buffer = LinkTarget.Buffer;
2524 
2525         /* Now that we have the correct source, reattempt to query information */
2526         Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2527         if (!NT_SUCCESS(Status))
2528         {
2529             FreePool(SourceDeviceName.Buffer);
2530             FreePool(SourceSymbolicName.Buffer);
2531             return Status;
2532         }
2533     }
2534 
2535     FreePool(SourceDeviceName.Buffer);
2536 
2537     /* Get information about target device */
2538     Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2539     if (!NT_SUCCESS(Status))
2540     {
2541         FreePool(SourceSymbolicName.Buffer);
2542         return Status;
2543     }
2544 
2545     /* Open the remote database */
2546     RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2547     if (RemoteDatabase == 0)
2548     {
2549         FreePool(SourceSymbolicName.Buffer);
2550         return STATUS_INSUFFICIENT_RESOURCES;
2551     }
2552 
2553     /* Browse all the entries */
2554     Offset = 0;
2555     for (;;)
2556     {
2557         DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2558         if (DatabaseEntry == NULL)
2559         {
2560             /* We didn't find ourselves, that's infortunate! */
2561             FreePool(SourceSymbolicName.Buffer);
2562             CloseRemoteDatabase(RemoteDatabase);
2563             return STATUS_INVALID_PARAMETER;
2564         }
2565 
2566         /* Try to find ourselves */
2567         DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2568         DbName.Length = DbName.MaximumLength;
2569         DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2570         if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2571         {
2572             break;
2573         }
2574 
2575         Offset += DatabaseEntry->EntrySize;
2576         FreePool(DatabaseEntry);
2577     }
2578 
2579     /* Dereference ourselves */
2580     DatabaseEntry->EntryReferences--;
2581     if (DatabaseEntry->EntryReferences == 0)
2582     {
2583         /* If we're still referenced, just update the entry */
2584         Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2585     }
2586     else
2587     {
2588         /* Otherwise, delete the entry */
2589         Status = DeleteRemoteDatabaseEntry(RemoteDatabase, Offset);
2590         if (!NT_SUCCESS(Status))
2591         {
2592             FreePool(DatabaseEntry);
2593             FreePool(SourceSymbolicName.Buffer);
2594             CloseRemoteDatabase(RemoteDatabase);
2595             return Status;
2596         }
2597 
2598         /* Also, delete our unique ID replicated record */
2599         for (Entry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink;
2600              Entry != &DeviceInformation->ReplicatedUniqueIdsListHead;
2601              Entry = Entry->Flink)
2602         {
2603             UniqueIdReplicate = CONTAINING_RECORD(Entry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
2604 
2605             if (UniqueIdReplicate->UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
2606                 RtlCompareMemory(UniqueIdReplicate->UniqueId->UniqueId,
2607                                  (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
2608                                  DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
2609             {
2610                 break;
2611             }
2612         }
2613 
2614         /* It has to exist! */
2615         if (Entry == &DeviceInformation->ReplicatedUniqueIdsListHead)
2616         {
2617             FreePool(DatabaseEntry);
2618             FreePool(SourceSymbolicName.Buffer);
2619             CloseRemoteDatabase(RemoteDatabase);
2620             return STATUS_UNSUCCESSFUL;
2621         }
2622 
2623         /* Remove it and free it */
2624         RemoveEntryList(&UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2625         FreePool(UniqueIdReplicate->UniqueId);
2626         FreePool(UniqueIdReplicate);
2627     }
2628 
2629     /* We're done with the remote database */
2630     FreePool(DatabaseEntry);
2631     CloseRemoteDatabase(RemoteDatabase);
2632 
2633     /* Check write operation succeed */
2634     if (!NT_SUCCESS(Status))
2635     {
2636         FreePool(SourceSymbolicName.Buffer);
2637         return Status;
2638     }
2639 
2640     /* Try to find our associated device entry */
2641     for (Entry = TargetDeviceInformation->AssociatedDevicesHead.Flink;
2642          Entry != &TargetDeviceInformation->AssociatedDevicesHead;
2643          Entry = Entry->Flink)
2644     {
2645         AssociatedEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
2646 
2647         /* If found, delete it */
2648         if (AssociatedEntry->DeviceInformation == DeviceInformation &&
2649             RtlEqualUnicodeString(&AssociatedEntry->String, &SourceSymbolicName, TRUE))
2650         {
2651             RemoveEntryList(&AssociatedEntry->AssociatedDevicesEntry);
2652             FreePool(AssociatedEntry->String.Buffer);
2653             FreePool(AssociatedEntry);
2654             break;
2655         }
2656     }
2657 
2658     /* We're done! */
2659     FreePool(SourceSymbolicName.Buffer);
2660     return STATUS_SUCCESS;
2661 }
2662 
2663 /*
2664  * @implemented
2665  */
2666 NTSTATUS
2667 NTAPI
2668 MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject,
2669                       IN PIRP Irp)
2670 {
2671     PIO_STACK_LOCATION Stack;
2672     NTSTATUS Status, LockStatus;
2673     PDEVICE_EXTENSION DeviceExtension;
2674 
2675     Stack = IoGetCurrentIrpStackLocation(Irp);
2676     DeviceExtension = DeviceObject->DeviceExtension;
2677 
2678     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2679 
2680     switch (Stack->Parameters.DeviceIoControl.IoControlCode)
2681     {
2682         case IOCTL_MOUNTMGR_CREATE_POINT:
2683             Status = MountMgrCreatePoint(DeviceExtension, Irp);
2684             break;
2685 
2686         case IOCTL_MOUNTMGR_DELETE_POINTS:
2687             Status = MountMgrDeletePoints(DeviceExtension, Irp);
2688             break;
2689 
2690         case IOCTL_MOUNTMGR_QUERY_POINTS:
2691             Status = MountMgrQueryPoints(DeviceExtension, Irp);
2692             break;
2693 
2694         case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY:
2695             Status = MountMgrDeletePointsDbOnly(DeviceExtension, Irp);
2696             break;
2697 
2698         case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER:
2699             Status = MountMgrNextDriveLetter(DeviceExtension, Irp);
2700             break;
2701 
2702         case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS:
2703         // NOTE: On Win7+, this is handled during driver re-initialization.
2704             DeviceExtension->AutomaticDriveLetter = TRUE;
2705             MountMgrAssignDriveLetters(DeviceExtension);
2706             ReconcileAllDatabasesWithMaster(DeviceExtension);
2707             WaitForOnlinesToComplete(DeviceExtension);
2708             Status = STATUS_SUCCESS;
2709             break;
2710 
2711         case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED:
2712             KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2713 
2714             LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2715             KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2716             Status = MountMgrVolumeMountPointCreated(DeviceExtension, Irp, LockStatus);
2717             if (NT_SUCCESS(LockStatus))
2718             {
2719                 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2720             }
2721 
2722             break;
2723 
2724         case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:
2725             KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2726 
2727             LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2728             KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2729             Status = MountMgrVolumeMountPointDeleted(DeviceExtension, Irp, LockStatus);
2730             if (NT_SUCCESS(LockStatus))
2731             {
2732                 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2733             }
2734 
2735             break;
2736 
2737         case IOCTL_MOUNTMGR_CHANGE_NOTIFY:
2738             Status = MountMgrChangeNotify(DeviceExtension, Irp);
2739             break;
2740 
2741         case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE:
2742             Status = MountMgrKeepLinksWhenOffline(DeviceExtension, Irp);
2743             break;
2744 
2745         case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES:
2746             Status = MountMgrCheckUnprocessedVolumes(DeviceExtension, Irp);
2747             goto Complete;
2748 
2749         case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION:
2750             KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2751             Status = MountMgrVolumeArrivalNotification(DeviceExtension, Irp);
2752             goto Complete;
2753 
2754         case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH:
2755             Status = MountMgrQueryDosVolumePath(DeviceExtension, Irp);
2756             break;
2757 
2758         case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS:
2759             Status = MountMgrQueryDosVolumePaths(DeviceExtension, Irp);
2760             break;
2761 
2762         case IOCTL_MOUNTMGR_SCRUB_REGISTRY:
2763             Status = MountMgrScrubRegistry(DeviceExtension);
2764             break;
2765 
2766         case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT:
2767             Status = MountMgrQueryAutoMount(DeviceExtension, Irp);
2768             break;
2769 
2770         case IOCTL_MOUNTMGR_SET_AUTO_MOUNT:
2771             Status = MountMgrSetAutoMount(DeviceExtension, Irp);
2772             break;
2773 
2774         default:
2775             Status = STATUS_INVALID_DEVICE_REQUEST;
2776     }
2777 
2778     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2779 
2780     if (Status != STATUS_PENDING)
2781     {
2782         goto Complete;
2783     }
2784 
2785     return Status;
2786 
2787 Complete:
2788     Irp->IoStatus.Status = Status;
2789     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2790 
2791     return Status;
2792 }
2793