forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1125 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1125 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "CacheLog.h"
 | |
| #include "CacheFileMetadata.h"
 | |
| 
 | |
| #include "CacheFileIOManager.h"
 | |
| #include "nsICacheEntry.h"
 | |
| #include "CacheHashUtils.h"
 | |
| #include "CacheFileChunk.h"
 | |
| #include "CacheFileUtils.h"
 | |
| #include "nsILoadContextInfo.h"
 | |
| #include "nsICacheEntry.h" // for nsICacheEntryMetaDataVisitor
 | |
| #include "../cache/nsCacheUtils.h"
 | |
| #include "nsIFile.h"
 | |
| #include "mozilla/Telemetry.h"
 | |
| #include "mozilla/DebugOnly.h"
 | |
| #include "mozilla/IntegerPrintfMacros.h"
 | |
| #include "prnetdb.h"
 | |
| 
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace net {
 | |
| 
 | |
| #define kMinMetadataRead 1024  // TODO find optimal value from telemetry
 | |
| #define kAlignSize       4096
 | |
| 
 | |
| // Most of the cache entries fit into one chunk due to current chunk size. Make
 | |
| // sure to tweak this value if kChunkSize is going to change.
 | |
| #define kInitialHashArraySize 1
 | |
| 
 | |
| // Initial elements buffer size.
 | |
| #define kInitialBufSize 64
 | |
| 
 | |
| // Max size of elements in bytes.
 | |
| #define kMaxElementsSize 64*1024
 | |
| 
 | |
| #define NOW_SECONDS() (uint32_t(PR_Now() / PR_USEC_PER_SEC))
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(CacheFileMetadata, CacheFileIOListener)
 | |
| 
 | |
| CacheFileMetadata::CacheFileMetadata(CacheFileHandle *aHandle, const nsACString &aKey)
 | |
|   : CacheMemoryConsumer(NORMAL)
 | |
|   , mHandle(aHandle)
 | |
|   , mHashArray(nullptr)
 | |
|   , mHashArraySize(0)
 | |
|   , mHashCount(0)
 | |
|   , mOffset(-1)
 | |
|   , mBuf(nullptr)
 | |
|   , mBufSize(0)
 | |
|   , mWriteBuf(nullptr)
 | |
|   , mElementsSize(0)
 | |
|   , mIsDirty(false)
 | |
|   , mAnonymous(false)
 | |
|   , mAllocExactSize(false)
 | |
|   , mFirstRead(true)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, handle=%p, key=%s]",
 | |
|        this, aHandle, PromiseFlatCString(aKey).get()));
 | |
| 
 | |
|   memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
 | |
|   mMetaHdr.mVersion = kCacheEntryVersion;
 | |
|   mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
 | |
|   mKey = aKey;
 | |
| 
 | |
|   DebugOnly<nsresult> rv;
 | |
|   rv = ParseKey(aKey);
 | |
|   MOZ_ASSERT(NS_SUCCEEDED(rv));
 | |
| }
 | |
| 
 | |
| CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly, bool aPinned, const nsACString &aKey)
 | |
|   : CacheMemoryConsumer(aMemoryOnly ? MEMORY_ONLY : NORMAL)
 | |
|   , mHandle(nullptr)
 | |
|   , mHashArray(nullptr)
 | |
|   , mHashArraySize(0)
 | |
|   , mHashCount(0)
 | |
|   , mOffset(0)
 | |
|   , mBuf(nullptr)
 | |
|   , mBufSize(0)
 | |
|   , mWriteBuf(nullptr)
 | |
|   , mElementsSize(0)
 | |
|   , mIsDirty(true)
 | |
|   , mAnonymous(false)
 | |
|   , mAllocExactSize(false)
 | |
|   , mFirstRead(true)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, key=%s]",
 | |
|        this, PromiseFlatCString(aKey).get()));
 | |
| 
 | |
|   memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
 | |
|   mMetaHdr.mVersion = kCacheEntryVersion;
 | |
|   if (aPinned) {
 | |
|     AddFlags(kCacheEntryIsPinned);
 | |
|   }
 | |
|   mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
 | |
|   mKey = aKey;
 | |
|   mMetaHdr.mKeySize = mKey.Length();
 | |
| 
 | |
|   DebugOnly<nsresult> rv;
 | |
|   rv = ParseKey(aKey);
 | |
|   MOZ_ASSERT(NS_SUCCEEDED(rv));
 | |
| }
 | |
| 
 | |
| CacheFileMetadata::CacheFileMetadata()
 | |
|   : CacheMemoryConsumer(DONT_REPORT /* This is a helper class */)
 | |
|   , mHandle(nullptr)
 | |
|   , mHashArray(nullptr)
 | |
|   , mHashArraySize(0)
 | |
|   , mHashCount(0)
 | |
|   , mOffset(0)
 | |
|   , mBuf(nullptr)
 | |
|   , mBufSize(0)
 | |
|   , mWriteBuf(nullptr)
 | |
|   , mElementsSize(0)
 | |
|   , mIsDirty(false)
 | |
|   , mAnonymous(false)
 | |
|   , mAllocExactSize(false)
 | |
|   , mFirstRead(true)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p]", this));
 | |
| 
 | |
|   memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
 | |
| }
 | |
| 
 | |
| CacheFileMetadata::~CacheFileMetadata()
 | |
| {
 | |
|   LOG(("CacheFileMetadata::~CacheFileMetadata() [this=%p]", this));
 | |
| 
 | |
|   MOZ_ASSERT(!mListener);
 | |
| 
 | |
|   if (mHashArray) {
 | |
|     CacheFileUtils::FreeBuffer(mHashArray);
 | |
|     mHashArray = nullptr;
 | |
|     mHashArraySize = 0;
 | |
|   }
 | |
| 
 | |
|   if (mBuf) {
 | |
|     CacheFileUtils::FreeBuffer(mBuf);
 | |
|     mBuf = nullptr;
 | |
|     mBufSize = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| CacheFileMetadata::SetHandle(CacheFileHandle *aHandle)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::SetHandle() [this=%p, handle=%p]", this, aHandle));
 | |
| 
 | |
|   MOZ_ASSERT(!mHandle);
 | |
| 
 | |
|   mHandle = aHandle;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::GetKey(nsACString &_retval)
 | |
| {
 | |
|   _retval = mKey;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::ReadMetadata(CacheFileMetadataListener *aListener)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::ReadMetadata() [this=%p, listener=%p]", this, aListener));
 | |
| 
 | |
|   MOZ_ASSERT(!mListener);
 | |
|   MOZ_ASSERT(!mHashArray);
 | |
|   MOZ_ASSERT(!mBuf);
 | |
|   MOZ_ASSERT(!mWriteBuf);
 | |
| 
 | |
|   nsresult rv;
 | |
| 
 | |
|   int64_t size = mHandle->FileSize();
 | |
|   MOZ_ASSERT(size != -1);
 | |
| 
 | |
|   if (size == 0) {
 | |
|     // this is a new entry
 | |
|     LOG(("CacheFileMetadata::ReadMetadata() - Filesize == 0, creating empty "
 | |
|          "metadata. [this=%p]", this));
 | |
| 
 | |
|     InitEmptyMetadata();
 | |
|     aListener->OnMetadataRead(NS_OK);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (size < int64_t(sizeof(CacheFileMetadataHeader) + 2*sizeof(uint32_t))) {
 | |
|     // there must be at least checksum, header and offset
 | |
|     LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating "
 | |
|          "empty metadata. [this=%p, filesize=%" PRId64 "]", this, size));
 | |
| 
 | |
|     InitEmptyMetadata();
 | |
|     aListener->OnMetadataRead(NS_OK);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // Set offset so that we read at least kMinMetadataRead if the file is big
 | |
|   // enough.
 | |
|   int64_t offset;
 | |
|   if (size < kMinMetadataRead) {
 | |
|     offset = 0;
 | |
|   } else {
 | |
|     offset = size - kMinMetadataRead;
 | |
|   }
 | |
| 
 | |
|   // round offset to kAlignSize blocks
 | |
|   offset = (offset / kAlignSize) * kAlignSize;
 | |
| 
 | |
|   mBufSize = size - offset;
 | |
|   mBuf = static_cast<char *>(moz_xmalloc(mBufSize));
 | |
| 
 | |
|   DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|   LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying "
 | |
|        "offset=%" PRId64 ", filesize=%" PRId64 " [this=%p]", offset, size, this));
 | |
| 
 | |
|   mReadStart = mozilla::TimeStamp::Now();
 | |
|   mListener = aListener;
 | |
|   rv = CacheFileIOManager::Read(mHandle, offset, mBuf, mBufSize, this);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed"
 | |
|          " synchronously, creating empty metadata. [this=%p, rv=0x%08" PRIx32 "]",
 | |
|          this, static_cast<uint32_t>(rv)));
 | |
| 
 | |
|     mListener = nullptr;
 | |
|     InitEmptyMetadata();
 | |
|     aListener->OnMetadataRead(NS_OK);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| CacheFileMetadata::CalcMetadataSize(uint32_t aElementsSize, uint32_t aHashCount)
 | |
| {
 | |
|   return sizeof(uint32_t) +                         // hash of the metadata
 | |
|          aHashCount * sizeof(CacheHash::Hash16_t) + // array of chunk hashes
 | |
|          sizeof(CacheFileMetadataHeader) +          // metadata header
 | |
|          mKey.Length() + 1 +                        // key with trailing null
 | |
|          aElementsSize +                            // elements
 | |
|          sizeof(uint32_t);                          // offset
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::WriteMetadata(uint32_t aOffset,
 | |
|                                  CacheFileMetadataListener *aListener)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::WriteMetadata() [this=%p, offset=%d, listener=%p]",
 | |
|        this, aOffset, aListener));
 | |
| 
 | |
|   MOZ_ASSERT(!mListener);
 | |
|   MOZ_ASSERT(!mWriteBuf);
 | |
| 
 | |
|   nsresult rv;
 | |
| 
 | |
|   mIsDirty = false;
 | |
| 
 | |
|   mWriteBuf = static_cast<char *>(malloc(CalcMetadataSize(mElementsSize,
 | |
|                                                           mHashCount)));
 | |
|   if (!mWriteBuf) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   char *p = mWriteBuf + sizeof(uint32_t);
 | |
|   if (mHashCount) {
 | |
|     memcpy(p, mHashArray, mHashCount * sizeof(CacheHash::Hash16_t));
 | |
|     p += mHashCount * sizeof(CacheHash::Hash16_t);
 | |
|   }
 | |
|   mMetaHdr.WriteToBuf(p);
 | |
|   p += sizeof(CacheFileMetadataHeader);
 | |
|   memcpy(p, mKey.get(), mKey.Length());
 | |
|   p += mKey.Length();
 | |
|   *p = 0;
 | |
|   p++;
 | |
|   if (mElementsSize) {
 | |
|     memcpy(p, mBuf, mElementsSize);
 | |
|     p += mElementsSize;
 | |
|   }
 | |
| 
 | |
|   CacheHash::Hash32_t hash;
 | |
|   hash = CacheHash::Hash(mWriteBuf + sizeof(uint32_t),
 | |
|                          p - mWriteBuf - sizeof(uint32_t));
 | |
|   NetworkEndian::writeUint32(mWriteBuf, hash);
 | |
| 
 | |
|   NetworkEndian::writeUint32(p, aOffset);
 | |
|   p += sizeof(uint32_t);
 | |
| 
 | |
|   char * writeBuffer = mWriteBuf;
 | |
|   if (aListener) {
 | |
|     mListener = aListener;
 | |
|   } else {
 | |
|     // We are not going to pass |this| as a callback so the buffer will be
 | |
|     // released by CacheFileIOManager. Just null out mWriteBuf here.
 | |
|     mWriteBuf = nullptr;
 | |
|   }
 | |
| 
 | |
|   rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer, p - writeBuffer,
 | |
|                                  true, true, aListener ? this : nullptr);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     LOG(("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() "
 | |
|          "failed synchronously. [this=%p, rv=0x%08" PRIx32 "]",
 | |
|          this, static_cast<uint32_t>(rv)));
 | |
| 
 | |
|     mListener = nullptr;
 | |
|     if (mWriteBuf) {
 | |
|       CacheFileUtils::FreeBuffer(mWriteBuf);
 | |
|       mWriteBuf = nullptr;
 | |
|     }
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|   }
 | |
| 
 | |
|   DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::SyncReadMetadata(nsIFile *aFile)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this));
 | |
| 
 | |
|   MOZ_ASSERT(!mListener);
 | |
|   MOZ_ASSERT(!mHandle);
 | |
|   MOZ_ASSERT(!mHashArray);
 | |
|   MOZ_ASSERT(!mBuf);
 | |
|   MOZ_ASSERT(!mWriteBuf);
 | |
|   MOZ_ASSERT(mKey.IsEmpty());
 | |
| 
 | |
|   nsresult rv;
 | |
| 
 | |
|   int64_t fileSize;
 | |
|   rv = aFile->GetFileSize(&fileSize);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     // Don't bloat the console
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   PRFileDesc *fd;
 | |
|   rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0600, &fd);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   int64_t offset = PR_Seek64(fd, fileSize - sizeof(uint32_t), PR_SEEK_SET);
 | |
|   if (offset == -1) {
 | |
|     PR_Close(fd);
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   uint32_t metaOffset;
 | |
|   int32_t bytesRead = PR_Read(fd, &metaOffset, sizeof(uint32_t));
 | |
|   if (bytesRead != sizeof(uint32_t)) {
 | |
|     PR_Close(fd);
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   metaOffset = NetworkEndian::readUint32(&metaOffset);
 | |
|   if (metaOffset > fileSize) {
 | |
|     PR_Close(fd);
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   mBuf = static_cast<char *>(malloc(fileSize - metaOffset));
 | |
|   if (!mBuf) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
|   mBufSize = fileSize - metaOffset;
 | |
| 
 | |
|   DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|   offset = PR_Seek64(fd, metaOffset, PR_SEEK_SET);
 | |
|   if (offset == -1) {
 | |
|     PR_Close(fd);
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   bytesRead = PR_Read(fd, mBuf, mBufSize);
 | |
|   PR_Close(fd);
 | |
|   if (bytesRead != static_cast<int32_t>(mBufSize)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   rv = ParseMetadata(metaOffset, 0, false);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| const char *
 | |
| CacheFileMetadata::GetElement(const char *aKey)
 | |
| {
 | |
|   const char *data = mBuf;
 | |
|   const char *limit = mBuf + mElementsSize;
 | |
| 
 | |
|   while (data != limit) {
 | |
|     size_t maxLen = limit - data;
 | |
|     size_t keyLen = strnlen(data, maxLen);
 | |
|     MOZ_RELEASE_ASSERT(keyLen != maxLen, "Metadata elements corrupted. Key "
 | |
|                        "isn't null terminated!");
 | |
|     MOZ_RELEASE_ASSERT(keyLen + 1 != maxLen, "Metadata elements corrupted. "
 | |
|                        "There is no value for the key!");
 | |
| 
 | |
|     const char *value = data + keyLen + 1;
 | |
|     maxLen = limit - value;
 | |
|     size_t valueLen = strnlen(value, maxLen);
 | |
|     MOZ_RELEASE_ASSERT(valueLen != maxLen, "Metadata elements corrupted. Value "
 | |
|                        "isn't null terminated!");
 | |
| 
 | |
|     if (strcmp(data, aKey) == 0) {
 | |
|       LOG(("CacheFileMetadata::GetElement() - Key found [this=%p, key=%s]",
 | |
|            this, aKey));
 | |
|       return value;
 | |
|     }
 | |
| 
 | |
|     // point to next pair
 | |
|     data += keyLen + valueLen + 2;
 | |
|   }
 | |
|   LOG(("CacheFileMetadata::GetElement() - Key not found [this=%p, key=%s]",
 | |
|        this, aKey));
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::SetElement(const char *aKey, const char *aValue)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::SetElement() [this=%p, key=%s, value=%p]",
 | |
|        this, aKey, aValue));
 | |
| 
 | |
|   MarkDirty();
 | |
| 
 | |
|   nsresult rv;
 | |
| 
 | |
|   const uint32_t keySize = strlen(aKey) + 1;
 | |
|   char *pos = const_cast<char *>(GetElement(aKey));
 | |
| 
 | |
|   if (!aValue) {
 | |
|     // No value means remove the key/value pair completely, if existing
 | |
|     if (pos) {
 | |
|       uint32_t oldValueSize = strlen(pos) + 1;
 | |
|       uint32_t offset = pos - mBuf;
 | |
|       uint32_t remainder = mElementsSize - (offset + oldValueSize);
 | |
| 
 | |
|       memmove(pos - keySize, pos + oldValueSize, remainder);
 | |
|       mElementsSize -= keySize + oldValueSize;
 | |
|     }
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   const uint32_t valueSize = strlen(aValue) + 1;
 | |
|   uint32_t newSize = mElementsSize + valueSize;
 | |
|   if (pos) {
 | |
|     const uint32_t oldValueSize = strlen(pos) + 1;
 | |
|     const uint32_t offset = pos - mBuf;
 | |
|     const uint32_t remainder = mElementsSize - (offset + oldValueSize);
 | |
| 
 | |
|     // Update the value in place
 | |
|     newSize -= oldValueSize;
 | |
|     rv = EnsureBuffer(newSize);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return rv;
 | |
|     }
 | |
| 
 | |
|     // Move the remainder to the right place
 | |
|     pos = mBuf + offset;
 | |
|     memmove(pos + valueSize, pos + oldValueSize, remainder);
 | |
|   } else {
 | |
|     // allocate new meta data element
 | |
|     newSize += keySize;
 | |
|     rv = EnsureBuffer(newSize);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return rv;
 | |
|     }
 | |
| 
 | |
|     // Add after last element
 | |
|     pos = mBuf + mElementsSize;
 | |
|     memcpy(pos, aKey, keySize);
 | |
|     pos += keySize;
 | |
|   }
 | |
| 
 | |
|   // Update value
 | |
|   memcpy(pos, aValue, valueSize);
 | |
|   mElementsSize = newSize;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::Visit(nsICacheEntryMetaDataVisitor *aVisitor)
 | |
| {
 | |
|   const char *data = mBuf;
 | |
|   const char *limit = mBuf + mElementsSize;
 | |
| 
 | |
|   while (data < limit) {
 | |
|     // Point to the value part
 | |
|     const char *value = data + strlen(data) + 1;
 | |
|     MOZ_ASSERT(value < limit, "Metadata elements corrupted");
 | |
| 
 | |
|     aVisitor->OnMetaDataElement(data, value);
 | |
| 
 | |
|     // Skip value part
 | |
|     data = value + strlen(value) + 1;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(data == limit, "Metadata elements corrupted");
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| CacheHash::Hash16_t
 | |
| CacheFileMetadata::GetHash(uint32_t aIndex)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex < mHashCount);
 | |
|   return NetworkEndian::readUint16(&mHashArray[aIndex]);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::SetHash(uint32_t aIndex, CacheHash::Hash16_t aHash)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::SetHash() [this=%p, idx=%d, hash=%x]",
 | |
|        this, aIndex, aHash));
 | |
| 
 | |
|   MarkDirty();
 | |
| 
 | |
|   MOZ_ASSERT(aIndex <= mHashCount);
 | |
| 
 | |
|   if (aIndex > mHashCount) {
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
|   } else if (aIndex == mHashCount) {
 | |
|     if ((aIndex + 1) * sizeof(CacheHash::Hash16_t) > mHashArraySize) {
 | |
|       // reallocate hash array buffer
 | |
|       if (mHashArraySize == 0) {
 | |
|         mHashArraySize = kInitialHashArraySize * sizeof(CacheHash::Hash16_t);
 | |
|       } else {
 | |
|         mHashArraySize *= 2;
 | |
|       }
 | |
|       mHashArray = static_cast<CacheHash::Hash16_t *>(
 | |
|                      moz_xrealloc(mHashArray, mHashArraySize));
 | |
|     }
 | |
| 
 | |
|     mHashCount++;
 | |
|   }
 | |
| 
 | |
|   NetworkEndian::writeUint16(&mHashArray[aIndex], aHash);
 | |
| 
 | |
|   DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::RemoveHash(uint32_t aIndex)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::RemoveHash() [this=%p, idx=%d]", this, aIndex));
 | |
| 
 | |
|   MarkDirty();
 | |
| 
 | |
|   MOZ_ASSERT((aIndex + 1) == mHashCount, "Can remove only last hash!");
 | |
| 
 | |
|   if (aIndex + 1 != mHashCount) {
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
|   }
 | |
| 
 | |
|   mHashCount--;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::AddFlags(uint32_t aFlags)
 | |
| {
 | |
|   MarkDirty(false);
 | |
|   mMetaHdr.mFlags |= aFlags;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::RemoveFlags(uint32_t aFlags)
 | |
| {
 | |
|   MarkDirty(false);
 | |
|   mMetaHdr.mFlags &= ~aFlags;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::GetFlags(uint32_t *_retval)
 | |
| {
 | |
|   *_retval = mMetaHdr.mFlags;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::SetExpirationTime() [this=%p, expirationTime=%d]",
 | |
|        this, aExpirationTime));
 | |
| 
 | |
|   MarkDirty(false);
 | |
|   mMetaHdr.mExpirationTime = aExpirationTime;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::GetExpirationTime(uint32_t *_retval)
 | |
| {
 | |
|   *_retval = mMetaHdr.mExpirationTime;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::SetFrecency(uint32_t aFrecency)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::SetFrecency() [this=%p, frecency=%f]",
 | |
|        this, (double)aFrecency));
 | |
| 
 | |
|   MarkDirty(false);
 | |
|   mMetaHdr.mFrecency = aFrecency;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::GetFrecency(uint32_t *_retval)
 | |
| {
 | |
|   *_retval = mMetaHdr.mFrecency;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::GetLastModified(uint32_t *_retval)
 | |
| {
 | |
|   *_retval = mMetaHdr.mLastModified;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::GetLastFetched(uint32_t *_retval)
 | |
| {
 | |
|   *_retval = mMetaHdr.mLastFetched;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::GetFetchCount(uint32_t *_retval)
 | |
| {
 | |
|   *_retval = mMetaHdr.mFetchCount;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::OnFetched()
 | |
| {
 | |
|   MarkDirty(false);
 | |
| 
 | |
|   mMetaHdr.mLastFetched = NOW_SECONDS();
 | |
|   ++mMetaHdr.mFetchCount;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| CacheFileMetadata::MarkDirty(bool aUpdateLastModified)
 | |
| {
 | |
|   mIsDirty = true;
 | |
|   if (aUpdateLastModified) {
 | |
|     mMetaHdr.mLastModified = NOW_SECONDS();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
 | |
| {
 | |
|   MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!");
 | |
|   return NS_ERROR_UNEXPECTED;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
 | |
|                                  nsresult aResult)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, result=0x%08" PRIx32 "]",
 | |
|        this, aHandle, static_cast<uint32_t>(aResult)));
 | |
| 
 | |
|   MOZ_ASSERT(mListener);
 | |
|   MOZ_ASSERT(mWriteBuf);
 | |
| 
 | |
|   CacheFileUtils::FreeBuffer(mWriteBuf);
 | |
|   mWriteBuf = nullptr;
 | |
| 
 | |
|   nsCOMPtr<CacheFileMetadataListener> listener;
 | |
| 
 | |
|   mListener.swap(listener);
 | |
|   listener->OnMetadataWritten(aResult);
 | |
| 
 | |
|   DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
 | |
|                               nsresult aResult)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08" PRIx32 "]",
 | |
|        this, aHandle, static_cast<uint32_t>(aResult)));
 | |
| 
 | |
|   MOZ_ASSERT(mListener);
 | |
| 
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<CacheFileMetadataListener> listener;
 | |
| 
 | |
|   if (NS_FAILED(aResult)) {
 | |
|     LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed"
 | |
|          ", creating empty metadata. [this=%p, rv=0x%08" PRIx32 "]",
 | |
|          this, static_cast<uint32_t>(aResult)));
 | |
| 
 | |
|     InitEmptyMetadata();
 | |
| 
 | |
|     mListener.swap(listener);
 | |
|     listener->OnMetadataRead(NS_OK);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (mFirstRead) {
 | |
|     Telemetry::AccumulateTimeDelta(
 | |
|       Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_TIME_MS, mReadStart);
 | |
|     Telemetry::Accumulate(
 | |
|       Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_SIZE, mBufSize);
 | |
|   } else {
 | |
|     Telemetry::AccumulateTimeDelta(
 | |
|       Telemetry::NETWORK_CACHE_METADATA_SECOND_READ_TIME_MS, mReadStart);
 | |
|   }
 | |
| 
 | |
|   // check whether we have read all necessary data
 | |
|   uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize -
 | |
|                                                   sizeof(uint32_t));
 | |
| 
 | |
|   int64_t size = mHandle->FileSize();
 | |
|   MOZ_ASSERT(size != -1);
 | |
| 
 | |
|   if (realOffset >= size) {
 | |
|     LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating "
 | |
|          "empty metadata. [this=%p, realOffset=%u, size=%" PRId64 "]", this,
 | |
|          realOffset, size));
 | |
| 
 | |
|     InitEmptyMetadata();
 | |
| 
 | |
|     mListener.swap(listener);
 | |
|     listener->OnMetadataRead(NS_OK);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   uint32_t maxHashCount = size / kChunkSize;
 | |
|   uint32_t maxMetadataSize = CalcMetadataSize(kMaxElementsSize, maxHashCount);
 | |
|   if (size - realOffset > maxMetadataSize) {
 | |
|     LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, metadata would "
 | |
|          "be too big, creating empty metadata. [this=%p, realOffset=%u, "
 | |
|          "maxMetadataSize=%u, size=%" PRId64 "]", this, realOffset, maxMetadataSize,
 | |
|          size));
 | |
| 
 | |
|     InitEmptyMetadata();
 | |
| 
 | |
|     mListener.swap(listener);
 | |
|     listener->OnMetadataRead(NS_OK);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   uint32_t usedOffset = size - mBufSize;
 | |
| 
 | |
|   if (realOffset < usedOffset) {
 | |
|     uint32_t missing = usedOffset - realOffset;
 | |
|     // we need to read more data
 | |
|     char *newBuf = static_cast<char *>(realloc(mBuf, mBufSize + missing));
 | |
|     if (!newBuf) {
 | |
|       LOG(("CacheFileMetadata::OnDataRead() - Error allocating %d more bytes "
 | |
|            "for the missing part of the metadata, creating empty metadata. "
 | |
|            "[this=%p]", missing, this));
 | |
| 
 | |
|       InitEmptyMetadata();
 | |
| 
 | |
|       mListener.swap(listener);
 | |
|       listener->OnMetadataRead(NS_OK);
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     mBuf = newBuf;
 | |
|     memmove(mBuf + missing, mBuf, mBufSize);
 | |
|     mBufSize += missing;
 | |
| 
 | |
|     DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|     LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to "
 | |
|          "have full metadata. [this=%p]", missing, this));
 | |
| 
 | |
|     mFirstRead = false;
 | |
|     mReadStart = mozilla::TimeStamp::Now();
 | |
|     rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, this);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() "
 | |
|            "failed synchronously, creating empty metadata. [this=%p, "
 | |
|            "rv=0x%08" PRIx32 "]", this, static_cast<uint32_t>(rv)));
 | |
| 
 | |
|       InitEmptyMetadata();
 | |
| 
 | |
|       mListener.swap(listener);
 | |
|       listener->OnMetadataRead(NS_OK);
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   Telemetry::Accumulate(Telemetry::NETWORK_CACHE_METADATA_SIZE,
 | |
|                         size - realOffset);
 | |
| 
 | |
|   // We have all data according to offset information at the end of the entry.
 | |
|   // Try to parse it.
 | |
|   rv = ParseMetadata(realOffset, realOffset - usedOffset, true);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating "
 | |
|          "empty metadata. [this=%p]", this));
 | |
|     InitEmptyMetadata();
 | |
|   } else {
 | |
|     // Shrink elements buffer.
 | |
|     mBuf = static_cast<char *>(moz_xrealloc(mBuf, mElementsSize));
 | |
|     mBufSize = mElementsSize;
 | |
| 
 | |
|     // There is usually no or just one call to SetMetadataElement() when the
 | |
|     // metadata is parsed from disk. Avoid allocating power of two sized buffer
 | |
|     // which we do in case of newly created metadata.
 | |
|     mAllocExactSize = true;
 | |
|   }
 | |
| 
 | |
|   mListener.swap(listener);
 | |
|   listener->OnMetadataRead(NS_OK);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
 | |
| {
 | |
|   MOZ_CRASH("CacheFileMetadata::OnFileDoomed should not be called!");
 | |
|   return NS_ERROR_UNEXPECTED;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
 | |
| {
 | |
|   MOZ_CRASH("CacheFileMetadata::OnEOFSet should not be called!");
 | |
|   return NS_ERROR_UNEXPECTED;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
 | |
| {
 | |
|   MOZ_CRASH("CacheFileMetadata::OnFileRenamed should not be called!");
 | |
|   return NS_ERROR_UNEXPECTED;
 | |
| }
 | |
| 
 | |
| void
 | |
| CacheFileMetadata::InitEmptyMetadata()
 | |
| {
 | |
|   if (mBuf) {
 | |
|     CacheFileUtils::FreeBuffer(mBuf);
 | |
|     mBuf = nullptr;
 | |
|     mBufSize = 0;
 | |
|   }
 | |
|   mAllocExactSize = false;
 | |
|   mOffset = 0;
 | |
|   mMetaHdr.mVersion = kCacheEntryVersion;
 | |
|   mMetaHdr.mFetchCount = 0;
 | |
|   mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
 | |
|   mMetaHdr.mKeySize = mKey.Length();
 | |
| 
 | |
|   // Deliberately not touching the "kCacheEntryIsPinned" flag.
 | |
| 
 | |
|   DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|   // We're creating a new entry. If there is any old data truncate it.
 | |
|   if (mHandle) {
 | |
|     mHandle->SetPinned(Pinned());
 | |
|     // We can pronounce the handle as invalid now, because it simply
 | |
|     // doesn't have the correct metadata.  This will cause IO operations
 | |
|     // be bypassed during shutdown (mainly dooming it, when a channel
 | |
|     // is canceled by closing the window.)
 | |
|     mHandle->SetInvalid();
 | |
|     if (mHandle->FileExists() && mHandle->FileSize()) {
 | |
|       CacheFileIOManager::TruncateSeekSetEOF(mHandle, 0, 0, nullptr);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset,
 | |
|                                  bool aHaveKey)
 | |
| {
 | |
|   LOG(("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, "
 | |
|        "bufOffset=%d, haveKey=%u]", this, aMetaOffset, aBufOffset, aHaveKey));
 | |
| 
 | |
|   nsresult rv;
 | |
| 
 | |
|   uint32_t metaposOffset = mBufSize - sizeof(uint32_t);
 | |
|   uint32_t hashesOffset = aBufOffset + sizeof(uint32_t);
 | |
|   uint32_t hashCount = aMetaOffset / kChunkSize;
 | |
|   if (aMetaOffset % kChunkSize)
 | |
|     hashCount++;
 | |
|   uint32_t hashesLen = hashCount * sizeof(CacheHash::Hash16_t);
 | |
|   uint32_t hdrOffset = hashesOffset + hashesLen;
 | |
|   uint32_t keyOffset = hdrOffset + sizeof(CacheFileMetadataHeader);
 | |
| 
 | |
|   LOG(("CacheFileMetadata::ParseMetadata() [this=%p]\n  metaposOffset=%d\n  "
 | |
|        "hashesOffset=%d\n  hashCount=%d\n  hashesLen=%d\n  hdfOffset=%d\n  "
 | |
|        "keyOffset=%d\n", this, metaposOffset, hashesOffset, hashCount,
 | |
|        hashesLen,hdrOffset, keyOffset));
 | |
| 
 | |
|   if (keyOffset > metaposOffset) {
 | |
|     LOG(("CacheFileMetadata::ParseMetadata() - Wrong keyOffset! [this=%p]",
 | |
|          this));
 | |
|     return NS_ERROR_FILE_CORRUPTED;
 | |
|   }
 | |
| 
 | |
|   mMetaHdr.ReadFromBuf(mBuf + hdrOffset);
 | |
| 
 | |
|   if (mMetaHdr.mVersion == 1) {
 | |
|     // Backward compatibility before we've added flags to the header
 | |
|     keyOffset -= sizeof(uint32_t);
 | |
|   } else if (mMetaHdr.mVersion == 2) {
 | |
|     // Version 2 just lacks the ability to store alternative data. Nothing to do
 | |
|     // here.
 | |
|   } else if (mMetaHdr.mVersion != kCacheEntryVersion) {
 | |
|     LOG(("CacheFileMetadata::ParseMetadata() - Not a version we understand to. "
 | |
|          "[version=0x%x, this=%p]", mMetaHdr.mVersion, this));
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   // Update the version stored in the header to make writes
 | |
|   // store the header in the current version form.
 | |
|   mMetaHdr.mVersion = kCacheEntryVersion;
 | |
| 
 | |
|   uint32_t elementsOffset = mMetaHdr.mKeySize + keyOffset + 1;
 | |
| 
 | |
|   if (elementsOffset > metaposOffset) {
 | |
|     LOG(("CacheFileMetadata::ParseMetadata() - Wrong elementsOffset %d "
 | |
|          "[this=%p]", elementsOffset, this));
 | |
|     return NS_ERROR_FILE_CORRUPTED;
 | |
|   }
 | |
| 
 | |
|   // check that key ends with \0
 | |
|   if (mBuf[elementsOffset - 1] != 0) {
 | |
|     LOG(("CacheFileMetadata::ParseMetadata() - Elements not null terminated. "
 | |
|          "[this=%p]", this));
 | |
|     return NS_ERROR_FILE_CORRUPTED;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   if (!aHaveKey) {
 | |
|     // get the key form metadata
 | |
|     mKey.Assign(mBuf + keyOffset, mMetaHdr.mKeySize);
 | |
| 
 | |
|     rv = ParseKey(mKey);
 | |
|     if (NS_FAILED(rv))
 | |
|       return rv;
 | |
|   }
 | |
|   else {
 | |
|     if (mMetaHdr.mKeySize != mKey.Length()) {
 | |
|       LOG(("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s "
 | |
|            "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(),
 | |
|            this));
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
| 
 | |
|     if (memcmp(mKey.get(), mBuf + keyOffset, mKey.Length()) != 0) {
 | |
|       LOG(("CacheFileMetadata::ParseMetadata() - Key collision (2), key=%s "
 | |
|            "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(),
 | |
|            this));
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // check metadata hash (data from hashesOffset to metaposOffset)
 | |
|   CacheHash::Hash32_t hashComputed, hashExpected;
 | |
|   hashComputed = CacheHash::Hash(mBuf + hashesOffset,
 | |
|                                  metaposOffset - hashesOffset);
 | |
|   hashExpected = NetworkEndian::readUint32(mBuf + aBufOffset);
 | |
| 
 | |
|   if (hashComputed != hashExpected) {
 | |
|     LOG(("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of "
 | |
|          "the metadata is %x, hash in file is %x [this=%p]", hashComputed,
 | |
|          hashExpected, this));
 | |
|     return NS_ERROR_FILE_CORRUPTED;
 | |
|   }
 | |
| 
 | |
|   // check elements
 | |
|   rv = CheckElements(mBuf + elementsOffset, metaposOffset - elementsOffset);
 | |
|   if (NS_FAILED(rv))
 | |
|     return rv;
 | |
| 
 | |
|   if (mHandle) {
 | |
|     if (!mHandle->SetPinned(Pinned())) {
 | |
|       LOG(("CacheFileMetadata::ParseMetadata() - handle was doomed for this "
 | |
|            "pinning state, truncate the file [this=%p, pinned=%d]", this, Pinned()));
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mHashArraySize = hashesLen;
 | |
|   mHashCount = hashCount;
 | |
|   if (mHashArraySize) {
 | |
|     mHashArray = static_cast<CacheHash::Hash16_t *>(
 | |
|                    moz_xmalloc(mHashArraySize));
 | |
|     memcpy(mHashArray, mBuf + hashesOffset, mHashArraySize);
 | |
|   }
 | |
| 
 | |
|   MarkDirty();
 | |
| 
 | |
|   mElementsSize = metaposOffset - elementsOffset;
 | |
|   memmove(mBuf, mBuf + elementsOffset, mElementsSize);
 | |
|   mOffset = aMetaOffset;
 | |
| 
 | |
|   DoMemoryReport(MemoryUsage());
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::CheckElements(const char *aBuf, uint32_t aSize)
 | |
| {
 | |
|   if (aSize) {
 | |
|     // Check if the metadata ends with a zero byte.
 | |
|     if (aBuf[aSize - 1] != 0) {
 | |
|       NS_ERROR("Metadata elements are not null terminated");
 | |
|       LOG(("CacheFileMetadata::CheckElements() - Elements are not null "
 | |
|            "terminated. [this=%p]", this));
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
|     // Check that there are an even number of zero bytes
 | |
|     // to match the pattern { key \0 value \0 }
 | |
|     bool odd = false;
 | |
|     for (uint32_t i = 0; i < aSize; i++) {
 | |
|       if (aBuf[i] == 0)
 | |
|         odd = !odd;
 | |
|     }
 | |
|     if (odd) {
 | |
|       NS_ERROR("Metadata elements are malformed");
 | |
|       LOG(("CacheFileMetadata::CheckElements() - Elements are malformed. "
 | |
|            "[this=%p]", this));
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::EnsureBuffer(uint32_t aSize)
 | |
| {
 | |
|   if (aSize > kMaxElementsSize) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   if (mBufSize < aSize) {
 | |
|     if (mAllocExactSize) {
 | |
|       // If this is not the only allocation, use power of two for following
 | |
|       // allocations.
 | |
|       mAllocExactSize = false;
 | |
|     } else {
 | |
|       // find smallest power of 2 greater than or equal to aSize
 | |
|       --aSize;
 | |
|       aSize |= aSize >> 1;
 | |
|       aSize |= aSize >> 2;
 | |
|       aSize |= aSize >> 4;
 | |
|       aSize |= aSize >> 8;
 | |
|       aSize |= aSize >> 16;
 | |
|       ++aSize;
 | |
|     }
 | |
| 
 | |
|     if (aSize < kInitialBufSize) {
 | |
|       aSize = kInitialBufSize;
 | |
|     }
 | |
| 
 | |
|     char *newBuf = static_cast<char *>(realloc(mBuf, aSize));
 | |
|     if (!newBuf) {
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
|     mBufSize = aSize;
 | |
|     mBuf = newBuf;
 | |
| 
 | |
|     DoMemoryReport(MemoryUsage());
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| CacheFileMetadata::ParseKey(const nsACString &aKey)
 | |
| {
 | |
|   nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
 | |
|   NS_ENSURE_TRUE(info, NS_ERROR_FAILURE);
 | |
| 
 | |
|   mAnonymous =  info->IsAnonymous();
 | |
|   mOriginAttributes = *info->OriginAttributesPtr();
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // Memory reporting
 | |
| 
 | |
| size_t
 | |
| CacheFileMetadata::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 | |
| {
 | |
|   size_t n = 0;
 | |
|   // mHandle reported via CacheFileIOManager.
 | |
|   n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
 | |
|   n += mallocSizeOf(mHashArray);
 | |
|   n += mallocSizeOf(mBuf);
 | |
|   // Ignore mWriteBuf, it's not safe to access it when metadata is being
 | |
|   // written and it's null otherwise.
 | |
|   // mListener is usually the owning CacheFile.
 | |
| 
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| size_t
 | |
| CacheFileMetadata::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 | |
| {
 | |
|   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
 | |
| }
 | |
| 
 | |
| } // namespace net
 | |
| } // namespace mozilla
 | 
