From: Pierre Schweitzer Date: Mon, 24 Jul 2017 17:05:05 +0000 (+0000) Subject: [RDBSS] X-Git-Tag: ReactOS-0.4.6~43 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=f8e166154982dece62e03430ad4042ee2a3d3e0c [RDBSS] - Implement __RxWriteReleaseResources(), RxCommonWrite(), RxCompleteMdl(), RxGetTopIrpIfRdbssIrp(), RxLowIoWriteShell(), RxLowIoWriteShellCompletion() - Finish implementation of RxCommonCleanup() so that it handles setting EOF on a file - Finish implementation of RxCommonCreate() so that it handles sharing violations and attempts to scavenge open files [RXCE] - Implement RxpScavengeFobxs(), RxpTrackDereference(), RxpTrackReference(), RxPurgeFobx(), RxPurgeRelatedFobxs(), RxReinitializeContext(), RxSetFileSizeWithLock(), RxScavengeFobxsForNetRoot() - Fix a bug in RxPrefixTableLookupName() where it was badly handling nodes in scavenger This commits brings several improvments to the NFS driver. First of all, now, the driver handles creating, extending and writing to files! It also handles purging dormant opened files when a file opening fails because of a sharing violation Finally, it also brings something to look at our references issues in RDBSS to help finding out why our FCB are never (or nearly) dereferenced CORE-8204 CORE-11327 CORE-13581 svn path=/trunk/; revision=75398 --- diff --git a/reactos/sdk/include/ddk/fcb.h b/reactos/sdk/include/ddk/fcb.h index a32d0c7f3c2..562059edb65 100644 --- a/reactos/sdk/include/ddk/fcb.h +++ b/reactos/sdk/include/ddk/fcb.h @@ -296,6 +296,9 @@ typedef struct _SRV_OPEN #define FOBX_FLAG_UNC_NAME 0x2000000 #define FOBX_FLAG_ENCLOSED_ALLOCATED 0x4000000 #define FOBX_FLAG_MARKED_AS_DORMANT 0x8000000 +#ifdef __REACTOS__ +#define FOBX_FLAG_DISABLE_COLLAPSING 0x20000000 +#endif typedef struct _FOBX { @@ -349,6 +352,7 @@ typedef struct _FOBX #define RDBSS_REF_TRACK_NETFCB 0x00000010 #define RDBSS_REF_TRACK_SRVOPEN 0x00000020 #define RX_PRINT_REF_TRACKING 0x40000000 +#define RX_LOG_REF_TRACKING 0x80000000 extern ULONG RdbssReferenceTracingValue; @@ -367,12 +371,21 @@ RxpTrackDereference( _In_ PVOID Instance); #define REF_TRACING_ON(TraceMask) (TraceMask & RdbssReferenceTracingValue) +#ifndef __REACTOS__ #define PRINT_REF_COUNT(TYPE, Count) \ if (REF_TRACING_ON( RDBSS_REF_TRACK_ ## TYPE) && \ (RdbssReferenceTracingValue & RX_PRINT_REF_TRACKING)) \ { \ DbgPrint("%ld\n", Count); \ } +#else +#define PRINT_REF_COUNT(TYPE, Count) \ + if (REF_TRACING_ON( RDBSS_REF_TRACK_ ## TYPE) && \ + (RdbssReferenceTracingValue & RX_PRINT_REF_TRACKING)) \ + { \ + DbgPrint("(%s:%d) %s: %ld\n", __FILE__, __LINE__, #TYPE, Count); \ + } +#endif #define RxReferenceSrvCall(SrvCall) \ RxpTrackReference(RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall); \ @@ -500,6 +513,11 @@ RxFinalizeVNetRoot( #define RxWaitForStableVNetRoot(V, R) RxWaitForStableCondition(&(V)->Condition, &(V)->TransitionWaitList, (R), NULL) #define RxTransitionVNetRoot(V, C) RxUpdateCondition((C), &(V)->Condition, &(V)->TransitionWaitList) +VOID +RxSetFileSizeWithLock( + _Inout_ PFCB Fcb, + _In_ PLONGLONG FileSize); + VOID RxGetFileSizeWithLock( _In_ PFCB Fcb, diff --git a/reactos/sdk/include/ddk/mrx.h b/reactos/sdk/include/ddk/mrx.h index fa486f6c86e..f0146ca06c9 100644 --- a/reactos/sdk/include/ddk/mrx.h +++ b/reactos/sdk/include/ddk/mrx.h @@ -323,6 +323,8 @@ typedef struct _LOWIO_CONTEXT #define LOWIO_CONTEXT_FLAG_CAN_COMPLETE_AT_DPC_LEVEL 0x08 #define LOWIO_READWRITEFLAG_PAGING_IO 0x01 +#define LOWIO_READWRITEFLAG_EXTENDING_FILESIZE 0x02 +#define LOWIO_READWRITEFLAG_EXTENDING_VDL 0x04 #define RDBSS_MANAGE_SRV_CALL_EXTENSION 0x01 #define RDBSS_MANAGE_NET_ROOT_EXTENSION 0x02 diff --git a/reactos/sdk/include/ddk/rxcontx.h b/reactos/sdk/include/ddk/rxcontx.h index bb8902810f5..bfab6952fc5 100644 --- a/reactos/sdk/include/ddk/rxcontx.h +++ b/reactos/sdk/include/ddk/rxcontx.h @@ -37,6 +37,10 @@ __RxInitializeTopLevelIrpContext( #define RxInitializeTopLevelIrpContext(a,b,c) __RxInitializeTopLevelIrpContext(a,b,c,0) +PIRP +RxGetTopIrpIfRdbssIrp( + VOID); + PRDBSS_DEVICE_OBJECT RxGetTopDeviceObjectIfRdbssIrp( VOID); @@ -307,6 +311,14 @@ typedef enum RX_CONTEXT_FLAG_MINIRDR_INITIATED = 0x80000000, } RX_CONTEXT_FLAGS; +#define RX_CONTEXT_PRESERVED_FLAGS (RX_CONTEXT_FLAG_FROM_POOL | \ + RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED | \ + RX_CONTEXT_FLAG_IN_FSP) + +#define RX_CONTEXT_INITIALIZATION_FLAGS (RX_CONTEXT_FLAG_WAIT | \ + RX_CONTEXT_FLAG_MUST_SUCCEED | \ + RX_CONTEXT_FLAG_MUST_SUCCEED_NONBLOCKING) + typedef enum { RX_CONTEXT_CREATE_FLAG_UNC_NAME = 0x1, @@ -329,6 +341,23 @@ typedef enum { RXCONTEXT_FLAG4LOWIO_LOCK_BUFFERED_ON_ENTRY = 0x200 } RX_CONTEXT_LOWIO_FLAGS; +#if DBG +#define RxSaveAndSetExceptionNoBreakpointFlag(R, F) \ +{ \ + F = FlagOn(R->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT); \ + SetFlag(R->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT); \ +} + +#define RxRestoreExceptionNoBreakpointFlag(R, F) \ +{ \ + ClearFlag(R->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT); \ + SetFlag(R->Flags, F); \ +} +#else +#define RxSaveAndSetExceptionNoBreakpointFlag(R, F) +#define RxRestoreExceptionNoBreakpointFlag(R, F) +#endif + #if DBG VOID __RxItsTheSameContext( @@ -470,6 +499,11 @@ NTAPI RxDereferenceAndDeleteRxContext_Real( _In_ PRX_CONTEXT RxContext); +VOID +NTAPI +RxReinitializeContext( + _Inout_ PRX_CONTEXT RxContext); + #if DBG #define RxDereferenceAndDeleteRxContext(RXCONTEXT) \ { \ diff --git a/reactos/sdk/include/ddk/rxprocs.h b/reactos/sdk/include/ddk/rxprocs.h index 63080ce35dc..819ba72d039 100644 --- a/reactos/sdk/include/ddk/rxprocs.h +++ b/reactos/sdk/include/ddk/rxprocs.h @@ -222,6 +222,7 @@ RxTrackPagingIoResource( } \ RxTrackPagingIoResource(Fcb, 1, __LINE__, __FILE__) +#ifndef __REACTOS__ #define RxAcquirePagingIoResourceShared(RxContext, Fcb, Flag) \ ExAcquireResourceSharedLite((Fcb)->Header.PagingIoResource, Flag); \ if (AcquiredFile) \ @@ -232,6 +233,21 @@ RxTrackPagingIoResource( } \ RxTrackPagingIoResource(Fcb, 2, __LINE__, __FILE__); \ } +#else +#define RxAcquirePagingIoResourceShared(RxContext, Fcb, Flag) \ + { \ + BOOLEAN AcquiredFile; \ + AcquiredFile = ExAcquireResourceSharedLite((Fcb)->Header.PagingIoResource, Flag); \ + if (AcquiredFile) \ + { \ + if (RxContext != NULL) \ + { \ + ((PRX_CONTEXT)RxContext)->FcbPagingIoResourceAcquired = TRUE; \ + } \ + RxTrackPagingIoResource(Fcb, 2, __LINE__, __FILE__); \ + } \ + } +#endif #define RxReleasePagingIoResource(RxContext, Fcb) \ RxTrackPagingIoResource(Fcb, 3, __LINE__, __FILE__); \ @@ -249,6 +265,26 @@ RxTrackPagingIoResource( } \ ExReleaseResourceForThreadLite((Fcb)->Header.PagingIoResource, (Thread)) +#ifdef __REACTOS__ +VOID +__RxWriteReleaseResources( + PRX_CONTEXT RxContext, + BOOLEAN ResourceOwnerSet +#ifdef RDBSS_TRACKER + , + ULONG LineNumber, + PCSTR FileName, + ULONG SerialNumber +#endif + ); + +#ifdef RDBSS_TRACKER +#define RxWriteReleaseResources(R, B) __RxWriteReleaseResources((R), (B), __LINE__, __FILE__, 0) +#else +#define RxWriteReleaseResources(R, B) __RxWriteReleaseResources((R), (B)) +#endif +#endif + BOOLEAN NTAPI RxAcquireFcbForLazyWrite( @@ -294,6 +330,7 @@ RxPrePostIrp( _In_ PIRP Irp); VOID +NTAPI RxAddToWorkque( _In_ PRX_CONTEXT RxContext, _In_ PIRP Irp); @@ -574,6 +611,10 @@ NTSTATUS RxPurgeFobxFromCache( PFOBX FobxToBePurged); +BOOLEAN +RxPurgeFobx( + PFOBX pFobx); + VOID RxUndoScavengerFinalizationMarking( PVOID Instance); @@ -745,4 +786,10 @@ RxCancelRoutine( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp); +#ifdef __REACTOS__ +#define RxWriteCacheingAllowed(F, S) ( \ + BooleanFlagOn((F)->FcbState, FCB_STATE_WRITECACHING_ENABLED) && \ + !BooleanFlagOn((S)->Flags, SRVOPEN_FLAG_DONTUSE_WRITE_CACHING)) +#endif + #endif diff --git a/reactos/sdk/include/ddk/scavengr.h b/reactos/sdk/include/ddk/scavengr.h index 2929b96e06f..05df9309054 100644 --- a/reactos/sdk/include/ddk/scavengr.h +++ b/reactos/sdk/include/ddk/scavengr.h @@ -35,6 +35,16 @@ VOID RxMarkFobxOnClose( _In_ PFOBX Fobx); +NTSTATUS +RxPurgeRelatedFobxs( + PNET_ROOT NetRoot, + PRX_CONTEXT RxContext, + BOOLEAN AttemptFinalization, + PFCB PurgingFcb); + +#define DONT_ATTEMPT_FINALIZE_ON_PURGE FALSE +#define ATTEMPT_FINALIZE_ON_PURGE TRUE + typedef enum _RDBSS_SCAVENGER_STATE { RDBSS_SCAVENGER_INACTIVE, @@ -116,6 +126,12 @@ BOOLEAN RxScavengeRelatedFobxs( _In_ PFCB Fcb); +VOID +RxScavengeFobxsForNetRoot( + PNET_ROOT NetRoot, + PFCB PurgingFcb, + BOOLEAN SynchronizeWithScavenger); + VOID RxpMarkInstanceForScavengedFinalization( PVOID Instance); diff --git a/reactos/sdk/lib/drivers/rdbsslib/rdbss.c b/reactos/sdk/lib/drivers/rdbsslib/rdbss.c index 2672d5fb0c0..6b0db09ebfc 100644 --- a/reactos/sdk/lib/drivers/rdbsslib/rdbss.c +++ b/reactos/sdk/lib/drivers/rdbsslib/rdbss.c @@ -344,6 +344,15 @@ NTAPI RxLowIoReadShellCompletion( PRX_CONTEXT RxContext); +NTSTATUS +RxLowIoWriteShell( + IN PRX_CONTEXT RxContext); + +NTSTATUS +NTAPI +RxLowIoWriteShellCompletion( + PRX_CONTEXT RxContext); + PVOID RxNewMapUserBuffer( PRX_CONTEXT RxContext); @@ -566,6 +575,7 @@ UCHAR RxSpaceForTheWrappersDeviceObject[sizeof(*RxFileSystemDeviceObject)]; KSPIN_LOCK TopLevelIrpSpinLock; LIST_ENTRY TopLevelIrpAllocatedContextsList; BOOLEAN RxForceQFIPassThrough = FALSE; +BOOLEAN RxNoAsync = FALSE; DECLARE_CONST_UNICODE_STRING(unknownId, L"???"); @@ -648,6 +658,59 @@ __RxInitializeTopLevelIrpContext( } } +/* + * @implemented + */ +VOID +__RxWriteReleaseResources( + PRX_CONTEXT RxContext, + BOOLEAN ResourceOwnerSet, + ULONG LineNumber, + PCSTR FileName, + ULONG SerialNumber) +{ + PFCB Fcb; + + PAGED_CODE(); + + ASSERT(RxContext != NULL); + + Fcb = (PFCB)RxContext->pFcb; + ASSERT(Fcb != NULL); + + /* If FCB resource was acquired, release it */ + if (RxContext->FcbResourceAcquired) + { + /* Taking care of owner */ + if (ResourceOwnerSet) + { + RxReleaseFcbForThread(RxContext, Fcb, RxContext->LowIoContext.ResourceThreadId); + } + else + { + RxReleaseFcb(RxContext, Fcb); + } + + RxContext->FcbResourceAcquired = FALSE; + } + + /* If FCB paging resource was acquired, release it */ + if (RxContext->FcbPagingIoResourceAcquired) + { + /* Taking care of owner */ + if (ResourceOwnerSet) + { + RxReleasePagingIoResourceForThread(RxContext, Fcb, RxContext->LowIoContext.ResourceThreadId); + } + else + { + RxReleasePagingIoResource(RxContext, Fcb); + } + + /* No need to release boolean here, RxReleasePagingIoResource() takes care of it */ + } +} + NTSTATUS NTAPI RxAcquireExclusiveFcbResourceInMRx( @@ -718,6 +781,7 @@ RxAddToTopLevelIrpAllocatedContextsList( * @implemented */ VOID +NTAPI RxAddToWorkque( IN PRX_CONTEXT RxContext, IN PIRP Irp) @@ -1514,7 +1578,7 @@ RxCloseAssociatedSrvOpen( RxDereferenceAndDeleteRxContext(LocalContext); } - return STATUS_SUCCESS; + return STATUS_SUCCESS; } ASSERT(RxIsFcbAcquiredExclusive(Fcb)); @@ -1735,6 +1799,9 @@ RxCollapseOrCreateSrvOpen( return Status; } +/* + * @implemented + */ NTSTATUS NTAPI RxCommonCleanup( @@ -2035,11 +2102,49 @@ RxCommonCleanup( RxRemoveShareAccess(FileObject, &Fcb->ShareAccess, "Cleanup the share access", "ClnUpShr"); } - /* In case there's caching, on a file, update file metadata */ - if (NodeType(Fcb) == RDBSS_NTC_STORAGE_TYPE_FILE && BooleanFlagOn(Fobx->Flags, 0x20000000) && - BooleanFlagOn(Fcb->FcbState, FCB_STATE_WRITECACHING_ENABLED) && !BooleanFlagOn(Fobx->pSrvOpen->Flags, SRVOPEN_FLAG_DONTUSE_WRITE_CACHING)) + /* In case there's caching, on a file, and we were asked to drop collapsing, handle it */ + if (NodeType(Fcb) == RDBSS_NTC_STORAGE_TYPE_FILE && BooleanFlagOn(Fobx->Flags, FOBX_FLAG_DISABLE_COLLAPSING) && + RxWriteCacheingAllowed(Fcb, Fobx->pSrvOpen)) { - UNIMPLEMENTED; + NTSTATUS InternalStatus; + PRX_CONTEXT InternalContext; + + /* If we can properly set EOF, there's no need to drop collapsing, try to do it */ + InternalStatus = STATUS_UNSUCCESSFUL; + InternalContext = RxCreateRxContext(Context->CurrentIrp, + Fcb->RxDeviceObject, + RX_CONTEXT_FLAG_WAIT | RX_CONTEXT_FLAG_MUST_SUCCEED_NONBLOCKING); + if (InternalContext != NULL) + { + FILE_END_OF_FILE_INFORMATION FileEOF; + + InternalStatus = STATUS_SUCCESS; + + /* Initialize the context for file information set */ + InternalContext->pFcb = RX_GET_MRX_FCB(Fcb); + InternalContext->pFobx = (PMRX_FOBX)Fobx; + InternalContext->pRelevantSrvOpen = Fobx->pSrvOpen; + + /* Get EOF from the FCB */ + FileEOF.EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart; + InternalContext->Info.FileInformationClass = FileEndOfFileInformation; + InternalContext->Info.Buffer = &FileEOF; + InternalContext->Info.Length = sizeof(FileEOF); + + /* Call the mini-rdr */ + MINIRDR_CALL_THROUGH(InternalStatus, Fcb->MRxDispatch, MRxSetFileInfo, (InternalContext)); + + /* We're done */ + RxDereferenceAndDeleteRxContext(InternalContext); + } + + /* We tried, so, clean the FOBX flag */ + ClearFlag(Fobx->Flags, FOBX_FLAG_DISABLE_COLLAPSING); + /* If it failed, then, disable collapsing on the FCB */ + if (!NT_SUCCESS(InternalStatus)) + { + ClearFlag(Fcb->FcbState, FCB_STATE_COLLAPSING_ENABLED); + } } /* We're clean! */ @@ -2219,6 +2324,9 @@ RxCommonClose( #undef BugCheckFileId } +/* + * @implemented + */ NTSTATUS NTAPI RxCommonCreate( @@ -2341,7 +2449,65 @@ RxCommonCreate( Status = RxCreateFromNetRoot(Context, &NetRootName); if (Status == STATUS_SHARING_VIOLATION) { - UNIMPLEMENTED; + ASSERT(!BooleanFlagOn(Context->Create.Flags, RX_CONTEXT_CREATE_FLAG_REPARSE)); + + /* If that happens for file creation, fail for real */ + if (Context->Create.NtCreateParameters.Disposition == FILE_CREATE) + { + Status = STATUS_OBJECT_NAME_COLLISION; + } + else + { + /* Otherwise, if possible, attempt to scavenger current FOBX + * to check whether a dormant FOBX is the reason for sharing violation + */ + if (Context->Create.TryForScavengingOnSharingViolation && + !Context->Create.ScavengingAlreadyTried) + { + /* Only doable with a VNetRoot */ + if (Context->Create.pVNetRoot != NULL) + { + PV_NET_ROOT VNetRoot; + NT_CREATE_PARAMETERS SavedParameters; + + /* Save create parameters */ + RtlCopyMemory(&SavedParameters, &Context->Create.NtCreateParameters, sizeof(NT_CREATE_PARAMETERS)); + + /* Reference the VNetRoot for the scavenging time */ + VNetRoot = (PV_NET_ROOT)Context->Create.pVNetRoot; + RxReferenceVNetRoot(VNetRoot); + + /* Prepare the RX_CONTEXT for reuse */ + RxpPrepareCreateContextForReuse(Context); + RxReinitializeContext(Context); + + /* Copy what we saved */ + RtlCopyMemory(&Context->Create.NtCreateParameters, &SavedParameters, sizeof(NT_CREATE_PARAMETERS)); + + /* And recopy what can be */ + RxCopyCreateParameters(Context); + + /* And start purging, then scavenging FOBX */ + RxPurgeRelatedFobxs((PNET_ROOT)VNetRoot->pNetRoot, Context, + DONT_ATTEMPT_FINALIZE_ON_PURGE, NULL); + RxScavengeFobxsForNetRoot((PNET_ROOT)VNetRoot->pNetRoot, + NULL, TRUE); + + /* Ask for a second round */ + Status = STATUS_MORE_PROCESSING_REQUIRED; + + /* Keep track we already scavenged */ + Context->Create.ScavengingAlreadyTried = TRUE; + + /* Reference our SRV_CALL for CBS handling */ + RxReferenceSrvCall(VNetRoot->pNetRoot->pSrvCall); + RxpProcessChangeBufferingStateRequests((PSRV_CALL)VNetRoot->pNetRoot->pSrvCall, FALSE); + + /* Drop our extra reference */ + RxDereferenceVNetRoot(VNetRoot, LHS_LockNotHeld); + } + } + } } else if (Status == STATUS_REPARSE) { @@ -2589,6 +2755,18 @@ NTAPI RxCommonFileSystemControl( PRX_CONTEXT Context) { + PIRP Irp; + ULONG ControlCode; + PIO_STACK_LOCATION Stack; + + PAGED_CODE(); + + Irp = Context->CurrentIrp; + Stack = Context->CurrentIrpSp; + ControlCode = Stack->Parameters.FileSystemControl.FsControlCode; + + DPRINT1("RxCommonFileSystemControl: %p, %p, %d, %lx\n", Context, Irp, Stack->MinorFunction, ControlCode); + UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } @@ -3405,151 +3583,1108 @@ RxCommonUnimplemented( NTSTATUS NTAPI RxCommonWrite( - PRX_CONTEXT Context) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -NTSTATUS -NTAPI -RxCompleteMdl( - IN PRX_CONTEXT RxContext) -{ - PAGED_CODE(); - - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* - * @implemented - */ -VOID -RxCopyCreateParameters( - IN PRX_CONTEXT RxContext) + PRX_CONTEXT RxContext) { PIRP Irp; - PVOID DfsContext; + PFCB Fcb; + PFOBX Fobx; + NTSTATUS Status; + PNET_ROOT NetRoot; + PSRV_OPEN SrvOpen; PFILE_OBJECT FileObject; PIO_STACK_LOCATION Stack; - PDFS_NAME_CONTEXT DfsNameContext; - PIO_SECURITY_CONTEXT SecurityContext; + LARGE_INTEGER ByteOffset; + NODE_TYPE_CODE NodeTypeCode; + PLOWIO_CONTEXT LowIoContext; + PRDBSS_DEVICE_OBJECT RxDeviceObject; + ULONG WriteLength, CapturedRxContextSerialNumber = RxContext->SerialNumber; + LONGLONG FileSize, ValidDataLength, InitialFileSize, InitialValidDataLength; + BOOLEAN CanWait, PagingIo, NoCache, Sync, NormalFile, WriteToEof, IsPipe, NoPreposting, InFsp, RecursiveWriteThrough, CalledByLazyWriter, SwitchBackToAsync, ExtendingFile, ExtendingValidData, UnwindOutstandingAsync, ResourceOwnerSet, PostIrp, ContextReferenced; - Irp = RxContext->CurrentIrp; - Stack = RxContext->CurrentIrpSp; - FileObject = Stack->FileObject; - SecurityContext = Stack->Parameters.Create.SecurityContext; + PAGED_CODE(); - RxContext->Create.NtCreateParameters.SecurityContext = SecurityContext; - if (SecurityContext->AccessState != NULL && SecurityContext->AccessState->SecurityDescriptor != NULL) - { - RxContext->Create.SdLength = RtlLengthSecurityDescriptor(SecurityContext->AccessState->SecurityDescriptor); - DPRINT("SD Ctxt: %p, Length: %lx\n", RxContext->Create.NtCreateParameters.SecurityContext, - RxContext->Create.SdLength); - } - if (SecurityContext->SecurityQos != NULL) + Fcb = (PFCB)RxContext->pFcb; + NodeTypeCode = NodeType(Fcb); + /* Validate FCB type */ + if (NodeTypeCode != RDBSS_NTC_STORAGE_TYPE_FILE && NodeTypeCode != RDBSS_NTC_VOLUME_FCB && + NodeTypeCode != RDBSS_NTC_SPOOLFILE && NodeTypeCode != RDBSS_NTC_MAILSLOT) { - RxContext->Create.NtCreateParameters.ImpersonationLevel = SecurityContext->SecurityQos->ImpersonationLevel; + return STATUS_INVALID_DEVICE_REQUEST; } - else + + /* We'll write to file, keep track of it */ + Fcb->IsFileWritten = TRUE; + + Stack = RxContext->CurrentIrpSp; + /* Set write through if asked */ + if (BooleanFlagOn(Stack->Flags, SL_WRITE_THROUGH)) { - RxContext->Create.NtCreateParameters.ImpersonationLevel = SecurityImpersonation; + SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_WRITE_THROUGH); } - RxContext->Create.NtCreateParameters.DesiredAccess = SecurityContext->DesiredAccess; - RxContext->Create.NtCreateParameters.AllocationSize.QuadPart = Irp->Overlay.AllocationSize.QuadPart; - RxContext->Create.NtCreateParameters.FileAttributes = Stack->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS; - RxContext->Create.NtCreateParameters.ShareAccess = Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS; - RxContext->Create.NtCreateParameters.Disposition = (Stack->Parameters.Create.Options >> 24) & 0x000000FF; - RxContext->Create.NtCreateParameters.CreateOptions = Stack->Parameters.Create.Options & 0xFFFFFF; + Fobx = (PFOBX)RxContext->pFobx; + DPRINT("RxCommonWrite(%p) FOBX: %p, FCB: %p\n", RxContext, Fobx, Fcb); - DfsContext = FileObject->FsContext2; - DfsNameContext = FileObject->FsContext; - RxContext->Create.NtCreateParameters.DfsContext = DfsContext; - RxContext->Create.NtCreateParameters.DfsNameContext = DfsNameContext; - ASSERT(DfsContext == NULL || DfsContext == UIntToPtr(DFS_OPEN_CONTEXT) || - DfsContext == UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT) || - DfsContext == UIntToPtr(DFS_CSCAGENT_NAME_CONTEXT) || - DfsContext == UIntToPtr(DFS_USER_NAME_CONTEXT)); - ASSERT(DfsNameContext == NULL || DfsNameContext->NameContextType == DFS_OPEN_CONTEXT || - DfsNameContext->NameContextType == DFS_DOWNLEVEL_OPEN_CONTEXT || - DfsNameContext->NameContextType == DFS_CSCAGENT_NAME_CONTEXT || - DfsNameContext->NameContextType == DFS_USER_NAME_CONTEXT); - FileObject->FsContext2 = NULL; - FileObject->FsContext = NULL; + /* Get some parameters */ + Irp = RxContext->CurrentIrp; + NoPreposting = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_NO_PREPOSTING_NEEDED); + InFsp = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP); + CanWait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT); + PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + NoCache = BooleanFlagOn(Irp->Flags, IRP_NOCACHE); + Sync = !BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION); + WriteLength = Stack->Parameters.Write.Length; + ByteOffset.QuadPart = Stack->Parameters.Write.ByteOffset.QuadPart; + DPRINT("Writing: %lx@%I64x %s %s %s %s\n", WriteLength, ByteOffset.QuadPart, + (CanWait ? "CW" : "!CW"), (PagingIo ? "PI" : "!PI"), (NoCache ? "NC" : "!NC"), (Sync ? "S" : "!S")); - RxContext->pFcb = NULL; - RxContext->Create.ReturnedCreateInformation = 0; + RxItsTheSameContext(); - /* if we stripped last \, it has to be a directory! */ - if (BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH)) - { - SetFlag(RxContext->Create.NtCreateParameters.CreateOptions, FILE_DIRECTORY_FILE); - } + RxContext->FcbResourceAcquired = FALSE; + RxContext->FcbPagingIoResourceAcquired = FALSE; - RxContext->Create.EaLength = Stack->Parameters.Create.EaLength; - if (RxContext->Create.EaLength == 0) + LowIoContext = &RxContext->LowIoContext; + CheckForLoudOperations(RxContext); + if (BooleanFlagOn(LowIoContext->Flags, LOWIO_CONTEXT_FLAG_LOUDOPS)) { - RxContext->Create.EaBuffer = NULL; + DPRINT("LoudWrite %I64x/%lx on %lx vdl/size/alloc %I64x/%I64x/%I64x\n", + ByteOffset, WriteLength, + Fcb, Fcb->Header.ValidDataLength, Fcb->Header.FileSize, Fcb->Header.AllocationSize); } - else + + RxDeviceObject = RxContext->RxDeviceObject; + /* Update stats */ + if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP) && Fcb->CachedNetRootType == NET_ROOT_DISK) { - RxContext->Create.EaBuffer = Irp->AssociatedIrp.SystemBuffer; - DPRINT("EA Buffer: %p, Length: %lx\n", Irp->AssociatedIrp.SystemBuffer, RxContext->Create.EaLength); - } -} + InterlockedIncrement((volatile long *)&RxDeviceObject->WriteOperations); -NTSTATUS -RxCreateFromNetRoot( - PRX_CONTEXT Context, - PUNICODE_STRING NetRootName) -{ - PFCB Fcb; - NTSTATUS Status; - PNET_ROOT NetRoot; - PFILE_OBJECT FileObject; - PIO_STACK_LOCATION Stack; - ACCESS_MASK DesiredAccess; - USHORT DesiredShareAccess; + if (ByteOffset.QuadPart != Fobx->Specific.DiskFile.PredictedWriteOffset) + { + InterlockedIncrement((volatile long *)&RxDeviceObject->RandomWriteOperations); + } + Fobx->Specific.DiskFile.PredictedWriteOffset = ByteOffset.QuadPart + WriteLength; - PAGED_CODE(); + if (PagingIo) + { + ExInterlockedAddLargeStatistic(&RxDeviceObject->PagingWriteBytesRequested, WriteLength); + } + else if (NoCache) + { + ExInterlockedAddLargeStatistic(&RxDeviceObject->NonPagingWriteBytesRequested, WriteLength); + } + else + { + ExInterlockedAddLargeStatistic(&RxDeviceObject->CacheWriteBytesRequested, WriteLength); + } + } - /* Validate that the context is consistent */ - if (Context->Create.pNetRoot == NULL) + NetRoot = (PNET_ROOT)Fcb->NetRoot; + IsPipe = (NetRoot->Type == NET_ROOT_PIPE); + /* Keep track for normal writes */ + if (NetRoot->Type == NET_ROOT_DISK || NetRoot->Type == NET_ROOT_WILD) { - return STATUS_BAD_NETWORK_PATH; + NormalFile = TRUE; } - - NetRoot = (PNET_ROOT)Context->Create.pNetRoot; - if (Context->RxDeviceObject != NetRoot->pSrvCall->RxDeviceObject) + else { - return STATUS_BAD_NETWORK_PATH; + NormalFile = FALSE; } - if (Context->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_OPEN_CONTEXT) && - !BooleanFlagOn(NetRoot->pSrvCall->Flags, SRVCALL_FLAG_DFS_AWARE_SERVER)) + /* Zero-length write is immediate success */ + if (NormalFile && WriteLength == 0) { - return STATUS_DFS_UNAVAILABLE; + return STATUS_SUCCESS; } - if (Context->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT) && - BooleanFlagOn(NetRoot->Flags, NETROOT_FLAG_DFS_AWARE_NETROOT)) + /* Check whether we have input data */ + if (Irp->UserBuffer == NULL && Irp->MdlAddress == NULL) { - return STATUS_OBJECT_TYPE_MISMATCH; + return STATUS_INVALID_PARAMETER; } - Stack = Context->CurrentIrpSp; - DesiredShareAccess = Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS; - if (NetRoot->Type == NET_ROOT_PRINT) + /* Are we writting to EOF? */ + WriteToEof = ((ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE) && (ByteOffset.HighPart == -1)); + /* FIXME: validate length/offset */ + + /* Get our SRV_OPEN in case of normal write */ + if (Fobx != NULL) { - DesiredShareAccess = FILE_SHARE_VALID_FLAGS; + SrvOpen = (PSRV_OPEN)Fobx->pSrvOpen; + } + else + { + SrvOpen = NULL; } - DesiredAccess = Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_ALL_ACCESS; + FileObject = Stack->FileObject; - /* We don't support renaming yet */ - if (BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY)) + /* If we have caching enabled, check whether we have to defer write */ + if (!NoCache) + { + if (RxWriteCacheingAllowed(Fcb, SrvOpen)) + { + if (!CcCanIWrite(FileObject, WriteLength, + (CanWait && !BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP)), + BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_DEFERRED_WRITE))) + { + BOOLEAN Retrying; + + Retrying = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_DEFERRED_WRITE); + + RxPrePostIrp(RxContext, Irp); + + SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_DEFERRED_WRITE); + + CcDeferWrite(FileObject, (PCC_POST_DEFERRED_WRITE)RxAddToWorkque, RxContext, Irp, WriteLength, Retrying); + + return STATUS_PENDING; + } + } + } + + /* Initialize the low IO context for write */ + RxInitializeLowIoContext(LowIoContext, LOWIO_OP_WRITE); + + /* Initialize our (many) booleans */ + RecursiveWriteThrough = FALSE; + CalledByLazyWriter = FALSE; + SwitchBackToAsync = FALSE; + ExtendingFile = FALSE; + ExtendingValidData = FALSE; + UnwindOutstandingAsync = FALSE; + ResourceOwnerSet = FALSE; + PostIrp = FALSE; + ContextReferenced = FALSE; + +#define _SEH2_TRY_RETURN(S) S; goto try_exit + + _SEH2_TRY + { + /* No volume FCB here! */ + ASSERT((NodeTypeCode == RDBSS_NTC_STORAGE_TYPE_FILE) || + (NodeTypeCode == RDBSS_NTC_SPOOLFILE) || + (NodeTypeCode == RDBSS_NTC_MAILSLOT)); + + /* Writing to EOF on a paging file is non sense */ + ASSERT(!(WriteToEof && PagingIo)); + + RxItsTheSameContext(); + + /* Start locking stuff */ + if (!PagingIo && !NoPreposting) + { + /* If it's already acquired, all fine */ + if (RxContext->FcbResourceAcquired) + { + ASSERT(!IsPipe); + } + else + { + /* Otherwise, try to acquire shared (excepted for pipes) */ + if (IsPipe) + { + Status = RxAcquireExclusiveFcb(RxContext, Fcb); + } + else if (CanWait || + (!NoCache && RxWriteCacheingAllowed(Fcb, SrvOpen))) + { + Status = RxAcquireSharedFcb(RxContext, Fcb); + } + else + { + Status = RxAcquireSharedFcbWaitForEx(RxContext, Fcb); + } + + /* We'll post IRP to retry */ + if (Status == STATUS_LOCK_NOT_GRANTED) + { + PostIrp = TRUE; + DPRINT1("Failed to acquire lock!\n"); + _SEH2_TRY_RETURN(Status); + } + + /* We'll just fail */ + if (Status != STATUS_SUCCESS) + { + _SEH2_TRY_RETURN(Status); + } + + /* Resource acquired */ + RxContext->FcbResourceAcquired = TRUE; + } + + /* At that point, resource is acquired */ + if (IsPipe) + { + ASSERT(RxContext->FcbResourceAcquired); + } + else + { + BOOLEAN IsDormant; + + /* Now, check whether we have to promote shared lock */ + if (NodeTypeCode == RDBSS_NTC_STORAGE_TYPE_FILE && Fobx != NULL) + { + IsDormant = BooleanFlagOn(Fobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT); + } + else + { + IsDormant = FALSE; + } + + /* We're writing beyond VDL, we'll need an exclusive lock if not dormant */ + if (RxIsFcbAcquiredShared(Fcb) && + ByteOffset.QuadPart + WriteLength > Fcb->Header.ValidDataLength.QuadPart) + { + if (!IsDormant) + { + RxReleaseFcb(RxContext, Fcb); + RxContext->FcbResourceAcquired = FALSE; + + Status = RxAcquireExclusiveFcb(RxContext, Fcb); + if (Status == STATUS_LOCK_NOT_GRANTED) + { + PostIrp = TRUE; + DPRINT1("Failed to acquire lock!\n"); + _SEH2_TRY_RETURN(Status); + } + + if (Status != STATUS_SUCCESS) + { + _SEH2_TRY_RETURN(Status); + } + + RxContext->FcbResourceAcquired = TRUE; + } + } + + /* If we're writing in VDL, or if we're dormant, shared lock is enough */ + if (ByteOffset.QuadPart + WriteLength <= Fcb->Header.ValidDataLength.QuadPart || + IsDormant) + { + if (RxIsFcbAcquiredExclusive(Fcb)) + { + RxConvertToSharedFcb(RxContext, Fcb); + } + } + else + { + /* We're extending file, disable collapsing */ + ASSERT(RxIsFcbAcquiredExclusive(Fcb)); + + DPRINT("Disabling collapsing\n"); + + if (NodeTypeCode == RDBSS_NTC_STORAGE_TYPE_FILE && Fobx != NULL) + { + SetFlag(Fobx->Flags, FOBX_FLAG_DISABLE_COLLAPSING); + } + } + + ASSERT(RxContext->FcbResourceAcquired); + } + + /* Keep track of the acquired resource */ + LowIoContext->Resource = Fcb->Header.Resource; + } + else + { + /* Paging IO */ + ASSERT(!IsPipe); + + /* Lock the paging resource */ + RxAcquirePagingIoResourceShared(RxContext, Fcb, TRUE); + + /* Keep track of the acquired resource */ + LowIoContext->Resource = Fcb->Header.PagingIoResource; + } + + if (IsPipe) + { + UNIMPLEMENTED; + _SEH2_TRY_RETURN(Status = STATUS_NOT_IMPLEMENTED); + } + + /* If it's a non cached write, or if caching is disallowed */ + if (NoCache || !RxWriteCacheingAllowed(Fcb, SrvOpen)) + { + /* If cache was previously enabled, we'll have to flush before writing */ + if (!PagingIo && Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) + { + LARGE_INTEGER FlushOffset; + + /* FCB is lock */ + ASSERT(RxIsFcbAcquiredExclusive(Fcb) || RxIsFcbAcquiredShared(Fcb)); + + /* If shared, we'll have to relock exclusive */ + if (!RxIsFcbAcquiredExclusive(Fcb)) + { + /* Release and retry exclusive */ + RxReleaseFcb(RxContext, Fcb); + RxContext->FcbResourceAcquired = FALSE; + + Status = RxAcquireExclusiveFcb(RxContext, Fcb); + if (Status == STATUS_LOCK_NOT_GRANTED) + { + PostIrp = TRUE; + DPRINT1("Failed to acquire lock for flush!\n"); + _SEH2_TRY_RETURN(Status); + } + + if (Status != STATUS_SUCCESS) + { + _SEH2_TRY_RETURN(Status); + } + + RxContext->FcbResourceAcquired = TRUE; + } + + /* Get the length to flush */ + if (WriteToEof) + { + RxGetFileSizeWithLock(Fcb, &FlushOffset.QuadPart); + } + else + { + FlushOffset.QuadPart = ByteOffset.QuadPart; + } + + /* Perform the flushing */ + RxAcquirePagingIoResource(RxContext, Fcb); + CcFlushCache(&Fcb->NonPaged->SectionObjectPointers, &FlushOffset, + WriteLength, &Irp->IoStatus); + RxReleasePagingIoResource(RxContext, Fcb); + + /* Cannot continue if flushing failed */ + if (!NT_SUCCESS(Irp->IoStatus.Status)) + { + _SEH2_TRY_RETURN(Status = Irp->IoStatus.Status); + } + + /* Synchronize */ + RxAcquirePagingIoResource(RxContext, Fcb); + RxReleasePagingIoResource(RxContext, Fcb); + + /* And purge */ + CcPurgeCacheSection(&Fcb->NonPaged->SectionObjectPointers, + &FlushOffset, WriteLength, FALSE); + } + } + + /* If not paging IO, check if write is allowed */ + if (!PagingIo) + { + if (!FsRtlCheckLockForWriteAccess(&Fcb->Specific.Fcb.FileLock, Irp)) + { + _SEH2_TRY_RETURN(Status = STATUS_FILE_LOCK_CONFLICT); + } + } + + /* Get file sizes */ + ValidDataLength = Fcb->Header.ValidDataLength.QuadPart; + RxGetFileSizeWithLock(Fcb, &FileSize); + ASSERT(ValidDataLength <= FileSize); + + /* If paging IO, we cannot write past file size + * so fix write length if needed + */ + if (PagingIo) + { + if (ByteOffset.QuadPart >= FileSize) + { + _SEH2_TRY_RETURN(Status = STATUS_SUCCESS); + } + + if (WriteLength > FileSize - ByteOffset.QuadPart) + { + WriteLength = FileSize - ByteOffset.QuadPart; + } + } + + /* If we're being called by the lazywrite */ + if (Fcb->Specific.Fcb.LazyWriteThread == PsGetCurrentThread()) + { + CalledByLazyWriter = TRUE; + + /* Fail if we're beyong VDL */ + if (BooleanFlagOn(Fcb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE)) + { + if ((ByteOffset.QuadPart + WriteLength > ValidDataLength) && + (ByteOffset.QuadPart < FileSize)) + { + if (ByteOffset.QuadPart + WriteLength > ((ValidDataLength + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + { + _SEH2_TRY_RETURN(Status = STATUS_FILE_LOCK_CONFLICT); + } + } + } + } + + /* If that's a recursive synchronous page write */ + if (BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && + BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_RECURSIVE_CALL)) + { + PIRP TopIrp; + + /* Check the top level IRP on the FastIO path */ + TopIrp = RxGetTopIrpIfRdbssIrp(); + if (TopIrp != NULL && (ULONG_PTR)TopIrp > FSRTL_FAST_IO_TOP_LEVEL_IRP) + { + PIO_STACK_LOCATION IrpStack; + + ASSERT(NodeType(TopIrp) == IO_TYPE_IRP); + + /* If the top level IRP was a cached write for this file, keep track */ + IrpStack = IoGetCurrentIrpStackLocation(TopIrp); + if (IrpStack->MajorFunction == IRP_MJ_WRITE && + IrpStack->FileObject->FsContext == FileObject->FsContext) + { + RecursiveWriteThrough = TRUE; + SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_WRITE_THROUGH); + } + } + } + + /* Now, deal with file size and VDL */ + if (!CalledByLazyWriter && !RecursiveWriteThrough && + (WriteToEof || ByteOffset.QuadPart + WriteLength > ValidDataLength)) + { + /* Not sync? Let's make it sync, just the time we extended */ + if (!Sync) + { + CanWait = TRUE; + SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_WAIT); + ClearFlag(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION); + Sync = TRUE; + + /* Keep track we'll have to switch back to async */ + if (NoCache) + { + SwitchBackToAsync = TRUE; + } + } + + /* Release all the locks */ + RxWriteReleaseResources(RxContext, 0); + + /* Acquire exclusive */ + Status = RxAcquireExclusiveFcb(RxContext, Fcb); + if (Status == STATUS_LOCK_NOT_GRANTED) + { + PostIrp = TRUE; + DPRINT1("Failed to acquire lock for extension!\n"); + _SEH2_TRY_RETURN(Status); + } + + if (Status != STATUS_SUCCESS) + { + _SEH2_TRY_RETURN(Status); + } + + RxContext->FcbResourceAcquired = TRUE; + + RxItsTheSameContext(); + + /* Get the sizes again, to be sure they didn't change in the meantime */ + ValidDataLength = Fcb->Header.ValidDataLength.QuadPart; + RxGetFileSizeWithLock(Fcb, &FileSize); + ASSERT(ValidDataLength <= FileSize); + + /* Check we can switch back to async? */ + if ((SwitchBackToAsync && Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) || + (ByteOffset.QuadPart + WriteLength > FileSize) || RxNoAsync) + { + SwitchBackToAsync = FALSE; + } + + /* If paging IO, check we don't try to extend the file */ + if (PagingIo) + { + if (ByteOffset.QuadPart >= FileSize) + { + _SEH2_TRY_RETURN(Status = STATUS_SUCCESS); + } + + if (WriteLength > FileSize - ByteOffset.QuadPart) + { + WriteLength = FileSize - ByteOffset.QuadPart; + } + } + } + + /* Save our initial sizes for potential rollback */ + InitialFileSize = FileSize; + InitialValidDataLength = ValidDataLength; + /* If writing to EOF, update byte offset with file size */ + if (WriteToEof) + { + ByteOffset.QuadPart = FileSize; + } + + /* Check again whether we're allowed to write */ + if (!PagingIo) + { + if (!FsRtlCheckLockForWriteAccess(&Fcb->Specific.Fcb.FileLock, Irp )) + { + _SEH2_TRY_RETURN(Status = STATUS_FILE_LOCK_CONFLICT); + } + + /* Do we have to extend? */ + if (NormalFile && (ByteOffset.QuadPart + WriteLength > FileSize)) + { + DPRINT("Need to extend file\n"); + ExtendingFile = TRUE; + SetFlag(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_EXTENDING_FILESIZE); + } + } + + /* Let's start to extend */ + if (ExtendingFile) + { + /* If we're past allocating, inform mini-rdr */ + FileSize = ByteOffset.QuadPart + WriteLength; + if (FileSize > Fcb->Header.AllocationSize.QuadPart) + { + LARGE_INTEGER NewAllocationSize; + + DPRINT("Extending %p\n", RxContext); + + if (NoCache) + { + C_ASSERT(sizeof(LONGLONG) == sizeof(LARGE_INTEGER)); + MINIRDR_CALL(Status, RxContext, Fcb->MRxDispatch, MRxExtendForNonCache, + (RxContext, (PLARGE_INTEGER)&FileSize, &NewAllocationSize)); + } + else + { + C_ASSERT(sizeof(LONGLONG) == sizeof(LARGE_INTEGER)); + MINIRDR_CALL(Status, RxContext, Fcb->MRxDispatch, MRxExtendForCache, + (RxContext, (PLARGE_INTEGER)&FileSize, &NewAllocationSize)); + } + + if (!NT_SUCCESS(Status)) + { + _SEH2_TRY_RETURN(Status); + } + + if (FileSize > NewAllocationSize.QuadPart) + { + NewAllocationSize.QuadPart = FileSize; + } + + /* And update FCB */ + Fcb->Header.AllocationSize.QuadPart = NewAllocationSize.QuadPart; + } + + /* Set the new sizes */ + RxSetFileSizeWithLock(Fcb, &FileSize); + RxAdjustAllocationSizeforCC(Fcb); + + /* And inform Cc */ + if (CcIsFileCached(FileObject)) + { + CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize); + } + } + + /* Do we have to extend VDL? */ + if (!CalledByLazyWriter && !RecursiveWriteThrough) + { + if (WriteToEof || ByteOffset.QuadPart + WriteLength > ValidDataLength) + { + ExtendingValidData = TRUE; + SetFlag(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_EXTENDING_VDL); + } + } + + /* If none cached write */ + if (PagingIo || NoCache || !RxWriteCacheingAllowed(Fcb, SrvOpen)) + { + /* Switch back to async, if asked to */ + if (SwitchBackToAsync) + { + CanWait = FALSE; + Sync = FALSE; + + ClearFlag(RxContext->Flags, RX_CONTEXT_FLAG_WAIT); + SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION); + } + + /* If not synchronous, keep track of writes to be finished */ + if (!Sync) + { + if (Fcb->NonPaged->OutstandingAsyncEvent == NULL) + { + Fcb->NonPaged->OutstandingAsyncEvent = &Fcb->NonPaged->TheActualEvent; + KeInitializeEvent(Fcb->NonPaged->OutstandingAsyncEvent, + NotificationEvent, FALSE); + } + + if (ExInterlockedAddUlong(&Fcb->NonPaged->OutstandingAsyncWrites, + 1, + &RxStrucSupSpinLock) == 0) + { + KeResetEvent(Fcb->NonPaged->OutstandingAsyncEvent); + } + + UnwindOutstandingAsync = TRUE; + LowIoContext->ParamsFor.ReadWrite.NonPagedFcb = Fcb->NonPaged; + } + + /* Set our LOWIO_CONTEXT information */ + LowIoContext->ParamsFor.ReadWrite.ByteOffset = ByteOffset.QuadPart; + LowIoContext->ParamsFor.ReadWrite.ByteCount = WriteLength; + + RxItsTheSameContext(); + + /* We have to be locked */ + ASSERT(RxContext->FcbResourceAcquired || RxContext->FcbPagingIoResourceAcquired); + + /* Update thread ID if we're in FSP */ + if (InFsp) + { + LowIoContext->ResourceThreadId = (ULONG_PTR)RxContext | 3; + + if (RxContext->FcbResourceAcquired) + { + ExSetResourceOwnerPointer(Fcb->Header.Resource, (PVOID)((ULONG_PTR)RxContext | 3)); + } + + if (RxContext->FcbPagingIoResourceAcquired) + { + ExSetResourceOwnerPointer(Fcb->Header.PagingIoResource, (PVOID)((ULONG_PTR)RxContext | 3)); + } + + ResourceOwnerSet = TRUE; + } + + /* And perform the write */ + Status = RxLowIoWriteShell(RxContext); + + RxItsTheSameContext(); + + /* Not outstanding write anymore */ + if (UnwindOutstandingAsync && Status == STATUS_PENDING) + { + UnwindOutstandingAsync = FALSE; + } + } + /* Cached write */ + else + { + /* If cache wasn't enabled yet, do it */ + if (FileObject->PrivateCacheMap == NULL) + { + if (BooleanFlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE)) + { + _SEH2_TRY_RETURN(Status = STATUS_FILE_CLOSED); + } + + RxAdjustAllocationSizeforCC(Fcb); + + CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, + FALSE, &RxData.CacheManagerCallbacks, Fcb); + + CcSetReadAheadGranularity(FileObject, NetRoot->DiskParameters.ReadAheadGranularity); + } + + /* If that's a MDL backed write */ + if (BooleanFlagOn(RxContext->MinorFunction, IRP_MN_MDL)) + { + /* Shouldn't happen */ + ASSERT(FALSE); + ASSERT(CanWait); + + /* Perform it, though */ + CcPrepareMdlWrite(FileObject, &ByteOffset, WriteLength, + &Irp->MdlAddress, &Irp->IoStatus); + + Status = Irp->IoStatus.Status; + } + else + { + PVOID SystemBuffer; + ULONG BreakpointsSave; + + /* Map the user buffer */ + SystemBuffer = RxNewMapUserBuffer(RxContext); + if (SystemBuffer == NULL) + { + _SEH2_TRY_RETURN(Status = STATUS_INSUFFICIENT_RESOURCES); + } + + RxSaveAndSetExceptionNoBreakpointFlag(RxContext, BreakpointsSave); + + RxItsTheSameContext(); + + /* And deal with Cc */ + if (!CcCopyWrite(FileObject, &ByteOffset, WriteLength, CanWait, + SystemBuffer)) + { + RxRestoreExceptionNoBreakpointFlag(RxContext, BreakpointsSave); + + RxItsTheSameContext(); + + DPRINT1("CcCopyWrite failed for: %p %I64d %d %lx\n", + FileObject, Fcb->Header.FileSize.QuadPart, WriteLength, Status); + + PostIrp = TRUE; + } + else + { + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = WriteLength; + + RxRestoreExceptionNoBreakpointFlag(RxContext, BreakpointsSave); + + RxItsTheSameContext(); + + DPRINT("CcCopyWrite succeed for: %p %I64d %d %lx\n", + FileObject, Fcb->Header.FileSize.QuadPart, WriteLength, Status); + } + } + } + +try_exit: NOTHING; + + /* If we've to post the IRP */ + if (PostIrp) + { + /* Reset the file size if required */ + if (ExtendingFile && !IsPipe) + { + ASSERT(RxWriteCacheingAllowed(Fcb, SrvOpen)); + ASSERT(Fcb->Header.PagingIoResource != NULL); + + RxAcquirePagingIoResource(RxContext, Fcb); + RxSetFileSizeWithLock(Fcb, &InitialFileSize); + RxReleasePagingIoResource(RxContext, Fcb); + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) + { + *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; + } + } + + InterlockedIncrement((volatile long *)&RxContext->ReferenceCount); + ContextReferenced = TRUE; + + /* Release locks */ + ASSERT(!ResourceOwnerSet); + RxWriteReleaseResources(RxContext, ResourceOwnerSet); + +#ifdef RDBSS_TRACKER + ASSERT(RxContext->AcquireReleaseFcbTrackerX == 0); +#endif + + /* And post the request */ + Status = RxFsdPostRequest(RxContext); + } + else + { + if (!IsPipe) + { + /* Update FILE_OBJECT if synchronous write succeed */ + if (!PagingIo) + { + if (NT_SUCCESS(Status) && BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO)) + { + FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + Irp->IoStatus.Information; + } + } + + /* If write succeed, ,also update FILE_OBJECT flags */ + if (NT_SUCCESS(Status) && Status != STATUS_PENDING) + { + /* File was modified */ + if (!PagingIo) + { + SetFlag(FileObject->Flags, FO_FILE_MODIFIED); + } + + /* If was even extended */ + if (ExtendingFile) + { + SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); + } + + /* If VDL was extended, update FCB and inform Cc */ + if (ExtendingValidData) + { + LONGLONG LastOffset; + + LastOffset = ByteOffset.QuadPart + Irp->IoStatus.Information; + if (FileSize < LastOffset) + { + LastOffset = FileSize; + } + + Fcb->Header.ValidDataLength.QuadPart = LastOffset; + + if (NoCache && CcIsFileCached(FileObject)) + { + CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize); + } + } + } + } + } + } + _SEH2_FINALLY + { + /* Finally, if we failed while extension was required */ + if (_SEH2_AbnormalTermination() && (ExtendingFile || ExtendingValidData)) + { + /* Rollback! */ + if (!IsPipe) + { + ASSERT(Fcb->Header.PagingIoResource != NULL); + + RxAcquirePagingIoResource(RxContext, Fcb); + RxSetFileSizeWithLock(Fcb, &InitialFileSize); + Fcb->Header.ValidDataLength.QuadPart = InitialValidDataLength; + RxReleasePagingIoResource(RxContext, Fcb); + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) + { + *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; + } + } + } + + /* One async write less */ + if (UnwindOutstandingAsync) + { + ASSERT(!IsPipe); + + ExInterlockedAddUlong(&Fcb->NonPaged->OutstandingAsyncWrites, -1, &RxStrucSupSpinLock); + KeSetEvent(Fcb->NonPaged->OutstandingAsyncEvent, IO_NO_INCREMENT, FALSE); + } + + /* And now, cleanup everything */ + if (_SEH2_AbnormalTermination() || Status != STATUS_PENDING || PostIrp) + { + /* If we didn't post, release every lock (for posting, it's already done) */ + if (!PostIrp) + { + RxWriteReleaseResources(RxContext, ResourceOwnerSet); + } + + /* If the context was referenced - posting, dereference it */ + if (ContextReferenced) + { + RxDereferenceAndDeleteRxContext(RxContext); + } + + /* If that's a pipe operation, resume any blocked one */ + if (!PostIrp) + { + if (BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION)) + { + RxResumeBlockedOperations_Serially(RxContext, &Fobx->Specific.NamedPipe.ReadSerializationQueue); + } + } + + /* Sanity check for write */ + if (Status == STATUS_SUCCESS) + { + ASSERT(Irp->IoStatus.Information <= Stack->Parameters.Write.Length); + } + } + /* Just dereference our context */ + else + { + ASSERT(!Sync); + RxDereferenceAndDeleteRxContext(RxContext); + } + } + _SEH2_END; + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +RxCompleteMdl( + IN PRX_CONTEXT RxContext) +{ + PIRP Irp; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + +#define BugCheckFileId RDBSS_BUG_CHECK_CACHESUP + + PAGED_CODE(); + + Irp = RxContext->CurrentIrp; + Stack = RxContext->CurrentIrpSp; + FileObject = Stack->FileObject; + + /* We can only complete for IRP_MJ_READ and IRP_MJ_WRITE */ + switch (RxContext->MajorFunction) + { + /* Call the Cc function */ + case IRP_MJ_READ: + CcMdlReadComplete(FileObject, Irp->MdlAddress); + break; + + case IRP_MJ_WRITE: + /* If here, we can wait */ + ASSERT(BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT)); + + /* Call the Cc function */ + CcMdlWriteComplete(FileObject, &Stack->Parameters.Write.ByteOffset, Irp->MdlAddress); + + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + + default: + DPRINT1("Invalid major for RxCompleteMdl: %d\n", RxContext->MajorFunction); + RxBugCheck(RxContext->MajorFunction, 0, 0); + break; + } + + /* MDL was freed */ + Irp->MdlAddress = NULL; + + /* And complete the IRP */ + RxCompleteRequest(RxContext, STATUS_SUCCESS); + +#undef BugCheckFileId + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +RxCopyCreateParameters( + IN PRX_CONTEXT RxContext) +{ + PIRP Irp; + PVOID DfsContext; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PDFS_NAME_CONTEXT DfsNameContext; + PIO_SECURITY_CONTEXT SecurityContext; + + Irp = RxContext->CurrentIrp; + Stack = RxContext->CurrentIrpSp; + FileObject = Stack->FileObject; + SecurityContext = Stack->Parameters.Create.SecurityContext; + + RxContext->Create.NtCreateParameters.SecurityContext = SecurityContext; + if (SecurityContext->AccessState != NULL && SecurityContext->AccessState->SecurityDescriptor != NULL) + { + RxContext->Create.SdLength = RtlLengthSecurityDescriptor(SecurityContext->AccessState->SecurityDescriptor); + DPRINT("SD Ctxt: %p, Length: %lx\n", RxContext->Create.NtCreateParameters.SecurityContext, + RxContext->Create.SdLength); + } + if (SecurityContext->SecurityQos != NULL) + { + RxContext->Create.NtCreateParameters.ImpersonationLevel = SecurityContext->SecurityQos->ImpersonationLevel; + } + else + { + RxContext->Create.NtCreateParameters.ImpersonationLevel = SecurityImpersonation; + } + RxContext->Create.NtCreateParameters.DesiredAccess = SecurityContext->DesiredAccess; + + RxContext->Create.NtCreateParameters.AllocationSize.QuadPart = Irp->Overlay.AllocationSize.QuadPart; + RxContext->Create.NtCreateParameters.FileAttributes = Stack->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS; + RxContext->Create.NtCreateParameters.ShareAccess = Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS; + RxContext->Create.NtCreateParameters.Disposition = (Stack->Parameters.Create.Options >> 24) & 0x000000FF; + RxContext->Create.NtCreateParameters.CreateOptions = Stack->Parameters.Create.Options & 0xFFFFFF; + + DfsContext = FileObject->FsContext2; + DfsNameContext = FileObject->FsContext; + RxContext->Create.NtCreateParameters.DfsContext = DfsContext; + RxContext->Create.NtCreateParameters.DfsNameContext = DfsNameContext; + ASSERT(DfsContext == NULL || DfsContext == UIntToPtr(DFS_OPEN_CONTEXT) || + DfsContext == UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT) || + DfsContext == UIntToPtr(DFS_CSCAGENT_NAME_CONTEXT) || + DfsContext == UIntToPtr(DFS_USER_NAME_CONTEXT)); + ASSERT(DfsNameContext == NULL || DfsNameContext->NameContextType == DFS_OPEN_CONTEXT || + DfsNameContext->NameContextType == DFS_DOWNLEVEL_OPEN_CONTEXT || + DfsNameContext->NameContextType == DFS_CSCAGENT_NAME_CONTEXT || + DfsNameContext->NameContextType == DFS_USER_NAME_CONTEXT); + FileObject->FsContext2 = NULL; + FileObject->FsContext = NULL; + + RxContext->pFcb = NULL; + RxContext->Create.ReturnedCreateInformation = 0; + + /* if we stripped last \, it has to be a directory! */ + if (BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH)) + { + SetFlag(RxContext->Create.NtCreateParameters.CreateOptions, FILE_DIRECTORY_FILE); + } + + RxContext->Create.EaLength = Stack->Parameters.Create.EaLength; + if (RxContext->Create.EaLength == 0) + { + RxContext->Create.EaBuffer = NULL; + } + else + { + RxContext->Create.EaBuffer = Irp->AssociatedIrp.SystemBuffer; + DPRINT("EA Buffer: %p, Length: %lx\n", Irp->AssociatedIrp.SystemBuffer, RxContext->Create.EaLength); + } +} + +NTSTATUS +RxCreateFromNetRoot( + PRX_CONTEXT Context, + PUNICODE_STRING NetRootName) +{ + PFCB Fcb; + NTSTATUS Status; + PNET_ROOT NetRoot; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + ACCESS_MASK DesiredAccess; + USHORT DesiredShareAccess; + + PAGED_CODE(); + + /* Validate that the context is consistent */ + if (Context->Create.pNetRoot == NULL) + { + return STATUS_BAD_NETWORK_PATH; + } + + NetRoot = (PNET_ROOT)Context->Create.pNetRoot; + if (Context->RxDeviceObject != NetRoot->pSrvCall->RxDeviceObject) + { + return STATUS_BAD_NETWORK_PATH; + } + + if (Context->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_OPEN_CONTEXT) && + !BooleanFlagOn(NetRoot->pSrvCall->Flags, SRVCALL_FLAG_DFS_AWARE_SERVER)) + { + return STATUS_DFS_UNAVAILABLE; + } + + if (Context->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT) && + BooleanFlagOn(NetRoot->Flags, NETROOT_FLAG_DFS_AWARE_NETROOT)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + Stack = Context->CurrentIrpSp; + DesiredShareAccess = Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS; + if (NetRoot->Type == NET_ROOT_PRINT) + { + DesiredShareAccess = FILE_SHARE_VALID_FLAGS; + } + + DesiredAccess = Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_ALL_ACCESS; + + /* We don't support renaming yet */ + if (BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY)) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; @@ -5115,6 +6250,25 @@ RxGetTopDeviceObjectIfRdbssIrp( return TopDevice; } +/* + * @implemented + */ +PIRP +RxGetTopIrpIfRdbssIrp( + VOID) +{ + PIRP Irp = NULL; + PRX_TOPLEVELIRP_CONTEXT TopLevel; + + TopLevel = (PRX_TOPLEVELIRP_CONTEXT)IoGetTopLevelIrp(); + if (RxIsThisAnRdbssTopLevelContext(TopLevel)) + { + Irp = TopLevel->Irp; + } + + return Irp; +} + /* * @implemented */ @@ -5645,6 +6799,176 @@ RxLowIoReadShellCompletion( return Status; } +/* + * @implemented + */ +NTSTATUS +RxLowIoWriteShell( + IN PRX_CONTEXT RxContext) +{ + PFCB Fcb; + NTSTATUS Status; + + PAGED_CODE(); + + DPRINT("RxLowIoWriteShell(%p)\n", RxContext); + + Fcb = (PFCB)RxContext->pFcb; + + ASSERT(!BooleanFlagOn(Fcb->FcbState, FCB_STATE_FILE_IS_BUF_COMPRESSED) && + !BooleanFlagOn(Fcb->FcbState, FCB_STATE_FILE_IS_DISK_COMPRESSED)); + + /* Always update stats for disks */ + if (Fcb->CachedNetRootType == NET_ROOT_DISK) + { + ExInterlockedAddLargeStatistic(&RxContext->RxDeviceObject->NetworkWriteBytesRequested, RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount); + } + + /* And forward the write to the mini-rdr */ + Status = RxLowIoSubmit(RxContext, RxLowIoWriteShellCompletion); + DPRINT("RxLowIoWriteShell(%p), Status: %lx\n", RxContext, Status); + + return Status; +} + +NTSTATUS +NTAPI +RxLowIoWriteShellCompletion( + PRX_CONTEXT RxContext) +{ + PIRP Irp; + PFCB Fcb; + NTSTATUS Status; + BOOLEAN PagingIo; + PLOWIO_CONTEXT LowIoContext; + + PAGED_CODE(); + + DPRINT("RxLowIoWriteShellCompletion(%p)\n", RxContext); + + Status = RxContext->IoStatusBlock.Status; + DPRINT("In %p, Status: %lx, Information: %lx\n", RxContext, Status, RxContext->IoStatusBlock.Information); + + Irp = RxContext->CurrentIrp; + + /* Set IRP information from the RX_CONTEXT status block */ + Irp->IoStatus.Information = RxContext->IoStatusBlock.Information; + + LowIoContext = &RxContext->LowIoContext; + ASSERT(RxLowIoIsBufferLocked(LowIoContext)); + + /* Perform a few sanity checks */ + Fcb = (PFCB)RxContext->pFcb; + if (Status == STATUS_SUCCESS) + { + if (BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_THIS_IO_BUFFERED)) + { + ASSERT(!BooleanFlagOn(Fcb->FcbState, FCB_STATE_FILE_IS_BUF_COMPRESSED) && + !BooleanFlagOn(Fcb->FcbState, FCB_STATE_FILE_IS_DISK_COMPRESSED)); + } + + ASSERT(!BooleanFlagOn(Fcb->FcbState, FCB_STATE_FILE_IS_SHADOWED)); + } + + PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + if (Status != STATUS_SUCCESS && PagingIo) + { + DPRINT1("Paging IO failed %p (%p) %lx\n", Fcb, Fcb->NetRoot, Status); + } + + /* In case of async call, perform last bits not done in RxCommonWrite */ + if (!BooleanFlagOn(LowIoContext->Flags, LOWIO_CONTEXT_FLAG_SYNCCALL)) + { + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + + /* We only succeed if we wrote what was asked for */ + if (NT_SUCCESS(Status) && !BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_OPERATION)) + { + ASSERT(Irp->IoStatus.Information == LowIoContext->ParamsFor.ReadWrite.ByteCount); + } + + /* If write succeed, ,also update FILE_OBJECT flags */ + Stack = RxContext->CurrentIrpSp; + FileObject = Stack->FileObject; + if (!PagingIo) + { + SetFlag(FileObject->Flags, FO_FILE_MODIFIED); + } + + if (BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_EXTENDING_FILESIZE)) + { + SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); + } + + /* If VDL was extended, fix attributes */ + if (BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_EXTENDING_VDL)) + { + LONGLONG LastOffset, FileSize; + + LastOffset = LowIoContext->ParamsFor.ReadWrite.ByteOffset + + Irp->IoStatus.Information; + RxGetFileSizeWithLock(Fcb, &FileSize); + + if (FileSize < LastOffset) + { + LastOffset = FileSize; + } + + Fcb->Header.ValidDataLength.QuadPart = LastOffset; + } + + /* One less outstanding write */ + if (!BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION)) + { + PNON_PAGED_FCB NonPagedFcb; + + NonPagedFcb = LowIoContext->ParamsFor.ReadWrite.NonPagedFcb; + if (NonPagedFcb != NULL) + { + if (ExInterlockedAddUlong(&NonPagedFcb->OutstandingAsyncWrites, + -1, &RxStrucSupSpinLock) == 1) + { + KeSetEvent(NonPagedFcb->OutstandingAsyncEvent, IO_NO_INCREMENT, FALSE); + } + } + } + + /* Release paging resource if acquired */ + if (RxContext->FcbPagingIoResourceAcquired) + { + RxReleasePagingIoResourceForThread(RxContext, Fcb, LowIoContext->ResourceThreadId); + } + + /* Resume blocked operations for pipes */ + if (BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION)) + { + RxResumeBlockedOperations_Serially(RxContext, + &((PFOBX)RxContext->pFobx)->Specific.NamedPipe.WriteSerializationQueue); + } + else + { + /* And release FCB only for files */ + if (RxContext->FcbResourceAcquired) + { + RxReleaseFcbForThread(RxContext, Fcb, LowIoContext->ResourceThreadId); + } + } + + /* Final sanity checks */ + ASSERT(Status != STATUS_RETRY); + ASSERT((Status != STATUS_SUCCESS) || (Irp->IoStatus.Information <= Stack->Parameters.Write.Length)); + ASSERT(RxContext->MajorFunction == IRP_MJ_WRITE); + + if (BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_OPERATION)) + { + UNIMPLEMENTED; + } + } + + return Status; +} + /* * @implemented */ diff --git a/reactos/sdk/lib/drivers/rxce/rxce.c b/reactos/sdk/lib/drivers/rxce/rxce.c index d3bd22a98eb..c5686a5be6d 100644 --- a/reactos/sdk/lib/drivers/rxce/rxce.c +++ b/reactos/sdk/lib/drivers/rxce/rxce.c @@ -121,7 +121,14 @@ BOOLEAN RxStopOnLoudCompletion = TRUE; BOOLEAN RxSrvCallConstructionDispatcherActive = FALSE; LIST_ENTRY RxSrvCalldownList; RX_SPIN_LOCK RxStrucSupSpinLock; -ULONG RdbssReferenceTracingValue; +#if 0 +ULONG RdbssReferenceTracingValue = (RDBSS_REF_TRACK_SRVCALL | RDBSS_REF_TRACK_NETROOT | + RDBSS_REF_TRACK_VNETROOT | RDBSS_REF_TRACK_NETFOBX | + RDBSS_REF_TRACK_NETFCB | RDBSS_REF_TRACK_SRVOPEN | + RX_PRINT_REF_TRACKING); +#else +ULONG RdbssReferenceTracingValue = 0; +#endif LARGE_INTEGER RxWorkQueueWaitInterval[RxMaximumWorkQueue]; LARGE_INTEGER RxSpinUpDispatcherWaitInterval; RX_DISPATCHER RxDispatcher; @@ -2906,7 +2913,7 @@ RxFinalizeNetRoot( HashBucket = &FcbTable->HashBuckets[Bucket]; ListEntry = HashBucket->Flink; while (ListEntry != HashBucket) - { + { PFCB Fcb; Fcb = CONTAINING_RECORD(ListEntry, FCB, FcbTableEntry.HashLinks); @@ -3866,7 +3873,7 @@ RxFinishFcbInitialization( { /* If our FCB newly points to a file, initiliaze everything related */ if (FileType == RDBSS_NTC_STORAGE_TYPE_FILE) - + { if (OldType != RDBSS_NTC_STORAGE_TYPE_FILE) { @@ -6302,7 +6309,7 @@ RxPrefixTableLookupName( { NODE_TYPE_CODE Type; - Type = NodeType(Container); + Type = (NodeType(Container) & ~RX_SCAVENGER_MASK); switch (Type) { case RDBSS_NTC_SRVCALL: @@ -6318,7 +6325,9 @@ RxPrefixTableLookupName( break; default: + DPRINT1("Invalid node type: %x\n", Type); ASSERT(FALSE); + RxReference(Container); break; } } @@ -6479,6 +6488,43 @@ RxProcessFcbChangeBufferingStateRequest( UNIMPLEMENTED; } +/* + * @implemented + */ +VOID +RxpScavengeFobxs( + PRDBSS_SCAVENGER Scavenger, + PLIST_ENTRY FobxToScavenge) +{ + /* Explore the whole list of FOBX to scavenge */ + while (!IsListEmpty(FobxToScavenge)) + { + PFCB Fcb; + PFOBX Fobx; + PLIST_ENTRY Entry; + + Entry = RemoveHeadList(FobxToScavenge); + Fobx = CONTAINING_RECORD(Entry, FOBX, ScavengerFinalizationList); + Fcb = (PFCB)Fobx->SrvOpen->pFcb; + + /* Try to acquire the lock exclusively to perform finalization */ + if (RxAcquireExclusiveFcb(NULL, Fcb) != STATUS_SUCCESS) + { + RxDereferenceNetRoot(Fobx, LHS_LockNotHeld); + } + else + { + RxReferenceNetFcb(Fcb); + RxDereferenceNetRoot(Fobx, LHS_ExclusiveLockHeld); + + if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE)) + { + RxReleaseFcb(NULL, Fcb); + } + } + } +} + BOOLEAN RxpTrackDereference( _In_ ULONG TraceType, @@ -6486,6 +6532,9 @@ RxpTrackDereference( _In_ ULONG Line, _In_ PVOID Instance) { + PCSTR InstanceType; + ULONG ReferenceCount; + PAGED_CODE(); if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType)) @@ -6493,7 +6542,53 @@ RxpTrackDereference( return TRUE; } - UNIMPLEMENTED; + switch (TraceType) + { + case RDBSS_REF_TRACK_SRVCALL: + InstanceType = "SrvCall"; + ReferenceCount = ((PSRV_CALL)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_NETROOT: + InstanceType = "NetRoot"; + ReferenceCount = ((PNET_ROOT)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_VNETROOT: + InstanceType = "VNetRoot"; + ReferenceCount = ((PV_NET_ROOT)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_NETFOBX: + InstanceType = "NetFobx"; + ReferenceCount = ((PFOBX)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_NETFCB: + InstanceType = "NetFcb"; + ReferenceCount = ((PFCB)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_SRVOPEN: + InstanceType = "SrvOpen"; + ReferenceCount = ((PSRV_OPEN)Instance)->NodeReferenceCount; + break; + + default: + DPRINT1("Invalid node type!\n"); + return TRUE; + } + + if (BooleanFlagOn(RdbssReferenceTracingValue, RX_LOG_REF_TRACKING)) + { + UNIMPLEMENTED; + } + + if (BooleanFlagOn(RdbssReferenceTracingValue, RX_PRINT_REF_TRACKING)) + { + DbgPrint("(%s:%d) %p (%s) dereferenced from %d\n", FileName, Line, Instance, InstanceType, ReferenceCount); + } + return TRUE; } @@ -6504,12 +6599,60 @@ RxpTrackReference( _In_ ULONG Line, _In_ PVOID Instance) { + PCSTR InstanceType; + ULONG ReferenceCount; + if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType)) { return; } - UNIMPLEMENTED; + switch (TraceType) + { + case RDBSS_REF_TRACK_SRVCALL: + InstanceType = "SrvCall"; + ReferenceCount = ((PSRV_CALL)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_NETROOT: + InstanceType = "NetRoot"; + ReferenceCount = ((PNET_ROOT)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_VNETROOT: + InstanceType = "VNetRoot"; + ReferenceCount = ((PV_NET_ROOT)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_NETFOBX: + InstanceType = "NetFobx"; + ReferenceCount = ((PFOBX)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_NETFCB: + InstanceType = "NetFcb"; + ReferenceCount = ((PFCB)Instance)->NodeReferenceCount; + break; + + case RDBSS_REF_TRACK_SRVOPEN: + InstanceType = "SrvOpen"; + ReferenceCount = ((PSRV_OPEN)Instance)->NodeReferenceCount; + break; + + default: + DPRINT1("Invalid node type!\n"); + return; + } + + if (BooleanFlagOn(RdbssReferenceTracingValue, RX_LOG_REF_TRACKING)) + { + UNIMPLEMENTED; + } + + if (BooleanFlagOn(RdbssReferenceTracingValue, RX_PRINT_REF_TRACKING)) + { + DbgPrint("(%s:%d) %p (%s) referenced from %d\n", FileName, Line, Instance, InstanceType, ReferenceCount); + } } /* @@ -6698,6 +6841,42 @@ RxPurgeFcbInSystemCache( return Status; } +/* + * @implemented + */ +BOOLEAN +RxPurgeFobx( + PFOBX pFobx) +{ + NTSTATUS Status; + PFCB FcbToBePurged; + + PAGED_CODE(); + + /* Get the associated FCB */ + FcbToBePurged = (PFCB)pFobx->pSrvOpen->pFcb; + Status = RxAcquireExclusiveFcb(NULL, FcbToBePurged); + ASSERT(Status == STATUS_SUCCESS); + + /* Purge it */ + Status = RxPurgeFcbInSystemCache(FcbToBePurged, NULL, 0, FALSE, TRUE); + if (Status != STATUS_SUCCESS) + { + DPRINT1("Purge failed for %p (%p)\n", FcbToBePurged, pFobx); + return FALSE; + } + + /* And flush */ + if (!MmFlushImageSection(&FcbToBePurged->NonPaged->SectionObjectPointers, MmFlushForWrite)) + { + DPRINT1("Image section flush failed for %p (%p)\n", FcbToBePurged, pFobx); + return FALSE; + } + + DPRINT("Purge OK for %p (%p)\n", FcbToBePurged, pFobx); + return TRUE; +} + /* * @implemented */ @@ -6744,6 +6923,155 @@ RxPurgeFobxFromCache( return Status; } +/* + * @implemented + */ +NTSTATUS +RxPurgeRelatedFobxs( + PNET_ROOT NetRoot, + PRX_CONTEXT RxContext, + BOOLEAN AttemptFinalization, + PFCB PurgingFcb) +{ + PLIST_ENTRY Entry; + ULONG SuccessfullPurge; + PRDBSS_SCAVENGER Scavenger; + PRDBSS_DEVICE_OBJECT RxDeviceObject; + PPURGE_SYNCHRONIZATION_CONTEXT PurgeSyncCtx; + + PAGED_CODE(); + + RxDeviceObject = RxContext->RxDeviceObject; + Scavenger = RxDeviceObject->pRdbssScavenger; + PurgeSyncCtx = &NetRoot->PurgeSyncronizationContext; + + RxAcquireScavengerMutex(); + + /* If there's already a purge in progress */ + if (PurgeSyncCtx->PurgeInProgress) + { + /* Add our RX_CONTEXT to the current run */ + InsertTailList(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion, + &RxContext->RxContextSerializationQLinks); + + /* And wait until it's done */ + RxReleaseScavengerMutex(); + RxWaitSync(RxContext); + RxAcquireScavengerMutex(); + } + + /* Start the purge */ + PurgeSyncCtx->PurgeInProgress = TRUE; + + /* While the purge is still handling our NET_ROOT, do nothing but wait */ + while (Scavenger->CurrentNetRootForClosePendingProcessing == NetRoot) + { + RxReleaseScavengerMutex(); + KeWaitForSingleObject(&Scavenger->ClosePendingProcessingSyncEvent, Executive, + KernelMode, TRUE, NULL); + RxAcquireScavengerMutex(); + } + + /* Now, for all the entries */ + SuccessfullPurge = 0; + Entry = Scavenger->ClosePendingFobxsList.Flink; + while (Entry != &Scavenger->ClosePendingFobxsList) + { + PFCB Fcb; + PFOBX Fobx; + BOOLEAN Success; + + Fobx = CONTAINING_RECORD(Entry, FOBX, ClosePendingList); + DPRINT("Dealing with FOBX: %p\n", Fobx); + + Entry = Entry->Flink; + + /* If it's not matching our NET_ROOT, ignore */ + if (Fobx->pSrvOpen == NULL || + Fobx->pSrvOpen->pFcb == NULL || + ((PFCB)Fobx->pSrvOpen->pFcb)->VNetRoot == NULL || + (PNET_ROOT)((PFCB)Fobx->pSrvOpen->pFcb)->VNetRoot->pNetRoot != NetRoot) + { + continue; + } + + /* Determine if it matches our FCB */ + Fcb = (PFCB)Fobx->pSrvOpen->pFcb; + if (PurgingFcb != NULL && NodeType(PurgingFcb) != RDBSS_NTC_STORAGE_TYPE_DIRECTORY && + PurgingFcb != Fcb) + { + NTSTATUS Status; + + MINIRDR_CALL_THROUGH(Status, RxDeviceObject->Dispatch, MRxAreFilesAliased, (Fcb, PurgingFcb)); + if (Status == STATUS_SUCCESS) + { + continue; + } + } + + /* Matching, we'll purge it */ + RemoveEntryList(&Fobx->ClosePendingList); + + /* Reference it so that it doesn't disappear */ + RxReferenceNetFobx(Fobx); + + RxReleaseScavengerMutex(); + + /* And purge */ + Success = RxPurgeFobx(Fobx); + if (Success) + { + ++SuccessfullPurge; + } + + /* If we don't have to finalize it (or if we cannot acquire lock exclusively + * Just normally dereference + */ + if ((AttemptFinalization == DONT_ATTEMPT_FINALIZE_ON_PURGE) || + RxAcquireExclusiveFcb(NULL, Fcb) != STATUS_SUCCESS) + { + RxDereferenceNetFobx(Fobx, LHS_LockNotHeld); + } + /* Otherwise, finalize */ + else + { + RxReferenceNetFcb(Fcb); + RxDereferenceNetFobx(Fobx, LHS_ExclusiveLockHeld); + if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE)) + { + RxReleaseFcb(NULL, Fcb); + } + } + + if (!Success) + { + DPRINT1("Failed purging %p (%p)\n", Fcb, Fobx); + } + + RxAcquireScavengerMutex(); + } + + /* If no contexts left, purge is not running */ + if (IsListEmpty(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion)) + { + PurgeSyncCtx->PurgeInProgress = FALSE; + } + /* Otherwise, notify a waiter it can start */ + else + { + PRX_CONTEXT Context; + + Entry = RemoveHeadList(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion); + Context = CONTAINING_RECORD(Entry, RX_CONTEXT, RxContextSerializationQLinks); + + RxSignalSynchronousWaiter(Context); + } + + RxReleaseScavengerMutex(); + + return (SuccessfullPurge > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); +} + /* * @implemented */ @@ -6954,6 +7282,38 @@ RxReference( RxReleaseScavengerMutex(); } +/* + * @implemented + */ +VOID +NTAPI +RxReinitializeContext( + IN OUT PRX_CONTEXT RxContext) +{ + PIRP Irp; + PRDBSS_DEVICE_OBJECT RxDeviceObject; + ULONG InitialContextFlags, SavedFlags; + + PAGED_CODE(); + + /* Backup a few flags */ + Irp = RxContext->CurrentIrp; + RxDeviceObject = RxContext->RxDeviceObject; + SavedFlags = RxContext->Flags & RX_CONTEXT_PRESERVED_FLAGS; + InitialContextFlags = RxContext->Flags & RX_CONTEXT_INITIALIZATION_FLAGS; + + /* Reset our context */ + RxPrepareContextForReuse(RxContext); + + /* Zero everything */ + RtlZeroMemory(&RxContext->MajorFunction, sizeof(RX_CONTEXT) - FIELD_OFFSET(RX_CONTEXT, MajorFunction)); + + /* Restore saved flags */ + RxContext->Flags = SavedFlags; + /* And reinit the context */ + RxInitializeContext(Irp, RxDeviceObject, InitialContextFlags, RxContext); +} + /* * @implemented */ @@ -7106,6 +7466,110 @@ RxResumeBlockedOperations_Serially( RxReleaseSerializationMutex(); } +/* + * @implemented + */ +VOID +RxSetFileSizeWithLock( + IN OUT PFCB Fcb, + IN PLONGLONG FileSize) +{ + PAGED_CODE(); + + /* Set attribute and increase version */ + Fcb->Header.FileSize.QuadPart = *FileSize; + ++Fcb->ulFileSizeVersion; +} + +/* + * @implemented + */ +VOID +RxScavengeFobxsForNetRoot( + PNET_ROOT NetRoot, + PFCB PurgingFcb, + BOOLEAN SynchronizeWithScavenger) +{ + PRDBSS_SCAVENGER Scavenger; + PRDBSS_DEVICE_OBJECT RxDeviceObject; + + PAGED_CODE(); + + RxDeviceObject = NetRoot->pSrvCall->RxDeviceObject; + Scavenger = RxDeviceObject->pRdbssScavenger; + + /* Wait for the scavenger, if asked to */ + if (SynchronizeWithScavenger) + { + KeWaitForSingleObject(&Scavenger->SyncEvent, Executive, KernelMode, FALSE, NULL); + } + + RxAcquireScavengerMutex(); + + /* If there's nothing left to do... */ + if (Scavenger->FobxsToBeFinalized <= 0) + { + RxReleaseScavengerMutex(); + } + else + { + PLIST_ENTRY Entry; + LIST_ENTRY FobxToScavenge; + + InitializeListHead(&FobxToScavenge); + + /* Browse all the FOBXs to finalize */ + Entry = Scavenger->FobxFinalizationList.Flink; + while (Entry != &Scavenger->FobxFinalizationList) + { + PFOBX Fobx; + + Fobx = CONTAINING_RECORD(Entry, FOBX, ScavengerFinalizationList); + Entry = Entry->Flink; + + if (Fobx->SrvOpen != NULL) + { + PFCB Fcb; + + Fcb = (PFCB)Fobx->SrvOpen->pFcb; + + /* If it matches our NET_ROOT */ + if ((PNET_ROOT)Fcb->pNetRoot == NetRoot) + { + NTSTATUS Status; + + /* Check whether it matches our FCB */ + Status = STATUS_MORE_PROCESSING_REQUIRED; + if (PurgingFcb != NULL && PurgingFcb != Fcb) + { + MINIRDR_CALL_THROUGH(Status, RxDeviceObject->Dispatch, MRxAreFilesAliased, (Fcb, PurgingFcb)); + } + + /* If so, add it to the list of the FOBXs to scavenge */ + if (Status != STATUS_SUCCESS) + { + RxReferenceNetFobx(Fobx); + ASSERT(NodeType(Fobx) == RDBSS_NTC_FOBX); + + RemoveEntryList(&Fobx->ScavengerFinalizationList); + InsertTailList(&FobxToScavenge, &Fobx->ScavengerFinalizationList); + } + } + } + } + + RxReleaseScavengerMutex(); + + /* Now, scavenge all the extracted FOBX */ + RxpScavengeFobxs(Scavenger, &FobxToScavenge); + } + + if (SynchronizeWithScavenger) + { + KeSetEvent(&Scavenger->SyncEvent, IO_NO_INCREMENT, FALSE); + } +} + /* * @implemented */