xref: /reactos/drivers/storage/mountmgr/mountmgr.c (revision 84344399)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2011 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/mountmgr.c
22  * PURPOSE:          Mount Manager
23  * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
24  *                   Alex Ionescu (alex.ionescu@reactos.org)
25  */
26 
27 #include "mntmgr.h"
28 
29 #define NDEBUG
30 #include <debug.h>
31 
32 /* FIXME */
33 GUID MountedDevicesGuid = {0x53F5630D, 0xB6BF, 0x11D0, {0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B}};
34 
35 PDEVICE_OBJECT gdeviceObject;
36 KEVENT UnloadEvent;
37 LONG Unloading;
38 
39 static const WCHAR Cunc[] = L"\\??\\C:";
40 #define Cunc_LETTER_POSITION 4
41 
42 /**
43  * @brief
44  * Sends a synchronous IOCTL to the specified device object.
45  *
46  * @param[in]   IoControlCode
47  * The IOCTL to send to the device.
48  *
49  * @param[in]   DeviceObject
50  * Pointer to the device object that will handle the IOCTL.
51  *
52  * @param[in]   InputBuffer
53  * Optional pointer to a buffer containing input data for the IOCTL.
54  * When specified, the buffer should be at least of InputBufferLength size.
55  *
56  * @param[in]   InputBufferLength
57  * Size in bytes, of the buffer pointed by InputBuffer.
58  *
59  * @param[out]  OutputBuffer
60  * Optional pointer to a buffer that will receive output data from the IOCTL.
61  * When specified, the buffer should be at least of OutputBufferLength size.
62  *
63  * @param[in]   OutputBufferLength
64  * Size in bytes, of the buffer pointed by OutputBuffer.
65  *
66  * @param[in]   FileObject
67  * Optional pointer to a file object that may be necessary for the IOCTL.
68  *
69  * @return
70  * An NTSTATUS code indicating success or failure of this function.
71  *
72  * @note
73  * Must be called at PASSIVE_LEVEL with all APCs enabled.
74  **/
75 _IRQL_requires_(PASSIVE_LEVEL)
76 NTSTATUS
77 MountMgrSendSyncDeviceIoCtl(
78     _In_ ULONG IoControlCode,
79     _In_ PDEVICE_OBJECT DeviceObject,
80     _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
81     _In_ ULONG InputBufferLength,
82     _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer,
83     _In_ ULONG OutputBufferLength,
84     _In_opt_ PFILE_OBJECT FileObject)
85 {
86     NTSTATUS Status;
87     KEVENT Event;
88     IO_STATUS_BLOCK IoStatusBlock;
89     PIRP Irp;
90 
91     /* We must be at passive level as we are using an on-stack event, and
92      * APCs must be enabled for allowing the Special Kernel APC queued by
93      * the IO Manager to run for completing the IRP */
94     ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
95     ASSERT(!KeAreAllApcsDisabled());
96 
97     /* Initialize the on-stack notification event and build the threaded IRP */
98     KeInitializeEvent(&Event, NotificationEvent, FALSE);
99     Irp = IoBuildDeviceIoControlRequest(IoControlCode,
100                                         DeviceObject,
101                                         InputBuffer,
102                                         InputBufferLength,
103                                         OutputBuffer,
104                                         OutputBufferLength,
105                                         FALSE,
106                                         &Event,
107                                         &IoStatusBlock);
108     if (!Irp)
109         return STATUS_INSUFFICIENT_RESOURCES;
110 
111     /* Set up the FileObject for the IOCTL if required */
112     if (FileObject)
113         IoGetNextIrpStackLocation(Irp)->FileObject = FileObject;
114 
115     /* Finally, call the driver and wait for IRP completion if necessary */
116     Status = IoCallDriver(DeviceObject, Irp);
117     if (Status == STATUS_PENDING)
118     {
119         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
120         Status = IoStatusBlock.Status;
121     }
122 
123     return Status;
124 }
125 
126 /*
127  * @implemented
128  */
129 BOOLEAN
130 IsOffline(PUNICODE_STRING SymbolicName)
131 {
132     NTSTATUS Status;
133     ULONG IsOffline, Default;
134     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
135 
136     /* Prepare to look in the registry to see if
137      * given volume is offline
138      */
139     RtlZeroMemory(QueryTable, sizeof(QueryTable));
140     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
141     QueryTable[0].Name = SymbolicName->Buffer;
142     QueryTable[0].EntryContext = &IsOffline;
143     QueryTable[0].DefaultType = REG_DWORD;
144     QueryTable[0].DefaultLength = sizeof(ULONG);
145     QueryTable[0].DefaultData = &Default;
146 
147     Default = 0;
148 
149     /* Query status */
150     Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
151                                     OfflinePath,
152                                     QueryTable,
153                                     NULL,
154                                     NULL);
155     if (!NT_SUCCESS(Status))
156     {
157         IsOffline = 0;
158     }
159 
160     return (IsOffline != 0);
161 }
162 
163 /*
164  * @implemented
165  */
166 BOOLEAN
167 HasDriveLetter(IN PDEVICE_INFORMATION DeviceInformation)
168 {
169     PLIST_ENTRY NextEntry;
170     PSYMLINK_INFORMATION SymlinkInfo;
171 
172     /* Browse all the symlinks to check if there is at least a drive letter */
173     for (NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
174          NextEntry != &DeviceInformation->SymbolicLinksListHead;
175          NextEntry = NextEntry->Flink)
176     {
177         SymlinkInfo = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
178 
179         if (IsDriveLetter(&SymlinkInfo->Name) && SymlinkInfo->Online)
180         {
181             return TRUE;
182         }
183     }
184 
185     return FALSE;
186 }
187 
188 /*
189  * @implemented
190  */
191 NTSTATUS
192 CreateNewDriveLetterName(OUT PUNICODE_STRING DriveLetter,
193                          IN PUNICODE_STRING DeviceName,
194                          IN UCHAR Letter,
195                          IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL)
196 {
197     NTSTATUS Status = STATUS_UNSUCCESSFUL;
198 
199     /* Allocate a big enough buffer to contain the symbolic link */
200     DriveLetter->MaximumLength = DosDevices.Length + 3 * sizeof(WCHAR);
201     DriveLetter->Buffer = AllocatePool(DriveLetter->MaximumLength);
202     if (!DriveLetter->Buffer)
203     {
204         return STATUS_INSUFFICIENT_RESOURCES;
205     }
206 
207     /* Copy prefix */
208     RtlCopyUnicodeString(DriveLetter, &DosDevices);
209 
210     /* Update string to reflect real contents */
211     DriveLetter->Length = DosDevices.Length + 2 * sizeof(WCHAR);
212     DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR) + 2] = UNICODE_NULL;
213     DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR) + 1] = L':';
214 
215     /* If caller wants a no drive entry */
216     if (Letter == (UCHAR)-1)
217     {
218         /* Then, create a no letter entry */
219         CreateNoDriveLetterEntry(UniqueId);
220         FreePool(DriveLetter->Buffer);
221         return STATUS_UNSUCCESSFUL;
222     }
223     else if (Letter)
224     {
225         /* Use the letter given by the caller */
226         DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR)] = (WCHAR)Letter;
227         Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName);
228         if (NT_SUCCESS(Status))
229         {
230             return Status;
231         }
232     }
233 
234     /* If caller didn't provide a letter, let's find one for him */
235 
236     if (RtlPrefixUnicodeString(&DeviceFloppy, DeviceName, TRUE))
237     {
238         /* If the device is a floppy, start with letter A */
239         Letter = 'A';
240     }
241     else if (RtlPrefixUnicodeString(&DeviceCdRom, DeviceName, TRUE))
242     {
243         /* If the device is a CD-ROM, start with letter D */
244         Letter = 'D';
245     }
246     else
247     {
248         /* Finally, if it's a disk, use C */
249         Letter = 'C';
250     }
251 
252     /* Try to affect a letter (up to Z, ofc) until it's possible */
253     for (; Letter <= 'Z'; Letter++)
254     {
255         DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR)] = (WCHAR)Letter;
256         Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName);
257         if (NT_SUCCESS(Status))
258         {
259             DPRINT("Assigned drive %c: to %wZ\n", Letter, DeviceName);
260             return Status;
261         }
262     }
263 
264     /* We failed to allocate a letter */
265     FreePool(DriveLetter->Buffer);
266     DPRINT("Failed to create a drive letter for %wZ\n", DeviceName);
267     return Status;
268 }
269 
270 /*
271  * @implemented
272  */
273 NTSTATUS
274 QueryDeviceInformation(
275     _In_ PUNICODE_STRING SymbolicName,
276     _Out_opt_ PUNICODE_STRING DeviceName,
277     _Out_opt_ PMOUNTDEV_UNIQUE_ID* UniqueId,
278     _Out_opt_ PBOOLEAN Removable,
279     _Out_opt_ PBOOLEAN GptDriveLetter,
280     _Out_opt_ PBOOLEAN HasGuid,
281     _Inout_opt_ LPGUID StableGuid,
282     _Out_opt_ PBOOLEAN IsFT)
283 {
284     NTSTATUS Status;
285     USHORT Size;
286     BOOLEAN IsRemovable;
287     PMOUNTDEV_NAME Name;
288     PMOUNTDEV_UNIQUE_ID Id;
289     PFILE_OBJECT FileObject;
290     PDEVICE_OBJECT DeviceObject;
291     PARTITION_INFORMATION_EX PartitionInfo;
292     STORAGE_DEVICE_NUMBER StorageDeviceNumber;
293     VOLUME_GET_GPT_ATTRIBUTES_INFORMATION GptAttributes;
294 
295     /* Get device associated with the symbolic name */
296     Status = IoGetDeviceObjectPointer(SymbolicName,
297                                       FILE_READ_ATTRIBUTES,
298                                       &FileObject,
299                                       &DeviceObject);
300     if (!NT_SUCCESS(Status))
301     {
302         return Status;
303     }
304 
305     /* The associate FO can't have a file name */
306     if (FileObject->FileName.Length)
307     {
308         ObDereferenceObject(FileObject);
309         return STATUS_OBJECT_NAME_NOT_FOUND;
310     }
311 
312     /* Check if it's removable & return to the user (if asked to) */
313     IsRemovable = (FileObject->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
314     if (Removable)
315     {
316         *Removable = IsRemovable;
317     }
318 
319     /* Get the attached device */
320     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
321 
322     /* If we've been asked for a GPT drive letter */
323     if (GptDriveLetter)
324     {
325         /* Consider it has one */
326         *GptDriveLetter = TRUE;
327 
328         if (!IsRemovable)
329         {
330             /* Query the GPT attributes */
331             Status = MountMgrSendSyncDeviceIoCtl(IOCTL_VOLUME_GET_GPT_ATTRIBUTES,
332                                                  DeviceObject,
333                                                  NULL,
334                                                  0,
335                                                  &GptAttributes,
336                                                  sizeof(GptAttributes),
337                                                  NULL);
338             /* Failure isn't major */
339             if (!NT_SUCCESS(Status))
340             {
341                 Status = STATUS_SUCCESS;
342             }
343             /* Check if it has a drive letter */
344             else if (GptAttributes.GptAttributes & GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER)
345             {
346                 *GptDriveLetter = FALSE;
347             }
348         }
349     }
350 
351     /* If caller wants to know if this is a FT volume */
352     if (IsFT)
353     {
354         /* Suppose it's not */
355         *IsFT = FALSE;
356 
357         /* FT volume can't be removable */
358         if (!IsRemovable)
359         {
360             /* Query partition information */
361             Status = MountMgrSendSyncDeviceIoCtl(IOCTL_DISK_GET_PARTITION_INFO_EX,
362                                                  DeviceObject,
363                                                  NULL,
364                                                  0,
365                                                  &PartitionInfo,
366                                                  sizeof(PartitionInfo),
367                                                  NULL);
368             /* Failure isn't major */
369             if (!NT_SUCCESS(Status))
370             {
371                 Status = STATUS_SUCCESS;
372             }
373             /* Check if this is a FT volume */
374             else if ((PartitionInfo.PartitionStyle == PARTITION_STYLE_MBR) &&
375                      IsFTPartition(PartitionInfo.Mbr.PartitionType))
376             {
377                 *IsFT = TRUE;
378             }
379 
380             /* It looks like a FT volume. Verify it is really one by checking
381              * that it does NOT lie on a specific storage device (i.e. it is
382              * not a basic volume). */
383             if (*IsFT)
384             {
385                 Status = MountMgrSendSyncDeviceIoCtl(IOCTL_STORAGE_GET_DEVICE_NUMBER,
386                                                      DeviceObject,
387                                                      NULL,
388                                                      0,
389                                                      &StorageDeviceNumber,
390                                                      sizeof(StorageDeviceNumber),
391                                                      NULL);
392                 if (!NT_SUCCESS(Status))
393                     Status = STATUS_SUCCESS;
394                 else
395                     *IsFT = FALSE; // Succeeded, so this cannot be a FT volume.
396             }
397         }
398     }
399 
400     /* If caller needs device name */
401     if (DeviceName)
402     {
403         /* Allocate a buffer just to request length */
404         Name = AllocatePool(sizeof(MOUNTDEV_NAME));
405         if (!Name)
406         {
407             ObDereferenceObject(DeviceObject);
408             ObDereferenceObject(FileObject);
409             return STATUS_INSUFFICIENT_RESOURCES;
410         }
411 
412         /* Query device name */
413         Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
414                                              DeviceObject,
415                                              NULL,
416                                              0,
417                                              Name,
418                                              sizeof(MOUNTDEV_NAME),
419                                              FileObject);
420         /* Retry with appropriate length */
421         if (Status == STATUS_BUFFER_OVERFLOW)
422         {
423             Size = Name->NameLength + sizeof(MOUNTDEV_NAME);
424 
425             FreePool(Name);
426 
427             /* Allocate proper size */
428             Name = AllocatePool(Size);
429             if (!Name)
430             {
431                 ObDereferenceObject(DeviceObject);
432                 ObDereferenceObject(FileObject);
433                 return STATUS_INSUFFICIENT_RESOURCES;
434             }
435 
436             /* And query name (for real that time) */
437             Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
438                                                  DeviceObject,
439                                                  NULL,
440                                                  0,
441                                                  Name,
442                                                  Size,
443                                                  FileObject);
444         }
445 
446         if (NT_SUCCESS(Status))
447         {
448             /* Copy back found name to the caller */
449             DeviceName->Length = Name->NameLength;
450             DeviceName->MaximumLength = Name->NameLength + sizeof(WCHAR);
451             DeviceName->Buffer = AllocatePool(DeviceName->MaximumLength);
452             if (!DeviceName->Buffer)
453             {
454                 Status = STATUS_INSUFFICIENT_RESOURCES;
455             }
456             else
457             {
458                 RtlCopyMemory(DeviceName->Buffer, Name->Name, Name->NameLength);
459                 DeviceName->Buffer[Name->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
460             }
461         }
462 
463         FreePool(Name);
464     }
465 
466     if (!NT_SUCCESS(Status))
467     {
468         ObDereferenceObject(DeviceObject);
469         ObDereferenceObject(FileObject);
470         return Status;
471     }
472 
473     /* If caller wants device unique ID */
474     if (UniqueId)
475     {
476         /* Prepare buffer to probe length */
477         Id = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID));
478         if (!Id)
479         {
480             ObDereferenceObject(DeviceObject);
481             ObDereferenceObject(FileObject);
482             return STATUS_INSUFFICIENT_RESOURCES;
483         }
484 
485         /* Query unique ID length */
486         Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID,
487                                              DeviceObject,
488                                              NULL,
489                                              0,
490                                              Id,
491                                              sizeof(MOUNTDEV_UNIQUE_ID),
492                                              FileObject);
493         /* Retry with appropriate length */
494         if (Status == STATUS_BUFFER_OVERFLOW)
495         {
496             Size = Id->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID);
497 
498             FreePool(Id);
499 
500             /* Allocate the correct buffer */
501             Id = AllocatePool(Size);
502             if (!Id)
503             {
504                 ObDereferenceObject(DeviceObject);
505                 ObDereferenceObject(FileObject);
506                 return STATUS_INSUFFICIENT_RESOURCES;
507             }
508 
509             /* Query unique ID */
510             Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID,
511                                                  DeviceObject,
512                                                  NULL,
513                                                  0,
514                                                  Id,
515                                                  Size,
516                                                  FileObject);
517         }
518 
519         /* Hands back unique ID */
520         if (NT_SUCCESS(Status))
521         {
522             *UniqueId = Id;
523         }
524         else
525         {
526             /* In case of failure, also free the rest */
527             FreePool(Id);
528             if (DeviceName->Length)
529                 FreePool(DeviceName->Buffer);
530 
531             ObDereferenceObject(DeviceObject);
532             ObDereferenceObject(FileObject);
533             return Status;
534         }
535     }
536 
537     /* If user wants to know about GUID */
538     if (HasGuid)
539     {
540         /* Query device stable GUID */
541         NTSTATUS IntStatus;
542         IntStatus = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_STABLE_GUID,
543                                                 DeviceObject,
544                                                 NULL,
545                                                 0,
546                                                 StableGuid,
547                                                 sizeof(GUID),
548                                                 FileObject);
549         *HasGuid = NT_SUCCESS(IntStatus);
550     }
551 
552     ObDereferenceObject(DeviceObject);
553     ObDereferenceObject(FileObject);
554     return Status;
555 }
556 
557 /*
558  * @implemented
559  */
560 NTSTATUS
561 FindDeviceInfo(IN PDEVICE_EXTENSION DeviceExtension,
562                IN PUNICODE_STRING SymbolicName,
563                IN BOOLEAN DeviceNameGiven,
564                OUT PDEVICE_INFORMATION * DeviceInformation)
565 {
566     NTSTATUS Status;
567     PLIST_ENTRY NextEntry;
568     UNICODE_STRING DeviceName;
569     PDEVICE_INFORMATION DeviceInfo = NULL;
570 
571     /* If a device name was given, use it */
572     if (DeviceNameGiven)
573     {
574         DeviceName.Length = SymbolicName->Length;
575         DeviceName.Buffer = SymbolicName->Buffer;
576     }
577     else
578     {
579         /* Otherwise, query it */
580         Status = QueryDeviceInformation(SymbolicName,
581                                         &DeviceName,
582                                         NULL, NULL,
583                                         NULL, NULL,
584                                         NULL, NULL);
585         if (!NT_SUCCESS(Status))
586         {
587             return Status;
588         }
589     }
590 
591     /* Look for device information matching devive */
592     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
593          NextEntry != &(DeviceExtension->DeviceListHead);
594          NextEntry = NextEntry->Flink)
595     {
596         DeviceInfo = CONTAINING_RECORD(NextEntry,
597                                        DEVICE_INFORMATION,
598                                        DeviceListEntry);
599 
600         if (RtlEqualUnicodeString(&DeviceName, &(DeviceInfo->DeviceName), TRUE))
601         {
602             break;
603         }
604     }
605 
606     /* Release our buffer if required */
607     if (!DeviceNameGiven)
608     {
609         FreePool(DeviceName.Buffer);
610     }
611 
612     /* Return found information */
613     if (NextEntry == &(DeviceExtension->DeviceListHead))
614     {
615         return STATUS_OBJECT_NAME_NOT_FOUND;
616     }
617 
618     *DeviceInformation = DeviceInfo;
619     return STATUS_SUCCESS;
620 }
621 
622 /*
623  * @implemented
624  */
625 VOID
626 MountMgrFreeDeadDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation)
627 {
628     FreePool(DeviceInformation->SymbolicName.Buffer);
629     FreePool(DeviceInformation);
630 }
631 
632 /*
633  * @implemented
634  */
635 VOID
636 MountMgrFreeMountedDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation)
637 {
638     PLIST_ENTRY NextEntry;
639     PSYMLINK_INFORMATION SymLink;
640     PUNIQUE_ID_REPLICATE UniqueId;
641     PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
642 
643     /* Purge symbolic links list */
644     while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
645     {
646         NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead));
647         SymLink = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
648 
649         GlobalDeleteSymbolicLink(&(SymLink->Name));
650         FreePool(SymLink->Name.Buffer);
651     }
652 
653     /* Purge replicated unique IDs list */
654     while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead)))
655     {
656         NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead));
657         UniqueId = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
658 
659         FreePool(UniqueId->UniqueId);
660         FreePool(UniqueId);
661     }
662 
663     while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead)))
664     {
665         NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead));
666         AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
667 
668         FreePool(AssociatedDevice->String.Buffer);
669         FreePool(AssociatedDevice);
670     }
671 
672     /* Free the rest of the buffers */
673     FreePool(DeviceInformation->SymbolicName.Buffer);
674     if (DeviceInformation->KeepLinks)
675     {
676         FreePool(DeviceInformation->UniqueId);
677     }
678     FreePool(DeviceInformation->DeviceName.Buffer);
679 
680     /* Finally, stop waiting for notifications for this device */
681     if (DeviceInformation->TargetDeviceNotificationEntry)
682     {
683         IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry);
684     }
685 }
686 
687 /*
688  * @implemented
689  */
690 VOID
691 MountMgrFreeSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation)
692 {
693     PLIST_ENTRY NextEntry;
694     PSYMLINK_INFORMATION SymlinkInformation;
695 
696     /* For all the saved links */
697     while (!IsListEmpty(&(SavedLinkInformation->SymbolicLinksListHead)))
698     {
699         NextEntry = RemoveHeadList(&(SavedLinkInformation->SymbolicLinksListHead));
700         SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
701 
702         /* Remove from system & free */
703         GlobalDeleteSymbolicLink(&(SymlinkInformation->Name));
704         FreePool(SymlinkInformation->Name.Buffer);
705         FreePool(SymlinkInformation);
706     }
707 
708     /* And free unique ID & entry */
709     FreePool(SavedLinkInformation->UniqueId);
710     FreePool(SavedLinkInformation);
711 }
712 
713 
714 /*
715  * @implemented
716  */
717 VOID
718 NTAPI
719 MountMgrUnload(IN PDRIVER_OBJECT DriverObject)
720 {
721     PLIST_ENTRY NextEntry;
722     PUNIQUE_ID_WORK_ITEM WorkItem;
723     PDEVICE_EXTENSION DeviceExtension;
724     PDEVICE_INFORMATION DeviceInformation;
725     PSAVED_LINK_INFORMATION SavedLinkInformation;
726 
727     UNREFERENCED_PARAMETER(DriverObject);
728 
729     /* Don't get notification any longer */
730     IoUnregisterShutdownNotification(gdeviceObject);
731 
732     /* Free registry buffer */
733     DeviceExtension = gdeviceObject->DeviceExtension;
734     if (DeviceExtension->RegistryPath.Buffer)
735     {
736         FreePool(DeviceExtension->RegistryPath.Buffer);
737         DeviceExtension->RegistryPath.Buffer = NULL;
738     }
739 
740     InterlockedExchange(&Unloading, TRUE);
741 
742     KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE);
743 
744     /* Wait for workers to finish */
745     if (InterlockedIncrement(&DeviceExtension->WorkerReferences) > 0)
746     {
747         KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore),
748                            IO_NO_INCREMENT, 1, FALSE);
749 
750         KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
751     }
752     else
753     {
754         InterlockedDecrement(&(DeviceExtension->WorkerReferences));
755     }
756 
757     /* Don't get any notification any longer² */
758     IoUnregisterPlugPlayNotification(DeviceExtension->NotificationEntry);
759 
760     /* Acquire the driver exclusively */
761     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode,
762                           FALSE, NULL);
763 
764     /* Clear offline devices list */
765     while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
766     {
767         NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead));
768         DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
769         MountMgrFreeDeadDeviceInfo(DeviceInformation);
770     }
771 
772     /* Clear saved links list */
773     while (!IsListEmpty(&(DeviceExtension->SavedLinksListHead)))
774     {
775         NextEntry = RemoveHeadList(&(DeviceExtension->SavedLinksListHead));
776         SavedLinkInformation = CONTAINING_RECORD(NextEntry, SAVED_LINK_INFORMATION, SavedLinksListEntry);
777         MountMgrFreeSavedLink(SavedLinkInformation);
778     }
779 
780     /* Clear workers list */
781     while (!IsListEmpty(&(DeviceExtension->UniqueIdWorkerItemListHead)))
782     {
783         NextEntry = RemoveHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead));
784         WorkItem = CONTAINING_RECORD(NextEntry, UNIQUE_ID_WORK_ITEM, UniqueIdWorkerItemListEntry);
785 
786         KeClearEvent(&UnloadEvent);
787         WorkItem->Event = &UnloadEvent;
788 
789         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT,
790                            1, FALSE);
791 
792         IoCancelIrp(WorkItem->Irp);
793         KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
794 
795         IoFreeIrp(WorkItem->Irp);
796         FreePool(WorkItem->DeviceName.Buffer);
797         FreePool(WorkItem->IrpBuffer);
798         FreePool(WorkItem);
799 
800         KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode,
801                               FALSE, NULL);
802     }
803 
804     /* If we have drive letter data, release */
805     if (DeviceExtension->DriveLetterData)
806     {
807         FreePool(DeviceExtension->DriveLetterData);
808         DeviceExtension->DriveLetterData = NULL;
809     }
810 
811     /* Release driver & quit */
812     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
813 
814     GlobalDeleteSymbolicLink(&DosDevicesMount);
815     IoDeleteDevice(gdeviceObject);
816 }
817 
818 /**
819  * @brief   Retrieves the "NoAutoMount" setting.
820  * @return  TRUE if AutoMount is disabled; FALSE if AutoMount is enabled.
821  **/
822 CODE_SEG("INIT")
823 BOOLEAN
824 MountmgrReadNoAutoMount(
825     _In_ PUNICODE_STRING RegistryPath)
826 {
827     NTSTATUS Status;
828     ULONG Result, Default = 0;
829     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
830 
831     /* Retrieve data from registry */
832     RtlZeroMemory(QueryTable, sizeof(QueryTable));
833     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
834     QueryTable[0].Name = L"NoAutoMount";
835     QueryTable[0].EntryContext = &Result;
836     QueryTable[0].DefaultType = REG_DWORD;
837     QueryTable[0].DefaultData = &Default;
838     QueryTable[0].DefaultLength = sizeof(Default);
839 
840     Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
841                                     RegistryPath->Buffer,
842                                     QueryTable,
843                                     NULL,
844                                     NULL);
845     if (!NT_SUCCESS(Status))
846         Result = Default;
847 
848     return (Result != 0);
849 }
850 
851 /*
852  * @implemented
853  */
854 NTSTATUS
855 MountMgrMountedDeviceArrival(IN PDEVICE_EXTENSION DeviceExtension,
856                              IN PUNICODE_STRING SymbolicName,
857                              IN BOOLEAN ManuallyRegistered)
858 {
859     WCHAR Letter;
860     GUID StableGuid;
861     HANDLE LinkHandle;
862     ULONG SymLinkCount, i;
863     PLIST_ENTRY NextEntry;
864     PUNICODE_STRING SymLinks;
865     NTSTATUS Status, IntStatus;
866     OBJECT_ATTRIBUTES ObjectAttributes;
867     PSYMLINK_INFORMATION SymlinkInformation;
868     PMOUNTDEV_UNIQUE_ID UniqueId, NewUniqueId;
869     PSAVED_LINK_INFORMATION SavedLinkInformation;
870     PDEVICE_INFORMATION DeviceInformation, CurrentDevice;
871     WCHAR CSymLinkBuffer[RTL_NUMBER_OF(Cunc)], LinkTargetBuffer[MAX_PATH];
872     UNICODE_STRING TargetDeviceName, SuggestedLinkName, DeviceName, VolumeName, DriveLetter, LinkTarget, CSymLink;
873     BOOLEAN HasGuid, HasGptDriveLetter, IsFT, UseOnlyIfThereAreNoOtherLinks;
874     BOOLEAN IsDrvLetter, IsOff, IsVolumeName, SetOnline;
875 
876     /* New device = new structure to represent it */
877     DeviceInformation = AllocatePool(sizeof(DEVICE_INFORMATION));
878     if (!DeviceInformation)
879     {
880         return STATUS_INSUFFICIENT_RESOURCES;
881     }
882 
883     /* Initialise device structure */
884     RtlZeroMemory(DeviceInformation, sizeof(DEVICE_INFORMATION));
885     InitializeListHead(&(DeviceInformation->SymbolicLinksListHead));
886     InitializeListHead(&(DeviceInformation->ReplicatedUniqueIdsListHead));
887     InitializeListHead(&(DeviceInformation->AssociatedDevicesHead));
888     DeviceInformation->SymbolicName.Length = SymbolicName->Length;
889     DeviceInformation->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(UNICODE_NULL);
890     DeviceInformation->SymbolicName.Buffer = AllocatePool(DeviceInformation->SymbolicName.MaximumLength);
891     if (!DeviceInformation->SymbolicName.Buffer)
892     {
893         FreePool(DeviceInformation);
894         return STATUS_INSUFFICIENT_RESOURCES;
895     }
896 
897     /* Copy symbolic name */
898     RtlCopyMemory(DeviceInformation->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length);
899     DeviceInformation->SymbolicName.Buffer[DeviceInformation->SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
900     DeviceInformation->ManuallyRegistered = ManuallyRegistered;
901     DeviceInformation->DeviceExtension = DeviceExtension;
902 
903     /* Query as much data as possible about device */
904     Status = QueryDeviceInformation(SymbolicName,
905                                     &TargetDeviceName,
906                                     &UniqueId,
907                                     &(DeviceInformation->Removable),
908                                     &HasGptDriveLetter,
909                                     &HasGuid,
910                                     &StableGuid,
911                                     &IsFT);
912     if (!NT_SUCCESS(Status))
913     {
914         KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
915 
916         for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink;
917              NextEntry != &(DeviceExtension->OfflineDeviceListHead);
918              NextEntry = NextEntry->Flink)
919         {
920             CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
921 
922             if (RtlEqualUnicodeString(&(DeviceInformation->SymbolicName), &(CurrentDevice->SymbolicName), TRUE))
923             {
924                 break;
925             }
926         }
927 
928         if (NextEntry != &(DeviceExtension->OfflineDeviceListHead))
929         {
930             MountMgrFreeDeadDeviceInfo(DeviceInformation);
931         }
932         else
933         {
934             InsertTailList(&(DeviceExtension->OfflineDeviceListHead), &(DeviceInformation->DeviceListEntry));
935         }
936 
937         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
938 
939         return Status;
940     }
941 
942     /* Save gathered data */
943     DeviceInformation->UniqueId = UniqueId;
944     DeviceInformation->DeviceName = TargetDeviceName;
945     DeviceInformation->KeepLinks = FALSE;
946 
947     /* If we found system partition, mark it */
948     if (DeviceExtension->DriveLetterData && UniqueId->UniqueIdLength == DeviceExtension->DriveLetterData->UniqueIdLength)
949     {
950         if (RtlCompareMemory(UniqueId->UniqueId, DeviceExtension->DriveLetterData->UniqueId, UniqueId->UniqueIdLength)
951             == UniqueId->UniqueIdLength)
952         {
953             IoSetSystemPartition(&TargetDeviceName);
954         }
955     }
956 
957     /* Check suggested link name */
958     Status = QuerySuggestedLinkName(&(DeviceInformation->SymbolicName),
959                                     &SuggestedLinkName,
960                                     &UseOnlyIfThereAreNoOtherLinks);
961     if (!NT_SUCCESS(Status))
962     {
963         SuggestedLinkName.Buffer = NULL;
964     }
965 
966     /* If it's OK, set it and save its letter (if any) */
967     if (SuggestedLinkName.Buffer && IsDriveLetter(&SuggestedLinkName))
968     {
969         DeviceInformation->SuggestedDriveLetter = (UCHAR)SuggestedLinkName.Buffer[LETTER_POSITION];
970     }
971 
972     /* Acquire driver exclusively */
973     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
974 
975     /* Check if we already have device in to prevent double registration */
976     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
977          NextEntry != &(DeviceExtension->DeviceListHead);
978          NextEntry = NextEntry->Flink)
979     {
980         CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
981 
982         if (RtlEqualUnicodeString(&(CurrentDevice->DeviceName), &TargetDeviceName, TRUE))
983         {
984             break;
985         }
986     }
987 
988     /* If we found it, clear ours, and return success, all correct */
989     if (NextEntry != &(DeviceExtension->DeviceListHead))
990     {
991         if (SuggestedLinkName.Buffer)
992         {
993             FreePool(SuggestedLinkName.Buffer);
994         }
995 
996         FreePool(UniqueId);
997         FreePool(TargetDeviceName.Buffer);
998         FreePool(DeviceInformation->DeviceName.Buffer);
999         FreePool(DeviceInformation);
1000 
1001         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1002 
1003         return STATUS_SUCCESS;
1004     }
1005 
1006     /* Check if there are symlinks associated with our device in registry */
1007     Status = QuerySymbolicLinkNamesFromStorage(DeviceExtension,
1008                                                DeviceInformation,
1009                                                (SuggestedLinkName.Buffer) ? &SuggestedLinkName : NULL,
1010                                                UseOnlyIfThereAreNoOtherLinks,
1011                                                &SymLinks,
1012                                                &SymLinkCount,
1013                                                HasGuid,
1014                                                &StableGuid);
1015 
1016     /* If our device is a CD-ROM */
1017     if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE))
1018     {
1019         LinkTarget.Length = 0;
1020         LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
1021         LinkTarget.Buffer = LinkTargetBuffer;
1022 
1023         RtlCopyMemory(CSymLinkBuffer, Cunc, sizeof(Cunc));
1024         RtlInitUnicodeString(&CSymLink, CSymLinkBuffer);
1025 
1026         /* Start checking all letters that could have been associated */
1027         for (Letter = L'D'; Letter <= L'Z'; Letter++)
1028         {
1029             CSymLink.Buffer[Cunc_LETTER_POSITION] = Letter;
1030 
1031             InitializeObjectAttributes(&ObjectAttributes,
1032                                        &CSymLink,
1033                                        OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1034                                        NULL,
1035                                        NULL);
1036 
1037             /* Try to open the associated symlink */
1038             Status = ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
1039             if (!NT_SUCCESS(Status))
1040             {
1041                 continue;
1042             }
1043 
1044             /* And query its target */
1045             Status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, NULL);
1046             ZwClose(LinkHandle);
1047 
1048             if (!NT_SUCCESS(Status))
1049             {
1050                 continue;
1051             }
1052 
1053             IntStatus = STATUS_UNSUCCESSFUL;
1054             if (!RtlEqualUnicodeString(&LinkTarget, &DeviceInformation->DeviceName, FALSE))
1055             {
1056                 continue;
1057             }
1058 
1059             /* This link is matching our device, whereas it's not supposed to have any
1060              * symlink associated.
1061              * Delete it
1062              */
1063             if (!SymLinkCount)
1064             {
1065                 IoDeleteSymbolicLink(&CSymLink);
1066                 continue;
1067             }
1068 
1069             /* Now, for all the symlinks, check for ours */
1070             for (i = 0; i < SymLinkCount; i++)
1071             {
1072                 if (IsDriveLetter(&(SymLinks[i])))
1073                 {
1074                     /* If it exists, that's correct */
1075                     if (SymLinks[i].Buffer[LETTER_POSITION] == Letter)
1076                     {
1077                         IntStatus = STATUS_SUCCESS;
1078                     }
1079                 }
1080             }
1081 
1082             /* Useless link, delete it */
1083             if (IntStatus == STATUS_UNSUCCESSFUL)
1084             {
1085                 IoDeleteSymbolicLink(&CSymLink);
1086             }
1087         }
1088     }
1089 
1090     /* Suggested name is no longer required */
1091     if (SuggestedLinkName.Buffer)
1092     {
1093         FreePool(SuggestedLinkName.Buffer);
1094     }
1095 
1096     /* If if failed, ensure we don't take symlinks into account */
1097     if (!NT_SUCCESS(Status))
1098     {
1099         SymLinks = NULL;
1100         SymLinkCount = 0;
1101     }
1102 
1103     /* Now we queried them, remove the symlinks */
1104     SavedLinkInformation = RemoveSavedLinks(DeviceExtension, UniqueId);
1105 
1106     IsDrvLetter = FALSE;
1107     IsOff = FALSE;
1108     IsVolumeName = FALSE;
1109     /* For all the symlinks */
1110     for (i = 0; i < SymLinkCount; i++)
1111     {
1112         /* Check if our device is a volume */
1113         if (MOUNTMGR_IS_VOLUME_NAME(&(SymLinks[i])))
1114         {
1115             IsVolumeName = TRUE;
1116         }
1117         /* If it has a drive letter */
1118         else if (IsDriveLetter(&(SymLinks[i])))
1119         {
1120             if (IsDrvLetter)
1121             {
1122                 DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId);
1123                 continue;
1124             }
1125             else
1126             {
1127                 IsDrvLetter = TRUE;
1128             }
1129         }
1130 
1131         /* And recreate the symlink to our device */
1132         Status = GlobalCreateSymbolicLink(&(SymLinks[i]), &TargetDeviceName);
1133         if (!NT_SUCCESS(Status))
1134         {
1135             BOOLEAN LinkError = TRUE;
1136 
1137             if ((SavedLinkInformation && !RedirectSavedLink(SavedLinkInformation, &(SymLinks[i]), &TargetDeviceName)) ||
1138                 !SavedLinkInformation)
1139             {
1140                 Status = QueryDeviceInformation(&(SymLinks[i]), &DeviceName, NULL, NULL, NULL, NULL, NULL, NULL);
1141                 if (NT_SUCCESS(Status))
1142                 {
1143                     LinkError = RtlEqualUnicodeString(&TargetDeviceName, &DeviceName, TRUE);
1144                     FreePool(DeviceName.Buffer);
1145                 }
1146 
1147                 if (!LinkError)
1148                 {
1149                     if (IsDriveLetter(&(SymLinks[i])))
1150                     {
1151                         IsDrvLetter = FALSE;
1152                         DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId);
1153                     }
1154 
1155                     FreePool(SymLinks[i].Buffer);
1156                     continue;
1157                 }
1158             }
1159         }
1160 
1161         /* Check if was offline */
1162         if (IsOffline(&(SymLinks[i])))
1163         {
1164             IsOff = TRUE;
1165         }
1166 
1167         /* Finally, associate this symlink with the device */
1168         SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
1169         if (!SymlinkInformation)
1170         {
1171             GlobalDeleteSymbolicLink(&(SymLinks[i]));
1172             FreePool(SymLinks[i].Buffer);
1173             continue;
1174         }
1175 
1176         SymlinkInformation->Name = SymLinks[i];
1177         SymlinkInformation->Online = TRUE;
1178 
1179         InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
1180                        &(SymlinkInformation->SymbolicLinksListEntry));
1181     }
1182 
1183     /* Now, for all the recreated symlinks, notify their recreation */
1184     for (NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
1185          NextEntry != &(DeviceInformation->SymbolicLinksListHead);
1186          NextEntry = NextEntry->Flink)
1187     {
1188         SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1189 
1190         SendLinkCreated(&(SymlinkInformation->Name));
1191     }
1192 
1193     /* If we had saved links, it's time to free them */
1194     if (SavedLinkInformation)
1195     {
1196         MountMgrFreeSavedLink(SavedLinkInformation);
1197     }
1198 
1199     /* If our device doesn't have a volume name */
1200     if (!IsVolumeName)
1201     {
1202         /* It's time to create one */
1203         Status = CreateNewVolumeName(&VolumeName, NULL);
1204         if (NT_SUCCESS(Status))
1205         {
1206             /* Write it to global database */
1207             RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1208                                   DatabasePath,
1209                                   VolumeName.Buffer,
1210                                   REG_BINARY,
1211                                   UniqueId->UniqueId,
1212                                   UniqueId->UniqueIdLength);
1213 
1214             /* And create the symlink */
1215             GlobalCreateSymbolicLink(&VolumeName, &TargetDeviceName);
1216 
1217             SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
1218             if (!SymlinkInformation)
1219             {
1220                 FreePool(VolumeName.Buffer);
1221             }
1222             /* Finally, associate it with the device and notify creation */
1223             else
1224             {
1225                 SymlinkInformation->Name = VolumeName;
1226                 SymlinkInformation->Online = TRUE;
1227                 InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
1228                                &(SymlinkInformation->SymbolicLinksListEntry));
1229 
1230                 SendLinkCreated(&VolumeName);
1231             }
1232         }
1233     }
1234 
1235     /* If we found a drive letter, then, ignore the suggested one */
1236     if (IsDrvLetter)
1237     {
1238         DeviceInformation->SuggestedDriveLetter = 0;
1239     }
1240     /* Else, it's time to set up one */
1241     else if ((!DeviceExtension->NoAutoMount || DeviceInformation->Removable) &&
1242              DeviceExtension->AutomaticDriveLetter &&
1243              (HasGptDriveLetter || DeviceInformation->SuggestedDriveLetter) &&
1244              !HasNoDriveLetterEntry(UniqueId))
1245     {
1246         /* Create a new drive letter */
1247         Status = CreateNewDriveLetterName(&DriveLetter, &TargetDeviceName,
1248                                           DeviceInformation->SuggestedDriveLetter,
1249                                           NULL);
1250         if (!NT_SUCCESS(Status))
1251         {
1252             CreateNoDriveLetterEntry(UniqueId);
1253         }
1254         else
1255         {
1256             /* Save it to global database */
1257             RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1258                                   DatabasePath,
1259                                   DriveLetter.Buffer,
1260                                   REG_BINARY,
1261                                   UniqueId->UniqueId,
1262                                   UniqueId->UniqueIdLength);
1263 
1264             /* Associate it with the device and notify creation */
1265             SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
1266             if (!SymlinkInformation)
1267             {
1268                 FreePool(DriveLetter.Buffer);
1269             }
1270             else
1271             {
1272                 SymlinkInformation->Name = DriveLetter;
1273                 SymlinkInformation->Online = TRUE;
1274                 InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
1275                                &(SymlinkInformation->SymbolicLinksListEntry));
1276 
1277                 SendLinkCreated(&DriveLetter);
1278             }
1279         }
1280     }
1281 
1282     /* If that's a PnP device, register for notifications */
1283     if (!ManuallyRegistered)
1284     {
1285         RegisterForTargetDeviceNotification(DeviceExtension, DeviceInformation);
1286     }
1287 
1288     /* Finally, insert the device into our devices list */
1289     InsertTailList(&(DeviceExtension->DeviceListHead), &(DeviceInformation->DeviceListEntry));
1290 
1291     /* Copy device unique ID */
1292     NewUniqueId = AllocatePool(UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1293     if (NewUniqueId)
1294     {
1295         NewUniqueId->UniqueIdLength = UniqueId->UniqueIdLength;
1296         RtlCopyMemory(NewUniqueId->UniqueId, UniqueId->UniqueId, UniqueId->UniqueIdLength);
1297     }
1298 
1299     /* Skip online notifications if the device is offline or a FT volume */
1300     if (IsOff || IsFT)
1301         DeviceInformation->SkipNotifications = TRUE;
1302 
1303     /* If automount is enabled or the device was already mounted, send now
1304      * the online notification if needed; otherwise, defer its posting */
1305     if (!DeviceExtension->NoAutoMount || IsDrvLetter)
1306         SetOnline = !DeviceInformation->SkipNotifications;
1307     else
1308         SetOnline = FALSE;
1309 
1310     /* Finally, release the exclusive lock */
1311     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1312 
1313     /* Set the device online now if necessary */
1314     if (SetOnline)
1315         SendOnlineNotification(SymbolicName);
1316 
1317     /* If we had symlinks (from storage), free them */
1318     if (SymLinks)
1319     {
1320         FreePool(SymLinks);
1321     }
1322 
1323     /* Notify about unique id change */
1324     if (NewUniqueId)
1325     {
1326         IssueUniqueIdChangeNotify(DeviceExtension, SymbolicName, NewUniqueId);
1327         FreePool(NewUniqueId);
1328     }
1329 
1330     /* If this drive was set to have a drive letter automatically
1331      * Now it's back, local databases sync will be required
1332      */
1333     if (DeviceExtension->AutomaticDriveLetter)
1334     {
1335         KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
1336 
1337         ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
1338 
1339         NextEntry = DeviceExtension->DeviceListHead.Flink;
1340         CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1341         while (CurrentDevice != DeviceInformation)
1342         {
1343             if (!CurrentDevice->NoDatabase)
1344             {
1345                 ReconcileThisDatabaseWithMaster(DeviceExtension, CurrentDevice);
1346             }
1347 
1348             NextEntry = NextEntry->Flink;
1349             CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1350         }
1351 
1352         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1353     }
1354 
1355     return STATUS_SUCCESS;
1356 }
1357 
1358 /*
1359  * @implemented
1360  */
1361 VOID
1362 MountMgrMountedDeviceRemoval(IN PDEVICE_EXTENSION DeviceExtension,
1363                              IN PUNICODE_STRING DeviceName)
1364 {
1365     PLIST_ENTRY NextEntry, DeviceEntry;
1366     PUNIQUE_ID_REPLICATE UniqueIdReplicate;
1367     PSYMLINK_INFORMATION SymlinkInformation;
1368     PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
1369     PSAVED_LINK_INFORMATION SavedLinkInformation = NULL;
1370     PDEVICE_INFORMATION DeviceInformation, CurrentDevice;
1371 
1372     /* Acquire device exclusively */
1373     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
1374 
1375     /* Look for the leaving device */
1376     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1377          NextEntry != &(DeviceExtension->DeviceListHead);
1378          NextEntry = NextEntry->Flink)
1379     {
1380         DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1381 
1382         if (!RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE))
1383         {
1384             break;
1385         }
1386     }
1387 
1388     /* If we found it */
1389     if (NextEntry != &(DeviceExtension->DeviceListHead))
1390     {
1391         /* If it's asked to keep links, then, prepare to save them */
1392         if (DeviceInformation->KeepLinks)
1393         {
1394             SavedLinkInformation = AllocatePool(sizeof(SAVED_LINK_INFORMATION));
1395             if (!SavedLinkInformation)
1396             {
1397                 DeviceInformation->KeepLinks = FALSE;
1398             }
1399         }
1400 
1401         /* If it's possible (and asked), start to save them */
1402         if (DeviceInformation->KeepLinks)
1403         {
1404             InsertTailList(&(DeviceExtension->SavedLinksListHead), &(SavedLinkInformation->SavedLinksListEntry));
1405             InitializeListHead(&(SavedLinkInformation->SymbolicLinksListHead));
1406             SavedLinkInformation->UniqueId = DeviceInformation->UniqueId;
1407         }
1408 
1409         /* For all the symlinks */
1410         while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
1411         {
1412             NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead));
1413             SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1414 
1415             /* If we have to, save the link */
1416             if (DeviceInformation->KeepLinks)
1417             {
1418                 InsertTailList(&(SavedLinkInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry));
1419             }
1420             /* Otherwise, just release it */
1421             else
1422             {
1423                 GlobalDeleteSymbolicLink(&(SymlinkInformation->Name));
1424                 FreePool(SymlinkInformation->Name.Buffer);
1425                 FreePool(SymlinkInformation);
1426             }
1427         }
1428 
1429         /* Free all the replicated unique IDs */
1430         while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead)))
1431         {
1432             NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead));
1433             UniqueIdReplicate = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
1434 
1435 
1436             FreePool(UniqueIdReplicate->UniqueId);
1437             FreePool(UniqueIdReplicate);
1438         }
1439 
1440         while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead)))
1441         {
1442             NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead));
1443             AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1444 
1445             DeviceInformation->NoDatabase = TRUE;
1446             FreePool(AssociatedDevice->String.Buffer);
1447             FreePool(AssociatedDevice);
1448         }
1449 
1450         /* Remove device from the device list */
1451         RemoveEntryList(&(DeviceInformation->DeviceListEntry));
1452 
1453         /* If there are still devices, check if some were associated with ours */
1454         if (!IsListEmpty(&(DeviceInformation->DeviceListEntry)))
1455         {
1456             for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1457                  NextEntry != &(DeviceExtension->DeviceListHead);
1458                  NextEntry = NextEntry->Flink)
1459             {
1460                 CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1461 
1462                 /* And then, remove them */
1463                 DeviceEntry = CurrentDevice->AssociatedDevicesHead.Flink;
1464                 while (DeviceEntry != &(CurrentDevice->AssociatedDevicesHead))
1465                 {
1466                     AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1467                     DeviceEntry = DeviceEntry->Flink;
1468 
1469                     if (AssociatedDevice->DeviceInformation != DeviceInformation)
1470                     {
1471                         continue;
1472                     }
1473 
1474                     RemoveEntryList(&(AssociatedDevice->AssociatedDevicesEntry));
1475                     FreePool(AssociatedDevice->String.Buffer);
1476                     FreePool(AssociatedDevice);
1477                 }
1478             }
1479         }
1480 
1481         /* Finally, clean up device name, symbolic name */
1482         FreePool(DeviceInformation->SymbolicName.Buffer);
1483         if (!DeviceInformation->KeepLinks)
1484         {
1485             FreePool(DeviceInformation->UniqueId);
1486         }
1487         FreePool(DeviceInformation->DeviceName.Buffer);
1488 
1489         /* Unregister notifications */
1490         if (DeviceInformation->TargetDeviceNotificationEntry)
1491         {
1492             IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry);
1493         }
1494 
1495         /*  And leave */
1496         FreePool(DeviceInformation);
1497     }
1498     else
1499     {
1500         /* We didn't find device, perhaps because it was offline */
1501         for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink;
1502              NextEntry != &(DeviceExtension->OfflineDeviceListHead);
1503              NextEntry = NextEntry->Flink)
1504         {
1505             DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1506 
1507             /* It was, remove it */
1508             if (RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE) == 0)
1509             {
1510                 RemoveEntryList(&(DeviceInformation->DeviceListEntry));
1511                 MountMgrFreeDeadDeviceInfo(DeviceInformation);
1512                 break;
1513             }
1514         }
1515     }
1516 
1517     /* Release driver */
1518     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1519 }
1520 
1521 /*
1522  * @implemented
1523  */
1524 NTSTATUS
1525 NTAPI
1526 MountMgrMountedDeviceNotification(IN PVOID NotificationStructure,
1527                                   IN PVOID Context)
1528 {
1529     BOOLEAN OldState;
1530     PDEVICE_EXTENSION DeviceExtension;
1531     PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification;
1532 
1533     /* Notification for a device arrived */
1534     /* Disable hard errors */
1535     OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
1536     PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
1537 
1538     DeviceExtension = Context;
1539     Notification = NotificationStructure;
1540 
1541     /* Dispatch according to the event */
1542     if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_ARRIVAL))
1543     {
1544         MountMgrMountedDeviceArrival(DeviceExtension, Notification->SymbolicLinkName, FALSE);
1545     }
1546     else if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_REMOVAL))
1547     {
1548         MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName);
1549     }
1550 
1551     /* Reset hard errors */
1552     PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
1553 
1554     return STATUS_SUCCESS;
1555 }
1556 
1557 /*
1558  * @implemented
1559  */
1560 NTSTATUS
1561 NTAPI
1562 MountMgrCreateClose(IN PDEVICE_OBJECT DeviceObject,
1563                     IN PIRP Irp)
1564 {
1565     PIO_STACK_LOCATION Stack;
1566     NTSTATUS Status = STATUS_SUCCESS;
1567 
1568     UNREFERENCED_PARAMETER(DeviceObject);
1569 
1570     Stack = IoGetCurrentIrpStackLocation(Irp);
1571 
1572     /* Allow driver opening for communication
1573      * as long as it's not taken for a directory
1574      */
1575     if (Stack->MajorFunction == IRP_MJ_CREATE &&
1576         Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
1577     {
1578         Status = STATUS_NOT_A_DIRECTORY;
1579     }
1580 
1581     Irp->IoStatus.Status = Status;
1582     Irp->IoStatus.Information = 0;
1583     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1584     return Status;
1585 }
1586 
1587 /*
1588  * @implemented
1589  */
1590 VOID
1591 NTAPI
1592 MountMgrCancel(IN PDEVICE_OBJECT DeviceObject,
1593                IN PIRP Irp)
1594 {
1595     UNREFERENCED_PARAMETER(DeviceObject);
1596 
1597     RemoveEntryList(&(Irp->Tail.Overlay.ListEntry));
1598 
1599     IoReleaseCancelSpinLock(Irp->CancelIrql);
1600 
1601     Irp->IoStatus.Information = 0;
1602     Irp->IoStatus.Status = STATUS_CANCELLED;
1603     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1604 }
1605 
1606 /*
1607  * @implemented
1608  */
1609 NTSTATUS
1610 NTAPI
1611 MountMgrCleanup(IN PDEVICE_OBJECT DeviceObject,
1612                 IN PIRP Irp)
1613 {
1614     PIRP ListIrp;
1615     KIRQL OldIrql;
1616     PLIST_ENTRY NextEntry;
1617     PFILE_OBJECT FileObject;
1618     PIO_STACK_LOCATION Stack;
1619     PDEVICE_EXTENSION DeviceExtension;
1620 
1621     DeviceExtension = DeviceObject->DeviceExtension;
1622     Stack = IoGetCurrentIrpStackLocation(Irp);
1623     FileObject = Stack->FileObject;
1624 
1625     IoAcquireCancelSpinLock(&OldIrql);
1626 
1627     /* If IRP list if empty, it's OK */
1628     if (IsListEmpty(&(DeviceExtension->IrpListHead)))
1629     {
1630         IoReleaseCancelSpinLock(OldIrql);
1631 
1632         Irp->IoStatus.Status = STATUS_SUCCESS;
1633         Irp->IoStatus.Information = 0;
1634         IoCompleteRequest(Irp, IO_NO_INCREMENT);
1635 
1636         return STATUS_SUCCESS;
1637     }
1638 
1639     /* Otherwise, cancel all the IRPs */
1640     NextEntry = DeviceExtension->IrpListHead.Flink;
1641     do
1642     {
1643         ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
1644         if (IoGetCurrentIrpStackLocation(ListIrp)->FileObject == FileObject)
1645         {
1646             ListIrp->Cancel = TRUE;
1647             ListIrp->CancelIrql = OldIrql;
1648             ListIrp->CancelRoutine = NULL;
1649             MountMgrCancel(DeviceObject, ListIrp);
1650 
1651             IoAcquireCancelSpinLock(&OldIrql);
1652         }
1653 
1654         NextEntry = NextEntry->Flink;
1655     }
1656     while (NextEntry != &(DeviceExtension->IrpListHead));
1657 
1658     IoReleaseCancelSpinLock(OldIrql);
1659 
1660     Irp->IoStatus.Status = STATUS_SUCCESS;
1661     Irp->IoStatus.Information = 0;
1662     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1663 
1664     return STATUS_SUCCESS;
1665 }
1666 
1667 /*
1668  * @implemented
1669  */
1670 NTSTATUS
1671 NTAPI
1672 MountMgrShutdown(IN PDEVICE_OBJECT DeviceObject,
1673                  IN PIRP Irp)
1674 {
1675     PDEVICE_EXTENSION DeviceExtension;
1676 
1677     DeviceExtension = DeviceObject->DeviceExtension;
1678 
1679     InterlockedExchange(&Unloading, TRUE);
1680 
1681     KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE);
1682 
1683     /* Wait for workers */
1684     if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)) > 0)
1685     {
1686         KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore),
1687                            IO_NO_INCREMENT,
1688                            1,
1689                            FALSE);
1690         KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
1691     }
1692     else
1693     {
1694         InterlockedDecrement(&(DeviceExtension->WorkerReferences));
1695     }
1696 
1697     Irp->IoStatus.Status = STATUS_SUCCESS;
1698     Irp->IoStatus.Information = 0;
1699     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1700 
1701     return STATUS_SUCCESS;
1702 }
1703 
1704 /* FUNCTIONS ****************************************************************/
1705 
1706 CODE_SEG("INIT")
1707 NTSTATUS
1708 NTAPI
1709 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1710             IN PUNICODE_STRING RegistryPath)
1711 {
1712     NTSTATUS Status;
1713     PDEVICE_OBJECT DeviceObject;
1714     PDEVICE_EXTENSION DeviceExtension;
1715 
1716     RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, DatabasePath);
1717 
1718     Status = IoCreateDevice(DriverObject,
1719                             sizeof(DEVICE_EXTENSION),
1720                             &DeviceMount,
1721                             FILE_DEVICE_NETWORK,
1722                             FILE_DEVICE_SECURE_OPEN,
1723                             FALSE,
1724                             &DeviceObject);
1725     if (!NT_SUCCESS(Status))
1726     {
1727         return Status;
1728     }
1729 
1730     DriverObject->DriverUnload = MountMgrUnload;
1731 
1732     DeviceExtension = DeviceObject->DeviceExtension;
1733     RtlZeroMemory(DeviceExtension, sizeof(DEVICE_EXTENSION));
1734     DeviceExtension->DeviceObject = DeviceObject;
1735     DeviceExtension->DriverObject = DriverObject;
1736 
1737     InitializeListHead(&(DeviceExtension->DeviceListHead));
1738     InitializeListHead(&(DeviceExtension->OfflineDeviceListHead));
1739 
1740     KeInitializeSemaphore(&(DeviceExtension->DeviceLock), 1, 1);
1741     KeInitializeSemaphore(&(DeviceExtension->RemoteDatabaseLock), 1, 1);
1742 
1743     InitializeListHead(&(DeviceExtension->IrpListHead));
1744     DeviceExtension->EpicNumber = 1;
1745 
1746     InitializeListHead(&(DeviceExtension->SavedLinksListHead));
1747 
1748     InitializeListHead(&(DeviceExtension->WorkerQueueListHead));
1749     KeInitializeSemaphore(&(DeviceExtension->WorkerSemaphore), 0, MAXLONG);
1750     DeviceExtension->WorkerReferences = -1;
1751     KeInitializeSpinLock(&(DeviceExtension->WorkerLock));
1752 
1753     InitializeListHead(&(DeviceExtension->UniqueIdWorkerItemListHead));
1754     InitializeListHead(&(DeviceExtension->OnlineNotificationListHead));
1755     DeviceExtension->OnlineNotificationCount = 1;
1756 
1757     DeviceExtension->RegistryPath.Length = RegistryPath->Length;
1758     DeviceExtension->RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR);
1759     DeviceExtension->RegistryPath.Buffer = AllocatePool(DeviceExtension->RegistryPath.MaximumLength);
1760     if (!DeviceExtension->RegistryPath.Buffer)
1761     {
1762         IoDeleteDevice(DeviceObject);
1763         return STATUS_INSUFFICIENT_RESOURCES;
1764     }
1765 
1766     RtlCopyUnicodeString(&(DeviceExtension->RegistryPath), RegistryPath);
1767 
1768     DeviceExtension->NoAutoMount = MountmgrReadNoAutoMount(&(DeviceExtension->RegistryPath));
1769 
1770     GlobalCreateSymbolicLink(&DosDevicesMount, &DeviceMount);
1771 
1772     /* Register for device arrival & removal. Ask to be notified for already
1773      * present devices
1774      */
1775     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
1776                                             PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
1777                                             &MountedDevicesGuid,
1778                                             DriverObject,
1779                                             MountMgrMountedDeviceNotification,
1780                                             DeviceExtension,
1781                                             &(DeviceExtension->NotificationEntry));
1782 
1783     if (!NT_SUCCESS(Status))
1784     {
1785         IoDeleteDevice(DeviceObject);
1786         return Status;
1787     }
1788 
1789     DriverObject->MajorFunction[IRP_MJ_CREATE]         =
1790     DriverObject->MajorFunction[IRP_MJ_CLOSE]          = MountMgrCreateClose;
1791     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MountMgrDeviceControl;
1792     DriverObject->MajorFunction[IRP_MJ_CLEANUP]        = MountMgrCleanup;
1793     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]       = MountMgrShutdown;
1794 
1795     gdeviceObject = DeviceObject;
1796 
1797     Status = IoRegisterShutdownNotification(DeviceObject);
1798     if (!NT_SUCCESS(Status))
1799     {
1800         IoDeleteDevice(DeviceObject);
1801     }
1802 
1803     return Status;
1804 }
1805