/*++

    Copyright (c) 1989-2000  Microsoft Corporation

    Module Name:

        sdbapi.c

    Abstract:

        BUGBUG: This module implements ...

    Author:

        dmunsil     created     sometime in 1999

    Revision History:

        several people contributed (vadimb, clupu, ...)

--*/

#include "sdbp.h"
#include "initguid.h"

DEFINE_GUID(GUID_SYSMAIN_SDB, 0x11111111, 0x1111, 0x1111, 0x11, 0x11, 0x11, 0x11, \
            0x11, 0x11, 0x11, 0x11);
DEFINE_GUID(GUID_APPHELP_SDB, 0x22222222, 0x2222, 0x2222, 0x22, 0x22, 0x22, 0x22, \
            0x22, 0x22, 0x22, 0x22);
DEFINE_GUID(GUID_SYSTEST_SDB, 0x33333333, 0x3333, 0x3333, 0x33, 0x33, 0x33, 0x33, \
            0x33, 0x33, 0x33, 0x33);
DEFINE_GUID(GUID_DRVMAIN_SDB, 0xF9AB2228, 0x3312, 0x4A73, 0xB6, 0xF9, 0x93, 0x6D, \
            0x70, 0xE1, 0x12, 0xEF);
DEFINE_GUID(GUID_MSIMAIN_SDB, 0xD8FF6D16, 0x6A3A, 0x468A, 0x8B, 0x44, 0x01, 0x71, \
            0x4D, 0xDC, 0x49, 0xEA);
DEFINE_GUID(GUID_APPHELP_SP_SDB, 0x44444444, 0x4444, 0x4444, 0x44, 0x44, 0x44, 0x44, \
            0x44, 0x44, 0x44, 0x44);

#ifdef _DEBUG_SPEW

//
// Shim Debug output support
//
int g_iShimDebugLevel = SHIM_DEBUG_UNINITIALIZED;

DBGLEVELINFO g_rgDbgLevelInfo[DEBUG_LEVELS] = {
    { "Err",   sdlError    },
    { "Warn",  sdlWarning  },
    { "Fail",  sdlFail     },
    { "Info",  sdlInfo     }
};

PCH g_szDbgLevelUser = "User";

#endif // _DEBUG_SPEW



BOOL
SdbpInitializeSearchDBContext(
    PSEARCHDBCONTEXT pContext
    );


#if defined(KERNEL_MODE) && defined(ALLOC_PRAGMA)
#pragma alloc_text(PAGE, ShimExceptionHandler)
#pragma alloc_text(PAGE, SdbpCreateSearchDBContext)
#pragma alloc_text(PAGE, SdbpInitializeSearchDBContext)
#pragma alloc_text(PAGE, SdbpReleaseSearchDBContext)
#pragma alloc_text(PAGE, SdbpCheckForMatch)
#pragma alloc_text(PAGE, SdbpSearchDB)
#pragma alloc_text(PAGE, SdbpCreateSearchDBContext)
#pragma alloc_text(PAGE, SdbGetDatabaseMatch)
#pragma alloc_text(PAGE, SdbQueryData)
#pragma alloc_text(PAGE, SdbQueryDataEx)
#pragma alloc_text(PAGE, SdbReadEntryInformation)
#pragma alloc_text(PAGE, PrepareFormatForUnicode)
#pragma alloc_text(PAGE, ShimDbgPrint)
#endif

#if DBG
const BOOL g_bDBG = TRUE;
#else
const BOOL g_bDBG = FALSE;
#endif

//
// Exception handler
//

ULONG
ShimExceptionHandler(
    PEXCEPTION_POINTERS pexi,
    char*               szFile,
    DWORD               dwLine
    )
{
#ifndef KERNEL_MODE // in kmode exceptions won't work anyway

    DBGPRINT((sdlError,
              "ShimExceptionHandler",
              "Shim Exception %#x in module \"%hs\", line %d, at address %#p. flags:%#x. !exr %#p !cxr %#p",
              pexi->ExceptionRecord->ExceptionCode,
              szFile,
              dwLine,
              CONTEXT_TO_PROGRAM_COUNTER(pexi->ContextRecord),
              pexi->ExceptionRecord->ExceptionFlags,
              pexi->ExceptionRecord,
              pexi->ContextRecord));

    //
    // Special-case stack overflow exception which is likely to occur due to
    // low memory conditions during stress. The process is dead anyway so we
    // will not handle this exception.
    //
    if (pexi->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
        return EXCEPTION_CONTINUE_SEARCH;
    }

#if DBG
    SDB_BREAK_POINT();
#endif // DBG

#endif // KERNEL_MODE

    return EXCEPTION_EXECUTE_HANDLER;
}

BOOL
SdbpResolveAndSplitPath(
    IN  DWORD   dwFlags,        // context flags (SEARCHDBF_NO_LFN in particular)
    IN  LPCTSTR szFullPath,     // a full UNC or DOS path & filename, "c:\foo\myfile.ext"
    OUT LPTSTR  szDir,          // the drive and dir portion of the filename "c:\foo\"
    OUT LPTSTR  szName,         // the filename portion "myfile"
    OUT LPTSTR  szExt           // the extension portion ".ext"
    )
/*++
    Return: TRUE on success, FALSE otherwise.

    Desc:   This function takes a full path and splits it into pieces ala splitpath,
            but also converts short file names to long names.
            NOTE: The caller is responsible for allocating enough space
                  for the passed-in strings to take any portion of the path.
                  For safety, allocate at least MAX_PATH WCHARS for each piece.
--*/
{
    TCHAR* szCursor;
    TCHAR  szLongFileName[MAX_PATH + 1];
    BOOL   bFound;
    DWORD  i;

    assert(szFullPath && szDir && szName && szExt);

    //
    // Parse the directory.
    //
    szDir[0] = _T('\0');

    szCursor = _tcsrchr(szFullPath, _T('\\')); // last backslash please
    if (szCursor == NULL) {
        szCursor = (LPTSTR)szFullPath;
    } else {
        _tcsncpy(szDir, szFullPath, szCursor - szFullPath + 1);
        szDir[szCursor - szFullPath + 1] = _T('\0');
    }

#ifndef KERNEL_MODE

    //
    // Make sure we're using the long filename
    //
    if (dwFlags & SEARCHDBF_NO_LFN) {
        assert(_tcslen(szCursor) < CHARCOUNT(szLongFileName));
        _tcscpy(szLongFileName, szCursor);
    } else {
        if (!SdbpGetLongFileName(szFullPath, szLongFileName)) {
            return FALSE;
        }
    }

#else // KERNEL_MODE

    //
    // When we are in kernel mode, our file name is always considered to be "long".
    // At this point szCursor points to the last '\\' or to the beginning of the name.
    //
    if (*szCursor == _T('\\')) {
        ++szCursor;
    }

    //
    // Make sure that we have enough room for the name.
    //
    assert(wcslen(szCursor) < CHARCOUNT(szLongFileName));
    wcscpy(szLongFileName, szCursor);

#endif // KERNEL_MODE

    //
    // Parse name & extension
    //
    szExt[0]  = _T('\0');
    szName[0] = _T('\0');

    //
    // Within the long file name find the last dot
    //
    szCursor = _tcsrchr(szLongFileName, _T('.'));
    
    if (szCursor != NULL) {
        _tcsncpy(szName, szLongFileName, szCursor - szLongFileName);
        szName [szCursor-szLongFileName] = _T('\0');
        _tcscpy(szExt, szCursor);
    } else {
        _tcscpy(szName, szLongFileName);
    }

    return TRUE;
}


BOOL
SdbpCreateSearchDBContext(
    PSEARCHDBCONTEXT pContext,
    LPCTSTR          szPath,
    LPCTSTR          szModuleName,
    LPCTSTR          pEnvironment
    )
/*++
    Return: TRUE - search db context was successfully created

    Desc:   This function creates context for searching the database, in particular, the
            context is initalized with the path of probable local database location,
            executable path is broken down into containing directory and the filename part.
--*/
{
    DWORD  dwPathLen;
    BOOL   bReturn     = FALSE;
    TCHAR* szDirectory = NULL;
    TCHAR* szExt       = NULL;
    TCHAR* szFullName  = NULL;
    TCHAR* szFileName  = NULL;
    TCHAR* szModule    = NULL;
    
    assert(NULL != szPath);
    assert(NULL != pContext);

    dwPathLen = _tcslen(szPath);

    //
    // Allocate enough to guarantee our strings will not overflow
    //
    szDirectory = SdbAlloc((dwPathLen + 1) * sizeof(TCHAR));
    szFullName  = SdbAlloc((_MAX_PATH + 1) * sizeof(TCHAR));

    if (szModuleName) {
        szModule = SdbAlloc((_tcslen(szModuleName) + 1) * sizeof(TCHAR));
        if (!szModule) {
            DBGPRINT((sdlError,
                      "SdbpCreateSearchDBContext",
                      "Unable to allocate memory for szModule.\n"));
            goto out;
        }
        _tcscpy(szModule, szModuleName);
    }

    STACK_ALLOC(szExt,      (_MAX_PATH + 1) * sizeof(TCHAR));
    STACK_ALLOC(szFileName, (_MAX_PATH + 1) * sizeof(TCHAR));

    if (!szDirectory || !szExt || !szFullName || !szFileName || !pContext) {
        DBGPRINT((sdlError,
                  "SdbpCreateSearchDBContext",
                  "Unable to allocate memory for strings.\n"));
        goto out;
    }

    if (!SdbpResolveAndSplitPath(pContext->dwFlags, szPath, szDirectory, szFileName, szExt)) {
        DBGPRINT((sdlError,
                  "SdbpCreateSearchDBContext",
                  "Unable to parse executable path for \"%s\".\n",
                  szPath));
        goto out;
    }

    _tcscpy(szFullName, szFileName);
    _tcscat(szFullName, szExt);

    pContext->pEnvironment = pEnvironment;
    pContext->szDir        = szDirectory;
    pContext->szName       = szFullName; // fullname (filename + ext)
    pContext->szModuleName = szModule;

    //
    // We do not retain szExt (don't need it)
    //
    // Calculate this later -- implied by RtlZeroMemory statement above
    //
    pContext->pSearchParts     = NULL;
    pContext->szProcessHistory = NULL;

    bReturn = TRUE;

out:
    if (szExt != NULL) {
        STACK_FREE(szExt);
    }

    if (szFileName != NULL) {
        STACK_FREE(szFileName);
    }

    if (!bReturn) {

        if (szDirectory != NULL) {
            SdbFree(szDirectory);
        }
        if (szFullName != NULL) {
            SdbFree(szFullName);
        }

        if (szModule != NULL) {
            SdbFree(szModule);
        }
    }

    return bReturn;
}


BOOL
SdbpInitializeSearchDBContext(
    PSEARCHDBCONTEXT pContext
    )
/*++
    Return: TRUE - the context was successfully initialized with the process history
                   which was broken down into the separate search paths

    Desc:   This function prepares search context for use, obtaining and parsing process
            history into separate paths. The array of these search paths is used then
            by the caller to inquire about matching files that might be present in one
            of the these places.
            In Kernel mode use SEARCHDBF_NO_PROCESS_HISTORY flag within context
            it will include only the current exe path into the process history
--*/
{
    BOOL   bSuccess = TRUE;
    LPTSTR pszProcessHistory = NULL;

    if (pContext->pSearchParts != NULL) {
        return TRUE;
    }

    if (pContext->dwFlags & SEARCHDBF_NO_PROCESS_HISTORY) {

        pszProcessHistory = pContext->szProcessHistory;
        
        if (pszProcessHistory == NULL) {
            
            DWORD DirLen  = _tcslen(pContext->szDir);
            DWORD NameLen = _tcslen(pContext->szName);

            //
            // We create a temporary process history
            //
            pContext->szProcessHistory = SdbAlloc((DirLen + NameLen + 1) * sizeof(TCHAR));
            
            if (pContext->szProcessHistory == NULL) {
                DBGPRINT((sdlError,
                          "SdbpInitializeSearchDBContext",
                          "Failed to allocate buffer %d bytes\n",
                          (DirLen + NameLen + 1) * sizeof(TCHAR)));
                return FALSE;
            }

            pszProcessHistory = pContext->szProcessHistory;

            RtlMoveMemory(pszProcessHistory, pContext->szDir, DirLen * sizeof(TCHAR));
            RtlMoveMemory(pszProcessHistory + DirLen, pContext->szName, NameLen * sizeof(TCHAR));

            *(pszProcessHistory + DirLen + NameLen) = TEXT('\0');
        }

        //
        // When we are here -- we either have a process history or we just
        // created it consisting of a single search item
        //

    } else {

#ifndef KERNEL_MODE
        if (pContext->szProcessHistory == NULL) {

            pContext->szProcessHistory = GetProcessHistory(pContext->pEnvironment,
                                                           pContext->szDir,
                                                           pContext->szName);
            if (pContext->szProcessHistory == NULL) {
                DBGPRINT((sdlError,
                          "SdbpInitializeSearchDBContext",
                          "Failed to retrieve process history\n"));
                return FALSE;
            }
        }

        pszProcessHistory = pContext->szProcessHistory;
#else
        //
        // This is the case with KERNEL_MODE. YOU HAVE TO SET SEARCHDBF_NO_PROCESS_HISTORY
        //
        assert(FALSE);
        pszProcessHistory = NULL;
#endif
    }

    //
    // At this point pszProcessHistory is NOT NULL
    //
    assert(pszProcessHistory != NULL);

    DBGPRINT((sdlInfo,
              "SdbpInitializeSearchDBContext",
              "Using Process History: \"%s\"\n",
              pszProcessHistory));

    bSuccess = SdbpCreateSearchPathPartsFromPath(pszProcessHistory, &pContext->pSearchParts);

    if (bSuccess) {
        pContext->dwFlags |= SEARCHDBF_INITIALIZED;
    }

    return bSuccess;
}

void
SdbpReleaseSearchDBContext(
    PSEARCHDBCONTEXT pContext
    )
/*++
    Return: void

    Desc:   Resets search DB context, frees memory allocated for each of the
            temporary buffers.
--*/
{
    if (pContext == NULL) {
        return;
    }
    
    if (pContext->szProcessHistory != NULL) {
        SdbFree(pContext->szProcessHistory);
        pContext->szProcessHistory = NULL;
    }

    if (pContext->pSearchParts != NULL) {
        SdbFree(pContext->pSearchParts);
        pContext->pSearchParts = NULL;
    }

    if (pContext->szDir != NULL) {
        SdbFree(pContext->szDir);
        pContext->szDir = NULL;
    }

    if (pContext->szName != NULL) {
        SdbFree(pContext->szName);
        pContext->szName = NULL;
    }

    if (pContext->szModuleName != NULL) {
        SdbFree(pContext->szModuleName);
        pContext->szModuleName = NULL;
    }
}

BOOL
SdbpIsExeEntryEnabled(
    IN  PDB    pdb,
    IN  TAGID  tiExe,
    OUT GUID*  pGUID,
    OUT DWORD* pdwFlags
    )
{
    TAGID tiExeID;
    DWORD i;
    BOOL  fSuccess = FALSE;

    //
    // Get the EXE's GUID
    //
    tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);

    if (tiExeID == TAGID_NULL) {
        DBGPRINT((sdlError,
                  "SdbpIsExeEntryEnabled",
                  "Failed to read TAG_EXE_ID for tiExe 0x%x !\n",
                  tiExe));
        goto error;
    }

    if (!SdbReadBinaryTag(pdb, tiExeID, (PBYTE)pGUID, sizeof(GUID))) {
        DBGPRINT((sdlError,
                  "SdbpIsExeEntryEnabled",
                  "Failed to read the GUID for tiExe 0x%x !\n",
                  tiExe));
        goto error;
    }

    if (!SdbGetEntryFlags(pGUID, pdwFlags)) {
        DBGPRINT((sdlWarning,
                  "SdbpIsExeEntryEnabled",
                  "No flags for tiExe 0x%lx\n",
                  tiExe));

        *pdwFlags = 0;
    } else {
        DBGPRINT((sdlInfo,
                  "SdbpIsExeEntryEnabled",
                  "Retrieved flags for this app 0x%x.\n",
                  *pdwFlags));
    }

    if (!(*pdwFlags & SHIMREG_DISABLE_SHIM)) {
        fSuccess = TRUE;
    }

error:

    return fSuccess;
}

#define EXTRA_BUF_SPACE (16 * sizeof(TCHAR))

//
// Matching an entry:
//
// 1. We check whether each file exists by calling SdbGetFileInfo
// 2. Each file's info is stored in FILEINFOCHAINITEM (allocated on the stack) - such as pointer
//    to the actual FILEINFO structure (stored in file attribute cache) and tiMatch denoting
//    the entry in the database for a given MATCHING_FILE
// 3. After we have verified that all the matching files do exist -- we proceed to walk the
//    chain of FILEINFOCHAINITEM structures and call SdbCheckAllAttributes to check on all the
//    other attributes of the file
// 4. Cleanup: File attribute cache is destroyed when the database is closed via call to
//    SdbCleanupAttributeMgr
// 5. No cleanup is needed for FILEINFOCHAINITEM structures (they are allocated on the stack and
//    just "go away")
//
//

typedef struct tagFILEINFOCHAINITEM {
    PVOID pFileInfo;                        // pointer to the actual FILEINFO
                                            // structure (from attribute cache)
    TAGID tiMatch;                          // matching entry in the database
    
    struct tagFILEINFOCHAINITEM* pNextItem; // pointer to the next matching file

} FILEINFOCHAINITEM, *PFILEINFOCHAINITEM;


BOOL
SdbpCheckForMatch(
    IN  HSDB                hSDB,        // context ptr
    IN  PDB                 pdb,         // pdb to get match criteria from
    IN  TAGID               tiExe,       // TAGID of exe record to get match criteria from
    IN  PSEARCHDBCONTEXT    pContext,    // search db context (includes name/path)
    OUT PMATCHMODE          pMatchMode,   // the match mode of this EXE
    OUT GUID*               pGUID,
    OUT DWORD*              pdwFlags
    )
/*++
    Return: TRUE if match is good, FALSE if this EXE doesn't match.

    Desc:   Given an EXE tag and a name and dir, checks  the DB for MATCHING_FILE
            tags, and checks all the matching info available for each the
            files listed. If all the files check out, returns TRUE. If any of
            the files don't exist, or don't match on one of the given
            criteria, returns FALSE.
--*/
{
    BOOL                bReturn = FALSE;
    BOOL                bMatchLogicNot = FALSE;
    BOOL                bAllAttributesMatch = FALSE;
    TAGID               tiMatch;
    TCHAR*              szTemp = NULL;
    LONG                nFullPathBufSize = 0;
    LONG                nFullPathReqBufSize = 0;
    LPTSTR              szFullPath = NULL;
    LONG                i;
    LONG                NameLen = _tcslen(pContext->szName);
    LONG                MatchFileLen;
    PSEARCHPATHPARTS    pSearchPath;
    PSEARCHPATHPART     pSearchPathPart;
    NTSTATUS            Status;
    PFILEINFOCHAINITEM  pFileInfoItem          = NULL;
    PFILEINFOCHAINITEM  pFileInfoItemList      = NULL;  // holds the list of matching files
                                                        // which were found
    PFILEINFOCHAINITEM  pFileInfoItemNext;              // holds the next item in the list
    PVOID               pFileInfo              = NULL;  // points to the current file's
                                                        // information structure
    BOOL                bDisableAttributeCache = FALSE; // will be set according to search

    TAGID               tiName, tiTemp, tiMatchLogicNot;
    TCHAR*              szMatchFile = NULL;
    HANDLE              hFileHandle; // handle for the file we're checking, optimization
    LPVOID              pImageBase;  // pointer to the image
    DWORD               dwImageSize = 0;
    WORD                wDefaultMatchMode;
    
    //
    // Check context's flags
    //
    if (pContext->dwFlags & SEARCHDBF_NO_ATTRIBUTE_CACHE) {
        bDisableAttributeCache = TRUE;
    }

    //
    // Loop through matching criteria.
    //
    tiMatch = SdbFindFirstTag(pdb, tiExe, TAG_MATCHING_FILE);

    while (tiMatch != TAGID_NULL) {

        tiMatchLogicNot = SdbFindFirstTag(pdb, tiMatch, TAG_MATCH_LOGIC_NOT);
        bMatchLogicNot = (tiMatchLogicNot != TAGID_NULL);

        tiName = SdbFindFirstTag(pdb, tiMatch, TAG_NAME);

        if (!tiName) {
            goto out;
        }

        szTemp = SdbGetStringTagPtr(pdb, tiName);

        if (szTemp == NULL) {
            DBGPRINT((sdlError,
                      "SdbpCheckForMatch",
                      "Failed to get the string from the database.\n"));
            goto out;
        }

        if (szTemp[0] == TEXT('*')) {
            //
            // This is a signal that we should use the exe name.
            //
            szMatchFile  = pContext->szName;
            MatchFileLen = NameLen;
            hFileHandle  = pContext->hMainFile;
            pImageBase   = pContext->pImageBase;
            dwImageSize  = pContext->dwImageSize;

        } else {

            szMatchFile  =  szTemp;
            MatchFileLen = _tcslen(szMatchFile);
            hFileHandle  = INVALID_HANDLE_VALUE;
            pImageBase   = NULL;
        }

        //
        // When searching for files, we look in all process' exe directories,
        // starting with the current process and working backwards through the process
        // tree.
        //

        //
        // See that the context is good...
        //
        if (!(pContext->dwFlags & SEARCHDBF_INITIALIZED)) {

            if (!SdbpInitializeSearchDBContext(pContext)) {
                DBGPRINT((sdlError,
                          "SdbpCheckForMatch",
                          "Failed to initialize SEARCHDBCONTEXT.\n"));
                goto out;
            }
        }

        pSearchPath = pContext->pSearchParts;

        assert(pSearchPath != NULL);

        for (i = 0; i < (LONG)pSearchPath->PartCount && NULL == pFileInfo; ++i) {

            pSearchPathPart = &pSearchPath->Parts[i];

            //
            // There are two ways to specify a matching file: A relative path
            // from the EXE, or an absolute path. To specify an absolute path,
            // an environment variable (like "%systemroot%") must be used
            // as the base of the path. Therefore, we check for the first character
            // of the matching file to be % and if so, we assume that it is an
            // absolute path.
            //
#ifndef KERNEL_MODE
            if (szMatchFile[0] == TEXT('%')) {
                //
                // Absolute path. Contains environment variables, get expanded size.
                //
                nFullPathReqBufSize = SdbExpandEnvironmentStrings(szMatchFile, NULL, 0);

            } else
#endif // KERNEL_MODE
            {
                //
                // Relative path. Determine size of full path.
                //
                nFullPathReqBufSize = (pSearchPathPart->PartLength + MatchFileLen + 1) * sizeof(TCHAR);
            }

            if (nFullPathBufSize < nFullPathReqBufSize) {
                //
                // Need to realloc the buffer.
                //
                if (szFullPath == NULL) {
                    nFullPathBufSize = _MAX_PATH * sizeof(TCHAR);

                    if (nFullPathReqBufSize >= nFullPathBufSize) {
                        nFullPathBufSize = nFullPathReqBufSize + EXTRA_BUF_SPACE;
                    }
                } else {
                    STACK_FREE(szFullPath);
                    nFullPathBufSize = nFullPathReqBufSize + EXTRA_BUF_SPACE;
                }

                STACK_ALLOC(szFullPath, nFullPathBufSize);
            }

            if (szFullPath == NULL) {
                DBGPRINT((sdlError,
                          "SdbpCheckForMatch",
                          "Failed to allocate %d bytes for FullPath.\n",
                          nFullPathBufSize));
                goto out;
            }

#ifndef KERNEL_MODE
            if (szMatchFile[0] == TEXT('%')) {
                //
                // Absolute Path. Path contains environment variables, expand it.
                //
                if (!SdbExpandEnvironmentStrings(szMatchFile, szFullPath, nFullPathBufSize)) {
                    DBGPRINT((sdlError,
                              "SdbpCheckForMatch",
                              "SdbExpandEnvironmentStrings failed to expand strings for %s.\n",
                              szMatchFile));
                    goto out;
                }

            } else
#endif  // KERNEL_MODE
            {
                //
                // Relative path. Concatenate EXE directory with specified relative path.
                //
                RtlMoveMemory(szFullPath,
                              pSearchPathPart->pszPart,
                              pSearchPathPart->PartLength * sizeof(TCHAR));

                RtlMoveMemory(szFullPath + pSearchPathPart->PartLength,
                              szMatchFile,
                              (MatchFileLen + 1) * sizeof(TCHAR));
            }

            pFileInfo = SdbGetFileInfo(hSDB,
                                       szFullPath,
                                       hFileHandle,
                                       pImageBase,
                                       dwImageSize, // this will be set ONLY if pImageBase != NULL
                                       bDisableAttributeCache);

            //
            // This is not a bug, attributes are cleaned up when the database
            // context is released.
            //
        }

        if (pFileInfo == NULL && !bMatchLogicNot) {
            DBGPRINT((sdlInfo,
                      "SdbpCheckForMatch",
                      "Matching file \"%s\" not found.\n",
                      szMatchFile));
            goto out;
        }

        //
        // Create and store a new FILEINFOITEM on the stack
        //
        STACK_ALLOC(pFileInfoItem, sizeof(*pFileInfoItem));

        if (pFileInfoItem == NULL) {
            DBGPRINT((sdlError,
                      "SdbpCheckForMatch",
                      "Failed to allocate %d bytes for FILEINFOITEM\n",
                      sizeof(*pFileInfoItem)));
            goto out;
        }

        pFileInfoItem->pFileInfo = pFileInfo;
        pFileInfoItem->tiMatch   = tiMatch;
        pFileInfoItem->pNextItem = pFileInfoItemList;
        pFileInfoItemList        = pFileInfoItem;

        //
        // We have the matching file.
        // Remember where it is for the second pass when we check all the file attributes.
        //
        tiMatch = SdbFindNextTag(pdb, tiExe, tiMatch);

        //
        // Reset the file matching. we don't touch this file again for now, it's info
        // is safely linked in pFileInfoItemList
        //
        pFileInfo = NULL;
    }

    //
    // We are still here. That means all the matching files have been found.
    // Check all the other attributes using fileinfoitemlist information.
    //

    pFileInfoItem = pFileInfoItemList;

    while (pFileInfoItem != NULL) {
        
        tiMatchLogicNot = SdbFindFirstTag(pdb, pFileInfoItem->tiMatch, TAG_MATCH_LOGIC_NOT);
        bMatchLogicNot = (tiMatchLogicNot != TAGID_NULL);

        if (pFileInfoItem->pFileInfo != NULL) {
            bAllAttributesMatch = SdbpCheckAllAttributes(hSDB,
                                                         pdb,
                                                         pFileInfoItem->tiMatch,
                                                         pFileInfoItem->pFileInfo);
        } else {
            bAllAttributesMatch = FALSE;
        }

        if (bAllAttributesMatch && bMatchLogicNot) {
            DBGPRINT((sdlInfo,
                      "SdbpCheckForMatch",
                      "All attributes match, but LOGIC=\"NOT\" was used which negates the match.\n"));
            goto out;
        }

        if (!bAllAttributesMatch && !bMatchLogicNot) {
            //
            // Debug output happened inside SdbpCheckAllAttributes, no
            // need for further spew here.
            //
            goto out;
        }

        //
        // Advance to the next item.
        //
        pFileInfoItem = pFileInfoItem->pNextItem;
    }

    //
    // It's a match! get the match mode
    //
    if (pMatchMode) {

        //
        //  Important: depending on a particular database, we may use a different mode if 
        //  there is match mode tag
        //
        //  For Custom DB: default is the all-additive mode
        //  For Main   DB: default is normal mode 
        //

#ifndef KERNEL_MODE        
        wDefaultMatchMode = SdbpIsMainPDB(hSDB, pdb) ? MATCHMODE_DEFAULT_MAIN : 
                                                       MATCHMODE_DEFAULT_CUSTOM;
#else  // KERNEL_MODE
        wDefaultMatchMode = MATCHMODE_DEFAULT_MAIN;
#endif // KERNEL_MODE
        
        tiTemp = SdbFindFirstTag(pdb, tiExe, TAG_MATCH_MODE);
        
        if (tiTemp) {
            pMatchMode->dwMatchMode = SdbReadWORDTag(pdb, tiTemp, wDefaultMatchMode);
        } else {
            pMatchMode->dwMatchMode = wDefaultMatchMode;
        }
    }

    bReturn = TRUE;

out:

    pFileInfoItem = pFileInfoItemList;

    while (pFileInfoItem != NULL) {

        pFileInfoItemNext = pFileInfoItem->pNextItem;

        if (pFileInfoItem->pFileInfo != NULL && bDisableAttributeCache) {
            SdbFreeFileInfo(pFileInfoItem->pFileInfo);
        }

        STACK_FREE(pFileInfoItem);
        pFileInfoItem = pFileInfoItemNext;
    }

    if (szFullPath != NULL) {
        STACK_FREE(szFullPath);
    }

    if (bReturn) {
        //
        // One last matching criteria: verify the entry is not disabled.
        //
        bReturn = SdbpIsExeEntryEnabled(pdb, tiExe, pGUID, pdwFlags);
    }

    return bReturn;
}

typedef enum _ADDITIVE_MODE {
    AM_NORMAL,
    AM_ADDITIVE_ONLY,
    AM_NO_ADDITIVE
} ADDITIVE_MODE, *PADDITIVE_MODE;

LPCTSTR 
SdbpFormatMatchModeType(
    DWORD dwMatchMode
    )
{
    LPCTSTR pszMatchMode;
    
    switch (dwMatchMode) {
    
    case MATCH_ADDITIVE:
        pszMatchMode = _T("Additive");
        break;
        
    case MATCH_EXCLUSIVE:
        pszMatchMode = _T("Exclusive");
        break;
        
    case MATCH_NORMAL:
        pszMatchMode = _T("Normal");
        break;
        
    default:
        pszMatchMode = _T("Unknown");
        break;
    }                

    return pszMatchMode;
}

LPCTSTR 
SdbpFormatMatchMode(
    PMATCHMODE pMatchMode
    )
{
static TCHAR szMatchMode[MAX_PATH];

    LPTSTR pszMatchMode = szMatchMode;
    int    nChars = CHARCOUNT(szMatchMode);
    int    nLen;

    nLen = _sntprintf(pszMatchMode,
                      nChars,
                      _T("0x%.2x%.2x [Mode: %s"), 
                      pMatchMode->Flags,
                      pMatchMode->Type,
                      SdbpFormatMatchModeType(pMatchMode->Type));
    if (nLen < 0) {
        goto eh;
    }
    
    nChars -= nLen;
    pszMatchMode += nLen;

eh:    

    //
    // Just in case, truncate
    //
    if (nLen < 0) {
        szMatchMode[CHARCOUNT(szMatchMode) - 1] = _T('\0');
    }

    return szMatchMode;
}

/*++
    SdbpCheckExe

    Checks a particular instance of an application in an SDB against for a match
    Information on the file is passed through pContext parameter

    result is returned in ptiExes

--*/

BOOL
SdbpCheckExe(
    IN  HSDB                hSDB,               //
    IN  PDB                 pdb,                //
    IN  TAGID               tiExe,              // tag for an exe in the database
    IN OUT PDWORD           pdwNumExes,         // returns (and passes in) the number of accumulated exe matches
    IN OUT PSEARCHDBCONTEXT pContext,           // information about the file which we match against
    IN  ADDITIVE_MODE       eMode,              // target Match mode, we filter entries based on this parameter
    IN  BOOL                bDebug,             // debug flag
    OUT PMATCHMODE          pMatchMode,         // returns match mode used if success
    OUT TAGID*              ptiExes,            // returns another entry in array of matched exes
    OUT GUID*               pGUID,              // matched exe id
    OUT DWORD*              pdwFlags            // matched exe flags
    )
{
    BOOL      bSuccess = FALSE;
    TAGID     tiAppName = TAGID_NULL;
    LPTSTR    szAppName = NULL;
    LPCTSTR   pszMatchMode = NULL;
    TAGID     tiRuntimePlatform;
    DWORD     dwRuntimePlatform;
    TAGID     tiOSSKU;
    DWORD     dwOSSKU;
    TAGID     tiSP;
    DWORD     dwSPMask;
    MATCHMODE MatchMode;

    //
    // For debug purposes we'd like to know the name of the app, which
    // is more useful when the exe name is, say, AUTORUN.EXE or SETUP.EXE
    //
    tiAppName = SdbFindFirstTag(pdb, tiExe, TAG_APP_NAME);

    if (tiAppName != TAGID_NULL) {
        szAppName = SdbGetStringTagPtr(pdb, tiAppName);
    }

    DBGPRINT((sdlInfo, "SdbpCheckExe", "---------\n"));
    DBGPRINT((sdlInfo,
              "SdbpCheckExe",
              "Index entry found for App: \"%s\" Exe: \"%s\"\n",
              szAppName,
              pContext->szName));

#ifndef KERNEL_MODE

    //
    // Check whether this exe is good for this platform first.
    //
    tiRuntimePlatform = SdbFindFirstTag(pdb, tiExe, TAG_RUNTIME_PLATFORM);
    
    if (tiRuntimePlatform) {
        dwRuntimePlatform = SdbReadDWORDTag(pdb, tiRuntimePlatform, RUNTIME_PLATFORM_ANY);
        
        //
        // Check for the platform match
        //
        if (!SdbpCheckRuntimePlatform(hSDB, szAppName, dwRuntimePlatform)) {
            //
            // Not the right platform. Debug spew would have occured in SdbpCheckRuntimePlatform
            //
            goto out;
        }
    }

    tiOSSKU = SdbFindFirstTag(pdb, tiExe, TAG_OS_SKU);
    
    if (tiOSSKU) {
        
        dwOSSKU = SdbReadDWORDTag(pdb, tiOSSKU, OS_SKU_ALL);

        if (dwOSSKU != OS_SKU_ALL) {

            PSDBCONTEXT pDBContext = (PSDBCONTEXT)hSDB;

            //
            // Check for the OS SKU match
            //
            if (!(dwOSSKU & pDBContext->dwOSSKU)) {
                DBGPRINT((sdlInfo,
                          "SdbpCheckExe",
                          "OS SKU Mismatch for \"%s\" Database(0x%lx) vs 0x%lx\n",
                          (szAppName ? szAppName : TEXT("Unknown")),
                          dwOSSKU,
                          pDBContext->dwOSSKU));
                goto out;
            }
        }
    }

    tiSP = SdbFindFirstTag(pdb, tiExe, TAG_OS_SERVICE_PACK);
    
    if (tiSP) {
        dwSPMask = SdbReadDWORDTag(pdb, tiSP, 0xFFFFFFFF);

        if (dwSPMask != 0xFFFFFFFF) {

            PSDBCONTEXT pDBContext = (PSDBCONTEXT)hSDB;

            //
            // Check for the OS SKU match
            //
            if (!(dwSPMask & pDBContext->dwSPMask)) {
                DBGPRINT((sdlInfo,
                          "SdbpCheckExe",
                          "OS SP Mismatch for \"%s\" Database(0x%lx) vs 0x%lx\n",
                          (szAppName ? szAppName : TEXT("Unknown")),
                          dwSPMask,
                          pDBContext->dwSPMask));
                goto out;
            }
        }
    }
#endif // KERNEL_MODE

    if (!SdbpCheckForMatch(hSDB, pdb, tiExe, pContext, &MatchMode, pGUID, pdwFlags)) {
        goto out;
    }
    
    if (eMode == AM_ADDITIVE_ONLY && MatchMode.Type != MATCH_ADDITIVE) {
        goto out;
    }
    
    if (eMode == AM_NO_ADDITIVE && MatchMode.Type == MATCH_ADDITIVE) {
        goto out;
    }

    pszMatchMode = SdbpFormatMatchMode(&MatchMode);

    //
    // If we're in debug mode, don't actually put the ones we find on the
    // list, just put up an error.
    //
    if (bDebug) {

        //
        // We are in debug mode, do not add the match
        //
        DBGPRINT((sdlError,
                  "SdbpCheckExe",
                  "-----------------------------------------------------\n"));

        DBGPRINT((sdlError|sdlLogPipe,
                  "SdbpCheckExe",
                  "!!!! Multiple matches! App: '%s', Exe: '%s',  Mode: %s\n",
                  hSDB,  // so that the pipe would use hPipe if needed
                  szAppName,
                  pContext->szName,
                  pszMatchMode));

        DBGPRINT((sdlError,
                  "SdbpCheckExe",
                  "-----------------------------------------------------\n"));

    } else {

        DBGPRINT((sdlWarning|sdlLogPipe,
                  "SdbpCheckExe",
                  "++++ Successful match for App: '%s', Exe: '%s', Mode: %s\n",
                  hSDB,
                  szAppName,
                  pContext->szName,
                  pszMatchMode));

        //
        // If this is an exclusive match, kill anything we've found up to now
        //
        if (MatchMode.Type == MATCH_EXCLUSIVE) {
            RtlZeroMemory(ptiExes, sizeof(TAGID) * SDB_MAX_EXES);
            *pdwNumExes = 0;
        }

        //
        // Save this match on the list
        //
        ptiExes[*pdwNumExes] = tiExe;
        (*pdwNumExes)++;

        bSuccess = TRUE;
    }

out:
    //
    // In case of success, return match mode information
    //

    if (bSuccess && pMatchMode != NULL) {
        pMatchMode->dwMatchMode = MatchMode.dwMatchMode;
    }

    return bSuccess;
}


DWORD
SdbpSearchDB(
    IN  HSDB             hSDB,
    IN  PDB              pdb,           // pdb to search in
    IN  TAG              tiSearchTag,   // OPTIONAL - target tag (TAG_EXE or TAG_APPHELP_EXE)
    IN  PSEARCHDBCONTEXT pContext,
    OUT TAGID*           ptiExes,       // caller needs to provide array of size SDB_MAX_EXES
    OUT GUID*            pLastExeGUID,
    OUT DWORD*           pLastExeFlags,
    OUT PMATCHMODE       pMatchMode     // reason why we stopped scanning
    )
/*++
    Return: TAGID of found EXE record, TAGID_NULL if not found.

    Desc:   This function searches a given shimDB for any EXEs with the given filename.
            If it finds one, it checks all the MATCHING_FILE records by
            calling SdbpCheckForMatch.
            If any EXEs are found, the number of EXEs found is returned in ptiExes.
            If not, it returns 0.

            when we get the matching mode out of the particular exe -- it is checked 
            to see whether we need to continue and then this matching mode is returned

            It will never return more than SDB_MAX_EXES EXE entries.

            Debug Output is controlled by three factors 
                -- a global one (controlled via the ifdef DBG), TRUE on checked builds
                -- a pipe handle in hSDB which is activated when we init the context
                -- a local variable that is set when we are in one of the conditions above
                   when the variable bDebug is set -- we do not actually store the matches
            
--*/
{
    TAGID       tiDatabase, tiExe;
    FIND_INFO   FindInfo;
    TAGID       tiAppName = TAGID_NULL;
    TCHAR*      szAppName = _T("(unknown)");
    BOOL        bUsingIndex = FALSE;
    DWORD       dwNumExes = 0;
    DWORD       dwMatchMode = MATCH_NORMAL;
    DWORD       i;
    DWORD       dwAdditiveMode = MATCH_NORMAL;
    BOOL        bDebug = FALSE;
    BOOL        bMultiple = FALSE;
    BOOL        bSuccess = FALSE;
    MATCHMODE   MatchMode; // internal match mode
    MATCHMODE   MatchModeExe;

#ifndef KERNEL_MODE

    if (pMatchMode) {
        MatchMode.dwMatchMode = pMatchMode->dwMatchMode;
    } else {
        MatchMode.dwMatchMode = SdbpIsMainPDB(hSDB, pdb) ? MATCHMODE_DEFAULT_MAIN : 
                                                           MATCHMODE_DEFAULT_CUSTOM;
    }
#else // KERNEL_MODE

    MatchMode.dwMatchMode = MATCHMODE_DEFAULT_MAIN;

#endif

    if (!tiSearchTag) {
        tiSearchTag = TAG_EXE;
    }

    //
    // ADDITIVE MATCHES -- wildcards 
    //
    if (tiSearchTag == TAG_EXE && SdbIsIndexAvailable(pdb, TAG_EXE, TAG_WILDCARD_NAME)) {

        tiExe = SdbpFindFirstIndexedWildCardTag(pdb,
                                                TAG_EXE,
                                                TAG_WILDCARD_NAME,
                                                pContext->szName,
                                                &FindInfo);

        while (tiExe != TAGID_NULL) {

            bSuccess = SdbpCheckExe(hSDB,
                                    pdb,
                                    tiExe,
                                    &dwNumExes,
                                    pContext,
                                    AM_ADDITIVE_ONLY, // match mode we request for this db
                                    bDebug,
                                    &MatchModeExe,    // this is the matched tag from the db
                                    ptiExes,
                                    pLastExeGUID,
                                    pLastExeFlags);

            if (bSuccess) {

                if (bDebug) {
                    bMultiple = TRUE;  // if bDebug is set -- we already seen a match 
                } else {
                
                    //
                    // We got a match, update the state and make decision on whether to continue
                    //
                    MatchMode = MatchModeExe;
                    
                    if (MatchModeExe.Type != MATCH_ADDITIVE) {
                        bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
                        if (!bDebug) {
                            goto out;
                        }
                    } 
                }
            }

            tiExe = SdbpFindNextIndexedWildCardTag(pdb, &FindInfo);
        }
    }

    //
    // Normal EXEs
    // 
    bUsingIndex = SdbIsIndexAvailable(pdb, tiSearchTag, TAG_NAME);

    if (bUsingIndex) {

        //
        // Look in the index.
        //
        tiExe = SdbFindFirstStringIndexedTag(pdb,
                                             tiSearchTag,
                                             TAG_NAME,
                                             pContext->szName,
                                             &FindInfo);

        if (tiExe == TAGID_NULL) {
            DBGPRINT((sdlInfo,
                      "SdbpSearchDB",
                      "SdbFindFirstStringIndexedTag failed to locate exe: \"%s\".\n",
                      pContext->szName));
        }

    } else {

        //
        // Searching without an index...
        //
        DBGPRINT((sdlInfo, "SdbpSearchDB", "Searching database with no index.\n"));

        //
        // First get the DATABASE
        //
        tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);

        if (tiDatabase != TAGID_NULL) {
            DBGPRINT((sdlError, "SdbpSearchDB", "No DATABASE tag found.\n"));
            goto out;
        }

        //
        // Then get the first EXE.
        //
        tiExe = SdbFindFirstNamedTag(pdb, tiDatabase, tiSearchTag, TAG_NAME, pContext->szName);
    }

    while (tiExe != TAGID_NULL) {

        bSuccess = SdbpCheckExe(hSDB,
                                pdb,
                                tiExe,
                                &dwNumExes,
                                pContext,
                                AM_NORMAL,
                                bDebug,
                                &MatchModeExe,
                                ptiExes,
                                pLastExeGUID,
                                pLastExeFlags);

        if (bSuccess) {

            if (bDebug) {

                bMultiple = TRUE;  // if bDebug is set -- we already seen a match 
                
            } else {
            
                //
                // We got a match, update the state and make decision on whether to continue
                // if we're not additive, we may go into debug mode
                //
                MatchMode = MatchModeExe;
                
                if (MatchModeExe.Type != MATCH_ADDITIVE) { 
                    bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
                    if (!bDebug) {
                        goto out;
                    }
                } 
            }
        }

        if (bUsingIndex) {
            tiExe = SdbFindNextStringIndexedTag(pdb, &FindInfo);
        } else {
            tiExe = SdbpFindNextNamedTag(pdb, tiDatabase, tiExe, TAG_NAME, pContext->szName);
        }
    }

#ifndef KERNEL_MODE
    //
    // Now we search by module name, if one is available
    // this case falls into 16-bit flags category 
    //
    if (tiSearchTag == TAG_EXE && pContext->szModuleName) {
        
        bUsingIndex = SdbIsIndexAvailable(pdb, tiSearchTag, TAG_16BIT_MODULE_NAME);

        if (bUsingIndex) {

            //
            // Look in the index.
            //
            tiExe = SdbFindFirstStringIndexedTag(pdb,
                                                 tiSearchTag,
                                                 TAG_16BIT_MODULE_NAME,
                                                 pContext->szModuleName,
                                                 &FindInfo);

            if (tiExe == TAGID_NULL) {
                DBGPRINT((sdlInfo,
                          "SdbpSearchDB",
                          "SdbFindFirstStringIndexedTag failed to locate exe (MODNAME): \"%s\".\n",
                          pContext->szModuleName));
            }

        } else {

            //
            // Searching without an index...
            //
            DBGPRINT((sdlInfo, "SdbpSearchDB", "Searching database with no index.\n"));

            //
            // First get the DATABASE
            //
            tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);

            if (tiDatabase != TAGID_NULL) {
                DBGPRINT((sdlError, "SdbpSearchDB", "No DATABASE tag found.\n"));
                goto out;
            }

            //
            // Then get the first EXE.
            //
            tiExe = SdbFindFirstNamedTag(pdb,
                                         tiDatabase,
                                         tiSearchTag,
                                         TAG_16BIT_MODULE_NAME,
                                         pContext->szModuleName);
        }

        while (tiExe != TAGID_NULL) {

            bSuccess = SdbpCheckExe(hSDB,
                                    pdb,
                                    tiExe,
                                    &dwNumExes,
                                    pContext,
                                    AM_NORMAL,
                                    bDebug,
                                    &MatchModeExe,
                                    ptiExes,
                                    pLastExeGUID,
                                    pLastExeFlags);

            if (bSuccess) {
                if (bDebug) {
                    bMultiple = TRUE;  // if bDebug is set -- we already seen a match 
                } else {
                
                    //
                    // We got a match, update the state and make decision on whether to continue
                    //
                    MatchMode = MatchModeExe;
                    
                    if (MatchModeExe.Type != MATCH_ADDITIVE) {
                        bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
                        if (!bDebug) {
                            goto out;
                        }
                    } 
                }
            }

            if (bUsingIndex) {
                tiExe = SdbFindNextStringIndexedTag(pdb, &FindInfo);
            } else {
                tiExe = SdbpFindNextNamedTag(pdb,
                                             tiDatabase,
                                             tiExe,
                                             TAG_16BIT_MODULE_NAME,
                                             pContext->szModuleName);
            }
        }
    }
#endif // KERNEL_MODE

    //
    // Now check for wild-card non-additive exes.
    //
    if (tiSearchTag == TAG_EXE && SdbIsIndexAvailable(pdb, TAG_EXE, TAG_WILDCARD_NAME)) {

        tiExe = SdbpFindFirstIndexedWildCardTag(pdb,
                                                TAG_EXE,
                                                TAG_WILDCARD_NAME,
                                                pContext->szName,
                                                &FindInfo);

        while (tiExe != TAGID_NULL) {

            bSuccess = SdbpCheckExe(hSDB,
                                    pdb,
                                    tiExe,
                                    &dwNumExes,
                                    pContext,
                                    AM_NO_ADDITIVE,
                                    bDebug,
                                    &MatchModeExe,
                                    ptiExes,
                                    pLastExeGUID,
                                    pLastExeFlags);

            if (bSuccess) {

                if (bDebug) {
                    bMultiple = TRUE;  // if bDebug is set -- we already seen a match 
                } else {
                
                    //
                    // we got a match, update the state and make decision on whether to continue
                    //
                    MatchMode = MatchModeExe;
                    
                    if (MatchModeExe.Type != MATCH_ADDITIVE) {
                        bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
                        if (!bDebug) {
                            goto out;
                        }
                    } 
                }
            }


            tiExe = SdbpFindNextIndexedWildCardTag(pdb, &FindInfo);
        }
    }

out:
    //
    // Now report the final resolution of the match.
    //
    for (i = 0; i < dwNumExes; ++i) {

        tiAppName = SdbFindFirstTag(pdb, ptiExes[i], TAG_APP_NAME);

        if (tiAppName != TAGID_NULL) {
            szAppName = SdbGetStringTagPtr(pdb, tiAppName);
        } else {
            szAppName = _T("(Unknown)");
        }

        DBGPRINT((sdlWarning,
                  "SdbpSearchDB",
                  "--------------------------------------------------------\n"));

        DBGPRINT((sdlWarning|sdlLogPipe,
                  "SdbpSearchDB",
                  "+ Final match is App: \"%s\", exe: \"%s\".\n",
                  hSDB,
                  szAppName,
                  pContext->szName));

        DBGPRINT((sdlWarning,
                  "SdbpSearchDB",
                  "--------------------------------------------------------\n"));
    }

    if (bMultiple) {
        DBGPRINT((sdlError,
                  "SdbpSearchDB",
                  "--------------------------------------------------------\n"));

        DBGPRINT((sdlError|sdlLogPipe,
                  "SdbpSearchDB",
                  "!!!!!!!  Multiple non-additive matches.          !!!!!\n",
                  hSDB));

        DBGPRINT((sdlError,
                  "SdbpSearchDB",
                  "--------------------------------------------------------\n"));
    }

    if (pMatchMode != NULL) {
        pMatchMode->dwMatchMode = MatchMode.dwMatchMode;
    }

    return dwNumExes;
}

TAGREF
SdbGetDatabaseMatch(
    IN HSDB    hSDB,
    IN LPCTSTR szPath,
    IN HANDLE  FileHandle  OPTIONAL,
    IN LPVOID  pImageBase  OPTIONAL,
    IN DWORD   dwImageSize OPTIONAL
    )
/*++
    Return: BUGBUG: ?

    Desc:   BUGBUG: ?
--*/
{
    SEARCHDBCONTEXT Context;
    PSDBCONTEXT     pSdbContext = (PSDBCONTEXT)hSDB;
    TAGID           tiExe = TAGID_NULL;
    TAGID           atiExes[SDB_MAX_EXES];
    TAGREF          trExe = TAGREF_NULL;
    DWORD           dwNumExes = 0;
    GUID            guid;
    DWORD           dwFlags = 0;

    assert(pSdbContext->pdbMain && szPath);

    RtlZeroMemory(&Context, sizeof(Context)); // do this so that we don't trip later
    RtlZeroMemory(atiExes, sizeof(atiExes));

    Context.dwFlags |= (SEARCHDBF_NO_PROCESS_HISTORY | SEARCHDBF_NO_ATTRIBUTE_CACHE);

    if (FileHandle != INVALID_HANDLE_VALUE || pImageBase != NULL) {
        Context.dwFlags |= SEARCHDBF_NO_LFN;
    }

    Context.hMainFile   = FileHandle; // used to optimize attribute retrieval
    Context.pImageBase  = pImageBase; // this will be used and not a file handle
    Context.dwImageSize = dwImageSize; // size of the image

    DBGPRINT((sdlInfo, "SdbGetDatabaseMatch", "Looking for \"%s\"\n", szPath));

    //
    // Create search db context, no process history needed.
    //
    if (!SdbpCreateSearchDBContext(&Context, szPath, NULL, NULL)) {
        DBGPRINT((sdlError,
                  "SdbGetDatabaseMatch",
                  "Failed to create search DB context.\n"));
        goto out;
    }

    //
    // We will be searching the main db
    //
    dwNumExes = SdbpSearchDB(pSdbContext,
                             pSdbContext->pdbMain,
                             TAG_EXE,
                             &Context,
                             atiExes,
                             &guid,
                             &dwFlags,
                             NULL);
    //
    // Convert to TAGREF
    //
    if (dwNumExes) {

        //
        // Always use the last exe in the list, as it will be the most specific
        //
        tiExe = atiExes[dwNumExes - 1];

        if (!SdbTagIDToTagRef(hSDB, pSdbContext->pdbMain, tiExe, &trExe)) {
            DBGPRINT((sdlError,
                      "SdbGetDatabaseMatch",
                      "Failed to convert tagid to tagref\n"));
            goto out;
        }
    }

out:

    SdbpReleaseSearchDBContext(&Context);

    return trExe;
}


DWORD
SdbQueryData(
    IN     HSDB    hSDB,              // database handle
    IN     TAGREF  trExe,             // tagref of the matching exe
    IN     LPCTSTR lpszDataName,      // if this is null, will try to return all the policy names
    OUT    LPDWORD lpdwDataType,      // pointer to data type (REG_SZ, REG_BINARY, etc)
    OUT    LPVOID  lpBuffer,          // buffer to fill with information
    IN OUT LPDWORD lpdwBufferSize     // pointer to buffer size
    )
{
    return SdbQueryDataEx(hSDB, trExe, lpszDataName, lpdwDataType, lpBuffer, lpdwBufferSize, NULL);
}


DWORD
SdbQueryDataExTagID(
    IN     PDB     pdb,               // database handle
    IN     TAGID   tiExe,             // tagref of the matching exe
    IN     LPCTSTR lpszDataName,      // if this is null, will try to return all the policy names
    OUT    LPDWORD lpdwDataType,      // pointer to data type (REG_SZ, REG_BINARY, etc)
    OUT    LPVOID  lpBuffer,          // buffer to fill with information
    IN OUT LPDWORD lpdwBufferSize,    // pointer to buffer size
    OUT    TAGID*  ptiData            // optional pointer to the retrieved data tag
    )
/*++
    Return: Error code or ERROR_SUCCESS if successful

    Desc:   See complete description with sample code
            in doc subdirectory
--*/
{
    TAGID     tiData;
    BOOL      bSuccess;
    TAGID     tiParent;
    TAGID     tiName;
    TAGID     tiValue;
    TAGID     tiValueType;
    DWORD     cbSize;
    DWORD     dwValueType;
    LPCTSTR   pszName;
    LPTSTR    pszNameBuffer = NULL;
    LPTSTR    pSlash;
    LPTSTR    pchBuffer;
    DWORD     dwData;
    TAG       tData;
    ULONGLONG ullData;
    LPVOID    lpValue;
    DWORD     Status = ERROR_NOT_SUPPORTED; // have it initialized

    if (lpszDataName == NULL) {

        if (lpdwBufferSize == NULL) {
            Status = ERROR_INVALID_PARAMETER;
            goto ErrHandle;
        }

        cbSize = 0;

        tiData = SdbFindFirstTag(pdb, tiExe, TAG_DATA);
        if (!tiData) {
            //
            // Bad entry.
            //
            DBGPRINT((sdlError,
                      "SdbQueryDataExTagID",
                      "The entry 0x%x does not appear to have data\n",
                      tiExe));

            Status = ERROR_INTERNAL_DB_CORRUPTION;
            goto ErrHandle;
        }

        while (tiData) {

            //
            // Pass one: Calculate the size needed.
            //
            tiName = SdbFindFirstTag(pdb, tiData, TAG_NAME);
            
            if (!tiName) {
                DBGPRINT((sdlError,
                          "SdbQueryDataExTagID",
                          "The entry 0x%x does not contain a name tag\n",
                          tiData));
                Status = ERROR_INTERNAL_DB_CORRUPTION;
                goto ErrHandle;
            }

            pszName = SdbGetStringTagPtr(pdb, tiName);
            
            if (!pszName) {
                DBGPRINT((sdlError,
                          "SdbQueryDataExTagID",
                          "The entry 0x%x contains NULL name\n",
                          tiName));
                Status = ERROR_INTERNAL_DB_CORRUPTION;
                goto ErrHandle;
            }

            cbSize += (_tcslen(pszName) + 1) * sizeof(*pszName);

            tiData = SdbFindNextTag(pdb, tiExe, tiData);
        }

        cbSize += sizeof(*pszName); // for the final 0

        //
        // We are done, compare the size.
        //
        if (lpBuffer == NULL || *lpdwBufferSize < cbSize) {
            *lpdwBufferSize = cbSize;
            Status = ERROR_INSUFFICIENT_BUFFER;
            goto ErrHandle;
        }

        //
        // lpBuffer != NULL here and there is enough room
        //
        pchBuffer = (LPTSTR)lpBuffer;

        tiData = SdbFindFirstTag(pdb, tiExe, TAG_DATA);

        while (tiData) {

            tiName = SdbFindFirstTag(pdb, tiData, TAG_NAME);
            
            if (tiName) {
                pszName = SdbGetStringTagPtr(pdb, tiName);

                if (pszName) {
                    _tcscpy(pchBuffer, pszName);
                    pchBuffer += _tcslen(pchBuffer) + 1;
                }
            }

            tiData = SdbFindNextTag(pdb, tiExe, tiData);
        }

        //
        // The buffer has been filled, terminate.
        //
        *pchBuffer++ = TEXT('\0');

        //
        // Save the size written to the buffer
        //
        *lpdwBufferSize = (DWORD)((ULONG_PTR)pchBuffer - (ULONG_PTR)lpBuffer);

        //
        // Save data type
        //
        if (lpdwDataType != NULL) {
            *lpdwDataType = REG_MULTI_SZ;
        }

        return ERROR_SUCCESS;
    }

    //
    // In this case we allow the query to proceed if
    // the buffer is null and lpdwBufferSize is not null or lpBufferSize is not null
    //
    if (lpBuffer == NULL && lpdwBufferSize == NULL) {
        DBGPRINT((sdlError,
                  "SdbQueryDataExTagID",
                  "One of lpBuffer or lpdwBufferSize should not be null\n"));
        Status = ERROR_INVALID_PARAMETER;
        goto ErrHandle;
    }

    //
    // Expect the name to be in format "name1\name2..."
    //
    STACK_ALLOC(pszNameBuffer, (_tcslen(lpszDataName) + 1) * sizeof(*pszNameBuffer));
    
    if (pszNameBuffer == NULL) {
        DBGPRINT((sdlError,
                  "SdbQueryDataExTagID",
                  "Cannot allocate temporary buffer for parsing the name \"%s\"\n",
                  lpszDataName));
        Status = ERROR_NOT_ENOUGH_MEMORY;
        goto ErrHandle;
    }

    tiParent = tiExe;
    tiData   = TAGID_NULL;
    
    do {
        pSlash = _tcschr(lpszDataName, TEXT('\\'));
        
        if (pSlash == NULL) {
            _tcscpy(pszNameBuffer, lpszDataName);
            lpszDataName = NULL;
        } else {
            _tcsncpy(pszNameBuffer, lpszDataName, pSlash - lpszDataName);
            pszNameBuffer[pSlash - lpszDataName] = TEXT('\0');
            lpszDataName = pSlash + 1; // go to the next char
        }
        
        tiData = SdbFindFirstNamedTag(pdb, tiParent, TAG_DATA, TAG_NAME, pszNameBuffer);
        tiParent = tiData;
    
    } while (lpszDataName != NULL && *lpszDataName != TEXT('\0') && tiData != TAGID_NULL);

    if (!tiData) {
        DBGPRINT((sdlError,
                  "SdbQueryDataExTagID",
                  "The entry \"%s\" not found\n",
                  pszNameBuffer));
        Status = ERROR_NOT_FOUND;
        goto ErrHandle;
    }

    //
    // Looks like we found the entry, query value type
    //
    dwValueType = REG_NONE;

    tiValueType = SdbFindFirstTag(pdb, tiData, TAG_DATA_VALUETYPE);
    
    if (!tiValueType) {
        DBGPRINT((sdlWarning,
                  "SdbQueryDataExTagID",
                  "The entry 0x%x does not have valuetype information\n",
                  tiData));
    } else {
        dwValueType = SdbReadDWORDTag(pdb, tiValueType, REG_NONE);
    }

    cbSize  = 0;
    lpValue = NULL;

    if (dwValueType != REG_NONE) {

        //
        // Find data tag
        //
        cbSize = 0;

        switch (dwValueType) {
        
        case REG_SZ:
            //
            // string data
            //
            tData = TAG_DATA_STRING;
            break;

        case REG_DWORD:
            tData = TAG_DATA_DWORD;
            break;

        case REG_QWORD:
            tData = TAG_DATA_QWORD;
            break;

        case REG_BINARY:
            tData = TAG_DATA_BITS;
            break;

        default:
            DBGPRINT((sdlError,
                      "SdbQueryDataExTagID",
                      "The entry 0x%x contains bad valuetype information 0x%x\n",
                      tiData,
                      dwValueType));
            Status = ERROR_INTERNAL_DB_CORRUPTION;
            goto ErrHandle;
            break;
        }

        tiValue = SdbFindFirstTag(pdb, tiData, tData);
        
        //
        // Find what the data size is if needed
        //
        if (!tiValue) {

            DBGPRINT((sdlWarning,
                      "SdbQueryDataExTagID",
                      "The entry 0x%x contains no value\n",
                      tiData));
            Status = ERROR_NOT_FOUND;
            goto ErrHandle;

        }

        //
        // For those who have no size quite yet...
        // (binary and a string)
        //
        switch (dwValueType) {
        
        case REG_SZ:
            pchBuffer = SdbGetStringTagPtr(pdb, tiValue);
            
            if (pchBuffer == NULL) {
                DBGPRINT((sdlWarning,
                          "SdbQueryDataExTagID",
                          "The entry 0x%x contains bad string value 0x%x\n",
                          tiData,
                          tiValue));
                Status = ERROR_NOT_FOUND;
                goto ErrHandle;
            }

            cbSize = (_tcslen(pchBuffer) + 1) * sizeof(*pchBuffer);
            lpValue = (LPVOID)pchBuffer;
            break;

        case REG_BINARY:
            cbSize = SdbGetTagDataSize(pdb, tiValue); // binary tag
            lpValue = SdbpGetMappedTagData(pdb, tiValue);

            if (lpValue == NULL) {
                DBGPRINT((sdlWarning,
                          "SdbQueryDataExTagID",
                          "The entry 0x%x contains bad binary value 0x%x\n",
                          tiData,
                          tiValue));
                Status = ERROR_NOT_FOUND;
                goto ErrHandle;
            }
            break;

        case REG_DWORD:
            dwData = SdbReadDWORDTag(pdb, tiValue, 0);
            cbSize = sizeof(dwData);
            lpValue = (LPVOID)&dwData;
            break;

        case REG_QWORD:
            ullData = SdbReadQWORDTag(pdb, tiValue, 0);
            cbSize = sizeof(ullData);
            lpValue = (LPVOID)&ullData;
            break;
        }

        //
        // At this point we have everything we need to get the pointer to data.
        //
    }

    //
    // Fix the output params and exit.
    //
    Status = ERROR_SUCCESS;

    if (cbSize == 0) {
        goto SkipCopy;
    }

    if (lpBuffer == NULL || (lpdwBufferSize != NULL && *lpdwBufferSize < cbSize)) {
        Status = ERROR_INSUFFICIENT_BUFFER;
        goto SkipCopy;
    }

    //
    // Buffer size checked out, now if buffer exists -- copy
    //
    if (lpBuffer != NULL) {
        RtlMoveMemory(lpBuffer, lpValue, cbSize);
    }

SkipCopy:

    if (lpdwBufferSize) {
        *lpdwBufferSize = cbSize;
    }

    if (lpdwDataType) {
        *lpdwDataType = dwValueType;
    }

    if (ptiData) {
        *ptiData = tiData;
    }

ErrHandle:

    if (pszNameBuffer != NULL) {
        STACK_FREE(pszNameBuffer);
    }

    return Status;
}

DWORD
SdbQueryDataEx(
    IN     HSDB    hSDB,              // database handle
    IN     TAGREF  trExe,             // tagref of the matching exe
    IN     LPCTSTR lpszDataName,      // if this is null, will try to return all the policy names
    OUT    LPDWORD lpdwDataType,      // pointer to data type (REG_SZ, REG_BINARY, etc)
    OUT    LPVOID  lpBuffer,          // buffer to fill with information
    IN OUT LPDWORD lpdwBufferSize,    // pointer to buffer size
    OUT    TAGREF* ptrData            // optional pointer to the retrieved data tag
    )
{
    BOOL     bSuccess;
    PDB      pdb   = NULL;
    TAGID    tiExe = TAGID_NULL;
    TAGID    tiData;
    NTSTATUS Status;

    bSuccess = SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe);

    if (!bSuccess) {
        DBGPRINT((sdlError,
                  "SdbQueryDataEx",
                  "Failed to convert tagref 0x%x to tagid\n",
                  trExe));
        Status = ERROR_INVALID_PARAMETER;
        goto ErrHandle;
    }

    Status = SdbQueryDataExTagID(pdb,
                                 tiExe,
                                 lpszDataName,
                                 lpdwDataType,
                                 lpBuffer,
                                 lpdwBufferSize,
                                 &tiData);
    //
    // See that we convert the output param
    //
    if (ptrData != NULL && NT_SUCCESS(Status)) {
        if (!SdbTagIDToTagRef(hSDB, pdb, tiData, ptrData)) {
            Status = ERROR_INVALID_DATA;
        }
    }

ErrHandle:

    return Status;
}

BOOL
SdbReadEntryInformation(
    IN  HSDB           hSDB,
    IN  TAGREF         trExe,
    OUT PSDBENTRYINFO  pEntryInfo
    )
/*++
    Return: BUGBUG: ?

    Desc:   BUGBUG: ?
--*/
{
    BOOL         bSuccess = FALSE;
    TAGID        tiExe;
    TAGID        tiData;
    TAGID        tiExeID;
    TAGID        tiPolicy;
    TAGID        tiRegPath;
    PDB          pdb;
    SDBENTRYINFO EntryInfo;

    RtlZeroMemory(&EntryInfo, sizeof(EntryInfo));

    bSuccess = SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe);
    
    if (!bSuccess) {
        DBGPRINT((sdlError,
                  "SdbReadEntryInformation",
                  "Failed to convert tagref 0x%x to tagid\n",
                  trExe));
        goto ErrHandle;
    }

    //
    // Get the EXE's ID
    //
    tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);

    if (tiExeID == TAGID_NULL) {
        DBGPRINT((sdlError,
                  "SdbReadEntryInformation",
                  "Failed to read TAG_EXE_ID for tiExe 0x%x !\n",
                  tiExe));
        goto ErrHandle;
    }

    bSuccess = SdbReadBinaryTag(pdb,
                                tiExeID,
                                (PBYTE)&EntryInfo.guidID,
                                sizeof(EntryInfo.guidID));
    if (!bSuccess) {
        DBGPRINT((sdlError,
                  "SdbReadEntryInformation",
                  "Failed to read GUID referenced by 0x%x\n",
                  tiExeID));
        goto ErrHandle;
    }

    //
    // Get the database id
    //
    if (!SdbGetDatabaseID(pdb, &EntryInfo.guidDB)) {
        DBGPRINT((sdlError,
                  "SdbReadEntryInformation",
                  "Failed to read GUID of the database\n"));
        goto ErrHandle;
    }

    //
    // Retrieve entry flags as referenced by the registry
    //
    if (!SdbGetEntryFlags(&EntryInfo.guidID, &EntryInfo.dwFlags)) {
        DBGPRINT((sdlWarning,
                  "SdbReadEntryInformation",
                  "No flags for tiExe 0x%x\n",
                  tiExe));

        EntryInfo.dwFlags = 0;
    } else {
        DBGPRINT((sdlInfo,
                  "SdbReadEntryInformation",
                  "Retrieved flags for this app 0x%x.\n",
                  EntryInfo.dwFlags));
    }

    //
    // Read the data tags
    //
    tiData = SdbFindFirstTag(pdb, tiExe, TAG_DATA);
    
    EntryInfo.tiData = tiData;
    
    if (tiData == TAGID_NULL) {
        //
        // This is not a data entry
        //
        DBGPRINT((sdlWarning,
                  "SdbReadEntryInformation",
                  "Entry tiExe 0x%x does not contain TAG_DATA.\n",
                  tiExe));
    }

    if (pEntryInfo != NULL) {
        RtlMoveMemory(pEntryInfo, &EntryInfo, sizeof(*pEntryInfo));
    }

    bSuccess = TRUE;

ErrHandle:

    return bSuccess;
}


//
// We may be compiled UNICODE or ANSI
// If we are compiled UNICODE we need to use UNICODE sprintf and convert
// the result back to ANSI for output with DbgPrint. This is accomplished
// by %ls format in DbgPrint. Format and Function name are always passed
// in as ANSI though. TCHAR strings are formatted just with %s
//

void
PrepareFormatForUnicode(
    PCH fmtUnicode,
    PCH format
    )
{
    PCH    pfmt;
    CHAR   ch;
    size_t nch;
    long   width;
    PCH    pend;

    strcpy(fmtUnicode, format);
    pfmt = fmtUnicode;

    while('\0' != (ch = *pfmt++)) {
        if (ch == '%') {

            if (*pfmt == '%') {
                continue;
            }

            //
            // Skip the characters that relate to  - + 0 ' ' #
            //
            nch = strspn(pfmt, "-+0 #");
            pfmt += nch;

            //
            // Parse the width.
            //
            if (*pfmt == '*') {
                //
                // Parameter defines the width
                //
                ++pfmt;
            } else {
                //
                // See whether we have width
                //
                if (isdigit(*pfmt)) {
                    pend = NULL;
                    width = atol(pfmt);

                    while (isdigit(*pfmt)) {
                        ++pfmt;
                    }
                }
            }

            //
            // Now we can have: .precision
            //
            if (*pfmt == '.') {
                ++pfmt;
                width = atol(pfmt);

                while (isdigit(*pfmt)) {
                    ++pfmt;
                }
            }

            //
            // Now is the format (one of: h, l, L, I64)
            //
            ch = *pfmt;
            pend = strchr("hlLNFw", ch);
            if (pend != NULL) {
                ++pfmt; // move past the modifier char
            } else {
                if (ch == 'I' && !strncpy(pfmt, "I64", 3)) {
                    pfmt += 3;
                }
            }

            //
            // We should have a type character here.
            //
            if (*pfmt == 's') {
                //
                // Convert to UPPER, making it UNICODE string with ansi vsnprintf
                //
                *pfmt = 'S';
            }

            //
            // Move past the format char if we are not at the end
            //
            if (*pfmt != '\0') {
                ++pfmt;
            }
        }
    }
}