/*++ 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;iFlink; 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); }