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/. */
6 #include "CacheFileMetadata.h"
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 "nsICacheEntry.h" // for nsICacheEntryMetaDataVisitor
15 #include "../cache/nsCacheUtils.h"
17 #include "mozilla/Telemetry.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/IntegerPrintfMacros.h"
26 #define kMinMetadataRead 1024 // TODO find optimal value from telemetry
27 #define kAlignSize 4096
29 // Most of the cache entries fit into one chunk due to current chunk size. Make
30 // sure to tweak this value if kChunkSize is going to change.
31 #define kInitialHashArraySize 1
33 // Initial elements buffer size.
34 #define kInitialBufSize 64
36 // Max size of elements in bytes.
37 #define kMaxElementsSize 64*1024
39 #define NOW_SECONDS() (uint32_t(PR_Now() / PR_USEC_PER_SEC))
41 NS_IMPL_ISUPPORTS(CacheFileMetadata
, CacheFileIOListener
)
43 CacheFileMetadata::CacheFileMetadata(CacheFileHandle
*aHandle
, const nsACString
&aKey
)
44 : CacheMemoryConsumer(NORMAL
)
56 , mAllocExactSize(false)
59 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, handle=%p, key=%s]",
60 this, aHandle
, PromiseFlatCString(aKey
).get()));
62 memset(&mMetaHdr
, 0, sizeof(CacheFileMetadataHeader
));
63 mMetaHdr
.mVersion
= kCacheEntryVersion
;
64 mMetaHdr
.mExpirationTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
67 DebugOnly
<nsresult
> rv
;
69 MOZ_ASSERT(NS_SUCCEEDED(rv
));
72 CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly
, bool aPinned
, const nsACString
&aKey
)
73 : CacheMemoryConsumer(aMemoryOnly
? MEMORY_ONLY
: NORMAL
)
85 , mAllocExactSize(false)
88 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, key=%s]",
89 this, PromiseFlatCString(aKey
).get()));
91 memset(&mMetaHdr
, 0, sizeof(CacheFileMetadataHeader
));
92 mMetaHdr
.mVersion
= kCacheEntryVersion
;
94 AddFlags(kCacheEntryIsPinned
);
96 mMetaHdr
.mExpirationTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
98 mMetaHdr
.mKeySize
= mKey
.Length();
100 DebugOnly
<nsresult
> rv
;
102 MOZ_ASSERT(NS_SUCCEEDED(rv
));
105 CacheFileMetadata::CacheFileMetadata()
106 : CacheMemoryConsumer(DONT_REPORT
/* This is a helper class */)
108 , mHashArray(nullptr)
118 , mAllocExactSize(false)
121 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p]", this));
123 memset(&mMetaHdr
, 0, sizeof(CacheFileMetadataHeader
));
126 CacheFileMetadata::~CacheFileMetadata()
128 LOG(("CacheFileMetadata::~CacheFileMetadata() [this=%p]", this));
130 MOZ_ASSERT(!mListener
);
133 CacheFileUtils::FreeBuffer(mHashArray
);
134 mHashArray
= nullptr;
139 CacheFileUtils::FreeBuffer(mBuf
);
146 CacheFileMetadata::SetHandle(CacheFileHandle
*aHandle
)
148 LOG(("CacheFileMetadata::SetHandle() [this=%p, handle=%p]", this, aHandle
));
150 MOZ_ASSERT(!mHandle
);
156 CacheFileMetadata::GetKey(nsACString
&_retval
)
163 CacheFileMetadata::ReadMetadata(CacheFileMetadataListener
*aListener
)
165 LOG(("CacheFileMetadata::ReadMetadata() [this=%p, listener=%p]", this, aListener
));
167 MOZ_ASSERT(!mListener
);
168 MOZ_ASSERT(!mHashArray
);
170 MOZ_ASSERT(!mWriteBuf
);
174 int64_t size
= mHandle
->FileSize();
175 MOZ_ASSERT(size
!= -1);
178 // this is a new entry
179 LOG(("CacheFileMetadata::ReadMetadata() - Filesize == 0, creating empty "
180 "metadata. [this=%p]", this));
183 aListener
->OnMetadataRead(NS_OK
);
187 if (size
< int64_t(sizeof(CacheFileMetadataHeader
) + 2*sizeof(uint32_t))) {
188 // there must be at least checksum, header and offset
189 LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating "
190 "empty metadata. [this=%p, filesize=%" PRId64
"]", this, size
));
193 aListener
->OnMetadataRead(NS_OK
);
197 // Set offset so that we read at least kMinMetadataRead if the file is big
200 if (size
< kMinMetadataRead
) {
203 offset
= size
- kMinMetadataRead
;
206 // round offset to kAlignSize blocks
207 offset
= (offset
/ kAlignSize
) * kAlignSize
;
209 mBufSize
= size
- offset
;
210 mBuf
= static_cast<char *>(moz_xmalloc(mBufSize
));
212 DoMemoryReport(MemoryUsage());
214 LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying "
215 "offset=%" PRId64
", filesize=%" PRId64
" [this=%p]", offset
, size
, this));
217 mReadStart
= mozilla::TimeStamp::Now();
218 mListener
= aListener
;
219 rv
= CacheFileIOManager::Read(mHandle
, offset
, mBuf
, mBufSize
, this);
221 LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed"
222 " synchronously, creating empty metadata. [this=%p, rv=0x%08" PRIx32
"]",
223 this, static_cast<uint32_t>(rv
)));
227 aListener
->OnMetadataRead(NS_OK
);
235 CacheFileMetadata::CalcMetadataSize(uint32_t aElementsSize
, uint32_t aHashCount
)
237 return sizeof(uint32_t) + // hash of the metadata
238 aHashCount
* sizeof(CacheHash::Hash16_t
) + // array of chunk hashes
239 sizeof(CacheFileMetadataHeader
) + // metadata header
240 mKey
.Length() + 1 + // key with trailing null
241 aElementsSize
+ // elements
242 sizeof(uint32_t); // offset
246 CacheFileMetadata::WriteMetadata(uint32_t aOffset
,
247 CacheFileMetadataListener
*aListener
)
249 LOG(("CacheFileMetadata::WriteMetadata() [this=%p, offset=%d, listener=%p]",
250 this, aOffset
, aListener
));
252 MOZ_ASSERT(!mListener
);
253 MOZ_ASSERT(!mWriteBuf
);
259 mWriteBuf
= static_cast<char *>(malloc(CalcMetadataSize(mElementsSize
,
262 return NS_ERROR_OUT_OF_MEMORY
;
265 char *p
= mWriteBuf
+ sizeof(uint32_t);
267 memcpy(p
, mHashArray
, mHashCount
* sizeof(CacheHash::Hash16_t
));
268 p
+= mHashCount
* sizeof(CacheHash::Hash16_t
);
270 mMetaHdr
.WriteToBuf(p
);
271 p
+= sizeof(CacheFileMetadataHeader
);
272 memcpy(p
, mKey
.get(), mKey
.Length());
277 memcpy(p
, mBuf
, mElementsSize
);
281 CacheHash::Hash32_t hash
;
282 hash
= CacheHash::Hash(mWriteBuf
+ sizeof(uint32_t),
283 p
- mWriteBuf
- sizeof(uint32_t));
284 NetworkEndian::writeUint32(mWriteBuf
, hash
);
286 NetworkEndian::writeUint32(p
, aOffset
);
287 p
+= sizeof(uint32_t);
289 char * writeBuffer
= mWriteBuf
;
291 mListener
= aListener
;
293 // We are not going to pass |this| as a callback so the buffer will be
294 // released by CacheFileIOManager. Just null out mWriteBuf here.
298 rv
= CacheFileIOManager::Write(mHandle
, aOffset
, writeBuffer
, p
- writeBuffer
,
299 true, true, aListener
? this : nullptr);
301 LOG(("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() "
302 "failed synchronously. [this=%p, rv=0x%08" PRIx32
"]",
303 this, static_cast<uint32_t>(rv
)));
307 CacheFileUtils::FreeBuffer(mWriteBuf
);
310 NS_ENSURE_SUCCESS(rv
, rv
);
313 DoMemoryReport(MemoryUsage());
319 CacheFileMetadata::SyncReadMetadata(nsIFile
*aFile
)
321 LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this));
323 MOZ_ASSERT(!mListener
);
324 MOZ_ASSERT(!mHandle
);
325 MOZ_ASSERT(!mHashArray
);
327 MOZ_ASSERT(!mWriteBuf
);
328 MOZ_ASSERT(mKey
.IsEmpty());
333 rv
= aFile
->GetFileSize(&fileSize
);
335 // Don't bloat the console
340 rv
= aFile
->OpenNSPRFileDesc(PR_RDONLY
, 0600, &fd
);
341 NS_ENSURE_SUCCESS(rv
, rv
);
343 int64_t offset
= PR_Seek64(fd
, fileSize
- sizeof(uint32_t), PR_SEEK_SET
);
346 return NS_ERROR_FAILURE
;
350 int32_t bytesRead
= PR_Read(fd
, &metaOffset
, sizeof(uint32_t));
351 if (bytesRead
!= sizeof(uint32_t)) {
353 return NS_ERROR_FAILURE
;
356 metaOffset
= NetworkEndian::readUint32(&metaOffset
);
357 if (metaOffset
> fileSize
) {
359 return NS_ERROR_FAILURE
;
362 mBuf
= static_cast<char *>(malloc(fileSize
- metaOffset
));
364 return NS_ERROR_OUT_OF_MEMORY
;
366 mBufSize
= fileSize
- metaOffset
;
368 DoMemoryReport(MemoryUsage());
370 offset
= PR_Seek64(fd
, metaOffset
, PR_SEEK_SET
);
373 return NS_ERROR_FAILURE
;
376 bytesRead
= PR_Read(fd
, mBuf
, mBufSize
);
378 if (bytesRead
!= static_cast<int32_t>(mBufSize
)) {
379 return NS_ERROR_FAILURE
;
382 rv
= ParseMetadata(metaOffset
, 0, false);
383 NS_ENSURE_SUCCESS(rv
, rv
);
389 CacheFileMetadata::GetElement(const char *aKey
)
391 const char *data
= mBuf
;
392 const char *limit
= mBuf
+ mElementsSize
;
394 while (data
!= limit
) {
395 size_t maxLen
= limit
- data
;
396 size_t keyLen
= strnlen(data
, maxLen
);
397 MOZ_RELEASE_ASSERT(keyLen
!= maxLen
, "Metadata elements corrupted. Key "
398 "isn't null terminated!");
399 MOZ_RELEASE_ASSERT(keyLen
+ 1 != maxLen
, "Metadata elements corrupted. "
400 "There is no value for the key!");
402 const char *value
= data
+ keyLen
+ 1;
403 maxLen
= limit
- value
;
404 size_t valueLen
= strnlen(value
, maxLen
);
405 MOZ_RELEASE_ASSERT(valueLen
!= maxLen
, "Metadata elements corrupted. Value "
406 "isn't null terminated!");
408 if (strcmp(data
, aKey
) == 0) {
409 LOG(("CacheFileMetadata::GetElement() - Key found [this=%p, key=%s]",
414 // point to next pair
415 data
+= keyLen
+ valueLen
+ 2;
417 LOG(("CacheFileMetadata::GetElement() - Key not found [this=%p, key=%s]",
423 CacheFileMetadata::SetElement(const char *aKey
, const char *aValue
)
425 LOG(("CacheFileMetadata::SetElement() [this=%p, key=%s, value=%p]",
426 this, aKey
, aValue
));
432 const uint32_t keySize
= strlen(aKey
) + 1;
433 char *pos
= const_cast<char *>(GetElement(aKey
));
436 // No value means remove the key/value pair completely, if existing
438 uint32_t oldValueSize
= strlen(pos
) + 1;
439 uint32_t offset
= pos
- mBuf
;
440 uint32_t remainder
= mElementsSize
- (offset
+ oldValueSize
);
442 memmove(pos
- keySize
, pos
+ oldValueSize
, remainder
);
443 mElementsSize
-= keySize
+ oldValueSize
;
448 const uint32_t valueSize
= strlen(aValue
) + 1;
449 uint32_t newSize
= mElementsSize
+ valueSize
;
451 const uint32_t oldValueSize
= strlen(pos
) + 1;
452 const uint32_t offset
= pos
- mBuf
;
453 const uint32_t remainder
= mElementsSize
- (offset
+ oldValueSize
);
455 // Update the value in place
456 newSize
-= oldValueSize
;
457 rv
= EnsureBuffer(newSize
);
462 // Move the remainder to the right place
464 memmove(pos
+ valueSize
, pos
+ oldValueSize
, remainder
);
466 // allocate new meta data element
468 rv
= EnsureBuffer(newSize
);
473 // Add after last element
474 pos
= mBuf
+ mElementsSize
;
475 memcpy(pos
, aKey
, keySize
);
480 memcpy(pos
, aValue
, valueSize
);
481 mElementsSize
= newSize
;
487 CacheFileMetadata::Visit(nsICacheEntryMetaDataVisitor
*aVisitor
)
489 const char *data
= mBuf
;
490 const char *limit
= mBuf
+ mElementsSize
;
492 while (data
< limit
) {
493 // Point to the value part
494 const char *value
= data
+ strlen(data
) + 1;
495 MOZ_ASSERT(value
< limit
, "Metadata elements corrupted");
497 aVisitor
->OnMetaDataElement(data
, value
);
500 data
= value
+ strlen(value
) + 1;
503 MOZ_ASSERT(data
== limit
, "Metadata elements corrupted");
509 CacheFileMetadata::GetHash(uint32_t aIndex
)
511 MOZ_ASSERT(aIndex
< mHashCount
);
512 return NetworkEndian::readUint16(&mHashArray
[aIndex
]);
516 CacheFileMetadata::SetHash(uint32_t aIndex
, CacheHash::Hash16_t aHash
)
518 LOG(("CacheFileMetadata::SetHash() [this=%p, idx=%d, hash=%x]",
519 this, aIndex
, aHash
));
523 MOZ_ASSERT(aIndex
<= mHashCount
);
525 if (aIndex
> mHashCount
) {
526 return NS_ERROR_INVALID_ARG
;
527 } else if (aIndex
== mHashCount
) {
528 if ((aIndex
+ 1) * sizeof(CacheHash::Hash16_t
) > mHashArraySize
) {
529 // reallocate hash array buffer
530 if (mHashArraySize
== 0) {
531 mHashArraySize
= kInitialHashArraySize
* sizeof(CacheHash::Hash16_t
);
535 mHashArray
= static_cast<CacheHash::Hash16_t
*>(
536 moz_xrealloc(mHashArray
, mHashArraySize
));
542 NetworkEndian::writeUint16(&mHashArray
[aIndex
], aHash
);
544 DoMemoryReport(MemoryUsage());
550 CacheFileMetadata::RemoveHash(uint32_t aIndex
)
552 LOG(("CacheFileMetadata::RemoveHash() [this=%p, idx=%d]", this, aIndex
));
556 MOZ_ASSERT((aIndex
+ 1) == mHashCount
, "Can remove only last hash!");
558 if (aIndex
+ 1 != mHashCount
) {
559 return NS_ERROR_INVALID_ARG
;
567 CacheFileMetadata::AddFlags(uint32_t aFlags
)
570 mMetaHdr
.mFlags
|= aFlags
;
575 CacheFileMetadata::RemoveFlags(uint32_t aFlags
)
578 mMetaHdr
.mFlags
&= ~aFlags
;
583 CacheFileMetadata::GetFlags(uint32_t *_retval
)
585 *_retval
= mMetaHdr
.mFlags
;
590 CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime
)
592 LOG(("CacheFileMetadata::SetExpirationTime() [this=%p, expirationTime=%d]",
593 this, aExpirationTime
));
596 mMetaHdr
.mExpirationTime
= aExpirationTime
;
601 CacheFileMetadata::GetExpirationTime(uint32_t *_retval
)
603 *_retval
= mMetaHdr
.mExpirationTime
;
608 CacheFileMetadata::SetFrecency(uint32_t aFrecency
)
610 LOG(("CacheFileMetadata::SetFrecency() [this=%p, frecency=%f]",
611 this, (double)aFrecency
));
614 mMetaHdr
.mFrecency
= aFrecency
;
619 CacheFileMetadata::GetFrecency(uint32_t *_retval
)
621 *_retval
= mMetaHdr
.mFrecency
;
626 CacheFileMetadata::GetLastModified(uint32_t *_retval
)
628 *_retval
= mMetaHdr
.mLastModified
;
633 CacheFileMetadata::GetLastFetched(uint32_t *_retval
)
635 *_retval
= mMetaHdr
.mLastFetched
;
640 CacheFileMetadata::GetFetchCount(uint32_t *_retval
)
642 *_retval
= mMetaHdr
.mFetchCount
;
647 CacheFileMetadata::OnFetched()
651 mMetaHdr
.mLastFetched
= NOW_SECONDS();
652 ++mMetaHdr
.mFetchCount
;
657 CacheFileMetadata::MarkDirty(bool aUpdateLastModified
)
660 if (aUpdateLastModified
) {
661 mMetaHdr
.mLastModified
= NOW_SECONDS();
666 CacheFileMetadata::OnFileOpened(CacheFileHandle
*aHandle
, nsresult aResult
)
668 MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!");
669 return NS_ERROR_UNEXPECTED
;
673 CacheFileMetadata::OnDataWritten(CacheFileHandle
*aHandle
, const char *aBuf
,
676 LOG(("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, result=0x%08" PRIx32
"]",
677 this, aHandle
, static_cast<uint32_t>(aResult
)));
679 MOZ_ASSERT(mListener
);
680 MOZ_ASSERT(mWriteBuf
);
682 CacheFileUtils::FreeBuffer(mWriteBuf
);
685 nsCOMPtr
<CacheFileMetadataListener
> listener
;
687 mListener
.swap(listener
);
688 listener
->OnMetadataWritten(aResult
);
690 DoMemoryReport(MemoryUsage());
696 CacheFileMetadata::OnDataRead(CacheFileHandle
*aHandle
, char *aBuf
,
699 LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08" PRIx32
"]",
700 this, aHandle
, static_cast<uint32_t>(aResult
)));
702 MOZ_ASSERT(mListener
);
705 nsCOMPtr
<CacheFileMetadataListener
> listener
;
707 if (NS_FAILED(aResult
)) {
708 LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed"
709 ", creating empty metadata. [this=%p, rv=0x%08" PRIx32
"]",
710 this, static_cast<uint32_t>(aResult
)));
714 mListener
.swap(listener
);
715 listener
->OnMetadataRead(NS_OK
);
720 Telemetry::AccumulateTimeDelta(
721 Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_TIME_MS
, mReadStart
);
722 Telemetry::Accumulate(
723 Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_SIZE
, mBufSize
);
725 Telemetry::AccumulateTimeDelta(
726 Telemetry::NETWORK_CACHE_METADATA_SECOND_READ_TIME_MS
, mReadStart
);
729 // check whether we have read all necessary data
730 uint32_t realOffset
= NetworkEndian::readUint32(mBuf
+ mBufSize
-
733 int64_t size
= mHandle
->FileSize();
734 MOZ_ASSERT(size
!= -1);
736 if (realOffset
>= size
) {
737 LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating "
738 "empty metadata. [this=%p, realOffset=%u, size=%" PRId64
"]", this,
743 mListener
.swap(listener
);
744 listener
->OnMetadataRead(NS_OK
);
748 uint32_t maxHashCount
= size
/ kChunkSize
;
749 uint32_t maxMetadataSize
= CalcMetadataSize(kMaxElementsSize
, maxHashCount
);
750 if (size
- realOffset
> maxMetadataSize
) {
751 LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, metadata would "
752 "be too big, creating empty metadata. [this=%p, realOffset=%u, "
753 "maxMetadataSize=%u, size=%" PRId64
"]", this, realOffset
, maxMetadataSize
,
758 mListener
.swap(listener
);
759 listener
->OnMetadataRead(NS_OK
);
763 uint32_t usedOffset
= size
- mBufSize
;
765 if (realOffset
< usedOffset
) {
766 uint32_t missing
= usedOffset
- realOffset
;
767 // we need to read more data
768 char *newBuf
= static_cast<char *>(realloc(mBuf
, mBufSize
+ missing
));
770 LOG(("CacheFileMetadata::OnDataRead() - Error allocating %d more bytes "
771 "for the missing part of the metadata, creating empty metadata. "
772 "[this=%p]", missing
, this));
776 mListener
.swap(listener
);
777 listener
->OnMetadataRead(NS_OK
);
782 memmove(mBuf
+ missing
, mBuf
, mBufSize
);
785 DoMemoryReport(MemoryUsage());
787 LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to "
788 "have full metadata. [this=%p]", missing
, this));
791 mReadStart
= mozilla::TimeStamp::Now();
792 rv
= CacheFileIOManager::Read(mHandle
, realOffset
, mBuf
, missing
, this);
794 LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() "
795 "failed synchronously, creating empty metadata. [this=%p, "
796 "rv=0x%08" PRIx32
"]", this, static_cast<uint32_t>(rv
)));
800 mListener
.swap(listener
);
801 listener
->OnMetadataRead(NS_OK
);
808 Telemetry::Accumulate(Telemetry::NETWORK_CACHE_METADATA_SIZE
,
811 // We have all data according to offset information at the end of the entry.
813 rv
= ParseMetadata(realOffset
, realOffset
- usedOffset
, true);
815 LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating "
816 "empty metadata. [this=%p]", this));
819 // Shrink elements buffer.
820 mBuf
= static_cast<char *>(moz_xrealloc(mBuf
, mElementsSize
));
821 mBufSize
= mElementsSize
;
823 // There is usually no or just one call to SetMetadataElement() when the
824 // metadata is parsed from disk. Avoid allocating power of two sized buffer
825 // which we do in case of newly created metadata.
826 mAllocExactSize
= true;
829 mListener
.swap(listener
);
830 listener
->OnMetadataRead(NS_OK
);
836 CacheFileMetadata::OnFileDoomed(CacheFileHandle
*aHandle
, nsresult aResult
)
838 MOZ_CRASH("CacheFileMetadata::OnFileDoomed should not be called!");
839 return NS_ERROR_UNEXPECTED
;
843 CacheFileMetadata::OnEOFSet(CacheFileHandle
*aHandle
, nsresult aResult
)
845 MOZ_CRASH("CacheFileMetadata::OnEOFSet should not be called!");
846 return NS_ERROR_UNEXPECTED
;
850 CacheFileMetadata::OnFileRenamed(CacheFileHandle
*aHandle
, nsresult aResult
)
852 MOZ_CRASH("CacheFileMetadata::OnFileRenamed should not be called!");
853 return NS_ERROR_UNEXPECTED
;
857 CacheFileMetadata::InitEmptyMetadata()
860 CacheFileUtils::FreeBuffer(mBuf
);
864 mAllocExactSize
= false;
866 mMetaHdr
.mVersion
= kCacheEntryVersion
;
867 mMetaHdr
.mFetchCount
= 0;
868 mMetaHdr
.mExpirationTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
869 mMetaHdr
.mKeySize
= mKey
.Length();
871 // Deliberately not touching the "kCacheEntryIsPinned" flag.
873 DoMemoryReport(MemoryUsage());
875 // We're creating a new entry. If there is any old data truncate it.
877 mHandle
->SetPinned(Pinned());
878 // We can pronounce the handle as invalid now, because it simply
879 // doesn't have the correct metadata. This will cause IO operations
880 // be bypassed during shutdown (mainly dooming it, when a channel
881 // is canceled by closing the window.)
882 mHandle
->SetInvalid();
883 if (mHandle
->FileExists() && mHandle
->FileSize()) {
884 CacheFileIOManager::TruncateSeekSetEOF(mHandle
, 0, 0, nullptr);
890 CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset
, uint32_t aBufOffset
,
893 LOG(("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, "
894 "bufOffset=%d, haveKey=%u]", this, aMetaOffset
, aBufOffset
, aHaveKey
));
898 uint32_t metaposOffset
= mBufSize
- sizeof(uint32_t);
899 uint32_t hashesOffset
= aBufOffset
+ sizeof(uint32_t);
900 uint32_t hashCount
= aMetaOffset
/ kChunkSize
;
901 if (aMetaOffset
% kChunkSize
)
903 uint32_t hashesLen
= hashCount
* sizeof(CacheHash::Hash16_t
);
904 uint32_t hdrOffset
= hashesOffset
+ hashesLen
;
905 uint32_t keyOffset
= hdrOffset
+ sizeof(CacheFileMetadataHeader
);
907 LOG(("CacheFileMetadata::ParseMetadata() [this=%p]\n metaposOffset=%d\n "
908 "hashesOffset=%d\n hashCount=%d\n hashesLen=%d\n hdfOffset=%d\n "
909 "keyOffset=%d\n", this, metaposOffset
, hashesOffset
, hashCount
,
910 hashesLen
,hdrOffset
, keyOffset
));
912 if (keyOffset
> metaposOffset
) {
913 LOG(("CacheFileMetadata::ParseMetadata() - Wrong keyOffset! [this=%p]",
915 return NS_ERROR_FILE_CORRUPTED
;
918 mMetaHdr
.ReadFromBuf(mBuf
+ hdrOffset
);
920 if (mMetaHdr
.mVersion
== 1) {
921 // Backward compatibility before we've added flags to the header
922 keyOffset
-= sizeof(uint32_t);
923 } else if (mMetaHdr
.mVersion
== 2) {
924 // Version 2 just lacks the ability to store alternative data. Nothing to do
926 } else if (mMetaHdr
.mVersion
!= kCacheEntryVersion
) {
927 LOG(("CacheFileMetadata::ParseMetadata() - Not a version we understand to. "
928 "[version=0x%x, this=%p]", mMetaHdr
.mVersion
, this));
929 return NS_ERROR_UNEXPECTED
;
932 // Update the version stored in the header to make writes
933 // store the header in the current version form.
934 mMetaHdr
.mVersion
= kCacheEntryVersion
;
936 uint32_t elementsOffset
= mMetaHdr
.mKeySize
+ keyOffset
+ 1;
938 if (elementsOffset
> metaposOffset
) {
939 LOG(("CacheFileMetadata::ParseMetadata() - Wrong elementsOffset %d "
940 "[this=%p]", elementsOffset
, this));
941 return NS_ERROR_FILE_CORRUPTED
;
944 // check that key ends with \0
945 if (mBuf
[elementsOffset
- 1] != 0) {
946 LOG(("CacheFileMetadata::ParseMetadata() - Elements not null terminated. "
948 return NS_ERROR_FILE_CORRUPTED
;
953 // get the key form metadata
954 mKey
.Assign(mBuf
+ keyOffset
, mMetaHdr
.mKeySize
);
961 if (mMetaHdr
.mKeySize
!= mKey
.Length()) {
962 LOG(("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s "
963 "[this=%p]", nsCString(mBuf
+ keyOffset
, mMetaHdr
.mKeySize
).get(),
965 return NS_ERROR_FILE_CORRUPTED
;
968 if (memcmp(mKey
.get(), mBuf
+ keyOffset
, mKey
.Length()) != 0) {
969 LOG(("CacheFileMetadata::ParseMetadata() - Key collision (2), key=%s "
970 "[this=%p]", nsCString(mBuf
+ keyOffset
, mMetaHdr
.mKeySize
).get(),
972 return NS_ERROR_FILE_CORRUPTED
;
976 // check metadata hash (data from hashesOffset to metaposOffset)
977 CacheHash::Hash32_t hashComputed
, hashExpected
;
978 hashComputed
= CacheHash::Hash(mBuf
+ hashesOffset
,
979 metaposOffset
- hashesOffset
);
980 hashExpected
= NetworkEndian::readUint32(mBuf
+ aBufOffset
);
982 if (hashComputed
!= hashExpected
) {
983 LOG(("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of "
984 "the metadata is %x, hash in file is %x [this=%p]", hashComputed
,
985 hashExpected
, this));
986 return NS_ERROR_FILE_CORRUPTED
;
990 rv
= CheckElements(mBuf
+ elementsOffset
, metaposOffset
- elementsOffset
);
995 if (!mHandle
->SetPinned(Pinned())) {
996 LOG(("CacheFileMetadata::ParseMetadata() - handle was doomed for this "
997 "pinning state, truncate the file [this=%p, pinned=%d]", this, Pinned()));
998 return NS_ERROR_FILE_CORRUPTED
;
1002 mHashArraySize
= hashesLen
;
1003 mHashCount
= hashCount
;
1004 if (mHashArraySize
) {
1005 mHashArray
= static_cast<CacheHash::Hash16_t
*>(
1006 moz_xmalloc(mHashArraySize
));
1007 memcpy(mHashArray
, mBuf
+ hashesOffset
, mHashArraySize
);
1012 mElementsSize
= metaposOffset
- elementsOffset
;
1013 memmove(mBuf
, mBuf
+ elementsOffset
, mElementsSize
);
1014 mOffset
= aMetaOffset
;
1016 DoMemoryReport(MemoryUsage());
1022 CacheFileMetadata::CheckElements(const char *aBuf
, uint32_t aSize
)
1025 // Check if the metadata ends with a zero byte.
1026 if (aBuf
[aSize
- 1] != 0) {
1027 NS_ERROR("Metadata elements are not null terminated");
1028 LOG(("CacheFileMetadata::CheckElements() - Elements are not null "
1029 "terminated. [this=%p]", this));
1030 return NS_ERROR_FILE_CORRUPTED
;
1032 // Check that there are an even number of zero bytes
1033 // to match the pattern { key \0 value \0 }
1035 for (uint32_t i
= 0; i
< aSize
; i
++) {
1040 NS_ERROR("Metadata elements are malformed");
1041 LOG(("CacheFileMetadata::CheckElements() - Elements are malformed. "
1042 "[this=%p]", this));
1043 return NS_ERROR_FILE_CORRUPTED
;
1050 CacheFileMetadata::EnsureBuffer(uint32_t aSize
)
1052 if (aSize
> kMaxElementsSize
) {
1053 return NS_ERROR_FAILURE
;
1056 if (mBufSize
< aSize
) {
1057 if (mAllocExactSize
) {
1058 // If this is not the only allocation, use power of two for following
1060 mAllocExactSize
= false;
1062 // find smallest power of 2 greater than or equal to aSize
1064 aSize
|= aSize
>> 1;
1065 aSize
|= aSize
>> 2;
1066 aSize
|= aSize
>> 4;
1067 aSize
|= aSize
>> 8;
1068 aSize
|= aSize
>> 16;
1072 if (aSize
< kInitialBufSize
) {
1073 aSize
= kInitialBufSize
;
1076 char *newBuf
= static_cast<char *>(realloc(mBuf
, aSize
));
1078 return NS_ERROR_OUT_OF_MEMORY
;
1083 DoMemoryReport(MemoryUsage());
1090 CacheFileMetadata::ParseKey(const nsACString
&aKey
)
1092 nsCOMPtr
<nsILoadContextInfo
> info
= CacheFileUtils::ParseKey(aKey
);
1093 NS_ENSURE_TRUE(info
, NS_ERROR_FAILURE
);
1095 mAnonymous
= info
->IsAnonymous();
1096 mOriginAttributes
= *info
->OriginAttributesPtr();
1104 CacheFileMetadata::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
1107 // mHandle reported via CacheFileIOManager.
1108 n
+= mKey
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
1109 n
+= mallocSizeOf(mHashArray
);
1110 n
+= mallocSizeOf(mBuf
);
1111 // Ignore mWriteBuf, it's not safe to access it when metadata is being
1112 // written and it's null otherwise.
1113 // mListener is usually the owning CacheFile.
1119 CacheFileMetadata::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
1121 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);
1125 } // namespace mozilla