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