|
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 "CacheFileMetadata.h" |
|
7 |
|
8 #include "CacheFileIOManager.h" |
|
9 #include "nsICacheEntry.h" |
|
10 #include "CacheHashUtils.h" |
|
11 #include "CacheFileChunk.h" |
|
12 #include "CacheFileUtils.h" |
|
13 #include "nsILoadContextInfo.h" |
|
14 #include "../cache/nsCacheUtils.h" |
|
15 #include "nsIFile.h" |
|
16 #include "mozilla/Telemetry.h" |
|
17 #include "mozilla/DebugOnly.h" |
|
18 #include "prnetdb.h" |
|
19 |
|
20 |
|
21 namespace mozilla { |
|
22 namespace net { |
|
23 |
|
24 #define kMinMetadataRead 1024 // TODO find optimal value from telemetry |
|
25 #define kAlignSize 4096 |
|
26 |
|
27 #define kCacheEntryVersion 1 |
|
28 |
|
29 NS_IMPL_ISUPPORTS(CacheFileMetadata, CacheFileIOListener) |
|
30 |
|
31 CacheFileMetadata::CacheFileMetadata(CacheFileHandle *aHandle, const nsACString &aKey) |
|
32 : CacheMemoryConsumer(NORMAL) |
|
33 , mHandle(aHandle) |
|
34 , mHashArray(nullptr) |
|
35 , mHashArraySize(0) |
|
36 , mHashCount(0) |
|
37 , mOffset(-1) |
|
38 , mBuf(nullptr) |
|
39 , mBufSize(0) |
|
40 , mWriteBuf(nullptr) |
|
41 , mElementsSize(0) |
|
42 , mIsDirty(false) |
|
43 , mAnonymous(false) |
|
44 , mInBrowser(false) |
|
45 , mAppId(nsILoadContextInfo::NO_APP_ID) |
|
46 { |
|
47 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, handle=%p, key=%s]", |
|
48 this, aHandle, PromiseFlatCString(aKey).get())); |
|
49 |
|
50 MOZ_COUNT_CTOR(CacheFileMetadata); |
|
51 memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader)); |
|
52 mMetaHdr.mVersion = kCacheEntryVersion; |
|
53 mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME; |
|
54 mKey = aKey; |
|
55 |
|
56 DebugOnly<nsresult> rv; |
|
57 rv = ParseKey(aKey); |
|
58 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
59 } |
|
60 |
|
61 CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly, const nsACString &aKey) |
|
62 : CacheMemoryConsumer(aMemoryOnly ? MEMORY_ONLY : NORMAL) |
|
63 , mHandle(nullptr) |
|
64 , mHashArray(nullptr) |
|
65 , mHashArraySize(0) |
|
66 , mHashCount(0) |
|
67 , mOffset(0) |
|
68 , mBuf(nullptr) |
|
69 , mBufSize(0) |
|
70 , mWriteBuf(nullptr) |
|
71 , mElementsSize(0) |
|
72 , mIsDirty(true) |
|
73 , mAnonymous(false) |
|
74 , mInBrowser(false) |
|
75 , mAppId(nsILoadContextInfo::NO_APP_ID) |
|
76 { |
|
77 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, key=%s]", |
|
78 this, PromiseFlatCString(aKey).get())); |
|
79 |
|
80 MOZ_COUNT_CTOR(CacheFileMetadata); |
|
81 memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader)); |
|
82 mMetaHdr.mVersion = kCacheEntryVersion; |
|
83 mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME; |
|
84 mMetaHdr.mFetchCount = 1; |
|
85 mKey = aKey; |
|
86 mMetaHdr.mKeySize = mKey.Length(); |
|
87 |
|
88 DebugOnly<nsresult> rv; |
|
89 rv = ParseKey(aKey); |
|
90 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
91 } |
|
92 |
|
93 CacheFileMetadata::CacheFileMetadata() |
|
94 : CacheMemoryConsumer(DONT_REPORT /* This is a helper class */) |
|
95 , mHandle(nullptr) |
|
96 , mHashArray(nullptr) |
|
97 , mHashArraySize(0) |
|
98 , mHashCount(0) |
|
99 , mOffset(0) |
|
100 , mBuf(nullptr) |
|
101 , mBufSize(0) |
|
102 , mWriteBuf(nullptr) |
|
103 , mElementsSize(0) |
|
104 , mIsDirty(false) |
|
105 , mAnonymous(false) |
|
106 , mInBrowser(false) |
|
107 , mAppId(nsILoadContextInfo::NO_APP_ID) |
|
108 { |
|
109 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p]", this)); |
|
110 |
|
111 MOZ_COUNT_CTOR(CacheFileMetadata); |
|
112 memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader)); |
|
113 } |
|
114 |
|
115 CacheFileMetadata::~CacheFileMetadata() |
|
116 { |
|
117 LOG(("CacheFileMetadata::~CacheFileMetadata() [this=%p]", this)); |
|
118 |
|
119 MOZ_COUNT_DTOR(CacheFileMetadata); |
|
120 MOZ_ASSERT(!mListener); |
|
121 |
|
122 if (mHashArray) { |
|
123 free(mHashArray); |
|
124 mHashArray = nullptr; |
|
125 mHashArraySize = 0; |
|
126 } |
|
127 |
|
128 if (mBuf) { |
|
129 free(mBuf); |
|
130 mBuf = nullptr; |
|
131 mBufSize = 0; |
|
132 } |
|
133 } |
|
134 |
|
135 void |
|
136 CacheFileMetadata::SetHandle(CacheFileHandle *aHandle) |
|
137 { |
|
138 LOG(("CacheFileMetadata::SetHandle() [this=%p, handle=%p]", this, aHandle)); |
|
139 |
|
140 MOZ_ASSERT(!mHandle); |
|
141 |
|
142 mHandle = aHandle; |
|
143 } |
|
144 |
|
145 nsresult |
|
146 CacheFileMetadata::GetKey(nsACString &_retval) |
|
147 { |
|
148 _retval = mKey; |
|
149 return NS_OK; |
|
150 } |
|
151 |
|
152 nsresult |
|
153 CacheFileMetadata::ReadMetadata(CacheFileMetadataListener *aListener) |
|
154 { |
|
155 LOG(("CacheFileMetadata::ReadMetadata() [this=%p, listener=%p]", this, aListener)); |
|
156 |
|
157 MOZ_ASSERT(!mListener); |
|
158 MOZ_ASSERT(!mHashArray); |
|
159 MOZ_ASSERT(!mBuf); |
|
160 MOZ_ASSERT(!mWriteBuf); |
|
161 |
|
162 nsresult rv; |
|
163 |
|
164 int64_t size = mHandle->FileSize(); |
|
165 MOZ_ASSERT(size != -1); |
|
166 |
|
167 if (size == 0) { |
|
168 // this is a new entry |
|
169 LOG(("CacheFileMetadata::ReadMetadata() - Filesize == 0, creating empty " |
|
170 "metadata. [this=%p]", this)); |
|
171 |
|
172 InitEmptyMetadata(); |
|
173 aListener->OnMetadataRead(NS_OK); |
|
174 return NS_OK; |
|
175 } |
|
176 |
|
177 if (size < int64_t(sizeof(CacheFileMetadataHeader) + 2*sizeof(uint32_t))) { |
|
178 // there must be at least checksum, header and offset |
|
179 LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating " |
|
180 "empty metadata. [this=%p, filesize=%lld]", this, size)); |
|
181 |
|
182 InitEmptyMetadata(); |
|
183 aListener->OnMetadataRead(NS_OK); |
|
184 return NS_OK; |
|
185 } |
|
186 |
|
187 // round offset to 4k blocks |
|
188 int64_t offset = (size / kAlignSize) * kAlignSize; |
|
189 |
|
190 if (size - offset < kMinMetadataRead && offset > kAlignSize) |
|
191 offset -= kAlignSize; |
|
192 |
|
193 mBufSize = size - offset; |
|
194 mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); |
|
195 |
|
196 DoMemoryReport(MemoryUsage()); |
|
197 |
|
198 LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying " |
|
199 "offset=%lld, filesize=%lld [this=%p]", offset, size, this)); |
|
200 |
|
201 mListener = aListener; |
|
202 rv = CacheFileIOManager::Read(mHandle, offset, mBuf, mBufSize, true, this); |
|
203 if (NS_FAILED(rv)) { |
|
204 LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed" |
|
205 " synchronously, creating empty metadata. [this=%p, rv=0x%08x]", |
|
206 this, rv)); |
|
207 |
|
208 mListener = nullptr; |
|
209 InitEmptyMetadata(); |
|
210 aListener->OnMetadataRead(NS_OK); |
|
211 return NS_OK; |
|
212 } |
|
213 |
|
214 return NS_OK; |
|
215 } |
|
216 |
|
217 nsresult |
|
218 CacheFileMetadata::WriteMetadata(uint32_t aOffset, |
|
219 CacheFileMetadataListener *aListener) |
|
220 { |
|
221 LOG(("CacheFileMetadata::WriteMetadata() [this=%p, offset=%d, listener=%p]", |
|
222 this, aOffset, aListener)); |
|
223 |
|
224 MOZ_ASSERT(!mListener); |
|
225 MOZ_ASSERT(!mWriteBuf); |
|
226 |
|
227 nsresult rv; |
|
228 |
|
229 mIsDirty = false; |
|
230 |
|
231 mWriteBuf = static_cast<char *>(moz_xmalloc(sizeof(uint32_t) + |
|
232 mHashCount * sizeof(CacheHash::Hash16_t) + |
|
233 sizeof(CacheFileMetadataHeader) + mKey.Length() + 1 + |
|
234 mElementsSize + sizeof(uint32_t))); |
|
235 |
|
236 char *p = mWriteBuf + sizeof(uint32_t); |
|
237 memcpy(p, mHashArray, mHashCount * sizeof(CacheHash::Hash16_t)); |
|
238 p += mHashCount * sizeof(CacheHash::Hash16_t); |
|
239 mMetaHdr.WriteToBuf(p); |
|
240 p += sizeof(CacheFileMetadataHeader); |
|
241 memcpy(p, mKey.get(), mKey.Length()); |
|
242 p += mKey.Length(); |
|
243 *p = 0; |
|
244 p++; |
|
245 memcpy(p, mBuf, mElementsSize); |
|
246 p += mElementsSize; |
|
247 |
|
248 CacheHash::Hash32_t hash; |
|
249 hash = CacheHash::Hash(mWriteBuf + sizeof(uint32_t), |
|
250 p - mWriteBuf - sizeof(uint32_t)); |
|
251 NetworkEndian::writeUint32(mWriteBuf, hash); |
|
252 |
|
253 NetworkEndian::writeUint32(p, aOffset); |
|
254 p += sizeof(uint32_t); |
|
255 |
|
256 char * writeBuffer; |
|
257 if (aListener) { |
|
258 mListener = aListener; |
|
259 writeBuffer = mWriteBuf; |
|
260 } else { |
|
261 // We are not going to pass |this| as callback to CacheFileIOManager::Write |
|
262 // so we must allocate a new buffer that will be released automatically when |
|
263 // write is finished. This is actually better than to let |
|
264 // CacheFileMetadata::OnDataWritten do the job, since when dispatching the |
|
265 // result from some reason fails during shutdown, we would unnecessarily leak |
|
266 // both this object and the buffer. |
|
267 writeBuffer = static_cast<char *>(moz_xmalloc(p - mWriteBuf)); |
|
268 memcpy(mWriteBuf, writeBuffer, p - mWriteBuf); |
|
269 } |
|
270 |
|
271 rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer, p - mWriteBuf, |
|
272 true, aListener ? this : nullptr); |
|
273 if (NS_FAILED(rv)) { |
|
274 LOG(("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() " |
|
275 "failed synchronously. [this=%p, rv=0x%08x]", this, rv)); |
|
276 |
|
277 mListener = nullptr; |
|
278 if (writeBuffer != mWriteBuf) { |
|
279 free(writeBuffer); |
|
280 } |
|
281 free(mWriteBuf); |
|
282 mWriteBuf = nullptr; |
|
283 NS_ENSURE_SUCCESS(rv, rv); |
|
284 } |
|
285 |
|
286 DoMemoryReport(MemoryUsage()); |
|
287 |
|
288 return NS_OK; |
|
289 } |
|
290 |
|
291 nsresult |
|
292 CacheFileMetadata::SyncReadMetadata(nsIFile *aFile) |
|
293 { |
|
294 LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this)); |
|
295 |
|
296 MOZ_ASSERT(!mListener); |
|
297 MOZ_ASSERT(!mHandle); |
|
298 MOZ_ASSERT(!mHashArray); |
|
299 MOZ_ASSERT(!mBuf); |
|
300 MOZ_ASSERT(!mWriteBuf); |
|
301 MOZ_ASSERT(mKey.IsEmpty()); |
|
302 |
|
303 nsresult rv; |
|
304 |
|
305 int64_t fileSize; |
|
306 rv = aFile->GetFileSize(&fileSize); |
|
307 NS_ENSURE_SUCCESS(rv, rv); |
|
308 |
|
309 PRFileDesc *fd; |
|
310 rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0600, &fd); |
|
311 NS_ENSURE_SUCCESS(rv, rv); |
|
312 |
|
313 int64_t offset = PR_Seek64(fd, fileSize - sizeof(uint32_t), PR_SEEK_SET); |
|
314 if (offset == -1) { |
|
315 PR_Close(fd); |
|
316 return NS_ERROR_FAILURE; |
|
317 } |
|
318 |
|
319 uint32_t metaOffset; |
|
320 int32_t bytesRead = PR_Read(fd, &metaOffset, sizeof(uint32_t)); |
|
321 if (bytesRead != sizeof(uint32_t)) { |
|
322 PR_Close(fd); |
|
323 return NS_ERROR_FAILURE; |
|
324 } |
|
325 |
|
326 metaOffset = NetworkEndian::readUint32(&metaOffset); |
|
327 if (metaOffset > fileSize) { |
|
328 PR_Close(fd); |
|
329 return NS_ERROR_FAILURE; |
|
330 } |
|
331 |
|
332 mBufSize = fileSize - metaOffset; |
|
333 mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); |
|
334 |
|
335 DoMemoryReport(MemoryUsage()); |
|
336 |
|
337 offset = PR_Seek64(fd, metaOffset, PR_SEEK_SET); |
|
338 if (offset == -1) { |
|
339 PR_Close(fd); |
|
340 return NS_ERROR_FAILURE; |
|
341 } |
|
342 |
|
343 bytesRead = PR_Read(fd, mBuf, mBufSize); |
|
344 PR_Close(fd); |
|
345 if (bytesRead != static_cast<int32_t>(mBufSize)) { |
|
346 return NS_ERROR_FAILURE; |
|
347 } |
|
348 |
|
349 rv = ParseMetadata(metaOffset, 0, false); |
|
350 NS_ENSURE_SUCCESS(rv, rv); |
|
351 |
|
352 return NS_OK; |
|
353 } |
|
354 |
|
355 const char * |
|
356 CacheFileMetadata::GetElement(const char *aKey) |
|
357 { |
|
358 const char *data = mBuf; |
|
359 const char *limit = mBuf + mElementsSize; |
|
360 |
|
361 while (data < limit) { |
|
362 // Point to the value part |
|
363 const char *value = data + strlen(data) + 1; |
|
364 MOZ_ASSERT(value < limit, "Metadata elements corrupted"); |
|
365 if (strcmp(data, aKey) == 0) { |
|
366 LOG(("CacheFileMetadata::GetElement() - Key found [this=%p, key=%s]", |
|
367 this, aKey)); |
|
368 return value; |
|
369 } |
|
370 |
|
371 // Skip value part |
|
372 data = value + strlen(value) + 1; |
|
373 } |
|
374 MOZ_ASSERT(data == limit, "Metadata elements corrupted"); |
|
375 LOG(("CacheFileMetadata::GetElement() - Key not found [this=%p, key=%s]", |
|
376 this, aKey)); |
|
377 return nullptr; |
|
378 } |
|
379 |
|
380 nsresult |
|
381 CacheFileMetadata::SetElement(const char *aKey, const char *aValue) |
|
382 { |
|
383 LOG(("CacheFileMetadata::SetElement() [this=%p, key=%s, value=%p]", |
|
384 this, aKey, aValue)); |
|
385 |
|
386 MarkDirty(); |
|
387 |
|
388 const uint32_t keySize = strlen(aKey) + 1; |
|
389 char *pos = const_cast<char *>(GetElement(aKey)); |
|
390 |
|
391 if (!aValue) { |
|
392 // No value means remove the key/value pair completely, if existing |
|
393 if (pos) { |
|
394 uint32_t oldValueSize = strlen(pos) + 1; |
|
395 uint32_t offset = pos - mBuf; |
|
396 uint32_t remainder = mElementsSize - (offset + oldValueSize); |
|
397 |
|
398 memmove(pos - keySize, pos + oldValueSize, remainder); |
|
399 mElementsSize -= keySize + oldValueSize; |
|
400 } |
|
401 return NS_OK; |
|
402 } |
|
403 |
|
404 const uint32_t valueSize = strlen(aValue) + 1; |
|
405 uint32_t newSize = mElementsSize + valueSize; |
|
406 if (pos) { |
|
407 const uint32_t oldValueSize = strlen(pos) + 1; |
|
408 const uint32_t offset = pos - mBuf; |
|
409 const uint32_t remainder = mElementsSize - (offset + oldValueSize); |
|
410 |
|
411 // Update the value in place |
|
412 newSize -= oldValueSize; |
|
413 EnsureBuffer(newSize); |
|
414 |
|
415 // Move the remainder to the right place |
|
416 pos = mBuf + offset; |
|
417 memmove(pos + valueSize, pos + oldValueSize, remainder); |
|
418 } else { |
|
419 // allocate new meta data element |
|
420 newSize += keySize; |
|
421 EnsureBuffer(newSize); |
|
422 |
|
423 // Add after last element |
|
424 pos = mBuf + mElementsSize; |
|
425 memcpy(pos, aKey, keySize); |
|
426 pos += keySize; |
|
427 } |
|
428 |
|
429 // Update value |
|
430 memcpy(pos, aValue, valueSize); |
|
431 mElementsSize = newSize; |
|
432 |
|
433 return NS_OK; |
|
434 } |
|
435 |
|
436 CacheHash::Hash16_t |
|
437 CacheFileMetadata::GetHash(uint32_t aIndex) |
|
438 { |
|
439 MOZ_ASSERT(aIndex < mHashCount); |
|
440 return NetworkEndian::readUint16(&mHashArray[aIndex]); |
|
441 } |
|
442 |
|
443 nsresult |
|
444 CacheFileMetadata::SetHash(uint32_t aIndex, CacheHash::Hash16_t aHash) |
|
445 { |
|
446 LOG(("CacheFileMetadata::SetHash() [this=%p, idx=%d, hash=%x]", |
|
447 this, aIndex, aHash)); |
|
448 |
|
449 MarkDirty(); |
|
450 |
|
451 MOZ_ASSERT(aIndex <= mHashCount); |
|
452 |
|
453 if (aIndex > mHashCount) { |
|
454 return NS_ERROR_INVALID_ARG; |
|
455 } else if (aIndex == mHashCount) { |
|
456 if ((aIndex + 1) * sizeof(CacheHash::Hash16_t) > mHashArraySize) { |
|
457 // reallocate hash array buffer |
|
458 if (mHashArraySize == 0) |
|
459 mHashArraySize = 32 * sizeof(CacheHash::Hash16_t); |
|
460 else |
|
461 mHashArraySize *= 2; |
|
462 mHashArray = static_cast<CacheHash::Hash16_t *>( |
|
463 moz_xrealloc(mHashArray, mHashArraySize)); |
|
464 } |
|
465 |
|
466 mHashCount++; |
|
467 } |
|
468 |
|
469 NetworkEndian::writeUint16(&mHashArray[aIndex], aHash); |
|
470 |
|
471 DoMemoryReport(MemoryUsage()); |
|
472 |
|
473 return NS_OK; |
|
474 } |
|
475 |
|
476 nsresult |
|
477 CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime) |
|
478 { |
|
479 LOG(("CacheFileMetadata::SetExpirationTime() [this=%p, expirationTime=%d]", |
|
480 this, aExpirationTime)); |
|
481 |
|
482 MarkDirty(); |
|
483 mMetaHdr.mExpirationTime = aExpirationTime; |
|
484 return NS_OK; |
|
485 } |
|
486 |
|
487 nsresult |
|
488 CacheFileMetadata::GetExpirationTime(uint32_t *_retval) |
|
489 { |
|
490 *_retval = mMetaHdr.mExpirationTime; |
|
491 return NS_OK; |
|
492 } |
|
493 |
|
494 nsresult |
|
495 CacheFileMetadata::SetLastModified(uint32_t aLastModified) |
|
496 { |
|
497 LOG(("CacheFileMetadata::SetLastModified() [this=%p, lastModified=%d]", |
|
498 this, aLastModified)); |
|
499 |
|
500 MarkDirty(); |
|
501 mMetaHdr.mLastModified = aLastModified; |
|
502 return NS_OK; |
|
503 } |
|
504 |
|
505 nsresult |
|
506 CacheFileMetadata::GetLastModified(uint32_t *_retval) |
|
507 { |
|
508 *_retval = mMetaHdr.mLastModified; |
|
509 return NS_OK; |
|
510 } |
|
511 |
|
512 nsresult |
|
513 CacheFileMetadata::SetFrecency(uint32_t aFrecency) |
|
514 { |
|
515 LOG(("CacheFileMetadata::SetFrecency() [this=%p, frecency=%f]", |
|
516 this, (double)aFrecency)); |
|
517 |
|
518 MarkDirty(); |
|
519 mMetaHdr.mFrecency = aFrecency; |
|
520 return NS_OK; |
|
521 } |
|
522 |
|
523 nsresult |
|
524 CacheFileMetadata::GetFrecency(uint32_t *_retval) |
|
525 { |
|
526 *_retval = mMetaHdr.mFrecency; |
|
527 return NS_OK; |
|
528 } |
|
529 |
|
530 nsresult |
|
531 CacheFileMetadata::GetLastFetched(uint32_t *_retval) |
|
532 { |
|
533 *_retval = mMetaHdr.mLastFetched; |
|
534 return NS_OK; |
|
535 } |
|
536 |
|
537 nsresult |
|
538 CacheFileMetadata::GetFetchCount(uint32_t *_retval) |
|
539 { |
|
540 *_retval = mMetaHdr.mFetchCount; |
|
541 return NS_OK; |
|
542 } |
|
543 |
|
544 nsresult |
|
545 CacheFileMetadata::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) |
|
546 { |
|
547 MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!"); |
|
548 return NS_ERROR_UNEXPECTED; |
|
549 } |
|
550 |
|
551 nsresult |
|
552 CacheFileMetadata::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, |
|
553 nsresult aResult) |
|
554 { |
|
555 LOG(("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, result=0x%08x]", |
|
556 this, aHandle, aResult)); |
|
557 |
|
558 MOZ_ASSERT(mListener); |
|
559 MOZ_ASSERT(mWriteBuf); |
|
560 |
|
561 free(mWriteBuf); |
|
562 mWriteBuf = nullptr; |
|
563 |
|
564 nsCOMPtr<CacheFileMetadataListener> listener; |
|
565 |
|
566 mListener.swap(listener); |
|
567 listener->OnMetadataWritten(aResult); |
|
568 |
|
569 DoMemoryReport(MemoryUsage()); |
|
570 |
|
571 return NS_OK; |
|
572 } |
|
573 |
|
574 nsresult |
|
575 CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf, |
|
576 nsresult aResult) |
|
577 { |
|
578 LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]", |
|
579 this, aHandle, aResult)); |
|
580 |
|
581 MOZ_ASSERT(mListener); |
|
582 |
|
583 nsresult rv, retval; |
|
584 nsCOMPtr<CacheFileMetadataListener> listener; |
|
585 |
|
586 if (NS_FAILED(aResult)) { |
|
587 LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed" |
|
588 ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult)); |
|
589 |
|
590 InitEmptyMetadata(); |
|
591 retval = NS_OK; |
|
592 |
|
593 mListener.swap(listener); |
|
594 listener->OnMetadataRead(retval); |
|
595 return NS_OK; |
|
596 } |
|
597 |
|
598 // check whether we have read all necessary data |
|
599 uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize - |
|
600 sizeof(uint32_t)); |
|
601 |
|
602 int64_t size = mHandle->FileSize(); |
|
603 MOZ_ASSERT(size != -1); |
|
604 |
|
605 if (realOffset >= size) { |
|
606 LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating " |
|
607 "empty metadata. [this=%p, realOffset=%d, size=%lld]", this, |
|
608 realOffset, size)); |
|
609 |
|
610 InitEmptyMetadata(); |
|
611 retval = NS_OK; |
|
612 |
|
613 mListener.swap(listener); |
|
614 listener->OnMetadataRead(retval); |
|
615 return NS_OK; |
|
616 } |
|
617 |
|
618 uint32_t usedOffset = size - mBufSize; |
|
619 |
|
620 if (realOffset < usedOffset) { |
|
621 uint32_t missing = usedOffset - realOffset; |
|
622 // we need to read more data |
|
623 mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize + missing)); |
|
624 memmove(mBuf + missing, mBuf, mBufSize); |
|
625 mBufSize += missing; |
|
626 |
|
627 DoMemoryReport(MemoryUsage()); |
|
628 |
|
629 LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to " |
|
630 "have full metadata. [this=%p]", missing, this)); |
|
631 |
|
632 rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, true, this); |
|
633 if (NS_FAILED(rv)) { |
|
634 LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() " |
|
635 "failed synchronously, creating empty metadata. [this=%p, " |
|
636 "rv=0x%08x]", this, rv)); |
|
637 |
|
638 InitEmptyMetadata(); |
|
639 retval = NS_OK; |
|
640 |
|
641 mListener.swap(listener); |
|
642 listener->OnMetadataRead(retval); |
|
643 return NS_OK; |
|
644 } |
|
645 |
|
646 return NS_OK; |
|
647 } |
|
648 |
|
649 // We have all data according to offset information at the end of the entry. |
|
650 // Try to parse it. |
|
651 rv = ParseMetadata(realOffset, realOffset - usedOffset, true); |
|
652 if (NS_FAILED(rv)) { |
|
653 LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating " |
|
654 "empty metadata. [this=%p]", this)); |
|
655 InitEmptyMetadata(); |
|
656 retval = NS_OK; |
|
657 } |
|
658 else { |
|
659 retval = NS_OK; |
|
660 } |
|
661 |
|
662 mListener.swap(listener); |
|
663 listener->OnMetadataRead(retval); |
|
664 |
|
665 return NS_OK; |
|
666 } |
|
667 |
|
668 nsresult |
|
669 CacheFileMetadata::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) |
|
670 { |
|
671 MOZ_CRASH("CacheFileMetadata::OnFileDoomed should not be called!"); |
|
672 return NS_ERROR_UNEXPECTED; |
|
673 } |
|
674 |
|
675 nsresult |
|
676 CacheFileMetadata::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) |
|
677 { |
|
678 MOZ_CRASH("CacheFileMetadata::OnEOFSet should not be called!"); |
|
679 return NS_ERROR_UNEXPECTED; |
|
680 } |
|
681 |
|
682 nsresult |
|
683 CacheFileMetadata::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) |
|
684 { |
|
685 MOZ_CRASH("CacheFileMetadata::OnFileRenamed should not be called!"); |
|
686 return NS_ERROR_UNEXPECTED; |
|
687 } |
|
688 |
|
689 void |
|
690 CacheFileMetadata::InitEmptyMetadata() |
|
691 { |
|
692 if (mBuf) { |
|
693 free(mBuf); |
|
694 mBuf = nullptr; |
|
695 mBufSize = 0; |
|
696 } |
|
697 mOffset = 0; |
|
698 mMetaHdr.mVersion = kCacheEntryVersion; |
|
699 mMetaHdr.mFetchCount = 1; |
|
700 mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME; |
|
701 mMetaHdr.mKeySize = mKey.Length(); |
|
702 |
|
703 DoMemoryReport(MemoryUsage()); |
|
704 } |
|
705 |
|
706 nsresult |
|
707 CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset, |
|
708 bool aHaveKey) |
|
709 { |
|
710 LOG(("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, " |
|
711 "bufOffset=%d, haveKey=%u]", this, aMetaOffset, aBufOffset, aHaveKey)); |
|
712 |
|
713 nsresult rv; |
|
714 |
|
715 uint32_t metaposOffset = mBufSize - sizeof(uint32_t); |
|
716 uint32_t hashesOffset = aBufOffset + sizeof(uint32_t); |
|
717 uint32_t hashCount = aMetaOffset / kChunkSize; |
|
718 if (aMetaOffset % kChunkSize) |
|
719 hashCount++; |
|
720 uint32_t hashesLen = hashCount * sizeof(CacheHash::Hash16_t); |
|
721 uint32_t hdrOffset = hashesOffset + hashesLen; |
|
722 uint32_t keyOffset = hdrOffset + sizeof(CacheFileMetadataHeader); |
|
723 |
|
724 LOG(("CacheFileMetadata::ParseMetadata() [this=%p]\n metaposOffset=%d\n " |
|
725 "hashesOffset=%d\n hashCount=%d\n hashesLen=%d\n hdfOffset=%d\n " |
|
726 "keyOffset=%d\n", this, metaposOffset, hashesOffset, hashCount, |
|
727 hashesLen,hdrOffset, keyOffset)); |
|
728 |
|
729 if (keyOffset > metaposOffset) { |
|
730 LOG(("CacheFileMetadata::ParseMetadata() - Wrong keyOffset! [this=%p]", |
|
731 this)); |
|
732 return NS_ERROR_FILE_CORRUPTED; |
|
733 } |
|
734 |
|
735 mMetaHdr.ReadFromBuf(mBuf + hdrOffset); |
|
736 |
|
737 if (mMetaHdr.mVersion != kCacheEntryVersion) { |
|
738 LOG(("CacheFileMetadata::ParseMetadata() - Not a version we understand to. " |
|
739 "[version=0x%x, this=%p]", mMetaHdr.mVersion, this)); |
|
740 return NS_ERROR_UNEXPECTED; |
|
741 } |
|
742 |
|
743 uint32_t elementsOffset = mMetaHdr.mKeySize + keyOffset + 1; |
|
744 |
|
745 if (elementsOffset > metaposOffset) { |
|
746 LOG(("CacheFileMetadata::ParseMetadata() - Wrong elementsOffset %d " |
|
747 "[this=%p]", elementsOffset, this)); |
|
748 return NS_ERROR_FILE_CORRUPTED; |
|
749 } |
|
750 |
|
751 // check that key ends with \0 |
|
752 if (mBuf[elementsOffset - 1] != 0) { |
|
753 LOG(("CacheFileMetadata::ParseMetadata() - Elements not null terminated. " |
|
754 "[this=%p]", this)); |
|
755 return NS_ERROR_FILE_CORRUPTED; |
|
756 } |
|
757 |
|
758 |
|
759 if (!aHaveKey) { |
|
760 // get the key form metadata |
|
761 mKey.Assign(mBuf + keyOffset, mMetaHdr.mKeySize); |
|
762 |
|
763 rv = ParseKey(mKey); |
|
764 if (NS_FAILED(rv)) |
|
765 return rv; |
|
766 } |
|
767 else { |
|
768 if (mMetaHdr.mKeySize != mKey.Length()) { |
|
769 LOG(("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s " |
|
770 "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(), |
|
771 this)); |
|
772 return NS_ERROR_FILE_CORRUPTED; |
|
773 } |
|
774 |
|
775 if (memcmp(mKey.get(), mBuf + keyOffset, mKey.Length()) != 0) { |
|
776 LOG(("CacheFileMetadata::ParseMetadata() - Key collision (2), key=%s " |
|
777 "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(), |
|
778 this)); |
|
779 return NS_ERROR_FILE_CORRUPTED; |
|
780 } |
|
781 } |
|
782 |
|
783 // check metadata hash (data from hashesOffset to metaposOffset) |
|
784 CacheHash::Hash32_t hashComputed, hashExpected; |
|
785 hashComputed = CacheHash::Hash(mBuf + hashesOffset, |
|
786 metaposOffset - hashesOffset); |
|
787 hashExpected = NetworkEndian::readUint32(mBuf + aBufOffset); |
|
788 |
|
789 if (hashComputed != hashExpected) { |
|
790 LOG(("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of " |
|
791 "the metadata is %x, hash in file is %x [this=%p]", hashComputed, |
|
792 hashExpected, this)); |
|
793 return NS_ERROR_FILE_CORRUPTED; |
|
794 } |
|
795 |
|
796 // check elements |
|
797 rv = CheckElements(mBuf + elementsOffset, metaposOffset - elementsOffset); |
|
798 if (NS_FAILED(rv)) |
|
799 return rv; |
|
800 |
|
801 mHashArraySize = hashesLen; |
|
802 mHashCount = hashCount; |
|
803 if (mHashArraySize) { |
|
804 mHashArray = static_cast<CacheHash::Hash16_t *>( |
|
805 moz_xmalloc(mHashArraySize)); |
|
806 memcpy(mHashArray, mBuf + hashesOffset, mHashArraySize); |
|
807 } |
|
808 |
|
809 |
|
810 mMetaHdr.mFetchCount++; |
|
811 MarkDirty(); |
|
812 |
|
813 mElementsSize = metaposOffset - elementsOffset; |
|
814 memmove(mBuf, mBuf + elementsOffset, mElementsSize); |
|
815 mOffset = aMetaOffset; |
|
816 |
|
817 // TODO: shrink memory if buffer is too big |
|
818 |
|
819 DoMemoryReport(MemoryUsage()); |
|
820 |
|
821 return NS_OK; |
|
822 } |
|
823 |
|
824 nsresult |
|
825 CacheFileMetadata::CheckElements(const char *aBuf, uint32_t aSize) |
|
826 { |
|
827 if (aSize) { |
|
828 // Check if the metadata ends with a zero byte. |
|
829 if (aBuf[aSize - 1] != 0) { |
|
830 NS_ERROR("Metadata elements are not null terminated"); |
|
831 LOG(("CacheFileMetadata::CheckElements() - Elements are not null " |
|
832 "terminated. [this=%p]", this)); |
|
833 return NS_ERROR_FILE_CORRUPTED; |
|
834 } |
|
835 // Check that there are an even number of zero bytes |
|
836 // to match the pattern { key \0 value \0 } |
|
837 bool odd = false; |
|
838 for (uint32_t i = 0; i < aSize; i++) { |
|
839 if (aBuf[i] == 0) |
|
840 odd = !odd; |
|
841 } |
|
842 if (odd) { |
|
843 NS_ERROR("Metadata elements are malformed"); |
|
844 LOG(("CacheFileMetadata::CheckElements() - Elements are malformed. " |
|
845 "[this=%p]", this)); |
|
846 return NS_ERROR_FILE_CORRUPTED; |
|
847 } |
|
848 } |
|
849 return NS_OK; |
|
850 } |
|
851 |
|
852 void |
|
853 CacheFileMetadata::EnsureBuffer(uint32_t aSize) |
|
854 { |
|
855 if (mBufSize < aSize) { |
|
856 mBufSize = aSize; |
|
857 mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize)); |
|
858 } |
|
859 |
|
860 DoMemoryReport(MemoryUsage()); |
|
861 } |
|
862 |
|
863 nsresult |
|
864 CacheFileMetadata::ParseKey(const nsACString &aKey) |
|
865 { |
|
866 nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey); |
|
867 NS_ENSURE_TRUE(info, NS_ERROR_FAILURE); |
|
868 |
|
869 mAnonymous = info->IsAnonymous(); |
|
870 mAppId = info->AppId(); |
|
871 mInBrowser = info->IsInBrowserElement(); |
|
872 |
|
873 return NS_OK; |
|
874 } |
|
875 |
|
876 // Memory reporting |
|
877 |
|
878 size_t |
|
879 CacheFileMetadata::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const |
|
880 { |
|
881 size_t n = 0; |
|
882 // mHandle reported via CacheFileIOManager. |
|
883 n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf); |
|
884 n += mallocSizeOf(mHashArray); |
|
885 n += mallocSizeOf(mBuf); |
|
886 n += mallocSizeOf(mWriteBuf); |
|
887 // mListener is usually the owning CacheFile. |
|
888 |
|
889 return n; |
|
890 } |
|
891 |
|
892 size_t |
|
893 CacheFileMetadata::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const |
|
894 { |
|
895 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); |
|
896 } |
|
897 |
|
898 } // net |
|
899 } // mozilla |