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.

3521 lines
102 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. contain.cxx
  5. Abstract:
  6. Abstract-for-module.
  7. Contents:
  8. Author:
  9. 16-Nov-1995
  10. [Environment:]
  11. optional-environment-info (e.g. kernel mode only...)
  12. [Notes:]
  13. optional-notes
  14. Revision History:
  15. 16-Nov-1995
  16. Created
  17. Shishir Pardikar (shishirp) added: (as of 7/6/96)
  18. 1) Container allows any size file. The file is cleanedup at scavneging time
  19. 2) Free 100% uses cleanupallurls, reinitializes memorymappedfile and cleansup
  20. all directories
  21. 3) CurrentCacheSIze and Cache Limit in the memorymapped file itself
  22. 4) FileCreation time and lastcheckedtime added
  23. 5) friendly naming scheme
  24. 25-Sep-1997
  25. Ahsan Kabir (akabir) made minor alterations to GetFileSizeAndTimeByName.
  26. --*/
  27. /*++
  28. Copyright (c) 1994 Microsoft Corporation
  29. Module Name:
  30. contain.cxx
  31. Abstract:
  32. Contains code that implements CONTAINER classes defined in
  33. contain.hxx.
  34. Author:
  35. Madan Appiah (madana) 28-Dec-1994
  36. Environment:
  37. User Mode - Win32
  38. Revision History:
  39. --*/
  40. #include <wininetp.h>
  41. #include <cache.hxx>
  42. // Beta logging
  43. #ifdef BETA_LOGGING
  44. #define BETA_LOG(stat) \
  45. {DWORD dw; INET_ASSERT (IsContentContainer()); \
  46. IncrementHeaderData (CACHE_HEADER_DATA_##stat, &dw);}
  47. #else
  48. #define BETA_LOG(stat) do { } while(0)
  49. #endif
  50. // Typedef for GetFileAttributeEx function
  51. typedef BOOL (WINAPI *PFNGETFILEATTREX)(LPCTSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
  52. extern PFNGETFILEATTREX gpfnGetFileAttributesEx;
  53. // private functions
  54. DWORD GetFileSizeAndTimeByName(
  55. LPCTSTR FileName,
  56. WIN32_FILE_ATTRIBUTE_DATA *lpFileAttrData
  57. )
  58. /*++
  59. Routine Description:
  60. Get the size and creation time and file attributes of the specified file.
  61. Arguments:
  62. FileName : full path name of the file whose size is asked for.
  63. lpFindData : pointer to a WIN32_FIND_DATA structure where the size and time value is
  64. returned. On WinNT, only the size and time fields are valid.
  65. Return Value:
  66. Windows Error Code.
  67. --*/
  68. {
  69. INET_ASSERT(lpFileAttrData != NULL);
  70. if (gpfnGetFileAttributesEx)
  71. {
  72. if(!gpfnGetFileAttributesEx(FileName, GetFileExInfoStandard, (LPVOID)lpFileAttrData))
  73. return( GetLastError() );
  74. }
  75. else
  76. {
  77. HANDLE hHandle;
  78. WIN32_FIND_DATA FindData;
  79. hHandle = FindFirstFile(FileName, &FindData);
  80. if( hHandle == INVALID_HANDLE_VALUE ) {
  81. return( GetLastError() );
  82. }
  83. memset(lpFileAttrData, 0, sizeof(WIN32_FILE_ATTRIBUTE_DATA));
  84. lpFileAttrData->dwFileAttributes = FindData.dwFileAttributes;
  85. lpFileAttrData->nFileSizeLow = FindData.nFileSizeLow;
  86. lpFileAttrData->nFileSizeHigh = FindData.nFileSizeHigh;
  87. lpFileAttrData->ftCreationTime = FindData.ftCreationTime;
  88. FindClose(hHandle);
  89. }
  90. return(ERROR_SUCCESS);
  91. }
  92. DWORD
  93. GetFileSizeByName(
  94. LPCTSTR pszFileName,
  95. DWORD *pdwFileSize
  96. )
  97. /*++
  98. Routine Description:
  99. Get the size of the specified file.
  100. Arguments:
  101. FileName : full path name of the file whose size is asked for.
  102. FileSize : pointer to a longlong location where the size value is
  103. returned.
  104. Return Value:
  105. Windows Error Code.
  106. --*/
  107. {
  108. DWORD dwError = ERROR_SUCCESS;
  109. // get the size of the file being cached.
  110. // since we do not handle 4+gb files, we can safely ignore the high dword
  111. INET_ASSERT(pdwFileSize!=NULL);
  112. if (gpfnGetFileAttributesEx)
  113. {
  114. WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
  115. if(!gpfnGetFileAttributesEx(pszFileName, GetFileExInfoStandard, &FileAttrData))
  116. return( GetLastError() );
  117. *pdwFileSize = FileAttrData.nFileSizeLow;
  118. }
  119. else
  120. {
  121. HANDLE hfFileHandle = CreateFile(
  122. pszFileName,
  123. GENERIC_READ,
  124. FILE_SHARE_READ | FILE_SHARE_WRITE,
  125. NULL,
  126. OPEN_EXISTING,
  127. FILE_ATTRIBUTE_NORMAL,
  128. NULL );
  129. if( hfFileHandle == INVALID_HANDLE_VALUE )
  130. return(GetLastError());
  131. *pdwFileSize = GetFileSize( hfFileHandle, NULL);
  132. if(*pdwFileSize == 0xFFFFFFFF)
  133. dwError = GetLastError();
  134. CloseHandle( hfFileHandle );
  135. }
  136. return dwError;
  137. }
  138. // -------------------------------URL_CONTAINER----------------------------------- //
  139. /*-----------------------------------------------------------------------------
  140. URL_CONTAINER::URL_CONTAINER Sets path, prefix and limit.
  141. -----------------------------------------------------------------------------*/
  142. URL_CONTAINER::URL_CONTAINER(LPTSTR CacheName, LPTSTR CachePath,
  143. LPTSTR CachePrefix, LONGLONG CacheStartUpLimit,
  144. DWORD dwOptions)
  145. {
  146. _fIsInitialized = FALSE;
  147. _fPerUserItem = TRUE;
  148. _dwLastReference = GetTickCountWrap();
  149. _fDeleted = FALSE;
  150. _fMarked = FALSE;
  151. _fDeletePending = FALSE;
  152. _fMustLaunchScavenger = FALSE;
  153. //#ifdef CHECKLOCK_NORMAL
  154. _dwTaken = 0;
  155. //#endif
  156. _dwRefCount = 0;
  157. _dwOptions = dwOptions;
  158. _dwBytesDownloaded = _dwItemsDownloaded = 0;
  159. _CacheEntryType = 0;
  160. _CacheName = NULL;
  161. _CachePath = NULL;
  162. _CachePrefix = NULL;
  163. if (!CachePath || !*CachePath || !CachePrefix || !CacheStartUpLimit)
  164. {
  165. _Status = ERROR_INVALID_PARAMETER;
  166. return;
  167. }
  168. _CacheName = NewString(CacheName == NULL ? TEXT(""):CacheName);
  169. _CachePathLen = strlen(CachePath);
  170. if (CachePath[_CachePathLen-1] != DIR_SEPARATOR_CHAR)
  171. {
  172. _CachePath = CatString(CachePath, DIR_SEPARATOR_STRING);
  173. _CachePathLen++;
  174. }
  175. else
  176. _CachePath = NewString(CachePath);
  177. _CachePrefix = NewString(CachePrefix);
  178. if (!_CachePath || !_CachePrefix || !_CacheName)
  179. {
  180. _Status = ERROR_NOT_ENOUGH_MEMORY;
  181. return;
  182. }
  183. _CachePrefixLen = strlen(_CachePrefix);
  184. _CacheStartUpLimit = CacheStartUpLimit;
  185. _UrlObjStorage = NULL;
  186. _FileManager = NULL;
  187. if (!memcmp(_CachePrefix, CONTENT_PREFIX, sizeof(CONTENT_PREFIX)))
  188. _CacheEntryType = NORMAL_CACHE_ENTRY;
  189. else if (!memcmp(_CachePrefix, COOKIE_PREFIX, sizeof(COOKIE_PREFIX)))
  190. _CacheEntryType = COOKIE_CACHE_ENTRY;
  191. else if (!memcmp(_CachePrefix, HISTORY_PREFIX, sizeof(HISTORY_PREFIX)))
  192. _CacheEntryType = URLHISTORY_CACHE_ENTRY;
  193. _Status = ERROR_SUCCESS;
  194. }
  195. #ifdef CHECKLOCK_PARANOID
  196. void URL_CONTAINER::CheckNoLocks(DWORD dwThreadId)
  197. {
  198. INET_ASSERT(_dwTaken == 0 || _dwThreadLocked != dwThreadId);
  199. }
  200. #endif
  201. /*-----------------------------------------------------------------------------
  202. URL_CONTAINER::Init
  203. -----------------------------------------------------------------------------*/
  204. DWORD URL_CONTAINER::Init()
  205. {
  206. _Status = ERROR_SUCCESS;
  207. _FileMapEntrySize = NORMAL_ENTRY_SIZE;
  208. MemMapStatus eMMStatus;
  209. BOOL fMustUnlock = FALSE;
  210. DWORDLONG dlSize;
  211. // Generate the mutex name based on the cache path.
  212. DWORD i;
  213. LPSTR szPrefix;
  214. CHAR MutexName[MAX_PATH + 1];
  215. LPTSTR pCachePath, pMutexName;
  216. i = 0;
  217. pCachePath = _CachePath,
  218. pMutexName = (LPSTR) MutexName;
  219. while( *pCachePath != '\0' && (i++ < MAX_PATH))
  220. {
  221. if( *pCachePath == DIR_SEPARATOR_CHAR )
  222. *pMutexName = '!';
  223. else
  224. *pMutexName = tolower(*pCachePath);
  225. pMutexName++;
  226. pCachePath++;
  227. }
  228. *pMutexName = '\0';
  229. // Open the existing mutex, or if first process, create a new one.
  230. BOOL fFirstProcess = FALSE;
  231. _MutexHandle = OpenMutex(SYNCHRONIZE, FALSE, (LPTSTR)MutexName);
  232. if (_MutexHandle == NULL && (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_INVALID_NAME))
  233. {
  234. SECURITY_ATTRIBUTES* psa = SHGetAllAccessSA();
  235. if (psa)
  236. {
  237. _MutexHandle = CreateMutex(psa, FALSE, (LPTSTR)MutexName);
  238. }
  239. if (_MutexHandle != NULL)
  240. fFirstProcess = TRUE;
  241. }
  242. if (_MutexHandle == NULL)
  243. {
  244. _Status = GetLastError();
  245. goto Cleanup;
  246. }
  247. // Lock the container.
  248. if (!LockContainer(&fMustUnlock))
  249. {
  250. if (fMustUnlock) ReleaseMutex(_MutexHandle);
  251. fMustUnlock = FALSE;
  252. _Status = GetLastError();
  253. if (_MutexHandle)
  254. {
  255. CloseHandle(_MutexHandle);
  256. _MutexHandle = NULL;
  257. }
  258. goto Cleanup;
  259. }
  260. if ((_CachePathLen > 1) && (_CachePath[_CachePathLen-1] != PATH_CONNECT_CHAR))
  261. {
  262. lstrcat( _CachePath, PATH_CONNECT_STRING );
  263. _CachePathLen++;
  264. }
  265. // Initialize _ClusterSizeMinusOne and _ClusterSizeMask
  266. if (!GetDiskInfo(_CachePath, &_ClusterSizeMinusOne, &dlSize, NULL))
  267. {
  268. _Status = GetLastError();
  269. goto Cleanup;
  270. }
  271. _ClusterSizeMinusOne--;
  272. _ClusterSizeMask = ~_ClusterSizeMinusOne;
  273. // Construct and initialize the memory mapped file object.
  274. _UrlObjStorage = new MEMMAP_FILE;
  275. if( _UrlObjStorage == NULL )
  276. {
  277. _Status = ERROR_NOT_ENOUGH_MEMORY;
  278. goto Cleanup;
  279. }
  280. eMMStatus = _UrlObjStorage->Init(_CachePath, _FileMapEntrySize);
  281. if((_Status = _UrlObjStorage->GetStatus()) != ERROR_SUCCESS )
  282. goto Cleanup;
  283. // for first process attach, we need to clean up the notification
  284. // hwnd, msg, gid and filter
  285. if( fFirstProcess && (_CacheEntryType == NORMAL_CACHE_ENTRY ))
  286. {
  287. RegisterCacheNotify(0, 0, 0, 0);
  288. }
  289. _UrlObjStorage->SetCacheLimit(_CacheStartUpLimit);
  290. // Construct and initialize the file manager.
  291. // Cookies and history don't use random subdirs.
  292. // BUGBUG - move this off to container manager.
  293. szPrefix = GetCachePrefix();
  294. if (!strcmp(szPrefix, COOKIE_PREFIX)
  295. || !strcmp(szPrefix, HISTORY_PREFIX)
  296. || (_dwOptions & INTERNET_CACHE_CONTAINER_NOSUBDIRS))
  297. {
  298. // Insecure cache -no random cache subdirs.
  299. _FileManager = new CFileMgr(_UrlObjStorage, GetOptions());
  300. if (!_FileManager)
  301. {
  302. _Status = ERROR_NOT_ENOUGH_MEMORY;
  303. goto Cleanup;
  304. }
  305. }
  306. else
  307. {
  308. // Secure cache - random cache subdirs.
  309. _FileManager = new CSecFileMgr(_UrlObjStorage, GetOptions());
  310. if (!_FileManager)
  311. {
  312. _Status = ERROR_NOT_ENOUGH_MEMORY;
  313. goto Cleanup;
  314. }
  315. }
  316. // If first process to attach, unlock any locked entries.
  317. if (fFirstProcess)
  318. UnlockAllItems();
  319. // If the memory mapped file was reinitialized
  320. // cleanup old files.
  321. if (eMMStatus == MEMMAP_STATUS_REINITIALIZED)
  322. _FileManager->Cleanup();
  323. else
  324. eMMStatus = MEMMAP_STATUS_OPENED_EXISTING;
  325. _fIsInitialized = TRUE;
  326. if (dlSize <= (DWORDLONG)(4*1024*1024))
  327. {
  328. // Yeah, this hurts start-up perf. We also have little room to maneuvre in, so we're going to try anyway.
  329. CleanupUrls(DEFAULT_CLEANUP_FACTOR, 0);
  330. }
  331. Cleanup:
  332. if( _Status != ERROR_SUCCESS)
  333. {
  334. INET_ASSERT(FALSE);
  335. TcpsvcsDbgPrint(( DEBUG_ERRORS,
  336. "URL_CONTAINER::URL_CONTAINER() failed, %ld\n", _Status ));
  337. SetLastError(_Status);
  338. }
  339. if (fMustUnlock) UnlockContainer();
  340. if (_Status == ERROR_SUCCESS)
  341. return (eMMStatus == MEMMAP_STATUS_OPENED_EXISTING ? ERROR_ALREADY_EXISTS : ERROR_SUCCESS);
  342. return _Status;
  343. }
  344. void URL_CONTAINER::CloseContainerFile()
  345. {
  346. // Cleanup.
  347. // _Filemanager holds a pointer to _UrlObjStorage and
  348. // must be deleted before _UrlObjStorage.
  349. delete _FileManager;
  350. delete _UrlObjStorage;
  351. _FileManager = NULL;
  352. _UrlObjStorage = NULL;
  353. if (!_fDeleted)
  354. {
  355. // Keep fixed container files from being deleted.
  356. if (!(_CacheEntryType & (NORMAL_CACHE_ENTRY | COOKIE_CACHE_ENTRY | URLHISTORY_CACHE_ENTRY)))
  357. {
  358. _fDeleted = GlobalUrlContainers->DeleteFileIfNotRegistered(this);
  359. if (_fDeleted) _fDeletePending = FALSE;
  360. }
  361. }
  362. }
  363. URL_CONTAINER::~URL_CONTAINER(
  364. VOID
  365. )
  366. /*++
  367. Routine Description:
  368. URL_CONTAINER destructor
  369. Arguments:
  370. None.
  371. Return Value:
  372. None.
  373. --*/
  374. {
  375. BOOL fMustUnlock;
  376. // If not initialized, only delete path and prefix.
  377. if (!IsInitialized())
  378. {
  379. // Free pending deleted container, even if someone has
  380. // a enum handle open on it. This is our last chance as we handle process
  381. // detach
  382. LOCK_CACHE();
  383. TryToUnmap(0xFFFFFFFF);
  384. UNLOCK_CACHE();
  385. if(_CacheName) delete _CacheName;
  386. if(_CachePath) delete _CachePath;
  387. if(_CachePrefix) delete _CachePrefix;
  388. return;
  389. }
  390. LockContainer(&fMustUnlock);
  391. // Otherwise, do a full destruct.
  392. CloseContainerFile();
  393. if (fMustUnlock) UnlockContainer();
  394. // Delete mutex.
  395. if( _MutexHandle != NULL )
  396. CloseHandle( _MutexHandle );
  397. if(_CacheName) delete _CacheName;
  398. if(_CachePath) delete _CachePath;
  399. if(_CachePrefix) delete _CachePrefix;
  400. }
  401. DWORD URL_CONTAINER::GetOptions()
  402. {
  403. return _dwOptions;
  404. }
  405. DWORD URL_CONTAINER::GetLastReference()
  406. {
  407. return _dwLastReference;
  408. }
  409. BOOL URL_CONTAINER::IsVisible()
  410. {
  411. return !(_fDeletePending || _fDeleted);
  412. }
  413. void URL_CONTAINER::Mark(BOOL fMarked)
  414. {
  415. _fMarked = fMarked;
  416. }
  417. BOOL URL_CONTAINER::GetMarked()
  418. {
  419. return _fMarked;
  420. }
  421. BOOL URL_CONTAINER::GetDeleted()
  422. {
  423. return _fDeleted;
  424. }
  425. void URL_CONTAINER::SetDeleted(BOOL fDeleted)
  426. {
  427. if (!_fIsInitialized) _fDeleted = fDeleted;
  428. }
  429. BOOL URL_CONTAINER::GetDeletePending()
  430. {
  431. return _fDeletePending;
  432. }
  433. void URL_CONTAINER::SetDeletePending(BOOL fDeletePending)
  434. {
  435. _fDeletePending = fDeletePending;
  436. }
  437. void URL_CONTAINER::AddRef()
  438. // THIS FUNCTION MUST BE CALLED WITH THE CACHE CRIT SEC.
  439. {
  440. _dwRefCount++;
  441. }
  442. void URL_CONTAINER::TryToUnmap(DWORD dwAcceptableRefCount)
  443. // THIS FUNCTION MUST BE CALLED WITH THE CACHE CRIT SEC.
  444. {
  445. BOOL fMustUnlock;
  446. if (_dwRefCount <= dwAcceptableRefCount)
  447. {
  448. if (_fIsInitialized)
  449. {
  450. LockContainer(&fMustUnlock);
  451. CloseContainerFile();
  452. if (fMustUnlock) UnlockContainer();
  453. // Delete mutex.
  454. if( _MutexHandle != NULL )
  455. {
  456. CloseHandle( _MutexHandle );
  457. _MutexHandle = NULL;
  458. }
  459. _fIsInitialized = FALSE;
  460. }
  461. else
  462. {
  463. if (!_fDeleted)
  464. {
  465. // Never CONTENT, COOKIES or HISTORY container.
  466. if (!(_CacheEntryType & (NORMAL_CACHE_ENTRY | COOKIE_CACHE_ENTRY | URLHISTORY_CACHE_ENTRY)))
  467. {
  468. if(GlobalUrlContainers)
  469. {
  470. _fDeleted = GlobalUrlContainers->DeleteFileIfNotRegistered(this);
  471. if (_fDeleted) _fDeletePending = FALSE;
  472. }
  473. }
  474. }
  475. }
  476. }
  477. }
  478. DWORD URL_CONTAINER::Release(BOOL fTryToUnmap)
  479. // THIS FUNCTION MUST BE CALLED WITH THE CACHE CRIT SEC.
  480. {
  481. DWORD dwRefCount = 0;
  482. INET_ASSERT(_dwRefCount);
  483. if (_dwRefCount)
  484. {
  485. dwRefCount = --_dwRefCount;
  486. if (fTryToUnmap && _dwRefCount == 0)
  487. {
  488. // Never CONTENT, COOKIES or HISTORY container.
  489. if (!(_CacheEntryType & (NORMAL_CACHE_ENTRY | COOKIE_CACHE_ENTRY | URLHISTORY_CACHE_ENTRY)))
  490. {
  491. if (_fDeletePending)
  492. {
  493. TryToUnmap(0);
  494. }
  495. }
  496. }
  497. }
  498. return dwRefCount;
  499. }
  500. BOOL URL_CONTAINER::LockContainer(BOOL *fMustUnlock)
  501. /*++
  502. Routine Description:
  503. This function waits for the container to be free.
  504. Arguments:
  505. NONE.
  506. Return Value:
  507. NONE.
  508. --*/
  509. {
  510. _dwLastReference = GetTickCountWrap();
  511. *fMustUnlock = FALSE;
  512. if( _MutexHandle == NULL )
  513. {
  514. // Bad mutex handle.
  515. TcpsvcsDbgPrint(( DEBUG_ERRORS, "Container Mutex Handle is NULL.\n" ));
  516. return FALSE;
  517. }
  518. //TcpsvcsDbgPrint((DEBUG_ERRORS, "LockContainer called by thread %x\n", GetCurrentThreadId()));
  519. //
  520. // Wait the for the mutex to be signalled.
  521. //
  522. DWORD Result;
  523. #if DBG
  524. DWORD MutexTimeoutCount;
  525. MutexTimeoutCount = 0;
  526. Waitagain:
  527. #endif
  528. // Check the mutex.
  529. #if DBG
  530. Result = WaitForSingleObject(_MutexHandle, MUTEX_DBG_TIMEOUT);
  531. #else
  532. Result = WaitForSingleObject(_MutexHandle, INFINITE);
  533. #endif
  534. switch ( Result )
  535. {
  536. case WAIT_OBJECT_0:
  537. // Mutex is signalled (normal result). We now have ownership of the mutex.
  538. // Do a CheckSizeGrowAndRemapAddress.
  539. _dwTaken++;
  540. #ifdef CHECKLOCK_NORMAL
  541. _dwThreadLocked = GetCurrentThreadId();
  542. #endif
  543. *fMustUnlock = TRUE;
  544. if (_UrlObjStorage)
  545. {
  546. if (_UrlObjStorage->CheckSizeGrowAndRemapAddress() != ERROR_SUCCESS)
  547. {
  548. return (FALSE);
  549. }
  550. }
  551. return TRUE;
  552. #if DBG
  553. case WAIT_TIMEOUT:
  554. // Exceeded debug timeout count. Try again.
  555. MutexTimeoutCount++;
  556. TcpsvcsDbgPrint(( DEBUG_ERRORS, "Mutex wait time-out (count = %ld).\n", MutexTimeoutCount ));
  557. goto Waitagain;
  558. #endif
  559. case WAIT_ABANDONED :
  560. // The thread owning the mutex failed to release it before it terminated.
  561. // We still get ownership of the mutex.
  562. _dwTaken++;
  563. #ifdef CHECKLOCK_NORMAL
  564. _dwThreadLocked = GetCurrentThreadId();
  565. #endif
  566. *fMustUnlock = TRUE;
  567. TcpsvcsDbgPrint(( DEBUG_ERRORS, "Mutex ABANDONED.\n" ));
  568. if (_UrlObjStorage)
  569. {
  570. if (_UrlObjStorage->CheckSizeGrowAndRemapAddress() != ERROR_SUCCESS)
  571. return (FALSE);
  572. }
  573. return TRUE;
  574. case WAIT_FAILED :
  575. // Failed to obtain mutex.
  576. TcpsvcsDbgPrint(( DEBUG_ERRORS, "Mutex wait failed (%ld).\n", GetLastError() ));
  577. return FALSE;
  578. }
  579. INET_ASSERT( FALSE );
  580. return FALSE;
  581. }
  582. VOID URL_CONTAINER::UnlockContainer(VOID)
  583. /*++
  584. Routine Description:
  585. This function frees the container to be used by someone else.
  586. Arguments:
  587. NONE.
  588. Return Value:
  589. NONE.
  590. --*/
  591. {
  592. BOOL fMustLaunchScavenger = FALSE;
  593. //TcpsvcsDbgPrint((DEBUG_ERRORS, "UnlockContainer called by thread %x\n", GetCurrentThreadId()));
  594. _dwLastReference = GetTickCountWrap();
  595. _dwTaken--;
  596. #ifdef CHECKLOCK_NORMAL
  597. if( _MutexHandle)
  598. {
  599. INET_ASSERT(_dwThreadLocked == GetCurrentThreadId());
  600. if (_dwTaken == 0)
  601. _dwThreadLocked = 0;
  602. #endif
  603. if (_dwTaken == 0)
  604. {
  605. fMustLaunchScavenger = _fMustLaunchScavenger;
  606. _fMustLaunchScavenger = FALSE;
  607. }
  608. if (ReleaseMutex( _MutexHandle ) == FALSE )
  609. {
  610. TcpsvcsDbgPrint(( DEBUG_ERRORS, "ReleaseMutex failed (%ld).\n", GetLastError() ));
  611. }
  612. /* KV
  613. if (fMustLaunchScavenger)
  614. LaunchScavenger();
  615. */
  616. #ifdef CHECKLOCK_NORMAL
  617. }
  618. #endif
  619. return;
  620. }
  621. BOOL URL_CONTAINER::UpdateOfflineRedirect
  622. (
  623. DWORD dwUrlItemOffset, // offset to hash table item of URL entry
  624. LPCSTR pszUrl, // URL string
  625. DWORD cbUrl, // URL length
  626. LPCSTR pszRedir // redirect string
  627. )
  628. /*++
  629. Routine Description:
  630. Marks a hash table item as allowing a redirect to add trailing slash,
  631. or creates a new redirect hash table item and memory mapped file entry.
  632. Addendum: We keep track of redirects in the cache and simulate them when
  633. offline. Often the redirected URL is the same as the original URL plus
  634. trailing slash.
  635. WARNING: this function has multiple calls which can grow and remap the
  636. memory map file, invalidating any pointers into the file. Be careful.
  637. Return Value: TRUE if redirect was cached
  638. --*/
  639. {
  640. DWORD cbRedir = strlen (pszRedir);
  641. DWORD dwUrlHash;
  642. DWORD dwRedirItemOffset;
  643. // Ignore the redirect URL if same as original URL.
  644. if (cbRedir == cbUrl && !memcmp(pszUrl, pszRedir, cbRedir))
  645. return FALSE;
  646. { // limit scope of pUrlItem
  647. HASH_ITEM* pUrlItem = (HASH_ITEM*)
  648. (*_UrlObjStorage->GetHeapStart() + dwUrlItemOffset);
  649. // Special case trailing slash redirect.
  650. if ( cbRedir + 1 == cbUrl
  651. && pszUrl [cbRedir] == '/'
  652. && !memcmp (pszUrl, pszRedir, cbRedir)
  653. )
  654. {
  655. pUrlItem->SetSlash();
  656. return TRUE;
  657. }
  658. // Record high bits of target URL hash value.
  659. dwUrlHash = pUrlItem->GetValue();
  660. }
  661. //
  662. // BUGBUG: we do not handle the case of redirect that once added a
  663. // trailing slash to redirect to another URL altogether. We should
  664. // scan the entire hash table slot and unset the trailing slash bit
  665. // if we find a match.
  666. //
  667. REDIR_FILEMAP_ENTRY* pEntry;
  668. { // limit scope of pRedirItem
  669. HASH_ITEM* pRedirItem = NULL;
  670. if (HashFindItem (pszRedir, LOOKUP_REDIR_CREATE, &pRedirItem))
  671. {
  672. // Found existing redirect entry; update it.
  673. pEntry = (REDIR_FILEMAP_ENTRY*) HashGetEntry (pRedirItem);
  674. INET_ASSERT (pEntry->dwSig == SIG_REDIR);
  675. pEntry->dwHashValue = dwUrlHash;
  676. pEntry->dwItemOffset = dwUrlItemOffset;
  677. INET_ASSERT (!lstrcmp (pEntry->szUrl, pszRedir));
  678. return TRUE;
  679. }
  680. if (!pRedirItem)
  681. return FALSE;
  682. dwRedirItemOffset = (DWORD) ((LPBYTE) pRedirItem - *_UrlObjStorage->GetHeapStart()); // 64BIT
  683. }
  684. // Created new hash table item so allocate a redir entry.
  685. DWORD cbEntry = sizeof(*pEntry) + cbRedir;
  686. pEntry = (REDIR_FILEMAP_ENTRY *) _UrlObjStorage->AllocateEntry (cbEntry);
  687. // Associate entry with hash table item.
  688. HASH_ITEM *pRedirItem =
  689. (HASH_ITEM*) (*_UrlObjStorage->GetHeapStart() + dwRedirItemOffset);
  690. if (!pEntry)
  691. {
  692. pRedirItem->MarkFree();
  693. return FALSE;
  694. }
  695. HashSetEntry (pRedirItem, (LPBYTE) pEntry);
  696. pRedirItem->SetRedir();
  697. // Initialize the redirect entry.
  698. pEntry->dwSig = SIG_REDIR;
  699. pEntry->dwHashValue = dwUrlHash;
  700. pEntry->dwItemOffset = dwUrlItemOffset;
  701. memcpy (pEntry->szUrl, pszRedir, cbRedir + 1);
  702. return TRUE;
  703. }
  704. LPSTR URL_CONTAINER::GetPrefixMap()
  705. {
  706. return TEXT("");
  707. }
  708. LPSTR URL_CONTAINER::GetVolumeLabel()
  709. {
  710. return TEXT("");
  711. }
  712. LPSTR URL_CONTAINER::GetVolumeTitle()
  713. {
  714. return TEXT("");
  715. }
  716. DWORD URL_CONTAINER::AddIdentityUrl (AddUrlArg* pArgs)
  717. {
  718. DWORD dwError = ERROR_SUCCESS;
  719. pArgs->dwEntryType |= IDENTITY_CACHE_ENTRY;
  720. // Lookup or create a hash item for the 0-URL entry.
  721. HASH_ITEM *pItem;
  722. BOOL fUpdate = HashFindItem (pArgs->pszUrl, LOOKUP_URL_DONT_FOLLOW, &pItem);
  723. if (fUpdate)
  724. {
  725. URL_FILEMAP_ENTRY* pOld = HashGetEntry (pItem);
  726. if (pOld && !IsPerUserEntry(pOld))
  727. {
  728. DeleteUrlEntry(pOld, pItem, SIG_DELETE);
  729. fUpdate = FALSE;
  730. }
  731. }
  732. if (!fUpdate)
  733. {
  734. DWORD dwIdentity = pArgs->dwIdentity;
  735. PTSTR pszFilePath = (PTSTR)pArgs->pszFilePath;
  736. pArgs->pszFilePath = NULL;
  737. pArgs->dwIdentity = 0;
  738. //////////////////////////////////////////////////////////////////////////////
  739. // WARNING: LOOKUP_URL_CREATE set,thus the file might be grown and remapped //
  740. // so all pointers into the file must be recalculated from their offsets! //
  741. //////////////////////////////////////////////////////////////////////////////
  742. // SUPERWARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  743. // Calling AddUrl recursively may be hazardous to your health
  744. // SUPERWARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  745. dwError = AddUrl(pArgs);
  746. pArgs->dwIdentity = dwIdentity;
  747. pArgs->pszFilePath = pszFilePath;
  748. }
  749. else
  750. {
  751. DWORD dwItemOffset = (DWORD) ((LPBYTE) pItem - *_UrlObjStorage->GetHeapStart()); // 64BIT
  752. DWORD dwUrlNameSize = lstrlen(pArgs->pszUrl) + 1;
  753. UpdateOfflineRedirect
  754. (dwItemOffset, pArgs->pszUrl, dwUrlNameSize - 1, pArgs->pszRedirect);
  755. }
  756. return dwError;
  757. }
  758. DWORD URL_CONTAINER::AddUrl (AddUrlArg* pArgs)
  759. /*++
  760. Routine Description:
  761. This member functions adds an URL to the container and moves the
  762. cache file to cache path.
  763. Arguments:
  764. UrlName : pointer to an URL string.
  765. lpszNewFileName : pointer to a cache file (full) name.
  766. ExpireTime : expire time of the file.
  767. LastModifiedTime : Last modified time of this file. if this value is
  768. zero, current time is set as the last modified time.
  769. dwCacheEntryType : type of this new entry.
  770. HeaderInfo : if this pointer is non-NULL, it stores the HeaderInfo
  771. data as part of the URL entry in the memory mapped file, otherwise
  772. the app may store it else where. The size of the header info is
  773. specified by the HeaderSize parameter.
  774. dwHeaderSize : size of the header info associated with this URL, this
  775. can be non-zero even if the HeaderInfo specified above is NULL.
  776. FileExtension : file extension used to create this file.
  777. Return Value:
  778. Windows Error Code.
  779. --*/
  780. {
  781. DWORD dwReturn;
  782. BOOL fMustUnlock;
  783. DWORD dwCurrentOffset;
  784. DWORD dwUrlNameSize;
  785. LPTSTR FileName;
  786. DWORD dwFileNameSize;
  787. DWORD dwUrlFileExtSize;
  788. DWORD dwEntrySize;
  789. FILETIME ftCreate;
  790. DWORD dwFileSize;
  791. LONGLONG CacheSize, CacheLimit;
  792. DWORD dwItemOffset;
  793. BOOL fUpdate;
  794. LPURL_FILEMAP_ENTRY NewEntry;
  795. HASH_ITEM *pItem;
  796. GROUP_ENTRY* pGroupEntry;
  797. GroupMgr gm;
  798. DWORD dwFileMapEntrySizeAligned;
  799. BOOL fPlaceAnyRedirect = TRUE;
  800. if (!LockContainer(&fMustUnlock))
  801. {
  802. dwReturn = GetLastError();
  803. goto exit;
  804. }
  805. // If this is an identity-specific entry, we need to setup an identity-0
  806. // referrer
  807. if (pArgs->dwIdentity)
  808. {
  809. dwReturn = AddIdentityUrl(pArgs);
  810. if (dwReturn!=ERROR_SUCCESS)
  811. {
  812. goto exit;
  813. }
  814. fPlaceAnyRedirect = FALSE;
  815. }
  816. // Calculate dwUrlNameSize.
  817. dwUrlNameSize = lstrlen(pArgs->pszUrl) + 1;
  818. // FileName points to the filename sans cachepath. Calculate dwFileNameSize.
  819. if(pArgs->pszFilePath)
  820. {
  821. // Is this an absolute path (edited)?
  822. if (!(pArgs->dwEntryType & EDITED_CACHE_ENTRY))
  823. // No, so find the last slash in the file name path.
  824. // This delimits the file name.
  825. {
  826. CHAR *pSlash = NULL,
  827. *ptr = (LPSTR) pArgs->pszFilePath;
  828. while (*ptr)
  829. {
  830. if (*ptr == DIR_SEPARATOR_CHAR)
  831. pSlash = ptr;
  832. ptr=CharNext(ptr);
  833. }
  834. FileName = pSlash + 1;
  835. dwFileNameSize = (DWORD) (ptr - FileName + 1); // 64BIT
  836. }
  837. // Have an absolute path so use the full path
  838. else
  839. {
  840. FileName = (char *)pArgs->pszFilePath;
  841. dwFileNameSize = lstrlen(FileName)+1;
  842. }
  843. }
  844. else
  845. {
  846. FileName = NULL;
  847. dwFileNameSize = 0;
  848. }
  849. // Calculate dwUrlFileExtSize
  850. if (FileName)
  851. {
  852. dwUrlFileExtSize =
  853. (pArgs->pszFileExt? (lstrlen(pArgs->pszFileExt) + 1) : sizeof("txt"));
  854. }
  855. else
  856. {
  857. dwUrlFileExtSize = 0;
  858. }
  859. // Get the file size.
  860. if (!pArgs->pszFilePath)
  861. dwFileSize = 0;
  862. else
  863. {
  864. if (!pArgs->dwFileSize)
  865. {
  866. WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
  867. dwReturn = GetFileSizeAndTimeByName(pArgs->pszFilePath, &FileAttrData);
  868. // If not succeed, return a uniform error.
  869. if ((ERROR_SUCCESS!=dwReturn) || FileAttrData.nFileSizeHigh) // Accept 0 length files too ...
  870. {
  871. // Reject files of length 0 or larger than 4 GB.
  872. dwReturn = ERROR_INVALID_DATA;
  873. goto exit;
  874. }
  875. dwFileSize = FileAttrData.nFileSizeLow;
  876. ftCreate = FileAttrData.ftCreationTime;
  877. }
  878. else
  879. {
  880. dwFileSize = pArgs->dwFileSize;
  881. ftCreate = pArgs->ftCreate;
  882. }
  883. }
  884. dwReturn = ERROR_SUCCESS;
  885. { // Open a new block to limit the scope of pointer variables.
  886. HASH_ITEM *pItem;
  887. // Lookup or create a hash item for the URL entry.
  888. fUpdate = HashFindItem (pArgs->pszUrl, LOOKUP_URL_CREATE, &pItem);
  889. //////////////////////////////////////////////////////////////////////////////
  890. // WARNING: LOOKUP_URL_CREATE set,thus the file might be grown and remapped //
  891. // so all pointers into the file must be recalculated from their offsets! //
  892. //////////////////////////////////////////////////////////////////////////////
  893. if (fUpdate)
  894. {
  895. // Check existing entry for refcount.
  896. URL_FILEMAP_ENTRY* pOld = HashGetEntry (pItem);
  897. if (pOld->NumReferences)
  898. {
  899. dwReturn = ERROR_SHARING_VIOLATION;
  900. goto exit;
  901. }
  902. // Validate the size of data to be copied from existing entry.
  903. // If the entry version is IE5, the value could be random, so
  904. // force it to the correct value. If the value is otherwise
  905. // bogus, also set it to the IE5 size for safety.
  906. if ( pOld->bVerCreate == ENTRY_VERSION_IE5
  907. || pOld->CopySize > pOld->nBlocks * NORMAL_ENTRY_SIZE
  908. || pOld->CopySize & 3 // should be dword aligned
  909. )
  910. {
  911. pOld->CopySize = ENTRY_COPYSIZE_IE5;
  912. }
  913. dwFileMapEntrySizeAligned = pOld->CopySize;
  914. }
  915. else if (!pItem)
  916. {
  917. // Item was not found but could not allocate another hash table.
  918. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  919. goto exit;
  920. }
  921. else // brand new entry
  922. {
  923. dwFileMapEntrySizeAligned = ENTRY_COPYSIZE_CURRENT;
  924. }
  925. // Save offsets in case the memmap file must be grown and remapped.
  926. dwItemOffset = (DWORD) ((LPBYTE) pItem - *_UrlObjStorage->GetHeapStart()); // 64BIT
  927. }
  928. //////////////////////////////////////////////////////////////////
  929. // BEGIN WARNING: The file might be grown and remapped, so all //
  930. // pointers into the file before this point may be invalidated. //
  931. //////////////////////////////////////////////////////////////////
  932. // Create/update offline redirect as necessary.
  933. if (pArgs->pszRedirect && fPlaceAnyRedirect)
  934. {
  935. UpdateOfflineRedirect
  936. (dwItemOffset, pArgs->pszUrl, dwUrlNameSize - 1, pArgs->pszRedirect);
  937. }
  938. DWORD dwUrlNameSizeAligned;
  939. DWORD dwFileNameSizeAligned;
  940. DWORD dwHeaderSizeAligned;
  941. DWORD dwUrlFileExtSizeAligned;
  942. // Calculate the total size of the entry, rounding up for alignment.
  943. dwUrlNameSizeAligned = ROUNDUPDWORD(dwUrlNameSize);
  944. dwFileNameSizeAligned = ROUNDUPDWORD(dwFileNameSize);
  945. dwHeaderSizeAligned = (pArgs->pbHeaders) ? ROUNDUPDWORD(pArgs->cbHeaders) : 0;
  946. dwUrlFileExtSizeAligned = ROUNDUPDWORD(dwUrlFileExtSize);
  947. INET_ASSERT (dwFileMapEntrySizeAligned % sizeof(DWORD) == 0);
  948. INET_ASSERT (sizeof(FILEMAP_ENTRY) % sizeof(DWORD) == 0);
  949. dwFileMapEntrySizeAligned += sizeof(FILEMAP_ENTRY);
  950. dwEntrySize = dwFileMapEntrySizeAligned
  951. + dwUrlNameSizeAligned
  952. + dwFileNameSizeAligned
  953. + dwHeaderSizeAligned
  954. + dwUrlFileExtSizeAligned;
  955. NewEntry = (LPURL_FILEMAP_ENTRY) _UrlObjStorage->AllocateEntry(dwEntrySize);
  956. //////////////////////////////////////////////////////////////////
  957. // END WARNING: The file might have grown and remapped, so all //
  958. // pointers into the file after this point must be recalculated //
  959. // from offsets. //
  960. //////////////////////////////////////////////////////////////////
  961. // Restore pointer to hash table item.
  962. pItem = (HASH_ITEM*) (*_UrlObjStorage->GetHeapStart() + dwItemOffset);
  963. if (!NewEntry)
  964. {
  965. if (!fUpdate)
  966. pItem->MarkFree();
  967. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  968. goto exit;
  969. }
  970. /*
  971. Handle any differences between cache entry versions.
  972. IE5 inits bVerUpdate to 0 when creating an entry, but incorrectly preserves
  973. the value when updating instead of forcing it to 0. Fortunately IE6 does
  974. not care since IE5 will not be able to find an identity-specific entry in
  975. order to update it.
  976. However, we should have a safety hatch for IE7+ to realize that a downlevel
  977. urlcache updated its entry and just copied over the opaque data. Of course
  978. the solution will be a total ugly hack. One possibility is for the uplevel
  979. urlcache to set dwUrlNameOffset to be dwCopySizeAligned + 4. This will
  980. leave an uninitialized dword "hole" between the fixed fields and the 4
  981. variable size fields. Once this hack is used, by IE7 for example, it
  982. should correctly set bVerUpdate2 so that IE8+ can detect both IE5-6 and IE7
  983. updates without creating further holes.
  984. */
  985. if (fUpdate)
  986. {
  987. URL_FILEMAP_ENTRY* pOld = HashGetEntry (pItem);
  988. if ((pOld->dwSig != SIG_URL)
  989. || (pOld->CopySize > PAGE_SIZE))
  990. {
  991. INET_ASSERT(FALSE);
  992. pItem->MarkFree();
  993. _UrlObjStorage->FreeEntry(NewEntry);
  994. dwReturn = ERROR_WINHTTP_INTERNAL_ERROR;
  995. goto exit;
  996. }
  997. CopyMemory (((LPBYTE) NewEntry) + sizeof(FILEMAP_ENTRY),
  998. ((LPBYTE) pOld) + sizeof(FILEMAP_ENTRY),
  999. pOld->CopySize);
  1000. INET_ASSERT (NewEntry->CopySize == pOld->CopySize);
  1001. if (NewEntry->CopySize == ENTRY_COPYSIZE_IE5)
  1002. NewEntry->bVerCreate = ENTRY_VERSION_IE5;
  1003. INET_ASSERT (NewEntry->bVerCreate == pOld->bVerCreate);
  1004. }
  1005. else
  1006. {
  1007. NewEntry->CopySize = ENTRY_COPYSIZE_CURRENT;
  1008. NewEntry->bVerCreate = ENTRY_VERSION_CURRENT;
  1009. NewEntry->bVerUpdate = ENTRY_VERSION_CURRENT;
  1010. }
  1011. // Invalidate the signature during the update.
  1012. NewEntry->dwSig = 0;
  1013. // We must set entry type, file size, and exempt delta before
  1014. // calling SetExemptDelta. We leave the sticky bit out of the
  1015. // entry type in case we have no more room for sticky items.
  1016. NewEntry->CacheEntryType = _CacheEntryType
  1017. | (pArgs->dwEntryType & ~STICKY_CACHE_ENTRY);
  1018. NewEntry->dwFileSize = dwFileSize;
  1019. if (!fUpdate)
  1020. {
  1021. // This is a brand new entry.
  1022. NewEntry->dwGroupOffset = 0;
  1023. NewEntry->NumAccessed = 1;
  1024. if (pArgs->fImage)
  1025. {
  1026. NewEntry->bSyncState = SYNCSTATE_IMAGE;
  1027. BETA_LOG (SYNCSTATE_IMAGE);
  1028. }
  1029. else
  1030. {
  1031. NewEntry->bSyncState = SYNCSTATE_VOLATILE;
  1032. if (IsContentContainer())
  1033. BETA_LOG (SYNCSTATE_VOLATILE);
  1034. }
  1035. NewEntry->dwExemptDelta = 0;
  1036. if (pArgs->dwEntryType & STICKY_CACHE_ENTRY)
  1037. SetExemptDelta (NewEntry, 24 * 60 * 60, dwItemOffset); // one day
  1038. }
  1039. else // if (fUpdate)
  1040. {
  1041. URL_FILEMAP_ENTRY* ExistingEntry = HashGetEntry (pItem);
  1042. // This is an update of an existing entry.
  1043. INET_ASSERT (NewEntry->dwGroupOffset == ExistingEntry->dwGroupOffset);
  1044. NewEntry->NumAccessed++; // BUGBUG: blowing off wraparound
  1045. if (ExistingEntry->bSyncState == SYNCSTATE_STATIC)
  1046. BETA_LOG (SYNCSTATE_STATIC_VOLATILE);
  1047. NewEntry->bSyncState = SYNCSTATE_VOLATILE;
  1048. if (ExistingEntry->dwExemptDelta ||
  1049. ExistingEntry->CacheEntryType & STICKY_CACHE_ENTRY )
  1050. {
  1051. // If item was previously sticky, it is preserved upon update.
  1052. NewEntry->dwExemptDelta = ExistingEntry->dwExemptDelta;
  1053. INET_ASSERT (ExistingEntry->CacheEntryType | STICKY_CACHE_ENTRY);
  1054. NewEntry->CacheEntryType |= STICKY_CACHE_ENTRY;
  1055. }
  1056. else
  1057. {
  1058. // If the item wasn't previously sticky, it might be made so.
  1059. NewEntry->dwExemptDelta = 0;
  1060. if (pArgs->dwEntryType & STICKY_CACHE_ENTRY)
  1061. SetExemptDelta (NewEntry, 24 * 60 * 60 * 1000, dwItemOffset); // one day
  1062. }
  1063. // if belongs to a group, adjust the group usage
  1064. if( ExistingEntry->dwGroupOffset &&
  1065. ExistingEntry->dwFileSize != NewEntry->dwFileSize )
  1066. {
  1067. LONGLONG llDelta = RealFileSize(NewEntry->dwFileSize) -
  1068. RealFileSize(ExistingEntry->dwFileSize);
  1069. if( pItem->HasMultiGroup() )
  1070. {
  1071. // multiple group
  1072. if( gm.Init(this) )
  1073. {
  1074. // dwGroupOffset is now offset to head of group list
  1075. gm.AdjustUsageOnList(ExistingEntry->dwGroupOffset, llDelta);
  1076. }
  1077. }
  1078. else
  1079. {
  1080. // single group
  1081. pGroupEntry = _UrlObjStorage->ValidateGroupOffset(
  1082. ExistingEntry->dwGroupOffset, pItem);
  1083. // WARNING: quota can be reached by usage increase
  1084. if( pGroupEntry )
  1085. {
  1086. AdjustGroupUsage(pGroupEntry, llDelta);
  1087. }
  1088. }
  1089. }
  1090. // Delete the old entry if it's not an installed entry or an EDITED_CACHE_ENTRY
  1091. // (Unless the new entry replacing it is also an EDITED_CACHE_ENTRY),
  1092. // either way the hash table item is preserved. We also check the filesize if
  1093. // we've determined the old entry was ECE but the new one is not, just in case
  1094. // the client deletes the file but doesn't get around to deleting from the
  1095. // cache index. The logic is optimized for the most likely case (want to del).
  1096. if ((ExistingEntry->DirIndex != INSTALLED_DIRECTORY_KEY)
  1097. && (!(ExistingEntry->CacheEntryType & EDITED_CACHE_ENTRY)))
  1098. {
  1099. // IDK=0 ECE=0 NECE=? FS=?
  1100. DeleteUrlEntry (ExistingEntry, NULL, SIG_UPDATE);
  1101. }
  1102. else if (ExistingEntry->DirIndex != INSTALLED_DIRECTORY_KEY)
  1103. {
  1104. // IDK=0 ECE=1 NECE=? FS=?
  1105. if (NewEntry->CacheEntryType & EDITED_CACHE_ENTRY)
  1106. {
  1107. // IDK=0 ECE=1 NECE=1 FS=?
  1108. DeleteUrlEntry (ExistingEntry, NULL, SIG_UPDATE);
  1109. }
  1110. else
  1111. {
  1112. // IDK=0 ECE=1 NECE=0 FS=?
  1113. // Only want to go out to the FS to get filesize if absolutely necessary
  1114. WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
  1115. FileAttrData.nFileSizeLow = 0;
  1116. dwReturn = GetFileSizeAndTimeByName(
  1117. (LPTSTR) OFFSET_TO_POINTER (ExistingEntry, ExistingEntry->InternalFileNameOffset),
  1118. &FileAttrData);
  1119. if (!FileAttrData.nFileSizeLow)
  1120. {
  1121. // IDK=0 ECE=1 NECE=0 FS=0 or not-exist
  1122. // if filesize is zero, might as well trounce it
  1123. DeleteUrlEntry (ExistingEntry, NULL, SIG_UPDATE);
  1124. }
  1125. else if (dwReturn == ERROR_SUCCESS)
  1126. {
  1127. // IDK=0 ECE=1 NECE=0 FS>0
  1128. // found a non-zero length file
  1129. _UrlObjStorage->FreeEntry(NewEntry);
  1130. pItem->MarkFree();
  1131. dwReturn = ERROR_SHARING_VIOLATION;
  1132. goto exit;
  1133. }
  1134. }
  1135. }
  1136. else
  1137. {
  1138. // IDK=1 ECE=? NECE=? FS=?
  1139. // Installed directory item
  1140. _UrlObjStorage->FreeEntry(NewEntry);
  1141. pItem->MarkFree();
  1142. dwReturn = ERROR_SHARING_VIOLATION;
  1143. goto exit;
  1144. }
  1145. } // end else if (fUpdate)
  1146. // Record the new offset in the hash table item.
  1147. HashSetEntry (pItem, (LPBYTE) NewEntry);
  1148. // Initialize NewEntry fields.
  1149. NewEntry->dwRedirHashItemOffset = 0;
  1150. NewEntry->NumReferences = 0;
  1151. NewEntry->HeaderInfoSize = pArgs->cbHeaders;
  1152. NewEntry->LastModifiedTime = pArgs->qwLastMod;
  1153. GetCurrentGmtTime ((FILETIME*) &NewEntry->LastAccessedTime);
  1154. FileTime2DosTime(
  1155. *LONGLONG_TO_FILETIME( &(pArgs->qwExpires) ),
  1156. &(NewEntry->dostExpireTime) );
  1157. FileTime2DosTime(
  1158. *LONGLONG_TO_FILETIME( &(pArgs->qwPostCheck) ),
  1159. &(NewEntry->dostPostCheckTime) );
  1160. // GetDirIndex will fail if the entry is stored at an absolute path outside
  1161. // the cache dir, This is valid for EDITED_CACHE_ENTRYs such as offline
  1162. // Office9 docs. Even tho the call fails in this case, NewEntry->DirIndex
  1163. // will be set to NOT_A_CACHE_SUBDIRECTORY
  1164. DWORD dwIndex;
  1165. if ((!_FileManager->GetDirIndex((LPSTR) pArgs->pszFilePath, &dwIndex))
  1166. && !((pArgs->dwEntryType & EDITED_CACHE_ENTRY)
  1167. || ((pArgs->dwEntryType & IDENTITY_CACHE_ENTRY) && !pArgs->dwIdentity)))
  1168. {
  1169. _UrlObjStorage->FreeEntry(NewEntry);
  1170. pItem->MarkFree();
  1171. dwReturn = ERROR_INVALID_PARAMETER;
  1172. goto exit;
  1173. }
  1174. NewEntry->DirIndex = (BYTE) dwIndex;
  1175. // If this entry points to the store directory, set
  1176. // the INSTALLED_CACHE_ENTRY bit so that retrieval
  1177. // from cache will not result in requests to the wire
  1178. // from wininet except for refresh requests.
  1179. if ((NewEntry->DirIndex == INSTALLED_DIRECTORY_KEY) ||
  1180. (pArgs->dwEntryType & EDITED_CACHE_ENTRY))
  1181. {
  1182. NewEntry->CacheEntryType |= INSTALLED_CACHE_ENTRY;
  1183. }
  1184. FileTimeToDosDateTime( (FILETIME *)&(NewEntry->LastAccessedTime),
  1185. (LPWORD)&(NewEntry->dostLastSyncTime),
  1186. ((LPWORD)&(NewEntry->dostLastSyncTime)+1));
  1187. // Specify identity.
  1188. NewEntry->dwIdentity = pArgs->dwIdentity;
  1189. if (pArgs->dwIdentity)
  1190. {
  1191. NewEntry->CacheEntryType |= IDENTITY_CACHE_ENTRY;
  1192. }
  1193. // The URL_FILEMAP_ENTRY will point to
  1194. //
  1195. // [URL_FILEMAP_ENTRY][UrlName][FileName][HeaderInfo][FileExtension]
  1196. // ^ ^ ^ ^
  1197. // | | | FileExtensionOffset
  1198. // | | |
  1199. // | | HeaderInfoOffset
  1200. // | |
  1201. // | FileNameOffset
  1202. // |
  1203. // UrlNameOffset
  1204. //
  1205. dwCurrentOffset = dwFileMapEntrySizeAligned;
  1206. NewEntry->UrlNameOffset = dwCurrentOffset;
  1207. // Copy UrlName into NewEntry
  1208. memcpy((LPSTR) OFFSET_TO_POINTER(NewEntry, NewEntry->UrlNameOffset),
  1209. pArgs->pszUrl, dwUrlNameSize);
  1210. dwCurrentOffset += dwUrlNameSizeAligned;
  1211. // Copy FileName into NewEntry
  1212. if(FileName)
  1213. {
  1214. NewEntry->InternalFileNameOffset = dwCurrentOffset;
  1215. memcpy((LPTSTR) OFFSET_TO_POINTER (NewEntry,
  1216. NewEntry->InternalFileNameOffset), FileName, dwFileNameSize);
  1217. // Get file creation time of cache file.
  1218. if (!pArgs->pszFilePath)
  1219. NewEntry->dostFileCreationTime = 0;
  1220. else
  1221. {
  1222. FileTimeToDosDateTime (&ftCreate,
  1223. (LPWORD)&(NewEntry->dostFileCreationTime),
  1224. ((LPWORD)&(NewEntry->dostFileCreationTime)+1));
  1225. }
  1226. dwCurrentOffset += dwFileNameSizeAligned;
  1227. }
  1228. else
  1229. {
  1230. NewEntry->InternalFileNameOffset = 0;
  1231. NewEntry->dostFileCreationTime = 0;
  1232. }
  1233. // Copy HeaderInfo into NewEntry
  1234. if(pArgs->pbHeaders)
  1235. {
  1236. NewEntry->HeaderInfoOffset = dwCurrentOffset;
  1237. memcpy((LPBYTE)NewEntry + NewEntry->HeaderInfoOffset,
  1238. pArgs->pbHeaders, pArgs->cbHeaders);
  1239. dwCurrentOffset += dwHeaderSizeAligned;
  1240. }
  1241. else
  1242. {
  1243. NewEntry->HeaderInfoOffset = 0;
  1244. }
  1245. // Copy FileExtension into NewEntry
  1246. if(pArgs->pszFileExt)
  1247. {
  1248. NewEntry->FileExtensionOffset = dwCurrentOffset;
  1249. memcpy ((LPTSTR) ((LPBYTE)NewEntry + NewEntry->FileExtensionOffset),
  1250. pArgs->pszFileExt, dwUrlFileExtSize);
  1251. dwCurrentOffset += dwUrlFileExtSizeAligned;
  1252. }
  1253. else
  1254. {
  1255. NewEntry->FileExtensionOffset = 0;
  1256. }
  1257. // Restore the signature.
  1258. NewEntry->dwSig = SIG_URL;
  1259. // Increment the FileManager's count
  1260. if (FileName)
  1261. {
  1262. // This is a no-op for the COOKIES and HISTORY containers.
  1263. _FileManager->NotifyCommit(NewEntry->DirIndex);
  1264. // Adjust CacheSize if not an installed entry or stored outside of cache dir.
  1265. // If disk quota is exceeded, initiate cleanup.
  1266. // NOTE: this attempts to take the critical section, so we must defer it
  1267. // until we have released the container mutex to avoid potential deadlock
  1268. if ((NewEntry->DirIndex != INSTALLED_DIRECTORY_KEY) && (!(NewEntry->CacheEntryType & EDITED_CACHE_ENTRY)))
  1269. {
  1270. _dwBytesDownloaded += (DWORD)RealFileSize(dwFileSize);
  1271. _dwItemsDownloaded++;
  1272. _UrlObjStorage->AdjustCacheSize(RealFileSize(dwFileSize));
  1273. CacheSize = _UrlObjStorage->GetCacheSize();
  1274. CacheLimit = _UrlObjStorage->GetCacheLimit();
  1275. if (CacheSize > CacheLimit)
  1276. _fMustLaunchScavenger = TRUE;
  1277. // We also want to scavenge if, even though there's plenty of space available in the
  1278. // cache, the amount of total disk space available falls below a certain threshold.
  1279. // We'll arbitrarily set the threshold to be 4MB (below which, things are likely to get
  1280. // painful.
  1281. if (!_fMustLaunchScavenger && ((_dwBytesDownloaded>(1024*1024)) || (_dwItemsDownloaded>100)))
  1282. {
  1283. DWORDLONG dlSize = 0;
  1284. _dwBytesDownloaded = _dwItemsDownloaded = 0;
  1285. if (GetDiskInfo(_CachePath, NULL, &dlSize, NULL))
  1286. {
  1287. _fMustLaunchScavenger = (BOOL)(dlSize <= (DWORDLONG)GlobalDiskUsageLowerBound);
  1288. }
  1289. }
  1290. }
  1291. }
  1292. // Flush index if this is an edited document or Cookie to mitigate the risk of dirty shutdown losing changes a client
  1293. // like Office9 might have made (they store edited docs in our cache).
  1294. if (pArgs->dwEntryType & EDITED_CACHE_ENTRY )
  1295. {
  1296. FlushViewOfFile((LPCVOID)NewEntry, dwCurrentOffset);
  1297. }
  1298. if( pArgs->dwEntryType & COOKIE_CACHE_ENTRY )
  1299. {
  1300. FlushViewOfFile( (void*)(*_UrlObjStorage->GetHeapStart()), 0 );
  1301. }
  1302. // Notification
  1303. // If item was previously sticky, it is preserved upon update.
  1304. // only need to report non sticky -> sticky
  1305. if( !fUpdate )
  1306. {
  1307. NotifyCacheChange(CACHE_NOTIFY_ADD_URL, dwItemOffset);
  1308. }
  1309. else
  1310. {
  1311. NotifyCacheChange(CACHE_NOTIFY_UPDATE_URL, dwItemOffset);
  1312. }
  1313. exit:
  1314. if (fMustUnlock) UnlockContainer();
  1315. return dwReturn;
  1316. }
  1317. DWORD URL_CONTAINER::AddLeakFile (LPCSTR pszFilePath)
  1318. {
  1319. DWORD dwReturn;
  1320. BOOL fUnlock;
  1321. if (!LockContainer(&fUnlock))
  1322. {
  1323. if(fUnlock)
  1324. ReleaseMutex(_MutexHandle);
  1325. return GetLastError();
  1326. }
  1327. //
  1328. // Calculate the size of the filename, after the last slash.
  1329. //
  1330. LPSTR pszSlash = NULL;
  1331. LPSTR pszScan = (LPSTR) pszFilePath;
  1332. while (*pszScan)
  1333. {
  1334. if (*pszScan == DIR_SEPARATOR_CHAR)
  1335. pszSlash = pszScan;
  1336. pszScan = CharNext(pszScan);
  1337. }
  1338. LPSTR pszFileName = pszSlash + 1;
  1339. DWORD cbFileName = (DWORD) (pszScan - pszFileName + 1); // 64BIT
  1340. //
  1341. // Determine the directory bucket.
  1342. //
  1343. DWORD nDirIndex;
  1344. if (!_FileManager->GetDirIndex((LPSTR) pszFilePath, &nDirIndex))
  1345. {
  1346. dwReturn = ERROR_INVALID_PARAMETER;
  1347. goto exit;
  1348. }
  1349. //
  1350. // Get the file size and create time.
  1351. //
  1352. WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
  1353. dwReturn = GetFileSizeAndTimeByName (pszFilePath, &FileAttrData);
  1354. if (!FileAttrData.nFileSizeLow || FileAttrData.nFileSizeHigh)
  1355. {
  1356. // Reject files of length 0 or larger than 4 GB.
  1357. dwReturn = ERROR_INVALID_DATA;
  1358. goto exit;
  1359. }
  1360. //
  1361. // Allocate a leaked file entry.
  1362. //
  1363. //////////////////////////////////////////////////////////////////
  1364. // BEGIN WARNING: The file might be grown and remapped, so all //
  1365. // pointers into the file before this point may be invalidated. //
  1366. //////////////////////////////////////////////////////////////////
  1367. URL_FILEMAP_ENTRY* NewEntry;
  1368. NewEntry = (URL_FILEMAP_ENTRY*) _UrlObjStorage->AllocateEntry
  1369. (sizeof(URL_FILEMAP_ENTRY) + cbFileName);
  1370. if (!NewEntry)
  1371. {
  1372. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  1373. goto exit;
  1374. }
  1375. //////////////////////////////////////////////////////////////////
  1376. // END WARNING: The file might have grown and remapped, so all //
  1377. // pointers into the file after this point must be recalculated //
  1378. // from offsets. //
  1379. //////////////////////////////////////////////////////////////////
  1380. //
  1381. // Fill only the fields important to WalkLeakList method.
  1382. //
  1383. NewEntry->dwSig = SIG_LEAK;
  1384. NewEntry->dwFileSize = FileAttrData.nFileSizeLow;
  1385. NewEntry->InternalFileNameOffset = sizeof(URL_FILEMAP_ENTRY);
  1386. memcpy ((LPBYTE) (NewEntry + 1), pszFileName, cbFileName);
  1387. NewEntry->DirIndex = (BYTE) nDirIndex;
  1388. FileTimeToDosDateTime( &FileAttrData.ftCreationTime,
  1389. (LPWORD)&(NewEntry->dostFileCreationTime),
  1390. ((LPWORD)&(NewEntry->dostFileCreationTime)+1));
  1391. NewEntry->NumReferences = 0;
  1392. //
  1393. // Add this entry on to the head of the scavenger leak list.
  1394. //
  1395. _UrlObjStorage->GetHeaderData
  1396. (CACHE_HEADER_DATA_ROOT_LEAK_OFFSET, &NewEntry->dwNextLeak);
  1397. _UrlObjStorage->SetHeaderData
  1398. (CACHE_HEADER_DATA_ROOT_LEAK_OFFSET,
  1399. OffsetFromPointer (NewEntry));
  1400. //
  1401. // Update the cache usage and directory count.
  1402. //
  1403. _UrlObjStorage->AdjustCacheSize(RealFileSize(NewEntry->dwFileSize));
  1404. _FileManager->NotifyCommit(NewEntry->DirIndex);
  1405. //
  1406. // We could check here if usage exceeds quota then launch scavenger,
  1407. // but it will probably happen soon enough anyway upon AddUrl.
  1408. //
  1409. dwReturn = ERROR_SUCCESS;
  1410. exit:
  1411. if (fUnlock)
  1412. UnlockContainer();
  1413. return dwReturn;
  1414. }
  1415. DWORD URL_CONTAINER::RetrieveUrl
  1416. (
  1417. LPCSTR UrlName,
  1418. LPCACHE_ENTRY_INFO* ppInfo,
  1419. LPDWORD pcbInfo,
  1420. DWORD dwLookupFlags, // e.g. redirect
  1421. DWORD dwRetrievalFlags
  1422. )
  1423. /*++
  1424. Routine Description:
  1425. This member function retrives an url from the cache. The url is marked
  1426. as referenced, so that caller should call UnlockUrl when he is done
  1427. using it.
  1428. Arguments:
  1429. UrlName : pointer to the url name.
  1430. ppInfo : ptr to ptr to an entry info buffer, where the url entry info
  1431. is returned.
  1432. pcbInfo : pointer to a DWORD location containing the size of the
  1433. above buffer, on return it has the size of the buffer consumed or
  1434. size of the buffer required for successful retrieval.
  1435. Return Value:
  1436. Windows Error Code.
  1437. --*/
  1438. {
  1439. DWORD Error;
  1440. LPURL_FILEMAP_ENTRY UrlEntry;
  1441. BOOL fMustUnlock;
  1442. if (!LockContainer(&fMustUnlock))
  1443. {
  1444. Error = GetLastError();
  1445. goto exit;
  1446. }
  1447. // Find the item.
  1448. HASH_ITEM *pItem;
  1449. if (!HashFindItem (UrlName, dwLookupFlags, &pItem))
  1450. {
  1451. Error = ERROR_FILE_NOT_FOUND;
  1452. goto exit;
  1453. }
  1454. // Get the hash entry.
  1455. UrlEntry = HashGetEntry (pItem);
  1456. if (UrlEntry->InternalFileNameOffset == 0)
  1457. {
  1458. Error = ERROR_INVALID_DATA;
  1459. goto exit;
  1460. }
  1461. // For content container, check that username matches.
  1462. if (IsContentContainer())
  1463. {
  1464. LPSTR pszHeaders = ((LPSTR) UrlEntry) + UrlEntry->HeaderInfoOffset;
  1465. TcpsvcsDbgPrint((DEBUG_CONTAINER,
  1466. "RetrieveUrl (contain.cxx): IsContentContainer() = TRUE; IsCorrectUser() = %s \r\n",
  1467. (IsCorrectUser(pszHeaders, UrlEntry->HeaderInfoSize))? "TRUE" : "FALSE"
  1468. ));
  1469. if (!IsCorrectUser(pszHeaders, UrlEntry->HeaderInfoSize))
  1470. {
  1471. Error = ERROR_FILE_NOT_FOUND;
  1472. goto exit;
  1473. }
  1474. }
  1475. else
  1476. {
  1477. TcpsvcsDbgPrint((DEBUG_CONTAINER,
  1478. "RetrieveUrl (contain.cxx): IsContentContainer() = FALSE\r\n"));
  1479. }
  1480. // Hide sparse cache entries from non-wininet clients.
  1481. if (UrlEntry->CacheEntryType & SPARSE_CACHE_ENTRY
  1482. && !(dwLookupFlags & LOOKUP_BIT_SPARSE))
  1483. {
  1484. Error = ERROR_FILE_NOT_FOUND;
  1485. goto exit;
  1486. }
  1487. // Found the entry. Copy URL info in the return buffer first.
  1488. Error = CopyUrlInfoGuard( UrlEntry, ppInfo, pcbInfo, dwRetrievalFlags );
  1489. if( Error != ERROR_SUCCESS )
  1490. goto Cleanup;
  1491. if ((*ppInfo)->CacheEntryType & SPARSE_CACHE_ENTRY)
  1492. {
  1493. // Delete the item and entry but not the file.
  1494. UrlEntry->InternalFileNameOffset = 0;
  1495. DeleteUrlEntry (UrlEntry, pItem, SIG_DELETE);
  1496. Error = ERROR_SUCCESS;
  1497. goto Cleanup;
  1498. }
  1499. // Verify the file size is what it is supposed to be.
  1500. if (dwRetrievalFlags & RETRIEVE_WITH_CHECKS)
  1501. {
  1502. DWORD dwFileSize;
  1503. Error = GetFileSizeByName
  1504. ((LPCSTR)((*ppInfo)->lpszLocalFileName), &dwFileSize);
  1505. if (Error != ERROR_SUCCESS)
  1506. {
  1507. Error = ERROR_INVALID_DATA;
  1508. goto Cleanup;
  1509. }
  1510. if (dwFileSize != UrlEntry->dwFileSize)
  1511. {
  1512. Error = ERROR_INVALID_DATA;
  1513. goto Cleanup;
  1514. }
  1515. }
  1516. // Hack to keep track of if any entries have locks.
  1517. GlobalRetrieveUrlCacheEntryFileCount++;
  1518. // Increment the reference count before returning.
  1519. if (UrlEntry->NumReferences)
  1520. {
  1521. if( !pItem->IsLocked() )
  1522. {
  1523. //
  1524. // corrupted index file
  1525. // entry says it's locked, hash table say it's not
  1526. // believe the hash table by fixing up the entry
  1527. //
  1528. INET_ASSERT (FALSE);
  1529. UrlEntry->NumReferences = 0;
  1530. }
  1531. }
  1532. else
  1533. pItem->SetLocked();
  1534. UrlEntry->NumReferences++;
  1535. // Update last accessed time.
  1536. GetCurrentGmtTime ((FILETIME*) &UrlEntry->LastAccessedTime);
  1537. // And the number of times this was accessed
  1538. UrlEntry->NumAccessed++;
  1539. Cleanup:
  1540. if (Error != ERROR_SUCCESS)
  1541. {
  1542. TcpsvcsDbgPrint(( DEBUG_ERRORS, "RetrieveUrl call failed, %ld.\n",
  1543. Error ));
  1544. SetLastError(Error);
  1545. }
  1546. exit:
  1547. if (fMustUnlock) UnlockContainer();
  1548. return Error;
  1549. }
  1550. DWORD URL_CONTAINER::DeleteUrl(LPCSTR UrlName)
  1551. /*++
  1552. Routine Description:
  1553. This member function deletes the specified url from the cache.
  1554. Arguments:
  1555. UrlName : pointer to the url name.
  1556. Return Value:
  1557. Windows Error Code.
  1558. --*/
  1559. {
  1560. BOOL fMustUnlock;
  1561. DWORD Error;
  1562. URL_FILEMAP_ENTRY *pEntry;
  1563. HASH_ITEM *pItem;
  1564. if (!LockContainer(&fMustUnlock))
  1565. {
  1566. Error = GetLastError();
  1567. goto exit;
  1568. }
  1569. // Find (and validate) the entry.
  1570. if (!HashFindItem (UrlName, LOOKUP_URL_NOCREATE, &pItem))
  1571. {
  1572. Error = ERROR_FILE_NOT_FOUND;
  1573. goto exit;
  1574. }
  1575. pEntry = HashGetEntry(pItem);
  1576. // Delete the hash table item and entry from the index.
  1577. Error = DeleteUrlEntry (pEntry, pItem, SIG_DELETE);
  1578. TcpsvcsDbgPrint((DEBUG_CONTAINER,
  1579. "DeleteUrl: RefCount=%d, DeletePending=%d \r\n",
  1580. pEntry->NumReferences,
  1581. (pEntry->CacheEntryType & PENDING_DELETE_CACHE_ENTRY)? 1 : 0
  1582. ));
  1583. // Notify
  1584. NotifyCacheChange(
  1585. CACHE_NOTIFY_DELETE_URL,
  1586. (DWORD)( ((LPBYTE) pItem) - *_UrlObjStorage->GetHeapStart())
  1587. );
  1588. exit:
  1589. if (fMustUnlock) UnlockContainer();
  1590. return Error;
  1591. }
  1592. /*++
  1593. Routine Description:
  1594. This member functions deletes an URL from the container and also
  1595. deletes the cache file from cache path.
  1596. dwSig - if we must put an uplevel entry on the async fixup list,
  1597. this param distinguishes between updates and deletions.
  1598. Return Value:
  1599. Windows Error Code.
  1600. --*/
  1601. DWORD URL_CONTAINER::DeleteUrlEntry
  1602. (URL_FILEMAP_ENTRY* pEntry, HASH_ITEM *pItem, DWORD dwSig)
  1603. {
  1604. INET_ASSERT (pItem? dwSig == SIG_DELETE : dwSig == SIG_UPDATE);
  1605. if (pEntry->dwSig != SIG_URL)
  1606. {
  1607. INET_ASSERT(FALSE);
  1608. return ERROR_INVALID_PARAMETER;
  1609. }
  1610. DWORD Error;
  1611. GROUP_ENTRY* pGroupEntry = NULL;
  1612. GroupMgr gm;
  1613. // Check for locked entry.
  1614. if (pEntry->NumReferences)
  1615. {
  1616. // Mark the entry for pending delete.
  1617. pEntry->CacheEntryType |= PENDING_DELETE_CACHE_ENTRY;
  1618. Error = ERROR_SHARING_VIOLATION;
  1619. goto Cleanup;
  1620. }
  1621. // If the entry version is beyond our understanding...
  1622. if (pEntry->bVerCreate & ENTRY_VERSION_NONCOMPAT_MASK)
  1623. {
  1624. INET_ASSERT (!(pEntry->bVerCreate & ENTRY_VERSION_NONCOMPAT_MASK));
  1625. pEntry->dwSig = dwSig; // mark as either updated or deleted
  1626. // Add entry to head of fixup list.
  1627. _UrlObjStorage->GetHeaderData
  1628. (CACHE_HEADER_DATA_ROOT_FIXUP_OFFSET, &pEntry->dwNextLeak);
  1629. _UrlObjStorage->SetHeaderData
  1630. (CACHE_HEADER_DATA_ROOT_FIXUP_OFFSET,
  1631. OffsetFromPointer (pEntry));
  1632. // Increment count of items on fixup list, maybe trigger fixup.
  1633. DWORD dwCount, dwTrigger;
  1634. _UrlObjStorage->GetHeaderData
  1635. (CACHE_HEADER_DATA_ROOT_FIXUP_COUNT, &dwCount);
  1636. _UrlObjStorage->GetHeaderData
  1637. (CACHE_HEADER_DATA_ROOT_FIXUP_TRIGGER, &dwTrigger);
  1638. if (++dwCount > dwTrigger)
  1639. _fMustLaunchScavenger = TRUE;
  1640. _UrlObjStorage->SetHeaderData
  1641. (CACHE_HEADER_DATA_ROOT_FIXUP_COUNT, dwCount);
  1642. goto delete_hash_item;
  1643. }
  1644. // If group associated, Adjust Group's disk Usage
  1645. // if pItem == NULL, we should perserve all the group info
  1646. if( pEntry->dwGroupOffset && pItem )
  1647. {
  1648. if( pItem->HasMultiGroup() )
  1649. {
  1650. // multiple group
  1651. if( gm.Init(this) )
  1652. {
  1653. // dwGroupOffset now offset to head of group list
  1654. gm.AdjustUsageOnList(
  1655. pEntry->dwGroupOffset, -RealFileSize(pEntry->dwFileSize) );
  1656. }
  1657. }
  1658. else
  1659. {
  1660. // single group
  1661. pGroupEntry = _UrlObjStorage->ValidateGroupOffset(
  1662. pEntry->dwGroupOffset, pItem);
  1663. if( pGroupEntry )
  1664. {
  1665. AdjustGroupUsage(pGroupEntry, -RealFileSize(pEntry->dwFileSize) );
  1666. }
  1667. }
  1668. }
  1669. // Delete any associated file.
  1670. if (pEntry->InternalFileNameOffset
  1671. && (pEntry->DirIndex != INSTALLED_DIRECTORY_KEY)
  1672. && (!(pEntry->CacheEntryType & EDITED_CACHE_ENTRY)))
  1673. {
  1674. // Reduce the exempt usage for sticky item (could be an update)
  1675. if ( pEntry->dwExemptDelta ||
  1676. (pEntry->CacheEntryType & STICKY_CACHE_ENTRY) )
  1677. _UrlObjStorage->AdjustExemptUsage(-RealFileSize(pEntry->dwFileSize));
  1678. // Get the absolute path to the file.
  1679. DWORD cb;
  1680. TCHAR szFile[MAX_PATH];
  1681. if (_FileManager->GetFilePathFromEntry(pEntry, szFile, &(cb = MAX_PATH))
  1682. && _FileManager->DeleteOneCachedFile
  1683. (szFile, pEntry->dostFileCreationTime, pEntry->DirIndex))
  1684. {
  1685. // Adjust cache usage.
  1686. _UrlObjStorage->AdjustCacheSize(-RealFileSize(pEntry->dwFileSize));
  1687. _UrlObjStorage->FreeEntry(pEntry);
  1688. }
  1689. else
  1690. {
  1691. // Link the entry at the head of leaked files list.
  1692. INET_ASSERT(pEntry->NumReferences==0);
  1693. pEntry->dwSig = SIG_LEAK;
  1694. _UrlObjStorage->GetHeaderData
  1695. (CACHE_HEADER_DATA_ROOT_LEAK_OFFSET, &pEntry->dwNextLeak);
  1696. _UrlObjStorage->SetHeaderData
  1697. (CACHE_HEADER_DATA_ROOT_LEAK_OFFSET,
  1698. OffsetFromPointer (pEntry));
  1699. }
  1700. }
  1701. else
  1702. {
  1703. // NOTE: In the case that the entry is in a store (INSTALLED_DIRECTORY_KEY)
  1704. // we do allow the cache entry to be deleted, but we do NOT allow the associated
  1705. // file to be deleted. This at least allows us to delete these entries from
  1706. // the cache without affecting their (permanent) backing store files.
  1707. _UrlObjStorage->FreeEntry(pEntry);
  1708. }
  1709. delete_hash_item:
  1710. // Delete this item from the hash table.
  1711. if (pItem)
  1712. pItem->MarkFree();
  1713. Error = ERROR_SUCCESS;
  1714. Cleanup:
  1715. TcpsvcsDbgPrint ((DEBUG_ERRORS,
  1716. "URL_CONTAINER::DeleteUrlEntry() returning %ld\n", Error));
  1717. return Error;
  1718. }
  1719. DWORD URL_CONTAINER::UnlockUrl(LPCSTR UrlName)
  1720. /*++
  1721. Routine Description:
  1722. This member function unreferences the url entry, so that it can be
  1723. freed up when used no one.
  1724. Arguments:
  1725. Url : pointer to an URL name.
  1726. Return Value:
  1727. Windows Error Code.
  1728. --*/
  1729. {
  1730. DWORD Error;
  1731. BOOL fMustUnlock;
  1732. if (!LockContainer(&fMustUnlock))
  1733. {
  1734. Error = GetLastError();
  1735. goto exit;
  1736. }
  1737. HASH_ITEM* pItem;
  1738. URL_FILEMAP_ENTRY* pEntry;
  1739. // Look up the entry.
  1740. if (HashFindItem (UrlName, LOOKUP_URL_NOCREATE, &pItem))
  1741. pEntry = HashGetEntry (pItem);
  1742. else
  1743. {
  1744. Error = ERROR_FILE_NOT_FOUND;
  1745. goto exit;
  1746. }
  1747. TcpsvcsDbgPrint((DEBUG_CONTAINER,
  1748. "RefCount=%d, DeletePending=%d \r\n",
  1749. pEntry->NumReferences,
  1750. (pEntry->CacheEntryType & PENDING_DELETE_CACHE_ENTRY)? 1 : 0
  1751. ));
  1752. UnlockItem (pEntry, pItem);
  1753. // Hack to keep track of if any entries have locks.
  1754. GlobalRetrieveUrlCacheEntryFileCount--;
  1755. Error = ERROR_SUCCESS;
  1756. exit:
  1757. if (fMustUnlock) UnlockContainer();
  1758. return Error;
  1759. }
  1760. DWORD URL_CONTAINER::GetUrlInfo
  1761. (
  1762. LPCSTR UrlName,
  1763. LPCACHE_ENTRY_INFO* ppUrlInfo,
  1764. LPDWORD UrlInfoLength,
  1765. DWORD dwLookupFlags,
  1766. DWORD dwEntryFlags,
  1767. DWORD dwRetrievalFlags
  1768. )
  1769. /*++
  1770. Routine Description:
  1771. This member function retrieves the url info.
  1772. Arguments:
  1773. UrlName : name of the url file (unused now).
  1774. ppUrlInfo : pointer to the pointer to the url info structure that receives the url
  1775. info.
  1776. UrlInfoLength : pointer to a location where length of
  1777. the above buffer is passed in. On return, this contains the length
  1778. of the above buffer that is fulled in.
  1779. dwLookupFlags: flags, e.g. translate through redirects
  1780. Return Value:
  1781. Windows Error Code.
  1782. --*/
  1783. {
  1784. DWORD Error;
  1785. BOOL fMustUnlock;
  1786. LPURL_FILEMAP_ENTRY UrlEntry;
  1787. if (!LockContainer(&fMustUnlock))
  1788. {
  1789. Error = GetLastError();
  1790. goto exit;
  1791. }
  1792. // Look up the entry.
  1793. UrlEntry = HashFindEntry (UrlName, dwLookupFlags);
  1794. if (!UrlEntry)
  1795. {
  1796. Error = ERROR_FILE_NOT_FOUND;
  1797. goto exit;
  1798. }
  1799. // For content container, check that username matches.
  1800. if (IsContentContainer())
  1801. {
  1802. LPSTR pszHeaders = ((LPSTR) UrlEntry) + UrlEntry->HeaderInfoOffset;
  1803. if (!IsCorrectUser(pszHeaders, UrlEntry->HeaderInfoSize))
  1804. {
  1805. Error = ERROR_FILE_NOT_FOUND;
  1806. goto exit;
  1807. }
  1808. }
  1809. // Hide sparse cache entries from non-wininet clients.
  1810. if (UrlEntry->CacheEntryType & SPARSE_CACHE_ENTRY
  1811. && !(dwLookupFlags & LOOKUP_BIT_SPARSE))
  1812. {
  1813. Error = ERROR_FILE_NOT_FOUND;
  1814. goto exit;
  1815. }
  1816. // Find only installed entry types.
  1817. if ((dwEntryFlags & INTERNET_CACHE_FLAG_INSTALLED_ENTRY)
  1818. && (!(UrlEntry->CacheEntryType & INSTALLED_CACHE_ENTRY)))
  1819. {
  1820. Error = ERROR_FILE_NOT_FOUND;
  1821. goto exit;
  1822. }
  1823. if (UrlInfoLength)
  1824. {
  1825. if (!ppUrlInfo || !*ppUrlInfo)
  1826. *UrlInfoLength = 0;
  1827. Error = CopyUrlInfoGuard( UrlEntry, ppUrlInfo, UrlInfoLength,
  1828. (dwEntryFlags & INTERNET_CACHE_FLAG_ADD_FILENAME_ONLY ?
  1829. RETRIEVE_ONLY_FILENAME : 0) |
  1830. (dwEntryFlags & INTERNET_CACHE_FLAG_GET_STRUCT_ONLY ?
  1831. RETRIEVE_ONLY_STRUCT_INFO : 0) |
  1832. dwRetrievalFlags);
  1833. }
  1834. else
  1835. Error = ERROR_SUCCESS;
  1836. exit:
  1837. if (fMustUnlock) UnlockContainer();
  1838. return( Error );
  1839. }
  1840. DWORD URL_CONTAINER::SetExemptDelta
  1841. (URL_FILEMAP_ENTRY* UrlEntry, DWORD dwExemptDelta, DWORD dwItemOffset)
  1842. {
  1843. // Expanded history calls with STICKY_CACHE_ENTRY for no good reason.
  1844. // INET_ASSERT (UrlEntry->FileSize);
  1845. DWORD dwError = ERROR_SUCCESS;
  1846. if (dwExemptDelta)
  1847. {
  1848. if (!UrlEntry->dwExemptDelta)
  1849. {
  1850. // Entry is changing from non-exempt to exempt.
  1851. // (exempt limit check should be done at UpdateStickness
  1852. dwError = UpdateStickness(UrlEntry, URLCACHE_OP_SET_STICKY, dwItemOffset);
  1853. if( dwError != ERROR_SUCCESS )
  1854. goto End;
  1855. }
  1856. }
  1857. else // if (!dwExemptDelta)
  1858. {
  1859. if (UrlEntry->dwExemptDelta)
  1860. {
  1861. // Entry is changing from exempt to non-exempt.
  1862. dwError = UpdateStickness(UrlEntry, URLCACHE_OP_UNSET_STICKY, dwItemOffset);
  1863. if( dwError != ERROR_SUCCESS )
  1864. goto End;
  1865. }
  1866. }
  1867. UrlEntry->dwExemptDelta = dwExemptDelta;
  1868. End:
  1869. return dwError;
  1870. }
  1871. DWORD URL_CONTAINER::SetUrlInfo(LPCSTR UrlName,
  1872. LPCACHE_ENTRY_INFO UrlInfo, DWORD FieldControl)
  1873. /*++
  1874. Routine Description:
  1875. Arguments:
  1876. UrlName : name of the url file (unused now).
  1877. UrlInfo : pointer to the url info structure that has the url info to
  1878. be set.
  1879. Return Value:
  1880. Windows Error Code.
  1881. --*/
  1882. {
  1883. DWORD Error;
  1884. LPURL_FILEMAP_ENTRY UrlEntry;
  1885. BOOL fMustUnlock;
  1886. HASH_ITEM *pItem;
  1887. if (!LockContainer(&fMustUnlock))
  1888. {
  1889. Error = GetLastError();
  1890. goto Cleanup;
  1891. }
  1892. // Look up the entry.
  1893. if (HashFindItem (UrlName, 0, &pItem))
  1894. {
  1895. UrlEntry = HashGetEntry (pItem);
  1896. }
  1897. else
  1898. {
  1899. UrlEntry = NULL;
  1900. }
  1901. if (!UrlEntry)
  1902. {
  1903. Error = ERROR_FILE_NOT_FOUND;
  1904. goto Cleanup;
  1905. }
  1906. // Set cache entry ATTRIBUTE.
  1907. if(FieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
  1908. {
  1909. // We must preserve IDENTITY_CACHE_ENTRY bits, if they set
  1910. UrlEntry->CacheEntryType = UrlInfo->CacheEntryType
  1911. | (UrlEntry->CacheEntryType & IDENTITY_CACHE_ENTRY);
  1912. }
  1913. // Reset cache entry HITRATE.
  1914. if(FieldControl & CACHE_ENTRY_HITRATE_FC)
  1915. UrlEntry->NumAccessed = UrlInfo->dwHitRate;
  1916. // Set last modified time.
  1917. if(FieldControl & CACHE_ENTRY_MODTIME_FC)
  1918. UrlEntry->LastModifiedTime = FT2LL(UrlInfo->LastModifiedTime);
  1919. // Set expire time.
  1920. if( FieldControl & CACHE_ENTRY_EXPTIME_FC)
  1921. {
  1922. FileTime2DosTime(UrlInfo->ExpireTime, &(UrlEntry->dostExpireTime) );
  1923. }
  1924. // Set last access time.
  1925. if(FieldControl & CACHE_ENTRY_ACCTIME_FC)
  1926. UrlEntry->LastAccessedTime = FT2LL(UrlInfo->LastAccessTime);
  1927. // Set last sync time.
  1928. if(FieldControl & CACHE_ENTRY_SYNCTIME_FC)
  1929. {
  1930. FileTimeToDosDateTime( &(UrlInfo->LastSyncTime),
  1931. (LPWORD)&(UrlEntry->dostLastSyncTime),
  1932. ((LPWORD)&(UrlEntry->dostLastSyncTime)+1));
  1933. if ( UrlEntry->bSyncState != SYNCSTATE_VOLATILE
  1934. && UrlEntry->bSyncState < SYNCSTATE_STATIC)
  1935. {
  1936. // See if we should transition to SYNCSTATE_STATIC.
  1937. if (UrlEntry->bSyncState == SYNCSTATE_IMAGE)
  1938. {
  1939. // We have not had the image long enough to
  1940. // conclude it is static. See if it is older
  1941. // than MIN_AGESYNC.
  1942. LONGLONG qwCreate;
  1943. INET_ASSERT (UrlEntry->dostFileCreationTime);
  1944. DosDateTimeToFileTime(
  1945. * (LPWORD)&(UrlEntry->dostFileCreationTime),
  1946. *((LPWORD)&(UrlEntry->dostFileCreationTime)+1),
  1947. (FILETIME*) &qwCreate);
  1948. if (FT2LL(UrlInfo->LastSyncTime) > qwCreate + MIN_AGESYNC)
  1949. {
  1950. UrlEntry->bSyncState++;
  1951. }
  1952. }
  1953. else
  1954. {
  1955. if (++UrlEntry->bSyncState == SYNCSTATE_STATIC)
  1956. BETA_LOG (SYNCSTATE_IMAGE_STATIC);
  1957. }
  1958. }
  1959. }
  1960. if (FieldControl & CACHE_ENTRY_TYPE_FC)
  1961. {
  1962. UrlEntry->CacheEntryType = UrlInfo->CacheEntryType;
  1963. }
  1964. // Set exemption delta.
  1965. if (FieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
  1966. {
  1967. Error = SetExemptDelta (
  1968. UrlEntry,
  1969. UrlInfo->dwExemptDelta,
  1970. (DWORD)( ((LPBYTE) pItem) - *_UrlObjStorage->GetHeapStart())
  1971. );
  1972. if (Error != ERROR_SUCCESS)
  1973. goto Cleanup;
  1974. }
  1975. Error = ERROR_SUCCESS;
  1976. NotifyCacheChange(CACHE_NOTIFY_UPDATE_URL,
  1977. (DWORD)( ((LPBYTE) pItem) - *_UrlObjStorage->GetHeapStart())
  1978. );
  1979. Cleanup:
  1980. if (fMustUnlock) UnlockContainer();
  1981. return Error;
  1982. }
  1983. /*++
  1984. Adds or removes a URL from a group. If adding, may set exemption time.
  1985. --*/
  1986. DWORD URL_CONTAINER::SetUrlGroup (LPCSTR lpszUrl, DWORD dwFlags, GROUPID GroupId)
  1987. {
  1988. DWORD Error;
  1989. BOOL fMustUnlock;
  1990. LPURL_FILEMAP_ENTRY pEntry;
  1991. GROUP_ENTRY* pGroupEntry = NULL;
  1992. GROUP_ENTRY* pOldGroupEntry = NULL;
  1993. GROUPID gid = 0;
  1994. HASH_ITEM *pItem = NULL;
  1995. GroupMgr gm;
  1996. if (dwFlags & INTERNET_CACHE_GROUP_NONE)
  1997. return ERROR_SUCCESS;
  1998. if (!GroupId)
  1999. return ERROR_INVALID_PARAMETER;
  2000. if (!LockContainer(&fMustUnlock))
  2001. {
  2002. Error = GetLastError();
  2003. goto exit;
  2004. }
  2005. //
  2006. // HashFindEntry will do the same thing, however, we need pItem
  2007. // here so that we can set/clear the group bit
  2008. //
  2009. if (HashFindItem (lpszUrl, 0, &pItem))
  2010. {
  2011. pEntry = HashGetEntry (pItem);
  2012. }
  2013. else
  2014. {
  2015. pEntry = NULL;
  2016. }
  2017. if (!pEntry)
  2018. {
  2019. Error = ERROR_FILE_NOT_FOUND;
  2020. goto exit;
  2021. }
  2022. if( !gm.Init(this) )
  2023. {
  2024. Error = ERROR_WINHTTP_INTERNAL_ERROR;
  2025. goto exit;
  2026. }
  2027. if (dwFlags & INTERNET_CACHE_GROUP_REMOVE)
  2028. {
  2029. // offset to GROUP_ENTRY*
  2030. DWORD dwGEOffset = 0;
  2031. // find the group via GroupOffset
  2032. if( !pEntry->dwGroupOffset )
  2033. {
  2034. Error = ERROR_FILE_NOT_FOUND;
  2035. goto exit;
  2036. }
  2037. // Get GroupEntry Offset
  2038. if( pItem->HasMultiGroup() )
  2039. {
  2040. // multiple group, get from list
  2041. Error = gm.GetOffsetFromList(
  2042. pEntry->dwGroupOffset, GroupId, &dwGEOffset);
  2043. if( Error != ERROR_SUCCESS )
  2044. goto exit;
  2045. }
  2046. else
  2047. {
  2048. dwGEOffset = pEntry->dwGroupOffset;
  2049. }
  2050. // get group entry from the offset
  2051. pGroupEntry = _UrlObjStorage->ValidateGroupOffset(dwGEOffset, pItem);
  2052. if( !pGroupEntry )
  2053. {
  2054. Error = ERROR_FILE_NOT_FOUND;
  2055. goto exit;
  2056. }
  2057. // Remove the group from list
  2058. if( pItem->HasMultiGroup() )
  2059. {
  2060. // remove it from list
  2061. DWORD dwNewHeaderOffset = pEntry->dwGroupOffset;
  2062. Error = gm.RemoveFromGroupList(
  2063. pEntry->dwGroupOffset, dwGEOffset, &dwNewHeaderOffset );
  2064. if( Error != ERROR_SUCCESS )
  2065. {
  2066. goto exit;
  2067. }
  2068. //
  2069. // header may have been changed (if head is the one we want)
  2070. // newHeaderOffset = 0 means last group has been removed
  2071. //
  2072. // NOTE: even we may have one item left on the list, we
  2073. // are not changing the multiGroup flag on this
  2074. // entry, so the dwGroupOffset are still points to
  2075. // the list
  2076. pEntry->dwGroupOffset = dwNewHeaderOffset;
  2077. }
  2078. else
  2079. {
  2080. // set offset to 0 (single group)
  2081. pEntry->dwGroupOffset = 0;
  2082. }
  2083. // if dwExamptDelta is set, we should leave the stick bit
  2084. if(!pEntry->dwExemptDelta)
  2085. {
  2086. //
  2087. // if the unassociated group is sticky, we are remove
  2088. // the sticky bit of this url
  2089. //
  2090. // For multiple groups, we will have to make sure all
  2091. // the remaining groups are non-sticky
  2092. //
  2093. if( IsStickyGroup(pGroupEntry->gid ) &&
  2094. ( !pItem->HasMultiGroup() ||
  2095. gm.NoMoreStickyEntryOnList(pEntry->dwGroupOffset) )
  2096. )
  2097. {
  2098. Error = UpdateStickness(
  2099. pEntry,
  2100. URLCACHE_OP_UNSET_STICKY,
  2101. (DWORD)( ((LPBYTE) pItem) - *_UrlObjStorage->GetHeapStart())
  2102. );
  2103. if( Error != ERROR_SUCCESS )
  2104. goto exit;
  2105. }
  2106. }
  2107. // update the usage
  2108. if( pItem->HasMultiGroup() )
  2109. {
  2110. // dwGroupOffset now offset to head of group list
  2111. gm.AdjustUsageOnList(
  2112. pEntry->dwGroupOffset, -RealFileSize(pEntry->dwFileSize) );
  2113. }
  2114. else
  2115. {
  2116. AdjustGroupUsage(pGroupEntry, -RealFileSize(pEntry->dwFileSize));
  2117. }
  2118. //
  2119. // update hash bit indicating no group associate with this url
  2120. // we won't clear the multiple group flag even if there is single
  2121. // group left on the group list.
  2122. //
  2123. if( !pEntry->dwGroupOffset )
  2124. {
  2125. pItem->ClearGroup();
  2126. pItem->ClearMultGroup();
  2127. }
  2128. }
  2129. else
  2130. {
  2131. // Find Group via gid
  2132. Error = gm.FindEntry(GroupId, &pGroupEntry, FALSE);
  2133. if( Error != ERROR_SUCCESS )
  2134. {
  2135. goto exit;
  2136. }
  2137. if( pItem->HasGroup() )
  2138. {
  2139. // multiple group
  2140. LPBYTE lpBase;
  2141. DWORD dwGroupEntryOffset = 0;
  2142. lpBase = *_UrlObjStorage->GetHeapStart();
  2143. dwGroupEntryOffset = PtrDiff32(pGroupEntry, lpBase);
  2144. DWORD dwListEntryOffset = 0;
  2145. DWORD dwEntryOffset = 0;
  2146. DWORD dwItemOffset = 0;
  2147. DWORD dwOldGroupEntryOffset = 0;
  2148. if( !pItem->HasMultiGroup() )
  2149. {
  2150. //
  2151. // switch from a single group to multiple
  2152. // group, need to
  2153. // 1) create a new group list
  2154. // 2) add the existing single group to the newly created list
  2155. //
  2156. //////////////////////////////////////////////////////////////////
  2157. // BEGIN WARNING: The file might be grown and remapped, so all //
  2158. // pointers into the file before this point may be invalidated. //
  2159. //////////////////////////////////////////////////////////////////
  2160. // save offset
  2161. dwEntryOffset = PtrDiff32(pEntry, lpBase);
  2162. dwItemOffset = PtrDiff32(pItem, lpBase);
  2163. if( pOldGroupEntry )
  2164. {
  2165. dwOldGroupEntryOffset = PtrDiff32(pOldGroupEntry, lpBase);
  2166. }
  2167. //
  2168. // get a new List (memfile may grown)
  2169. //
  2170. Error = gm.CreateNewGroupList(&dwListEntryOffset);
  2171. if( Error != ERROR_SUCCESS )
  2172. {
  2173. goto exit;
  2174. }
  2175. // restore pointers based on (possible) new base addr
  2176. lpBase = *_UrlObjStorage->GetHeapStart();
  2177. pEntry = (URL_FILEMAP_ENTRY*)(lpBase + dwEntryOffset);
  2178. pGroupEntry = (GROUP_ENTRY*)(lpBase + dwGroupEntryOffset);
  2179. pItem = (HASH_ITEM*) (lpBase + dwItemOffset);
  2180. if( pOldGroupEntry )
  2181. {
  2182. pOldGroupEntry
  2183. = (GROUP_ENTRY*) (lpBase + dwOldGroupEntryOffset);
  2184. }
  2185. //////////////////////////////////////////////////////////////////
  2186. // END WARNING: The file might have grown and remapped, so all //
  2187. // pointers into the file after this point must be recalculated //
  2188. // from offsets. //
  2189. //////////////////////////////////////////////////////////////////
  2190. //
  2191. // add the original group (whose offset is indicated
  2192. // with dwGroupOffset of the url entry)
  2193. // to the newly created list
  2194. //
  2195. //////////////////////////////////////////////////////////////////
  2196. // BEGIN WARNING: The file might be grown and remapped, so all //
  2197. // pointers into the file before this point may be invalidated. //
  2198. //////////////////////////////////////////////////////////////////
  2199. // save offset
  2200. lpBase = *_UrlObjStorage->GetHeapStart();
  2201. dwEntryOffset = PtrDiff32(pEntry, lpBase);
  2202. dwItemOffset = PtrDiff32(pItem, lpBase);
  2203. dwGroupEntryOffset = PtrDiff32(pGroupEntry, lpBase);
  2204. if( pOldGroupEntry )
  2205. {
  2206. dwOldGroupEntryOffset = PtrDiff32(pOldGroupEntry, lpBase);
  2207. }
  2208. Error = gm.AddToGroupList(
  2209. dwListEntryOffset, pEntry->dwGroupOffset);
  2210. // restore offset
  2211. lpBase = *_UrlObjStorage->GetHeapStart();
  2212. pEntry = (URL_FILEMAP_ENTRY*)(lpBase + dwEntryOffset);
  2213. pGroupEntry = (GROUP_ENTRY*)(lpBase + dwGroupEntryOffset);
  2214. pItem = (HASH_ITEM*) (lpBase + dwItemOffset);
  2215. if( pOldGroupEntry )
  2216. {
  2217. pOldGroupEntry
  2218. = (GROUP_ENTRY*) (lpBase + dwOldGroupEntryOffset);
  2219. }
  2220. //////////////////////////////////////////////////////////////////
  2221. // END WARNING: The file might have grown and remapped, so all //
  2222. // pointers into the file after this point must be recalculated //
  2223. // from offsets. //
  2224. //////////////////////////////////////////////////////////////////
  2225. if( Error != ERROR_SUCCESS )
  2226. {
  2227. goto exit;
  2228. }
  2229. //
  2230. // the dwGroupOffset of the url entry now
  2231. // points the the head of a group list
  2232. //
  2233. pEntry->dwGroupOffset = dwListEntryOffset;
  2234. pItem->MarkMultGroup();
  2235. }
  2236. //
  2237. // Multiple group, just add the new group to the list
  2238. //
  2239. //////////////////////////////////////////////////////////////////
  2240. // BEGIN WARNING: The file might be grown and remapped, so all //
  2241. // pointers into the file before this point may be invalidated. //
  2242. //////////////////////////////////////////////////////////////////
  2243. // save offset
  2244. lpBase = *_UrlObjStorage->GetHeapStart();
  2245. dwEntryOffset = PtrDiff32(pEntry, lpBase);
  2246. dwItemOffset = PtrDiff32(pItem, lpBase);
  2247. dwGroupEntryOffset = PtrDiff32(pGroupEntry, lpBase);
  2248. Error = gm.AddToGroupList(
  2249. pEntry->dwGroupOffset, dwGroupEntryOffset);
  2250. if( Error != ERROR_SUCCESS )
  2251. {
  2252. goto exit;
  2253. }
  2254. // remap since multiple group may cause memfile grow
  2255. lpBase = *_UrlObjStorage->GetHeapStart();
  2256. pEntry = (URL_FILEMAP_ENTRY*)(lpBase + dwEntryOffset);
  2257. pGroupEntry = (GROUP_ENTRY*)(lpBase + dwGroupEntryOffset);
  2258. pItem = (HASH_ITEM*) (lpBase + dwItemOffset);
  2259. //////////////////////////////////////////////////////////////////
  2260. // END WARNING: The file might have grown and remapped, so all //
  2261. // pointers into the file after this point must be recalculated //
  2262. // from offsets. //
  2263. //////////////////////////////////////////////////////////////////
  2264. }
  2265. else
  2266. {
  2267. // single group, dwGroupOffset points the real group
  2268. pEntry->dwGroupOffset = PtrDiff32(pGroupEntry, *_UrlObjStorage->GetHeapStart());
  2269. }
  2270. // update hash bit indicating group associate with this url
  2271. pItem->MarkGroup();
  2272. // if group is sticky, mark the entry to sticky as well
  2273. if( IsStickyGroup(pGroupEntry->gid) )
  2274. {
  2275. Error = UpdateStickness(
  2276. pEntry,
  2277. URLCACHE_OP_SET_STICKY,
  2278. (DWORD)( ((LPBYTE) pItem) - *_UrlObjStorage->GetHeapStart())
  2279. );
  2280. if( Error != ERROR_SUCCESS )
  2281. goto exit;
  2282. }
  2283. // update the usage
  2284. if( pItem->HasMultiGroup() )
  2285. {
  2286. // dwGroupOffset now offset to head of group list
  2287. gm.AdjustUsageOnList(
  2288. pEntry->dwGroupOffset, RealFileSize(pEntry->dwFileSize) );
  2289. }
  2290. else
  2291. {
  2292. AdjustGroupUsage(pGroupEntry, RealFileSize(pEntry->dwFileSize) );
  2293. }
  2294. //
  2295. // track the usage and quota
  2296. // NOTE: we still allow this url to be added to the group
  2297. // even if usage > quota, DISK_FULL error will be
  2298. // returned, so the client is responsible to take
  2299. // futher action
  2300. //
  2301. if( pGroupEntry->llDiskUsage > (pGroupEntry->dwDiskQuota * 1024) )
  2302. {
  2303. Error = ERROR_NOT_ENOUGH_QUOTA;
  2304. goto exit;
  2305. }
  2306. }
  2307. Error = ERROR_SUCCESS;
  2308. NotifyCacheChange(CACHE_NOTIFY_UPDATE_URL,
  2309. (DWORD)( ((LPBYTE) pItem) - *_UrlObjStorage->GetHeapStart())
  2310. );
  2311. exit:
  2312. if (fMustUnlock) UnlockContainer();
  2313. return Error;
  2314. }
  2315. /*++
  2316. Gets group ID and exemption time for a particular URL.
  2317. --*/
  2318. DWORD URL_CONTAINER::GetUrlInGroup
  2319. (LPCSTR lpszUrl, GROUPID* pGroupId, LPDWORD pdwExemptDelta)
  2320. {
  2321. DWORD dwError;
  2322. BOOL fMustUnlock;
  2323. URL_FILEMAP_ENTRY* pEntry;
  2324. GROUP_ENTRY* pGroupEntry = NULL;
  2325. HASH_ITEM* pItem = NULL;
  2326. if (!LockContainer(&fMustUnlock))
  2327. {
  2328. dwError = GetLastError();
  2329. goto exit;
  2330. }
  2331. // Look up the entry.
  2332. if (HashFindItem (lpszUrl, 0, &pItem))
  2333. {
  2334. pEntry = HashGetEntry (pItem);
  2335. }
  2336. else
  2337. {
  2338. pEntry = NULL;
  2339. }
  2340. if (!pEntry)
  2341. dwError = ERROR_FILE_NOT_FOUND;
  2342. else
  2343. {
  2344. if( pEntry->dwGroupOffset )
  2345. {
  2346. pGroupEntry = _UrlObjStorage->ValidateGroupOffset(
  2347. pEntry->dwGroupOffset, pItem);
  2348. if( pGroupEntry )
  2349. {
  2350. INET_ASSERT(pGroupEntry->gid);
  2351. *((LONGLONG*) pGroupId) = pGroupEntry->gid;
  2352. }
  2353. else
  2354. {
  2355. dwError = ERROR_FILE_NOT_FOUND;
  2356. }
  2357. }
  2358. else
  2359. {
  2360. *((LONGLONG*) pGroupId) = 0;
  2361. }
  2362. *pdwExemptDelta = pEntry->dwExemptDelta;
  2363. dwError = ERROR_SUCCESS;
  2364. }
  2365. exit:
  2366. if (fMustUnlock) UnlockContainer();
  2367. return dwError;
  2368. }
  2369. DWORD URL_CONTAINER::CreateUniqueFile(LPCSTR UrlName, DWORD ExpectedSize,
  2370. LPCSTR lpszFileExtension, LPTSTR FileName,
  2371. HANDLE *phfHandle, BOOL fCreatePerUser)
  2372. /*++
  2373. Routine Description:
  2374. This function creates a temperary file in the cache storage. This call
  2375. is called by the application when it receives a url file from a
  2376. server. When the receive is completed it caches this file to url cache
  2377. management, which will move the file to permanent cache file. The idea
  2378. is the cache file is written only once directly into the cache store.
  2379. Arguments:
  2380. UrlName : name of the url file (unused now).
  2381. ExpectedSize : expected size of the incoming file. If it is unknown
  2382. this value should be set to 0.
  2383. lpszFileExtension: extension for the filename created
  2384. FileName : pointer to a buffer that receives the full path name of the
  2385. the temp file.
  2386. phfHandle : pointer to a handle that receives the handle of the file
  2387. being create; pass null if we don't care (the file will be closed).
  2388. Return Value:
  2389. Windows Error Code.
  2390. --*/
  2391. {
  2392. DWORD Error;
  2393. BOOL fMustUnlock;
  2394. // BUGBUG - adding LockContainer here.
  2395. if (!LockContainer(&fMustUnlock))
  2396. {
  2397. Error = (GetLastError());
  2398. goto exit;
  2399. }
  2400. Error = _FileManager->CreateUniqueFile((LPSTR) UrlName, (DWORD) ExpectedSize, (LPSTR) FileName,
  2401. (LPSTR) lpszFileExtension, (HANDLE*) phfHandle, (BOOL)fCreatePerUser);
  2402. exit:
  2403. if (fMustUnlock) UnlockContainer();
  2404. return( Error );
  2405. }
  2406. DWORD URL_CONTAINER::FindNextEntry
  2407. (LPDWORD lpdwEnum, LPCACHE_ENTRY_INFO *ppCEI, LPDWORD lpdwCEI, DWORD dwFilter, GROUPID GroupId, DWORD dwFlags, DWORD dwRetrievalFlags)
  2408. {
  2409. DWORD Error;
  2410. URL_FILEMAP_ENTRY* pEntry;
  2411. DWORD dwEnumSave;
  2412. BOOL fMustUnlock;
  2413. DWORD dwCopyFlags;
  2414. if (!LockContainer(&fMustUnlock))
  2415. {
  2416. Error = GetLastError();
  2417. goto Cleanup;
  2418. }
  2419. BOOL fCheckUser;
  2420. fCheckUser = IsContentContainer() && !(dwFilter & OTHER_USER_CACHE_ENTRY);
  2421. dwCopyFlags = 0;
  2422. if (dwFlags & FIND_FLAGS_RETRIEVE_ONLY_STRUCT_INFO)
  2423. {
  2424. dwCopyFlags = RETRIEVE_ONLY_STRUCT_INFO;
  2425. }
  2426. else if (dwFlags & FIND_FLAGS_RETRIEVE_ONLY_FIXED_AND_FILENAME)
  2427. {
  2428. dwCopyFlags = RETRIEVE_ONLY_FILENAME;
  2429. }
  2430. dwCopyFlags |= dwRetrievalFlags;
  2431. while (1)
  2432. {
  2433. dwEnumSave = *lpdwEnum;
  2434. pEntry = (URL_FILEMAP_ENTRY*) _UrlObjStorage->FindNextEntry(lpdwEnum, dwFilter, GroupId);
  2435. if(!pEntry)
  2436. {
  2437. Error = ERROR_NO_MORE_ITEMS;
  2438. goto Cleanup;
  2439. }
  2440. // For content container, skip items marked for another user.
  2441. if (fCheckUser)
  2442. {
  2443. LPSTR pszHeaders = ((LPSTR) pEntry) + pEntry->HeaderInfoOffset;
  2444. if (!IsCorrectUser(pszHeaders, pEntry->HeaderInfoSize))
  2445. continue;
  2446. }
  2447. // Copy the data
  2448. Error = CopyUrlInfoGuard(pEntry, ppCEI, lpdwCEI, dwCopyFlags);
  2449. switch (Error)
  2450. {
  2451. case ERROR_INSUFFICIENT_BUFFER:
  2452. // Restore current enum position.
  2453. *lpdwEnum = dwEnumSave;
  2454. goto Cleanup;
  2455. case ERROR_FILE_NOT_FOUND:
  2456. continue;
  2457. default:
  2458. INET_ASSERT (FALSE);
  2459. // intentional fall through
  2460. case ERROR_SUCCESS:
  2461. goto Cleanup;
  2462. }
  2463. } // end while(1)
  2464. Cleanup:
  2465. if (fMustUnlock) UnlockContainer();
  2466. return Error;
  2467. }
  2468. /*------------------------------------------------------------------------------
  2469. CopyUrlInfo
  2470. Routine Description:
  2471. Copy URL info data from an URL_FILEMAP_ENTRY in the memory mapped file
  2472. to CACHE_ENTRY_INFO output buffer. If the buffer given is sufficient,
  2473. it returns ERROR_INSUFFICIENT_BUFFER, and pcbInfo will contain
  2474. buffer size required.
  2475. Arguments:
  2476. pEntry : pointer to the source of the URL info.
  2477. ppInfo : ptr to ptr to an entry info buffer, where the url entry info
  2478. is returned.
  2479. pcbInfo : pointer to a DWORD location containing the size of the
  2480. above buffer, on return it has the size of the buffer consumed or
  2481. size of the buffer required for successful retrieval.
  2482. Return Value:
  2483. Windows Error Code.
  2484. ------------------------------------------------------------------------------*/
  2485. DWORD URL_CONTAINER::CopyUrlInfo(LPURL_FILEMAP_ENTRY pEntry,
  2486. LPCACHE_ENTRY_INFO* ppInfo,
  2487. LPDWORD pcbInfo,
  2488. DWORD dwFlags)
  2489. {
  2490. DWORD cbRequired;
  2491. DWORD dwError = ERROR_SUCCESS;
  2492. DWORD cbSourceUrlName;
  2493. DWORD cbLocalFileName;
  2494. DWORD cbHeaderInfo;
  2495. DWORD cbFileExt;
  2496. INET_ASSERT(!((dwFlags & RETRIEVE_WITH_ALLOCATION) &&
  2497. (dwFlags & RETRIEVE_ONLY_FILENAME
  2498. || dwFlags & RETRIEVE_ONLY_STRUCT_INFO)));
  2499. // Check signature
  2500. if (pEntry->dwSig != SIG_URL)
  2501. {
  2502. INET_ASSERT(FALSE);
  2503. dwError = ERROR_FILE_NOT_FOUND;
  2504. goto exit;
  2505. }
  2506. // Verify url string exists.
  2507. if (!pEntry->UrlNameOffset)
  2508. {
  2509. INET_ASSERT(FALSE);
  2510. dwError = ERROR_FILE_NOT_FOUND;
  2511. goto exit;
  2512. }
  2513. // Hate using goto's but, don't want to clutter anymore than I have to.
  2514. // We assume that anything functions that pass these flags will have allocated
  2515. // enough memory before hand.
  2516. if ((dwFlags & RETRIEVE_ONLY_FILENAME) || (dwFlags & RETRIEVE_ONLY_STRUCT_INFO))
  2517. {
  2518. if (ppInfo && *ppInfo)
  2519. {
  2520. memset(*ppInfo, 0, sizeof(INTERNET_CACHE_ENTRY_INFO));
  2521. }
  2522. goto ShortCircuit;
  2523. }
  2524. // ----------------- Calculate embedded data sizes ------------------------
  2525. // All byte counts are sizes.
  2526. // SourceUrlName length;
  2527. cbSourceUrlName = strlen((LPSTR) OFFSET_TO_POINTER(pEntry, pEntry->UrlNameOffset)) + 1;
  2528. // LocalFileName length.
  2529. if(pEntry->InternalFileNameOffset)
  2530. {
  2531. cbLocalFileName =
  2532. _FileManager->GetDirLen(pEntry->DirIndex)
  2533. + strlen((LPSTR) OFFSET_TO_POINTER(pEntry, pEntry->InternalFileNameOffset))
  2534. + 1;
  2535. }
  2536. else
  2537. cbLocalFileName = 0;
  2538. // HeaderInfo length.
  2539. cbHeaderInfo = (pEntry->HeaderInfoOffset) ? pEntry->HeaderInfoSize + 1 : 0;
  2540. // File extension length.
  2541. if (pEntry->FileExtensionOffset)
  2542. {
  2543. cbFileExt =
  2544. strlen((LPSTR) OFFSET_TO_POINTER(pEntry, pEntry->FileExtensionOffset)) + 1;
  2545. }
  2546. else
  2547. cbFileExt = 0;
  2548. // Alignment - these quantities are already aligned in
  2549. // URL_FILEMAP_ENTRY and should be reflected in its size.
  2550. cbSourceUrlName = ROUNDUPDWORD(cbSourceUrlName);
  2551. cbLocalFileName = ROUNDUPDWORD(cbLocalFileName);
  2552. cbHeaderInfo = ROUNDUPDWORD(cbHeaderInfo);
  2553. cbFileExt = ROUNDUPDWORD(cbFileExt);
  2554. cbRequired = *pcbInfo;
  2555. *pcbInfo = sizeof(CACHE_ENTRY_INFO)
  2556. + cbSourceUrlName
  2557. + cbLocalFileName
  2558. + cbHeaderInfo
  2559. + cbFileExt;
  2560. if (dwFlags & RETRIEVE_WITH_ALLOCATION)
  2561. {
  2562. // If we are allocating entry info, use the ex version.
  2563. *pcbInfo += sizeof(CACHE_ENTRY_INFOEX) - sizeof(CACHE_ENTRY_INFO);
  2564. *ppInfo = (LPCACHE_ENTRY_INFO)ALLOCATE_FIXED_MEMORY(*pcbInfo);
  2565. if (!*ppInfo)
  2566. {
  2567. dwError = ERROR_NOT_ENOUGH_MEMORY;
  2568. goto exit;
  2569. }
  2570. }
  2571. else
  2572. {
  2573. // Second check for required buffer size.
  2574. if (cbRequired < *pcbInfo )
  2575. {
  2576. dwError = ERROR_INSUFFICIENT_BUFFER;
  2577. goto exit;
  2578. }
  2579. }
  2580. // ---------------------- Copy embedded data --------------------------------
  2581. // A Typical CACHE_ENTRY_INFO will look like
  2582. //
  2583. // [CACHE_ENTRY_INFO][UrlName][FileName][Headers][FileExtension]
  2584. //
  2585. // ^ ^ ^ ^
  2586. // | | | |
  2587. // | | | lpszFileExtension
  2588. // | | |
  2589. // | | lpHeaderInfo
  2590. // | |
  2591. // | lpszLocalFileName
  2592. // |
  2593. // lpszSourceUrlName
  2594. //
  2595. // Pointer walks through CACHE_ENTRY_INFO appended data.
  2596. LPBYTE pCur;
  2597. pCur = (LPBYTE) *ppInfo + sizeof(CACHE_ENTRY_INFO);
  2598. if (dwFlags & RETRIEVE_WITH_ALLOCATION)
  2599. {
  2600. // If we are creating the -ex version, skip over those fields.
  2601. pCur += sizeof(CACHE_ENTRY_INFOEX) - sizeof(CACHE_ENTRY_INFO);
  2602. }
  2603. // UrlName.
  2604. memcpy(pCur, OFFSET_TO_POINTER(pEntry, pEntry->UrlNameOffset), cbSourceUrlName);
  2605. (*ppInfo)->lpszSourceUrlName = (LPSTR) pCur;
  2606. pCur += cbSourceUrlName;
  2607. // FileName
  2608. if (cbLocalFileName)
  2609. {
  2610. DWORD cb;
  2611. if (!_FileManager->GetFilePathFromEntry(pEntry, (LPSTR) pCur, &(cb = MAX_PATH)))
  2612. {
  2613. dwError = ERROR_FILE_NOT_FOUND;
  2614. goto exit;
  2615. }
  2616. (*ppInfo)->lpszLocalFileName = (LPTSTR) pCur;
  2617. pCur += cbLocalFileName;
  2618. }
  2619. else
  2620. (*ppInfo)->lpszLocalFileName = NULL;
  2621. // HeaderInfo
  2622. if (cbHeaderInfo)
  2623. {
  2624. memcpy (pCur, OFFSET_TO_POINTER(pEntry, pEntry->HeaderInfoOffset),
  2625. pEntry->HeaderInfoSize);
  2626. pCur[pEntry->HeaderInfoSize] = 0;
  2627. (*ppInfo)->lpHeaderInfo = (LPTSTR)pCur;
  2628. pCur += cbHeaderInfo;
  2629. }
  2630. else
  2631. (*ppInfo)->lpHeaderInfo = NULL;
  2632. // FileExt
  2633. if (cbFileExt)
  2634. {
  2635. memcpy(pCur, OFFSET_TO_POINTER(pEntry, pEntry->FileExtensionOffset), cbFileExt);
  2636. (*ppInfo)->lpszFileExtension = (LPTSTR) pCur;
  2637. pCur += cbFileExt;
  2638. }
  2639. else
  2640. (*ppInfo)->lpszFileExtension = NULL;
  2641. // ------------ Set remaining CACHE_ENTRY_INFO members -------------
  2642. ShortCircuit:
  2643. // Struct size, entry type, use count and hit rate.
  2644. (*ppInfo)->dwStructSize = URL_CACHE_VERSION_NUM;
  2645. (*ppInfo)->CacheEntryType = pEntry->CacheEntryType & ~IDENTITY_CACHE_ENTRY;
  2646. if (pEntry->bSyncState == SYNCSTATE_STATIC)
  2647. (*ppInfo)->CacheEntryType |= STATIC_CACHE_ENTRY;
  2648. (*ppInfo)->dwUseCount = pEntry->NumReferences;
  2649. (*ppInfo)->dwHitRate = pEntry->NumAccessed;
  2650. // File size.
  2651. (*ppInfo)->dwSizeLow = pEntry->dwFileSize;
  2652. (*ppInfo)->dwSizeHigh = 0;
  2653. // Last modified, expire, last access and last sync times, maybe download time.
  2654. (*ppInfo)->LastModifiedTime = *LONGLONG_TO_FILETIME(&pEntry->LastModifiedTime);
  2655. // expire time may be 0
  2656. DosTime2FileTime(pEntry->dostExpireTime, &((*ppInfo)->ExpireTime));
  2657. (*ppInfo)->LastAccessTime = *LONGLONG_TO_FILETIME(&pEntry->LastAccessedTime);
  2658. if (dwFlags & RETRIEVE_WITH_ALLOCATION)
  2659. {
  2660. CACHE_ENTRY_INFOEX* pCEI = (CACHE_ENTRY_INFOEX*) *ppInfo;
  2661. DosDateTimeToFileTime(*(LPWORD)&(pEntry->dostFileCreationTime),
  2662. *((LPWORD)&(pEntry->dostFileCreationTime)+1),
  2663. &pCEI->ftDownload);
  2664. DosTime2FileTime(pEntry->dostPostCheckTime, &pCEI->ftPostCheck);
  2665. }
  2666. DosDateTimeToFileTime(*(LPWORD)&(pEntry->dostLastSyncTime),
  2667. *((LPWORD)&(pEntry->dostLastSyncTime)+1),
  2668. &((*ppInfo)->LastSyncTime));
  2669. // Header info size and exempt delta.
  2670. (*ppInfo)->dwHeaderInfoSize = pEntry->HeaderInfoSize;
  2671. (*ppInfo)->dwExemptDelta = pEntry->dwExemptDelta;
  2672. // If we want only struct info and filename, we'll assume that we've preallocated
  2673. // enough memory.
  2674. if (dwFlags & RETRIEVE_ONLY_FILENAME)
  2675. {
  2676. DWORD cb;
  2677. if (!_FileManager->GetFilePathFromEntry(pEntry, (LPSTR) (*ppInfo) + sizeof(INTERNET_CACHE_ENTRY_INFO),
  2678. &(cb = MAX_PATH)))
  2679. {
  2680. dwError = ERROR_FILE_NOT_FOUND;
  2681. goto exit;
  2682. }
  2683. (*ppInfo)->lpszLocalFileName = (LPTSTR) (*ppInfo) + sizeof(INTERNET_CACHE_ENTRY_INFO);
  2684. }
  2685. exit:
  2686. return dwError;
  2687. }
  2688. // CopyUrlInfoGuard puts an exception handler around CopyUrlInfo
  2689. // for those case when we don't get a chance to flush the memory-mapped
  2690. // file, thus corrupting the cache.
  2691. // We put the try in this function, rather than in CopyUrlInfo, to
  2692. // avoid affecting the perf characteristics.
  2693. DWORD URL_CONTAINER::CopyUrlInfoGuard(LPURL_FILEMAP_ENTRY pEntry,
  2694. LPCACHE_ENTRY_INFO* ppInfo,
  2695. LPDWORD pcbInfo,
  2696. DWORD dwFlags)
  2697. {
  2698. DWORD dwError;
  2699. __try
  2700. {
  2701. dwError = CopyUrlInfo(pEntry, ppInfo, pcbInfo, dwFlags);
  2702. } // __try
  2703. __except(EXCEPTION_EXECUTE_HANDLER)
  2704. {
  2705. INET_ASSERT(FALSE);
  2706. dwError = ERROR_FILE_NOT_FOUND;
  2707. }
  2708. ENDEXCEPT
  2709. return dwError;
  2710. }
  2711. void URL_CONTAINER::UnlockItem (URL_FILEMAP_ENTRY* pEntry, HASH_ITEM* pItem)
  2712. {
  2713. // Possibly a bogus assert due to using lazy-write mappings?
  2714. //INET_ASSERT (pEntry->NumReferences);
  2715. if (pEntry->NumReferences)
  2716. {
  2717. if (--pEntry->NumReferences)
  2718. {
  2719. if( !pItem->IsLocked() )
  2720. {
  2721. // corrupted index file, we have to believe the hash table
  2722. // to fixup the cache entry
  2723. INET_ASSERT (FALSE);
  2724. pEntry->NumReferences = 0;
  2725. }
  2726. }
  2727. else
  2728. {
  2729. pItem->ClearLocked();
  2730. // If the item is marked for pending delete, do it now.
  2731. if (pEntry->CacheEntryType & PENDING_DELETE_CACHE_ENTRY)
  2732. DeleteUrlEntry (pEntry, pItem, SIG_DELETE);
  2733. }
  2734. }
  2735. }
  2736. void URL_CONTAINER::UnlockAllItems (void)
  2737. {
  2738. DWORD dwEnum = *(_UrlObjStorage->GetPtrToHashTableOffset());
  2739. // Enumerate hash table items.
  2740. while (dwEnum)
  2741. {
  2742. HASH_ITEM *pItem = HashGetNextItem
  2743. (_UrlObjStorage, *(_UrlObjStorage->GetHeapStart()), &dwEnum, 0);
  2744. if (pItem && (pItem->IsLocked()))
  2745. {
  2746. // Validate and unlock the entry.
  2747. URL_FILEMAP_ENTRY *pEntry =
  2748. _UrlObjStorage->ValidateUrlOffset (pItem->dwOffset);
  2749. if (!pEntry)
  2750. pItem->MarkFree(); // invalid item
  2751. else
  2752. {
  2753. // Clear the lockcount.
  2754. pEntry->NumReferences = 1;
  2755. UnlockItem (pEntry, pItem);
  2756. }
  2757. }
  2758. }
  2759. }
  2760. DWORD URL_CONTAINER::RegisterCacheNotify( HWND hWnd,
  2761. UINT uMsg,
  2762. GROUPID gid,
  2763. DWORD dwFilter)
  2764. {
  2765. BOOL fUnlock;
  2766. LockContainer(&fUnlock);
  2767. _UrlObjStorage->SetHeaderData(
  2768. CACHE_HEADER_DATA_NOTIFICATION_HWND, GuardedCast((DWORD_PTR)hWnd));
  2769. _UrlObjStorage->SetHeaderData(
  2770. CACHE_HEADER_DATA_NOTIFICATION_MESG, (DWORD)uMsg);
  2771. _UrlObjStorage->SetHeaderData(
  2772. CACHE_HEADER_DATA_NOTIFICATION_FILTER, (DWORD)dwFilter);
  2773. if (fUnlock) UnlockContainer();
  2774. return ERROR_SUCCESS;
  2775. }
  2776. // update stickness will do:
  2777. // 1. flip the bit
  2778. // 2. update the exempt usage
  2779. // 3. send notification
  2780. DWORD URL_CONTAINER::UpdateStickness( URL_FILEMAP_ENTRY* pEntry,
  2781. DWORD dwOp,
  2782. DWORD dwItemOffset)
  2783. {
  2784. DWORD dwError = ERROR_SUCCESS;
  2785. if( dwOp == URLCACHE_OP_SET_STICKY )
  2786. {
  2787. if( !( pEntry->CacheEntryType & STICKY_CACHE_ENTRY ) )
  2788. {
  2789. // Ensure that exempt items do not crowd the cache.
  2790. LONGLONG FileUsage = RealFileSize(pEntry->dwFileSize);
  2791. LONGLONG ExemptUsage = _UrlObjStorage->GetExemptUsage();
  2792. LONGLONG CacheLimit = _UrlObjStorage->GetCacheLimit();
  2793. LONGLONG MaxExempt = (CacheLimit * MAX_EXEMPT_PERCENTAGE) / 100;
  2794. if (ExemptUsage + FileUsage > MaxExempt)
  2795. return ERROR_DISK_FULL;
  2796. pEntry->CacheEntryType |= STICKY_CACHE_ENTRY;
  2797. _UrlObjStorage->AdjustExemptUsage(RealFileSize(pEntry->dwFileSize));
  2798. NotifyCacheChange(CACHE_NOTIFY_URL_SET_STICKY, dwItemOffset);
  2799. }
  2800. }
  2801. else
  2802. if( dwOp == URLCACHE_OP_UNSET_STICKY )
  2803. {
  2804. if( pEntry->CacheEntryType & STICKY_CACHE_ENTRY )
  2805. {
  2806. pEntry->CacheEntryType &= ~STICKY_CACHE_ENTRY;
  2807. _UrlObjStorage->AdjustExemptUsage(-RealFileSize(pEntry->dwFileSize));
  2808. NotifyCacheChange(CACHE_NOTIFY_URL_UNSET_STICKY, dwItemOffset);
  2809. }
  2810. }
  2811. else
  2812. {
  2813. dwError = ERROR_INVALID_PARAMETER;
  2814. }
  2815. return dwError;
  2816. }
  2817. VOID FileTime2DosTime(FILETIME ft, DWORD* pdt)
  2818. {
  2819. INET_ASSERT(pdt);
  2820. *pdt = 0;
  2821. if( FT2LL(ft) != LONGLONG_ZERO )
  2822. {
  2823. if( FT2LL(ft) == MAX_FILETIME)
  2824. {
  2825. *pdt = MAX_DOSTIME;
  2826. }
  2827. else
  2828. {
  2829. FileTimeToDosDateTime(
  2830. &ft,
  2831. ((LPWORD)(pdt) ),
  2832. ((LPWORD)(pdt) + 1)
  2833. );
  2834. }
  2835. }
  2836. }
  2837. VOID DosTime2FileTime(DWORD dt, FILETIME* pft)
  2838. {
  2839. INET_ASSERT(pft);
  2840. LONGLONG llZero = LONGLONG_ZERO;
  2841. LONGLONG llMax = MAX_FILETIME;
  2842. if( dt )
  2843. {
  2844. if( dt == MAX_DOSTIME )
  2845. {
  2846. *pft = *LONGLONG_TO_FILETIME(&llMax);
  2847. }
  2848. else
  2849. {
  2850. DosDateTimeToFileTime(
  2851. *((LPWORD)&(dt) ),
  2852. *((LPWORD)&(dt) + 1),
  2853. pft
  2854. );
  2855. }
  2856. }
  2857. else
  2858. {
  2859. *pft = *LONGLONG_TO_FILETIME(&llZero);
  2860. }
  2861. }