Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2324 lines
54 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 = INVALID_HANDLE_VALUE;
  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 ( _hFile != INVALID_HANDLE_VALUE )
  134. {
  135. if ( FAILED( ReadSecurityDescriptor() ) )
  136. {
  137. return NULL;
  138. }
  139. }
  140. else
  141. {
  142. //
  143. // The file is cached, therefore we must have security already
  144. //
  145. }
  146. return _bufSecDesc.QueryPtr();
  147. }
  148. HRESULT
  149. W3_FILE_INFO::GenerateETag(
  150. VOID
  151. )
  152. /*++
  153. Routine Description:
  154. Generate ETag string
  155. Arguments:
  156. None
  157. Return Value:
  158. HRESULT
  159. --*/
  160. {
  161. CHAR * psz = _achETag;
  162. PBYTE pbTime = (PBYTE) &_ftLastWriteTime;
  163. DWORD dwChangeNumber;
  164. const CHAR szHex[] = "0123456789abcdef";
  165. FILETIME ftNow;
  166. __int64 iNow;
  167. __int64 iFileTime;
  168. //
  169. // Is this ETag weak? If so put the preceding W/
  170. //
  171. GetSystemTimeAsFileTime(&ftNow);
  172. iNow = (__int64)*(__int64 *)&ftNow;
  173. iFileTime = (__int64)*(__int64 *)&_ftLastWriteTime;
  174. if ( ( iNow - iFileTime ) <= STRONG_ETAG_DELTA )
  175. {
  176. //
  177. // This is a weak ETag
  178. //
  179. *psz++ = 'W';
  180. *psz++ = '/';
  181. }
  182. //
  183. // System change number is from the metabase
  184. //
  185. dwChangeNumber = g_pW3Server->QuerySystemChangeNumber();
  186. //
  187. // Generate the meat of the ETag
  188. //
  189. *psz++ = '\"';
  190. for (int i = 0; i < 8; i++)
  191. {
  192. BYTE b = *pbTime++;
  193. BYTE bH = b >> 4;
  194. if (bH != 0)
  195. *psz++ = szHex[bH];
  196. *psz++ = szHex[b & 0xF];
  197. }
  198. *psz++ = ':';
  199. psz += strlen(_itoa((DWORD) dwChangeNumber, psz, 16));
  200. *psz++ = '\"';
  201. *psz = '\0';
  202. _cchETag = DIFF(psz - _achETag);
  203. return NO_ERROR;
  204. }
  205. HRESULT
  206. W3_FILE_INFO::GenerateLastModifiedTimeString(
  207. VOID
  208. )
  209. /*++
  210. Routine Description:
  211. Generate the Last-Modified-Time header string
  212. Arguments:
  213. None
  214. Return Value:
  215. HRESULT
  216. --*/
  217. {
  218. SYSTEMTIME st;
  219. FileTimeToSystemTime( &_ftLastWriteTime, &st );
  220. if ( !SystemTimeToGMT( st,
  221. _achLastModified,
  222. sizeof(_achLastModified) ) )
  223. {
  224. return HRESULT_FROM_WIN32( GetLastError() );
  225. }
  226. else
  227. {
  228. return NO_ERROR;
  229. }
  230. }
  231. HRESULT
  232. W3_FILE_INFO::DoAccessCheck(
  233. CACHE_USER * pFileCacheUser
  234. )
  235. /*++
  236. Routine Description:
  237. Check whether given token has access to this file
  238. Arguments:
  239. pFileCacheUser - User to access cache with
  240. Return Value:
  241. HRESULT
  242. --*/
  243. {
  244. BYTE psFile[SIZE_PRIVILEGE_SET];
  245. DWORD dwPS;
  246. DWORD dwGrantedAccess;
  247. BOOL fAccess;
  248. if ( pFileCacheUser == NULL )
  249. {
  250. DBG_ASSERT( FALSE );
  251. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  252. }
  253. //
  254. // If we don't have a security descriptor, then local system must have
  255. // accessed the file originally. Just return success
  256. //
  257. if ( pFileCacheUser->_hToken == NULL )
  258. {
  259. return NO_ERROR;
  260. }
  261. //
  262. // If we have a last-user-sid, and the caller provided a sid, then do a
  263. // quick check of sid equality
  264. //
  265. if ( QueryLastSid() != NULL &&
  266. pFileCacheUser->_pSid != NULL )
  267. {
  268. if ( EqualSid( QueryLastSid(), pFileCacheUser->_pSid ) )
  269. {
  270. return NO_ERROR;
  271. }
  272. }
  273. if ( ISUNC( QueryPhysicalPath() ) )
  274. {
  275. //
  276. // If this is a UNC file, and the webserver and the UNC server
  277. // are not on a domain, the sid of the user on the webserver will
  278. // not match the sid in the security-descriptor, so AccessCheck will
  279. // fail, instead do GetFileAttributes
  280. //
  281. if ( !SetThreadToken( NULL, pFileCacheUser->_hToken ) )
  282. {
  283. return HRESULT_FROM_WIN32( GetLastError() );
  284. }
  285. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  286. DWORD dwAttributes = GetFileAttributes(QueryPhysicalPath());
  287. DWORD dwErr = GetLastError();
  288. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  289. DBG_REQUIRE( RevertToSelf() );
  290. if ( dwAttributes == INVALID_FILE_ATTRIBUTES )
  291. {
  292. return HRESULT_FROM_WIN32( dwErr );
  293. }
  294. }
  295. else
  296. {
  297. //
  298. // Ok. Just use the token and cached security descriptor
  299. //
  300. dwPS = sizeof(psFile);
  301. ((PRIVILEGE_SET*)&psFile)->PrivilegeCount = 0;
  302. //
  303. // We must have a security descriptor if we've cached the file
  304. //
  305. DBG_ASSERT( QuerySecDesc() );
  306. if ( !AccessCheck( QuerySecDesc(),
  307. pFileCacheUser->_hToken,
  308. FILE_GENERIC_READ,
  309. &g_gmFile,
  310. (PRIVILEGE_SET*)psFile,
  311. &dwPS,
  312. &dwGrantedAccess,
  313. &fAccess ) || !fAccess )
  314. {
  315. return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  316. }
  317. }
  318. return NO_ERROR;
  319. }
  320. HRESULT
  321. W3_FILE_INFO::OpenFile(
  322. STRU & strFileName,
  323. CACHE_USER * pOpeningUser,
  324. BOOL fBufferFile
  325. )
  326. /*++
  327. Routine Description:
  328. Open the given file (but don't read in the file contents). This method
  329. does the minimum needed to allow the caller to make a reasonable
  330. decision about whether this file should be cached here or in UL
  331. Arguments:
  332. strFileName - file name to open
  333. pOpeningUser - User to open file under
  334. fBufferFile - Should the file be opened with FILE_FLAG_NO_BUFFERING?
  335. Return Value:
  336. HRESULT
  337. --*/
  338. {
  339. HANDLE hFile = INVALID_HANDLE_VALUE;
  340. STACK_STRU( strFilePath, MAX_PATH + 1 );
  341. HRESULT hr = NO_ERROR;
  342. BOOL fImpersonated = FALSE;
  343. BY_HANDLE_FILE_INFORMATION FileInfo;
  344. DWORD dwFileType;
  345. BOOL bRet;
  346. if ( pOpeningUser == NULL )
  347. {
  348. DBG_ASSERT( FALSE );
  349. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  350. }
  351. //
  352. // Turn off NT file canonicalization
  353. //
  354. hr = MakePathCanonicalizationProof( strFileName.QueryStr(),
  355. &strFilePath );
  356. if ( FAILED( hr ) )
  357. {
  358. goto Finished;
  359. }
  360. //
  361. // Avoid the infamous ::$DATA bug
  362. //
  363. if ( wcschr( strFileName.QueryStr() + 6, L':' ) != NULL )
  364. {
  365. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  366. goto Finished;
  367. }
  368. //
  369. // We may need to impersonate some other user to open the file
  370. //
  371. if ( pOpeningUser->_hToken != NULL )
  372. {
  373. if ( !SetThreadToken( NULL, pOpeningUser->_hToken ) )
  374. {
  375. hr = HRESULT_FROM_WIN32( GetLastError() );
  376. goto Finished;
  377. }
  378. fImpersonated = TRUE;
  379. }
  380. //
  381. // Open the file. CreateFile() perf can be underwhelming. We'll need to
  382. // potentially let out another thread while making the call. Much
  383. // like we do for calls into ISAPI extensions
  384. //
  385. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  386. hFile = CreateFile( strFilePath.QueryStr(),
  387. GENERIC_READ,
  388. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  389. NULL,
  390. OPEN_EXISTING,
  391. FILE_ATTRIBUTE_ENCRYPTED | FILE_DIRECTORY_FILE | FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS | (fBufferFile ? 0 : FILE_FLAG_NO_BUFFERING),
  392. NULL );
  393. if ( hFile == INVALID_HANDLE_VALUE )
  394. {
  395. hr = HRESULT_FROM_WIN32( GetLastError() );
  396. }
  397. //
  398. // Undo the threshold adjustment
  399. //
  400. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  401. if ( FAILED(hr) )
  402. {
  403. goto Finished;
  404. }
  405. //
  406. // Stop impersonating
  407. //
  408. if ( fImpersonated )
  409. {
  410. RevertToSelf();
  411. fImpersonated = FALSE;
  412. }
  413. //
  414. // We shouldn't be opening byte streams (like COM, LPT)
  415. //
  416. dwFileType = GetFileType( hFile );
  417. if ( dwFileType != FILE_TYPE_DISK )
  418. {
  419. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  420. CloseHandle( hFile );
  421. hFile = INVALID_HANDLE_VALUE;
  422. goto Finished;
  423. }
  424. //
  425. // Get file attributes
  426. //
  427. bRet = GetFileInformationByHandle( hFile, &FileInfo );
  428. if ( !bRet )
  429. {
  430. hr = HRESULT_FROM_WIN32( GetLastError() );
  431. CloseHandle( hFile );
  432. hFile = INVALID_HANDLE_VALUE;
  433. goto Finished;
  434. }
  435. //
  436. // Set the minimum properties now
  437. //
  438. _hFile = hFile;
  439. hFile = INVALID_HANDLE_VALUE;
  440. _ftLastWriteTime = FileInfo.ftLastWriteTime;
  441. _dwFileAttributes = FileInfo.dwFileAttributes;
  442. _nFileSizeLow = FileInfo.nFileSizeLow;
  443. _nFileSizeHigh = FileInfo.nFileSizeHigh;
  444. *((__int64 *)&_CastratedLastWriteTime)
  445. = (*((__int64 *)&_ftLastWriteTime) / 10000000) * 10000000;
  446. //
  447. // Create the ETag and LastModified strings
  448. //
  449. hr = GenerateETag();
  450. if ( FAILED( hr ) )
  451. {
  452. goto Finished;
  453. }
  454. hr = GenerateLastModifiedTimeString();
  455. if ( FAILED( hr ) )
  456. {
  457. goto Finished;
  458. }
  459. _msLastAttributeCheckTime = GetTickCount();
  460. //
  461. // Turn off the hidden attribute if this is a root directory listing
  462. // (root some times has the bit set for no apparent reason)
  463. //
  464. if ( _dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )
  465. {
  466. if ( strFileName.QueryCCH() >= 2 )
  467. {
  468. if ( strFileName.QueryStr()[ 1 ] == L':' )
  469. {
  470. if ( ( strFileName.QueryStr()[ 2 ] == L'\0' ) ||
  471. ( strFileName.QueryStr()[ 2 ] == L'\\' &&
  472. strFileName.QueryStr()[ 3 ] == L'\0' ) )
  473. {
  474. //
  475. // This looks like a local root. Mask out the bit
  476. //
  477. _dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
  478. }
  479. }
  480. }
  481. }
  482. Finished:
  483. if ( FAILED( hr ) )
  484. {
  485. if ( fImpersonated )
  486. {
  487. RevertToSelf();
  488. fImpersonated = FALSE;
  489. }
  490. if ( hFile != INVALID_HANDLE_VALUE )
  491. {
  492. CloseHandle( hFile );
  493. hFile = INVALID_HANDLE_VALUE;
  494. }
  495. }
  496. return hr;
  497. }
  498. HRESULT
  499. W3_FILE_INFO::ReadSecurityDescriptor(
  500. VOID
  501. )
  502. /*++
  503. Routine Description:
  504. Read security descriptor for current file
  505. Arguments:
  506. None
  507. Return Value:
  508. HRESULT
  509. --*/
  510. {
  511. DWORD cbRequired;
  512. //
  513. // Cache the security descriptor
  514. //
  515. if ( !GetKernelObjectSecurity( _hFile,
  516. OWNER_SECURITY_INFORMATION
  517. | GROUP_SECURITY_INFORMATION
  518. | DACL_SECURITY_INFORMATION,
  519. (PSECURITY_DESCRIPTOR) _bufSecDesc.QueryPtr(),
  520. _bufSecDesc.QuerySize(),
  521. &cbRequired ) )
  522. {
  523. if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
  524. {
  525. return HRESULT_FROM_WIN32( GetLastError() );
  526. }
  527. DBG_ASSERT( cbRequired > _bufSecDesc.QuerySize() );
  528. if ( !_bufSecDesc.Resize( cbRequired ) )
  529. {
  530. return HRESULT_FROM_WIN32( GetLastError() );
  531. }
  532. //
  533. // Try again with the bigger buffer. No more excuses
  534. //
  535. if ( !GetKernelObjectSecurity( _hFile,
  536. OWNER_SECURITY_INFORMATION
  537. | GROUP_SECURITY_INFORMATION
  538. | DACL_SECURITY_INFORMATION,
  539. (PSECURITY_DESCRIPTOR) _bufSecDesc.QueryPtr(),
  540. _bufSecDesc.QuerySize(),
  541. &cbRequired ) )
  542. {
  543. return HRESULT_FROM_WIN32( GetLastError() );
  544. }
  545. }
  546. return NO_ERROR;
  547. }
  548. HRESULT
  549. W3_FILE_INFO::MakeCacheable(
  550. CACHE_USER *pFileUser,
  551. FILE_CACHE_ASYNC_CONTEXT *pAsyncContext,
  552. BOOL *pfHandledSync,
  553. BOOL fCheckForExistenceOnly
  554. )
  555. /*++
  556. Routine Description:
  557. Make the file cacheable by reading contents into memory, and caching
  558. the security descriptor
  559. Arguments:
  560. pFileUser - User trying to open file
  561. pAsyncContext - Provides the information necessary to notify the caller
  562. when the file has been read when done async
  563. pfHandledSync - If provided, on return tells whether the open completed
  564. synchronously
  565. fCheckForExistenceOnly - Are we caching the state of existence only?
  566. Return Value:
  567. HRESULT
  568. --*/
  569. {
  570. HRESULT hr;
  571. W3_FILE_INFO_CACHE* pFileCache;
  572. if ( pFileUser == NULL )
  573. {
  574. DBG_ASSERT( FALSE );
  575. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  576. }
  577. DBG_ASSERT( IsCacheable() );
  578. //
  579. // We must have a file handle if we're here
  580. //
  581. DBG_ASSERT( _hFile != INVALID_HANDLE_VALUE );
  582. //
  583. // Get the security descriptor
  584. //
  585. hr = ReadSecurityDescriptor();
  586. if ( FAILED( hr ) )
  587. {
  588. return hr;
  589. }
  590. //
  591. // On top of reading the security descriptor, we will also store the
  592. // last sid accessing the file if available
  593. //
  594. if ( pFileUser->_pSid != NULL )
  595. {
  596. if ( GetLengthSid( pFileUser->_pSid ) <= sizeof( _abLastSid ) )
  597. {
  598. memcpy( _abLastSid,
  599. pFileUser->_pSid,
  600. GetLengthSid( pFileUser->_pSid ) );
  601. _pLastSid = (PSID) _abLastSid;
  602. }
  603. }
  604. //
  605. // Now read the contents of the file into memory since we cannot cache
  606. // the file handle itself
  607. //
  608. if ( _nFileSizeHigh > 0 )
  609. {
  610. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  611. }
  612. //
  613. // If we're just caching a descriptor to check the existence of the file,
  614. // then we don't need to read the file contents
  615. //
  616. if ( fCheckForExistenceOnly )
  617. {
  618. CloseHandle( _hFile );
  619. _hFile = INVALID_HANDLE_VALUE;
  620. DBG_ASSERT( _pFileBuffer == NULL );
  621. return NO_ERROR;
  622. }
  623. //
  624. // Set up the context for completion
  625. //
  626. if ( pAsyncContext != NULL )
  627. {
  628. pAsyncContext->pFileInfo = this;
  629. }
  630. pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
  631. //
  632. // Read the file
  633. //
  634. hr = pFileCache->ReadFileIntoMemoryCache( _hFile,
  635. _nFileSizeLow,
  636. (PVOID*) &_pFileBuffer,
  637. pAsyncContext,
  638. pfHandledSync );
  639. if ( FAILED( hr ) )
  640. {
  641. if ( pAsyncContext != NULL )
  642. {
  643. pAsyncContext->pFileInfo = NULL;
  644. }
  645. }
  646. else if ( pfHandledSync == NULL || *pfHandledSync )
  647. {
  648. CloseHandle( _hFile );
  649. _hFile = INVALID_HANDLE_VALUE;
  650. }
  651. return hr;
  652. }
  653. BOOL
  654. W3_FILE_INFO::IsCacheable(
  655. VOID
  656. ) const
  657. /*++
  658. Routine Description:
  659. Is this file cacheable? Specically, we should we even attempt to cache
  660. this file?
  661. Arguments:
  662. None
  663. Return Value:
  664. TRUE if cacheable
  665. --*/
  666. {
  667. W3_FILE_INFO_CACHE * pFileCache;
  668. pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
  669. DBG_ASSERT( pFileCache != NULL );
  670. //
  671. // Are we past the limit of file entries?
  672. //
  673. if ( pFileCache->QueryElementLimitExceeded() )
  674. {
  675. return FALSE;
  676. }
  677. return IsUlCacheable();
  678. }
  679. BOOL
  680. W3_FILE_INFO::IsUlCacheable(
  681. VOID
  682. ) const
  683. /*++
  684. Routine Description:
  685. Is this file cacheable? Specically, we should we even attempt to cache
  686. this file?
  687. Arguments:
  688. None
  689. Return Value:
  690. TRUE if cacheable
  691. --*/
  692. {
  693. LARGE_INTEGER liFileSize;
  694. W3_FILE_INFO_CACHE * pFileCache;
  695. pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
  696. DBG_ASSERT( pFileCache != NULL );
  697. //
  698. // No caching of directories
  699. //
  700. if ( _dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  701. {
  702. return FALSE;
  703. }
  704. //
  705. // No caching of encrypted files
  706. //
  707. if ( _dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED )
  708. {
  709. return FALSE;
  710. }
  711. //
  712. // No caching of file sizes greater than the configured threshold
  713. //
  714. liFileSize.LowPart = _nFileSizeLow;
  715. liFileSize.HighPart = _nFileSizeHigh;
  716. if ( liFileSize.QuadPart > pFileCache->QueryFileSizeThreshold() )
  717. {
  718. return FALSE;
  719. }
  720. return TRUE;
  721. }
  722. BOOL
  723. W3_FILE_INFO::QueryIsOkToFlushDirmon(
  724. WCHAR * pszPath,
  725. DWORD cchPath
  726. )
  727. /*++
  728. Routine Description:
  729. Determine whether this file entry should be flushed, given the path
  730. which has changed (dir monitor changed)
  731. Arguments:
  732. pszPath - Path which changed
  733. cchPath - Size of path changed
  734. Return Value:
  735. TRUE if we should flush, else FALSE
  736. --*/
  737. {
  738. DBG_ASSERT( _cacheKey._pszFileKey != NULL );
  739. if ( _wcsnicmp( _cacheKey._pszFileKey,
  740. pszPath,
  741. cchPath ) == 0 )
  742. {
  743. return TRUE;
  744. }
  745. else
  746. {
  747. return FALSE;
  748. }
  749. }
  750. //static
  751. HRESULT
  752. W3_FILE_INFO::Initialize(
  753. VOID
  754. )
  755. /*++
  756. Routine Description:
  757. Initialize W3_FILE_INFO lookaside
  758. Arguments:
  759. None
  760. Return Value:
  761. HRESULT
  762. --*/
  763. {
  764. ALLOC_CACHE_CONFIGURATION acConfig;
  765. HRESULT hr;
  766. //
  767. // Initialize allocation lookaside
  768. //
  769. acConfig.nConcurrency = 1;
  770. acConfig.nThreshold = 100;
  771. acConfig.cbSize = sizeof( W3_FILE_INFO );
  772. DBG_ASSERT( sm_pachW3FileInfo == NULL );
  773. sm_pachW3FileInfo = new ALLOC_CACHE_HANDLER( "W3_FILE_INFO",
  774. &acConfig );
  775. if ( sm_pachW3FileInfo == NULL )
  776. {
  777. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  778. DBGPRINTF(( DBG_CONTEXT,
  779. "Error initializing sm_pachW3FileInfo. hr = 0x%x\n",
  780. hr ));
  781. return hr;
  782. }
  783. return NO_ERROR;
  784. }
  785. //static
  786. VOID
  787. W3_FILE_INFO::Terminate(
  788. VOID
  789. )
  790. /*++
  791. Routine Description:
  792. Cleanup W3_FILE_INFO lookaside
  793. Arguments:
  794. None
  795. Return Value:
  796. None
  797. --*/
  798. {
  799. if ( sm_pachW3FileInfo != NULL )
  800. {
  801. delete sm_pachW3FileInfo;
  802. sm_pachW3FileInfo = NULL;
  803. }
  804. }
  805. //static
  806. VOID
  807. W3_FILE_INFO_CACHE::MemoryCacheAdjustor(
  808. PVOID pCache,
  809. BOOLEAN TimerOrWaitFired
  810. )
  811. /*++
  812. Routine Description:
  813. Called to adjust our memory cache size if necessary
  814. Arguments:
  815. pCache - Points to file cache
  816. Return Value:
  817. None
  818. --*/
  819. {
  820. W3_FILE_INFO_CACHE * pFileCache;
  821. MEMORYSTATUSEX MemoryStatus;
  822. pFileCache = (W3_FILE_INFO_CACHE*) pCache;
  823. MemoryStatus.dwLength = sizeof( MemoryStatus );
  824. GlobalMemoryStatusEx( &MemoryStatus );
  825. EnterCriticalSection( &( pFileCache->_csMemCache ) );
  826. pFileCache->_cbMemCacheLimit = min(
  827. MemoryStatus.ullAvailPhys + pFileCache->_cbMemCacheCurrentSize,
  828. MemoryStatus.ullTotalVirtual ) / 2;
  829. LeaveCriticalSection( &( pFileCache->_csMemCache ) );
  830. }
  831. W3_FILE_INFO_CACHE::W3_FILE_INFO_CACHE()
  832. {
  833. _cbFileSizeThreshold = DEFAULT_FILE_SIZE_THRESHOLD;
  834. _cmsecFileAttributeCheckThreshold = DEFAULT_FILE_ATTRIBUTE_CHECK_THRESHOLD * 1000;
  835. _cbMemoryCacheSize = 0;
  836. _cMaxFileEntries = 0;
  837. _cbMemCacheLimit = 0;
  838. _cbMemCacheCurrentSize = 0;
  839. _cbMaxMemCacheSize = 0;
  840. _hMemCacheHeap = NULL;
  841. _hTimer = NULL;
  842. _fEnableCache = TRUE;
  843. _fDoDirmonForUnc = FALSE;
  844. }
  845. W3_FILE_INFO_CACHE::~W3_FILE_INFO_CACHE()
  846. {
  847. }
  848. HRESULT
  849. W3_FILE_INFO_CACHE::InitializeMemoryCache(
  850. VOID
  851. )
  852. /*++
  853. Routine Description:
  854. Initialize the memory cache
  855. Arguments:
  856. None
  857. Return Value:
  858. HRESULT
  859. --*/
  860. {
  861. BOOL fRet;
  862. HRESULT hr = NO_ERROR;
  863. fRet = INITIALIZE_CRITICAL_SECTION( &_csMemCache );
  864. if ( !fRet )
  865. {
  866. return HRESULT_FROM_WIN32( GetLastError() );
  867. }
  868. //
  869. // If the memory cache size was not explicitly set, then we occasionally
  870. // check memory status when determining what to cache
  871. //
  872. if ( _cbMemoryCacheSize == 0 )
  873. {
  874. MEMORYSTATUSEX MemoryStatus;
  875. MemoryStatus.dwLength = sizeof( MemoryStatus );
  876. //
  877. // Get our own estimate of size of cache
  878. //
  879. GlobalMemoryStatusEx( &MemoryStatus );
  880. _cbMemCacheLimit = min( MemoryStatus.ullAvailPhys,
  881. MemoryStatus.ullTotalVirtual ) / 2;
  882. //
  883. // Setup timer so we can update our memory status
  884. //
  885. fRet = CreateTimerQueueTimer( &_hTimer,
  886. NULL,
  887. W3_FILE_INFO_CACHE::MemoryCacheAdjustor,
  888. this,
  889. 30000,
  890. 30000,
  891. WT_EXECUTELONGFUNCTION );
  892. if ( !fRet )
  893. {
  894. hr = HRESULT_FROM_WIN32( GetLastError() );
  895. }
  896. }
  897. else
  898. {
  899. _cbMemCacheLimit = _cbMemoryCacheSize;
  900. }
  901. //
  902. // Allocate a private heap
  903. //
  904. if ( SUCCEEDED( hr ) )
  905. {
  906. _hMemCacheHeap = HeapCreate( 0, 0, 0 );
  907. if ( _hMemCacheHeap == NULL )
  908. {
  909. hr = HRESULT_FROM_WIN32( GetLastError() );
  910. }
  911. }
  912. if ( FAILED( hr ) )
  913. {
  914. if ( _hMemCacheHeap != NULL )
  915. {
  916. HeapDestroy( _hMemCacheHeap );
  917. _hMemCacheHeap = NULL;
  918. }
  919. if ( _hTimer != NULL )
  920. {
  921. DeleteTimerQueueTimer( NULL,
  922. _hTimer,
  923. INVALID_HANDLE_VALUE );
  924. _hTimer = NULL;
  925. }
  926. DeleteCriticalSection( &_csMemCache );
  927. }
  928. return hr;
  929. }
  930. HRESULT
  931. W3_FILE_INFO_CACHE::ReadFileIntoMemoryCache(
  932. IN HANDLE hFile,
  933. IN DWORD cbFile,
  934. OUT VOID ** ppvBuffer,
  935. IN FILE_CACHE_ASYNC_CONTEXT *pAsyncContext,
  936. OUT BOOL *pfHandledSync
  937. )
  938. /*++
  939. Routine Description:
  940. Read contents of file into a buffer
  941. Arguments:
  942. hFile - Handle to valid file
  943. cbFile - Size of file ( ==> size of buffer )
  944. ppvBuffer - Filled in with pointer to buffer with file contents. Set
  945. to NULL on failure
  946. pAsyncContext - Provides the information necessary to notify the caller
  947. when the file has been read when done async
  948. pfHandledSync - If provided, on return tells whether the open completed
  949. synchronously
  950. Return Value:
  951. HRESULT
  952. --*/
  953. {
  954. BOOL bRet;
  955. VOID * pvBuffer = NULL;
  956. DWORD cbRead;
  957. OVERLAPPED Overlapped;
  958. HRESULT hr = NO_ERROR;
  959. DBG_ASSERT( hFile && ( hFile != INVALID_HANDLE_VALUE ) );
  960. DBG_ASSERT( ppvBuffer != NULL );
  961. ZeroMemory( &Overlapped, sizeof(Overlapped) );
  962. //
  963. // First check whether there will be room in cache for the blob
  964. //
  965. EnterCriticalSection( &_csMemCache );
  966. if ( ( _cbMemCacheCurrentSize + cbFile ) > _cbMemCacheLimit )
  967. {
  968. //
  969. // Not enough room for cache
  970. //
  971. LeaveCriticalSection( &_csMemCache );
  972. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  973. }
  974. _cbMemCacheCurrentSize += cbFile;
  975. _cbMaxMemCacheSize = max( _cbMaxMemCacheSize, _cbMemCacheCurrentSize );
  976. LeaveCriticalSection( &_csMemCache );
  977. //
  978. // Allocate blob for file
  979. //
  980. DBG_ASSERT( _hMemCacheHeap != NULL );
  981. pvBuffer = HeapAlloc( _hMemCacheHeap, 0, cbFile );
  982. if ( pvBuffer == NULL )
  983. {
  984. hr = HRESULT_FROM_WIN32( GetLastError() );
  985. goto Finished;
  986. }
  987. *ppvBuffer = pvBuffer;
  988. if (pAsyncContext != NULL)
  989. {
  990. ZeroMemory( &pAsyncContext->Overlapped, sizeof(pAsyncContext->Overlapped) );
  991. //
  992. // We can restrict what files we do async reads on further
  993. //
  994. if (!ThreadPoolBindIoCompletionCallback( hFile,
  995. W3_FILE_INFO::FileReadCompletion,
  996. 0 ))
  997. {
  998. pAsyncContext = NULL;
  999. *pfHandledSync = TRUE;
  1000. pfHandledSync = NULL;
  1001. }
  1002. }
  1003. //
  1004. // Read file into blob
  1005. //
  1006. bRet = ReadFile( hFile,
  1007. pvBuffer,
  1008. cbFile,
  1009. pAsyncContext ? NULL : &cbRead,
  1010. pAsyncContext ? &pAsyncContext->Overlapped : &Overlapped );
  1011. if (!bRet)
  1012. {
  1013. hr = HRESULT_FROM_WIN32( GetLastError() );
  1014. if ( hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING ) )
  1015. {
  1016. //
  1017. // Something bad happened
  1018. //
  1019. goto Finished;
  1020. }
  1021. //
  1022. // Reset the error lest we confuse ourselves later on cleanup
  1023. //
  1024. hr = NO_ERROR;
  1025. //
  1026. // If async is ok, return now
  1027. //
  1028. if (pAsyncContext != NULL)
  1029. {
  1030. *pfHandledSync = FALSE;
  1031. goto Finished;
  1032. }
  1033. //
  1034. // Wait for async read to complete (if async is not ok)
  1035. //
  1036. bRet = GetOverlappedResult( hFile,
  1037. &Overlapped,
  1038. &cbRead,
  1039. TRUE );
  1040. if ( !bRet )
  1041. {
  1042. //
  1043. // Something bad happened
  1044. //
  1045. hr = HRESULT_FROM_WIN32( GetLastError() );
  1046. goto Finished;
  1047. }
  1048. }
  1049. if (pAsyncContext != NULL)
  1050. {
  1051. *pfHandledSync = FALSE;
  1052. goto Finished;
  1053. }
  1054. //
  1055. // Ensure that we read the number of bytes we expected to
  1056. //
  1057. if ( cbRead != cbFile )
  1058. {
  1059. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  1060. }
  1061. Finished:
  1062. if ( FAILED( hr ) )
  1063. {
  1064. *ppvBuffer = NULL;
  1065. //
  1066. // Undo changes to memory cache statistics
  1067. //
  1068. EnterCriticalSection( &_csMemCache );
  1069. _cbMemCacheCurrentSize -= cbFile;
  1070. LeaveCriticalSection( &_csMemCache );
  1071. if ( pvBuffer != NULL )
  1072. {
  1073. HeapFree( _hMemCacheHeap, 0, pvBuffer );
  1074. pvBuffer = NULL;
  1075. }
  1076. }
  1077. return hr;
  1078. }
  1079. HRESULT
  1080. W3_FILE_INFO_CACHE::ReleaseFromMemoryCache(
  1081. IN VOID * pvBuffer,
  1082. IN DWORD cbBuffer
  1083. )
  1084. /*++
  1085. Routine Description:
  1086. Release file content blob from cache
  1087. Arguments:
  1088. pvBuffer - Buffer to release
  1089. cbBuffer - Size of buffer
  1090. Return Value:
  1091. HRESULT
  1092. --*/
  1093. {
  1094. DBG_ASSERT( pvBuffer );
  1095. DBG_ASSERT( _hMemCacheHeap != NULL);
  1096. HeapFree( _hMemCacheHeap, 0, pvBuffer );
  1097. EnterCriticalSection( &_csMemCache );
  1098. _cbMemCacheCurrentSize -= cbBuffer;
  1099. LeaveCriticalSection( &_csMemCache );
  1100. return NO_ERROR;
  1101. }
  1102. VOID
  1103. W3_FILE_INFO_CACHE::TerminateMemoryCache(
  1104. VOID
  1105. )
  1106. /*++
  1107. Routine Description:
  1108. Terminate memory cache
  1109. Arguments:
  1110. Return Value:
  1111. None
  1112. --*/
  1113. {
  1114. if ( _hTimer != NULL )
  1115. {
  1116. DeleteTimerQueueTimer( NULL,
  1117. _hTimer,
  1118. INVALID_HANDLE_VALUE );
  1119. _hTimer = NULL;
  1120. }
  1121. if ( _hMemCacheHeap != NULL )
  1122. {
  1123. HeapDestroy( _hMemCacheHeap );
  1124. _hMemCacheHeap = NULL;
  1125. }
  1126. DeleteCriticalSection( &_csMemCache );
  1127. }
  1128. HRESULT
  1129. W3_FILE_INFO_CACHE::GetFileInfo(
  1130. STRU & strFileName,
  1131. DIRMON_CONFIG * pDirmonConfig,
  1132. CACHE_USER * pOpeningUser,
  1133. BOOL fDoCache,
  1134. W3_FILE_INFO ** ppFileInfo,
  1135. FILE_CACHE_ASYNC_CONTEXT *pAsyncContext,
  1136. BOOL *pfHandledSync,
  1137. BOOL fAllowNoBuffering,
  1138. BOOL fCheckForExistenceOnly
  1139. )
  1140. /*++
  1141. Routine Description:
  1142. Returns a W3_FILE_INFO for the given file path. Depending on fDoCache,
  1143. this W3_FILE_INFO will be cached
  1144. Arguments:
  1145. strFileName - file name to find
  1146. pDirmonConfig - Dir monitor config
  1147. pOpeningUser - Token for user accessing the cache
  1148. fDoCache - Set to TRUE if we should attempt to cache if possible
  1149. ppFileInfo - Points to W3_FILE_INFO on success
  1150. pAsyncContext - Provides the information necessary to notify the caller
  1151. when the file has been read when done async
  1152. pfHandledSync - If provided, on return tells whether the open completed
  1153. synchronously
  1154. fAllowNoBuffering - Allow the file to be opened with FILE_FLAG_NO_BUFFERING
  1155. fCheckForExistenceOnly - If TRUE, ensure the file is accessible but do
  1156. not cache if it is not already cached
  1157. Return Value:
  1158. HRESULT
  1159. --*/
  1160. {
  1161. W3_FILE_INFO_KEY fileKey;
  1162. DIRMON_CONFIG DefaultDirmonConfig;
  1163. STACK_STRU( strParentDir, MAX_PATH );
  1164. WCHAR * pszParentDir;
  1165. W3_FILE_INFO * pFileInfo;
  1166. HRESULT hr;
  1167. STACK_STRU( strFilePathKey, MAX_PATH );
  1168. BOOL fShouldCacheHint = FALSE;
  1169. BOOL fBufferFile = TRUE;
  1170. if ( ppFileInfo == NULL ||
  1171. pOpeningUser == NULL )
  1172. {
  1173. DBG_ASSERT( FALSE );
  1174. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1175. }
  1176. //
  1177. // Both of them should be null (for sync) or non-null (for async)
  1178. //
  1179. if ( ( pAsyncContext != NULL && pfHandledSync == NULL ) ||
  1180. ( pAsyncContext == NULL && pfHandledSync != NULL ) )
  1181. {
  1182. DBG_ASSERT( FALSE );
  1183. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1184. }
  1185. *ppFileInfo = NULL;
  1186. //
  1187. // We need to upper case the path to avoid a bunch of insensitive
  1188. // compares in the hash table lookup
  1189. //
  1190. hr = strFilePathKey.Copy( strFileName );
  1191. if ( FAILED( hr ) )
  1192. {
  1193. return hr;
  1194. }
  1195. _wcsupr( strFilePathKey.QueryStr() );
  1196. //
  1197. // If the cache is enabled, lookup there first
  1198. //
  1199. if ( QueryCacheEnabled() )
  1200. {
  1201. //
  1202. // Make a key for the lookup
  1203. //
  1204. hr = fileKey.CreateCacheKey( strFilePathKey.QueryStr(),
  1205. strFilePathKey.QueryCCH(),
  1206. FALSE );
  1207. if ( FAILED( hr ) )
  1208. {
  1209. return hr;
  1210. }
  1211. //
  1212. // Look it up
  1213. //
  1214. hr = FindCacheEntry( &fileKey,
  1215. (CACHE_ENTRY **)&pFileInfo,
  1216. &fShouldCacheHint );
  1217. if ( SUCCEEDED( hr ) )
  1218. {
  1219. BOOL fHasChanged = FALSE;
  1220. DBG_ASSERT( pFileInfo != NULL );
  1221. //
  1222. // If it is a UNC file, we do not do dirmon but rather make sure
  1223. // it has not changed on each open
  1224. // Otherwise, we need to do an accesscheck against the current user
  1225. //
  1226. if ( !_fDoDirmonForUnc &&
  1227. ISUNC( strFilePathKey.QueryStr() ) &&
  1228. ( GetTickCount() - pFileInfo->QueryLastAttributeCheckTime() ) >
  1229. _cmsecFileAttributeCheckThreshold )
  1230. {
  1231. hr = pFileInfo->CheckIfFileHasChanged( &fHasChanged,
  1232. pOpeningUser );
  1233. }
  1234. else
  1235. {
  1236. hr = pFileInfo->DoAccessCheck( pOpeningUser );
  1237. }
  1238. if ( FAILED( hr ) )
  1239. {
  1240. pFileInfo->DereferenceCacheEntry();
  1241. return hr;
  1242. }
  1243. if ( !fHasChanged )
  1244. {
  1245. //
  1246. // We have found a cached entry which we have access to.
  1247. //
  1248. // One more complication: If this file entry exists without
  1249. // a handle/memory-buffer, then it is there for file
  1250. // existence purposes only. If we're currently checking
  1251. // for existence only, then great! If not, this entry is
  1252. // bogus and we'll have to stuff in a new content entry
  1253. //
  1254. if ( pFileInfo->QueryFileBuffer() == NULL &&
  1255. pFileInfo->QueryFileHandle() == INVALID_HANDLE_VALUE &&
  1256. !fCheckForExistenceOnly )
  1257. {
  1258. //
  1259. // We need a real cache entry. Get rid of this entry
  1260. //
  1261. FlushCacheEntry( pFileInfo->QueryCacheKey() );
  1262. pFileInfo->DereferenceCacheEntry();
  1263. pFileInfo = NULL;
  1264. //
  1265. // Fall thru so that we try to cache it again
  1266. // (this time completely).
  1267. //
  1268. }
  1269. else
  1270. {
  1271. //
  1272. // We've satisfied the file cache request. Return the
  1273. // descriptor
  1274. //
  1275. *ppFileInfo = pFileInfo;
  1276. if ( pfHandledSync != NULL )
  1277. {
  1278. *pfHandledSync = TRUE;
  1279. }
  1280. return NO_ERROR;
  1281. }
  1282. }
  1283. else
  1284. {
  1285. pFileInfo->DereferenceCacheEntry();
  1286. pFileInfo = NULL;
  1287. //
  1288. // Release this entry but continue on to get a fresh one
  1289. //
  1290. }
  1291. }
  1292. }
  1293. //
  1294. // OK. We have to open the file. Figure out whether we should open the file
  1295. // buffered or not
  1296. //
  1297. if ( !QueryCacheEnabled() ||
  1298. !fDoCache ||
  1299. !fShouldCacheHint )
  1300. {
  1301. if ( fAllowNoBuffering )
  1302. {
  1303. fBufferFile = FALSE;
  1304. }
  1305. }
  1306. //
  1307. // We will simply open the file and return the object
  1308. //
  1309. pFileInfo = new W3_FILE_INFO( this );
  1310. if ( pFileInfo == NULL )
  1311. {
  1312. return HRESULT_FROM_WIN32( GetLastError() );
  1313. }
  1314. //
  1315. // Setup the cache key (in case we want to cache it)
  1316. //
  1317. hr = ((W3_FILE_INFO_KEY*) pFileInfo->QueryCacheKey())->CreateCacheKey(
  1318. strFilePathKey.QueryStr(),
  1319. strFilePathKey.QueryCCH(),
  1320. TRUE );
  1321. if ( FAILED( hr ) )
  1322. {
  1323. pFileInfo->DereferenceCacheEntry();
  1324. return hr;
  1325. }
  1326. //
  1327. // Open the file. Note that we use the original strFileName
  1328. // parameter (which is not upper cased). This is done to support
  1329. // case sensitive file systems
  1330. //
  1331. hr = pFileInfo->OpenFile( strFileName,
  1332. pOpeningUser,
  1333. fBufferFile );
  1334. if ( FAILED( hr ) )
  1335. {
  1336. pFileInfo->DereferenceCacheEntry();
  1337. return hr;
  1338. }
  1339. //
  1340. // If we aren't asked to cache the file, OR the file is not cacheable
  1341. // then we can return it now
  1342. //
  1343. if ( !QueryCacheEnabled() ||
  1344. !fDoCache ||
  1345. !fShouldCacheHint ||
  1346. !pFileInfo->IsUlCacheable() )
  1347. {
  1348. *ppFileInfo = pFileInfo;
  1349. if (pfHandledSync != NULL)
  1350. {
  1351. *pfHandledSync = TRUE;
  1352. }
  1353. return NO_ERROR;
  1354. }
  1355. pFileInfo->AllowUlCache();
  1356. if ( !pFileInfo->IsCacheable() )
  1357. {
  1358. *ppFileInfo = pFileInfo;
  1359. if ( pfHandledSync != NULL )
  1360. {
  1361. *pfHandledSync = TRUE;
  1362. }
  1363. return NO_ERROR;
  1364. }
  1365. //
  1366. // In general, caching a file means dirmoning it. The exception is if this is a UNC
  1367. // file and we're configured to do file validation
  1368. //
  1369. if ( _fDoDirmonForUnc ||
  1370. !ISUNC( strFilePathKey.QueryStr() ) )
  1371. {
  1372. //
  1373. // If we're supposed to cache but no dirmon was configured, then just
  1374. // assume the directory to monitor is the parent directory (and token
  1375. // to use is NULL)
  1376. //
  1377. if ( pDirmonConfig == NULL )
  1378. {
  1379. DefaultDirmonConfig.hToken = NULL;
  1380. pszParentDir = wcsrchr( strFilePathKey.QueryStr(), L'\\' );
  1381. if ( pszParentDir != NULL )
  1382. {
  1383. hr = strParentDir.Copy( strFilePathKey.QueryStr(),
  1384. DIFF( pszParentDir - strFilePathKey.QueryStr() ) );
  1385. if ( SUCCEEDED( hr ) )
  1386. {
  1387. DefaultDirmonConfig.pszDirPath = strParentDir.QueryStr();
  1388. pDirmonConfig = &DefaultDirmonConfig;
  1389. }
  1390. }
  1391. }
  1392. //
  1393. // If we still don't have a dir mon configuration, then just don't cache
  1394. //
  1395. if ( pDirmonConfig == NULL )
  1396. {
  1397. *ppFileInfo = pFileInfo;
  1398. if (pfHandledSync != NULL)
  1399. {
  1400. *pfHandledSync = TRUE;
  1401. }
  1402. return NO_ERROR;
  1403. }
  1404. //
  1405. // Start monitoring the appropriate directory for changes
  1406. //
  1407. hr = pFileInfo->AddDirmonInvalidator( pDirmonConfig );
  1408. if ( FAILED( hr ) )
  1409. {
  1410. //
  1411. // If we can't monitor the directory, then just don't cache the item
  1412. //
  1413. *ppFileInfo = pFileInfo;
  1414. if (pfHandledSync != NULL)
  1415. {
  1416. *pfHandledSync = TRUE;
  1417. }
  1418. return NO_ERROR;
  1419. }
  1420. }
  1421. //
  1422. // Attempt to cache the file. Caching the file means reading the
  1423. // contents into memory, as well as caching the security descriptor
  1424. //
  1425. hr = pFileInfo->MakeCacheable( pOpeningUser,
  1426. pAsyncContext,
  1427. pfHandledSync,
  1428. fCheckForExistenceOnly );
  1429. if ( FAILED( hr ) )
  1430. {
  1431. *ppFileInfo = pFileInfo;
  1432. if (pfHandledSync != NULL)
  1433. {
  1434. *pfHandledSync = TRUE;
  1435. }
  1436. return NO_ERROR;
  1437. }
  1438. //
  1439. // If an async read is pending, then return out now
  1440. //
  1441. if (pfHandledSync != NULL &&
  1442. !*pfHandledSync)
  1443. {
  1444. *ppFileInfo = NULL;
  1445. return NO_ERROR;
  1446. }
  1447. //
  1448. // Insert into the hash table. AddCacheEntry() will only error if
  1449. // we cannot add the item, that is not fatal and we will simply return
  1450. // this item and it will cleanup on dereference
  1451. //
  1452. AddCacheEntry( pFileInfo );
  1453. *ppFileInfo = pFileInfo;
  1454. return NO_ERROR;
  1455. }
  1456. HRESULT
  1457. W3_FILE_INFO_CACHE::Initialize(
  1458. VOID
  1459. )
  1460. /*++
  1461. Routine Description:
  1462. Initialize the file cache
  1463. Arguments:
  1464. None
  1465. Return Value:
  1466. HRESULT
  1467. --*/
  1468. {
  1469. DWORD dwError;
  1470. DWORD dwType;
  1471. DWORD dwValue;
  1472. DWORD cbData;
  1473. DWORD csecTTL = DEFAULT_W3_FILE_INFO_CACHE_TTL;
  1474. DWORD csecActivity = DEFAULT_W3_FILE_INFO_CACHE_ACTIVITY;
  1475. HKEY hKey = NULL;
  1476. HRESULT hr;
  1477. CACHE_HINT_CONFIG cacheHintConfig;
  1478. //
  1479. // Read the registry configuration of the file cache.
  1480. // For now, that is just the legacy confiugration from IIS 5.x
  1481. //
  1482. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1483. L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
  1484. 0,
  1485. KEY_READ,
  1486. &hKey );
  1487. if ( dwError == ERROR_SUCCESS )
  1488. {
  1489. DBG_ASSERT( hKey != NULL );
  1490. //
  1491. // Should we be file caching at all?
  1492. //
  1493. cbData = sizeof( DWORD );
  1494. dwError = RegQueryValueEx( hKey,
  1495. L"DisableMemoryCache",
  1496. NULL,
  1497. &dwType,
  1498. (LPBYTE) &dwValue,
  1499. &cbData );
  1500. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1501. {
  1502. _fEnableCache = dwValue ? FALSE : TRUE;
  1503. }
  1504. //
  1505. // What is the biggest file we should cache in user mode?
  1506. //
  1507. cbData = sizeof( DWORD );
  1508. dwError = RegQueryValueEx( hKey,
  1509. L"MaxCachedFileSize",
  1510. NULL,
  1511. &dwType,
  1512. (LPBYTE) &dwValue,
  1513. &cbData );
  1514. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1515. {
  1516. _cbFileSizeThreshold = dwValue;
  1517. }
  1518. //
  1519. // What is the size of our memory cache? Size is in MB
  1520. //
  1521. cbData = sizeof( DWORD );
  1522. dwError = RegQueryValueEx( hKey,
  1523. L"MemCacheSize",
  1524. NULL,
  1525. &dwType,
  1526. (LPBYTE)&dwValue,
  1527. &cbData );
  1528. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1529. {
  1530. _cbMemoryCacheSize = dwValue * (1024 * 1024);
  1531. }
  1532. //
  1533. // Read the maximum # of files in cache
  1534. //
  1535. cbData = sizeof( DWORD );
  1536. dwError = RegQueryValueEx( hKey,
  1537. L"MaxOpenFiles",
  1538. NULL,
  1539. &dwType,
  1540. (LPBYTE) &dwValue,
  1541. &cbData );
  1542. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1543. {
  1544. _cMaxFileEntries = dwValue;
  1545. }
  1546. //
  1547. // What is the TTL for the file cache?
  1548. //
  1549. cbData = sizeof( DWORD );
  1550. dwError = RegQueryValueEx( hKey,
  1551. L"ObjectCacheTTL",
  1552. NULL,
  1553. &dwType,
  1554. (LPBYTE) &dwValue,
  1555. &cbData );
  1556. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1557. {
  1558. csecTTL = dwValue;
  1559. }
  1560. //
  1561. // What is the activity period before putting into cache
  1562. //
  1563. cbData = sizeof( DWORD );
  1564. dwError = RegQueryValueEx( hKey,
  1565. L"ActivityPeriod",
  1566. NULL,
  1567. &dwType,
  1568. (LPBYTE) &dwValue,
  1569. &cbData );
  1570. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1571. {
  1572. csecActivity = dwValue;
  1573. }
  1574. //
  1575. // Do we do dirmonitoring for UNC's?
  1576. //
  1577. cbData = sizeof( DWORD );
  1578. dwError = RegQueryValueEx( hKey,
  1579. L"DoDirMonitoringForUnc",
  1580. NULL,
  1581. &dwType,
  1582. (LPBYTE) &dwValue,
  1583. &cbData );
  1584. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1585. {
  1586. _fDoDirmonForUnc = dwValue;
  1587. }
  1588. //
  1589. // What is the file attribute threshold time (for UNCs without dirmon)
  1590. //
  1591. cbData = sizeof( DWORD );
  1592. dwError = RegQueryValueEx( hKey,
  1593. L"FileAttributeCheckThreshold",
  1594. NULL,
  1595. &dwType,
  1596. (LPBYTE) &dwValue,
  1597. &cbData );
  1598. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  1599. {
  1600. _cmsecFileAttributeCheckThreshold = dwValue * 1000;
  1601. }
  1602. RegCloseKey( hKey );
  1603. }
  1604. //
  1605. // Initialize memory cache
  1606. //
  1607. hr = InitializeMemoryCache();
  1608. if ( FAILED( hr ) )
  1609. {
  1610. return hr;
  1611. }
  1612. //
  1613. // Setup cache hint config (for now hardcoded)
  1614. //
  1615. if ( csecActivity != 0 )
  1616. {
  1617. cacheHintConfig.cmsecActivityWindow = csecActivity * 1000;
  1618. cacheHintConfig.cmsecScavengeTime = cacheHintConfig.cmsecActivityWindow * 2;
  1619. cacheHintConfig.cmsecTTL = cacheHintConfig.cmsecActivityWindow * 2;
  1620. }
  1621. //
  1622. // We'll use TTL for scavenge period, and expect two inactive periods to
  1623. // flush
  1624. //
  1625. hr = SetCacheConfiguration( csecTTL * 1000,
  1626. csecTTL * 1000,
  1627. CACHE_INVALIDATION_DIRMON_FLUSH |
  1628. CACHE_INVALIDATION_DIRMON_SPECIFIC,
  1629. csecActivity ? &cacheHintConfig : NULL );
  1630. if ( FAILED( hr ) )
  1631. {
  1632. TerminateMemoryCache();
  1633. return hr;
  1634. }
  1635. //
  1636. // Initialize file info lookaside
  1637. //
  1638. hr = W3_FILE_INFO::Initialize();
  1639. if ( FAILED( hr ) )
  1640. {
  1641. TerminateMemoryCache();
  1642. }
  1643. return hr;
  1644. }
  1645. VOID
  1646. W3_FILE_INFO_CACHE::Terminate(
  1647. VOID
  1648. )
  1649. /*++
  1650. Routine Description:
  1651. Terminate the file cache
  1652. Argument:
  1653. None
  1654. Return Value:
  1655. None
  1656. --*/
  1657. {
  1658. TerminateMemoryCache();
  1659. W3_FILE_INFO::Terminate();
  1660. }
  1661. VOID
  1662. W3_FILE_INFO_CACHE::DoDirmonInvalidationSpecific(
  1663. WCHAR * pszPath
  1664. )
  1665. /*++
  1666. Routine Description:
  1667. Handle dirmon invalidation
  1668. Arguments:
  1669. pszPath - Path which changed
  1670. Return Value:
  1671. HRESULT
  1672. --*/
  1673. {
  1674. HRESULT hr;
  1675. W3_FILE_INFO_KEY fileKey;
  1676. DBG_ASSERT( pszPath != NULL );
  1677. //
  1678. // We're not flushing all, then just lookup given file and flush it
  1679. //
  1680. hr = fileKey.CreateCacheKey( pszPath,
  1681. wcslen( pszPath ),
  1682. FALSE );
  1683. if ( SUCCEEDED( hr ) )
  1684. {
  1685. FlushCacheEntry( &fileKey );
  1686. }
  1687. }
  1688. //static
  1689. W3_FILE_INFO_CACHE *
  1690. W3_FILE_INFO_CACHE::GetFileCache(
  1691. VOID
  1692. )
  1693. {
  1694. DBG_ASSERT( g_pW3Server != NULL );
  1695. return g_pW3Server->QueryFileCache();
  1696. }
  1697. HRESULT
  1698. W3_FILE_INFO::CheckIfFileHasChanged(
  1699. BOOL *pfHasChanged,
  1700. CACHE_USER *pOpeningUser
  1701. )
  1702. /*++
  1703. This function determines whether the file has changed for what is
  1704. in the cache. This is useful when we do not do directory change
  1705. monitoring on UNC files.
  1706. --*/
  1707. {
  1708. HRESULT hr = S_OK;
  1709. BOOL fImpersonated = FALSE;
  1710. //
  1711. // We may need to impersonate some other user to open the file
  1712. //
  1713. if ( pOpeningUser->_hToken != NULL )
  1714. {
  1715. if ( !SetThreadToken( NULL, pOpeningUser->_hToken ) )
  1716. {
  1717. return HRESULT_FROM_WIN32( GetLastError() );
  1718. }
  1719. fImpersonated = TRUE;
  1720. }
  1721. //
  1722. // Going to a UNC, let us bump up our thread count
  1723. //
  1724. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  1725. WIN32_FILE_ATTRIBUTE_DATA fileData;
  1726. if (!GetFileAttributesEx(_cacheKey._pszFileKey,
  1727. GetFileExInfoStandard,
  1728. &fileData))
  1729. {
  1730. hr = HRESULT_FROM_WIN32(GetLastError());
  1731. goto Finished;
  1732. }
  1733. //
  1734. // If the attributes, WriteTime, and if it is a file, size are same
  1735. // then the file has not changed
  1736. //
  1737. if (fileData.dwFileAttributes == _dwFileAttributes &&
  1738. *(__int64 *)&fileData.ftLastWriteTime == *(__int64 *)&_ftLastWriteTime &&
  1739. ((fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
  1740. (fileData.nFileSizeHigh == _nFileSizeHigh &&
  1741. fileData.nFileSizeLow == _nFileSizeLow)
  1742. )
  1743. )
  1744. {
  1745. *pfHasChanged = FALSE;
  1746. _msLastAttributeCheckTime = GetTickCount();
  1747. }
  1748. else
  1749. {
  1750. *pfHasChanged = TRUE;
  1751. }
  1752. Finished:
  1753. //
  1754. // We are back, we can bump down the count again
  1755. //
  1756. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  1757. if (fImpersonated)
  1758. {
  1759. DBG_REQUIRE(RevertToSelf());
  1760. }
  1761. return hr;
  1762. }
  1763. BOOL
  1764. W3_FILE_INFO::Checkout(
  1765. CACHE_USER *pOpeningUser
  1766. )
  1767. {
  1768. BOOL fHasChanged = FALSE;
  1769. //
  1770. // If it is a UNC file, we do not do dirmon but rather make sure
  1771. // it has not changed on each open
  1772. //
  1773. if (!g_pW3Server->QueryFileCache()->QueryDoDirmonForUnc() &&
  1774. ISUNC(_cacheKey._pszFileKey) &&
  1775. ( GetTickCount() - QueryLastAttributeCheckTime() ) >
  1776. g_pW3Server->QueryFileCache()->QueryFileAttributeCheckThreshold() )
  1777. {
  1778. if (FAILED(CheckIfFileHasChanged(&fHasChanged,
  1779. pOpeningUser)) ||
  1780. fHasChanged)
  1781. {
  1782. return FALSE;
  1783. }
  1784. }
  1785. return CACHE_ENTRY::Checkout(pOpeningUser);
  1786. }
  1787. // static
  1788. VOID CALLBACK W3_FILE_INFO::FileReadCompletion(
  1789. DWORD dwErrorCode,
  1790. DWORD dwNumberOfBytesTransfered,
  1791. LPOVERLAPPED lpOverlapped)
  1792. {
  1793. FILE_CACHE_ASYNC_CONTEXT *pAsyncContext =
  1794. CONTAINING_RECORD(lpOverlapped,
  1795. FILE_CACHE_ASYNC_CONTEXT,
  1796. Overlapped);
  1797. HRESULT hr = S_OK;
  1798. if (dwErrorCode != 0)
  1799. {
  1800. hr = HRESULT_FROM_WIN32(dwErrorCode);
  1801. }
  1802. else
  1803. {
  1804. ULARGE_INTEGER liSize;
  1805. W3_FILE_INFO *pFileInfo = pAsyncContext->pFileInfo;
  1806. pFileInfo->QuerySize(&liSize);
  1807. DBG_ASSERT(dwNumberOfBytesTransfered == liSize.QuadPart);
  1808. CloseHandle(pFileInfo->_hFile);
  1809. pFileInfo->_hFile = INVALID_HANDLE_VALUE;
  1810. g_pW3Server->QueryFileCache()->AddCacheEntry( pFileInfo );
  1811. }
  1812. pAsyncContext->pfnCallback(pAsyncContext, hr);
  1813. }