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