Source code of Windows XP (NT5)
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.

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