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.

1728 lines
46 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name :
  4. cache.cxx
  5. Abstract:
  6. This module contains the tsunami caching routines
  7. Author:
  8. Murali R. Krishnan ( MuraliK ) 16-Jan-1995
  9. --*/
  10. #include "TsunamiP.Hxx"
  11. #pragma hdrstop
  12. #include <mbstring.h>
  13. #include <lonsi.hxx>
  14. #include <dbgutil.h>
  15. //
  16. // Items in a Bin list beyond this position will get moved to the front
  17. // on an object cache hit
  18. //
  19. #define REORDER_LIST_THRESHOLD 5
  20. //
  21. // Current count of cached file handles across a UNC connection
  22. //
  23. DWORD cCachedUNCHandles = 0;
  24. //
  25. // Enable caching of security descriptor & AccessCheck
  26. //
  27. BOOL g_fCacheSecDesc = TRUE;
  28. BOOL g_fEnableCaching = TRUE;
  29. BOOL
  30. RemoveLruHandleCacheItem(
  31. VOID
  32. );
  33. CACHE_TABLE CacheTable;
  34. #if TSUNAMI_REF_DEBUG
  35. PTRACE_LOG RefTraceLog;
  36. #endif // TSUNAMI_REF_DEBUG
  37. BOOL
  38. Cache_Initialize(
  39. IN DWORD MaxOpenFileInUse
  40. )
  41. {
  42. int index;
  43. //
  44. // Initialize configuration block
  45. //
  46. ZeroMemory(&Configuration,sizeof( Configuration ));
  47. InitializeCriticalSection( &CacheTable.CriticalSection );
  48. SET_CRITICAL_SECTION_SPIN_COUNT( &CacheTable.CriticalSection,
  49. IIS_DEFAULT_CS_SPIN_COUNT);
  50. InitializeListHead( &CacheTable.MruList );
  51. CacheTable.OpenFileInUse = 0;
  52. CacheTable.MaxOpenFileInUse = MaxOpenFileInUse;
  53. for ( index=0; index<MAX_BINS; index++ ) {
  54. InitializeListHead( &CacheTable.Items[ index ] );
  55. }
  56. return( TRUE );
  57. } // Cache_Initialize
  58. BOOL
  59. TsCacheDirectoryBlob(
  60. IN const TSVC_CACHE &TSvcCache,
  61. IN PCSTR pszDirectoryName,
  62. IN ULONG iDemultiplexor,
  63. IN PVOID pvBlob,
  64. IN BOOLEAN bKeepCheckedOut,
  65. IN PSECURITY_DESCRIPTOR pSecDesc
  66. )
  67. /*++
  68. Routine Description:
  69. This function associates the Blob given as input with the specified
  70. directory and demultiplexing number. Services should use this
  71. function to add a Blob to the cache.
  72. Callers must not cache the same Blob twice. Once a Blob is cached,
  73. its contents must not be modified, and it must not be freed or re-cached.
  74. Arguments
  75. --*/
  76. {
  77. CACHE_OBJECT *cache = NULL;
  78. PBLOB_HEADER pbhBlob;
  79. BOOLEAN bSuccess;
  80. ULONG iBin;
  81. PLIST_ENTRY pEntry;
  82. PCACHE_OBJECT pCache;
  83. PCHAR pszTemp;
  84. HASH_TYPE htHash;
  85. ULONG cchLength;
  86. ASSERT( pszDirectoryName != NULL );
  87. ASSERT( pvBlob != NULL );
  88. IF_DEBUG( CACHE) {
  89. DBGPRINTF( (DBG_CONTEXT,
  90. "TsCacheDirectoryBlob called with"
  91. " Dir=%S, DeMux=%u, PvBlob=%08x, ChkedOut=%d\n",
  92. pszDirectoryName,
  93. iDemultiplexor,
  94. pvBlob,
  95. bKeepCheckedOut
  96. ));
  97. }
  98. if ( g_fDisableCaching )
  99. {
  100. goto Cannot_Cache;
  101. }
  102. //
  103. // The caller will have passed their pointer to the usable area of the
  104. // Blob, so we have to adjust it to point to the beginning.
  105. //
  106. pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
  107. ASSERT( !pbhBlob->IsCached );
  108. //
  109. // Hash the directory name.
  110. //
  111. htHash = CalculateHashAndLengthOfPathName( pszDirectoryName,
  112. &cchLength );
  113. //
  114. // Allocate the cache object. We (effectively) allocate cchLength + 1
  115. // bytes, to allow for the trailing NULL.
  116. //
  117. cache = (PCACHE_OBJECT)ALLOC( sizeof(CACHE_OBJECT) + cchLength);
  118. if ( cache == NULL ) {
  119. IF_DEBUG( CACHE) {
  120. DBGPRINTF( ( DBG_CONTEXT,
  121. "Unable to alloc Cache Object. Failure.\n"));
  122. }
  123. goto Cannot_Cache;
  124. }
  125. cache->Signature = CACHE_OBJ_SIGNATURE;
  126. cache->hash = htHash;
  127. cache->cchLength = cchLength;
  128. //
  129. // Store the Blob in the new object.
  130. //
  131. cache->pbhBlob = pbhBlob;
  132. //
  133. // Store the security descriptor in the new object.
  134. //
  135. cache->pSecDesc = pSecDesc;
  136. cache->hLastSuccessAccessToken = NULL;
  137. //
  138. // We need to be able to find the cache entry from the Blob.
  139. //
  140. pbhBlob->pCache = cache;
  141. //
  142. // Initialize the check-out count.
  143. //
  144. cache->references = ( bKeepCheckedOut) ? 2 : 1;
  145. cache->iDemux = iDemultiplexor;
  146. cache->dwService = TSvcCache.GetServiceId();
  147. cache->dwInstance = TSvcCache.GetInstanceId();
  148. cache->TTL = 1;
  149. TSUNAMI_TRACE( cache->references, cache );
  150. IF_DEBUG(OPLOCKS) {
  151. DBGPRINTF( (DBG_CONTEXT,"TsCacheDirectoryBlob(%s) iDemux=%08lx, cache=%08lx, references=%d\n",
  152. pszDirectoryName, iDemultiplexor, cache, cache->references ));
  153. }
  154. InitializeListHead( &cache->DirChangeList );
  155. //
  156. // Lock the cache table against changes. We need to take the lock
  157. // before we add the new object to the directory change death list,
  158. // so that a directory change that kills this object will not find
  159. // the cache table without the object present.
  160. //
  161. EnterCriticalSection( &CacheTable.CriticalSection );
  162. //
  163. // Copy the directory name to the cache object.
  164. //
  165. memcpy( cache->szPath, pszDirectoryName, cache->cchLength + 1 );
  166. //
  167. // Add the object to the directory change expiry list.
  168. //
  169. // There's an ugly, disgusting hack here making this code aware
  170. // of the structure of URI info, but it's better than going
  171. // through everywhere and fixing the call to this routine to pass
  172. // in the file path as well as the cache key name.
  173. //
  174. if (iDemultiplexor == RESERVED_DEMUX_URI_INFO)
  175. {
  176. PW3_URI_INFO pURIInfo = (PW3_URI_INFO)pvBlob;
  177. pszTemp = pURIInfo->pszName;
  178. } else
  179. {
  180. pszTemp = (PCHAR)pszDirectoryName;
  181. }
  182. bSuccess = DcmAddNewItem(
  183. (PIIS_SERVER_INSTANCE)TSvcCache.GetServerInstance(),
  184. pszTemp,
  185. cache
  186. );
  187. if ( !bSuccess )
  188. {
  189. //
  190. // For whatever reason, we cannot get notifications of changes
  191. // in the directory containing the to-be-cached item. We won't
  192. // be adding this object to the cache table, so we unlock the
  193. // table and jump to the failure-handling code.
  194. //
  195. LeaveCriticalSection( &CacheTable.CriticalSection );
  196. IF_DEBUG( CACHE) {
  197. DBGPRINTF( ( DBG_CONTEXT,
  198. " Unable to cache. Due to rejection by DirChngMgr\n"));
  199. }
  200. goto Cannot_Cache;
  201. }
  202. //
  203. // Mark this blob as cached, since we'll either cache it or throw it
  204. // away hereafter.
  205. //
  206. pbhBlob->IsCached = TRUE;
  207. //
  208. // Add the object to the cache table, as the most-recently-used object.
  209. //
  210. iBin = HASH_TO_BIN( cache->hash );
  211. //
  212. // Look for a previously cached object for the same directory. If we
  213. // find one, remove it.
  214. //
  215. for ( pEntry = CacheTable.Items[ iBin ].Flink;
  216. pEntry != &CacheTable.Items[ iBin ];
  217. pEntry = pEntry->Flink )
  218. {
  219. pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
  220. if ( pCache->cchLength == cache->cchLength &&
  221. pCache->hash == cache->hash &&
  222. pCache->iDemux == cache->iDemux &&
  223. pCache->dwService == cache->dwService &&
  224. pCache->dwInstance== cache->dwInstance &&
  225. !_memicmp( cache->szPath, pCache->szPath, cache->cchLength ) )
  226. {
  227. //
  228. // We found a matching cache object. We remove it, since it
  229. // has been replaced by this new object.
  230. //
  231. IF_DEBUG(OPLOCKS) {
  232. DBGPRINTF( (DBG_CONTEXT,"TsCacheDirectoryBlob - Decache(%s)\n", pCache->szPath ));
  233. }
  234. DeCache( pCache, FALSE );
  235. IF_DEBUG( CACHE) {
  236. DBGPRINTF( ( DBG_CONTEXT,
  237. " Matching cache object found."
  238. " Throwing that object ( %08x) out of cache\n",
  239. pEntry));
  240. }
  241. break;
  242. }
  243. }
  244. //
  245. // Add this object to the cache.
  246. //
  247. InsertHeadList( &CacheTable.Items[ iBin ], &cache->BinList );
  248. //
  249. // Since this object was just added, put it at the head of the MRU list.
  250. //
  251. InsertHeadList( &CacheTable.MruList, &cache->MruList );
  252. //
  253. // Increase the running size of cached objects by the size of the one
  254. // just cached.
  255. //
  256. IF_DEBUG(OPLOCKS) {
  257. DBGPRINTF( (DBG_CONTEXT,"TsCacheDirectoryBlob(%s)\n",
  258. pszDirectoryName));
  259. }
  260. //
  261. // Limit number of open file entries in cache.
  262. // Note that in the current scenario pOpenFileInfo is set only after the URI_INFO
  263. // blob is inserted in cache, so TsCreateFileFromURI also has to check for
  264. // # of open file in cache.
  265. //
  266. if ( (iDemultiplexor == RESERVED_DEMUX_OPEN_FILE) ||
  267. (iDemultiplexor == RESERVED_DEMUX_URI_INFO &&
  268. ((W3_URI_INFO*)pvBlob)->bFileInfoValid &&
  269. ((W3_URI_INFO*)pvBlob)->pOpenFileInfo != NULL) )
  270. {
  271. TsIncreaseFileHandleCount( TRUE );
  272. }
  273. //
  274. // Unlock the cache table.
  275. //
  276. LeaveCriticalSection( &CacheTable.CriticalSection );
  277. ASSERT( BLOB_IS_OR_WAS_CACHED( pvBlob ) );
  278. //
  279. // Return success.
  280. //
  281. IF_DEBUG( CACHE) {
  282. DBGPRINTF( ( DBG_CONTEXT,
  283. " Cached object(%08x) contains Blob (%08x)."
  284. " Returning TRUE\n",
  285. cache, pvBlob));
  286. }
  287. return( TRUE );
  288. Cannot_Cache:
  289. //
  290. // The cleanup code does not cleanup a directory change item.
  291. //
  292. if ( cache != NULL )
  293. {
  294. cache->Signature = CACHE_OBJ_SIGNATURE_X;
  295. FREE( cache );
  296. cache = NULL;
  297. }
  298. ASSERT( !BLOB_IS_OR_WAS_CACHED( pvBlob ) );
  299. IF_DEBUG( CACHE) {
  300. DBGPRINTF( (DBG_CONTEXT, " Failure to cache the object ( %08x)\n",
  301. pvBlob));
  302. }
  303. return( FALSE );
  304. } // TsCacheDirectoryBlob
  305. BOOL
  306. TsCheckOutCachedBlob(
  307. IN const TSVC_CACHE &TSvcCache,
  308. IN PCSTR pszDirectoryName,
  309. IN ULONG iDemultiplexor,
  310. IN PVOID * ppvBlob,
  311. IN HANDLE hAccessToken,
  312. IN BOOL fMayCacheAccessToken,
  313. IN PSECURITY_DESCRIPTOR* ppSecDesc
  314. )
  315. {
  316. HASH_TYPE hash;
  317. ULONG cchLength;
  318. int iBin;
  319. BOOL Result;
  320. LONG refCount;
  321. PLIST_ENTRY pEntry;
  322. PCACHE_OBJECT pCache;
  323. DWORD Position = 0;
  324. BOOL fSkipIdCheck = (iDemultiplexor != RESERVED_DEMUX_OPEN_FILE) &&
  325. (iDemultiplexor != RESERVED_DEMUX_URI_INFO);
  326. ASSERT( pszDirectoryName != NULL );
  327. ASSERT( ppvBlob != NULL );
  328. //
  329. // Prepare the return value such that we fail by default.
  330. //
  331. Result = FALSE;
  332. if ( ppSecDesc )
  333. {
  334. *ppSecDesc = NULL;
  335. }
  336. //
  337. // Calculate the hash and length of the path name.
  338. //
  339. hash = CalculateHashAndLengthOfPathName( pszDirectoryName, &cchLength );
  340. //
  341. // Calculate the bin of the hash table that should head the list
  342. // containing the sought-after item.
  343. //
  344. iBin = HASH_TO_BIN( hash );
  345. EnterCriticalSection( &CacheTable.CriticalSection );
  346. __try
  347. {
  348. //
  349. // Look for a previously cached object for the same directory. If we
  350. // find one, return it.
  351. //
  352. for ( pEntry = CacheTable.Items[ iBin ].Flink;
  353. pEntry != &CacheTable.Items[ iBin ];
  354. pEntry = pEntry->Flink, Position++ )
  355. {
  356. pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
  357. ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
  358. ASSERT( pCache->pbhBlob->IsCached );
  359. ASSERT( pCache->pbhBlob->pCache == pCache );
  360. if ( pCache->cchLength == cchLength &&
  361. pCache->hash == hash &&
  362. pCache->iDemux == iDemultiplexor &&
  363. pCache->references > 0 &&
  364. ( fSkipIdCheck ||
  365. ( pCache->dwService == TSvcCache.GetServiceId() &&
  366. pCache->dwInstance == TSvcCache.GetInstanceId() ) ) &&
  367. !_memicmp( pCache->szPath, pszDirectoryName, cchLength ) )
  368. {
  369. //
  370. // Check access rights
  371. //
  372. if ( pCache->pSecDesc && hAccessToken &&
  373. hAccessToken != pCache->hLastSuccessAccessToken )
  374. {
  375. BOOL fAccess;
  376. DWORD dwGrantedAccess;
  377. BYTE psFile[SIZE_PRIVILEGE_SET];
  378. DWORD dwPS = sizeof( psFile );
  379. if ( !::AccessCheck(
  380. pCache->pSecDesc,
  381. hAccessToken,
  382. FILE_GENERIC_READ,
  383. &g_gmFile,
  384. (PRIVILEGE_SET*)psFile,
  385. &dwPS,
  386. &dwGrantedAccess,
  387. &fAccess ) || !fAccess )
  388. {
  389. DBGPRINTF( (DBG_CONTEXT, "[TsCheckOutCachedBlob] AccessCheck failed error %d\n", GetLastError() ));
  390. Result = FALSE;
  391. goto Exit;
  392. }
  393. if ( fMayCacheAccessToken )
  394. {
  395. pCache->hLastSuccessAccessToken = hAccessToken;
  396. }
  397. }
  398. //
  399. // We found a matching cache object. We return it and increase
  400. // its reference count.
  401. //
  402. *ppvBlob = pCache->pbhBlob + 1;
  403. ASSERT( pCache->pbhBlob->IsCached );
  404. //
  405. // Increase the reference count of the cached object, to prevent
  406. // it from expiration while it is checked out.
  407. //
  408. refCount = REFERENCE_CACHE_OBJ( pCache );
  409. if( refCount == 1 ) {
  410. //
  411. // The reference count was zero before we incremented
  412. // it, meaning this cache entry is in the midst of
  413. // getting deleted. We'll restore the reference count
  414. // and ignore this entry.
  415. //
  416. DEREFERENCE_CACHE_OBJ( pCache );
  417. continue;
  418. }
  419. TSUNAMI_TRACE( refCount, pCache );
  420. IF_DEBUG(OPLOCKS) {
  421. DBGPRINTF( (DBG_CONTEXT,"TsCheckOutCachedBlob(%s) iDemux=%08lx, cache=%08lx, references=%d\n",
  422. pszDirectoryName, pCache->iDemux, pCache, refCount ));
  423. }
  424. pCache->TTL = 1;
  425. Result = TRUE;
  426. //
  427. // If the found item is far enough back in the list, move
  428. // it to the front so the next hit will be quicker
  429. //
  430. if ( Position > REORDER_LIST_THRESHOLD )
  431. {
  432. RemoveEntryList( pEntry );
  433. InsertHeadList( &CacheTable.Items[ iBin ], pEntry );
  434. IF_DEBUG( CACHE ) {
  435. DBGPRINTF(( DBG_CONTEXT,
  436. "[TsCheckOutCachedBlobW] Reordered list for item at %d position\n",
  437. Position ));
  438. }
  439. }
  440. if ( ppSecDesc && pCache->pSecDesc )
  441. {
  442. if ( *ppSecDesc = (PSECURITY_DESCRIPTOR)LocalAlloc( LMEM_FIXED,
  443. GetSecurityDescriptorLength(pCache->pSecDesc) ) )
  444. {
  445. memcpy( *ppSecDesc,
  446. pCache->pSecDesc,
  447. GetSecurityDescriptorLength(pCache->pSecDesc) );
  448. }
  449. }
  450. break;
  451. }
  452. }
  453. }
  454. __except( EXCEPTION_EXECUTE_HANDLER )
  455. {
  456. //
  457. // As far as I can see, the only way we can end up here with
  458. // Result == TRUE is an exception on LeaveCriticalSection(). If
  459. // that happens, we're toast anyway, since noone will ever get to
  460. // the CacheTable again.
  461. //
  462. ASSERT( !Result );
  463. Result = FALSE;
  464. }
  465. Exit:
  466. LeaveCriticalSection( &CacheTable.CriticalSection );
  467. if ( Result) {
  468. INC_COUNTER( TSvcCache.GetServiceId(), CacheHits );
  469. } else {
  470. INC_COUNTER( TSvcCache.GetServiceId(), CacheMisses );
  471. }
  472. return( Result );
  473. } // TsCheckOutCachedBlobW
  474. VOID
  475. InsertHeadPhysFile(
  476. IN PPHYS_OPEN_FILE_INFO lpPFInfo,
  477. IN PVOID pvBlob
  478. )
  479. {
  480. PBLOB_HEADER pbhBlob;
  481. ASSERT( lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
  482. pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
  483. EnterCriticalSection( &CacheTable.CriticalSection );
  484. ASSERT( IsListEmpty( &pbhBlob->PFList ) );
  485. InsertHeadList( &lpPFInfo->OpenReferenceList, &pbhBlob->PFList );
  486. LeaveCriticalSection( &CacheTable.CriticalSection );
  487. }
  488. BOOL
  489. TsCheckOutCachedPhysFile(
  490. IN const TSVC_CACHE &TSvcCache,
  491. IN PCSTR pszDirectoryName,
  492. IN PVOID * ppvBlob
  493. )
  494. {
  495. HASH_TYPE hash;
  496. ULONG cchLength;
  497. int iBin;
  498. BOOL Result;
  499. BOOL Found;
  500. LONG refCount;
  501. PLIST_ENTRY pEntry;
  502. PCACHE_OBJECT pCache = NULL;
  503. DWORD Position = 0;
  504. PBLOB_HEADER pbhBlob;
  505. PPHYS_OPEN_FILE_INFO pPhysFileInfo;
  506. ASSERT( pszDirectoryName != NULL );
  507. ASSERT( ppvBlob != NULL );
  508. //
  509. // Prepare the return value such that we fail by default.
  510. //
  511. Result = FALSE;
  512. Found = FALSE;
  513. *ppvBlob = NULL;
  514. //
  515. // Calculate the hash and length of the path name.
  516. //
  517. hash = CalculateHashAndLengthOfPathName( pszDirectoryName, &cchLength );
  518. //
  519. // Calculate the bin of the hash table that should head the list
  520. // containing the sought-after item.
  521. //
  522. iBin = HASH_TO_BIN( hash );
  523. EnterCriticalSection( &CacheTable.CriticalSection );
  524. __try
  525. {
  526. //
  527. // Look for a previously cached object for the same directory. If we
  528. // find one, return it.
  529. //
  530. for ( pEntry = CacheTable.Items[ iBin ].Flink;
  531. pEntry != &CacheTable.Items[ iBin ];
  532. pEntry = pEntry->Flink, Position++ )
  533. {
  534. pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
  535. ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
  536. ASSERT( pCache->pbhBlob->IsCached );
  537. ASSERT( pCache->pbhBlob->pCache == pCache );
  538. if ( pCache->cchLength == cchLength &&
  539. pCache->hash == hash &&
  540. pCache->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE &&
  541. pCache->references > 0 &&
  542. !_memicmp( pCache->szPath, pszDirectoryName, cchLength ) )
  543. {
  544. //
  545. // We found a matching cache object. We return it and increase
  546. // its reference count.
  547. //
  548. *ppvBlob = pCache->pbhBlob + 1;
  549. ASSERT( pCache->pbhBlob->IsCached );
  550. //
  551. // Increase the reference count of the cached object, to prevent
  552. // it from expiration while it is checked out.
  553. //
  554. refCount = REFERENCE_CACHE_OBJ( pCache );
  555. if( refCount == 1 ) {
  556. //
  557. // The reference count was zero before we incremented
  558. // it, meaning this cache entry is in the midst of
  559. // getting deleted. We'll restore the reference count
  560. // and ignore this entry.
  561. //
  562. DEREFERENCE_CACHE_OBJ( pCache );
  563. continue;
  564. }
  565. TSUNAMI_TRACE( refCount, pCache );
  566. IF_DEBUG(OPLOCKS) {
  567. DBGPRINTF( (DBG_CONTEXT,"TsCheckOutCachedPhysFile(%s) iDemux=%08lx, cache=%08lx, references=%d\n",
  568. pszDirectoryName, pCache->iDemux, pCache, refCount ));
  569. }
  570. pCache->TTL = 1;
  571. Result = TRUE;
  572. Found = TRUE;
  573. //
  574. // If the found item is far enough back in the list, move
  575. // it to the front so the next hit will be quicker
  576. //
  577. if ( Position > REORDER_LIST_THRESHOLD )
  578. {
  579. RemoveEntryList( pEntry );
  580. InsertHeadList( &CacheTable.Items[ iBin ], pEntry );
  581. IF_DEBUG( OPLOCKS ) {
  582. DBGPRINTF(( DBG_CONTEXT,
  583. "[TsCheckOutCachedBlobW] Reordered list for item at %d position\n",
  584. Position ));
  585. }
  586. }
  587. break;
  588. }
  589. }
  590. }
  591. __except( EXCEPTION_EXECUTE_HANDLER )
  592. {
  593. //
  594. // As far as I can see, the only way we can end up here with
  595. // Result == TRUE is an exception on LeaveCriticalSection(). If
  596. // that happens, we're toast anyway, since noone will ever get to
  597. // the CacheTable again.
  598. //
  599. ASSERT( !Result );
  600. Result = FALSE;
  601. }
  602. //
  603. // If we don't find a cache entry for the file, create one
  604. //
  605. pCache = NULL;
  606. if ( !Result ) {
  607. Result = TsAllocateEx( TSvcCache,
  608. sizeof( PHYS_OPEN_FILE_INFO ),
  609. ppvBlob,
  610. DisposePhysOpenFileInfo );
  611. if ( Result ) {
  612. pPhysFileInfo = (PPHYS_OPEN_FILE_INFO)*ppvBlob;
  613. TSUNAMI_TRACE( TRACE_PHYS_CREATE, pPhysFileInfo );
  614. pPhysFileInfo->Signature = PHYS_OBJ_SIGNATURE;
  615. pPhysFileInfo->hOpenFile = INVALID_HANDLE_VALUE;
  616. pPhysFileInfo->fInitComplete = FALSE;
  617. pPhysFileInfo->dwLastError = ERROR_FILE_NOT_FOUND;
  618. pPhysFileInfo->fSecurityDescriptor = FALSE;
  619. pPhysFileInfo->fDeleteOnClose = FALSE;
  620. pPhysFileInfo->fIsCached = FALSE;
  621. InitializeListHead( &pPhysFileInfo->OpenReferenceList );
  622. pPhysFileInfo->abSecurityDescriptor = (BYTE *)ALLOC( SECURITY_DESC_DEFAULT_SIZE );
  623. if ( pPhysFileInfo->abSecurityDescriptor == NULL ) {
  624. TsFree( TSvcCache, *ppvBlob );
  625. *ppvBlob = NULL;
  626. Result = FALSE;
  627. goto Exit;
  628. } else {
  629. pPhysFileInfo->cbSecDescMaxSize = SECURITY_DESC_DEFAULT_SIZE;
  630. }
  631. //
  632. // *ppvBlob points to the usable area of the
  633. // Blob, so we have to adjust it to point to the beginning.
  634. //
  635. pbhBlob = (( PBLOB_HEADER )*ppvBlob ) - 1;
  636. ASSERT( !pbhBlob->IsCached );
  637. if ( g_fDisableCaching )
  638. {
  639. goto Cannot_Cache;
  640. }
  641. //
  642. // Allocate the cache object. We (effectively) allocate cchLength + 1
  643. // bytes, to allow for the trailing NULL.
  644. //
  645. pCache = (PCACHE_OBJECT)ALLOC( sizeof(CACHE_OBJECT) + cchLength);
  646. if ( pCache == NULL ) {
  647. IF_DEBUG( OPLOCKS ) {
  648. DBGPRINTF( ( DBG_CONTEXT,
  649. "Unable to alloc Cache Object. Failure.\n"));
  650. }
  651. TsFree( TSvcCache, *ppvBlob );
  652. *ppvBlob = NULL;
  653. Result = FALSE;
  654. goto Exit;
  655. }
  656. pCache->Signature = CACHE_OBJ_SIGNATURE;
  657. pCache->hash = hash;
  658. pCache->cchLength = cchLength;
  659. //
  660. // Store the Blob in the new object.
  661. //
  662. pCache->pbhBlob = pbhBlob;
  663. //
  664. // Store the security descriptor in the new object.
  665. //
  666. pCache->pSecDesc = NULL;
  667. pCache->hLastSuccessAccessToken = NULL;
  668. //
  669. // We need to be able to find the cache entry from the Blob.
  670. //
  671. pbhBlob->pCache = pCache;
  672. //
  673. // Initialize the check-out count.
  674. //
  675. pCache->references = 1;
  676. pCache->iDemux = RESERVED_DEMUX_PHYSICAL_OPEN_FILE;
  677. pCache->dwService = TSvcCache.GetServiceId();
  678. pCache->dwInstance = TSvcCache.GetInstanceId();
  679. pCache->TTL = 1;
  680. TSUNAMI_TRACE( pCache->references, pCache );
  681. IF_DEBUG(OPLOCKS) {
  682. DBGPRINTF( (DBG_CONTEXT,"TsCheckOutCachedPhysFile(%s) cache=%08lx, references=%d\n",
  683. pszDirectoryName, pCache, pCache->references ));
  684. }
  685. InitializeListHead( &pCache->DirChangeList );
  686. //
  687. // Copy the directory name to the cache object.
  688. //
  689. memcpy( pCache->szPath, pszDirectoryName, pCache->cchLength + 1 );
  690. #if 0
  691. Result = DcmAddNewItem(
  692. (PIIS_SERVER_INSTANCE)TSvcCache.GetServerInstance(),
  693. (PCHAR)pszDirectoryName,
  694. pCache
  695. );
  696. if ( !Result )
  697. {
  698. //
  699. // For whatever reason, we cannot get notifications of changes
  700. // in the directory containing the to-be-cached item. We won't
  701. // be adding this object to the cache table, so we unlock the
  702. // table and jump to the failure-handling code.
  703. //
  704. IF_DEBUG( OPLOCKS) {
  705. DBGPRINTF( ( DBG_CONTEXT,
  706. " Unable to cache. Due to rejection by DirChngMgr\n"));
  707. }
  708. goto Cannot_Cache;
  709. }
  710. #endif
  711. //
  712. // Mark this blob as cached, since we'll either cache it or throw it
  713. // away hereafter.
  714. //
  715. pbhBlob->IsCached = TRUE;
  716. pPhysFileInfo->fIsCached = TRUE;
  717. //
  718. // Add the object to the cache table, as the most-recently-used object.
  719. //
  720. iBin = HASH_TO_BIN( pCache->hash );
  721. //
  722. // Add this object to the cache.
  723. //
  724. InsertHeadList( &CacheTable.Items[ iBin ], &pCache->BinList );
  725. //
  726. // Since this object was just added, put it at the head of the MRU list.
  727. //
  728. InsertHeadList( &CacheTable.MruList, &pCache->MruList );
  729. //
  730. // Increase the running size of cached objects by the size of the one
  731. // just cached.
  732. //
  733. IF_DEBUG(OPLOCKS) {
  734. DBGPRINTF( (DBG_CONTEXT,"TsCheckoutCachedPhysFile(%s)\n",
  735. pszDirectoryName ));
  736. }
  737. ASSERT( BLOB_IS_OR_WAS_CACHED( *ppvBlob ) );
  738. //
  739. // Return success.
  740. //
  741. IF_DEBUG( OPLOCKS) {
  742. DBGPRINTF( ( DBG_CONTEXT,
  743. " Cached object(%08x) contains Blob (%08x)."
  744. " Returning TRUE\n",
  745. pCache, *ppvBlob));
  746. }
  747. goto Exit;
  748. } else {
  749. IF_DEBUG(OPLOCKS) {
  750. DBGPRINTF( (DBG_CONTEXT,"TsCheckOutCachedPhysFile(%s) Alloc Failed!\n",
  751. pszDirectoryName ));
  752. }
  753. }
  754. } else {
  755. goto Exit;
  756. }
  757. Cannot_Cache:
  758. //
  759. // The cleanup code does not cleanup a directory change item.
  760. //
  761. if ( pCache != NULL )
  762. {
  763. pCache->Signature = CACHE_OBJ_SIGNATURE_X;
  764. FREE( pCache );
  765. pCache = NULL;
  766. }
  767. ASSERT( !BLOB_IS_OR_WAS_CACHED( *ppvBlob ) );
  768. IF_DEBUG( OPLOCKS) {
  769. DBGPRINTF( (DBG_CONTEXT, " Failure to cache the object ( %08x)\n",
  770. *ppvBlob));
  771. }
  772. Result = FALSE;
  773. Exit:
  774. LeaveCriticalSection( &CacheTable.CriticalSection );
  775. if ( Result) {
  776. INC_COUNTER( TSvcCache.GetServiceId(), CacheHits );
  777. } else {
  778. INC_COUNTER( TSvcCache.GetServiceId(), CacheMisses );
  779. }
  780. return( Found );
  781. } // TsCheckOutCachedPhysFile
  782. BOOL
  783. TsCheckInCachedBlob(
  784. IN PVOID pvBlob
  785. )
  786. {
  787. PBLOB_HEADER pbhBlob;
  788. PCACHE_OBJECT pCache;
  789. BOOL bEjected;
  790. pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
  791. ASSERT( pbhBlob->IsCached );
  792. pCache = pbhBlob->pCache;
  793. ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
  794. ASSERT( pCache->pbhBlob == pbhBlob );
  795. ASSERT( pCache->references > 0 );
  796. TsDereferenceCacheObj( pCache, TRUE );
  797. return( TRUE );
  798. } // TsCheckInCachedBlob
  799. BOOL
  800. TsAddRefCachedBlob(
  801. IN PVOID pvBlob
  802. )
  803. {
  804. PBLOB_HEADER pbhBlob;
  805. PCACHE_OBJECT pCache;
  806. BOOL bEjected;
  807. LONG refCount;
  808. pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
  809. ASSERT( pbhBlob->IsCached );
  810. pCache = pbhBlob->pCache;
  811. ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
  812. ASSERT( pCache->pbhBlob == pbhBlob );
  813. ASSERT( pCache->references > 0 );
  814. refCount = REFERENCE_CACHE_OBJ( pCache );
  815. TSUNAMI_TRACE( refCount, pCache );
  816. return( TRUE );
  817. } // TsCheckInCachedBlob
  818. BOOL
  819. TsExpireCachedBlob(
  820. IN const TSVC_CACHE &TSvcCache,
  821. IN PVOID pvBlob
  822. )
  823. {
  824. PBLOB_HEADER pbhBlob;
  825. PCACHE_OBJECT pCache;
  826. pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
  827. ASSERT( pbhBlob->IsCached );
  828. pCache = pbhBlob->pCache;
  829. ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
  830. ASSERT( pCache->pbhBlob == pbhBlob );
  831. ASSERT( pCache->references > 0 );
  832. return( DeCache( pCache, TRUE ) );
  833. } // TsExpireCachedBlob
  834. VOID
  835. TsDereferenceCacheObj(
  836. IN PCACHE_OBJECT pCache,
  837. IN BOOL fLockCacheTable
  838. )
  839. {
  840. LONG refCount;
  841. ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
  842. ASSERT( pCache->references > 0 );
  843. ASSERT( pCache->pbhBlob->IsCached );
  844. refCount = DEREFERENCE_CACHE_OBJ( pCache );
  845. TSUNAMI_TRACE( refCount, pCache );
  846. IF_DEBUG(OPLOCKS) {
  847. DBGPRINTF( (DBG_CONTEXT,"TsDereferenceCacheObj(%s) iDemux=%08lx, cache=%08lx, references=%d\n",
  848. pCache->szPath, pCache->iDemux, pCache, refCount ));
  849. }
  850. if( refCount == 0 ) {
  851. EnterCriticalSection( &CacheTable.CriticalSection );
  852. if ( pCache->references != 0 ) {
  853. LeaveCriticalSection( &CacheTable.CriticalSection );
  854. return;
  855. }
  856. if ( pCache->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE ) {
  857. RemoveCacheObjFromLists( pCache, FALSE );
  858. }
  859. if (!DisableSPUD) {
  860. if (!IsListEmpty( &pCache->pbhBlob->PFList ) ) {
  861. RemoveEntryList( &pCache->pbhBlob->PFList );
  862. }
  863. }
  864. LeaveCriticalSection( &CacheTable.CriticalSection );
  865. //
  866. // We best not be on a list if we're about to be freed here
  867. //
  868. ASSERT( IsListEmpty( &pCache->BinList ) );
  869. //
  870. // We really want to call TsFree here, but we don't have a TsvcCache
  871. //
  872. IF_DEBUG( CACHE )
  873. {
  874. DBGPRINTF(( DBG_CONTEXT,
  875. "[DeCache] Free routine: 0x%lx, Blob: 0x%lx Cache obj: 0x%lx\n",
  876. pCache->pbhBlob->pfnFreeRoutine,
  877. pCache->pbhBlob,
  878. pCache ));
  879. }
  880. if ( pCache->pbhBlob->pfnFreeRoutine )
  881. pCache->pbhBlob->pfnFreeRoutine( pCache->pbhBlob + 1);
  882. IF_DEBUG(OPLOCKS) {
  883. DBGPRINTF( (DBG_CONTEXT,"TsDereferenceCacheObj(%s)\n",
  884. pCache->szPath ));
  885. }
  886. DEC_COUNTER( pCache->dwService, CurrentObjects );
  887. if ( pCache->pSecDesc )
  888. {
  889. LocalFree( pCache->pSecDesc );
  890. }
  891. if ( pCache->iDemux == RESERVED_DEMUX_OPEN_FILE )
  892. {
  893. TsDecreaseFileHandleCount();
  894. }
  895. pCache->Signature = CACHE_OBJ_SIGNATURE_X;
  896. FREE( pCache->pbhBlob );
  897. FREE( pCache );
  898. }
  899. } // TsDereferenceCacheObj
  900. VOID
  901. TsDecreaseFileHandleCount(
  902. VOID
  903. )
  904. {
  905. ASSERT( CacheTable.OpenFileInUse != 0 );
  906. if ( CacheTable.OpenFileInUse )
  907. {
  908. InterlockedDecrement( (LONG*)&CacheTable.OpenFileInUse );
  909. }
  910. }
  911. VOID
  912. TsIncreaseFileHandleCount(
  913. BOOL fInCacheLock
  914. )
  915. {
  916. if ( (UINT)(pfnInterlockedExchangeAdd( (LONG*)&CacheTable.OpenFileInUse, 1) )
  917. >= CacheTable.MaxOpenFileInUse )
  918. {
  919. if ( !fInCacheLock )
  920. {
  921. EnterCriticalSection( &CacheTable.CriticalSection );
  922. }
  923. RemoveLruHandleCacheItem();
  924. if ( !fInCacheLock )
  925. {
  926. LeaveCriticalSection( &CacheTable.CriticalSection );
  927. }
  928. }
  929. }
  930. BOOL
  931. RemoveLruHandleCacheItem(
  932. VOID
  933. )
  934. /*++
  935. Routine Description:
  936. Remove the least recently used cached item referencing a file handle
  937. THE CACHE TABLE LOCK MUST BE TAKEN PRIOR TO CALLING THIS FUNCTION
  938. Arguments:
  939. None
  940. --*/
  941. {
  942. PLIST_ENTRY pEntry;
  943. for ( pEntry = CacheTable.MruList.Blink ;
  944. pEntry != &CacheTable.MruList ;
  945. pEntry = pEntry->Blink )
  946. {
  947. //
  948. // The least recently used entry is the one at the tail of the MRU
  949. // list.
  950. //
  951. PCACHE_OBJECT pCacheObject =
  952. CONTAINING_RECORD( pEntry,
  953. CACHE_OBJECT,
  954. MruList );
  955. PW3_URI_INFO pURI = (PW3_URI_INFO)(pCacheObject->pbhBlob+1);
  956. if ( (pCacheObject->iDemux == RESERVED_DEMUX_OPEN_FILE) ||
  957. (pCacheObject->iDemux == RESERVED_DEMUX_URI_INFO &&
  958. pURI->bFileInfoValid &&
  959. pURI->pOpenFileInfo != NULL) )
  960. {
  961. DeCache( pCacheObject, FALSE );
  962. IF_DEBUG( CACHE) {
  963. DBGPRINTF( ( DBG_CONTEXT,
  964. " Throwing out object ( %08x) to reduce file handle ref\n",
  965. pCacheObject));
  966. }
  967. return TRUE;
  968. }
  969. }
  970. return FALSE;
  971. } // RemoveLruCacheItem
  972. BOOL
  973. TsCacheQueryStatistics(
  974. IN DWORD Level,
  975. IN DWORD dwServerMask,
  976. IN INETA_CACHE_STATISTICS * pCacheCtrs
  977. )
  978. /*++
  979. Routine Description:
  980. This function returns the statistics for the global cache or for the
  981. individual services
  982. Arguments:
  983. Level - Only valid value is 0
  984. dwServerMask - Server mask to retrieve statistics for or 0 for the sum
  985. of the services
  986. pCacheCtrs - Receives the statistics for cache
  987. Notes:
  988. CacheBytesTotal and CacheBytesInUse are not kept on a per-server basis
  989. so they are only returned when retrieving summary statistics.
  990. Returns:
  991. TRUE on success, FALSE on failure
  992. --*/
  993. {
  994. if ( Level != 0 ||
  995. dwServerMask > LAST_PERF_CTR_SVC ||
  996. !pCacheCtrs )
  997. {
  998. SetLastError( ERROR_INVALID_PARAMETER );
  999. return FALSE;
  1000. }
  1001. if ( dwServerMask )
  1002. {
  1003. memcpy( pCacheCtrs,
  1004. &Configuration.Stats[ MaskIndex(dwServerMask) ],
  1005. sizeof( Configuration.Stats[ 0 ] ) );
  1006. }
  1007. else
  1008. {
  1009. //
  1010. // Add up all of the statistics
  1011. //
  1012. memset( pCacheCtrs, 0, sizeof( *pCacheCtrs ));
  1013. for ( int i = 0; i < MAX_PERF_CTR_SVCS; i++ )
  1014. {
  1015. DWORD index = MaskIndex( 1 << i );
  1016. pCacheCtrs->CurrentOpenFileHandles+= Configuration.Stats[index].CurrentOpenFileHandles;
  1017. pCacheCtrs->CurrentDirLists += Configuration.Stats[index].CurrentDirLists;
  1018. pCacheCtrs->CurrentObjects += Configuration.Stats[index].CurrentObjects;
  1019. pCacheCtrs->FlushesFromDirChanges += Configuration.Stats[index].FlushesFromDirChanges;
  1020. pCacheCtrs->CacheHits += Configuration.Stats[index].CacheHits;
  1021. pCacheCtrs->CacheMisses += Configuration.Stats[index].CacheMisses;
  1022. #if 0
  1023. pCacheCtrs->TotalSuccessGetSecDesc+= Configuration.Stats[index].TotalSuccessGetSecDesc;
  1024. pCacheCtrs->TotalFailGetSecDesc += Configuration.Stats[index].TotalFailGetSecDesc;
  1025. if ( pCacheCtrs->CurrentSizeSecDesc < Configuration.Stats[index].CurrentSizeSecDesc )
  1026. {
  1027. pCacheCtrs->CurrentSizeSecDesc = Configuration.Stats[index].CurrentSizeSecDesc;
  1028. }
  1029. pCacheCtrs->TotalAccessCheck += Configuration.Stats[index].TotalAccessCheck;
  1030. #endif
  1031. }
  1032. }
  1033. return TRUE;
  1034. }
  1035. BOOL
  1036. TsCacheClearStatistics(
  1037. IN DWORD dwServerMask
  1038. )
  1039. /*++
  1040. Routine Description:
  1041. Clears the the specified service's statistics
  1042. --*/
  1043. {
  1044. if ( dwServerMask > LAST_PERF_CTR_SVC )
  1045. {
  1046. SetLastError( ERROR_INVALID_PARAMETER );
  1047. return FALSE;
  1048. }
  1049. //
  1050. // Currently this function isn't supported
  1051. //
  1052. SetLastError( ERROR_NOT_SUPPORTED );
  1053. return FALSE;
  1054. } // TsCacheClearStatistics
  1055. BOOL
  1056. TsCacheFlush(
  1057. IN DWORD dwServerMask
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. This function flushes the cache of all items for the specified service
  1062. or for all services if dwServerMask is zero.
  1063. --*/
  1064. {
  1065. LIST_ENTRY * pEntry;
  1066. LIST_ENTRY * pNext;
  1067. if ( dwServerMask == 0 ) {
  1068. return(TRUE);
  1069. }
  1070. EnterCriticalSection( &CacheTable.CriticalSection );
  1071. for ( pEntry = CacheTable.MruList.Flink;
  1072. pEntry != &CacheTable.MruList;
  1073. )
  1074. {
  1075. pNext = pEntry->Flink;
  1076. PCACHE_OBJECT pCacheObject =
  1077. CONTAINING_RECORD( pEntry,
  1078. CACHE_OBJECT,
  1079. MruList );
  1080. if ( pCacheObject->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE ) {
  1081. pEntry = pNext;
  1082. continue;
  1083. }
  1084. if ( dwServerMask == pCacheObject->dwService ) {
  1085. DeCache( pCacheObject, FALSE );
  1086. IF_DEBUG( CACHE) {
  1087. DBGPRINTF( ( DBG_CONTEXT,
  1088. " Throwing out object ( %08x) due to manual flush\n",
  1089. pCacheObject));
  1090. }
  1091. }
  1092. pEntry = pNext;
  1093. }
  1094. LeaveCriticalSection( &CacheTable.CriticalSection );
  1095. return TRUE;
  1096. } // TsCacheFlush
  1097. BOOL
  1098. TsCacheFlushUser(
  1099. IN HANDLE hUserToken,
  1100. IN BOOL fDefer
  1101. )
  1102. /*++
  1103. Routine Description:
  1104. This function flushes all file handles associated the passed user context
  1105. Arguments:
  1106. hUserToken - User token to flush from the cache
  1107. fDefer - Build list but close handles later in worker thread (Not supported)
  1108. --*/
  1109. {
  1110. LIST_ENTRY * pEntry;
  1111. LIST_ENTRY * pNext;
  1112. ASSERT( !fDefer );
  1113. EnterCriticalSection( &CacheTable.CriticalSection );
  1114. for ( pEntry = CacheTable.MruList.Flink;
  1115. pEntry != &CacheTable.MruList;
  1116. )
  1117. {
  1118. pNext = pEntry->Flink;
  1119. PCACHE_OBJECT pCacheObject = CONTAINING_RECORD( pEntry,
  1120. CACHE_OBJECT,
  1121. MruList );
  1122. ASSERT( pCacheObject->Signature == CACHE_OBJ_SIGNATURE );
  1123. if ( pCacheObject->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE ) {
  1124. pEntry = pNext;
  1125. continue;
  1126. }
  1127. //
  1128. // Find all occurrences of the matching user token in the cache and
  1129. // decache them
  1130. //
  1131. if ( pCacheObject->iDemux == RESERVED_DEMUX_OPEN_FILE &&
  1132. ((TS_OPEN_FILE_INFO *)(pCacheObject->pbhBlob + 1))->
  1133. QueryOpeningUser() == hUserToken )
  1134. {
  1135. DeCache( pCacheObject, FALSE );
  1136. IF_DEBUG( CACHE) {
  1137. DBGPRINTF( ( DBG_CONTEXT,
  1138. " Throwing out object ( %08x) due to user token flush\n",
  1139. pCacheObject));
  1140. }
  1141. }
  1142. else if ( pCacheObject->iDemux == RESERVED_DEMUX_DIRECTORY_LISTING &&
  1143. ((TS_DIRECTORY_HEADER *)(pCacheObject->pbhBlob + 1))->
  1144. QueryListingUser() == hUserToken )
  1145. {
  1146. DeCache( pCacheObject, FALSE );
  1147. IF_DEBUG( CACHE) {
  1148. DBGPRINTF( ( DBG_CONTEXT,
  1149. " Throwing out object ( %08x) due to user token flush\n",
  1150. pCacheObject));
  1151. }
  1152. }
  1153. else if ( (pCacheObject->hLastSuccessAccessToken == hUserToken) )
  1154. {
  1155. //
  1156. // If security descriptor is present, simply cancel Last successful access token
  1157. // otherwise must decache cache object, as security check are entirely dependent
  1158. // on last successful access token in this case
  1159. //
  1160. if ( pCacheObject->pSecDesc )
  1161. {
  1162. pCacheObject->hLastSuccessAccessToken = NULL;
  1163. }
  1164. else
  1165. {
  1166. DeCache( pCacheObject, FALSE );
  1167. IF_DEBUG( CACHE) {
  1168. DBGPRINTF( ( DBG_CONTEXT,
  1169. " Throwing out object ( %08x) due to user token flush\n",
  1170. pCacheObject));
  1171. }
  1172. }
  1173. }
  1174. pEntry = pNext;
  1175. }
  1176. LeaveCriticalSection( &CacheTable.CriticalSection );
  1177. return TRUE;
  1178. } // TsCacheFlushUser
  1179. BOOL
  1180. TsCacheFlushDemux(
  1181. IN ULONG iDemux
  1182. )
  1183. /*++
  1184. Routine Description:
  1185. Flush all cache items whose demultiplexor matches that specified.
  1186. Arguments:
  1187. iDemux - Value of demux whose cache items are to be flushed.
  1188. --*/
  1189. {
  1190. LIST_ENTRY * pEntry;
  1191. LIST_ENTRY * pNext;
  1192. EnterCriticalSection( &CacheTable.CriticalSection );
  1193. for ( pEntry = CacheTable.MruList.Flink;
  1194. pEntry != &CacheTable.MruList;
  1195. )
  1196. {
  1197. pNext = pEntry->Flink;
  1198. PCACHE_OBJECT pCacheObject = CONTAINING_RECORD( pEntry,
  1199. CACHE_OBJECT,
  1200. MruList );
  1201. ASSERT( pCacheObject->Signature == CACHE_OBJ_SIGNATURE );
  1202. if ( pCacheObject->iDemux == iDemux )
  1203. {
  1204. DeCache( pCacheObject, FALSE );
  1205. IF_DEBUG( CACHE) {
  1206. DBGPRINTF( ( DBG_CONTEXT,
  1207. " Throwing out object ( %08x) due to demux flush\n",
  1208. pCacheObject));
  1209. }
  1210. //
  1211. // The last Decache may have thrown out the next entry.
  1212. // Since this is just a shutdown path restart the scan
  1213. // of the list from the beginning
  1214. //
  1215. pEntry = CacheTable.MruList.Flink;
  1216. continue;
  1217. }
  1218. pEntry = pNext;
  1219. }
  1220. LeaveCriticalSection( &CacheTable.CriticalSection );
  1221. return TRUE;
  1222. } // TsCacheFlushDemux
  1223. VOID
  1224. TsFlushURL(
  1225. IN const TSVC_CACHE &TSvcCache,
  1226. IN PCSTR pszURL,
  1227. IN DWORD dwURLLength,
  1228. IN ULONG iDemultiplexor
  1229. )
  1230. /*++
  1231. Routine Description:
  1232. This routine takes as input a URL and removes from the cache all cached
  1233. objects that have the input URL as their prefix. This is mostly called
  1234. when we get a change notify for metadata.
  1235. Arguments
  1236. TSvcCache - Service cache
  1237. pszURL - The URL prefix to be flushed.
  1238. iDemultiplexor - The demultiplexor for the caller's entries.
  1239. Returns
  1240. Nothing
  1241. --*/
  1242. {
  1243. PLIST_ENTRY pEntry;
  1244. PLIST_ENTRY pNext;
  1245. LIST_ENTRY ListHead;
  1246. PCACHE_OBJECT pCacheObject;
  1247. BOOL bIsRoot;
  1248. // The basic algorithm is to lock the cache table, then walk the cache
  1249. // table looking for matches and decaching those. This could get
  1250. // expensive if the table is big and this routine is called frequently -
  1251. // in that case we may need to schedule the decaches for later, or
  1252. // periodically free and reaquire the critical section.
  1253. InitializeListHead( &ListHead );
  1254. if (!memcmp(pszURL, "/", sizeof("/")))
  1255. {
  1256. bIsRoot = TRUE;
  1257. }
  1258. else
  1259. {
  1260. bIsRoot = FALSE;
  1261. }
  1262. EnterCriticalSection( &CacheTable.CriticalSection );
  1263. pEntry = CacheTable.MruList.Flink;
  1264. while (pEntry != &CacheTable.MruList)
  1265. {
  1266. pNext = pEntry->Flink;
  1267. pCacheObject = CONTAINING_RECORD( pEntry, CACHE_OBJECT, MruList );
  1268. ASSERT( pCacheObject->Signature == CACHE_OBJ_SIGNATURE );
  1269. // Check this cache object to see if it matches.
  1270. if ( pCacheObject->iDemux == iDemultiplexor &&
  1271. pCacheObject->dwService == TSvcCache.GetServiceId() &&
  1272. pCacheObject->dwInstance == TSvcCache.GetInstanceId() &&
  1273. (bIsRoot ? TRUE : (
  1274. !_mbsnbicmp( (PUCHAR)pCacheObject->szPath, (PUCHAR)pszURL, dwURLLength) &&
  1275. (pCacheObject->szPath[dwURLLength] == '/' ||
  1276. pCacheObject->szPath[dwURLLength] == '\0')))
  1277. )
  1278. {
  1279. if ( !RemoveCacheObjFromLists( pCacheObject, FALSE ) ) {
  1280. ASSERT( FALSE );
  1281. continue;
  1282. }
  1283. InsertTailList( &ListHead, &pCacheObject->DirChangeList );
  1284. IF_DEBUG( CACHE)
  1285. {
  1286. DBGPRINTF( ( DBG_CONTEXT,
  1287. " Throwing cache object ( %08x) out of cache because of URL match\n",
  1288. pCacheObject));
  1289. }
  1290. }
  1291. pEntry = pNext;
  1292. }
  1293. for ( pEntry = ListHead.Flink;
  1294. pEntry != &ListHead;
  1295. pEntry = pNext ) {
  1296. pNext = pEntry->Flink;
  1297. pCacheObject = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
  1298. ASSERT( pCacheObject->Signature == CACHE_OBJ_SIGNATURE );
  1299. TsDereferenceCacheObj( pCacheObject, FALSE );
  1300. }
  1301. LeaveCriticalSection( &CacheTable.CriticalSection );
  1302. }