//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File:        db3.cpp
//
// Contents:    Cert Server Database interface implementation
//
// History:     13-June-97       larrys created
//
//---------------------------------------------------------------------------

#include <pch.cpp>

#pragma hdrstop

#include <stdio.h>

#include "csprop.h"

#define __dwFILE__	__dwFILE_CERTSRV_DB3_CPP__


ICertDB *g_pCertDB = NULL;
BOOL g_fDBRecovered = FALSE;

WCHAR g_wszDatabase[MAX_PATH];
WCHAR g_wszLogDir[MAX_PATH];
WCHAR g_wszSystemDir[MAX_PATH];

const WCHAR g_wszCertSrvDotExe[] = L"certsrv.exe";
const int MAXDWORD_STRLEN = 11;

HRESULT
dbCheckRecoveryState(
    IN HKEY hkeyConfig,
    IN DWORD cSession,
    IN WCHAR const *pwszEventSource,
    IN WCHAR const *pwszLogDir,
    IN WCHAR const *pwszSystemDir,
    IN WCHAR const *pwszTempDir);

typedef struct _REGDBDIR
{
    WCHAR const *pwszRegName;
    BOOL	 fMustExist;
    WCHAR       *pwszBuf;
} REGDBDIR;

HRESULT dbGetRestoreDataDWORD(
    LPCWSTR pwszRestoreFile,
    LPCWSTR pwszName,
    DWORD* pdwData)
{
    WCHAR buffer[MAXDWORD_STRLEN]; // large enough to fit MAXDWORD decimal (4294967295)

    GetPrivateProfileString(
        wszRESTORE_SECTION,
        pwszName,
        L"",
        buffer,
        ARRAYSIZE(buffer),
        pwszRestoreFile);

    if(0==wcscmp(buffer, L""))
    {
        return S_FALSE;
    }

    *pdwData = _wtoi(buffer);

    return S_OK;
}

HRESULT dbGetRestoreDataLPWSZ(
    LPCWSTR pwszRestoreFile,
    LPCWSTR pwszName,
    LPWSTR* ppwszData)
{
    HRESULT hr = S_OK;
    WCHAR buffer[MAX_PATH+1];

    GetPrivateProfileString(
        wszRESTORE_SECTION,
        pwszName,
        L"",
        buffer,
        ARRAYSIZE(buffer),
        pwszRestoreFile);

    if(0==wcscmp(buffer, L""))
    {
        return S_FALSE;
    }

    *ppwszData = (LPWSTR)LocalAlloc(LMEM_FIXED,
        sizeof(WCHAR)*(wcslen(buffer)+1));
    _JumpIfAllocFailed(*ppwszData, error);

    wcscpy(*ppwszData, buffer);

error:
    return hr;
}

HRESULT dbGetRestoreDataMULTISZ(
    LPCWSTR pwszRestoreFile,
    LPCWSTR pwszName,
    LPWSTR *ppwszData,
    DWORD *pcbData)
{
   HRESULT hr = S_OK;
   WCHAR buffer[MAX_PATH+1];
   int cData;
   LPWSTR pwszFullName = NULL;
   DWORD cbData = 0;
   LPWSTR pwszData = NULL;
   WCHAR *pwszCrt = NULL; // no free

   pwszFullName = (LPWSTR)LocalAlloc(LMEM_FIXED, 
       sizeof(WCHAR)* 
              (wcslen(pwszName)+
               wcslen(wszRESTORE_NEWLOGSUFFIX)+
               MAXDWORD_STRLEN+1));
   _JumpIfAllocFailed(pwszFullName, error);

   wcscpy(pwszFullName, L"");

   for(cbData=0, cData = 0;; cData++)
   {
       wsprintf(pwszFullName, L"%s%d", pwszName, cData);

       GetPrivateProfileString(
            wszRESTORE_SECTION,
            pwszFullName,
            L"",
            buffer,
            ARRAYSIZE(buffer),
            pwszRestoreFile);

        if(0==wcscmp(buffer, L""))
        {
            if(0==cData)
            {
                hr = S_FALSE;
                _JumpErrorStr(hr, error, "no restore data", pwszRestoreFile);
            }
            else
            {
                break;
            }
        }

        cbData += wcslen(buffer)+1;

       wsprintf(pwszFullName, L"%s%s%d", pwszName, wszRESTORE_NEWLOGSUFFIX, 
           cData);

       GetPrivateProfileString(
            wszRESTORE_SECTION,
            pwszFullName,
            L"",
            buffer,
            ARRAYSIZE(buffer),
            pwszRestoreFile);

        if(0==wcscmp(buffer, L""))
        {
            hr = ERROR_INVALID_DATA;
            _JumpErrorStr(hr, error, 
                "restore file contains inconsistent data", pwszRestoreFile);
        }

        cbData += wcslen(buffer)+1;
   }

   cbData++; // trailing zero
   cbData *= sizeof(WCHAR);

   pwszData = (LPWSTR)LocalAlloc(LMEM_FIXED, cbData);
   _JumpIfAllocFailed(pwszData, error);

   for(pwszCrt=pwszData, cData = 0;; cData++)
   {
       wsprintf(pwszFullName, L"%s%d", pwszName, cData);

       GetPrivateProfileString(
            wszRESTORE_SECTION,
            pwszFullName,
            L"",
            buffer,
            ARRAYSIZE(buffer),
            pwszRestoreFile);

       if(0==wcscmp(buffer, L""))
       {
           break;
       }

       wcscpy(pwszCrt, buffer);
       pwszCrt += wcslen(buffer)+1;

       wsprintf(pwszFullName, L"%s%s%d", pwszName, wszRESTORE_NEWLOGSUFFIX, 
           cData);

       GetPrivateProfileString(
            wszRESTORE_SECTION,
            pwszFullName,
            L"",
            buffer,
            ARRAYSIZE(buffer),
            pwszRestoreFile);

       wcscpy(pwszCrt, buffer);
       pwszCrt += wcslen(buffer)+1;
   }

   *pwszCrt = L'\0';

   *ppwszData = pwszData;
   *pcbData = cbData;

error:
   LOCAL_FREE(pwszFullName);
   if(S_OK!=hr)
   {
       LOCAL_FREE(pwszData);
   }
   return hr;
}

HRESULT dbRestoreRecoveryStateFromFile(LPCWSTR pwszLogDir)
{
    HRESULT hr = S_OK;
    LPWSTR pwszRestoreFile = NULL;
    WCHAR buffer[256];
    DWORD dwRestoreMapCount, 
        dwRegLowLogNumber,
        dwRegHighLogNumber, 
        dwDatabaseRecovered;
    LPWSTR pwszRestoreMap = NULL;
    DWORD cbRestoreMap = 0;
    LPWSTR pwszPath = NULL;
    HKEY hkey = NULL;
    DWORD dwDisposition;
    HKEY hkeyRestore = NULL;
    BOOL fDatabaseRecovered;

    LPWSTR pwszBackupLogDir = NULL;
    LPWSTR pwszCheckpointFile = NULL;
    LPWSTR pwszLogPath = NULL;

    CSASSERT(pwszLogDir);

    pwszRestoreFile = (LPWSTR)LocalAlloc(LMEM_FIXED,
        sizeof(WCHAR)*(wcslen(pwszLogDir)+wcslen(wszRESTORE_FILENAME)+2));
    _JumpIfAllocFailed(pwszRestoreFile, error);

    wcscpy(pwszRestoreFile, pwszLogDir);
    wcscat(pwszRestoreFile, L"\\");
    wcscat(pwszRestoreFile, wszRESTORE_FILENAME);

    // is there a restore state file?
    if(-1 != GetFileAttributes(pwszRestoreFile))
    {
        // check first if a restore is in progress
        GetPrivateProfileString(
            wszRESTORE_SECTION,
            wszREGRESTORESTATUS,
            L"",
            buffer,
            ARRAYSIZE(buffer),
            pwszRestoreFile);

        if(wcscmp(buffer, L""))
        {
            // restore in progress, bail
            hr = _wtoi(buffer);
            _JumpError(hr, error, "A restore is in progress");
        }

        hr = myRegOpenRelativeKey(
                            NULL,
                            L"",
                            RORKF_CREATESUBKEYS,
                            &pwszPath,
                            NULL,           // ppwszName
                            &hkey);
        _JumpIfError(hr, error, "myRegOpenRelativeKey");


        hr = RegCreateKeyEx(
                        hkey,
                        wszREGKEYRESTOREINPROGRESS,
                        0,                  // Reserved
                        NULL,               // lpClass
                        0,                  // dwOptions
                        KEY_ALL_ACCESS,
                        NULL,
                        &hkeyRestore,
                        &dwDisposition);
        _JumpIfErrorStr(hr, error, "RegCreateKeyEx", wszREGKEYRESTOREINPROGRESS);

        hr = dbGetRestoreDataDWORD(
            pwszRestoreFile,
            wszREGRESTOREMAPCOUNT,
            &dwRestoreMapCount);
        if(S_FALSE==hr)
        {
            // mandatory
            hr = E_ABORT;
        }
        _JumpIfError(hr, error, 
            "restore ini file invalid, wszREGRESTOREMAPCOUNT not found" );

        hr = dbGetRestoreDataDWORD(
            pwszRestoreFile,
            wszREGLOWLOGNUMBER,
            &dwRegLowLogNumber);
        if(S_FALSE==hr)
        {
            // mandatory
            hr = E_ABORT;
        }
        _JumpIfError(hr, error, 
            "restore ini file invalid, wszREGLOWLOGNUMBER not found" );

        hr = dbGetRestoreDataDWORD(
            pwszRestoreFile,
            wszREGHIGHLOGNUMBER,
            &dwRegHighLogNumber);
        if(S_FALSE==hr)
        {
            // mandatory
            hr = E_ABORT;
        }
        _JumpIfError(hr, error, 
            "restore ini file invalid, wszREGHIGHLOGNUMBER not found" );

        hr = dbGetRestoreDataDWORD(
            pwszRestoreFile,
            wszREGDATABASERECOVERED,
            &dwDatabaseRecovered);
        if(S_FALSE==hr)
        {
            // mandatory
            hr = E_ABORT;
        }
        _JumpIfError(hr, error, 
            "restore ini file invalid, wszREGDATABASERECOVERED not found" );

        fDatabaseRecovered = dwDatabaseRecovered?TRUE:FALSE;

        hr = dbGetRestoreDataLPWSZ(
            pwszRestoreFile,
            wszREGBACKUPLOGDIRECTORY,
            &pwszBackupLogDir);
        if(S_FALSE==hr)
        {
            // optional
            hr = S_OK;
        }
        _JumpIfErrorStr(hr, error, "dbGetRestoreDataLPWSZ", wszREGBACKUPLOGDIRECTORY );


        hr = dbGetRestoreDataLPWSZ(
            pwszRestoreFile,
            wszREGCHECKPOINTFILE,
            &pwszCheckpointFile);
        if(S_FALSE==hr)
        {
            // optional
            hr = S_OK;
        }
        _JumpIfErrorStr(hr, error, "dbGetRestoreDataLPWSZ", wszREGCHECKPOINTFILE );


        hr = dbGetRestoreDataLPWSZ(
            pwszRestoreFile,
            wszREGLOGPATH,
            &pwszLogPath);
        if(S_FALSE==hr)
        {
            // optional
            hr = S_OK;
        }
        _JumpIfErrorStr(hr, error, "dbGetRestoreDataLPWSZ", wszREGLOGPATH );


        hr = dbGetRestoreDataMULTISZ(
            pwszRestoreFile,
            wszREGRESTOREMAP,
            &pwszRestoreMap,
            &cbRestoreMap);
        if(S_FALSE==hr)
        {
            // optional
            hr = S_OK;
        }
        _JumpIfErrorStr(hr, error, "dbGetRestoreDataDWORD", L"wszRESTOREMAP");

       hr = RegSetValueEx(
                       hkeyRestore,
                       wszREGRESTOREMAPCOUNT,
                       0,
                       REG_DWORD,
                       (BYTE *) &dwRestoreMapCount,
                       sizeof(DWORD));
       _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGRESTOREMAPCOUNT);

       hr = RegSetValueEx(
                       hkeyRestore,
                       wszREGLOWLOGNUMBER,
                       0,
                       REG_DWORD,
                       (BYTE *) &dwRegLowLogNumber,
                       sizeof(DWORD));
       _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGLOWLOGNUMBER);

       hr = RegSetValueEx(
                       hkeyRestore,
                       wszREGHIGHLOGNUMBER,
                       0,
                       REG_DWORD,
                       (BYTE *) &dwRegHighLogNumber,
                       sizeof(DWORD));
       _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGHIGHLOGNUMBER);

        hr = RegSetValueEx(
                        hkeyRestore,
                        wszREGDATABASERECOVERED,
                        0,
                        REG_BINARY,
                        (BYTE *) &fDatabaseRecovered,
                        sizeof(BOOLEAN));
        _JumpIfError(hr, error, "RegSetValueEx");

        if(pwszBackupLogDir)
        {
            hr = SetRegistryLocalPathString(
                                        hkeyRestore,
                                        wszREGBACKUPLOGDIRECTORY,
                                        pwszBackupLogDir);
            _JumpIfErrorStr(hr, error, "SetRegistryLocalPathString", 
                wszREGBACKUPLOGDIRECTORY);
        }

        if(pwszCheckpointFile)
        {
            hr = SetRegistryLocalPathString(
                                        hkeyRestore,
                                        wszREGCHECKPOINTFILE,
                                        pwszCheckpointFile);
            _JumpIfErrorStr(hr, error, "SetRegistryLocalPathString", 
                wszREGCHECKPOINTFILE);
        }

        if(pwszLogPath)
        {
            hr = SetRegistryLocalPathString(
                                        hkeyRestore,
                                        wszREGLOGPATH,
                                        pwszLogPath);
            _JumpIfErrorStr(hr, error, "SetRegistryLocalPathString", 
               wszREGCHECKPOINTFILE);
        }

        if(pwszRestoreMap)
        {
            hr = RegSetValueEx(
                hkeyRestore,
                wszREGRESTOREMAP,
                0,
                REG_MULTI_SZ,
                (BYTE *) pwszRestoreMap,
                cbRestoreMap);
            _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGRESTOREMAP);
        }

        if(!DeleteFile(pwszRestoreFile))
        {
            _PrintError(myHLastError(), "DeleteFile restore file");
        }
    }
    else
    {
        hr = myHLastError();
        // no restore state file OK
        if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
            hr = S_OK;
        _JumpIfErrorStr(hr, error, "GetFileAttributes", pwszRestoreFile);
    }

error:
    
    LOCAL_FREE(pwszRestoreFile);
    LOCAL_FREE(pwszRestoreMap);
    LOCAL_FREE(pwszPath);
    LOCAL_FREE(pwszBackupLogDir);
    LOCAL_FREE(pwszCheckpointFile);
    LOCAL_FREE(pwszLogPath);
    if(hkey)
    {
        RegCloseKey(hkey);
    }
    if(hkeyRestore)
    {
        RegCloseKey(hkeyRestore);
    }
    return hr;
}


//+--------------------------------------------------------------------------
// DB file storage locations:
//
//   wszREGDBDIRECTORY:
//	Your Name.EDB		from csregstr.h: wszDBFILENAMEEXT .edb
//
//   wszREGDBLOGDIRECTORY:
//	EDB.log			from csregstr.h: wszDBBASENAMEPARM edb
//	EDB00001.log		from csregstr.h: wszDBBASENAMEPARM edb
//	EDB00002.log		from csregstr.h: wszDBBASENAMEPARM edb
//	res1.log
//	res2.log
//
//   wszREGDBSYSDIRECTORY:
//	EDB.chk			from csregstr.h: wszDBBASENAMEPARM edb
//
//   wszREGDBTEMPDIRECTORY:
//	tmp.edb			fixed name
//
//
//   wszREGDBOPTIONALFLAGS:
//      wszFlags                CDBOPEN_CIRCULARLOGGING
//      
// Backed up files:
//   DB files (Attachments):
//	wszREGDBDIRECTORY:	Your Name.EDB -- CSBFT_CERTSERVER_DATABASE
//
//   Log files:
//	wszREGDBLOGDIRECTORY:	EDB00001.log -- CSBFT_LOG
//	wszREGDBLOGDIRECTORY:	EDB00002.log -- CSBFT_LOG
//	wszREGDBDIRECTORY:	Your Name.pat -- CSBFT_PATCH_FILE
//
//+--------------------------------------------------------------------------


///// initialize database access

#define DBSESSIONCOUNTMIN	4
#define DBSESSIONCOUNTMAX	1024

HRESULT
DBOpen(
    WCHAR const *pwszSanitizedName)
{
    HRESULT hr = S_OK;
    DWORD cb;
    DWORD i;
    DWORD dwState;
    DWORD SessionCount;
    HKEY hkey = NULL;
    WCHAR wszTempDir[MAX_PATH];
    DWORD dwOptionalFlags;
    BOOL fRestarted;

    REGDBDIR adbdir[] =
    {
	{ wszREGDBDIRECTORY,        TRUE,  g_wszDatabase, },
	{ wszREGDBLOGDIRECTORY,     TRUE,  g_wszLogDir, },
	{ wszREGDBSYSDIRECTORY,     TRUE,  g_wszSystemDir, },
	{ wszREGDBTEMPDIRECTORY,    TRUE,  wszTempDir, },
    };

    // check machine setup status

    hr = GetSetupStatus(NULL, &dwState);
    _JumpIfError(hr, error, "GetSetupStatus");

    hr = RegOpenKey(HKEY_LOCAL_MACHINE, g_wszRegKeyConfigPath, &hkey);
    _JumpIfError(hr, error, "RegOpenKey(CAName)");

    // get info from registry

    for (i = 0; i < ARRAYSIZE(adbdir); i++)
    {
	cb = sizeof(WCHAR) * MAX_PATH;
	hr = RegQueryValueEx(
			hkey,
			adbdir[i].pwszRegName,
			NULL,
			NULL,
			(BYTE *) adbdir[i].pwszBuf,
			&cb);
	if ((HRESULT) ERROR_FILE_NOT_FOUND == hr && !adbdir[i].fMustExist)
	{
	    adbdir[i].pwszBuf[0] = L'\0';
	    hr = S_OK;
	}
	_JumpIfError(hr, error, "RegQueryValueEx(DB*Dir)");
    }
    wcscat(g_wszDatabase, L"\\");
    wcscat(g_wszDatabase, pwszSanitizedName);
    wcscat(g_wszDatabase, wszDBFILENAMEEXT);

    cb = sizeof(SessionCount);
    hr = RegQueryValueEx(
		    hkey,
		    wszREGDBSESSIONCOUNT,
		    NULL,
		    NULL,
		    (BYTE *) &SessionCount,
		    &cb);
    if (S_OK != hr)
    {
	_PrintErrorStr(hr, "RegQueryValueEx", wszREGDBSESSIONCOUNT);
	SessionCount = DBSESSIONCOUNTDEFAULT;
    }
    if (DBSESSIONCOUNTMIN > SessionCount)
    {
	SessionCount = DBSESSIONCOUNTMIN;
    }
    if (DBSESSIONCOUNTMAX < SessionCount)
    {
	SessionCount = DBSESSIONCOUNTMAX;
    }

    cb = sizeof(dwOptionalFlags);
    hr = RegQueryValueEx(
		    hkey,
		    wszREGDBOPTIONALFLAGS,
		    NULL,
		    NULL,
		    (BYTE *) &dwOptionalFlags,
		    &cb);
    if (S_OK != hr)
    {
	//_PrintErrorStr(hr, "RegQueryValueEx", wszREGDBOPTIONALFLAGS);
	dwOptionalFlags = 0;
    }


    hr = dbCheckRecoveryState(
			hkey,
			2,			// cSession
			g_wszCertSrvDotExe,	// pwszEventSource
			g_wszLogDir,		// pwszLogDir
			g_wszSystemDir,		// pwszSystemDir
			wszTempDir);		// pwszTempDir
    _JumpIfError(hr, error, "dbCheckRecoveryState");


    CONSOLEPRINT1((DBG_SS_CERTSRV, "Opening Database %ws\n", g_wszDatabase));

    __try
    {
	DWORD dwFlags = 0;

	hr = CoCreateInstance(
			   CLSID_CCertDB,
			   NULL,               // pUnkOuter
			   CLSCTX_INPROC_SERVER,
			   IID_ICertDB,
			   (VOID **) &g_pCertDB);
	_LeaveIfError(hr, "CoCreateInstance(ICertDB)");

	if (g_fCreateDB || (SETUP_CREATEDB_FLAG & dwState))
	{
	    dwFlags |= CDBOPEN_CREATEIFNEEDED;
	}
	if (dwOptionalFlags & CDBOPEN_CIRCULARLOGGING)
	{
	    dwFlags |= CDBOPEN_CIRCULARLOGGING;
	}

	//only perform Hash if the auditing is enabled

	if (AUDIT_FILTER_STARTSTOP & g_dwAuditFilter)
	{
	    hr = ComputeMAC(g_wszDatabase, &g_pwszDBFileHash);
	
	    // db file does not exist when starting the CA first time

	    if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
	    {
		_PrintErrorStr(hr, "Database file not found, can't calculate hash", g_wszDatabase);
		hr = S_OK;
	    }
	    _LeaveIfErrorStr(hr, "ComputeMAC", g_wszDatabase);
	}

	// S_FALSE means a DB schema change was made that requires a restart
	// to take effect.  Open the DB a second time if S_FALSE is returned.

	fRestarted = FALSE;
	while (TRUE)
	{
	    hr = g_pCertDB->Open(
			    dwFlags,		// Flags
			    SessionCount,	// cSession
			    g_wszCertSrvDotExe,	// pwszEventSource
			    g_wszDatabase,	// pwszDBFile
			    g_wszLogDir,	// pwszLogDir
			    g_wszSystemDir,	// pwszSystemDir
			    wszTempDir);	// pwszTempDir
	    if (S_OK == hr)
	    {
		break;
	    }
	    if (S_FALSE == hr && fRestarted)
	    {
		_PrintError(hr, "Open");
		break;
	    }
	    if (S_FALSE != hr)
	    {
		_LeaveError(hr, "Open");
	    }
	    hr = g_pCertDB->ShutDown(0);
            _PrintIfError(hr, "DB ShutDown");

	    fRestarted = TRUE;
	}
	if (SETUP_CREATEDB_FLAG & dwState)
	{
	    hr = SetSetupStatus(NULL, SETUP_CREATEDB_FLAG, FALSE);
	    _LeaveIfError(hr, "SetSetupStatus");
	}
	hr = S_OK;
	CONSOLEPRINT0((DBG_SS_CERTSRV, "Database open\n"));
    }
    __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
    {
    }

error:
    if (S_OK != hr)
    {
        if (NULL != g_pCertDB)
        {
            g_pCertDB->Release();
            g_pCertDB = NULL;
        }
    }
    if (NULL != hkey)
    {
        RegCloseKey(hkey);
    }
    return(hr);
}


HRESULT
DBShutDown(
    IN BOOL fPendingNotify)
{
    HRESULT hr = S_OK;

    if (NULL != g_pCertDB)
    {
	hr = g_pCertDB->ShutDown(fPendingNotify? CDBSHUTDOWN_PENDING : 0);
	if (!fPendingNotify)
	{
	    g_pCertDB->Release();
	    g_pCertDB = NULL;
	}
    }
    return(hr);
}


HRESULT
dbRecoverAfterRestore(
    IN DWORD cSession,
    IN WCHAR const *pwszEventSource,
    IN WCHAR const *pwszLogDir,
    IN WCHAR const *pwszSystemDir,
    IN WCHAR const *pwszTempDir,
    IN WCHAR const *pwszCheckPointFile,
    IN WCHAR const *pwszLogPath,
    IN CSEDB_RSTMAPW rgrstmap[],
    IN LONG crstmap,
    IN WCHAR const *pwszBackupLogPath,
    IN DWORD genLow,
    IN DWORD genHigh)
{
    HRESULT hr;
    ICertDBRestore *pCertDBRestore = NULL;

    __try
    {
	WCHAR *apwsz[2];

	hr = CoCreateInstance(
			   CLSID_CCertDBRestore,
			   NULL,               // pUnkOuter
			   CLSCTX_INPROC_SERVER,
			   IID_ICertDBRestore,
			   (VOID **) &pCertDBRestore);
	_LeaveIfError(hr, "CoCreateInstance(ICertDBRestore)");

	hr = pCertDBRestore->RecoverAfterRestore(
					    cSession,
					    pwszEventSource,
					    pwszLogDir,
					    pwszSystemDir,
					    pwszTempDir,
					    pwszCheckPointFile,
					    pwszLogPath,
					    rgrstmap,
					    crstmap,
					    pwszBackupLogPath,
					    genLow,
					    genHigh);
	_LeaveIfError(hr, "RecoverAfterRestore");

	apwsz[0] = wszREGDBLASTFULLBACKUP;
	apwsz[1] = wszREGDBLASTINCREMENTALBACKUP;
	hr = CertSrvSetRegistryFileTimeValue(
					TRUE,
					wszREGDBLASTRECOVERY,
					ARRAYSIZE(apwsz),
					apwsz);
	_PrintIfError(hr, "CertSrvSetRegistryFileTimeValue");
	hr = S_OK;
    }
    __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
    {
    }
    if (NULL != pCertDBRestore)
    {
	pCertDBRestore->Release();
    }
    return(hr);
}


HRESULT
dbPerformRecovery(
    IN DWORD cSession,
    IN WCHAR const *pwszEventSource,
    IN WCHAR const *pwszLogDir,
    IN WCHAR const *pwszSystemDir,
    IN WCHAR const *pwszTempDir,
    IN WCHAR const *pwszCheckPointFile,
    IN WCHAR const *pwszLogPath,
    IN CSEDB_RSTMAPW rgrstmap[],
    IN LONG crstmap,
    IN WCHAR const *pwszBackupLogPath,
    IN unsigned long genLow,
    IN unsigned long genHigh,
    IN OUT BOOLEAN *pfRecoverJetDatabase)
{
    HRESULT hr = S_OK;

    // Call into JET to let it munge the databases.
    // Note that the JET interpretation of LogPath and BackupLogPath is
    // totally wierd, and we want to pass in LogPath to both parameters.

    if (!*pfRecoverJetDatabase)
    {
	hr = dbRecoverAfterRestore(
				cSession,
				pwszEventSource,
				pwszLogDir,
				pwszSystemDir,
				pwszTempDir,
				pwszCheckPointFile,
				pwszLogPath,
				rgrstmap,
				crstmap,
				pwszBackupLogPath,
				genLow,
				genHigh);
	_JumpIfError(hr, error, "dbRecoverAfterRestore");
    }

    // Ok, we were able to recover the database.  Let the other side of the
    // API know about it so it can do something "reasonable".

    *pfRecoverJetDatabase = TRUE;

    // Mark the DB as a restored version - Add any external notification here

error:
    return(hr);
}


//+--------------------------------------------------------------------------
// dbCheckRecoveryState -- recover a database after a restore if necessary.
//
// Parameters:
// pwszParametersRoot - the root of the parameters section for the service in
//     the registry.
//
// Returns: HRESULT - S_OK if successful; error code if not.
//
// The NTBACKUP program will place a key at the location:
// $(pwszParametersRoot)\Restore in Progress
//
// This key contains the following values:
// BackupLogPath - The full path for the logs after a backup
// CheckPointFilePath - The full path for the path that contains the checkpoint
// *HighLogNumber - The maximum log file number found.
// *LowLogNumber - The minimum log file number found.
// LogPath - The current path for the logs.
// JET_RstMap - Restore map for database - this is a REG_MULTISZ, where odd
//    entries go into the pwszDatabase field, and the even entries go into the
//    pwszNewDatabase field of a JET_RstMap
// *JET_RstMap Size - The number of entries in the restoremap.
//
// * - These entries are REG_DWORD's.  All others are REG_SZ's (except where
//     mentioned).
//---------------------------------------------------------------------------

HRESULT
dbCheckRecoveryState(
    IN HKEY hkeyConfig,
    IN DWORD cSession,
    IN WCHAR const *pwszEventSource,
    IN WCHAR const *pwszLogDir,
    IN WCHAR const *pwszSystemDir,
    IN WCHAR const *pwszTempDir)
{
    HRESULT hr;
    HKEY hkeyRestore = NULL;
    DWORD cb;
    WCHAR wszCheckPointFilePath[MAX_PATH];
    WCHAR wszBackupLogPath[MAX_PATH];
    WCHAR wszLogPath[MAX_PATH];
    WCHAR *pwszCheckPointFilePath;
    WCHAR *pwszBackupLogPath;
    WCHAR *pwszLogPath;
    WCHAR *pwszRestoreMap = NULL;
    CSEDB_RSTMAPW *pRstMap = NULL;
    LONG cRstMap;
    LONG i;
    DWORD genLow;
    DWORD genHigh;
    WCHAR *pwsz;
    DWORD dwType;
    HRESULT hrRestoreError;
    BOOLEAN fDatabaseRecovered = FALSE;
    WCHAR wszActiveLogPath[MAX_PATH];

    hr = dbRestoreRecoveryStateFromFile(pwszLogDir);
    _JumpIfError(hr, error, "dbRestoreRecoveryStateFromFile");

    
    hr = RegOpenKey(HKEY_LOCAL_MACHINE, wszREGKEYCONFIGRESTORE, &hkeyRestore);
    if (S_OK != hr)
    {
	// We want to ignore file_not_found - it is ok.

	if (hr == ERROR_FILE_NOT_FOUND)
	{
	    hr = S_OK;
	}
	_PrintIfError(hr, "RegOpenKey");
	goto error;
    }

    CONSOLEPRINT0((DBG_SS_CERTSRV, "Started Database Recovery\n"));

    // If there's a restore in progress, then fail to perform any other
    // restore operations.

    dwType = REG_DWORD;
    cb = sizeof(DWORD);
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGRESTORESTATUS,
		    0,
		    &dwType,
		    (BYTE *) &hrRestoreError,
		    &cb);
    if (S_OK == hr)
    {
	hr = hrRestoreError;
	_JumpError(hr, error, "hrRestoreError");
    }

    cb = sizeof(wszActiveLogPath);
    hr = RegQueryValueEx(
		    hkeyConfig,
		    wszREGDBLOGDIRECTORY,
		    NULL,
		    NULL,
		    (BYTE *) wszActiveLogPath,
		    &cb);
    _JumpIfErrorStr(hr, error, "RegQueryValueEx", wszREGDBLOGDIRECTORY);

    // We have now opened the restore-in-progress key.  This means that we have
    // something to do now.  Find out what it is.  First, let's get the backup
    // log file path.

    dwType = REG_SZ;

    cb = sizeof(wszBackupLogPath);
    pwszBackupLogPath = wszBackupLogPath;
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGBACKUPLOGDIRECTORY,
		    0,
		    &dwType,
		    (BYTE *) wszBackupLogPath,
		    &cb);
    if (S_OK != hr)
    {
	if (hr != ERROR_FILE_NOT_FOUND)
	{
	    _JumpError(hr, error, "RegQueryValueEx");
	}
	pwszBackupLogPath = NULL;
    }

    // Then, the checkpoint file path.

    cb = sizeof(wszCheckPointFilePath);
    pwszCheckPointFilePath = wszCheckPointFilePath;
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGCHECKPOINTFILE,
		    0,
		    &dwType,
		    (BYTE *) wszCheckPointFilePath,
		    &cb);
    if (S_OK != hr)
    {
	if (hr != ERROR_FILE_NOT_FOUND)
	{
	    _JumpError(hr, error, "RegQueryValueEx");
	}
	pwszCheckPointFilePath = NULL;
    }

    // Then, the Log path.

    cb = sizeof(wszLogPath);
    pwszLogPath = wszLogPath;
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGLOGPATH,
		    0,
		    &dwType,
		    (BYTE *) wszLogPath,
		    &cb);
    if (S_OK != hr)
    {
	if ((HRESULT) ERROR_FILE_NOT_FOUND != hr)
	{
	    _JumpError(hr, error, "RegQueryValueEx");
	}
	pwszLogPath = NULL;
    }

    // Then, the low log number.

    dwType = REG_DWORD;
    cb = sizeof(genLow);
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGLOWLOGNUMBER,
		    0,
		    &dwType,
		    (BYTE *) &genLow,
		    &cb);
    _JumpIfError(hr, error, "RegQueryValueEx");

    // And, the high log number.

    cb = sizeof(genHigh);
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGHIGHLOGNUMBER,
		    0,
		    &dwType,
		    (BYTE *) &genHigh,
		    &cb);
    _JumpIfError(hr, error, "RegQueryValueEx");

    // Now determine if we had previously recovered the database.

    dwType = REG_BINARY;
    cb = sizeof(fDatabaseRecovered);

    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGDATABASERECOVERED,
		    0,
		    &dwType,
		    &fDatabaseRecovered,
		    &cb);
    if (S_OK != hr && (HRESULT) ERROR_FILE_NOT_FOUND != hr)
    {
	// If there was an error other than "value doesn't exist", bail.

	_JumpError(hr, error, "RegQueryValueEx");
    }

    // Now the tricky one.  We want to get the restore map.
    // First we figure out how big it is.

    dwType = REG_DWORD;
    cb = sizeof(cRstMap);
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGRESTOREMAPCOUNT,
		    0,
		    &dwType,
		    (BYTE *) &cRstMap,
		    &cb);
    _JumpIfError(hr, error, "RegQueryValueEx");

    pRstMap = (CSEDB_RSTMAPW *) LocalAlloc(
					LMEM_FIXED,
					sizeof(CSEDB_RSTMAPW) * cRstMap);
    if (NULL == pRstMap)
    {
	hr = E_OUTOFMEMORY;
	_JumpError(hr, error, "LocalAlloc");
    }

    // First find out how much memory is needed to hold the restore map.

    dwType = REG_MULTI_SZ;
    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGRESTOREMAP,
		    0,
		    &dwType,
		    NULL,
		    &cb);
    if (S_OK != hr && (HRESULT) ERROR_MORE_DATA != hr)
    {
	_JumpError(hr, error, "RegQueryValueEx");
    }

    pwszRestoreMap = (WCHAR *) LocalAlloc(LMEM_FIXED, cb + 2 * sizeof(WCHAR));
    if (NULL == pwszRestoreMap)
    {
	hr = E_OUTOFMEMORY;
	_JumpError(hr, error, "LocalAlloc");
    }

    hr = RegQueryValueEx(
		    hkeyRestore,
		    wszREGRESTOREMAP,
		    0,
		    &dwType,
		    (BYTE *) pwszRestoreMap,
		    &cb);
    _JumpIfError(hr, error, "RegQueryValueEx");
    
    pwszRestoreMap[cb / sizeof(WCHAR)] = L'\0';
    pwszRestoreMap[cb / sizeof(WCHAR) + 1] = L'\0';

    pwsz = pwszRestoreMap;
    for (i = 0; i < cRstMap; i++)
    {
	if (L'\0' == *pwsz)
	{
	    break;
	}
	pRstMap[i].pwszDatabaseName = pwsz;
	pwsz += wcslen(pwsz) + 1;

	if (L'\0' == *pwsz)
	{
	    break;
	}
	pRstMap[i].pwszNewDatabaseName = pwsz;
	pwsz += wcslen(pwsz) + 1;
    }
    if (i < cRstMap || L'\0' != *pwsz)
    {
	hr = E_INVALIDARG;
	_JumpError(hr, error, "Restore Map");
    }

    {
        CertSrv::CAuditEvent event(SE_AUDITID_CERTSRV_RESTORESTART, g_dwAuditFilter);
        hr = event.Report();
        _JumpIfError(hr, error, "CAuditEvent::Report");
    }

    hr = dbPerformRecovery(
		cSession,
		pwszEventSource,
		pwszLogDir,
		pwszSystemDir,
		pwszTempDir,
		pwszCheckPointFilePath,
		NULL != pwszLogPath? pwszLogPath : wszActiveLogPath,
		pRstMap,
		cRstMap,
		NULL != pwszBackupLogPath? pwszBackupLogPath : wszActiveLogPath,
		genLow,
		genHigh,
		&fDatabaseRecovered);
    if (S_OK != hr)
    {
	// The recovery failed.  If recovering the database succeeded, flag it
	// in the registry so we don't try again.  Ignore RegSetValueEx errors,
	// because the recovery error is more important.

	RegSetValueEx(
		    hkeyRestore,
		    wszREGDATABASERECOVERED,
		    0,
		    REG_BINARY,
		    (BYTE *) &fDatabaseRecovered,
		    sizeof(fDatabaseRecovered));
	_JumpError(hr, error, "dbPerformRecovery");
    }

    {
        CertSrv::CAuditEvent event(SE_AUDITID_CERTSRV_RESTOREEND, g_dwAuditFilter);
        hr = event.Report();
        _JumpIfError(hr, error, "CAuditEvent::Report");
    }

    CONSOLEPRINT0((DBG_SS_CERTSRV, "Completed Database Recovery\n"));

    g_fDBRecovered = TRUE;

    // Ok, we're all done.  We can now delete the key, since we're done
    // with it.

    RegCloseKey(hkeyRestore);
    hkeyRestore = NULL;

    hr = RegDeleteKey(HKEY_LOCAL_MACHINE, wszREGKEYCONFIGRESTORE);
    _JumpIfError(hr, error, "RegDeleteKey");

error:
    if (NULL != pwszRestoreMap)
    {
	LocalFree(pwszRestoreMap);
    }
    if (NULL != pRstMap)
    {
	LocalFree(pRstMap);
    }
    if (NULL != hkeyRestore)
    {
        RegCloseKey(hkeyRestore);
    }
    return(hr);
}