#include "priv.h" #include #include "uacount.h" #include "regdb.h" #include "uemapp.h" #include "uareg.h" #define DM_UEMTRACE TF_UEM #define DM_PERF 0 // perf tune #define DB_NOLOG FALSE #define SZ_CTLSESSION TEXT("UEME_CTLSESSION") #define SZ_CUACount_ctor TEXT("UEME_CTLCUACount:ctor") #define SZ_DEL_PREFIX TEXT("del.") #define SZ_RUN_PREFIX TEXT("UEME_RUN") //*** Reg_CreateOpenKey -- merged RegCreate/RegOpen // HKEY Reg_CreateOpenKey(HKEY hkey, LPCTSTR pszSubKey, DWORD grfMode) { HKEY hkeyNew = 0; long i; CHAR szSubKey[MAXIMUM_SUB_KEY_LENGTH]; SHTCharToAnsi(pszSubKey, szSubKey, ARRAYSIZE(szSubKey)); ASSERT(grfMode == STGM_READ || grfMode == STGM_WRITE); ASSERT(pszSubKey != NULL); if (pszSubKey != NULL) { if (grfMode == STGM_READ) { i = RegOpenKeyA(hkey, szSubKey, &hkeyNew); } else { i = RegCreateKeyA(hkey, szSubKey, &hkeyNew); } // the following ASSERT is not req'd but is currently always true. // if you hit it, the bug is someplace in the caller. // i.e. do *not* remove this ASSERT to 'fix' your pblm. // a typical cause of failure is that CoCreateInst is failing because // something isn't properly regsvr32'ed. ASSERT(i == ERROR_SUCCESS || hkeyNew == 0); } return hkeyNew; } //*** // DESCRIPTION // inc this any time you change the format of *anything* below {guid} // doing so will cause us to nuke the {guid} subtree and start fresh #define UA_VERSION 3 #if 0 char c_szDotDot[] = TEXT(".."); // RegStrFS does *not* support #endif // kind of hoaky to do INITGUID, but we want the GUID private to this file #define INITGUID #include // {C28EB156-523C-11d2-A561-00A0C92DBFE8} DEFINE_GUID(CLSID_GCTaskTOID, 0xc28eb156, 0x523c, 0x11d2, 0xa5, 0x61, 0x0, 0xa0, 0xc9, 0x2d, 0xbf, 0xe8); #undef INITGUID class CGCTask : public CRunnableTask { public: //*** IUnknown // (... from CRunnableTask) //*** THISCLASS HRESULT Initialize(CEMDBLog *that); virtual STDMETHODIMP RunInitRT(); protected: CGCTask(); virtual ~CGCTask(); friend CGCTask *CGCTask_Create(CEMDBLog *that); CEMDBLog *_that; }; // { //*** CEMDBLog -- //CRITICAL_SECTION g_csDbSvr /*=0*/ ; CEMDBLog *g_uempDbSvr[UEMIND_NSTANDARD + UEMIND_NINSTR]; // 0=shell 1=browser //*** g_fDidUAGC -- breadcrumbs in case we die (even non-DEBUG) // keep minimal state in case we deadlock or die or whatever // 0:not 1:pre-task 2:pre-GC 3:post-GC int g_fDidUAGC; FNNRW3 CEMDBLog::s_Nrw3Info = { CEMDBLog::s_Read, CEMDBLog::s_Write, CEMDBLog::s_Delete, }; //*** helpers { //*** // NOTES // or, could use Reg_CreateOpenKey(..., STGM_READ) if we removed the asserts STDAPI_(DWORD) SHRegOpenKey(HKEY hk, LPCTSTR ptszSubKey, PHKEY phkOut) { long i; CHAR szSubKey[MAXIMUM_SUB_KEY_LENGTH]; CHAR *psz; if (ptszSubKey) { SHTCharToAnsi(ptszSubKey, szSubKey, ARRAYSIZE(szSubKey)); psz = szSubKey; } else { psz = NULL; } i = RegOpenKeyA(hk, psz, phkOut); return (DWORD)i; } STDAPI_(DWORD) SHRegOpenKeyEx(HKEY hk, LPCTSTR ptszSubKey, DWORD dwReserved, REGSAM samDesired, PHKEY phkOut) { long i; CHAR szSubKey[MAXIMUM_SUB_KEY_LENGTH]; CHAR *psz; if (ptszSubKey) { SHTCharToAnsi(ptszSubKey, szSubKey, ARRAYSIZE(szSubKey)); psz = szSubKey; } else { psz = NULL; } i = RegOpenKeyExA(hk, psz, dwReserved, samDesired, phkOut); return (DWORD)i; } #define E_NUKE (E_FAIL + 1) //*** RegGetVersion -- check registry tree 'Version' // ENTRY/EXIT // (see RegChkVersion) // hr (ret) S_OK:ok S_FALSE:no tree E_NUKE:old E_FAIL:new HRESULT RegGetVersion(HKEY hk, LPTSTR pszSubkey, LPTSTR pszValue, DWORD dwVers) { HRESULT hr; HKEY hk2; DWORD dwData; DWORD cbSize; if (!pszValue) pszValue = TEXT("Version"); hr = S_FALSE; // assume nothing there at all if (SHRegOpenKey(hk, pszSubkey, &hk2) == ERROR_SUCCESS) { hr = E_NUKE; // assume version mismatch cbSize = SIZEOF(dwData); if (SHGetValue(hk2, NULL, pszValue, NULL, (BYTE*)&dwData, &cbSize) == ERROR_SUCCESS) { if (dwData == dwVers) hr = S_OK; // great! else if (dwData > dwVers) hr = E_FAIL; // we're an old client, fail else ASSERT(hr == E_NUKE); // we're a new client, nuke it } RegCloseKey(hk2); } return hr; } //*** RegChkVersion -- check registry tree 'version', nuke if outdated // ENTRY/EXIT // hk e.g. hkey for "HKCU/.../Uassist" // pszSubkey e.g. "{clsid}" // pszValue e.g. "Version" // dwVers e.g. 3 // hr (ret) S_OK:matched, S_FAIL:mismatched and del'ed, E_FAIL:o.w. // (other) (SE) pszSubkey deleted if not matched HRESULT RegChkVersion(HKEY hk, LPTSTR pszSubkey, LPTSTR pszValue, DWORD dwVers) { HRESULT hr; DWORD i; // RegGetVersion() S_OK:ok S_FALSE:new E_NUKE:old E_FAIL:fail hr = RegGetVersion(hk, pszSubkey, pszValue, dwVers); // at this point, we have: // S_OK: ok // S_FALSE: entire tree missing // E_NUKE: no "Version" or old version (nuke it) // E_FAIL: new version (we can't handle it) if (hr == E_FAIL) { TraceMsg(DM_UEMTRACE, "bui.rcv: incompat (uplevel)"); } if (hr == E_NUKE) { TraceMsg(DM_UEMTRACE, "bui.rcv: bad tree, try delete"); hr = S_FALSE; // assume nuked i = SHDeleteKey(hk, pszSubkey); if (i != ERROR_SUCCESS) { TraceMsg(DM_UEMTRACE, "bui.rcv: delete failed!"); hr = E_FAIL; // bogus tree left laying around } } TraceMsg(DM_UEMTRACE, "bui.rcv: hr=0x%x", hr); return hr; } //*** GetUEMLogger -- get the (shared) instance of our logger object // NOTES // BY DESIGN: we leak g_uempDbSvr. // race condition on g_uempDbSvr. our caller guards against this. // the 5 billion ASSERTs below were for diagnosing nt5:145449 (fixed). HRESULT GetUEMLogger(int iSvr, CEMDBLog **p) { HRESULT hr, hrVers; CEMDBLog *pDbSvr; DWORD dwData, cbSize; ASSERT(iSvr < ARRAYSIZE(g_uempDbSvr)); pDbSvr = g_uempDbSvr[iSvr]; if (pDbSvr) { pDbSvr->AddRef(); *p = pDbSvr; return S_OK; } pDbSvr = CEMDBLog_Create(); if (EVAL(pDbSvr)) { TCHAR szClass[GUIDSTR_MAX]; // "{clsid}" SHStringFromGUID(IND_NONINSTR(iSvr) ? UEMIID_BROWSER : UEMIID_SHELL, szClass, GUIDSTR_MAX); TraceMsg(DM_UEMTRACE, "bui.gul: UEMIID_%s=%s", IND_NONINSTR(iSvr) ? TEXT("BROWSER") : TEXT("SHELL"), szClass); hr = pDbSvr->ChDir(!IND_ISINSTR(iSvr) ? SZ_UASSIST : SZ_UASSIST2); if (SUCCEEDED(hr)) { hrVers = RegChkVersion(pDbSvr->GetHkey(), szClass, SZ_UAVERSION, UA_VERSION); if (FAILED(hrVers)) { TraceMsg(DM_UEMTRACE, "bui.gul: rcv()=0x%x (!)", hrVers); hr = hrVers; } } if (SUCCEEDED(hr)) { hr = pDbSvr->ChDir(szClass); ASSERT(hrVers == S_OK || hrVers == S_FALSE); if (SUCCEEDED(hr) && hrVers == S_FALSE) { dwData = UA_VERSION; cbSize = SIZEOF(dwData); hr = pDbSvr->SetValue(SZ_UAVERSION, REG_DWORD, (BYTE*)&dwData, cbSize); } } if (SUCCEEDED(hr)) hr = pDbSvr->ChDir(SZ_COUNT); // n.b. we can't call pDbSvr->GarbageCollect here since flags // (e.g. _fNoDecay) not set yet // pDbSvr->GarbageCollect(FALSE); if (FAILED(hr)) { // this fails during RunOnce pDbSvr->Release(); pDbSvr = NULL; } } if (pDbSvr) { ENTERCRITICAL; if (g_uempDbSvr[iSvr] == 0) { g_uempDbSvr[iSvr] = pDbSvr; // xfer refcnt pDbSvr = NULL; } LEAVECRITICAL; if (pDbSvr) pDbSvr->Release(); } *p = g_uempDbSvr[iSvr]; return *p ? S_OK : E_FAIL; } CEMDBLog::CEMDBLog() : _cRef(1) { ASSERT(_fBackup == FALSE); ASSERT(_fNoEncrypt == FALSE); return; } CEMDBLog::~CEMDBLog() { #if XXX_CACHE int i; for (i = 0; i < ARRAYSIZE(_rgCache); i++) { if (_rgCache[i].pv) { LocalFree(_rgCache[i].pv); _rgCache[i].pv = NULL; _rgCache[i].cbSize = 0; } } #endif SetRoot(0, STGM_READ); // close ASSERT(!_hkey); return; } void CEMDBLog_CleanUp() { int i; CEMDBLog *pDbSvr; TraceMsg(DM_UEMTRACE, "bui.uadb_cu: cleaning up"); for (i = 0; i < UEMIND_NSTANDARD + UEMIND_NINSTR; i++) { if ((pDbSvr = (CEMDBLog *)InterlockedExchangePointer((void**) &g_uempDbSvr[i], (LPVOID) -1))) delete pDbSvr; } return; } HRESULT CEMDBLog::Initialize(HKEY hkey, DWORD grfMode) { HRESULT hr; hr = SetRoot(hkey, grfMode); return hr; } //*** // hkey e.g. HKLM // pszSubKey e.g. "...\\Explorer\\Instance\\{...}" // grfMode subset of STGM_* values HRESULT CEMDBLog::SetRoot(HKEY hkeyNew, DWORD grfMode) { ASSERT(grfMode == STGM_READ || grfMode == STGM_WRITE); if (_hkey) { RegCloseKey(_hkey); _grfMode = 0; _hkey = 0; } if (hkeyNew) { _grfMode = grfMode; _hkey = SHRegDuplicateHKey(hkeyNew); // xfer ownership (and up khey refcnt) if (_hkey == NULL) return E_FAIL; } return S_OK; } HRESULT CEMDBLog::ChDir(LPCTSTR pszSubKey) { HRESULT hr = E_FAIL; HKEY hkeyNew; ASSERT(_hkey); ASSERT(_grfMode == STGM_READ || _grfMode == STGM_WRITE); hkeyNew = Reg_CreateOpenKey(_hkey, pszSubKey, _grfMode); if (hkeyNew) { RegCloseKey(_hkey); _hkey = hkeyNew; hr = S_OK; } return hr; } HRESULT CEMDBLog::QueryValueStr(LPCTSTR pszName, LPTSTR pszVal, LPDWORD pcbVal) { long i; CHAR szSubKey[MAXIMUM_SUB_KEY_LENGTH]; CHAR szStrData[MAX_URL_STRING]; DWORD cbSize = sizeof(szStrData); SHTCharToAnsi(pszName, szSubKey, ARRAYSIZE(szSubKey)); i = SHQueryValueExA(_hkey, szSubKey, NULL, NULL, (BYTE*)szStrData, &cbSize); ASSERT((int)*pcbVal > lstrlenA(szStrData)); // I better be using a big enough buffer. SHAnsiToTChar(szStrData, pszVal, *pcbVal / sizeof(TCHAR)); if (pcbVal) *pcbVal = lstrlen(pszVal); return (i == ERROR_SUCCESS) ? S_OK : E_FAIL; } HRESULT CEMDBLog::SetValueStr(LPCTSTR pszName, LPCTSTR pszVal) { return SetValueStrEx(pszName, REG_SZ, pszVal); } HRESULT CEMDBLog::SetValueStrEx(LPCTSTR pszName, DWORD dwType, LPCTSTR pszVal) { long i; int cbTmp; CHAR szSubKey[MAXIMUM_SUB_KEY_LENGTH]; CHAR szStrData[MAX_URL_STRING]; SHTCharToAnsi(pszName, szSubKey, ARRAYSIZE(szSubKey)); SHTCharToAnsi(pszVal, szStrData, ARRAYSIZE(szStrData)); ASSERT(_grfMode == STGM_WRITE); cbTmp = (lstrlenA(szStrData) + 1); i = RegSetValueExA(_hkey, szSubKey, NULL, dwType, (BYTE*)szStrData, cbTmp); return (i == ERROR_SUCCESS) ? S_OK : E_FAIL; } // } // } // { //*** CEMDBLog -- file-system-like view of registry // DESCRIPTION // basically keeps track of where we are and does 'relative' opens from // there. NYI: intent is to eventually support 'chdir' ops. // NOTES // CEMDBLog *CEMDBLog_Create() { HKEY hk = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, NULL, TRUE); if (hk) { CEMDBLog *prsfs = new CEMDBLog; if (prsfs && FAILED(prsfs->Initialize(hk, STGM_WRITE))) { prsfs->Release(); prsfs = NULL; } RegCloseKey(hk); return prsfs; } return NULL; } //*** IsREG_XX_SZ -- see if ansi/unicode is an issue // #define IsREG_XX_SZ(dwTyp) \ ((dwTyp) == REG_SZ || (dwTyp) == REG_MULTI_SZ || (dwTyp) == REG_EXPAND_SZ) HRESULT CEMDBLog::QueryValue(LPCTSTR pszName, BYTE *pbData, LPDWORD pcbData) { long i; DWORD dwType; CHAR szaTmp[MAX_URL_STRING]; // we need to thunk since no RegXxxValueW on win95 SHTCharToAnsi(pszName, szaTmp, ARRAYSIZE(szaTmp)); i = SHQueryValueExA(_hkey, szaTmp, NULL, &dwType, pbData, pcbData); ASSERT(i != ERROR_SUCCESS || !IsREG_XX_SZ(dwType)); return (i == ERROR_SUCCESS) ? S_OK : E_FAIL; } HRESULT CEMDBLog::SetValue(LPCTSTR pszName, DWORD dwType, const BYTE *pbData, DWORD cbData) { long i; CHAR szaTmp[MAX_URL_STRING]; ASSERT(_grfMode == STGM_WRITE); // we need to thunk since no RegXxxValueW on win95 SHTCharToAnsi(pszName, szaTmp, ARRAYSIZE(szaTmp)); ASSERT(!IsREG_XX_SZ(dwType)); i = RegSetValueExA(_hkey, szaTmp, NULL, dwType, pbData, cbData); return (i == ERROR_SUCCESS) ? S_OK : E_FAIL; } HRESULT CEMDBLog::DeleteValue(LPCTSTR pszName) { long i; ASSERT(_grfMode == STGM_WRITE); i = SHDeleteValue(_hkey, NULL, pszName); return (i == ERROR_SUCCESS) ? S_OK : E_FAIL; } HRESULT CEMDBLog::RmDir(LPCTSTR pszName, BOOL fRecurse) { HRESULT hr = E_FAIL; DWORD i; CHAR szaTmp[MAX_URL_STRING]; ASSERT(fRecurse); // others NYI ASSERT(_grfMode == STGM_WRITE); SHTCharToAnsi(pszName, szaTmp, ARRAYSIZE(szaTmp)); if (fRecurse) { i = SHDeleteKeyA(_hkey, szaTmp); } else { // not sure what to do, since we want a non-recursive delete // but we do want to handle presence of values (which shlwapi // doesn't support) //i = DeleteEmptyKey(_hkey, pszName); i = -1; } return (i == ERROR_SUCCESS) ? S_OK : E_FAIL; } //*** THIS::Count -- increment profile count for command // ENTRY/EXIT // fUpdate FALSE for the GC case (since can't update reg during RegEnum) // NOTES HRESULT CEMDBLog::GetCount(LPCTSTR pszCmd) { return _GetCountRW(pszCmd, TRUE); } // Returns the Filetime that is encoded in the Count Object. // note: we do a delayed upgrade of the binary stream in the registry. We will // use the old uem count info, but tack on the new filetime information when we increment the useage. FILETIME CEMDBLog::GetFileTime(LPCTSTR pszCmd) { NRWINFO rwi; HRESULT hres; CUACount aCnt; rwi.self = this; rwi.pszName = pszCmd; // This is a bizzar way of reading a string from the registry.... hres = aCnt.LoadFrom(&s_Nrw3Info, &rwi); return aCnt.GetFileTime(); } HRESULT CEMDBLog::_GetCountRW(LPCTSTR pszCmd, BOOL fUpdate) { HRESULT hr; CUACount aCnt; NRWINFO rwi; int i; hr = _GetCountWithDefault(pszCmd, TRUE, &aCnt); i = aCnt.GetCount(); if (fUpdate) { rwi.self = this; rwi.pszName = pszCmd; hr = aCnt.SaveTo(FALSE, &s_Nrw3Info, &rwi); } return i; } //*** // ENTRY/EXIT // hr (ret) S_OK if dead, o.w. != S_OK HRESULT CEMDBLog::IsDead(LPCTSTR pszCmd) { HRESULT hr; hr = _GetCountRW(pszCmd, FALSE); return hr; } extern DWORD g_dCleanSess; //*** // NOTES // we need to be careful not to party on guys that either aren't counts // (e.g. UEME_CTLSESSION), or are 'special' (e.g. UEME_CTLCUACOUNT), or // shouldn't be deleted (e.g. "del.xxx"). for now we take a conservative // approach and just nuke things w/ UEME_RUN* as a prefix. better might // be to use a dope vector and delete anything that's marked as 'cleanup'. HRESULT CEMDBLog::GarbageCollect(BOOL fForce) { int i; if (!fForce) { if (g_dCleanSess != 0) { i = GetSessionId(); if ((i % g_dCleanSess) != 0) { TraceMsg(DM_UEMTRACE, "uadb.gc: skip"); return S_FALSE; } } } g_fDidUAGC = 1; // breadcrumbs in case we die (even non-DEBUG) // do _GarbageCollectSlow(), in the background HRESULT hr = E_FAIL; CGCTask *pTask = CGCTask_Create(this); if (pTask) { IShellTaskScheduler *pSched; hr = CoCreateInstance(CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_IShellTaskScheduler, (void**)&pSched); if (SUCCEEDED(hr)) { hr = pSched->AddTask(pTask, CLSID_GCTaskTOID, 0L, ITSAT_DEFAULT_PRIORITY); pSched->Release(); // (o.k. even if task hasn't completed) } pTask->Release(); } return hr; } HRESULT CEMDBLog::_GarbageCollectSlow() { HKEY hk; int i; DWORD dwI, dwCch, dwType; HDSA hdsa; TCHAR *p; TCHAR szKey[MAXIMUM_SUB_KEY_LENGTH]; TraceMsg(DM_UEMTRACE, "uadb.gc: hit"); hdsa = DSA_Create(SIZEOF(szKey), 4); // max size, oh well... if (hdsa) { TCHAR szRun[SIZEOF(SZ_RUN_PREFIX)]; TCHAR *pszRun; pszRun = _MayEncrypt(SZ_RUN_PREFIX, szRun, ARRAYSIZE(szRun)); if (pszRun != szRun) lstrcpy(szRun, pszRun); ASSERT(lstrlen(szRun) == lstrlen(SZ_RUN_PREFIX)); hk = GetHkey(); for (dwI = 0; ; dwI++) { dwCch = ARRAYSIZE(szKey); if (SHEnumValue(hk, dwI, szKey, &dwCch, &dwType, NULL, NULL) != NOERROR) break; if (StrCmpN(szKey, szRun, ARRAYSIZE(szRun) - 1) == 0) { if (IsDead(szKey) == S_OK) DSA_AppendItem(hdsa, szKey); } } for (i = DSA_GetItemCount(hdsa) - 1; i > 0; i--) { p = (TCHAR *)DSA_GetItemPtr(hdsa, i); TraceMsg(DM_UEMTRACE, "uadb.gc: nuke %s", p); GetCount(p); // decay to 0 will delete } DSA_Destroy(hdsa); hdsa = NULL; } return S_OK; } HRESULT CEMDBLog::IncCount(LPCTSTR pszCmd) { HRESULT hr; NRWINFO rwi; TraceMsg(DM_UEMTRACE, "uemt: ic <%s>", pszCmd); if (DB_NOLOG) return E_FAIL; #if 0 // ChDir is currently done at create time hr = ChDir(SZ_COUNT); #endif CUACount aCnt; hr = _GetCountWithDefault(pszCmd, TRUE, &aCnt); aCnt.IncCount(); // Since we are incrementing the count, // We should update the last execute time aCnt.UpdateFileTime(); rwi.self = this; rwi.pszName = pszCmd; hr = aCnt.SaveTo(TRUE, &s_Nrw3Info, &rwi); return hr; } HRESULT CEMDBLog::SetCount(LPCTSTR pszCmd, int cCnt) { HRESULT hr; NRWINFO rwi; TraceMsg(DM_UEMTRACE, "uemt: ic <%s>", pszCmd); if (DB_NOLOG) return E_FAIL; CUACount aCnt; // fDef=FALSE so don't create if doesn't exist hr = _GetCountWithDefault(pszCmd, /*fDef=*/FALSE, &aCnt); if (SUCCEEDED(hr)) { // don't want default... aCnt.SetCount(cCnt); rwi.self = this; rwi.pszName = pszCmd; hr = aCnt.SaveTo(TRUE, &s_Nrw3Info, &rwi); } return hr; } //*** // ENTRY/EXIT // fDefault provide default if entry not found // ret S_OK: found w/o default; S_FALSE: needed default; E_xxx: error // NOTES // calling w/ fDefault=FALSE can still return S_FALSE HRESULT CEMDBLog::_GetCountWithDefault(LPCTSTR pszCmd, BOOL fDefault, CUACount *pCnt) { HRESULT hr, hrDef; NRWINFO rwi; rwi.self = this; rwi.pszName = pszCmd; hr = pCnt->LoadFrom(&s_Nrw3Info, &rwi); hrDef = S_OK; if (FAILED(hr)) { hrDef = S_FALSE; if (fDefault) { rwi.pszName = SZ_CUACount_ctor; hr = pCnt->LoadFrom(&s_Nrw3Info, &rwi); // pCnt->Initialize happens below (possibly 2x) if (FAILED(hr)) { TraceMsg(DM_UEMTRACE, "uadb._gcwd: create ctor %s", SZ_CUACount_ctor); hr = pCnt->Initialize(SAFECAST(this, IUASession *)); ASSERT(pCnt->_GetCount() == 0); pCnt->_SetMru(SID_SNOWINIT); // start clock ticking... // cnt=UAC_NEWCOUNT, age=Now int i = _fNoDecay ? 1 : UAC_NEWCOUNT; pCnt->SetCount(i); // force age ASSERT(pCnt->_GetCount() == i); hr = pCnt->SaveTo(/*fForce*/TRUE, &s_Nrw3Info, &rwi); } #if XXX_DELETE pCnt->_SetFlags(UACF_INHERITED, UACF_INHERITED); #endif } } hr = pCnt->Initialize(SAFECAST(this, IUASession *)); if (SUCCEEDED(hr)) pCnt->_SetFlags(UAXF_XMASK, _SetFlags(0, 0) & UAXF_XMASK); return SUCCEEDED(hr) ? hrDef : hr; } HRESULT CEMDBLog::SetFileTime(LPCTSTR pszCmd, const FILETIME *pft) { HRESULT hr; NRWINFO rwi; TraceMsg(DM_UEMTRACE, "uemt: sft <%s>", pszCmd); if (DB_NOLOG) return E_FAIL; CUACount aCnt; // fDef=FALSE so don't create if doesn't exist hr = _GetCountWithDefault(pszCmd, /*fDef=*/FALSE, &aCnt); if (SUCCEEDED(hr)) { // don't want default... aCnt.SetFileTime(pft); rwi.self = this; rwi.pszName = pszCmd; hr = aCnt.SaveTo(TRUE, &s_Nrw3Info, &rwi); } return hr; } #if XXX_DELETE #define BTOM(b, m) ((b) ? (m) : 0) DWORD CEMDBLog::_SetFlags(DWORD dwMask, DWORD dwFlags) { // standard guys if (dwMask & UAXF_NOPURGE) _fNoPurge = BOOLIFY(dwFlags & UAXF_NOPURGE); if (dwMask & UAXF_BACKUP) _fBackup = BOOLIFY(dwFlags & UAXF_BACKUP); if (dwMask & UAXF_NOENCRYPT) _fNoEncrypt = BOOLIFY(dwFlags & UAXF_NOENCRYPT); if (dwMask & UAXF_NODECAY) _fNoDecay = BOOLIFY(dwFlags & UAXF_NODECAY); // my guys // (none) return 0 // n.b. see continuation line(s)!!! | BTOM(_fNoPurge , UAXF_NOPURGE) | BTOM(_fBackup , UAXF_BACKUP) | BTOM(_fNoEncrypt, UAXF_NOENCRYPT) | BTOM(_fNoDecay , UAXF_NODECAY) ; } #endif #define ROT13(i) (((i) + 13) % 26) #define XXX_HASH 0 // proto code for way-shorter regnames #if !defined(DEBUG) && XXX_HASH #pragma message("warning: XXX_HASH defined non-DEBUG") #endif //*** _MayEncrypt -- encrypt registry key/value name // NOTES TCHAR *CEMDBLog::_MayEncrypt(LPCTSTR pszSrcPlain, LPTSTR pszDstEnc, int cchDst) { TCHAR *pszName; if (!_fNoEncrypt) { #if XXX_HASH DWORD dwHash; HashData((BYTE*)pszSrcPlain, lstrlen(pszSrcPlain), (BYTE*)&dwHash, SIZEOF(dwHash)); pszName = pszDstEnc; if (EVAL(cchDst >= (8 + 1))) wsprintf(pszName, TEXT("%x"), dwHash); else pszName = (TCHAR *)pszSrcPlain; #else TCHAR ch; // uh-oh, gotta figure out an intl-aware encryption scheme... pszName = pszDstEnc; pszDstEnc[--cchDst] = 0; // pre-terminate for overflow case ch = -1; while (cchDst-- > 0 && ch != 0) { ch = *pszSrcPlain++; if (TEXT('a') <= ch && ch <= TEXT('z')) ch = TEXT('a') + ROT13(ch - TEXT('a')); else if (TEXT('A') <= ch && ch <= TEXT('Z')) ch = TEXT('A') + ROT13(ch - TEXT('A')); else ; *pszDstEnc++ = ch; } #endif TraceMsg(DM_UEMTRACE, "uadb._me: plain=%s(enc=%s)", pszSrcPlain - (pszDstEnc - pszName), pszName); } else { pszName = (TCHAR *)pszSrcPlain; } return pszName; } #if XXX_CACHE // { //*** // ENTRY/EXIT // op 0:read, 1:write, 2:delete // HRESULT CEMDBLog::CacheOp(CACHEOP op, void *pvBuf, DWORD cbBuf, PNRWINFO prwi) { static TCHAR * const pszNameTab[] = { SZ_CTLSESSION, SZ_CUACount_ctor, }; int i; ASSERT(ARRAYSIZE(pszNameTab) == ARRAYSIZE(_rgCache)); for (i = 0; i < ARRAYSIZE(pszNameTab); i++) { if (lstrcmp(prwi->pszName, pszNameTab[i]) == 0) { TraceMsg(DM_PERF, "cedl.s_%c: this'=%x n=%s", TEXT("rwd")[op], prwi->self, prwi->pszName); switch (op) { // Read from the cache case CO_READ: // Do we have a cached item? if (_rgCache[i].pv) { // The cached buffer should be smaller than or equal to the // passed buffer size, or we get a buffer overflow ASSERT (_rgCache[i].cbSize <= cbBuf); // Load the cache into the buffer. Note that the // size requested may be larger than the size cached. This // is due to upgrade senarios memcpy(pvBuf, _rgCache[i].pv, _rgCache[i].cbSize); return S_OK; } break; // Write to the Cache case CO_WRITE: // Is the size different or not initialized? // When we first allocate this spot, it's size is zero. The // incomming buffer should be greater. if (_rgCache[i].cbSize != cbBuf) { // The size is different or uninialized. if (_rgCache[i].pv) // Free whatever we've got { // because we're getting a new one. _rgCache[i].cbSize = 0; // Set the size to zero. LocalFree(_rgCache[i].pv); } // Allocate a new buffer of the current size. _rgCache[i].pv = LocalAlloc(LPTR, cbBuf); } // Were we successful in allocating a cache buffer? if (_rgCache[i].pv) { // Yes, make the buffer size the same... Do this here incase the // allocate fails. _rgCache[i].cbSize = cbBuf; memcpy(_rgCache[i].pv, pvBuf, _rgCache[i].cbSize); return S_OK; } break; case CO_DELETE: // delete if (_rgCache[i].pv) { LocalFree(_rgCache[i].pv); _rgCache[i].pv = NULL; _rgCache[i].cbSize = 0; } return S_OK; default: ASSERT(0); // 'impossible' break; } TraceMsg(DM_PERF, "cedl.s_%c: this'=%x n=%s cache miss", TEXT("rwd")[op], prwi->self, prwi->pszName); break; } } return S_FALSE; } #endif // } HRESULT CEMDBLog::s_Read(void *pvBuf, DWORD cbBuf, PNRWINFO prwi) { HRESULT hr; CEMDBLog *pdb = (CEMDBLog *)prwi->self; TCHAR *pszName; TCHAR szNameEnc[MAX_URL_STRING]; #if XXX_CACHE if (pdb->CacheOp(CO_READ, pvBuf, cbBuf, prwi) == S_OK) return S_OK; #endif pszName = pdb->_MayEncrypt(prwi->pszName, szNameEnc, ARRAYSIZE(szNameEnc)); hr = pdb->QueryValue(pszName, (BYTE *)pvBuf, &cbBuf); #if XXX_CACHE pdb->CacheOp(CO_WRITE, pvBuf, cbBuf, prwi); #endif return hr; } HRESULT CEMDBLog::s_Write(void *pvBuf, DWORD cbBuf, PNRWINFO prwi) { HRESULT hr; CEMDBLog *pdb = (CEMDBLog *)prwi->self; TCHAR *pszName; TCHAR szNameEnc[MAX_URL_STRING]; #if XXX_CACHE // CO_DELETE not CO_WRITE (easier/safer) (perf fine since rarely write) pdb->CacheOp(CO_DELETE, pvBuf, cbBuf, prwi); #endif pszName = pdb->_MayEncrypt(prwi->pszName, szNameEnc, ARRAYSIZE(szNameEnc)); hr = pdb->SetValue(pszName, REG_BINARY, (BYTE *)pvBuf, cbBuf); return hr; } HRESULT CEMDBLog::s_Delete(void *pvBuf, DWORD cbBuf, PNRWINFO prwi) { HRESULT hr; CEMDBLog *pdb = (CEMDBLog *)prwi->self; TCHAR *pszName; TCHAR szNameEnc[MAX_URL_STRING]; #if XXX_CACHE pdb->CacheOp(CO_DELETE, pvBuf, cbBuf, prwi); #endif pszName = pdb->_MayEncrypt(prwi->pszName, szNameEnc, ARRAYSIZE(szNameEnc)); if (pdb->_fBackup) { TCHAR szDel[MAX_URL_STRING]; wnsprintf(szDel, ARRAYSIZE(szDel), SZ_DEL_PREFIX TEXT("%s"), pszName); if (pvBuf == NULL) { // happily we already have the data // o.w. we'd need to QueryValue into a mega-buffer TraceMsg(TF_WARNING, "uadb.s_d: _fBackup && !pvBuf (!)"); ASSERT(0); } if (pvBuf != NULL) { hr = pdb->SetValue(szDel, REG_BINARY, (BYTE *)pvBuf, cbBuf); if (FAILED(hr)) TraceMsg(TF_WARNING, "uadb.s_d: _fBackup hr=%x (!)", hr); } // (we'll do delete whether or not the _fBackup works) } hr = pdb->DeleteValue(pszName); TraceMsg(DM_UEMTRACE, "uadb.s_d: delete s=%s(%s) (_fBackup=%d) pRaw=0x%x hr=%x", pszName, prwi->pszName, pdb->_fBackup, pvBuf, hr); #if 1 // unneeded? if (FAILED(hr)) hr = s_Write(pvBuf, cbBuf, prwi); #endif return hr; } // } //*** THIS::IUASession::* { int CEMDBLog::GetSessionId() { HRESULT hr; NRWINFO rwi; CUASession aSess; int i; rwi.self = this; rwi.pszName = SZ_CTLSESSION; hr = aSess.LoadFrom(&s_Nrw3Info, &rwi); aSess.Initialize(); i = aSess.GetSessionId(); hr = aSess.SaveTo(FALSE, &s_Nrw3Info, &rwi); return i; } void CEMDBLog::SetSession(UAQUANTUM uaq, BOOL fForce) { HRESULT hr; NRWINFO rwi; CUASession aSess; rwi.self = this; rwi.pszName = SZ_CTLSESSION; hr = aSess.LoadFrom(&s_Nrw3Info, &rwi); aSess.Initialize(); aSess.SetSession(uaq, fForce); hr = aSess.SaveTo(TRUE, &s_Nrw3Info, &rwi); return; } // } //*** THIS::CUASession::* { extern DWORD g_dSessTime; CUASession::CUASession() { _fInited = FALSE; _fDirty = FALSE; return; } HRESULT CUASession::Initialize() { if (!_fInited) { _fInited = TRUE; _cCnt = 0; _qtMru = 0; _fDirty = TRUE; } return S_OK; } //*** THIS::GetSessionId -- increment profile count for command // int CUASession::GetSessionId() { return _cCnt; } //*** // ENTRY/EXIT // fForce ignore threshhold rules (e.g. for DEBUG) void CUASession::SetSession(UAQUANTUM uaq, BOOL fForce) { UATIME qtNow; qtNow = GetUaTime(NULL); if (qtNow - _qtMru >= g_dSessTime || fForce) { TraceMsg(DM_UEMTRACE, "uadb.ss: sid=%d++", _cCnt); _cCnt++; // nt5:173090 // if we wrap, there's nothing we can do. it would be pretty // bad, since everything would get promoted (since 'now' will // be *older* than 'mru' so there will be no decay). worse still // they'd stay promoted for a v. long time. we could detect that // in the decay code and (lazily) reset the count to 'now,1' or // somesuch, but it should never happen so we simply ASSERT. ASSERT(_cCnt != 0); // 'impossible' _qtMru = qtNow; _fDirty = TRUE; } return; } HRESULT CUASession::LoadFrom(PFNNRW3 pfnIO, PNRWINFO pRwi) { HRESULT hr; hr = (*pfnIO->_pfnRead)(_GetRawData(), _GetRawCount(), pRwi); if (SUCCEEDED(hr)) _fInited = TRUE; return hr; } HRESULT CUASession::SaveTo(BOOL fForce, PFNNRW3 pfnIO, PNRWINFO pRwi) { HRESULT hr; hr = S_FALSE; if (fForce || _fDirty) { hr = (*pfnIO->_pfnWrite)(_GetRawData(), _GetRawCount(), pRwi); _fDirty = FALSE; } return hr; } // } //*** CGCTask::* { CGCTask *CGCTask_Create(CEMDBLog *that) { CGCTask *pthis = new CGCTask; if (pthis) { if (FAILED(pthis->Initialize(that))) { delete pthis; pthis = NULL; } } return pthis; } HRESULT CGCTask::Initialize(CEMDBLog *that) { ASSERT(!_that); ASSERT(that); that->AddRef(); _that = that; return S_OK; } CGCTask::CGCTask() : CRunnableTask(RTF_DEFAULT) { } CGCTask::~CGCTask() { if (_that) _that->Release(); } //*** CGCTask::CRunnableTaskRT::* { HRESULT CGCTask::RunInitRT() { HRESULT hr; ASSERT(_that); g_fDidUAGC = 2; // breadcrumbs in case we die (even non-DEBUG) hr = _that->_GarbageCollectSlow(); g_fDidUAGC = 3; // breadcrumbs in case we die (even non-DEBUG) return hr; } // } // } #if 0 #ifdef DEBUG void emdbtst() { HRESULT hr; CEMDBLog *pdb = new CEMDBLog; if (pdb) { hr = pdb->Initialize(HKEY_CURRENT_USER, TEXT("UIProf")); ASSERT(SUCCEEDED(hr)); pdb->CountIncr("foo"); pdb->CountIncr("bar"); pdb->CountIncr("foo"); delete pdb; } return; } #endif #endif // }