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.

673 lines
15 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1994 **/
  4. /**********************************************************************/
  5. /*
  6. blobcach.cxx
  7. This module implements the private interface to the blob cache.
  8. FILE HISTORY:
  9. MCourage 09-Dec-1997 Created
  10. */
  11. #include <tsunami.hxx>
  12. #include <tscache.hxx>
  13. #include <dbgutil.h>
  14. #include "tsmemp.hxx"
  15. #include "blobcach.hxx"
  16. #include "blobhash.hxx"
  17. /*
  18. * Globals
  19. */
  20. CBlobHashTable * g_pURITable;
  21. CBlobHashTable * g_pBlobTable;
  22. CBlobCacheStats * g_pURICacheStats;
  23. CBlobCacheStats * g_pBlobCacheStats;
  24. #if TSUNAMI_REF_DEBUG
  25. PTRACE_LOG g_pBlobRefTraceLog;
  26. #endif
  27. /*
  28. * Private helper function declarations
  29. */
  30. inline VOID DerefBlob(PBLOB_HEADER pBlob);
  31. BOOL BlobFlushFilterAll(PBLOB_HEADER pBlob, PVOID pv);
  32. #define CHECK_BLOB_TYPE(dmx) DBG_ASSERT( \
  33. RESERVED_DEMUX_DIRECTORY_LISTING == (dmx) \
  34. || RESERVED_DEMUX_ATOMIC_DIRECTORY_GUARD == (dmx) \
  35. || RESERVED_DEMUX_URI_INFO == (dmx) \
  36. || RESERVED_DEMUX_FILE_DATA == (dmx) \
  37. || RESERVED_DEMUX_SSI == (dmx) \
  38. || RESERVED_DEMUX_QUERY_CACHE == (dmx) \
  39. )
  40. /*
  41. * function definitions
  42. */
  43. BOOL
  44. BlobCache_Initialize(
  45. VOID
  46. )
  47. {
  48. #if TSUNAMI_REF_DEBUG
  49. g_pBlobRefTraceLog = CreateRefTraceLog(
  50. 256, // LogSize
  51. 0 // ExtraBytesInHeader
  52. );
  53. #endif // TSUNAMI_REF_DEBUG
  54. g_pURITable = new CBlobHashTable("uri");
  55. if (!g_pURITable) {
  56. goto error;
  57. }
  58. g_pBlobTable = new CBlobHashTable("blob");
  59. if (!g_pBlobTable) {
  60. goto error;
  61. }
  62. g_pURICacheStats = new CBlobCacheStats;
  63. if (!g_pURICacheStats) {
  64. goto error;
  65. }
  66. g_pBlobCacheStats = new CBlobCacheStats;
  67. if (!g_pBlobCacheStats) {
  68. goto error;
  69. }
  70. return TRUE;
  71. error:
  72. if (g_pURICacheStats) {
  73. delete g_pURICacheStats;
  74. }
  75. if (g_pBlobCacheStats) {
  76. delete g_pBlobCacheStats;
  77. }
  78. if (g_pBlobTable) {
  79. delete g_pBlobTable;
  80. }
  81. if (g_pURITable) {
  82. delete g_pURITable;
  83. }
  84. return FALSE;
  85. }
  86. VOID
  87. BlobCache_Terminate(
  88. VOID
  89. )
  90. {
  91. FlushBlobCache();
  92. delete g_pBlobTable;
  93. DBGPRINTF(( DBG_CONTEXT,
  94. "BlobCache_Terminate: deleted g_pBlobTable.\n" ));
  95. delete g_pURITable;
  96. DBGPRINTF(( DBG_CONTEXT,
  97. "FileCache_Terminate: deleted g_pURITable.\n" ));
  98. delete g_pURICacheStats;
  99. delete g_pBlobCacheStats;
  100. #if TSUNAMI_REF_DEBUG
  101. if( g_pBlobRefTraceLog != NULL ) {
  102. DestroyRefTraceLog( g_pBlobRefTraceLog );
  103. g_pBlobRefTraceLog = NULL;
  104. }
  105. #endif // TSUNAMI_REF_DEBUG
  106. }
  107. BOOL
  108. CacheBlob(
  109. IN PBLOB_HEADER pBlob
  110. )
  111. /*++
  112. Routine Description:
  113. Add a Blob info structure to the cache.
  114. Arguments:
  115. pBlob - The structure to be cached.
  116. Return Values:
  117. TRUE on success.
  118. FALSE on failure (the item was not cached)
  119. --*/
  120. {
  121. CBlobHashTable * pHT;
  122. CBlobCacheStats * pCS;
  123. enum LK_RETCODE lkrc;
  124. BOOL bRetval;
  125. DBG_ASSERT( NULL != pBlob );
  126. DBG_ASSERT( NULL != pBlob->pBlobKey );
  127. DBG_ASSERT( TS_BLOB_SIGNATURE == pBlob->Signature );
  128. DBG_ASSERT( !pBlob->IsCached );
  129. DBG_ASSERT( pBlob->pBlobKey != NULL );
  130. CHECK_BLOB_TYPE(pBlob->pBlobKey->m_dwDemux);
  131. //
  132. // URI's in the URI table, and directories in the dir table
  133. //
  134. if (RESERVED_DEMUX_URI_INFO == pBlob->pBlobKey->m_dwDemux) {
  135. pHT = g_pURITable;
  136. pCS = g_pURICacheStats;
  137. } else {
  138. pHT = g_pBlobTable;
  139. pCS = g_pBlobCacheStats;
  140. }
  141. //
  142. // Add a reference for the caller
  143. //
  144. pBlob->AddRef();
  145. pBlob->IsCached = TRUE;
  146. //
  147. // put it in the hash table
  148. //
  149. lkrc = pHT->InsertRecord(pBlob, false);
  150. if (LK_SUCCESS == lkrc) {
  151. bRetval = TRUE;
  152. DBG_ASSERT( pCS );
  153. pCS->IncBlobsCached();
  154. } else {
  155. bRetval = FALSE;
  156. pBlob->IsCached = FALSE;
  157. }
  158. return (pBlob->IsCached);
  159. }
  160. VOID
  161. DecacheBlob(
  162. IN PBLOB_HEADER pBlob
  163. )
  164. /*++
  165. Routine Description:
  166. Remove a Blob info entry from the cache. After a call to DecacheBlob
  167. the entry will not be returned by CheckoutBlob. The entry itself is
  168. cleaned up when the last CheckinBlob occurs. Calling DecacheBlob
  169. checks the entry in.
  170. Arguments:
  171. pBlob - The Blob info structure to be decached
  172. Return Values:
  173. None.
  174. --*/
  175. {
  176. CBlobHashTable * pHT;
  177. CBlobCacheStats * pCS;
  178. DBG_ASSERT( NULL != pBlob );
  179. DBG_ASSERT( TS_BLOB_SIGNATURE == pBlob->Signature );
  180. DBG_ASSERT( pBlob->IsCached );
  181. DBG_ASSERT( pBlob->pBlobKey != NULL );
  182. CHECK_BLOB_TYPE(pBlob->pBlobKey->m_dwDemux);
  183. if (RESERVED_DEMUX_URI_INFO == pBlob->pBlobKey->m_dwDemux) {
  184. pHT = g_pURITable;
  185. pCS = g_pURICacheStats;
  186. } else {
  187. pHT = g_pBlobTable;
  188. pCS = g_pBlobCacheStats;
  189. }
  190. DBG_REQUIRE( LK_SUCCESS == pHT->DeleteKey(pBlob->pBlobKey) );
  191. DBG_ASSERT( pCS );
  192. pCS->DecBlobsCached();
  193. pBlob->IsFlushed = TRUE;
  194. DerefBlob(pBlob);
  195. }
  196. VOID
  197. FlushBlobCache(
  198. VOID
  199. )
  200. /*++
  201. Routine Description:
  202. Removes all entries from the cache. Unlike DecacheBlob, this
  203. function does not check any entries in.
  204. Arguments:
  205. None
  206. Return Value:
  207. None
  208. --*/
  209. {
  210. DBG_ASSERT( g_pBlobCacheStats );
  211. g_pBlobCacheStats->IncFlushes();
  212. FilteredFlushBlobCache(BlobFlushFilterAll, NULL);
  213. }
  214. LK_PREDICATE
  215. BlobFlushCachePredicate(
  216. PBLOB_HEADER pBlob,
  217. void* pvState
  218. )
  219. {
  220. TS_BLOB_FLUSH_STATE * pFlushState = static_cast<TS_BLOB_FLUSH_STATE*>(pvState);
  221. LK_PREDICATE lkpAction;
  222. if (pFlushState->pfnFilter(pBlob, pFlushState->pvParm)) {
  223. //
  224. // put it on the list
  225. //
  226. pBlob->AddRef(); // for the list
  227. InsertHeadList(&pFlushState->ListHead, &pBlob->FlushList);
  228. lkpAction = LKP_PERFORM;
  229. } else {
  230. lkpAction = LKP_NO_ACTION;
  231. }
  232. return lkpAction;
  233. }
  234. VOID
  235. FilteredFlushBlobCacheHelper (
  236. IN CBlobHashTable * pHT,
  237. IN PBLOBFILTERRTN pFilterRoutine,
  238. IN PVOID pv
  239. )
  240. /*++
  241. Routine Description:
  242. Removes entries based on a caller specified filter. The caller
  243. provides a boolean function which takes a cache entry as a
  244. parameter. The function will be called with each item in the cache.
  245. If the function returns TRUE, the item will be decached (but not
  246. checked in). Otherwise the item will remain in the cache.
  247. Arguments:
  248. pFilterRoutine - A pointer to the filter function
  249. Return Value:
  250. None
  251. --*/
  252. {
  253. TS_BLOB_FLUSH_STATE FlushState;
  254. //
  255. // Initialize the flush state
  256. //
  257. FlushState.pfnFilter = pFilterRoutine;
  258. InitializeListHead(&FlushState.ListHead);
  259. FlushState.pvParm = pv;
  260. //
  261. // Delete elements from table and construct list
  262. //
  263. pHT->DeleteIf(BlobFlushCachePredicate, &FlushState);
  264. //
  265. // Update element state and delete blobs as necessary
  266. //
  267. PLIST_ENTRY pEntry;
  268. PLIST_ENTRY pNext;
  269. PBLOB_HEADER pBlob;
  270. for (pEntry = FlushState.ListHead.Flink;
  271. pEntry != &FlushState.ListHead;
  272. pEntry = pNext ) {
  273. pNext = pEntry->Flink;
  274. pBlob = CONTAINING_RECORD( pEntry, BLOB_HEADER, FlushList );
  275. DBG_ASSERT( TS_BLOB_SIGNATURE == pBlob->Signature );
  276. pBlob->IsFlushed = TRUE;
  277. DerefBlob(pBlob); // remove list's reference
  278. }
  279. }
  280. VOID
  281. FilteredFlushBlobCache (
  282. IN PBLOBFILTERRTN pFilterRoutine,
  283. IN PVOID pv
  284. )
  285. /*++
  286. Routine Description:
  287. Removes entries based on a caller specified filter. The caller
  288. provides a boolean function which takes a cache entry as a
  289. parameter. The function will be called with each item in the cache.
  290. If the function returns TRUE, the item will be decached (but not
  291. checked in). Otherwise the item will remain in the cache.
  292. Arguments:
  293. pFilterRoutine - A pointer to the filter function
  294. Return Value:
  295. None
  296. --*/
  297. {
  298. DBG_ASSERT( g_pURICacheStats );
  299. DBG_ASSERT( g_pBlobCacheStats );
  300. g_pURICacheStats->IncFlushes();
  301. g_pBlobCacheStats->IncFlushes();
  302. FilteredFlushBlobCacheHelper(g_pURITable, pFilterRoutine, pv);
  303. FilteredFlushBlobCacheHelper(g_pBlobTable, pFilterRoutine, pv);
  304. }
  305. VOID
  306. FilteredFlushURIBlobCache (
  307. IN PBLOBFILTERRTN pFilterRoutine,
  308. IN PVOID pv
  309. )
  310. /*++
  311. Routine Description:
  312. Removes entries based on a caller specified filter. The caller
  313. provides a boolean function which takes a cache entry as a
  314. parameter. The function will be called with each item in the cache.
  315. If the function returns TRUE, the item will be decached (but not
  316. checked in). Otherwise the item will remain in the cache.
  317. This routine only flushes the URI cache.
  318. Arguments:
  319. pFilterRoutine - A pointer to the filter function
  320. Return Value:
  321. None
  322. --*/
  323. {
  324. DBG_ASSERT( g_pURICacheStats );
  325. g_pURICacheStats->IncFlushes();
  326. FilteredFlushBlobCacheHelper(g_pURITable, pFilterRoutine, pv);
  327. }
  328. BOOL
  329. CheckoutBlob(
  330. IN LPCSTR pstrPath,
  331. IN ULONG cchPath,
  332. IN DWORD dwService,
  333. IN DWORD dwInstance,
  334. IN ULONG iDemux,
  335. OUT PBLOB_HEADER * ppBlob
  336. )
  337. /*++
  338. Routine Description:
  339. Look up an entry in the cache and return it.
  340. Arguments:
  341. pstrPath - The pathname of the desired Blob info in UPPERCASE!!
  342. TSvcCache - This structure identifies the calling server instance,
  343. which is also used to identify the cache entry.
  344. ppBlob - On success this output points to the cached entry.
  345. Otherwise it is set to NULL.
  346. Return Value:
  347. TRUE if the item was found, FALSE otherwise.
  348. --*/
  349. {
  350. CBlobHashTable * pHT;
  351. CBlobCacheStats * pCS;
  352. CBlobKey blobKey;
  353. PBLOB_HEADER pBlob;
  354. BOOL bRetVal;
  355. BOOL bFlushed;
  356. DBG_ASSERT( pstrPath != NULL );
  357. CHECK_BLOB_TYPE(iDemux);
  358. //
  359. // Look in the hash table
  360. //
  361. blobKey.m_pszPathName = const_cast<char *>(pstrPath);
  362. blobKey.m_cbPathName = cchPath;
  363. blobKey.m_dwService = dwService;
  364. blobKey.m_dwInstance = dwInstance;
  365. blobKey.m_dwDemux = iDemux;
  366. //
  367. // URI's in the URI table, and everything else in the blob table
  368. //
  369. if (RESERVED_DEMUX_URI_INFO == blobKey.m_dwDemux) {
  370. pHT = g_pURITable;
  371. pCS = g_pURICacheStats;
  372. } else {
  373. pHT = g_pBlobTable;
  374. pCS = g_pBlobCacheStats;
  375. }
  376. pHT->FindKey(&blobKey, &pBlob);
  377. if (NULL == pBlob) {
  378. bRetVal = FALSE;
  379. goto exit;
  380. }
  381. //
  382. // Make sure it's valid and update state
  383. //
  384. DBG_ASSERT( pBlob->IsCached );
  385. DBG_ASSERT( !pBlob->IsFlushed );
  386. //
  387. // success
  388. //
  389. bRetVal = TRUE;
  390. *ppBlob = pBlob;
  391. exit:
  392. DBG_ASSERT( pCS );
  393. if (bRetVal) {
  394. pCS->IncHits();
  395. } else {
  396. pCS->IncMisses();
  397. }
  398. return bRetVal;
  399. }
  400. BOOL
  401. CheckoutBlobEntry(
  402. IN PBLOB_HEADER pBlob
  403. )
  404. /*++
  405. Routine Description:
  406. This function checks out an entry to which the caller already has
  407. a reference.
  408. Arguments:
  409. pBlob - The blob structure to be checked out.
  410. Return Value:
  411. TRUE - Blob was successfully checked out
  412. FALSE - Blob was checked out, but should not be used by the
  413. caller. (it's been flushed)
  414. --*/
  415. {
  416. BOOL bSuccess;
  417. CBlobCacheStats * pCS;
  418. DBG_ASSERT( pBlob != NULL );
  419. DBG_ASSERT( pBlob->IsCached );
  420. DBG_ASSERT( pBlob->pBlobKey != NULL );
  421. CHECK_BLOB_TYPE(pBlob->pBlobKey->m_dwDemux);
  422. pBlob->AddRef();
  423. if (RESERVED_DEMUX_URI_INFO == pBlob->pBlobKey->m_dwDemux) {
  424. pCS = g_pURICacheStats;
  425. } else {
  426. pCS = g_pBlobCacheStats;
  427. }
  428. DBG_ASSERT( pCS );
  429. if (pBlob->IsFlushed == FALSE) {
  430. pCS->IncHits();
  431. bSuccess = TRUE;
  432. } else {
  433. pCS->IncMisses();
  434. bSuccess = FALSE;
  435. }
  436. return bSuccess;
  437. }
  438. VOID
  439. CheckinBlob(
  440. IN PBLOB_HEADER pBlob
  441. )
  442. /*++
  443. Routine Description:
  444. Indicate that a previously checked out Blob info is no longer in use.
  445. Arguments:
  446. pvBlob - The Blob info structure to be checked in.
  447. Return Value:
  448. None.
  449. --*/
  450. {
  451. DBG_ASSERT( pBlob != NULL );
  452. DBG_ASSERT( pBlob->IsCached );
  453. DBG_ASSERT( pBlob->pBlobKey != NULL );
  454. CHECK_BLOB_TYPE(pBlob->pBlobKey->m_dwDemux);
  455. DerefBlob(pBlob);
  456. }
  457. inline VOID
  458. DerefBlob(
  459. PBLOB_HEADER pBlob
  460. )
  461. /*++
  462. --*/
  463. {
  464. DBG_ASSERT( pBlob != NULL );
  465. DBG_ASSERT( pBlob->IsCached );
  466. DBG_ASSERT( pBlob->pBlobKey != NULL );
  467. DBG_ASSERT( pBlob->Signature == TS_BLOB_SIGNATURE );
  468. CHECK_BLOB_TYPE(pBlob->pBlobKey->m_dwDemux);
  469. LONG lRefCount;
  470. //
  471. // Track memory corruption in free builds.
  472. //
  473. if ( TS_BLOB_SIGNATURE != pBlob->Signature )
  474. {
  475. DBG_ASSERT(!"The blob is corrupted");
  476. // This was hit once on winweb during NT5 deployment. Unfortunately
  477. // it was too close to escrow for a complete investigation.
  478. return;
  479. }
  480. lRefCount = pBlob->Deref();
  481. DBG_ASSERT( lRefCount >= 0 );
  482. if (lRefCount == 0) {
  483. DBG_ASSERT(pBlob->IsFlushed);
  484. pBlob->Signature = TS_FREE_BLOB_SIGNATURE;
  485. //
  486. // No one is using this one. Destroy!
  487. // First let the user specified cleanup run
  488. //
  489. if (pBlob->pfnFreeRoutine) {
  490. DBG_REQUIRE( pBlob->pfnFreeRoutine(pBlob) );
  491. }
  492. //
  493. // Delete the key stuff.
  494. // This should probably be in some other place
  495. // since we didn't allocate this string.
  496. // The FREE should go in some counterpart to
  497. // TsCacheDirectoryBlob.
  498. //
  499. CBlobKey * pblock = pBlob->pBlobKey;
  500. FREE(pblock->m_pszPathName);
  501. //
  502. // Delete the blob
  503. // Since TsAllocateEx does this funky trick to
  504. // allocate the key and the blob at once, we
  505. // only have to free the key.
  506. //
  507. FREE(pblock);
  508. }
  509. }
  510. BOOL
  511. BlobFlushFilterAll(
  512. PBLOB_HEADER pBlob,
  513. PVOID pv
  514. )
  515. {
  516. return TRUE;
  517. }
  518. //
  519. // blobcach.cxx
  520. //