Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1302 lines
29 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
cache.c
Abstract:
This module implements the NtFlushBuffersFile API for NT and provides
support routines for read and write caches.
Author:
Colin Watson (ColinW) 22-Jan-1991
Revision History:
22-Jan-1991 colinw
Created
--*/
#include "precomp.h"
#pragma hdrstop
VOID
FindOldestFcb(
IN PFCB FcbToCheck,
IN PVOID Ctx
);
VOID
PurgeDormantCachedFile(
IN PFCB FcbToCheck,
IN PVOID Context
);
VOID
PurgeAnyDormantCachedFile(
IN PFCB FcbToCheck,
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RdrFsdFlushBuffersFile)
#pragma alloc_text(PAGE, RdrFspFlushBuffersFile)
#pragma alloc_text(PAGE, RdrFscFlushBuffersFile)
#pragma alloc_text(PAGE, RdrAcquireFcbForLazyWrite)
#pragma alloc_text(PAGE, RdrReleaseFcbFromLazyWrite)
#pragma alloc_text(PAGE, RdrAcquireFcbForReadAhead)
#pragma alloc_text(PAGE, RdrReleaseFcbFromReadAhead)
#pragma alloc_text(PAGE, RdrPurgeCacheFile)
#pragma alloc_text(PAGE, RdrUninitializeCacheMap)
#pragma alloc_text(PAGE, RdrFlushCacheFile)
#pragma alloc_text(PAGE, RdrPurgeDormantCachedFiles)
#pragma alloc_text(PAGE, RdrSetDormantCachedFile)
#pragma alloc_text(PAGE, RdrPurgeDormantFilesOnConnection)
#pragma alloc_text(PAGE, PurgeDormantCachedFile)
#pragma alloc_text(PAGE, PurgeAnyDormantCachedFile)
#pragma alloc_text(PAGE, FindOldestFcb)
#endif
NTSTATUS
RdrFsdFlushBuffersFile (
IN PFS_DEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD version of the NtFlushBuffersFile API.
Arguments:
IN PFS_DEVICE_OBJECT DeviceObject, - Supplies the device object for this
request
IN PIRP Irp - Supplies the IRP that describes the request
Return Value:
NTSTATUS - Status of operation
--*/
{
NTSTATUS Status;
PAGED_CODE();
dprintf(DPRT_READWRITE, ("RdrFsdFlushBuffersFile\n"));
FsRtlEnterFileSystem();
//
// Decide if we can block for I/O
//
try {
Status = RdrFscFlushBuffersFile( CanFsdWait( Irp ), DeviceObject, Irp );
} except (RdrExceptionFilter(GetExceptionInformation(), &Status)) {
Status = RdrProcessException(Irp, Status);
}
dprintf(DPRT_READWRITE, ("RdrFsdFlushBuffersFile -> %X\n", Status));
FsRtlExitFileSystem();
return Status;
}
NTSTATUS
RdrFspFlushBuffersFile (
IN PFS_DEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSP version of the NtFlushBuffersFile API.
API.
Arguments:
IN PFS_DEVICE_OBJECT DeviceObject, - Supplies the device object for this
request
IN PIRP Irp - Supplies the IRP that describes the request
Return Value:
NTSTATUS - Status of operation
--*/
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
PAGED_CODE();
dprintf(DPRT_READWRITE, ("RdrFspFlushBuffersFile\n"));
//
// Call the common routine. The Fsp is always allowed to block
//
return RdrFscFlushBuffersFile( TRUE, DeviceObject, Irp );
}
NTSTATUS
RdrFscFlushBuffersFile (
IN BOOLEAN Wait,
IN PFS_DEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD version of the NtFlushBuffersFile API.
API.
Arguments:
IN PFS_DEVICE_OBJECT DeviceObject, - Supplies the device object for this
request
IN PIRP Irp - Supplies the IRP that describes the request
Return Value:
NTSTATUS - Status of operation
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
BOOLEAN FcbLocked = FALSE;
PICB Icb;
PSMB_BUFFER SMBBuffer;
PSMB_HEADER Smb;
PREQ_FLUSH FlushFile;
PMDL SendMDL;
ULONG SendLength;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
Icb = ICB_OF(IrpSp);
dprintf(DPRT_READWRITE, ("RdrFscFlushBuffersFile. Wait: %lx, Irp:%08lx, FileObject: %08lx\n", Wait, Irp, IrpSp->FileObject));
try {
if (!RdrAcquireFcbLock(Icb->Fcb, ExclusiveLock, Wait)) {
try_return(Status = STATUS_PENDING);
}
FcbLocked = TRUE;
Status = RdrIsOperationValid(Icb, IrpSp->MajorFunction, IrpSp->FileObject);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
try {
dprintf(DPRT_READWRITE, ("RdrFscFlushBuffersFile. Type:%lx\n", Icb->Type));
switch ( Icb->Type ) {
case NamedPipe:
if (!Wait) {
try_return(Status = STATUS_PENDING);
}
Status = RdrNpFlushBuffers( Wait, Irp, Icb);
try_return(Status);
break;
case DiskFile:
//
// If this file is cached, flush the cache contents
//
if (!Wait) {
try_return(Status = STATUS_PENDING);
}
dprintf(DPRT_READWRITE, ("Flush cache for file %lx\n", IrpSp->FileObject));
Status = RdrFlushWriteBufferForFile(Irp, Icb, (BOOLEAN)!RdrUseAsyncWriteBehind);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
//RdrLog(( "ccflush1", &Icb->Fcb->FileName, 1, 0xffffffff ));
CcFlushCache(&Icb->NonPagedFcb->SectionObjectPointer, NULL, 0, &Irp->IoStatus);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
//
// Serialize behind paging I/O to ensure flush is done.
//
ExAcquireResourceExclusive(Icb->Fcb->Header.PagingIoResource, TRUE);
ExReleaseResource(Icb->Fcb->Header.PagingIoResource);
//
// Send a flush SMB to the server.
//
if ((SMBBuffer = RdrAllocateSMBBuffer()) == NULL) {
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
//
// Build the SMB
//
Smb = (PSMB_HEADER)SMBBuffer->Buffer;
Smb->Command = SMB_COM_FLUSH;
FlushFile = (PREQ_FLUSH)(Smb+1);
FlushFile->WordCount = 1;
SmbPutUshort(&FlushFile->Fid, Icb->FileId);
SmbPutUshort( &FlushFile->ByteCount, 0);
SendLength = FlushFile->Buffer - (PUCHAR )(Smb);
SendMDL = SMBBuffer->Mdl;
SendMDL->ByteCount = SendLength;
Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT, // Flags
Irp,
Icb->Fcb->Connection,
SendMDL,
NULL, // Only interested in the error code.
Icb->Se);
RdrFreeSMBBuffer(SMBBuffer);
if (Status == STATUS_INVALID_HANDLE) {
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
}
try_return(NOTHING);
break;
default:
try_return(Status = STATUS_SUCCESS);
break;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
try_return(Status = GetExceptionCode());
}
try_exit:NOTHING;
} finally {
if (FcbLocked) {
RdrReleaseFcbLock(Icb->Fcb);
}
}
if ( Status == STATUS_PENDING ) {
//
// Need to block and caller requested thread not to block.
//
ASSERT (Wait == FALSE);
RdrFsdPostToFsp(DeviceObject, Irp);
} else {
//
// Complete the I/O request with the specified status.
//
RdrCompleteRequest(Irp, Status);
}
dprintf(DPRT_READWRITE, ("Returning status %X\n", Status));
return Status;
}
BOOLEAN
RdrAcquireFcbForLazyWrite (
IN PVOID Context,
IN BOOLEAN Wait
)
/*++
Routine Description:
This routine acquires an FCB for shared access for a lazy write
operation.
Arguments:
IN PVOID Context - Supplies an FCB to lock.
IN BOOLEAN Wait - True if we can block the callers thread for this request
Return Value:
None.
--*/
{
PFCB Fcb = Context;
BOOLEAN RetValue;
PAGED_CODE();
ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
RetValue = ExAcquireResourceShared(Fcb->Header.PagingIoResource, Wait);
if (RetValue) {
//
// Remember the thread ID of the lazy writer. We use this to
// avoid updating valid data length if the write behind extends
// valid data length.
//
Fcb->LazyWritingThread = PsGetCurrentThread();
}
return RetValue;
}
VOID
RdrReleaseFcbFromLazyWrite (
IN PVOID Context
)
/*++
Routine Description:
This routine releases an FCB that was acquired for a close operation.
Arguments:
IN PVOID Context - Supplies an FCB to lock.
Return Value:
None.
--*/
{
PFCB Fcb = Context;
PAGED_CODE();
ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
//
// We're not doing a lazy write any more, we're done.
//
Fcb->LazyWritingThread = NULL;
ExReleaseResource(Fcb->Header.PagingIoResource);
}
BOOLEAN
RdrAcquireFcbForReadAhead (
IN PVOID Context,
IN BOOLEAN Wait
)
/*++
Routine Description:
This routine acquires an FCB for shared access for a read ahead operation.
Arguments:
IN PVOID Context - Supplies an FCB to lock.
IN BOOLEAN Wait - True if we can tie up the callers thread for this request
Return Value:
None.
--*/
{
PFCB Fcb = Context;
BOOLEAN RetValue;
PAGED_CODE();
ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
RetValue = RdrAcquireFcbLock(Fcb, SharedLock, Wait);
return RetValue;
}
VOID
RdrReleaseFcbFromReadAhead (
IN PVOID Context
)
/*++
Routine Description:
This routine releases an FCB that was acquired for a close operation.
Arguments:
IN PVOID Context - Supplies an FCB to lock.
Return Value:
None.
--*/
{
PFCB Fcb = Context;
PAGED_CODE();
ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
RdrReleaseFcbLock(Fcb);
}
NTSTATUS
RdrPurgeCacheFile (
IN PFCB Fcb
)
/*++
Routine Description:
This routine will purge the specified file from the cache.
Arguments:
IN PFCB Fcb - Supplies an FCB for the file to purge.
Return Value:
NTSTATUS - Status of purge operation. This operation may raise.
Note:
The file must be locked exclusively before calling this routine.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PFILE_OBJECT CacheFileObject;
PLIST_ENTRY IcbEntry;
PAGED_CODE();
dprintf(DPRT_CACHE, ("RdrPurgeCacheFile Fcb:%lx (%wZ)\n", Fcb, &Fcb->FileName));
ASSERT ((Fcb->NonPagedFcb->Type == DiskFile) ||
(Fcb->NonPagedFcb->Type == Directory) ||
(Fcb->NonPagedFcb->Type == FileOrDirectory));
ASSERT (Fcb->NonPagedFcb->FileType == FileTypeDisk);
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
for (IcbEntry = Fcb->InstanceChain.Flink;
IcbEntry != &Fcb->InstanceChain ;
IcbEntry = IcbEntry->Flink) {
PICB IcbToFlush = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
if (IcbToFlush->Type == DiskFile &&
IcbToFlush->Flags & ICB_OPENED) {
Status = RdrFlushWriteBufferForFile(NULL, IcbToFlush, TRUE);
}
}
//
// Tell the cache manager to blow away all the references for all the
// file objects associated with this FCB.
//
//RdrLog(( "ccpurge1", &Fcb->FileName, 2, 0xffffffff, 1 << 24 ));
CcPurgeCacheSection(&Fcb->NonPagedFcb->SectionObjectPointer, NULL, 0, TRUE);
//
// Now try to get the cache file object from the cache manager,
// and if there is none, we're done now.
//
CacheFileObject = CcGetFileObjectFromSectionPtrs(&Fcb->NonPagedFcb->SectionObjectPointer);
dprintf(DPRT_CACHE, ("Removing file %lx (Fcb %lx) from the cache (hard)\n", CacheFileObject, Fcb));
if (CacheFileObject != NULL) {
//RdrLog(( "rdunini1", &Fcb->FileName, 0 ));
RdrUninitializeCacheMap(CacheFileObject, &RdrZero);
} else {
//
// Make sure that this thread owns the FCB.
//
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
//
// Release the lock on the FCB that our caller applied.
//
// We do this to make sure that other threads can come in while we
// are waiting for the cache flush to complete.
//
RdrReleaseFcbLock(Fcb);
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
KeWaitForSingleObject(&Fcb->NonPagedFcb->PurgeCacheSynchronizer, Executive, KernelMode, FALSE, NULL);
//
// Re-acquire the FCB lock once we've waited for MM
// finish forcing the section closed.
//
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
}
//
// Make sure that this thread owns the FCB.
//
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
//
// Release the lock on the FCB that our caller applied.
//
RdrReleaseFcbLock(Fcb);
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
//
// If this is an executable opened over the net, then
// its possible that the executables image section
// might still be kept open.
//
// Ask MM to flush the section closed. This will fail
// if the executable in question is still running.
//
//RdrLog(( "mmflush1", &Fcb->FileName, 1, MmFlushForWrite ));
MmFlushImageSection(&Fcb->NonPagedFcb->SectionObjectPointer, MmFlushForWrite);
//
// There is also a possiblity that there is a user section
// open on this file, in which case we need to force the
// section closed to make sure that they are cleaned up.
//
//RdrLog(( "mmforce1", &Fcb->FileName, 1, TRUE ));
MmForceSectionClosed(&Fcb->NonPagedFcb->SectionObjectPointer, TRUE);
//
// Re-acquire the FCB lock once we've waited for MM
// finish forcing the section closed.
//
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);;
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
dprintf(DPRT_CACHE, ("RdrPurgeCacheFile returning STATUS_SUCCESS, Fcb: %lx\n", Fcb));
return STATUS_SUCCESS;
}
BOOLEAN
RdrUninitializeCacheMap(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER TruncateSize
)
/*++
Routine Description:
This routine is a redirector 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 (Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
//
// Make sure that this thread owns the FCB.
//
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
//
// In order to guarantee that only one thread is calling
// RdrPurgeCacheFile, 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);
//
// 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);
//RdrLog(( "ccunini1", &Fcb->FileName, 2,
// (TruncateSize == NULL) ? 0xffffffff : TruncateSize->LowPart,
// (ULONG)&PurgeCompleteEvent ));
CacheReturnValue = CcUninitializeCacheMap(FileObject, TruncateSize, &PurgeCompleteEvent);
//
// Release the lock on the FCB that our caller applied.
//
RdrReleaseFcbLock(Fcb);
//
// Make sure that this thread doesn't own the FCB.
//
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
//
// 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.
//
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
//
// 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);
return(CacheReturnValue);
}
NTSTATUS
RdrFlushCacheFile (
IN PFCB Fcb
)
/*++
Routine Description:
This routine will purge the specified file from the cache.
Arguments:
IN PFCB Fcb - Supplies an FCB for the file to purge.
Return Value:
NTSTATUS - Status of purge operation. This operation may raise.
Note:
The file must be locked exclusively before calling this routine.
--*/
{
IO_STATUS_BLOCK IoStatus;
PLIST_ENTRY IcbEntry;
NTSTATUS Status;
PAGED_CODE();
dprintf(DPRT_CACHE, ("RdrFlushCacheFile Fcb:%lx (%wZ)\n", Fcb, &Fcb->FileName));
ASSERT ((Fcb->NonPagedFcb->Type == DiskFile) ||
(Fcb->NonPagedFcb->Type == Directory) ||
(Fcb->NonPagedFcb->Type == FileOrDirectory));
ASSERT (Fcb->NonPagedFcb->FileType == FileTypeDisk);
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
for (IcbEntry = Fcb->InstanceChain.Flink ;
IcbEntry != &Fcb->InstanceChain ;
IcbEntry = IcbEntry->Flink) {
PICB Icb = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
ASSERT (Icb->Signature == STRUCTURE_SIGNATURE_ICB);
if (Icb->Type == DiskFile &&
FlagOn(Icb->Flags, ICB_OPENED)) {
//
// Initiate a flush of the write behind data for this file.
//
Status = RdrFlushWriteBufferForFile(NULL, Icb, (BOOLEAN)!RdrUseAsyncWriteBehind);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Now wait for the flush operation on the file to complete.
//
RdrWaitForWriteBehindOperation(Icb);
}
}
//
// Flush dirty data for this file object from the cache.
//
//RdrLog(( "ccflush2", &Fcb->FileName, 1, 0xffffffff ));
CcFlushCache(&Fcb->NonPagedFcb->SectionObjectPointer, NULL, 0, &IoStatus);
if (NT_SUCCESS(IoStatus.Status)) {
//
// Serialize behind paging I/O to ensure flush is done.
//
ExAcquireResourceExclusive(Fcb->Header.PagingIoResource, TRUE);
ExReleaseResource(Fcb->Header.PagingIoResource);
}
//
// At this point, dirty data should be flushed for this file
//
return IoStatus.Status;
}
typedef struct _FINDOLDESTFCB {
PFCB OldestFcb;
ULONG NumberOfDormantCachedFiles;
} FINDOLDESTFCB, *PFINDOLDESTFCB;
VOID
RdrSetDormantCachedFile(
IN PFCB Fcb
)
/*++
Routine Description:
This routine will mark a specified FCB as being dormant.
Arguments:
IN PFCB Fcb - Fcb to mark as being dormant.
Return Value:
None.
Note:
This routine assumes that the FCB is currently locked.
--*/
{
PAGED_CODE();
//
// If we are in 'TurboMode' we will cache all of the files we can, without
// limit. This is for benchmarks
//
if( RdrTurboMode == FALSE ) {
FINDOLDESTFCB Context;
BOOLEAN FcbLocked = FALSE;
Context.NumberOfDormantCachedFiles = 0;
Context.OldestFcb = NULL;
//
// This thread cannot own the FCB resource on entry. If it does, we
// might deadlock.
//
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
RdrForeachFcbOnConnection (Fcb->Connection, NoLock, FindOldestFcb, &Context);
//
// Now that we've scanned the list to find the oldest cached file,
// we want to pull this file out of the list if we've reached our
// limit on dormant cached files.
//
if (Context.NumberOfDormantCachedFiles >= RdrData.DormantFileLimit) {
//
// Lock this FCB to protect the DormantTimeout field in the FCB.
//
RdrAcquireFcbLock(Context.OldestFcb, ExclusiveLock, TRUE);
FcbLocked = TRUE;
ASSERT (Context.OldestFcb != NULL);
ASSERT (Context.OldestFcb != Fcb);
//
// If the oldest FCB is still dormant, purge it now. Note that
// if this FCB is no longer dormant, we'll end up with an extra
// dormant file, above the limit. So be it.
//
if (Context.OldestFcb->NumberOfOpens == 0) {
//RdrLog(( "rdflush1", &Context.OldestFcb->FileName, 0 ));
RdrFlushCacheFile(Context.OldestFcb);
//RdrLog(( "rdpurge1", &Context.OldestFcb->FileName, 0 ));
RdrPurgeCacheFile(Context.OldestFcb);
}
}
//
// We're done with the oldest FCB, we can release it now.
//
if (Context.OldestFcb != NULL) {
BOOLEAN FcbDeleted;
FcbDeleted = RdrDereferenceFcb(NULL, Context.OldestFcb->NonPagedFcb, FcbLocked, 0, NULL);
// if (!FcbDeleted) {
// ASSERT (Fcb->Header.Resource->Threads[0] != (ULONG) ExGetCurrentResourceThread());
// }
}
}
//
// Lock this FCB to protect the DormantTimeout field in the FCB.
//
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
//
// Even for benchmarks, we want to eventually push out file changes
//
if( Fcb->UpdatedFile || RdrTurboMode == FALSE ) {
ULONG CacheFileTimeout;
//
// Mark this file as being dormant
//
//
// If the timeout is -1, we don't want to ever purge dormant
// files, otherwise we will set the dormant timeout appropriately.
//
CacheFileTimeout = RdrData.CachedFileTimeout;
if (CacheFileTimeout != -1) {
Fcb->DormantTimeout = RdrCurrentTime + CacheFileTimeout;
}
}
RdrReleaseFcbLock(Fcb);
//
// Now set the hint to indicate that there might be a dormant file on this connection
//
Fcb->Connection->NumberOfDormantFiles = 1;
}
VOID
FindOldestFcb(
IN PFCB FcbToCheck,
IN PVOID Ctx
)
/*++
Routine Description:
This routine is called on each FCB open on a connection to determine which
of them is the oldest FCB.
Arguments:
IN PFCB FcbToCheck - Fcb to check
IN PVOID Ctx - Context block - contains the oldest FCB pointer.
IN OUT PBOOLEAN UnlockFcb - Set/Cleared to indicate if we should unlock the
FCB in question when we dereference it.
Return Value:
None.
--*/
{
PFINDOLDESTFCB Context = Ctx;
PAGED_CODE();
//
// If this FCB is dormant, then it is a candidate for flushing.
//
//
// Please note that files that are currently open have a dormant
// timeout of -1, thus we will always skip over the file we are
// going to mark as dormant.
//
if ((FcbToCheck->NonPagedFcb->Type == DiskFile)
&&
(FcbToCheck->DormantTimeout != 0xffffffff)
&&
(FcbToCheck->NumberOfOpens == 0)) {
Context->NumberOfDormantCachedFiles += 1;
if ((Context->OldestFcb == NULL) ||
(FcbToCheck->DormantTimeout < Context->OldestFcb->DormantTimeout)) {
BOOLEAN FcbDeleted;
if (Context->OldestFcb != NULL) {
FcbDeleted = RdrDereferenceFcb(NULL, Context->OldestFcb->NonPagedFcb, FALSE, 0, NULL);
// if (!FcbDeleted) {
// ASSERT (Fcb->Header.Resource->Threads[0] != (ULONG) ExGetCurrentResourceThread());
// }
}
//
// Reference the new oldest FCB to make sure it doesn't
// go away.
//
Context->OldestFcb = FcbToCheck;
RdrReferenceFcb(Context->OldestFcb->NonPagedFcb);
}
}
}
VOID
RdrPurgeDormantCachedFiles (
VOID
)
/*++
Routine Description:
This routine walks the FCB database and purges all cached files that have
been dormant for longer than the global dormant timeout.
Arguments:
None
Return Value:
None.
--*/
{
PAGED_CODE();
//
// If the discardable code reference count is 0, this means that there are
// no open files that can possibly be dormant (the only files left are
// either tree connections or are handles to the redirector directly),
// so we can simply early out.
//
if (!RdrIsDiscardableCodeReferenced(RdrFileDiscardableSection)) {
return;
}
RdrReferenceDiscardableCode(RdrFileDiscardableSection);
RdrForeachFcb(NoLock, PurgeDormantCachedFile, NULL);
RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
}
VOID
PurgeDormantCachedFile(
IN PFCB FcbToCheck,
IN PVOID Context
)
/*++
Routine Description:
This routine will purge a file from the cache if it is dormant.
Arguments:
None
Return Value:
None.
--*/
{
PAGED_CODE();
// ASSERT (ExIsResourceAcquiredExclusive(FcbToCheck->Header.Resource));
//
// If this FCB is dormant, and is older than the dormant file timeout,
// then it is to be flushed.
//
if ((FcbToCheck->NonPagedFcb->Type == DiskFile) &&
(FcbToCheck->NumberOfOpens == 0) &&
(RdrCurrentTime > FcbToCheck->DormantTimeout)
) {
RdrAcquireFcbLock(FcbToCheck, ExclusiveLock, TRUE);
if ((FcbToCheck->NumberOfOpens == 0) &&
(RdrCurrentTime > FcbToCheck->DormantTimeout)
) {
//
// Flush any write behind data outstanding on the file
//
//RdrLog(( "rdflush2", &FcbToCheck->FileName, 0 ));
RdrFlushCacheFile(FcbToCheck);
//
// Now pull the file from the cache.
//
//RdrLog(( "rdpurge2", &FcbToCheck->FileName, 0 ));
RdrPurgeCacheFile(FcbToCheck);
}
RdrReleaseFcbLock(FcbToCheck);
}
}
VOID
RdrPurgeDormantFilesOnConnection(
IN PCONNECTLISTENTRY Connection
)
/*++
Routine Description:
This routine will close all dormant files on a connection. This fixes a series
of problems such as copy a file and do a dir on the destination directory. If
the connection is not purged then the dir will return 0 bytes as the file length.
Arguments:
IN PCONNECTLISTENTRY Connection - Connection to scan for dormant files.
Return Value:
None.
Note:
This routine assumes that the FCB is currently locked.
--*/
{
PAGED_CODE();
//
// Early out if there aren't any dormant files on the connection.
//
if ( Connection->NumberOfDormantFiles == 0 ) {
return;
}
Connection->NumberOfDormantFiles = 0;
RdrForeachFcbOnConnection(Connection, NoLock, PurgeAnyDormantCachedFile, NULL);
}
VOID
PurgeAnyDormantCachedFile(
IN PFCB FcbToCheck,
IN PVOID Context
)
/*++
Routine Description:
This routine will purge a file from the cache if it is dormant.
Arguments:
None
Return Value:
None.
--*/
{
PAGED_CODE();
//
// If this FCB is dormant, then it is to be flushed.
//
if ((FcbToCheck->NonPagedFcb->Type == DiskFile) &&
(FcbToCheck->UpdatedFile == TRUE) &&
(FcbToCheck->NumberOfOpens == 0)) {
RdrAcquireFcbLock(FcbToCheck, ExclusiveLock, TRUE);
if (FcbToCheck->NumberOfOpens == 0) {
//
// Flush any write behind data outstanding on the file
//
//RdrLog(( "rdflush3", &FcbToCheck->FileName, 0 ));
RdrFlushCacheFile(FcbToCheck);
//
// Now pull the file from the cache.
//
//RdrLog(( "rdpurge3", &FcbToCheck->FileName, 0 ));
RdrPurgeCacheFile(FcbToCheck);
}
RdrReleaseFcbLock(FcbToCheck);
}
}