mirror of https://github.com/tongzx/nt5src
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
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
|
|
|