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.

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