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.

1594 lines
36 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1998 **/
  4. /**********************************************************************/
  5. /*
  6. tsunami.cxx
  7. This module contains most of the public Tsunami Cache routines.
  8. FILE HISTORY:
  9. MCourage 09-Dec-1997 Created
  10. */
  11. #include <tsunami.hxx>
  12. #include <inetinfo.h>
  13. #include <issched.hxx>
  14. #include "inetreg.h"
  15. #include "globals.hxx"
  16. #include "tsunamip.hxx"
  17. #include <inetsvcs.h>
  18. #include "metacach.hxx"
  19. #include "filecach.hxx"
  20. #include "blobcach.hxx"
  21. #include "atq.h"
  22. #include "tracelog.h"
  23. #include <lkrhash.h>
  24. #include "filehash.hxx"
  25. #include "blobhash.hxx"
  26. #include "tlcach.h"
  27. #include "etagmb.h"
  28. BOOL g_fCacheSecDesc = TRUE;
  29. //
  30. // from TsInit.cxx
  31. //
  32. HANDLE g_hQuit = NULL;
  33. HANDLE g_hNewItem = NULL;
  34. BOOL g_fW3OnlyNoAuth = FALSE;
  35. BOOL TsNoDirOpenSupport = FALSE;
  36. #if TSUNAMI_REF_DEBUG
  37. PTRACE_LOG RefTraceLog;
  38. #endif // TSUNAMI_REF_DEBUG
  39. //
  40. // The TTL to scavenge the cache and the id of the scheduled work item of the
  41. // next scheduled scavenge
  42. //
  43. DWORD g_cmsecObjectCacheTTL = (INETA_DEF_OBJECT_CACHE_TTL * 1000);
  44. DWORD g_dwObjectCacheCookie = 0;
  45. # define MIN_CACHE_SCAVENGE_TIME (5*1000) // 5 seconds
  46. //
  47. // Disables Tsunami Caching
  48. //
  49. BOOL DisableTsunamiCaching = FALSE;
  50. //
  51. // DisableSPUD
  52. //
  53. BOOL DisableSPUD = FALSE;
  54. //
  55. // Allows us to mask the invalid flags
  56. //
  57. DWORD TsValidCreateFileOptions = TS_IIS_VALID_FLAGS;
  58. //
  59. // from globals.cxx
  60. //
  61. CONFIGURATION Configuration;
  62. BOOL g_fDisableCaching = FALSE;
  63. //
  64. // Initialization and cleanup
  65. //
  66. BOOL
  67. Tsunami_Initialize(
  68. VOID
  69. )
  70. /*++
  71. Routine Description:
  72. Sets up all the core caches. Call this before using any
  73. cache routines.
  74. Arguments:
  75. None.
  76. Return Values:
  77. TRUE on success
  78. --*/
  79. {
  80. HRESULT hr;
  81. HKEY hKey;
  82. DWORD dwType;
  83. DWORD nBytes;
  84. DWORD dwValue;
  85. DWORD dwMaxFile;
  86. DWORD err;
  87. #if TSUNAMI_REF_DEBUG
  88. RefTraceLog = CreateRefTraceLog(
  89. 256, // LogSize
  90. 0 // ExtraBytesInHeader
  91. );
  92. #endif // TSUNAMI_REF_DEBUG
  93. //
  94. // Initialize global events
  95. //
  96. g_hQuit = IIS_CREATE_EVENT(
  97. "g_hQuit",
  98. &g_hQuit,
  99. TRUE,
  100. FALSE
  101. );
  102. g_hNewItem = IIS_CREATE_EVENT(
  103. "g_hNewItem",
  104. &g_hNewItem,
  105. FALSE,
  106. FALSE
  107. );
  108. if ( (g_hQuit == NULL) || (g_hNewItem == NULL) ) {
  109. goto Failure;
  110. }
  111. //
  112. // Set defaults
  113. //
  114. MEMORYSTATUS ms;
  115. ms.dwLength = sizeof(MEMORYSTATUS);
  116. GlobalMemoryStatus( &ms );
  117. //
  118. // default is 1K files per 32MB of physical memory after the 1st 8MB,
  119. // minimum INETA_MIN_DEF_FILE_HANDLE
  120. //
  121. if ( ms.dwTotalPhys > 8 * 1024 * 1024 )
  122. {
  123. dwMaxFile = (DWORD)(ms.dwTotalPhys - 8 * 1024 * 1024) / ( 32 * 1024 );
  124. if ( dwMaxFile < INETA_MIN_DEF_FILE_HANDLE )
  125. {
  126. dwMaxFile = INETA_MIN_DEF_FILE_HANDLE;
  127. }
  128. }
  129. else
  130. {
  131. dwMaxFile = INETA_MIN_DEF_FILE_HANDLE;
  132. }
  133. //
  134. // If this is not a NTS, disable tsunami caching by default
  135. //
  136. DisableSPUD = !AtqSpudInitialized();
  137. if ( !TsIsNtServer() ) {
  138. DisableTsunamiCaching = TRUE;
  139. DisableSPUD = TRUE;
  140. }
  141. DisableSPUD = FALSE;
  142. //
  143. // no overlapped i/o in win95.
  144. //
  145. if ( TsIsWindows95() ) {
  146. TsCreateFileFlags = FILE_FLAG_SEQUENTIAL_SCAN;
  147. TsCreateFileShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
  148. TsNoDirOpenSupport = TRUE;
  149. // |FILE_FLAG_BACKUP_SEMANTICS;
  150. }
  151. //
  152. // Read the registry key to see whether tsunami caching is enabled
  153. //
  154. err = RegOpenKeyEx(
  155. HKEY_LOCAL_MACHINE,
  156. INETA_PARAMETERS_KEY,
  157. 0,
  158. KEY_READ,
  159. &hKey
  160. );
  161. if ( err == ERROR_SUCCESS ) {
  162. //
  163. // This cannot be overridded in win95
  164. //
  165. if ( !TsIsWindows95() ) {
  166. nBytes = sizeof(dwValue);
  167. err = RegQueryValueEx(
  168. hKey,
  169. INETA_DISABLE_TSUNAMI_CACHING,
  170. NULL,
  171. &dwType,
  172. (LPBYTE)&dwValue,
  173. &nBytes
  174. );
  175. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  176. DisableTsunamiCaching = (BOOL)dwValue;
  177. }
  178. nBytes = sizeof(dwValue);
  179. err = RegQueryValueEx(
  180. hKey,
  181. INETA_DISABLE_TSUNAMI_SPUD,
  182. NULL,
  183. &dwType,
  184. (LPBYTE)&dwValue,
  185. &nBytes
  186. );
  187. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  188. DisableSPUD = (BOOL)dwValue;
  189. if ( DisableSPUD ) {
  190. DbgPrint("DisableCacheOplocks set to TRUE in Registry.\n");
  191. } else {
  192. DbgPrint("DisableCacheOplocks set to FALSE in Registry.\n");
  193. }
  194. DbgPrint("The Registry Setting will override the default.\n");
  195. }
  196. //
  197. // How big do files have to be before we stop caching them
  198. //
  199. err = RegQueryValueEx(
  200. hKey,
  201. INETA_MAX_CACHED_FILE_SIZE,
  202. NULL,
  203. &dwType,
  204. (LPBYTE)&dwValue,
  205. &nBytes
  206. );
  207. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  208. g_liFileCacheByteThreshold.LowPart = dwValue;
  209. } else {
  210. g_liFileCacheByteThreshold.LowPart = INETA_DEF_MAX_CACHED_FILE_SIZE;
  211. }
  212. g_liFileCacheByteThreshold.HighPart = 0; // Sorry Mr. > 4gb file.
  213. //
  214. // How big is the memory cache in megabytes
  215. //
  216. err = RegQueryValueEx(
  217. hKey,
  218. INETA_MEM_CACHE_SIZE,
  219. NULL,
  220. &dwType,
  221. (LPBYTE)&dwValue,
  222. &nBytes
  223. );
  224. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  225. //
  226. // set the size in megabytes
  227. //
  228. g_dwMemCacheSize = dwValue * (1024 * 1024);
  229. } else {
  230. g_dwMemCacheSize = INETA_DEF_MEM_CACHE_SIZE;
  231. }
  232. //
  233. // Do we use the sequential read flag to read files?
  234. //
  235. err = RegQueryValueEx(
  236. hKey,
  237. INETA_ENABLE_SEQUENTIAL_READ,
  238. NULL,
  239. &dwType,
  240. (LPBYTE)&dwValue,
  241. &nBytes
  242. );
  243. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  244. g_bEnableSequentialRead = dwValue ? 1 : 0;
  245. } else {
  246. g_bEnableSequentialRead = INETA_DEF_ENABLE_SEQUENTIAL_READ;
  247. }
  248. }
  249. if ( g_fW3OnlyNoAuth )
  250. {
  251. //
  252. // TODO: investigate is security descriptor caching
  253. // can be used in the non-SYSTEM account case.
  254. //
  255. g_fCacheSecDesc = FALSE;
  256. }
  257. else
  258. {
  259. //
  260. // read the enable cache sec desc flag
  261. //
  262. nBytes = sizeof(dwValue);
  263. err = RegQueryValueEx(
  264. hKey,
  265. INETA_CACHE_USE_ACCESS_CHECK,
  266. NULL,
  267. &dwType,
  268. (LPBYTE)&dwValue,
  269. &nBytes
  270. );
  271. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  272. g_fCacheSecDesc = !!dwValue;
  273. }
  274. else {
  275. g_fCacheSecDesc = INETA_DEF_CACHE_USE_ACCESS_CHECK;
  276. }
  277. }
  278. //
  279. // Read the maximum # of files in cache
  280. //
  281. nBytes = sizeof(dwValue);
  282. if ( RegQueryValueEx(
  283. hKey,
  284. INETA_MAX_OPEN_FILE,
  285. NULL,
  286. &dwType,
  287. (LPBYTE) &dwValue,
  288. &nBytes
  289. ) == ERROR_SUCCESS && dwType == REG_DWORD )
  290. {
  291. dwMaxFile = dwValue;
  292. }
  293. RegCloseKey( hKey );
  294. }
  295. //
  296. // if tsunami caching is disabled, set the flags accordingly
  297. //
  298. if ( DisableTsunamiCaching ) {
  299. g_fDisableCaching = TRUE;
  300. TsValidCreateFileOptions = TS_PWS_VALID_FLAGS;
  301. g_fCacheSecDesc = FALSE;
  302. }
  303. //
  304. // Initialize the ETag Metabase Change Number
  305. //
  306. hr = ETagChangeNumber::Create();
  307. if ( FAILED(hr) ) {
  308. goto Failure;
  309. }
  310. //
  311. // Initialize the directory change manager
  312. //
  313. if ( !DcmInitialize( ) ) {
  314. goto Failure;
  315. }
  316. //
  317. // Initialize the tsunami cache manager
  318. //
  319. if ( !FileCache_Initialize( dwMaxFile )) {
  320. goto Failure;
  321. }
  322. if ( !MetaCache_Initialize() ) {
  323. goto Failure;
  324. }
  325. if ( !BlobCache_Initialize() ) {
  326. goto Failure;
  327. }
  328. return( TRUE );
  329. Failure:
  330. IIS_PRINTF( ( buff, "Tsunami_Initialize() Failed. Error = %d\n",
  331. GetLastError()));
  332. if ( g_hQuit )
  333. {
  334. CloseHandle( g_hQuit );
  335. g_hQuit = NULL;
  336. }
  337. if ( g_hNewItem )
  338. {
  339. CloseHandle( g_hNewItem );
  340. g_hNewItem = NULL;
  341. }
  342. return FALSE;
  343. } // Tsunami_Initialize
  344. VOID
  345. Tsunami_Terminate(
  346. VOID
  347. )
  348. /*++
  349. Routine Description:
  350. Cleans up all the core caches.
  351. Arguments:
  352. None.
  353. Return Values:
  354. None.
  355. --*/
  356. {
  357. DWORD dwResult;
  358. if ( !SetEvent( g_hQuit ) ) {
  359. IIS_PRINTF((buff,
  360. "No Quit event posted for Tsunami. No Cleanup\n"));
  361. return;
  362. }
  363. //
  364. // Flush all items from the cache
  365. //
  366. TsCacheFlush( 0 );
  367. //
  368. // Synchronize with our thread so we don't leave here before the
  369. // thread has finished cleaning up
  370. //
  371. CloseHandle( g_hQuit );
  372. CloseHandle( g_hNewItem );
  373. BlobCache_Terminate();
  374. MetaCache_Terminate();
  375. FileCache_Terminate();
  376. DcmTerminate();
  377. ETagChangeNumber::Destroy();
  378. #if TSUNAMI_REF_DEBUG
  379. if( RefTraceLog != NULL ) {
  380. DestroyRefTraceLog( RefTraceLog );
  381. RefTraceLog = NULL;
  382. }
  383. #endif // TSUNAMI_REF_DEBUG
  384. } // Tsunami_Terminate
  385. //
  386. // Scavenger routines
  387. //
  388. BOOL
  389. FileFlushFilterTTL(
  390. TS_OPEN_FILE_INFO * pFileInfo,
  391. PVOID pv
  392. )
  393. {
  394. if (pFileInfo->GetIORefCount()) {
  395. //
  396. // Try not to time out entries which are in use for I/O.
  397. //
  398. return FALSE;
  399. }
  400. if (pFileInfo->GetTTL() == 0) {
  401. pFileInfo->TraceCheckpointEx(TS_MAGIC_TIMEOUT, 0, 0);
  402. return TRUE;
  403. } else {
  404. if (pFileInfo->IsInitialized()) {
  405. pFileInfo->DecrementTTL();
  406. }
  407. return FALSE;
  408. }
  409. }
  410. BOOL
  411. BlobFlushFilterTTL(
  412. PBLOB_HEADER pBlob,
  413. PVOID pv
  414. )
  415. {
  416. if (pBlob->TTL == 0) {
  417. pBlob->TraceCheckpointEx(TS_MAGIC_TIMEOUT, 0, 0);
  418. return TRUE;
  419. } else {
  420. pBlob->TTL--;
  421. return FALSE;
  422. }
  423. }
  424. VOID
  425. WINAPI
  426. CacheScavenger(
  427. VOID * pContext
  428. )
  429. {
  430. FilteredFlushFileCache(FileFlushFilterTTL, NULL);
  431. FilteredFlushBlobCache(BlobFlushFilterTTL, NULL);
  432. }
  433. BOOL
  434. InitializeCacheScavenger(
  435. VOID
  436. )
  437. /*++
  438. Routine Description:
  439. This function kicks off the scheduled tsunami object cache scavenger
  440. Arguments:
  441. None.
  442. Return Values:
  443. TRUE on success
  444. --*/
  445. {
  446. HKEY hkey;
  447. //
  448. // Schedule a scavenger to close all of the objects that haven't been
  449. // referenced in the last ttl
  450. //
  451. if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  452. INETA_PARAMETERS_KEY,
  453. 0,
  454. KEY_READ,
  455. &hkey ))
  456. {
  457. DWORD dwType;
  458. DWORD nBytes;
  459. DWORD dwValue;
  460. nBytes = sizeof(dwValue);
  461. if ( RegQueryValueEx(
  462. hkey,
  463. INETA_OBJECT_CACHE_TTL,
  464. NULL,
  465. &dwType,
  466. (LPBYTE) &dwValue,
  467. &nBytes
  468. ) == ERROR_SUCCESS && dwType == REG_DWORD )
  469. {
  470. g_cmsecObjectCacheTTL = dwValue;
  471. } else {
  472. g_cmsecObjectCacheTTL = 0;
  473. }
  474. //
  475. // Don't schedule anything if the scavenger should be disabled
  476. //
  477. if ( g_cmsecObjectCacheTTL == 0xffffffff )
  478. {
  479. RegCloseKey( hkey );
  480. return TRUE;
  481. }
  482. //
  483. // The registry setting is in seconds, convert to milliseconds
  484. //
  485. g_cmsecObjectCacheTTL *= 1000;
  486. //
  487. // Supply the default if no value was specified
  488. //
  489. if ( !g_cmsecObjectCacheTTL )
  490. {
  491. g_cmsecObjectCacheTTL = INETA_DEF_OBJECT_CACHE_TTL * 1000;
  492. }
  493. RegCloseKey( hkey );
  494. }
  495. //
  496. // Require a minimum of thirty seconds
  497. //
  498. g_cmsecObjectCacheTTL = max( g_cmsecObjectCacheTTL,
  499. MIN_CACHE_SCAVENGE_TIME );
  500. g_dwObjectCacheCookie = ScheduleWorkItem(
  501. CacheScavenger,
  502. NULL,
  503. g_cmsecObjectCacheTTL,
  504. TRUE ); // Periodic
  505. if ( !g_dwObjectCacheCookie )
  506. {
  507. return FALSE;
  508. }
  509. return TRUE;
  510. }
  511. VOID
  512. TerminateCacheScavenger(
  513. VOID
  514. )
  515. /*++
  516. Routine Description:
  517. Stops the cache scavenger
  518. Arguments:
  519. None.
  520. Return Values:
  521. None.
  522. --*/
  523. {
  524. if ( g_dwObjectCacheCookie )
  525. {
  526. RemoveWorkItem( g_dwObjectCacheCookie );
  527. g_dwObjectCacheCookie = 0;
  528. }
  529. }
  530. //
  531. // Blob memory management
  532. //
  533. BOOL
  534. TsAllocate(
  535. IN const TSVC_CACHE &TSvcCache,
  536. IN ULONG cbSize,
  537. IN OUT PVOID * ppvNewBlock
  538. )
  539. {
  540. return( TsAllocateEx( TSvcCache,
  541. cbSize,
  542. ppvNewBlock,
  543. NULL ) );
  544. }
  545. BOOL
  546. TsAllocateEx(
  547. IN const TSVC_CACHE &TSvcCache,
  548. IN ULONG cbSize,
  549. IN OUT PVOID * ppvNewBlock,
  550. OPTIONAL PUSER_FREE_ROUTINE pfnFreeRoutine
  551. )
  552. /*++
  553. Routine Description:
  554. This function allocates a memory block for the calling server.
  555. The returned block is suitable for use as a parameter to
  556. TsCacheDirectoryBlob(). Blocks allocated by this function
  557. must either be cached or freed with TsFree(). Freeing of
  558. cached blocks will be handled by the cache manager.
  559. Anything allocated with this routine MUST be derived from
  560. BLOB_HEADER!
  561. Arguments:
  562. TSvcCache - An initialized TSVC_CACHE structure.
  563. cbSize - Number of bytes to allocate. (Must be strictly
  564. greater than zero.)
  565. ppvNewBlock - Address of a pointer to store the new block's
  566. address in.
  567. pfnFreeRoutine - pointer to a routine that will be called to
  568. clean up the block when it is decached.
  569. Return Value:
  570. TRUE - The allocation succeeded, and *ppvNewBlock points to
  571. at least cbSize accessable bytes.
  572. FALSE - The allocation failed.
  573. --*/
  574. {
  575. CBlobKey * pBlobKey;
  576. PBLOB_HEADER pbhNewBlock;
  577. DBG_ASSERT( cbSize > 0 );
  578. DBG_ASSERT( ppvNewBlock != NULL );
  579. //
  580. // allocate the blob and the key while we're at it.
  581. //
  582. pBlobKey = (CBlobKey *) ALLOC(cbSize + sizeof(CBlobKey));
  583. if ( pBlobKey != NULL )
  584. {
  585. //
  586. // If the allocation succeeded, we return a pointer to
  587. // the new structure which is directly preceded by it's key.
  588. //
  589. pbhNewBlock = (PBLOB_HEADER) (pBlobKey + 1);
  590. *ppvNewBlock = ( PVOID )( pbhNewBlock );
  591. //
  592. // Set up the BLOB_HEADER: Normal flags and stored allocation
  593. // size.
  594. //
  595. pbhNewBlock->Signature = TS_BLOB_SIGNATURE;
  596. pbhNewBlock->pBlobKey = pBlobKey;
  597. pbhNewBlock->IsCached = FALSE;
  598. pbhNewBlock->pfnFreeRoutine = pfnFreeRoutine;
  599. pbhNewBlock->lRefCount = 0;
  600. pbhNewBlock->TTL = 1;
  601. pbhNewBlock->pSecDesc = NULL;
  602. pbhNewBlock->hLastSuccessAccessToken = INVALID_HANDLE_VALUE;
  603. pBlobKey->m_pszPathName = NULL;
  604. pBlobKey->m_cbPathName = 0;
  605. pBlobKey->m_dwService = TSvcCache.GetServiceId();
  606. pBlobKey->m_dwInstance = TSvcCache.GetInstanceId();
  607. pBlobKey->m_dwDemux = 0;
  608. pbhNewBlock->TraceCheckpointEx(TS_MAGIC_ALLOCATE, (PVOID) (ULONG_PTR) cbSize, pfnFreeRoutine);
  609. }
  610. else
  611. {
  612. //
  613. // The allocation failed, and we need to return NULL
  614. //
  615. *ppvNewBlock = NULL;
  616. return FALSE;
  617. }
  618. return TRUE;
  619. }
  620. BOOL
  621. TsFree(
  622. IN const TSVC_CACHE &TSvcCache,
  623. IN PVOID pvOldBlock
  624. )
  625. /*++
  626. Routine Description:
  627. This function frees a memory block allocated with TsAllocate().
  628. Blocks that are currently cached cannot be freed with this
  629. function.
  630. Arguments:
  631. TSvcCache - An initialized TSVC_CACHE structure.
  632. pvOldBlock - The address of the block to free. (Must be
  633. non-NULL.)
  634. Return Value:
  635. TRUE - The block was freed. The pointer pvOldBlock is no longer
  636. valid.
  637. FALSE - The block was not freed. Possible reasons include:
  638. - pvOldBlock does not point to a block allocated with
  639. TsAllocate().
  640. - pvOldBlock points to a block that has been cached
  641. with CacheDirectoryBlob().
  642. - pServiceInfo does not point to a valid SERVICE_INFO
  643. structure.
  644. --*/
  645. {
  646. BOOL bSuccess;
  647. PBLOB_HEADER pbhOldBlock;
  648. CBlobKey * pRealOldBlock;
  649. DBG_ASSERT( pvOldBlock != NULL );
  650. //
  651. // Adjust the input pointer to refer to the BLOB_HEADER.
  652. //
  653. pbhOldBlock = (( PBLOB_HEADER )pvOldBlock );
  654. DBG_ASSERT( TS_BLOB_SIGNATURE == pbhOldBlock->Signature );
  655. //
  656. // Track memory corruption in free builds.
  657. //
  658. if ( TS_BLOB_SIGNATURE != pbhOldBlock->Signature ) {
  659. SetLastError( ERROR_INVALID_PARAMETER );
  660. return FALSE;
  661. }
  662. //
  663. // If the Blob is currently in the cache, we can't free it.
  664. // Check for this in the Blob's flags, and fail if it
  665. // occurs.
  666. //
  667. if ( pbhOldBlock->IsCached )
  668. {
  669. DBGPRINTF(( DBG_CONTEXT,
  670. "A service (%d) has attempted to TsFree a BLOB that it put in the cache.",
  671. TSvcCache.GetServiceId() ));
  672. BREAKPOINT();
  673. bSuccess = FALSE;
  674. }
  675. else
  676. {
  677. pbhOldBlock->Signature = TS_FREE_BLOB_SIGNATURE;
  678. if ( pbhOldBlock->pfnFreeRoutine )
  679. {
  680. bSuccess = pbhOldBlock->pfnFreeRoutine( pvOldBlock );
  681. }
  682. else
  683. {
  684. bSuccess = TRUE;
  685. }
  686. if ( bSuccess )
  687. {
  688. //
  689. // Free the memory used by the Blob.
  690. //
  691. pRealOldBlock = ((CBlobKey *) pvOldBlock) - 1;
  692. DBG_ASSERT( NULL == pRealOldBlock->m_pszPathName );
  693. pbhOldBlock->TraceCheckpointEx(TS_MAGIC_DELETE_NC,
  694. (PVOID) (ULONG_PTR) (pRealOldBlock->m_dwDemux),
  695. pbhOldBlock->pfnFreeRoutine);
  696. bSuccess = !!FREE( pRealOldBlock );
  697. /*
  698. DEC_COUNTER( TSvcCache.GetServiceId(),
  699. CurrentObjects );
  700. */
  701. }
  702. }
  703. return( bSuccess );
  704. } // TsFree
  705. //
  706. // Standard cache operations
  707. //
  708. BOOL
  709. TsCacheDirectoryBlob(
  710. IN const TSVC_CACHE &TSvcCache,
  711. IN PCSTR pszDirectoryName,
  712. IN ULONG cchDirectoryName,
  713. IN ULONG iDemultiplexor,
  714. IN PVOID pvBlob,
  715. IN BOOLEAN bKeepCheckedOut,
  716. IN PSECURITY_DESCRIPTOR pSecDesc
  717. )
  718. /*++
  719. Routine Description:
  720. This function associates the Blob given as input with the specified
  721. directory and demultiplexing number. Services should use this
  722. function to add a Blob to the cache.
  723. Callers must not cache the same Blob twice. Once a Blob is cached,
  724. its contents must not be modified, and it must not be freed or re-cached.
  725. Arguments:
  726. TSvcCache - An initialized TSVC_CACHE structure.
  727. pszDirectoryName - The name that will be used as a key in the cache.
  728. iDemultiplexor - Identifies the type of the object to be stored
  729. pvBlob - Pointer to the actual object to be stored
  730. bKeepCheckedOut - If TRUE, the caller can keep a reference to the cached object.
  731. pSecDesc - An optional SECURITY_DESCRIPTOR that goes along with the object
  732. Return Values:
  733. TRUE - The block successfully added to the cache
  734. FALSE - The block could not be added to the cache
  735. --*/
  736. {
  737. BOOL bSuccess;
  738. PBLOB_HEADER pBlob = (PBLOB_HEADER)pvBlob;
  739. DBG_ASSERT( TS_BLOB_SIGNATURE == pBlob->Signature );
  740. //
  741. // set up the key
  742. //
  743. CBlobKey * pbk = pBlob->pBlobKey;
  744. DBG_ASSERT( NULL != pbk );
  745. pbk->m_cbPathName = cchDirectoryName;
  746. pbk->m_pszPathName = (PCHAR) ALLOC(pbk->m_cbPathName + 1);
  747. if (NULL != pbk->m_pszPathName) {
  748. memcpy(pbk->m_pszPathName, pszDirectoryName, pbk->m_cbPathName + 1);
  749. } else {
  750. pbk->m_cbPathName = 0;
  751. pbk->m_pszPathName = NULL;
  752. return FALSE;
  753. }
  754. IISstrupr( (PUCHAR)pbk->m_pszPathName );
  755. pbk->m_dwService = TSvcCache.GetServiceId();
  756. pbk->m_dwInstance = TSvcCache.GetInstanceId();
  757. pbk->m_dwDemux = iDemultiplexor;
  758. //
  759. // try to cache
  760. //
  761. bSuccess = CacheBlob(pBlob);
  762. if (bSuccess && !bKeepCheckedOut) {
  763. CheckinBlob(pBlob);
  764. }
  765. if (!bSuccess) {
  766. FREE(pbk->m_pszPathName);
  767. pbk->m_pszPathName = NULL;
  768. pbk->m_cbPathName = 0;
  769. }
  770. return bSuccess;
  771. } // TsCacheDirectoryBlob
  772. BOOL
  773. TsDeCacheCachedBlob(
  774. PVOID pBlobPayload
  775. )
  776. /*++
  777. Description:
  778. This function removes a blob payload object from the cache
  779. Arguments:
  780. pCacheObject - Object to decache
  781. Return Values:
  782. TRUE on success
  783. --*/
  784. {
  785. DecacheBlob( (PBLOB_HEADER)pBlobPayload );
  786. return TRUE;
  787. }
  788. BOOL
  789. TsCheckOutCachedBlob(
  790. IN const TSVC_CACHE &TSvcCache,
  791. IN PCSTR pszDirectoryName,
  792. IN ULONG cchDirectoryName,
  793. IN ULONG iDemultiplexor,
  794. IN PVOID * ppvBlob,
  795. IN HANDLE ,
  796. IN BOOL ,
  797. IN PSECURITY_DESCRIPTOR* )
  798. /*++
  799. Routine Description:
  800. Searches the cache for a named cache entry. If the entry is found,
  801. it is checked out and returned to the caller.
  802. Arguments:
  803. TSvcCache - An initialized TSVC_CACHE structure.
  804. pszDirectoryName - The name used as a key in the cache.
  805. iDemultiplexor - Identifies the type of the object to be stored
  806. ppvBlob - If the entry is found, a pointer to it will be
  807. placed here.
  808. hAccessToken - Optional parameter used to determine if the
  809. caller is allowed to access the cached object.
  810. fMayCacheAccessToken - If this is TRUE, and the caller succesfully gains
  811. access to the cached object, the hAccessToken will
  812. be saved with the object in the cache.
  813. ppSecDesc - If this is non-NULL, the caller will be given a
  814. copy of the objects security descriptor.
  815. Return Values:
  816. None.
  817. --*/
  818. {
  819. CHAR achUpName[MAX_PATH+1];
  820. BOOL bSuccess;
  821. // People really do use this.
  822. // DBG_ASSERT( ppSecDesc == NULL );
  823. //
  824. // Make sure the path is upper case
  825. //
  826. IISstrncpy(achUpName, pszDirectoryName, MAX_PATH);
  827. achUpName[MAX_PATH] = 0;
  828. cchDirectoryName = min(cchDirectoryName, MAX_PATH);
  829. IISstrupr( reinterpret_cast<PUCHAR>(achUpName) );
  830. bSuccess = CheckoutBlob(achUpName,
  831. cchDirectoryName,
  832. TSvcCache.GetServiceId(),
  833. TSvcCache.GetInstanceId(),
  834. iDemultiplexor,
  835. (PBLOB_HEADER *) ppvBlob);
  836. if (bSuccess) {
  837. //
  838. // Security handled by the caller
  839. //
  840. ((PBLOB_HEADER)*ppvBlob)->TTL = 1;
  841. return TRUE;
  842. } else {
  843. return FALSE;
  844. }
  845. }
  846. BOOL
  847. TsCheckInCachedBlob(
  848. IN PVOID pvBlob
  849. )
  850. /*++
  851. Routine Description:
  852. When a client is done with a blob it must check it back into the cache.
  853. Arguments:
  854. pvBlob - The object to be checked in
  855. Return Values:
  856. TRUE for success
  857. --*/
  858. {
  859. CheckinBlob((PBLOB_HEADER) pvBlob);
  860. return( TRUE );
  861. } // TsCheckInCachedBlob
  862. BOOL
  863. TsCheckInOrFree(
  864. IN PVOID pvOldBlock
  865. )
  866. /*++
  867. Routine Description:
  868. This function checks in a cached memory block or
  869. frees a non-cached memory block allocated with TsAllocate().
  870. Arguments:
  871. pvOldBlock - The address of the block to free. (Must be
  872. non-NULL.)
  873. Return Value:
  874. TRUE - The block was freed. The pointer pvOldBlock is no longer
  875. valid.
  876. FALSE - The block was not freed. Possible reasons include:
  877. - pvOldBlock does not point to a block allocated with
  878. TsAllocate().
  879. --*/
  880. {
  881. PBLOB_HEADER pBlob = (PBLOB_HEADER) pvOldBlock;
  882. TSVC_CACHE dummy;
  883. if (pBlob->IsCached) {
  884. CheckinBlob(pBlob);
  885. } else {
  886. TsFree(dummy, (PVOID)pBlob);
  887. }
  888. return( TRUE );
  889. } // TsCheckInOrFree
  890. BOOL
  891. TsCacheFlushDemux(
  892. IN ULONG iDemux
  893. )
  894. /*++
  895. Routine Description:
  896. Flush all cache items whose demultiplexor matches that specified.
  897. Arguments:
  898. iDemux - Value of demux whose cache items are to be flushed.
  899. --*/
  900. {
  901. if (RESERVED_DEMUX_OPEN_FILE == iDemux) {
  902. FlushFileCache();
  903. } else {
  904. //
  905. // Only place where this function is called from is from odbc with
  906. // a demux of RESERVED_DEMUX_QUERY_CACHE. We do not need to worry
  907. // about other cases
  908. //
  909. FlushBlobCache();
  910. }
  911. return TRUE;
  912. } // TsCacheFlushDemux
  913. BOOL
  914. FlushFilterService(
  915. PBLOB_HEADER pBlob,
  916. PVOID pv
  917. )
  918. {
  919. DWORD dwServerMask = * (DWORD *)pv;
  920. return (pBlob->pBlobKey->m_dwService == dwServerMask);
  921. }
  922. BOOL
  923. TsCacheFlush(
  924. IN DWORD dwServerMask
  925. )
  926. /*++
  927. Routine Description:
  928. This function flushes the blob cache of all items for the specified service
  929. or for all services if dwServerMask is zero.
  930. --*/
  931. {
  932. if (dwServerMask) {
  933. FilteredFlushBlobCache(FlushFilterService, &dwServerMask);
  934. } else {
  935. FlushBlobCache();
  936. }
  937. return TRUE;
  938. } // TsCacheFlush
  939. BOOL
  940. FlushFilterUser(
  941. TS_OPEN_FILE_INFO *pOpenFile,
  942. PVOID pv
  943. )
  944. {
  945. HANDLE hUser = * (HANDLE *)pv;
  946. return (pOpenFile->QueryUser() == hUser);
  947. }
  948. BOOL
  949. TsCacheFlushUser(
  950. IN HANDLE hUserToken,
  951. IN BOOL fDefer
  952. )
  953. /*++
  954. Routine Description:
  955. This function flushes all file handles associated the passed user context
  956. Arguments:
  957. hUserToken - User token to flush from the cache
  958. fDefer - Build list but close handles later in worker thread (Not supported)
  959. --*/
  960. {
  961. FilteredFlushFileCache(FlushFilterUser, &hUserToken);
  962. return TRUE;
  963. } // TsCacheFlushUser
  964. typedef struct _FLUSH_URL_PARAM {
  965. PCSTR pszURL;
  966. DWORD cbURL;
  967. DWORD dwService;
  968. DWORD dwInstance;
  969. } FLUSH_URL_PARAM;
  970. BOOL
  971. FlushFilterURL(
  972. PBLOB_HEADER pBlob,
  973. PVOID pv
  974. )
  975. {
  976. DBG_ASSERT( pBlob );
  977. DBG_ASSERT( pBlob->pBlobKey );
  978. FLUSH_URL_PARAM * fup = (FLUSH_URL_PARAM *)pv;
  979. CBlobKey * pbk = pBlob->pBlobKey;
  980. BOOL bAtRoot;
  981. //
  982. // If we're flushing everything, then don't bother
  983. // with the string comparison
  984. //
  985. bAtRoot = (fup->cbURL == 1) && (fup->pszURL[0] == '/');
  986. //
  987. // If the service, instance, and URL prefixes match then we flush.
  988. //
  989. return ( (pbk->m_dwService == fup->dwService)
  990. && (pbk->m_dwInstance == fup->dwInstance)
  991. && (bAtRoot
  992. || ((pbk->m_cbPathName >= fup->cbURL)
  993. && (memcmp(pbk->m_pszPathName, fup->pszURL, fup->cbURL) == 0))) );
  994. }
  995. VOID
  996. TsFlushURL(
  997. IN const TSVC_CACHE &TSvcCache,
  998. IN PCSTR pszURL,
  999. IN DWORD dwURLLength,
  1000. IN ULONG iDemultiplexor
  1001. )
  1002. /*++
  1003. Routine Description:
  1004. This routine takes as input a URL and removes from the cache all cached
  1005. objects that have the input URL as their prefix. This is mostly called
  1006. when we get a change notify for metadata.
  1007. Arguments
  1008. TSvcCache - Service cache
  1009. pszURL - The URL prefix to be flushed.
  1010. iDemultiplexor - The demultiplexor for the caller's entries.
  1011. Returns
  1012. Nothing
  1013. --*/
  1014. {
  1015. FLUSH_URL_PARAM fuparam;
  1016. CHAR achUpName[MAX_PATH+1];
  1017. //
  1018. // It really only makes sense to flush the URI cache
  1019. // with this function.
  1020. //
  1021. DBG_ASSERT( RESERVED_DEMUX_URI_INFO == iDemultiplexor );
  1022. DBG_ASSERT( MAX_PATH >= dwURLLength );
  1023. //
  1024. // Make sure the path is upper case
  1025. //
  1026. IISstrncpy(achUpName, pszURL, MAX_PATH);
  1027. achUpName[dwURLLength] = 0;
  1028. IISstrupr( (PUCHAR) achUpName );
  1029. fuparam.pszURL = achUpName;
  1030. fuparam.cbURL = dwURLLength;
  1031. fuparam.dwService = TSvcCache.GetServiceId();
  1032. fuparam.dwInstance = TSvcCache.GetInstanceId();
  1033. FilteredFlushURIBlobCache(FlushFilterURL, &fuparam);
  1034. }
  1035. BOOL
  1036. TsExpireCachedBlob(
  1037. IN const TSVC_CACHE &TSvcCache,
  1038. IN PVOID pvBlob
  1039. )
  1040. {
  1041. DecacheBlob((PBLOB_HEADER) pvBlob);
  1042. return TRUE;
  1043. } // TsExpireCachedBlob
  1044. //
  1045. // Misc cache management
  1046. //
  1047. BOOL
  1048. TsCacheQueryStatistics(
  1049. IN DWORD Level,
  1050. IN DWORD dwServerMask,
  1051. IN INETA_CACHE_STATISTICS * pCacheCtrs
  1052. )
  1053. /*++
  1054. Routine Description:
  1055. This function returns the statistics for the global cache or for the
  1056. individual services
  1057. Arguments:
  1058. Level - Only valid value is 0
  1059. dwServerMask - Server mask to retrieve statistics for or 0 for the sum
  1060. of the services
  1061. pCacheCtrs - Receives the statistics for cache
  1062. Notes:
  1063. CacheBytesTotal and CacheBytesInUse are not kept on a per-server basis
  1064. so they are only returned when retrieving summary statistics.
  1065. Returns:
  1066. TRUE on success, FALSE on failure
  1067. --*/
  1068. {
  1069. if ( dwServerMask > LAST_PERF_CTR_SVC )
  1070. {
  1071. SetLastError( ERROR_INVALID_PARAMETER );
  1072. return FALSE;
  1073. }
  1074. if ( g_pFileCacheStats
  1075. && g_pURICacheStats
  1076. && g_pBlobCacheStats
  1077. && (dwServerMask == 0) ) {
  1078. pCacheCtrs->FilesCached = g_pFileCacheStats->GetFilesCached();
  1079. pCacheCtrs->TotalFilesCached = g_pFileCacheStats->GetTotalFilesCached();
  1080. pCacheCtrs->FileHits = g_pFileCacheStats->GetHits();
  1081. pCacheCtrs->FileMisses = g_pFileCacheStats->GetMisses();
  1082. pCacheCtrs->FileFlushes = g_pFileCacheStats->GetFlushes();
  1083. pCacheCtrs->FlushedEntries = g_pFileCacheStats->GetFlushedEntries();
  1084. pCacheCtrs->TotalFlushed = g_pFileCacheStats->GetTotalFlushed();
  1085. pCacheCtrs->URICached = g_pURICacheStats->GetBlobsCached();
  1086. pCacheCtrs->TotalURICached = g_pURICacheStats->GetTotalBlobsCached();
  1087. pCacheCtrs->URIHits = g_pURICacheStats->GetHits();
  1088. pCacheCtrs->URIMisses = g_pURICacheStats->GetMisses();
  1089. pCacheCtrs->URIFlushes = g_pURICacheStats->GetFlushes();
  1090. pCacheCtrs->TotalURIFlushed = g_pURICacheStats->GetTotalFlushed();
  1091. pCacheCtrs->BlobCached = g_pBlobCacheStats->GetBlobsCached();
  1092. pCacheCtrs->TotalBlobCached = g_pBlobCacheStats->GetTotalBlobsCached();
  1093. pCacheCtrs->BlobHits = g_pBlobCacheStats->GetHits();
  1094. pCacheCtrs->BlobMisses = g_pBlobCacheStats->GetMisses();
  1095. pCacheCtrs->BlobFlushes = g_pBlobCacheStats->GetFlushes();
  1096. pCacheCtrs->TotalBlobFlushed = g_pBlobCacheStats->GetTotalFlushed();
  1097. QueryMemoryCacheStatistics( pCacheCtrs, FALSE );
  1098. } else {
  1099. //
  1100. // Either we're reporting for a specific service
  1101. // or stats are not set up. Set all cache
  1102. // counters to zero.
  1103. //
  1104. pCacheCtrs->FilesCached = 0;
  1105. pCacheCtrs->TotalFilesCached = 0;
  1106. pCacheCtrs->FileHits = 0;
  1107. pCacheCtrs->FileMisses = 0;
  1108. pCacheCtrs->FileFlushes = 0;
  1109. pCacheCtrs->FlushedEntries = 0;
  1110. pCacheCtrs->TotalFlushed = 0;
  1111. pCacheCtrs->URICached = 0;
  1112. pCacheCtrs->TotalURICached = 0;
  1113. pCacheCtrs->URIHits = 0;
  1114. pCacheCtrs->URIMisses = 0;
  1115. pCacheCtrs->URIFlushes = 0;
  1116. pCacheCtrs->TotalURIFlushed = 0;
  1117. pCacheCtrs->BlobCached = 0;
  1118. pCacheCtrs->TotalBlobCached = 0;
  1119. pCacheCtrs->BlobHits = 0;
  1120. pCacheCtrs->BlobMisses = 0;
  1121. pCacheCtrs->BlobFlushes = 0;
  1122. pCacheCtrs->TotalBlobFlushed = 0;
  1123. QueryMemoryCacheStatistics( pCacheCtrs, TRUE );
  1124. }
  1125. return TRUE;
  1126. }
  1127. BOOL
  1128. TsCacheClearStatistics(
  1129. IN DWORD dwServerMask
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. Clears the the specified service's statistics
  1134. --*/
  1135. {
  1136. if ( dwServerMask > LAST_PERF_CTR_SVC )
  1137. {
  1138. SetLastError( ERROR_INVALID_PARAMETER );
  1139. return FALSE;
  1140. }
  1141. //
  1142. // Currently this function isn't supported
  1143. //
  1144. SetLastError( ERROR_NOT_SUPPORTED );
  1145. return FALSE;
  1146. } // TsCacheClearStatistics
  1147. const char * g_IISAuxCounterNames[] =
  1148. {
  1149. "Aac Open URI Files",
  1150. "Cac Calls to TsOpenURI()",
  1151. "Cac Calls to TsCloseURI()",
  1152. "Max Counters"
  1153. };
  1154. extern "C"
  1155. VOID
  1156. TsDumpCacheCounters( OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
  1157. {
  1158. DWORD cb = 0;
  1159. *lpcbBuffer = cb;
  1160. return ;
  1161. } // TsDumpCacheCounters()
  1162. VOID
  1163. TsDumpHashTableStats( IN OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
  1164. {
  1165. CLKRHashTableStats hts;
  1166. if (!g_pFileInfoTable) {
  1167. *lpcbBuffer = 0;
  1168. return;
  1169. }
  1170. hts = g_pFileInfoTable->GetStatistics();
  1171. *lpcbBuffer = sprintf( pchBuffer,
  1172. "<TABLE>"
  1173. "<TR><TD>Record Count</TD><TD>%d</TD></TR>"
  1174. "<TR><TD>Table Size</TD><TD>%d</TD></TR>"
  1175. "<TR><TD>Directory Size</TD><TD>%d</TD></TR>"
  1176. "<TR><TD>Longest Chain</TD><TD>%d</TD></TR>"
  1177. "<TR><TD>Empty Slots</TD><TD>%d</TD></TR>"
  1178. "<TR><TD>Split Factor</TD><TD>%f</TD></TR>"
  1179. "<TR><TD>Average Search Length</TD><TD>%f</TD></TR>"
  1180. "<TR><TD>Expected Search Length</TD><TD>%f</TD></TR>"
  1181. "<TR><TD>Average Unsuccessful Search Length</TD><TD>%f</TD></TR>"
  1182. "<TR><TD>Expected Unsuccessful Search Length</TD><TD>%f</TD></TR>"
  1183. "</TABLE>",
  1184. hts.RecordCount,
  1185. hts.TableSize,
  1186. hts.DirectorySize,
  1187. hts.LongestChain,
  1188. hts.EmptySlots,
  1189. hts.SplitFactor,
  1190. hts.AvgSearchLength,
  1191. hts.ExpSearchLength,
  1192. hts.AvgUSearchLength,
  1193. hts.ExpUSearchLength );
  1194. }
  1195. VOID
  1196. TsDumpCacheToHtml( OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
  1197. {
  1198. LIST_ENTRY * pEntry;
  1199. DWORD cItemsOnBin = 0;
  1200. DWORD cTotalItems = 0;
  1201. DWORD i, c, cb;
  1202. DWORD cbTable;
  1203. cb = wsprintf( pchBuffer,
  1204. " <h4>File Hash Table Stats</h4> " );
  1205. TsDumpHashTableStats( pchBuffer + cb, &cbTable );
  1206. cb += cbTable;
  1207. cb += wsprintf( pchBuffer + cb,
  1208. " <h4>Some other stats</h4> ");
  1209. if (g_pFileCacheStats) {
  1210. g_pFileCacheStats->DumpToHtml(pchBuffer + cb, &cbTable);
  1211. cb += cbTable;
  1212. }
  1213. DumpMemoryCacheToHtml( pchBuffer + cb, &cbTable );
  1214. cb += cbTable;
  1215. *lpcbBuffer = cb;
  1216. return;
  1217. } // TsDumpCacheToHtml()
  1218. //
  1219. // tsunami.cxx
  1220. //