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