/* * ReactOS kernel * Copyright (C) 2011 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: drivers/filesystem/mountmgr/mountmgr.c * PURPOSE: Mount Manager * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) * Alex Ionescu (alex.ionescu@reactos.org) */ #include "mntmgr.h" #define NDEBUG #include /* FIXME */ GUID MountedDevicesGuid = {0x53F5630D, 0xB6BF, 0x11D0, {0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B}}; PDEVICE_OBJECT gdeviceObject; KEVENT UnloadEvent; LONG Unloading; static const WCHAR Cunc[] = L"\\??\\C:"; #define Cunc_LETTER_POSITION 4 /* * @implemented */ BOOLEAN IsOffline(PUNICODE_STRING SymbolicName) { NTSTATUS Status; ULONG IsOffline, Default; RTL_QUERY_REGISTRY_TABLE QueryTable[2]; /* Prepare to look in the registry to see if * given volume is offline */ RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; QueryTable[0].Name = SymbolicName->Buffer; QueryTable[0].EntryContext = &IsOffline; QueryTable[0].DefaultType = REG_DWORD; QueryTable[0].DefaultLength = sizeof(ULONG); QueryTable[0].DefaultData = &Default; Default = 0; /* Query status */ Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, OfflinePath, QueryTable, NULL, NULL); if (!NT_SUCCESS(Status)) { IsOffline = 0; } return (IsOffline != 0); } /* * @implemented */ BOOLEAN HasDriveLetter(IN PDEVICE_INFORMATION DeviceInformation) { PLIST_ENTRY NextEntry; PSYMLINK_INFORMATION SymlinkInfo; /* Browse all the symlinks to check if there is at least a drive letter */ for (NextEntry = DeviceInformation->SymbolicLinksListHead.Flink; NextEntry != &DeviceInformation->SymbolicLinksListHead; NextEntry = NextEntry->Flink) { SymlinkInfo = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); if (IsDriveLetter(&SymlinkInfo->Name) && SymlinkInfo->Online) { return TRUE; } } return FALSE; } /* * @implemented */ NTSTATUS CreateNewDriveLetterName(OUT PUNICODE_STRING DriveLetter, IN PUNICODE_STRING DeviceName, IN UCHAR Letter, IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL) { NTSTATUS Status = STATUS_UNSUCCESSFUL; /* Allocate a big enough buffer to contain the symbolic link */ DriveLetter->MaximumLength = DosDevices.Length + 3 * sizeof(WCHAR); DriveLetter->Buffer = AllocatePool(DriveLetter->MaximumLength); if (!DriveLetter->Buffer) { return STATUS_INSUFFICIENT_RESOURCES; } /* Copy prefix */ RtlCopyUnicodeString(DriveLetter, &DosDevices); /* Update string to reflect real contents */ DriveLetter->Length = DosDevices.Length + 2 * sizeof(WCHAR); DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR) + 2] = UNICODE_NULL; DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR) + 1] = L':'; /* If caller wants a no drive entry */ if (Letter == (UCHAR)-1) { /* Then, create a no letter entry */ CreateNoDriveLetterEntry(UniqueId); FreePool(DriveLetter->Buffer); return STATUS_UNSUCCESSFUL; } else if (Letter) { /* Use the letter given by the caller */ DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR)] = (WCHAR)Letter; Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName); if (NT_SUCCESS(Status)) { return Status; } } /* If caller didn't provide a letter, let's find one for him */ if (RtlPrefixUnicodeString(&DeviceFloppy, DeviceName, TRUE)) { /* If the device is a floppy, start with letter A */ Letter = 'A'; } else if (RtlPrefixUnicodeString(&DeviceCdRom, DeviceName, TRUE)) { /* If the device is a CD-ROM, start with letter D */ Letter = 'D'; } else { /* Finally, if it's a disk, use C */ Letter = 'C'; } /* Try to affect a letter (up to Z, ofc) until it's possible */ for (; Letter <= 'Z'; Letter++) { DriveLetter->Buffer[DosDevices.Length / sizeof(WCHAR)] = (WCHAR)Letter; Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName); if (NT_SUCCESS(Status)) { DPRINT("Assigned drive %c: to %wZ\n", Letter, DeviceName); return Status; } } /* We failed to allocate a letter */ FreePool(DriveLetter->Buffer); DPRINT("Failed to create a drive letter for %wZ\n", DeviceName); return Status; } /* * @implemented */ NTSTATUS QueryDeviceInformation(IN PUNICODE_STRING SymbolicName, OUT PUNICODE_STRING DeviceName OPTIONAL, OUT PMOUNTDEV_UNIQUE_ID * UniqueId OPTIONAL, OUT PBOOLEAN Removable OPTIONAL, OUT PBOOLEAN GptDriveLetter OPTIONAL, OUT PBOOLEAN HasGuid OPTIONAL, IN OUT LPGUID StableGuid OPTIONAL, OUT PBOOLEAN Valid OPTIONAL) { PIRP Irp; USHORT Size; KEVENT Event; BOOLEAN IsRemovable; PMOUNTDEV_NAME Name; PMOUNTDEV_UNIQUE_ID Id; PFILE_OBJECT FileObject; PIO_STACK_LOCATION Stack; NTSTATUS Status, IntStatus; PDEVICE_OBJECT DeviceObject; IO_STATUS_BLOCK IoStatusBlock; PARTITION_INFORMATION_EX PartitionInfo; STORAGE_DEVICE_NUMBER StorageDeviceNumber; VOLUME_GET_GPT_ATTRIBUTES_INFORMATION GptAttributes; /* Get device associated with the symbolic name */ Status = IoGetDeviceObjectPointer(SymbolicName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); if (!NT_SUCCESS(Status)) { return Status; } /* The associate FO can't have a file name */ if (FileObject->FileName.Length) { ObDereferenceObject(FileObject); return STATUS_OBJECT_NAME_NOT_FOUND; } /* Check if it's removable & return to the user (if asked to) */ IsRemovable = (FileObject->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA); if (Removable) { *Removable = IsRemovable; } /* Get the attached device */ DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); /* If we've been asked for a GPT drive letter */ if (GptDriveLetter) { /* Consider it has one */ *GptDriveLetter = TRUE; if (!IsRemovable) { /* Query the GPT attributes */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_GET_GPT_ATTRIBUTES, DeviceObject, NULL, 0, &GptAttributes, sizeof(GptAttributes), FALSE, &Event, &IoStatusBlock); if (!Irp) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } /* In case of failure, don't fail, that's no vital */ if (!NT_SUCCESS(Status)) { Status = STATUS_SUCCESS; } /* Check if it has a drive letter */ else if (GptAttributes.GptAttributes & GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER) { *GptDriveLetter = FALSE; } } } /* If caller wants to know if there's valid contents */ if (Valid) { /* Suppose it's not OK */ *Valid = FALSE; if (!IsRemovable) { /* Query partitions information */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX, DeviceObject, NULL, 0, &PartitionInfo, sizeof(PartitionInfo), FALSE, &Event, &IoStatusBlock); if (!Irp) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } /* Once again here, failure isn't major */ if (!NT_SUCCESS(Status)) { Status = STATUS_SUCCESS; } /* Verify we know something in */ else if (PartitionInfo.PartitionStyle == PARTITION_STYLE_MBR && IsRecognizedPartition(PartitionInfo.Mbr.PartitionType)) { *Valid = TRUE; } /* It looks correct, ensure it is & query device number */ if (*Valid) { KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER, DeviceObject, NULL, 0, &StorageDeviceNumber, sizeof(StorageDeviceNumber), FALSE, &Event, &IoStatusBlock); if (!Irp) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } if (!NT_SUCCESS(Status)) { Status = STATUS_SUCCESS; } else { *Valid = FALSE; } } } } /* If caller needs device name */ if (DeviceName) { /* Allocate a buffer just to request length */ Name = AllocatePool(sizeof(MOUNTDEV_NAME)); if (!Name) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } /* Query device name */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, DeviceObject, NULL, 0, Name, sizeof(MOUNTDEV_NAME), FALSE, &Event, &IoStatusBlock); if (!Irp) { FreePool(Name); ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Stack = IoGetNextIrpStackLocation(Irp); Stack->FileObject = FileObject; Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } /* Now, we've got the correct length */ if (Status == STATUS_BUFFER_OVERFLOW) { Size = Name->NameLength + sizeof(MOUNTDEV_NAME); FreePool(Name); /* Allocate proper size */ Name = AllocatePool(Size); if (!Name) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } /* And query name (for real that time) */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, DeviceObject, NULL, 0, Name, Size, FALSE, &Event, &IoStatusBlock); if (!Irp) { FreePool(Name); ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Stack = IoGetNextIrpStackLocation(Irp); Stack->FileObject = FileObject; Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } } if (NT_SUCCESS(Status)) { /* Copy back found name to the caller */ DeviceName->Length = Name->NameLength; DeviceName->MaximumLength = Name->NameLength + sizeof(WCHAR); DeviceName->Buffer = AllocatePool(DeviceName->MaximumLength); if (!DeviceName->Buffer) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { RtlCopyMemory(DeviceName->Buffer, Name->Name, Name->NameLength); DeviceName->Buffer[Name->NameLength / sizeof(WCHAR)] = UNICODE_NULL; } } FreePool(Name); } if (!NT_SUCCESS(Status)) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return Status; } /* If caller wants device unique ID */ if (UniqueId) { /* Prepare buffer to probe length */ Id = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID)); if (!Id) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } /* Query unique ID length */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, DeviceObject, NULL, 0, Id, sizeof(MOUNTDEV_UNIQUE_ID), FALSE, &Event, &IoStatusBlock); if (!Irp) { FreePool(Id); ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Stack = IoGetNextIrpStackLocation(Irp); Stack->FileObject = FileObject; Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } /* Retry with appropriate length */ if (Status == STATUS_BUFFER_OVERFLOW) { Size = Id->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID); FreePool(Id); /* Allocate the correct buffer */ Id = AllocatePool(Size); if (!Id) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } /* Query unique ID */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, DeviceObject, NULL, 0, Id, Size, FALSE, &Event, &IoStatusBlock); if (!Irp) { FreePool(Id); ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Stack = IoGetNextIrpStackLocation(Irp); Stack->FileObject = FileObject; Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } } /* Hands back unique ID */ if (NT_SUCCESS(Status)) { *UniqueId = Id; } else { /* In case of failure, also free the rest */ FreePool(Id); if (DeviceName->Length) { FreePool(DeviceName->Buffer); } ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return Status; } } /* If user wants to know about GUID */ if (HasGuid) { /* Query device stable GUID */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_STABLE_GUID, DeviceObject, NULL, 0, StableGuid, sizeof(GUID), FALSE, &Event, &IoStatusBlock); if (!Irp) { ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Stack = IoGetNextIrpStackLocation(Irp); Stack->FileObject = FileObject; IntStatus = IoCallDriver(DeviceObject, Irp); if (IntStatus == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); IntStatus = IoStatusBlock.Status; } *HasGuid = NT_SUCCESS(IntStatus); } ObDereferenceObject(DeviceObject); ObDereferenceObject(FileObject); return Status; } /* * @implemented */ NTSTATUS FindDeviceInfo(IN PDEVICE_EXTENSION DeviceExtension, IN PUNICODE_STRING SymbolicName, IN BOOLEAN DeviceNameGiven, OUT PDEVICE_INFORMATION * DeviceInformation) { NTSTATUS Status; PLIST_ENTRY NextEntry; UNICODE_STRING DeviceName; PDEVICE_INFORMATION DeviceInfo = NULL; /* If a device name was given, use it */ if (DeviceNameGiven) { DeviceName.Length = SymbolicName->Length; DeviceName.Buffer = SymbolicName->Buffer; } else { /* Otherwise, query it */ Status = QueryDeviceInformation(SymbolicName, &DeviceName, NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(Status)) { return Status; } } /* Look for device information matching devive */ for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &(DeviceExtension->DeviceListHead); NextEntry = NextEntry->Flink) { DeviceInfo = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); if (RtlEqualUnicodeString(&DeviceName, &(DeviceInfo->DeviceName), TRUE)) { break; } } /* Release our buffer if required */ if (!DeviceNameGiven) { FreePool(DeviceName.Buffer); } /* Return found information */ if (NextEntry == &(DeviceExtension->DeviceListHead)) { return STATUS_OBJECT_NAME_NOT_FOUND; } *DeviceInformation = DeviceInfo; return STATUS_SUCCESS; } /* * @implemented */ VOID MountMgrFreeDeadDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation) { FreePool(DeviceInformation->SymbolicName.Buffer); FreePool(DeviceInformation); } /* * @implemented */ VOID MountMgrFreeMountedDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation) { PLIST_ENTRY NextEntry; PSYMLINK_INFORMATION SymLink; PUNIQUE_ID_REPLICATE UniqueId; PASSOCIATED_DEVICE_ENTRY AssociatedDevice; /* Purge symbolic links list */ while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead))) { NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead)); SymLink = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); GlobalDeleteSymbolicLink(&(SymLink->Name)); FreePool(SymLink->Name.Buffer); } /* Purge replicated unique IDs list */ while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead))) { NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead)); UniqueId = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry); FreePool(UniqueId->UniqueId); FreePool(UniqueId); } while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead))) { NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead)); AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); FreePool(AssociatedDevice->String.Buffer); FreePool(AssociatedDevice); } /* Free the rest of the buffers */ FreePool(DeviceInformation->SymbolicName.Buffer); if (DeviceInformation->KeepLinks) { FreePool(DeviceInformation->UniqueId); } FreePool(DeviceInformation->DeviceName.Buffer); /* Finally, stop waiting for notifications for this device */ if (DeviceInformation->TargetDeviceNotificationEntry) { IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry); } } /* * @implemented */ VOID MountMgrFreeSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation) { PLIST_ENTRY NextEntry; PSYMLINK_INFORMATION SymlinkInformation; /* For all the saved links */ while (!IsListEmpty(&(SavedLinkInformation->SymbolicLinksListHead))) { NextEntry = RemoveHeadList(&(SavedLinkInformation->SymbolicLinksListHead)); SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); /* Remove from system & free */ GlobalDeleteSymbolicLink(&(SymlinkInformation->Name)); FreePool(SymlinkInformation->Name.Buffer); FreePool(SymlinkInformation); } /* And free unique ID & entry */ FreePool(SavedLinkInformation->UniqueId); FreePool(SavedLinkInformation); } /* * @implemented */ VOID NTAPI MountMgrUnload(IN struct _DRIVER_OBJECT *DriverObject) { PLIST_ENTRY NextEntry; PUNIQUE_ID_WORK_ITEM WorkItem; PDEVICE_EXTENSION DeviceExtension; PDEVICE_INFORMATION DeviceInformation; PSAVED_LINK_INFORMATION SavedLinkInformation; UNREFERENCED_PARAMETER(DriverObject); /* Don't get notification any longer */ IoUnregisterShutdownNotification(gdeviceObject); /* Free registry buffer */ DeviceExtension = gdeviceObject->DeviceExtension; if (DeviceExtension->RegistryPath.Buffer) { FreePool(DeviceExtension->RegistryPath.Buffer); DeviceExtension->RegistryPath.Buffer = NULL; } InterlockedExchange(&Unloading, TRUE); KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE); /* Wait for workers to finish */ if (InterlockedIncrement(&DeviceExtension->WorkerReferences) > 0) { KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE); KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL); } else { InterlockedDecrement(&(DeviceExtension->WorkerReferences)); } /* Don't get any notification any longerĀ² */ IoUnregisterPlugPlayNotification(DeviceExtension->NotificationEntry); /* Acquire the driver exclusively */ KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); /* Clear offline devices list */ while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead))) { NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead)); DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); MountMgrFreeDeadDeviceInfo(DeviceInformation); } /* Clear saved links list */ while (!IsListEmpty(&(DeviceExtension->SavedLinksListHead))) { NextEntry = RemoveHeadList(&(DeviceExtension->SavedLinksListHead)); SavedLinkInformation = CONTAINING_RECORD(NextEntry, SAVED_LINK_INFORMATION, SavedLinksListEntry); MountMgrFreeSavedLink(SavedLinkInformation); } /* Clear workers list */ while (!IsListEmpty(&(DeviceExtension->UniqueIdWorkerItemListHead))) { NextEntry = RemoveHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead)); WorkItem = CONTAINING_RECORD(NextEntry, UNIQUE_ID_WORK_ITEM, UniqueIdWorkerItemListEntry); KeClearEvent(&UnloadEvent); WorkItem->Event = &UnloadEvent; KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); IoCancelIrp(WorkItem->Irp); KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL); IoFreeIrp(WorkItem->Irp); FreePool(WorkItem->DeviceName.Buffer); FreePool(WorkItem->IrpBuffer); FreePool(WorkItem); KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); } /* If we have drive letter data, release */ if (DeviceExtension->DriveLetterData) { FreePool(DeviceExtension->DriveLetterData); DeviceExtension->DriveLetterData = NULL; } /* Release driver & quit */ KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); GlobalDeleteSymbolicLink(&DosDevicesMount); IoDeleteDevice(gdeviceObject); } /* * @implemented */ CODE_SEG("INIT") BOOLEAN MountmgrReadNoAutoMount(IN PUNICODE_STRING RegistryPath) { NTSTATUS Status; ULONG Result, Default = 0; RTL_QUERY_REGISTRY_TABLE QueryTable[2]; RtlZeroMemory(QueryTable, sizeof(QueryTable)); /* Simply read data from register */ QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; QueryTable[0].Name = L"NoAutoMount"; QueryTable[0].EntryContext = &Result; QueryTable[0].DefaultType = REG_NONE; QueryTable[0].DefaultData = &Default; QueryTable[0].DefaultLength = sizeof(ULONG); Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RegistryPath->Buffer, QueryTable, NULL, NULL); if (!NT_SUCCESS(Status)) { return (Default != 0); } return (Result != 0); } /* * @implemented */ NTSTATUS MountMgrMountedDeviceArrival(IN PDEVICE_EXTENSION DeviceExtension, IN PUNICODE_STRING SymbolicName, IN BOOLEAN ManuallyRegistered) { WCHAR Letter; GUID StableGuid; HANDLE LinkHandle; ULONG SymLinkCount, i; PLIST_ENTRY NextEntry; PUNICODE_STRING SymLinks; NTSTATUS Status, IntStatus; OBJECT_ATTRIBUTES ObjectAttributes; PSYMLINK_INFORMATION SymlinkInformation; PMOUNTDEV_UNIQUE_ID UniqueId, NewUniqueId; PSAVED_LINK_INFORMATION SavedLinkInformation; PDEVICE_INFORMATION DeviceInformation, CurrentDevice; WCHAR CSymLinkBuffer[RTL_NUMBER_OF(Cunc)], LinkTargetBuffer[MAX_PATH]; UNICODE_STRING TargetDeviceName, SuggestedLinkName, DeviceName, VolumeName, DriveLetter, LinkTarget, CSymLink; BOOLEAN HasGuid, HasGptDriveLetter, Valid, UseOnlyIfThereAreNoOtherLinks, IsDrvLetter, IsOff, IsVolumeName, LinkError; /* New device = new structure to represent it */ DeviceInformation = AllocatePool(sizeof(DEVICE_INFORMATION)); if (!DeviceInformation) { return STATUS_INSUFFICIENT_RESOURCES; } /* Initialise device structure */ RtlZeroMemory(DeviceInformation, sizeof(DEVICE_INFORMATION)); InitializeListHead(&(DeviceInformation->SymbolicLinksListHead)); InitializeListHead(&(DeviceInformation->ReplicatedUniqueIdsListHead)); InitializeListHead(&(DeviceInformation->AssociatedDevicesHead)); DeviceInformation->SymbolicName.Length = SymbolicName->Length; DeviceInformation->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(UNICODE_NULL); DeviceInformation->SymbolicName.Buffer = AllocatePool(DeviceInformation->SymbolicName.MaximumLength); if (!DeviceInformation->SymbolicName.Buffer) { FreePool(DeviceInformation); return STATUS_INSUFFICIENT_RESOURCES; } /* Copy symbolic name */ RtlCopyMemory(DeviceInformation->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length); DeviceInformation->SymbolicName.Buffer[DeviceInformation->SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL; DeviceInformation->ManuallyRegistered = ManuallyRegistered; DeviceInformation->DeviceExtension = DeviceExtension; /* Query as much data as possible about device */ Status = QueryDeviceInformation(SymbolicName, &TargetDeviceName, &UniqueId, &(DeviceInformation->Removable), &HasGptDriveLetter, &HasGuid, &StableGuid, &Valid); if (!NT_SUCCESS(Status)) { KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink; NextEntry != &(DeviceExtension->OfflineDeviceListHead); NextEntry = NextEntry->Flink) { CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); if (RtlEqualUnicodeString(&(DeviceInformation->SymbolicName), &(CurrentDevice->SymbolicName), TRUE)) { break; } } if (NextEntry != &(DeviceExtension->OfflineDeviceListHead)) { MountMgrFreeDeadDeviceInfo(DeviceInformation); } else { InsertTailList(&(DeviceExtension->OfflineDeviceListHead), &(DeviceInformation->DeviceListEntry)); } KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); return Status; } /* Save gathered data */ DeviceInformation->UniqueId = UniqueId; DeviceInformation->DeviceName = TargetDeviceName; DeviceInformation->KeepLinks = FALSE; /* If we found system partition, mark it */ if (DeviceExtension->DriveLetterData && UniqueId->UniqueIdLength == DeviceExtension->DriveLetterData->UniqueIdLength) { if (RtlCompareMemory(UniqueId->UniqueId, DeviceExtension->DriveLetterData->UniqueId, UniqueId->UniqueIdLength) == UniqueId->UniqueIdLength) { IoSetSystemPartition(&TargetDeviceName); } } /* Check suggested link name */ Status = QuerySuggestedLinkName(&(DeviceInformation->SymbolicName), &SuggestedLinkName, &UseOnlyIfThereAreNoOtherLinks); if (!NT_SUCCESS(Status)) { SuggestedLinkName.Buffer = NULL; } /* If it's OK, set it and save its letter (if any) */ if (SuggestedLinkName.Buffer && IsDriveLetter(&SuggestedLinkName)) { DeviceInformation->SuggestedDriveLetter = (UCHAR)SuggestedLinkName.Buffer[LETTER_POSITION]; } /* Acquire driver exclusively */ KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); /* Check if we already have device in to prevent double registration */ for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &(DeviceExtension->DeviceListHead); NextEntry = NextEntry->Flink) { CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); if (RtlEqualUnicodeString(&(CurrentDevice->DeviceName), &TargetDeviceName, TRUE)) { break; } } /* If we found it, clear ours, and return success, all correct */ if (NextEntry != &(DeviceExtension->DeviceListHead)) { if (SuggestedLinkName.Buffer) { FreePool(SuggestedLinkName.Buffer); } FreePool(UniqueId); FreePool(TargetDeviceName.Buffer); FreePool(DeviceInformation->DeviceName.Buffer); FreePool(DeviceInformation); KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); return STATUS_SUCCESS; } /* Check if there are symlinks associated with our device in registry */ Status = QuerySymbolicLinkNamesFromStorage(DeviceExtension, DeviceInformation, (SuggestedLinkName.Buffer) ? &SuggestedLinkName : NULL, UseOnlyIfThereAreNoOtherLinks, &SymLinks, &SymLinkCount, HasGuid, &StableGuid); /* If our device is a CD-ROM */ if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE)) { LinkTarget.Length = 0; LinkTarget.MaximumLength = sizeof(LinkTargetBuffer); LinkTarget.Buffer = LinkTargetBuffer; RtlCopyMemory(CSymLinkBuffer, Cunc, sizeof(Cunc)); RtlInitUnicodeString(&CSymLink, CSymLinkBuffer); /* Start checking all letters that could have been associated */ for (Letter = L'D'; Letter <= L'Z'; Letter++) { CSymLink.Buffer[Cunc_LETTER_POSITION] = Letter; InitializeObjectAttributes(&ObjectAttributes, &CSymLink, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); /* Try to open the associated symlink */ Status = ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes); if (!NT_SUCCESS(Status)) { continue; } /* And query its target */ Status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, NULL); ZwClose(LinkHandle); if (!NT_SUCCESS(Status)) { continue; } IntStatus = STATUS_UNSUCCESSFUL; if (!RtlEqualUnicodeString(&LinkTarget, &DeviceInformation->DeviceName, FALSE)) { continue; } /* This link is matching our device, whereas it's not supposed to have any * symlink associated. * Delete it */ if (!SymLinkCount) { IoDeleteSymbolicLink(&CSymLink); continue; } /* Now, for all the symlinks, check for ours */ for (i = 0; i < SymLinkCount; i++) { if (IsDriveLetter(&(SymLinks[i]))) { /* If it exists, that's correct */ if (SymLinks[i].Buffer[LETTER_POSITION] == Letter) { IntStatus = STATUS_SUCCESS; } } } /* Useless link, delete it */ if (IntStatus == STATUS_UNSUCCESSFUL) { IoDeleteSymbolicLink(&CSymLink); } } } /* Suggested name is no longer required */ if (SuggestedLinkName.Buffer) { FreePool(SuggestedLinkName.Buffer); } /* If if failed, ensure we don't take symlinks into account */ if (!NT_SUCCESS(Status)) { SymLinks = NULL; SymLinkCount = 0; } /* Now we queried them, remove the symlinks */ SavedLinkInformation = RemoveSavedLinks(DeviceExtension, UniqueId); IsDrvLetter = FALSE; IsOff = FALSE; IsVolumeName = FALSE; /* For all the symlinks */ for (i = 0; i < SymLinkCount; i++) { /* Check if our device is a volume */ if (MOUNTMGR_IS_VOLUME_NAME(&(SymLinks[i]))) { IsVolumeName = TRUE; } /* If it has a drive letter */ else if (IsDriveLetter(&(SymLinks[i]))) { if (IsDrvLetter) { DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId); continue; } else { IsDrvLetter = TRUE; } } /* And recreate the symlink to our device */ Status = GlobalCreateSymbolicLink(&(SymLinks[i]), &TargetDeviceName); if (!NT_SUCCESS(Status)) { LinkError = TRUE; if ((SavedLinkInformation && !RedirectSavedLink(SavedLinkInformation, &(SymLinks[i]), &TargetDeviceName)) || !SavedLinkInformation) { Status = QueryDeviceInformation(&(SymLinks[i]), &DeviceName, NULL, NULL, NULL, NULL, NULL, NULL); if (NT_SUCCESS(Status)) { LinkError = RtlEqualUnicodeString(&TargetDeviceName, &DeviceName, TRUE); FreePool(DeviceName.Buffer); } if (!LinkError) { if (IsDriveLetter(&(SymLinks[i]))) { IsDrvLetter = FALSE; DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId); } FreePool(SymLinks[i].Buffer); continue; } } } /* Check if was offline */ if (IsOffline(&(SymLinks[i]))) { IsOff = TRUE; } /* Finally, associate this symlink with the device */ SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION)); if (!SymlinkInformation) { GlobalDeleteSymbolicLink(&(SymLinks[i])); FreePool(SymLinks[i].Buffer); continue; } SymlinkInformation->Name = SymLinks[i]; SymlinkInformation->Online = TRUE; InsertTailList(&(DeviceInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry)); } /* Now, for all the recreated symlinks, notify their recreation */ for (NextEntry = DeviceInformation->SymbolicLinksListHead.Flink; NextEntry != &(DeviceInformation->SymbolicLinksListHead); NextEntry = NextEntry->Flink) { SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); SendLinkCreated(&(SymlinkInformation->Name)); } /* If we had saved links, it's time to free them */ if (SavedLinkInformation) { MountMgrFreeSavedLink(SavedLinkInformation); } /* If our device doesn't have a volume name */ if (!IsVolumeName) { /* It's time to create one */ Status = CreateNewVolumeName(&VolumeName, NULL); if (NT_SUCCESS(Status)) { /* Write it to global database */ RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, VolumeName.Buffer, REG_BINARY, UniqueId->UniqueId, UniqueId->UniqueIdLength); /* And create the symlink */ GlobalCreateSymbolicLink(&VolumeName, &TargetDeviceName); SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION)); if (!SymlinkInformation) { FreePool(VolumeName.Buffer); } /* Finally, associate it with the device and notify creation */ else { SymlinkInformation->Name = VolumeName; SymlinkInformation->Online = TRUE; InsertTailList(&(DeviceInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry)); SendLinkCreated(&VolumeName); } } } /* If we found a drive letter, then, ignore the suggested one */ if (IsDrvLetter) { DeviceInformation->SuggestedDriveLetter = 0; } /* Else, it's time to set up one */ else if ((DeviceExtension->NoAutoMount || DeviceInformation->Removable) && DeviceExtension->AutomaticDriveLetter && (HasGptDriveLetter || DeviceInformation->SuggestedDriveLetter) && !HasNoDriveLetterEntry(UniqueId)) { /* Create a new drive letter */ Status = CreateNewDriveLetterName(&DriveLetter, &TargetDeviceName, DeviceInformation->SuggestedDriveLetter, NULL); if (!NT_SUCCESS(Status)) { CreateNoDriveLetterEntry(UniqueId); } else { /* Save it to global database */ RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, DriveLetter.Buffer, REG_BINARY, UniqueId->UniqueId, UniqueId->UniqueIdLength); /* Associate it with the device and notify creation */ SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION)); if (!SymlinkInformation) { FreePool(DriveLetter.Buffer); } else { SymlinkInformation->Name = DriveLetter; SymlinkInformation->Online = TRUE; InsertTailList(&(DeviceInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry)); SendLinkCreated(&DriveLetter); } } } /* If that's a PnP device, register for notifications */ if (!ManuallyRegistered) { RegisterForTargetDeviceNotification(DeviceExtension, DeviceInformation); } /* Finally, insert the device into our devices list */ InsertTailList(&(DeviceExtension->DeviceListHead), &(DeviceInformation->DeviceListEntry)); /* Copy device unique ID */ NewUniqueId = AllocatePool(UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); if (NewUniqueId) { NewUniqueId->UniqueIdLength = UniqueId->UniqueIdLength; RtlCopyMemory(NewUniqueId->UniqueId, UniqueId->UniqueId, UniqueId->UniqueIdLength); } /* If device's offline or valid, skip its notifications */ if (IsOff || Valid) { DeviceInformation->SkipNotifications = TRUE; } /* In case device is valid and is set to no automount, * set it offline. */ if (DeviceExtension->NoAutoMount || IsDrvLetter) { IsOff = !DeviceInformation->SkipNotifications; } else { IsOff = FALSE; } /* Finally, release the exclusive lock */ KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); /* If device is not offline, notify its arrival */ if (!IsOff) { SendOnlineNotification(SymbolicName); } /* If we had symlinks (from storage), free them */ if (SymLinks) { FreePool(SymLinks); } /* Notify about unique id change */ if (NewUniqueId) { IssueUniqueIdChangeNotify(DeviceExtension, SymbolicName, NewUniqueId); FreePool(NewUniqueId); } /* If this drive was set to have a drive letter automatically * Now it's back, local databases sync will be required */ if (DeviceExtension->AutomaticDriveLetter) { KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); NextEntry = DeviceExtension->DeviceListHead.Flink; CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); while (CurrentDevice != DeviceInformation) { if (!CurrentDevice->NoDatabase) { ReconcileThisDatabaseWithMaster(DeviceExtension, CurrentDevice); } NextEntry = NextEntry->Flink; CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); } KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); } return STATUS_SUCCESS; } /* * @implemented */ VOID MountMgrMountedDeviceRemoval(IN PDEVICE_EXTENSION DeviceExtension, IN PUNICODE_STRING DeviceName) { PLIST_ENTRY NextEntry, DeviceEntry; PUNIQUE_ID_REPLICATE UniqueIdReplicate; PSYMLINK_INFORMATION SymlinkInformation; PASSOCIATED_DEVICE_ENTRY AssociatedDevice; PSAVED_LINK_INFORMATION SavedLinkInformation = NULL; PDEVICE_INFORMATION DeviceInformation, CurrentDevice; /* Acquire device exclusively */ KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); /* Look for the leaving device */ for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &(DeviceExtension->DeviceListHead); NextEntry = NextEntry->Flink) { DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); if (!RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE)) { break; } } /* If we found it */ if (NextEntry != &(DeviceExtension->DeviceListHead)) { /* If it's asked to keep links, then, prepare to save them */ if (DeviceInformation->KeepLinks) { SavedLinkInformation = AllocatePool(sizeof(SAVED_LINK_INFORMATION)); if (!SavedLinkInformation) { DeviceInformation->KeepLinks = FALSE; } } /* If it's possible (and asked), start to save them */ if (DeviceInformation->KeepLinks) { InsertTailList(&(DeviceExtension->SavedLinksListHead), &(SavedLinkInformation->SavedLinksListEntry)); InitializeListHead(&(SavedLinkInformation->SymbolicLinksListHead)); SavedLinkInformation->UniqueId = DeviceInformation->UniqueId; } /* For all the symlinks */ while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead))) { NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead)); SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); /* If we have to, save the link */ if (DeviceInformation->KeepLinks) { InsertTailList(&(SavedLinkInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry)); } /* Otherwise, just release it */ else { GlobalDeleteSymbolicLink(&(SymlinkInformation->Name)); FreePool(SymlinkInformation->Name.Buffer); FreePool(SymlinkInformation); } } /* Free all the replicated unique IDs */ while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead))) { NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead)); UniqueIdReplicate = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry); FreePool(UniqueIdReplicate->UniqueId); FreePool(UniqueIdReplicate); } while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead))) { NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead)); AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); DeviceInformation->NoDatabase = TRUE; FreePool(AssociatedDevice->String.Buffer); FreePool(AssociatedDevice); } /* Remove device from the device list */ RemoveEntryList(&(DeviceInformation->DeviceListEntry)); /* If there are still devices, check if some were associated with ours */ if (!IsListEmpty(&(DeviceInformation->DeviceListEntry))) { for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &(DeviceExtension->DeviceListHead); NextEntry = NextEntry->Flink) { CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); /* And then, remove them */ DeviceEntry = CurrentDevice->AssociatedDevicesHead.Flink; while (DeviceEntry != &(CurrentDevice->AssociatedDevicesHead)) { AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); DeviceEntry = DeviceEntry->Flink; if (AssociatedDevice->DeviceInformation != DeviceInformation) { continue; } RemoveEntryList(&(AssociatedDevice->AssociatedDevicesEntry)); FreePool(AssociatedDevice->String.Buffer); FreePool(AssociatedDevice); } } } /* Finally, clean up device name, symbolic name */ FreePool(DeviceInformation->SymbolicName.Buffer); if (!DeviceInformation->KeepLinks) { FreePool(DeviceInformation->UniqueId); } FreePool(DeviceInformation->DeviceName.Buffer); /* Unregister notifications */ if (DeviceInformation->TargetDeviceNotificationEntry) { IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry); } /* And leave */ FreePool(DeviceInformation); } else { /* We didn't find device, perhaps because it was offline */ for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink; NextEntry != &(DeviceExtension->OfflineDeviceListHead); NextEntry = NextEntry->Flink) { DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); /* It was, remove it */ if (RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE) == 0) { RemoveEntryList(&(DeviceInformation->DeviceListEntry)); MountMgrFreeDeadDeviceInfo(DeviceInformation); break; } } } /* Release driver */ KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); } /* * @implemented */ NTSTATUS NTAPI MountMgrMountedDeviceNotification(IN PVOID NotificationStructure, IN PVOID Context) { BOOLEAN OldState; PDEVICE_EXTENSION DeviceExtension; PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification; /* Notification for a device arrived */ /* Disable hard errors */ OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread()); PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE); DeviceExtension = Context; Notification = NotificationStructure; /* Dispatch according to the event */ if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_ARRIVAL)) { MountMgrMountedDeviceArrival(DeviceExtension, Notification->SymbolicLinkName, FALSE); } else if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_REMOVAL)) { MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName); } /* Reset hard errors */ PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState); return STATUS_SUCCESS; } /* * @implemented */ NTSTATUS NTAPI MountMgrCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION Stack; NTSTATUS Status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(DeviceObject); Stack = IoGetCurrentIrpStackLocation(Irp); /* Allow driver opening for communication * as long as it's not taken for a directory */ if (Stack->MajorFunction == IRP_MJ_CREATE && Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE) { Status = STATUS_NOT_A_DIRECTORY; } Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } /* * @implemented */ VOID NTAPI MountMgrCancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); RemoveEntryList(&(Irp->Tail.Overlay.ListEntry)); IoReleaseCancelSpinLock(Irp->CancelIrql); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); } /* * @implemented */ NTSTATUS NTAPI MountMgrCleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIRP ListIrp; KIRQL OldIrql; PLIST_ENTRY NextEntry; PFILE_OBJECT FileObject; PIO_STACK_LOCATION Stack; PDEVICE_EXTENSION DeviceExtension; DeviceExtension = DeviceObject->DeviceExtension; Stack = IoGetCurrentIrpStackLocation(Irp); FileObject = Stack->FileObject; IoAcquireCancelSpinLock(&OldIrql); /* If IRP list if empty, it's OK */ if (IsListEmpty(&(DeviceExtension->IrpListHead))) { IoReleaseCancelSpinLock(OldIrql); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } /* Otherwise, cancel all the IRPs */ NextEntry = DeviceExtension->IrpListHead.Flink; do { ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry); if (IoGetCurrentIrpStackLocation(ListIrp)->FileObject == FileObject) { ListIrp->Cancel = TRUE; ListIrp->CancelIrql = OldIrql; ListIrp->CancelRoutine = NULL; MountMgrCancel(DeviceObject, ListIrp); IoAcquireCancelSpinLock(&OldIrql); } NextEntry = NextEntry->Flink; } while (NextEntry != &(DeviceExtension->IrpListHead)); IoReleaseCancelSpinLock(OldIrql); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } /* * @implemented */ NTSTATUS NTAPI MountMgrShutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION DeviceExtension; DeviceExtension = DeviceObject->DeviceExtension; InterlockedExchange(&Unloading, TRUE); KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE); /* Wait for workers */ if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)) > 0) { KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE); KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL); } else { InterlockedDecrement(&(DeviceExtension->WorkerReferences)); } Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } /* FUNCTIONS ****************************************************************/ CODE_SEG("INIT") NTSTATUS NTAPI DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS Status; PDEVICE_OBJECT DeviceObject; PDEVICE_EXTENSION DeviceExtension; RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, DatabasePath); Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceMount, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { return Status; } DriverObject->DriverUnload = MountMgrUnload; DeviceExtension = DeviceObject->DeviceExtension; RtlZeroMemory(DeviceExtension, sizeof(DEVICE_EXTENSION)); DeviceExtension->DeviceObject = DeviceObject; DeviceExtension->DriverObject = DriverObject; InitializeListHead(&(DeviceExtension->DeviceListHead)); InitializeListHead(&(DeviceExtension->OfflineDeviceListHead)); KeInitializeSemaphore(&(DeviceExtension->DeviceLock), 1, 1); KeInitializeSemaphore(&(DeviceExtension->RemoteDatabaseLock), 1, 1); InitializeListHead(&(DeviceExtension->IrpListHead)); DeviceExtension->EpicNumber = 1; InitializeListHead(&(DeviceExtension->SavedLinksListHead)); InitializeListHead(&(DeviceExtension->WorkerQueueListHead)); KeInitializeSemaphore(&(DeviceExtension->WorkerSemaphore), 0, MAXLONG); DeviceExtension->WorkerReferences = -1; KeInitializeSpinLock(&(DeviceExtension->WorkerLock)); InitializeListHead(&(DeviceExtension->UniqueIdWorkerItemListHead)); InitializeListHead(&(DeviceExtension->OnlineNotificationListHead)); DeviceExtension->OnlineNotificationCount = 1; DeviceExtension->RegistryPath.Length = RegistryPath->Length; DeviceExtension->RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR); DeviceExtension->RegistryPath.Buffer = AllocatePool(DeviceExtension->RegistryPath.MaximumLength); if (!DeviceExtension->RegistryPath.Buffer) { IoDeleteDevice(DeviceObject); return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyUnicodeString(&(DeviceExtension->RegistryPath), RegistryPath); DeviceExtension->NoAutoMount = MountmgrReadNoAutoMount(&(DeviceExtension->RegistryPath)); GlobalCreateSymbolicLink(&DosDevicesMount, &DeviceMount); /* Register for device arrival & removal. Ask to be notified for already * present devices */ Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, &MountedDevicesGuid, DriverObject, MountMgrMountedDeviceNotification, DeviceExtension, &(DeviceExtension->NotificationEntry)); if (!NT_SUCCESS(Status)) { IoDeleteDevice(DeviceObject); return Status; } DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverObject->MajorFunction[IRP_MJ_CLOSE] = MountMgrCreateClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MountMgrDeviceControl; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MountMgrCleanup; DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = MountMgrShutdown; gdeviceObject = DeviceObject; Status = IoRegisterShutdownNotification(DeviceObject); if (!NT_SUCCESS(Status)) { IoDeleteDevice(DeviceObject); } return Status; }