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