BOOLEAN DokanCheckCCB(__in PDokanDCB Dcb, __in_opt PDokanCCB Ccb) { PDokanVCB vcb; ASSERT(Dcb != NULL); if (GetIdentifierType(Dcb) != DCB) { PrintIdType(Dcb); return FALSE; } if (Ccb == NULL) { PrintIdType(Dcb); DDbgPrint(" ccb is NULL\n"); return FALSE; } if (Ccb->MountId != Dcb->MountId) { DDbgPrint(" MountId is different\n"); return FALSE; } vcb = Dcb->Vcb; if (!vcb || IsUnmountPendingVcb(vcb)) { DDbgPrint(" Not mounted\n"); return FALSE; } return TRUE; }
NTSTATUS DokanRegisterPendingIrpForEvent(__in PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) { PDokanVCB vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB) { DDbgPrint(" IdentifierType is not VCB\n"); return STATUS_INVALID_PARAMETER; } if (IsUnmountPendingVcb(vcb)) { DDbgPrint(" Volume is dismounted\n"); return STATUS_NO_SUCH_DEVICE; } // DDbgPrint("DokanRegisterPendingIrpForEvent\n"); vcb->HasEventWait = TRUE; return RegisterPendingIrpMain(DeviceObject, Irp, 0, // SerialNumber &vcb->Dcb->PendingEvent, 0, // Flags TRUE); }
NTSTATUS DiskDeviceControl(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { PIO_STACK_LOCATION irpSp; PDokanDCB dcb; PDokanVCB vcb; NTSTATUS status = STATUS_NOT_IMPLEMENTED; ULONG outputLength = 0; ULONG inputLength = 0; DDbgPrint(" => DokanDiskDeviceControl\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); dcb = DeviceObject->DeviceExtension; outputLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; inputLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; if (GetIdentifierType(dcb) != DCB) { PrintIdType(dcb); DDbgPrint(" Device is not dcb so go out here\n"); return STATUS_INVALID_PARAMETER; } if (IsDeletePending(DeviceObject)) { DDbgPrint(" Device object is pending for delete valid anymore\n"); return STATUS_DEVICE_REMOVED; } vcb = dcb->Vcb; if (IsUnmountPendingVcb(vcb)) { DDbgPrint(" Volume is unmounted so ignore dcb requests\n"); return STATUS_NO_SUCH_DEVICE; } DDbgPrint(" DiskDeviceControl Device name %wZ \n", dcb->DiskDeviceName); switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_DISK_GET_DRIVE_GEOMETRY: { PDISK_GEOMETRY diskGeometry; DDbgPrint(" IOCTL_DISK_GET_DRIVE_GEOMETRY\n"); if (outputLength < sizeof(DISK_GEOMETRY)) { Irp->IoStatus.Information = 0; status = STATUS_BUFFER_TOO_SMALL; break; } diskGeometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer; ASSERT(diskGeometry != NULL); DokanPopulateDiskGeometry(diskGeometry); Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); status = STATUS_SUCCESS; } break; case IOCTL_DISK_GET_LENGTH_INFO: { PGET_LENGTH_INFORMATION getLengthInfo; DDbgPrint(" IOCTL_DISK_GET_LENGTH_INFO\n"); if (outputLength < sizeof(GET_LENGTH_INFORMATION)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; break; } getLengthInfo = (PGET_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ASSERT(getLengthInfo != NULL); getLengthInfo->Length.QuadPart = 1024 * 1024 * 500; status = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); } break; case IOCTL_DISK_GET_DRIVE_LAYOUT: case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: case IOCTL_DISK_GET_PARTITION_INFO: case IOCTL_DISK_GET_PARTITION_INFO_EX: { // Fake drive layout/partition information VOID *outputBuffer = Irp->AssociatedIrp.SystemBuffer; ULONG ioctl = irpSp->Parameters.DeviceIoControl.IoControlCode; switch (ioctl) { case IOCTL_DISK_GET_DRIVE_LAYOUT: DDbgPrint(" IOCTL_DISK_GET_DRIVE_LAYOUT\n"); Irp->IoStatus.Information = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]); break; case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: DDbgPrint(" IOCTL_DISK_GET_DRIVE_LAYOUT_EX\n"); Irp->IoStatus.Information = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]); break; case IOCTL_DISK_GET_PARTITION_INFO: DDbgPrint(" IOCTL_DISK_GET_PARTITION_INFO\n"); Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); break; case IOCTL_DISK_GET_PARTITION_INFO_EX: DDbgPrint(" IOCTL_DISK_GET_PARTITION_INFO_EX\n"); Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); break; default: DDbgPrint(" unknown ioctl %d\n", ioctl); break; } if (outputLength < Irp->IoStatus.Information) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; break; } RtlZeroMemory(outputBuffer, Irp->IoStatus.Information); // if we are getting the drive layout, then we need to start by // adding some of the non-partition stuff that says we have // exactly one partition available. if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT) { PDRIVE_LAYOUT_INFORMATION layout; layout = (PDRIVE_LAYOUT_INFORMATION)outputBuffer; layout->PartitionCount = 1; layout->Signature = 1; outputBuffer = (PVOID)(layout->PartitionEntry); ioctl = IOCTL_DISK_GET_PARTITION_INFO; } else if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT_EX) { PDRIVE_LAYOUT_INFORMATION_EX layoutEx; layoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)outputBuffer; layoutEx->PartitionStyle = PARTITION_STYLE_MBR; layoutEx->PartitionCount = 1; layoutEx->Mbr.Signature = 1; outputBuffer = (PVOID)(layoutEx->PartitionEntry); ioctl = IOCTL_DISK_GET_PARTITION_INFO_EX; } // NOTE: the local var 'ioctl' is now modified to either EX or // non-EX version. the local var 'systemBuffer' is now pointing // to the partition information structure. if (ioctl == IOCTL_DISK_GET_PARTITION_INFO) { PPARTITION_INFORMATION partitionInfo; partitionInfo = (PPARTITION_INFORMATION)outputBuffer; partitionInfo->RewritePartition = FALSE; partitionInfo->RecognizedPartition = FALSE; partitionInfo->PartitionType = PARTITION_ENTRY_UNUSED; partitionInfo->BootIndicator = FALSE; partitionInfo->HiddenSectors = 0; partitionInfo->StartingOffset.QuadPart = 0; partitionInfo->PartitionLength.QuadPart = DOKAN_DEFAULT_DISK_SIZE; // Partition size equels disk size here partitionInfo->PartitionNumber = 0; } else { PPARTITION_INFORMATION_EX partitionInfo; partitionInfo = (PPARTITION_INFORMATION_EX)outputBuffer; partitionInfo->PartitionStyle = PARTITION_STYLE_MBR; partitionInfo->RewritePartition = FALSE; partitionInfo->Mbr.RecognizedPartition = FALSE; partitionInfo->Mbr.PartitionType = PARTITION_ENTRY_UNUSED; partitionInfo->Mbr.BootIndicator = FALSE; partitionInfo->Mbr.HiddenSectors = 0; partitionInfo->StartingOffset.QuadPart = 0; partitionInfo->PartitionLength.QuadPart = DOKAN_DEFAULT_DISK_SIZE; // Partition size equels disk size here partitionInfo->PartitionNumber = 0; } status = STATUS_SUCCESS; } break; case IOCTL_DISK_IS_WRITABLE: DDbgPrint(" IOCTL_DISK_IS_WRITABLE\n"); status = IS_DEVICE_READ_ONLY(DeviceObject) ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_SUCCESS; break; case IOCTL_DISK_MEDIA_REMOVAL: DDbgPrint(" IOCTL_DISK_MEDIA_REMOVAL\n"); status = STATUS_SUCCESS; break; case IOCTL_STORAGE_MEDIA_REMOVAL: DDbgPrint(" IOCTL_STORAGE_MEDIA_REMOVAL\n"); status = STATUS_SUCCESS; break; case IOCTL_DISK_SET_PARTITION_INFO: DDbgPrint(" IOCTL_DISK_SET_PARTITION_INFO\n"); break; case IOCTL_DISK_VERIFY: DDbgPrint(" IOCTL_DISK_VERIFY\n"); break; case IOCTL_STORAGE_GET_HOTPLUG_INFO: { PSTORAGE_HOTPLUG_INFO hotplugInfo; DDbgPrint(" IOCTL_STORAGE_GET_HOTPLUG_INFO\n"); if (outputLength < sizeof(STORAGE_HOTPLUG_INFO)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; break; } hotplugInfo = Irp->AssociatedIrp.SystemBuffer; hotplugInfo->Size = sizeof(STORAGE_HOTPLUG_INFO); hotplugInfo->MediaRemovable = 1; hotplugInfo->MediaHotplug = 1; hotplugInfo->DeviceHotplug = 1; hotplugInfo->WriteCacheEnableOverride = 0; status = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO); } break; case IOCTL_VOLUME_GET_GPT_ATTRIBUTES: { DDbgPrint(" IOCTL_VOLUME_GET_GPT_ATTRIBUTES\n"); PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION gptAttrInfo; if (outputLength < sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; break; } // Set GPT read-only flag if device is not writable gptAttrInfo = Irp->AssociatedIrp.SystemBuffer; if (IS_DEVICE_READ_ONLY(DeviceObject)) gptAttrInfo->GptAttributes = GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY; Irp->IoStatus.Information = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION); status = STATUS_SUCCESS; } break; case IOCTL_STORAGE_CHECK_VERIFY: case IOCTL_DISK_CHECK_VERIFY: DDbgPrint(" IOCTL_STORAGE_CHECK_VERIFY\n"); status = STATUS_SUCCESS; break; case IOCTL_STORAGE_CHECK_VERIFY2: DDbgPrint(" IOCTL_STORAGE_CHECK_VERIFY2\n"); status = STATUS_SUCCESS; break; case IOCTL_STORAGE_QUERY_PROPERTY: DDbgPrint(" IOCTL_STORAGE_QUERY_PROPERTY\n"); PSTORAGE_PROPERTY_QUERY query = NULL; query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer; ASSERT(query != NULL); if (query->QueryType == PropertyExistsQuery) { if (query->PropertyId == StorageDeviceUniqueIdProperty) { PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storage; DDbgPrint(" PropertyExistsQuery StorageDeviceUniqueIdProperty\n"); if (outputLength < sizeof(STORAGE_DEVICE_UNIQUE_IDENTIFIER)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; break; } storage = Irp->AssociatedIrp.SystemBuffer; status = STATUS_SUCCESS; } else if (query->PropertyId == StorageDeviceWriteCacheProperty) { DDbgPrint(" PropertyExistsQuery StorageDeviceWriteCacheProperty\n"); status = STATUS_NOT_IMPLEMENTED; } else { DDbgPrint(" PropertyExistsQuery Unknown %d\n", query->PropertyId); status = STATUS_NOT_IMPLEMENTED; } } else if (query->QueryType == PropertyStandardQuery) { if (query->PropertyId == StorageDeviceProperty) { PSTORAGE_DEVICE_DESCRIPTOR storage; DDbgPrint(" PropertyStandardQuery StorageDeviceProperty\n"); if (outputLength < sizeof(STORAGE_DEVICE_DESCRIPTOR)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; break; } storage = Irp->AssociatedIrp.SystemBuffer; status = STATUS_SUCCESS; } else if (query->PropertyId == StorageAdapterProperty) { DDbgPrint(" PropertyStandardQuery StorageAdapterProperty\n"); status = STATUS_NOT_IMPLEMENTED; } else { DDbgPrint(" PropertyStandardQuery Unknown %d\n", query->PropertyId); status = STATUS_ACCESS_DENIED; } } else { DDbgPrint(" Unknown query type %d\n", query->QueryType); status = STATUS_ACCESS_DENIED; } break; case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: { PMOUNTDEV_NAME mountdevName; PUNICODE_STRING deviceName = dcb->DiskDeviceName; DDbgPrint(" IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n"); if (outputLength < sizeof(MOUNTDEV_NAME)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); break; } mountdevName = (PMOUNTDEV_NAME)Irp->AssociatedIrp.SystemBuffer; ASSERT(mountdevName != NULL); /* NOTE: When Windows API GetVolumeNameForVolumeMountPoint is called, this IO control is called. Even if status = STATUS_SUCCESS, GetVolumeNameForVolumeMountPoint can returns error if it doesn't match cached data from mount manager (looks like). */ RtlZeroMemory(mountdevName, outputLength); mountdevName->NameLength = deviceName->Length; if (sizeof(USHORT) + mountdevName->NameLength <= outputLength) { RtlCopyMemory((PCHAR)mountdevName->Name, deviceName->Buffer, mountdevName->NameLength); Irp->IoStatus.Information = sizeof(USHORT) + mountdevName->NameLength; status = STATUS_SUCCESS; DDbgPrint(" DeviceName %wZ\n", deviceName); } else { Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); status = STATUS_BUFFER_OVERFLOW; } } break; case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: { PMOUNTDEV_UNIQUE_ID uniqueId; DDbgPrint(" IOCTL_MOUNTDEV_QUERY_UNIQUE_ID\n"); if (outputLength < sizeof(MOUNTDEV_UNIQUE_ID)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); break; } uniqueId = (PMOUNTDEV_UNIQUE_ID)Irp->AssociatedIrp.SystemBuffer; ASSERT(uniqueId != NULL); uniqueId->UniqueIdLength = dcb->DiskDeviceName->Length; if (sizeof(USHORT) + uniqueId->UniqueIdLength <= outputLength) { RtlCopyMemory((PCHAR)uniqueId->UniqueId, dcb->DiskDeviceName->Buffer, uniqueId->UniqueIdLength); Irp->IoStatus.Information = FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + uniqueId->UniqueIdLength; status = STATUS_SUCCESS; DDbgPrint(" UniqueName %wZ\n", dcb->DiskDeviceName); break; } else { Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); status = STATUS_BUFFER_OVERFLOW; } } break; case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: { PMOUNTDEV_SUGGESTED_LINK_NAME linkName; DDbgPrint(" IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME\n"); if (outputLength < sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); break; } linkName = (PMOUNTDEV_SUGGESTED_LINK_NAME)Irp->AssociatedIrp.SystemBuffer; ASSERT(linkName != NULL); if (dcb->MountPoint != NULL && dcb->MountPoint->Length > 0) { if (IsMountPointDriveLetter(dcb->MountPoint) == STATUS_SUCCESS) { linkName->UseOnlyIfThereAreNoOtherLinks = FALSE; linkName->NameLength = dcb->MountPoint->Length; if (sizeof(USHORT) + linkName->NameLength <= outputLength) { RtlCopyMemory((PCHAR)linkName->Name, dcb->MountPoint->Buffer, linkName->NameLength); Irp->IoStatus.Information = FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name[0]) + linkName->NameLength; status = STATUS_SUCCESS; DDbgPrint(" LinkName %wZ (%d)\n", dcb->MountPoint, dcb->MountPoint->Length); break; } else { Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); status = STATUS_BUFFER_OVERFLOW; } } else { DDbgPrint(" MountPoint %wZ is not a drive\n", dcb->MountPoint); status = STATUS_NOT_FOUND; } } else { DDbgPrint(" MountPoint is NULL or undefined\n"); status = STATUS_NOT_FOUND; } } break; case IOCTL_MOUNTDEV_LINK_CREATED: { PMOUNTDEV_NAME mountdevName = Irp->AssociatedIrp.SystemBuffer; DDbgPrint(" IOCTL_MOUNTDEV_LINK_CREATED\n"); status = STATUS_SUCCESS; if (!IsUnmountPending(DeviceObject) && mountdevName != NULL && mountdevName->NameLength > 0) { WCHAR *symbolicLinkNameBuf = ExAllocatePool((mountdevName->NameLength + 1) * sizeof(WCHAR)); if (symbolicLinkNameBuf == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlZeroMemory(symbolicLinkNameBuf, (mountdevName->NameLength + 1) * sizeof(WCHAR)); RtlCopyMemory(symbolicLinkNameBuf, mountdevName->Name, mountdevName->NameLength); DDbgPrint(" MountDev Name: %ws\n", symbolicLinkNameBuf); if (wcsncmp(symbolicLinkNameBuf, L"\\DosDevices\\", 12) == 0) { if (dcb->MountPoint != NULL && dcb->MountPoint->Length == 0) { ExFreePool(dcb->MountPoint); dcb->MountPoint = NULL; } if (!dcb->MountPoint || ((dcb->MountPoint->Length != mountdevName->NameLength || RtlCompareMemory(mountdevName->Name, dcb->MountPoint->Buffer, mountdevName->NameLength) != mountdevName->NameLength))) { DDbgPrint(" Update mount Point by %ws\n", symbolicLinkNameBuf); ExFreePool(dcb->MountPoint); dcb->MountPoint = DokanAllocateUnicodeString(symbolicLinkNameBuf); if (dcb->DiskDeviceName != NULL) { PMOUNT_ENTRY mountEntry; PDOKAN_CONTROL dokanControl = ExAllocatePool(sizeof(DOKAN_CONTROL)); if (dokanControl == NULL) { ExFreePool(symbolicLinkNameBuf); status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlZeroMemory(dokanControl, sizeof(*dokanControl)); RtlCopyMemory(dokanControl->DeviceName, dcb->DiskDeviceName->Buffer, dcb->DiskDeviceName->Length); if (dcb->UNCName->Buffer != NULL && dcb->UNCName->Length > 0) { RtlCopyMemory(dokanControl->UNCName, dcb->UNCName->Buffer, dcb->UNCName->Length); } mountEntry = FindMountEntry(dcb->Global, dokanControl, TRUE); ExFreePool(dokanControl); if (mountEntry != NULL) { RtlStringCchCopyW(mountEntry->MountControl.MountPoint, MAXIMUM_FILENAME_LENGTH, symbolicLinkNameBuf); } else { DDbgPrint(" Cannot found associated MountEntry.\n"); } } else { DDbgPrint( " DiskDeviceName is null. Is device currently unmounted?\n"); } } else { DDbgPrint(" Mount Point match, no need to update it.\n"); } } else { DDbgPrint(" Mount Point is not DosDevices, ignored.\n"); } ExFreePool(symbolicLinkNameBuf); } else { DDbgPrint(" MountDev Name is undefined or unmounting in progress.\n"); } } break; case IOCTL_MOUNTDEV_LINK_DELETED: { PMOUNTDEV_NAME mountdevName = Irp->AssociatedIrp.SystemBuffer; DDbgPrint(" IOCTL_MOUNTDEV_LINK_DELETED\n"); status = STATUS_SUCCESS; if (dcb->UseMountManager) { if (mountdevName != NULL && mountdevName->NameLength > 0) { WCHAR *symbolicLinkNameBuf = ExAllocatePool((mountdevName->NameLength + 1) * sizeof(WCHAR)); if (symbolicLinkNameBuf == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlZeroMemory(symbolicLinkNameBuf, (mountdevName->NameLength + 1) * sizeof(WCHAR)); RtlCopyMemory(symbolicLinkNameBuf, mountdevName->Name, mountdevName->NameLength); DDbgPrint(" MountDev Name: %ws\n", symbolicLinkNameBuf); if (dcb->MountPoint != NULL && dcb->MountPoint->Length > 0) { // If deleted mount point match user requested mount point, release // devices if (dcb->MountPoint->Length == mountdevName->NameLength && RtlCompareMemory(mountdevName->Name, dcb->MountPoint->Buffer, mountdevName->NameLength) == mountdevName->NameLength) { status = DokanEventRelease(vcb->DeviceObject, Irp); } else { DDbgPrint(" Deleted Mount Point doesn't match device excepted " "mount point.\n"); } } // Or, if no requested mount point, we assume the first deleted one // release devices else { status = DokanEventRelease(vcb->DeviceObject, Irp); } ExFreePool(symbolicLinkNameBuf); } else { DDbgPrint(" MountDev Name is undefined.\n"); } } else { DDbgPrint(" Mount Manager is not enabled for this device. Ignored.\n"); } } break; // case IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY: // DDbgPrint(" IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY\n"); // break; case IOCTL_MOUNTDEV_QUERY_STABLE_GUID: DDbgPrint(" IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n"); break; case IOCTL_VOLUME_ONLINE: DDbgPrint(" IOCTL_VOLUME_ONLINE\n"); status = STATUS_SUCCESS; break; case IOCTL_VOLUME_OFFLINE: DDbgPrint(" IOCTL_VOLUME_OFFLINE\n"); status = STATUS_SUCCESS; break; case IOCTL_VOLUME_READ_PLEX: DDbgPrint(" IOCTL_VOLUME_READ_PLEX\n"); break; case IOCTL_VOLUME_PHYSICAL_TO_LOGICAL: DDbgPrint(" IOCTL_VOLUME_PHYSICAL_TO_LOGICAL\n"); break; case IOCTL_VOLUME_IS_CLUSTERED: DDbgPrint(" IOCTL_VOLUME_IS_CLUSTERED\n"); break; case IOCTL_VOLUME_PREPARE_FOR_CRITICAL_IO: DDbgPrint(" IOCTL_VOLUME_PREPARE_FOR_CRITICAL_IO\n"); break; case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS: { PVOLUME_DISK_EXTENTS volume; ULONG bufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; DDbgPrint(" IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n"); if (bufferLength < sizeof(VOLUME_DISK_EXTENTS)) { status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; break; } volume = Irp->AssociatedIrp.SystemBuffer; RtlZeroMemory(volume, sizeof(VOLUME_DISK_EXTENTS)); volume->NumberOfDiskExtents = 1; Irp->IoStatus.Information = sizeof(VOLUME_DISK_EXTENTS); status = STATUS_SUCCESS; } break; case IOCTL_STORAGE_EJECT_MEDIA: { DDbgPrint(" IOCTL_STORAGE_EJECT_MEDIA\n"); DokanUnmount(dcb); status = STATUS_SUCCESS; } break; case IOCTL_REDIR_QUERY_PATH_EX: case IOCTL_REDIR_QUERY_PATH: { PQUERY_PATH_RESPONSE pathResp; BOOLEAN prefixOk = FALSE; if (dcb->UNCName != NULL && dcb->UNCName->Length > 0) { if (Irp->RequestorMode != KernelMode) { break; } WCHAR *lpPath = NULL; ULONG ulPath = 0; if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_REDIR_QUERY_PATH) { PQUERY_PATH_REQUEST pathReq; DDbgPrint(" IOCTL_REDIR_QUERY_PATH\n"); if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(QUERY_PATH_REQUEST)) { status = STATUS_BUFFER_OVERFLOW; break; } // Always a METHOD_NEITHER IOCTL pathReq = (PQUERY_PATH_REQUEST) irpSp->Parameters.DeviceIoControl.Type3InputBuffer; DDbgPrint(" PathNameLength = %d\n", pathReq->PathNameLength); DDbgPrint(" SecurityContext = %p\n", pathReq->SecurityContext); DDbgPrint(" FilePathName = %.*ls\n", (unsigned int)(pathReq->PathNameLength / sizeof(WCHAR)), pathReq->FilePathName); lpPath = pathReq->FilePathName; ulPath = pathReq->PathNameLength / sizeof(WCHAR); if (pathReq->PathNameLength >= dcb->UNCName->Length / sizeof(WCHAR)) { prefixOk = (_wcsnicmp(pathReq->FilePathName, dcb->UNCName->Buffer, dcb->UNCName->Length / sizeof(WCHAR)) == 0); } } else { PQUERY_PATH_REQUEST_EX pathReqEx; DDbgPrint(" IOCTL_REDIR_QUERY_PATH_EX\n"); if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(QUERY_PATH_REQUEST_EX)) { status = STATUS_BUFFER_OVERFLOW; break; } // Always a METHOD_NEITHER IOCTL pathReqEx = (PQUERY_PATH_REQUEST_EX) irpSp->Parameters.DeviceIoControl.Type3InputBuffer; DDbgPrint(" pSecurityContext = %p\n", pathReqEx->pSecurityContext); DDbgPrint(" EaLength = %d\n", pathReqEx->EaLength); DDbgPrint(" pEaBuffer = %p\n", pathReqEx->pEaBuffer); DDbgPrint(" PathNameLength = %d\n", pathReqEx->PathName.Length); DDbgPrint(" FilePathName = %*ls\n", (unsigned int)(pathReqEx->PathName.Length / sizeof(WCHAR)), pathReqEx->PathName.Buffer); lpPath = pathReqEx->PathName.Buffer; ulPath = pathReqEx->PathName.Length / sizeof(WCHAR); if (pathReqEx->PathName.Length >= dcb->UNCName->Length) { prefixOk = (_wcsnicmp(pathReqEx->PathName.Buffer, dcb->UNCName->Buffer, dcb->UNCName->Length / sizeof(WCHAR)) == 0); } } unsigned int i = 1; for (; i < ulPath && i * sizeof(WCHAR) < dcb->UNCName->Length && !prefixOk; ++i) { if (_wcsnicmp(&lpPath[i], &dcb->UNCName->Buffer[i], 1) != 0) { break; } if ((i + 1) * sizeof(WCHAR) < dcb->UNCName->Length) { prefixOk = (dcb->UNCName->Buffer[i + 1] == L'\\'); } } if (!prefixOk) { status = STATUS_BAD_NETWORK_PATH; break; } for (; i < ulPath && i * sizeof(WCHAR) < dcb->UNCName->Length && prefixOk; ++i) { if (_wcsnicmp(&lpPath[i], &dcb->UNCName->Buffer[i], 1) != 0) { prefixOk = FALSE; } } if (!prefixOk) { status = STATUS_BAD_NETWORK_NAME; break; } pathResp = (PQUERY_PATH_RESPONSE)Irp->UserBuffer; pathResp->LengthAccepted = dcb->UNCName->Length; status = STATUS_SUCCESS; } } break; case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: { PGET_MEDIA_TYPES mediaTypes = NULL; PDEVICE_MEDIA_INFO mediaInfo = NULL; //&mediaTypes->MediaInfo[0]; // We alway return only one media type DDbgPrint(" IOCTL_STORAGE_GET_MEDIA_TYPES_EX\n"); if (outputLength < sizeof(GET_MEDIA_TYPES)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; break; } mediaTypes = (PGET_MEDIA_TYPES)Irp->AssociatedIrp.SystemBuffer; ASSERT(mediaTypes != NULL); mediaInfo = &mediaTypes->MediaInfo[0]; mediaTypes->DeviceType = FILE_DEVICE_VIRTUAL_DISK; mediaTypes->MediaInfoCount = 1; PDISK_GEOMETRY diskGeometry = ExAllocatePool(sizeof(DISK_GEOMETRY)); if (diskGeometry == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlZeroMemory(diskGeometry, sizeof(*diskGeometry)); DokanPopulateDiskGeometry(diskGeometry); mediaInfo->DeviceSpecific.DiskInfo.MediaType = diskGeometry->MediaType; mediaInfo->DeviceSpecific.DiskInfo.NumberMediaSides = 1; mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics = (MEDIA_CURRENTLY_MOUNTED | MEDIA_READ_WRITE); mediaInfo->DeviceSpecific.DiskInfo.Cylinders.QuadPart = diskGeometry->Cylinders.QuadPart; mediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder = diskGeometry->TracksPerCylinder; mediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack = diskGeometry->SectorsPerTrack; mediaInfo->DeviceSpecific.DiskInfo.BytesPerSector = diskGeometry->BytesPerSector; ExFreePool(diskGeometry); status = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(GET_MEDIA_TYPES); } break; case IOCTL_STORAGE_GET_DEVICE_NUMBER: { PSTORAGE_DEVICE_NUMBER deviceNumber; DDbgPrint(" IOCTL_STORAGE_GET_DEVICE_NUMBER\n"); if (outputLength < sizeof(STORAGE_DEVICE_NUMBER)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); break; } deviceNumber = (PSTORAGE_DEVICE_NUMBER)Irp->AssociatedIrp.SystemBuffer; ASSERT(deviceNumber != NULL); deviceNumber->DeviceType = FILE_DEVICE_VIRTUAL_DISK; if (vcb) { deviceNumber->DeviceType = vcb->DeviceObject->DeviceType; } deviceNumber->DeviceNumber = 0; // Always one volume only per disk device deviceNumber->PartitionNumber = (ULONG)-1; // Not partitionable Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); status = STATUS_SUCCESS; } break; default: PrintUnknownDeviceIoctlCode( irpSp->Parameters.DeviceIoControl.IoControlCode); status = STATUS_INVALID_DEVICE_REQUEST; break; } DDbgPrint(" <= DokanDiskDeviceControl\n"); return status; }
// When user-mode file system application returns EventInformation, // search corresponding pending IRP and complete it NTSTATUS DokanCompleteIrp(__in PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) { KIRQL oldIrql; PLIST_ENTRY thisEntry, nextEntry, listHead; PIRP_ENTRY irpEntry; PDokanVCB vcb; PEVENT_INFORMATION eventInfo; eventInfo = (PEVENT_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ASSERT(eventInfo != NULL); // DDbgPrint("==> DokanCompleteIrp [EventInfo #%X]\n", // eventInfo->SerialNumber); vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB) { return STATUS_INVALID_PARAMETER; } if (IsUnmountPendingVcb(vcb)) { DDbgPrint(" Volume is not mounted\n"); return STATUS_NO_SUCH_DEVICE; } // DDbgPrint(" Lock IrpList.ListLock\n"); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(&vcb->Dcb->PendingIrp.ListLock, &oldIrql); // search corresponding IRP through pending IRP list listHead = &vcb->Dcb->PendingIrp.ListHead; for (thisEntry = listHead->Flink; thisEntry != listHead; thisEntry = nextEntry) { PIRP irp; PIO_STACK_LOCATION irpSp; nextEntry = thisEntry->Flink; irpEntry = CONTAINING_RECORD(thisEntry, IRP_ENTRY, ListEntry); // check whether this is corresponding IRP // DDbgPrint("SerialNumber irpEntry %X eventInfo %X\n", // irpEntry->SerialNumber, eventInfo->SerialNumber); // this irpEntry must be freed in this if statement if (irpEntry->SerialNumber != eventInfo->SerialNumber) { continue; } RemoveEntryList(thisEntry); irp = irpEntry->Irp; if (irp == NULL) { // this IRP is already canceled ASSERT(irpEntry->CancelRoutineFreeMemory == FALSE); DokanFreeIrpEntry(irpEntry); irpEntry = NULL; break; } if (IoSetCancelRoutine(irp, NULL) == NULL) { // Cancel routine will run as soon as we release the lock InitializeListHead(&irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = TRUE; break; } // IRP is not canceled yet irpSp = irpEntry->IrpSp; ASSERT(irpSp != NULL); // IrpEntry is saved here for CancelRoutine // Clear it to prevent to be completed by CancelRoutine twice irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_IRP_ENTRY] = NULL; KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); if (IsUnmountPendingVcb(vcb)) { DDbgPrint(" Volume is not mounted second check\n"); return STATUS_NO_SUCH_DEVICE; } if (eventInfo->Status == STATUS_PENDING) { DDbgPrint( " !!WARNING!! Do not return STATUS_PENDING DokanCompleteIrp!"); } switch (irpSp->MajorFunction) { case IRP_MJ_DIRECTORY_CONTROL: DokanCompleteDirectoryControl(irpEntry, eventInfo); break; case IRP_MJ_READ: DokanCompleteRead(irpEntry, eventInfo); break; case IRP_MJ_WRITE: DokanCompleteWrite(irpEntry, eventInfo); break; case IRP_MJ_QUERY_INFORMATION: DokanCompleteQueryInformation(irpEntry, eventInfo); break; case IRP_MJ_QUERY_VOLUME_INFORMATION: DokanCompleteQueryVolumeInformation(irpEntry, eventInfo, DeviceObject); break; case IRP_MJ_CREATE: DokanCompleteCreate(irpEntry, eventInfo); break; case IRP_MJ_CLEANUP: DokanCompleteCleanup(irpEntry, eventInfo); break; case IRP_MJ_LOCK_CONTROL: DokanCompleteLock(irpEntry, eventInfo); break; case IRP_MJ_SET_INFORMATION: DokanCompleteSetInformation(irpEntry, eventInfo); break; case IRP_MJ_FLUSH_BUFFERS: DokanCompleteFlush(irpEntry, eventInfo); break; case IRP_MJ_QUERY_SECURITY: DokanCompleteQuerySecurity(irpEntry, eventInfo); break; case IRP_MJ_SET_SECURITY: DokanCompleteSetSecurity(irpEntry, eventInfo); break; default: DDbgPrint("Unknown IRP %d\n", irpSp->MajorFunction); // TODO: in this case, should complete this IRP break; } DokanFreeIrpEntry(irpEntry); irpEntry = NULL; return STATUS_SUCCESS; } KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); // DDbgPrint("<== AACompleteIrp [EventInfo #%X]\n", eventInfo->SerialNumber); // TODO: should return error return STATUS_SUCCESS; }
NTSTATUS RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, __in ULONG SerialNumber, __in PIRP_LIST IrpList, __in ULONG Flags, __in ULONG CheckMount) { PIRP_ENTRY irpEntry; PIO_STACK_LOCATION irpSp; KIRQL oldIrql; PDokanVCB vcb = NULL; DDbgPrint("==> DokanRegisterPendingIrpMain\n"); if (GetIdentifierType(DeviceObject->DeviceExtension) == VCB) { vcb = DeviceObject->DeviceExtension; if (CheckMount && IsUnmountPendingVcb(vcb)) { DDbgPrint(" device is not mounted\n"); return STATUS_NO_SUCH_DEVICE; } } irpSp = IoGetCurrentIrpStackLocation(Irp); // Allocate a record and save all the event context. irpEntry = DokanAllocateIrpEntry(); if (NULL == irpEntry) { DDbgPrint(" can't allocate IRP_ENTRY\n"); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(irpEntry, sizeof(IRP_ENTRY)); InitializeListHead(&irpEntry->ListEntry); irpEntry->SerialNumber = SerialNumber; irpEntry->FileObject = irpSp->FileObject; irpEntry->Irp = Irp; irpEntry->IrpSp = irpSp; irpEntry->IrpList = IrpList; irpEntry->Flags = Flags; // Update the irp timeout for the entry if (vcb) { ExAcquireResourceExclusiveLite(&vcb->Dcb->Resource, TRUE); DokanUpdateTimeout(&irpEntry->TickCount, vcb->Dcb->IrpTimeout); ExReleaseResourceLite(&vcb->Dcb->Resource); } else { DokanUpdateTimeout(&irpEntry->TickCount, DOKAN_IRP_PENDING_TIMEOUT); } // DDbgPrint(" Lock IrpList.ListLock\n"); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(&IrpList->ListLock, &oldIrql); IoSetCancelRoutine(Irp, DokanIrpCancelRoutine); if (Irp->Cancel) { if (IoSetCancelRoutine(Irp, NULL) != NULL) { // DDbgPrint(" Release IrpList.ListLock %d\n", __LINE__); KeReleaseSpinLock(&IrpList->ListLock, oldIrql); DokanFreeIrpEntry(irpEntry); return STATUS_CANCELLED; } } IoMarkIrpPending(Irp); InsertTailList(&IrpList->ListHead, &irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = FALSE; // save the pointer in order to be accessed by cancel routine Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_IRP_ENTRY] = irpEntry; KeSetEvent(&IrpList->NotEmpty, IO_NO_INCREMENT, FALSE); // DDbgPrint(" Release IrpList.ListLock\n"); KeReleaseSpinLock(&IrpList->ListLock, oldIrql); DDbgPrint("<== DokanRegisterPendingIrpMain\n"); return STATUS_PENDING; }
NTSTATUS DokanDispatchCleanup(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) /*++ Routine Description: This device control dispatcher handles Cleanup IRP. Arguments: DeviceObject - Context for the activity. Irp - The device control argument block. Return Value: NTSTATUS --*/ { PDokanVCB vcb; PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_INVALID_PARAMETER; PFILE_OBJECT fileObject; PDokanCCB ccb = NULL; PDokanFCB fcb = NULL; PEVENT_CONTEXT eventContext; ULONG eventLength; DOKAN_INIT_LOGGER(logger, DeviceObject->DriverObject, IRP_MJ_CLEANUP); __try { DDbgPrint("==> DokanCleanup\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); // Cleanup must be success in any case if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_SUCCESS; __leave; } vcb = DeviceObject->DeviceExtension; if (vcb == NULL) { DDbgPrint(" No device extension\n"); status = STATUS_SUCCESS; __leave; } if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_SUCCESS; __leave; } ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); if (fcb->IsKeepalive) { DokanFCBLockRW(fcb); BOOLEAN shouldUnmount = ccb->IsKeepaliveActive; if (shouldUnmount) { // Here we intentionally let the VCB-level flag stay set, because // there's no sense in having an opportunity for an "operation timeout // unmount" in this case. ccb->IsKeepaliveActive = FALSE; } DokanFCBUnlock(fcb); if (shouldUnmount) { if (IsUnmountPendingVcb(vcb)) { DokanLogInfo(&logger, L"Ignoring keepalive close because unmount is already in" L" progress."); } else { DokanLogInfo(&logger, L"Unmounting due to keepalive close."); DokanUnmount(vcb->Dcb); } } } if (fcb->BlockUserModeDispatch) { status = STATUS_SUCCESS; __leave; } FlushFcb(fcb, fileObject); DokanFCBLockRW(fcb); eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length; eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); if (eventContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; DokanFCBUnlock(fcb); __leave; } fileObject->Flags |= FO_CLEANUP_COMPLETE; eventContext->Context = ccb->UserContext; eventContext->FileFlags |= DokanCCBFlagsGet(ccb); // DDbgPrint(" get Context %X\n", (ULONG)ccb->UserContext); // copy the filename to EventContext from ccb eventContext->Operation.Cleanup.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.Cleanup.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // FsRtlCheckOpLock is called with non-NULL completion routine - not blocking. status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, eventContext, DokanOplockComplete, DokanPrePostIrp); DokanFCBUnlock(fcb); // // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted // to service an oplock break and we need to leave now. // if (status != STATUS_SUCCESS) { if (status == STATUS_PENDING) { DDbgPrint(" FsRtlCheckOplock returned STATUS_PENDING\n"); } else { DokanFreeEventContext(eventContext); } __leave; } // register this IRP to pending IRP list status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); } __finally { DokanCompleteIrpRequest(Irp, status, 0); DDbgPrint("<== DokanCleanup\n"); } return status; }