Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3049 lines
74 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
blkfile.c
Abstract:
This module implements routines for managing various kinds of file
control blocks.
Master File Control Block (MFCB) -- one per named file that is open
at least once. Used to support compatibility mode and oplocks.
Local File Control Block (LFCB) -- one for each local open instance.
Represents local file object/handle. There may be multiple
LFCBs linked to a single MFCB.
Remote File Control Block (RFCB) -- one for each remote open instance.
Represents remote FID. There is usually one RFCB per LFCB, but
multiple compatibility mode RFCBs may be linked to a single LFCB.
Multiple remote FCB opens for a single file from a single session
are folded into one RFCB, because old DOS redirectors only send
one close.
Author:
Chuck Lenzmeier (chuckl) 4-Oct-1989
Revision History:
--*/
#include "precomp.h"
#include "blkfile.tmh"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_BLKFILE
//
// Get the address of the SRV_LOCK which corresponds to FileNameHashValue bucket
//
#define MFCB_LOCK_ADDR( _hash ) SrvMfcbHashTable[ HASH_TO_MFCB_INDEX( _hash ) ].Lock
//
// Forward declarations of local functions.
//
VOID
AllocateMfcb(
OUT PMFCB *Mfcb,
IN PUNICODE_STRING FileName,
IN ULONG FileNameHashValue,
IN PWORK_CONTEXT WorkContext
);
STATIC
VOID
CloseRfcbInternal (
IN PRFCB Rfcb,
IN KIRQL OldIrql
);
STATIC
VOID
DereferenceRfcbInternal (
IN PRFCB Rfcb,
IN KIRQL OldIrql
);
STATIC
VOID
ReferenceRfcbInternal (
PRFCB Rfcb,
IN KIRQL OldIrql
);
STATIC
VOID
UnlinkLfcbFromMfcb (
IN PLFCB Lfcb
);
STATIC
VOID
UnlinkRfcbFromLfcb (
IN PRFCB Rfcb
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AllocateMfcb )
#pragma alloc_text( PAGE, SrvCreateMfcb )
#pragma alloc_text( PAGE, SrvFindMfcb )
#pragma alloc_text( PAGE, SrvFreeMfcb )
#pragma alloc_text( PAGE, UnlinkLfcbFromMfcb )
#pragma alloc_text( PAGE, SrvDereferenceMfcb )
#pragma alloc_text( PAGE, SrvAllocateLfcb )
#pragma alloc_text( PAGE, SrvDereferenceLfcb )
#pragma alloc_text( PAGE, SrvFreeLfcb )
#pragma alloc_text( PAGE, UnlinkRfcbFromLfcb )
#pragma alloc_text( PAGE, SrvAllocateRfcb )
#pragma alloc_text( PAGE, SrvCloseRfcbsOnLfcb )
#pragma alloc_text( PAGE, SrvFreeRfcb )
#pragma alloc_text( PAGE8FIL, SrvCheckAndReferenceRfcb )
#pragma alloc_text( PAGE8FIL, SrvCloseRfcb )
#pragma alloc_text( PAGE8FIL, CloseRfcbInternal )
#pragma alloc_text( PAGE8FIL, SrvCompleteRfcbClose )
//#pragma alloc_text( PAGE8FIL, SrvDereferenceRfcb )
//#pragma alloc_text( PAGE8FIL, DereferenceRfcbInternal )
#pragma alloc_text( PAGE8FIL, SrvReferenceRfcb )
#pragma alloc_text( PAGE8FIL, ReferenceRfcbInternal )
#pragma alloc_text( PAGE8FIL, SrvCloseCachedRfcb )
//#pragma alloc_text( PAGE8FIL, SrvCloseCachedRfcbsOnConnection )
#pragma alloc_text( PAGE8FIL, SrvCloseCachedRfcbsOnLfcb )
#endif
#if 0
#pragma alloc_text( PAGECONN, SrvCloseRfcbsOnSessionOrPid )
#pragma alloc_text( PAGECONN, SrvCloseRfcbsOnTree )
#pragma alloc_text( PAGECONN, SrvFindCachedRfcb )
#endif
//
// Master File Control Block (MFCB) routines.
//
VOID
AllocateMfcb (
OUT PMFCB *Mfcb,
IN PUNICODE_STRING FileName,
IN ULONG FileNameHashValue,
IN PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This function allocates an MFCB from pool and places it in the hash table.
The bucket's Lock must be held exclusive when this is called!!
Arguments:
Mfcb - Returns a pointer to the MFCB, or NULL if no space was
available.
Return Value:
None.
--*/
{
CLONG blockLength;
PMFCB mfcb;
PNONPAGED_MFCB nonpagedMfcb = NULL;
PWORK_QUEUE queue = WorkContext->CurrentWorkQueue;
PLIST_ENTRY listHead;
PSINGLE_LIST_ENTRY listEntry;
PAGED_CODE();
//
// Attempt to allocate from pool.
//
blockLength = sizeof(MFCB) + FileName->Length + sizeof(WCHAR);
mfcb = ALLOCATE_HEAP( blockLength, BlockTypeMfcb );
*Mfcb = mfcb;
if ( mfcb == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"AllocateMfcb: Unable to allocate %d bytes from pool\n",
blockLength,
NULL
);
// The caller will log the error
return;
}
nonpagedMfcb = (PNONPAGED_MFCB)InterlockedExchangePointer(
&queue->CachedFreeMfcb,
nonpagedMfcb );
if( nonpagedMfcb == NULL ) {
listEntry = ExInterlockedPopEntrySList(
&queue->MfcbFreeList,
&queue->SpinLock
);
if( listEntry != NULL ) {
InterlockedDecrement( &queue->FreeMfcbs );
nonpagedMfcb = CONTAINING_RECORD( listEntry, NONPAGED_MFCB, SingleListEntry );
} else {
nonpagedMfcb = ALLOCATE_NONPAGED_POOL(
sizeof(NONPAGED_MFCB),
BlockTypeNonpagedMfcb );
if ( nonpagedMfcb == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"AllocateMfcb: Unable to allocate %d bytes from pool\n",
sizeof(NONPAGED_MFCB),
NULL
);
// The caller will log the error
FREE_HEAP( mfcb );
*Mfcb = NULL;
return;
}
IF_DEBUG(HEAP) {
KdPrint(( "AllocateMfcb: Allocated MFCB at 0x%p\n", mfcb ));
}
nonpagedMfcb->Type = BlockTypeNonpagedMfcb;
}
}
nonpagedMfcb->PagedBlock = mfcb;
RtlZeroMemory( mfcb, blockLength );
mfcb->NonpagedMfcb = nonpagedMfcb;
//
// Initialize the MFCB.
//
SET_BLOCK_TYPE_STATE_SIZE( mfcb, BlockTypeMfcb, BlockStateClosing, blockLength );
mfcb->BlockHeader.ReferenceCount = 1;
InitializeListHead( &mfcb->LfcbList );
INITIALIZE_LOCK( &nonpagedMfcb->Lock, MFCB_LOCK_LEVEL, "MfcbLock" );
//
// Store the filename as it was passed into us
//
mfcb->FileName.Length = FileName->Length;
mfcb->FileName.MaximumLength = (SHORT)(FileName->Length + sizeof(WCHAR));
mfcb->FileName.Buffer = (PWCH)(mfcb + 1);
RtlCopyMemory( mfcb->FileName.Buffer, FileName->Buffer, FileName->Length );
//
// Store the hash value for the filename
//
mfcb->FileNameHashValue = FileNameHashValue;
//
// Store the SnapShot time if set
//
mfcb->SnapShotTime.QuadPart = WorkContext->SnapShotTime.QuadPart;
INITIALIZE_REFERENCE_HISTORY( mfcb );
//
// Add it to the hash table
//
listHead = &SrvMfcbHashTable[ HASH_TO_MFCB_INDEX( FileNameHashValue ) ].List;
InsertHeadList( listHead, &mfcb->MfcbHashTableEntry );
#if SRVCATCH
if( SrvCatch.Length ) {
UNICODE_STRING baseName;
SrvGetBaseFileName( FileName, &baseName );
if( RtlCompareUnicodeString( &SrvCatch, &baseName, TRUE ) == 0 ) {
mfcb->SrvCatch = 1;
}
}
if( SrvCatchExt.Length && WorkContext->TreeConnect->Share->IsCatchShare ) {
UNICODE_STRING baseName;
SrvGetBaseFileName( FileName, &baseName );
if( baseName.Length > 6 )
{
baseName.Buffer += (baseName.Length-6)>>1;
baseName.Length = 6;
if( RtlCompareUnicodeString( &SrvCatchExt, &baseName, TRUE ) == 0 ) {
mfcb->SrvCatch = 2;
}
}
}
#endif
INCREMENT_DEBUG_STAT( SrvDbgStatistics.MfcbInfo.Allocations );
return;
} // AllocateMfcb
PMFCB
SrvCreateMfcb(
IN PUNICODE_STRING FileName,
IN PWORK_CONTEXT WorkContext,
IN ULONG HashValue
)
/*++
Routine Description:
Called when a file is about to be opened. Searches the Master File
Table to see if the named file is already open. If it isn't, a
Master File Control Block is allocated and added to the list.
*** The MFCB list lock must be held when this routine is called. It
remains held on exit.
*** Note that the master file list CANNOT be walked to find and
possibly delete open file instances. This is because new
instances are added to the list before the file is actually
opened. The connection file tables must be used to find "real"
open file instances.
Arguments:
FileName - Fully qualified name of file being opened. If a new
master file block is created, the string data is copied to that
block, so the original data is no longer needed.
HashValue - the pre-computed hash value for this filename
Return Value:
PMFCB - Pointer to existing or newly created MFCB; NULL if unable
allocate space for MFCB.
--*/
{
PMFCB mfcb;
PLIST_ENTRY listEntryRoot, listEntry;
PAGED_CODE( );
//
// Search the Hash File List to determine whether the named file
// is already open.
//
ASSERT( ExIsResourceAcquiredExclusiveLite( MFCB_LOCK_ADDR( HashValue )) );
listEntryRoot = &SrvMfcbHashTable[ HASH_TO_MFCB_INDEX( HashValue ) ].List;
for( listEntry = listEntryRoot->Flink;
listEntry != listEntryRoot;
listEntry = listEntry->Flink ) {
mfcb = CONTAINING_RECORD( listEntry, MFCB, MfcbHashTableEntry );
if( mfcb->FileNameHashValue == HashValue &&
mfcb->FileName.Length == FileName->Length &&
mfcb->SnapShotTime.QuadPart == WorkContext->SnapShotTime.QuadPart &&
RtlEqualMemory( mfcb->FileName.Buffer,
FileName->Buffer,
FileName->Length ) ) {
//
// We've found a matching entry!
//
return mfcb;
}
}
//
// The named file is not yet open. Allocate an MFCB
//
AllocateMfcb( &mfcb, FileName, HashValue, WorkContext );
return mfcb;
} // SrvCreateMfcb
PMFCB
SrvFindMfcb(
IN PUNICODE_STRING FileName,
IN BOOLEAN CaseInsensitive,
OUT PSRV_LOCK *Lock,
OUT PULONG HashValue,
IN PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Searches the Master File Table to see if the named file is already
open, returning the address of an MFCB if it is.
*** The MFCB list lock will be acquire exclusively whether or not
this routine succeeds. The address of the lock is placed in *Lock
Arguments:
FileName - Fully qualified name of file being opened.
CaseInsensitive - TRUE if the search should be case-insensitive.
HashValue - if the MFCB was NOT found, *HashValue filled in with the hash
value derived from the filename. This can then be passed into
SrvCreateMfcb later
Return Value:
PMFCB - Pointer to existing created MFCB, if the named file is
already open; NULL otherwise.
--*/
{
PLIST_ENTRY listEntry, listEntryRoot;
ULONG localHashValue;
PMFCB mfcb;
PAGED_CODE( );
//
// Search the Master File List to determine whether the named file
// is already open. If the length of the file name is zero, then
// do not actually look in the list--the prefix routines do not
// work with zero-length strings, and we know that we'll never
// open a file with a name length == 0.
//
// !!! For SMB 4.0 (NT-NT), do we need to worry about share root
// directories?
if ( FileName->Length == 0 ) {
*HashValue = 0;
*Lock = NULL;
return NULL;
}
COMPUTE_STRING_HASH( FileName, &localHashValue );
listEntryRoot = &SrvMfcbHashTable[ HASH_TO_MFCB_INDEX( localHashValue ) ].List;
*Lock = MFCB_LOCK_ADDR( localHashValue );
ACQUIRE_LOCK( *Lock );
//
// Search the Hash File List to determine whether the named file
// is already open.
//
for( listEntry = listEntryRoot->Flink;
listEntry != listEntryRoot;
listEntry = listEntry->Flink ) {
mfcb = CONTAINING_RECORD( listEntry, MFCB, MfcbHashTableEntry );
if( mfcb->FileNameHashValue == localHashValue &&
mfcb->FileName.Length == FileName->Length &&
mfcb->SnapShotTime.QuadPart == WorkContext->SnapShotTime.QuadPart &&
RtlEqualUnicodeString( &mfcb->FileName, FileName,CaseInsensitive)) {
//
// We've found a matching entry!
//
ASSERT( GET_BLOCK_TYPE(mfcb) == BlockTypeMfcb );
ASSERT( GET_BLOCK_STATE(mfcb) == BlockStateClosing );
mfcb->BlockHeader.ReferenceCount++;
UPDATE_REFERENCE_HISTORY( mfcb, FALSE );
IF_DEBUG(REFCNT) {
KdPrint(( "Referencing MFCB %p; new refcnt %lx\n",
mfcb, mfcb->BlockHeader.ReferenceCount ));
}
return mfcb;
}
}
//
// We didn't find the entry! The file is not open
//
*HashValue = localHashValue;
return NULL;
} // SrvFindMfcb
VOID
SrvFreeMfcb (
IN PMFCB Mfcb
)
/*++
Routine Description:
This function returns an MFCB to the FSP heap.
If you change this code, you should also look in FreeIdleWorkItems
in scavengr.c
Arguments:
Mfcb - Address of MFCB
Return Value:
None.
--*/
{
PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
PNONPAGED_MFCB nonpagedMfcb = Mfcb->NonpagedMfcb;
PAGED_CODE();
DEBUG SET_BLOCK_TYPE_STATE_SIZE( Mfcb, BlockTypeGarbage, BlockStateDead, -1 );
TERMINATE_REFERENCE_HISTORY( Mfcb );
//
// Delete the lock on the MFCB. The lock must not be held.
//
ASSERT( RESOURCE_OF(nonpagedMfcb->Lock).ActiveCount == 0 );
DELETE_LOCK( &nonpagedMfcb->Lock );
nonpagedMfcb = (PNONPAGED_MFCB)InterlockedExchangePointer(
&queue->CachedFreeMfcb,
nonpagedMfcb );
if( nonpagedMfcb != NULL ) {
//
// This check allows for the possibility that FreeMfcbs might exceed
// MaxFreeMfcbs, but it's fairly unlikely given the operation of kernel
// queue objects. But even so, it probably won't exceed it by much and
// is really only advisory anyway.
//
if( queue->FreeMfcbs < queue->MaxFreeMfcbs ) {
ExInterlockedPushEntrySList(
&queue->MfcbFreeList,
&nonpagedMfcb->SingleListEntry,
&queue->SpinLock
);
InterlockedIncrement( &queue->FreeMfcbs );
} else {
DEALLOCATE_NONPAGED_POOL( nonpagedMfcb );
}
}
FREE_HEAP( Mfcb );
IF_DEBUG(HEAP) KdPrint(( "SrvFreeMfcb: Freed MFCB at 0x%p\n", Mfcb ));
INCREMENT_DEBUG_STAT( SrvDbgStatistics.MfcbInfo.Frees );
return;
} // SrvFreeMfcb
VOID
UnlinkLfcbFromMfcb (
IN PLFCB Lfcb
)
/*++
Routine Description:
This function unlinks an LFCB from its parent MFCB and decrements
the MFCB's reference count. If the count goes to zero, the MFCB
is removed from the Master File Table and deleted.
*** The MFCB lock must be held when this routine is called. It
is released before exit.
Arguments:
Lfcb - Address of LFCB
Return Value:
None.
--*/
{
PMFCB mfcb = Lfcb->Mfcb;
PAGED_CODE( );
ASSERT( mfcb != NULL );
ASSERT( ExIsResourceAcquiredExclusiveLite(&RESOURCE_OF(mfcb->NonpagedMfcb->Lock)) );
//
// Remove the LFCB from the MFCB's list. Decrement the reference
// count on the MFCB. The MFCB lock must be released before
// dereferencing the MFCB, because that may cause the MFCB to be
// deleted.
//
SrvRemoveEntryList( &mfcb->LfcbList, &Lfcb->MfcbListEntry );
RELEASE_LOCK( &mfcb->NonpagedMfcb->Lock );
SrvDereferenceMfcb( mfcb );
return;
} // UnlinkLfcbFromMfcb
VOID
SrvDereferenceMfcb (
IN PMFCB Mfcb
)
/*++
Routine Description:
This function decrements the reference count for an MFCB. If
the reference count reaches zero, the block is freed.
*** The MFCB lock (not the MFCB _list_ lock) must not be held when
this routine is called, unless the caller has an extra reference
to the MFCB, because otherwise this routine could destroy the
MFCB and the lock. Note that sequences beginning in DoDelete
and SrvMoveFile and coming here via SrvCloseRfcbsOnLfcb cause
this routine to be called with the MFCB lock held.
Arguments:
Mfcb - A pointer to the MFCB
Return Value:
None.
--*/
{
PSRV_LOCK lock = MFCB_LOCK_ADDR( Mfcb->FileNameHashValue );
PAGED_CODE( );
IF_DEBUG(REFCNT) {
KdPrint(( "Dereferencing MFCB %p; old refcnt %lx\n",
Mfcb, Mfcb->BlockHeader.ReferenceCount ));
}
//
// Acquire the MFCB table lock. This lock protects the reference
// count on the MFCB.
//
ACQUIRE_LOCK( lock );
ASSERT( GET_BLOCK_TYPE( Mfcb ) == BlockTypeMfcb );
ASSERT( (LONG)Mfcb->BlockHeader.ReferenceCount > 0 );
UPDATE_REFERENCE_HISTORY( Mfcb, TRUE );
if ( --Mfcb->BlockHeader.ReferenceCount == 0 ) {
//
// This is the last reference to the MFCB. Delete the block.
// Unlink the MFCB from the Master File Table.
//
ASSERT( Mfcb->LfcbList.Flink == &Mfcb->LfcbList );
RemoveEntryList( &Mfcb->MfcbHashTableEntry );
RELEASE_LOCK( lock );
//
// Free the MFCB. Note that SrvFreeMfcb deletes the MFCB's
// lock.
//
SrvFreeMfcb( Mfcb );
} else {
RELEASE_LOCK( lock );
}
} // SrvDereferenceMfcb
//
// Local File Control Block (LFCB) routines.
//
VOID
SrvAllocateLfcb (
OUT PLFCB *Lfcb,
IN PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This function allocates an LFCB from pool.
Arguments:
Lfcb - Returns a pointer to the LFCB, or NULL if no space was
available.
Return Value:
None.
--*/
{
PLFCB lfcb = NULL;
PWORK_QUEUE queue = WorkContext->CurrentWorkQueue;
PAGED_CODE();
//
// Attempt to allocate from pool.
//
lfcb = ALLOCATE_HEAP( sizeof(LFCB), BlockTypeLfcb );
*Lfcb = lfcb;
if ( lfcb == NULL ) {
ULONG size = sizeof( LFCB );
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"SrvAllocateLfcb: Unable to allocate %d bytes from paged pool.",
sizeof( LFCB ),
NULL
);
// The caller will log the error
return;
}
IF_DEBUG(HEAP) {
KdPrint(( "SrvAllocateLfcb: Allocated LFCB at 0x%p\n", lfcb ));
}
//
// Initialize the LFCB. Zero it first.
//
RtlZeroMemory( lfcb, sizeof(LFCB) );
//
// Initialize the LFCB.
//
SET_BLOCK_TYPE_STATE_SIZE( lfcb, BlockTypeLfcb, BlockStateClosing, sizeof( LFCB ) );
//
// !!! Note that the block's reference count is set to 1 to account
// for the open handle. No other reference is needed
// because 1) the LFCB is a temporary object, and 2) the
// caller (SrvAddOpenFileInstance) doesn't really need to
// reference the block, because it owns the appropriate lock
// for the entire time that it's doing its thing.
//
lfcb->BlockHeader.ReferenceCount = 1;
InitializeListHead( &lfcb->RfcbList );
INITIALIZE_REFERENCE_HISTORY( lfcb );
INCREMENT_DEBUG_STAT( SrvDbgStatistics.LfcbInfo.Allocations );
return;
} // SrvAllocateLfcb
VOID
SrvDereferenceLfcb (
IN PLFCB Lfcb
)
/*++
Routine Description:
This function dereference the LFCB and frees the LFCB if the reference
count reaches 0.
*** The caller of this function must own the MFCB lock for the file.
The lock is released by this function.
Arguments:
Lfcb - The LFCB to dereference
Return Value:
None.
--*/
{
PAGED_CODE( );
ASSERT( ExIsResourceAcquiredExclusiveLite(&RESOURCE_OF(Lfcb->Mfcb->NonpagedMfcb->Lock)) );
ASSERT( GET_BLOCK_TYPE( Lfcb ) == BlockTypeLfcb );
ASSERT( (LONG)Lfcb->BlockHeader.ReferenceCount > 0 );
UPDATE_REFERENCE_HISTORY( Lfcb, TRUE );
if ( --Lfcb->BlockHeader.ReferenceCount == 0 ) {
//
// This is the last reference to the LFCB. Unlink the
// LFCB from the MFCB's list.
//
ASSERT( Lfcb->RfcbList.Flink == &Lfcb->RfcbList );
ASSERT( Lfcb->HandleCount == 0 );
IF_DEBUG( CREATE ) {
KdPrint(( "SrvDereferenceLfcb: deref %wZ fileObject\n",
&Lfcb->Mfcb->FileName ));
}
//
// UnlinkLfcbFromMfcb will release the MFCB lock that we hold.
//
UnlinkLfcbFromMfcb( Lfcb );
//
// Dereference the file object.
//
ObDereferenceObject( Lfcb->FileObject );
DEBUG Lfcb->FileObject = NULL;
//
// Decrement the count of open files on the session and tree
// connect.
//
ACQUIRE_LOCK( &Lfcb->Connection->Lock );
ASSERT( Lfcb->Session->CurrentFileOpenCount != 0 );
Lfcb->Session->CurrentFileOpenCount--;
ASSERT( Lfcb->TreeConnect->CurrentFileOpenCount != 0 );
Lfcb->TreeConnect->CurrentFileOpenCount--;
RELEASE_LOCK( &Lfcb->Connection->Lock );
//
// Dereference the tree connect, session, and connection that
// the LFCB points to.
//
SrvDereferenceTreeConnect( Lfcb->TreeConnect );
DEBUG Lfcb->TreeConnect = NULL;
SrvDereferenceSession( Lfcb->Session );
DEBUG Lfcb->Session = NULL;
SrvDereferenceConnection( Lfcb->Connection );
DEBUG Lfcb->Connection = NULL;
//
// Free the LFCB.
//
SrvFreeLfcb( Lfcb, PROCESSOR_TO_QUEUE() );
} else {
RELEASE_LOCK( &Lfcb->Mfcb->NonpagedMfcb->Lock );
}
} // SrvDereferenceLfcb
VOID
SrvFreeLfcb (
IN PLFCB Lfcb,
IN PWORK_QUEUE queue
)
/*++
Routine Description:
This function returns an LFCB to the system nonpaged pool.
If you change this routine, look also in FreeIdleWorkItems in scavengr.c
Arguments:
Lfcb - Address of LFCB
Return Value:
None.
--*/
{
PAGED_CODE();
ASSERT ( Lfcb->HandleCount == 0 );
DEBUG SET_BLOCK_TYPE_STATE_SIZE( Lfcb, BlockTypeGarbage, BlockStateDead, -1 );
DEBUG Lfcb->BlockHeader.ReferenceCount = (ULONG)-1;
TERMINATE_REFERENCE_HISTORY( Lfcb );
FREE_HEAP( Lfcb );
INCREMENT_DEBUG_STAT( SrvDbgStatistics.LfcbInfo.Frees );
IF_DEBUG(HEAP) KdPrint(( "SrvFreeLfcb: Freed LFCB at 0x%p\n", Lfcb ));
return;
} // SrvFreeLfcb
VOID
UnlinkRfcbFromLfcb (
IN PRFCB Rfcb
)
/*++
Routine Description:
This function unlinks an RFCB from its parent LFCB and decrements
the LFCB's reference count. If the count goes to zero, the LFCB
is unlinked from its parent MFCB and deleted.
Arguments:
Rfcb - Address of RFCB
Return Value:
None.
--*/
{
PLFCB lfcb = Rfcb->Lfcb;
LARGE_INTEGER offset;
HANDLE handle;
PAGED_CODE( );
UpdateRfcbHistory( Rfcb, 'klnu' );
ASSERT( lfcb != NULL );
if( Rfcb->PagedRfcb->IpxSmartCardContext ) {
IF_DEBUG( SIPX ) {
KdPrint(("Calling Smart Card Close for Rfcb %p\n", Rfcb ));
}
SrvIpxSmartCard.Close( Rfcb->PagedRfcb->IpxSmartCardContext );
}
#ifdef INCLUDE_SMB_PERSISTENT
if (Rfcb->PersistentHandle) {
// SrvPostPersistentClose( Rfcb );
}
#endif
//
// Acquire the lock that guards access to the LFCB's RFCB list.
//
ACQUIRE_LOCK( &lfcb->Mfcb->NonpagedMfcb->Lock );
//
// Decrement the active RFCB count for the LFCB. This must be here
// instead of in SrvCloseRfcb because the MFCB lock must be held to
// update the count.
//
--lfcb->Mfcb->ActiveRfcbCount;
UPDATE_REFERENCE_HISTORY( lfcb, FALSE );
//
// Decrement the open handle count on the LFCB.
//
if ( --lfcb->HandleCount == 0 ) {
handle = lfcb->FileHandle;
//
// Other SMB processors may still have a referenced pointer to
// the LFCB. Ensure that any attempt to use the file handle fails.
//
lfcb->FileHandle = 0;
//
// This was the last open RFCB referencing the LFCB. Close the
// file handle.
//
SRVDBG_RELEASE_HANDLE( handle, "FIL", 3, lfcb );
IF_DEBUG( CREATE ) {
KdPrint(( "UnlinkRfcbFromLfcb: rfcb %p, close handle for %wZ\n",
Rfcb, &lfcb->Mfcb->FileName ));
}
SrvNtClose( handle, TRUE );
//
// If this is a print spool file, schedule the job on the
// printer.
//
if ( Rfcb->ShareType == ShareTypePrint ) {
SrvSchedulePrintJob(
lfcb->TreeConnect->Share->Type.hPrinter,
lfcb->JobId
);
}
//
// Release the open handle reference to the LFCB. The open
// lock is release by SrvDereferenceLfcb(). Note that this
// releases the MFCB lock.
//
SrvDereferenceLfcb( lfcb );
} else {
//
// Other RFCBs have references to the LFCB, so we can't close
// the file yet. (This must be a compatibility mode open.)
// Release all locks taken out by the process that opened the
// file.
//
// *** Note that if any locks were taken out using PIDs other
// than that which opened the FID, those locks cannot be
// automatically deleted. We count on the redirector to do
// the right thing in this case.
//
offset.QuadPart = 0;
IF_SMB_DEBUG(LOCK1) {
KdPrint(( "UnlinkRfcbFromLfcb: Issuing UnlockAllByKey for "
"file object 0x%p, key 0x%lx\n",
lfcb->FileObject,
Rfcb->ShiftedFid | Rfcb->Pid ));
}
(VOID)SrvIssueUnlockRequest(
lfcb->FileObject,
&lfcb->DeviceObject,
IRP_MN_UNLOCK_ALL_BY_KEY,
offset,
offset,
Rfcb->ShiftedFid | Rfcb->Pid
);
//
// Release the MFCB lock.
//
RELEASE_LOCK( &lfcb->Mfcb->NonpagedMfcb->Lock );
}
return;
} // UnlinkRfcbFromLfcb
//
// Remote File Control Block (RFCB) routines.
//
VOID SRVFASTCALL
SrvAllocateRfcb (
OUT PRFCB *Rfcb,
IN PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This function allocates an RFCB from nonpaged pool. Nonpaged pool
is used so that read/write completion can be handled in the FSD.
Arguments:
Rfcb - Returns a pointer to the RFCB, or NULL if no space was
available.
Return Value:
None.
--*/
{
PRFCB rfcb = NULL;
PPAGED_RFCB pagedRfcb;
PWORK_QUEUE queue = WorkContext->CurrentWorkQueue;
PAGED_CODE();
//
// Attempt to grab an rfcb structure off the per-queue free list
//
rfcb = (PRFCB)InterlockedExchangePointer( &queue->CachedFreeRfcb,
rfcb );
if( rfcb != NULL ) {
*Rfcb = rfcb;
pagedRfcb = rfcb->PagedRfcb;
} else {
if( queue->FreeRfcbs ) {
PSINGLE_LIST_ENTRY listEntry;
listEntry = ExInterlockedPopEntrySList(
&queue->RfcbFreeList,
&queue->SpinLock
);
if( listEntry != NULL ) {
InterlockedIncrement( &queue->FreeRfcbs );
rfcb = CONTAINING_RECORD( listEntry, RFCB, SingleListEntry );
*Rfcb= rfcb;
pagedRfcb = rfcb->PagedRfcb;
}
}
if( rfcb == NULL ) {
//
// Attempt to allocate from nonpaged pool.
//
rfcb = ALLOCATE_NONPAGED_POOL( sizeof(RFCB), BlockTypeRfcb );
*Rfcb = rfcb;
if ( rfcb == NULL ) {
INTERNAL_ERROR (
ERROR_LEVEL_EXPECTED,
"SrvAllocateRfcb: Unable to allocate %d bytes from nonpaged pool.",
sizeof( RFCB ),
NULL
);
return;
}
pagedRfcb = ALLOCATE_HEAP( sizeof(PAGED_RFCB), BlockTypePagedRfcb );
if ( pagedRfcb == NULL ) {
INTERNAL_ERROR (
ERROR_LEVEL_EXPECTED,
"SrvAllocateRfcb: Unable to allocate %d bytes from paged pool.",
sizeof( PAGED_RFCB ),
NULL
);
DEALLOCATE_NONPAGED_POOL( rfcb );
*Rfcb = NULL;
return;
}
IF_DEBUG(HEAP) {
KdPrint(( "SrvAllocateRfcb: Allocated RFCB at 0x%p\n", rfcb ));
}
}
}
//
// Initialize the RFCB. Zero it first.
//
RtlZeroMemory( rfcb, sizeof( RFCB ));
RtlZeroMemory( pagedRfcb, sizeof(PAGED_RFCB) );
rfcb->PagedRfcb = pagedRfcb;
pagedRfcb->PagedHeader.NonPagedBlock = rfcb;
pagedRfcb->PagedHeader.Type = BlockTypePagedRfcb;
SET_BLOCK_TYPE_STATE_SIZE( rfcb, BlockTypeRfcb, BlockStateActive, sizeof(RFCB) );
rfcb->BlockHeader.ReferenceCount = 2; // allow for Active status
// and caller's pointer
INITIALIZE_REFERENCE_HISTORY( rfcb );
rfcb->NewOplockLevel = NO_OPLOCK_BREAK_IN_PROGRESS;
pagedRfcb->LastFailingLockOffset.QuadPart = -1;
rfcb->IsCacheable = ( SrvCachedOpenLimit > 0 );
InterlockedIncrement(
(PLONG)&SrvStatistics.CurrentNumberOfOpenFiles
);
INCREMENT_DEBUG_STAT( SrvDbgStatistics.RfcbInfo.Allocations );
//
// Lock the file-based code section.
//
REFERENCE_UNLOCKABLE_CODE( 8FIL );
InitializeListHead( &rfcb->RawWriteSerializationList );
InitializeListHead( &rfcb->WriteMpx.GlomDelayList );
#ifdef INCLUDE_SMB_PERSISTENT
InitializeListHead( &pagedRfcb->ByteRangeLocks );
#endif
return;
} // SrvAllocateRfcb
BOOLEAN SRVFASTCALL
SrvCheckAndReferenceRfcb (
PRFCB Rfcb
)
/*++
Routine Description:
This function atomically verifies that an RFCB is active and
increments the reference count on the RFCB if it is.
Arguments:
Rfcb - Address of RFCB
Return Value:
BOOLEAN - Returns TRUE if the RFCB is active, FALSE otherwise.
--*/
{
KIRQL oldIrql;
UNLOCKABLE_CODE( 8FIL );
//
// Acquire the lock that guards the RFCB's state field.
//
ACQUIRE_SPIN_LOCK( &Rfcb->Connection->SpinLock, &oldIrql );
//
// If the RFCB is active, reference it and return TRUE. Note that
// ReferenceRfcbInternal releases the spin lock.
//
if ( GET_BLOCK_STATE(Rfcb) == BlockStateActive ) {
ReferenceRfcbInternal( Rfcb, oldIrql );
return TRUE;
}
//
// The RFCB isn't active. Return FALSE.
//
RELEASE_SPIN_LOCK( &Rfcb->Connection->SpinLock, oldIrql );
return FALSE;
} // SrvCheckAndReferenceRfcb
VOID SRVFASTCALL
SrvCloseRfcb (
PRFCB Rfcb
)
/*++
Routine Description:
This is the external routine for closing a file. It acquires the
appropriate spin lock, then calls CloseRfcbInternal.
Arguments:
Rfcb - Supplies a pointer to the RFCB to be closed.
Return Value:
None.
--*/
{
KIRQL oldIrql;
UNLOCKABLE_CODE( 8FIL );
//
// Acquire the lock that guards the RFCB's state field. Call the
// internal close routine. That routine releases the spin lock.
//
ACQUIRE_SPIN_LOCK( &Rfcb->Connection->SpinLock, &oldIrql );
CloseRfcbInternal( Rfcb, oldIrql );
return;
} // SrvCloseRfcb
VOID
CloseRfcbInternal (
PRFCB Rfcb,
IN KIRQL OldIrql
)
/*++
Routine Description:
This internal function does the core of a file close. It sets the
state of the RFCB to Closing, unlinks it from its parent LFCB, and
dereferences the RFCB. The RFCB will be destroyed as soon as all
other references to it are eliminated.
*** This routine must be called with the spin lock synchronizing
access to the RFCB's state field (the connection spin lock)
held. The lock is released on exit from this routine.
Arguments:
Rfcb - Supplies a pointer to the RFCB to be closed.
OldIrql - The previous IRQL value obtained when the spin lock was
acquired.
Return Value:
None.
--*/
{
KIRQL oldIrql = OldIrql;
LARGE_INTEGER cacheOffset;
PMDL mdlChain;
PCONNECTION connection = Rfcb->Connection;
PWORK_CONTEXT workContext;
ULONG i;
ULONG writeLength;
NTSTATUS status;
UNLOCKABLE_CODE( 8FIL );
ASSERT( GET_BLOCK_TYPE( Rfcb ) == BlockTypeRfcb );
//
// If the RFCB's state is still Active, change it to Closing and
// cause cleanup to happen.
//
if ( GET_BLOCK_STATE(Rfcb) == BlockStateActive ) {
IF_DEBUG(BLOCK1) KdPrint(( "Closing RFCB at 0x%p\n", Rfcb ));
UpdateRfcbHistory( Rfcb, 'solc' );
SET_BLOCK_STATE( Rfcb, BlockStateClosing );
//
// Invalidate the cached rfcb
//
if ( connection->CachedFid == (ULONG)Rfcb->Fid ) {
connection->CachedFid = (ULONG)-1;
}
//
// Don't cleanup if raw writes are still in progress
//
if ( Rfcb->RawWriteCount != 0 ) {
//
// Cleanup will happen in SrvDecrementRawWriteCount
//
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
return;
}
//
// Do we have write mpx outstanding?
//
if ( Rfcb->WriteMpx.ReferenceCount != 0 ) {
//
// Cleanup will happen when the ref count drops to 0
//
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
return;
} else if ( Rfcb->WriteMpx.Glomming ) {
//
// We need to complete this write mdl
//
Rfcb->WriteMpx.Glomming = FALSE;
Rfcb->WriteMpx.GlomComplete = FALSE;
//
// Save the offset and MDL address.
//
cacheOffset.QuadPart = Rfcb->WriteMpx.StartOffset;
mdlChain = Rfcb->WriteMpx.MdlChain;
writeLength = Rfcb->WriteMpx.Length;
DEBUG Rfcb->WriteMpx.MdlChain = NULL;
DEBUG Rfcb->WriteMpx.StartOffset = 0;
DEBUG Rfcb->WriteMpx.Length = 0;
//
// Now we can release the lock.
//
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
//
// Tell the cache manager that we're done with this MDL write.
//
if( Rfcb->Lfcb->MdlWriteComplete == NULL ||
Rfcb->Lfcb->MdlWriteComplete(
Rfcb->WriteMpx.FileObject,
&cacheOffset,
mdlChain,
Rfcb->Lfcb->DeviceObject ) == FALSE ) {
status = SrvIssueMdlCompleteRequest( NULL, Rfcb->WriteMpx.FileObject,
mdlChain,
IRP_MJ_WRITE,
&cacheOffset,
writeLength
);
if( !NT_SUCCESS( status ) ) {
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
}
}
} else {
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
}
//
// Do the actual close
//
SrvCompleteRfcbClose( Rfcb );
} else {
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
}
return;
} // CloseRfcbInternal
VOID
SrvCloseRfcbsOnLfcb (
PLFCB Lfcb
)
/*++
Routine Description:
This routine closes all RFCBs on an LFCB. It is used by Delete and
Rename processors to close all open instances of a file opened in
compability mode (or FCB).
*** The MFCB lock of the MFCB corresponding to this LFCB must be
held on entry to this routine; the lock remains held on exit.
The caller must also have an additional reference to the MFCB,
in order to prevent it from being deleted while the MFCB lock
is held.
Arguments:
Lfcb - Supplies a pointer to the LFCB whose RFCBs are to be closed.
Return Value:
None.
--*/
{
PPAGED_RFCB pagedRfcb;
PPAGED_RFCB nextPagedRfcb;
PRFCB rfcb;
PAGED_CODE( );
ASSERT( ExIsResourceAcquiredExclusiveLite(&RESOURCE_OF(Lfcb->Mfcb->NonpagedMfcb->Lock)) );
//
// Loop through the LFCB's RFCB list. Note that the fact that we
// hold the MFCB lock throughout this routine means that no changes
// to the list, other than the ones we make, can occur. This makes
// it safe to capture the address of the next RFCB in the list
// before closing the current one.
//
pagedRfcb = CONTAINING_RECORD(
Lfcb->RfcbList.Flink,
PAGED_RFCB,
LfcbListEntry
);
while ( &pagedRfcb->LfcbListEntry != &Lfcb->RfcbList ) {
nextPagedRfcb = CONTAINING_RECORD(
pagedRfcb->LfcbListEntry.Flink,
PAGED_RFCB,
LfcbListEntry
);
//
// A file owned by the specified LFCB has been found. Close it.
//
rfcb = pagedRfcb->PagedHeader.NonPagedBlock;
if ( GET_BLOCK_STATE(rfcb) == BlockStateActive ) {
SrvCloseRfcb( rfcb );
}
//
// Move to the next RFCB in the LFCB's list.
//
pagedRfcb = nextPagedRfcb;
}
//
// Close cached RFCBs. These aren't dealt with in the loop above
// because their state is BlockStateClosing.
//
SrvCloseCachedRfcbsOnLfcb( Lfcb );
return;
} // SrvCloseRfcbsOnLfcb
VOID
SrvCloseRfcbsOnSessionOrPid (
IN PSESSION Session,
IN PUSHORT Pid OPTIONAL
)
/*++
Routine Description:
This routine closes all files "owned" by the specified session and/or
PID in response to a Process Exit SMB. PIDs are unique within the
session that creates them. This routine walks the file table of the
connection that owns the specified session, closing all RFCBs whose
owning session and PID are equal to the PID passed to this routine.
Each session has a unique UID, so we can compare Uid's instead of comparing
the actual session pointer.
Arguments:
Session - Supplies a pointer to the session block corresponding to
the specified PID, if specified.
Pid - if present, Supplies pointer to the PID for which files are
to be closed.
Return Value:
None.
--*/
{
PTABLE_HEADER tableHeader;
PCONNECTION connection;
PRFCB rfcb;
USHORT i;
KIRQL oldIrql;
USHORT Uid;
PLIST_ENTRY listEntry;
//UNLOCKABLE_CODE( CONN );
//
// Get the address of the connection's file table.
//
connection = Session->Connection;
tableHeader = &connection->FileTable;
Uid = Session->Uid;
//
// Acquire the lock that guards the file table. This lock is held
// while walking the table, in order to prevent the table from
// changing.
//
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
//
// Walk the file table, looking for files owned by the specified
// session and/or PID.
//
for ( i = 0; i < tableHeader->TableSize; i++ ) {
rfcb = (PRFCB)tableHeader->Table[i].Owner;
if((rfcb != NULL) &&
(GET_BLOCK_STATE(rfcb) == BlockStateActive) &&
(rfcb->Uid == Uid) &&
(!ARGUMENT_PRESENT( Pid ) || (rfcb->Pid == *Pid)) ) {
//
// A file owned by the specified session/process has
// been found. Close the RFCB, and make sure it doesn't
// end up in the RFCB cache.
//
rfcb->IsCacheable = FALSE;
CloseRfcbInternal( rfcb, oldIrql );
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
}
}
//
// Now walk the RFCB cache to see if we have cached files that refer
// to this session that need to be closed.
//
again:
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvCloseRfcbsOnSessionOrPid: "
"checking for cached RFCBS\n" ));
for ( listEntry = connection->CachedOpenList.Flink;
listEntry != &connection->CachedOpenList;
listEntry = listEntry->Flink ) {
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
if( (rfcb->Uid == Uid) &&
( !ARGUMENT_PRESENT( Pid ) || rfcb->Pid == *Pid) ) {
//
// This cached file is owned by session and/or process.
// Close the RFCB.
//
SrvCloseCachedRfcb( rfcb, oldIrql );
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
goto again;
}
}
//
// All done. Release the lock.
//
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
return;
} // SrvCloseRfcbsOnSessionOrPid
VOID
SrvCloseRfcbsOnTree (
PTREE_CONNECT TreeConnect
)
/*++
Routine Description:
This routine closes all files "owned" by the specified tree connect.
It walks the file table of the connection that owns the tree
connection. Each file in that table that is owned by the tree
connect is closed.
Arguments:
TreeConnect - Supplies a pointer to the tree connect block for which
files are to be closed.
Return Value:
None.
--*/
{
PRFCB rfcb;
PTABLE_HEADER tableHeader;
PCONNECTION connection;
USHORT i;
KIRQL oldIrql;
PLIST_ENTRY listEntry;
USHORT Tid;
//UNLOCKABLE_CODE( CONN );
//
// Get the address of the connection's file table.
//
connection = TreeConnect->Connection;
tableHeader = &connection->FileTable;
Tid = TreeConnect->Tid;
//
// Acquire the lock that guards the file table. This lock is held
// while walking the table, in order to prevent the table from
// changing.
//
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
//
// Walk the file table, looking for files owned by the specified
// tree and PID.
//
for ( i = 0; i < tableHeader->TableSize; i++ ) {
rfcb = (PRFCB)tableHeader->Table[i].Owner;
if((rfcb != NULL) &&
(GET_BLOCK_STATE(rfcb) == BlockStateActive) &&
(rfcb->Tid == Tid )) {
//
// A file owned by the specified tree connect has been found.
// Close the RFCB and make sure it doesn't get cached
//
rfcb->IsCacheable = FALSE;
CloseRfcbInternal( rfcb, oldIrql );
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
}
}
//
// Walk the cached open list, looking for files open on this tree
// Close any that we find.
//
again:
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvCloseRfcbsOnTree: checking for cached RFCBS\n" ));
for ( listEntry = connection->CachedOpenList.Flink;
listEntry != &connection->CachedOpenList;
listEntry = listEntry->Flink ) {
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
if( rfcb->Tid == Tid ) {
//
// This cached file is owned by the specifiec tree connect.
// Close the RFCB.
//
SrvCloseCachedRfcb( rfcb, oldIrql );
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
goto again;
}
}
//
// All done. Release the lock.
//
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
return;
} // SrvCloseRfcbsOnTree
VOID
SrvCompleteRfcbClose (
IN PRFCB Rfcb
)
/*++
Routine Description:
This routine completes the rfcb close.
Arguments:
Rfcb - Supplies a pointer to the RFCB to be closed.
Return Value:
None.
--*/
{
KIRQL oldIrql;
PCONNECTION connection = Rfcb->Connection;
UNLOCKABLE_CODE( 8FIL );
UpdateRfcbHistory( Rfcb, 'tlpc' );
//
// Remove the Rfcb from the oplockbreaksinprogresslist. When the
// Rfcb gets closed, we don't process any more oplock breaks
// responses.
//
ACQUIRE_LOCK( &SrvOplockBreakListLock );
if ( Rfcb->OnOplockBreaksInProgressList ) {
Rfcb->NewOplockLevel = NO_OPLOCK_BREAK_IN_PROGRESS;
Rfcb->OplockState = OplockStateNone;
//
// Remove the Rfcb from the Oplock breaks in progress list, and
// release the Rfcb reference.
//
SrvRemoveEntryList( &SrvOplockBreaksInProgressList, &Rfcb->ListEntry );
Rfcb->OnOplockBreaksInProgressList = FALSE;
#if DBG
Rfcb->ListEntry.Flink = Rfcb->ListEntry.Blink = NULL;
#endif
RELEASE_LOCK( &SrvOplockBreakListLock );
SrvDereferenceRfcb( Rfcb );
ExInterlockedAddUlong(
&connection->OplockBreaksInProgress,
(ULONG)-1,
connection->EndpointSpinLock
);
} else {
RELEASE_LOCK( &SrvOplockBreakListLock );
}
//
// If this RFCB has a batch oplock, then it is eligible for caching.
//
if ( Rfcb->IsCacheable && Rfcb->NumberOfLocks == 0 &&
((Rfcb->OplockState == OplockStateOwnBatch) ||
(Rfcb->OplockState == OplockStateOwnServerBatch)) &&
(Rfcb->PagedRfcb->FcbOpenCount == 0) &&
!Rfcb->Mfcb->CompatibilityOpen ) {
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
if ( Rfcb->IsCacheable &&
((Rfcb->OplockState == OplockStateOwnBatch) ||
(Rfcb->OplockState == OplockStateOwnServerBatch)) &&
(GET_BLOCK_STATE(connection) == BlockStateActive) ) {
//
// Indicate that this RFCB now has a server-owned batch
// oplock. Indicate that it is on the cached-after-close
// list. Insert it on that list.
//
UpdateRfcbHistory( Rfcb, 'hcac' );
Rfcb->OplockState = OplockStateOwnServerBatch;
Rfcb->CachedOpen = TRUE;
InsertHeadList(
&connection->CachedOpenList,
&Rfcb->CachedOpenListEntry
);
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvCompleteRfcbClose: caching rfcb %p\n", Rfcb ));
//
// Increment the count of cached RFCBs. If there are now
// too many cached RFCBs, close the oldest one.
//
if ( ++connection->CachedOpenCount > SrvCachedOpenLimit ) {
PRFCB rfcbToClose;
rfcbToClose = CONTAINING_RECORD(
connection->CachedOpenList.Blink,
RFCB,
CachedOpenListEntry
);
//
// SrvCloseCachedRfcb releases the spin lock.
//
SrvCloseCachedRfcb( rfcbToClose, oldIrql );
} else {
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
}
if( Rfcb->PagedRfcb->IpxSmartCardContext ) {
IF_DEBUG( SIPX ) {
KdPrint(("Calling Smart Card Close for Rfcb %p\n", Rfcb ));
}
SrvIpxSmartCard.Close( Rfcb->PagedRfcb->IpxSmartCardContext );
Rfcb->PagedRfcb->IpxSmartCardContext = NULL;
}
return;
}
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
}
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvCompleteRfcbClose: can't cache rfcb %p, %wZ\n",
Rfcb, &Rfcb->Lfcb->Mfcb->FileName ));
//
// Unlink the RFCB from the LFCB. If this is the last RFCB for
// this LFCB, this will force the file closed even if there are
// still references to the RFCB. This will unblock blocked I/O.
//
UnlinkRfcbFromLfcb( Rfcb );
//
// Now reacquire the spin lock so that we can release the "open"
// reference to the Rfcb. DereferenceRfcbInternal releases the
// spin lock before returning.
//
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
DereferenceRfcbInternal( Rfcb, oldIrql );
INCREMENT_DEBUG_STAT( SrvDbgStatistics.RfcbInfo.Closes );
return;
} // SrvCompleteRfcbClose
VOID SRVFASTCALL
SrvDereferenceRfcb (
IN PRFCB Rfcb
)
/*++
Routine Description:
This function decrements the reference count on an RFCB. If the
reference count goes to zero, the RFCB is deleted.
Arguments:
Rfcb - Address of RFCB.
Return Value:
None.
--*/
{
KIRQL oldIrql;
UNLOCKABLE_CODE( 8FIL );
//
// Acquire the lock that guards the RFCB's reference count and the
// connection's file table. Then call the internal routine to
// decrement the count and possibly delete the RFCB. That function
// releases the spin lock before returning.
//
//
// !!! If you change the way this routine and
// DereferenceRfcbInternal work, make sure you check
// fsd.c\SrvFsdRestartSmbComplete to see if it needs to be
// changed too.
//
ACQUIRE_SPIN_LOCK( &Rfcb->Connection->SpinLock, &oldIrql );
DereferenceRfcbInternal( Rfcb, oldIrql );
return;
} // SrvDereferenceRfcb
VOID
DereferenceRfcbInternal (
IN PRFCB Rfcb,
IN KIRQL OldIrql
)
/*++
Routine Description:
This internal function decrements the reference count on an RFCB.
If the reference count goes to zero, the RFCB is deleted. This
function is called from other routines in this module.
*** The spin lock synchronizing access to the RFCB's reference count
must be held when this function is called. The lock is released
before this function returns.
Arguments:
Rfcb - Address of RFCB.
OldIrql - The previous IRQL value obtained when the spin lock was
acquired.
Return Value:
None.
--*/
{
PLFCB lfcb;
PPAGED_RFCB pagedRfcb;
PCONNECTION connection;
PWORK_QUEUE queue;
UNLOCKABLE_CODE( 8FIL );
ASSERT( GET_BLOCK_TYPE( Rfcb ) == BlockTypeRfcb );
ASSERT( (LONG)Rfcb->BlockHeader.ReferenceCount > 0 );
//
// The lock that guards the RFCB's reference count is held when this
// function is called.
//
// Decrement the reference count. If it goes to zero, remove the
// RFCB's entry in the file table, remove the RFCB from its parent
// LFCB's list, and deallocate the RFCB.
//
//
// !!! If you change the way this routine and SrvDereferenceRfcb
// work, make sure you check fsd.c\SrvFsdRestartSmbComplete to
// see if it needs to be changed too.
//
IF_DEBUG(REFCNT) {
KdPrint(( "Dereferencing RFCB 0x%p; old refcnt 0x%lx\n",
Rfcb, Rfcb->BlockHeader.ReferenceCount ));
}
connection = Rfcb->Connection;
queue = connection->CurrentWorkQueue;
Rfcb->BlockHeader.ReferenceCount--;
UPDATE_REFERENCE_HISTORY( Rfcb, TRUE );
if ( Rfcb->BlockHeader.ReferenceCount != 0 ) {
//
// Release the spin lock.
//
RELEASE_SPIN_LOCK( &connection->SpinLock, OldIrql );
} else {
ASSERT( GET_BLOCK_STATE(Rfcb) == BlockStateClosing );
ASSERT( Rfcb->ListEntry.Flink == NULL && \
Rfcb->ListEntry.Blink == NULL );
UpdateRfcbHistory( Rfcb, '0fer' );
//
// Remove the file entry from the appropriate connection file
// table.
//
SrvRemoveEntryTable(
&connection->FileTable,
FID_INDEX( Rfcb->Fid )
);
//
// Release the spin lock.
//
RELEASE_SPIN_LOCK( &connection->SpinLock, OldIrql );
//
// Free the IRP if one has been allocated.
//
if ( Rfcb->Irp != NULL ) {
UpdateRfcbHistory( Rfcb, 'prif' );
IoFreeIrp( Rfcb->Irp );
}
//
// Remove the RFCB from the LFCB's list and dereference the LFCB.
// Acquire the MFCB lock. SrvDereferenceLfcb will release it.
//
pagedRfcb = Rfcb->PagedRfcb;
lfcb = Rfcb->Lfcb;
ACQUIRE_LOCK( &lfcb->Mfcb->NonpagedMfcb->Lock);
//
// Remove the RFCB from the global list of RFCBs.
//
SrvRemoveEntryOrderedList( &SrvRfcbList, Rfcb );
SrvRemoveEntryList( &lfcb->RfcbList, &pagedRfcb->LfcbListEntry );
SrvDereferenceLfcb( lfcb );
DEBUG Rfcb->Lfcb = 0;
//
// Free the RFCB.
//
SrvFreeRfcb( Rfcb, queue );
}
return;
} // DereferenceRfcbInternal
VOID SRVFASTCALL
SrvFreeRfcb (
IN PRFCB Rfcb,
PWORK_QUEUE queue
)
/*++
Routine Description:
This function returns an RFCB to the system nonpaged pool. If changes are
made here, check out FreeIdleWorkItems in scavengr.c!
Arguments:
Rfcb - Address of RFCB
Return Value:
None.
--*/
{
PAGED_CODE();
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvFreeRfcb: %p\n", Rfcb ));
ASSERT( Rfcb->RawWriteCount == 0 );
ASSERT( IsListEmpty(&Rfcb->RawWriteSerializationList) );
UpdateRfcbHistory( Rfcb, 'eerf' );
//
// Free the the RFCB.
//
DEBUG SET_BLOCK_TYPE_STATE_SIZE( Rfcb, BlockTypeGarbage, BlockStateDead, -1 );
DEBUG Rfcb->BlockHeader.ReferenceCount = (ULONG)-1;
TERMINATE_REFERENCE_HISTORY( Rfcb );
Rfcb = (PRFCB)InterlockedExchangePointer( &queue->CachedFreeRfcb,
Rfcb );
if( Rfcb != NULL ) {
//
// This check allows for the possibility that FreeRfcbs might exceed
// MaxFreeRfcbs, but it's fairly unlikely given the operation of kernel
// queue objects. But even so, it probably won't exceed it by much and
// is really only advisory anyway.
//
if( queue->FreeRfcbs < queue->MaxFreeRfcbs ) {
ExInterlockedPushEntrySList(
&queue->RfcbFreeList,
&Rfcb->SingleListEntry,
&queue->SpinLock
);
InterlockedIncrement( &queue->FreeRfcbs );
} else {
FREE_HEAP( Rfcb->PagedRfcb );
DEALLOCATE_NONPAGED_POOL( Rfcb );
IF_DEBUG(HEAP) KdPrint(( "SrvFreeRfcb: Freed RFCB at 0x%p\n", Rfcb ));
INCREMENT_DEBUG_STAT( SrvDbgStatistics.RfcbInfo.Frees );
}
}
//
// Unlock the file-based code section.
//
DEREFERENCE_UNLOCKABLE_CODE( 8FIL );
InterlockedDecrement(
(PLONG)&SrvStatistics.CurrentNumberOfOpenFiles
);
return;
} // SrvFreeRfcb
VOID SRVFASTCALL
SrvReferenceRfcb (
PRFCB Rfcb
)
/*++
Routine Description:
This function increments the reference count on an RFCB.
Arguments:
Rfcb - Address of RFCB
Return Value:
None.
--*/
{
KIRQL oldIrql;
UNLOCKABLE_CODE( 8FIL );
//
// Acquire the spin lock that protects the RFCB's reference count,
// then call an internal routine to increment the RFCB's reference
// count. That routine releases the spin lock.
//
ACQUIRE_SPIN_LOCK( &Rfcb->Connection->SpinLock, &oldIrql );
ReferenceRfcbInternal( Rfcb, oldIrql );
return;
} // SrvReferenceRfcb
VOID
ReferenceRfcbInternal (
PRFCB Rfcb,
KIRQL OldIrql
)
/*++
Routine Description:
This function increments the reference count on an RFCB.
*** The spin lock synchronizing access to the RFCB's reference count
must be held when this function is called. The lock is released
before this function returns.
Arguments:
Rfcb - Address of RFCB
Return Value:
None.
--*/
{
UNLOCKABLE_CODE( 8FIL );
ASSERT( (LONG)Rfcb->BlockHeader.ReferenceCount > 0 );
ASSERT( GET_BLOCK_TYPE(Rfcb) == BlockTypeRfcb );
// ASSERT( GET_BLOCK_STATE(Rfcb) == BlockStateActive );
UPDATE_REFERENCE_HISTORY( Rfcb, FALSE );
//
// Increment the RFCB's reference count.
//
Rfcb->BlockHeader.ReferenceCount++;
IF_DEBUG(REFCNT) {
KdPrint(( "Referencing RFCB 0x%p; new refcnt 0x%lx\n",
Rfcb, Rfcb->BlockHeader.ReferenceCount ));
}
//
// Release the spin lock before returning to the caller.
//
RELEASE_SPIN_LOCK( &Rfcb->Connection->SpinLock, OldIrql );
return;
} // ReferenceRfcbInternal
BOOLEAN
SrvFindCachedRfcb (
IN PWORK_CONTEXT WorkContext,
IN PMFCB Mfcb,
IN ACCESS_MASK DesiredAccess,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN OPLOCK_TYPE RequestedOplockType,
OUT PNTSTATUS Status
)
/*++
Routine Description:
This routine searches a connection's cached-after-close RFCB list
to attempt to find an existing handle that can be matched up with
a new open attempt. If one is found, it is removed from the list
and reactivated.
Arguments:
WorkContext - Pointer to work context block.
Mfcb - Address of MFCB for file being opened.
DesiredAccess - Desired access for new open. Used for matching
purposes.
ShareAccess - Share access for new open. Used for matching
purposes.
CreateDisposition - Create disposition for new open. Used for
matching purposes.
CreateOptions - Create options for new open. Used for matching
purposes.
RequestedOplockType - Oplock type requested by the client (or the
server) for the new open. Used for matching purposes.
Status - Returns the status of the search. Only valid if return
value is TRUE. Will be STATUS_SUCCESS if a cached open was
found and taken out of the cache. In this case, the RFCB
address is stored in WorkContext->Rfcb. Status will be
STATUS_OBJECT_NAME_COLLISION if the file is cached but the
caller wants the open to file if the file exists.
Return Value:
BOOLEAN - TRUE if a cached open was found and returned.
--*/
{
PCONNECTION connection = WorkContext->Connection;
PLIST_ENTRY listEntry;
PRFCB rfcb;
KIRQL oldIrql;
USHORT uid, tid;
BOOLEAN wantsWriteThrough, isWriteThrough;
ACCESS_MASK nongenericDesiredAccess;
//UNLOCKABLE_CODE( CONN );
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvFindCachedRfcb: called for %wZ\n", &Mfcb->FileName ));
//
// If the client doesn't want an oplock, then the server should have
// asked for its own batch oplock.
//
ASSERT( (RequestedOplockType == OplockTypeBatch) ||
(RequestedOplockType == OplockTypeExclusive) ||
(RequestedOplockType == OplockTypeServerBatch) );
//
// This routine must not be called for create dispositions that are
// inconsistent with reusing a cached open. Specifically, supersede
// and overwrite are not allowed.
//
ASSERT( (CreateDisposition == FILE_OPEN) ||
(CreateDisposition == FILE_CREATE) ||
(CreateDisposition == FILE_OPEN_IF) );
//
// If the connection has no cached RFCBs, get out quick.
//
if ( connection->CachedOpenCount == 0 ) {
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvFindCachedRfcb: connection has no cached RFCBs\n" ));
return FALSE;
}
//
// The input DesiredAccess may include generic access modes, but the
// RFCB has specific access modes, so we have to translate
// DesiredAccess.
//
nongenericDesiredAccess = DesiredAccess;
IoCheckDesiredAccess( &nongenericDesiredAccess, 0 );
uid = WorkContext->Session->Uid;
tid = WorkContext->TreeConnect->Tid;
//
// Lock the cached open list and look for a matching RFCB.
//
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
for ( listEntry = connection->CachedOpenList.Flink;
listEntry != &connection->CachedOpenList;
listEntry = listEntry->Flink ) {
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvFindCachedRfcb: checking rfcb %p; mfcb = %p\n",
rfcb, rfcb->Mfcb ));
ASSERT( rfcb->OplockState == OplockStateOwnServerBatch );
ASSERT( rfcb->CachedOpen );
ASSERT( GET_BLOCK_STATE(rfcb) == BlockStateClosing );
//
// If this RFCB is for the right file, we can proceed with other
// checks.
//
if ( rfcb->Mfcb == Mfcb ) {
//
// If the client asked for FILE_CREATE, we can fail the open
// now, because the file exists.
//
if ( CreateDisposition == FILE_CREATE ) {
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvFindCachedRfcb: client wants to create\n" ));
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
*Status = STATUS_OBJECT_NAME_COLLISION;
return TRUE;
}
//
// Check the access modes to make sure they're compatible.
// The new open must:
//
// a) have the same desired access as what was granted before;
// b) have the same share access;
// c) have the create disposition (in the bits we care about);
// d) be requesting a batch oplock;
// e) be for the same UID and TID.
//
#define FILE_MODE_FLAGS (FILE_DIRECTORY_FILE | \
FILE_SEQUENTIAL_ONLY | \
FILE_NON_DIRECTORY_FILE | \
FILE_NO_EA_KNOWLEDGE | \
FILE_RANDOM_ACCESS | \
FILE_OPEN_REPARSE_POINT | \
FILE_OPEN_FOR_BACKUP_INTENT)
if ( (rfcb->GrantedAccess != nongenericDesiredAccess) ||
(rfcb->ShareAccess != ShareAccess) ||
((rfcb->FileMode & FILE_MODE_FLAGS) !=
(CreateOptions & FILE_MODE_FLAGS)) ||
(RequestedOplockType == OplockTypeExclusive) ||
(rfcb->Uid != uid) ||
(rfcb->Tid != tid) ) {
#if 0
IF_DEBUG(FILE_CACHE) {
if ( rfcb->GrantedAccess != nongenericDesiredAccess )
KdPrint(( "SrvFindCachedRfcb: granted access %x doesn't match desired access %x\n",
rfcb->GrantedAccess, nongenericDesiredAccess ));
if ( rfcb->ShareAccess != ShareAccess )
KdPrint(( "SrvFindCachedRfcb: share access %x doesn't match share access %x\n",
rfcb->ShareAccess, ShareAccess ));
if ( (rfcb->FileMode & FILE_MODE_FLAGS) != (CreateOptions & FILE_MODE_FLAGS))
KdPrint(( "SrvFindCachedRfcb: share access %x doesn't match share access %x\n",
rfcb->FileMode&FILE_MODE_FLAGS, CreateOptions&FILE_MODE_FLAGS ));
if ( RequestedOplockType == OplockTypeExclusive )
KdPrint(( "SrvFindCachedRfcb: client wants exclusive oplock\n" ));
if ( rfcb->Uid != uid )
KdPrint(( "SrvFindCachedRfcb: UID %x doesn't match UID %x\n", rfcb->Uid, uid ));
if ( rfcb->Tid != tid )
KdPrint(( "SrvFindCachedRfcb: TID %x doesn't match TID %x\n", rfcb->Tid, tid ));
}
#endif
//
// The file is cached, but the new open is inconsistent
// with the cached open. We must not use the cached
// open. It would be more efficient to close the cached
// RFCB here, since we know the caller is going to turn
// around and open the file because we're returning
// FALSE, thus breaking the batch oplock. However, our
// caller owns the MFCB lock, while closing an RFCB
// requires obtaining the MFCB list lock. Acquiring
// these locks in this order leads to deadlock.
//
// Note that there is no need to continue the list walk.
// We have a batch oplock, so we can only have the file
// open once.
//
#if 0
SrvCloseCachedRfcb( rfcb, oldIrql );
#else
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
#endif
return FALSE;
}
//
// The file is cached and the new open is consistent with the
// cached open. Remove the open from the cache and give it
// to the new opener.
//
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvFindCachedRfcb: Reusing cached RFCB %p\n", rfcb ));
UpdateRfcbHistory( rfcb, ' $nu' );
RemoveEntryList( &rfcb->CachedOpenListEntry );
connection->CachedOpenCount--;
ASSERT( (LONG)connection->CachedOpenCount >= 0 );
rfcb->CachedOpen = FALSE;
if ( RequestedOplockType == OplockTypeBatch ) {
rfcb->OplockState = OplockStateOwnBatch;
}
SET_BLOCK_STATE( rfcb, BlockStateActive );
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
WorkContext->Rfcb = rfcb;
SrvReferenceRfcb( rfcb );
rfcb->IsActive = FALSE;
rfcb->WrittenTo = FALSE;
wantsWriteThrough = (BOOLEAN)((CreateOptions & FILE_WRITE_THROUGH) != 0);
isWriteThrough = (BOOLEAN)((rfcb->Lfcb->FileMode & FILE_WRITE_THROUGH) == 0);
if ( wantsWriteThrough != isWriteThrough ) {
SrvSetFileWritethroughMode( rfcb->Lfcb, wantsWriteThrough );
}
INCREMENT_DEBUG_STAT( SrvDbgStatistics.OpensSatisfiedWithCachedRfcb );
WorkContext->Irp->IoStatus.Information = FILE_OPENED;
*Status = STATUS_SUCCESS;
return TRUE;
}
}
//
// We couldn't find the requested file in the cache.
//
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
return FALSE;
} // SrvFindCachedRfcb
ULONG
SrvCountCachedRfcbsForTid(
PCONNECTION connection,
USHORT Tid
)
/*++
Routine Description:
This returns the number of RFCBS in the cache that are associated with Tid
Arguments:
connection - Address of the CONNECTION structure of interest
Return Value:
Count of cached RFCBs
--*/
{
PLIST_ENTRY listEntry;
PRFCB rfcb;
KIRQL oldIrql;
USHORT count = 0;
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
for ( listEntry = connection->CachedOpenList.Flink;
listEntry != &connection->CachedOpenList;
listEntry = listEntry->Flink ) {
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
if( rfcb->Tid == Tid ) {
++count;
}
}
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
return count;
}
ULONG
SrvCountCachedRfcbsForUid(
PCONNECTION connection,
USHORT Uid
)
/*++
Routine Description:
This returns the number of RFCBS in the cache that are associated with Uid
Arguments:
connection - Address of the CONNECTION structure of interest
Return Value:
Count of cached RFCBs
--*/
{
PLIST_ENTRY listEntry;
PRFCB rfcb;
KIRQL oldIrql;
ULONG count = 0;
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
for ( listEntry = connection->CachedOpenList.Flink;
listEntry != &connection->CachedOpenList;
listEntry = listEntry->Flink ) {
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
if( rfcb->Uid == Uid ) {
++count;
}
}
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
return count;
}
VOID
SrvCloseCachedRfcb (
IN PRFCB Rfcb,
IN KIRQL OldIrql
)
/*++
Routine Description:
This routine closes a cached open.
*** This routine must be called with the connection spin lock held.
Arguments:
Rfcb - Address of RFCB to close.
OldIrql - IRQL at which the called acquired the connection spin
lock. This must be lower than DISPATCH_LEVEL!
Return Value:
None.
--*/
{
PCONNECTION connection = Rfcb->Connection;
KIRQL oldIrql;
UNLOCKABLE_CODE( 8FIL );
UpdateRfcbHistory( Rfcb, '$slc' );
//
// This routine must be called with the connection spin lock held.
// The caller must have been at low IRQL before acquiring the spin
// lock.
//
IF_DEBUG(FILE_CACHE) KdPrint(( "SrvCloseCachedRfcb called for rfcb %p", Rfcb ));
ASSERT( OldIrql < DISPATCH_LEVEL );
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
//
// Remove the RFCB from the connection's cache.
//
ASSERT( Rfcb->CachedOpen );
Rfcb->CachedOpen = FALSE;
Rfcb->OplockState = OplockStateNone;
RemoveEntryList( &Rfcb->CachedOpenListEntry );
connection->CachedOpenCount--;
ASSERT( (LONG)connection->CachedOpenCount >= 0 );
RELEASE_SPIN_LOCK( &connection->SpinLock, OldIrql );
IF_DEBUG(FILE_CACHE) KdPrint(( "; file %wZ\n", &Rfcb->Mfcb->FileName ));
//
// Unlink the RFCB from the LFCB. If this is the last RFCB for
// this LFCB, this will force the file closed even if there are
// still references to the RFCB. This will unblock blocked I/O.
//
UnlinkRfcbFromLfcb( Rfcb );
//
// Now acquire the FSD spin lock so that we can release the "open"
// reference to the Rfcb. DereferenceRfcbInternal releases the spin
// lock before returning.
//
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
DereferenceRfcbInternal( Rfcb, oldIrql );
INCREMENT_DEBUG_STAT( SrvDbgStatistics.RfcbInfo.Closes );
return;
} // SrvCloseCachedRfcb
VOID
SrvCloseCachedRfcbsOnConnection (
IN PCONNECTION Connection
)
/*++
Routine Description:
This routine closes all cached opens on a connection.
Arguments:
Connection - Address of connection for which cached opens are to be closed.
Return Value:
None.
--*/
{
PLIST_ENTRY listEntry;
PRFCB rfcb;
KIRQL OldIrql;
IF_DEBUG(FILE_CACHE) {
KdPrint(( "SrvCloseCachedRfcbsOnConnection called for connection %p\n", Connection ));
}
//
// Remove all RFCBs from the connection's open file cache.
//
// This routine needs to be protected from the situation where a Blocking Rename causes us to close all
// cached opens, but an Oplock break comes during that time and sees that Cached Open is still set to TRUE
// (Since we didn't hold the SpinLock during the operation)
ACQUIRE_SPIN_LOCK( &Connection->SpinLock, &OldIrql );
while ( IsListEmpty( &Connection->CachedOpenList ) == FALSE ) {
listEntry = RemoveHeadList( &Connection->CachedOpenList );
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
UpdateRfcbHistory( rfcb, 'nc$c' );
//
// Remove the RFCB from the connection's cache.
//
Connection->CachedOpenCount--;
ASSERT( rfcb->CachedOpen );
rfcb->CachedOpen = FALSE;
ASSERT( rfcb->OplockState == OplockStateOwnServerBatch );
rfcb->OplockState = OplockStateNone;
IF_DEBUG(FILE_CACHE) {
KdPrint(( "SrvCloseCachedRfcbsOnConnection; closing rfcb %p file %wZ\n",
rfcb, &rfcb->Mfcb->FileName ));
}
RELEASE_SPIN_LOCK( &Connection->SpinLock, OldIrql );
//
// Unlink the RFCB from the LFCB. If this is the last RFCB for
// this LFCB, this will force the file closed even if there are
// still references to the RFCB. This will unblock blocked I/O.
//
UnlinkRfcbFromLfcb( rfcb );
//
// Release the "open" reference to the Rfcb.
//
SrvDereferenceRfcb( rfcb );
INCREMENT_DEBUG_STAT( SrvDbgStatistics.RfcbInfo.Closes );
ACQUIRE_SPIN_LOCK( &Connection->SpinLock, &OldIrql );
}
RELEASE_SPIN_LOCK( &Connection->SpinLock, OldIrql );
return;
} // SrvCloseCachedRfcbsOnConnection
VOID
SrvCloseCachedRfcbsOnLfcb (
IN PLFCB Lfcb
)
/*++
Routine Description:
This routine closes all cached opens associated with a specific LFCB.
Arguments:
Lfcb - Address of LFCB for which cached opens are to be closed.
Return Value:
None.
--*/
{
PCONNECTION connection;
PLIST_ENTRY listEntry;
PLIST_ENTRY nextListEntry;
PRFCB rfcb;
KIRQL oldIrql;
LIST_ENTRY rfcbsToClose;
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
connection = Lfcb->Connection;
IF_DEBUG(FILE_CACHE) {
KdPrint(( "SrvCloseCachedRfcbsOnLfcb called for lfcb %p connection %p", Lfcb, connection ));
}
InitializeListHead( &rfcbsToClose );
//
// Lock and walk the connection's cached open list. We don't
// actually closed the RFCBs on the first pass, since that would
// require releasing the lock. Instead, we remove them from the
// connection list and add them to a local list.
//
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
for ( listEntry = connection->CachedOpenList.Flink;
listEntry != &connection->CachedOpenList;
listEntry = nextListEntry ) {
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
nextListEntry = listEntry->Flink;
if ( rfcb->Lfcb == Lfcb ) {
//
// Remove the RFCB from the connection's cache.
//
UpdateRfcbHistory( rfcb, 'fl$c' );
RemoveEntryList( listEntry );
connection->CachedOpenCount--;
InsertTailList( &rfcbsToClose, listEntry );
ASSERT( rfcb->CachedOpen );
rfcb->CachedOpen = FALSE;
ASSERT( rfcb->OplockState == OplockStateOwnServerBatch );
rfcb->OplockState = OplockStateNone;
}
}
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
//
// Walk the local list and close each RFCB.
//
for ( listEntry = rfcbsToClose.Flink;
listEntry != &rfcbsToClose;
listEntry = nextListEntry ) {
rfcb = CONTAINING_RECORD( listEntry, RFCB, CachedOpenListEntry );
nextListEntry = listEntry->Flink;
IF_DEBUG(FILE_CACHE) {
KdPrint(( "SrvCloseCachedRfcbsOnConnection; closing rfcb %p file %wZ\n",
rfcb, &rfcb->Mfcb->FileName ));
}
//
// Unlink the RFCB from the LFCB. If this is the last RFCB for
// this LFCB, this will force the file closed even if there are
// still references to the RFCB. This will unblock blocked I/O.
//
UnlinkRfcbFromLfcb( rfcb );
//
// Release the "open" reference to the Rfcb.
//
SrvDereferenceRfcb( rfcb );
INCREMENT_DEBUG_STAT( SrvDbgStatistics.RfcbInfo.Closes );
}
return;
} // SrvCloseCachedRfcbsOnLfcb
#ifdef SRVDBG_RFCBHIST
VOID
UpdateRfcbHistory (
IN PRFCB Rfcb,
IN ULONG Event
)
{
KIRQL oldIrql;
ACQUIRE_SPIN_LOCK( &Rfcb->SpinLock, &oldIrql );
Rfcb->History[Rfcb->HistoryIndex++] = Event;
RELEASE_SPIN_LOCK( &Rfcb->SpinLock, oldIrql );
return;
}
#endif