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