/*++

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"

//
//  Common node type codes
// 

#define NTFS_NTC_SCB_DATA 0x705
#define FAT_NTC_FCB 0x502 

//
//  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(
    ULONG64 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);

    InitTypeRead(pFileLockInfo, FILE_LOCK_INFO);
    dprintf("%sStart = %08I64x  Length = %08I64x  End    = %08I64x (%s)\n"
            "%sKey   = %08x   FileOb = %08p   ProcId = %08p\n",
            Space,
            ReadField(StartingByte),
            ReadField(Length),
            ReadField(EndingByte),
            (ReadField(ExclusiveLock) ? "Ex":"Sh"),
            Space,
            (ULONG) ReadField(Key),
            ReadField(FileObject),
            ReadField(ProcessId));

    RestoreSpace(Indent);
}

__inline
ULONG64
ExLockAddress(
    ULONG64 ExLockSplayLinks
    )
{
    static ULONG Off=0, GotOff=0;

    if (!GotOff) {
        if (!GetFieldOffset("nt!_EX_LOCK", "Links", &Off))
            GotOff = TRUE;
    }
    return ExLockSplayLinks ?
                ( ExLockSplayLinks - Off ) : 0;
}

VOID
DumpExclusiveNode(
    ULONG64 ExclusiveNodeSplayLinks,
    ULONG Indent
    )
/*++

    Purpose:

        Dump an exclusive lock node

    Arguments:

        ExclusiveNodeSplayLinks     - splay links of an exclusive node

        Indent                      - indent level to use

    Return:

        None

--*/
{
    ULONG64 Parent, pExLock;
    ULONG Off;

    pExLock = ExLockAddress(ExclusiveNodeSplayLinks);

    if (GetFieldValue(pExLock, "nt!_EX_LOCK", "Links.Parent", Parent)) {
        dprintf("Cannot read nt!_EX_LOCK at %p.\n", pExLock);
        return;
    }

    MakeSpace(Indent);

    InitTypeRead(pExLock, EX_LOCK);
    dprintf("%sLock @ %08x ("
            "P = %08x  R = %08x  L = %08x)\n",
            Space,
            pExLock,
            ExLockAddress(Parent),
            ExLockAddress(ReadField(Links.RightChild)),
            ExLockAddress(ReadField(Links.LeftChild)));

    RestoreSpace(Indent);

    GetFieldOffset("nt!_EX_LOCK", "LockInfo", &Off);
    DumpFileLockInfo(pExLock + Off, Indent);
}

__inline
ULONG64 
LockTreeAddress(
    ULONG64 LockTreeSplayLinks
    )
{
    static ULONG Off=0, GotOff=0;

    if (!GotOff) {
        if (GetFieldOffset("nt!_LOCKTREE_NODE", "Links", &Off))
            GotOff = TRUE;
    }
    return LockTreeSplayLinks ?
                ( LockTreeSplayLinks - Off ) : 0;
}

VOID
DumpSharedNode(
    ULONG64 SharedNodeSplayLinks,
    ULONG Indent
    )
/*++

    Purpose:

        Dump a shared lock node

    Arguments:

        SharedNodeSplayLinks        - splay links of an exclusive node

        Indent                      - indent level to use

    Return:

        None

--*/
{
    ULONG64 pLockTreeNode;
    ULONG64 pShLock;
    ULONG64 pLink, Next;
    ULONG   Off, LockInfoOff;

    pLockTreeNode = LockTreeAddress(SharedNodeSplayLinks);

    if (GetFieldValue(pLockTreeNode, "nt!_LOCKTREE_NODE", "Locks.Next", Next)) {
        dprintf("Cannot read nt!_LOCKTREE_NODE at %p\n", pLockTreeNode);
        return;
    }

    MakeSpace(Indent);

    InitTypeRead(pLockTreeNode, nt!_LOCKTREE_NODE);
    dprintf("%sLockTreeNode @ %08p ("
            "P = %08p  R = %08p  L = %08p)%s\n",
            Space,
            pLockTreeNode,
            LockTreeAddress(ReadField(Links.Parent)),
            LockTreeAddress(ReadField(Links.RightChild)),
            LockTreeAddress(ReadField(Links.LeftChild)),
            (ReadField(HoleyNode) ? " (Holey)" : ""));

    RestoreSpace(Indent);

    GetFieldOffset("nt!_SH_LOCK", "Link", &Off);
    GetFieldOffset("nt!_SH_LOCK", "LockInfo", &LockInfoOff);
    for (pLink = Next;
         pLink;
         pLink = Next) {

        CheckForBreak();

        pShLock = ( pLink - Off);

        if (GetFieldValue(pShLock, "nt!_SH_LOCK", "Link.Next", Next)) {
            dprintf("Cannot read nt!_SH_LOCK AT %p.\n", pShLock);
            return;
        }
    
        MakeSpace(Indent);

        dprintf("%sLock @ %08p\n", Space, pShLock);

        RestoreSpace(Indent);

        DumpFileLockInfo(pShLock + LockInfoOff, Indent);
    }
}

VOID
DumpFileLock(
    ULONG64 pFileLock
    )
/*++

    Purpose:

        Dump the fsrtl FILE_LOCK structure at debugee

    Arguments:

        pFileLock   - debugee address of FILE_LOCK

    Return:

        None

--*/
{
    ULONG64 pFileLockInfo;
    ULONG64 pLockInfo;
    ULONG Count;
    ULONG64 LastReturnedLock, LockInformation, LowestLockOffset;
    ULONG64 SharedLockTree, ExclusiveLockTree;

    if (GetFieldValue(pFileLock, "FILE_LOCK", "LastReturnedLock", LastReturnedLock)) {
        dprintf("Cannot read FILE_LOCK at %p\n", pFileLock);
        return;
    }

    InitTypeRead(pFileLock, FILE_LOCK);
    dprintf("FileLock @ %08p\n"
            "FastIoIsQuestionable = %c\n"
            "CompletionRoutine    = %08p\n"
            "UnlockRoutine        = %08p\n"
            "LastReturnedLock     = %08p\n",
            pFileLock,
            ReadField(FastIoIsQuestionable) ? 'T':'F',
            ReadField(CompleteLockIrpRoutine),
            ReadField(UnlockRoutine),
            LastReturnedLock);
    
    LockInformation = ReadField(LockInformation);

    if (LastReturnedLock != 0) {
        ULONG Off;

        //
        //  We never reset the enumeration info, so it can be out of date ...
        //

        GetFieldOffset("FILE_LOCK", "LastReturnedLockInfo", &Off);
        dprintf("LastReturnedLockInfo:\n");
        DumpFileLockInfo(pFileLock + Off, 0);
    }

    if (LockInformation == 0) {

        dprintf("No Locks\n");
        return;

    } else {

        if (GetFieldValue(LockInformation, "nt!_LOCK_INFO", "LowestLockOffset", LowestLockOffset)) {
            dprintf("Canot read nt!_LOCK_INFO at %p\n", LockInformation);
            return;
        }
    }

    dprintf("LowestLockOffset     = %08p\n\n", LowestLockOffset);

    GetFieldValue(LockInformation, "nt!_LOCK_INFO", "LockQueue.SharedLockTree", SharedLockTree);
    GetFieldValue(LockInformation, "nt!_LOCK_INFO", "LockQueue.ExclusiveLockTree", ExclusiveLockTree);
    
    Count = DumpSplayTree(SharedLockTree, DumpSharedNode);

    if (!Count) {

        dprintf("No Shared Locks\n");
    }

    dprintf("\n");

    Count = DumpSplayTree(ExclusiveLockTree, DumpExclusiveNode);

    if (!Count) {

        dprintf("No Exclusive Locks\n");
    }
}


DECLARE_API( filelock )
/*++

Routine Description:

    Dump file locks

Arguments:

    arg - <Address>

Return Value:

    None

--*/
{
    ULONG64 FileLock = 0;
    CSHORT NodeType = 0;
    CSHORT FileType = 0;
    ULONG64 FsContext = 0;
    ULONG Offset;

    RtlFillMemory(Space, sizeof(Space), ' ');


    if ((FileLock = GetExpression(args)) == 0) {

        //
        //  No args
        //

        return E_INVALIDARG;
    }

    //
    //  We raise out if the user whacketh control-c
    //

    __try {

        //
        //  Test for fileobject
        //  

        GetFieldValue( FileLock, "nt!_FILE_OBJECT", "Type", FileType );

        if (FileType == IO_TYPE_FILE) {

            //
            //  its really a fileobject so grab the fscontext
            // 

            if (!GetFieldValue( FileLock, "nt!_FILE_OBJECT", "FsContext", FsContext )) {
                GetFieldValue( FsContext, "nt!_FSRTL_COMMON_FCB_HEADER", "NodeTypeCode", NodeType );

                dprintf( "%x\n", NodeType );

                if (NodeType == NTFS_NTC_SCB_DATA) {
                    GetFieldValue( FsContext, "ntfs!_SCB", "ScbType.Data.FileLock", FileLock );
                } else if (NodeType == FAT_NTC_FCB) {
                    GetFieldOffset( "fastfat!_FCB",  "Specific", &Offset );
                    dprintf( "Offset: 0x%x\n", Offset );
                    FileLock = FsContext + Offset;
                } else {
                    dprintf( "Unknown fscontext - you'll have to find the filelock within the fileobject manually\n" );
                    return S_OK;
                }
            }

            if (FileLock == 0) {
                dprintf( "There is no filelock in this fileobject\n" );
                return S_OK;
            }
        }

        DumpFileLock(FileLock);

    } __except (EXCEPTION_EXECUTE_HANDLER) {

        NOTHING;
    }

    return S_OK;
}