diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 6b14e9af..dc880aeb 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -184,6 +184,10 @@ typedef struct { UINT64 UserContext; UINT64 UserContext2; + UINT64 Address; + UINT64 Offset; + UINT32 Length; + UINT32 Key; } Read; struct { diff --git a/src/sys/driver.c b/src/sys/driver.c index 4c2fe17a..038c77a7 100644 --- a/src/sys/driver.c +++ b/src/sys/driver.c @@ -70,6 +70,7 @@ NTSTATUS DriverEntry( FspIopPrepareFunction[IRP_MJ_CREATE] = FspFsvolCreatePrepare; FspIopCompleteFunction[IRP_MJ_CREATE] = FspFsvolCreateComplete; FspIopCompleteFunction[IRP_MJ_CLOSE] = FspFsvolCloseComplete; + FspIopPrepareFunction[IRP_MJ_READ] = FspFsvolReadPrepare; FspIopCompleteFunction[IRP_MJ_READ] = FspFsvolReadComplete; FspIopPrepareFunction[IRP_MJ_WRITE] = FspFsvolWritePrepare; FspIopCompleteFunction[IRP_MJ_WRITE] = FspFsvolWriteComplete; diff --git a/src/sys/driver.h b/src/sys/driver.h index ff95e529..599e4305 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -262,6 +262,7 @@ FSP_IOCMPL_DISPATCH FspFsvolQueryEaComplete; FSP_IOCMPL_DISPATCH FspFsvolQueryInformationComplete; FSP_IOCMPL_DISPATCH FspFsvolQuerySecurityComplete; FSP_IOCMPL_DISPATCH FspFsvolQueryVolumeInformationComplete; +FSP_IOPREP_DISPATCH FspFsvolReadPrepare; FSP_IOCMPL_DISPATCH FspFsvolReadComplete; FSP_IOCMPL_DISPATCH FspFsvolSetEaComplete; FSP_IOCMPL_DISPATCH FspFsvolSetInformationComplete; diff --git a/src/sys/read.c b/src/sys/read.c index e3662a32..9bc00b59 100644 --- a/src/sys/read.c +++ b/src/sys/read.c @@ -13,6 +13,7 @@ static NTSTATUS FspFsvolReadCached( BOOLEAN CanWait); static NTSTATUS FspFsvolReadNonCached( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +FSP_IOPREP_DISPATCH FspFsvolReadPrepare; FSP_IOCMPL_DISPATCH FspFsvolReadComplete; static FSP_IOP_REQUEST_FINI FspFsvolReadNonCachedRequestFini; FSP_DRIVER_DISPATCH FspRead; @@ -21,11 +22,21 @@ FSP_DRIVER_DISPATCH FspRead; #pragma alloc_text(PAGE, FspFsvolRead) #pragma alloc_text(PAGE, FspFsvolReadCached) #pragma alloc_text(PAGE, FspFsvolReadNonCached) +#pragma alloc_text(PAGE, FspFsvolReadPrepare) #pragma alloc_text(PAGE, FspFsvolReadComplete) #pragma alloc_text(PAGE, FspFsvolReadNonCachedRequestFini) #pragma alloc_text(PAGE, FspRead) #endif +enum +{ + /* ReadNonCached */ + RequestIrp = 0, + RequestSafeMdl = 1, + RequestAddress = 2, + RequestProcess = 3, +}; + static NTSTATUS FspFsvolRead( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -164,7 +175,101 @@ static NTSTATUS FspFsvolReadNonCached( { PAGED_CODE(); - return STATUS_INVALID_DEVICE_REQUEST; + NTSTATUS Result; + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset; + ULONG ReadLength = IrpSp->Parameters.Read.Length; + ULONG ReadKey = IrpSp->Parameters.Read.Key; + FSP_FSCTL_TRANSACT_REQ *Request; + + ASSERT(FileNode == FileDesc->FileNode); + + /* no MDL requests on the non-cached path */ + if (FlagOn(IrpSp->MinorFunction, IRP_MN_MDL)) + return STATUS_INVALID_PARAMETER; + + /* probe and lock the user buffer */ + if (0 == Irp->MdlAddress) + { + Result = FspLockUserBuffer(Irp->UserBuffer, ReadLength, + Irp->RequestorMode, IoReadAccess, &Irp->MdlAddress); + if (!NT_SUCCESS(Result)) + return Result; + } + + /* create request */ + Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolReadNonCachedRequestFini, &Request); + if (!NT_SUCCESS(Result)) + return Result; + + Request->Kind = FspFsctlTransactReadKind; + Request->Req.Read.UserContext = FileNode->UserContext; + Request->Req.Read.UserContext2 = FileDesc->UserContext2; + Request->Req.Read.Offset = ReadOffset.QuadPart; + Request->Req.Read.Length = ReadLength; + Request->Req.Read.Key = ReadKey; + + return FSP_STATUS_IOQ_POST; +} + +NTSTATUS FspFsvolReadPrepare( + PIRP Irp, FSP_FSCTL_TRANSACT_REQ *Request) +{ + PAGED_CODE(); + + NTSTATUS Result; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_SAFE_MDL *SafeMdl = 0; + PVOID Address; + PEPROCESS Process; + BOOLEAN Success; + + Success = DEBUGTEST(90, TRUE) && FspFileNodeTryAcquireShared(FileNode, Full); + if (!Success) + { + FspIopRetryPrepareIrp(Irp, &Result); + return Result; + } + + /* create a "safe" MDL if necessary */ + if (!FspSafeMdlCheck(Irp->MdlAddress)) + { + Result = FspSafeMdlCreate(Irp->MdlAddress, IoWriteAccess, &SafeMdl); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + /* map the MDL into user-mode */ + Result = FspMapLockedPagesInUserMode(0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address); + if (!NT_SUCCESS(Result)) + { + if (0 != SafeMdl) + FspSafeMdlDelete(SafeMdl); + + FspFileNodeRelease(FileNode, Full); + return Result; + } + + /* get a pointer to the current process so that we can unmap the address later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.Read.Address = (UINT64)(UINT_PTR)Address; + + FspFileNodeSetOwner(FileNode, Full, Request); + FspIopRequestContext(Request, RequestIrp) = Irp; + FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; } NTSTATUS FspFsvolReadComplete( @@ -172,6 +277,29 @@ NTSTATUS FspFsvolReadComplete( { FSP_ENTER_IOC(PAGED_CODE()); + PFILE_OBJECT FileObject = IrpSp->FileObject; + LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset; + BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); + + if (!NT_SUCCESS(Response->IoStatus.Status)) + { + Irp->IoStatus.Information = 0; + Result = Response->IoStatus.Status; + FSP_RETURN(); + } + + if (!PagingIo) + { + /* update the current file offset if synchronous I/O (and not paging I/O) */ + if (SynchronousIo) + FileObject->CurrentByteOffset.QuadPart = + ReadOffset.QuadPart + Response->IoStatus.Information; + } + + Irp->IoStatus.Information = Response->IoStatus.Information; + Result = STATUS_SUCCESS; + FSP_LEAVE_IOC( "FileObject=%p, UserBuffer=%p, MdlAddress=%p, " "Key=%#lx, ByteOffset=%#lx:%#lx, Length=%ld", @@ -181,6 +309,44 @@ NTSTATUS FspFsvolReadComplete( IrpSp->Parameters.Read.Length); } +static VOID FspFsvolReadNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]) +{ + PAGED_CODE(); + + PIRP Irp = Context[RequestIrp]; + FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; + + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; + + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); + + if (Attach) + KeStackAttachProcess(Process, &ApcState); + MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } + + if (0 != SafeMdl) + FspSafeMdlDelete(SafeMdl); + + if (0 != Irp) + { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + FSP_FILE_NODE *FileNode = IrpSp->FileObject->FsContext; + + FspFileNodeReleaseOwner(FileNode, Full, Request); + } +} + NTSTATUS FspRead( PDEVICE_OBJECT DeviceObject, PIRP Irp) { diff --git a/src/sys/write.c b/src/sys/write.c index c5b1dfac..87a53123 100644 --- a/src/sys/write.c +++ b/src/sys/write.c @@ -251,38 +251,10 @@ static NTSTATUS FspFsvolWriteNonCached( if (PagingIo && WriteToEndOfFile) return STATUS_INVALID_PARAMETER; - /* if non-cached I/O check the offset/length alignment */ - /* - * We are going to avoid doing this test, because we don't really need to restrict - * ourselves for non-cached I/O, but also because we do not always know the correct - * file size for our alignment test. The file size is needed, because the alignment - * test is: - * - * if WriteOffset is sector aligned - * and (WriteLength is sector aligned or WriteOffset + WriteLength >= FileSize) - * - * This means that the user-mode file system must be able to deal with variable size - * I/O, but this was the case anyway because of the following part of the test: - * - * WriteOffset + WriteLength >= FileSize - * - * In any case the user-mode file system can enforce this rule if it wants! - */ -#if 0 - if (!PagingIo) - { - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = - FspFsvolDeviceExtension(FsvolDeviceObject); - if (0 != WriteOffset.QuadPart % FsvolDeviceExtension->VolumeParams.SectorSize || - 0 != WriteLength % FsvolDeviceExtension->VolumeParams.SectorSize) - return STATUS_NOT_IMPLEMENTED; /* FastFat does this! */ - } -#endif - /* probe and lock the user buffer */ if (0 == Irp->MdlAddress) { - Result = FspLockUserBuffer(Irp->UserBuffer, IrpSp->Parameters.Write.Length, + Result = FspLockUserBuffer(Irp->UserBuffer, WriteLength, Irp->RequestorMode, IoReadAccess, &Irp->MdlAddress); if (!NT_SUCCESS(Result)) return Result; @@ -382,7 +354,7 @@ NTSTATUS FspFsvolWritePrepare( Request->Req.Write.Address = (UINT64)(UINT_PTR)Address; - FspFileNodeSetOwner(FileNode, Pgio, Request); + FspFileNodeSetOwner(FileNode, Full, Request); FspIopRequestContext(Request, RequestIrp) = Irp; FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl; FspIopRequestContext(Request, RequestAddress) = Address;