923 lines
30 KiB
C
923 lines
30 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Cleanup.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the File Cleanup routine for Rx called by the
|
|||
|
dispatch driver.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Joe Linn [JoeLinn] 12-sep-94
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
//
|
|||
|
// The Bug check file id for this module
|
|||
|
//
|
|||
|
|
|||
|
#define BugCheckFileId (RDBSS_BUG_CHECK_CLEANUP)
|
|||
|
|
|||
|
//
|
|||
|
// The local debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (DEBUG_TRACE_CLEANUP)
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
RxUninitializeCacheMap(
|
|||
|
IN OUT PRX_CONTEXT RxContext,
|
|||
|
IN PFILE_OBJECT FileObject,
|
|||
|
IN PLARGE_INTEGER TruncateSize
|
|||
|
);
|
|||
|
|
|||
|
#if DBG
|
|||
|
//this is just a dbg thing
|
|||
|
extern
|
|||
|
BOOLEAN
|
|||
|
RxLockEnumerator (
|
|||
|
IN OUT struct _MRX_SRV_OPEN_ * SrvOpen,
|
|||
|
IN OUT PVOID *ContinuationHandle,
|
|||
|
OUT PLARGE_INTEGER FileOffset,
|
|||
|
OUT PLARGE_INTEGER LockRange,
|
|||
|
OUT PBOOLEAN IsLockExclusive
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
RxFakeLockEnumerator (
|
|||
|
IN OUT struct _SRV_OPEN * SrvOpen,
|
|||
|
IN OUT PVOID *ContinuationHandle,
|
|||
|
OUT PLARGE_INTEGER FileOffset,
|
|||
|
OUT PLARGE_INTEGER LockRange,
|
|||
|
OUT PBOOLEAN IsLockExclusive
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
RxDumpSerializationQueue(
|
|||
|
PLIST_ENTRY SQ,
|
|||
|
PSZ TagText1,
|
|||
|
PSZ TagText2
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, RxDumpSerializationQueue)
|
|||
|
#endif
|
|||
|
|
|||
|
#endif //if DBG
|
|||
|
|
|||
|
VOID
|
|||
|
RxCleanupPipeQueues (
|
|||
|
IN PRX_CONTEXT RxContext
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, RxCommonCleanup)
|
|||
|
#pragma alloc_text(PAGE, RxAdjustFileTimesAndSize)
|
|||
|
#pragma alloc_text(PAGE, RxCleanupPipeQueues)
|
|||
|
#pragma alloc_text(PAGE, RxUninitializeCacheMap)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RxCommonCleanup ( RXCOMMON_SIGNATURE )
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the common routine for cleanup of a file/directory called by
|
|||
|
both the fsd and fsp threads.
|
|||
|
|
|||
|
Cleanup is invoked whenever the last handle to a file object is
|
|||
|
closed. This is different than the Close operation which is invoked
|
|||
|
when the last reference to a file object is deleted.
|
|||
|
|
|||
|
The function of cleanup is to essentially "cleanup" the
|
|||
|
file/directory after a user is done with it. The Fcb/Dcb remains
|
|||
|
around (because MM still has the file object referenced) but is now
|
|||
|
available for another user to open (i.e., as far as the user is
|
|||
|
concerned the file object is now closed). See close for a more complete
|
|||
|
description of what close does.
|
|||
|
|
|||
|
Please see the discussion in openclos.txt.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the Irp to process
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
RXSTATUS - The return status for the operation
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
RxCaptureRequestPacket;
|
|||
|
RxCaptureFcb;
|
|||
|
RxCaptureFobx;
|
|||
|
RxCaptureParamBlock;
|
|||
|
RxCaptureFileObject;
|
|||
|
|
|||
|
NODE_TYPE_CODE TypeOfOpen = NodeType(capFcb);
|
|||
|
NET_ROOT_TYPE NetRootType;
|
|||
|
PNET_ROOT NetRoot;
|
|||
|
|
|||
|
PSHARE_ACCESS ShareAccess = NULL;
|
|||
|
|
|||
|
PLARGE_INTEGER TruncateSize = NULL;
|
|||
|
LARGE_INTEGER LocalTruncateSize;
|
|||
|
|
|||
|
BOOLEAN UninitializeCacheMap = FALSE;
|
|||
|
BOOLEAN LastUncleanOnGoodFcb = FALSE;
|
|||
|
BOOLEAN NeedPurge = FALSE;
|
|||
|
BOOLEAN NeedDelete = FALSE;
|
|||
|
|
|||
|
BOOLEAN AcquiredFcb = FALSE;
|
|||
|
BOOLEAN AcquiredTableLock = FALSE;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
RxDbgTrace(+1, Dbg, ("RxCommonCleanup IrpC/Fobx/Fcb/FileObj = %08lx %08lx %08lx %08lx\n",
|
|||
|
RxContext,capFobx,capFcb,capFileObject));
|
|||
|
RxLog(("CommonCleanup %lx %lx %lx\n",RxContext,capFobx,capFcb));
|
|||
|
|
|||
|
// If this cleanup is for the case of directories opened for renames etc.,
|
|||
|
// where there is no file object cleanup succeeds immediately.
|
|||
|
if (!capFobx) {
|
|||
|
if (capFcb->UncleanCount > 0) {
|
|||
|
InterlockedDecrement(&capFcb->UncleanCount);
|
|||
|
}
|
|||
|
//RxMarkFcbForScavengingAtCleanup(capFcb);
|
|||
|
RxDbgTrace(-1, Dbg, ("Cleanup nullfobx open\n", 0));
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
// Cleanup applies to certain types of opens. If it is not one of those
|
|||
|
// abort immediately.
|
|||
|
if ((TypeOfOpen != RDBSS_NTC_STORAGE_TYPE_FILE) &&
|
|||
|
(TypeOfOpen != RDBSS_NTC_STORAGE_TYPE_DIRECTORY) &&
|
|||
|
(TypeOfOpen != RDBSS_NTC_STORAGE_TYPE_UNKNOWN) &&
|
|||
|
(TypeOfOpen != RDBSS_NTC_SPOOLFILE)) {
|
|||
|
RxLog(("RxCC Invalid Open %lx %lx %lx\n",RxContext,capFobx,capFcb));
|
|||
|
RxBugCheck( TypeOfOpen, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
// Ensure that the object has not been cleaned up before. This should
|
|||
|
// never occur.
|
|||
|
ASSERT( !FlagOn( capFileObject->Flags, FO_CLEANUP_COMPLETE ));
|
|||
|
|
|||
|
RxMarkFobxOnCleanup(capFobx,&NeedPurge);
|
|||
|
|
|||
|
// Acquire the FCB. In most cases no further resource acquisition is required
|
|||
|
// to complete the cleanup operation. The only exceptions are when the file
|
|||
|
// was initially opened with the DELETE_ON_CLOSE option. In such cases the
|
|||
|
// FCB table lock of the associated NET_ROOT instance is required.
|
|||
|
|
|||
|
Status = RxAcquireExclusiveFcb( RxContext, capFcb );
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
RxDbgTrace(-1, Dbg, ("RxCommonCleanup Failed to acquire FCB -> %lx\n)", Status));
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
AcquiredFcb = TRUE;
|
|||
|
|
|||
|
|
|||
|
if (FlagOn(capFcb->FcbState,FCB_STATE_ORPHANED)) {
|
|||
|
ASSERT( capFcb->UncleanCount );
|
|||
|
InterlockedDecrement(&capFcb->UncleanCount);
|
|||
|
if (FlagOn(capFileObject->Flags,FO_NO_INTERMEDIATE_BUFFERING)) {
|
|||
|
capFcb->UncachedUncleanCount--;
|
|||
|
}
|
|||
|
|
|||
|
MINIRDR_CALL(Status,RxContext,capFcb->MRxDispatch,MRxCleanupFobx,(RxContext));
|
|||
|
ASSERT( capFobx->SrvOpen->UncleanFobxCount );
|
|||
|
|
|||
|
capFobx->SrvOpen->UncleanFobxCount--;
|
|||
|
RxReleaseFcb(RxContext,capFcb);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NetRootType = capFcb->VNetRoot->NetRoot->Type ;
|
|||
|
NetRoot = (PNET_ROOT)capFcb->VNetRoot->NetRoot;
|
|||
|
|
|||
|
if ( FlagOn(capFobx->Flags, FOBX_FLAG_DELETE_ON_CLOSE) ) {
|
|||
|
SetFlag( capFcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
|
|||
|
}
|
|||
|
|
|||
|
ShareAccess = &capFcb->ShareAccess;
|
|||
|
LastUncleanOnGoodFcb = (capFcb->UncleanCount == 1);
|
|||
|
|
|||
|
if (LastUncleanOnGoodFcb && FlagOn(capFcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) {
|
|||
|
// if we can't get it right way, drop the Fcb and acquire/acquire
|
|||
|
// to perserve lock order. No one else can change the counts while we have
|
|||
|
// the fcb lock; neither can a file become DELETE_ON_CLOSE or be opened via
|
|||
|
// CommonCreate. If we are not deleting, get rid of the tablelock after we
|
|||
|
// verify the count.
|
|||
|
|
|||
|
if ( RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, FALSE) ) {
|
|||
|
// this is the fast way....hope it works
|
|||
|
AcquiredTableLock = TRUE;
|
|||
|
} else {
|
|||
|
// Release the FCB and reqcquire the locks in the correct order.
|
|||
|
// PrefixTableLock followed by the FCB.
|
|||
|
RxReleaseFcb( RxContext, capFcb );
|
|||
|
AcquiredFcb = FALSE;
|
|||
|
|
|||
|
(VOID)RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, TRUE);
|
|||
|
AcquiredTableLock = TRUE;
|
|||
|
|
|||
|
Status = RxAcquireExclusiveFcb( RxContext, capFcb );
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
RxDbgTrace(-1, Dbg, ("RxCommonCleanup Failed to acquire FCB -> %lx\n)", Status));
|
|||
|
return Status;
|
|||
|
}
|
|||
|
AcquiredFcb = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (capFcb->UncleanCount != 1) {
|
|||
|
RxReleaseFcbTableLock(&NetRoot->FcbTable);
|
|||
|
AcquiredTableLock = FALSE;
|
|||
|
NeedDelete = FALSE;
|
|||
|
} else {
|
|||
|
NeedDelete = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
switch (NetRootType) {
|
|||
|
case NET_ROOT_PIPE:
|
|||
|
case NET_ROOT_PRINT:
|
|||
|
{
|
|||
|
// If the file object corresponds to a pipe or spool file additional
|
|||
|
// cleanup operations are required. This deals with the special
|
|||
|
// serialization mechanism for pipes.
|
|||
|
|
|||
|
RxCleanupPipeQueues(RxContext);
|
|||
|
}
|
|||
|
break;
|
|||
|
case NET_ROOT_DISK:
|
|||
|
{
|
|||
|
switch (TypeOfOpen) {
|
|||
|
case RDBSS_NTC_STORAGE_TYPE_FILE :
|
|||
|
{
|
|||
|
// If the file object corresponds to a disk file, assert the locks
|
|||
|
// and update the associated file times and sizes.
|
|||
|
|
|||
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|||
|
|
|||
|
LowIoContext->Flags |= LOWIO_CONTEXT_FLAG_SAVEUNLOCKS;
|
|||
|
|
|||
|
FsRtlFastUnlockAll(
|
|||
|
&capFcb->Specific.Fcb.FileLock,
|
|||
|
capFileObject,
|
|||
|
IoGetRequestorProcess( capReqPacket ),
|
|||
|
RxContext );
|
|||
|
|
|||
|
if (LowIoContext->ParamsFor.Locks.LockList != NULL) {
|
|||
|
RxDbgTrace(0, Dbg, ("--->before init, locklist=%08lx\n", LowIoContext->ParamsFor.Locks.LockList));
|
|||
|
RxInitializeLowIoContext(LowIoContext,LOWIO_OP_UNLOCK_MULTIPLE);
|
|||
|
LowIoContext->ParamsFor.Locks.Flags = 0; //no flags
|
|||
|
Status = RxLowIoLockControlShell(RxContext);
|
|||
|
}
|
|||
|
|
|||
|
RxAdjustFileTimesAndSize(RXCOMMON_ARGUMENTS);
|
|||
|
|
|||
|
// If the file object corresponds to a disk file/directory and this
|
|||
|
// is the last cleanup call for the FCB additional processing is required.
|
|||
|
if (LastUncleanOnGoodFcb) {
|
|||
|
try {
|
|||
|
// If the file object was marked DELETE_ON_CLOSE set the file size to
|
|||
|
// zero ( synchronizing with the paging resource)
|
|||
|
if (NeedDelete) {
|
|||
|
RxAcquirePagingIoResource( capFcb );
|
|||
|
|
|||
|
capFcb->Header.FileSize.QuadPart = 0;
|
|||
|
|
|||
|
if (TypeOfOpen == RDBSS_NTC_STORAGE_TYPE_FILE) {
|
|||
|
capFcb->Header.ValidDataLength.QuadPart = 0;
|
|||
|
}
|
|||
|
|
|||
|
RxReleasePagingIoResource( capFcb );
|
|||
|
} else {
|
|||
|
// If the file object was not marked for deletion and it is not
|
|||
|
// a paging file ensure that the portion between the valid data
|
|||
|
// length and the file size is zero extended.
|
|||
|
if (!FlagOn(capFcb->FcbState, FCB_STATE_PAGING_FILE) &&
|
|||
|
(capFcb->Header.ValidDataLength.QuadPart <
|
|||
|
capFcb->Header.FileSize.QuadPart)) {
|
|||
|
RxDbgTrace(0, Dbg, ("---------->zeroextend!!!!!!!\n", 0));
|
|||
|
MINIRDR_CALL(Status,RxContext,capFcb->MRxDispatch,MRxZeroExtend,(RxContext));
|
|||
|
|
|||
|
capFcb->Header.ValidDataLength.QuadPart =
|
|||
|
capFcb->Header.FileSize.QuadPart;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// If the file object was marked for truncation capture the
|
|||
|
// sizes for uninitializing the cache maps subsequently.
|
|||
|
if (FlagOn(capFcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE)) {
|
|||
|
|
|||
|
RxDbgTrace(0, Dbg, ("truncate file allocation\n", 0));
|
|||
|
|
|||
|
MINIRDR_CALL(Status,RxContext,capFcb->MRxDispatch,MRxTruncate,(RxContext));
|
|||
|
|
|||
|
// Setup to truncate the Cache Map because
|
|||
|
// this is the only way we have of trashing the
|
|||
|
// truncated pages.
|
|||
|
|
|||
|
LocalTruncateSize = capFcb->Header.FileSize;
|
|||
|
TruncateSize = &LocalTruncateSize;
|
|||
|
|
|||
|
// Mark the Fcb as having now been truncated, just
|
|||
|
// in case we have to reprocess this later.
|
|||
|
|
|||
|
capFcb->FcbState &= ~FCB_STATE_TRUNCATE_ON_CLOSE;
|
|||
|
}
|
|||
|
|
|||
|
} except ( CATCH_EXPECTED_EXCEPTIONS ) {
|
|||
|
|
|||
|
DbgPrint("!!! Handling Exceptions\n");
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Purging can be done now if this FCB does not support collapsed opens
|
|||
|
if (!NeedPurge) {
|
|||
|
NeedPurge = (LastUncleanOnGoodFcb &&
|
|||
|
(NeedDelete ||
|
|||
|
!FlagOn(capFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED)));
|
|||
|
}
|
|||
|
|
|||
|
UninitializeCacheMap = TRUE;
|
|||
|
}
|
|||
|
break;
|
|||
|
case RDBSS_NTC_STORAGE_TYPE_DIRECTORY :
|
|||
|
case RDBSS_NTC_STORAGE_TYPE_UNKNOWN :
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// We've just finished everything associated with an unclean
|
|||
|
// fcb so now decrement the unclean count before releasing
|
|||
|
// the resource.
|
|||
|
|
|||
|
ASSERT( capFcb->UncleanCount );
|
|||
|
InterlockedDecrement(&capFcb->UncleanCount);
|
|||
|
|
|||
|
if (FlagOn(capFileObject->Flags,FO_NO_INTERMEDIATE_BUFFERING)) {
|
|||
|
capFcb->UncachedUncleanCount--;
|
|||
|
}
|
|||
|
|
|||
|
MINIRDR_CALL(Status,RxContext,capFcb->MRxDispatch,MRxCleanupFobx,(RxContext));
|
|||
|
|
|||
|
ASSERT( capFobx->SrvOpen->UncleanFobxCount );
|
|||
|
capFobx->SrvOpen->UncleanFobxCount--;
|
|||
|
|
|||
|
// If this was the last cached open, and there are open
|
|||
|
// non-cached handles, attempt a flush and purge operation
|
|||
|
// to avoid cache coherency overhead from these non-cached
|
|||
|
// handles later. We ignore any I/O errors from the flush.
|
|||
|
|
|||
|
if (capFcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) {
|
|||
|
RxFlushFile( RxContext, capFcb );
|
|||
|
}
|
|||
|
|
|||
|
if (!FlagOn( capFileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) &&
|
|||
|
(capFcb->UncachedUncleanCount != 0) &&
|
|||
|
(capFcb->UncachedUncleanCount == capFcb->UncleanCount) &&
|
|||
|
(capFcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)) {
|
|||
|
|
|||
|
RxFlushFile( RxContext, capFcb );
|
|||
|
CcPurgeCacheSection(
|
|||
|
&capFcb->NonPaged->SectionObjectPointers,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
FALSE );
|
|||
|
}
|
|||
|
|
|||
|
// do we need a flush?
|
|||
|
if (!NeedDelete && NeedPurge) {
|
|||
|
RxDbgTrace(0, Dbg, ("CleanupPurge:CCFlush\n", 0));
|
|||
|
RxFlushFile( RxContext, capFcb );
|
|||
|
}
|
|||
|
|
|||
|
// cleanup the cache map to get rid of pages that are no longer part
|
|||
|
// of the file. amazingly, this works even if we didn't init the Cachemap!!!!!
|
|||
|
|
|||
|
if (UninitializeCacheMap) {
|
|||
|
RxUninitializeCacheMap( RxContext, capFileObject, TruncateSize );
|
|||
|
}
|
|||
|
|
|||
|
// finish up a delete...we have to purge because MM is holding the file open....
|
|||
|
// just for the record, NeedPurge is set for files and clear for directories......
|
|||
|
|
|||
|
if (NeedDelete || NeedPurge) {
|
|||
|
|
|||
|
RxDbgTrace(0, Dbg, ("CleanupPurge:MmFlushImage\n", 0));
|
|||
|
MmFlushImageSection(&capFcb->NonPaged->SectionObjectPointers,
|
|||
|
MmFlushForWrite);
|
|||
|
|
|||
|
ASSERT((AcquiredFcb));
|
|||
|
RxReleaseFcb( RxContext, capFcb );
|
|||
|
AcquiredFcb = FALSE;
|
|||
|
|
|||
|
MmForceSectionClosed(&capFcb->NonPaged->SectionObjectPointers, TRUE);
|
|||
|
|
|||
|
RxAcquireExclusiveFcb(RxContext,capFcb);
|
|||
|
AcquiredFcb = TRUE;
|
|||
|
|
|||
|
RxDbgTrace(0, Dbg, ("CleanupPurge:PurgingFinished\n", 0));
|
|||
|
|
|||
|
if (NeedDelete) {
|
|||
|
RxRemoveNameNetFcb( capFcb );
|
|||
|
RxReleaseFcbTableLock(&NetRoot->FcbTable);
|
|||
|
AcquiredTableLock = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// The Close Call and the Cleanup Call may be far apart. The share access
|
|||
|
// must be cleaned up if the file was mapped through this File Object.
|
|||
|
if ((ShareAccess != NULL) &&
|
|||
|
(NetRootType == NET_ROOT_DISK)) {
|
|||
|
ASSERT (NetRootType == NET_ROOT_DISK);
|
|||
|
RxRemoveShareAccess( capFileObject, ShareAccess, "Cleanup the Share access", "ClnUpShr" );
|
|||
|
}
|
|||
|
|
|||
|
if (TypeOfOpen == RDBSS_NTC_STORAGE_TYPE_FILE) {
|
|||
|
// Coordinate the cleanup operation with the oplock state.
|
|||
|
// Cleanup operations can always cleanup immediately.
|
|||
|
|
|||
|
FsRtlCheckOplock( &capFcb->Specific.Fcb.Oplock, capReqPacket,
|
|||
|
RxContext, NULL, NULL );
|
|||
|
|
|||
|
//capFcb->Header.IsFastIoPossible = RxIsFastIoPossible( capFcb );
|
|||
|
}
|
|||
|
|
|||
|
if (AcquiredFcb) {
|
|||
|
RxReleaseFcb( RxContext, capFcb );
|
|||
|
AcquiredFcb = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// A local filesystem would do this..........
|
|||
|
// If the NET_ROOT is on a removeable media, flush the volume. We do
|
|||
|
// this in lieu of write through for removeable media for
|
|||
|
// performance considerations. That is, data is guaranteed
|
|||
|
// to be out when NtCloseFile returns.
|
|||
|
// The file needs to be flushed
|
|||
|
|
|||
|
// The cleanup for this file object has been successfully completed at
|
|||
|
// this point.
|
|||
|
|
|||
|
SetFlag( capFileObject->Flags, FO_CLEANUP_COMPLETE );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
DebugUnwind( RxCommonCleanup );
|
|||
|
|
|||
|
if (AcquiredFcb) {
|
|||
|
RxReleaseFcb( RxContext, capFcb );
|
|||
|
}
|
|||
|
|
|||
|
if (AcquiredTableLock) {
|
|||
|
RxReleaseFcbTableLock(&NetRoot->FcbTable);
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG {
|
|||
|
if (AbnormalTermination()) {
|
|||
|
RxDbgTrace(-1, Dbg, ("RxCommonCleanup -> Abnormal Termination %08lx\n", Status));
|
|||
|
} else {
|
|||
|
RxDbgTrace(-1, Dbg, ("RxCommonCleanup -> %08lx\n", Status));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RxAdjustFileTimesAndSize ( RXCOMMON_SIGNATURE )
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to adjust the times and the filesize on a cleanup
|
|||
|
or a flush.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the Irp to process
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
RXSTATUS - The return status for the operation
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
RxCaptureFcb;
|
|||
|
RxCaptureFobx;
|
|||
|
RxCaptureParamBlock;
|
|||
|
RxCaptureFileObject;
|
|||
|
|
|||
|
BOOLEAN UpdateFileSize;
|
|||
|
BOOLEAN UpdateLastWriteTime;
|
|||
|
BOOLEAN UpdateLastAccessTime;
|
|||
|
BOOLEAN UpdateLastChangeTime;
|
|||
|
|
|||
|
LARGE_INTEGER CurrentTime;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//if there's no cachemap then we don't have to send because the guy is
|
|||
|
//tracking everything on the other end.
|
|||
|
//LOCAL.MINI for a localminiFS we would still have to do this; so the answer to this question
|
|||
|
// (whether to do it or not) should be exposed in the fcb/fobx
|
|||
|
if ( capFileObject->PrivateCacheMap == NULL ) return;
|
|||
|
|
|||
|
KeQuerySystemTime( &CurrentTime );
|
|||
|
|
|||
|
//
|
|||
|
// Note that we HAVE to use BooleanFlagOn() here because
|
|||
|
// FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte).
|
|||
|
//
|
|||
|
|
|||
|
UpdateFileSize = BooleanFlagOn(capFileObject->Flags, FO_FILE_SIZE_CHANGED);
|
|||
|
|
|||
|
UpdateLastWriteTime = FlagOn(capFileObject->Flags, FO_FILE_MODIFIED) &&
|
|||
|
!FlagOn(capFobx->Flags, FOBX_FLAG_USER_SET_LAST_WRITE);
|
|||
|
|
|||
|
UpdateLastChangeTime = FlagOn(capFileObject->Flags, FO_FILE_MODIFIED) &&
|
|||
|
!FlagOn(capFobx->Flags, FOBX_FLAG_USER_SET_LAST_CHANGE);
|
|||
|
|
|||
|
UpdateLastAccessTime =
|
|||
|
(UpdateLastWriteTime ||
|
|||
|
(FlagOn(capFileObject->Flags, FO_FILE_FAST_IO_READ) &&
|
|||
|
!FlagOn(capFobx->Flags, FOBX_FLAG_USER_SET_LAST_ACCESS)));
|
|||
|
|
|||
|
if (UpdateFileSize ||
|
|||
|
UpdateLastWriteTime ||
|
|||
|
UpdateLastChangeTime ||
|
|||
|
UpdateLastAccessTime ) {
|
|||
|
|
|||
|
ULONG NotifyFilter = 0;
|
|||
|
BOOLEAN DoTheTimeUpdate = FALSE;
|
|||
|
|
|||
|
FILE_BASIC_INFORMATION BasicInformation;
|
|||
|
FILE_END_OF_FILE_INFORMATION EofInformation;
|
|||
|
|
|||
|
RxDbgTrace(0, Dbg, ("Update Time and/or file size on File\n", 0));
|
|||
|
RtlZeroMemory(&BasicInformation,sizeof(BasicInformation));
|
|||
|
|
|||
|
try { //for finally
|
|||
|
try { //for exceptions
|
|||
|
|
|||
|
if (UpdateLastWriteTime) {
|
|||
|
|
|||
|
//
|
|||
|
// Update its time of last write
|
|||
|
|
|||
|
DoTheTimeUpdate = TRUE;
|
|||
|
capFcb->LastWriteTime = CurrentTime;
|
|||
|
BasicInformation.LastWriteTime = CurrentTime;
|
|||
|
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES
|
|||
|
| FILE_NOTIFY_CHANGE_LAST_WRITE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (UpdateLastChangeTime) {
|
|||
|
|
|||
|
//
|
|||
|
// Update its time of last write
|
|||
|
|
|||
|
DoTheTimeUpdate = TRUE;
|
|||
|
BasicInformation.ChangeTime = capFcb->LastChangeTime;
|
|||
|
|
|||
|
//NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES
|
|||
|
// | FILE_NOTIFY_CHANGE_LAST_CHANGE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (UpdateLastAccessTime) {
|
|||
|
|
|||
|
DoTheTimeUpdate = TRUE;
|
|||
|
capFcb->LastAccessTime = CurrentTime;
|
|||
|
BasicInformation.LastAccessTime = CurrentTime;
|
|||
|
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (DoTheTimeUpdate) {
|
|||
|
NTSTATUS Status; //if it doesn't work.....sigh
|
|||
|
RxContext->Info.FileInformationClass = (FileBasicInformation);
|
|||
|
RxContext->Info.Buffer = &BasicInformation;
|
|||
|
RxContext->Info.Length = sizeof(BasicInformation);
|
|||
|
MINIRDR_CALL(Status,RxContext,capFcb->MRxDispatch,MRxSetFileInfoAtCleanup,(RxContext));
|
|||
|
}
|
|||
|
|
|||
|
if (UpdateFileSize) {
|
|||
|
NTSTATUS Status; //if it doesn't work.....sigh
|
|||
|
EofInformation.EndOfFile = capFcb->Header.FileSize;
|
|||
|
RxContext->Info.FileInformationClass = (FileEndOfFileInformation);
|
|||
|
RxContext->Info.Buffer = &EofInformation;
|
|||
|
RxContext->Info.Length = sizeof(EofInformation);
|
|||
|
MINIRDR_CALL(Status,RxContext,capFcb->MRxDispatch,MRxSetFileInfoAtCleanup,(RxContext));
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_SIZE;
|
|||
|
}
|
|||
|
|
|||
|
//RxNotifyReportChange( RxContext, Vcb, Fcb,
|
|||
|
// NotifyFilter,
|
|||
|
// FILE_ACTION_MODIFIED );
|
|||
|
|
|||
|
//MINIRDR_CALL(Status,capFcb->MRxDispatch,MRxSetTimeAndSize,(RxContext));
|
|||
|
} except( CATCH_EXPECTED_EXCEPTIONS ) {
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
} finally {
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#define RxMoveAllButFirstToAnotherList(List1,List2) { \
|
|||
|
PLIST_ENTRY FrontListEntry = (List1)->Flink; \
|
|||
|
if (FrontListEntry->Flink == (List1)) { \
|
|||
|
(List2)->Flink = (List2)->Blink = (List2); \
|
|||
|
} else { \
|
|||
|
(List2)->Blink = (List1)->Blink; \
|
|||
|
(List2)->Blink->Flink = (List2); \
|
|||
|
(List1)->Blink = FrontListEntry; \
|
|||
|
(List2)->Flink = FrontListEntry->Flink; \
|
|||
|
FrontListEntry->Flink = (List1); \
|
|||
|
(List2)->Flink->Blink = (List2); \
|
|||
|
} \
|
|||
|
}
|
|||
|
#if DBG
|
|||
|
PSZ RxDSQTagText[FOBX_NUMBER_OF_SERIALIZATION_QUEUES] = {"read","write"};
|
|||
|
VOID
|
|||
|
RxDumpSerializationQueue(
|
|||
|
PLIST_ENTRY SQ,
|
|||
|
PSZ TagText1,
|
|||
|
PSZ TagText2
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (IsListEmpty(SQ)) {
|
|||
|
RxDbgTrace(0, Dbg, ("RxDumpSerializationQueue %s%s is empty\n", TagText1, TagText2));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
RxDbgTrace(0, Dbg, ("RxDumpSerializationQueue %s%s:\n", TagText1, TagText2));
|
|||
|
for (ListEntry=SQ->Flink;
|
|||
|
ListEntry!=SQ;
|
|||
|
ListEntry=ListEntry->Flink) {
|
|||
|
//print out the contexts and the major op for validation
|
|||
|
PRX_CONTEXT RxContext = CONTAINING_RECORD( ListEntry,RX_CONTEXT,RxContextSerializationQLinks);
|
|||
|
RxDbgTrace(0, Dbg, (" rxc=%08lx op=%02lx\n", RxContext, RxContext->MajorFunction));
|
|||
|
}
|
|||
|
}
|
|||
|
#else
|
|||
|
#define RxDumpSerializationQueue(___r,___t12,___t13) {NOTHING;}
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
RxCleanupPipeQueues (
|
|||
|
IN PRX_CONTEXT RxContext
|
|||
|
)
|
|||
|
{
|
|||
|
RxCaptureFcb; RxCaptureFobx;
|
|||
|
LIST_ENTRY SecondaryBlockedQs[FOBX_NUMBER_OF_SERIALIZATION_QUEUES];
|
|||
|
PLIST_ENTRY PrimaryBlockedQs = &capFobx->Specific.NamedPipe.ReadSerializationQueue;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
RxDbgTrace(+1, Dbg, ("RxCleanupPipeQueues \n"));
|
|||
|
|
|||
|
//for pipes there are two sources of unhappiness...........
|
|||
|
//first, we have to get rid of any blocked operations.
|
|||
|
//second, if there are blocking operations that have already gone by then we have to send the
|
|||
|
// close smb early so that the server will, in turn, complete the outstanding
|
|||
|
ExAcquireFastMutexUnsafe(&RxContextPerFileSerializationMutex);
|
|||
|
|
|||
|
for (i=0;i<FOBX_NUMBER_OF_SERIALIZATION_QUEUES;i++) {
|
|||
|
RxDumpSerializationQueue(&PrimaryBlockedQs[i],RxDSQTagText[i],"Primary");
|
|||
|
if (!IsListEmpty(&PrimaryBlockedQs[i])) {
|
|||
|
RxMoveAllButFirstToAnotherList(
|
|||
|
&PrimaryBlockedQs[i],
|
|||
|
&SecondaryBlockedQs[i]);
|
|||
|
RxDumpSerializationQueue(&PrimaryBlockedQs[i],RxDSQTagText[i],"Primary");
|
|||
|
RxDumpSerializationQueue(&SecondaryBlockedQs[i],RxDSQTagText[i],"Secondary");
|
|||
|
} else {
|
|||
|
InitializeListHead(&SecondaryBlockedQs[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
ExReleaseFastMutexUnsafe(&RxContextPerFileSerializationMutex);
|
|||
|
|
|||
|
for (i=0;i<FOBX_NUMBER_OF_SERIALIZATION_QUEUES;i++) {
|
|||
|
for (;!IsListEmpty(&SecondaryBlockedQs[i]);) {
|
|||
|
PLIST_ENTRY FrontListEntry = (&SecondaryBlockedQs[i])->Flink;
|
|||
|
PRX_CONTEXT FrontRxContext = CONTAINING_RECORD( FrontListEntry,RX_CONTEXT,RxContextSerializationQLinks);
|
|||
|
RemoveEntryList(FrontListEntry);
|
|||
|
if (!FlagOn(FrontRxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
|
|||
|
RxDbgTrace(0, Dbg, (" unblocking %08lx\n",FrontRxContext));
|
|||
|
RxContext->StoredStatus = STATUS_PIPE_CLOSING;
|
|||
|
RxSignalSynchronousWaiter(FrontRxContext);
|
|||
|
} else {
|
|||
|
RxDbgTrace(0, Dbg, (" completing %08lx\n",FrontRxContext));
|
|||
|
RxCompleteAsynchronousRequest( RxContext, STATUS_PIPE_CLOSING );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RxDbgTrace(-1, Dbg, ("RxCleanupPipeQueues exit\n"));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
RxFakeLockEnumerator (
|
|||
|
IN OUT struct _SRV_OPEN * SrvOpen,
|
|||
|
IN OUT PVOID *ContinuationHandle,
|
|||
|
OUT PLARGE_INTEGER FileOffset,
|
|||
|
OUT PLARGE_INTEGER LockRange,
|
|||
|
OUT PBOOLEAN IsLockExclusive
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
THIS ROUTINE IS A FAKE THAT IS JUST USED FOR TESTING PURPOSES!
|
|||
|
|
|||
|
This routine is called from a minirdr to enumerate the filelocks on an FCB; it gets
|
|||
|
one lock on each call. currently, we just pass thru to the fsrtl routine which is very funky
|
|||
|
because it keeps the enumeration state internally; as a result, only one enumeration can be in progress
|
|||
|
at any time. we can change over to something better if it's ever required.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SrvOpen - a srvopen on the fcb to be enumerated.
|
|||
|
|
|||
|
ContinuationHandle - a handle passed back and forth representing the state of the enumeration.
|
|||
|
if a NULL is passed in, then we are to start at the beginning.
|
|||
|
|
|||
|
FileOffset,LockRange,IsLockExclusive - the description of the returned lock
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
a BOOLEAN. FALSE means you've reached the end of the list; TRUE means the returned lock data is valid
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG LockNumber;
|
|||
|
|
|||
|
LockNumber = (ULONG)(*ContinuationHandle);
|
|||
|
if (LockNumber>=12) {
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
LockNumber++;
|
|||
|
RxDbgTrace(0, Dbg, ("Rxlockenum %08lx\n", LockNumber ));
|
|||
|
FileOffset->QuadPart = LockNumber;
|
|||
|
LockRange->QuadPart = 1;
|
|||
|
*IsLockExclusive = (LockNumber&0x4)==0;
|
|||
|
*ContinuationHandle = (PVOID)LockNumber;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
RxUninitializeCacheMap(
|
|||
|
IN OUT PRX_CONTEXT RxContext,
|
|||
|
IN PFILE_OBJECT FileObject,
|
|||
|
IN PLARGE_INTEGER TruncateSize
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is a wrapper for CcUninitializeCacheMap.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PFILE_OBJECT FileObject - Supplies the file object for the file to purge.
|
|||
|
IN PLARGE_INTEGER TruncateSize - Specifies the new size for the file.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if file has been immediately purged, FALSE if we had to wait.
|
|||
|
|
|||
|
Note:
|
|||
|
The file must be locked exclusively before calling this routine.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN CacheReturnValue;
|
|||
|
CACHE_UNINITIALIZE_EVENT PurgeCompleteEvent;
|
|||
|
PFCB Fcb = FileObject->FsContext;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT ( NodeTypeIsFcb(Fcb) );
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that this thread owns the FCB.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT ( RxIsFcbAcquiredExclusive ( Fcb ) );
|
|||
|
|
|||
|
#if 0
|
|||
|
BUGBUG do we need this part too? [joejoe] of course we do. or something similar. we have to synchronize openers
|
|||
|
with this piece of code.
|
|||
|
//
|
|||
|
// In order to guarantee that only one thread is calling
|
|||
|
// RxPurgeCacheFile, we reset this event to the
|
|||
|
// not-signalled state before calling CcUninitializeCacheMap,
|
|||
|
// and then set it when we exit. If any other threads come in
|
|||
|
// while we are waiting on the event, they will find that
|
|||
|
// CacheFileObject is NULL, and thus wait until the cache purge
|
|||
|
// completes.
|
|||
|
//
|
|||
|
|
|||
|
KeClearEvent(&Fcb->NonPagedFcb->PurgeCacheSynchronizer);
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Now uninitialize the cache managers own file object. This is
|
|||
|
// done basically simply to allow us to wait until the cache purge
|
|||
|
// is complete.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent(&PurgeCompleteEvent.Event, SynchronizationEvent, FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// Release the lock on the FCB that our caller applied.
|
|||
|
//
|
|||
|
|
|||
|
RxReleaseFcb( RxContext, Fcb );
|
|||
|
|
|||
|
//RxLog(( "ccunini1", &Fcb->FileName, 2,
|
|||
|
// (TruncateSize == NULL) ? 0xffffffff : TruncateSize->LowPart,
|
|||
|
// (ULONG)&PurgeCompleteEvent ));
|
|||
|
CacheReturnValue = CcUninitializeCacheMap(FileObject, TruncateSize, &PurgeCompleteEvent);
|
|||
|
|
|||
|
#if 0
|
|||
|
//
|
|||
|
// Make sure that this thread doesn't own the FCB.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
|
|||
|
#endif
|
|||
|
//
|
|||
|
// Now wait for the cache manager to finish purging the file.
|
|||
|
//
|
|||
|
|
|||
|
KeWaitForSingleObject(&PurgeCompleteEvent.Event,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Re-acquire the FCB lock once we've waited for the
|
|||
|
// cache manager to finish the uninitialize.
|
|||
|
//
|
|||
|
|
|||
|
RxAcquireExclusiveFcb( RxContext, Fcb );
|
|||
|
#if 0
|
|||
|
//
|
|||
|
// Now set the purge cache event to the signalled state to allow
|
|||
|
// other threads waiting on the cache purge to continue.
|
|||
|
//
|
|||
|
|
|||
|
KeSetEvent(&Fcb->NonPagedFcb->PurgeCacheSynchronizer, 0, FALSE);
|
|||
|
#endif
|
|||
|
return(CacheReturnValue);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|