mirror of https://github.com/lianthony/NT4.0
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.
571 lines
11 KiB
571 lines
11 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
FileLock.c
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Author:
|
|
|
|
Dan Lovinger 12-Apr-96
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// XXX This must be kept in sync with ntos\fsrtl\filelock.c
|
|
//
|
|
// It isn't in a header because it should be opaque outside of
|
|
// the filelock module
|
|
//
|
|
|
|
typedef struct {
|
|
//
|
|
// List of locks under this node
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY Locks;
|
|
|
|
//
|
|
// Maximum byte offset affected by locks under Locks
|
|
// Note: minimum offset is the starting offset of the
|
|
// first lock at this node.
|
|
//
|
|
|
|
ULONGLONG Extent;
|
|
|
|
//
|
|
// Splay tree links to parent, lock groups strictly less than
|
|
// and lock groups strictly greater than locks under Locks
|
|
//
|
|
|
|
RTL_SPLAY_LINKS Links;
|
|
|
|
//
|
|
// Last lock in the list (useful for node collapse under insert)
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY Tail;
|
|
|
|
} LOCKTREE_NODE, *PLOCKTREE_NODE;
|
|
|
|
//
|
|
// Define the threading wrappers for lock information
|
|
//
|
|
|
|
//
|
|
// Each shared lock record corresponds to a current granted lock and is
|
|
// maintained in a queue off of a LOCKTREE_NODE's Locks list. The list
|
|
// of current locks is ordered according to the starting byte of the lock.
|
|
//
|
|
|
|
typedef struct _SH_LOCK {
|
|
|
|
//
|
|
// The link structures for the list of shared locks.
|
|
// (must be first element - see FsRtlPrivateLimitFreeLockList)
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY Link;
|
|
|
|
//
|
|
// The actual locked range
|
|
//
|
|
|
|
FILE_LOCK_INFO LockInfo;
|
|
|
|
} SH_LOCK;
|
|
typedef SH_LOCK *PSH_LOCK;
|
|
|
|
//
|
|
// Each exclusive lock record corresponds to a current granted lock and is
|
|
// threaded into the exclusive lock tree.
|
|
//
|
|
|
|
typedef struct _EX_LOCK {
|
|
|
|
//
|
|
// The link structures for the list of current locks.
|
|
// (must be first element - see FsRtlPrivateLimitFreeLockList)
|
|
//
|
|
|
|
union {
|
|
|
|
//
|
|
// Simple list reference for the freelist
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY Link;
|
|
|
|
//
|
|
// The actual splay links when inserted
|
|
//
|
|
|
|
RTL_SPLAY_LINKS Links;
|
|
};
|
|
|
|
//
|
|
// The actual locked range
|
|
//
|
|
|
|
FILE_LOCK_INFO LockInfo;
|
|
|
|
} EX_LOCK;
|
|
typedef EX_LOCK *PEX_LOCK;
|
|
|
|
//
|
|
// Each Waiting lock record corresponds to a IRP that is waiting for a
|
|
// lock to be granted and is maintained in a queue off of the FILE_LOCK's
|
|
// WaitingLockQueue list.
|
|
//
|
|
|
|
typedef struct _WAITING_LOCK {
|
|
|
|
//
|
|
// The link structures for the list of waiting locks
|
|
// (must be first element - see FsRtlPrivateLimitFreeLockList)
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY Link;
|
|
|
|
//
|
|
// The context field to use when completing the irp via the alternate
|
|
// routine
|
|
//
|
|
|
|
PVOID Context;
|
|
|
|
//
|
|
// A pointer to the IRP that is waiting for a lock
|
|
//
|
|
|
|
PIRP Irp;
|
|
|
|
} WAITING_LOCK;
|
|
typedef WAITING_LOCK *PWAITING_LOCK;
|
|
|
|
|
|
//
|
|
// Each lock or waiting onto some lock queue.
|
|
//
|
|
|
|
typedef struct _LOCK_QUEUE {
|
|
|
|
//
|
|
// SpinLock to guard queue access
|
|
//
|
|
|
|
KSPIN_LOCK QueueSpinLock;
|
|
|
|
//
|
|
// The items contain locktrees of the current granted
|
|
// locks and a list of the waiting locks
|
|
//
|
|
|
|
PRTL_SPLAY_LINKS SharedLockTree;
|
|
PRTL_SPLAY_LINKS ExclusiveLockTree;
|
|
SINGLE_LIST_ENTRY WaitingLocks;
|
|
SINGLE_LIST_ENTRY WaitingLocksTail;
|
|
|
|
} LOCK_QUEUE, *PLOCK_QUEUE;
|
|
|
|
|
|
//
|
|
// Any file_lock which has had a lock applied gets non-paged pool
|
|
// lock_info structure which tracks the current locks applied to
|
|
// the file
|
|
//
|
|
typedef struct _LOCK_INFO {
|
|
|
|
//
|
|
// LowestLockOffset retains the offset of the lowest existing
|
|
// lock. This facilitates a quick check to see if a read or
|
|
// write can proceed without locking the lock database. This is
|
|
// helpful for applications that use mirrored locks -- all locks
|
|
// are higher than file data.
|
|
//
|
|
// If the lowest lock has an offset > 0xffffffff, LowestLockOffset
|
|
// is set to 0xffffffff.
|
|
//
|
|
|
|
ULONG LowestLockOffset;
|
|
|
|
//
|
|
// The optional procedure to call to complete a request
|
|
//
|
|
|
|
PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine;
|
|
|
|
//
|
|
// The optional procedure to call when unlocking a byte range
|
|
//
|
|
|
|
PUNLOCK_ROUTINE UnlockRoutine;
|
|
|
|
//
|
|
// The locked ranges
|
|
//
|
|
|
|
LOCK_QUEUE LockQueue;
|
|
|
|
} LOCK_INFO, *PLOCK_INFO;
|
|
|
|
|
|
//
|
|
// dprintf is really expensive to iteratively call to do the indenting,
|
|
// so we just build up some avaliable spaces to mangle as required
|
|
//
|
|
|
|
#define MIN(a,b) ((a) > (b) ? (b) : (a))
|
|
|
|
#define MAXINDENT 128
|
|
#define INDENTSTEP 2
|
|
#define MakeSpace(I) Space[MIN((I)*INDENTSTEP, MAXINDENT)] = '\0'
|
|
#define RestoreSpace(I) Space[MIN((I)*INDENTSTEP, MAXINDENT)] = ' '
|
|
|
|
CHAR Space[MAXINDENT*INDENTSTEP + 1];
|
|
|
|
__inline VOID CheckForBreak()
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Encapsulate control c checking code
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
None, raises if break is needed
|
|
--*/
|
|
{
|
|
if ( CheckControlC() ) {
|
|
|
|
RaiseException(0, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Helper macros for printing 64bit quantities
|
|
//
|
|
|
|
#define SplitLI(LI) (LI).HighPart, (LI).LowPart
|
|
|
|
VOID
|
|
DumpFileLockInfo(
|
|
PFILE_LOCK_INFO pFileLockInfo,
|
|
ULONG Indent
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Dump the local internal FILE_LOCK_INFO structure
|
|
|
|
Arguments:
|
|
|
|
pFileLock - debugger address of FILE_LOCK_INFO to dump
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
MakeSpace(Indent);
|
|
|
|
dprintf("%sStart = %x%08x Length = %x%08x End = %x%08x (%s)\n"
|
|
"%sKey = %08x FileOb = %08x ProcId = %08x\n",
|
|
Space,
|
|
SplitLI(pFileLockInfo->StartingByte),
|
|
SplitLI(pFileLockInfo->Length),
|
|
SplitLI(pFileLockInfo->EndingByte),
|
|
pFileLockInfo->ExclusiveLock ? "Ex":"Sh",
|
|
Space,
|
|
pFileLockInfo->Key,
|
|
pFileLockInfo->FileObject,
|
|
pFileLockInfo->ProcessId);
|
|
|
|
RestoreSpace(Indent);
|
|
}
|
|
|
|
__inline
|
|
PVOID
|
|
ExLockAddress(
|
|
PVOID ExLockSplayLinks
|
|
)
|
|
{
|
|
return ExLockSplayLinks ?
|
|
CONTAINING_RECORD( ExLockSplayLinks, EX_LOCK, Links ) : NULL;
|
|
}
|
|
|
|
VOID
|
|
DumpExclusiveNode(
|
|
PVOID ExclusiveNodeSplayLinks,
|
|
ULONG Indent
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Dump an exclusive lock node
|
|
|
|
Arguments:
|
|
|
|
ExclusiveNodeSplayLinks - splay links of an exclusive node
|
|
|
|
Indent - indent level to use
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
EX_LOCK ExLock, *pExLock;
|
|
|
|
pExLock = ExLockAddress(ExclusiveNodeSplayLinks);
|
|
|
|
if (!ReadAtAddress(pExLock, &ExLock, sizeof(EX_LOCK), &pExLock)) {
|
|
|
|
return;
|
|
}
|
|
|
|
MakeSpace(Indent);
|
|
|
|
dprintf("%sLock @ %08x ("
|
|
"P = %08x R = %08x L = %08x)\n",
|
|
Space,
|
|
pExLock,
|
|
ExLockAddress(DbgRtlParent(ExLock.Links)),
|
|
ExLockAddress(DbgRtlRightChild(ExLock.Links)),
|
|
ExLockAddress(DbgRtlLeftChild(ExLock.Links)));
|
|
|
|
RestoreSpace(Indent);
|
|
|
|
DumpFileLockInfo(&ExLock.LockInfo, Indent);
|
|
}
|
|
|
|
__inline
|
|
PVOID
|
|
LockTreeAddress(
|
|
PVOID LockTreeSplayLinks
|
|
)
|
|
{
|
|
return LockTreeSplayLinks ?
|
|
CONTAINING_RECORD( LockTreeSplayLinks, LOCKTREE_NODE, Links ) : NULL;
|
|
}
|
|
|
|
VOID
|
|
DumpSharedNode(
|
|
PVOID SharedNodeSplayLinks,
|
|
ULONG Indent
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Dump a shared lock node
|
|
|
|
Arguments:
|
|
|
|
SharedNodeSplayLinks - splay links of an exclusive node
|
|
|
|
Indent - indent level to use
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LOCKTREE_NODE LockTreeNode, *pLockTreeNode;
|
|
SH_LOCK ShLock, *pShLock;
|
|
SINGLE_LIST_ENTRY *pLink;
|
|
|
|
pLockTreeNode = LockTreeAddress(SharedNodeSplayLinks);
|
|
|
|
if (!ReadAtAddress(pLockTreeNode, &LockTreeNode, sizeof(LOCKTREE_NODE), &pLockTreeNode)) {
|
|
|
|
return;
|
|
}
|
|
|
|
MakeSpace(Indent);
|
|
|
|
dprintf("%sLockTreeNode @ %08x ("
|
|
"P = %08x R = %08x L = %08x)\n",
|
|
Space,
|
|
pLockTreeNode,
|
|
LockTreeAddress(DbgRtlParent(LockTreeNode.Links)),
|
|
LockTreeAddress(DbgRtlRightChild(LockTreeNode.Links)),
|
|
LockTreeAddress(DbgRtlLeftChild(LockTreeNode.Links)));
|
|
|
|
RestoreSpace(Indent);
|
|
|
|
for (pLink = LockTreeNode.Locks.Next;
|
|
pLink;
|
|
pLink = ShLock.Link.Next) {
|
|
|
|
CheckForBreak();
|
|
|
|
pShLock = CONTAINING_RECORD( pLink, SH_LOCK, Link );
|
|
|
|
if (!ReadAtAddress(pShLock, &ShLock, sizeof(SH_LOCK), &pShLock)) {
|
|
|
|
return;
|
|
}
|
|
|
|
MakeSpace(Indent);
|
|
|
|
dprintf("%sLock @ %08x\n", Space, pShLock);
|
|
|
|
RestoreSpace(Indent);
|
|
|
|
DumpFileLockInfo(&ShLock.LockInfo, Indent);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DumpFileLock(
|
|
PVOID pFileLock
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Dump the fsrtl FILE_LOCK structure at debugee
|
|
|
|
Arguments:
|
|
|
|
pFileLock - debugee address of FILE_LOCK
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
FILE_LOCK FileLock;
|
|
FILE_LOCK_INFO FileLockInfo, *pFileLockInfo;
|
|
LOCK_INFO LockInfo, *pLockInfo;
|
|
ULONG Count;
|
|
|
|
if (!ReadAtAddress(pFileLock, &FileLock, sizeof(FILE_LOCK), &pFileLock)) {
|
|
|
|
return;
|
|
}
|
|
|
|
dprintf("FileLock @ %08x\n"
|
|
"FastIoIsQuestionable = %c\n"
|
|
"CompletionRoutine = %08x\n"
|
|
"UnlockRoutine = %08x\n"
|
|
"LastReturnedLock = %08x\n",
|
|
pFileLock,
|
|
FileLock.FastIoIsQuestionable ? 'T':'F',
|
|
FileLock.CompleteLockIrpRoutine,
|
|
FileLock.UnlockRoutine,
|
|
FileLock.LastReturnedLock);
|
|
|
|
if (FileLock.LastReturnedLock != NULL) {
|
|
|
|
//
|
|
// We never reset the enumeration info, so it can be out of date ...
|
|
//
|
|
|
|
dprintf("LastReturnedLockInfo:\n");
|
|
DumpFileLockInfo(&FileLockInfo, 0);
|
|
}
|
|
|
|
if (FileLock.LockInformation == NULL) {
|
|
|
|
dprintf("No Locks\n");
|
|
return;
|
|
|
|
} else {
|
|
|
|
if (!ReadAtAddress(FileLock.LockInformation, &LockInfo, sizeof(LOCK_INFO), &pLockInfo)) {
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
dprintf("LowestLockOffset = %08x\n\n", LockInfo.LowestLockOffset);
|
|
|
|
Count = DumpSplayTree(LockInfo.LockQueue.SharedLockTree, DumpSharedNode);
|
|
|
|
if (!Count) {
|
|
|
|
dprintf("No Shared Locks\n");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
Count = DumpSplayTree(LockInfo.LockQueue.ExclusiveLockTree, DumpExclusiveNode);
|
|
|
|
if (!Count) {
|
|
|
|
dprintf("No Exclusive Locks\n");
|
|
}
|
|
}
|
|
|
|
|
|
DECLARE_API( filelock )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump file locks
|
|
|
|
Arguments:
|
|
|
|
arg - <Address>
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PVOID FileLock = NULL;
|
|
|
|
RtlFillMemory(Space, sizeof(Space), ' ');
|
|
|
|
if (sscanf(args, "%lx", &FileLock) != 1) {
|
|
|
|
//
|
|
// No args
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We raise out if the user whacketh control-c
|
|
//
|
|
|
|
__try {
|
|
|
|
DumpFileLock(FileLock);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
NOTHING;
|
|
}
|
|
|
|
return;
|
|
}
|