359 lines
9.2 KiB
C++
359 lines
9.2 KiB
C++
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
oplock.cxx
|
||
|
||
Abstract:
|
||
|
||
This module implements the oplock object.
|
||
|
||
Author:
|
||
|
||
Keith Moore (keithmo) 29-Aug-1997
|
||
Split off from the overly huge creatfil.cxx.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include "TsunamiP.Hxx"
|
||
#pragma hdrstop
|
||
|
||
|
||
|
||
POPLOCK_OBJECT
|
||
CreateOplockObject(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates a new oplock object.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
POPLOCK_OBJECT - Pointer to the newly created object if successful,
|
||
NULL otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
POPLOCK_OBJECT oplock;
|
||
LPSTR eventName;
|
||
|
||
oplock = (POPLOCK_OBJECT)ALLOC( sizeof(*oplock) );
|
||
|
||
if( oplock != NULL ) {
|
||
|
||
oplock->Signature = OPLOCK_OBJ_SIGNATURE;
|
||
oplock->lpPFInfo = NULL;
|
||
|
||
//
|
||
// On checked builds, we'll create a name for our event. The
|
||
// name contains the address of the containing oplock structure
|
||
// and the process ID. This ensures the name should be unique
|
||
// across all processes. (Using a named event makes it easy
|
||
// to search for orphaned oplocks using the handles.exe utility.)
|
||
//
|
||
// On free builds, we'll create an unnamed event.
|
||
//
|
||
|
||
#if DBG
|
||
CHAR dbgEventName[sizeof("Oplock @ 12345678 PID 1234567890")];
|
||
|
||
wsprintf(
|
||
dbgEventName,
|
||
"Oplock @ %08lx PID %lu",
|
||
oplock,
|
||
GetCurrentProcessId()
|
||
);
|
||
|
||
eventName = dbgEventName;
|
||
#else
|
||
eventName = NULL;
|
||
#endif
|
||
|
||
oplock->hOplockInitComplete = CreateEvent(
|
||
NULL,
|
||
TRUE,
|
||
FALSE,
|
||
eventName
|
||
);
|
||
|
||
if( oplock->hOplockInitComplete == NULL ) {
|
||
FREE( oplock );
|
||
oplock = NULL;
|
||
} else {
|
||
TSUNAMI_TRACE( TRACE_OPLOCK_CREATE, oplock );
|
||
}
|
||
|
||
}
|
||
|
||
return oplock;
|
||
|
||
} // CreateOplockObject
|
||
|
||
|
||
VOID
|
||
DestroyOplockObject(
|
||
IN POPLOCK_OBJECT Oplock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Destroys an oplock object.
|
||
|
||
Arguments:
|
||
|
||
Oplock - The oplock object to destroy.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
BOOL bSuccess;
|
||
|
||
ASSERT( Oplock != NULL );
|
||
ASSERT( Oplock->Signature == OPLOCK_OBJ_SIGNATURE );
|
||
TSUNAMI_TRACE( TRACE_OPLOCK_DESTROY, Oplock );
|
||
|
||
bSuccess = CloseHandle( Oplock->hOplockInitComplete );
|
||
ASSERT( bSuccess );
|
||
|
||
Oplock->Signature = OPLOCK_OBJ_SIGNATURE_X;
|
||
FREE( Oplock );
|
||
|
||
} // DestroyOplockObject
|
||
|
||
|
||
VOID
|
||
OplockCreateFile(
|
||
PVOID Context,
|
||
DWORD Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by ATQ after an oplock break notification
|
||
is received. This routine is responsible for flushing the item
|
||
from the open file cache to ensure the file will be closed in
|
||
a timely manner.
|
||
|
||
Arguments:
|
||
|
||
Context - An uninterpreted context value. In reality, this is
|
||
a pointer to an OPLOCK_OBJECT.
|
||
|
||
Status - The oplock break status. This will be one of the following
|
||
values:
|
||
|
||
OPLOCK_BREAK_NO_OPLOCK - This notification is sent
|
||
immediately if the oplock could not be acquired.
|
||
This typically occurs if the target file is already
|
||
open.
|
||
|
||
OPLOCK_BREAK_OPEN -
|
||
|
||
OPLOCK_BREAK_CLOSE -
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
POPLOCK_OBJECT lpOplock = (POPLOCK_OBJECT)Context;
|
||
PBLOB_HEADER pbhBlob;
|
||
PCACHE_OBJECT cache;
|
||
PCACHE_OBJECT pCacheTmp;
|
||
PCACHE_OBJECT TmpCache;
|
||
BOOL result = FALSE;
|
||
LIST_ENTRY ListHead;
|
||
PLIST_ENTRY pEntry;
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"OplockCreateFile(%08lx, %08lx) - Entered\n",
|
||
Context,
|
||
Status
|
||
));
|
||
}
|
||
|
||
ASSERT( lpOplock != NULL );
|
||
ASSERT( lpOplock->Signature == OPLOCK_OBJ_SIGNATURE );
|
||
|
||
if( Status != OPLOCK_BREAK_NO_OPLOCK ) {
|
||
DWORD waitResult;
|
||
|
||
while( TRUE ) {
|
||
waitResult = WaitForSingleObject(
|
||
lpOplock->hOplockInitComplete,
|
||
1000
|
||
);
|
||
|
||
if( waitResult == WAIT_OBJECT_0 ) {
|
||
break;
|
||
}
|
||
|
||
ASSERT( waitResult == WAIT_TIMEOUT );
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"Rewaiting on oplock @ %08lx\n",
|
||
lpOplock
|
||
));
|
||
}
|
||
}
|
||
|
||
EnterCriticalSection( &CacheTable.CriticalSection );
|
||
EnterCriticalSection( &csVirtualRoots );
|
||
|
||
if( lpOplock->lpPFInfo == NULL ) {
|
||
TSUNAMI_TRACE( TRACE_OPLOCK_FAILURE, lpOplock );
|
||
} else {
|
||
ASSERT( lpOplock->lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
|
||
ASSERT( lpOplock->lpPFInfo->pOplock == lpOplock );
|
||
lpOplock->lpPFInfo->pOplock = NULL;
|
||
|
||
TSUNAMI_TRACE( TRACE_OPLOCK_BREAK, lpOplock );
|
||
pbhBlob = (( PBLOB_HEADER )lpOplock->lpPFInfo ) - 1;
|
||
if( pbhBlob->IsCached ) {
|
||
cache = pbhBlob->pCache;
|
||
TSUNAMI_TRACE( TRACE_CACHE_BREAK, cache );
|
||
|
||
InitializeListHead( &ListHead );
|
||
|
||
for( pEntry = CacheTable.MruList.Flink;
|
||
pEntry != &CacheTable.MruList;
|
||
pEntry = pEntry->Flink ) {
|
||
|
||
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, MruList );
|
||
|
||
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
|
||
|
||
if( pCacheTmp != cache ) {
|
||
continue;
|
||
}
|
||
|
||
result = TRUE;
|
||
|
||
while( !IsListEmpty( &lpOplock->lpPFInfo->OpenReferenceList ) ) {
|
||
pEntry = RemoveHeadList( &lpOplock->lpPFInfo->OpenReferenceList );
|
||
InitializeListHead( pEntry );
|
||
|
||
pbhBlob = CONTAINING_RECORD( pEntry, BLOB_HEADER, PFList );
|
||
TmpCache = pbhBlob->pCache;
|
||
|
||
if( !RemoveCacheObjFromLists( TmpCache, FALSE ) ) {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"OplockCreateFile(%08lx, %08lx, %08lx) - Error Processing Open Reference %08lx\n",
|
||
Context,
|
||
Status,
|
||
pEntry,
|
||
TmpCache
|
||
));
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"OplockCreateFile(%08lx, %08lx, %08lx) - Processing Open Reference %08lx\n",
|
||
Context,
|
||
Status,
|
||
pEntry,
|
||
TmpCache
|
||
));
|
||
}
|
||
|
||
InsertTailList( &ListHead, pEntry );
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
if( result ) {
|
||
while( !IsListEmpty( &ListHead ) ) {
|
||
pEntry = RemoveHeadList( &ListHead );
|
||
InitializeListHead( pEntry );
|
||
|
||
pbhBlob = CONTAINING_RECORD( pEntry, BLOB_HEADER, PFList );
|
||
TmpCache = pbhBlob->pCache;
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"OplockCreateFile(%08lx, %08lx) - Processing Open Reference %08lx %08lx\n",
|
||
Context,
|
||
Status,
|
||
pEntry,
|
||
TmpCache
|
||
));
|
||
}
|
||
|
||
TsDereferenceCacheObj( TmpCache, FALSE );
|
||
}
|
||
|
||
if( RemoveCacheObjFromLists( cache, FALSE ) ) {
|
||
if( cache->fZombie ) {
|
||
//DON'T DO IT! TsDereferenceCacheObj( cache, FALSE );
|
||
}
|
||
} else {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"OplockCreateFile(%08lx, %08lx) - Error Processing Open Reference %08lx\n",
|
||
Context,
|
||
Status,
|
||
cache
|
||
));
|
||
}
|
||
}
|
||
|
||
} else {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"Unknown oplock %08lx, cache == %08lx\n",
|
||
lpOplock,
|
||
cache
|
||
));
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
LeaveCriticalSection( &csVirtualRoots );
|
||
LeaveCriticalSection( &CacheTable.CriticalSection );
|
||
|
||
DestroyOplockObject( lpOplock );
|
||
|
||
} // OplockCreateFile
|
||
|