2639 lines
65 KiB
C
2639 lines
65 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
fcb.c
|
||
|
||
Abstract:
|
||
|
||
This module holds the FCB manipulation routines for the NT redirector
|
||
|
||
Author:
|
||
|
||
Larry Osterman (LarryO) 12-Jun-1990
|
||
|
||
Revision History:
|
||
|
||
12-Jun-1990 LarryO
|
||
|
||
Created
|
||
|
||
--*/
|
||
|
||
#define INCLUDE_SMB_SEARCH
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
KSPIN_LOCK
|
||
RdrFcbReferenceLock = {0};
|
||
|
||
KSPIN_LOCK
|
||
RdrFileSizeLock = {0};
|
||
|
||
ERESOURCE
|
||
RdrSharingCheckResource = {0};
|
||
|
||
|
||
ERESOURCE
|
||
RdrFileSizeResource = {0};
|
||
|
||
VOID
|
||
CompleteInvalidateFileId(
|
||
PVOID Ctx
|
||
);
|
||
|
||
DBGSTATIC
|
||
PFCB
|
||
FindFcb (
|
||
IN PUNICODE_STRING FileName,
|
||
IN PCONNECTLISTENTRY Connection OPTIONAL,
|
||
IN BOOLEAN DfsFile,
|
||
IN BOOLEAN OpenTargetDirectory
|
||
);
|
||
|
||
PFCB
|
||
RdrCreateFcb(
|
||
IN PUNICODE_STRING FileName,
|
||
IN PCONNECTLISTENTRY Connection OPTIONAL,
|
||
IN BOOLEAN OpenTargetDirectory,
|
||
IN BOOLEAN DfsFile,
|
||
IN PFILE_OBJECT FileObject,
|
||
IN USHORT ShareAccess,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN LockFcb
|
||
);
|
||
|
||
VOID
|
||
InvalidateFcb(
|
||
IN PFCB Fcb,
|
||
IN PVOID Ctx
|
||
);
|
||
|
||
VOID
|
||
RdrNullAcquireSize(
|
||
IN PFCB Fcb
|
||
);
|
||
|
||
VOID
|
||
RdrNullReleaseSize(
|
||
IN PFCB Fcb
|
||
);
|
||
|
||
VOID
|
||
RdrRealAcquireSize(
|
||
IN PFCB Fcb
|
||
);
|
||
|
||
VOID
|
||
RdrRealReleaseSize(
|
||
IN PFCB Fcb
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, RdrAllocateIcb)
|
||
#pragma alloc_text(PAGE, RdrFreeIcb)
|
||
#pragma alloc_text(PAGE, RdrAllocateFcb)
|
||
#pragma alloc_text(PAGE, FindFcb)
|
||
#pragma alloc_text(PAGE, RdrCreateFcb)
|
||
#pragma alloc_text(PAGE, RdrUnlinkAndFreeIcb)
|
||
#pragma alloc_text(PAGE, RdrIsOperationValid)
|
||
#pragma alloc_text(PAGE, RdrFastIoCheckIfPossible)
|
||
#pragma alloc_text(PAGE, CompleteInvalidateFileId)
|
||
#pragma alloc_text(PAGE, RdrInvalidateConnectionFiles)
|
||
#pragma alloc_text(PAGE, InvalidateFcb)
|
||
#pragma alloc_text(PAGE, RdrForeachFcbOnConnection)
|
||
#pragma alloc_text(PAGE, RdrForeachFcb)
|
||
#pragma alloc_text(PAGE, RdrAcquireFcbLock)
|
||
#pragma alloc_text(PAGE, RdrFindOplockedFcb)
|
||
#pragma alloc_text(PAGE, RdrCheckShareAccess)
|
||
#pragma alloc_text(PAGE, RdrRemoveShareAccess)
|
||
#pragma alloc_text(INIT, RdrpInitializeFcb)
|
||
#pragma alloc_text(PAGE, RdrpUninitializeFcb)
|
||
#pragma alloc_text(PAGE, RdrNullAcquireSize)
|
||
#pragma alloc_text(PAGE, RdrNullReleaseSize)
|
||
#pragma alloc_text(PAGE, RdrRealAcquireSize)
|
||
#pragma alloc_text(PAGE, RdrRealReleaseSize)
|
||
|
||
#pragma alloc_text(PAGE3FILE, RdrReferenceFcb)
|
||
#pragma alloc_text(PAGE3FILE, RdrDereferenceFcb)
|
||
#pragma alloc_text(PAGE3FILE, RdrInvalidateFileId)
|
||
|
||
#endif
|
||
|
||
|
||
|
||
#ifndef RDRDBG_FCBREF
|
||
#define GET_CALLERS_ADDRESS(caller,callerscaller) NOTHING
|
||
#define UPDATE_REFERENCE_HISTORY(history,isdereference,caller,callerscaller) NOTHING
|
||
#define INITIALIZE_REFERENCE_HISTORY(history,initialcount) NOTHING
|
||
#define TERMINATE_REFERENCE_HISTORY(history) NOTHING
|
||
#else
|
||
KSPIN_LOCK ReferenceHistorySpinLock = 0;
|
||
BOOLEAN ReferenceHistoryInitialized = FALSE;
|
||
|
||
#define GET_CALLERS_ADDRESS(caller,callerscaller) \
|
||
RtlGetCallersAddress((caller),(callerscaller))
|
||
|
||
#define UPDATE_REFERENCE_HISTORY(history,isdereference,caller,callerscaller) \
|
||
RdrUpdateReferenceHistory( \
|
||
(history), \
|
||
(caller), \
|
||
(callerscaller), \
|
||
(isdereference) \
|
||
);
|
||
|
||
#define INITIALIZE_REFERENCE_HISTORY(history,initialcount) \
|
||
RdrInitializeReferenceHistory((history),(initialcount))
|
||
|
||
#define TERMINATE_REFERENCE_HISTORY(history) \
|
||
RdrTerminateReferenceHistory(history)
|
||
|
||
VOID
|
||
RdrUpdateReferenceHistory (
|
||
IN PREFERENCE_HISTORY ReferenceHistory,
|
||
IN PVOID Caller,
|
||
IN PVOID CallersCaller,
|
||
IN BOOLEAN IsDereference
|
||
)
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
ACQUIRE_SPIN_LOCK( &ReferenceHistorySpinLock, &oldIrql );
|
||
|
||
if ( IsDereference ) {
|
||
ReferenceHistory->TotalDereferences++;
|
||
} else {
|
||
ReferenceHistory->TotalReferences++;
|
||
}
|
||
|
||
if ( ReferenceHistory->HistoryTable != NULL ) {
|
||
|
||
PREFERENCE_HISTORY_ENTRY entry;
|
||
PREFERENCE_HISTORY_ENTRY priorEntry;
|
||
|
||
entry = &ReferenceHistory->HistoryTable[ ReferenceHistory->NextEntry ];
|
||
|
||
if ( ReferenceHistory->NextEntry == 0 ) {
|
||
priorEntry =
|
||
&ReferenceHistory->HistoryTable[ REFERENCE_HISTORY_LENGTH-1 ];
|
||
} else {
|
||
priorEntry =
|
||
&ReferenceHistory->HistoryTable[ ReferenceHistory->NextEntry-1 ];
|
||
}
|
||
|
||
entry->Caller = Caller;
|
||
entry->CallersCaller = CallersCaller;
|
||
|
||
if ( IsDereference ) {
|
||
entry->NewReferenceCount = priorEntry->NewReferenceCount - 1;
|
||
entry->IsDereference = (ULONG)TRUE;
|
||
} else {
|
||
entry->NewReferenceCount = priorEntry->NewReferenceCount + 1;
|
||
entry->IsDereference = (ULONG)FALSE;
|
||
}
|
||
|
||
ReferenceHistory->NextEntry++;
|
||
|
||
if ( ReferenceHistory->NextEntry >= REFERENCE_HISTORY_LENGTH ) {
|
||
ReferenceHistory->NextEntry = 0;
|
||
}
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK( &ReferenceHistorySpinLock, oldIrql );
|
||
|
||
} // RdrUpdateReferenceHistory
|
||
|
||
VOID
|
||
RdrInitializeReferenceHistory (
|
||
IN PREFERENCE_HISTORY ReferenceHistory,
|
||
IN ULONG InitialReferenceCount
|
||
)
|
||
|
||
{
|
||
PVOID caller, callersCaller;
|
||
|
||
ULONG historyTableSize = sizeof(REFERENCE_HISTORY_ENTRY) *
|
||
REFERENCE_HISTORY_LENGTH;
|
||
|
||
if ( !ReferenceHistoryInitialized ) {
|
||
KeInitializeSpinLock( &ReferenceHistorySpinLock );
|
||
ReferenceHistoryInitialized = TRUE;
|
||
}
|
||
|
||
ReferenceHistory->HistoryTable = ALLOCATE_POOL(
|
||
NonPagedPool,
|
||
historyTableSize,
|
||
POOL_REFERENCE_HISTORY
|
||
);
|
||
//
|
||
// It we weren't able to allocate the memory, don't track references
|
||
// and dereferences.
|
||
//
|
||
|
||
if ( ReferenceHistory->HistoryTable == NULL ) {
|
||
ReferenceHistory->NextEntry = (ULONG)-1;
|
||
} else {
|
||
ReferenceHistory->NextEntry = 0;
|
||
RtlZeroMemory( ReferenceHistory->HistoryTable, historyTableSize );
|
||
}
|
||
|
||
ReferenceHistory->TotalReferences = InitialReferenceCount;
|
||
ReferenceHistory->TotalDereferences = 0;
|
||
|
||
//
|
||
// Account for the initial reference(s).
|
||
//
|
||
|
||
RtlGetCallersAddress( &caller, &callersCaller );
|
||
|
||
while ( InitialReferenceCount-- > 0 ) {
|
||
RdrUpdateReferenceHistory( ReferenceHistory, caller, callersCaller, FALSE );
|
||
}
|
||
|
||
return;
|
||
|
||
} // RdrInitializeReferenceHistory
|
||
|
||
|
||
VOID
|
||
RdrTerminateReferenceHistory (
|
||
IN PREFERENCE_HISTORY ReferenceHistory
|
||
)
|
||
|
||
{
|
||
if ( ReferenceHistory->HistoryTable != NULL ) {
|
||
FREE_POOL( ReferenceHistory->HistoryTable );
|
||
}
|
||
|
||
return;
|
||
|
||
} // RdrTerminateReferenceHistory
|
||
#endif // def RDRDBG_FCBREF
|
||
|
||
PICB
|
||
RdrAllocateIcb (
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates an ICB and associates it with the given file object
|
||
|
||
Arguments:
|
||
|
||
IN PFILE_OBJECT FileObject - Supplies the file object this FCB is to be
|
||
associated with
|
||
|
||
Return Value:
|
||
|
||
PFCB - Pointer to new FCB, or NULL if allocation failed.
|
||
|
||
--*/
|
||
|
||
{
|
||
PICB NewIcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
NewIcb = ALLOCATE_POOL (NonPagedPool, sizeof(ICB), POOL_ICB);
|
||
|
||
if (NewIcb==NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
NewIcb->Signature = STRUCTURE_SIGNATURE_ICB;
|
||
|
||
NewIcb->Type = Unknown;
|
||
NewIcb->Fcb = NULL;
|
||
NewIcb->u.d.Scb = NULL;
|
||
NewIcb->Se = NULL;
|
||
NewIcb->Flags = 0; // Initialize flags to 0.
|
||
// NewIcb->ClosersThread = 0; // There is no thread closing the file.
|
||
NewIcb->u.f.Flags = 0; // Set file specific flags to 0.
|
||
NewIcb->u.f.FileObject = NULL;
|
||
NewIcb->u.f.CcReadAhead = TRUE;
|
||
NewIcb->u.f.CcReliable = TRUE;
|
||
NewIcb->DeletePending = FALSE;
|
||
|
||
RtlInitUnicodeString(&NewIcb->DeviceName, NULL);
|
||
|
||
//
|
||
// Indicate that the ICB hasn't been linked into an FCB yet.
|
||
//
|
||
|
||
NewIcb->InstanceNext.Flink = NULL;
|
||
NewIcb->InstanceNext.Blink = NULL;
|
||
|
||
FileObject->FsContext2 = NewIcb; // Link ICB back into file object.
|
||
|
||
return NewIcb;
|
||
}
|
||
|
||
VOID
|
||
RdrFreeIcb (
|
||
IN PICB Icb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees an allocated ICB.
|
||
|
||
Arguments:
|
||
|
||
IN PICB ICB - Supplies a pointer to the ICB to free
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb = Icb->Fcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_FCB, ("RdrFreeICB: Icb: %08lx, Fcb: %08lx\n", Icb, Fcb));
|
||
|
||
#if DBG
|
||
//
|
||
// Guarantee that someone owns the FCB resource.
|
||
//
|
||
// DANGER: THIS RELIES ON THE STRUCTURE OF A RESOURCE!
|
||
//
|
||
|
||
if (Fcb != NULL) {
|
||
ASSERT (Fcb->Header.Resource->ActiveCount != 0);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If the file has an FCB associated with it, free up it's storage.
|
||
//
|
||
|
||
if (Fcb != NULL) {
|
||
|
||
if (Icb->InstanceNext.Flink != NULL) {
|
||
ASSERT (Icb->InstanceNext.Blink != NULL);
|
||
|
||
//
|
||
// Remove this ICB from the FCB's ICB chain
|
||
//
|
||
|
||
ExAcquireResourceExclusive(&Fcb->NonPagedFcb->InstanceChainLock, TRUE);
|
||
RemoveEntryList(&Icb->InstanceNext); // Remove ICB from FCB list.
|
||
ExReleaseResource(&Fcb->NonPagedFcb->InstanceChainLock);
|
||
#if DBG
|
||
} else {
|
||
ASSERT (Icb->InstanceNext.Blink == NULL);
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// If we get an error such as a sharing violation, then the file
|
||
// will not have been opened, and thus structures like the backpack
|
||
// and buffering pool have not been initialized yet.
|
||
//
|
||
|
||
if (Icb->Flags & ICB_OPENED) {
|
||
ASSERT (Icb->Type == Directory ? Icb->u.d.Scb == NULL : TRUE);
|
||
|
||
//
|
||
// If this ICB has a handle on the remote end, then it also has a
|
||
// lock structure hanging off it. Free it up.
|
||
//
|
||
|
||
if (Icb->Type == DiskFile) {
|
||
|
||
ASSERT(Icb->u.f.LockHead.Signature == STRUCTURE_SIGNATURE_LOCKHEAD);
|
||
|
||
RdrUninitializeLockHead(&Icb->u.f.LockHead);
|
||
|
||
RdrUninitializeWriteBufferHead(&Icb->u.f.WriteBufferHead);
|
||
}
|
||
|
||
//
|
||
// Fcb will be non-null for all Icb's that are FileTypeByteModePipe
|
||
// and have read ahead/write behind buffers allocated. These
|
||
// buffers will be null if we tried to allocate but got
|
||
// INSUFFICIENT_RESOURCES
|
||
//
|
||
|
||
if ( Fcb->NonPagedFcb->FileType == FileTypeByteModePipe ) {
|
||
|
||
ASSERT(Icb->Type == NamedPipe);
|
||
ASSERT(Icb->NonPagedFcb->Type == NamedPipe);
|
||
|
||
if ( Icb->u.p.ReadData.Buffer != NULL ) {
|
||
// This deallocates both buffers
|
||
FREE_POOL(Icb->u.p.ReadData.Buffer);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// if (Icb->DeviceName.Buffer != NULL) {
|
||
// FREE_POOL(Icb->DeviceName.Buffer);
|
||
// }
|
||
|
||
|
||
FREE_POOL(Icb);
|
||
|
||
}
|
||
|
||
PFCB
|
||
RdrAllocateFcb (
|
||
IN PICB Icb,
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PUNICODE_STRING FileName,
|
||
IN PUNICODE_STRING BaseFileName OPTIONAL,
|
||
IN USHORT ShareAccess,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN OpenTargetDirectory,
|
||
IN BOOLEAN DfsFile,
|
||
IN PCONNECTLISTENTRY Connection,
|
||
OUT PBOOLEAN FcbWasCreated,
|
||
OUT PBOOLEAN BaseFcbWasCreated OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates an FCB to be associated with the ICB specified.
|
||
If an FCB already exists with the same name, it will be re-used.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies ICB to associate the FCB with.
|
||
IN PFILE_OBJECT FileObject OPTIONAL - File object to link fcb into.
|
||
IN PUNICODE_STRING FileName - Supplies the file name for the FCB.
|
||
IN BOOLEAN OpenTargetDirectory - True if we are opening the directory of FileName.
|
||
OUT PBOOLEAN FcbWasCreated - Set to TRUE if this is a newly allocated FCB.
|
||
|
||
Return Value:
|
||
|
||
PFCB - Pointer to newly allocated (or re-referenced) FCB.
|
||
|
||
|
||
Note:
|
||
This routine returns with the FCB creation lock and the FCB lock held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb;
|
||
PNONPAGED_FCB NonPagedFcb;
|
||
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!NT_SUCCESS(KeWaitForMutexObject(&RdrDatabaseMutex,
|
||
Executive, KernelMode, FALSE, NULL))) {
|
||
InternalError(("Unable to claim FCB mutex in RdrAllocateFCB"));
|
||
return NULL;
|
||
}
|
||
|
||
dprintf(DPRT_FCB, ("RdrAllocateFcb: %wZ, Icb: %08lx\n", FileName, Icb));
|
||
|
||
*FcbWasCreated = FALSE;
|
||
|
||
//
|
||
// Find a new FCB to match the name passed in.
|
||
//
|
||
|
||
Fcb = FindFcb(FileName, Connection, DfsFile, OpenTargetDirectory);
|
||
|
||
if (Fcb == NULL) {
|
||
|
||
//
|
||
// No FCB could be found that matches this name, so create a new
|
||
// FCB.
|
||
//
|
||
|
||
Fcb = RdrCreateFcb(FileName,
|
||
Connection,
|
||
OpenTargetDirectory,
|
||
DfsFile,
|
||
FileObject,
|
||
ShareAccess,
|
||
DesiredAccess,
|
||
TRUE);
|
||
|
||
if (Fcb == NULL) {
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
NonPagedFcb = Fcb->NonPagedFcb;
|
||
|
||
if (ARGUMENT_PRESENT(BaseFileName) && (BaseFileName->Length != 0)) {
|
||
//
|
||
// If there is a base file name specified, look up that FCB.
|
||
//
|
||
|
||
NonPagedFcb->SharingCheckFcb = FindFcb(BaseFileName, Connection, DfsFile, FALSE);
|
||
|
||
//
|
||
// If there is no sharing check FCB, allocate a new FCB for
|
||
// the base file name.
|
||
//
|
||
|
||
if (NonPagedFcb->SharingCheckFcb == NULL) {
|
||
UNICODE_STRING BaseName;
|
||
|
||
if (NT_SUCCESS(RdrpDuplicateUnicodeStringWithString(&BaseName,
|
||
BaseFileName, PagedPool, FALSE))) {
|
||
|
||
NonPagedFcb->SharingCheckFcb = RdrCreateFcb(&BaseName,
|
||
Connection,
|
||
FALSE,
|
||
DfsFile,
|
||
FileObject,
|
||
ShareAccess,
|
||
DesiredAccess,
|
||
FALSE);
|
||
|
||
if (ARGUMENT_PRESENT(BaseFcbWasCreated)) {
|
||
*BaseFcbWasCreated = TRUE;
|
||
}
|
||
if (NonPagedFcb->SharingCheckFcb == NULL) {
|
||
if (BaseName.Buffer != NULL) {
|
||
FREE_POOL(BaseName.Buffer);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (NonPagedFcb->SharingCheckFcb == NULL) {
|
||
ExReleaseResource(Fcb->Header.Resource);
|
||
RdrDereferenceFcb(NULL, Fcb->NonPagedFcb, FALSE, 0, NULL);
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
} else {
|
||
|
||
if (ARGUMENT_PRESENT(BaseFcbWasCreated)) {
|
||
*BaseFcbWasCreated = FALSE;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
NonPagedFcb->SharingCheckFcb = NULL;
|
||
}
|
||
|
||
ASSERT (NonPagedFcb->SharingCheckFcb != Fcb);
|
||
|
||
*FcbWasCreated = TRUE;
|
||
|
||
dprintf(DPRT_FCB, ("Create New fcb: %08lx\n", Fcb));
|
||
|
||
}
|
||
|
||
//
|
||
// We unilaterally dereference the connection now, since we don't need
|
||
// the reference applied in RdrDetermineFileConnection().
|
||
//
|
||
// If we hooked this FCB to the CLE, it was referenced in RdrCreateFcb.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Connection)) {
|
||
ASSERT (Fcb->Connection == Connection);
|
||
|
||
//
|
||
// Dereference the connection we just established.
|
||
//
|
||
// We can do this because the FCB already references the
|
||
// connection.
|
||
//
|
||
|
||
RdrDereferenceConnection(NULL, Connection, NULL, FALSE);
|
||
}
|
||
|
||
//
|
||
// Link the FCB into the file object.
|
||
//
|
||
|
||
FileObject->FsContext = Fcb;
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
if (!*FcbWasCreated) {
|
||
|
||
//
|
||
// This thread had better NOT own this resource.
|
||
//
|
||
|
||
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
|
||
|
||
//
|
||
// Wait for any open operations active on the file to complete
|
||
// before attempting to acquire the FCB resource. This is necessary
|
||
// because we might be releasing the FCB resource in the middle
|
||
// of the open operation, and we don't want another thread
|
||
// coming in to use this file while we are in the process of opening
|
||
// it.
|
||
//
|
||
|
||
Status = KeWaitForSingleObject(&Fcb->NonPagedFcb->CreateComplete, Executive,
|
||
KernelMode, FALSE, NULL);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Wait for any asynchronous operations outstanding on this
|
||
// file to complete before allowing the open to continue.
|
||
//
|
||
|
||
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
|
||
}
|
||
|
||
//
|
||
// Stick this instance onto the tail of the FCB's instance
|
||
// chain.
|
||
//
|
||
|
||
ExAcquireResourceExclusive(&Fcb->NonPagedFcb->InstanceChainLock, TRUE);
|
||
InsertTailList (&Fcb->InstanceChain, &Icb->InstanceNext);
|
||
ExReleaseResource(&Fcb->NonPagedFcb->InstanceChainLock);
|
||
|
||
return Fcb;
|
||
|
||
}
|
||
|
||
PFCB
|
||
RdrCreateFcb(
|
||
IN PUNICODE_STRING FileName,
|
||
IN PCONNECTLISTENTRY Connection OPTIONAL,
|
||
IN BOOLEAN OpenTargetDirectory,
|
||
IN BOOLEAN DfsFile,
|
||
IN PFILE_OBJECT FileObject,
|
||
IN USHORT ShareAccess,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN LockFcb
|
||
)
|
||
{
|
||
PFCB Fcb;
|
||
PNONPAGED_FCB NonPagedFcb;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
NonPagedFcb = ALLOCATE_POOL(NonPagedPool, sizeof(NONPAGED_FCB), POOL_NONPAGED_FCB);
|
||
|
||
if (NonPagedFcb == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
Fcb = ALLOCATE_POOL(PagedPool, sizeof(FCB), POOL_FCB);
|
||
|
||
if (Fcb == NULL) {
|
||
FREE_POOL(NonPagedFcb);
|
||
return NULL;
|
||
}
|
||
|
||
RtlZeroMemory( Fcb, sizeof( FCB ) );
|
||
RtlZeroMemory( NonPagedFcb, sizeof( NONPAGED_FCB ) );
|
||
|
||
Fcb->Header.NodeTypeCode = STRUCTURE_SIGNATURE_FCB;
|
||
Fcb->Header.NodeByteSize = sizeof(FCB);
|
||
|
||
NonPagedFcb->Signature = STRUCTURE_SIGNATURE_FCB;
|
||
NonPagedFcb->Size = sizeof(NONPAGED_FCB);
|
||
|
||
Fcb->NonPagedFcb = NonPagedFcb;
|
||
NonPagedFcb->PagedFcb = Fcb;
|
||
|
||
if (FileName->Length==0) {
|
||
Fcb->FileName = *FileName;
|
||
} else {
|
||
UNICODE_STRING PathString;
|
||
UNICODE_STRING TargetFileName;
|
||
|
||
Status = RdrExtractPathAndFileName(FileName, &PathString, &TargetFileName);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
FREE_POOL(Fcb);
|
||
FREE_POOL(NonPagedFcb);
|
||
return NULL;
|
||
}
|
||
|
||
if (OpenTargetDirectory) {
|
||
|
||
//
|
||
// We're opening the directory of the file specified. In that
|
||
// case, the filename is everything BUT the last part of the
|
||
// path.
|
||
//
|
||
|
||
Fcb->FileName = PathString;
|
||
|
||
Status = RdrExtractPathAndFileName(&PathString, &PathString, &TargetFileName);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
FREE_POOL(Fcb);
|
||
FREE_POOL(NonPagedFcb);
|
||
return NULL;
|
||
}
|
||
|
||
Fcb->LastFileName = TargetFileName;
|
||
|
||
} else {
|
||
|
||
Fcb->FileName = *FileName;
|
||
|
||
Fcb->LastFileName = TargetFileName;
|
||
}
|
||
}
|
||
|
||
NonPagedFcb->Type = Unknown;
|
||
|
||
INITIALIZE_REFERENCE_HISTORY(&NonPagedFcb->ReferenceHistory, 1);
|
||
|
||
NonPagedFcb->RefCount = 1;
|
||
|
||
if (DfsFile) {
|
||
NonPagedFcb->Flags = FCB_DFSFILE;
|
||
}
|
||
|
||
//
|
||
// Initialize file type.
|
||
//
|
||
|
||
NonPagedFcb->FileType = FileTypeUnknown;
|
||
|
||
//
|
||
// Assume the open fails.
|
||
//
|
||
|
||
Fcb->OpenError = STATUS_UNSUCCESSFUL;
|
||
|
||
//
|
||
// Initialize the file attribute to "NORMAL".
|
||
//
|
||
|
||
Fcb->Attribute = FILE_ATTRIBUTE_NORMAL;
|
||
|
||
//
|
||
// Initialize the number of write behind pages assuming the transport
|
||
// runs at full ethernet bandwidth. This works out to be about
|
||
// 30M of write behind data allowed.
|
||
//
|
||
|
||
Fcb->WriteBehindPages = (ETHERNET_BANDWIDTH*WRITE_BEHIND_AMOUNT_TIME) / PAGE_SIZE;
|
||
|
||
Fcb->AcquireSizeRoutine = RdrNullAcquireSize;
|
||
|
||
Fcb->ReleaseSizeRoutine = RdrNullReleaseSize;
|
||
|
||
//
|
||
// Initialize the resource that protects the contents of the FCB.
|
||
//
|
||
|
||
Fcb->Header.Resource = ALLOCATE_POOL(NonPagedPool, sizeof(ERESOURCE), POOL_FCBLOCK);
|
||
|
||
if (Fcb->Header.Resource == NULL) {
|
||
|
||
TERMINATE_REFERENCE_HISTORY(&NonPagedFcb->ReferenceHistory);
|
||
FREE_POOL( Fcb );
|
||
FREE_POOL(NonPagedFcb);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
ExInitializeResource(Fcb->Header.Resource);
|
||
|
||
//
|
||
// Initialize the resource that protects the contents of the FCB.
|
||
//
|
||
|
||
Fcb->Header.PagingIoResource = ALLOCATE_POOL(NonPagedPool, sizeof(ERESOURCE), POOL_FCBPAGINGLOCK);
|
||
|
||
if (Fcb->Header.PagingIoResource == NULL) {
|
||
|
||
ExDeleteResource( Fcb->Header.Resource );
|
||
FREE_POOL( Fcb->Header.Resource );
|
||
|
||
TERMINATE_REFERENCE_HISTORY(&NonPagedFcb->ReferenceHistory);
|
||
FREE_POOL( Fcb );
|
||
FREE_POOL(NonPagedFcb);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
ExInitializeResource(Fcb->Header.PagingIoResource);
|
||
|
||
ExInitializeResource(&NonPagedFcb->InstanceChainLock);
|
||
|
||
//
|
||
// Initialize the event synchronizing opens.
|
||
//
|
||
|
||
KeInitializeEvent(&NonPagedFcb->CreateComplete, SynchronizationEvent, TRUE);
|
||
|
||
//
|
||
// Initialize the event protecting purge cache operations.
|
||
//
|
||
|
||
KeInitializeEvent(&NonPagedFcb->PurgeCacheSynchronizer, NotificationEvent, TRUE);
|
||
|
||
Fcb->Header.NodeByteSize = sizeof(FCB);
|
||
|
||
Fcb->Header.IsFastIoPossible = FastIoIsQuestionable;
|
||
|
||
FsRtlInitializeFileLock(&Fcb->FileLock,
|
||
RdrLockOperationCompletion, RdrUnlockOperation);
|
||
|
||
dprintf(DPRT_CREATE, ("Setting share access for file object %08lx, Fcb = %08lx, ShareAccess = %08lx\n", FileObject, Fcb, &Fcb->ShareAccess));
|
||
|
||
IoSetShareAccess(DesiredAccess, ShareAccess, FileObject,
|
||
&Fcb->ShareAccess);
|
||
|
||
//
|
||
// We know we're going to return this FCB. Stick it on the global
|
||
// and per-connection FCB chain.
|
||
//
|
||
// If requested, lock the FCB before making it visible.
|
||
//
|
||
|
||
if (LockFcb) {
|
||
ExAcquireResourceExclusive(Fcb->Header.Resource, TRUE);
|
||
}
|
||
|
||
InsertTailList (&RdrFcbHead, &Fcb->GlobalNext);
|
||
|
||
InitializeListHead (&Fcb->InstanceChain);
|
||
|
||
if (ARGUMENT_PRESENT(Connection)) {
|
||
Fcb->Connection = Connection;
|
||
|
||
InsertTailList(&Connection->FcbChain, &Fcb->ConnectNext);
|
||
|
||
//
|
||
// Reference the connection, since we hooked the FCB into the chain.
|
||
//
|
||
|
||
RdrReferenceConnection(Connection);
|
||
|
||
}
|
||
|
||
return Fcb;
|
||
}
|
||
|
||
DBGSTATIC
|
||
PFCB
|
||
FindFcb (
|
||
IN PUNICODE_STRING FileName,
|
||
IN PCONNECTLISTENTRY Connection OPTIONAL,
|
||
IN BOOLEAN DfsFile,
|
||
IN BOOLEAN OpenTargetDirectory
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds an existing FCB whose name matches the supplied
|
||
filename.
|
||
|
||
Arguments:
|
||
|
||
IN PUNICODE_STRING FileName - Supplies the name of the file to look up.
|
||
|
||
Return Value:
|
||
|
||
PFCB - FCB whose name matches the supplied name, or none.
|
||
|
||
Note:
|
||
|
||
This code assumes that the FCB mutex is owned.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY FcbEntry;
|
||
PFCB Fcb;
|
||
UNICODE_STRING NameToCheck;
|
||
UNICODE_STRING TargetFileName;
|
||
|
||
ULONG DfsFlagToCheck;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (OpenTargetDirectory) {
|
||
RdrExtractPathAndFileName(FileName, &NameToCheck, &TargetFileName);
|
||
} else {
|
||
NameToCheck = *FileName;
|
||
}
|
||
|
||
DfsFlagToCheck = DfsFile ? FCB_DFSFILE : 0;
|
||
|
||
if (Connection == NULL) {
|
||
for (FcbEntry = RdrFcbHead.Flink
|
||
;
|
||
FcbEntry != &RdrFcbHead
|
||
;
|
||
FcbEntry = FcbEntry->Flink) {
|
||
Fcb = CONTAINING_RECORD(FcbEntry, FCB, GlobalNext);
|
||
|
||
dprintf(DPRT_FCB, ("Compare %wZ with %wZ\n", FileName, &Fcb->FileName));
|
||
|
||
if ((Fcb->NonPagedFcb->Flags & FCB_DFSFILE) != DfsFlagToCheck) {
|
||
continue;
|
||
}
|
||
|
||
if (RtlEqualUnicodeString(&Fcb->FileName, &NameToCheck, TRUE)) {
|
||
|
||
//
|
||
// This thread had better NOT own this FCB.
|
||
//
|
||
|
||
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
|
||
|
||
//
|
||
// Now apply a new reference to this FCB.
|
||
//
|
||
|
||
RdrReferenceFcb(Fcb->NonPagedFcb);
|
||
|
||
return Fcb;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Only look at the FCB's on the connection, as opposed to all the
|
||
// FCB's in the system.
|
||
//
|
||
|
||
for (FcbEntry = Connection->FcbChain.Flink
|
||
;
|
||
FcbEntry != &Connection->FcbChain
|
||
;
|
||
FcbEntry = FcbEntry->Flink) {
|
||
Fcb = CONTAINING_RECORD(FcbEntry, FCB, ConnectNext);
|
||
|
||
dprintf(DPRT_FCB, ("Compare %wZ with %wZ\n", FileName, &Fcb->FileName));
|
||
|
||
if ((Fcb->NonPagedFcb->Flags & FCB_DFSFILE) != DfsFlagToCheck) {
|
||
continue;
|
||
}
|
||
|
||
if (RtlEqualUnicodeString(&Fcb->FileName, &NameToCheck, TRUE)) {
|
||
|
||
//
|
||
// This thread had better NOT own this FCB.
|
||
//
|
||
|
||
ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
|
||
|
||
//
|
||
// Now apply a new reference to this FCB.
|
||
//
|
||
|
||
RdrReferenceFcb(Fcb->NonPagedFcb);
|
||
|
||
return Fcb;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
BOOLEAN
|
||
RdrDereferenceFcb (
|
||
IN PIRP Irp OPTIONAL,
|
||
IN PNONPAGED_FCB NonPagedFcb,
|
||
IN BOOLEAN FcbLocked,
|
||
IN ERESOURCE_THREAD DereferencingThread OPTIONAL,
|
||
IN PSECURITY_ENTRY Se OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will delete a reference to an FCB. If the FCB's reference
|
||
count goes to zero, it will release the storage allocated to the FCB.
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies an instance of the file to remove.
|
||
IN PFCB Fcb - Supplies the FCB to dereference.
|
||
IN BOOLEAN FcbLocked - True if FCB is locked currently.
|
||
IN ERESOURCE_THREAD DereferencingThread - Supplies the thread to use
|
||
when releasing the fcb lock if appropriate.
|
||
IN PSECURITY ENTRY Se - Supplies Se to be used on a tree disconnect.
|
||
IN BOOLEAN ForcablyRemoveConnection - True iff connection should not be
|
||
allowed to go dormant.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if FCB was deleted.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PFCB Fcb = NonPagedFcb->PagedFcb;
|
||
KIRQL OldIrql;
|
||
#ifdef RDRDBG_FCBREF
|
||
PVOID Caller, CallersCaller;
|
||
#endif
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
dprintf(DPRT_FCB, ("DereferenceFCB, FCB: %08lx\n", Fcb));
|
||
|
||
//
|
||
// If we're not going to delete the FCB, and we don't own the FCB
|
||
// lock, early out by dereferencing the FCB while we hold the spin
|
||
// lock. Since we're not going to modify any of the database
|
||
// chains, we don't need to hold the database mutex, just the FCB
|
||
// reference count spin lock.
|
||
//
|
||
// We can't take the fast path if we own the FCB lock, because
|
||
// if we did we'd end up touching the FCB after decrementing the
|
||
// reference count and releasing the lock, which would mean that
|
||
// some other thread could interrupt us and decrement the count
|
||
// to 0 and free the FCB.
|
||
//
|
||
|
||
GET_CALLERS_ADDRESS(&Caller, &CallersCaller);
|
||
|
||
if ( !FcbLocked && (NonPagedFcb->RefCount > 1) ) {
|
||
|
||
ACQUIRE_SPIN_LOCK(&RdrFcbReferenceLock, &OldIrql);
|
||
|
||
ASSERT((LONG)NonPagedFcb->RefCount > 0);
|
||
|
||
if (NonPagedFcb->RefCount > 1) {
|
||
|
||
UPDATE_REFERENCE_HISTORY(&NonPagedFcb->ReferenceHistory, TRUE, Caller, CallersCaller);
|
||
NonPagedFcb->RefCount -= 1;
|
||
|
||
ASSERT (NonPagedFcb->RefCount > 0);
|
||
|
||
RELEASE_SPIN_LOCK(&RdrFcbReferenceLock, OldIrql);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&RdrFcbReferenceLock, OldIrql);
|
||
}
|
||
|
||
ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
|
||
|
||
//
|
||
// The odds are very high that this dereference will delete the FCB.
|
||
//
|
||
// Take the database mutex to protect the FCB chains, and dereference
|
||
// the FCB.
|
||
//
|
||
|
||
Status = KeWaitForMutexObject(&RdrDatabaseMutex,
|
||
Executive, KernelMode, FALSE, NULL);
|
||
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
#if DBG
|
||
//
|
||
// Guarantee that the right thread owns the FCB resource.
|
||
//
|
||
// DANGER: THIS RELIES ON THE STRUCTURE OF A RESOURCE!
|
||
//
|
||
|
||
if (FcbLocked) {
|
||
ASSERT (Fcb->Header.Resource->ActiveCount != 0);
|
||
|
||
if (DereferencingThread == 0) {
|
||
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource) ||
|
||
ExIsResourceAcquiredShared(Fcb->Header.Resource));
|
||
// } else {
|
||
// ASSERT (Fcb->Header.Resource->Threads[0] == DereferencingThread);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
ACQUIRE_SPIN_LOCK(&RdrFcbReferenceLock, &OldIrql);
|
||
|
||
ASSERT(NonPagedFcb->RefCount > 0);
|
||
|
||
UPDATE_REFERENCE_HISTORY(&NonPagedFcb->ReferenceHistory, TRUE, Caller, CallersCaller);
|
||
NonPagedFcb->RefCount -= 1;
|
||
|
||
if (NonPagedFcb->RefCount == 0) {
|
||
|
||
RELEASE_SPIN_LOCK(&RdrFcbReferenceLock, OldIrql);
|
||
|
||
ASSERT (Fcb->NumberOfOpens == 0);
|
||
|
||
//
|
||
// The FCB's reference count just went to 0, free it up.
|
||
//
|
||
|
||
dprintf(DPRT_FCB, ("Free FCB %08lx\n", Fcb));
|
||
|
||
#if DBG
|
||
ASSERT(IsListEmpty(&Fcb->InstanceChain));
|
||
#endif
|
||
|
||
if (Fcb->FileName.Buffer != NULL) {
|
||
FREE_POOL(Fcb->FileName.Buffer);
|
||
}
|
||
|
||
RemoveEntryList(&Fcb->GlobalNext);
|
||
|
||
if (Fcb->ConnectNext.Flink) {
|
||
|
||
//
|
||
// Remove the FCB from the connectlist FCB chain.
|
||
//
|
||
|
||
RemoveEntryList(&Fcb->ConnectNext);
|
||
|
||
//
|
||
// Now the FCB database mutex can safely be released. Note
|
||
// that we must hold the mutex until after we remove the
|
||
// FCB from the connection list, otherwise a thread walking
|
||
// that list (e.g., RdrInvalidateConnectionFiles) could
|
||
// find the FCB and reference it, even though we're about
|
||
// to delete it.
|
||
//
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
//
|
||
// Close this reference to the connectlist.
|
||
//
|
||
|
||
RdrDereferenceConnection(Irp, Fcb->Connection, Se, FALSE);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Release the FCB database mutex.
|
||
//
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
}
|
||
|
||
//
|
||
// If the FCB has a sharing check FCB associated with it,
|
||
// then dereference that FCB.
|
||
//
|
||
|
||
ASSERT (NonPagedFcb->SharingCheckFcb != Fcb);
|
||
|
||
if (NonPagedFcb->SharingCheckFcb != NULL) {
|
||
RdrDereferenceFcb(Irp, NonPagedFcb->SharingCheckFcb->NonPagedFcb, FALSE, DereferencingThread, Se);
|
||
|
||
NonPagedFcb->SharingCheckFcb = NULL;
|
||
}
|
||
|
||
|
||
if (NonPagedFcb->OplockedSecurityEntry != NULL) {
|
||
RdrDereferenceSecurityEntry(NonPagedFcb->OplockedSecurityEntry);
|
||
|
||
NonPagedFcb->OplockedSecurityEntry = NULL;
|
||
}
|
||
|
||
//
|
||
// Let the FSRTL library know that the file containing the locks
|
||
// is going away.
|
||
//
|
||
|
||
FsRtlUninitializeFileLock(&Fcb->FileLock);
|
||
|
||
ExDeleteResource(&NonPagedFcb->InstanceChainLock);
|
||
|
||
ExDeleteResource(Fcb->Header.Resource);
|
||
FREE_POOL(Fcb->Header.Resource);
|
||
|
||
ExDeleteResource(Fcb->Header.PagingIoResource);
|
||
FREE_POOL(Fcb->Header.PagingIoResource);
|
||
|
||
TERMINATE_REFERENCE_HISTORY(&NonPagedFcb->ReferenceHistory);
|
||
|
||
FREE_POOL(NonPagedFcb);
|
||
|
||
FREE_POOL(Fcb);
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
RELEASE_SPIN_LOCK(&RdrFcbReferenceLock, OldIrql);
|
||
|
||
//
|
||
// Note that even though we have decremented the reference count
|
||
// and released the spin lock, it is still safe to touch the FCB
|
||
// here because we own the database mutex, and no other thread
|
||
// can decrement the count to 0 until we release the mutex.
|
||
//
|
||
|
||
if (FcbLocked) {
|
||
|
||
//
|
||
// Allow other &X behind operations to the file.
|
||
//
|
||
|
||
//
|
||
// We want to release the lock that was applied during the start
|
||
// of the close operation. This is the first time that it is
|
||
// safe to do so.
|
||
//
|
||
|
||
if (DereferencingThread != 0) {
|
||
RdrReleaseFcbLockForThread(Fcb, DereferencingThread);
|
||
} else {
|
||
|
||
RdrReleaseFcbLock(Fcb);
|
||
|
||
}
|
||
}
|
||
|
||
dprintf(DPRT_FCB, ("Decrement reference count on FCB %08lx\n", Fcb));
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
}
|
||
VOID
|
||
RdrReferenceFcb (
|
||
IN PNONPAGED_FCB NonPagedFcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will add a reference to an FCB.
|
||
|
||
Arguments:
|
||
|
||
IN PFCB Fcb - Supplies the FCB to dereference.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
PFCB Fcb = NonPagedFcb->PagedFcb;
|
||
#ifdef RDRDBG_FCBREF
|
||
PVOID Caller, CallersCaller;
|
||
#endif
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
dprintf(DPRT_FCB, ("ReferenceFCB, FCB: %08lx\n", Fcb));
|
||
|
||
GET_CALLERS_ADDRESS(&Caller, &CallersCaller);
|
||
|
||
ACQUIRE_SPIN_LOCK(&RdrFcbReferenceLock, &OldIrql);
|
||
|
||
ASSERT(NonPagedFcb->RefCount > 0);
|
||
|
||
UPDATE_REFERENCE_HISTORY(&NonPagedFcb->ReferenceHistory, FALSE, Caller, CallersCaller);
|
||
|
||
NonPagedFcb->RefCount += 1;
|
||
|
||
RELEASE_SPIN_LOCK(&RdrFcbReferenceLock, OldIrql);
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RdrUnlinkAndFreeIcb (
|
||
IN PIRP Irp OPTIONAL,
|
||
IN PICB Icb,
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine safely removes an instance of a file from the connectlist
|
||
database chain. It will also free up the storage allocated for the ICB
|
||
and dereference the connection.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies the ICB to unlink.
|
||
IN PFILE_OBJECT FileObject - The file object associated with the ICB.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCONNECTLISTENTRY Connection = Icb->Fcb->Connection;
|
||
PSECURITY_ENTRY Se = Icb->Se;
|
||
PFCB Fcb = Icb->Fcb;
|
||
FILE_TYPE FileType = Icb->Type;
|
||
// ERESOURCE_THREAD ClosersThread = Icb->ClosersThread;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(FileObject->FsContext2 == Icb);
|
||
|
||
dprintf(DPRT_FCB, ("RdrUnlinkAndFreeIcb: Icb: %08lx, Fcb: %08lx, File: %wZ\n", Icb, Icb->Fcb, &Icb->Fcb->FileName));
|
||
|
||
//
|
||
// Free up the ICB
|
||
//
|
||
|
||
RdrFreeIcb(Icb);
|
||
|
||
//
|
||
// Remove the open file SecurityEntry reference for this file.
|
||
//
|
||
|
||
RdrDereferenceSecurityEntryForFile(Se);
|
||
|
||
//
|
||
// Now dereference the FCB (and possibly the CLE/SLE)
|
||
//
|
||
|
||
RdrDereferenceFcb(Irp, Fcb->NonPagedFcb, TRUE, 0, Se);
|
||
|
||
//
|
||
// Remove the reference to the security entry for this
|
||
// file.
|
||
//
|
||
|
||
RdrDereferenceSecurityEntry(Se->NonPagedSecurityEntry);
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RdrIsOperationValid (
|
||
IN PICB Icb,
|
||
IN ULONG NtOperation,
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if a file can be modified, returns the appropriate
|
||
error if it cannot. If it can be modified, it returns STATUS_SUCCESS.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies the open instance to check.
|
||
IN ULONG NtOperations - Supplies the operation we are going to perform.
|
||
IN PFILE_OBJECT FileObject - Describes the file object to perform the op on.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status of operation - STATUS_SUCCESS if file can be modified.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (Icb->Type > sizeof(RdrLegalIrpFunctions)/sizeof(RdrLegalIrpFunctions[0])) {
|
||
InternalError(("Unsupported file type passed in to RdrIsOperationValid: %lx\n", Icb->Type));
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
if (!(RdrLegalIrpFunctions[Icb->Type] & (1 << NtOperation))) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// If the file object has already been cleaned up, and
|
||
//
|
||
// A) This request is a close operation, or
|
||
// B) This request is a set or query info call (for Lou)
|
||
//
|
||
// let it pass, otherwise return STATUS_FILE_CLOSED.
|
||
//
|
||
// Note that we check for paging I/O external to this routine.
|
||
//
|
||
|
||
if ( FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE) ) {
|
||
|
||
if ( (NtOperation == IRP_MJ_CLOSE) ||
|
||
(NtOperation == IRP_MJ_SET_INFORMATION) ||
|
||
(NtOperation == IRP_MJ_QUERY_INFORMATION) ) {
|
||
|
||
NOTHING;
|
||
|
||
} else {
|
||
|
||
return STATUS_FILE_CLOSED;
|
||
}
|
||
}
|
||
|
||
//
|
||
// All files can be closed, so we don't have to do the following checks.
|
||
//
|
||
// In addition, we allow FSCTL's to go through on all legal files.
|
||
//
|
||
|
||
if ((NtOperation == IRP_MJ_CLOSE) ||
|
||
(NtOperation == IRP_MJ_CLEANUP) ||
|
||
(NtOperation == IRP_MJ_FILE_SYSTEM_CONTROL)) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if ( Icb->Flags & (ICB_ERROR | ICB_RENAMED | ICB_FORCECLOSED |
|
||
ICB_DELETE_PENDING )) {
|
||
//
|
||
// If the file was administratively closed (NETUSEDEL),
|
||
// return an appropriate error.
|
||
//
|
||
|
||
if (Icb->Flags & ICB_FORCECLOSED) {
|
||
return STATUS_FILE_FORCED_CLOSED;
|
||
}
|
||
|
||
//
|
||
// If the file was invalidated due to some other reason (ie a VC
|
||
// going down), return unexpected network error.
|
||
//
|
||
|
||
if (Icb->Flags & ICB_ERROR) {
|
||
return STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
if (FileObject->DeletePending) {
|
||
return STATUS_DELETE_PENDING;
|
||
}
|
||
|
||
if (Icb->Flags & ICB_RENAMED) {
|
||
return STATUS_FILE_RENAMED;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If this ICB doesn't have a handle, but we are trying to perform
|
||
// an operation that requires a handle (like a read/write/lock),
|
||
// we want to return an error unless we have an ICB thats going
|
||
// to have a handle created by the read/write/lock.
|
||
//
|
||
//
|
||
// We perform this check in addition to the above global check because
|
||
// certain operation can be performed on the file that can cause
|
||
// its handle to "go away", such as RENAME or DELETE.
|
||
//
|
||
|
||
if (!(Icb->Flags & (ICB_HASHANDLE | ICB_DEFERREDOPEN)) &&
|
||
((NtOperation == IRP_MJ_READ) ||
|
||
(NtOperation == IRP_MJ_WRITE) ||
|
||
(NtOperation == IRP_MJ_LOCK_CONTROL) ||
|
||
(NtOperation == IRP_MJ_FLUSH_BUFFERS) ||
|
||
(NtOperation == IRP_MJ_DEVICE_CONTROL))) {
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RdrFastIoCheckIfPossible(
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
IN BOOLEAN CheckForReadOperation,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks if fast i/o is possible for a read/write operation
|
||
|
||
Arguments:
|
||
|
||
FileObject - Supplies the file object used in the query
|
||
|
||
FileOffset - Supplies the starting byte offset for the read/write operation
|
||
|
||
Length - Supplies the length, in bytes, of the read/write operation
|
||
|
||
Wait - Indicates if we can wait
|
||
|
||
LockKey - Supplies the lock key
|
||
|
||
CheckForReadOperation - Indicates if this is a check for a read or write
|
||
operation
|
||
|
||
IoStatus - Receives the status of the operation if our return value is
|
||
FastIoReturnError
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if fast I/O is possible and FALSE if the caller needs
|
||
to take the long route
|
||
|
||
--*/
|
||
{
|
||
PFCB Fcb = FileObject->FsContext;
|
||
PICB Icb = FileObject->FsContext2;
|
||
LARGE_INTEGER LargeLength;
|
||
BOOLEAN Results;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT (Icb->Signature == STRUCTURE_SIGNATURE_ICB);
|
||
|
||
ASSERT (Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
|
||
|
||
//RdrLog(( "chkfast", &Icb->Fcb->FileName, 2, FileOffset->LowPart,
|
||
// Length | (CheckForReadOperation ? ('r'<<24) : ('w'<<24)) ));
|
||
|
||
if (!RdrAcquireFcbLock(Fcb, SharedLock, Wait)) {
|
||
return FALSE;
|
||
}
|
||
|
||
try {
|
||
|
||
IoStatus->Status = RdrIsOperationValid(Icb, (CheckForReadOperation ? IRP_MJ_READ : IRP_MJ_WRITE), FileObject);
|
||
|
||
if (!NT_SUCCESS(IoStatus->Status)) {
|
||
try_return(Results = FALSE);
|
||
}
|
||
|
||
//
|
||
// If this is a write, and the file is opened with a level II oplock,
|
||
// we need to take the long route.
|
||
//
|
||
|
||
if (!CheckForReadOperation &&
|
||
Fcb->NonPagedFcb->OplockLevel == SMB_OPLOCK_LEVEL_II) {
|
||
|
||
try_return(Results = FALSE);
|
||
}
|
||
|
||
|
||
//
|
||
// If the file cannot be buffered, we have to take the long route.
|
||
//
|
||
|
||
if (!RdrCanFileBeBuffered(Icb)) {
|
||
try_return (Results = FALSE);
|
||
}
|
||
|
||
LargeLength.QuadPart = Length;
|
||
|
||
//
|
||
// Based on whether this is a read or write operation we call
|
||
// fsrtl check for read/write
|
||
//
|
||
|
||
if (CheckForReadOperation) {
|
||
|
||
if (FsRtlFastCheckLockForRead( &Fcb->FileLock,
|
||
FileOffset,
|
||
&LargeLength,
|
||
LockKey,
|
||
FileObject,
|
||
PsGetCurrentProcess() )) {
|
||
|
||
try_return(Results = TRUE);
|
||
}
|
||
|
||
} else {
|
||
|
||
if (FsRtlFastCheckLockForWrite( &Fcb->FileLock,
|
||
FileOffset,
|
||
&LargeLength,
|
||
LockKey,
|
||
FileObject,
|
||
PsGetCurrentProcess() )) {
|
||
|
||
try_return(Results = TRUE);
|
||
}
|
||
}
|
||
|
||
|
||
try_return(Results = FALSE);
|
||
|
||
try_exit:NOTHING;
|
||
} finally {
|
||
RdrReleaseFcbLock(Fcb);
|
||
}
|
||
|
||
return Results;
|
||
}
|
||
|
||
typedef struct _INVALIDATE_FILEID_CONTEXT {
|
||
WORK_QUEUE_ITEM WorkItem;
|
||
PNONPAGED_FCB Fcb;
|
||
USHORT FileId;
|
||
} INVALIDATE_FILEID_CONTEXT, *PINVALIDATE_FILEID_CONTEXT;
|
||
|
||
|
||
VOID
|
||
RdrInvalidateFileId(
|
||
IN PNONPAGED_FCB Fcb,
|
||
IN USHORT FileId
|
||
)
|
||
{
|
||
PINVALIDATE_FILEID_CONTEXT Context;
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
Context = ALLOCATE_POOL(NonPagedPool, sizeof(INVALIDATE_FILEID_CONTEXT), POOL_INVFILEIDCTX);
|
||
|
||
//
|
||
// We will probably find out the problem soon anyway, so ignore the error
|
||
// for now.
|
||
//
|
||
|
||
if (Context == NULL) {
|
||
return;
|
||
}
|
||
|
||
RdrReferenceFcb(Fcb);
|
||
|
||
Context->Fcb = Fcb;
|
||
|
||
Context->FileId = FileId;
|
||
|
||
ExInitializeWorkItem(&Context->WorkItem, CompleteInvalidateFileId, Context);
|
||
|
||
RdrQueueWorkItem(&Context->WorkItem, DelayedWorkQueue);
|
||
|
||
}
|
||
|
||
VOID
|
||
CompleteInvalidateFileId(
|
||
PVOID Ctx
|
||
)
|
||
{
|
||
PINVALIDATE_FILEID_CONTEXT Context = Ctx;
|
||
PNONPAGED_FCB NonPagedFcb = Context->Fcb;
|
||
PFCB Fcb = NonPagedFcb->PagedFcb;
|
||
USHORT FileId = Context->FileId;
|
||
PLIST_ENTRY IcbEntry;
|
||
|
||
PAGED_CODE();
|
||
|
||
FREE_POOL(Context);
|
||
|
||
//
|
||
// Lock the FCB Exclusively.
|
||
//
|
||
|
||
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
|
||
|
||
for (IcbEntry = Fcb->InstanceChain.Flink ;
|
||
IcbEntry != &Fcb->InstanceChain ;
|
||
IcbEntry = IcbEntry->Flink) {
|
||
PICB Icb = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
|
||
|
||
if (Icb->FileId == FileId) {
|
||
Icb->Flags &= ~ICB_HASHANDLE;
|
||
Icb->Flags |= (ICB_ERROR | ICB_FORCECLOSED);
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Dereference (and unlock) the FCB.
|
||
//
|
||
|
||
RdrReferenceDiscardableCode(RdrFileDiscardableSection);
|
||
RdrDereferenceFcb(NULL, NonPagedFcb, TRUE, 0, NULL);
|
||
RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
|
||
|
||
}
|
||
|
||
typedef struct _INVALIDATE_FCB_CONTEXT {
|
||
PIRP Irp;
|
||
PUNICODE_STRING DeviceName;
|
||
PSECURITY_ENTRY Se;
|
||
BOOLEAN CloseFile;
|
||
} INVALIDATE_FCB_CONTEXT, *PINVALIDATE_FCB_CONTEXT;
|
||
|
||
|
||
VOID
|
||
RdrInvalidateConnectionFiles(
|
||
IN PIRP Irp OPTIONAL,
|
||
IN PCONNECTLISTENTRY Connection,
|
||
IN PUNICODE_STRING DeviceName OPTIONAL,
|
||
IN PSECURITY_ENTRY Se OPTIONAL,
|
||
IN BOOLEAN CloseFile
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will invalidate all open files outstanding on a connection
|
||
when the connection is disconnected. It is called with the connection
|
||
database mutex locked.
|
||
|
||
Arguments:
|
||
|
||
IN PCONNECTLISTENTRY Connection - Supplies the open connection to blast.
|
||
IN BOOLEAN CloseFile - if TRUE, close the file with a close SMB.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
INVALIDATE_FCB_CONTEXT Context;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT (Connection->Signature == STRUCTURE_SIGNATURE_CONNECTLISTENTRY);
|
||
|
||
Context.Irp = Irp;
|
||
Context.DeviceName = DeviceName;
|
||
Context.Se = Se;
|
||
Context.CloseFile = CloseFile;
|
||
|
||
dprintf(DPRT_FCB, ("RdrInvalidateConnectionFiles.."));
|
||
|
||
RdrForeachFcbOnConnection(Connection, ExclusiveLock, InvalidateFcb, &Context);
|
||
|
||
dprintf(DPRT_FCB, ("..Done\n"));
|
||
|
||
}
|
||
|
||
VOID
|
||
InvalidateFcb(
|
||
IN PFCB Fcb,
|
||
IN PVOID Ctx
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
PLIST_ENTRY IcbEntry;
|
||
PINVALIDATE_FCB_CONTEXT Context = Ctx;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If we are supposed to close this file down, then we want to
|
||
// purge the file from the cache
|
||
//
|
||
|
||
if (Fcb->NonPagedFcb->Type == DiskFile) {
|
||
|
||
if (Context->CloseFile) {
|
||
|
||
//RdrLog(( "rdflush9", &Fcb->FileName, 0 ));
|
||
Status = RdrFlushCacheFile(Fcb);
|
||
|
||
Status = RdrUnlockFileLocks(Fcb, Context->DeviceName);
|
||
|
||
}
|
||
|
||
//RdrLog(( "rdpurge9", &Fcb->FileName, 0 ));
|
||
Status = RdrPurgeCacheFile(Fcb);
|
||
|
||
}
|
||
|
||
ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
|
||
|
||
for (IcbEntry = Fcb->InstanceChain.Flink ;
|
||
IcbEntry != &Fcb->InstanceChain ;
|
||
IcbEntry = IcbEntry->Flink) {
|
||
PICB Icb = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
|
||
|
||
//
|
||
// If this file is handle based, and we are trying to force close
|
||
// the file, invalidate the file (and handle).
|
||
//
|
||
// This means that we invalidate both directories and files when
|
||
// we are doing a NetUseDel (or transport unbind).
|
||
//
|
||
|
||
if (Context->CloseFile &&
|
||
(Icb->Flags & ICB_HASHANDLE)) {
|
||
|
||
//
|
||
// If there is no Se provided, or if this ICB is opened for
|
||
// the specified Se, close it down.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(Context->Se)
|
||
|
||
||
|
||
|
||
(Icb->Se == Context->Se)
|
||
|
||
// ||
|
||
//RdrAdminAccessCheck(Context->Irp, NULL)
|
||
) {
|
||
|
||
if (!ARGUMENT_PRESENT(Context->DeviceName)
|
||
|
||
||
|
||
|
||
RtlEqualUnicodeString(&Icb->DeviceName, Context->DeviceName, TRUE)) {
|
||
|
||
PLIST_ENTRY IcbEntry2;
|
||
|
||
//
|
||
// Mark this ICB as being in error, and remove the fact that it
|
||
// has a valid handle.
|
||
//
|
||
// This means that path type operations (setinfofile,
|
||
// queryinfofile) will continue to work, but handle type
|
||
// operations (read/write) will fail.
|
||
//
|
||
|
||
Icb->Flags |= ICB_ERROR;
|
||
|
||
//
|
||
// We've found a candidate to close. Make sure that any
|
||
// other collapsed opens on this FCB are also marked as being
|
||
// invalidated.
|
||
//
|
||
|
||
for (IcbEntry2 = Icb->Fcb->InstanceChain.Flink ;
|
||
IcbEntry2 != &Icb->Fcb->InstanceChain ;
|
||
IcbEntry2 = IcbEntry2->Flink) {
|
||
PICB Icb2 = CONTAINING_RECORD(IcbEntry2, ICB, InstanceNext);
|
||
|
||
if ((Icb2 != Icb)
|
||
|
||
&&
|
||
|
||
(Icb2->Flags & ICB_HASHANDLE)
|
||
|
||
&&
|
||
|
||
(Icb2->FileId == Icb->FileId)) {
|
||
|
||
ASSERT ((Icb2->u.f.Flags & ICBF_OPENEDOPLOCKED) ||
|
||
(Icb2->Type == Directory) );
|
||
|
||
Icb2->Flags &= ~ICB_HASHANDLE;
|
||
Icb2->Flags |= (ICB_ERROR | ICB_FORCECLOSED);
|
||
}
|
||
}
|
||
|
||
if (Context->CloseFile &&
|
||
(Icb->Flags & ICB_HASHANDLE)) {
|
||
|
||
ULONG TimeSince1970;
|
||
|
||
//
|
||
// If this is a disk file, wait for any outstanding
|
||
// AndX behind operations on the file to complete.
|
||
//
|
||
|
||
if (Icb->Type == DiskFile) {
|
||
RdrWaitForAndXBehindOperation(&Icb->u.f.AndXBehind);
|
||
}
|
||
|
||
RdrTimeToSecondsSince1970(&Fcb->LastWriteTime, Icb->Fcb->Connection->Server, &TimeSince1970);
|
||
|
||
Status = RdrCloseFileFromFileId(Context->Irp, Icb->FileId, TimeSince1970, Icb->Se, Icb->Fcb->Connection);
|
||
|
||
//
|
||
// Mark that this file was administratively closed.
|
||
//
|
||
|
||
Icb->Flags |= ICB_FORCECLOSED;
|
||
}
|
||
|
||
//
|
||
// Make sure that this ICB isn't flagged as having a valid
|
||
// handle anymore.
|
||
//
|
||
|
||
Icb->Flags &= ~ICB_HASHANDLE;
|
||
|
||
}
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Either we're not supposed to close the file (ie. this is
|
||
// from dropping a VC), or we don't have a remote handle to the
|
||
// file. If we can safely blow this handle away, do so.
|
||
//
|
||
// If we are dropping the VC, it's possible that the ICB has
|
||
// the ICB_HASHANDLE flag set, so remove it if set.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(Context->Se)
|
||
|
||
||
|
||
|
||
(Icb->Se == Context->Se)
|
||
|
||
// ||
|
||
//RdrAdminAccessCheck(Context->Irp, NULL)
|
||
) {
|
||
|
||
if (!ARGUMENT_PRESENT(Context->DeviceName)
|
||
|
||
||
|
||
|
||
RtlEqualUnicodeString(&Icb->DeviceName, Context->DeviceName, TRUE)) {
|
||
|
||
Icb->Flags |= ICB_ERROR;
|
||
Icb->Flags &= ~ICB_HASHANDLE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the file has been invalidated, then it certainly isn't
|
||
// oplocked anymore.
|
||
//
|
||
//
|
||
// Also mark the FCB as invalidated so the cache manager won't
|
||
// pick the file up at some later point.
|
||
//
|
||
|
||
Fcb->NonPagedFcb->Flags &= ~(FCB_OPLOCKED | FCB_HASOPLOCKHANDLE);
|
||
|
||
if (Fcb->NonPagedFcb->OplockedSecurityEntry != NULL) {
|
||
|
||
RdrDereferenceSecurityEntry(Fcb->NonPagedFcb->OplockedSecurityEntry);
|
||
|
||
}
|
||
|
||
Fcb->NonPagedFcb->OplockedSecurityEntry = NULL;
|
||
|
||
Fcb->NonPagedFcb->OplockedFileId = 0xffff;
|
||
}
|
||
|
||
typedef struct _COUNTCONNECTIONFILES_CONTEXT {
|
||
ULONG ConnectionCount;
|
||
ULONG OpenFileCount;
|
||
ULONG DirectoryCount;
|
||
} COUNTCONNECTIONFILES_CONTEXT, *PCOUNTCONNECTIONFILES_CONTEXT;
|
||
|
||
|
||
|
||
BOOLEAN
|
||
RdrAcquireFcbLock(
|
||
IN PFCB Fcb,
|
||
IN FCB_LOCK_TYPE LockType,
|
||
IN BOOLEAN WaitForLock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires either a shared, or an exclusive lock to a file.
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies a pointer to an instance of the file to lock.
|
||
IN FCB_LOCK_TYPE - Supplies the type of lock to apply, Shared or Exclusive.
|
||
IN BOOLEAN WaitForLock - TRUE if we want to wait until the lock is acquired
|
||
|
||
Return Value:
|
||
|
||
TRUE if lock obtained, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
BOOLEAN Result;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
// ASSERT(Fcb->Signature == STRUCTURE_SIGNATURE_FCB);
|
||
|
||
//
|
||
// If the database mutex is owned, make sure we don't own it. If we
|
||
// do, we might deadlock.
|
||
//
|
||
|
||
ASSERT (RdrDatabaseMutex.OwnerThread != KeGetCurrentThread());
|
||
|
||
if (LockType == SharedLock) {
|
||
|
||
dprintf(DPRT_FCB, ("Acquiring shared FCB lock: File: %wZ, FCB: %08lx\n", &Fcb->FileName, Fcb));
|
||
Result = ExAcquireResourceShared(Fcb->Header.Resource, WaitForLock);
|
||
|
||
dprintf(DPRT_FCB, ("Acquired shared FCB lock: File: %wZ, FCB: %08lx\n", &Fcb->FileName, Fcb));
|
||
|
||
} else {
|
||
ASSERT(LockType==ExclusiveLock);
|
||
|
||
dprintf(DPRT_FCB, ("Acquiring exclusive FCB lock: File: %wZ, FCB: %08lx\n", &Fcb->FileName, Fcb));
|
||
|
||
Result = ExAcquireResourceExclusive(Fcb->Header.Resource, WaitForLock);
|
||
|
||
dprintf(DPRT_FCB, ("Acquired exclusive FCB lock: File: %wZ, FCB: %08lx\n", &Fcb->FileName, Fcb));
|
||
}
|
||
#if RDRDBG
|
||
if (!Result) {
|
||
dprintf(DPRT_FCB, ("Failed to acquire FCB lock, FCB: %08lx\n", Fcb));
|
||
}
|
||
#endif
|
||
|
||
|
||
return Result;
|
||
}
|
||
|
||
PFCB
|
||
RdrFindOplockedFcb (
|
||
IN USHORT FileId,
|
||
IN PSERVERLISTENTRY Server
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine walks the FCB chain and finds an oplocked FCB that matches
|
||
the FID and server specified.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN USHORT FileId - Supplies the File Id to look up.
|
||
IN PSERVERLISTENTRY Server - Supplies the server to look the file up on.
|
||
|
||
|
||
Return Value:
|
||
|
||
PFCB - Oplocked FCB, or NULL if none found.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PLIST_ENTRY FcbEntry;
|
||
|
||
PLIST_ENTRY ConnectEntry;
|
||
PFCB Fcb = NULL;
|
||
BOOLEAN MutexLocked = TRUE;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = KeWaitForMutexObject(&RdrDatabaseMutex,
|
||
Executive, KernelMode, FALSE, NULL);
|
||
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
try {
|
||
|
||
//
|
||
// Walk all the connections associated with this server.
|
||
//
|
||
|
||
for (ConnectEntry = Server->CLEHead.Flink
|
||
;
|
||
ConnectEntry != &Server->CLEHead
|
||
;
|
||
ConnectEntry = ConnectEntry->Flink) {
|
||
|
||
PCONNECTLISTENTRY Connection = CONTAINING_RECORD(ConnectEntry, CONNECTLISTENTRY, SiblingNext);
|
||
|
||
//
|
||
// Walk all the FCB's associated with this connection.
|
||
//
|
||
|
||
for (FcbEntry = Connection->FcbChain.Flink
|
||
;
|
||
FcbEntry != &Connection->FcbChain
|
||
;
|
||
FcbEntry = Fcb->ConnectNext.Flink) {
|
||
|
||
Fcb = CONTAINING_RECORD(FcbEntry, FCB, ConnectNext);
|
||
|
||
if ((Fcb->NonPagedFcb->Flags & FCB_OPLOCKED) &&
|
||
(Fcb->NonPagedFcb->OplockedFileId == FileId)) {
|
||
|
||
dprintf(DPRT_OPLOCK, ("Return FCB for %wZ (%lx)\n", &Fcb->FileName, Fcb));
|
||
|
||
//
|
||
// Mark the file as having an oplock break in progress
|
||
// and bump the reference count on the file to prevent it
|
||
// from going away by a close.
|
||
//
|
||
|
||
RdrReferenceFcb(Fcb->NonPagedFcb);
|
||
|
||
try_return(Fcb);
|
||
}
|
||
|
||
}
|
||
|
||
Fcb = NULL;
|
||
|
||
}
|
||
try_exit:NOTHING;
|
||
} finally {
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
}
|
||
|
||
return Fcb;
|
||
}
|
||
|
||
VOID
|
||
RdrForeachFcbOnConnection(
|
||
IN PCONNECTLISTENTRY Connection,
|
||
IN FCB_LOCK_TYPE LockType,
|
||
IN PFCB_ENUMERATION_ROUTINE EnumerationRoutine,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine enumerates the FCBs on the per connection FCB chain.
|
||
|
||
It will call the supplied enumeration routine iteratively with each
|
||
FCB in the appropriate chain. The FCB will be locked with either a shared
|
||
or an exclusive lock (depending on the locktype parameter).
|
||
|
||
The enumeration routine has the ability to indicate whether or not this
|
||
lock should be released when skiping onto the next FCB in the chain.
|
||
|
||
Arguments:
|
||
|
||
IN PCONNECTLISTENTRY Connection - Supplies the connection to enumerate.
|
||
IN FCB_LOCK_TYPE LockType - Supplies the type of the FCB lock (shared or exclusive)
|
||
IN PFCB_ENUMERATION_ROUTINE EnumerationRoutine - Supplies the routine to call
|
||
IN PVOID Context - Supplies a context block for that routine.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
Since it is possible for a dereference of an FCB to dereference TWO FCB's
|
||
to 0, not just 1, we have to be careful to reference the next FCB entry before
|
||
dereferencing the one we passed into the enumeration routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb;
|
||
PLIST_ENTRY FcbEntry;
|
||
BOOLEAN FcbDeleted;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT (Connection->Signature == STRUCTURE_SIGNATURE_CONNECTLISTENTRY);
|
||
|
||
//
|
||
// Lock the connection and FCB database.
|
||
//
|
||
|
||
if (!NT_SUCCESS(KeWaitForMutexObject(&RdrDatabaseMutex,
|
||
Executive, KernelMode, FALSE, NULL))) {
|
||
InternalError(("Unable to claim FCB mutex in RdrForeachFcbOnConnection"));
|
||
return;
|
||
}
|
||
|
||
//
|
||
// The list is empty. We can now return.
|
||
//
|
||
|
||
if (Connection->FcbChain.Flink == &Connection->FcbChain) {
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
return;
|
||
}
|
||
|
||
FcbEntry = Connection->FcbChain.Flink;
|
||
|
||
Fcb = CONTAINING_RECORD(FcbEntry, FCB, ConnectNext);
|
||
|
||
//
|
||
// Reference the FCB to make sure it doesn't go away.
|
||
//
|
||
|
||
RdrReferenceFcb(Fcb->NonPagedFcb);
|
||
|
||
do {
|
||
PFCB NewFcb;
|
||
|
||
//
|
||
// Now release the FCB mutex.
|
||
//
|
||
// We need to release it before we acquire the FCB lock,
|
||
// because it's possible that there's some shared owner of the
|
||
// FCB that is trying to dereference the FCB which will
|
||
// cause us to deadlock waiting on the FCB database mutex.
|
||
//
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
if (LockType != NoLock) {
|
||
RdrAcquireFcbLock(Fcb, LockType, TRUE);
|
||
}
|
||
|
||
//
|
||
// Call the supplied enumeration routine.
|
||
//
|
||
|
||
EnumerationRoutine(Fcb, Context);
|
||
|
||
//
|
||
// Re-Claim the FCB database mutex for the next pass.
|
||
//
|
||
|
||
if (!NT_SUCCESS(KeWaitForMutexObject(&RdrDatabaseMutex,
|
||
Executive, KernelMode, FALSE, NULL))) {
|
||
InternalError(("Unable to claim FCB mutex in RdrForeachFcb"));
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Find the next FCB to check.
|
||
//
|
||
|
||
FcbEntry = FcbEntry->Flink;
|
||
|
||
//
|
||
// If we're not at the end of the list, reference the next FCB in
|
||
// the chain. If we're at the end of the list, return NULL.
|
||
//
|
||
|
||
if (FcbEntry != &Connection->FcbChain) {
|
||
|
||
NewFcb = CONTAINING_RECORD(FcbEntry, FCB, ConnectNext);
|
||
|
||
//
|
||
// If we managed to pick up the same FCB twice in a row, this means
|
||
// that the FCB chain is corrupted.
|
||
//
|
||
|
||
ASSERT (NewFcb != Fcb);
|
||
|
||
RdrReferenceFcb(NewFcb->NonPagedFcb);
|
||
|
||
}
|
||
|
||
//
|
||
// Dereference the FCB. This will also release the FCB lock if
|
||
// appropriate.
|
||
//
|
||
|
||
FcbDeleted = RdrDereferenceFcb(NULL, Fcb->NonPagedFcb, (BOOLEAN)(LockType != NoLock),
|
||
0, NULL);
|
||
|
||
Fcb = NewFcb;
|
||
|
||
} while ( FcbEntry != &Connection->FcbChain );
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
}
|
||
|
||
VOID
|
||
RdrForeachFcb (
|
||
IN FCB_LOCK_TYPE LockType,
|
||
IN PFCB_ENUMERATION_ROUTINE EnumerationRoutine,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine enumerates all the FCBs open in the redirector.
|
||
|
||
It will call the supplied enumeration routine iteratively with each
|
||
FCB in the global chain. The FCB will be locked with either a shared
|
||
or an exclusive lock (depending on the locktype parameter).
|
||
|
||
The enumeration routine has the ability to indicate whether or not this
|
||
lock should be released when skiping onto the next FCB in the chain.
|
||
|
||
Arguments:
|
||
|
||
IN FCB_LOCK_TYPE LockType - Supplies the type of the FCB lock (shared or exclusive)
|
||
IN PFCB_ENUMERATION_ROUTINE EnumerationRoutine - Supplies the routine to call
|
||
IN PVOID Context - Supplies a context block for that routine.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
Since it is possible for a dereference of an FCB to dereference TWO FCB's
|
||
to 0, not just 1, we have to be careful to reference the next FCB entry before
|
||
dereferencing the one we passed into the enumeration routine.
|
||
|
||
--*/
|
||
{
|
||
PFCB Fcb;
|
||
BOOLEAN UnlockFcb = TRUE;
|
||
PLIST_ENTRY FcbEntry;
|
||
BOOLEAN FcbDeleted;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Lock the connection and FCB database.
|
||
//
|
||
|
||
if (!NT_SUCCESS(KeWaitForMutexObject(&RdrDatabaseMutex,
|
||
Executive, KernelMode, FALSE, NULL))) {
|
||
InternalError(("Unable to claim FCB mutex in RdrForeachFcb"));
|
||
return;
|
||
}
|
||
|
||
//
|
||
// The list is empty. We can now return.
|
||
//
|
||
|
||
if (RdrFcbHead.Flink == &RdrFcbHead) {
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
return;
|
||
}
|
||
|
||
FcbEntry = RdrFcbHead.Flink;
|
||
|
||
Fcb = CONTAINING_RECORD(FcbEntry, FCB, GlobalNext);
|
||
|
||
//
|
||
// Reference the FCB to make sure it doesn't go away.
|
||
//
|
||
|
||
RdrReferenceFcb(Fcb->NonPagedFcb);
|
||
|
||
do {
|
||
PFCB NewFcb;
|
||
|
||
//
|
||
// Now release the FCB mutex.
|
||
//
|
||
// We need to release it before we acquire the FCB lock,
|
||
// because it's possible that there's some shared owner of the
|
||
// FCB that is trying to dereference the FCB which will
|
||
// cause us to deadlock waiting on the FCB database mutex.
|
||
//
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
|
||
if (LockType != NoLock) {
|
||
RdrAcquireFcbLock(Fcb, LockType, TRUE);
|
||
}
|
||
|
||
//
|
||
// Call the supplied enumeration routine.
|
||
//
|
||
|
||
EnumerationRoutine(Fcb, Context);
|
||
|
||
//
|
||
// Re-Claim the FCB database mutex for the next pass.
|
||
//
|
||
|
||
if (!NT_SUCCESS(KeWaitForMutexObject(&RdrDatabaseMutex,
|
||
Executive, KernelMode, FALSE, NULL))) {
|
||
InternalError(("Unable to claim FCB mutex in RdrPurgeDormantCachedFiles"));
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Find the next FCB to check.
|
||
//
|
||
|
||
FcbEntry = FcbEntry->Flink;
|
||
|
||
//
|
||
// If we're not at the end of the list, reference the next FCB in
|
||
// the chain. If we're at the end of the list, return NULL.
|
||
//
|
||
|
||
if (FcbEntry != &RdrFcbHead) {
|
||
|
||
NewFcb = CONTAINING_RECORD(FcbEntry, FCB, GlobalNext);
|
||
|
||
//
|
||
// If we managed to pick up the same FCB twice in a row, this means
|
||
// that the FCB chain is corrupted.
|
||
//
|
||
|
||
ASSERT (NewFcb != Fcb);
|
||
|
||
RdrReferenceFcb(NewFcb->NonPagedFcb);
|
||
|
||
}
|
||
|
||
//
|
||
// Dereference the FCB. This will also release the FCB lock if
|
||
// appropriate.
|
||
//
|
||
|
||
FcbDeleted = RdrDereferenceFcb(NULL, Fcb->NonPagedFcb, (BOOLEAN)(LockType != NoLock),
|
||
0, NULL);
|
||
|
||
Fcb = NewFcb;
|
||
|
||
} while ( FcbEntry != &RdrFcbHead );
|
||
|
||
KeReleaseMutex(&RdrDatabaseMutex, FALSE);
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrCheckShareAccess(
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN USHORT ShareAccess,
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PSHARE_ACCESS IoShareAccess
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
ExAcquireResourceExclusive(&RdrSharingCheckResource, TRUE);
|
||
|
||
dprintf(DPRT_CREATE, ("Check share access for File Object: %08lx, share access: %08lx\n", FileObject, IoShareAccess));
|
||
|
||
Status = IoCheckShareAccess(DesiredAccess,
|
||
ShareAccess,
|
||
FileObject,
|
||
IoShareAccess,
|
||
TRUE);
|
||
|
||
ExReleaseResource(&RdrSharingCheckResource);
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
RdrRemoveShareAccess(
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PSHARE_ACCESS IoShareAccess
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExAcquireResourceExclusive(&RdrSharingCheckResource, TRUE);
|
||
|
||
dprintf(DPRT_CLOSE, ("Remove share access for File Object: %08lx, share access: %08lx\n", FileObject, IoShareAccess));
|
||
|
||
IoRemoveShareAccess(FileObject,
|
||
IoShareAccess);
|
||
|
||
ExReleaseResource(&RdrSharingCheckResource);
|
||
}
|
||
|
||
VOID
|
||
RdrNullAcquireSize(
|
||
IN PFCB Fcb
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(Fcb);
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RdrNullReleaseSize(
|
||
IN PFCB Fcb
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(Fcb);
|
||
|
||
return;
|
||
}
|
||
VOID
|
||
RdrRealAcquireSize(
|
||
IN PFCB Fcb
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExAcquireResourceShared(&RdrFileSizeResource, TRUE);
|
||
return;
|
||
|
||
}
|
||
|
||
VOID
|
||
RdrRealReleaseSize(
|
||
IN PFCB Fcb
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExReleaseResource(&RdrFileSizeResource);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
RdrpInitializeFcb (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the redirector ICB chain.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Initialize the FCB chain linked list.
|
||
//
|
||
|
||
InitializeListHead(&RdrFcbHead);
|
||
|
||
ExInitializeResource(&RdrSharingCheckResource);
|
||
|
||
ExInitializeResource(&RdrFileSizeResource);
|
||
|
||
KeInitializeSpinLock(&RdrFcbReferenceLock);
|
||
|
||
KeInitializeSpinLock(&RdrFileSizeLock);
|
||
|
||
}
|
||
VOID
|
||
RdrpUninitializeFcb (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uninitializes the redirector ICB and FCB chain.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExDeleteResource(&RdrSharingCheckResource);
|
||
|
||
ExDeleteResource(&RdrFileSizeResource);
|
||
|
||
ASSERT (IsListEmpty(&RdrFcbHead));
|
||
}
|