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