diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 677ce3f3..33e1b5d6 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -181,6 +181,22 @@ typedef struct UINT64 UserContext2; } Close; struct + { + UINT64 UserContext; + UINT64 UserContext2; + } Read; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT64 Address; + UINT64 Offset; + UINT32 Length; + UINT32 Key; + UINT32 Append:1; /* append to end of file */ + UINT32 PagingIo:1; /* write's beyond EOF are NOP's (file size remains same) */ + } Write; + struct { UINT64 UserContext; UINT64 UserContext2; @@ -281,6 +297,10 @@ typedef struct FSP_FSCTL_FILE_INFO FileInfo; } Overwrite; struct + { + FSP_FSCTL_FILE_INFO FileInfo; + } Write; + struct { FSP_FSCTL_FILE_INFO FileInfo; } QueryInformation; diff --git a/src/sys/callbacks.c b/src/sys/callbacks.c index f49c6477..1f4e182b 100644 --- a/src/sys/callbacks.c +++ b/src/sys/callbacks.c @@ -61,6 +61,10 @@ VOID FspAcquireFileForNtCreateSection( { FSP_ENTER_VOID(PAGED_CODE()); + FSP_FILE_NODE *FileNode = FileObject->FsContext; + + FspFileNodeAcquireExclusive(FileNode, Full); + FSP_LEAVE_VOID("FileObject=%p", FileObject); } @@ -69,6 +73,10 @@ VOID FspReleaseFileForNtCreateSection( { FSP_ENTER_VOID(PAGED_CODE()); + FSP_FILE_NODE *FileNode = FileObject->FsContext; + + FspFileNodeRelease(FileNode, Full); + FSP_LEAVE_VOID("FileObject=%p", FileObject); } @@ -80,7 +88,10 @@ NTSTATUS FspAcquireForModWrite( { FSP_ENTER(PAGED_CODE()); - Result = STATUS_NOT_IMPLEMENTED; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + + FspFileNodeAcquireExclusive(FileNode, Full); + *ResourceToRelease = 0; FSP_LEAVE("FileObject=%p", FileObject); } @@ -92,7 +103,9 @@ NTSTATUS FspReleaseForModWrite( { FSP_ENTER(PAGED_CODE()); - Result = STATUS_NOT_IMPLEMENTED; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + + FspFileNodeRelease(FileNode, Full); FSP_LEAVE("FileObject=%p", FileObject); } @@ -103,7 +116,9 @@ NTSTATUS FspAcquireForCcFlush( { FSP_ENTER(PAGED_CODE()); - Result = STATUS_NOT_IMPLEMENTED; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + + FspFileNodeAcquireExclusive(FileNode, Full); FSP_LEAVE("FileObject=%p", FileObject); } @@ -114,7 +129,9 @@ NTSTATUS FspReleaseForCcFlush( { FSP_ENTER(PAGED_CODE()); - Result = STATUS_NOT_IMPLEMENTED; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + + FspFileNodeRelease(FileNode, Full); FSP_LEAVE("FileObject=%p", FileObject); } @@ -125,7 +142,9 @@ BOOLEAN FspAcquireForLazyWrite( { FSP_ENTER_BOOL(PAGED_CODE()); - Result = FALSE; + FSP_FILE_NODE *FileNode = Context; + + FspFileNodeAcquireExclusive(FileNode, Full); FSP_LEAVE_BOOL("Context=%p, Wait=%d", Context, Wait); } @@ -135,6 +154,10 @@ VOID FspReleaseFromLazyWrite( { FSP_ENTER_VOID(PAGED_CODE()); + FSP_FILE_NODE *FileNode = Context; + + FspFileNodeRelease(FileNode, Full); + FSP_LEAVE_VOID("Context=%p", Context); } @@ -144,7 +167,9 @@ BOOLEAN FspAcquireForReadAhead( { FSP_ENTER_BOOL(PAGED_CODE()); - Result = FALSE; + FSP_FILE_NODE *FileNode = Context; + + FspFileNodeAcquireShared(FileNode, Full); FSP_LEAVE_BOOL("Context=%p, Wait=%d", Context, Wait); } @@ -154,5 +179,9 @@ VOID FspReleaseFromReadAhead( { FSP_ENTER_VOID(PAGED_CODE()); + FSP_FILE_NODE *FileNode = Context; + + FspFileNodeRelease(FileNode, Full); + FSP_LEAVE_VOID("Context=%p", Context); } diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c index 3356ecb0..ea7dc788 100644 --- a/src/sys/cleanup.c +++ b/src/sys/cleanup.c @@ -61,7 +61,9 @@ static NTSTATUS FspFsvolCleanup( ASSERT(FileNode == FileDesc->FileNode); + /* !!!: REVISIT! */ FspFileNodeClose(FileNode, FileObject, &DeletePending); + CcUninitializeCacheMap(FileObject, 0, 0); /* * If DeletePending is TRUE, the FileNode is no longer in the Context table, diff --git a/src/sys/create.c b/src/sys/create.c index 42679171..4d441050 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -583,6 +583,10 @@ NTSTATUS FspFsvolCreateComplete( FileObject->PrivateCacheMap = 0; FileObject->FsContext = FileNode; FileObject->FsContext2 = FileDesc; + if (FspTimeoutInfinity32 == FsvolDeviceExtension->VolumeParams.FileInfoTimeout && + !FlagOn(IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING)) + /* enable caching! */ + SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED); if (FILE_SUPERSEDED != Response->IoStatus.Information && FILE_OVERWRITTEN != Response->IoStatus.Information) diff --git a/src/sys/driver.c b/src/sys/driver.c index 10e128a8..4c2fe17a 100644 --- a/src/sys/driver.c +++ b/src/sys/driver.c @@ -93,8 +93,8 @@ NTSTATUS DriverEntry( /* setup fast I/O and resource acquisition */ FspFastIoDispatch.SizeOfFastIoDispatch = sizeof FspFastIoDispatch; FspFastIoDispatch.FastIoCheckIfPossible = FspFastIoCheckIfPossible; - FspFastIoDispatch.FastIoRead = FsRtlCopyRead; - FspFastIoDispatch.FastIoWrite = FsRtlCopyWrite; + //FspFastIoDispatch.FastIoRead = 0; + //FspFastIoDispatch.FastIoWrite = 0; //FspFastIoDispatch.FastIoQueryBasicInfo = 0; //FspFastIoDispatch.FastIoQueryStandardInfo = 0; //FspFastIoDispatch.FastIoLock = 0; @@ -107,10 +107,10 @@ NTSTATUS DriverEntry( //FspFastIoDispatch.FastIoDetachDevice = 0; //FspFastIoDispatch.FastIoQueryNetworkOpenInfo = 0; FspFastIoDispatch.AcquireForModWrite = FspAcquireForModWrite; - FspFastIoDispatch.MdlRead = FsRtlMdlReadDev; - FspFastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev; - FspFastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev; - FspFastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev; + //FspFastIoDispatch.MdlRead = 0; + //FspFastIoDispatch.MdlReadComplete = 0; + //FspFastIoDispatch.PrepareMdlWrite = 0; + //FspFastIoDispatch.MdlWriteComplete = 0; //FspFastIoDispatch.FastIoReadCompressed = 0; //FspFastIoDispatch.FastIoWriteCompressed = 0; //FspFastIoDispatch.MdlReadCompleteCompressed = 0; diff --git a/src/sys/driver.h b/src/sys/driver.h index e7b6a846..d7b0b541 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -273,6 +273,7 @@ FSP_IOPREP_DISPATCH FspFsvolSetSecurityPrepare; FSP_IOCMPL_DISPATCH FspFsvolSetSecurityComplete; FSP_IOCMPL_DISPATCH FspFsvolSetVolumeInformationComplete; FSP_IOCMPL_DISPATCH FspFsvolShutdownComplete; +FSP_IOPREP_DISPATCH FspFsvolWritePrepare; FSP_IOCMPL_DISPATCH FspFsvolWriteComplete; /* fast I/O and resource acquisition callbacks */ @@ -378,13 +379,18 @@ VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE NTSTATUS FspCreateGuid(GUID *Guid); NTSTATUS FspLockUserBuffer(PVOID UserBuffer, ULONG Length, KPROCESSOR_MODE RequestorMode, LOCK_OPERATION Operation, PMDL *PMdl); +NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress); +NTSTATUS FspCcInitializeCacheMap(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes, + BOOLEAN PinAccess, PCACHE_MANAGER_CALLBACKS Callbacks, PVOID CallbackContext); NTSTATUS FspCcSetFileSizes(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes); +NTSTATUS FspCcCopyWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, + BOOLEAN Wait, PVOID Buffer); +NTSTATUS FspCcPrepareMdlWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, + PMDL *PMdlChain, PIO_STATUS_BLOCK IoStatus); NTSTATUS FspCcMdlWriteComplete(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain); NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor); -#define FspSetTopLevelIrp(Irp) (IoGetTopLevelIrp() ? FALSE : (IoSetTopLevelIrp(Irp), TRUE)) -#define FspResetTopLevelIrp(TopLevel) ((TopLevel) ? IoSetTopLevelIrp(0) : (void)0) /* utility: synchronous work queue */ typedef struct @@ -409,6 +415,19 @@ VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); VOID FspQueueDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, LARGE_INTEGER Delay); +/* utility: safe MDL */ +typedef struct +{ + PMDL Mdl; + PVOID Buffer; + PMDL UserMdl; + LOCK_OPERATION Operation; +} FSP_SAFE_MDL; +BOOLEAN FspSafeMdlCheck(PMDL Mdl); +NTSTATUS FspSafeMdlCreate(PMDL UserMdl, LOCK_OPERATION Operation, FSP_SAFE_MDL **PSafeMdl); +VOID FspSafeMdlCopyBack(FSP_SAFE_MDL *SafeMdl); +VOID FspSafeMdlDelete(FSP_SAFE_MDL *SafeMdl); + /* IRP context */ #define FspIrpTimestampInfinity ((ULONG)-1L) #define FspIrpTimestamp(Irp) \ diff --git a/src/sys/util.c b/src/sys/util.c index eadc4f60..de646892 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -11,7 +11,14 @@ VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE NTSTATUS FspCreateGuid(GUID *Guid); NTSTATUS FspLockUserBuffer(PVOID UserBuffer, ULONG Length, KPROCESSOR_MODE RequestorMode, LOCK_OPERATION Operation, PMDL *PMdl); +NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress); +NTSTATUS FspCcInitializeCacheMap(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes, + BOOLEAN PinAccess, PCACHE_MANAGER_CALLBACKS Callbacks, PVOID CallbackContext); NTSTATUS FspCcSetFileSizes(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes); +NTSTATUS FspCcCopyWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, + BOOLEAN Wait, PVOID Buffer); +NTSTATUS FspCcPrepareMdlWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, + PMDL *PMdlChain, PIO_STATUS_BLOCK IoStatus); NTSTATUS FspCcMdlWriteComplete(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain); NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, @@ -24,13 +31,21 @@ VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); VOID FspQueueDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, LARGE_INTEGER Delay); static KDEFERRED_ROUTINE FspQueueDelayedWorkItemDPC; +BOOLEAN FspSafeMdlCheck(PMDL Mdl); +NTSTATUS FspSafeMdlCreate(PMDL UserMdl, LOCK_OPERATION Operation, FSP_SAFE_MDL **PSafeMdl); +VOID FspSafeMdlCopyBack(FSP_SAFE_MDL *SafeMdl); +VOID FspSafeMdlDelete(FSP_SAFE_MDL *SafeMdl); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspUnicodePathIsValid) #pragma alloc_text(PAGE, FspUnicodePathSuffix) #pragma alloc_text(PAGE, FspCreateGuid) #pragma alloc_text(PAGE, FspLockUserBuffer) +#pragma alloc_text(PAGE, FspMapLockedPagesInUserMode) +#pragma alloc_text(PAGE, FspCcInitializeCacheMap) #pragma alloc_text(PAGE, FspCcSetFileSizes) +#pragma alloc_text(PAGE, FspCcCopyWrite) +#pragma alloc_text(PAGE, FspCcPrepareMdlWrite) #pragma alloc_text(PAGE, FspCcMdlWriteComplete) #pragma alloc_text(PAGE, FspQuerySecurityDescriptorInfo) #pragma alloc_text(PAGE, FspInitializeSynchronousWorkItem) @@ -38,6 +53,10 @@ static KDEFERRED_ROUTINE FspQueueDelayedWorkItemDPC; #pragma alloc_text(PAGE, FspExecuteSynchronousWorkItemRoutine) #pragma alloc_text(PAGE, FspInitializeDelayedWorkItem) #pragma alloc_text(PAGE, FspQueueDelayedWorkItem) +#pragma alloc_text(PAGE, FspSafeMdlCheck) +#pragma alloc_text(PAGE, FspSafeMdlCreate) +#pragma alloc_text(PAGE, FspSafeMdlCopyBack) +#pragma alloc_text(PAGE, FspSafeMdlDelete) #endif static const LONG Delays[] = @@ -183,6 +202,38 @@ NTSTATUS FspLockUserBuffer(PVOID UserBuffer, ULONG Length, return STATUS_SUCCESS; } +NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress) +{ + PAGED_CODE(); + + try + { + *PAddress = MmMapLockedPagesSpecifyCache(Mdl, UserMode, MmCached, 0, FALSE, NormalPagePriority); + return 0 != *PAddress ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES; + } + except (EXCEPTION_EXECUTE_HANDLER) + { + *PAddress = 0; + return GetExceptionCode(); + } +} + +NTSTATUS FspCcInitializeCacheMap(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes, + BOOLEAN PinAccess, PCACHE_MANAGER_CALLBACKS Callbacks, PVOID CallbackContext) +{ + PAGED_CODE(); + + try + { + CcInitializeCacheMap(FileObject, FileSizes, PinAccess, Callbacks, CallbackContext); + return STATUS_SUCCESS; + } + except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } +} + NTSTATUS FspCcSetFileSizes(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes) { PAGED_CODE(); @@ -198,6 +249,56 @@ NTSTATUS FspCcSetFileSizes(PFILE_OBJECT FileObject, PCC_FILE_SIZES FileSizes) } } +NTSTATUS FspCcCopyWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, + BOOLEAN Wait, PVOID Buffer) +{ + PAGED_CODE(); + + try + { + BOOLEAN Success = CcCopyWrite(FileObject, FileOffset, Length, Wait, Buffer); + return Success ? STATUS_SUCCESS : STATUS_PENDING; + } + except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } +} + +NTSTATUS FspCcPrepareMdlWrite(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, + PMDL *PMdlChain, PIO_STATUS_BLOCK IoStatus) +{ + PAGED_CODE(); + + NTSTATUS Result; + + *PMdlChain = 0; + + try + { + CcPrepareMdlWrite(FileObject, FileOffset, Length, PMdlChain, IoStatus); + Result = IoStatus->Status; + } + except(EXCEPTION_EXECUTE_HANDLER) + { + Result = GetExceptionCode(); + } + + if (!NT_SUCCESS(Result)) + { + if (0 != *PMdlChain) + { + CcMdlWriteAbort(FileObject, *PMdlChain); + *PMdlChain = 0; + } + + IoStatus->Information = 0; + IoStatus->Status = Result; + } + + return Result; +} + NTSTATUS FspCcMdlWriteComplete(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain) { PAGED_CODE(); @@ -293,3 +394,218 @@ static VOID FspQueueDelayedWorkItemDPC(PKDPC Dpc, ExQueueWorkItem(&DelayedWorkItem->WorkQueueItem, DelayedWorkQueue); } + +BOOLEAN FspSafeMdlCheck(PMDL Mdl) +{ + PAGED_CODE(); + + PVOID VirtualAddress = MmGetMdlVirtualAddress(Mdl); + ULONG ByteCount = MmGetMdlByteCount(Mdl); + + return 0 == BYTE_OFFSET(VirtualAddress) && 0 == BYTE_OFFSET(ByteCount); +} + +NTSTATUS FspSafeMdlCreate(PMDL UserMdl, LOCK_OPERATION Operation, FSP_SAFE_MDL **PSafeMdl) +{ + PAGED_CODE(); + + NTSTATUS Result; + PVOID VirtualAddress = MmGetMdlVirtualAddress(UserMdl); + ULONG ByteCount = MmGetMdlByteCount(UserMdl); + ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, ByteCount); + FSP_SAFE_MDL *SafeMdl; + PMDL TempMdl; + PPFN_NUMBER UserPfnArray, SafePfnArray, TempPfnArray; + ULONG ByteOffsetBgn0, ByteOffsetEnd0, ByteOffsetEnd1; + BOOLEAN Buffer0, Buffer1; + ULONG BufferPageCount; + + ASSERT(0 != PageCount); + + *PSafeMdl = 0; + + SafeMdl = FspAllocNonPaged(sizeof *SafeMdl); + if (0 == SafeMdl) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + RtlZeroMemory(SafeMdl, sizeof *SafeMdl); + + SafeMdl->Mdl = IoAllocateMdl(VirtualAddress, ByteCount, FALSE, FALSE, 0); + if (0 == SafeMdl->Mdl) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + UserPfnArray = MmGetMdlPfnArray(UserMdl); + SafePfnArray = MmGetMdlPfnArray(SafeMdl->Mdl); + RtlCopyMemory(SafePfnArray, UserPfnArray, PageCount * sizeof(PFN_NUMBER)); + + /* + * Possible cases: + * + * ----+---------+---------+---- + * + * *---------* + + * + *----* + + * *----* + + + * + *---* + + + * *--------...--------* + * + *---...--------* + * *--------...---* + + * + *---...---* + + */ + if (1 == PageCount) + { + ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); + ByteOffsetEnd0 = BYTE_OFFSET(ByteCount + (PAGE_SIZE - 1)); + ByteOffsetEnd1 = 0; + Buffer0 = 0 != ByteOffsetBgn0 || PAGE_SIZE != ByteOffsetEnd0; + Buffer1 = FALSE; + } + else + { + ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); + ByteOffsetEnd0 = PAGE_SIZE; + ByteOffsetEnd1 = BYTE_OFFSET((PUINT8)VirtualAddress + ByteCount + (PAGE_SIZE - 1)); + Buffer0 = 0 != ByteOffsetBgn0; + Buffer1 = PAGE_SIZE != ByteOffsetEnd1; + } + BufferPageCount = Buffer0 + Buffer1; + + if (0 < BufferPageCount) + { + SafeMdl->Buffer = FspAllocNonPaged(PAGE_SIZE * BufferPageCount); + if (0 == SafeMdl->Buffer) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + TempMdl = IoAllocateMdl(SafeMdl->Buffer, PAGE_SIZE * BufferPageCount, FALSE, FALSE, 0); + if (0 == TempMdl) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + MmBuildMdlForNonPagedPool(TempMdl); + + TempPfnArray = MmGetMdlPfnArray(SafeMdl); + if (IoReadAccess == Operation) + { + if (Buffer0) + { + RtlZeroMemory((PUINT8)SafeMdl->Buffer, ByteOffsetBgn0); + RtlCopyMemory((PUINT8)SafeMdl->Buffer + ByteOffsetBgn0, + (PUINT8)VirtualAddress + ByteOffsetBgn0, ByteOffsetEnd0 - ByteOffsetBgn0); + RtlZeroMemory((PUINT8)SafeMdl->Buffer + ByteOffsetEnd0, PAGE_SIZE - ByteOffsetEnd0); + UserPfnArray[0] = TempPfnArray[0]; + } + if (Buffer1) + { + RtlCopyMemory((PUINT8)SafeMdl->Buffer + (BufferPageCount - 1) * PAGE_SIZE, + (PUINT8)VirtualAddress + (PageCount - 1) * PAGE_SIZE, ByteOffsetEnd1); + RtlZeroMemory((PUINT8)SafeMdl->Buffer + (BufferPageCount - 1) * PAGE_SIZE + ByteOffsetEnd1, + PAGE_SIZE - ByteOffsetEnd1); + UserPfnArray[PageCount - 1] = TempPfnArray[BufferPageCount - 1]; + } + } + else + { + RtlZeroMemory((PUINT8)SafeMdl->Buffer, PAGE_SIZE * BufferPageCount); + if (Buffer0) + UserPfnArray[0] = TempPfnArray[0]; + if (Buffer1) + UserPfnArray[PageCount - 1] = TempPfnArray[BufferPageCount - 1]; + } + + IoFreeMdl(TempMdl); + } + + SafeMdl->UserMdl = UserMdl; + *PSafeMdl = SafeMdl; + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result) && 0 != SafeMdl) + { + if (0 != SafeMdl->Buffer) + FspFree(SafeMdl->Buffer); + if (0 != SafeMdl->Mdl) + IoFreeMdl(SafeMdl->Mdl); + FspFree(SafeMdl); + } + + return Result; +} + +VOID FspSafeMdlCopyBack(FSP_SAFE_MDL *SafeMdl) +{ + PAGED_CODE(); + + if (IoReadAccess == SafeMdl->Operation) + return; + + PVOID VirtualAddress = MmGetMdlVirtualAddress(SafeMdl->UserMdl); + ULONG ByteCount = MmGetMdlByteCount(SafeMdl->UserMdl); + ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, ByteCount); + ULONG ByteOffsetBgn0, ByteOffsetEnd0, ByteOffsetEnd1; + BOOLEAN Buffer0, Buffer1; + ULONG BufferPageCount; + + /* + * Possible cases: + * + * ----+---------+---------+---- + * + * *---------* + + * + *----* + + * *----* + + + * + *---* + + + * *--------...--------* + * + *---...--------* + * *--------...---* + + * + *---...---* + + */ + if (1 == PageCount) + { + ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); + ByteOffsetEnd0 = BYTE_OFFSET(ByteCount + (PAGE_SIZE - 1)); + ByteOffsetEnd1 = 0; + Buffer0 = 0 != ByteOffsetBgn0 || PAGE_SIZE != ByteOffsetEnd0; + Buffer1 = FALSE; + } + else + { + ByteOffsetBgn0 = BYTE_OFFSET(VirtualAddress); + ByteOffsetEnd0 = PAGE_SIZE; + ByteOffsetEnd1 = BYTE_OFFSET((PUINT8)VirtualAddress + ByteCount + (PAGE_SIZE - 1)); + Buffer0 = 0 != ByteOffsetBgn0; + Buffer1 = PAGE_SIZE != ByteOffsetEnd1; + } + BufferPageCount = Buffer0 + Buffer1; + + if (0 < BufferPageCount) + { + if (Buffer0) + RtlCopyMemory((PUINT8)VirtualAddress + ByteOffsetBgn0, + (PUINT8)SafeMdl->Buffer + ByteOffsetBgn0, ByteOffsetEnd0 - ByteOffsetBgn0); + if (Buffer1) + RtlCopyMemory((PUINT8)VirtualAddress + (PageCount - 1) * PAGE_SIZE, + (PUINT8)SafeMdl->Buffer + (BufferPageCount - 1) * PAGE_SIZE, ByteOffsetEnd1); + } +} + +VOID FspSafeMdlDelete(FSP_SAFE_MDL *SafeMdl) +{ + PAGED_CODE(); + + if (0 != SafeMdl->Buffer) + FspFree(SafeMdl->Buffer); + if (0 != SafeMdl->Mdl) + IoFreeMdl(SafeMdl->Mdl); + FspFree(SafeMdl); +} diff --git a/src/sys/write.c b/src/sys/write.c index 9443b866..523365bc 100644 --- a/src/sys/write.c +++ b/src/sys/write.c @@ -14,7 +14,9 @@ static NTSTATUS FspFsvolWriteCached( static VOID FspFsvolWriteCachedDeferred(PVOID Context1, PVOID Context2); static NTSTATUS FspFsvolWriteNonCached( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +FSP_IOPREP_DISPATCH FspFsvolWritePrepare; FSP_IOCMPL_DISPATCH FspFsvolWriteComplete; +static FSP_IOP_REQUEST_FINI FspFsvolWriteNonCachedRequestFini; FSP_DRIVER_DISPATCH FspWrite; #ifdef ALLOC_PRAGMA @@ -22,10 +24,21 @@ FSP_DRIVER_DISPATCH FspWrite; #pragma alloc_text(PAGE, FspFsvolWriteCached) #pragma alloc_text(PAGE, FspFsvolWriteCachedDeferred) #pragma alloc_text(PAGE, FspFsvolWriteNonCached) +#pragma alloc_text(PAGE, FspFsvolWritePrepare) #pragma alloc_text(PAGE, FspFsvolWriteComplete) +#pragma alloc_text(PAGE, FspFsvolWriteNonCachedRequestFini) #pragma alloc_text(PAGE, FspWrite) #endif +enum +{ + /* WriteNonCached */ + RequestIrp = 0, + RequestSafeMdl = 1, + RequestAddress = 2, + RequestProcess = 3, +}; + static NTSTATUS FspFsvolWrite( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -36,49 +49,36 @@ static NTSTATUS FspFsvolWrite( return STATUS_INVALID_DEVICE_REQUEST; NTSTATUS Result; - BOOLEAN TopLevel = FspSetTopLevelIrp(Irp); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; /* is this an MDL complete request? */ if (FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE)) { - Result = FspCcMdlWriteComplete(IrpSp->FileObject, + Result = FspCcMdlWriteComplete(FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress); Irp->MdlAddress = 0; - goto exit; + return Result; } /* only regular files can be written */ - if (((FSP_FILE_NODE *)IrpSp->FileObject->FsContext)->IsDirectory) - { - Result = STATUS_INVALID_PARAMETER; - goto exit; - } + if (FileNode->IsDirectory) + return STATUS_INVALID_PARAMETER; /* do we have anything to write? */ if (0 == IrpSp->Parameters.Write.Length) { Irp->IoStatus.Information = 0; - Result = STATUS_SUCCESS; - goto exit; + return STATUS_SUCCESS; } - /* probe and lock the user buffer */ - if (0 == Irp->MdlAddress) - { - Result = FspLockUserBuffer(Irp->UserBuffer, IrpSp->Parameters.Write.Length, - Irp->RequestorMode, IoReadAccess, &Irp->MdlAddress); - if (!NT_SUCCESS(Result)) - goto exit; - } - - if (!FlagOn(Irp->Flags, IRP_PAGING_IO | IRP_NOCACHE)) + /* are we doing cached or non-cached I/O? */ + if (FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED) && + !FlagOn(Irp->Flags, IRP_PAGING_IO | IRP_NOCACHE)) Result = FspFsvolWriteCached(FsvolDeviceObject, Irp, IrpSp, IoIsOperationSynchronous(Irp)); else Result = FspFsvolWriteNonCached(FsvolDeviceObject, Irp, IrpSp); -exit: - FspResetTopLevelIrp(TopLevel); - return Result; } @@ -89,32 +89,36 @@ static NTSTATUS FspFsvolWriteCached( PAGED_CODE(); NTSTATUS Result; - FSP_FSCTL_TRANSACT_REQ *RequestWorkItem = FspIrpRequest(Irp); + BOOLEAN Retrying = 0 != FspIrpRequest(Irp); + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; FSP_FILE_DESC *FileDesc = FileObject->FsContext2; -#if 0 - ULONG WriteKey = IrpSp->Parameters.Write.Key; LARGE_INTEGER WriteOffset = IrpSp->Parameters.Write.ByteOffset; -#endif ULONG WriteLength = IrpSp->Parameters.Write.Length; #if 0 - BOOLEAN WriteToEof = - FILE_WRITE_TO_END_OF_FILE == WriteOffset.LowPart && -1L == WriteOffset.HighPart; + /* !!!: lock support! */ + ULONG WriteKey = IrpSp->Parameters.Write.Key; #endif + BOOLEAN WriteToEndOfFile = + FILE_WRITE_TO_END_OF_FILE == WriteOffset.LowPart && -1L == WriteOffset.HighPart; + BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); + FSP_FSCTL_FILE_INFO FileInfo; + CC_FILE_SIZES FileSizes; + UINT64 WriteEndOffset; + BOOLEAN Success; ASSERT(FileNode == FileDesc->FileNode); /* should we defer the write? */ - if (DEBUGRANDTEST(10, TRUE) || - !CcCanIWrite(FileObject, WriteLength, CanWait, 0 != RequestWorkItem)) + Success = DEBUGRANDTEST(90, TRUE) && CcCanIWrite(FileObject, WriteLength, CanWait, Retrying); + if (!Success) { Result = FspWqCreateIrpWorkItem(Irp, FspFsvolWriteCached, 0); if (NT_SUCCESS(Result)) { IoMarkIrpPending(Irp); - CcDeferWrite(FileObject, FspFsvolWriteCachedDeferred, Irp, 0, WriteLength, - 0 != RequestWorkItem); + CcDeferWrite(FileObject, FspFsvolWriteCachedDeferred, Irp, 0, WriteLength, Retrying); return STATUS_PENDING; } @@ -122,7 +126,97 @@ static NTSTATUS FspFsvolWriteCached( /* if we are unable to defer we will go ahead and (try to) service the IRP now! */ } - return STATUS_INVALID_DEVICE_REQUEST; + /* try to acquire the FileNode Main exclusive */ + Success = DEBUGRANDTEST(90, TRUE) && + FspFileNodeTryAcquireExclusiveF(FileNode, FspFileNodeAcquireMain, CanWait); + if (!Success) + return FspWqRepostIrpWorkItem(Irp, FspFsvolWriteCached, 0); + + /* compute new file size and allocation size */ + ASSERT(FspTimeoutInfinity32 == FsvolDeviceExtension->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) + { + /* file is being extended */ + FileSizes.FileSize.QuadPart = WriteEndOffset; + if (FileSizes.FileSize.QuadPart > FileSizes.AllocationSize.QuadPart) + { + UINT64 AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize * + FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit; + FileSizes.AllocationSize.QuadPart = (FileSizes.FileSize.QuadPart + AllocationUnit - 1) + / AllocationUnit * AllocationUnit; + } + } + + /* initialize cache if not already initialized! */ + if (0 == FileObject->PrivateCacheMap) + { + Result = FspCcInitializeCacheMap(FileObject, &FileSizes, FALSE, + &FspCacheManagerCallbacks, FileNode); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Main); + return Result; + } + } + else if (FileInfo.FileSize < WriteEndOffset) + { + /* file is being extended */ + Result = FspCcSetFileSizes(FileObject, &FileSizes); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Main); + return Result; + } + } + + /* are we using the copy or MDL interface? */ + if (!FlagOn(IrpSp->MinorFunction, IRP_MN_MDL)) + { + PVOID Buffer; + + Buffer = 0 == Irp->MdlAddress ? + Irp->UserBuffer : MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); + if (0 == Buffer) + { + FspFileNodeRelease(FileNode, Main); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Result = FspCcCopyWrite(FileObject, &WriteOffset, WriteLength, CanWait || Retrying, Buffer); + if (STATUS_PENDING == Result) + { + FspFileNodeRelease(FileNode, Main); + return FspWqRepostIrpWorkItem(Irp, FspFsvolWriteCached, 0); + } + + Irp->IoStatus.Information = WriteLength; + } + else + { + ASSERT(0 == Irp->MdlAddress); + + Result = FspCcPrepareMdlWrite(FileObject, &WriteOffset, WriteLength, + &Irp->MdlAddress, &Irp->IoStatus); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Main); + return Result; + } + } + + /* update the current file offset if synchronous I/O */ + if (SynchronousIo) + FileObject->CurrentByteOffset.QuadPart = WriteEndOffset; + + FspFileNodeRelease(FileNode, Main); + + return STATUS_SUCCESS; } static VOID FspFsvolWriteCachedDeferred(PVOID Context1, PVOID Context2) @@ -135,7 +229,166 @@ static NTSTATUS FspFsvolWriteNonCached( { 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 WriteOffset = IrpSp->Parameters.Write.ByteOffset; + ULONG WriteLength = IrpSp->Parameters.Write.Length; + ULONG WriteKey = IrpSp->Parameters.Write.Key; + BOOLEAN WriteToEndOfFile = + FILE_WRITE_TO_END_OF_FILE == WriteOffset.LowPart && -1L == WriteOffset.HighPart; + BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + 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; + + /* paging I/O cannot change the file size */ + 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, + Irp->RequestorMode, IoReadAccess, &Irp->MdlAddress); + if (!NT_SUCCESS(Result)) + return Result; + } + + /* create request */ + Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolWriteNonCachedRequestFini, &Request); + if (!NT_SUCCESS(Result)) + return Result; + + Request->Kind = FspFsctlTransactWriteKind; + Request->Req.Write.UserContext = FileNode->UserContext; + Request->Req.Write.UserContext2 = FileDesc->UserContext2; + Request->Req.Write.Offset = WriteOffset.QuadPart; + Request->Req.Write.Length = WriteLength; + Request->Req.Write.Key = WriteKey; + Request->Req.Write.Append = WriteToEndOfFile; + Request->Req.Write.PagingIo = PagingIo; + + return FSP_STATUS_IOQ_POST; +} + +NTSTATUS FspFsvolWritePrepare( + 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; + BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + FSP_SAFE_MDL *SafeMdl = 0; + PVOID Address; + PEPROCESS Process; + BOOLEAN Success; + + Success = DEBUGRANDTEST(90, TRUE) && FspFileNodeTryAcquireExclusive(FileNode, Full); + if (!Success) + { + FspIopRetryPrepareIrp(Irp, &Result); + return Result; + } + + /* if this is a non-cached transfer on a cached file then flush and purge the file */ + if (!PagingIo && 0 != FileObject->SectionObjectPointer->DataSectionObject) + { + LARGE_INTEGER FlushOffset = IrpSp->Parameters.Write.ByteOffset; + PLARGE_INTEGER PFlushOffset = &FlushOffset; + ULONG FlushLength = IrpSp->Parameters.Write.Length; + FSP_FSCTL_FILE_INFO FileInfo; + IO_STATUS_BLOCK IoStatus = { 0 }; + + if (FILE_WRITE_TO_END_OF_FILE == FlushOffset.LowPart && -1L == FlushOffset.HighPart) + { + if (FspFileNodeTryGetFileInfo(FileNode, &FileInfo)) + FlushOffset.QuadPart = FileInfo.FileSize; + else + PFlushOffset = 0; /* we don't know how big the file is, so flush it all! */ + } + + CcFlushCache(FileObject->SectionObjectPointer, PFlushOffset, FlushLength, &IoStatus); + if (!NT_SUCCESS(IoStatus.Status)) + { + FspFileNodeRelease(FileNode, Full); + return IoStatus.Status; + } + + CcPurgeCacheSection(FileObject->SectionObjectPointer, PFlushOffset, FlushLength, FALSE); + } + + /* create a "safe" MDL if necessary */ + if (!FspSafeMdlCheck(Irp->MdlAddress)) + { + Result = FspSafeMdlCreate(Irp->MdlAddress, IoReadAccess, &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.Write.Address = (UINT64)(UINT_PTR)Address; + + FspFileNodeSetOwner(FileNode, Pgio, Request); + FspIopRequestContext(Request, RequestIrp) = Irp; + FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; } NTSTATUS FspFsvolWriteComplete( @@ -143,6 +396,36 @@ NTSTATUS FspFsvolWriteComplete( { FSP_ENTER_IOC(PAGED_CODE()); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + LARGE_INTEGER WriteOffset = IrpSp->Parameters.Write.ByteOffset; + BOOLEAN WriteToEndOfFile = + FILE_WRITE_TO_END_OF_FILE == WriteOffset.LowPart && -1L == WriteOffset.HighPart; + 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 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) + FileObject->CurrentByteOffset.QuadPart = WriteToEndOfFile ? + Response->Rsp.Write.FileInfo.FileSize : + WriteOffset.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", @@ -152,6 +435,44 @@ NTSTATUS FspFsvolWriteComplete( IrpSp->Parameters.Write.Length); } +static VOID FspFsvolWriteNonCachedRequestFini(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 FspWrite( PDEVICE_OBJECT DeviceObject, PIRP Irp) {