/* 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/. */
#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)
CacheFileMetadata::CacheFileMetadata()
: CacheMemoryConsumer(DONT_REPORT /* This is a helper class */),
mIsDirty(false),
mAnonymous(false),
mAllocExactSize(false),
mFirstRead(true),
mLock(new CacheFileUtils::CacheFileLock()) {
LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p]", this));
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));
// 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;
}
NetworkEndian::writeUint32(p, aOffset);
p += sizeof(uint32_t);
char* writeBuffer = mWriteBuf; if (aListener) {
mListener = aListener;
rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer,
p - writeBuffer, true, true, this);
} 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::WriteWithoutCallback(mHandle, aOffset, writeBuffer,
p - writeBuffer, true, true);
}
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!");
constchar* 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;
}
// 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;
while (data < limit) { // Point to the value part constchar* 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");
}
nsresult CacheFileMetadata::OnFileOpened(CacheFileHandle* aHandle,
nsresult aResult) {
MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!"); return NS_ERROR_UNEXPECTED;
}
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));
// 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);
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;
}
// 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);
}
}
}
if (mMetaHdr.mVersion == 1) { // Backward compatibility before we've added flags to the header
keyOffset -= sizeof(uint32_t);
} elseif (mMetaHdr.mVersion == 2) { // Version 2 just lacks the ability to store alternative data. Nothing to do // here.
} elseif (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;
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;
}
}
nsresult CacheFileMetadata::CheckElements(constchar* 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;
}
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;
}
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.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.