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