Leaked source code of windows server 2003
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

  1. #include "priv.h"
  2. #include <runtask.h>
  3. #include "uacount.h"
  4. #include "regdb.h"
  5. #include "uemapp.h"
  6. #include "uareg.h"
  7. #define DM_UEMTRACE TF_UEM
  8. #define DM_PERF 0 // perf tune
  9. #define DB_NOLOG FALSE
  10. #define SZ_CTLSESSION TEXT("UEME_CTLSESSION")
  11. #define SZ_CUACount_ctor TEXT("UEME_CTLCUACount:ctor")
  12. #define SZ_DEL_PREFIX TEXT("del.")
  13. #define SZ_RUN_PREFIX TEXT("UEME_RUN")
  14. //***
  15. // DESCRIPTION
  16. // inc this any time you change the format of *anything* below {guid}
  17. // doing so will cause us to nuke the {guid} subtree and start fresh
  18. #define UA_VERSION 3
  19. #if 0
  20. char c_szDotDot[] = TEXT(".."); // RegStrFS does *not* support
  21. #endif
  22. // kind of hoaky to do INITGUID, but we want the GUID private to this file
  23. #define INITGUID
  24. #include <initguid.h>
  25. // {C28EB156-523C-11d2-A561-00A0C92DBFE8}
  26. DEFINE_GUID(CLSID_GCTaskTOID,
  27. 0xc28eb156, 0x523c, 0x11d2, 0xa5, 0x61, 0x0, 0xa0, 0xc9, 0x2d, 0xbf, 0xe8);
  28. #undef INITGUID
  29. class CGCTask : public CRunnableTask
  30. {
  31. public:
  32. //*** IUnknown
  33. // (... from CRunnableTask)
  34. //*** THISCLASS
  35. HRESULT Initialize(CEMDBLog *that);
  36. virtual STDMETHODIMP RunInitRT();
  37. protected:
  38. CGCTask();
  39. virtual ~CGCTask();
  40. friend CGCTask *CGCTask_Create(CEMDBLog *that);
  41. CEMDBLog *_that;
  42. };
  43. // {
  44. //*** CEMDBLog --
  45. //CRITICAL_SECTION g_csDbSvr /*=0*/ ;
  46. CEMDBLog *g_uempDbSvr[UEMIND_NSTANDARD + UEMIND_NINSTR]; // 0=shell 1=browser
  47. //*** g_fDidUAGC -- breadcrumbs in case we die (even non-DEBUG)
  48. // keep minimal state in case we deadlock or die or whatever
  49. // 0:not 1:pre-task 2:pre-GC 3:post-GC
  50. int g_fDidUAGC;
  51. FNNRW3 CEMDBLog::s_Nrw3Info = {
  52. CEMDBLog::s_Read,
  53. CEMDBLog::s_Write,
  54. CEMDBLog::s_Delete,
  55. };
  56. //*** helpers {
  57. #define E_NUKE (E_FAIL + 1)
  58. //*** RegGetVersion -- check registry tree 'Version'
  59. // ENTRY/EXIT
  60. // (see RegChkVersion)
  61. // hr (ret) S_OK:ok S_FALSE:no tree E_NUKE:old E_FAIL:new
  62. HRESULT RegGetVersion(HKEY hk, LPTSTR pszSubkey, LPTSTR pszValue, DWORD dwVers)
  63. {
  64. HRESULT hr;
  65. HKEY hk2;
  66. if (RegOpenKeyEx(hk, pszSubkey, 0, KEY_QUERY_VALUE, &hk2) == ERROR_SUCCESS)
  67. {
  68. if (!pszValue)
  69. pszValue = TEXT("Version");
  70. hr = E_NUKE; // assume version mismatch
  71. DWORD dwData;
  72. if (SHRegGetDWORD(hk2, NULL, pszValue, &dwData) == ERROR_SUCCESS)
  73. {
  74. if (dwData == dwVers)
  75. hr = S_OK; // great!
  76. else if (dwData > dwVers)
  77. hr = E_FAIL; // we're an old client, fail
  78. else
  79. ASSERT(hr == E_NUKE); // we're a new client, nuke it
  80. }
  81. RegCloseKey(hk2);
  82. }
  83. else
  84. {
  85. hr = S_FALSE; // assume nothing there at all
  86. }
  87. return hr;
  88. }
  89. //*** RegChkVersion -- check registry tree 'version', nuke if outdated
  90. // ENTRY/EXIT
  91. // hk e.g. hkey for "HKCU/.../Uassist"
  92. // pszSubkey e.g. "{clsid}"
  93. // pszValue e.g. "Version"
  94. // dwVers e.g. 3
  95. // hr (ret) S_OK:matched, S_FAIL:mismatched and del'ed, E_FAIL:o.w.
  96. // (other) (SE) pszSubkey deleted if not matched
  97. HRESULT RegChkVersion(HKEY hk, LPTSTR pszSubkey, LPTSTR pszValue, DWORD dwVers)
  98. {
  99. HRESULT hr;
  100. DWORD i;
  101. // RegGetVersion() S_OK:ok S_FALSE:new E_NUKE:old E_FAIL:fail
  102. hr = RegGetVersion(hk, pszSubkey, pszValue, dwVers);
  103. // at this point, we have:
  104. // S_OK: ok
  105. // S_FALSE: entire tree missing
  106. // E_NUKE: no "Version" or old version (nuke it)
  107. // E_FAIL: new version (we can't handle it)
  108. if (hr == E_FAIL) {
  109. TraceMsg(DM_UEMTRACE, "bui.rcv: incompat (uplevel)");
  110. }
  111. if (hr == E_NUKE) {
  112. TraceMsg(DM_UEMTRACE, "bui.rcv: bad tree, try delete");
  113. hr = S_FALSE; // assume nuked
  114. i = SHDeleteKey(hk, pszSubkey);
  115. if (i != ERROR_SUCCESS) {
  116. TraceMsg(DM_UEMTRACE, "bui.rcv: delete failed!");
  117. hr = E_FAIL; // bogus tree left laying around
  118. }
  119. }
  120. TraceMsg(DM_UEMTRACE, "bui.rcv: hr=0x%x", hr);
  121. return hr;
  122. }
  123. //*** GetUEMLogger -- get the (shared) instance of our logger object
  124. // NOTES
  125. // BY DESIGN: we leak g_uempDbSvr.
  126. // race condition on g_uempDbSvr. our caller guards against this.
  127. // the 5 billion ASSERTs below were for diagnosing nt5:145449 (fixed).
  128. HRESULT GetUEMLogger(int iSvr, CEMDBLog **p)
  129. {
  130. HRESULT hr, hrVers;
  131. CEMDBLog *pDbSvr;
  132. DWORD dwData, cbSize;
  133. ASSERT(iSvr < ARRAYSIZE(g_uempDbSvr));
  134. pDbSvr = g_uempDbSvr[iSvr];
  135. if (pDbSvr) {
  136. pDbSvr->AddRef();
  137. *p = pDbSvr;
  138. return S_OK;
  139. }
  140. pDbSvr = CEMDBLog_Create();
  141. if (EVAL(pDbSvr)) {
  142. TCHAR szClass[GUIDSTR_MAX]; // "{clsid}"
  143. SHStringFromGUID(IND_NONINSTR(iSvr) ? UEMIID_BROWSER : UEMIID_SHELL, szClass, GUIDSTR_MAX);
  144. TraceMsg(DM_UEMTRACE, "bui.gul: UEMIID_%s=%s", IND_NONINSTR(iSvr) ? TEXT("BROWSER") : TEXT("SHELL"), szClass);
  145. hr = pDbSvr->ChDir(!IND_ISINSTR(iSvr) ? SZ_UASSIST : SZ_UASSIST2);
  146. if (SUCCEEDED(hr)) {
  147. hrVers = RegChkVersion(pDbSvr->GetHkey(), szClass, SZ_UAVERSION, UA_VERSION);
  148. if (FAILED(hrVers)) {
  149. TraceMsg(DM_UEMTRACE, "bui.gul: rcv()=0x%x (!)", hrVers);
  150. hr = hrVers;
  151. }
  152. }
  153. if (SUCCEEDED(hr)) {
  154. hr = pDbSvr->ChDir(szClass);
  155. ASSERT(hrVers == S_OK || hrVers == S_FALSE);
  156. if (SUCCEEDED(hr) && hrVers == S_FALSE) {
  157. dwData = UA_VERSION;
  158. cbSize = SIZEOF(dwData);
  159. hr = pDbSvr->SetValue(SZ_UAVERSION, REG_DWORD, (BYTE*)&dwData, cbSize);
  160. }
  161. }
  162. if (SUCCEEDED(hr))
  163. hr = pDbSvr->ChDir(SZ_COUNT);
  164. // n.b. we can't call pDbSvr->GarbageCollect here since flags
  165. // (e.g. _fNoDecay) not set yet
  166. // pDbSvr->GarbageCollect(FALSE);
  167. if (FAILED(hr))
  168. {
  169. // this fails during RunOnce
  170. pDbSvr->Release();
  171. pDbSvr = NULL;
  172. }
  173. }
  174. if (pDbSvr) {
  175. ENTERCRITICAL;
  176. if (g_uempDbSvr[iSvr] == 0) {
  177. g_uempDbSvr[iSvr] = pDbSvr; // xfer refcnt
  178. pDbSvr = NULL;
  179. }
  180. LEAVECRITICAL;
  181. if (pDbSvr)
  182. pDbSvr->Release();
  183. }
  184. *p = g_uempDbSvr[iSvr];
  185. return *p ? S_OK : E_FAIL;
  186. }
  187. CEMDBLog::CEMDBLog() : _cRef(1)
  188. {
  189. ASSERT(_fBackup == FALSE);
  190. ASSERT(_fNoEncrypt == FALSE);
  191. return;
  192. }
  193. CEMDBLog::~CEMDBLog()
  194. {
  195. #if XXX_CACHE
  196. int i;
  197. for (i = 0; i < ARRAYSIZE(_rgCache); i++)
  198. {
  199. if (_rgCache[i].pv)
  200. {
  201. LocalFree(_rgCache[i].pv);
  202. _rgCache[i].pv = NULL;
  203. _rgCache[i].cbSize = 0;
  204. }
  205. }
  206. #endif
  207. SetRoot(0, STGM_READ); // close
  208. ASSERT(!_hkey);
  209. return;
  210. }
  211. void CEMDBLog_CleanUp()
  212. {
  213. int i;
  214. CEMDBLog *pDbSvr;
  215. TraceMsg(DM_UEMTRACE, "bui.uadb_cu: cleaning up");
  216. for (i = 0; i < UEMIND_NSTANDARD + UEMIND_NINSTR; i++) {
  217. if ((pDbSvr = (CEMDBLog *)InterlockedExchangePointer((void**) &g_uempDbSvr[i], (LPVOID) -1)))
  218. delete pDbSvr;
  219. }
  220. return;
  221. }
  222. HRESULT CEMDBLog::Initialize(HKEY hkey, DWORD grfMode)
  223. {
  224. HRESULT hr;
  225. hr = SetRoot(hkey, grfMode);
  226. return hr;
  227. }
  228. //***
  229. // hkey e.g. HKLM
  230. // pszSubKey e.g. "...\\Explorer\\Instance\\{...}"
  231. // grfMode subset of STGM_* values
  232. HRESULT CEMDBLog::SetRoot(HKEY hkeyNew, DWORD grfMode)
  233. {
  234. ASSERT(grfMode == STGM_READ || grfMode == STGM_WRITE);
  235. if (_hkey) {
  236. RegCloseKey(_hkey);
  237. _grfMode = 0;
  238. _hkey = 0;
  239. }
  240. if (hkeyNew) {
  241. _grfMode = grfMode;
  242. _hkey = SHRegDuplicateHKey(hkeyNew); // xfer ownership (and up khey refcnt)
  243. if (_hkey == NULL)
  244. return E_FAIL;
  245. }
  246. return S_OK;
  247. }
  248. HRESULT CEMDBLog::ChDir(LPCTSTR pszSubKey)
  249. {
  250. RIPMSG(!!pszSubKey, "ChDir: caller passed invalid pszSubKey!");
  251. HRESULT hr;
  252. if (pszSubKey)
  253. {
  254. if (_hkey && (_grfMode == STGM_READ || _grfMode == STGM_WRITE))
  255. {
  256. LONG lr;
  257. HKEY hkeyNew;
  258. if (_grfMode == STGM_READ)
  259. {
  260. lr = RegOpenKeyEx(_hkey, pszSubKey, 0, KEY_QUERY_VALUE, &hkeyNew);
  261. }
  262. else
  263. {
  264. lr = RegCreateKeyEx(_hkey, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE, NULL, &hkeyNew, NULL);
  265. }
  266. if (lr == ERROR_SUCCESS)
  267. {
  268. RegCloseKey(_hkey);
  269. _hkey = hkeyNew;
  270. }
  271. hr = HRESULT_FROM_WIN32(lr);
  272. }
  273. else
  274. {
  275. ASSERT(_hkey);
  276. ASSERT(_grfMode == STGM_READ || _grfMode == STGM_WRITE);
  277. hr = E_UNEXPECTED;
  278. }
  279. }
  280. else
  281. {
  282. hr = E_INVALIDARG;
  283. }
  284. return hr;
  285. }
  286. //*** CEMDBLog -- file-system-like view of registry
  287. // DESCRIPTION
  288. // basically keeps track of where we are and does 'relative' opens from
  289. // there. NYI: intent is to eventually support 'chdir' ops.
  290. // NOTES
  291. //
  292. CEMDBLog *CEMDBLog_Create()
  293. {
  294. HKEY hk = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, NULL, TRUE);
  295. if (hk)
  296. {
  297. CEMDBLog *prsfs = new CEMDBLog;
  298. if (prsfs && FAILED(prsfs->Initialize(hk, STGM_WRITE))) {
  299. prsfs->Release();
  300. prsfs = NULL;
  301. }
  302. RegCloseKey(hk);
  303. return prsfs;
  304. }
  305. return NULL;
  306. }
  307. //*** IsREG_XX_SZ -- see if ansi/unicode is an issue
  308. //
  309. #define IsREG_XX_SZ(dwTyp) \
  310. ((dwTyp) == REG_SZ || (dwTyp) == REG_MULTI_SZ || (dwTyp) == REG_EXPAND_SZ)
  311. HRESULT CEMDBLog::QueryValue(LPCTSTR pszName, BYTE *pbData, LPDWORD pcbData)
  312. {
  313. long i;
  314. DWORD dwType;
  315. i = SHQueryValueEx(_hkey, pszName, NULL, &dwType, pbData, pcbData);
  316. ASSERT(i != ERROR_SUCCESS || !IsREG_XX_SZ(dwType));
  317. return (i == ERROR_SUCCESS) ? S_OK : E_FAIL;
  318. }
  319. HRESULT CEMDBLog::SetValue(LPCTSTR pszName, DWORD dwType, const BYTE *pbData, DWORD cbData)
  320. {
  321. long i;
  322. ASSERT(_grfMode == STGM_WRITE);
  323. ASSERT(!IsREG_XX_SZ(dwType));
  324. i = RegSetValueEx(_hkey, pszName, NULL, dwType, pbData, cbData);
  325. return (i == ERROR_SUCCESS) ? S_OK : E_FAIL;
  326. }
  327. HRESULT CEMDBLog::DeleteValue(LPCTSTR pszName)
  328. {
  329. long i;
  330. ASSERT(_grfMode == STGM_WRITE);
  331. i = SHDeleteValue(_hkey, NULL, pszName);
  332. return (i == ERROR_SUCCESS) ? S_OK : E_FAIL;
  333. }
  334. HRESULT CEMDBLog::RmDir(LPCTSTR pszName, BOOL fRecurse)
  335. {
  336. HRESULT hr = E_FAIL;
  337. DWORD i;
  338. ASSERT(fRecurse); // others NYI
  339. ASSERT(_grfMode == STGM_WRITE);
  340. if (fRecurse) {
  341. i = SHDeleteKey(_hkey, pszName);
  342. }
  343. else {
  344. // not sure what to do, since we want a non-recursive delete
  345. // but we do want to handle presence of values (which shlwapi
  346. // doesn't support)
  347. //i = DeleteEmptyKey(_hkey, pszName);
  348. i = -1;
  349. }
  350. return (i == ERROR_SUCCESS) ? S_OK : E_FAIL;
  351. }
  352. //*** THIS::Count -- increment profile count for command
  353. // ENTRY/EXIT
  354. // fUpdate FALSE for the GC case (since can't update reg during RegEnum)
  355. // NOTES
  356. HRESULT CEMDBLog::GetCount(LPCTSTR pszCmd)
  357. {
  358. return _GetCountRW(pszCmd, TRUE);
  359. }
  360. // Returns the Filetime that is encoded in the Count Object.
  361. // note: we do a delayed upgrade of the binary stream in the registry. We will
  362. // use the old uem count info, but tack on the new filetime information when we increment the useage.
  363. FILETIME CEMDBLog::GetFileTime(LPCTSTR pszCmd)
  364. {
  365. NRWINFO rwi;
  366. HRESULT hres;
  367. CUACount aCnt;
  368. rwi.self = this;
  369. rwi.pszName = pszCmd;
  370. // This is a bizzar way of reading a string from the registry....
  371. hres = aCnt.LoadFrom(&s_Nrw3Info, &rwi);
  372. return aCnt.GetFileTime();
  373. }
  374. HRESULT CEMDBLog::_GetCountRW(LPCTSTR pszCmd, BOOL fUpdate)
  375. {
  376. HRESULT hr;
  377. CUACount aCnt;
  378. NRWINFO rwi;
  379. int i;
  380. hr = _GetCountWithDefault(pszCmd, TRUE, &aCnt);
  381. i = aCnt.GetCount();
  382. if (fUpdate) {
  383. rwi.self = this;
  384. rwi.pszName = pszCmd;
  385. hr = aCnt.SaveTo(FALSE, &s_Nrw3Info, &rwi);
  386. }
  387. return i;
  388. }
  389. //***
  390. // ENTRY/EXIT
  391. // hr (ret) S_OK if dead, o.w. != S_OK
  392. HRESULT CEMDBLog::IsDead(LPCTSTR pszCmd)
  393. {
  394. HRESULT hr;
  395. hr = _GetCountRW(pszCmd, FALSE);
  396. return hr;
  397. }
  398. extern DWORD g_dCleanSess;
  399. //***
  400. // NOTES
  401. // we need to be careful not to party on guys that either aren't counts
  402. // (e.g. UEME_CTLSESSION), or are 'special' (e.g. UEME_CTLCUACOUNT), or
  403. // shouldn't be deleted (e.g. "del.xxx"). for now we take a conservative
  404. // approach and just nuke things w/ UEME_RUN* as a prefix. better might
  405. // be to use a dope vector and delete anything that's marked as 'cleanup'.
  406. HRESULT CEMDBLog::GarbageCollect(BOOL fForce)
  407. {
  408. int i;
  409. if (!fForce) {
  410. if (g_dCleanSess != 0) {
  411. i = GetSessionId();
  412. if ((i % g_dCleanSess) != 0) {
  413. TraceMsg(DM_UEMTRACE, "uadb.gc: skip");
  414. return S_FALSE;
  415. }
  416. }
  417. }
  418. g_fDidUAGC = 1; // breadcrumbs in case we die (even non-DEBUG)
  419. // do _GarbageCollectSlow(), in the background
  420. HRESULT hr = E_FAIL;
  421. CGCTask *pTask = CGCTask_Create(this);
  422. if (pTask) {
  423. IShellTaskScheduler *pSched;
  424. hr = CoCreateInstance(CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_IShellTaskScheduler, (void**)&pSched);
  425. if (SUCCEEDED(hr)) {
  426. hr = pSched->AddTask(pTask, CLSID_GCTaskTOID, 0L, ITSAT_DEFAULT_PRIORITY);
  427. pSched->Release(); // (o.k. even if task hasn't completed)
  428. }
  429. pTask->Release();
  430. }
  431. return hr;
  432. }
  433. HRESULT CEMDBLog::_GarbageCollectSlow()
  434. {
  435. HKEY hk;
  436. int i;
  437. DWORD dwI, dwCch, dwType;
  438. HDSA hdsa;
  439. TCHAR *p;
  440. TCHAR szKey[MAXIMUM_SUB_KEY_LENGTH];
  441. TraceMsg(DM_UEMTRACE, "uadb.gc: hit");
  442. hdsa = DSA_Create(SIZEOF(szKey), 4); // max size, oh well...
  443. if (hdsa) {
  444. TCHAR szRun[SIZEOF(SZ_RUN_PREFIX)];
  445. TCHAR szTemp[SIZEOF(SZ_RUN_PREFIX)];
  446. TCHAR *pszTemp;
  447. pszTemp = _MayEncrypt(SZ_RUN_PREFIX, szTemp, ARRAYSIZE(szTemp));
  448. StringCchCopy(szRun, ARRAYSIZE(szRun), pszTemp);
  449. ASSERT(lstrlen(szRun) == lstrlen(SZ_RUN_PREFIX));
  450. hk = GetHkey();
  451. for (dwI = 0; ; dwI++) {
  452. dwCch = ARRAYSIZE(szKey);
  453. if (SHEnumValue(hk, dwI, szKey, &dwCch, &dwType, NULL, NULL) != NOERROR)
  454. break;
  455. if (StrCmpN(szKey, szRun, ARRAYSIZE(szRun) - 1) == 0) {
  456. if (IsDead(szKey) == S_OK)
  457. DSA_AppendItem(hdsa, szKey);
  458. }
  459. }
  460. for (i = DSA_GetItemCount(hdsa) - 1; i > 0; i--) {
  461. p = (TCHAR *)DSA_GetItemPtr(hdsa, i);
  462. TraceMsg(DM_UEMTRACE, "uadb.gc: nuke %s", p);
  463. GetCount(p); // decay to 0 will delete
  464. }
  465. DSA_Destroy(hdsa);
  466. hdsa = NULL;
  467. }
  468. return S_OK;
  469. }
  470. HRESULT CEMDBLog::IncCount(LPCTSTR pszCmd)
  471. {
  472. HRESULT hr;
  473. NRWINFO rwi;
  474. TraceMsg(DM_UEMTRACE, "uemt: ic <%s>", pszCmd);
  475. if (DB_NOLOG)
  476. return E_FAIL;
  477. #if 0 // ChDir is currently done at create time
  478. hr = ChDir(SZ_COUNT);
  479. #endif
  480. CUACount aCnt;
  481. hr = _GetCountWithDefault(pszCmd, TRUE, &aCnt);
  482. aCnt.IncCount();
  483. // Since we are incrementing the count,
  484. // We should update the last execute time
  485. aCnt.UpdateFileTime();
  486. rwi.self = this;
  487. rwi.pszName = pszCmd;
  488. hr = aCnt.SaveTo(TRUE, &s_Nrw3Info, &rwi);
  489. return hr;
  490. }
  491. HRESULT CEMDBLog::SetCount(LPCTSTR pszCmd, int cCnt)
  492. {
  493. HRESULT hr;
  494. NRWINFO rwi;
  495. TraceMsg(DM_UEMTRACE, "uemt: ic <%s>", pszCmd);
  496. if (DB_NOLOG)
  497. return E_FAIL;
  498. CUACount aCnt;
  499. // fDef=FALSE so don't create if doesn't exist
  500. hr = _GetCountWithDefault(pszCmd, /*fDef=*/FALSE, &aCnt);
  501. if (SUCCEEDED(hr)) { // don't want default...
  502. aCnt.SetCount(cCnt);
  503. rwi.self = this;
  504. rwi.pszName = pszCmd;
  505. hr = aCnt.SaveTo(TRUE, &s_Nrw3Info, &rwi);
  506. }
  507. return hr;
  508. }
  509. //***
  510. // ENTRY/EXIT
  511. // fDefault provide default if entry not found
  512. // ret S_OK: found w/o default; S_FALSE: needed default; E_xxx: error
  513. // NOTES
  514. // calling w/ fDefault=FALSE can still return S_FALSE
  515. HRESULT CEMDBLog::_GetCountWithDefault(LPCTSTR pszCmd, BOOL fDefault, CUACount *pCnt)
  516. {
  517. HRESULT hr, hrDef;
  518. NRWINFO rwi;
  519. rwi.self = this;
  520. rwi.pszName = pszCmd;
  521. hr = pCnt->LoadFrom(&s_Nrw3Info, &rwi);
  522. hrDef = S_OK;
  523. if (FAILED(hr)) {
  524. hrDef = S_FALSE;
  525. if (fDefault) {
  526. rwi.pszName = SZ_CUACount_ctor;
  527. hr = pCnt->LoadFrom(&s_Nrw3Info, &rwi);
  528. // pCnt->Initialize happens below (possibly 2x)
  529. if (FAILED(hr)) {
  530. TraceMsg(DM_UEMTRACE, "uadb._gcwd: create ctor %s", SZ_CUACount_ctor);
  531. hr = pCnt->Initialize(SAFECAST(this, IUASession *));
  532. ASSERT(pCnt->_GetCount() == 0);
  533. pCnt->_SetMru(SID_SNOWINIT); // start clock ticking...
  534. // cnt=UAC_NEWCOUNT, age=Now
  535. int i = _fNoDecay ? 1 : UAC_NEWCOUNT;
  536. pCnt->SetCount(i); // force age
  537. ASSERT(pCnt->_GetCount() == i);
  538. hr = pCnt->SaveTo(/*fForce*/TRUE, &s_Nrw3Info, &rwi);
  539. }
  540. #if XXX_DELETE
  541. pCnt->_SetFlags(UACF_INHERITED, UACF_INHERITED);
  542. #endif
  543. }
  544. }
  545. hr = pCnt->Initialize(SAFECAST(this, IUASession *));
  546. if (SUCCEEDED(hr))
  547. pCnt->_SetFlags(UAXF_XMASK, _SetFlags(0, 0) & UAXF_XMASK);
  548. return SUCCEEDED(hr) ? hrDef : hr;
  549. }
  550. HRESULT CEMDBLog::SetFileTime(LPCTSTR pszCmd, const FILETIME *pft)
  551. {
  552. HRESULT hr;
  553. NRWINFO rwi;
  554. TraceMsg(DM_UEMTRACE, "uemt: sft <%s>", pszCmd);
  555. if (DB_NOLOG)
  556. return E_FAIL;
  557. CUACount aCnt;
  558. // fDef=FALSE so don't create if doesn't exist
  559. hr = _GetCountWithDefault(pszCmd, /*fDef=*/FALSE, &aCnt);
  560. if (SUCCEEDED(hr)) { // don't want default...
  561. aCnt.SetFileTime(pft);
  562. rwi.self = this;
  563. rwi.pszName = pszCmd;
  564. hr = aCnt.SaveTo(TRUE, &s_Nrw3Info, &rwi);
  565. }
  566. return hr;
  567. }
  568. #if XXX_DELETE
  569. #define BTOM(b, m) ((b) ? (m) : 0)
  570. DWORD CEMDBLog::_SetFlags(DWORD dwMask, DWORD dwFlags)
  571. {
  572. // standard guys
  573. if (dwMask & UAXF_NOPURGE)
  574. _fNoPurge = BOOLIFY(dwFlags & UAXF_NOPURGE);
  575. if (dwMask & UAXF_BACKUP)
  576. _fBackup = BOOLIFY(dwFlags & UAXF_BACKUP);
  577. if (dwMask & UAXF_NOENCRYPT)
  578. _fNoEncrypt = BOOLIFY(dwFlags & UAXF_NOENCRYPT);
  579. if (dwMask & UAXF_NODECAY)
  580. _fNoDecay = BOOLIFY(dwFlags & UAXF_NODECAY);
  581. // my guys
  582. // (none)
  583. return 0 // n.b. see continuation line(s)!!!
  584. | BTOM(_fNoPurge , UAXF_NOPURGE)
  585. | BTOM(_fBackup , UAXF_BACKUP)
  586. | BTOM(_fNoEncrypt, UAXF_NOENCRYPT)
  587. | BTOM(_fNoDecay , UAXF_NODECAY)
  588. ;
  589. }
  590. #endif
  591. #define ROT13(i) (((i) + 13) % 26)
  592. #define XXX_HASH 0 // proto code for way-shorter regnames
  593. #if !defined(DEBUG) && XXX_HASH
  594. #pragma message("warning: XXX_HASH defined non-DEBUG")
  595. #endif
  596. //*** _MayEncrypt -- encrypt registry key/value name
  597. // NOTES
  598. TCHAR *CEMDBLog::_MayEncrypt(LPCTSTR pszSrcPlain, LPTSTR pszDstEnc, int cchDst)
  599. {
  600. TCHAR *pszName;
  601. if (!_fNoEncrypt) {
  602. #if XXX_HASH
  603. DWORD dwHash;
  604. HashData((BYTE*)pszSrcPlain, lstrlen(pszSrcPlain), (BYTE*)&dwHash, SIZEOF(dwHash));
  605. if (EVAL(cchDst >= (8 + 1)))
  606. {
  607. StringCchPrintf(pszDstEnc, cchDst, TEXT("%x"), dwHash);
  608. pszName = pszDstEnc;
  609. }
  610. else
  611. pszName = (TCHAR *)pszSrcPlain;
  612. #else
  613. TCHAR ch;
  614. // uh-oh, gotta figure out an intl-aware encryption scheme...
  615. pszName = pszDstEnc;
  616. pszDstEnc[--cchDst] = 0; // pre-terminate for overflow case
  617. ch = -1;
  618. while (cchDst-- > 0 && ch != 0) {
  619. ch = *pszSrcPlain++;
  620. if (TEXT('a') <= ch && ch <= TEXT('z'))
  621. ch = TEXT('a') + ROT13(ch - TEXT('a'));
  622. else if (TEXT('A') <= ch && ch <= TEXT('Z'))
  623. ch = TEXT('A') + ROT13(ch - TEXT('A'));
  624. else
  625. ;
  626. *pszDstEnc++ = ch;
  627. }
  628. #endif
  629. TraceMsg(DM_UEMTRACE, "uadb._me: plain=%s(enc=%s)", pszSrcPlain - (pszDstEnc - pszName), pszName);
  630. }
  631. else {
  632. pszName = (TCHAR *)pszSrcPlain;
  633. }
  634. return pszName;
  635. }
  636. #if XXX_CACHE // {
  637. //***
  638. // ENTRY/EXIT
  639. // op 0:read, 1:write, 2:delete
  640. //
  641. HRESULT CEMDBLog::CacheOp(CACHEOP op, void *pvBuf, DWORD cbBuf, PNRWINFO prwi)
  642. {
  643. static TCHAR * const pszNameTab[] = { SZ_CTLSESSION, SZ_CUACount_ctor, };
  644. int i;
  645. ASSERT(ARRAYSIZE(pszNameTab) == ARRAYSIZE(_rgCache));
  646. for (i = 0; i < ARRAYSIZE(pszNameTab); i++)
  647. {
  648. if (lstrcmp(prwi->pszName, pszNameTab[i]) == 0)
  649. {
  650. TraceMsg(DM_PERF, "cedl.s_%c: this'=%x n=%s", TEXT("rwd")[op], prwi->self, prwi->pszName);
  651. switch (op)
  652. {
  653. // Read from the cache
  654. case CO_READ:
  655. // Do we have a cached item?
  656. if (_rgCache[i].pv)
  657. {
  658. // The cached buffer should be smaller than or equal to the
  659. // passed buffer size, or we get a buffer overflow
  660. if (_rgCache[i].cbSize <= cbBuf)
  661. {
  662. // Load the cache into the buffer. Note that the
  663. // size requested may be larger than the size cached. This
  664. // is due to upgrade senarios
  665. memcpy(pvBuf, _rgCache[i].pv, _rgCache[i].cbSize);
  666. return S_OK;
  667. }
  668. else
  669. {
  670. ASSERT(FALSE);
  671. }
  672. }
  673. break;
  674. // Write to the Cache
  675. case CO_WRITE:
  676. // Is the size different or not initialized?
  677. // When we first allocate this spot, it's size is zero. The
  678. // incomming buffer should be greater.
  679. if (_rgCache[i].cbSize != cbBuf)
  680. {
  681. // The size is different or uninialized.
  682. if (_rgCache[i].pv) // Free whatever we've got
  683. { // because we're getting a new one.
  684. _rgCache[i].cbSize = 0; // Set the size to zero.
  685. LocalFree(_rgCache[i].pv);
  686. }
  687. // Allocate a new buffer of the current size.
  688. _rgCache[i].pv = LocalAlloc(LPTR, cbBuf);
  689. }
  690. // Were we successful in allocating a cache buffer?
  691. if (_rgCache[i].pv)
  692. {
  693. // Yes, make the buffer size the same... Do this here incase the
  694. // allocate fails.
  695. _rgCache[i].cbSize = cbBuf;
  696. memcpy(_rgCache[i].pv, pvBuf, _rgCache[i].cbSize);
  697. return S_OK;
  698. }
  699. break;
  700. case CO_DELETE: // delete
  701. if (_rgCache[i].pv)
  702. {
  703. LocalFree(_rgCache[i].pv);
  704. _rgCache[i].pv = NULL;
  705. _rgCache[i].cbSize = 0;
  706. }
  707. return S_OK;
  708. default:
  709. ASSERT(0); // 'impossible'
  710. break;
  711. }
  712. TraceMsg(DM_PERF, "cedl.s_%c: this'=%x n=%s cache miss", TEXT("rwd")[op], prwi->self, prwi->pszName);
  713. break;
  714. }
  715. }
  716. return S_FALSE;
  717. }
  718. #endif // }
  719. HRESULT CEMDBLog::s_Read(void *pvBuf, DWORD cbBuf, PNRWINFO prwi)
  720. {
  721. HRESULT hr;
  722. CEMDBLog *pdb = (CEMDBLog *)prwi->self;
  723. TCHAR *pszName;
  724. TCHAR szNameEnc[MAX_URL_STRING];
  725. #if XXX_CACHE
  726. if (pdb->CacheOp(CO_READ, pvBuf, cbBuf, prwi) == S_OK)
  727. return S_OK;
  728. #endif
  729. pszName = pdb->_MayEncrypt(prwi->pszName, szNameEnc, ARRAYSIZE(szNameEnc));
  730. hr = pdb->QueryValue(pszName, (BYTE *)pvBuf, &cbBuf);
  731. #if XXX_CACHE
  732. pdb->CacheOp(CO_WRITE, pvBuf, cbBuf, prwi);
  733. #endif
  734. return hr;
  735. }
  736. HRESULT CEMDBLog::s_Write(void *pvBuf, DWORD cbBuf, PNRWINFO prwi)
  737. {
  738. HRESULT hr;
  739. CEMDBLog *pdb = (CEMDBLog *)prwi->self;
  740. TCHAR *pszName;
  741. TCHAR szNameEnc[MAX_URL_STRING];
  742. #if XXX_CACHE
  743. // CO_DELETE not CO_WRITE (easier/safer) (perf fine since rarely write)
  744. pdb->CacheOp(CO_DELETE, pvBuf, cbBuf, prwi);
  745. #endif
  746. pszName = pdb->_MayEncrypt(prwi->pszName, szNameEnc, ARRAYSIZE(szNameEnc));
  747. hr = pdb->SetValue(pszName, REG_BINARY, (BYTE *)pvBuf, cbBuf);
  748. return hr;
  749. }
  750. HRESULT CEMDBLog::s_Delete(void *pvBuf, DWORD cbBuf, PNRWINFO prwi)
  751. {
  752. HRESULT hr;
  753. CEMDBLog *pdb = (CEMDBLog *)prwi->self;
  754. TCHAR *pszName;
  755. TCHAR szNameEnc[MAX_URL_STRING];
  756. #if XXX_CACHE
  757. pdb->CacheOp(CO_DELETE, pvBuf, cbBuf, prwi);
  758. #endif
  759. pszName = pdb->_MayEncrypt(prwi->pszName, szNameEnc, ARRAYSIZE(szNameEnc));
  760. if (pdb->_fBackup)
  761. {
  762. if (pvBuf == NULL)
  763. {
  764. // happily we already have the data
  765. // o.w. we'd need to QueryValue into a mega-buffer
  766. TraceMsg(TF_WARNING, "uadb.s_d: _fBackup && !pvBuf (!)");
  767. ASSERT(0);
  768. }
  769. else
  770. {
  771. TCHAR szDel[MAX_URL_STRING];
  772. hr = StringCchPrintf(szDel, ARRAYSIZE(szDel), SZ_DEL_PREFIX TEXT("%s"), pszName);
  773. if (SUCCEEDED(hr))
  774. {
  775. hr = pdb->SetValue(szDel, REG_BINARY, (BYTE *)pvBuf, cbBuf);
  776. }
  777. if (FAILED(hr))
  778. TraceMsg(TF_WARNING, "uadb.s_d: _fBackup hr=%x (!)", hr);
  779. }
  780. // (we'll do delete whether or not the _fBackup works)
  781. }
  782. hr = pdb->DeleteValue(pszName);
  783. TraceMsg(DM_UEMTRACE, "uadb.s_d: delete s=%s(%s) (_fBackup=%d) pRaw=0x%x hr=%x", pszName, prwi->pszName, pdb->_fBackup, pvBuf, hr);
  784. #if 1 // unneeded?
  785. if (FAILED(hr))
  786. hr = s_Write(pvBuf, cbBuf, prwi);
  787. #endif
  788. return hr;
  789. }
  790. // }
  791. //*** THIS::IUASession::* {
  792. int CEMDBLog::GetSessionId()
  793. {
  794. HRESULT hr;
  795. NRWINFO rwi;
  796. CUASession aSess;
  797. int i;
  798. rwi.self = this;
  799. rwi.pszName = SZ_CTLSESSION;
  800. hr = aSess.LoadFrom(&s_Nrw3Info, &rwi);
  801. aSess.Initialize();
  802. i = aSess.GetSessionId();
  803. hr = aSess.SaveTo(FALSE, &s_Nrw3Info, &rwi);
  804. return i;
  805. }
  806. void CEMDBLog::SetSession(UAQUANTUM uaq, BOOL fForce)
  807. {
  808. HRESULT hr;
  809. NRWINFO rwi;
  810. CUASession aSess;
  811. rwi.self = this;
  812. rwi.pszName = SZ_CTLSESSION;
  813. hr = aSess.LoadFrom(&s_Nrw3Info, &rwi);
  814. aSess.Initialize();
  815. aSess.SetSession(uaq, fForce);
  816. hr = aSess.SaveTo(TRUE, &s_Nrw3Info, &rwi);
  817. return;
  818. }
  819. // }
  820. //*** THIS::CUASession::* {
  821. extern DWORD g_dSessTime;
  822. CUASession::CUASession()
  823. {
  824. _fInited = FALSE;
  825. _fDirty = FALSE;
  826. return;
  827. }
  828. HRESULT CUASession::Initialize()
  829. {
  830. if (!_fInited) {
  831. _fInited = TRUE;
  832. _cCnt = 0;
  833. _qtMru = 0;
  834. _fDirty = TRUE;
  835. }
  836. return S_OK;
  837. }
  838. //*** THIS::GetSessionId -- increment profile count for command
  839. //
  840. int CUASession::GetSessionId()
  841. {
  842. return _cCnt;
  843. }
  844. //***
  845. // ENTRY/EXIT
  846. // fForce ignore threshhold rules (e.g. for DEBUG)
  847. void CUASession::SetSession(UAQUANTUM uaq, BOOL fForce)
  848. {
  849. UATIME qtNow;
  850. qtNow = GetUaTime(NULL);
  851. if (qtNow - _qtMru >= g_dSessTime || fForce) {
  852. TraceMsg(DM_UEMTRACE, "uadb.ss: sid=%d++", _cCnt);
  853. _cCnt++;
  854. // nt5:173090
  855. // if we wrap, there's nothing we can do. it would be pretty
  856. // bad, since everything would get promoted (since 'now' will
  857. // be *older* than 'mru' so there will be no decay). worse still
  858. // they'd stay promoted for a v. long time. we could detect that
  859. // in the decay code and (lazily) reset the count to 'now,1' or
  860. // somesuch, but it should never happen so we simply ASSERT.
  861. ASSERT(_cCnt != 0); // 'impossible'
  862. _qtMru = qtNow;
  863. _fDirty = TRUE;
  864. }
  865. return;
  866. }
  867. HRESULT CUASession::LoadFrom(PFNNRW3 pfnIO, PNRWINFO pRwi)
  868. {
  869. HRESULT hr;
  870. hr = (*pfnIO->_pfnRead)(_GetRawData(), _GetRawCount(), pRwi);
  871. if (SUCCEEDED(hr))
  872. _fInited = TRUE;
  873. return hr;
  874. }
  875. HRESULT CUASession::SaveTo(BOOL fForce, PFNNRW3 pfnIO, PNRWINFO pRwi)
  876. {
  877. HRESULT hr;
  878. hr = S_FALSE;
  879. if (fForce || _fDirty) {
  880. hr = (*pfnIO->_pfnWrite)(_GetRawData(), _GetRawCount(), pRwi);
  881. _fDirty = FALSE;
  882. }
  883. return hr;
  884. }
  885. // }
  886. //*** CGCTask::* {
  887. CGCTask *CGCTask_Create(CEMDBLog *that)
  888. {
  889. CGCTask *pthis = new CGCTask;
  890. if (pthis) {
  891. if (FAILED(pthis->Initialize(that))) {
  892. delete pthis;
  893. pthis = NULL;
  894. }
  895. }
  896. return pthis;
  897. }
  898. HRESULT CGCTask::Initialize(CEMDBLog *that)
  899. {
  900. ASSERT(!_that);
  901. ASSERT(that);
  902. that->AddRef();
  903. _that = that;
  904. return S_OK;
  905. }
  906. CGCTask::CGCTask() : CRunnableTask(RTF_DEFAULT)
  907. {
  908. }
  909. CGCTask::~CGCTask()
  910. {
  911. if (_that)
  912. _that->Release();
  913. }
  914. //*** CGCTask::CRunnableTaskRT::* {
  915. HRESULT CGCTask::RunInitRT()
  916. {
  917. HRESULT hr;
  918. ASSERT(_that);
  919. g_fDidUAGC = 2; // breadcrumbs in case we die (even non-DEBUG)
  920. hr = _that->_GarbageCollectSlow();
  921. g_fDidUAGC = 3; // breadcrumbs in case we die (even non-DEBUG)
  922. return hr;
  923. }
  924. // }
  925. // }
  926. #if 0
  927. #ifdef DEBUG
  928. void emdbtst()
  929. {
  930. HRESULT hr;
  931. CEMDBLog *pdb = new CEMDBLog;
  932. if (pdb)
  933. {
  934. hr = pdb->Initialize(HKEY_CURRENT_USER, TEXT("UIProf"));
  935. ASSERT(SUCCEEDED(hr));
  936. pdb->CountIncr("foo");
  937. pdb->CountIncr("bar");
  938. pdb->CountIncr("foo");
  939. delete pdb;
  940. }
  941. return;
  942. }
  943. #endif
  944. #endif
  945. // }