You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1167 lines
30 KiB
1167 lines
30 KiB
#include "priv.h"
|
|
#include <runtask.h>
|
|
#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")
|
|
|
|
|
|
//***
|
|
// 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 <initguid.h>
|
|
// {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 {
|
|
|
|
#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;
|
|
|
|
if (RegOpenKeyEx(hk, pszSubkey, 0, KEY_QUERY_VALUE, &hk2) == ERROR_SUCCESS)
|
|
{
|
|
if (!pszValue)
|
|
pszValue = TEXT("Version");
|
|
|
|
hr = E_NUKE; // assume version mismatch
|
|
DWORD dwData;
|
|
if (SHRegGetDWORD(hk2, NULL, pszValue, &dwData) == 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);
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE; // assume nothing there at all
|
|
}
|
|
|
|
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)
|
|
{
|
|
RIPMSG(!!pszSubKey, "ChDir: caller passed invalid pszSubKey!");
|
|
|
|
HRESULT hr;
|
|
|
|
if (pszSubKey)
|
|
{
|
|
if (_hkey && (_grfMode == STGM_READ || _grfMode == STGM_WRITE))
|
|
{
|
|
LONG lr;
|
|
HKEY hkeyNew;
|
|
|
|
if (_grfMode == STGM_READ)
|
|
{
|
|
lr = RegOpenKeyEx(_hkey, pszSubKey, 0, KEY_QUERY_VALUE, &hkeyNew);
|
|
}
|
|
else
|
|
{
|
|
lr = RegCreateKeyEx(_hkey, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE, NULL, &hkeyNew, NULL);
|
|
}
|
|
|
|
if (lr == ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(_hkey);
|
|
_hkey = hkeyNew;
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32(lr);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(_hkey);
|
|
ASSERT(_grfMode == STGM_READ || _grfMode == STGM_WRITE);
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//*** 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;
|
|
|
|
i = SHQueryValueEx(_hkey, pszName, 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;
|
|
|
|
ASSERT(_grfMode == STGM_WRITE);
|
|
|
|
ASSERT(!IsREG_XX_SZ(dwType));
|
|
i = RegSetValueEx(_hkey, pszName, 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;
|
|
|
|
ASSERT(fRecurse); // others NYI
|
|
|
|
ASSERT(_grfMode == STGM_WRITE);
|
|
|
|
if (fRecurse) {
|
|
i = SHDeleteKey(_hkey, pszName);
|
|
}
|
|
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 szTemp[SIZEOF(SZ_RUN_PREFIX)];
|
|
TCHAR *pszTemp;
|
|
|
|
pszTemp = _MayEncrypt(SZ_RUN_PREFIX, szTemp, ARRAYSIZE(szTemp));
|
|
StringCchCopy(szRun, ARRAYSIZE(szRun), pszTemp);
|
|
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));
|
|
if (EVAL(cchDst >= (8 + 1)))
|
|
{
|
|
StringCchPrintf(pszDstEnc, cchDst, TEXT("%x"), dwHash);
|
|
pszName = pszDstEnc;
|
|
}
|
|
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
|
|
if (_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;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szDel[MAX_URL_STRING];
|
|
hr = StringCchPrintf(szDel, ARRAYSIZE(szDel), SZ_DEL_PREFIX TEXT("%s"), pszName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
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
|
|
|
|
// }
|