1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheFileMetadata.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,899 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CacheLog.h" 1.9 +#include "CacheFileMetadata.h" 1.10 + 1.11 +#include "CacheFileIOManager.h" 1.12 +#include "nsICacheEntry.h" 1.13 +#include "CacheHashUtils.h" 1.14 +#include "CacheFileChunk.h" 1.15 +#include "CacheFileUtils.h" 1.16 +#include "nsILoadContextInfo.h" 1.17 +#include "../cache/nsCacheUtils.h" 1.18 +#include "nsIFile.h" 1.19 +#include "mozilla/Telemetry.h" 1.20 +#include "mozilla/DebugOnly.h" 1.21 +#include "prnetdb.h" 1.22 + 1.23 + 1.24 +namespace mozilla { 1.25 +namespace net { 1.26 + 1.27 +#define kMinMetadataRead 1024 // TODO find optimal value from telemetry 1.28 +#define kAlignSize 4096 1.29 + 1.30 +#define kCacheEntryVersion 1 1.31 + 1.32 +NS_IMPL_ISUPPORTS(CacheFileMetadata, CacheFileIOListener) 1.33 + 1.34 +CacheFileMetadata::CacheFileMetadata(CacheFileHandle *aHandle, const nsACString &aKey) 1.35 + : CacheMemoryConsumer(NORMAL) 1.36 + , mHandle(aHandle) 1.37 + , mHashArray(nullptr) 1.38 + , mHashArraySize(0) 1.39 + , mHashCount(0) 1.40 + , mOffset(-1) 1.41 + , mBuf(nullptr) 1.42 + , mBufSize(0) 1.43 + , mWriteBuf(nullptr) 1.44 + , mElementsSize(0) 1.45 + , mIsDirty(false) 1.46 + , mAnonymous(false) 1.47 + , mInBrowser(false) 1.48 + , mAppId(nsILoadContextInfo::NO_APP_ID) 1.49 +{ 1.50 + LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, handle=%p, key=%s]", 1.51 + this, aHandle, PromiseFlatCString(aKey).get())); 1.52 + 1.53 + MOZ_COUNT_CTOR(CacheFileMetadata); 1.54 + memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader)); 1.55 + mMetaHdr.mVersion = kCacheEntryVersion; 1.56 + mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME; 1.57 + mKey = aKey; 1.58 + 1.59 + DebugOnly<nsresult> rv; 1.60 + rv = ParseKey(aKey); 1.61 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.62 +} 1.63 + 1.64 +CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly, const nsACString &aKey) 1.65 + : CacheMemoryConsumer(aMemoryOnly ? MEMORY_ONLY : NORMAL) 1.66 + , mHandle(nullptr) 1.67 + , mHashArray(nullptr) 1.68 + , mHashArraySize(0) 1.69 + , mHashCount(0) 1.70 + , mOffset(0) 1.71 + , mBuf(nullptr) 1.72 + , mBufSize(0) 1.73 + , mWriteBuf(nullptr) 1.74 + , mElementsSize(0) 1.75 + , mIsDirty(true) 1.76 + , mAnonymous(false) 1.77 + , mInBrowser(false) 1.78 + , mAppId(nsILoadContextInfo::NO_APP_ID) 1.79 +{ 1.80 + LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, key=%s]", 1.81 + this, PromiseFlatCString(aKey).get())); 1.82 + 1.83 + MOZ_COUNT_CTOR(CacheFileMetadata); 1.84 + memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader)); 1.85 + mMetaHdr.mVersion = kCacheEntryVersion; 1.86 + mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME; 1.87 + mMetaHdr.mFetchCount = 1; 1.88 + mKey = aKey; 1.89 + mMetaHdr.mKeySize = mKey.Length(); 1.90 + 1.91 + DebugOnly<nsresult> rv; 1.92 + rv = ParseKey(aKey); 1.93 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.94 +} 1.95 + 1.96 +CacheFileMetadata::CacheFileMetadata() 1.97 + : CacheMemoryConsumer(DONT_REPORT /* This is a helper class */) 1.98 + , mHandle(nullptr) 1.99 + , mHashArray(nullptr) 1.100 + , mHashArraySize(0) 1.101 + , mHashCount(0) 1.102 + , mOffset(0) 1.103 + , mBuf(nullptr) 1.104 + , mBufSize(0) 1.105 + , mWriteBuf(nullptr) 1.106 + , mElementsSize(0) 1.107 + , mIsDirty(false) 1.108 + , mAnonymous(false) 1.109 + , mInBrowser(false) 1.110 + , mAppId(nsILoadContextInfo::NO_APP_ID) 1.111 +{ 1.112 + LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p]", this)); 1.113 + 1.114 + MOZ_COUNT_CTOR(CacheFileMetadata); 1.115 + memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader)); 1.116 +} 1.117 + 1.118 +CacheFileMetadata::~CacheFileMetadata() 1.119 +{ 1.120 + LOG(("CacheFileMetadata::~CacheFileMetadata() [this=%p]", this)); 1.121 + 1.122 + MOZ_COUNT_DTOR(CacheFileMetadata); 1.123 + MOZ_ASSERT(!mListener); 1.124 + 1.125 + if (mHashArray) { 1.126 + free(mHashArray); 1.127 + mHashArray = nullptr; 1.128 + mHashArraySize = 0; 1.129 + } 1.130 + 1.131 + if (mBuf) { 1.132 + free(mBuf); 1.133 + mBuf = nullptr; 1.134 + mBufSize = 0; 1.135 + } 1.136 +} 1.137 + 1.138 +void 1.139 +CacheFileMetadata::SetHandle(CacheFileHandle *aHandle) 1.140 +{ 1.141 + LOG(("CacheFileMetadata::SetHandle() [this=%p, handle=%p]", this, aHandle)); 1.142 + 1.143 + MOZ_ASSERT(!mHandle); 1.144 + 1.145 + mHandle = aHandle; 1.146 +} 1.147 + 1.148 +nsresult 1.149 +CacheFileMetadata::GetKey(nsACString &_retval) 1.150 +{ 1.151 + _retval = mKey; 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +nsresult 1.156 +CacheFileMetadata::ReadMetadata(CacheFileMetadataListener *aListener) 1.157 +{ 1.158 + LOG(("CacheFileMetadata::ReadMetadata() [this=%p, listener=%p]", this, aListener)); 1.159 + 1.160 + MOZ_ASSERT(!mListener); 1.161 + MOZ_ASSERT(!mHashArray); 1.162 + MOZ_ASSERT(!mBuf); 1.163 + MOZ_ASSERT(!mWriteBuf); 1.164 + 1.165 + nsresult rv; 1.166 + 1.167 + int64_t size = mHandle->FileSize(); 1.168 + MOZ_ASSERT(size != -1); 1.169 + 1.170 + if (size == 0) { 1.171 + // this is a new entry 1.172 + LOG(("CacheFileMetadata::ReadMetadata() - Filesize == 0, creating empty " 1.173 + "metadata. [this=%p]", this)); 1.174 + 1.175 + InitEmptyMetadata(); 1.176 + aListener->OnMetadataRead(NS_OK); 1.177 + return NS_OK; 1.178 + } 1.179 + 1.180 + if (size < int64_t(sizeof(CacheFileMetadataHeader) + 2*sizeof(uint32_t))) { 1.181 + // there must be at least checksum, header and offset 1.182 + LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating " 1.183 + "empty metadata. [this=%p, filesize=%lld]", this, size)); 1.184 + 1.185 + InitEmptyMetadata(); 1.186 + aListener->OnMetadataRead(NS_OK); 1.187 + return NS_OK; 1.188 + } 1.189 + 1.190 + // round offset to 4k blocks 1.191 + int64_t offset = (size / kAlignSize) * kAlignSize; 1.192 + 1.193 + if (size - offset < kMinMetadataRead && offset > kAlignSize) 1.194 + offset -= kAlignSize; 1.195 + 1.196 + mBufSize = size - offset; 1.197 + mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); 1.198 + 1.199 + DoMemoryReport(MemoryUsage()); 1.200 + 1.201 + LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying " 1.202 + "offset=%lld, filesize=%lld [this=%p]", offset, size, this)); 1.203 + 1.204 + mListener = aListener; 1.205 + rv = CacheFileIOManager::Read(mHandle, offset, mBuf, mBufSize, true, this); 1.206 + if (NS_FAILED(rv)) { 1.207 + LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed" 1.208 + " synchronously, creating empty metadata. [this=%p, rv=0x%08x]", 1.209 + this, rv)); 1.210 + 1.211 + mListener = nullptr; 1.212 + InitEmptyMetadata(); 1.213 + aListener->OnMetadataRead(NS_OK); 1.214 + return NS_OK; 1.215 + } 1.216 + 1.217 + return NS_OK; 1.218 +} 1.219 + 1.220 +nsresult 1.221 +CacheFileMetadata::WriteMetadata(uint32_t aOffset, 1.222 + CacheFileMetadataListener *aListener) 1.223 +{ 1.224 + LOG(("CacheFileMetadata::WriteMetadata() [this=%p, offset=%d, listener=%p]", 1.225 + this, aOffset, aListener)); 1.226 + 1.227 + MOZ_ASSERT(!mListener); 1.228 + MOZ_ASSERT(!mWriteBuf); 1.229 + 1.230 + nsresult rv; 1.231 + 1.232 + mIsDirty = false; 1.233 + 1.234 + mWriteBuf = static_cast<char *>(moz_xmalloc(sizeof(uint32_t) + 1.235 + mHashCount * sizeof(CacheHash::Hash16_t) + 1.236 + sizeof(CacheFileMetadataHeader) + mKey.Length() + 1 + 1.237 + mElementsSize + sizeof(uint32_t))); 1.238 + 1.239 + char *p = mWriteBuf + sizeof(uint32_t); 1.240 + memcpy(p, mHashArray, mHashCount * sizeof(CacheHash::Hash16_t)); 1.241 + p += mHashCount * sizeof(CacheHash::Hash16_t); 1.242 + mMetaHdr.WriteToBuf(p); 1.243 + p += sizeof(CacheFileMetadataHeader); 1.244 + memcpy(p, mKey.get(), mKey.Length()); 1.245 + p += mKey.Length(); 1.246 + *p = 0; 1.247 + p++; 1.248 + memcpy(p, mBuf, mElementsSize); 1.249 + p += mElementsSize; 1.250 + 1.251 + CacheHash::Hash32_t hash; 1.252 + hash = CacheHash::Hash(mWriteBuf + sizeof(uint32_t), 1.253 + p - mWriteBuf - sizeof(uint32_t)); 1.254 + NetworkEndian::writeUint32(mWriteBuf, hash); 1.255 + 1.256 + NetworkEndian::writeUint32(p, aOffset); 1.257 + p += sizeof(uint32_t); 1.258 + 1.259 + char * writeBuffer; 1.260 + if (aListener) { 1.261 + mListener = aListener; 1.262 + writeBuffer = mWriteBuf; 1.263 + } else { 1.264 + // We are not going to pass |this| as callback to CacheFileIOManager::Write 1.265 + // so we must allocate a new buffer that will be released automatically when 1.266 + // write is finished. This is actually better than to let 1.267 + // CacheFileMetadata::OnDataWritten do the job, since when dispatching the 1.268 + // result from some reason fails during shutdown, we would unnecessarily leak 1.269 + // both this object and the buffer. 1.270 + writeBuffer = static_cast<char *>(moz_xmalloc(p - mWriteBuf)); 1.271 + memcpy(mWriteBuf, writeBuffer, p - mWriteBuf); 1.272 + } 1.273 + 1.274 + rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer, p - mWriteBuf, 1.275 + true, aListener ? this : nullptr); 1.276 + if (NS_FAILED(rv)) { 1.277 + LOG(("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() " 1.278 + "failed synchronously. [this=%p, rv=0x%08x]", this, rv)); 1.279 + 1.280 + mListener = nullptr; 1.281 + if (writeBuffer != mWriteBuf) { 1.282 + free(writeBuffer); 1.283 + } 1.284 + free(mWriteBuf); 1.285 + mWriteBuf = nullptr; 1.286 + NS_ENSURE_SUCCESS(rv, rv); 1.287 + } 1.288 + 1.289 + DoMemoryReport(MemoryUsage()); 1.290 + 1.291 + return NS_OK; 1.292 +} 1.293 + 1.294 +nsresult 1.295 +CacheFileMetadata::SyncReadMetadata(nsIFile *aFile) 1.296 +{ 1.297 + LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this)); 1.298 + 1.299 + MOZ_ASSERT(!mListener); 1.300 + MOZ_ASSERT(!mHandle); 1.301 + MOZ_ASSERT(!mHashArray); 1.302 + MOZ_ASSERT(!mBuf); 1.303 + MOZ_ASSERT(!mWriteBuf); 1.304 + MOZ_ASSERT(mKey.IsEmpty()); 1.305 + 1.306 + nsresult rv; 1.307 + 1.308 + int64_t fileSize; 1.309 + rv = aFile->GetFileSize(&fileSize); 1.310 + NS_ENSURE_SUCCESS(rv, rv); 1.311 + 1.312 + PRFileDesc *fd; 1.313 + rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0600, &fd); 1.314 + NS_ENSURE_SUCCESS(rv, rv); 1.315 + 1.316 + int64_t offset = PR_Seek64(fd, fileSize - sizeof(uint32_t), PR_SEEK_SET); 1.317 + if (offset == -1) { 1.318 + PR_Close(fd); 1.319 + return NS_ERROR_FAILURE; 1.320 + } 1.321 + 1.322 + uint32_t metaOffset; 1.323 + int32_t bytesRead = PR_Read(fd, &metaOffset, sizeof(uint32_t)); 1.324 + if (bytesRead != sizeof(uint32_t)) { 1.325 + PR_Close(fd); 1.326 + return NS_ERROR_FAILURE; 1.327 + } 1.328 + 1.329 + metaOffset = NetworkEndian::readUint32(&metaOffset); 1.330 + if (metaOffset > fileSize) { 1.331 + PR_Close(fd); 1.332 + return NS_ERROR_FAILURE; 1.333 + } 1.334 + 1.335 + mBufSize = fileSize - metaOffset; 1.336 + mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); 1.337 + 1.338 + DoMemoryReport(MemoryUsage()); 1.339 + 1.340 + offset = PR_Seek64(fd, metaOffset, PR_SEEK_SET); 1.341 + if (offset == -1) { 1.342 + PR_Close(fd); 1.343 + return NS_ERROR_FAILURE; 1.344 + } 1.345 + 1.346 + bytesRead = PR_Read(fd, mBuf, mBufSize); 1.347 + PR_Close(fd); 1.348 + if (bytesRead != static_cast<int32_t>(mBufSize)) { 1.349 + return NS_ERROR_FAILURE; 1.350 + } 1.351 + 1.352 + rv = ParseMetadata(metaOffset, 0, false); 1.353 + NS_ENSURE_SUCCESS(rv, rv); 1.354 + 1.355 + return NS_OK; 1.356 +} 1.357 + 1.358 +const char * 1.359 +CacheFileMetadata::GetElement(const char *aKey) 1.360 +{ 1.361 + const char *data = mBuf; 1.362 + const char *limit = mBuf + mElementsSize; 1.363 + 1.364 + while (data < limit) { 1.365 + // Point to the value part 1.366 + const char *value = data + strlen(data) + 1; 1.367 + MOZ_ASSERT(value < limit, "Metadata elements corrupted"); 1.368 + if (strcmp(data, aKey) == 0) { 1.369 + LOG(("CacheFileMetadata::GetElement() - Key found [this=%p, key=%s]", 1.370 + this, aKey)); 1.371 + return value; 1.372 + } 1.373 + 1.374 + // Skip value part 1.375 + data = value + strlen(value) + 1; 1.376 + } 1.377 + MOZ_ASSERT(data == limit, "Metadata elements corrupted"); 1.378 + LOG(("CacheFileMetadata::GetElement() - Key not found [this=%p, key=%s]", 1.379 + this, aKey)); 1.380 + return nullptr; 1.381 +} 1.382 + 1.383 +nsresult 1.384 +CacheFileMetadata::SetElement(const char *aKey, const char *aValue) 1.385 +{ 1.386 + LOG(("CacheFileMetadata::SetElement() [this=%p, key=%s, value=%p]", 1.387 + this, aKey, aValue)); 1.388 + 1.389 + MarkDirty(); 1.390 + 1.391 + const uint32_t keySize = strlen(aKey) + 1; 1.392 + char *pos = const_cast<char *>(GetElement(aKey)); 1.393 + 1.394 + if (!aValue) { 1.395 + // No value means remove the key/value pair completely, if existing 1.396 + if (pos) { 1.397 + uint32_t oldValueSize = strlen(pos) + 1; 1.398 + uint32_t offset = pos - mBuf; 1.399 + uint32_t remainder = mElementsSize - (offset + oldValueSize); 1.400 + 1.401 + memmove(pos - keySize, pos + oldValueSize, remainder); 1.402 + mElementsSize -= keySize + oldValueSize; 1.403 + } 1.404 + return NS_OK; 1.405 + } 1.406 + 1.407 + const uint32_t valueSize = strlen(aValue) + 1; 1.408 + uint32_t newSize = mElementsSize + valueSize; 1.409 + if (pos) { 1.410 + const uint32_t oldValueSize = strlen(pos) + 1; 1.411 + const uint32_t offset = pos - mBuf; 1.412 + const uint32_t remainder = mElementsSize - (offset + oldValueSize); 1.413 + 1.414 + // Update the value in place 1.415 + newSize -= oldValueSize; 1.416 + EnsureBuffer(newSize); 1.417 + 1.418 + // Move the remainder to the right place 1.419 + pos = mBuf + offset; 1.420 + memmove(pos + valueSize, pos + oldValueSize, remainder); 1.421 + } else { 1.422 + // allocate new meta data element 1.423 + newSize += keySize; 1.424 + EnsureBuffer(newSize); 1.425 + 1.426 + // Add after last element 1.427 + pos = mBuf + mElementsSize; 1.428 + memcpy(pos, aKey, keySize); 1.429 + pos += keySize; 1.430 + } 1.431 + 1.432 + // Update value 1.433 + memcpy(pos, aValue, valueSize); 1.434 + mElementsSize = newSize; 1.435 + 1.436 + return NS_OK; 1.437 +} 1.438 + 1.439 +CacheHash::Hash16_t 1.440 +CacheFileMetadata::GetHash(uint32_t aIndex) 1.441 +{ 1.442 + MOZ_ASSERT(aIndex < mHashCount); 1.443 + return NetworkEndian::readUint16(&mHashArray[aIndex]); 1.444 +} 1.445 + 1.446 +nsresult 1.447 +CacheFileMetadata::SetHash(uint32_t aIndex, CacheHash::Hash16_t aHash) 1.448 +{ 1.449 + LOG(("CacheFileMetadata::SetHash() [this=%p, idx=%d, hash=%x]", 1.450 + this, aIndex, aHash)); 1.451 + 1.452 + MarkDirty(); 1.453 + 1.454 + MOZ_ASSERT(aIndex <= mHashCount); 1.455 + 1.456 + if (aIndex > mHashCount) { 1.457 + return NS_ERROR_INVALID_ARG; 1.458 + } else if (aIndex == mHashCount) { 1.459 + if ((aIndex + 1) * sizeof(CacheHash::Hash16_t) > mHashArraySize) { 1.460 + // reallocate hash array buffer 1.461 + if (mHashArraySize == 0) 1.462 + mHashArraySize = 32 * sizeof(CacheHash::Hash16_t); 1.463 + else 1.464 + mHashArraySize *= 2; 1.465 + mHashArray = static_cast<CacheHash::Hash16_t *>( 1.466 + moz_xrealloc(mHashArray, mHashArraySize)); 1.467 + } 1.468 + 1.469 + mHashCount++; 1.470 + } 1.471 + 1.472 + NetworkEndian::writeUint16(&mHashArray[aIndex], aHash); 1.473 + 1.474 + DoMemoryReport(MemoryUsage()); 1.475 + 1.476 + return NS_OK; 1.477 +} 1.478 + 1.479 +nsresult 1.480 +CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime) 1.481 +{ 1.482 + LOG(("CacheFileMetadata::SetExpirationTime() [this=%p, expirationTime=%d]", 1.483 + this, aExpirationTime)); 1.484 + 1.485 + MarkDirty(); 1.486 + mMetaHdr.mExpirationTime = aExpirationTime; 1.487 + return NS_OK; 1.488 +} 1.489 + 1.490 +nsresult 1.491 +CacheFileMetadata::GetExpirationTime(uint32_t *_retval) 1.492 +{ 1.493 + *_retval = mMetaHdr.mExpirationTime; 1.494 + return NS_OK; 1.495 +} 1.496 + 1.497 +nsresult 1.498 +CacheFileMetadata::SetLastModified(uint32_t aLastModified) 1.499 +{ 1.500 + LOG(("CacheFileMetadata::SetLastModified() [this=%p, lastModified=%d]", 1.501 + this, aLastModified)); 1.502 + 1.503 + MarkDirty(); 1.504 + mMetaHdr.mLastModified = aLastModified; 1.505 + return NS_OK; 1.506 +} 1.507 + 1.508 +nsresult 1.509 +CacheFileMetadata::GetLastModified(uint32_t *_retval) 1.510 +{ 1.511 + *_retval = mMetaHdr.mLastModified; 1.512 + return NS_OK; 1.513 +} 1.514 + 1.515 +nsresult 1.516 +CacheFileMetadata::SetFrecency(uint32_t aFrecency) 1.517 +{ 1.518 + LOG(("CacheFileMetadata::SetFrecency() [this=%p, frecency=%f]", 1.519 + this, (double)aFrecency)); 1.520 + 1.521 + MarkDirty(); 1.522 + mMetaHdr.mFrecency = aFrecency; 1.523 + return NS_OK; 1.524 +} 1.525 + 1.526 +nsresult 1.527 +CacheFileMetadata::GetFrecency(uint32_t *_retval) 1.528 +{ 1.529 + *_retval = mMetaHdr.mFrecency; 1.530 + return NS_OK; 1.531 +} 1.532 + 1.533 +nsresult 1.534 +CacheFileMetadata::GetLastFetched(uint32_t *_retval) 1.535 +{ 1.536 + *_retval = mMetaHdr.mLastFetched; 1.537 + return NS_OK; 1.538 +} 1.539 + 1.540 +nsresult 1.541 +CacheFileMetadata::GetFetchCount(uint32_t *_retval) 1.542 +{ 1.543 + *_retval = mMetaHdr.mFetchCount; 1.544 + return NS_OK; 1.545 +} 1.546 + 1.547 +nsresult 1.548 +CacheFileMetadata::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) 1.549 +{ 1.550 + MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!"); 1.551 + return NS_ERROR_UNEXPECTED; 1.552 +} 1.553 + 1.554 +nsresult 1.555 +CacheFileMetadata::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, 1.556 + nsresult aResult) 1.557 +{ 1.558 + LOG(("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, result=0x%08x]", 1.559 + this, aHandle, aResult)); 1.560 + 1.561 + MOZ_ASSERT(mListener); 1.562 + MOZ_ASSERT(mWriteBuf); 1.563 + 1.564 + free(mWriteBuf); 1.565 + mWriteBuf = nullptr; 1.566 + 1.567 + nsCOMPtr<CacheFileMetadataListener> listener; 1.568 + 1.569 + mListener.swap(listener); 1.570 + listener->OnMetadataWritten(aResult); 1.571 + 1.572 + DoMemoryReport(MemoryUsage()); 1.573 + 1.574 + return NS_OK; 1.575 +} 1.576 + 1.577 +nsresult 1.578 +CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf, 1.579 + nsresult aResult) 1.580 +{ 1.581 + LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]", 1.582 + this, aHandle, aResult)); 1.583 + 1.584 + MOZ_ASSERT(mListener); 1.585 + 1.586 + nsresult rv, retval; 1.587 + nsCOMPtr<CacheFileMetadataListener> listener; 1.588 + 1.589 + if (NS_FAILED(aResult)) { 1.590 + LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed" 1.591 + ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult)); 1.592 + 1.593 + InitEmptyMetadata(); 1.594 + retval = NS_OK; 1.595 + 1.596 + mListener.swap(listener); 1.597 + listener->OnMetadataRead(retval); 1.598 + return NS_OK; 1.599 + } 1.600 + 1.601 + // check whether we have read all necessary data 1.602 + uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize - 1.603 + sizeof(uint32_t)); 1.604 + 1.605 + int64_t size = mHandle->FileSize(); 1.606 + MOZ_ASSERT(size != -1); 1.607 + 1.608 + if (realOffset >= size) { 1.609 + LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating " 1.610 + "empty metadata. [this=%p, realOffset=%d, size=%lld]", this, 1.611 + realOffset, size)); 1.612 + 1.613 + InitEmptyMetadata(); 1.614 + retval = NS_OK; 1.615 + 1.616 + mListener.swap(listener); 1.617 + listener->OnMetadataRead(retval); 1.618 + return NS_OK; 1.619 + } 1.620 + 1.621 + uint32_t usedOffset = size - mBufSize; 1.622 + 1.623 + if (realOffset < usedOffset) { 1.624 + uint32_t missing = usedOffset - realOffset; 1.625 + // we need to read more data 1.626 + mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize + missing)); 1.627 + memmove(mBuf + missing, mBuf, mBufSize); 1.628 + mBufSize += missing; 1.629 + 1.630 + DoMemoryReport(MemoryUsage()); 1.631 + 1.632 + LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to " 1.633 + "have full metadata. [this=%p]", missing, this)); 1.634 + 1.635 + rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, true, this); 1.636 + if (NS_FAILED(rv)) { 1.637 + LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() " 1.638 + "failed synchronously, creating empty metadata. [this=%p, " 1.639 + "rv=0x%08x]", this, rv)); 1.640 + 1.641 + InitEmptyMetadata(); 1.642 + retval = NS_OK; 1.643 + 1.644 + mListener.swap(listener); 1.645 + listener->OnMetadataRead(retval); 1.646 + return NS_OK; 1.647 + } 1.648 + 1.649 + return NS_OK; 1.650 + } 1.651 + 1.652 + // We have all data according to offset information at the end of the entry. 1.653 + // Try to parse it. 1.654 + rv = ParseMetadata(realOffset, realOffset - usedOffset, true); 1.655 + if (NS_FAILED(rv)) { 1.656 + LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating " 1.657 + "empty metadata. [this=%p]", this)); 1.658 + InitEmptyMetadata(); 1.659 + retval = NS_OK; 1.660 + } 1.661 + else { 1.662 + retval = NS_OK; 1.663 + } 1.664 + 1.665 + mListener.swap(listener); 1.666 + listener->OnMetadataRead(retval); 1.667 + 1.668 + return NS_OK; 1.669 +} 1.670 + 1.671 +nsresult 1.672 +CacheFileMetadata::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) 1.673 +{ 1.674 + MOZ_CRASH("CacheFileMetadata::OnFileDoomed should not be called!"); 1.675 + return NS_ERROR_UNEXPECTED; 1.676 +} 1.677 + 1.678 +nsresult 1.679 +CacheFileMetadata::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) 1.680 +{ 1.681 + MOZ_CRASH("CacheFileMetadata::OnEOFSet should not be called!"); 1.682 + return NS_ERROR_UNEXPECTED; 1.683 +} 1.684 + 1.685 +nsresult 1.686 +CacheFileMetadata::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) 1.687 +{ 1.688 + MOZ_CRASH("CacheFileMetadata::OnFileRenamed should not be called!"); 1.689 + return NS_ERROR_UNEXPECTED; 1.690 +} 1.691 + 1.692 +void 1.693 +CacheFileMetadata::InitEmptyMetadata() 1.694 +{ 1.695 + if (mBuf) { 1.696 + free(mBuf); 1.697 + mBuf = nullptr; 1.698 + mBufSize = 0; 1.699 + } 1.700 + mOffset = 0; 1.701 + mMetaHdr.mVersion = kCacheEntryVersion; 1.702 + mMetaHdr.mFetchCount = 1; 1.703 + mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME; 1.704 + mMetaHdr.mKeySize = mKey.Length(); 1.705 + 1.706 + DoMemoryReport(MemoryUsage()); 1.707 +} 1.708 + 1.709 +nsresult 1.710 +CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset, 1.711 + bool aHaveKey) 1.712 +{ 1.713 + LOG(("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, " 1.714 + "bufOffset=%d, haveKey=%u]", this, aMetaOffset, aBufOffset, aHaveKey)); 1.715 + 1.716 + nsresult rv; 1.717 + 1.718 + uint32_t metaposOffset = mBufSize - sizeof(uint32_t); 1.719 + uint32_t hashesOffset = aBufOffset + sizeof(uint32_t); 1.720 + uint32_t hashCount = aMetaOffset / kChunkSize; 1.721 + if (aMetaOffset % kChunkSize) 1.722 + hashCount++; 1.723 + uint32_t hashesLen = hashCount * sizeof(CacheHash::Hash16_t); 1.724 + uint32_t hdrOffset = hashesOffset + hashesLen; 1.725 + uint32_t keyOffset = hdrOffset + sizeof(CacheFileMetadataHeader); 1.726 + 1.727 + LOG(("CacheFileMetadata::ParseMetadata() [this=%p]\n metaposOffset=%d\n " 1.728 + "hashesOffset=%d\n hashCount=%d\n hashesLen=%d\n hdfOffset=%d\n " 1.729 + "keyOffset=%d\n", this, metaposOffset, hashesOffset, hashCount, 1.730 + hashesLen,hdrOffset, keyOffset)); 1.731 + 1.732 + if (keyOffset > metaposOffset) { 1.733 + LOG(("CacheFileMetadata::ParseMetadata() - Wrong keyOffset! [this=%p]", 1.734 + this)); 1.735 + return NS_ERROR_FILE_CORRUPTED; 1.736 + } 1.737 + 1.738 + mMetaHdr.ReadFromBuf(mBuf + hdrOffset); 1.739 + 1.740 + if (mMetaHdr.mVersion != kCacheEntryVersion) { 1.741 + LOG(("CacheFileMetadata::ParseMetadata() - Not a version we understand to. " 1.742 + "[version=0x%x, this=%p]", mMetaHdr.mVersion, this)); 1.743 + return NS_ERROR_UNEXPECTED; 1.744 + } 1.745 + 1.746 + uint32_t elementsOffset = mMetaHdr.mKeySize + keyOffset + 1; 1.747 + 1.748 + if (elementsOffset > metaposOffset) { 1.749 + LOG(("CacheFileMetadata::ParseMetadata() - Wrong elementsOffset %d " 1.750 + "[this=%p]", elementsOffset, this)); 1.751 + return NS_ERROR_FILE_CORRUPTED; 1.752 + } 1.753 + 1.754 + // check that key ends with \0 1.755 + if (mBuf[elementsOffset - 1] != 0) { 1.756 + LOG(("CacheFileMetadata::ParseMetadata() - Elements not null terminated. " 1.757 + "[this=%p]", this)); 1.758 + return NS_ERROR_FILE_CORRUPTED; 1.759 + } 1.760 + 1.761 + 1.762 + if (!aHaveKey) { 1.763 + // get the key form metadata 1.764 + mKey.Assign(mBuf + keyOffset, mMetaHdr.mKeySize); 1.765 + 1.766 + rv = ParseKey(mKey); 1.767 + if (NS_FAILED(rv)) 1.768 + return rv; 1.769 + } 1.770 + else { 1.771 + if (mMetaHdr.mKeySize != mKey.Length()) { 1.772 + LOG(("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s " 1.773 + "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(), 1.774 + this)); 1.775 + return NS_ERROR_FILE_CORRUPTED; 1.776 + } 1.777 + 1.778 + if (memcmp(mKey.get(), mBuf + keyOffset, mKey.Length()) != 0) { 1.779 + LOG(("CacheFileMetadata::ParseMetadata() - Key collision (2), key=%s " 1.780 + "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(), 1.781 + this)); 1.782 + return NS_ERROR_FILE_CORRUPTED; 1.783 + } 1.784 + } 1.785 + 1.786 + // check metadata hash (data from hashesOffset to metaposOffset) 1.787 + CacheHash::Hash32_t hashComputed, hashExpected; 1.788 + hashComputed = CacheHash::Hash(mBuf + hashesOffset, 1.789 + metaposOffset - hashesOffset); 1.790 + hashExpected = NetworkEndian::readUint32(mBuf + aBufOffset); 1.791 + 1.792 + if (hashComputed != hashExpected) { 1.793 + LOG(("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of " 1.794 + "the metadata is %x, hash in file is %x [this=%p]", hashComputed, 1.795 + hashExpected, this)); 1.796 + return NS_ERROR_FILE_CORRUPTED; 1.797 + } 1.798 + 1.799 + // check elements 1.800 + rv = CheckElements(mBuf + elementsOffset, metaposOffset - elementsOffset); 1.801 + if (NS_FAILED(rv)) 1.802 + return rv; 1.803 + 1.804 + mHashArraySize = hashesLen; 1.805 + mHashCount = hashCount; 1.806 + if (mHashArraySize) { 1.807 + mHashArray = static_cast<CacheHash::Hash16_t *>( 1.808 + moz_xmalloc(mHashArraySize)); 1.809 + memcpy(mHashArray, mBuf + hashesOffset, mHashArraySize); 1.810 + } 1.811 + 1.812 + 1.813 + mMetaHdr.mFetchCount++; 1.814 + MarkDirty(); 1.815 + 1.816 + mElementsSize = metaposOffset - elementsOffset; 1.817 + memmove(mBuf, mBuf + elementsOffset, mElementsSize); 1.818 + mOffset = aMetaOffset; 1.819 + 1.820 + // TODO: shrink memory if buffer is too big 1.821 + 1.822 + DoMemoryReport(MemoryUsage()); 1.823 + 1.824 + return NS_OK; 1.825 +} 1.826 + 1.827 +nsresult 1.828 +CacheFileMetadata::CheckElements(const char *aBuf, uint32_t aSize) 1.829 +{ 1.830 + if (aSize) { 1.831 + // Check if the metadata ends with a zero byte. 1.832 + if (aBuf[aSize - 1] != 0) { 1.833 + NS_ERROR("Metadata elements are not null terminated"); 1.834 + LOG(("CacheFileMetadata::CheckElements() - Elements are not null " 1.835 + "terminated. [this=%p]", this)); 1.836 + return NS_ERROR_FILE_CORRUPTED; 1.837 + } 1.838 + // Check that there are an even number of zero bytes 1.839 + // to match the pattern { key \0 value \0 } 1.840 + bool odd = false; 1.841 + for (uint32_t i = 0; i < aSize; i++) { 1.842 + if (aBuf[i] == 0) 1.843 + odd = !odd; 1.844 + } 1.845 + if (odd) { 1.846 + NS_ERROR("Metadata elements are malformed"); 1.847 + LOG(("CacheFileMetadata::CheckElements() - Elements are malformed. " 1.848 + "[this=%p]", this)); 1.849 + return NS_ERROR_FILE_CORRUPTED; 1.850 + } 1.851 + } 1.852 + return NS_OK; 1.853 +} 1.854 + 1.855 +void 1.856 +CacheFileMetadata::EnsureBuffer(uint32_t aSize) 1.857 +{ 1.858 + if (mBufSize < aSize) { 1.859 + mBufSize = aSize; 1.860 + mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize)); 1.861 + } 1.862 + 1.863 + DoMemoryReport(MemoryUsage()); 1.864 +} 1.865 + 1.866 +nsresult 1.867 +CacheFileMetadata::ParseKey(const nsACString &aKey) 1.868 +{ 1.869 + nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey); 1.870 + NS_ENSURE_TRUE(info, NS_ERROR_FAILURE); 1.871 + 1.872 + mAnonymous = info->IsAnonymous(); 1.873 + mAppId = info->AppId(); 1.874 + mInBrowser = info->IsInBrowserElement(); 1.875 + 1.876 + return NS_OK; 1.877 +} 1.878 + 1.879 +// Memory reporting 1.880 + 1.881 +size_t 1.882 +CacheFileMetadata::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.883 +{ 1.884 + size_t n = 0; 1.885 + // mHandle reported via CacheFileIOManager. 1.886 + n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf); 1.887 + n += mallocSizeOf(mHashArray); 1.888 + n += mallocSizeOf(mBuf); 1.889 + n += mallocSizeOf(mWriteBuf); 1.890 + // mListener is usually the owning CacheFile. 1.891 + 1.892 + return n; 1.893 +} 1.894 + 1.895 +size_t 1.896 +CacheFileMetadata::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.897 +{ 1.898 + return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); 1.899 +} 1.900 + 1.901 +} // net 1.902 +} // mozilla