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