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.

1809 lines
38 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. filecache.cxx
  5. Abstract:
  6. A file cache (filename->W3_FILE_INFO cache)
  7. Author:
  8. Bilal Alam (balam) 11-Nov-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #define STRONG_ETAG_DELTA 30000000
  16. #define SIZE_PRIVILEGE_SET 128
  17. ALLOC_CACHE_HANDLER * W3_FILE_INFO::sm_pachW3FileInfo;
  18. GENERIC_MAPPING g_gmFile = {
  19. FILE_GENERIC_READ,
  20. FILE_GENERIC_WRITE,
  21. FILE_GENERIC_EXECUTE,
  22. FILE_ALL_ACCESS
  23. };
  24. HRESULT
  25. W3_FILE_INFO_KEY::CreateCacheKey(
  26. WCHAR * pszFileKey,
  27. DWORD cchFileKey,
  28. BOOL fCopy
  29. )
  30. /*++
  31. Routine Description:
  32. Initialize a file cache key
  33. Arguments:
  34. pszFileKey - filename
  35. cchFileKey - size of filename
  36. fCopy - TRUE if we should copy into key buffer, otherwise just keep ref
  37. Return Value:
  38. HRESULT
  39. --*/
  40. {
  41. HRESULT hr;
  42. if ( fCopy )
  43. {
  44. hr = _strFileKey.Copy( pszFileKey );
  45. if ( FAILED( hr ) )
  46. {
  47. return hr;
  48. }
  49. _pszFileKey = _strFileKey.QueryStr();
  50. _cchFileKey = _strFileKey.QueryCCH();
  51. }
  52. else
  53. {
  54. _pszFileKey = pszFileKey;
  55. _cchFileKey = cchFileKey;
  56. }
  57. return NO_ERROR;
  58. }
  59. W3_FILE_INFO::~W3_FILE_INFO(
  60. VOID
  61. )
  62. {
  63. HRESULT hr;
  64. W3_FILE_INFO_CACHE* pFileCache;
  65. DBG_ASSERT( CheckSignature() );
  66. _dwSignature = W3_FILE_INFO_SIGNATURE_FREE;
  67. //
  68. // Clear any associated object
  69. //
  70. LockCacheEntry();
  71. if ( _pAssociatedObject != NULL )
  72. {
  73. _pAssociatedObject->Cleanup();
  74. _pAssociatedObject = NULL;
  75. }
  76. UnlockCacheEntry();
  77. //
  78. // Release the contents buffer if it exists
  79. //
  80. if ( _pFileBuffer != NULL )
  81. {
  82. pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
  83. hr = pFileCache->ReleaseFromMemoryCache( _pFileBuffer,
  84. _nFileSizeLow );
  85. DBG_ASSERT( SUCCEEDED( hr ) );
  86. _pFileBuffer = NULL;
  87. }
  88. //
  89. // Close the file handle if it still around
  90. //
  91. if ( _hFile != INVALID_HANDLE_VALUE )
  92. {
  93. CloseHandle( _hFile );
  94. _hFile = NULL;
  95. }
  96. }
  97. BOOL
  98. W3_FILE_INFO::SetAssociatedObject(
  99. ASSOCIATED_FILE_OBJECT * pObject
  100. )
  101. /*++
  102. Routine Description:
  103. Associate object with this cache entry
  104. Arguments:
  105. pObject - Object to associate
  106. Return Value:
  107. BOOL
  108. --*/
  109. {
  110. BOOL fRet = FALSE;
  111. LockCacheEntry();
  112. if ( _pAssociatedObject == NULL )
  113. {
  114. _pAssociatedObject = pObject;
  115. fRet = TRUE;
  116. }
  117. UnlockCacheEntry();
  118. return fRet;
  119. }
  120. PSECURITY_DESCRIPTOR
  121. W3_FILE_INFO::QuerySecDesc(
  122. VOID
  123. )
  124. /*++
  125. Routine Description:
  126. Return security descriptor
  127. Arguments:
  128. None
  129. Return Value:
  130. pointer to security descriptor
  131. --*/
  132. {
  133. if ( _pFileBuffer != NULL )
  134. {
  135. //
  136. // The file is cached, therefore we must have security already
  137. //
  138. return _bufSecDesc.QueryPtr();
  139. }
  140. else
  141. {
  142. DBG_ASSERT( _hFile != NULL );
  143. if ( FAILED( ReadSecurityDescriptor() ) )
  144. {
  145. return NULL;
  146. }
  147. return _bufSecDesc.QueryPtr();
  148. }
  149. }
  150. HRESULT
  151. W3_FILE_INFO::GenerateETag(
  152. VOID
  153. )
  154. /*++
  155. Routine Description:
  156. Generate ETag string
  157. Arguments:
  158. None
  159. Return Value:
  160. HRESULT
  161. --*/
  162. {
  163. CHAR * psz = _achETag;
  164. PBYTE pbTime = (PBYTE) &_ftLastWriteTime;
  165. DWORD dwChangeNumber;
  166. const CHAR szHex[] = "0123456789abcdef";
  167. FILETIME ftNow;
  168. __int64 iNow;
  169. __int64 iFileTime;
  170. //
  171. // Is this ETag weak? If so put the preceding W/
  172. //
  173. GetSystemTimeAsFileTime(&ftNow);
  174. iNow = (__int64)*(__int64 *)&ftNow;
  175. iFileTime = (__int64)*(__int64 *)&_ftLastWriteTime;
  176. if ( ( iNow - iFileTime ) <= STRONG_ETAG_DELTA )
  177. {
  178. //
  179. // This is a weak ETag
  180. //
  181. *psz++ = 'W';
  182. *psz++ = '/';
  183. }
  184. //
  185. // System change number is from the metabase
  186. //
  187. dwChangeNumber = g_pW3Server->QuerySystemChangeNumber();
  188. //
  189. // Generate the meat of the ETag
  190. //
  191. *psz++ = '\"';
  192. for (int i = 0; i < 8; i++)
  193. {
  194. BYTE b = *pbTime++;
  195. BYTE bH = b >> 4;
  196. if (bH != 0)
  197. *psz++ = szHex[bH];
  198. *psz++ = szHex[b & 0xF];
  199. }
  200. *psz++ = ':';
  201. psz += strlen(_itoa((DWORD) dwChangeNumber, psz, 16));
  202. *psz++ = '\"';
  203. *psz = '\0';
  204. _cchETag = DIFF(psz - _achETag);
  205. return NO_ERROR;
  206. }
  207. HRESULT
  208. W3_FILE_INFO::GenerateLastModifiedTimeString(
  209. VOID
  210. )
  211. /*++
  212. Routine Description:
  213. Generate the Last-Modified-Time header string
  214. Arguments:
  215. None
  216. Return Value:
  217. HRESULT
  218. --*/
  219. {
  220. SYSTEMTIME st;
  221. FileTimeToSystemTime( &_ftLastWriteTime, &st );
  222. if ( !SystemTimeToGMT( st,
  223. _achLastModified,
  224. sizeof(_achLastModified) ) )
  225. {
  226. return HRESULT_FROM_WIN32( GetLastError() );
  227. }
  228. else
  229. {
  230. return NO_ERROR;
  231. }
  232. }
  233. HRESULT
  234. W3_FILE_INFO::DoAccessCheck(
  235. FILE_CACHE_USER * pFileCacheUser
  236. )
  237. /*++
  238. Routine Description:
  239. Check whether given token has access to this file
  240. Arguments:
  241. pFileCacheUser - User to access cache with
  242. Return Value:
  243. HRESULT
  244. --*/
  245. {
  246. BYTE psFile[SIZE_PRIVILEGE_SET];
  247. DWORD dwPS;
  248. DWORD dwGrantedAccess;
  249. BOOL fAccess;
  250. if ( pFileCacheUser == NULL )
  251. {
  252. DBG_ASSERT( FALSE );
  253. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  254. }
  255. //
  256. // If we don't have a security descriptor, then local system must have
  257. // accessed the file originally. Just return success
  258. //
  259. if ( pFileCacheUser->_hToken == NULL )
  260. {
  261. return NO_ERROR;
  262. }
  263. //
  264. // If we have a last-user-sid, and the caller provided a sid, then do a
  265. // quick check of sid equality
  266. //
  267. if ( QueryLastSid() != NULL &&
  268. pFileCacheUser->_pSid != NULL )
  269. {
  270. if ( EqualSid( QueryLastSid(), pFileCacheUser->_pSid ) )
  271. {
  272. return NO_ERROR;
  273. }
  274. }
  275. //
  276. // Ok. Just use the token and cached security descriptor
  277. //
  278. dwPS = sizeof(psFile);
  279. ((PRIVILEGE_SET*)&psFile)->PrivilegeCount = 0;
  280. //
  281. // We must have a security descriptor if we've cached the file
  282. //
  283. DBG_ASSERT( QuerySecDesc() );
  284. if ( !AccessCheck( QuerySecDesc(),
  285. pFileCacheUser->_hToken,
  286. FILE_GENERIC_READ,
  287. &g_gmFile,
  288. (PRIVILEGE_SET*)psFile,
  289. &dwPS,
  290. &dwGrantedAccess,
  291. &fAccess ) || !fAccess )
  292. {
  293. return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  294. }
  295. return NO_ERROR;
  296. }
  297. HRESULT
  298. W3_FILE_INFO::OpenFile(
  299. STRU & strFileName,
  300. FILE_CACHE_USER * pOpeningUser
  301. )
  302. /*++
  303. Routine Description:
  304. Open the given file (but don't read in the file contents). This method
  305. does the minimum needed to allow the caller to make a reasonable
  306. decision about whether this file should be cached here or in UL
  307. Arguments:
  308. strFileName - file name to open
  309. pOpeningUser - User to open file under
  310. Return Value:
  311. HRESULT
  312. --*/
  313. {
  314. HANDLE hFile = INVALID_HANDLE_VALUE;
  315. STACK_STRU( strFilePath, MAX_PATH + 1 );
  316. HRESULT hr = NO_ERROR;
  317. BOOL fImpersonated = FALSE;
  318. BY_HANDLE_FILE_INFORMATION FileInfo;
  319. if ( pOpeningUser == NULL )
  320. {
  321. DBG_ASSERT( FALSE );
  322. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  323. }
  324. //
  325. // First make a cache key
  326. //
  327. hr = _cacheKey.CreateCacheKey( strFileName.QueryStr(),
  328. strFileName.QueryCCH(),
  329. TRUE );
  330. if ( FAILED( hr ) )
  331. {
  332. goto Finished;
  333. }
  334. //
  335. // Avoid the infamous ::$DATA bug
  336. //
  337. if ( wcschr( strFileName.QueryStr() + 2, L':' ) != NULL )
  338. {
  339. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  340. goto Finished;
  341. }
  342. //
  343. // Turn off NT file canonicalization
  344. //
  345. hr = MakePathCanonicalizationProof( strFileName.QueryStr(),
  346. &strFilePath );
  347. if ( FAILED( hr ) )
  348. {
  349. goto Finished;
  350. }
  351. //
  352. // We may need to impersonate some other user to open the file
  353. //
  354. if ( pOpeningUser->_hToken != NULL )
  355. {
  356. if ( !SetThreadToken( NULL, pOpeningUser->_hToken ) )
  357. {
  358. hr = HRESULT_FROM_WIN32( GetLastError() );
  359. goto Finished;
  360. }
  361. fImpersonated = TRUE;
  362. }
  363. //
  364. // Open the file
  365. //
  366. hFile = CreateFileW( strFilePath.QueryStr(),
  367. GENERIC_READ,
  368. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  369. NULL,
  370. OPEN_EXISTING,
  371. FILE_DIRECTORY_FILE | FILE_FLAG_BACKUP_SEMANTICS,
  372. NULL );
  373. if ( hFile == INVALID_HANDLE_VALUE )
  374. {
  375. hr = HRESULT_FROM_WIN32( GetLastError() );
  376. goto Finished;
  377. }
  378. //
  379. // Stop impersonating
  380. //
  381. if ( fImpersonated )
  382. {
  383. RevertToSelf();
  384. fImpersonated = FALSE;
  385. }
  386. //
  387. // We shouldn't be opening byte streams (like COM, LPT)
  388. //
  389. if ( GetFileType( hFile ) != FILE_TYPE_DISK )
  390. {
  391. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  392. CloseHandle( hFile );
  393. hFile = INVALID_HANDLE_VALUE;
  394. goto Finished;
  395. }
  396. //
  397. // Get file attributes
  398. //
  399. if ( !GetFileInformationByHandle( hFile,
  400. &FileInfo ) )
  401. {
  402. hr = HRESULT_FROM_WIN32( GetLastError() );
  403. CloseHandle( hFile );
  404. hFile = INVALID_HANDLE_VALUE;
  405. goto Finished;
  406. }
  407. //
  408. // Set the minimum properties now
  409. //
  410. _hFile = hFile;
  411. _ftLastWriteTime = FileInfo.ftLastWriteTime;
  412. _dwFileAttributes = FileInfo.dwFileAttributes;
  413. _nFileSizeLow = FileInfo.nFileSizeLow;
  414. _nFileSizeHigh = FileInfo.nFileSizeHigh;
  415. *((__int64 *)&_CastratedLastWriteTime)
  416. = (*((__int64 *)&_ftLastWriteTime) / 10000000) * 10000000;
  417. //
  418. // Create the ETag and LastModified strings
  419. //
  420. hr = GenerateETag();
  421. if ( FAILED( hr ) )
  422. {
  423. goto Finished;
  424. }
  425. hr = GenerateLastModifiedTimeString();
  426. if ( FAILED( hr ) )
  427. {
  428. goto Finished;
  429. }
  430. //
  431. // Turn off the hidden attribute if this is a root directory listing
  432. // (root some times has the bit set for no apparent reason)
  433. //
  434. if ( _dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )
  435. {
  436. if ( strFileName.QueryCCH() >= 2 )
  437. {
  438. if ( strFileName.QueryStr()[ 1 ] == L':' )
  439. {
  440. if ( ( strFileName.QueryStr()[ 2 ] == L'\0' ) ||
  441. ( strFileName.QueryStr()[ 2 ] == L'\\' &&
  442. strFileName.QueryStr()[ 3 ] == L'\0' ) )
  443. {
  444. //
  445. // This looks like a local root. Mask out the bit
  446. //
  447. _dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
  448. }
  449. }
  450. }
  451. }
  452. Finished:
  453. if ( FAILED( hr ) )
  454. {
  455. if ( fImpersonated )
  456. {
  457. RevertToSelf();
  458. fImpersonated = FALSE;
  459. }
  460. if ( hFile != INVALID_HANDLE_VALUE )
  461. {
  462. CloseHandle( hFile );
  463. hFile = INVALID_HANDLE_VALUE;
  464. }
  465. }
  466. return hr;
  467. }
  468. HRESULT
  469. W3_FILE_INFO::ReadSecurityDescriptor(
  470. VOID
  471. )
  472. /*++
  473. Routine Description:
  474. Read security descriptor for current file
  475. Arguments:
  476. None
  477. Return Value:
  478. HRESULT
  479. --*/
  480. {
  481. DWORD cbRequired;
  482. //
  483. // Cache the security descriptor
  484. //
  485. if ( !GetKernelObjectSecurity( _hFile,
  486. OWNER_SECURITY_INFORMATION
  487. | GROUP_SECURITY_INFORMATION
  488. | DACL_SECURITY_INFORMATION,
  489. (PSECURITY_DESCRIPTOR) _bufSecDesc.QueryPtr(),
  490. _bufSecDesc.QuerySize(),
  491. &cbRequired ) )
  492. {
  493. if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
  494. {
  495. return HRESULT_FROM_WIN32( GetLastError() );
  496. }
  497. DBG_ASSERT( cbRequired > _bufSecDesc.QuerySize() );
  498. if ( !_bufSecDesc.Resize( cbRequired ) )
  499. {
  500. return HRESULT_FROM_WIN32( GetLastError() );
  501. }
  502. //
  503. // Try again with the bigger buffer. No more excuses
  504. //
  505. if ( !GetKernelObjectSecurity( _hFile,
  506. OWNER_SECURITY_INFORMATION
  507. | GROUP_SECURITY_INFORMATION
  508. | DACL_SECURITY_INFORMATION,
  509. (PSECURITY_DESCRIPTOR) _bufSecDesc.QueryPtr(),
  510. _bufSecDesc.QuerySize(),
  511. &cbRequired ) )
  512. {
  513. return HRESULT_FROM_WIN32( GetLastError() );
  514. }
  515. }
  516. return NO_ERROR;
  517. }
  518. HRESULT
  519. W3_FILE_INFO::MakeCacheable(
  520. FILE_CACHE_USER * pFileUser
  521. )
  522. /*++
  523. Routine Description:
  524. Make the file cacheable by reading contents into memory, and caching
  525. the security descriptor
  526. Arguments:
  527. pFileUser - User trying to open file
  528. Return Value:
  529. HRESULT
  530. --*/
  531. {
  532. DWORD dwError;
  533. HRESULT hr;
  534. W3_FILE_INFO_CACHE* pFileCache;
  535. if ( pFileUser == NULL )
  536. {
  537. DBG_ASSERT( FALSE );
  538. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  539. }
  540. DBG_ASSERT( IsCacheable() );
  541. //
  542. // We must have a file handle if we're here
  543. //
  544. DBG_ASSERT( _hFile != INVALID_HANDLE_VALUE );
  545. //
  546. // Get the security descriptor
  547. //
  548. hr = ReadSecurityDescriptor();
  549. if ( FAILED( hr ) )
  550. {
  551. return hr;
  552. }
  553. //
  554. // On top of reading the security descriptor, we will also store the
  555. // last sid accessing the file if available
  556. //
  557. if ( pFileUser->_pSid != NULL )
  558. {
  559. if ( GetLengthSid( pFileUser->_pSid ) <= sizeof( _abLastSid ) )
  560. {
  561. memcpy( _abLastSid,
  562. pFileUser->_pSid,
  563. GetLengthSid( pFileUser->_pSid ) );
  564. _pLastSid = (PSID) _abLastSid;
  565. }
  566. }
  567. //
  568. // Now read the contents of the file into memory since we cannot cache
  569. // the file handle itself
  570. //
  571. pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
  572. dwError = pFileCache->ReadFileIntoMemoryCache( _hFile,
  573. _nFileSizeLow,
  574. (PVOID*) &_pFileBuffer );
  575. if ( dwError == ERROR_SUCCESS )
  576. {
  577. //
  578. // OK. The contents are now in memory. We can now close the file
  579. //
  580. CloseHandle( _hFile );
  581. _hFile = INVALID_HANDLE_VALUE;
  582. return NO_ERROR;
  583. }
  584. else
  585. {
  586. return HRESULT_FROM_WIN32( dwError );
  587. }
  588. }
  589. BOOL
  590. W3_FILE_INFO::IsCacheable(
  591. VOID
  592. ) const
  593. /*++
  594. Routine Description:
  595. Is this file cacheable? Specically, we should we even attempt to cache
  596. this file?
  597. Arguments:
  598. None
  599. Return Value:
  600. TRUE if cacheable
  601. --*/
  602. {
  603. LARGE_INTEGER liFileSize;
  604. W3_FILE_INFO_CACHE * pFileCache;
  605. pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
  606. DBG_ASSERT( pFileCache != NULL );
  607. //
  608. // Are we past the limit of file entries?
  609. //
  610. if ( pFileCache->QueryElementLimitExceeded() )
  611. {
  612. return FALSE;
  613. }
  614. //
  615. // No caching of directories
  616. //
  617. if ( _dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  618. {
  619. return FALSE;
  620. }
  621. //
  622. // No caching of file sizes greater than the configured threshold
  623. //
  624. liFileSize.LowPart = _nFileSizeLow;
  625. liFileSize.HighPart = _nFileSizeHigh;
  626. if ( liFileSize.QuadPart > pFileCache->QueryFileSizeThreshold() )
  627. {
  628. return FALSE;
  629. }
  630. return TRUE;
  631. }
  632. BOOL
  633. W3_FILE_INFO::QueryIsOkToFlushDirmon(
  634. WCHAR * pszPath,
  635. DWORD cchPath
  636. )
  637. /*++
  638. Routine Description:
  639. Determine whether this file entry should be flushed, given the path
  640. which has changed (dir monitor changed)
  641. Arguments:
  642. pszPath - Path which changed
  643. cchPath - Size of path changed
  644. Return Value:
  645. TRUE if we should flush, else FALSE
  646. --*/
  647. {
  648. DBG_ASSERT( _cacheKey._pszFileKey != NULL );
  649. if ( _wcsnicmp( _cacheKey._pszFileKey,
  650. pszPath,
  651. cchPath ) == 0 )
  652. {
  653. return TRUE;
  654. }
  655. else
  656. {
  657. return FALSE;
  658. }
  659. }
  660. //static
  661. HRESULT
  662. W3_FILE_INFO::Initialize(
  663. VOID
  664. )
  665. /*++
  666. Routine Description:
  667. Initialize W3_FILE_INFO lookaside
  668. Arguments:
  669. None
  670. Return Value:
  671. HRESULT
  672. --*/
  673. {
  674. ALLOC_CACHE_CONFIGURATION acConfig;
  675. HRESULT hr;
  676. //
  677. // Initialize allocation lookaside
  678. //
  679. acConfig.nConcurrency = 1;
  680. acConfig.nThreshold = 100;
  681. acConfig.cbSize = sizeof( W3_FILE_INFO );
  682. DBG_ASSERT( sm_pachW3FileInfo == NULL );
  683. sm_pachW3FileInfo = new ALLOC_CACHE_HANDLER( "W3_FILE_INFO",
  684. &acConfig );
  685. if ( sm_pachW3FileInfo == NULL )
  686. {
  687. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  688. DBGPRINTF(( DBG_CONTEXT,
  689. "Error initializing sm_pachW3FileInfo. hr = 0x%x\n",
  690. hr ));
  691. return hr;
  692. }
  693. return NO_ERROR;
  694. }
  695. //static
  696. VOID
  697. W3_FILE_INFO::Terminate(
  698. VOID
  699. )
  700. /*++
  701. Routine Description:
  702. Cleanup W3_FILE_INFO lookaside
  703. Arguments:
  704. None
  705. Return Value:
  706. None
  707. --*/
  708. {
  709. if ( sm_pachW3FileInfo != NULL )
  710. {
  711. delete sm_pachW3FileInfo;
  712. sm_pachW3FileInfo = NULL;
  713. }
  714. }
  715. //static
  716. VOID
  717. W3_FILE_INFO_CACHE::MemoryCacheAdjustor(
  718. PVOID pCache,
  719. BOOLEAN TimerOrWaitFired
  720. )
  721. /*++
  722. Routine Description:
  723. Called to adjust our memory cache size if necessary
  724. Arguments:
  725. pCache - Points to file cache
  726. Return Value:
  727. None
  728. --*/
  729. {
  730. W3_FILE_INFO_CACHE * pFileCache;
  731. MEMORYSTATUSEX MemoryStatus;
  732. pFileCache = (W3_FILE_INFO_CACHE*) pCache;
  733. MemoryStatus.dwLength = sizeof( MemoryStatus );
  734. GlobalMemoryStatusEx( &MemoryStatus );
  735. EnterCriticalSection( &( pFileCache->_csMemCache ) );
  736. pFileCache->_cbMemCacheLimit = min(
  737. MemoryStatus.ullAvailPhys + pFileCache->_cbMemCacheCurrentSize,
  738. MemoryStatus.ullTotalVirtual ) / 2;
  739. LeaveCriticalSection( &( pFileCache->_csMemCache ) );
  740. }
  741. W3_FILE_INFO_CACHE::W3_FILE_INFO_CACHE()
  742. {
  743. _cbFileSizeThreshold = DEFAULT_FILE_SIZE_THRESHOLD;
  744. _cbMemoryCacheSize = 0;
  745. _cMaxFileEntries = 0;
  746. _cbMemCacheLimit = 0;
  747. _cbMemCacheCurrentSize = 0;
  748. _cbMaxMemCacheSize = 0;
  749. _hMemCacheHeap = NULL;
  750. _hTimer = NULL;
  751. _fEnableCache = TRUE;
  752. }
  753. W3_FILE_INFO_CACHE::~W3_FILE_INFO_CACHE()
  754. {
  755. }
  756. HRESULT
  757. W3_FILE_INFO_CACHE::InitializeMemoryCache(
  758. VOID
  759. )
  760. /*++
  761. Routine Description:
  762. Initialize the memory cache
  763. Arguments:
  764. None
  765. Return Value:
  766. HRESULT
  767. --*/
  768. {
  769. BOOL fRet;
  770. HRESULT hr = NO_ERROR;
  771. InitializeCriticalSection( &_csMemCache );
  772. //
  773. // If the memory cache size was not explicitly set, then we occasionally
  774. // check memory status when determining what to cache
  775. //
  776. if ( _cbMemoryCacheSize == 0 )
  777. {
  778. MEMORYSTATUSEX MemoryStatus;
  779. MemoryStatus.dwLength = sizeof( MemoryStatus );
  780. //
  781. // Get our own estimate of size of cache
  782. //
  783. GlobalMemoryStatusEx( &MemoryStatus );
  784. _cbMemCacheLimit = min( MemoryStatus.ullAvailPhys,
  785. MemoryStatus.ullTotalVirtual ) / 2;
  786. //
  787. // Setup timer so we can update our memory status
  788. //
  789. fRet = CreateTimerQueueTimer( &_hTimer,
  790. NULL,
  791. W3_FILE_INFO_CACHE::MemoryCacheAdjustor,
  792. this,
  793. 30000,
  794. 30000,
  795. WT_EXECUTELONGFUNCTION );
  796. if ( !fRet )
  797. {
  798. hr = HRESULT_FROM_WIN32( GetLastError() );
  799. }
  800. }
  801. else
  802. {
  803. _cbMemCacheLimit = _cbMemoryCacheSize;
  804. }
  805. //
  806. // Allocate a private heap
  807. //
  808. if ( SUCCEEDED( hr ) )
  809. {
  810. _hMemCacheHeap = HeapCreate( 0, 0, 0 );
  811. if ( _hMemCacheHeap == NULL )
  812. {
  813. hr = HRESULT_FROM_WIN32( GetLastError() );
  814. }
  815. }
  816. if ( FAILED( hr ) )
  817. {
  818. if ( _hMemCacheHeap != NULL )
  819. {
  820. HeapDestroy( _hMemCacheHeap );
  821. _hMemCacheHeap = NULL;
  822. }
  823. if ( _hTimer != NULL )
  824. {
  825. DeleteTimerQueueTimer( NULL,
  826. _hTimer,
  827. INVALID_HANDLE_VALUE );
  828. _hTimer = NULL;
  829. }
  830. DeleteCriticalSection( &_csMemCache );
  831. }
  832. return hr;
  833. }
  834. HRESULT
  835. W3_FILE_INFO_CACHE::ReadFileIntoMemoryCache(
  836. IN HANDLE hFile,
  837. IN DWORD cbFile,
  838. OUT VOID ** ppvBuffer
  839. )
  840. /*++
  841. Routine Description:
  842. Read contents of file into a buffer
  843. Arguments:
  844. hFile - Handle to valid file
  845. cbFile - Size of file ( ==> size of buffer )
  846. ppvBuffer - Filled in with pointer to buffer with file contents. Set
  847. to NULL on failure
  848. Return Value:
  849. HRESULT
  850. --*/
  851. {
  852. BOOL bRet;
  853. VOID * pvBuffer = NULL;
  854. DWORD cbRead;
  855. OVERLAPPED Overlapped;
  856. HRESULT hr = NO_ERROR;
  857. DBG_ASSERT( hFile && ( hFile != INVALID_HANDLE_VALUE ) );
  858. DBG_ASSERT( ppvBuffer != NULL );
  859. //
  860. // First check whether there will be room in cache for the blob
  861. //
  862. EnterCriticalSection( &_csMemCache );
  863. if ( ( _cbMemCacheCurrentSize + cbFile ) > _cbMemCacheLimit )
  864. {
  865. //
  866. // Not enough room for cache
  867. //
  868. LeaveCriticalSection( &_csMemCache );
  869. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  870. }
  871. _cbMemCacheCurrentSize += cbFile;
  872. _cbMaxMemCacheSize = max( _cbMaxMemCacheSize, _cbMemCacheCurrentSize );
  873. LeaveCriticalSection( &_csMemCache );
  874. //
  875. // Allocate blob for file
  876. //
  877. DBG_ASSERT( _hMemCacheHeap != NULL );
  878. pvBuffer = HeapAlloc( _hMemCacheHeap, 0, cbFile );
  879. if ( pvBuffer == NULL )
  880. {
  881. hr = HRESULT_FROM_WIN32( GetLastError() );
  882. goto Finished;
  883. }
  884. //
  885. // Read file into blob
  886. //
  887. Overlapped.Offset = 0;
  888. Overlapped.OffsetHigh = 0;
  889. Overlapped.hEvent = NULL;
  890. bRet = ReadFile( hFile,
  891. pvBuffer,
  892. cbFile,
  893. &cbRead,
  894. &Overlapped );
  895. if ( !bRet )
  896. {
  897. hr = HRESULT_FROM_WIN32( GetLastError() );
  898. if ( hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING ) )
  899. {
  900. //
  901. // Something bad happened
  902. //
  903. goto Finished;
  904. }
  905. else
  906. {
  907. //
  908. // Reset the error lest we confuse ourselves later on cleanup
  909. //
  910. hr = NO_ERROR;
  911. //
  912. // Wait for async read to complete
  913. //
  914. bRet = GetOverlappedResult( hFile,
  915. &Overlapped,
  916. &cbRead,
  917. TRUE );
  918. if ( !bRet )
  919. {
  920. //
  921. // Something bad happened
  922. //
  923. hr = HRESULT_FROM_WIN32( GetLastError() );
  924. goto Finished;
  925. }
  926. }
  927. }
  928. //
  929. // Ensure that we read the number of bytes we expected to
  930. //
  931. if ( cbRead != cbFile )
  932. {
  933. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  934. }
  935. Finished:
  936. if ( FAILED( hr ) )
  937. {
  938. //
  939. // Undo changes to memory cache statistics
  940. //
  941. EnterCriticalSection( &_csMemCache );
  942. _cbMemCacheCurrentSize -= cbFile;
  943. LeaveCriticalSection( &_csMemCache );
  944. if ( pvBuffer != NULL )
  945. {
  946. HeapFree( _hMemCacheHeap, 0, pvBuffer );
  947. pvBuffer = NULL;
  948. }
  949. }
  950. *ppvBuffer = pvBuffer;
  951. return hr;
  952. }
  953. HRESULT
  954. W3_FILE_INFO_CACHE::ReleaseFromMemoryCache(
  955. IN VOID * pvBuffer,
  956. IN DWORD cbBuffer
  957. )
  958. /*++
  959. Routine Description:
  960. Release file content blob from cache
  961. Arguments:
  962. pvBuffer - Buffer to release
  963. cbBuffer - Size of buffer
  964. Return Value:
  965. HRESULT
  966. --*/
  967. {
  968. DBG_ASSERT( pvBuffer );
  969. DBG_ASSERT( _hMemCacheHeap != NULL);
  970. HeapFree( _hMemCacheHeap, 0, pvBuffer );
  971. EnterCriticalSection( &_csMemCache );
  972. _cbMemCacheCurrentSize -= cbBuffer;
  973. LeaveCriticalSection( &_csMemCache );
  974. return NO_ERROR;
  975. }
  976. VOID
  977. W3_FILE_INFO_CACHE::TerminateMemoryCache(
  978. VOID
  979. )
  980. /*++
  981. Routine Description:
  982. Terminate memory cache
  983. Arguments:
  984. Return Value:
  985. None
  986. --*/
  987. {
  988. if ( _hTimer != NULL )
  989. {
  990. DeleteTimerQueueTimer( NULL,
  991. _hTimer,
  992. INVALID_HANDLE_VALUE );
  993. _hTimer = NULL;
  994. }
  995. if ( _hMemCacheHeap != NULL )
  996. {
  997. HeapDestroy( _hMemCacheHeap );
  998. _hMemCacheHeap = NULL;
  999. }
  1000. DeleteCriticalSection( &_csMemCache );
  1001. }
  1002. HRESULT
  1003. W3_FILE_INFO_CACHE::GetFileInfo(
  1004. STRU & strFileName,
  1005. DIRMON_CONFIG * pDirmonConfig,
  1006. FILE_CACHE_USER * pOpeningUser,
  1007. BOOL fDoCache,
  1008. W3_FILE_INFO ** ppFileInfo
  1009. )
  1010. /*++
  1011. Routine Description:
  1012. Returns a W3_FILE_INFO for the given file path. Depending on fDoCache,
  1013. this W3_FILE_INFO will be cached
  1014. Arguments:
  1015. strFileName - file name to find
  1016. pDirmonConfig - Dir monitor config
  1017. pOpeningUser - Token for user accessing the cache
  1018. fDoCache - Set to TRUE if we should attempt to cache if possible
  1019. ppFileInfo - Points to W3_FILE_INFO on success
  1020. Return Value:
  1021. HRESULT
  1022. --*/
  1023. {
  1024. W3_FILE_INFO_KEY fileKey;
  1025. DIRMON_CONFIG DefaultDirmonConfig;
  1026. STACK_STRU( strParentDir, MAX_PATH );
  1027. WCHAR * pszParentDir;
  1028. W3_FILE_INFO * pFileInfo;
  1029. HRESULT hr;
  1030. STACK_STRU( strFilePath, MAX_PATH );
  1031. BOOL fShouldCacheHint = FALSE;
  1032. if ( ppFileInfo == NULL ||
  1033. pOpeningUser == NULL )
  1034. {
  1035. DBG_ASSERT( FALSE );
  1036. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1037. }
  1038. *ppFileInfo = NULL;
  1039. //
  1040. // We need to upper case the path to avoid a bunch of insensitive
  1041. // compares in the hash table lookup
  1042. //
  1043. hr = strFilePath.Copy( strFileName );
  1044. if ( FAILED( hr ) )
  1045. {
  1046. return hr;
  1047. }
  1048. _wcsupr( strFilePath.QueryStr() );
  1049. //
  1050. // If the cache is enabled, lookup there first
  1051. //
  1052. if ( QueryCacheEnabled() )
  1053. {
  1054. //
  1055. // Make a key for the lookup
  1056. //
  1057. hr = fileKey.CreateCacheKey( strFilePath.QueryStr(),
  1058. strFilePath.QueryCCH(),
  1059. FALSE );
  1060. if ( FAILED( hr ) )
  1061. {
  1062. return hr;
  1063. }
  1064. //
  1065. // Look it up
  1066. //
  1067. hr = FindCacheEntry( &fileKey,
  1068. (CACHE_ENTRY**) &pFileInfo,
  1069. &fShouldCacheHint );
  1070. if ( SUCCEEDED( hr ) )
  1071. {
  1072. DBG_ASSERT( pFileInfo != NULL );
  1073. hr = pFileInfo->DoAccessCheck( pOpeningUser );
  1074. if ( SUCCEEDED( hr ) )
  1075. {
  1076. *ppFileInfo = pFileInfo;
  1077. }
  1078. else
  1079. {
  1080. pFileInfo->DereferenceCacheEntry();
  1081. }
  1082. return hr;
  1083. }
  1084. }
  1085. //
  1086. // We will simply open the file and return the object
  1087. //
  1088. pFileInfo = new W3_FILE_INFO( this );
  1089. if ( pFileInfo == NULL )
  1090. {
  1091. return HRESULT_FROM_WIN32( GetLastError() );
  1092. }
  1093. //
  1094. // Open the file
  1095. //
  1096. hr = pFileInfo->OpenFile( strFilePath,
  1097. pOpeningUser );
  1098. if ( FAILED( hr ) )
  1099. {
  1100. pFileInfo->DereferenceCacheEntry();
  1101. return hr;
  1102. }
  1103. //
  1104. // If we aren't asked to cache the file, OR the file is not cacheable
  1105. // then we can return it now
  1106. //
  1107. if ( !QueryCacheEnabled() ||
  1108. !fDoCache ||
  1109. !pFileInfo->IsCacheable() ||
  1110. !fShouldCacheHint )
  1111. {
  1112. *ppFileInfo = pFileInfo;
  1113. return NO_ERROR;
  1114. }
  1115. //
  1116. // If we're supposed to cache but no dirmon was configured, then just
  1117. // assume the directory to monitor is the parent directory (and token
  1118. // to use is NULL)
  1119. //
  1120. if ( pDirmonConfig == NULL )
  1121. {
  1122. DefaultDirmonConfig.hToken = NULL;
  1123. pszParentDir = wcsrchr( strFilePath.QueryStr(), L'\\' );
  1124. if ( pszParentDir != NULL )
  1125. {
  1126. hr = strParentDir.Copy( strFilePath.QueryStr(),
  1127. DIFF( pszParentDir - strFilePath.QueryStr() ) );
  1128. if ( SUCCEEDED( hr ) )
  1129. {
  1130. DefaultDirmonConfig.pszDirPath = strParentDir.QueryStr();
  1131. pDirmonConfig = &DefaultDirmonConfig;
  1132. }
  1133. }
  1134. }
  1135. //
  1136. // If we still don't have a dir mon configuration, then just don't cache
  1137. //
  1138. if ( pDirmonConfig == NULL )
  1139. {
  1140. *ppFileInfo = pFileInfo;
  1141. return NO_ERROR;
  1142. }
  1143. //
  1144. // Start monitoring the appropriate directory for changes
  1145. //
  1146. hr = pFileInfo->AddDirmonInvalidator( pDirmonConfig );
  1147. if ( FAILED( hr ) )
  1148. {
  1149. //
  1150. // If we can't monitor the directory, then just don't cache the item
  1151. //
  1152. *ppFileInfo = pFileInfo;
  1153. return NO_ERROR;
  1154. }
  1155. //
  1156. // Attempt to cache the file. Caching the file means reading the
  1157. // contents into memory, as well as caching the security descriptor
  1158. //
  1159. hr = pFileInfo->MakeCacheable( pOpeningUser );
  1160. if ( FAILED( hr ) )
  1161. {
  1162. *ppFileInfo = pFileInfo;
  1163. return NO_ERROR;
  1164. }
  1165. //
  1166. // Insert into the hash table. AddCacheEntry() will only error if some
  1167. // thing fatal happened. If someone else already added the item, that
  1168. // is not fatal and we will simply return this item and it will cleanup
  1169. // on dereference
  1170. //
  1171. AddCacheEntry( pFileInfo );
  1172. *ppFileInfo = pFileInfo;
  1173. return NO_ERROR;
  1174. }
  1175. HRESULT
  1176. W3_FILE_INFO_CACHE::Initialize(
  1177. VOID
  1178. )
  1179. /*++
  1180. Routine Description:
  1181. Initialize the file cache
  1182. Arguments:
  1183. None
  1184. Return Value:
  1185. HRESULT
  1186. --*/
  1187. {
  1188. DWORD dwError;
  1189. DWORD dwType;
  1190. DWORD dwValue;
  1191. DWORD cbData;
  1192. DWORD csecTTL = DEFAULT_W3_FILE_INFO_CACHE_TTL;
  1193. DWORD csecActivity = DEFAULT_W3_FILE_INFO_CACHE_ACTIVITY;
  1194. HKEY hKey = NULL;
  1195. HRESULT hr;
  1196. CACHE_HINT_CONFIG cacheHintConfig;
  1197. //
  1198. // Read the registry configuration of the file cache.
  1199. // For now, that is just the legacy confiugration from IIS 5.x
  1200. //
  1201. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1202. L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
  1203. 0,
  1204. KEY_READ,
  1205. &hKey );
  1206. if ( dwError == ERROR_SUCCESS )
  1207. {
  1208. DBG_ASSERT( hKey != NULL );
  1209. //
  1210. // Should we be file caching at all?
  1211. //
  1212. cbData = sizeof( DWORD );
  1213. dwError = RegQueryValueEx( hKey,
  1214. L"DisableMemoryCache",
  1215. NULL,
  1216. &dwType,
  1217. (LPBYTE) &dwValue,
  1218. &cbData );
  1219. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1220. {
  1221. _fEnableCache = dwValue ? FALSE : TRUE;
  1222. }
  1223. //
  1224. // What is the biggest file we should cache in user mode?
  1225. //
  1226. cbData = sizeof( DWORD );
  1227. dwError = RegQueryValueEx( hKey,
  1228. L"MaxCachedFileSize",
  1229. NULL,
  1230. &dwType,
  1231. (LPBYTE) &dwValue,
  1232. &cbData );
  1233. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1234. {
  1235. _cbFileSizeThreshold = dwValue;
  1236. }
  1237. //
  1238. // What is the size of our memory cache? Size is in MB
  1239. //
  1240. cbData = sizeof( DWORD );
  1241. dwError = RegQueryValueEx( hKey,
  1242. L"MemCacheSize",
  1243. NULL,
  1244. &dwType,
  1245. (LPBYTE)&dwValue,
  1246. &cbData );
  1247. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1248. {
  1249. _cbMemoryCacheSize = dwValue * (1024 * 1024);
  1250. }
  1251. //
  1252. // Read the maximum # of files in cache
  1253. //
  1254. cbData = sizeof( DWORD );
  1255. dwError = RegQueryValueEx( hKey,
  1256. L"MaxOpenFiles",
  1257. NULL,
  1258. &dwType,
  1259. (LPBYTE) &dwValue,
  1260. &cbData );
  1261. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1262. {
  1263. _cMaxFileEntries = dwValue;
  1264. }
  1265. //
  1266. // What is the TTL for the file cache?
  1267. //
  1268. cbData = sizeof( DWORD );
  1269. dwError = RegQueryValueEx( hKey,
  1270. L"ObjectCacheTTL",
  1271. NULL,
  1272. &dwType,
  1273. (LPBYTE) &dwValue,
  1274. &cbData );
  1275. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1276. {
  1277. csecTTL = dwValue;
  1278. }
  1279. //
  1280. // What is the activity period before putting into cache
  1281. //
  1282. cbData = sizeof( DWORD );
  1283. dwError = RegQueryValueEx( hKey,
  1284. L"ActivityPeriod",
  1285. NULL,
  1286. &dwType,
  1287. (LPBYTE) &dwValue,
  1288. &cbData );
  1289. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1290. {
  1291. csecActivity = dwValue;
  1292. }
  1293. RegCloseKey( hKey );
  1294. }
  1295. //
  1296. // Initialize memory cache
  1297. //
  1298. hr = InitializeMemoryCache();
  1299. if ( FAILED( hr ) )
  1300. {
  1301. return hr;
  1302. }
  1303. //
  1304. // Setup cache hint config (for now hardcoded)
  1305. //
  1306. if ( csecActivity != 0 )
  1307. {
  1308. cacheHintConfig.cmsecActivityWindow = csecActivity * 1000;
  1309. cacheHintConfig.cmsecScavengeTime = cacheHintConfig.cmsecActivityWindow * 2;
  1310. cacheHintConfig.cmsecTTL = cacheHintConfig.cmsecActivityWindow * 2;
  1311. }
  1312. //
  1313. // We'll use TTL for scavenge period, and expect two inactive periods to
  1314. // flush
  1315. //
  1316. hr = SetCacheConfiguration( csecTTL * 1000,
  1317. csecTTL * 1000,
  1318. CACHE_INVALIDATION_DIRMON_FLUSH |
  1319. CACHE_INVALIDATION_DIRMON_SPECIFIC,
  1320. csecActivity ? &cacheHintConfig : NULL );
  1321. if ( FAILED( hr ) )
  1322. {
  1323. return hr;
  1324. }
  1325. //
  1326. // Initialize file info lookaside
  1327. //
  1328. return W3_FILE_INFO::Initialize();
  1329. }
  1330. VOID
  1331. W3_FILE_INFO_CACHE::Terminate(
  1332. VOID
  1333. )
  1334. /*++
  1335. Routine Description:
  1336. Terminate the file cache
  1337. Argument:
  1338. None
  1339. Return Value:
  1340. None
  1341. --*/
  1342. {
  1343. TerminateMemoryCache();
  1344. W3_FILE_INFO::Terminate();
  1345. }
  1346. VOID
  1347. W3_FILE_INFO_CACHE::DoDirmonInvalidationSpecific(
  1348. WCHAR * pszPath
  1349. )
  1350. /*++
  1351. Routine Description:
  1352. Handle dirmon invalidation
  1353. Arguments:
  1354. pszPath - Path which changed
  1355. Return Value:
  1356. HRESULT
  1357. --*/
  1358. {
  1359. HRESULT hr;
  1360. W3_FILE_INFO_KEY fileKey;
  1361. DBG_ASSERT( pszPath != NULL );
  1362. //
  1363. // We're not flushing all, then just lookup given file and flush it
  1364. //
  1365. hr = fileKey.CreateCacheKey( pszPath,
  1366. wcslen( pszPath ),
  1367. FALSE );
  1368. if ( SUCCEEDED( hr ) )
  1369. {
  1370. FlushCacheEntry( &fileKey );
  1371. }
  1372. }
  1373. //static
  1374. W3_FILE_INFO_CACHE *
  1375. W3_FILE_INFO_CACHE::GetFileCache(
  1376. VOID
  1377. )
  1378. {
  1379. DBG_ASSERT( g_pW3Server != NULL );
  1380. return g_pW3Server->QueryFileCache();
  1381. }