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