From 85470b1b8eb9d33a9b17d1cf7993a7a8e7544894 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Wed, 26 Jul 2017 09:28:41 +0000 Subject: [PATCH] [RDBSS] - Implement RxFastIoWrite() - Finish implementation of RxFastIoCheckIfPossible() [COPYSUP] - Implement FsRtlCopyWrite2() This brings (in link with r75398 fast IO writes to RDBSS, and thus NFS! CORE-8204 CORE-11327 svn path=/trunk/; revision=75409 --- reactos/sdk/include/ddk/mrxfcb.h | 1 + reactos/sdk/lib/drivers/copysup/copysup.c | 512 ++++++++++++++++++++++ reactos/sdk/lib/drivers/copysup/copysup.h | 12 + reactos/sdk/lib/drivers/rdbsslib/rdbss.c | 69 ++- 4 files changed, 587 insertions(+), 7 deletions(-) diff --git a/reactos/sdk/include/ddk/mrxfcb.h b/reactos/sdk/include/ddk/mrxfcb.h index 9aadec36464..93a63206594 100644 --- a/reactos/sdk/include/ddk/mrxfcb.h +++ b/reactos/sdk/include/ddk/mrxfcb.h @@ -179,6 +179,7 @@ typedef struct _MRX_SRV_OPEN_ } MRX_SRV_OPEN, *PMRX_SRV_OPEN; #define FOBX_FLAG_DFS_OPEN 0x0001 +#define FOBX_FLAG_BAD_HANDLE 0x0002 #define FOBX_FLAG_BACKUP_INTENT 0x0004 typedef struct _MRX_PIPE_HANDLE_INFORMATION diff --git a/reactos/sdk/lib/drivers/copysup/copysup.c b/reactos/sdk/lib/drivers/copysup/copysup.c index 05d2cc44717..d7f705114d7 100644 --- a/reactos/sdk/lib/drivers/copysup/copysup.c +++ b/reactos/sdk/lib/drivers/copysup/copysup.c @@ -28,6 +28,8 @@ #include "copysup.h" #include +#define NDEBUG +#include /* FUNCTIONS ****************************************************************/ @@ -180,3 +182,513 @@ CriticalSection: return Ret; } + +/* + * @implemented + */ +BOOLEAN +FsRtlCopyWrite2( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + IN PVOID Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject, + IN PVOID TopLevelContext) +{ + IO_STATUS_BLOCK LocalIoStatus; + PFSRTL_ADVANCED_FCB_HEADER Fcb; + BOOLEAN WriteToEof, AcquiredShared, FileSizeChanged, Ret; + LARGE_INTEGER WriteOffset, LastOffset, InitialFileSize, InitialValidDataLength; + + PAGED_CODE(); + + /* First, check whether we're writing to EOF */ + WriteToEof = ((FileOffset->LowPart == FILE_WRITE_TO_END_OF_FILE) && + (FileOffset->HighPart == -1)); + + /* If Cc says we cannot write, fail now */ + if (!CcCanIWrite(FileObject, Length, Wait, FALSE)) + { + return FALSE; + } + + /* Write through means no cache */ + if (BooleanFlagOn(FileObject->Flags, FO_WRITE_THROUGH)) + { + return FALSE; + } + + /* If write is > 64Kb, don't use FastIO */ + if (!CcCopyWriteWontFlush(FileObject, FileOffset, Length)) + { + return FALSE; + } + + /* Initialize the IO_STATUS_BLOCK */ + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = Length; + + /* No length, it's already written! */ + if (Length == 0) + { + return TRUE; + } + + AcquiredShared = FALSE; + FileSizeChanged = FALSE; + Fcb = FileObject->FsContext; + + FsRtlEnterFileSystem(); + + /* If we cannot wait, or deal with files bigger then 4GB */ + if (!Wait || (Fcb->AllocationSize.HighPart != 0)) + { + /* If we're to extend the file, then, acquire exclusively */ + if (WriteToEof || FileOffset->QuadPart + Length > Fcb->ValidDataLength.QuadPart) + { + if (!ExAcquireResourceExclusiveLite(Fcb->Resource, Wait)) + { + FsRtlExitFileSystem(); + return FALSE; + } + } + /* Otherwise, a shared lock is enough */ + else + { + if (!ExAcquireResourceSharedLite(Fcb->Resource, Wait)) + { + FsRtlExitFileSystem(); + return FALSE; + } + + AcquiredShared = TRUE; + } + + /* Get first write offset, and last */ + if (WriteToEof) + { + WriteOffset.QuadPart = Fcb->FileSize.QuadPart; + LastOffset.QuadPart = WriteOffset.QuadPart + Length; + } + else + { + WriteOffset.QuadPart = FileOffset->QuadPart; + LastOffset.QuadPart = WriteOffset.QuadPart + Length; + } + + /* If cache wasn't initialized, fail */ + if (FileObject->PrivateCacheMap == NULL || + Fcb->IsFastIoPossible == FastIoIsNotPossible) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + + /* If we're to write beyond allocation size, it's no go, + * same is we create a hole bigger than 8kb + */ + if ((Fcb->ValidDataLength.QuadPart + 0x2000 <= WriteOffset.QuadPart) || + (Length > MAXLONGLONG - WriteOffset.QuadPart) || + (Fcb->AllocationSize.QuadPart < LastOffset.QuadPart)) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + + /* If we have to extend the VDL, shared lock isn't enough */ + if (AcquiredShared && LastOffset.QuadPart > Fcb->ValidDataLength.QuadPart) + { + /* So release, and attempt to acquire exclusively */ + ExReleaseResourceLite(Fcb->Resource); + if (!ExAcquireResourceExclusiveLite(Fcb->Resource, Wait)) + { + FsRtlExitFileSystem(); + return FALSE; + } + + /* Get again EOF, in case file size changed in between */ + if (WriteToEof) + { + WriteOffset.QuadPart = Fcb->FileSize.QuadPart; + LastOffset.QuadPart = WriteOffset.QuadPart + Length; + } + + /* Make sure caching is still enabled */ + if (FileObject->PrivateCacheMap == NULL || + Fcb->IsFastIoPossible == FastIoIsNotPossible) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + + /* And that we're not writting beyond allocation size */ + if (Fcb->AllocationSize.QuadPart < LastOffset.QuadPart) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + } + + /* If FastIO is questionable, then question */ + if (Fcb->IsFastIoPossible == FastIoIsQuestionable) + { + PFAST_IO_DISPATCH FastIoDispatch; + PDEVICE_OBJECT RelatedDeviceObject; + + RelatedDeviceObject = IoGetRelatedDeviceObject(FileObject); + FastIoDispatch = RelatedDeviceObject->DriverObject->FastIoDispatch; + ASSERT(FastIoDispatch != NULL); + ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL); + + if (!FastIoDispatch->FastIoCheckIfPossible(FileObject, + &WriteOffset, + Length, Wait, LockKey, + FALSE, &LocalIoStatus, + RelatedDeviceObject)) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + } + + /* If we write beyond EOF, then, save previous sizes (in case of failure) + * and update file size, to allow writing + */ + if (LastOffset.QuadPart > Fcb->FileSize.QuadPart) + { + FileSizeChanged = TRUE; + InitialFileSize.QuadPart = Fcb->FileSize.QuadPart; + InitialValidDataLength.QuadPart = Fcb->ValidDataLength.QuadPart; + + if (LastOffset.HighPart != Fcb->FileSize.HighPart && + Fcb->PagingIoResource != NULL) + { + ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE); + Fcb->FileSize.QuadPart = LastOffset.QuadPart; + ExReleaseResourceLite(Fcb->PagingIoResource); + } + else + { + Fcb->FileSize.QuadPart = LastOffset.QuadPart; + } + } + + /* Set caller provided context as top level IRP */ + IoSetTopLevelIrp(TopLevelContext); + + Ret = TRUE; + + /* And perform the writing */ + _SEH2_TRY + { + /* Check whether we've to create a hole first */ + if (LastOffset.QuadPart > Fcb->ValidDataLength.QuadPart) + { + Ret = CcZeroData(FileObject, &Fcb->ValidDataLength, + &WriteOffset, Wait); + } + + /* If not needed, or if it worked, write data */ + if (Ret) + { + Ret = CcCopyWrite(FileObject, &WriteOffset, + Length, Wait, Buffer); + } + } + _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH) + { + Ret = FALSE; + } + _SEH2_END; + + /* Restore top level IRP */ + IoSetTopLevelIrp(NULL); + + /* If writing succeed */ + if (Ret) + { + /* If we wrote beyond VDL, update it */ + if (LastOffset.QuadPart > Fcb->ValidDataLength.QuadPart) + { + if (LastOffset.HighPart != Fcb->ValidDataLength.HighPart && + Fcb->PagingIoResource != NULL) + { + ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE); + Fcb->ValidDataLength.QuadPart = LastOffset.QuadPart; + ExReleaseResourceLite(Fcb->PagingIoResource); + } + else + { + Fcb->ValidDataLength.QuadPart = LastOffset.QuadPart; + } + } + + /* File was obviously modified */ + SetFlag(FileObject->Flags, FO_FILE_MODIFIED); + + /* And if we increased it, modify size in Cc and update FO */ + if (FileSizeChanged) + { + (*CcGetFileSizePointer(FileObject)).QuadPart = LastOffset.QuadPart; + SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); + } + + /* Update offset */ + FileObject->CurrentByteOffset.QuadPart = WriteOffset.QuadPart + Length; + } + else + { + /* We failed, we need to restore previous sizes */ + if (FileSizeChanged) + { + if (Fcb->PagingIoResource != NULL) + { + ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE); + Fcb->FileSize.QuadPart = InitialFileSize.QuadPart; + Fcb->ValidDataLength.QuadPart = InitialValidDataLength.QuadPart; + ExReleaseResourceLite(Fcb->PagingIoResource); + } + else + { + Fcb->FileSize.QuadPart = InitialFileSize.QuadPart; + Fcb->ValidDataLength.QuadPart = InitialValidDataLength.QuadPart; + } + } + } + } + else + { + BOOLEAN AboveFour; + + WriteOffset.HighPart = 0; + LastOffset.HighPart = 0; + + /* If we're to extend the file, then, acquire exclusively + * Here, easy stuff, we know we can wait, no return to check! + */ + if (WriteToEof || FileOffset->QuadPart + Length > Fcb->ValidDataLength.QuadPart) + { + ExAcquireResourceExclusiveLite(Fcb->Resource, TRUE); + } + /* Otherwise, a shared lock is enough */ + else + { + ExAcquireResourceSharedLite(Fcb->Resource, TRUE); + AcquiredShared = TRUE; + } + + /* Get first write offset, and last + * Also check whether our writing will bring us + * beyond the 4GB + */ + if (WriteToEof) + { + WriteOffset.LowPart = Fcb->FileSize.LowPart; + LastOffset.LowPart = WriteOffset.LowPart + Length; + AboveFour = (LastOffset.LowPart < Fcb->FileSize.LowPart); + } + else + { + WriteOffset.LowPart = FileOffset->LowPart; + LastOffset.LowPart = WriteOffset.LowPart + Length; + AboveFour = (LastOffset.LowPart < FileOffset->LowPart) || + (FileOffset->HighPart != 0); + } + + /* If cache wasn't initialized, fail */ + if (FileObject->PrivateCacheMap == NULL || + Fcb->IsFastIoPossible == FastIoIsNotPossible) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + + /* If we're to write beyond allocation size, it's no go, + * same is we create a hole bigger than 8kb + * same if we end writing beyond 4GB + */ + if ((Fcb->AllocationSize.LowPart < LastOffset.LowPart) || + (WriteOffset.LowPart >= Fcb->ValidDataLength.LowPart + 0x2000) || + AboveFour) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + + /* If we have to extend the VDL, shared lock isn't enough */ + if (AcquiredShared && LastOffset.LowPart > Fcb->ValidDataLength.LowPart) + { + /* So release, and acquire exclusively */ + ExReleaseResourceLite(Fcb->Resource); + ExAcquireResourceExclusiveLite(Fcb->Resource, TRUE); + + /* Get again EOF, in case file size changed in between and + * recheck we won't go beyond 4GB + */ + if (WriteToEof) + { + WriteOffset.LowPart = Fcb->FileSize.LowPart; + LastOffset.LowPart = WriteOffset.LowPart + Length; + AboveFour = (((LARGE_INTEGER)LastOffset).LowPart < + Fcb->FileSize.LowPart); + } + + /* Make sure caching is still enabled */ + if (FileObject->PrivateCacheMap == NULL || + Fcb->IsFastIoPossible == FastIoIsNotPossible) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + + /* And that we're not writting beyond allocation size + * and that we're not going above 4GB + */ + if ((Fcb->AllocationSize.LowPart < LastOffset.LowPart) || + (Fcb->AllocationSize.HighPart != 0) || AboveFour) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + } + + /* If FastIO is questionable, then question */ + if (Fcb->IsFastIoPossible == FastIoIsQuestionable) + { + PFAST_IO_DISPATCH FastIoDispatch; + PDEVICE_OBJECT RelatedDeviceObject; + C_ASSERT(sizeof(LARGE_INTEGER) == sizeof(LONGLONG)); + + RelatedDeviceObject = IoGetRelatedDeviceObject(FileObject); + FastIoDispatch = RelatedDeviceObject->DriverObject->FastIoDispatch; + ASSERT(FastIoDispatch != NULL); + ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL); + + if (!FastIoDispatch->FastIoCheckIfPossible(FileObject, + &WriteOffset, + Length, Wait, LockKey, + FALSE, &LocalIoStatus, + RelatedDeviceObject)) + { + ExReleaseResourceLite(Fcb->Resource); + FsRtlExitFileSystem(); + + return FALSE; + } + } + + /* If we write beyond EOF, then, save previous sizes (in case of failure) + * and update file size, to allow writing + */ + if (LastOffset.LowPart > Fcb->FileSize.LowPart) + { + FileSizeChanged = TRUE; + InitialFileSize.LowPart = Fcb->FileSize.LowPart; + InitialValidDataLength.LowPart = Fcb->ValidDataLength.LowPart; + Fcb->FileSize.LowPart = LastOffset.LowPart; + } + + /* Set caller provided context as top level IRP */ + IoSetTopLevelIrp(TopLevelContext); + + Ret = TRUE; + + /* And perform the writing */ + _SEH2_TRY + { + /* Check whether we've to create a hole first - + * it cannot fail, we can wait + */ + if (LastOffset.LowPart > Fcb->ValidDataLength.LowPart) + { + CcZeroData(FileObject, &Fcb->ValidDataLength, &WriteOffset, TRUE); + } + + /* Write data */ + CcFastCopyWrite(FileObject, WriteOffset.LowPart, Length, Buffer); + } + _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH) + { + Ret = FALSE; + } + _SEH2_END; + + /* Restore top level IRP */ + IoSetTopLevelIrp(NULL); + + /* If writing succeed */ + if (Ret) + { + /* If we wrote beyond VDL, update it */ + if (LastOffset.LowPart > Fcb->ValidDataLength.LowPart) + { + Fcb->ValidDataLength.LowPart = LastOffset.LowPart; + } + + /* File was obviously modified */ + SetFlag(FileObject->Flags, FO_FILE_MODIFIED); + + /* And if we increased it, modify size in Cc and update FO */ + if (FileSizeChanged) + { + (*CcGetFileSizePointer(FileObject)).LowPart = LastOffset.LowPart; + SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); + } + + /* Update offset - we're still below 4GB, so high part must be 0 */ + FileObject->CurrentByteOffset.LowPart = WriteOffset.LowPart + Length; + FileObject->CurrentByteOffset.HighPart = 0; + } + else + { + /* We failed, we need to restore previous sizes */ + if (FileSizeChanged) + { + if (Fcb->PagingIoResource != NULL) + { + ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE); + Fcb->FileSize.LowPart = InitialFileSize.LowPart; + Fcb->ValidDataLength.LowPart = InitialValidDataLength.LowPart; + ExReleaseResourceLite(Fcb->PagingIoResource); + } + else + { + Fcb->FileSize.LowPart = InitialFileSize.LowPart; + Fcb->ValidDataLength.LowPart = InitialValidDataLength.LowPart; + } + } + } + } + + /* Release our resource and leave */ + ExReleaseResourceLite(Fcb->Resource); + + FsRtlExitFileSystem(); + + return Ret; +} diff --git a/reactos/sdk/lib/drivers/copysup/copysup.h b/reactos/sdk/lib/drivers/copysup/copysup.h index 40cf9243846..8b78ec37346 100644 --- a/reactos/sdk/lib/drivers/copysup/copysup.h +++ b/reactos/sdk/lib/drivers/copysup/copysup.h @@ -15,4 +15,16 @@ FsRtlCopyRead2( _In_ PDEVICE_OBJECT DeviceObject, _In_ PVOID TopLevelContext); +BOOLEAN +FsRtlCopyWrite2( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_ PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PVOID TopLevelContext); + #endif diff --git a/reactos/sdk/lib/drivers/rdbsslib/rdbss.c b/reactos/sdk/lib/drivers/rdbsslib/rdbss.c index 43c89c6504c..f63943aa050 100644 --- a/reactos/sdk/lib/drivers/rdbsslib/rdbss.c +++ b/reactos/sdk/lib/drivers/rdbsslib/rdbss.c @@ -5364,6 +5364,9 @@ RxDumpWantedAccess( PAGED_CODE(); } +/* + * @implemented + */ BOOLEAN NTAPI RxFastIoCheckIfPossible( @@ -5376,6 +5379,7 @@ RxFastIoCheckIfPossible( { PFCB Fcb; PSRV_OPEN SrvOpen; + LARGE_INTEGER LargeLength; PAGED_CODE(); @@ -5436,11 +5440,11 @@ RxFastIoCheckIfPossible( RxProcessChangeBufferingStateRequestsForSrvOpen(SrvOpen); FsRtlExitFileSystem(); + LargeLength.QuadPart = Length; + /* If operation to come is a read operation */ if (CheckForReadOperation) { - LARGE_INTEGER LargeLength; - /* Check that read cache is enabled */ if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_READCACHING_ENABLED)) { @@ -5449,7 +5453,6 @@ RxFastIoCheckIfPossible( } /* Check whether there's a lock conflict */ - LargeLength.QuadPart = Length; if (!FsRtlFastCheckLockForRead(&Fcb->Specific.Fcb.FileLock, FileOffset, &LargeLength, @@ -5464,8 +5467,26 @@ RxFastIoCheckIfPossible( return TRUE; } - UNIMPLEMENTED; - return FALSE; + /* Check that write cache is enabled */ + if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_WRITECACHING_ENABLED)) + { + DPRINT1("Write caching disabled\n"); + return FALSE; + } + + /* Check whether there's a lock conflict */ + if (!FsRtlFastCheckLockForWrite(&Fcb->Specific.Fcb.FileLock, + FileOffset, + &LargeLength, + LockKey, + FileObject, + PsGetCurrentProcess())) + { + DPRINT1("FsRtlFastCheckLockForWrite failed\n"); + return FALSE; + } + + return TRUE; } BOOLEAN @@ -5536,6 +5557,9 @@ RxFastIoRead( return Ret; } +/* + * @implemented + */ BOOLEAN NTAPI RxFastIoWrite( @@ -5548,8 +5572,39 @@ RxFastIoWrite( PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { - UNIMPLEMENTED; - return FALSE; + PFOBX Fobx; + BOOLEAN Ret; + RX_TOPLEVELIRP_CONTEXT TopLevelContext; + + PAGED_CODE(); + + Fobx = (PFOBX)FileObject->FsContext2; + if (BooleanFlagOn(Fobx->Flags, FOBX_FLAG_BAD_HANDLE)) + { + return FALSE; + } + + DPRINT("RxFastIoWrite: %p (%p, %p)\n", FileObject, FileObject->FsContext, + FileObject->FsContext2); + DPRINT("Writing %ld at %I64x\n", Length, FileOffset->QuadPart); + + /* Prepare a TLI context */ + ASSERT(RxIsThisTheTopLevelIrp(NULL)); + RxInitializeTopLevelIrpContext(&TopLevelContext, (PIRP)FSRTL_FAST_IO_TOP_LEVEL_IRP, + (PRDBSS_DEVICE_OBJECT)DeviceObject); + + Ret = FsRtlCopyWrite2(FileObject, FileOffset, Length, Wait, LockKey, Buffer, + IoStatus, DeviceObject, &TopLevelContext); + if (Ret) + { + DPRINT("Write OK\n"); + } + else + { + DPRINT1("Write failed!\n"); + } + + return Ret; } NTSTATUS -- 2.17.1