diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 836293b8..8c4a20e9 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -197,7 +197,6 @@ typedef struct UINT64 Offset; UINT32 Length; UINT32 Key; - UINT32 Constrained:1; /* write's beyond EOF are NOP's (file size remains same) */ } Write; struct { diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 82cbc465..d6a5b1db 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -70,7 +70,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo); NTSTATUS (*Write)(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, - PVOID FileNode, PVOID Buffer, UINT64 Offset, ULONG Length, BOOLEAN Constrained, + PVOID FileNode, PVOID Buffer, UINT64 Offset, ULONG Length, BOOLEAN WriteToEndOfFile, PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo); NTSTATUS (*GetFileInfo)(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 2489be63..faffcf23 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -446,7 +446,7 @@ FSP_API NTSTATUS FspFileSystemOpWrite(FSP_FILE_SYSTEM *FileSystem, (PVOID)Request->Req.Write.Address, Request->Req.Write.Offset, Request->Req.Write.Length, - Request->Req.Write.Constrained, + (UINT64)-1LL == Request->Req.Write.Offset, &BytesTransferred, &FileInfo); if (!NT_SUCCESS(Result)) diff --git a/src/sys/callbacks.c b/src/sys/callbacks.c index 3a01727e..9f486f56 100644 --- a/src/sys/callbacks.c +++ b/src/sys/callbacks.c @@ -174,7 +174,10 @@ BOOLEAN FspAcquireForLazyWrite( ASSERT(0 == IoGetTopLevelIrp()); Result = FspFileNodeTryAcquireExclusiveF(FileNode, FspFileNodeAcquireFull, Wait); if (Result) + { + FileNode->LazyWriteThread = PsGetCurrentThread(); IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + } FSP_LEAVE_BOOL("Context=%p, Wait=%d", Context, Wait); } @@ -192,6 +195,7 @@ VOID FspReleaseFromLazyWrite( ASSERT((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP == IoGetTopLevelIrp()); IoSetTopLevelIrp(0); + FileNode->LazyWriteThread = 0; FspFileNodeRelease(FileNode, Full); FSP_LEAVE_VOID("Context=%p", Context); diff --git a/src/sys/driver.h b/src/sys/driver.h index 7e0e9288..f8799e93 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -793,6 +793,7 @@ typedef struct UINT64 Security; ULONG SecurityChangeNumber; BOOLEAN TruncateOnClose; + PVOID LazyWriteThread; /* read-only after creation (and insertion in the ContextTable) */ PDEVICE_OBJECT FsvolDeviceObject; UINT64 UserContext; diff --git a/src/sys/read.c b/src/sys/read.c index be5d2e7c..8ab70706 100644 --- a/src/sys/read.c +++ b/src/sys/read.c @@ -88,6 +88,9 @@ static NTSTATUS FspFsvolReadCached( { PAGED_CODE(); + /* assert: must be top-level IRP */ + ASSERT(0 == FspIrpTopFlags(Irp)); + NTSTATUS Result; PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; @@ -151,6 +154,11 @@ static NTSTATUS FspFsvolReadCached( Result = FspCcCopyRead(FileObject, &ReadOffset, ReadLength, CanWait, Buffer, &Irp->IoStatus); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Main); + return Result; + } if (STATUS_PENDING == Result) { FspFileNodeRelease(FileNode, Main); @@ -184,6 +192,9 @@ static NTSTATUS FspFsvolReadNonCached( { PAGED_CODE(); + /* assert: either a top-level IRP or Paging I/O */ + ASSERT(0 == FspIrpTopFlags(Irp) || FlagOn(Irp->Flags, IRP_PAGING_IO)); + NTSTATUS Result; PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; @@ -191,6 +202,7 @@ static NTSTATUS FspFsvolReadNonCached( LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset; ULONG ReadLength = IrpSp->Parameters.Read.Length; ULONG ReadKey = IrpSp->Parameters.Read.Key; + FSP_FSCTL_FILE_INFO FileInfo; FSP_FSCTL_TRANSACT_REQ *Request; ASSERT(FileNode == FileDesc->FileNode); @@ -199,6 +211,12 @@ static NTSTATUS FspFsvolReadNonCached( if (FlagOn(IrpSp->MinorFunction, IRP_MN_MDL)) return STATUS_INVALID_PARAMETER; + /* if this is a recursive read (cache) see if we can optimize it away! */ + if (FlagOn(FspIrpTopFlags(Irp), FspFileNodeAcquireMain) && /* if TopLevelIrp has acquired Main */ + FspFileNodeTryGetFileInfo(FileNode, &FileInfo) && /* and the cached FileSize is valid */ + (UINT64)ReadOffset.QuadPart >= FileInfo.FileSize) /* and the ReadOffset is past EOF */ + return STATUS_END_OF_FILE; + /* probe and lock the user buffer */ if (0 == Irp->MdlAddress) { @@ -306,18 +324,19 @@ NTSTATUS FspFsvolReadComplete( if (0 != SafeMdl) FspSafeMdlCopyBack(SafeMdl); - if (!PagingIo) + /* if we are top-level */ + if (0 == FspIrpTopFlags(Irp)) { if (FspFsctlTransactReadKind == Request->Kind) { InfoChangeNumber = FileNode->InfoChangeNumber; - FspIopResetRequest(Request, 0); - Request->Kind = FspFsctlTransactReservedKind; + FspIopResetRequest(Request, 0); FspIopRequestContext(Request, RequestInfoChangeNumber) = (PVOID)InfoChangeNumber; } else - InfoChangeNumber = (ULONG)(UINT_PTR)FspIopRequestContext(Request, RequestInfoChangeNumber); + InfoChangeNumber = + (ULONG)(UINT_PTR)FspIopRequestContext(Request, RequestInfoChangeNumber); Success = DEBUGTEST(90, TRUE) && FspFileNodeTryAcquireExclusive(FileNode, Main); if (!Success) @@ -326,18 +345,22 @@ NTSTATUS FspFsvolReadComplete( FSP_RETURN(); } + /* update file info */ FspFileNodeTrySetFileInfo(FileNode, FileObject, &Response->Rsp.Read.FileInfo, InfoChangeNumber); /* update the current file offset if synchronous I/O (and not paging I/O) */ - if (SynchronousIo) + if (SynchronousIo && !PagingIo) FileObject->CurrentByteOffset.QuadPart = ReadOffset.QuadPart + Response->IoStatus.Information; FspFileNodeRelease(FileNode, Main); } else + { + ASSERT(PagingIo); FspIopResetRequest(Request, 0); + } Irp->IoStatus.Information = Response->IoStatus.Information; Result = STATUS_SUCCESS; diff --git a/src/sys/write.c b/src/sys/write.c index 3fd1ca12..986fc4ef 100644 --- a/src/sys/write.c +++ b/src/sys/write.c @@ -88,6 +88,9 @@ static NTSTATUS FspFsvolWriteCached( { PAGED_CODE(); + /* assert: must be top-level IRP */ + ASSERT(0 == FspIrpTopFlags(Irp)); + NTSTATUS Result; BOOLEAN Retrying = 0 != FspIrpRequest(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; @@ -104,6 +107,7 @@ static NTSTATUS FspFsvolWriteCached( FSP_FSCTL_FILE_INFO FileInfo; CC_FILE_SIZES FileSizes; UINT64 WriteEndOffset; + BOOLEAN ExtendingFile; BOOLEAN Success; /* should we defer the write? */ @@ -132,22 +136,19 @@ static NTSTATUS FspFsvolWriteCached( ASSERT(FspTimeoutInfinity32 == FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.FileInfoTimeout); FspFileNodeGetFileInfo(FileNode, &FileInfo); - FileSizes.AllocationSize.QuadPart = FileInfo.AllocationSize; - FileSizes.FileSize.QuadPart = FileInfo.FileSize; - FileSizes.ValidDataLength.QuadPart = MAXLONGLONG; WriteEndOffset = WriteToEndOfFile ? FileInfo.FileSize + WriteLength : WriteOffset.QuadPart + WriteLength; - if (FileInfo.FileSize < WriteEndOffset) + ExtendingFile = FileInfo.FileSize < WriteEndOffset; + if (ExtendingFile) { - /* file is being extended */ - FileSizes.FileSize.QuadPart = WriteEndOffset; - if (FileSizes.FileSize.QuadPart > FileSizes.AllocationSize.QuadPart) + FileInfo.FileSize = WriteEndOffset; + if (FileInfo.FileSize > FileInfo.AllocationSize) { FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); UINT64 AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize * FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit; - FileSizes.AllocationSize.QuadPart = (FileSizes.FileSize.QuadPart + AllocationUnit - 1) + FileInfo.AllocationSize = (FileInfo.FileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; } } @@ -155,6 +156,10 @@ static NTSTATUS FspFsvolWriteCached( /* initialize cache if not already initialized! */ if (0 == FileObject->PrivateCacheMap) { + FileSizes.AllocationSize.QuadPart = FileInfo.AllocationSize; + FileSizes.FileSize.QuadPart = FileInfo.FileSize; + FileSizes.ValidDataLength.QuadPart = MAXLONGLONG; + Result = FspCcInitializeCacheMap(FileObject, &FileSizes, FALSE, &FspCacheManagerCallbacks, FileNode); if (!NT_SUCCESS(Result)) @@ -163,8 +168,12 @@ static NTSTATUS FspFsvolWriteCached( return Result; } } - else if (FileInfo.FileSize < WriteEndOffset) + else if (ExtendingFile) { + FileSizes.AllocationSize.QuadPart = FileInfo.AllocationSize; + FileSizes.FileSize.QuadPart = FileInfo.FileSize; + FileSizes.ValidDataLength.QuadPart = MAXLONGLONG; + /* file is being extended */ Result = FspCcSetFileSizes(FileObject, &FileSizes); if (!NT_SUCCESS(Result)) @@ -188,6 +197,11 @@ static NTSTATUS FspFsvolWriteCached( } Result = FspCcCopyWrite(FileObject, &WriteOffset, WriteLength, CanWait, Buffer); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Main); + return Result; + } if (STATUS_PENDING == Result) { FspFileNodeRelease(FileNode, Main); @@ -213,6 +227,9 @@ static NTSTATUS FspFsvolWriteCached( if (SynchronousIo) FileObject->CurrentByteOffset.QuadPart = WriteEndOffset; + if (ExtendingFile) + FspFileNodeSetFileInfo(FileNode, 0, &FileInfo); + FspFileNodeRelease(FileNode, Main); return STATUS_SUCCESS; @@ -228,6 +245,9 @@ static NTSTATUS FspFsvolWriteNonCached( { PAGED_CODE(); + /* assert: either a top-level IRP or Paging I/O */ + ASSERT(0 == FspIrpTopFlags(Irp) || FlagOn(Irp->Flags, IRP_PAGING_IO)); + NTSTATUS Result; PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; @@ -238,6 +258,7 @@ static NTSTATUS FspFsvolWriteNonCached( BOOLEAN WriteToEndOfFile = FILE_WRITE_TO_END_OF_FILE == WriteOffset.LowPart && -1L == WriteOffset.HighPart; BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + FSP_FSCTL_FILE_INFO FileInfo; FSP_FSCTL_TRANSACT_REQ *Request; ASSERT(FileNode == FileDesc->FileNode); @@ -254,6 +275,23 @@ static NTSTATUS FspFsvolWriteNonCached( if (FspIoqStopped(FspFsvolDeviceExtension(FsvolDeviceObject)->Ioq)) return FspFsvolDeviceStoppedStatus(FsvolDeviceObject); + /* if we are called by the lazy writer we must constrain writes */ + if (FlagOn(FspIrpTopFlags(Irp), FspFileNodeAcquireMain) && /* if TopLevelIrp has acquired Main */ + FileNode->LazyWriteThread == PsGetCurrentThread()) /* and this is a lazy writer thread */ + { + ASSERT(PagingIo); + ASSERT(FspTimeoutInfinity32 == + FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.FileInfoTimeout); + + FspFileNodeGetFileInfo(FileNode, &FileInfo); + + if ((UINT64)WriteOffset.QuadPart >= FileInfo.FileSize) + return STATUS_SUCCESS; + + if (WriteLength > (ULONG)(FileInfo.FileSize - WriteOffset.QuadPart)) + WriteLength = (ULONG)(FileInfo.FileSize - WriteOffset.QuadPart); + } + /* probe and lock the user buffer */ if (0 == Irp->MdlAddress) { @@ -274,7 +312,6 @@ static NTSTATUS FspFsvolWriteNonCached( Request->Req.Write.Offset = WriteOffset.QuadPart; Request->Req.Write.Length = WriteLength; Request->Req.Write.Key = WriteKey; - Request->Req.Write.Constrained = PagingIo; return FSP_STATUS_IOQ_POST; } @@ -386,19 +423,25 @@ NTSTATUS FspFsvolWriteComplete( BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); - if (!PagingIo) + /* if we are top-level */ + if (0 == FspIrpTopFlags(Irp)) { /* update file info */ FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.Write.FileInfo); /* update the current file offset if synchronous I/O (and not paging I/O) */ - if (SynchronousIo) + if (SynchronousIo && !PagingIo) FileObject->CurrentByteOffset.QuadPart = WriteToEndOfFile ? Response->Rsp.Write.FileInfo.FileSize : WriteOffset.QuadPart + Response->IoStatus.Information; - } - FspIopResetRequest(Request, 0); + FspIopResetRequest(Request, 0); + } + else + { + ASSERT(PagingIo); + FspIopResetRequest(Request, 0); + } Irp->IoStatus.Information = Response->IoStatus.Information; Result = STATUS_SUCCESS; diff --git a/tst/winfsp-tests/memfs.cpp b/tst/winfsp-tests/memfs.cpp index 18c3b68c..34a94eff 100644 --- a/tst/winfsp-tests/memfs.cpp +++ b/tst/winfsp-tests/memfs.cpp @@ -438,33 +438,17 @@ static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, - PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length, BOOLEAN Constrained, + PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length, BOOLEAN WriteToEndOfFile, PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo) { MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; UINT64 EndOffset; - if ((UINT64)-1LL == Offset) + if (WriteToEndOfFile) Offset = FileNode->FileInfo.FileSize; - - if (Constrained) - { - if (Offset >= FileNode->FileInfo.FileSize) - { - *PBytesTransferred = 0; - return STATUS_SUCCESS; - } - - EndOffset = Offset + Length; - if (EndOffset > FileNode->FileInfo.FileSize) - EndOffset = FileNode->FileInfo.FileSize; - } - else - { - EndOffset = Offset + Length; - if (EndOffset > FileNode->FileInfo.FileSize) - SetFileSize(FileSystem, Request, FileNode, EndOffset, FileInfo); - } + EndOffset = Offset + Length; + if (EndOffset > FileNode->FileInfo.FileSize) + SetFileSize(FileSystem, Request, FileNode, EndOffset, FileInfo); memcpy((PUINT8)FileNode->FileData + Offset, Buffer, EndOffset - Offset);