nsresult CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) { LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]", this, aHandle, aResult)); nsCOMPtr<CacheFileChunkListener> listener; { CacheFileAutoLock lock(mFile); MOZ_ASSERT(mState == WRITING); MOZ_ASSERT(mListener); if (NS_WARN_IF(NS_FAILED(aResult))) { SetError(aResult); } mState = READY; if (!mBuf) { mBuf = mRWBuf; mBufSize = mRWBufSize; mRWBuf = nullptr; mRWBufSize = 0; } else { free(mRWBuf); mRWBuf = nullptr; mRWBufSize = 0; ChunkAllocationChanged(); } DoMemoryReport(MemorySize()); mListener.swap(listener); } listener->OnChunkWritten(aResult, this); return NS_OK; }
void CacheFileMetadata::InitEmptyMetadata() { if (mBuf) { free(mBuf); mBuf = nullptr; mBufSize = 0; } mOffset = 0; mMetaHdr.mVersion = kCacheEntryVersion; mMetaHdr.mFetchCount = 0; mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME; mMetaHdr.mKeySize = mKey.Length(); DoMemoryReport(MemoryUsage()); // We're creating a new entry. If there is any old data truncate it. if (mHandle && mHandle->FileExists() && mHandle->FileSize()) { CacheFileIOManager::TruncateSeekSetEOF(mHandle, 0, 0, nullptr); } }
nsresult CacheFileMetadata::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) { LOG(("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, result=0x%08x]", this, aHandle, aResult)); MOZ_ASSERT(mListener); MOZ_ASSERT(mWriteBuf); free(mWriteBuf); mWriteBuf = nullptr; nsCOMPtr<CacheFileMetadataListener> listener; mListener.swap(listener); listener->OnMetadataWritten(aResult); DoMemoryReport(MemoryUsage()); 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; }
void CacheFileChunk::EnsureBufSize(uint32_t aBufSize) { mFile->AssertOwnsLock(); if (mBufSize >= aBufSize) return; bool copy = false; if (!mBuf && mState == WRITING) { // We need to duplicate the data that is being written on the background // thread, so make sure that all the data fits into the new buffer. copy = true; if (mRWBufSize > aBufSize) aBufSize = mRWBufSize; } // find smallest power of 2 greater than or equal to aBufSize aBufSize--; aBufSize |= aBufSize >> 1; aBufSize |= aBufSize >> 2; aBufSize |= aBufSize >> 4; aBufSize |= aBufSize >> 8; aBufSize |= aBufSize >> 16; aBufSize++; const uint32_t minBufSize = kMinBufSize; const uint32_t maxBufSize = kChunkSize; aBufSize = clamped(aBufSize, minBufSize, maxBufSize); mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize)); mBufSize = aBufSize; if (copy) memcpy(mBuf, mRWBuf, mRWBufSize); DoMemoryReport(MemorySize()); }
nsresult CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]", this, aHandle, aResult)); nsCOMPtr<CacheFileChunkListener> listener; { CacheFileAutoLock lock(mFile); MOZ_ASSERT(mState == READING); MOZ_ASSERT(mListener); if (NS_SUCCEEDED(aResult)) { CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize); if (hash != mReadHash) { LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is" " %hx, hash in metadata is %hx. [this=%p, idx=%d]", hash, mReadHash, this, mIndex)); aResult = NS_ERROR_FILE_CORRUPTED; } else { if (!mBuf) { // Just swap the buffers if we don't have mBuf yet MOZ_ASSERT(mDataSize == mRWBufSize); mBuf = mRWBuf; mBufSize = mRWBufSize; mRWBuf = nullptr; mRWBufSize = 0; } else { LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]", this)); // Merge data with write buffer if (mRWBufSize >= mBufSize) { // The new data will fit into the buffer that contains data read // from the disk. Simply copy the valid pieces. mValidityMap.Log(); for (uint32_t i = 0; i < mValidityMap.Length(); i++) { if (mValidityMap[i].Offset() + mValidityMap[i].Len() > mBufSize) { MOZ_CRASH("Unexpected error in validity map!"); } memcpy(mRWBuf + mValidityMap[i].Offset(), mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len()); } mValidityMap.Clear(); free(mBuf); mBuf = mRWBuf; mBufSize = mRWBufSize; mRWBuf = nullptr; mRWBufSize = 0; ChunkAllocationChanged(); } else { // Buffer holding the new data is larger. Use it as the destination // buffer to avoid reallocating mRWBuf. We need to copy those pieces // from mRWBuf which are not valid in mBuf. uint32_t invalidOffset = 0; uint32_t invalidLength; mValidityMap.Log(); for (uint32_t i = 0; i < mValidityMap.Length(); i++) { MOZ_ASSERT(invalidOffset <= mValidityMap[i].Offset()); invalidLength = mValidityMap[i].Offset() - invalidOffset; if (invalidLength > 0) { if (invalidOffset + invalidLength > mRWBufSize) { MOZ_CRASH("Unexpected error in validity map!"); } memcpy(mBuf + invalidOffset, mRWBuf + invalidOffset, invalidLength); } invalidOffset = mValidityMap[i].Offset() + mValidityMap[i].Len(); } if (invalidOffset < mRWBufSize) { invalidLength = invalidOffset - mRWBufSize; memcpy(mBuf + invalidOffset, mRWBuf + invalidOffset, invalidLength); } mValidityMap.Clear(); free(mRWBuf); mRWBuf = nullptr; mRWBufSize = 0; ChunkAllocationChanged(); } DoMemoryReport(MemorySize()); } } } if (NS_FAILED(aResult)) { aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND; SetError(aResult); mDataSize = 0; } mState = READY; mListener.swap(listener); } listener->OnChunkRead(aResult, this); return NS_OK; }
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 != kCacheEntryVersion) { LOG(("CacheFileMetadata::ParseMetadata() - Not a version we understand to. " "[version=0x%x, this=%p]", mMetaHdr.mVersion, this)); return NS_ERROR_UNEXPECTED; } 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; mHashArraySize = hashesLen; mHashCount = hashCount; if (mHashArraySize) { mHashArray = static_cast<CacheHash::Hash16_t *>( moz_xmalloc(mHashArraySize)); memcpy(mHashArray, mBuf + hashesOffset, mHashArraySize); } mMetaHdr.mFetchCount++; MarkDirty(); mElementsSize = metaposOffset - elementsOffset; memmove(mBuf, mBuf + elementsOffset, mElementsSize); mOffset = aMetaOffset; // TODO: shrink memory if buffer is too big DoMemoryReport(MemoryUsage()); return NS_OK; }
nsresult CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]", this, aHandle, aResult)); MOZ_ASSERT(mListener); nsresult rv, retval; nsCOMPtr<CacheFileMetadataListener> listener; if (NS_FAILED(aResult)) { LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed" ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult)); InitEmptyMetadata(); retval = NS_OK; mListener.swap(listener); listener->OnMetadataRead(retval); return NS_OK; } // 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=%d, size=%lld]", this, realOffset, size)); InitEmptyMetadata(); retval = NS_OK; mListener.swap(listener); listener->OnMetadataRead(retval); return NS_OK; } uint32_t usedOffset = size - mBufSize; if (realOffset < usedOffset) { uint32_t missing = usedOffset - realOffset; // we need to read more data mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize + missing)); 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)); rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, true, this); if (NS_FAILED(rv)) { LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() " "failed synchronously, creating empty metadata. [this=%p, " "rv=0x%08x]", this, rv)); InitEmptyMetadata(); retval = NS_OK; mListener.swap(listener); listener->OnMetadataRead(retval); return NS_OK; } return NS_OK; } // 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(); retval = NS_OK; } else { retval = NS_OK; } mListener.swap(listener); listener->OnMetadataRead(retval); 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; } mBufSize = fileSize - metaOffset; mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); 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; }
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 *>(moz_xmalloc(sizeof(uint32_t) + mHashCount * sizeof(CacheHash::Hash16_t) + sizeof(CacheFileMetadataHeader) + mKey.Length() + 1 + mElementsSize + sizeof(uint32_t))); char *p = mWriteBuf + sizeof(uint32_t); 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++; 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; if (aListener) { mListener = aListener; writeBuffer = mWriteBuf; } else { // We are not going to pass |this| as callback to CacheFileIOManager::Write // so we must allocate a new buffer that will be released automatically when // write is finished. This is actually better than to let // CacheFileMetadata::OnDataWritten do the job, since when dispatching the // result from some reason fails during shutdown, we would unnecessarily leak // both this object and the buffer. writeBuffer = static_cast<char *>(moz_xmalloc(p - mWriteBuf)); memcpy(mWriteBuf, writeBuffer, p - mWriteBuf); } rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer, p - mWriteBuf, true, aListener ? this : nullptr); if (NS_FAILED(rv)) { LOG(("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() " "failed synchronously. [this=%p, rv=0x%08x]", this, rv)); mListener = nullptr; if (writeBuffer != mWriteBuf) { free(writeBuffer); } free(mWriteBuf); mWriteBuf = nullptr; NS_ENSURE_SUCCESS(rv, rv); } DoMemoryReport(MemoryUsage()); 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=%lld]", 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=%lld, filesize=%lld [this=%p]", offset, size, this)); mListener = aListener; rv = CacheFileIOManager::Read(mHandle, offset, mBuf, mBufSize, true, this); if (NS_FAILED(rv)) { LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed" " synchronously, creating empty metadata. [this=%p, rv=0x%08x]", this, rv)); mListener = nullptr; InitEmptyMetadata(); aListener->OnMetadataRead(NS_OK); return NS_OK; } return NS_OK; }
nsresult CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]", this, aHandle, 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%08x]", this, 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=%lld]", 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=%lld]", 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%08x]", this, 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::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); 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++; 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%08x]", this, rv)); mListener = nullptr; if (mWriteBuf) { free(mWriteBuf); mWriteBuf = nullptr; } NS_ENSURE_SUCCESS(rv, rv); } DoMemoryReport(MemoryUsage()); return NS_OK; }
nsresult CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]", this, aHandle, aResult)); nsCOMPtr<CacheFileChunkListener> listener; { CacheFileAutoLock lock(mFile); MOZ_ASSERT(mState == READING); MOZ_ASSERT(mListener); if (NS_SUCCEEDED(aResult)) { CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize); if (hash != mReadHash) { LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is" " %hx, hash in metadata is %hx. [this=%p, idx=%d]", hash, mReadHash, this, mIndex)); aResult = NS_ERROR_FILE_CORRUPTED; } else { if (!mBuf) { // Just swap the buffers if we don't have mBuf yet MOZ_ASSERT(mDataSize == mRWBufSize); mBuf = mRWBuf; mBufSize = mRWBufSize; mRWBuf = nullptr; mRWBufSize = 0; } else { LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]", this)); // Merge data with write buffer if (mRWBufSize < mBufSize) { mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize)); mRWBufSize = mBufSize; } mValidityMap.Log(); for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) { memcpy(mRWBuf + mValidityMap[i].Offset(), mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len()); } mValidityMap.Clear(); free(mBuf); mBuf = mRWBuf; mBufSize = mRWBufSize; mRWBuf = nullptr; mRWBufSize = 0; DoMemoryReport(MemorySize()); } } } if (NS_FAILED(aResult)) { aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND; SetError(aResult); mDataSize = 0; } else { mState = READY; } mListener.swap(listener); } listener->OnChunkRead(aResult, this); return NS_OK; }
nsresult CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset) { LOG(("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, " "bufOffset=%d]", this, aMetaOffset, aBufOffset)); 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(CacheHashUtils::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; } uint32_t elementsOffset = reinterpret_cast<CacheFileMetadataHeader *>( mBuf + hdrOffset)->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; } nsAutoCString origKey; uint32_t keySize = reinterpret_cast<CacheFileMetadataHeader *>( mBuf + hdrOffset)->mKeySize; if (mKeyIsHash) { // get the original key origKey.Assign(mBuf + keyOffset, keySize); } else { if (keySize != mKey.Length()) { LOG(("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s " "[this=%p]", nsCString(mBuf + keyOffset, keySize).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, keySize).get(), this)); return NS_ERROR_FILE_CORRUPTED; } } // check metadata hash (data from hashesOffset to metaposOffset) CacheHashUtils::Hash32_t hash; hash = CacheHashUtils::Hash(mBuf + hashesOffset, metaposOffset - hashesOffset); if (hash != PR_ntohl(*(reinterpret_cast<uint32_t *>(mBuf + aBufOffset)))) { LOG(("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of " "the metadata is %x, hash in file is %x [this=%p]", hash, PR_ntohl(*(reinterpret_cast<uint32_t *>(mBuf + aBufOffset))), this)); return NS_ERROR_FILE_CORRUPTED; } // check elements rv = CheckElements(mBuf + elementsOffset, metaposOffset - elementsOffset); if (NS_FAILED(rv)) return rv; mHashArraySize = hashesLen; mHashCount = hashCount; if (mHashArraySize) { mHashArray = static_cast<CacheHashUtils::Hash16_t *>( moz_xmalloc(mHashArraySize)); memcpy(mHashArray, mBuf + hashesOffset, mHashArraySize); } memcpy(&mMetaHdr, mBuf + hdrOffset, sizeof(CacheFileMetadataHeader)); mMetaHdr.mFetchCount++; MarkDirty(); mElementsSize = metaposOffset - elementsOffset; memmove(mBuf, mBuf + elementsOffset, mElementsSize); mOffset = aMetaOffset; if (mKeyIsHash) { mKey = origKey; mKeyIsHash = false; } // TODO: shrink memory if buffer is too big DoMemoryReport(MemoryUsage()); 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) { if (mKeyIsHash) { LOG(("CacheFileMetadata::ReadMetadata() - Filesize == 0, cannot create " "empty metadata since key is a hash. [this=%p]", this)); CacheFileIOManager::DoomFile(mHandle, nullptr); return NS_ERROR_NOT_AVAILABLE; } // 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))) { if (mKeyIsHash) { LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, cannot " "create empty metadata since key is a hash. [this=%p, " "filesize=%lld]", this, size)); CacheFileIOManager::DoomFile(mHandle, nullptr); return NS_ERROR_FILE_CORRUPTED; } // there must be at least checksum, header and offset LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating " "empty metadata. [this=%p, filesize=%lld]", this, size)); InitEmptyMetadata(); aListener->OnMetadataRead(NS_OK); return NS_OK; } // round offset to 4k blocks int64_t offset = (size / kAlignSize) * kAlignSize; if (size - offset < kMinMetadataRead && offset > kAlignSize) offset -= kAlignSize; mBufSize = size - offset; mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); DoMemoryReport(MemoryUsage()); LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying " "offset=%lld, filesize=%lld [this=%p]", offset, size, this)); mListener = aListener; rv = CacheFileIOManager::Read(mHandle, offset, mBuf, mBufSize, this); if (NS_FAILED(rv)) { if (mKeyIsHash) { LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() " "failed synchronously, cannot create empty metadata since key is " "a hash. [this=%p, rv=0x%08x]", this, rv)); CacheFileIOManager::DoomFile(mHandle, nullptr); return rv; } LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed" " synchronously, creating empty metadata. [this=%p, rv=0x%08x]", this, rv)); mListener = nullptr; InitEmptyMetadata(); aListener->OnMetadataRead(NS_OK); return NS_OK; } return NS_OK; }