/src/mozilla-central/netwerk/cache2/CacheFileContextEvictor.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "CacheLog.h" |
6 | | #include "CacheFileContextEvictor.h" |
7 | | #include "CacheFileIOManager.h" |
8 | | #include "CacheIndex.h" |
9 | | #include "CacheIndexIterator.h" |
10 | | #include "CacheFileUtils.h" |
11 | | #include "nsIFile.h" |
12 | | #include "LoadContextInfo.h" |
13 | | #include "nsThreadUtils.h" |
14 | | #include "nsString.h" |
15 | | #include "nsISimpleEnumerator.h" |
16 | | #include "nsIDirectoryEnumerator.h" |
17 | | #include "mozilla/Base64.h" |
18 | | #include "mozilla/IntegerPrintfMacros.h" |
19 | | #include "mozilla/net/MozURL.h" |
20 | | |
21 | | |
22 | | namespace mozilla { |
23 | | namespace net { |
24 | | |
25 | 0 | #define CONTEXT_EVICTION_PREFIX "ce_" |
26 | | const uint32_t kContextEvictionPrefixLength = |
27 | | sizeof(CONTEXT_EVICTION_PREFIX) - 1; |
28 | | |
29 | | bool CacheFileContextEvictor::sDiskAlreadySearched = false; |
30 | | |
31 | | CacheFileContextEvictor::CacheFileContextEvictor() |
32 | | : mEvicting(false) |
33 | | , mIndexIsUpToDate(false) |
34 | 0 | { |
35 | 0 | LOG(("CacheFileContextEvictor::CacheFileContextEvictor() [this=%p]", this)); |
36 | 0 | } |
37 | | |
38 | | CacheFileContextEvictor::~CacheFileContextEvictor() |
39 | 0 | { |
40 | 0 | LOG(("CacheFileContextEvictor::~CacheFileContextEvictor() [this=%p]", this)); |
41 | 0 | } |
42 | | |
43 | | nsresult |
44 | | CacheFileContextEvictor::Init(nsIFile *aCacheDirectory) |
45 | 0 | { |
46 | 0 | LOG(("CacheFileContextEvictor::Init()")); |
47 | 0 |
|
48 | 0 | nsresult rv; |
49 | 0 |
|
50 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
51 | 0 |
|
52 | 0 | CacheIndex::IsUpToDate(&mIndexIsUpToDate); |
53 | 0 |
|
54 | 0 | mCacheDirectory = aCacheDirectory; |
55 | 0 |
|
56 | 0 | rv = aCacheDirectory->Clone(getter_AddRefs(mEntriesDir)); |
57 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
58 | 0 | return rv; |
59 | 0 | } |
60 | 0 | |
61 | 0 | rv = mEntriesDir->AppendNative(NS_LITERAL_CSTRING(ENTRIES_DIR)); |
62 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
63 | 0 | return rv; |
64 | 0 | } |
65 | 0 | |
66 | 0 | if (!sDiskAlreadySearched) { |
67 | 0 | LoadEvictInfoFromDisk(); |
68 | 0 | if ((mEntries.Length() != 0) && mIndexIsUpToDate) { |
69 | 0 | CreateIterators(); |
70 | 0 | StartEvicting(); |
71 | 0 | } |
72 | 0 | } |
73 | 0 |
|
74 | 0 | return NS_OK; |
75 | 0 | } |
76 | | |
77 | | void |
78 | | CacheFileContextEvictor::Shutdown() |
79 | 0 | { |
80 | 0 | LOG(("CacheFileContextEvictor::Shutdown()")); |
81 | 0 |
|
82 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
83 | 0 |
|
84 | 0 | CloseIterators(); |
85 | 0 | } |
86 | | |
87 | | uint32_t |
88 | | CacheFileContextEvictor::ContextsCount() |
89 | 0 | { |
90 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
91 | 0 |
|
92 | 0 | return mEntries.Length(); |
93 | 0 | } |
94 | | |
95 | | nsresult |
96 | | CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo, |
97 | | bool aPinned, |
98 | | const nsAString& aOrigin) |
99 | 0 | { |
100 | 0 | LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p, pinned=%d]", |
101 | 0 | this, aLoadContextInfo, aPinned)); |
102 | 0 |
|
103 | 0 | nsresult rv; |
104 | 0 |
|
105 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
106 | 0 |
|
107 | 0 | CacheFileContextEvictorEntry *entry = nullptr; |
108 | 0 | if (aLoadContextInfo) { |
109 | 0 | for (uint32_t i = 0; i < mEntries.Length(); ++i) { |
110 | 0 | if (mEntries[i]->mInfo && |
111 | 0 | mEntries[i]->mInfo->Equals(aLoadContextInfo) && |
112 | 0 | mEntries[i]->mPinned == aPinned && |
113 | 0 | mEntries[i]->mOrigin.Equals(aOrigin)) { |
114 | 0 | entry = mEntries[i]; |
115 | 0 | break; |
116 | 0 | } |
117 | 0 | } |
118 | 0 | } else { |
119 | 0 | // Not providing load context info means we want to delete everything, |
120 | 0 | // so let's not bother with any currently running context cleanups |
121 | 0 | // for the same pinning state. |
122 | 0 | for (uint32_t i = mEntries.Length(); i > 0;) { |
123 | 0 | --i; |
124 | 0 | if (mEntries[i]->mInfo && mEntries[i]->mPinned == aPinned) { |
125 | 0 | RemoveEvictInfoFromDisk(mEntries[i]->mInfo, mEntries[i]->mPinned, |
126 | 0 | mEntries[i]->mOrigin); |
127 | 0 | mEntries.RemoveElementAt(i); |
128 | 0 | } |
129 | 0 | } |
130 | 0 | } |
131 | 0 |
|
132 | 0 | if (!entry) { |
133 | 0 | entry = new CacheFileContextEvictorEntry(); |
134 | 0 | entry->mInfo = aLoadContextInfo; |
135 | 0 | entry->mPinned = aPinned; |
136 | 0 | entry->mOrigin = aOrigin; |
137 | 0 | mEntries.AppendElement(entry); |
138 | 0 | } |
139 | 0 |
|
140 | 0 | entry->mTimeStamp = PR_Now() / PR_USEC_PER_MSEC; |
141 | 0 |
|
142 | 0 | PersistEvictionInfoToDisk(aLoadContextInfo, aPinned, aOrigin); |
143 | 0 |
|
144 | 0 | if (mIndexIsUpToDate) { |
145 | 0 | // Already existing context could be added again, in this case the iterator |
146 | 0 | // would be recreated. Close the old iterator explicitely. |
147 | 0 | if (entry->mIterator) { |
148 | 0 | entry->mIterator->Close(); |
149 | 0 | entry->mIterator = nullptr; |
150 | 0 | } |
151 | 0 |
|
152 | 0 | rv = CacheIndex::GetIterator(aLoadContextInfo, false, |
153 | 0 | getter_AddRefs(entry->mIterator)); |
154 | 0 | if (NS_FAILED(rv)) { |
155 | 0 | // This could probably happen during shutdown. Remove the entry from |
156 | 0 | // the array, but leave the info on the disk. No entry can be opened |
157 | 0 | // during shutdown and we'll load the eviction info on next start. |
158 | 0 | LOG(("CacheFileContextEvictor::AddContext() - Cannot get an iterator. " |
159 | 0 | "[rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv))); |
160 | 0 | mEntries.RemoveElement(entry); |
161 | 0 | return rv; |
162 | 0 | } |
163 | 0 |
|
164 | 0 | StartEvicting(); |
165 | 0 | } |
166 | 0 |
|
167 | 0 | return NS_OK; |
168 | 0 | } |
169 | | |
170 | | nsresult |
171 | | CacheFileContextEvictor::CacheIndexStateChanged() |
172 | 0 | { |
173 | 0 | LOG(("CacheFileContextEvictor::CacheIndexStateChanged() [this=%p]", this)); |
174 | 0 |
|
175 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
176 | 0 |
|
177 | 0 | bool isUpToDate = false; |
178 | 0 | CacheIndex::IsUpToDate(&isUpToDate); |
179 | 0 | if (mEntries.Length() == 0) { |
180 | 0 | // Just save the state and exit, since there is nothing to do |
181 | 0 | mIndexIsUpToDate = isUpToDate; |
182 | 0 | return NS_OK; |
183 | 0 | } |
184 | 0 | |
185 | 0 | if (!isUpToDate && !mIndexIsUpToDate) { |
186 | 0 | // Index is outdated and status has not changed, nothing to do. |
187 | 0 | return NS_OK; |
188 | 0 | } |
189 | 0 | |
190 | 0 | if (isUpToDate && mIndexIsUpToDate) { |
191 | 0 | // Status has not changed, but make sure the eviction is running. |
192 | 0 | if (mEvicting) { |
193 | 0 | return NS_OK; |
194 | 0 | } |
195 | 0 | |
196 | 0 | // We're not evicting, but we should be evicting?! |
197 | 0 | LOG(("CacheFileContextEvictor::CacheIndexStateChanged() - Index is up to " |
198 | 0 | "date, we have some context to evict but eviction is not running! " |
199 | 0 | "Starting now.")); |
200 | 0 | } |
201 | 0 |
|
202 | 0 | mIndexIsUpToDate = isUpToDate; |
203 | 0 |
|
204 | 0 | if (mIndexIsUpToDate) { |
205 | 0 | CreateIterators(); |
206 | 0 | StartEvicting(); |
207 | 0 | } else { |
208 | 0 | CloseIterators(); |
209 | 0 | } |
210 | 0 |
|
211 | 0 | return NS_OK; |
212 | 0 | } |
213 | | |
214 | | nsresult |
215 | | CacheFileContextEvictor::WasEvicted(const nsACString &aKey, nsIFile *aFile, |
216 | | bool *aEvictedAsPinned, bool *aEvictedAsNonPinned) |
217 | 0 | { |
218 | 0 | LOG(("CacheFileContextEvictor::WasEvicted() [key=%s]", |
219 | 0 | PromiseFlatCString(aKey).get())); |
220 | 0 |
|
221 | 0 | nsresult rv; |
222 | 0 |
|
223 | 0 | *aEvictedAsPinned = false; |
224 | 0 | *aEvictedAsNonPinned = false; |
225 | 0 |
|
226 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
227 | 0 |
|
228 | 0 | nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey); |
229 | 0 | MOZ_ASSERT(info); |
230 | 0 | if (!info) { |
231 | 0 | LOG(("CacheFileContextEvictor::WasEvicted() - Cannot parse key!")); |
232 | 0 | return NS_OK; |
233 | 0 | } |
234 | 0 |
|
235 | 0 | for (uint32_t i = 0; i < mEntries.Length(); ++i) { |
236 | 0 | CacheFileContextEvictorEntry *entry = mEntries[i]; |
237 | 0 |
|
238 | 0 | if (entry->mInfo && !info->Equals(entry->mInfo)) { |
239 | 0 | continue; |
240 | 0 | } |
241 | 0 | |
242 | 0 | PRTime lastModifiedTime; |
243 | 0 | rv = aFile->GetLastModifiedTime(&lastModifiedTime); |
244 | 0 | if (NS_FAILED(rv)) { |
245 | 0 | LOG(("CacheFileContextEvictor::WasEvicted() - Cannot get last modified time" |
246 | 0 | ", returning false.")); |
247 | 0 | return NS_OK; |
248 | 0 | } |
249 | 0 |
|
250 | 0 | if (lastModifiedTime > entry->mTimeStamp) { |
251 | 0 | // File has been modified since context eviction. |
252 | 0 | continue; |
253 | 0 | } |
254 | 0 | |
255 | 0 | LOG(("CacheFileContextEvictor::WasEvicted() - evicted [pinning=%d, " |
256 | 0 | "mTimeStamp=%" PRId64 ", lastModifiedTime=%" PRId64 "]", |
257 | 0 | entry->mPinned, entry->mTimeStamp, lastModifiedTime)); |
258 | 0 |
|
259 | 0 | if (entry->mPinned) { |
260 | 0 | *aEvictedAsPinned = true; |
261 | 0 | } else { |
262 | 0 | *aEvictedAsNonPinned = true; |
263 | 0 | } |
264 | 0 | } |
265 | 0 |
|
266 | 0 | return NS_OK; |
267 | 0 | } |
268 | | |
269 | | nsresult |
270 | | CacheFileContextEvictor::PersistEvictionInfoToDisk( |
271 | | nsILoadContextInfo *aLoadContextInfo, bool aPinned, |
272 | | const nsAString& aOrigin) |
273 | 0 | { |
274 | 0 | LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, " |
275 | 0 | "loadContextInfo=%p]", this, aLoadContextInfo)); |
276 | 0 |
|
277 | 0 | nsresult rv; |
278 | 0 |
|
279 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
280 | 0 |
|
281 | 0 | nsCOMPtr<nsIFile> file; |
282 | 0 | rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, getter_AddRefs(file)); |
283 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
284 | 0 | return rv; |
285 | 0 | } |
286 | 0 | |
287 | 0 | nsCString path = file->HumanReadablePath(); |
288 | 0 |
|
289 | 0 | PRFileDesc *fd; |
290 | 0 | rv = file->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, |
291 | 0 | &fd); |
292 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
293 | 0 | LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Creating file " |
294 | 0 | "failed! [path=%s, rv=0x%08" PRIx32 "]", path.get(), static_cast<uint32_t>(rv))); |
295 | 0 | return rv; |
296 | 0 | } |
297 | 0 |
|
298 | 0 | PR_Close(fd); |
299 | 0 |
|
300 | 0 | LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Successfully " |
301 | 0 | "created file. [path=%s]", path.get())); |
302 | 0 |
|
303 | 0 | return NS_OK; |
304 | 0 | } |
305 | | |
306 | | nsresult |
307 | | CacheFileContextEvictor::RemoveEvictInfoFromDisk( |
308 | | nsILoadContextInfo *aLoadContextInfo, bool aPinned, const nsAString& aOrigin) |
309 | 0 | { |
310 | 0 | LOG(("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, " |
311 | 0 | "loadContextInfo=%p]", this, aLoadContextInfo)); |
312 | 0 |
|
313 | 0 | nsresult rv; |
314 | 0 |
|
315 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
316 | 0 |
|
317 | 0 | nsCOMPtr<nsIFile> file; |
318 | 0 | rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, getter_AddRefs(file)); |
319 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
320 | 0 | return rv; |
321 | 0 | } |
322 | 0 | |
323 | 0 | nsCString path = file->HumanReadablePath(); |
324 | 0 |
|
325 | 0 | rv = file->Remove(false); |
326 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
327 | 0 | LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Removing file" |
328 | 0 | " failed! [path=%s, rv=0x%08" PRIx32 "]", path.get(), static_cast<uint32_t>(rv))); |
329 | 0 | return rv; |
330 | 0 | } |
331 | 0 |
|
332 | 0 | LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Successfully " |
333 | 0 | "removed file. [path=%s]", path.get())); |
334 | 0 |
|
335 | 0 | return NS_OK; |
336 | 0 | } |
337 | | |
338 | | nsresult |
339 | | CacheFileContextEvictor::LoadEvictInfoFromDisk() |
340 | 0 | { |
341 | 0 | LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() [this=%p]", this)); |
342 | 0 |
|
343 | 0 | nsresult rv; |
344 | 0 |
|
345 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
346 | 0 |
|
347 | 0 | sDiskAlreadySearched = true; |
348 | 0 |
|
349 | 0 | nsCOMPtr<nsIDirectoryEnumerator> dirEnum; |
350 | 0 | rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(dirEnum)); |
351 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
352 | 0 | return rv; |
353 | 0 | } |
354 | 0 | |
355 | 0 | while (true) { |
356 | 0 | nsCOMPtr<nsIFile> file; |
357 | 0 | rv = dirEnum->GetNextFile(getter_AddRefs(file)); |
358 | 0 | if (!file) { |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | |
362 | 0 | bool isDir = false; |
363 | 0 | file->IsDirectory(&isDir); |
364 | 0 | if (isDir) { |
365 | 0 | continue; |
366 | 0 | } |
367 | 0 | |
368 | 0 | nsAutoCString leaf; |
369 | 0 | rv = file->GetNativeLeafName(leaf); |
370 | 0 | if (NS_FAILED(rv)) { |
371 | 0 | LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - " |
372 | 0 | "GetNativeLeafName() failed! Skipping file.")); |
373 | 0 | continue; |
374 | 0 | } |
375 | 0 |
|
376 | 0 | if (leaf.Length() < kContextEvictionPrefixLength) { |
377 | 0 | continue; |
378 | 0 | } |
379 | 0 | |
380 | 0 | if (!StringBeginsWith(leaf, NS_LITERAL_CSTRING(CONTEXT_EVICTION_PREFIX))) { |
381 | 0 | continue; |
382 | 0 | } |
383 | 0 | |
384 | 0 | nsAutoCString encoded; |
385 | 0 | encoded = Substring(leaf, kContextEvictionPrefixLength); |
386 | 0 | encoded.ReplaceChar('-', '/'); |
387 | 0 |
|
388 | 0 | nsAutoCString decoded; |
389 | 0 | rv = Base64Decode(encoded, decoded); |
390 | 0 | if (NS_FAILED(rv)) { |
391 | 0 | LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Base64 decoding " |
392 | 0 | "failed. Removing the file. [file=%s]", leaf.get())); |
393 | 0 | file->Remove(false); |
394 | 0 | continue; |
395 | 0 | } |
396 | 0 |
|
397 | 0 | bool pinned = decoded[0] == '\t'; |
398 | 0 | if (pinned) { |
399 | 0 | decoded = Substring(decoded, 1); |
400 | 0 | } |
401 | 0 |
|
402 | 0 | // Let's see if we have an origin. |
403 | 0 | nsAutoCString origin; |
404 | 0 | if (decoded.Contains('\t')) { |
405 | 0 | auto split = decoded.Split('\t'); |
406 | 0 | MOZ_ASSERT(decoded.CountChar('\t') == 2); |
407 | 0 |
|
408 | 0 | origin = split.Get(0); |
409 | 0 | decoded = split.Get(1); |
410 | 0 | } |
411 | 0 |
|
412 | 0 | nsCOMPtr<nsILoadContextInfo> info; |
413 | 0 | if (!NS_LITERAL_CSTRING("*").Equals(decoded)) { |
414 | 0 | // "*" is indication of 'delete all', info left null will pass |
415 | 0 | // to CacheFileContextEvictor::AddContext and clear all the cache data. |
416 | 0 | info = CacheFileUtils::ParseKey(decoded); |
417 | 0 | if (!info) { |
418 | 0 | LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse " |
419 | 0 | "context key, removing file. [contextKey=%s, file=%s]", |
420 | 0 | decoded.get(), leaf.get())); |
421 | 0 | file->Remove(false); |
422 | 0 | continue; |
423 | 0 | } |
424 | 0 | } |
425 | 0 |
|
426 | 0 |
|
427 | 0 | PRTime lastModifiedTime; |
428 | 0 | rv = file->GetLastModifiedTime(&lastModifiedTime); |
429 | 0 | if (NS_FAILED(rv)) { |
430 | 0 | continue; |
431 | 0 | } |
432 | 0 | |
433 | 0 | CacheFileContextEvictorEntry *entry = new CacheFileContextEvictorEntry(); |
434 | 0 | entry->mInfo = info; |
435 | 0 | entry->mPinned = pinned; |
436 | 0 | CopyUTF8toUTF16(origin, entry->mOrigin); |
437 | 0 | entry->mTimeStamp = lastModifiedTime; |
438 | 0 | mEntries.AppendElement(entry); |
439 | 0 | } |
440 | 0 |
|
441 | 0 | return NS_OK; |
442 | 0 | } |
443 | | |
444 | | nsresult |
445 | | CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo, |
446 | | bool aPinned, |
447 | | const nsAString& aOrigin, |
448 | | nsIFile **_retval) |
449 | 0 | { |
450 | 0 | nsresult rv; |
451 | 0 |
|
452 | 0 | nsAutoCString leafName; |
453 | 0 | leafName.AssignLiteral(CONTEXT_EVICTION_PREFIX); |
454 | 0 |
|
455 | 0 | nsAutoCString keyPrefix; |
456 | 0 | if (aPinned) { |
457 | 0 | // Mark pinned context files with a tab char at the start. |
458 | 0 | // Tab is chosen because it can never be used as a context key tag. |
459 | 0 | keyPrefix.Append('\t'); |
460 | 0 | } |
461 | 0 | if (aLoadContextInfo) { |
462 | 0 | CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix); |
463 | 0 | } else { |
464 | 0 | keyPrefix.Append('*'); |
465 | 0 | } |
466 | 0 | if (!aOrigin.IsEmpty()) { |
467 | 0 | keyPrefix.Append('\t'); |
468 | 0 | keyPrefix.Append(NS_ConvertUTF16toUTF8(aOrigin)); |
469 | 0 | } |
470 | 0 |
|
471 | 0 | nsAutoCString data64; |
472 | 0 | rv = Base64Encode(keyPrefix, data64); |
473 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
474 | 0 | return rv; |
475 | 0 | } |
476 | 0 | |
477 | 0 | // Replace '/' with '-' since '/' cannot be part of the filename. |
478 | 0 | data64.ReplaceChar('/', '-'); |
479 | 0 |
|
480 | 0 | leafName.Append(data64); |
481 | 0 |
|
482 | 0 | nsCOMPtr<nsIFile> file; |
483 | 0 | rv = mCacheDirectory->Clone(getter_AddRefs(file)); |
484 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
485 | 0 | return rv; |
486 | 0 | } |
487 | 0 | |
488 | 0 | rv = file->AppendNative(leafName); |
489 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
490 | 0 | return rv; |
491 | 0 | } |
492 | 0 | |
493 | 0 | file.swap(*_retval); |
494 | 0 | return NS_OK; |
495 | 0 | } |
496 | | |
497 | | void |
498 | | CacheFileContextEvictor::CreateIterators() |
499 | 0 | { |
500 | 0 | LOG(("CacheFileContextEvictor::CreateIterators() [this=%p]", this)); |
501 | 0 |
|
502 | 0 | CloseIterators(); |
503 | 0 |
|
504 | 0 | nsresult rv; |
505 | 0 |
|
506 | 0 | for (uint32_t i = 0; i < mEntries.Length(); ) { |
507 | 0 | rv = CacheIndex::GetIterator(mEntries[i]->mInfo, false, |
508 | 0 | getter_AddRefs(mEntries[i]->mIterator)); |
509 | 0 | if (NS_FAILED(rv)) { |
510 | 0 | LOG(("CacheFileContextEvictor::CreateIterators() - Cannot get an iterator" |
511 | 0 | ". [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv))); |
512 | 0 | mEntries.RemoveElementAt(i); |
513 | 0 | continue; |
514 | 0 | } |
515 | 0 |
|
516 | 0 | ++i; |
517 | 0 | } |
518 | 0 | } |
519 | | |
520 | | void |
521 | | CacheFileContextEvictor::CloseIterators() |
522 | 0 | { |
523 | 0 | LOG(("CacheFileContextEvictor::CloseIterators() [this=%p]", this)); |
524 | 0 |
|
525 | 0 | for (uint32_t i = 0; i < mEntries.Length(); ++i) { |
526 | 0 | if (mEntries[i]->mIterator) { |
527 | 0 | mEntries[i]->mIterator->Close(); |
528 | 0 | mEntries[i]->mIterator = nullptr; |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } |
532 | | |
533 | | void |
534 | | CacheFileContextEvictor::StartEvicting() |
535 | 0 | { |
536 | 0 | LOG(("CacheFileContextEvictor::StartEvicting() [this=%p]", this)); |
537 | 0 |
|
538 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
539 | 0 |
|
540 | 0 | if (mEvicting) { |
541 | 0 | LOG(("CacheFileContextEvictor::StartEvicting() - already evicting.")); |
542 | 0 | return; |
543 | 0 | } |
544 | 0 |
|
545 | 0 | if (mEntries.Length() == 0) { |
546 | 0 | LOG(("CacheFileContextEvictor::StartEvicting() - no context to evict.")); |
547 | 0 | return; |
548 | 0 | } |
549 | 0 |
|
550 | 0 | nsCOMPtr<nsIRunnable> ev; |
551 | 0 | ev = NewRunnableMethod("net::CacheFileContextEvictor::EvictEntries", |
552 | 0 | this, |
553 | 0 | &CacheFileContextEvictor::EvictEntries); |
554 | 0 |
|
555 | 0 | RefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread(); |
556 | 0 |
|
557 | 0 | nsresult rv = ioThread->Dispatch(ev, CacheIOThread::EVICT); |
558 | 0 | if (NS_FAILED(rv)) { |
559 | 0 | LOG(("CacheFileContextEvictor::StartEvicting() - Cannot dispatch event to " |
560 | 0 | "IO thread. [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv))); |
561 | 0 | } |
562 | 0 |
|
563 | 0 | mEvicting = true; |
564 | 0 | } |
565 | | |
566 | | nsresult |
567 | | CacheFileContextEvictor::EvictEntries() |
568 | 0 | { |
569 | 0 | LOG(("CacheFileContextEvictor::EvictEntries()")); |
570 | 0 |
|
571 | 0 | nsresult rv; |
572 | 0 |
|
573 | 0 | MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); |
574 | 0 |
|
575 | 0 | mEvicting = false; |
576 | 0 |
|
577 | 0 | if (!mIndexIsUpToDate) { |
578 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to " |
579 | 0 | "outdated index.")); |
580 | 0 | return NS_OK; |
581 | 0 | } |
582 | 0 |
|
583 | 0 | while (true) { |
584 | 0 | if (CacheObserver::ShuttingDown()) { |
585 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to " |
586 | 0 | "shutdown.")); |
587 | 0 | mEvicting = true; // We don't want to start eviction again during shutdown |
588 | 0 | // process. Setting this flag to true ensures it. |
589 | 0 | return NS_OK; |
590 | 0 | } |
591 | 0 |
|
592 | 0 | if (CacheIOThread::YieldAndRerun()) { |
593 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Breaking loop for higher " |
594 | 0 | "level events.")); |
595 | 0 | mEvicting = true; |
596 | 0 | return NS_OK; |
597 | 0 | } |
598 | 0 |
|
599 | 0 | if (mEntries.Length() == 0) { |
600 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting, there " |
601 | 0 | "is no context to evict.")); |
602 | 0 |
|
603 | 0 | // Allow index to notify AsyncGetDiskConsumption callbacks. The size is |
604 | 0 | // actual again. |
605 | 0 | CacheIndex::OnAsyncEviction(false); |
606 | 0 | return NS_OK; |
607 | 0 | } |
608 | 0 |
|
609 | 0 | SHA1Sum::Hash hash; |
610 | 0 | rv = mEntries[0]->mIterator->GetNextHash(&hash); |
611 | 0 | if (rv == NS_ERROR_NOT_AVAILABLE) { |
612 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - No more entries left in " |
613 | 0 | "iterator. [iterator=%p, info=%p]", mEntries[0]->mIterator.get(), |
614 | 0 | mEntries[0]->mInfo.get())); |
615 | 0 | RemoveEvictInfoFromDisk(mEntries[0]->mInfo, mEntries[0]->mPinned, |
616 | 0 | mEntries[0]->mOrigin); |
617 | 0 | mEntries.RemoveElementAt(0); |
618 | 0 | continue; |
619 | 0 | } else if (NS_FAILED(rv)) { |
620 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Iterator failed to " |
621 | 0 | "provide next hash (shutdown?), keeping eviction info on disk." |
622 | 0 | " [iterator=%p, info=%p]", mEntries[0]->mIterator.get(), |
623 | 0 | mEntries[0]->mInfo.get())); |
624 | 0 | mEntries.RemoveElementAt(0); |
625 | 0 | continue; |
626 | 0 | } |
627 | 0 |
|
628 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Processing hash. " |
629 | 0 | "[hash=%08x%08x%08x%08x%08x, iterator=%p, info=%p]", LOGSHA1(&hash), |
630 | 0 | mEntries[0]->mIterator.get(), mEntries[0]->mInfo.get())); |
631 | 0 |
|
632 | 0 | RefPtr<CacheFileHandle> handle; |
633 | 0 | CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, |
634 | 0 | getter_AddRefs(handle)); |
635 | 0 | if (handle) { |
636 | 0 | // We doom any active handle in CacheFileIOManager::EvictByContext(), so |
637 | 0 | // this must be a new one. Skip it. |
638 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since we " |
639 | 0 | "found an active handle. [handle=%p]", handle.get())); |
640 | 0 | continue; |
641 | 0 | } |
642 | 0 |
|
643 | 0 | CacheIndex::EntryStatus status; |
644 | 0 | bool pinned = false; |
645 | 0 | auto callback = [&pinned](const CacheIndexEntry * aEntry) { |
646 | 0 | pinned = aEntry->IsPinned(); |
647 | 0 | }; |
648 | 0 | rv = CacheIndex::HasEntry(hash, &status, callback); |
649 | 0 | // This must never fail, since eviction (this code) happens only when the index |
650 | 0 | // is up-to-date and thus the informatin is known. |
651 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
652 | 0 |
|
653 | 0 | if (pinned != mEntries[0]->mPinned) { |
654 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since pinning " |
655 | 0 | "doesn't match [evicting pinned=%d, entry pinned=%d]", |
656 | 0 | mEntries[0]->mPinned, pinned)); |
657 | 0 | continue; |
658 | 0 | } |
659 | 0 |
|
660 | 0 | if (!mEntries[0]->mOrigin.IsEmpty()) { |
661 | 0 | nsCOMPtr<nsIFile> file; |
662 | 0 | CacheFileIOManager::gInstance->GetFile(&hash, getter_AddRefs(file)); |
663 | 0 |
|
664 | 0 | // Read metadata from the file synchronously |
665 | 0 | RefPtr<CacheFileMetadata> metadata = new CacheFileMetadata(); |
666 | 0 | rv = metadata->SyncReadMetadata(file); |
667 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
668 | 0 | continue; |
669 | 0 | } |
670 | 0 | |
671 | 0 | // Now get the context + enhance id + URL from the key. |
672 | 0 | nsAutoCString key; |
673 | 0 | metadata->GetKey(key); |
674 | 0 |
|
675 | 0 | nsAutoCString uriSpec; |
676 | 0 |
|
677 | 0 | RefPtr<nsILoadContextInfo> info = |
678 | 0 | CacheFileUtils::ParseKey(key, nullptr, &uriSpec); |
679 | 0 | MOZ_ASSERT(info); |
680 | 0 | if (!info) { |
681 | 0 | continue; |
682 | 0 | } |
683 | 0 | |
684 | 0 | RefPtr<MozURL> url; |
685 | 0 | rv = MozURL::Init(getter_AddRefs(url), uriSpec); |
686 | 0 | if (NS_FAILED(rv)) { |
687 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since MozURL " |
688 | 0 | "fails in the parsing of the uriSpec")); |
689 | 0 | continue; |
690 | 0 | } |
691 | 0 |
|
692 | 0 | nsAutoCString urlOrigin; |
693 | 0 | url->Origin(urlOrigin); |
694 | 0 | if (urlOrigin.Equals(NS_ConvertUTF16toUTF8(mEntries[0]->mOrigin))) { |
695 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since origin " |
696 | 0 | "doesn't match")); |
697 | 0 | continue; |
698 | 0 | } |
699 | 0 | } |
700 | 0 |
|
701 | 0 | nsAutoCString leafName; |
702 | 0 | CacheFileIOManager::HashToStr(&hash, leafName); |
703 | 0 |
|
704 | 0 | PRTime lastModifiedTime; |
705 | 0 | nsCOMPtr<nsIFile> file; |
706 | 0 | rv = mEntriesDir->Clone(getter_AddRefs(file)); |
707 | 0 | if (NS_SUCCEEDED(rv)) { |
708 | 0 | rv = file->AppendNative(leafName); |
709 | 0 | } |
710 | 0 | if (NS_SUCCEEDED(rv)) { |
711 | 0 | rv = file->GetLastModifiedTime(&lastModifiedTime); |
712 | 0 | } |
713 | 0 | if (NS_FAILED(rv)) { |
714 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Cannot get last modified " |
715 | 0 | "time, skipping entry.")); |
716 | 0 | continue; |
717 | 0 | } |
718 | 0 |
|
719 | 0 | if (lastModifiedTime > mEntries[0]->mTimeStamp) { |
720 | 0 | LOG(("CacheFileContextEvictor::EvictEntries() - Skipping newer entry. " |
721 | 0 | "[mTimeStamp=%" PRId64 ", lastModifiedTime=%" PRId64 "]", mEntries[0]->mTimeStamp, |
722 | 0 | lastModifiedTime)); |
723 | 0 | continue; |
724 | 0 | } |
725 | 0 |
|
726 | 0 | LOG(("CacheFileContextEvictor::EvictEntries - Removing entry.")); |
727 | 0 | file->Remove(false); |
728 | 0 | CacheIndex::RemoveEntry(&hash); |
729 | 0 | } |
730 | 0 |
|
731 | 0 | MOZ_ASSERT_UNREACHABLE("We should never get here"); |
732 | 0 | return NS_OK; |
733 | 0 | } |
734 | | |
735 | | } // namespace net |
736 | | } // namespace mozilla |