netwerk/cache2/CacheFileMetadata.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "CacheLog.h"
michael@0 6 #include "CacheFileMetadata.h"
michael@0 7
michael@0 8 #include "CacheFileIOManager.h"
michael@0 9 #include "nsICacheEntry.h"
michael@0 10 #include "CacheHashUtils.h"
michael@0 11 #include "CacheFileChunk.h"
michael@0 12 #include "CacheFileUtils.h"
michael@0 13 #include "nsILoadContextInfo.h"
michael@0 14 #include "../cache/nsCacheUtils.h"
michael@0 15 #include "nsIFile.h"
michael@0 16 #include "mozilla/Telemetry.h"
michael@0 17 #include "mozilla/DebugOnly.h"
michael@0 18 #include "prnetdb.h"
michael@0 19
michael@0 20
michael@0 21 namespace mozilla {
michael@0 22 namespace net {
michael@0 23
michael@0 24 #define kMinMetadataRead 1024 // TODO find optimal value from telemetry
michael@0 25 #define kAlignSize 4096
michael@0 26
michael@0 27 #define kCacheEntryVersion 1
michael@0 28
michael@0 29 NS_IMPL_ISUPPORTS(CacheFileMetadata, CacheFileIOListener)
michael@0 30
michael@0 31 CacheFileMetadata::CacheFileMetadata(CacheFileHandle *aHandle, const nsACString &aKey)
michael@0 32 : CacheMemoryConsumer(NORMAL)
michael@0 33 , mHandle(aHandle)
michael@0 34 , mHashArray(nullptr)
michael@0 35 , mHashArraySize(0)
michael@0 36 , mHashCount(0)
michael@0 37 , mOffset(-1)
michael@0 38 , mBuf(nullptr)
michael@0 39 , mBufSize(0)
michael@0 40 , mWriteBuf(nullptr)
michael@0 41 , mElementsSize(0)
michael@0 42 , mIsDirty(false)
michael@0 43 , mAnonymous(false)
michael@0 44 , mInBrowser(false)
michael@0 45 , mAppId(nsILoadContextInfo::NO_APP_ID)
michael@0 46 {
michael@0 47 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, handle=%p, key=%s]",
michael@0 48 this, aHandle, PromiseFlatCString(aKey).get()));
michael@0 49
michael@0 50 MOZ_COUNT_CTOR(CacheFileMetadata);
michael@0 51 memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
michael@0 52 mMetaHdr.mVersion = kCacheEntryVersion;
michael@0 53 mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
michael@0 54 mKey = aKey;
michael@0 55
michael@0 56 DebugOnly<nsresult> rv;
michael@0 57 rv = ParseKey(aKey);
michael@0 58 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 59 }
michael@0 60
michael@0 61 CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly, const nsACString &aKey)
michael@0 62 : CacheMemoryConsumer(aMemoryOnly ? MEMORY_ONLY : NORMAL)
michael@0 63 , mHandle(nullptr)
michael@0 64 , mHashArray(nullptr)
michael@0 65 , mHashArraySize(0)
michael@0 66 , mHashCount(0)
michael@0 67 , mOffset(0)
michael@0 68 , mBuf(nullptr)
michael@0 69 , mBufSize(0)
michael@0 70 , mWriteBuf(nullptr)
michael@0 71 , mElementsSize(0)
michael@0 72 , mIsDirty(true)
michael@0 73 , mAnonymous(false)
michael@0 74 , mInBrowser(false)
michael@0 75 , mAppId(nsILoadContextInfo::NO_APP_ID)
michael@0 76 {
michael@0 77 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, key=%s]",
michael@0 78 this, PromiseFlatCString(aKey).get()));
michael@0 79
michael@0 80 MOZ_COUNT_CTOR(CacheFileMetadata);
michael@0 81 memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
michael@0 82 mMetaHdr.mVersion = kCacheEntryVersion;
michael@0 83 mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
michael@0 84 mMetaHdr.mFetchCount = 1;
michael@0 85 mKey = aKey;
michael@0 86 mMetaHdr.mKeySize = mKey.Length();
michael@0 87
michael@0 88 DebugOnly<nsresult> rv;
michael@0 89 rv = ParseKey(aKey);
michael@0 90 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 91 }
michael@0 92
michael@0 93 CacheFileMetadata::CacheFileMetadata()
michael@0 94 : CacheMemoryConsumer(DONT_REPORT /* This is a helper class */)
michael@0 95 , mHandle(nullptr)
michael@0 96 , mHashArray(nullptr)
michael@0 97 , mHashArraySize(0)
michael@0 98 , mHashCount(0)
michael@0 99 , mOffset(0)
michael@0 100 , mBuf(nullptr)
michael@0 101 , mBufSize(0)
michael@0 102 , mWriteBuf(nullptr)
michael@0 103 , mElementsSize(0)
michael@0 104 , mIsDirty(false)
michael@0 105 , mAnonymous(false)
michael@0 106 , mInBrowser(false)
michael@0 107 , mAppId(nsILoadContextInfo::NO_APP_ID)
michael@0 108 {
michael@0 109 LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p]", this));
michael@0 110
michael@0 111 MOZ_COUNT_CTOR(CacheFileMetadata);
michael@0 112 memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
michael@0 113 }
michael@0 114
michael@0 115 CacheFileMetadata::~CacheFileMetadata()
michael@0 116 {
michael@0 117 LOG(("CacheFileMetadata::~CacheFileMetadata() [this=%p]", this));
michael@0 118
michael@0 119 MOZ_COUNT_DTOR(CacheFileMetadata);
michael@0 120 MOZ_ASSERT(!mListener);
michael@0 121
michael@0 122 if (mHashArray) {
michael@0 123 free(mHashArray);
michael@0 124 mHashArray = nullptr;
michael@0 125 mHashArraySize = 0;
michael@0 126 }
michael@0 127
michael@0 128 if (mBuf) {
michael@0 129 free(mBuf);
michael@0 130 mBuf = nullptr;
michael@0 131 mBufSize = 0;
michael@0 132 }
michael@0 133 }
michael@0 134
michael@0 135 void
michael@0 136 CacheFileMetadata::SetHandle(CacheFileHandle *aHandle)
michael@0 137 {
michael@0 138 LOG(("CacheFileMetadata::SetHandle() [this=%p, handle=%p]", this, aHandle));
michael@0 139
michael@0 140 MOZ_ASSERT(!mHandle);
michael@0 141
michael@0 142 mHandle = aHandle;
michael@0 143 }
michael@0 144
michael@0 145 nsresult
michael@0 146 CacheFileMetadata::GetKey(nsACString &_retval)
michael@0 147 {
michael@0 148 _retval = mKey;
michael@0 149 return NS_OK;
michael@0 150 }
michael@0 151
michael@0 152 nsresult
michael@0 153 CacheFileMetadata::ReadMetadata(CacheFileMetadataListener *aListener)
michael@0 154 {
michael@0 155 LOG(("CacheFileMetadata::ReadMetadata() [this=%p, listener=%p]", this, aListener));
michael@0 156
michael@0 157 MOZ_ASSERT(!mListener);
michael@0 158 MOZ_ASSERT(!mHashArray);
michael@0 159 MOZ_ASSERT(!mBuf);
michael@0 160 MOZ_ASSERT(!mWriteBuf);
michael@0 161
michael@0 162 nsresult rv;
michael@0 163
michael@0 164 int64_t size = mHandle->FileSize();
michael@0 165 MOZ_ASSERT(size != -1);
michael@0 166
michael@0 167 if (size == 0) {
michael@0 168 // this is a new entry
michael@0 169 LOG(("CacheFileMetadata::ReadMetadata() - Filesize == 0, creating empty "
michael@0 170 "metadata. [this=%p]", this));
michael@0 171
michael@0 172 InitEmptyMetadata();
michael@0 173 aListener->OnMetadataRead(NS_OK);
michael@0 174 return NS_OK;
michael@0 175 }
michael@0 176
michael@0 177 if (size < int64_t(sizeof(CacheFileMetadataHeader) + 2*sizeof(uint32_t))) {
michael@0 178 // there must be at least checksum, header and offset
michael@0 179 LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating "
michael@0 180 "empty metadata. [this=%p, filesize=%lld]", this, size));
michael@0 181
michael@0 182 InitEmptyMetadata();
michael@0 183 aListener->OnMetadataRead(NS_OK);
michael@0 184 return NS_OK;
michael@0 185 }
michael@0 186
michael@0 187 // round offset to 4k blocks
michael@0 188 int64_t offset = (size / kAlignSize) * kAlignSize;
michael@0 189
michael@0 190 if (size - offset < kMinMetadataRead && offset > kAlignSize)
michael@0 191 offset -= kAlignSize;
michael@0 192
michael@0 193 mBufSize = size - offset;
michael@0 194 mBuf = static_cast<char *>(moz_xmalloc(mBufSize));
michael@0 195
michael@0 196 DoMemoryReport(MemoryUsage());
michael@0 197
michael@0 198 LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying "
michael@0 199 "offset=%lld, filesize=%lld [this=%p]", offset, size, this));
michael@0 200
michael@0 201 mListener = aListener;
michael@0 202 rv = CacheFileIOManager::Read(mHandle, offset, mBuf, mBufSize, true, this);
michael@0 203 if (NS_FAILED(rv)) {
michael@0 204 LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed"
michael@0 205 " synchronously, creating empty metadata. [this=%p, rv=0x%08x]",
michael@0 206 this, rv));
michael@0 207
michael@0 208 mListener = nullptr;
michael@0 209 InitEmptyMetadata();
michael@0 210 aListener->OnMetadataRead(NS_OK);
michael@0 211 return NS_OK;
michael@0 212 }
michael@0 213
michael@0 214 return NS_OK;
michael@0 215 }
michael@0 216
michael@0 217 nsresult
michael@0 218 CacheFileMetadata::WriteMetadata(uint32_t aOffset,
michael@0 219 CacheFileMetadataListener *aListener)
michael@0 220 {
michael@0 221 LOG(("CacheFileMetadata::WriteMetadata() [this=%p, offset=%d, listener=%p]",
michael@0 222 this, aOffset, aListener));
michael@0 223
michael@0 224 MOZ_ASSERT(!mListener);
michael@0 225 MOZ_ASSERT(!mWriteBuf);
michael@0 226
michael@0 227 nsresult rv;
michael@0 228
michael@0 229 mIsDirty = false;
michael@0 230
michael@0 231 mWriteBuf = static_cast<char *>(moz_xmalloc(sizeof(uint32_t) +
michael@0 232 mHashCount * sizeof(CacheHash::Hash16_t) +
michael@0 233 sizeof(CacheFileMetadataHeader) + mKey.Length() + 1 +
michael@0 234 mElementsSize + sizeof(uint32_t)));
michael@0 235
michael@0 236 char *p = mWriteBuf + sizeof(uint32_t);
michael@0 237 memcpy(p, mHashArray, mHashCount * sizeof(CacheHash::Hash16_t));
michael@0 238 p += mHashCount * sizeof(CacheHash::Hash16_t);
michael@0 239 mMetaHdr.WriteToBuf(p);
michael@0 240 p += sizeof(CacheFileMetadataHeader);
michael@0 241 memcpy(p, mKey.get(), mKey.Length());
michael@0 242 p += mKey.Length();
michael@0 243 *p = 0;
michael@0 244 p++;
michael@0 245 memcpy(p, mBuf, mElementsSize);
michael@0 246 p += mElementsSize;
michael@0 247
michael@0 248 CacheHash::Hash32_t hash;
michael@0 249 hash = CacheHash::Hash(mWriteBuf + sizeof(uint32_t),
michael@0 250 p - mWriteBuf - sizeof(uint32_t));
michael@0 251 NetworkEndian::writeUint32(mWriteBuf, hash);
michael@0 252
michael@0 253 NetworkEndian::writeUint32(p, aOffset);
michael@0 254 p += sizeof(uint32_t);
michael@0 255
michael@0 256 char * writeBuffer;
michael@0 257 if (aListener) {
michael@0 258 mListener = aListener;
michael@0 259 writeBuffer = mWriteBuf;
michael@0 260 } else {
michael@0 261 // We are not going to pass |this| as callback to CacheFileIOManager::Write
michael@0 262 // so we must allocate a new buffer that will be released automatically when
michael@0 263 // write is finished. This is actually better than to let
michael@0 264 // CacheFileMetadata::OnDataWritten do the job, since when dispatching the
michael@0 265 // result from some reason fails during shutdown, we would unnecessarily leak
michael@0 266 // both this object and the buffer.
michael@0 267 writeBuffer = static_cast<char *>(moz_xmalloc(p - mWriteBuf));
michael@0 268 memcpy(mWriteBuf, writeBuffer, p - mWriteBuf);
michael@0 269 }
michael@0 270
michael@0 271 rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer, p - mWriteBuf,
michael@0 272 true, aListener ? this : nullptr);
michael@0 273 if (NS_FAILED(rv)) {
michael@0 274 LOG(("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() "
michael@0 275 "failed synchronously. [this=%p, rv=0x%08x]", this, rv));
michael@0 276
michael@0 277 mListener = nullptr;
michael@0 278 if (writeBuffer != mWriteBuf) {
michael@0 279 free(writeBuffer);
michael@0 280 }
michael@0 281 free(mWriteBuf);
michael@0 282 mWriteBuf = nullptr;
michael@0 283 NS_ENSURE_SUCCESS(rv, rv);
michael@0 284 }
michael@0 285
michael@0 286 DoMemoryReport(MemoryUsage());
michael@0 287
michael@0 288 return NS_OK;
michael@0 289 }
michael@0 290
michael@0 291 nsresult
michael@0 292 CacheFileMetadata::SyncReadMetadata(nsIFile *aFile)
michael@0 293 {
michael@0 294 LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this));
michael@0 295
michael@0 296 MOZ_ASSERT(!mListener);
michael@0 297 MOZ_ASSERT(!mHandle);
michael@0 298 MOZ_ASSERT(!mHashArray);
michael@0 299 MOZ_ASSERT(!mBuf);
michael@0 300 MOZ_ASSERT(!mWriteBuf);
michael@0 301 MOZ_ASSERT(mKey.IsEmpty());
michael@0 302
michael@0 303 nsresult rv;
michael@0 304
michael@0 305 int64_t fileSize;
michael@0 306 rv = aFile->GetFileSize(&fileSize);
michael@0 307 NS_ENSURE_SUCCESS(rv, rv);
michael@0 308
michael@0 309 PRFileDesc *fd;
michael@0 310 rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0600, &fd);
michael@0 311 NS_ENSURE_SUCCESS(rv, rv);
michael@0 312
michael@0 313 int64_t offset = PR_Seek64(fd, fileSize - sizeof(uint32_t), PR_SEEK_SET);
michael@0 314 if (offset == -1) {
michael@0 315 PR_Close(fd);
michael@0 316 return NS_ERROR_FAILURE;
michael@0 317 }
michael@0 318
michael@0 319 uint32_t metaOffset;
michael@0 320 int32_t bytesRead = PR_Read(fd, &metaOffset, sizeof(uint32_t));
michael@0 321 if (bytesRead != sizeof(uint32_t)) {
michael@0 322 PR_Close(fd);
michael@0 323 return NS_ERROR_FAILURE;
michael@0 324 }
michael@0 325
michael@0 326 metaOffset = NetworkEndian::readUint32(&metaOffset);
michael@0 327 if (metaOffset > fileSize) {
michael@0 328 PR_Close(fd);
michael@0 329 return NS_ERROR_FAILURE;
michael@0 330 }
michael@0 331
michael@0 332 mBufSize = fileSize - metaOffset;
michael@0 333 mBuf = static_cast<char *>(moz_xmalloc(mBufSize));
michael@0 334
michael@0 335 DoMemoryReport(MemoryUsage());
michael@0 336
michael@0 337 offset = PR_Seek64(fd, metaOffset, PR_SEEK_SET);
michael@0 338 if (offset == -1) {
michael@0 339 PR_Close(fd);
michael@0 340 return NS_ERROR_FAILURE;
michael@0 341 }
michael@0 342
michael@0 343 bytesRead = PR_Read(fd, mBuf, mBufSize);
michael@0 344 PR_Close(fd);
michael@0 345 if (bytesRead != static_cast<int32_t>(mBufSize)) {
michael@0 346 return NS_ERROR_FAILURE;
michael@0 347 }
michael@0 348
michael@0 349 rv = ParseMetadata(metaOffset, 0, false);
michael@0 350 NS_ENSURE_SUCCESS(rv, rv);
michael@0 351
michael@0 352 return NS_OK;
michael@0 353 }
michael@0 354
michael@0 355 const char *
michael@0 356 CacheFileMetadata::GetElement(const char *aKey)
michael@0 357 {
michael@0 358 const char *data = mBuf;
michael@0 359 const char *limit = mBuf + mElementsSize;
michael@0 360
michael@0 361 while (data < limit) {
michael@0 362 // Point to the value part
michael@0 363 const char *value = data + strlen(data) + 1;
michael@0 364 MOZ_ASSERT(value < limit, "Metadata elements corrupted");
michael@0 365 if (strcmp(data, aKey) == 0) {
michael@0 366 LOG(("CacheFileMetadata::GetElement() - Key found [this=%p, key=%s]",
michael@0 367 this, aKey));
michael@0 368 return value;
michael@0 369 }
michael@0 370
michael@0 371 // Skip value part
michael@0 372 data = value + strlen(value) + 1;
michael@0 373 }
michael@0 374 MOZ_ASSERT(data == limit, "Metadata elements corrupted");
michael@0 375 LOG(("CacheFileMetadata::GetElement() - Key not found [this=%p, key=%s]",
michael@0 376 this, aKey));
michael@0 377 return nullptr;
michael@0 378 }
michael@0 379
michael@0 380 nsresult
michael@0 381 CacheFileMetadata::SetElement(const char *aKey, const char *aValue)
michael@0 382 {
michael@0 383 LOG(("CacheFileMetadata::SetElement() [this=%p, key=%s, value=%p]",
michael@0 384 this, aKey, aValue));
michael@0 385
michael@0 386 MarkDirty();
michael@0 387
michael@0 388 const uint32_t keySize = strlen(aKey) + 1;
michael@0 389 char *pos = const_cast<char *>(GetElement(aKey));
michael@0 390
michael@0 391 if (!aValue) {
michael@0 392 // No value means remove the key/value pair completely, if existing
michael@0 393 if (pos) {
michael@0 394 uint32_t oldValueSize = strlen(pos) + 1;
michael@0 395 uint32_t offset = pos - mBuf;
michael@0 396 uint32_t remainder = mElementsSize - (offset + oldValueSize);
michael@0 397
michael@0 398 memmove(pos - keySize, pos + oldValueSize, remainder);
michael@0 399 mElementsSize -= keySize + oldValueSize;
michael@0 400 }
michael@0 401 return NS_OK;
michael@0 402 }
michael@0 403
michael@0 404 const uint32_t valueSize = strlen(aValue) + 1;
michael@0 405 uint32_t newSize = mElementsSize + valueSize;
michael@0 406 if (pos) {
michael@0 407 const uint32_t oldValueSize = strlen(pos) + 1;
michael@0 408 const uint32_t offset = pos - mBuf;
michael@0 409 const uint32_t remainder = mElementsSize - (offset + oldValueSize);
michael@0 410
michael@0 411 // Update the value in place
michael@0 412 newSize -= oldValueSize;
michael@0 413 EnsureBuffer(newSize);
michael@0 414
michael@0 415 // Move the remainder to the right place
michael@0 416 pos = mBuf + offset;
michael@0 417 memmove(pos + valueSize, pos + oldValueSize, remainder);
michael@0 418 } else {
michael@0 419 // allocate new meta data element
michael@0 420 newSize += keySize;
michael@0 421 EnsureBuffer(newSize);
michael@0 422
michael@0 423 // Add after last element
michael@0 424 pos = mBuf + mElementsSize;
michael@0 425 memcpy(pos, aKey, keySize);
michael@0 426 pos += keySize;
michael@0 427 }
michael@0 428
michael@0 429 // Update value
michael@0 430 memcpy(pos, aValue, valueSize);
michael@0 431 mElementsSize = newSize;
michael@0 432
michael@0 433 return NS_OK;
michael@0 434 }
michael@0 435
michael@0 436 CacheHash::Hash16_t
michael@0 437 CacheFileMetadata::GetHash(uint32_t aIndex)
michael@0 438 {
michael@0 439 MOZ_ASSERT(aIndex < mHashCount);
michael@0 440 return NetworkEndian::readUint16(&mHashArray[aIndex]);
michael@0 441 }
michael@0 442
michael@0 443 nsresult
michael@0 444 CacheFileMetadata::SetHash(uint32_t aIndex, CacheHash::Hash16_t aHash)
michael@0 445 {
michael@0 446 LOG(("CacheFileMetadata::SetHash() [this=%p, idx=%d, hash=%x]",
michael@0 447 this, aIndex, aHash));
michael@0 448
michael@0 449 MarkDirty();
michael@0 450
michael@0 451 MOZ_ASSERT(aIndex <= mHashCount);
michael@0 452
michael@0 453 if (aIndex > mHashCount) {
michael@0 454 return NS_ERROR_INVALID_ARG;
michael@0 455 } else if (aIndex == mHashCount) {
michael@0 456 if ((aIndex + 1) * sizeof(CacheHash::Hash16_t) > mHashArraySize) {
michael@0 457 // reallocate hash array buffer
michael@0 458 if (mHashArraySize == 0)
michael@0 459 mHashArraySize = 32 * sizeof(CacheHash::Hash16_t);
michael@0 460 else
michael@0 461 mHashArraySize *= 2;
michael@0 462 mHashArray = static_cast<CacheHash::Hash16_t *>(
michael@0 463 moz_xrealloc(mHashArray, mHashArraySize));
michael@0 464 }
michael@0 465
michael@0 466 mHashCount++;
michael@0 467 }
michael@0 468
michael@0 469 NetworkEndian::writeUint16(&mHashArray[aIndex], aHash);
michael@0 470
michael@0 471 DoMemoryReport(MemoryUsage());
michael@0 472
michael@0 473 return NS_OK;
michael@0 474 }
michael@0 475
michael@0 476 nsresult
michael@0 477 CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime)
michael@0 478 {
michael@0 479 LOG(("CacheFileMetadata::SetExpirationTime() [this=%p, expirationTime=%d]",
michael@0 480 this, aExpirationTime));
michael@0 481
michael@0 482 MarkDirty();
michael@0 483 mMetaHdr.mExpirationTime = aExpirationTime;
michael@0 484 return NS_OK;
michael@0 485 }
michael@0 486
michael@0 487 nsresult
michael@0 488 CacheFileMetadata::GetExpirationTime(uint32_t *_retval)
michael@0 489 {
michael@0 490 *_retval = mMetaHdr.mExpirationTime;
michael@0 491 return NS_OK;
michael@0 492 }
michael@0 493
michael@0 494 nsresult
michael@0 495 CacheFileMetadata::SetLastModified(uint32_t aLastModified)
michael@0 496 {
michael@0 497 LOG(("CacheFileMetadata::SetLastModified() [this=%p, lastModified=%d]",
michael@0 498 this, aLastModified));
michael@0 499
michael@0 500 MarkDirty();
michael@0 501 mMetaHdr.mLastModified = aLastModified;
michael@0 502 return NS_OK;
michael@0 503 }
michael@0 504
michael@0 505 nsresult
michael@0 506 CacheFileMetadata::GetLastModified(uint32_t *_retval)
michael@0 507 {
michael@0 508 *_retval = mMetaHdr.mLastModified;
michael@0 509 return NS_OK;
michael@0 510 }
michael@0 511
michael@0 512 nsresult
michael@0 513 CacheFileMetadata::SetFrecency(uint32_t aFrecency)
michael@0 514 {
michael@0 515 LOG(("CacheFileMetadata::SetFrecency() [this=%p, frecency=%f]",
michael@0 516 this, (double)aFrecency));
michael@0 517
michael@0 518 MarkDirty();
michael@0 519 mMetaHdr.mFrecency = aFrecency;
michael@0 520 return NS_OK;
michael@0 521 }
michael@0 522
michael@0 523 nsresult
michael@0 524 CacheFileMetadata::GetFrecency(uint32_t *_retval)
michael@0 525 {
michael@0 526 *_retval = mMetaHdr.mFrecency;
michael@0 527 return NS_OK;
michael@0 528 }
michael@0 529
michael@0 530 nsresult
michael@0 531 CacheFileMetadata::GetLastFetched(uint32_t *_retval)
michael@0 532 {
michael@0 533 *_retval = mMetaHdr.mLastFetched;
michael@0 534 return NS_OK;
michael@0 535 }
michael@0 536
michael@0 537 nsresult
michael@0 538 CacheFileMetadata::GetFetchCount(uint32_t *_retval)
michael@0 539 {
michael@0 540 *_retval = mMetaHdr.mFetchCount;
michael@0 541 return NS_OK;
michael@0 542 }
michael@0 543
michael@0 544 nsresult
michael@0 545 CacheFileMetadata::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
michael@0 546 {
michael@0 547 MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!");
michael@0 548 return NS_ERROR_UNEXPECTED;
michael@0 549 }
michael@0 550
michael@0 551 nsresult
michael@0 552 CacheFileMetadata::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
michael@0 553 nsresult aResult)
michael@0 554 {
michael@0 555 LOG(("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, result=0x%08x]",
michael@0 556 this, aHandle, aResult));
michael@0 557
michael@0 558 MOZ_ASSERT(mListener);
michael@0 559 MOZ_ASSERT(mWriteBuf);
michael@0 560
michael@0 561 free(mWriteBuf);
michael@0 562 mWriteBuf = nullptr;
michael@0 563
michael@0 564 nsCOMPtr<CacheFileMetadataListener> listener;
michael@0 565
michael@0 566 mListener.swap(listener);
michael@0 567 listener->OnMetadataWritten(aResult);
michael@0 568
michael@0 569 DoMemoryReport(MemoryUsage());
michael@0 570
michael@0 571 return NS_OK;
michael@0 572 }
michael@0 573
michael@0 574 nsresult
michael@0 575 CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
michael@0 576 nsresult aResult)
michael@0 577 {
michael@0 578 LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
michael@0 579 this, aHandle, aResult));
michael@0 580
michael@0 581 MOZ_ASSERT(mListener);
michael@0 582
michael@0 583 nsresult rv, retval;
michael@0 584 nsCOMPtr<CacheFileMetadataListener> listener;
michael@0 585
michael@0 586 if (NS_FAILED(aResult)) {
michael@0 587 LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed"
michael@0 588 ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult));
michael@0 589
michael@0 590 InitEmptyMetadata();
michael@0 591 retval = NS_OK;
michael@0 592
michael@0 593 mListener.swap(listener);
michael@0 594 listener->OnMetadataRead(retval);
michael@0 595 return NS_OK;
michael@0 596 }
michael@0 597
michael@0 598 // check whether we have read all necessary data
michael@0 599 uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize -
michael@0 600 sizeof(uint32_t));
michael@0 601
michael@0 602 int64_t size = mHandle->FileSize();
michael@0 603 MOZ_ASSERT(size != -1);
michael@0 604
michael@0 605 if (realOffset >= size) {
michael@0 606 LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating "
michael@0 607 "empty metadata. [this=%p, realOffset=%d, size=%lld]", this,
michael@0 608 realOffset, size));
michael@0 609
michael@0 610 InitEmptyMetadata();
michael@0 611 retval = NS_OK;
michael@0 612
michael@0 613 mListener.swap(listener);
michael@0 614 listener->OnMetadataRead(retval);
michael@0 615 return NS_OK;
michael@0 616 }
michael@0 617
michael@0 618 uint32_t usedOffset = size - mBufSize;
michael@0 619
michael@0 620 if (realOffset < usedOffset) {
michael@0 621 uint32_t missing = usedOffset - realOffset;
michael@0 622 // we need to read more data
michael@0 623 mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize + missing));
michael@0 624 memmove(mBuf + missing, mBuf, mBufSize);
michael@0 625 mBufSize += missing;
michael@0 626
michael@0 627 DoMemoryReport(MemoryUsage());
michael@0 628
michael@0 629 LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to "
michael@0 630 "have full metadata. [this=%p]", missing, this));
michael@0 631
michael@0 632 rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, true, this);
michael@0 633 if (NS_FAILED(rv)) {
michael@0 634 LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() "
michael@0 635 "failed synchronously, creating empty metadata. [this=%p, "
michael@0 636 "rv=0x%08x]", this, rv));
michael@0 637
michael@0 638 InitEmptyMetadata();
michael@0 639 retval = NS_OK;
michael@0 640
michael@0 641 mListener.swap(listener);
michael@0 642 listener->OnMetadataRead(retval);
michael@0 643 return NS_OK;
michael@0 644 }
michael@0 645
michael@0 646 return NS_OK;
michael@0 647 }
michael@0 648
michael@0 649 // We have all data according to offset information at the end of the entry.
michael@0 650 // Try to parse it.
michael@0 651 rv = ParseMetadata(realOffset, realOffset - usedOffset, true);
michael@0 652 if (NS_FAILED(rv)) {
michael@0 653 LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating "
michael@0 654 "empty metadata. [this=%p]", this));
michael@0 655 InitEmptyMetadata();
michael@0 656 retval = NS_OK;
michael@0 657 }
michael@0 658 else {
michael@0 659 retval = NS_OK;
michael@0 660 }
michael@0 661
michael@0 662 mListener.swap(listener);
michael@0 663 listener->OnMetadataRead(retval);
michael@0 664
michael@0 665 return NS_OK;
michael@0 666 }
michael@0 667
michael@0 668 nsresult
michael@0 669 CacheFileMetadata::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
michael@0 670 {
michael@0 671 MOZ_CRASH("CacheFileMetadata::OnFileDoomed should not be called!");
michael@0 672 return NS_ERROR_UNEXPECTED;
michael@0 673 }
michael@0 674
michael@0 675 nsresult
michael@0 676 CacheFileMetadata::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
michael@0 677 {
michael@0 678 MOZ_CRASH("CacheFileMetadata::OnEOFSet should not be called!");
michael@0 679 return NS_ERROR_UNEXPECTED;
michael@0 680 }
michael@0 681
michael@0 682 nsresult
michael@0 683 CacheFileMetadata::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
michael@0 684 {
michael@0 685 MOZ_CRASH("CacheFileMetadata::OnFileRenamed should not be called!");
michael@0 686 return NS_ERROR_UNEXPECTED;
michael@0 687 }
michael@0 688
michael@0 689 void
michael@0 690 CacheFileMetadata::InitEmptyMetadata()
michael@0 691 {
michael@0 692 if (mBuf) {
michael@0 693 free(mBuf);
michael@0 694 mBuf = nullptr;
michael@0 695 mBufSize = 0;
michael@0 696 }
michael@0 697 mOffset = 0;
michael@0 698 mMetaHdr.mVersion = kCacheEntryVersion;
michael@0 699 mMetaHdr.mFetchCount = 1;
michael@0 700 mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
michael@0 701 mMetaHdr.mKeySize = mKey.Length();
michael@0 702
michael@0 703 DoMemoryReport(MemoryUsage());
michael@0 704 }
michael@0 705
michael@0 706 nsresult
michael@0 707 CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset,
michael@0 708 bool aHaveKey)
michael@0 709 {
michael@0 710 LOG(("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, "
michael@0 711 "bufOffset=%d, haveKey=%u]", this, aMetaOffset, aBufOffset, aHaveKey));
michael@0 712
michael@0 713 nsresult rv;
michael@0 714
michael@0 715 uint32_t metaposOffset = mBufSize - sizeof(uint32_t);
michael@0 716 uint32_t hashesOffset = aBufOffset + sizeof(uint32_t);
michael@0 717 uint32_t hashCount = aMetaOffset / kChunkSize;
michael@0 718 if (aMetaOffset % kChunkSize)
michael@0 719 hashCount++;
michael@0 720 uint32_t hashesLen = hashCount * sizeof(CacheHash::Hash16_t);
michael@0 721 uint32_t hdrOffset = hashesOffset + hashesLen;
michael@0 722 uint32_t keyOffset = hdrOffset + sizeof(CacheFileMetadataHeader);
michael@0 723
michael@0 724 LOG(("CacheFileMetadata::ParseMetadata() [this=%p]\n metaposOffset=%d\n "
michael@0 725 "hashesOffset=%d\n hashCount=%d\n hashesLen=%d\n hdfOffset=%d\n "
michael@0 726 "keyOffset=%d\n", this, metaposOffset, hashesOffset, hashCount,
michael@0 727 hashesLen,hdrOffset, keyOffset));
michael@0 728
michael@0 729 if (keyOffset > metaposOffset) {
michael@0 730 LOG(("CacheFileMetadata::ParseMetadata() - Wrong keyOffset! [this=%p]",
michael@0 731 this));
michael@0 732 return NS_ERROR_FILE_CORRUPTED;
michael@0 733 }
michael@0 734
michael@0 735 mMetaHdr.ReadFromBuf(mBuf + hdrOffset);
michael@0 736
michael@0 737 if (mMetaHdr.mVersion != kCacheEntryVersion) {
michael@0 738 LOG(("CacheFileMetadata::ParseMetadata() - Not a version we understand to. "
michael@0 739 "[version=0x%x, this=%p]", mMetaHdr.mVersion, this));
michael@0 740 return NS_ERROR_UNEXPECTED;
michael@0 741 }
michael@0 742
michael@0 743 uint32_t elementsOffset = mMetaHdr.mKeySize + keyOffset + 1;
michael@0 744
michael@0 745 if (elementsOffset > metaposOffset) {
michael@0 746 LOG(("CacheFileMetadata::ParseMetadata() - Wrong elementsOffset %d "
michael@0 747 "[this=%p]", elementsOffset, this));
michael@0 748 return NS_ERROR_FILE_CORRUPTED;
michael@0 749 }
michael@0 750
michael@0 751 // check that key ends with \0
michael@0 752 if (mBuf[elementsOffset - 1] != 0) {
michael@0 753 LOG(("CacheFileMetadata::ParseMetadata() - Elements not null terminated. "
michael@0 754 "[this=%p]", this));
michael@0 755 return NS_ERROR_FILE_CORRUPTED;
michael@0 756 }
michael@0 757
michael@0 758
michael@0 759 if (!aHaveKey) {
michael@0 760 // get the key form metadata
michael@0 761 mKey.Assign(mBuf + keyOffset, mMetaHdr.mKeySize);
michael@0 762
michael@0 763 rv = ParseKey(mKey);
michael@0 764 if (NS_FAILED(rv))
michael@0 765 return rv;
michael@0 766 }
michael@0 767 else {
michael@0 768 if (mMetaHdr.mKeySize != mKey.Length()) {
michael@0 769 LOG(("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s "
michael@0 770 "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(),
michael@0 771 this));
michael@0 772 return NS_ERROR_FILE_CORRUPTED;
michael@0 773 }
michael@0 774
michael@0 775 if (memcmp(mKey.get(), mBuf + keyOffset, mKey.Length()) != 0) {
michael@0 776 LOG(("CacheFileMetadata::ParseMetadata() - Key collision (2), key=%s "
michael@0 777 "[this=%p]", nsCString(mBuf + keyOffset, mMetaHdr.mKeySize).get(),
michael@0 778 this));
michael@0 779 return NS_ERROR_FILE_CORRUPTED;
michael@0 780 }
michael@0 781 }
michael@0 782
michael@0 783 // check metadata hash (data from hashesOffset to metaposOffset)
michael@0 784 CacheHash::Hash32_t hashComputed, hashExpected;
michael@0 785 hashComputed = CacheHash::Hash(mBuf + hashesOffset,
michael@0 786 metaposOffset - hashesOffset);
michael@0 787 hashExpected = NetworkEndian::readUint32(mBuf + aBufOffset);
michael@0 788
michael@0 789 if (hashComputed != hashExpected) {
michael@0 790 LOG(("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of "
michael@0 791 "the metadata is %x, hash in file is %x [this=%p]", hashComputed,
michael@0 792 hashExpected, this));
michael@0 793 return NS_ERROR_FILE_CORRUPTED;
michael@0 794 }
michael@0 795
michael@0 796 // check elements
michael@0 797 rv = CheckElements(mBuf + elementsOffset, metaposOffset - elementsOffset);
michael@0 798 if (NS_FAILED(rv))
michael@0 799 return rv;
michael@0 800
michael@0 801 mHashArraySize = hashesLen;
michael@0 802 mHashCount = hashCount;
michael@0 803 if (mHashArraySize) {
michael@0 804 mHashArray = static_cast<CacheHash::Hash16_t *>(
michael@0 805 moz_xmalloc(mHashArraySize));
michael@0 806 memcpy(mHashArray, mBuf + hashesOffset, mHashArraySize);
michael@0 807 }
michael@0 808
michael@0 809
michael@0 810 mMetaHdr.mFetchCount++;
michael@0 811 MarkDirty();
michael@0 812
michael@0 813 mElementsSize = metaposOffset - elementsOffset;
michael@0 814 memmove(mBuf, mBuf + elementsOffset, mElementsSize);
michael@0 815 mOffset = aMetaOffset;
michael@0 816
michael@0 817 // TODO: shrink memory if buffer is too big
michael@0 818
michael@0 819 DoMemoryReport(MemoryUsage());
michael@0 820
michael@0 821 return NS_OK;
michael@0 822 }
michael@0 823
michael@0 824 nsresult
michael@0 825 CacheFileMetadata::CheckElements(const char *aBuf, uint32_t aSize)
michael@0 826 {
michael@0 827 if (aSize) {
michael@0 828 // Check if the metadata ends with a zero byte.
michael@0 829 if (aBuf[aSize - 1] != 0) {
michael@0 830 NS_ERROR("Metadata elements are not null terminated");
michael@0 831 LOG(("CacheFileMetadata::CheckElements() - Elements are not null "
michael@0 832 "terminated. [this=%p]", this));
michael@0 833 return NS_ERROR_FILE_CORRUPTED;
michael@0 834 }
michael@0 835 // Check that there are an even number of zero bytes
michael@0 836 // to match the pattern { key \0 value \0 }
michael@0 837 bool odd = false;
michael@0 838 for (uint32_t i = 0; i < aSize; i++) {
michael@0 839 if (aBuf[i] == 0)
michael@0 840 odd = !odd;
michael@0 841 }
michael@0 842 if (odd) {
michael@0 843 NS_ERROR("Metadata elements are malformed");
michael@0 844 LOG(("CacheFileMetadata::CheckElements() - Elements are malformed. "
michael@0 845 "[this=%p]", this));
michael@0 846 return NS_ERROR_FILE_CORRUPTED;
michael@0 847 }
michael@0 848 }
michael@0 849 return NS_OK;
michael@0 850 }
michael@0 851
michael@0 852 void
michael@0 853 CacheFileMetadata::EnsureBuffer(uint32_t aSize)
michael@0 854 {
michael@0 855 if (mBufSize < aSize) {
michael@0 856 mBufSize = aSize;
michael@0 857 mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize));
michael@0 858 }
michael@0 859
michael@0 860 DoMemoryReport(MemoryUsage());
michael@0 861 }
michael@0 862
michael@0 863 nsresult
michael@0 864 CacheFileMetadata::ParseKey(const nsACString &aKey)
michael@0 865 {
michael@0 866 nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
michael@0 867 NS_ENSURE_TRUE(info, NS_ERROR_FAILURE);
michael@0 868
michael@0 869 mAnonymous = info->IsAnonymous();
michael@0 870 mAppId = info->AppId();
michael@0 871 mInBrowser = info->IsInBrowserElement();
michael@0 872
michael@0 873 return NS_OK;
michael@0 874 }
michael@0 875
michael@0 876 // Memory reporting
michael@0 877
michael@0 878 size_t
michael@0 879 CacheFileMetadata::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 880 {
michael@0 881 size_t n = 0;
michael@0 882 // mHandle reported via CacheFileIOManager.
michael@0 883 n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
michael@0 884 n += mallocSizeOf(mHashArray);
michael@0 885 n += mallocSizeOf(mBuf);
michael@0 886 n += mallocSizeOf(mWriteBuf);
michael@0 887 // mListener is usually the owning CacheFile.
michael@0 888
michael@0 889 return n;
michael@0 890 }
michael@0 891
michael@0 892 size_t
michael@0 893 CacheFileMetadata::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 894 {
michael@0 895 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
michael@0 896 }
michael@0 897
michael@0 898 } // net
michael@0 899 } // mozilla

mercurial