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
16 #include "mozilla/ScopeExit.h"
17 #include "mozilla/Telemetry.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/IntegerPrintfMacros.h"
22 namespace mozilla::net
{
24 #define kMinMetadataRead 1024 // TODO find optimal value from telemetry
25 #define kAlignSize 4096
27 // Most of the cache entries fit into one chunk due to current chunk size. Make
28 // sure to tweak this value if kChunkSize is going to change.
29 #define kInitialHashArraySize 1
31 // Initial elements buffer size.
32 #define kInitialBufSize 64
34 // Max size of elements in bytes.
35 #define kMaxElementsSize (64 * 1024)
37 #define NOW_SECONDS() (uint32_t(PR_Now() / PR_USEC_PER_SEC))
39 NS_IMPL_ISUPPORTS(CacheFileMetadata
, CacheFileIOListener
)
41 CacheFileMetadata::CacheFileMetadata(
42 CacheFileHandle
* aHandle
, const nsACString
& aKey
,
43 NotNull
<CacheFileUtils::CacheFileLock
*> aLock
)
44 : CacheMemoryConsumer(NORMAL
),
49 mAllocExactSize(false),
52 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, handle=%p, key=%s]",
53 this, aHandle
, PromiseFlatCString(aKey
).get()));
55 memset(&mMetaHdr
, 0, sizeof(CacheFileMetadataHeader
));
56 mMetaHdr
.mVersion
= kCacheEntryVersion
;
57 mMetaHdr
.mExpirationTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
60 DebugOnly
<nsresult
> rv
{};
62 MOZ_ASSERT(NS_SUCCEEDED(rv
));
65 CacheFileMetadata::CacheFileMetadata(
66 bool aMemoryOnly
, bool aPinned
, const nsACString
& aKey
,
67 NotNull
<CacheFileUtils::CacheFileLock
*> aLock
)
68 : CacheMemoryConsumer(aMemoryOnly
? MEMORY_ONLY
: NORMAL
),
71 mAllocExactSize(false),
74 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, key=%s]", this,
75 PromiseFlatCString(aKey
).get()));
77 memset(&mMetaHdr
, 0, sizeof(CacheFileMetadataHeader
));
78 mMetaHdr
.mVersion
= kCacheEntryVersion
;
80 AddFlags(kCacheEntryIsPinned
);
82 mMetaHdr
.mExpirationTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
84 mMetaHdr
.mKeySize
= mKey
.Length();
86 DebugOnly
<nsresult
> rv
{};
88 MOZ_ASSERT(NS_SUCCEEDED(rv
));
91 CacheFileMetadata::CacheFileMetadata()
92 : CacheMemoryConsumer(DONT_REPORT
/* This is a helper class */),
95 mAllocExactSize(false),
97 mLock(new CacheFileUtils::CacheFileLock()) {
98 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p]", this));
100 memset(&mMetaHdr
, 0, sizeof(CacheFileMetadataHeader
));
103 CacheFileMetadata::~CacheFileMetadata() {
104 LOG(("CacheFileMetadata::~CacheFileMetadata() [this=%p]", this));
106 MOZ_ASSERT(!mListener
);
109 CacheFileUtils::FreeBuffer(mHashArray
);
110 mHashArray
= nullptr;
115 CacheFileUtils::FreeBuffer(mBuf
);
121 void CacheFileMetadata::SetHandle(CacheFileHandle
* aHandle
) {
122 LOG(("CacheFileMetadata::SetHandle() [this=%p, handle=%p]", this, aHandle
));
124 MOZ_ASSERT(!mHandle
);
129 void CacheFileMetadata::ReadMetadata(CacheFileMetadataListener
* aListener
) {
130 LOG(("CacheFileMetadata::ReadMetadata() [this=%p, listener=%p]", this,
133 MOZ_ASSERT(!mListener
);
134 MOZ_ASSERT(!mHashArray
);
136 MOZ_ASSERT(!mWriteBuf
);
140 int64_t size
= mHandle
->FileSize();
141 MOZ_ASSERT(size
!= -1);
144 // this is a new entry
146 ("CacheFileMetadata::ReadMetadata() - Filesize == 0, creating empty "
147 "metadata. [this=%p]",
151 aListener
->OnMetadataRead(NS_OK
);
155 if (size
< int64_t(sizeof(CacheFileMetadataHeader
) + 2 * sizeof(uint32_t))) {
156 // there must be at least checksum, header and offset
158 ("CacheFileMetadata::ReadMetadata() - File is corrupted, creating "
159 "empty metadata. [this=%p, filesize=%" PRId64
"]",
163 aListener
->OnMetadataRead(NS_OK
);
167 // Set offset so that we read at least kMinMetadataRead if the file is big
170 if (size
< kMinMetadataRead
) {
173 offset
= size
- kMinMetadataRead
;
176 // round offset to kAlignSize blocks
177 offset
= (offset
/ kAlignSize
) * kAlignSize
;
179 mBufSize
= size
- offset
;
180 mBuf
= static_cast<char*>(moz_xmalloc(mBufSize
));
182 DoMemoryReport(MemoryUsage());
185 ("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying "
186 "offset=%" PRId64
", filesize=%" PRId64
" [this=%p]",
187 offset
, size
, this));
189 mReadStart
= mozilla::TimeStamp::Now();
190 mListener
= aListener
;
191 rv
= CacheFileIOManager::Read(mHandle
, offset
, mBuf
, mBufSize
, this);
194 ("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed"
195 " synchronously, creating empty metadata. [this=%p, rv=0x%08" PRIx32
197 this, static_cast<uint32_t>(rv
)));
201 aListener
->OnMetadataRead(NS_OK
);
205 uint32_t CacheFileMetadata::CalcMetadataSize(uint32_t aElementsSize
,
206 uint32_t aHashCount
) {
207 return sizeof(uint32_t) + // hash of the metadata
208 aHashCount
* sizeof(CacheHash::Hash16_t
) + // array of chunk hashes
209 sizeof(CacheFileMetadataHeader
) + // metadata header
210 mKey
.Length() + 1 + // key with trailing null
211 aElementsSize
+ // elements
212 sizeof(uint32_t); // offset
215 nsresult
CacheFileMetadata::WriteMetadata(
216 uint32_t aOffset
, CacheFileMetadataListener
* aListener
) {
217 LOG(("CacheFileMetadata::WriteMetadata() [this=%p, offset=%d, listener=%p]",
218 this, aOffset
, aListener
));
220 MOZ_ASSERT(!mListener
);
221 MOZ_ASSERT(!mWriteBuf
);
228 static_cast<char*>(malloc(CalcMetadataSize(mElementsSize
, mHashCount
)));
230 return NS_ERROR_OUT_OF_MEMORY
;
233 char* p
= mWriteBuf
+ sizeof(uint32_t);
235 memcpy(p
, mHashArray
, mHashCount
* sizeof(CacheHash::Hash16_t
));
236 p
+= mHashCount
* sizeof(CacheHash::Hash16_t
);
238 mMetaHdr
.WriteToBuf(p
);
239 p
+= sizeof(CacheFileMetadataHeader
);
240 memcpy(p
, mKey
.get(), mKey
.Length());
245 memcpy(p
, mBuf
, mElementsSize
);
249 CacheHash::Hash32_t hash
;
250 hash
= CacheHash::Hash(mWriteBuf
+ sizeof(uint32_t),
251 p
- mWriteBuf
- sizeof(uint32_t));
252 NetworkEndian::writeUint32(mWriteBuf
, hash
);
254 NetworkEndian::writeUint32(p
, aOffset
);
255 p
+= sizeof(uint32_t);
257 char* writeBuffer
= mWriteBuf
;
259 mListener
= aListener
;
261 // We are not going to pass |this| as a callback so the buffer will be
262 // released by CacheFileIOManager. Just null out mWriteBuf here.
266 rv
= CacheFileIOManager::Write(mHandle
, aOffset
, writeBuffer
, p
- writeBuffer
,
267 true, true, aListener
? this : nullptr);
270 ("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() "
271 "failed synchronously. [this=%p, rv=0x%08" PRIx32
"]",
272 this, static_cast<uint32_t>(rv
)));
276 CacheFileUtils::FreeBuffer(mWriteBuf
);
279 NS_ENSURE_SUCCESS(rv
, rv
);
282 DoMemoryReport(MemoryUsage());
287 nsresult
CacheFileMetadata::SyncReadMetadata(nsIFile
* aFile
) {
288 LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this));
290 MOZ_ASSERT(!mListener
);
291 MOZ_ASSERT(!mHandle
);
292 MOZ_ASSERT(!mHashArray
);
294 MOZ_ASSERT(!mWriteBuf
);
295 MOZ_ASSERT(mKey
.IsEmpty());
300 rv
= aFile
->GetFileSize(&fileSize
);
302 // Don't bloat the console
307 rv
= aFile
->OpenNSPRFileDesc(PR_RDONLY
, 0600, &fd
);
308 NS_ENSURE_SUCCESS(rv
, rv
);
310 int64_t offset
= PR_Seek64(fd
, fileSize
- sizeof(uint32_t), PR_SEEK_SET
);
313 return NS_ERROR_FAILURE
;
317 int32_t bytesRead
= PR_Read(fd
, &metaOffset
, sizeof(uint32_t));
318 if (bytesRead
!= sizeof(uint32_t)) {
320 return NS_ERROR_FAILURE
;
323 metaOffset
= NetworkEndian::readUint32(&metaOffset
);
324 if (metaOffset
> fileSize
) {
326 return NS_ERROR_FAILURE
;
329 mBuf
= static_cast<char*>(malloc(fileSize
- metaOffset
));
331 return NS_ERROR_OUT_OF_MEMORY
;
333 mBufSize
= fileSize
- metaOffset
;
335 DoMemoryReport(MemoryUsage());
337 offset
= PR_Seek64(fd
, metaOffset
, PR_SEEK_SET
);
340 return NS_ERROR_FAILURE
;
343 bytesRead
= PR_Read(fd
, mBuf
, mBufSize
);
345 if (bytesRead
!= static_cast<int32_t>(mBufSize
)) {
346 return NS_ERROR_FAILURE
;
349 rv
= ParseMetadata(metaOffset
, 0, false);
350 NS_ENSURE_SUCCESS(rv
, rv
);
355 const char* CacheFileMetadata::GetElement(const char* aKey
) {
356 const char* data
= mBuf
;
357 const char* limit
= mBuf
+ mElementsSize
;
359 while (data
!= limit
) {
360 size_t maxLen
= limit
- data
;
361 size_t keyLen
= strnlen(data
, maxLen
);
362 MOZ_RELEASE_ASSERT(keyLen
!= maxLen
,
363 "Metadata elements corrupted. Key "
364 "isn't null terminated!");
365 MOZ_RELEASE_ASSERT(keyLen
+ 1 != maxLen
,
366 "Metadata elements corrupted. "
367 "There is no value for the key!");
369 const char* value
= data
+ keyLen
+ 1;
370 maxLen
= limit
- value
;
371 size_t valueLen
= strnlen(value
, maxLen
);
372 MOZ_RELEASE_ASSERT(valueLen
!= maxLen
,
373 "Metadata elements corrupted. Value "
374 "isn't null terminated!");
376 if (strcmp(data
, aKey
) == 0) {
377 LOG(("CacheFileMetadata::GetElement() - Key found [this=%p, key=%s]",
382 // point to next pair
383 data
+= keyLen
+ valueLen
+ 2;
385 LOG(("CacheFileMetadata::GetElement() - Key not found [this=%p, key=%s]",
390 nsresult
CacheFileMetadata::SetElement(const char* aKey
, const char* aValue
) {
391 LOG(("CacheFileMetadata::SetElement() [this=%p, key=%s, value=%p]", this,
394 mLock
->Lock().AssertCurrentThreadOwns();
400 const uint32_t keySize
= strlen(aKey
) + 1;
401 char* pos
= const_cast<char*>(GetElement(aKey
));
404 // No value means remove the key/value pair completely, if existing
406 uint32_t oldValueSize
= strlen(pos
) + 1;
407 uint32_t offset
= pos
- mBuf
;
408 uint32_t remainder
= mElementsSize
- (offset
+ oldValueSize
);
410 memmove(pos
- keySize
, pos
+ oldValueSize
, remainder
);
411 mElementsSize
-= keySize
+ oldValueSize
;
416 const uint32_t valueSize
= strlen(aValue
) + 1;
417 uint32_t newSize
= mElementsSize
+ valueSize
;
419 const uint32_t oldValueSize
= strlen(pos
) + 1;
420 const uint32_t offset
= pos
- mBuf
;
421 const uint32_t remainder
= mElementsSize
- (offset
+ oldValueSize
);
423 // Update the value in place
424 newSize
-= oldValueSize
;
425 rv
= EnsureBuffer(newSize
);
430 // Move the remainder to the right place
432 memmove(pos
+ valueSize
, pos
+ oldValueSize
, remainder
);
434 // allocate new meta data element
436 rv
= EnsureBuffer(newSize
);
441 // Add after last element
442 pos
= mBuf
+ mElementsSize
;
443 memcpy(pos
, aKey
, keySize
);
448 memcpy(pos
, aValue
, valueSize
);
449 mElementsSize
= newSize
;
454 void CacheFileMetadata::Visit(nsICacheEntryMetaDataVisitor
* aVisitor
) {
455 const char* data
= mBuf
;
456 const char* limit
= mBuf
+ mElementsSize
;
458 while (data
< limit
) {
459 // Point to the value part
460 const char* value
= data
+ strlen(data
) + 1;
461 MOZ_ASSERT(value
< limit
, "Metadata elements corrupted");
463 aVisitor
->OnMetaDataElement(data
, value
);
466 data
= value
+ strlen(value
) + 1;
469 MOZ_ASSERT(data
== limit
, "Metadata elements corrupted");
472 CacheHash::Hash16_t
CacheFileMetadata::GetHash(uint32_t aIndex
) {
473 mLock
->Lock().AssertCurrentThreadOwns();
475 MOZ_ASSERT(aIndex
< mHashCount
);
476 return NetworkEndian::readUint16(&mHashArray
[aIndex
]);
479 nsresult
CacheFileMetadata::SetHash(uint32_t aIndex
,
480 CacheHash::Hash16_t aHash
) {
481 LOG(("CacheFileMetadata::SetHash() [this=%p, idx=%d, hash=%x]", this, aIndex
,
484 mLock
->Lock().AssertCurrentThreadOwns();
488 MOZ_ASSERT(aIndex
<= mHashCount
);
490 if (aIndex
> mHashCount
) {
491 return NS_ERROR_INVALID_ARG
;
493 if (aIndex
== mHashCount
) {
494 if ((aIndex
+ 1) * sizeof(CacheHash::Hash16_t
) > mHashArraySize
) {
495 // reallocate hash array buffer
496 if (mHashArraySize
== 0) {
497 mHashArraySize
= kInitialHashArraySize
* sizeof(CacheHash::Hash16_t
);
501 mHashArray
= static_cast<CacheHash::Hash16_t
*>(
502 moz_xrealloc(mHashArray
, mHashArraySize
));
508 NetworkEndian::writeUint16(&mHashArray
[aIndex
], aHash
);
510 DoMemoryReport(MemoryUsage());
515 nsresult
CacheFileMetadata::RemoveHash(uint32_t aIndex
) {
516 LOG(("CacheFileMetadata::RemoveHash() [this=%p, idx=%d]", this, aIndex
));
518 mLock
->Lock().AssertCurrentThreadOwns();
522 MOZ_ASSERT((aIndex
+ 1) == mHashCount
, "Can remove only last hash!");
524 if (aIndex
+ 1 != mHashCount
) {
525 return NS_ERROR_INVALID_ARG
;
532 void CacheFileMetadata::AddFlags(uint32_t aFlags
) {
534 mMetaHdr
.mFlags
|= aFlags
;
537 void CacheFileMetadata::RemoveFlags(uint32_t aFlags
) {
539 mMetaHdr
.mFlags
&= ~aFlags
;
542 void CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime
) {
543 LOG(("CacheFileMetadata::SetExpirationTime() [this=%p, expirationTime=%d]",
544 this, aExpirationTime
));
547 mMetaHdr
.mExpirationTime
= aExpirationTime
;
550 void CacheFileMetadata::SetFrecency(uint32_t aFrecency
) {
551 LOG(("CacheFileMetadata::SetFrecency() [this=%p, frecency=%f]", this,
555 mMetaHdr
.mFrecency
= aFrecency
;
558 void CacheFileMetadata::OnFetched() {
561 mMetaHdr
.mLastFetched
= NOW_SECONDS();
562 ++mMetaHdr
.mFetchCount
;
565 void CacheFileMetadata::MarkDirty(bool aUpdateLastModified
) {
567 if (aUpdateLastModified
) {
568 mMetaHdr
.mLastModified
= NOW_SECONDS();
572 nsresult
CacheFileMetadata::OnFileOpened(CacheFileHandle
* aHandle
,
574 MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!");
575 return NS_ERROR_UNEXPECTED
;
578 nsresult
CacheFileMetadata::OnDataWritten(CacheFileHandle
* aHandle
,
579 const char* aBuf
, nsresult aResult
) {
581 ("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, "
582 "result=0x%08" PRIx32
"]",
583 this, aHandle
, static_cast<uint32_t>(aResult
)));
585 nsCOMPtr
<CacheFileMetadataListener
> listener
;
587 MutexAutoLock
lock(mLock
->Lock());
589 MOZ_ASSERT(mListener
);
590 MOZ_ASSERT(mWriteBuf
);
592 CacheFileUtils::FreeBuffer(mWriteBuf
);
595 mListener
.swap(listener
);
596 DoMemoryReport(MemoryUsage());
599 listener
->OnMetadataWritten(aResult
);
604 nsresult
CacheFileMetadata::OnDataRead(CacheFileHandle
* aHandle
, char* aBuf
,
607 "CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08" PRIx32
609 this, aHandle
, static_cast<uint32_t>(aResult
)));
611 MOZ_ASSERT(mListener
);
614 nsCOMPtr
<CacheFileMetadataListener
> listener
;
616 auto notifyListenerOutsideLock
= mozilla::MakeScopeExit([&listener
] {
618 listener
->OnMetadataRead(NS_OK
);
622 MutexAutoLock
lock(mLock
->Lock());
624 if (NS_FAILED(aResult
)) {
626 ("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed"
627 ", creating empty metadata. [this=%p, rv=0x%08" PRIx32
"]",
628 this, static_cast<uint32_t>(aResult
)));
632 mListener
.swap(listener
);
637 Telemetry::AccumulateTimeDelta(
638 Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_TIME_MS
, mReadStart
);
640 Telemetry::AccumulateTimeDelta(
641 Telemetry::NETWORK_CACHE_METADATA_SECOND_READ_TIME_MS
, mReadStart
);
644 // check whether we have read all necessary data
645 uint32_t realOffset
=
646 NetworkEndian::readUint32(mBuf
+ mBufSize
- sizeof(uint32_t));
648 int64_t size
= mHandle
->FileSize();
649 MOZ_ASSERT(size
!= -1);
651 if (realOffset
>= size
) {
653 ("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating "
654 "empty metadata. [this=%p, realOffset=%u, size=%" PRId64
"]",
655 this, realOffset
, size
));
659 mListener
.swap(listener
);
663 uint32_t maxHashCount
= size
/ kChunkSize
;
664 uint32_t maxMetadataSize
= CalcMetadataSize(kMaxElementsSize
, maxHashCount
);
665 if (size
- realOffset
> maxMetadataSize
) {
667 ("CacheFileMetadata::OnDataRead() - Invalid realOffset, metadata would "
668 "be too big, creating empty metadata. [this=%p, realOffset=%u, "
669 "maxMetadataSize=%u, size=%" PRId64
"]",
670 this, realOffset
, maxMetadataSize
, size
));
674 mListener
.swap(listener
);
678 uint32_t usedOffset
= size
- mBufSize
;
680 if (realOffset
< usedOffset
) {
681 uint32_t missing
= usedOffset
- realOffset
;
682 // we need to read more data
683 char* newBuf
= static_cast<char*>(realloc(mBuf
, mBufSize
+ missing
));
686 ("CacheFileMetadata::OnDataRead() - Error allocating %d more bytes "
687 "for the missing part of the metadata, creating empty metadata. "
693 mListener
.swap(listener
);
698 memmove(mBuf
+ missing
, mBuf
, mBufSize
);
701 DoMemoryReport(MemoryUsage());
704 ("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to "
705 "have full metadata. [this=%p]",
709 mReadStart
= mozilla::TimeStamp::Now();
710 rv
= CacheFileIOManager::Read(mHandle
, realOffset
, mBuf
, missing
, this);
713 ("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() "
714 "failed synchronously, creating empty metadata. [this=%p, "
715 "rv=0x%08" PRIx32
"]",
716 this, static_cast<uint32_t>(rv
)));
720 mListener
.swap(listener
);
727 Telemetry::Accumulate(Telemetry::NETWORK_CACHE_METADATA_SIZE_2
,
730 // We have all data according to offset information at the end of the entry.
732 rv
= ParseMetadata(realOffset
, realOffset
- usedOffset
, true);
735 ("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating "
736 "empty metadata. [this=%p]",
740 // Shrink elements buffer.
741 mBuf
= static_cast<char*>(moz_xrealloc(mBuf
, mElementsSize
));
742 mBufSize
= mElementsSize
;
744 // There is usually no or just one call to SetMetadataElement() when the
745 // metadata is parsed from disk. Avoid allocating power of two sized buffer
746 // which we do in case of newly created metadata.
747 mAllocExactSize
= true;
750 mListener
.swap(listener
);
755 nsresult
CacheFileMetadata::OnFileDoomed(CacheFileHandle
* aHandle
,
757 MOZ_CRASH("CacheFileMetadata::OnFileDoomed should not be called!");
758 return NS_ERROR_UNEXPECTED
;
761 nsresult
CacheFileMetadata::OnEOFSet(CacheFileHandle
* aHandle
,
763 MOZ_CRASH("CacheFileMetadata::OnEOFSet should not be called!");
764 return NS_ERROR_UNEXPECTED
;
767 nsresult
CacheFileMetadata::OnFileRenamed(CacheFileHandle
* aHandle
,
769 MOZ_CRASH("CacheFileMetadata::OnFileRenamed should not be called!");
770 return NS_ERROR_UNEXPECTED
;
773 void CacheFileMetadata::InitEmptyMetadata() {
775 CacheFileUtils::FreeBuffer(mBuf
);
779 mAllocExactSize
= false;
781 mMetaHdr
.mVersion
= kCacheEntryVersion
;
782 mMetaHdr
.mFetchCount
= 0;
783 mMetaHdr
.mExpirationTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
784 mMetaHdr
.mKeySize
= mKey
.Length();
786 // Deliberately not touching the "kCacheEntryIsPinned" flag.
788 DoMemoryReport(MemoryUsage());
790 // We're creating a new entry. If there is any old data truncate it.
792 mHandle
->SetPinned(Pinned());
793 // We can pronounce the handle as invalid now, because it simply
794 // doesn't have the correct metadata. This will cause IO operations
795 // be bypassed during shutdown (mainly dooming it, when a channel
796 // is canceled by closing the window.)
797 mHandle
->SetInvalid();
798 if (mHandle
->FileExists() && mHandle
->FileSize()) {
799 CacheFileIOManager::TruncateSeekSetEOF(mHandle
, 0, 0, nullptr);
804 nsresult
CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset
,
805 uint32_t aBufOffset
, bool aHaveKey
) {
807 ("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, "
808 "bufOffset=%d, haveKey=%u]",
809 this, aMetaOffset
, aBufOffset
, aHaveKey
));
813 uint32_t metaposOffset
= mBufSize
- sizeof(uint32_t);
814 uint32_t hashesOffset
= aBufOffset
+ sizeof(uint32_t);
815 uint32_t hashCount
= aMetaOffset
/ kChunkSize
;
816 if (aMetaOffset
% kChunkSize
) hashCount
++;
817 uint32_t hashesLen
= hashCount
* sizeof(CacheHash::Hash16_t
);
818 uint32_t hdrOffset
= hashesOffset
+ hashesLen
;
819 uint32_t keyOffset
= hdrOffset
+ sizeof(CacheFileMetadataHeader
);
822 ("CacheFileMetadata::ParseMetadata() [this=%p]\n metaposOffset=%d\n "
823 "hashesOffset=%d\n hashCount=%d\n hashesLen=%d\n hdfOffset=%d\n "
825 this, metaposOffset
, hashesOffset
, hashCount
, hashesLen
, hdrOffset
,
828 if (keyOffset
> metaposOffset
) {
829 LOG(("CacheFileMetadata::ParseMetadata() - Wrong keyOffset! [this=%p]",
831 return NS_ERROR_FILE_CORRUPTED
;
834 mMetaHdr
.ReadFromBuf(mBuf
+ hdrOffset
);
836 if (mMetaHdr
.mVersion
== 1) {
837 // Backward compatibility before we've added flags to the header
838 keyOffset
-= sizeof(uint32_t);
839 } else if (mMetaHdr
.mVersion
== 2) {
840 // Version 2 just lacks the ability to store alternative data. Nothing to do
842 } else if (mMetaHdr
.mVersion
!= kCacheEntryVersion
) {
844 ("CacheFileMetadata::ParseMetadata() - Not a version we understand to. "
845 "[version=0x%x, this=%p]",
846 mMetaHdr
.mVersion
, this));
847 return NS_ERROR_UNEXPECTED
;
850 // Update the version stored in the header to make writes
851 // store the header in the current version form.
852 mMetaHdr
.mVersion
= kCacheEntryVersion
;
854 uint32_t elementsOffset
= mMetaHdr
.mKeySize
+ keyOffset
+ 1;
856 if (elementsOffset
> metaposOffset
) {
858 ("CacheFileMetadata::ParseMetadata() - Wrong elementsOffset %d "
860 elementsOffset
, this));
861 return NS_ERROR_FILE_CORRUPTED
;
864 // check that key ends with \0
865 if (mBuf
[elementsOffset
- 1] != 0) {
867 ("CacheFileMetadata::ParseMetadata() - Elements not null terminated. "
870 return NS_ERROR_FILE_CORRUPTED
;
874 // get the key form metadata
875 mKey
.Assign(mBuf
+ keyOffset
, mMetaHdr
.mKeySize
);
878 if (NS_FAILED(rv
)) return rv
;
880 if (mMetaHdr
.mKeySize
!= mKey
.Length()) {
882 ("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s "
884 nsCString(mBuf
+ keyOffset
, mMetaHdr
.mKeySize
).get(), this));
885 return NS_ERROR_FILE_CORRUPTED
;
888 if (memcmp(mKey
.get(), mBuf
+ keyOffset
, mKey
.Length()) != 0) {
890 ("CacheFileMetadata::ParseMetadata() - Key collision (2), key=%s "
892 nsCString(mBuf
+ keyOffset
, mMetaHdr
.mKeySize
).get(), this));
893 return NS_ERROR_FILE_CORRUPTED
;
897 // check metadata hash (data from hashesOffset to metaposOffset)
898 CacheHash::Hash32_t hashComputed
, hashExpected
;
900 CacheHash::Hash(mBuf
+ hashesOffset
, metaposOffset
- hashesOffset
);
901 hashExpected
= NetworkEndian::readUint32(mBuf
+ aBufOffset
);
903 if (hashComputed
!= hashExpected
) {
905 ("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of "
906 "the metadata is %x, hash in file is %x [this=%p]",
907 hashComputed
, hashExpected
, this));
908 return NS_ERROR_FILE_CORRUPTED
;
912 rv
= CheckElements(mBuf
+ elementsOffset
, metaposOffset
- elementsOffset
);
913 if (NS_FAILED(rv
)) return rv
;
916 if (!mHandle
->SetPinned(Pinned())) {
918 ("CacheFileMetadata::ParseMetadata() - handle was doomed for this "
919 "pinning state, truncate the file [this=%p, pinned=%d]",
921 return NS_ERROR_FILE_CORRUPTED
;
925 mHashArraySize
= hashesLen
;
926 mHashCount
= hashCount
;
927 if (mHashArraySize
) {
928 mHashArray
= static_cast<CacheHash::Hash16_t
*>(moz_xmalloc(mHashArraySize
));
929 memcpy(mHashArray
, mBuf
+ hashesOffset
, mHashArraySize
);
934 mElementsSize
= metaposOffset
- elementsOffset
;
935 memmove(mBuf
, mBuf
+ elementsOffset
, mElementsSize
);
936 mOffset
= aMetaOffset
;
938 DoMemoryReport(MemoryUsage());
943 nsresult
CacheFileMetadata::CheckElements(const char* aBuf
, uint32_t aSize
) {
945 // Check if the metadata ends with a zero byte.
946 if (aBuf
[aSize
- 1] != 0) {
947 NS_ERROR("Metadata elements are not null terminated");
949 ("CacheFileMetadata::CheckElements() - Elements are not null "
950 "terminated. [this=%p]",
952 return NS_ERROR_FILE_CORRUPTED
;
954 // Check that there are an even number of zero bytes
955 // to match the pattern { key \0 value \0 }
957 for (uint32_t i
= 0; i
< aSize
; i
++) {
958 if (aBuf
[i
] == 0) odd
= !odd
;
961 NS_ERROR("Metadata elements are malformed");
963 ("CacheFileMetadata::CheckElements() - Elements are malformed. "
966 return NS_ERROR_FILE_CORRUPTED
;
972 nsresult
CacheFileMetadata::EnsureBuffer(uint32_t aSize
) {
973 if (aSize
> kMaxElementsSize
) {
974 return NS_ERROR_FAILURE
;
977 if (mBufSize
< aSize
) {
978 if (mAllocExactSize
) {
979 // If this is not the only allocation, use power of two for following
981 mAllocExactSize
= false;
983 // find smallest power of 2 greater than or equal to aSize
989 aSize
|= aSize
>> 16;
993 if (aSize
< kInitialBufSize
) {
994 aSize
= kInitialBufSize
;
997 char* newBuf
= static_cast<char*>(realloc(mBuf
, aSize
));
999 return NS_ERROR_OUT_OF_MEMORY
;
1004 DoMemoryReport(MemoryUsage());
1010 nsresult
CacheFileMetadata::ParseKey(const nsACString
& aKey
) {
1011 nsCOMPtr
<nsILoadContextInfo
> info
= CacheFileUtils::ParseKey(aKey
);
1012 NS_ENSURE_TRUE(info
, NS_ERROR_FAILURE
);
1014 mAnonymous
= info
->IsAnonymous();
1015 mOriginAttributes
= *info
->OriginAttributesPtr();
1022 size_t CacheFileMetadata::SizeOfExcludingThis(
1023 mozilla::MallocSizeOf mallocSizeOf
) const {
1025 // mHandle reported via CacheFileIOManager.
1026 n
+= mKey
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
1027 n
+= mallocSizeOf(mHashArray
);
1028 n
+= mallocSizeOf(mBuf
);
1029 // Ignore mWriteBuf, it's not safe to access it when metadata is being
1030 // written and it's null otherwise.
1031 // mListener is usually the owning CacheFile.
1036 size_t CacheFileMetadata::SizeOfIncludingThis(
1037 mozilla::MallocSizeOf mallocSizeOf
) const {
1038 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);
1041 } // namespace mozilla::net