/** * @file sys/fsctl.c * * @copyright 2015 Bill Zissimopoulos */ #include static NTSTATUS FspFsctlCreateVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvrtDeleteVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvrtTransact( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsctlFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvrtFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvolFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); DRIVER_DISPATCH FspFileSystemControl; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspFsctlCreateVolume) #pragma alloc_text(PAGE, FspFsvrtDeleteVolume) #pragma alloc_text(PAGE, FspFsvrtTransact) #pragma alloc_text(PAGE, FspFsctlFileSystemControl) #pragma alloc_text(PAGE, FspFsvrtFileSystemControl) #pragma alloc_text(PAGE, FspFsvolFileSystemControl) #pragma alloc_text(PAGE, FspFileSystemControl) #endif static NTSTATUS FspFsctlCreateVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { /* check parameters */ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; PVOID SystemBuffer = Irp->AssociatedIrp.SystemBuffer; if (0 == InputBufferLength || 0 == SystemBuffer || !RtlValidRelativeSecurityDescriptor(SystemBuffer, InputBufferLength, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION)) return STATUS_INVALID_PARAMETER; if (FSP_FSCTL_CREATE_BUFFER_SIZE > OutputBufferLength) return STATUS_BUFFER_TOO_SMALL; NTSTATUS Result; /* create volume guid */ GUID Guid; Result = FspCreateGuid(&Guid); if (!NT_SUCCESS(Result)) return Result; /* copy the security descriptor from the system buffer to a temporary one */ PVOID SecurityDescriptor = ExAllocatePoolWithTag(PagedPool, InputBufferLength, FSP_TAG); if (0 == SecurityDescriptor) return STATUS_INSUFFICIENT_RESOURCES; RtlCopyMemory(SecurityDescriptor, SystemBuffer, InputBufferLength); /* create the virtual volume device */ PDEVICE_OBJECT FsvrtDeviceObject; UNICODE_STRING DeviceSddl; UNICODE_STRING DeviceName; RtlInitUnicodeString(&DeviceSddl, L"" FSP_FSVRT_DEVICE_SDDL); RtlInitEmptyUnicodeString(&DeviceName, SystemBuffer, FSP_FSCTL_CREATE_BUFFER_SIZE); Result = RtlUnicodeStringPrintf(&DeviceName, L"\\Device\\Volume{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", Guid.Data1, Guid.Data2, Guid.Data3, Guid.Data4[0], Guid.Data4[1], Guid.Data4[2], Guid.Data4[3], Guid.Data4[4], Guid.Data4[5], Guid.Data4[6], Guid.Data4[7]); ASSERT(NT_SUCCESS(Result)); Result = IoCreateDeviceSecure(DeviceObject->DriverObject, sizeof(FSP_FSVRT_DEVICE_EXTENSION) + InputBufferLength, &DeviceName, FILE_DEVICE_VIRTUAL_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceSddl, 0, &FsvrtDeviceObject); if (NT_SUCCESS(Result)) { FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject); FsvrtDeviceExtension->Base.Kind = FspFsvrtDeviceExtensionKind; FspIoqInitialize(&FsvrtDeviceExtension->Ioq); RtlCopyMemory(FspFsvrtDeviceExtension(FsvrtDeviceObject)->SecurityDescriptorBuf, SecurityDescriptor, InputBufferLength); Irp->IoStatus.Information = DeviceName.Length + 1; } /* free the temporary security descriptor */ ExFreePoolWithTag(SecurityDescriptor, FSP_TAG); return Result; } static NTSTATUS FspFsvrtDeleteVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { NTSTATUS Result; FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject); /* access check */ Result = FspSecuritySubjectContextAccessCheck( FsvrtDeviceExtension->SecurityDescriptorBuf, FILE_WRITE_DATA, Irp->RequestorMode); if (!NT_SUCCESS(Result)) return Result; return STATUS_INVALID_DEVICE_REQUEST; } static NTSTATUS FspFsvrtTransact( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { /* check parameters */ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; PVOID SystemBuffer = Irp->AssociatedIrp.SystemBuffer; if (sizeof(FSP_TRANSACT_RSP) > InputBufferLength || 0 == SystemBuffer) return STATUS_INVALID_PARAMETER; if (FSP_FSCTL_TRANSACT_BUFFER_SIZE > OutputBufferLength) return STATUS_BUFFER_TOO_SMALL; NTSTATUS Result; FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject); PUINT8 SystemBufferEnd; const FSP_TRANSACT_RSP *Response, *NextResponse; FSP_TRANSACT_REQ *Request, *NextRequest, *PendingIrpRequest; PIRP ProcessIrp, PendingIrp; /* access check */ Result = FspSecuritySubjectContextAccessCheck( FsvrtDeviceExtension->SecurityDescriptorBuf, FILE_WRITE_DATA, Irp->RequestorMode); if (!NT_SUCCESS(Result)) return Result; /* process any user-mode file system responses */ Response = SystemBuffer; SystemBufferEnd = (PUINT8)SystemBuffer + InputBufferLength; for (;;) { NextResponse = FspFsctlTransactConsumeResponse(Response, SystemBufferEnd); if (0 == NextResponse) break; ProcessIrp = FspIoqEndProcessingIrp(&FsvrtDeviceExtension->Ioq, (UINT_PTR)Response->Hint); if (0 == ProcessIrp) /* either IRP was canceled or a bogus Hint was provided */ continue; //FspDispatchProcessedIrp(ProcessIrp, Response); Response = NextResponse; } /* wait for an IRP to arrive */ while (0 == (PendingIrp = FspIoqNextPendingIrp(&FsvrtDeviceExtension->Ioq, (ULONG)-1L))) { if (FspIoqStopped(&FsvrtDeviceExtension->Ioq)) return STATUS_CANCELLED; } /* send any pending IRP's to the user-mode file system */ Request = SystemBuffer; SystemBufferEnd = (PUINT8)SystemBuffer + OutputBufferLength; for (;;) { PendingIrpRequest = PendingIrp->Tail.Overlay.DriverContext[0]; NextRequest = FspFsctlTransactProduceRequest( Request, PendingIrpRequest->Size, SystemBufferEnd); ASSERT(0 != NextRequest || Request != SystemBuffer); if (0 == NextRequest) break; RtlCopyMemory(Request, PendingIrpRequest, PendingIrpRequest->Size); Request = NextRequest; if (!FspIoqStartProcessingIrp(&FsvrtDeviceExtension->Ioq, PendingIrp)) { /* * This can only happen if the Ioq was stopped. Abandon everything * and return STATUS_CANCELLED. Any IRP's in the Pending and Process * queues of the Ioq will be cancelled during FspIoqStop(). We must * also cancel the PendingIrp we have in our hands. */ ASSERT(FspIoqStopped(&FsvrtDeviceExtension->Ioq)); FspCompleteRequest(PendingIrp, STATUS_CANCELLED); return STATUS_CANCELLED; } /* check that we have enough space before pulling the next pending IRP off the queue */ if ((PUINT8)Request + FSP_FSCTL_TRANSACT_REQ_SIZEMAX > SystemBufferEnd) break; PendingIrp = FspIoqNextPendingIrp(&FsvrtDeviceExtension->Ioq, 0); if (0 == PendingIrp) break; } Irp->IoStatus.Information = (PUINT8)Request - (PUINT8)SystemBuffer; return STATUS_SUCCESS; } static NTSTATUS FspFsctlFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST; switch (IrpSp->MinorFunction) { case IRP_MN_USER_FS_REQUEST: switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { case FSP_FSCTL_CREATE: Result = FspFsctlCreateVolume(DeviceObject, Irp, IrpSp); break; } } return Result; } static NTSTATUS FspFsvrtFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST; switch (IrpSp->MinorFunction) { case IRP_MN_USER_FS_REQUEST: switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { case FSP_FSCTL_DELETE: Result = FspFsvrtDeleteVolume(DeviceObject, Irp, IrpSp); break; case FSP_FSCTL_TRANSACT: Result = FspFsvrtTransact(DeviceObject, Irp, IrpSp); break; } break; } return Result; } static NTSTATUS FspFsvolFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST; switch (IrpSp->MinorFunction) { case IRP_MN_USER_FS_REQUEST: break; case IRP_MN_MOUNT_VOLUME: break; case IRP_MN_VERIFY_VOLUME: break; } return Result; } NTSTATUS FspFileSystemControl( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) { FSP_ENTER_MJ(PAGED_CODE()); ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); switch (FspDeviceExtension(DeviceObject)->Kind) { case FspFsvolDeviceExtensionKind: FSP_RETURN(Result = FspFsctlFileSystemControl(DeviceObject, Irp, IrpSp)); case FspFsvrtDeviceExtensionKind: FSP_RETURN(Result = FspFsvrtFileSystemControl(DeviceObject, Irp, IrpSp)); case FspFsctlDeviceExtensionKind: FSP_RETURN(Result = FspFsvolFileSystemControl(DeviceObject, Irp, IrpSp)); default: FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); } FSP_LEAVE_MJ( "FileObject=%p%s%s", IrpSp->FileObject, IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? ", " : "", IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? IoctlCodeSym(IrpSp->Parameters.FileSystemControl.FsControlCode) : ""); }