//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       catadnew.cpp
//
//  Contents:   Microsoft Internet Security Catalog Utilities
//
//  Functions:  CryptCATAdminAcquireContext
//              CryptCATAdminReleaseContext
//              CryptCATAdminAddCatalog
//              CryptCATAdminRemoveCatalog
//              CryptCATAdminEnumCatalogFromHash
//              CryptCATCatalogInfoFromContext
//              CryptCATAdminReleaseCatalogContext
//              CryptCATAdminResolveCatalogPath
//              CryptCATAdminPauseServiceForBackup
//              CryptCATAdminCalcHashFromFileHandle
//              I_CryptCatAdminMigrateToNewCatDB
//              CatAdminDllMain
//
//  History:    01-Jan-2000 reidk created
//
//--------------------------------------------------------------------------

#include    "global.hxx"
#include    "cryptreg.h"
#include    "wintrust.h"
#include    "softpub.h"
#include    "eventlst.h"
#include    "sipguids.h"
#include    "mscat32.h"
#include    "catdb.h"
#include    "voidlist.h"
#include    "catutil.h"
#include    "..\..\common\catdbsvc\catdbcli.h"
#include    "errlog.h"

//
//  default system guid for apps that just make calls to CryptCATAdminAddCatalog with
//  hCatAdmin == NULL...
//
//          {127D0A1D-4EF2-11d1-8608-00C04FC295EE}
//
#define DEF_CAT_SUBSYS_ID                                               \
                {                                                       \
                    0x127d0a1d,                                         \
                    0x4ef2,                                             \
                    0x11d1,                                             \
                    { 0x86, 0x8, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee }    \
                }

#define WSZ_CATALOG_FILE_BASE_DIRECTORY     L"CatRoot"
#define WSZ_DATABASE_FILE_BASE_DIRECTORY    L"CatRoot2"

#define WSZ_REG_FILES_NOT_TO_BACKUP         L"System\\CurrentControlSet\\Control\\BackupRestore\\FilesNotToBackup"
#define WSZ_REG_CATALOG_DATABASE_VALUE      L"Catalog Database"
#define WSZ_PATH_NOT_TO_BACKUP              L"%SystemRoot%\\System32\\CatRoot2\\* /s\0"

static WCHAR        *gpwszDatabaseFileBaseDirectory = NULL;
static WCHAR        *gpwszCatalogFileBaseDirectory = NULL;

#define WSZ_CATALOG_SUBSYTEM_SEARCH_STRING  L"{????????????????????????????????????}"


#define CATADMIN_LOGERR_LASTERR()           ErrLog_LogError(NULL, \
                                                            ERRLOG_CLIENT_ID_CATADMIN, \
                                                            __LINE__, \
                                                            0, \
                                                            FALSE, \
                                                            FALSE);

#define CATADMIN_SETERR_LOG_RETURN(x, y)    SetLastError(x); \
                                            ErrLog_LogError(NULL, \
                                                            ERRLOG_CLIENT_ID_CATADMIN, \
                                                            __LINE__, \
                                                            0, \
                                                            FALSE, \
                                                            FALSE); \
                                            goto y;

typedef struct CATALOG_INFO_CONTEXT_
{
    HANDLE          hMappedFile;
    BYTE            *pbMappedFile;
    WCHAR           *pwszCatalogFile;
    PCCTL_CONTEXT   pCTLContext; 
    BOOL            fResultOfAdd;
} CATALOG_INFO_CONTEXT;

typedef struct CRYPT_CAT_ADMIN_
{
    DWORD                   cbStruct;
    BOOL                    fUseDefSubSysId;
    LPWSTR                  pwszSubSysGUID;
    LPWSTR                  pwszCatalogFileDir;     // full path to .cat files
    LPWSTR                  pwszDatabaseFileDir;    // full path to CatDB file
    DWORD                   dwLastDBError;
    LIST                    CatalogInfoContextList;
    int                     nOpenCatInfoContexts;
    CRITICAL_SECTION        CriticalSection;
    BOOL                    fCSInitialized;
    BOOL                    fCSEntered;
    HANDLE                  hClearCacheEvent;
    HANDLE                  hRegisterWaitForClearCache;
    BOOL                    fRegisteredForChangeNotification;

} CRYPT_CAT_ADMIN;

#define CATINFO_CONTEXT_ALLOCATION_SIZE 64

LPWSTR  ppwszFilesToDelete[] = {L"hashmast.cbd", 
                                L"hashmast.cbk",
                                L"catmast.cbd",
                                L"catmast.cbk",
                                L"sysmast.cbd",
                                L"sysmast.cbk"};

#define   NUM_FILES_TO_DELETE  (sizeof(ppwszFilesToDelete) / \
                                sizeof(ppwszFilesToDelete[0]))


BOOL
_CatAdminMigrateSingleDatabase(
    LPWSTR  pwszDatabaseGUID);

BOOL 
_CatAdminSetupDefaults(void);

void 
_CatAdminCleanupDefaults(void);

BOOL
_CatAdminTimeStampFilesInSync(
    LPWSTR  pwszDatabaseGUID,
    BOOL    *pfInSync);

BOOL 
_CatAdminRegisterForChangeNotification(
    CRYPT_CAT_ADMIN         *pCatAdmin
    );

BOOL 
_CatAdminFreeCachedCatalogs(
    CRYPT_CAT_ADMIN         *pCatAdmin
    );

VOID CALLBACK 
_CatAdminWaitOrTimerCallback(
    PVOID                   lpParameter, 
    BOOLEAN                 TimerOrWaitFired
    );

BOOL 
_CatAdminAddCatalogsToCache(
    CRYPT_CAT_ADMIN         *pCatAdmin,
    LPWSTR                  pwszSubSysGUID, 
    CRYPT_DATA_BLOB         *pCryptDataBlob, 
    LIST_NODE               **ppFirstListNodeAdded
    );

BOOL 
_CatAdminAddSingleCatalogToCache(
    CRYPT_CAT_ADMIN         *pCatAdmin,
    LPWSTR                  pwszCatalog, 
    LIST_NODE               **ppListNodeAdded
    );

BOOL
_CatAdminMigrateCatalogDatabase(
    LPWSTR                  pwszFrom, 
    LPWSTR                  pwszTo
    );

void
_CatAdminBToHex (
    LPBYTE                  pbDigest, 
    DWORD                   iByte, 
    LPWSTR                  pwszHashTag
    );

BOOL
_CatAdminCreateHashTag(
    BYTE                    *pbHash,
    DWORD                   cbHash,
    LPWSTR                  *ppwszHashTag,
    CRYPT_DATA_BLOB         *pCryptDataBlob
    );

BOOL 
_CatAdminRecursiveCreateDirectory(
    IN LPCWSTR              pwszDir,
    LPSECURITY_ATTRIBUTES   lpSecurityAttributes
    );

LPWSTR 
_CatAdminCreatePath(
    IN LPCWSTR              pwsz1,
    IN LPCWSTR              pwsz2,
    IN BOOL                 fAddEndingSlash
    );


void __RPC_FAR * __RPC_API MIDL_user_allocate(size_t len)
{
    return(LocalAlloc(LMEM_ZEROINIT, len));
}

void __RPC_API MIDL_user_free(void __RPC_FAR * ptr)
{
    if (ptr != NULL)
    {
        LocalFree(ptr);
    }
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminAcquireContext
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CryptCATAdminAcquireContext_Internal(
    HCATADMIN   *phCatAdmin, 
    const GUID  *pgSubsystem, 
    DWORD       dwFlags,
    BOOL        fCalledFromMigrate)
{
    GUID            gDefault    = DEF_CAT_SUBSYS_ID;
    const GUID      *pgCatroot  = &gDefault;
    CRYPT_CAT_ADMIN *pCatAdmin  = NULL;
    BOOL            fRet        = TRUE;
    DWORD           dwErr       = 0;
    WCHAR           wszGUID[256];
    BOOL            fInSync;

    //
    // Validata parameters
    //
    if (phCatAdmin == NULL)
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, ErrorInvalidParam)
    }
    *phCatAdmin = NULL;

    //
    // Allocate a new CatAdmin state struct
    //
    if (NULL == (pCatAdmin = (CRYPT_CAT_ADMIN *) malloc(sizeof(CRYPT_CAT_ADMIN))))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }
    memset(pCatAdmin, 0, sizeof(CRYPT_CAT_ADMIN));
    pCatAdmin->cbStruct = sizeof(CRYPT_CAT_ADMIN);

    LIST_Initialize(&(pCatAdmin->CatalogInfoContextList));

    //
    // Check to see if caller specified the Catroot dir to use
    //
    if (pgSubsystem == NULL)
    {
        pCatAdmin->fUseDefSubSysId = TRUE;
    }
    else
    {
        pgCatroot = pgSubsystem; 
    }

    guid2wstr(pgCatroot, wszGUID);

    //
    // Initialize the critical section
    //
    __try
    {
        InitializeCriticalSection(&(pCatAdmin->CriticalSection));
    }
    __except(EXCEPTION_EXECUTE_HANDLER) 
    {
        SetLastError(GetExceptionCode());
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }
    pCatAdmin->fCSInitialized = TRUE;
    pCatAdmin->fCSEntered = FALSE;

    //
    // Save a copy of the GUID as a string
    // 
    if (NULL == (pCatAdmin->pwszSubSysGUID = (LPWSTR)
                                malloc((wcslen(wszGUID) + 1) * sizeof(WCHAR))))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }
    wcscpy(pCatAdmin->pwszSubSysGUID, wszGUID);

    //
    // Get the complete paths for the catalog files and the db file
    //
    if (NULL == (pCatAdmin->pwszCatalogFileDir = _CatAdminCreatePath(
                                                        gpwszCatalogFileBaseDirectory,
                                                        wszGUID, 
                                                        TRUE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    if (NULL == (pCatAdmin->pwszDatabaseFileDir = _CatAdminCreatePath(
                                                        gpwszDatabaseFileBaseDirectory,
                                                        wszGUID, 
                                                        TRUE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Make sure catalog file and database file sub-directories exists
    //
    if (!_CatAdminRecursiveCreateDirectory(
            pCatAdmin->pwszCatalogFileDir,
            NULL))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

     if (!_CatAdminRecursiveCreateDirectory(
            pCatAdmin->pwszDatabaseFileDir,
            NULL))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Create the event which is notified when the catalog db changes, and register
    // a callback for when the event is signaled
    //
    if (NULL == (pCatAdmin->hClearCacheEvent = CreateEvent(NULL, FALSE, FALSE, NULL)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorCreateEvent;
    } 
    
    if (!RegisterWaitForSingleObject(
            &(pCatAdmin->hRegisterWaitForClearCache),
            pCatAdmin->hClearCacheEvent,
            _CatAdminWaitOrTimerCallback,
            pCatAdmin,
            INFINITE,
            WT_EXECUTEDEFAULT))
    {
        
        CATADMIN_LOGERR_LASTERR()
        goto ErrorRegisterWaitForSingleObject;
    }

    //
    // If we are being called by a real client (not the migrate code) then make sure
    // the TimeStamp files are in a consistent state, and if not, migrate (re-add) 
    // the catalog files for that database
    //
    if (!fCalledFromMigrate)
    {        
        if (_CatAdminTimeStampFilesInSync(wszGUID, &fInSync))
        {
            if (!fInSync)
            {
                //
                // FIX FIX - may need to migrate 
                // all DBs if the wszGUID is DEF_CAT_SUBSYS_ID
                //

                if (!_CatAdminMigrateSingleDatabase(wszGUID))
                {
                    CATADMIN_LOGERR_LASTERR()
                    goto ErrorReturn;
                }
            }
        }
        else
        {
            CATADMIN_LOGERR_LASTERR()
            goto ErrorReturn;
        }
    }

    //
    // NOTE:
    // Defer registering with the service for the change notificatation so we
    // don't rely on the service during an acquire context
    //

    *phCatAdmin = (HCATADMIN)pCatAdmin;

CommonReturn:
    return(fRet);

ErrorReturn:
        
    if (pCatAdmin != NULL)
    {
        dwErr = GetLastError();

        if (pCatAdmin->hRegisterWaitForClearCache != NULL)
        {
            UnregisterWaitEx(
                pCatAdmin->hRegisterWaitForClearCache, 
                INVALID_HANDLE_VALUE);
        }

        // call UnregisterWaitEx before deteling the critical section
        // because the cb thread tries to enter it
        if (pCatAdmin->fCSInitialized)
        {
            DeleteCriticalSection(&(pCatAdmin->CriticalSection)); 
        }

        if (pCatAdmin->hClearCacheEvent != NULL)
        {
            CloseHandle(pCatAdmin->hClearCacheEvent);
        }

        if (pCatAdmin->pwszSubSysGUID != NULL)
        {
            free(pCatAdmin->pwszSubSysGUID);
        }

        if (pCatAdmin->pwszCatalogFileDir != NULL)
        {
            free(pCatAdmin->pwszCatalogFileDir); 
        }

        if (pCatAdmin->pwszDatabaseFileDir != NULL)
        {
            free(pCatAdmin->pwszDatabaseFileDir); 
        }

        free(pCatAdmin);

        SetLastError(dwErr);
    }

    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorMemory)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorInvalidParam)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorRegisterWaitForSingleObject)  
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorCreateEvent)      
} 

BOOL WINAPI 
CryptCATAdminAcquireContext(
    OUT HCATADMIN   *phCatAdmin, 
    IN const GUID  *pgSubsystem, 
    IN DWORD       dwFlags)
{
    return (CryptCATAdminAcquireContext_Internal(
                phCatAdmin,
                pgSubsystem,
                dwFlags,
                FALSE));
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminReleaseContext
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CryptCATAdminReleaseContext(
    IN HCATADMIN   hCatAdmin, 
    IN DWORD       dwFlags)
{
    CRYPT_CAT_ADMIN         *pCatAdmin          = (CRYPT_CAT_ADMIN *)hCatAdmin;
    BOOL                    fRet                = TRUE;
    int                     i                   = 0;
    LIST_NODE               *pListNode          = NULL;
    
    //
    // Validate input params
    //
    if ((pCatAdmin == NULL) || 
        (pCatAdmin->cbStruct != sizeof(CRYPT_CAT_ADMIN)))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, ErrorInvalidParam) 
    }

    //
    // Un-Register for change notifications from DB process
    //
    // This needs to happen first thing, so that no callbacks
    // happen during cleanup
    //
    if (pCatAdmin->fRegisteredForChangeNotification)
    {
        Client_SSCatDBRegisterForChangeNotification(
                                (DWORD_PTR) pCatAdmin->hClearCacheEvent,
                                0,
                                pCatAdmin->pwszSubSysGUID,
                                TRUE);
    }
    UnregisterWaitEx(pCatAdmin->hRegisterWaitForClearCache, INVALID_HANDLE_VALUE);
    CloseHandle(pCatAdmin->hClearCacheEvent);

    _CatAdminFreeCachedCatalogs(pCatAdmin);
    
    free(pCatAdmin->pwszSubSysGUID);
    free(pCatAdmin->pwszCatalogFileDir); 
    free(pCatAdmin->pwszDatabaseFileDir);
    
    DeleteCriticalSection(&(pCatAdmin->CriticalSection));
    
    free(pCatAdmin);

CommonReturn:
    return(fRet);

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorInvalidParam)
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminAddCatalog
//
//---------------------------------------------------------------------------------------
HCATINFO WINAPI 
CryptCATAdminAddCatalog(
    IN HCATADMIN hCatAdmin,
    IN WCHAR *pwszCatalogFile,
    IN WCHAR *pwszSelectBaseName,
    IN DWORD dwFlags)
{
    CRYPT_CAT_ADMIN         *pCatAdmin                      = (CRYPT_CAT_ADMIN *)hCatAdmin;
    CATALOG_INFO_CONTEXT    *pCatInfoContext                = NULL;
    BOOL                    fRet;
    LPWSTR                  pwszName                        = NULL;
    DWORD                   dwErr                           = 0;
    LPWSTR                  pwszCatalogNameUsed             = NULL;
    LPWSTR                  pwszCatalogNameUsedCopy         = NULL;
    LPWSTR                  pwszFullyQualifiedCatalogFile   = NULL;
    DWORD                   dwLength                        = 0;
    LIST_NODE               *pListNode                      = NULL;
    WCHAR                   wszTmp[1];
    
    if ((pCatAdmin == NULL)                                 ||
        (pCatAdmin->cbStruct != sizeof(CRYPT_CAT_ADMIN))    ||
        (pwszCatalogFile == NULL)                           ||
        (dwFlags != 0))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, ErrorInvalidParam) 
    }

    ErrLog_LogString(NULL, L"Adding Catalog File: ", pwszSelectBaseName, TRUE);

    //
    //  first, check the catalog...
    //
    if (!(IsCatalogFile(INVALID_HANDLE_VALUE, pwszCatalogFile)))
    {
        if (GetLastError() == ERROR_FILE_NOT_FOUND)
        {
            CATADMIN_LOGERR_LASTERR()
            goto ErrorReturn;
        }

        CATADMIN_SETERR_LOG_RETURN(ERROR_BAD_FORMAT, ErrorBadFileFormat) 
    }

    __try
    {
        EnterCriticalSection(&(pCatAdmin->CriticalSection));
    }
    __except(EXCEPTION_EXECUTE_HANDLER) 
    {
        SetLastError(GetExceptionCode());
        CATADMIN_LOGERR_LASTERR()
        return NULL;
    }
    pCatAdmin->fCSEntered = TRUE;

    if (!_CatAdminRegisterForChangeNotification(pCatAdmin))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Clear the cache, since doing the add may change things
    //
    _CatAdminFreeCachedCatalogs(pCatAdmin);

    //
    // If the file name specified by pwszCatalogFile is not a fully qualified
    // path name, we need to build one before calling the service.
    //
    if ((wcschr(pwszCatalogFile, L'\\') == NULL) &&
        (wcschr(pwszCatalogFile, L':') == NULL))
    {
        dwLength = GetCurrentDirectoryW(1, wszTmp) * sizeof(WCHAR);
        dwLength += (wcslen(pwszCatalogFile) + 1) * sizeof(WCHAR);
        if (NULL == (pwszFullyQualifiedCatalogFile = (LPWSTR) malloc(dwLength)))
        {
            CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
        }

        if (!GetCurrentDirectoryW(
                dwLength / sizeof(WCHAR), 
                pwszFullyQualifiedCatalogFile))
        {
            CATADMIN_LOGERR_LASTERR()
            goto ErrorReturn;
        }

        if ((pwszFullyQualifiedCatalogFile[wcslen(pwszFullyQualifiedCatalogFile) - 1] 
                != L'\\'))
        {
            wcscat(pwszFullyQualifiedCatalogFile, L"\\");
        }
        wcscat(pwszFullyQualifiedCatalogFile, pwszCatalogFile);        
    }
    
    //
    // Call the DB process to add the catalog
    //
    if (0 != (dwErr = Client_SSCatDBAddCatalog( 
                            0,
                            pCatAdmin->pwszSubSysGUID,
                            (pwszFullyQualifiedCatalogFile != NULL) ? 
                                pwszFullyQualifiedCatalogFile : 
                                pwszCatalogFile,
                            pwszSelectBaseName,
                            &pwszCatalogNameUsed)))
    {
        CATADMIN_SETERR_LOG_RETURN(dwErr, ErrorCatDBProcess)
    }
    
    //
    // Touch the TimeStamp file
    //
    TimeStampFile_Touch(pCatAdmin->pwszCatalogFileDir);

    //
    // create a psuedo list entry, that really isn't part of the list...
    // this is so the caller can call CryptCATCatalogInfoFromContext
    //
    if (NULL == (pwszCatalogNameUsedCopy = (LPWSTR)
                    malloc((wcslen(pwszCatalogNameUsed) + 1) * sizeof(WCHAR))))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }
   
    wcscpy(pwszCatalogNameUsedCopy, pwszCatalogNameUsed);

    if (NULL == (pCatInfoContext = (CATALOG_INFO_CONTEXT *) 
                    malloc(sizeof(CATALOG_INFO_CONTEXT))))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }
    
    memset(pCatInfoContext, 0, sizeof(CATALOG_INFO_CONTEXT));
    pCatInfoContext->pwszCatalogFile = pwszCatalogNameUsedCopy;
    pCatInfoContext->fResultOfAdd = TRUE;

    if (NULL == (pListNode = (LIST_NODE *) malloc(sizeof(LIST_NODE))))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }
    
    memset(pListNode, 0, sizeof(LIST_NODE));
    pListNode->pElement = pCatInfoContext;
    
CommonReturn:

    MIDL_user_free(pwszCatalogNameUsed);
    
    if (pwszFullyQualifiedCatalogFile != NULL)
    {
        free(pwszFullyQualifiedCatalogFile);
    }

    if ((pCatAdmin != NULL) &&
        (pCatAdmin->fCSEntered))
    {              
        pCatAdmin->fCSEntered = FALSE;
        LeaveCriticalSection(&(pCatAdmin->CriticalSection));   
    }

    ErrLog_LogString(NULL, L"DONE Adding Catalog File: ", pwszSelectBaseName, TRUE);

    return((HCATINFO) pListNode);

ErrorReturn:

    if (pwszCatalogNameUsedCopy != NULL)
    {
        free(pwszCatalogNameUsedCopy);
    }

    if (pCatInfoContext != NULL)
    {
        free(pCatInfoContext);
    }

    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorInvalidParam)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorBadFileFormat)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorCatDBProcess)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorMemory)
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminRemoveCatalog
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CryptCATAdminRemoveCatalog(
    IN HCATADMIN hCatAdmin,
    IN LPCWSTR pwszCatalogFile,
    IN DWORD dwFlags)
{
    BOOL            fRet        = TRUE;
    DWORD           dwErr       = 0;
    CRYPT_CAT_ADMIN *pCatAdmin  = (CRYPT_CAT_ADMIN *)hCatAdmin;

    //
    // Call the DB process to delete the catalog
    //
    if (0 != (dwErr = Client_SSCatDBDeleteCatalog( 
                            0,
                            pCatAdmin->pwszSubSysGUID,
                            pwszCatalogFile)))
    {
        CATADMIN_SETERR_LOG_RETURN(dwErr, ErrorCatDBProcess)
    }

    //
    // Touch the TimeStamp file
    //
    TimeStampFile_Touch(pCatAdmin->pwszCatalogFileDir);

CommonReturn:
    
    return(fRet);

ErrorReturn:

    fRet = FALSE;

    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorCatDBProcess)
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminEnumCatalogFromHash
//
//---------------------------------------------------------------------------------------
HCATINFO WINAPI
CryptCATAdminEnumCatalogFromHash(
    IN HCATADMIN hCatAdmin,
    IN BYTE *pbHash, 
    IN DWORD cbHash,
    IN DWORD dwFlags,
    IN HCATINFO *phPrevCatInfo)
{
    CRYPT_CAT_ADMIN         *pCatAdmin                  = (CRYPT_CAT_ADMIN *)hCatAdmin;
    BOOL                    fFindFirstOnly;
    BOOL                    fInitializeEnum             = FALSE;
    int                     i                           = 0;
    CRYPT_DATA_BLOB         CryptDataBlobHash;
    CRYPT_DATA_BLOB         CryptDataBlobHashTag;
    LPWSTR                  pwszSearch                  = NULL;
    HANDLE                  hFindHandle                 = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATAW        FindData;
    LPWSTR                  pwszHashTag                 = NULL;
    DWORD                   dwErr                       = 0;
    LIST_NODE               *pPrevListNode              = NULL;
    LIST_NODE               *pListNodeToReturn          = NULL;
    LIST_NODE               *pListNode                  = NULL;
    CATALOG_INFO_CONTEXT    *pCatInfoContext            = NULL;
    
    //
    // Validate input params
    //
    if ((pCatAdmin == NULL)                                ||
        (pCatAdmin->cbStruct != sizeof(CRYPT_CAT_ADMIN))   ||
        (cbHash == 0)                                      ||
        (cbHash > MAX_HASH_LEN)                            ||
        (dwFlags != 0))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, ErrorInvalidParam) 
    }

    if (!_CatAdminRegisterForChangeNotification(pCatAdmin))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // If phPrevCatInfo is NULL then that means the caller is only interested
    // in the first catalog that contains the hash, thus no enum state is 
    // started.  If phPrevCatInfo is non NULL, then it contains NULL, or a
    // HCATINFO that was returned from a previous call to 
    // CryptCATAdminEnumCatalogFromHash.  If it contains NULL, then this is 
    // the start of an enum, otherwise it is enuming the next catalog containing
    // the hash.
    //
    if (phPrevCatInfo == NULL)
    {
        fFindFirstOnly = TRUE;
    }
    else
    {
        fFindFirstOnly = FALSE;
        pPrevListNode = (LIST_NODE *) *phPrevCatInfo;        
    }

    //
    // Only allow one thread to view/modify at a time
    //
    __try
    {
        EnterCriticalSection(&(pCatAdmin->CriticalSection));
    }
    __except(EXCEPTION_EXECUTE_HANDLER) 
    {
        SetLastError(GetExceptionCode());
        CATADMIN_LOGERR_LASTERR()
        return FALSE;
    }
    pCatAdmin->fCSEntered = TRUE;
    
    __try
    {

    //
    // This data blob is used to do the find in the database
    //
    CryptDataBlobHash.pbData = pbHash;
    CryptDataBlobHash.cbData = cbHash;

    //
    // Create the tag to be used for calls to CertFindSubjectInSortedCTL
    //
    if (!_CatAdminCreateHashTag(pbHash, cbHash, &pwszHashTag, &CryptDataBlobHashTag))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }
    
    //
    // The enum works as follows:
    //
    // if enum-state is not being initialized OR this is the first call to start an enum
    //
    //      loop through all currently cached catalogs until a catalog containing the 
    //      the hash is found, and return it
    //
    //      if a catalog was not found in the cache, then call the DB process to try and 
    //      find one
    //
    // else (enum state has already been started)
    //
    //      loop through currently cached catalogs, starting with the catalog just after
    //      the current catalog, and until a catalog containing the hash is found
    //

    if ((fFindFirstOnly)  || (pPrevListNode == NULL))
    {
        pListNode = LIST_GetFirst(&(pCatAdmin->CatalogInfoContextList));
        while (pListNode != NULL)
        {
            pCatInfoContext = (CATALOG_INFO_CONTEXT *) LIST_GetElement(pListNode);

            if (CertFindSubjectInSortedCTL(
                    &CryptDataBlobHashTag,
                    pCatInfoContext->pCTLContext,
                    NULL,
                    NULL,
                    NULL))
            {
                pListNodeToReturn = pListNode;
                goto CommonReturn;
            }

            pListNode = LIST_GetNext(pListNode);
        }

        //
        // If we are here, that means we did not find a cached catalog that contained
        // the hash, so call the DB process to try and find one or more.
        // 
        // Call the DB process once if we are not using the default sub-system ID,
        // otherwise call the DB process once for each sub-system.

        if (!pCatAdmin->fUseDefSubSysId)
        {
            if (_CatAdminAddCatalogsToCache(
                        pCatAdmin,
                        pCatAdmin->pwszSubSysGUID, 
                        &CryptDataBlobHash, 
                        &pListNodeToReturn))
            {
                if (pListNodeToReturn == NULL)
                {
                    SetLastError(ERROR_NOT_FOUND);
                    //CATADMIN_LOGERR_LASTERR()
                    goto CatNotFound;
                }

                goto CommonReturn;
            }
            else
            {
                CATADMIN_LOGERR_LASTERR()
                goto ErrorReturn;
            }
        }
        else
        {
            //
            // For each subdir, add all the catalogs that contain the hash
            //

            //
            // Create search string to find all subdirs
            //
            if (NULL == (pwszSearch = _CatAdminCreatePath(
                                            gpwszDatabaseFileBaseDirectory, 
                                            WSZ_CATALOG_SUBSYTEM_SEARCH_STRING,
                                            FALSE)))               
            {
                CATADMIN_LOGERR_LASTERR()
                goto ErrorReturn;
            }

            //
            // Do the initial find
            //
            hFindHandle = FindFirstFileU(pwszSearch, &FindData);
            if (hFindHandle == INVALID_HANDLE_VALUE)
            {
                dwErr = GetLastError();

                //
                // no sub dirs found
                //
                if ((dwErr == ERROR_NO_MORE_FILES)  ||
                    (dwErr == ERROR_PATH_NOT_FOUND) ||
                    (dwErr == ERROR_FILE_NOT_FOUND))
                {
                    CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_FOUND, CatNotFound) 
                }
                else
                {
                    goto ErrorFindFirstFile;
                }
            }    

            while (1)
            {
                //
                // Only care about directories
                //
                if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
                    //
                    // Add all the catalogs in this subdir that contain the hash to
                    // the catalog cache
                    //
                    if (!_CatAdminAddCatalogsToCache(
                                pCatAdmin,
                                FindData.cFileName, 
                                &CryptDataBlobHash, 
                                (pListNodeToReturn == NULL) ? 
                                        &pListNodeToReturn : NULL))
                    {
                        CATADMIN_LOGERR_LASTERR()
                        goto ErrorReturn;
                    }
                }                                                       

                //
                // Get next subdir
                //
                if (!FindNextFileU(hFindHandle, &FindData))            
                {
                    if (GetLastError() == ERROR_NO_MORE_FILES)
                    {
                        break;
                    }
                    else
                    {
                        goto ErrorFindNextFile;
                    }
                }
            }
            
            if (pListNodeToReturn == NULL)
            {
                SetLastError(ERROR_NOT_FOUND);
                //CATADMIN_LOGERR_LASTERR()
                goto CatNotFound; 
            }
        }        
    }
    else
    {
        //
        // Enum state already started, so just search through the rest of the cached 
        // catalogs to try and find one that contains the hash
        //
        pListNode = LIST_GetNext(pPrevListNode);
        while (pListNode != NULL)
        {
            pCatInfoContext = (CATALOG_INFO_CONTEXT *) LIST_GetElement(pListNode);

            if (CertFindSubjectInSortedCTL(
                        &CryptDataBlobHashTag,
                        pCatInfoContext->pCTLContext,
                        NULL,
                        NULL,
                        NULL))
            {
                pListNodeToReturn = pListNode;
                goto CommonReturn;
            }
            
            pListNode = LIST_GetNext(pListNode);
        }

        //
        // If we get here that means no catalog was found
        //
        SetLastError(ERROR_NOT_FOUND);
    }

    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        CATADMIN_SETERR_LOG_RETURN(GetExceptionCode(), ErrorException)
    }

CommonReturn:

    dwErr = GetLastError();

    if (pwszHashTag != NULL)
    {
        free(pwszHashTag);
    }

    if (pwszSearch != NULL)
    {
        free(pwszSearch);
    }

    if (hFindHandle != INVALID_HANDLE_VALUE)
    {
        FindClose(hFindHandle);
    }

    if (pListNodeToReturn != NULL)
    {
        pCatAdmin->nOpenCatInfoContexts++;
    }
    
    if (pPrevListNode != NULL)
    {
        *phPrevCatInfo = NULL;

        //
        // Decrement, since this is the equivalent of 
        // calling CryptCATAdminReleaseCatalogContext
        //
        pCatAdmin->nOpenCatInfoContexts--;
    }

    if ((pCatAdmin != NULL) &&
        (pCatAdmin->fCSEntered))
    {
        pCatAdmin->fCSEntered = FALSE;
        LeaveCriticalSection(&(pCatAdmin->CriticalSection));        
    }

    SetLastError(dwErr);

    return((HCATINFO) pListNodeToReturn);

ErrorReturn:

    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorInvalidParam)
TRACE_ERROR_EX(DBG_SS_TRUST, CatNotFound) 
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorFindFirstFile) 
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorFindNextFile) 
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorException) 
}


//---------------------------------------------------------------------------------------
//
//  CryptCATCatalogInfoFromContext
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CryptCATCatalogInfoFromContext(
    IN HCATINFO hCatInfo,
    IN OUT CATALOG_INFO *psCatInfo,
    IN DWORD dwFlags)
{
    BOOL                    fRet        = TRUE;
    LIST_NODE               *pListNode  = (LIST_NODE *) hCatInfo;
    CATALOG_INFO_CONTEXT    *pContext   = NULL;
    

    if ((pListNode == NULL) || (psCatInfo == NULL))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, ErrorInvalidParam)
    }

    pContext = (CATALOG_INFO_CONTEXT *) LIST_GetElement(pListNode);

    if (pContext->pwszCatalogFile != NULL)
    {
        if ((wcslen(pContext->pwszCatalogFile) + 1) > MAX_PATH)
        {
            CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorTooLong)
        }

        wcscpy(psCatInfo->wszCatalogFile, pContext->pwszCatalogFile);
    }

CommonReturn:
    return(fRet);

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorInvalidParam)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorTooLong)
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminReleaseCatalogContext
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CryptCATAdminReleaseCatalogContext(
    IN HCATADMIN   hCatAdmin, 
    IN HCATINFO    hCatInfo, 
    IN DWORD       dwFlags)
{
    BOOL                    fRet                = TRUE;
    CRYPT_CAT_ADMIN         *pCatAdmin          = (CRYPT_CAT_ADMIN *)hCatAdmin;
    LIST_NODE               *pListNode          = (LIST_NODE *) hCatInfo;
    CATALOG_INFO_CONTEXT    *pCatInfoContext    = NULL;

    if ((pCatAdmin == NULL)                                     ||
        (pCatAdmin->cbStruct != sizeof(CRYPT_CAT_ADMIN))        ||
        (pListNode == NULL))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, ErrorInvalidParam)
    }

    //
    // check to see if this is from and add operation, if so, then clean
    // up allocated memory, otherwise, just decrement ref count
    // 
    pCatInfoContext = (CATALOG_INFO_CONTEXT *) LIST_GetElement(pListNode);
    if (pCatInfoContext->fResultOfAdd)
    {
        free(pCatInfoContext->pwszCatalogFile);
        free(pCatInfoContext);
        free(pListNode);
    }
    else
    {
        // FIX FIX - may need to be smarter about this... like verify
        // the node is actually in the list.
        pCatAdmin->nOpenCatInfoContexts--;
    }
    
CommonReturn:
    return(fRet);

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorInvalidParam);
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminResolveCatalogPath
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CryptCATAdminResolveCatalogPath(
    IN HCATADMIN hCatAdmin,
    IN WCHAR *pwszCatalogFile,
    IN OUT CATALOG_INFO *psCatInfo,
    IN DWORD dwFlags)
{
    BOOL            fRet        = TRUE;
    CRYPT_CAT_ADMIN *pCatAdmin  = (CRYPT_CAT_ADMIN *)hCatAdmin;

    if ((pCatAdmin == NULL)                                 ||
        (pCatAdmin->cbStruct != sizeof(CRYPT_CAT_ADMIN))    ||
        (pwszCatalogFile == NULL)                           ||
        (psCatInfo == NULL)                                 ||
        (psCatInfo->cbStruct != sizeof(CATALOG_INFO))       ||
        (dwFlags != 0))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, ErrorInvalidParam)
    }

    if ((wcslen(pCatAdmin->pwszCatalogFileDir)  +
         wcslen(pwszCatalogFile)                +
         1) > MAX_PATH)
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorTooLong)
    }

    wcscpy(psCatInfo->wszCatalogFile, pCatAdmin->pwszCatalogFileDir);
    wcscat(psCatInfo->wszCatalogFile, pwszCatalogFile);

CommonReturn:
    return(fRet);

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorInvalidParam)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorTooLong)
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminPauseServiceForBackup
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CryptCATAdminPauseServiceForBackup(
    IN DWORD dwFlags,
    IN BOOL  fResume)
{
    BOOL    fRet = TRUE;
    DWORD   dwErr = 0;
    
    //
    // Call the DB process to delete the catalog
    //
    if (0 != (dwErr = Client_SSCatDBPauseResumeService( 
                            0,
                            fResume)))
    {
        CATADMIN_SETERR_LOG_RETURN(dwErr, ErrorCatDBProcess)
    }

CommonReturn:
    
    return(fRet);

ErrorReturn:

    fRet = FALSE;

    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorCatDBProcess)
}


//---------------------------------------------------------------------------------------
//
//  CryptCATAdminCalcHashFromFileHandle
//
//---------------------------------------------------------------------------------------
BOOL WINAPI
CryptCATAdminCalcHashFromFileHandle(
    IN      HANDLE  hFile, 
    IN OUT  DWORD   *pcbHash, 
    IN      BYTE    *pbHash,
    IN      DWORD   dwFlags)
{
    BYTE                *pbRet          = NULL;
    SIP_INDIRECT_DATA   *pbIndirectData = NULL;
    BOOL                fRet;
    GUID                gSubject;
    SIP_DISPATCH_INFO   sSip;

    if ((hFile == NULL)                 ||
        (hFile == INVALID_HANDLE_VALUE) ||
        (pcbHash == NULL)               ||
        (dwFlags != 0))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_INVALID_PARAMETER, InvalidParam)
    }

    if (!CryptSIPRetrieveSubjectGuidForCatalogFile(L"CATADMIN", hFile, &gSubject))
    {
        goto ErrorMemory;
    }

    memset(&sSip, 0x00, sizeof(SIP_DISPATCH_INFO));

    sSip.cbSize = sizeof(SIP_DISPATCH_INFO);

    if (!CryptSIPLoad(&gSubject, 0, &sSip))
    {
        CATADMIN_LOGERR_LASTERR()
        goto SIPLoadError;
    }

    SIP_SUBJECTINFO     sSubjInfo;
    DWORD               cbIndirectData;

    memset(&sSubjInfo, 0x00, sizeof(SIP_SUBJECTINFO));
    sSubjInfo.cbSize                    = sizeof(SIP_SUBJECTINFO);
    sSubjInfo.DigestAlgorithm.pszObjId  = (char *)CertAlgIdToOID(CALG_SHA1);
    sSubjInfo.dwFlags                   =   SPC_INC_PE_RESOURCES_FLAG | 
                                            SPC_INC_PE_IMPORT_ADDR_TABLE_FLAG |
                                            MSSIP_FLAGS_PROHIBIT_RESIZE_ON_CREATE;
    sSubjInfo.pgSubjectType             = &gSubject;
    sSubjInfo.hFile                     = hFile;
    sSubjInfo.pwsFileName               = L"CATADMIN";
    sSubjInfo.dwEncodingType            = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;

    cbIndirectData = 0;

    sSip.pfCreate(&sSubjInfo, &cbIndirectData, NULL);

    if (cbIndirectData == 0)
    {
        SetLastError(E_NOTIMPL);
        //CATADMIN_LOGERR_LASTERR()
        goto SIPError; 
    }

    if (NULL == (pbIndirectData = (SIP_INDIRECT_DATA *) malloc(cbIndirectData)))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }

    if (!(sSip.pfCreate(&sSubjInfo, &cbIndirectData, pbIndirectData)))
    {
        if (GetLastError() == 0)
        {
            SetLastError(ERROR_INVALID_DATA);
        }

        CATADMIN_LOGERR_LASTERR()
        goto SIPError;
    }

    if ((pbIndirectData->Digest.cbData == 0) || 
        (pbIndirectData->Digest.cbData > MAX_HASH_LEN))
    {
        SetLastError( ERROR_INVALID_DATA );
        goto SIPError;
    }

    if (NULL == (pbRet = (BYTE *) malloc(pbIndirectData->Digest.cbData))) 
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }

    memcpy(pbRet, pbIndirectData->Digest.pbData, pbIndirectData->Digest.cbData);

    fRet = TRUE;

CommonReturn:
    if (pbRet)
    {
        if (*pcbHash < pbIndirectData->Digest.cbData)
        {
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            fRet = FALSE;
        }
        else if (pbHash)
        {
            memcpy(pbHash, pbRet, pbIndirectData->Digest.cbData);
        }

        *pcbHash = pbIndirectData->Digest.cbData;

        free(pbRet);
    }

    if (pbIndirectData)
    {
        free(pbIndirectData);
    }

    if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) && 
        (pbHash == NULL))
    {
        fRet = TRUE;
    }

    return(fRet);

ErrorReturn:
    free(pbRet);
    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, SIPLoadError)
TRACE_ERROR_EX(DBG_SS_TRUST, SIPError)
TRACE_ERROR_EX(DBG_SS_TRUST, InvalidParam)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorMemory)
}


//---------------------------------------------------------------------------------------
//
//  I_CryptCatAdminMigrateToNewCatDB
//
//---------------------------------------------------------------------------------------
BOOL WINAPI
I_CryptCatAdminMigrateToNewCatDB()
{
    BOOL                fRet                = TRUE;
    LPWSTR              pwszSearchCatDirs   = NULL;
    LPWSTR              pwszDeleteFile      = NULL;
    LPWSTR              pwsz                = NULL;
    LPWSTR              pwszMigrateFromDir  = NULL;
    HCATADMIN           hCatAdmin           = NULL;
    GUID                gDefault            = DEF_CAT_SUBSYS_ID;
    HANDLE              hFindHandleCatDirs  = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATAW    FindDataCatDirs;
    DWORD               dwErr               = 0;
    HKEY                hKey;
    DWORD               dwDisposition;
    int                 i;
    BOOL                fInSync;
    WCHAR               wszGUID[256];
    LPWSTR              pwszCatalogFileDir  = NULL;
    LPWSTR              pwszDatabaseFileDir = NULL;

    //
    // FIRST!!
    //
    // Clean up the old reg based catroot entry, and if needed, move 
    // the old style catalog database from its old directory to the new directory, 
    // then do the migrate from there
    //
    if (RegCreateKeyExU(    
                HKEY_LOCAL_MACHINE,
                REG_MACHINE_SETTINGS_KEY,
                0, 
                NULL, 
                REG_OPTION_NON_VOLATILE,
                KEY_ALL_ACCESS, 
                NULL,
                &hKey, 
                &dwDisposition) == ERROR_SUCCESS)
    {
        DWORD   dwType;
        DWORD   cbSize;
        
        cbSize = 0;
        RegQueryValueExU(
            hKey, 
            WSZ_CATALOG_FILE_BASE_DIRECTORY, 
            NULL, 
            &dwType, 
            NULL, 
            &cbSize);

        if (cbSize > 0)
        {
            if (NULL == (pwszMigrateFromDir = (LPWSTR) 
                            malloc(sizeof(WCHAR) * ((cbSize / sizeof(WCHAR)) + 3))))
            {
                RegCloseKey(hKey);
                CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
            }

            pwszMigrateFromDir[0] = NULL;

            RegQueryValueExU(
                hKey, 
                WSZ_CATALOG_FILE_BASE_DIRECTORY, 
                NULL, 
                &dwType,
                (BYTE *)pwszMigrateFromDir, 
                &cbSize);

            if (!_CatAdminMigrateCatalogDatabase(
                        pwszMigrateFromDir, 
                        gpwszCatalogFileBaseDirectory))
            {
                RegCloseKey(hKey);
                CATADMIN_LOGERR_LASTERR()
                goto ErrorReturn;
            }
            
            RegDeleteValueU(hKey, WSZ_CATALOG_FILE_BASE_DIRECTORY);
        }

        RegCloseKey(hKey);
    }

    //
    // NOW, that we are in a consistent state
    //
    // For each catalog sub-system, enumerate all catalogs and add them to the 
    // new catalog database under the same sub-system GUID.
    //

    //
    // Create search string to find all catalog sub dirs
    //
    if (NULL == (pwszSearchCatDirs = _CatAdminCreatePath(
                                            gpwszCatalogFileBaseDirectory, 
                                            WSZ_CATALOG_SUBSYTEM_SEARCH_STRING,
                                            FALSE))) 

    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Do the initial find
    //
    hFindHandleCatDirs = FindFirstFileU(pwszSearchCatDirs, &FindDataCatDirs);
    if (hFindHandleCatDirs == INVALID_HANDLE_VALUE)
    {
        //   
        // See if a real error occurred, or just no files
        //
        dwErr = GetLastError();
        if ((dwErr == ERROR_NO_MORE_FILES)  ||
            (dwErr == ERROR_PATH_NOT_FOUND) ||
            (dwErr == ERROR_FILE_NOT_FOUND))
        {
            //
            // There is nothing to do
            //
            SetLastError(0);
            goto RegKeyAdd; 
        }
        else
        {
            CATADMIN_LOGERR_LASTERR()
            goto ErrorFindFirstFile;
        }
    }    

    while (1)
    {
        //
        // Only care about directories
        //
        if (FindDataCatDirs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            _CatAdminMigrateSingleDatabase(FindDataCatDirs.cFileName);   
        } 

        //
        // Get rid of old files
        //
        dwErr = GetLastError();
        if (NULL != (pwsz = _CatAdminCreatePath(
                                    gpwszCatalogFileBaseDirectory, 
                                    FindDataCatDirs.cFileName,
                                    FALSE))) 
        {
            for (i=0; i<NUM_FILES_TO_DELETE; i++)
            {
                            
                if (NULL != (pwszDeleteFile = _CatAdminCreatePath(
                                                    pwsz, 
                                                    ppwszFilesToDelete[i],
                                                    FALSE))) 
                {
                    if (!DeleteFileU(pwszDeleteFile))
                    {
                        //
                        // If delete fails, then log for delete after reboot
                        //
                        MoveFileExW(pwszDeleteFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
                    }
                    free(pwszDeleteFile);
                }             
            }

            free(pwsz);
        }
        SetLastError(dwErr);
        
        //
        // Get next subdir
        //
        if (!FindNextFileU(hFindHandleCatDirs, &FindDataCatDirs))            
        {
            if (GetLastError() == ERROR_NO_MORE_FILES)
            {
                SetLastError(0);
                break;
            }
            else
            {
                CATADMIN_LOGERR_LASTERR()
                goto ErrorFindNextFile;
            }
        }
    }

    //
    // Get rid of old files
    // 
    dwErr = GetLastError();
    for (i=0; i<NUM_FILES_TO_DELETE; i++)
    {
        if (NULL != (pwszDeleteFile = _CatAdminCreatePath(
                                            gpwszCatalogFileBaseDirectory, 
                                            ppwszFilesToDelete[i],
                                            FALSE))) 
        {
            if (!DeleteFileU(pwszDeleteFile))
            {
                //
                // If delete fails, then log for delete after reboot
                //
                MoveFileExW(pwszDeleteFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
            }   
            free(pwszDeleteFile);
        }
    }
    SetLastError(dwErr);


RegKeyAdd:

    //
    // Set reg key so backup does not backup the catroot2 directory
    // which contains jet db files
    //
    if (RegCreateKeyExW(    
            HKEY_LOCAL_MACHINE,
            WSZ_REG_FILES_NOT_TO_BACKUP,
            0, 
            NULL, 
            REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS, 
            NULL,
            &hKey, 
            &dwDisposition) == ERROR_SUCCESS)
    {
        if (RegSetValueExW(
                hKey, 
                WSZ_REG_CATALOG_DATABASE_VALUE,
                0, 
                REG_MULTI_SZ,
                (BYTE *) WSZ_PATH_NOT_TO_BACKUP,
                (wcslen(WSZ_PATH_NOT_TO_BACKUP) + 2) * sizeof(WCHAR)) != ERROR_SUCCESS)
        {
            CATADMIN_LOGERR_LASTERR()
        }

        RegCloseKey(hKey);
    }
    else
    {
        CATADMIN_LOGERR_LASTERR()
    }


    //
    // Force the default DB to be created
    //
    if (CryptCATAdminAcquireContext_Internal(
                &hCatAdmin, 
                &gDefault, 
                NULL, 
                TRUE))
    {
        BYTE        rgHash[20]  = {0};
        HCATINFO    hCatInfo    = NULL;

        hCatInfo = CryptCATAdminEnumCatalogFromHash(
                        hCatAdmin,
                        rgHash,
                        20,
                        0,
                        NULL);

        if (hCatInfo != NULL)
        {
            CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);
        }

        CryptCATAdminReleaseContext(hCatAdmin, 0);

        //
        // Need to create the timestamp files if they don't exist
        //

        guid2wstr(&gDefault, wszGUID);

        //
        // Construct full subdir path to Catalog files TimeStamp location
        //
        if (NULL == (pwszCatalogFileDir = _CatAdminCreatePath(
                                                gpwszCatalogFileBaseDirectory,
                                                wszGUID,
                                                FALSE)))
        {
            CATADMIN_LOGERR_LASTERR()
            goto CommonReturn; // non fatal for the function, so don't error out
        }

        //
        // Construct full subdir path to Database files TimeStamp location
        //
        if (NULL == (pwszDatabaseFileDir = _CatAdminCreatePath(
                                                gpwszDatabaseFileBaseDirectory,
                                                wszGUID,
                                                FALSE)))
        {
            CATADMIN_LOGERR_LASTERR()
            goto CommonReturn; // non fatal for the function, so don't error out
        }

        //
        // See if they are in sync (if they don't exist, that equals out of sync)
        //
        if (TimeStampFile_InSync(
                    pwszCatalogFileDir,
                    pwszDatabaseFileDir,
                    &fInSync))
        {
            if (!fInSync)
            {
                TimeStampFile_Touch(pwszCatalogFileDir);
                TimeStampFile_Touch(pwszDatabaseFileDir);
            }
        }
        else
        {
            CATADMIN_LOGERR_LASTERR()
        }
    }  
    else
    {
        CATADMIN_LOGERR_LASTERR()
    }

CommonReturn:

    dwErr = GetLastError();

    if (pwszMigrateFromDir != NULL)
    {
        free(pwszMigrateFromDir);
    }

    if (pwszSearchCatDirs != NULL)
    {
        free(pwszSearchCatDirs);
    }

    if (hFindHandleCatDirs != INVALID_HANDLE_VALUE)
    {
        FindClose(hFindHandleCatDirs);
    }

    if (pwszCatalogFileDir != NULL)
    {
        free(pwszCatalogFileDir);
    }

    if (pwszDatabaseFileDir != NULL)
    {
        free(pwszDatabaseFileDir);
    }

    SetLastError(dwErr);
    
    return(fRet);

ErrorReturn:

    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorMemory);
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorFindFirstFile)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorFindNextFile)
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminMigrateSingleDatabase
//
//---------------------------------------------------------------------------------------
BOOL
_CatAdminMigrateSingleDatabase(
    LPWSTR  pwszDatabaseGUID)
{
    BOOL                fRet                        = TRUE;
    LPWSTR              pwszCatalogFile             = NULL;
    LPWSTR              pwszSearchCatalogsInDir     = NULL;
    HANDLE              hFindHandleCatalogsInDir    = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATAW    FindDataCatalogsInDir;
    GUID                guid;
    HCATINFO            hCatInfo                    = NULL;
    HCATADMIN           hCatAdmin                   = NULL;
    DWORD               dwErr                       = 0;
    LPWSTR              pwszSubDir                  = NULL;
    LPWSTR              pwszTempDir                 = NULL;
    LPWSTR              pwszTempCatalogFile         = NULL;

    //
    // Acquire the catadmin context to add the catalog files to
    //
    if (!wstr2guid(pwszDatabaseGUID, &guid))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }
    if (!CryptCATAdminAcquireContext_Internal(&hCatAdmin, &guid, NULL, TRUE))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }            

    //
    // Construct full subdir path so we can search for all cat files
    //
    if (NULL == (pwszSubDir = _CatAdminCreatePath(
                                    gpwszCatalogFileBaseDirectory,
                                    pwszDatabaseGUID,
                                    FALSE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Construct temp directory path, and create the directory to back it
    //
    if (NULL == (pwszTempDir = _CatAdminCreatePath(
                                                pwszSubDir,
                                                L"TempDir",
                                                FALSE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    if (!_CatAdminRecursiveCreateDirectory(
            pwszTempDir,
            NULL))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Construct the search string
    //
    if (NULL == (pwszSearchCatalogsInDir = _CatAdminCreatePath(
                                                pwszSubDir,
                                                L"*",
                                                FALSE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // First copy all the catalogs to a temp directory, then add each catalog
    // to the database from the temporary location
    //

    //
    // Copy each file
    //
    memset(&FindDataCatalogsInDir, 0, sizeof(FindDataCatalogsInDir));
    hFindHandleCatalogsInDir = FindFirstFileU(
                                    pwszSearchCatalogsInDir, 
                                    &FindDataCatalogsInDir);
    
    if (hFindHandleCatalogsInDir == INVALID_HANDLE_VALUE)
    {
        dwErr = GetLastError();

        //
        // no files found
        //
        if ((dwErr == ERROR_NO_MORE_FILES)  ||
            (dwErr == ERROR_FILE_NOT_FOUND))
        {
            SetLastError(0);
        }
        else
        {
            CATADMIN_LOGERR_LASTERR()
            goto ErrorFindFirstFile;
        }
    }
    else
    {            
        while (1)
        {
            //
            // Only care about files
            //
            if (!(FindDataCatalogsInDir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                //
                // Construct fully qualified path name to catalog file
                //
                if (NULL == (pwszCatalogFile = _CatAdminCreatePath(
                                                    pwszSubDir,
                                                    FindDataCatalogsInDir.cFileName,
                                                    FALSE)))
                {
                    CATADMIN_LOGERR_LASTERR()
                    goto ErrorReturn;
                }
                
                //
                // Verify that this is a catalog and then copy it to the temp dir
                // which is where it will be installed from
                //
                if (IsCatalogFile(NULL, pwszCatalogFile))
                {
                    if (NULL == (pwszTempCatalogFile = _CatAdminCreatePath(
                                                            pwszTempDir,
                                                            FindDataCatalogsInDir.cFileName,
                                                            FALSE)))
                    {
                        CATADMIN_LOGERR_LASTERR()
                        goto ErrorReturn;
                    }

                    if (!CopyFileU(pwszCatalogFile, pwszTempCatalogFile, FALSE))
                    {
                        CATADMIN_LOGERR_LASTERR()
                        goto ErrorReturn;
                    }

                    free(pwszTempCatalogFile);
                    pwszTempCatalogFile = NULL;                    
                }
            
                free(pwszCatalogFile);
                pwszCatalogFile = NULL;
            }                                                       

            //
            // Get next catalog file
            //
            if (!FindNextFileU(hFindHandleCatalogsInDir, &FindDataCatalogsInDir))            
            {
                if (GetLastError() == ERROR_NO_MORE_FILES)
                {
                    SetLastError(0);
                    break;
                }
                else
                {
                    CATADMIN_LOGERR_LASTERR()
                    goto ErrorFindNextFile;
                }
            }
        }
    }

    //
    // Free up stuff used for find
    //
    free(pwszSearchCatalogsInDir);
    pwszSearchCatalogsInDir = NULL;
    FindClose(hFindHandleCatalogsInDir);
    hFindHandleCatalogsInDir = INVALID_HANDLE_VALUE;
    memset(&FindDataCatalogsInDir, 0, sizeof(FindDataCatalogsInDir));

    //
    // Construct the new search string which point to the temp dir
    //
    if (NULL == (pwszSearchCatalogsInDir = _CatAdminCreatePath(
                                                pwszTempDir,
                                                L"*",
                                                FALSE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Add each catalog in the temp dir to the database
    //
    hFindHandleCatalogsInDir = FindFirstFileU(
                                    pwszSearchCatalogsInDir, 
                                    &FindDataCatalogsInDir);
    
    if (hFindHandleCatalogsInDir == INVALID_HANDLE_VALUE)
    {
        dwErr = GetLastError();

        //
        // no files found
        //
        if ((dwErr == ERROR_NO_MORE_FILES)  ||
            (dwErr == ERROR_FILE_NOT_FOUND))
        {
            SetLastError(0);
        }
        else
        {
            CATADMIN_LOGERR_LASTERR()
            goto ErrorFindFirstFile;
        }
    }
    else
    {            
        while (1)
        {
            //
            // Only care about files
            //
            if (!(FindDataCatalogsInDir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                //
                // Construct fully qualified path name to catalog file
                //
                if (NULL == (pwszCatalogFile = _CatAdminCreatePath(
                                                    pwszTempDir,
                                                    FindDataCatalogsInDir.cFileName,
                                                    FALSE)))
                {
                    CATADMIN_LOGERR_LASTERR()
                    goto ErrorReturn;
                }
                
                hCatInfo = CryptCATAdminAddCatalog(
                                hCatAdmin,
                                pwszCatalogFile,
                                FindDataCatalogsInDir.cFileName,
                                NULL);
                
                if (hCatInfo != NULL)
                {
                    CryptCATAdminReleaseCatalogContext(
                            hCatAdmin, 
                            hCatInfo, 
                            NULL);
                    hCatInfo = NULL;
                }
                else
                {
                    // Log error
                    CATADMIN_LOGERR_LASTERR()
                }
                        
                free(pwszCatalogFile);
                pwszCatalogFile = NULL;
            }                                                       

            //
            // Get next catalog file
            //
            if (!FindNextFileU(hFindHandleCatalogsInDir, &FindDataCatalogsInDir))            
            {
                if (GetLastError() == ERROR_NO_MORE_FILES)
                {
                    SetLastError(0);
                    break;
                }
                else
                {
                    CATADMIN_LOGERR_LASTERR()
                    goto ErrorFindNextFile;
                }
            }
        }
    }

CommonReturn:

    dwErr = GetLastError();

    if (pwszSubDir != NULL)
    {
        free(pwszSubDir);
    }

    if (pwszCatalogFile != NULL)
    {
        free(pwszCatalogFile);
    }

    if (pwszSearchCatalogsInDir != NULL)
    {
        free(pwszSearchCatalogsInDir);
    }

    if (pwszTempDir != NULL)
    {
        I_RecursiveDeleteDirectory(pwszTempDir);
        free(pwszTempDir);
    }

    if (pwszTempCatalogFile != NULL)
    {
        free(pwszTempCatalogFile);
    }

    if (hFindHandleCatalogsInDir != INVALID_HANDLE_VALUE)
    {
        FindClose(hFindHandleCatalogsInDir);
    }

    if (hCatAdmin != NULL)
    {
        CryptCATAdminReleaseContext(hCatAdmin, NULL);
    }

    SetLastError(dwErr);
    
    return(fRet);

ErrorReturn:

    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorFindFirstFile)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorFindNextFile)
}


//---------------------------------------------------------------------------------------
//
//  CatAdminDllMain
//
//---------------------------------------------------------------------------------------
BOOL WINAPI 
CatAdminDllMain(
    HANDLE hInstDLL,
    DWORD fdwReason,
    LPVOID lpvReserved)
{
    BOOL fRet = TRUE;

    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
                fRet = _CatAdminSetupDefaults();
                break;

        case DLL_PROCESS_DETACH:
                _CatAdminCleanupDefaults();
                break;
    }

    return(fRet);
}


//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// Internal functions
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------


//---------------------------------------------------------------------------------------
//
//  _CatAdminSetupDefaults
//
//---------------------------------------------------------------------------------------
BOOL 
_CatAdminSetupDefaults(void)
{
    BOOL    fRet                    = TRUE;
    WCHAR   wszDefaultSystemDir[MAX_PATH + 1];
    
    //
    // Get System default directory
    //
    wszDefaultSystemDir[0] = NULL;
    if (0 == GetSystemDirectoryW(wszDefaultSystemDir, MAX_PATH))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorSystemError;
    }
    
    //
    // Get catalog file base directory 
    //
    if (NULL == (gpwszCatalogFileBaseDirectory = 
                            _CatAdminCreatePath(
                                    wszDefaultSystemDir, 
                                    WSZ_CATALOG_FILE_BASE_DIRECTORY, 
                                    TRUE)))                   
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Get database file base directory
    //
    if (NULL == (gpwszDatabaseFileBaseDirectory = 
                            _CatAdminCreatePath(
                                    wszDefaultSystemDir, 
                                    WSZ_DATABASE_FILE_BASE_DIRECTORY, 
                                    TRUE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

CommonReturn:

    return(fRet);

ErrorReturn:

    if (gpwszCatalogFileBaseDirectory != NULL)
    {
        free(gpwszCatalogFileBaseDirectory);
        gpwszCatalogFileBaseDirectory = NULL;
    }

    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorSystemError);
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminCleanupDefaults
//
//---------------------------------------------------------------------------------------
void _CatAdminCleanupDefaults(void)
{
    if (gpwszCatalogFileBaseDirectory != NULL)
    {
        free(gpwszCatalogFileBaseDirectory);
        gpwszCatalogFileBaseDirectory = NULL;
    }

    if (gpwszDatabaseFileBaseDirectory != NULL)
    {
        free(gpwszDatabaseFileBaseDirectory);
        gpwszDatabaseFileBaseDirectory = NULL;
    }
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminTimeStampFilesInSync
//
//---------------------------------------------------------------------------------------
BOOL
_CatAdminTimeStampFilesInSync(
    LPWSTR  pwszDatabaseGUID,
    BOOL    *pfInSync)
{
    LPWSTR  pwszCatalogFileDir  = NULL;
    LPWSTR  pwszDatabaseFileDir = NULL;
    BOOL    fRet                = TRUE;

    *pfInSync = FALSE;

    //
    // Construct full subdir path to Catalog files TimeStamp location
    //
    if (NULL == (pwszCatalogFileDir = _CatAdminCreatePath(
                                            gpwszCatalogFileBaseDirectory,
                                            pwszDatabaseGUID,
                                            FALSE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Construct full subdir path to Database files TimeStamp location
    //
    if (NULL == (pwszDatabaseFileDir = _CatAdminCreatePath(
                                            gpwszDatabaseFileBaseDirectory,
                                            pwszDatabaseGUID,
                                            FALSE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    fRet = TimeStampFile_InSync(
                pwszCatalogFileDir,
                pwszDatabaseFileDir,
                pfInSync);

CommonReturn:

    if (pwszCatalogFileDir != NULL)
    {
        free(pwszCatalogFileDir);
    }

    if (pwszDatabaseFileDir != NULL)
    {
        free(pwszDatabaseFileDir);
    }
    
    return(fRet);

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;
}

//---------------------------------------------------------------------------------------
//
//  _CatAdminRegisterForChangeNotification
//
//---------------------------------------------------------------------------------------
BOOL 
_CatAdminRegisterForChangeNotification(
    CRYPT_CAT_ADMIN *pCatAdmin
    )
{
    BOOL    fRet    = TRUE;
    DWORD   dwErr   = 0;

    //
    // See if already registered
    //
    if (pCatAdmin->fRegisteredForChangeNotification)
    {
        goto CommonReturn;
    }

    //
    // NOTE:
    // Currently the service ignores the pwszSubSysGUID when registering a change
    // notification because it DOES NOT do notifications on a per pwszSubSysDir basis... 
    // it really should at some point.
    // When it does start to do notifications on per pwszSubSysGUID this will need to
    // change.  CryptCatAdminAcquireContext can be called with a NULL subSysGUID,
    // in which case all SubSysDirs are used, so we would need to register a 
    // change notification for all of them.
    //

    //
    // Register the event with the DB process, so the DB process can SetEvent() it
    // when a changed occurs
    //
    if (0 != (dwErr = Client_SSCatDBRegisterForChangeNotification(
                            (DWORD_PTR) pCatAdmin->hClearCacheEvent,
                            0,
                            pCatAdmin->pwszSubSysGUID,
                            FALSE)))
    {
        CATADMIN_SETERR_LOG_RETURN(dwErr, ErrorCatDBProcess)
    }

    pCatAdmin->fRegisteredForChangeNotification = TRUE;

CommonReturn:

    return fRet;

ErrorReturn:
    
    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorCatDBProcess)
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminFreeCachedCatalogs
//
//---------------------------------------------------------------------------------------
BOOL 
_CatAdminFreeCachedCatalogs(
    CRYPT_CAT_ADMIN         *pCatAdmin)
{
    BOOL                    fRet                = TRUE;
    LIST_NODE               *pListNode          = NULL;
    CATALOG_INFO_CONTEXT    *pCatInfoContext    = NULL;
    
    //
    // NOTE: the caller of this function must have entered the Critical Section for
    // the CatAdminContext
    //

    //
    // Enumerate through all the cached CATALOG_INFO_CONTEXTs and free all the 
    // resources for each
    //
    pListNode = LIST_GetFirst(&(pCatAdmin->CatalogInfoContextList));
    while (pListNode != NULL)
    {
        pCatInfoContext = (CATALOG_INFO_CONTEXT *) LIST_GetElement(pListNode);
        
        free(pCatInfoContext->pwszCatalogFile);
        CertFreeCTLContext(pCatInfoContext->pCTLContext);
        UnmapViewOfFile(pCatInfoContext->pbMappedFile);
        CloseHandle(pCatInfoContext->hMappedFile);

        free(pCatInfoContext);

        pListNode = LIST_GetNext(pListNode);
    }
    LIST_RemoveAll(&(pCatAdmin->CatalogInfoContextList));

    return(fRet);
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminWaitOrTimerCallback
//
//---------------------------------------------------------------------------------------
VOID CALLBACK
_CatAdminWaitOrTimerCallback(
    PVOID lpParameter, 
    BOOLEAN TimerOrWaitFired)
{
    CRYPT_CAT_ADMIN         *pCatAdmin          = (CRYPT_CAT_ADMIN *) lpParameter;
    int                     i                   = 0;
    LIST_NODE               *pListNode          = NULL;
    CATALOG_INFO_CONTEXT    *pCatInfoContext    = NULL;

    //
    // Enter the CS before wacking anything
    //
    __try
    {
        EnterCriticalSection(&(pCatAdmin->CriticalSection));
    }
    __except(EXCEPTION_EXECUTE_HANDLER) 
    {
        SetLastError(GetExceptionCode());
        CATADMIN_LOGERR_LASTERR()
        return;
    }
    pCatAdmin->fCSEntered = TRUE;

    //
    // If there is an open ref count, then we can't clean up
    //
    if (pCatAdmin->nOpenCatInfoContexts != 0)
    {
        pCatAdmin->fCSEntered = FALSE;
        LeaveCriticalSection(&(pCatAdmin->CriticalSection));
        return;  
    }

    //
    // Cleanup all the cached CATALOG_INFO_CONTEXTs
    //
    _CatAdminFreeCachedCatalogs(pCatAdmin);
    
    pCatAdmin->fCSEntered = FALSE;
    LeaveCriticalSection(&(pCatAdmin->CriticalSection));
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminAddCatalogsToCache
//
//---------------------------------------------------------------------------------------
BOOL 
_CatAdminAddCatalogsToCache(
    CRYPT_CAT_ADMIN *pCatAdmin,
    LPWSTR pwszSubSysGUID, 
    CRYPT_DATA_BLOB *pCryptDataBlob, 
    LIST_NODE **ppFirstListNodeAdded)
{
    BOOL                    fRet                = TRUE;
    LPWSTR                  pwszCopy            = NULL;
    DWORD                   i;
    DWORD                   dwNumCatalogNames   = 0;
    LPWSTR                  *ppwszCatalogNames  = NULL;
    DWORD                   dwErr               = 0;
    LIST_NODE               *pListNode          = NULL;
    LPWSTR                  pwszSubSysDir       = NULL;   
    BOOL                    fFirstCatalogAdded  = FALSE;
    
    if (ppFirstListNodeAdded != NULL)
    {
        *ppFirstListNodeAdded = NULL;
    }

    if (NULL == (pwszSubSysDir = _CatAdminCreatePath(
                                        gpwszCatalogFileBaseDirectory, 
                                        pwszSubSysGUID, 
                                        FALSE)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    //
    // Call DB process and get list of catalogs into ppwszCatalogNames
    //
    // NOTE: the order in which the service adds CatNames to the list results in
    // only the first CatName of the list being guaranteed to contain the
    // hash... all other CatNames may or may not contain the hash.  Which
    // is OK because this code only assumes the first CatName contains the 
    // hash, and then searches all other CatNames for the hash before returning them.
    //
    if (0 != (dwErr = Client_SSCatDBEnumCatalogs(
                            0,
                            pwszSubSysGUID,
                            pCryptDataBlob->pbData,
                            pCryptDataBlob->cbData,
                            &dwNumCatalogNames,
                            &ppwszCatalogNames)))
    {
        CATADMIN_SETERR_LOG_RETURN(dwErr, ErrorServiceError)
    }

    //
    // Loop for each catalog and create the CTL context
    //
    for (i=0; i<dwNumCatalogNames; i++)
    {
        //
        // Make a copy of the catalog file name
        //
        if (NULL == (pwszCopy = _CatAdminCreatePath(
                                        pwszSubSysDir, 
                                        ppwszCatalogNames[i], 
                                        FALSE)))                         
        {
            CATADMIN_LOGERR_LASTERR()
            goto ErrorReturn;
        }

        if (!_CatAdminAddSingleCatalogToCache(
                pCatAdmin,
                pwszCopy, 
                &pListNode))
        {
            //
            // if this isn't the first catalog, then continue since the
            // macro operation may still succeed without the current catalog
            //
            if (i != 0)
            {
                CATADMIN_LOGERR_LASTERR()
                continue;
            }
            
            CATADMIN_LOGERR_LASTERR()
            goto ErrorReturn;
        }

        //
        // This will only be set for the first catalog added, 
        // as per the NOTE above
        //
        if ((ppFirstListNodeAdded != NULL) &&
            (*ppFirstListNodeAdded == NULL))
        {
            *ppFirstListNodeAdded = pListNode;
        }
    }

CommonReturn:

    if (ppwszCatalogNames != NULL)
    {
        for (i=0; i<dwNumCatalogNames; i++)
        {
            MIDL_user_free(ppwszCatalogNames[i]);
        }

        MIDL_user_free(ppwszCatalogNames);
    }
    
    if (pwszSubSysDir != NULL)
    {
        free(pwszSubSysDir);
    }

    return(fRet);

ErrorReturn:

    if (pwszCopy != NULL)
    {
        free(pwszCopy);
    }

    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorServiceError)
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminAddSingleCatalogToCache
//
//---------------------------------------------------------------------------------------
BOOL 
_CatAdminAddSingleCatalogToCache(
    CRYPT_CAT_ADMIN *pCatAdmin,
    LPWSTR pwszCatalog, 
    LIST_NODE **ppListNodeAdded)
{
    BOOL                    fRet                = TRUE;
    DWORD                   dwErr               = 0;
    LIST_NODE               *pListNode          = NULL;
    CATALOG_INFO_CONTEXT    *pCatInfoContext    = NULL;
    CATALOG_INFO_CONTEXT    *pCatInfoContextAdd = NULL;

    *ppListNodeAdded = NULL;

    //
    // If there is already a copy of this catalog, then just get out
    //
    pListNode = LIST_GetFirst(&(pCatAdmin->CatalogInfoContextList));
    while (pListNode != NULL)
    {
        pCatInfoContext = (CATALOG_INFO_CONTEXT *) LIST_GetElement(pListNode);

        if (_wcsicmp(pCatInfoContext->pwszCatalogFile, pwszCatalog) == 0)
        {
            *ppListNodeAdded = pListNode;
            goto CommonReturn;
        }

        pListNode = LIST_GetNext(pListNode);
    }

    //
    // Allocate space for a new cached catalog context
    //
    if (NULL == (pCatInfoContextAdd = (CATALOG_INFO_CONTEXT *) 
                    malloc(sizeof(CATALOG_INFO_CONTEXT))))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }
    memset(pCatInfoContextAdd, 0, sizeof(CATALOG_INFO_CONTEXT));
    pCatInfoContextAdd->fResultOfAdd = FALSE;
    
    //
    // Open, create a file mapping, and create the CTL context for 
    // the catalog file
    //
    if (!CatUtil_CreateCTLContextFromFileName(
            pwszCatalog,
            &pCatInfoContextAdd->hMappedFile,
            &pCatInfoContextAdd->pbMappedFile,
            &pCatInfoContextAdd->pCTLContext,
            TRUE))
    {
        CATADMIN_LOGERR_LASTERR()
        ErrLog_LogString(NULL, L"The following file was not found - ", pwszCatalog, TRUE);
        goto ErrorReturn;
    }
    
    pCatInfoContextAdd->pwszCatalogFile = pwszCatalog;

    //
    // Add to the list of cached catalog contexts
    //
    if (NULL == (pListNode = LIST_AddTail(
                                &(pCatAdmin->CatalogInfoContextList), 
                                pCatInfoContextAdd)))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    *ppListNodeAdded = pListNode;
        
CommonReturn:

    return(fRet);

ErrorReturn:

    dwErr = GetLastError();
    
    if (pCatInfoContextAdd != NULL)
    {
        if (pCatInfoContextAdd->pCTLContext != NULL)
        {
            CertFreeCTLContext(pCatInfoContextAdd->pCTLContext);
        }

        if (pCatInfoContextAdd->pbMappedFile != NULL)
        {
            UnmapViewOfFile(pCatInfoContextAdd->pbMappedFile);
        }

        if (pCatInfoContextAdd->hMappedFile != NULL)
        {
            CloseHandle(pCatInfoContextAdd->hMappedFile);
        }

        free(pCatInfoContextAdd);        
    }

    SetLastError(dwErr);

    fRet = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, ErrorMemory)
}


//---------------------------------------------------------------------------------------
//
// _CatAdminMigrateCatalogDatabase
//
// This migration code deals with very old catalog databases.  In the olden days, the 
// catroot dir location could be specified by a particular registry key... that is no 
// longer true.  So, if an old system is being upgraded that has the registry key, this 
// code moves all the catalog files from the location specified by the registry key to 
// the %SystemDefaultDir%\Catroot dir.  Then it shwacks the registry key.
//
//---------------------------------------------------------------------------------------
BOOL 
_CatAdminMigrateCatalogDatabase(
    LPWSTR pwszFrom, 
    LPWSTR pwszTo)
{
    DWORD   dwAttr = 0;
    WCHAR   wszFrom[MAX_PATH];
    WCHAR   wszTo[MAX_PATH];

    //
    // If they are the same dir then just get out
    //
    if (((wcslen(pwszFrom) + 1) > MAX_PATH) ||
        ((wcslen(pwszFrom) + 1) > MAX_PATH))
    {
        return TRUE;
    }
    wcscpy(wszFrom, pwszFrom);
    wcscpy(wszTo, pwszTo);
    if (wszFrom[wcslen(wszFrom) - 1] != L'\\')
    {
        wcscat(wszFrom, L"\\");
    }
    if (wszTo[wcslen(wszTo) - 1] != L'\\')
    {
        wcscat(wszTo, L"\\");
    }
    if (_wcsicmp(wszFrom, wszTo) == 0)
    {
        return TRUE;
    }

    //
    // if the pwszTo dir already exists, then don't do a thing.
    //
    dwAttr = GetFileAttributesU(pwszTo);

    if (0xFFFFFFFF != dwAttr) 
    {
        if (FILE_ATTRIBUTE_DIRECTORY & dwAttr)
        {
            //
            // dir already exists... 
            //
            return TRUE;
        }
        else
        {
            //
            // something exists with pwszTo name, but it isn't a dir
            //
            CATADMIN_LOGERR_LASTERR()
            return FALSE;
        }
    }

    //
    // if the pwszFrom dir does not exist, then don't do a thing.
    //
    dwAttr = GetFileAttributesU(pwszFrom);

    if ((0xFFFFFFFF == dwAttr) || (!(FILE_ATTRIBUTE_DIRECTORY & dwAttr)))
    {
        return TRUE;
    }

    if (!_CatAdminRecursiveCreateDirectory(pwszTo, NULL))
    {
        CATADMIN_LOGERR_LASTERR()
        return FALSE;
    }

    if (!I_RecursiveCopyDirectory(pwszFrom, pwszTo))
    {
        CATADMIN_LOGERR_LASTERR()
        return FALSE;
    }

    //
    // Don't check for error on delete since this operation is NOT mandatory
    //
    I_RecursiveDeleteDirectory(pwszFrom);

    return TRUE;
}




//---------------------------------------------------------------------------------------
//
//  _CatAdminBToHex
//
//---------------------------------------------------------------------------------------
WCHAR rgHexDigit[] = {  L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
                        L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F' };
void 
_CatAdminBToHex (
    LPBYTE pbDigest, 
    DWORD iByte, 
    LPWSTR pwszHashTag)
{
    DWORD iTag;
    DWORD iHexDigit1;
    DWORD iHexDigit2;

    iTag = iByte * 2;
    iHexDigit1 = (pbDigest[iByte] & 0xF0) >> 4;
    iHexDigit2 = (pbDigest[iByte] & 0x0F);

    pwszHashTag[iTag] = rgHexDigit[iHexDigit1];
    pwszHashTag[iTag + 1] = rgHexDigit[iHexDigit2];
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminCreateHashTag
//
//---------------------------------------------------------------------------------------
BOOL
_CatAdminCreateHashTag(
    BYTE            *pbHash,
    DWORD           cbHash,
    LPWSTR          *ppwszHashTag,
    CRYPT_DATA_BLOB *pCryptDataBlob)
{
    DWORD           cwTag;
    DWORD           cCount;
    
    cwTag = ((cbHash * 2) + 1);
    if (NULL == (*ppwszHashTag = (LPWSTR) malloc(cwTag * sizeof(WCHAR))))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        CATADMIN_LOGERR_LASTERR()
        return(FALSE);
    }

    for (cCount = 0; cCount < cbHash; cCount++)
    {
        _CatAdminBToHex(pbHash, cCount, *ppwszHashTag);
    }
    (*ppwszHashTag)[cwTag - 1] = L'\0';

    pCryptDataBlob->pbData = (BYTE *) *ppwszHashTag;
    pCryptDataBlob->cbData = cwTag * sizeof(WCHAR);

    return (TRUE);
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminRecursiveCreateDirectory
//
//---------------------------------------------------------------------------------------
BOOL 
_CatAdminRecursiveCreateDirectory(
    IN LPCWSTR pwszDir,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes
    )
{
    BOOL fResult;

    DWORD dwAttr;
    DWORD dwErr;
    LPCWSTR pwsz;
    DWORD cch;
    WCHAR wch;
    LPWSTR pwszParent = NULL;

    //
    // if last char is a '\', then just strip it and recurse
    //
    if (pwszDir[wcslen(pwszDir) - 1] == L'\\')
    {
        cch = wcslen(pwszDir);
        if (NULL == (pwszParent = (LPWSTR) malloc(cch * sizeof(WCHAR))))
        {
            CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
        }

        memcpy(pwszParent, pwszDir, (cch - 1) * sizeof(WCHAR));
        pwszParent[cch - 1] = L'\0';

        fResult = _CatAdminRecursiveCreateDirectory(
                        pwszParent, 
                        lpSecurityAttributes);

        goto CommonReturn;
    }

    //
    // See if dir already exists
    //
    dwAttr = GetFileAttributesU(pwszDir);
    if (0xFFFFFFFF != dwAttr) 
    {
        if (FILE_ATTRIBUTE_DIRECTORY & dwAttr)
        {
            return TRUE;
        }

        CATADMIN_LOGERR_LASTERR()
        goto InvalidDirectoryAttr;
    }

    //
    // If it was an error other than file/path not found, error out
    //
    dwErr = GetLastError();
    if (!(ERROR_PATH_NOT_FOUND == dwErr || ERROR_FILE_NOT_FOUND == dwErr))
    {
        CATADMIN_LOGERR_LASTERR()
        goto GetFileAttrError;
    }

    //
    // Try creating the new dir
    //
    if (CreateDirectoryU(
            pwszDir,
            lpSecurityAttributes)) 
    {
        SetFileAttributesU(pwszDir, FILE_ATTRIBUTE_NORMAL);
        return TRUE;
    }

    dwErr = GetLastError();
    if (!(ERROR_PATH_NOT_FOUND == dwErr || ERROR_FILE_NOT_FOUND == dwErr))
    {
        CATADMIN_LOGERR_LASTERR()
        goto CreateDirectoryError;
    }

    //
    // Peal off the last path name component
    //
    cch = wcslen(pwszDir);
    pwsz = pwszDir + cch;

    while (L'\\' != *pwsz) 
    {
        if (pwsz == pwszDir)
        {
            // Path didn't have a \.
            CATADMIN_SETERR_LOG_RETURN(ERROR_BAD_PATHNAME, BadDirectoryPath)
        }
        pwsz--;
    }

    cch = (DWORD)(pwsz - pwszDir);
    if (0 == cch)
    {
        // Detected leading \Path
        CATADMIN_SETERR_LOG_RETURN(ERROR_BAD_PATHNAME, BadDirectoryPath)
    }


    // Check for leading \\ or x:\.
    wch = *(pwsz - 1);
    if ((1 == cch && L'\\' == wch) || (2 == cch && L':' == wch))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_BAD_PATHNAME, BadDirectoryPath)
    }

    if (NULL == (pwszParent = (LPWSTR) malloc((cch + 1) * sizeof(WCHAR))))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }

    memcpy(pwszParent, pwszDir, cch * sizeof(WCHAR));
    pwszParent[cch] = L'\0';

    if (!_CatAdminRecursiveCreateDirectory(pwszParent, lpSecurityAttributes))
    {
        CATADMIN_LOGERR_LASTERR()
        goto ErrorReturn;
    }

    if (!CreateDirectoryU(
            pwszDir,
            lpSecurityAttributes)) 
    {
        CATADMIN_LOGERR_LASTERR()
        goto CreateDirectory2Error;
    }
    SetFileAttributesU(pwszDir, FILE_ATTRIBUTE_NORMAL);

    fResult = TRUE;

CommonReturn:

    if (pwszParent != NULL)
    {
        free(pwszParent);
    }
    return fResult;
ErrorReturn:

    fResult = FALSE;
    goto CommonReturn;

TRACE_ERROR_EX(DBG_SS_TRUST, InvalidDirectoryAttr)
TRACE_ERROR_EX(DBG_SS_TRUST, GetFileAttrError)
TRACE_ERROR_EX(DBG_SS_TRUST, CreateDirectoryError)
TRACE_ERROR_EX(DBG_SS_TRUST, BadDirectoryPath)
TRACE_ERROR_EX(DBG_SS_TRUST, ErrorMemory)
TRACE_ERROR_EX(DBG_SS_TRUST, CreateDirectory2Error)
}


//---------------------------------------------------------------------------------------
//
//  _CatAdminCreatePath
//
//---------------------------------------------------------------------------------------
LPWSTR 
_CatAdminCreatePath(
    IN LPCWSTR  pwsz1,
    IN LPCWSTR  pwsz2,
    IN BOOL     fAddEndingSlash
    )
{
    LPWSTR  pwszTemp    = NULL;
    int     nTotalLen   = 0;
    int     nLenStr1    = 0;

    //
    // Calculate the length of the resultant string as the sum of the length
    // of pwsz1, length of pwsz2, a NULL char, and a possible extra '\' char
    //
    nLenStr1 = wcslen(pwsz1);
    nTotalLen = nLenStr1 + wcslen(pwsz2) + 2;
    if (fAddEndingSlash)
    {
        nTotalLen++;   
    }

    //
    // Allocate the string and copy pwsz1 into the buffer
    //
    if (NULL == (pwszTemp = (LPWSTR) malloc(sizeof(WCHAR) * nTotalLen)))
    {
        CATADMIN_SETERR_LOG_RETURN(ERROR_NOT_ENOUGH_MEMORY, ErrorMemory)
    }

    wcscpy(pwszTemp, pwsz1);

    //
    // Add the extra '\' if needed
    //
    if (pwsz1[nLenStr1 - 1] != L'\\')
    {
        wcscat(pwszTemp, L"\\");
    }

    //
    // Tack on pwsz2
    //
    wcscat(pwszTemp, pwsz2);

    if (fAddEndingSlash)
    {
        wcscat(pwszTemp, L"\\");
    }

CommonReturn:

    return (pwszTemp);

ErrorReturn:

    goto CommonReturn; 

TRACE_ERROR_EX(DBG_SS_CATDBSVC, ErrorMemory)
}