netwerk/cache2/CacheFileMetadata.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:abb8dd952a38
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

mercurial