netwerk/cache/nsDiskCacheStreams.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 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7
michael@0 8 #include "nsCache.h"
michael@0 9 #include "nsDiskCache.h"
michael@0 10 #include "nsDiskCacheDevice.h"
michael@0 11 #include "nsDiskCacheStreams.h"
michael@0 12 #include "nsCacheService.h"
michael@0 13 #include "mozilla/FileUtils.h"
michael@0 14 #include "nsThreadUtils.h"
michael@0 15 #include "mozilla/MemoryReporting.h"
michael@0 16 #include "mozilla/Telemetry.h"
michael@0 17 #include "mozilla/TimeStamp.h"
michael@0 18 #include <algorithm>
michael@0 19 #include "mozilla/VisualEventTracer.h"
michael@0 20
michael@0 21 // we pick 16k as the max buffer size because that is the threshold above which
michael@0 22 // we are unable to store the data in the cache block files
michael@0 23 // see nsDiskCacheMap.[cpp,h]
michael@0 24 #define kMaxBufferSize (16 * 1024)
michael@0 25
michael@0 26 // Assumptions:
michael@0 27 // - cache descriptors live for life of streams
michael@0 28 // - streams will only be used by FileTransport,
michael@0 29 // they will not be directly accessible to clients
michael@0 30 // - overlapped I/O is NOT supported
michael@0 31
michael@0 32
michael@0 33 /******************************************************************************
michael@0 34 * nsDiskCacheInputStream
michael@0 35 *****************************************************************************/
michael@0 36 class nsDiskCacheInputStream : public nsIInputStream {
michael@0 37
michael@0 38 public:
michael@0 39
michael@0 40 nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
michael@0 41 PRFileDesc * fileDesc,
michael@0 42 const char * buffer,
michael@0 43 uint32_t endOfStream);
michael@0 44
michael@0 45 virtual ~nsDiskCacheInputStream();
michael@0 46
michael@0 47 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 48 NS_DECL_NSIINPUTSTREAM
michael@0 49
michael@0 50 private:
michael@0 51 nsDiskCacheStreamIO * mStreamIO; // backpointer to parent
michael@0 52 PRFileDesc * mFD;
michael@0 53 const char * mBuffer;
michael@0 54 uint32_t mStreamEnd;
michael@0 55 uint32_t mPos; // stream position
michael@0 56 bool mClosed;
michael@0 57 };
michael@0 58
michael@0 59
michael@0 60 NS_IMPL_ISUPPORTS(nsDiskCacheInputStream, nsIInputStream)
michael@0 61
michael@0 62
michael@0 63 nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
michael@0 64 PRFileDesc * fileDesc,
michael@0 65 const char * buffer,
michael@0 66 uint32_t endOfStream)
michael@0 67 : mStreamIO(parent)
michael@0 68 , mFD(fileDesc)
michael@0 69 , mBuffer(buffer)
michael@0 70 , mStreamEnd(endOfStream)
michael@0 71 , mPos(0)
michael@0 72 , mClosed(false)
michael@0 73 {
michael@0 74 NS_ADDREF(mStreamIO);
michael@0 75 mStreamIO->IncrementInputStreamCount();
michael@0 76 }
michael@0 77
michael@0 78
michael@0 79 nsDiskCacheInputStream::~nsDiskCacheInputStream()
michael@0 80 {
michael@0 81 Close();
michael@0 82 mStreamIO->DecrementInputStreamCount();
michael@0 83 NS_RELEASE(mStreamIO);
michael@0 84 }
michael@0 85
michael@0 86
michael@0 87 NS_IMETHODIMP
michael@0 88 nsDiskCacheInputStream::Close()
michael@0 89 {
michael@0 90 if (!mClosed) {
michael@0 91 if (mFD) {
michael@0 92 (void) PR_Close(mFD);
michael@0 93 mFD = nullptr;
michael@0 94 }
michael@0 95 mClosed = true;
michael@0 96 }
michael@0 97 return NS_OK;
michael@0 98 }
michael@0 99
michael@0 100
michael@0 101 NS_IMETHODIMP
michael@0 102 nsDiskCacheInputStream::Available(uint64_t * bytesAvailable)
michael@0 103 {
michael@0 104 if (mClosed) return NS_BASE_STREAM_CLOSED;
michael@0 105 if (mStreamEnd < mPos) return NS_ERROR_UNEXPECTED;
michael@0 106
michael@0 107 *bytesAvailable = mStreamEnd - mPos;
michael@0 108 return NS_OK;
michael@0 109 }
michael@0 110
michael@0 111
michael@0 112 NS_IMETHODIMP
michael@0 113 nsDiskCacheInputStream::Read(char * buffer, uint32_t count, uint32_t * bytesRead)
michael@0 114 {
michael@0 115 *bytesRead = 0;
michael@0 116
michael@0 117 if (mClosed) {
michael@0 118 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
michael@0 119 "[stream=%p] stream was closed",
michael@0 120 this, buffer, count));
michael@0 121 return NS_OK;
michael@0 122 }
michael@0 123
michael@0 124 if (mPos == mStreamEnd) {
michael@0 125 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
michael@0 126 "[stream=%p] stream at end of file",
michael@0 127 this, buffer, count));
michael@0 128 return NS_OK;
michael@0 129 }
michael@0 130 if (mPos > mStreamEnd) {
michael@0 131 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
michael@0 132 "[stream=%p] stream past end of file (!)",
michael@0 133 this, buffer, count));
michael@0 134 return NS_ERROR_UNEXPECTED;
michael@0 135 }
michael@0 136
michael@0 137 if (count > mStreamEnd - mPos)
michael@0 138 count = mStreamEnd - mPos;
michael@0 139
michael@0 140 if (mFD) {
michael@0 141 // just read from file
michael@0 142 int32_t result = PR_Read(mFD, buffer, count);
michael@0 143 if (result < 0) {
michael@0 144 nsresult rv = NS_ErrorAccordingToNSPR();
michael@0 145 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read PR_Read failed"
michael@0 146 "[stream=%p, rv=%d, NSPR error %s",
michael@0 147 this, int(rv), PR_ErrorToName(PR_GetError())));
michael@0 148 return rv;
michael@0 149 }
michael@0 150
michael@0 151 mPos += (uint32_t)result;
michael@0 152 *bytesRead = (uint32_t)result;
michael@0 153
michael@0 154 } else if (mBuffer) {
michael@0 155 // read data from mBuffer
michael@0 156 memcpy(buffer, mBuffer + mPos, count);
michael@0 157 mPos += count;
michael@0 158 *bytesRead = count;
michael@0 159 } else {
michael@0 160 // no data source for input stream
michael@0 161 }
michael@0 162
michael@0 163 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
michael@0 164 "[stream=%p, count=%ud, byteRead=%ud] ",
michael@0 165 this, unsigned(count), unsigned(*bytesRead)));
michael@0 166 return NS_OK;
michael@0 167 }
michael@0 168
michael@0 169
michael@0 170 NS_IMETHODIMP
michael@0 171 nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer,
michael@0 172 void * closure,
michael@0 173 uint32_t count,
michael@0 174 uint32_t * bytesRead)
michael@0 175 {
michael@0 176 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 177 }
michael@0 178
michael@0 179
michael@0 180 NS_IMETHODIMP
michael@0 181 nsDiskCacheInputStream::IsNonBlocking(bool * nonBlocking)
michael@0 182 {
michael@0 183 *nonBlocking = false;
michael@0 184 return NS_OK;
michael@0 185 }
michael@0 186
michael@0 187
michael@0 188
michael@0 189
michael@0 190 /******************************************************************************
michael@0 191 * nsDiskCacheStreamIO
michael@0 192 *****************************************************************************/
michael@0 193 NS_IMPL_ISUPPORTS(nsDiskCacheStreamIO, nsIOutputStream)
michael@0 194
michael@0 195 nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding)
michael@0 196 : mBinding(binding)
michael@0 197 , mInStreamCount(0)
michael@0 198 , mFD(nullptr)
michael@0 199 , mStreamEnd(0)
michael@0 200 , mBufSize(0)
michael@0 201 , mBuffer(nullptr)
michael@0 202 , mOutputStreamIsOpen(false)
michael@0 203 {
michael@0 204 mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice();
michael@0 205
michael@0 206 // acquire "death grip" on cache service
michael@0 207 nsCacheService *service = nsCacheService::GlobalInstance();
michael@0 208 NS_ADDREF(service);
michael@0 209 }
michael@0 210
michael@0 211
michael@0 212 nsDiskCacheStreamIO::~nsDiskCacheStreamIO()
michael@0 213 {
michael@0 214 nsCacheService::AssertOwnsLock();
michael@0 215
michael@0 216 // Close the outputstream
michael@0 217 if (mBinding && mOutputStreamIsOpen) {
michael@0 218 (void)CloseOutputStream();
michael@0 219 }
michael@0 220
michael@0 221 // release "death grip" on cache service
michael@0 222 nsCacheService *service = nsCacheService::GlobalInstance();
michael@0 223 NS_RELEASE(service);
michael@0 224
michael@0 225 // assert streams closed
michael@0 226 NS_ASSERTION(!mOutputStreamIsOpen, "output stream still open");
michael@0 227 NS_ASSERTION(mInStreamCount == 0, "input stream still open");
michael@0 228 NS_ASSERTION(!mFD, "file descriptor not closed");
michael@0 229
michael@0 230 DeleteBuffer();
michael@0 231 }
michael@0 232
michael@0 233
michael@0 234 // NOTE: called with service lock held
michael@0 235 nsresult
michael@0 236 nsDiskCacheStreamIO::GetInputStream(uint32_t offset, nsIInputStream ** inputStream)
michael@0 237 {
michael@0 238 NS_ENSURE_ARG_POINTER(inputStream);
michael@0 239 NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED);
michael@0 240
michael@0 241 *inputStream = nullptr;
michael@0 242
michael@0 243 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
michael@0 244
michael@0 245 if (mOutputStreamIsOpen) {
michael@0 246 NS_WARNING("already have an output stream open");
michael@0 247 return NS_ERROR_NOT_AVAILABLE;
michael@0 248 }
michael@0 249
michael@0 250 nsresult rv;
michael@0 251 PRFileDesc * fd = nullptr;
michael@0 252
michael@0 253 mStreamEnd = mBinding->mCacheEntry->DataSize();
michael@0 254 if (mStreamEnd == 0) {
michael@0 255 // there's no data to read
michael@0 256 NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size");
michael@0 257 } else if (mBinding->mRecord.DataFile() == 0) {
michael@0 258 // open file desc for data
michael@0 259 rv = OpenCacheFile(PR_RDONLY, &fd);
michael@0 260 if (NS_FAILED(rv)) return rv; // unable to open file
michael@0 261 NS_ASSERTION(fd, "cache stream lacking open file.");
michael@0 262
michael@0 263 } else if (!mBuffer) {
michael@0 264 // read block file for data
michael@0 265 rv = ReadCacheBlocks(mStreamEnd);
michael@0 266 if (NS_FAILED(rv)) return rv;
michael@0 267 }
michael@0 268 // else, mBuffer already contains all of the data (left over from a
michael@0 269 // previous block-file read or write).
michael@0 270
michael@0 271 NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream");
michael@0 272
michael@0 273 // create a new input stream
michael@0 274 nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd);
michael@0 275 if (!inStream) return NS_ERROR_OUT_OF_MEMORY;
michael@0 276
michael@0 277 NS_ADDREF(*inputStream = inStream);
michael@0 278 return NS_OK;
michael@0 279 }
michael@0 280
michael@0 281
michael@0 282 // NOTE: called with service lock held
michael@0 283 nsresult
michael@0 284 nsDiskCacheStreamIO::GetOutputStream(uint32_t offset, nsIOutputStream ** outputStream)
michael@0 285 {
michael@0 286 NS_ENSURE_ARG_POINTER(outputStream);
michael@0 287 *outputStream = nullptr;
michael@0 288
michael@0 289 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
michael@0 290
michael@0 291 NS_ASSERTION(!mOutputStreamIsOpen, "already have an output stream open");
michael@0 292 NS_ASSERTION(mInStreamCount == 0, "we already have input streams open");
michael@0 293 if (mOutputStreamIsOpen || mInStreamCount) return NS_ERROR_NOT_AVAILABLE;
michael@0 294
michael@0 295 mStreamEnd = mBinding->mCacheEntry->DataSize();
michael@0 296
michael@0 297 // Inits file or buffer and truncate at the desired offset
michael@0 298 nsresult rv = SeekAndTruncate(offset);
michael@0 299 if (NS_FAILED(rv)) return rv;
michael@0 300
michael@0 301 mOutputStreamIsOpen = true;
michael@0 302 NS_ADDREF(*outputStream = this);
michael@0 303 return NS_OK;
michael@0 304 }
michael@0 305
michael@0 306 nsresult
michael@0 307 nsDiskCacheStreamIO::ClearBinding()
michael@0 308 {
michael@0 309 nsresult rv = NS_OK;
michael@0 310 if (mBinding && mOutputStreamIsOpen)
michael@0 311 rv = CloseOutputStream();
michael@0 312 mBinding = nullptr;
michael@0 313 return rv;
michael@0 314 }
michael@0 315
michael@0 316 NS_IMETHODIMP
michael@0 317 nsDiskCacheStreamIO::Close()
michael@0 318 {
michael@0 319 if (!mOutputStreamIsOpen) return NS_OK;
michael@0 320
michael@0 321 mozilla::TimeStamp start = mozilla::TimeStamp::Now();
michael@0 322
michael@0 323 // grab service lock
michael@0 324 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_CLOSEOUTPUTSTREAM));
michael@0 325
michael@0 326 if (!mBinding) { // if we're severed, just clear member variables
michael@0 327 mOutputStreamIsOpen = false;
michael@0 328 return NS_ERROR_NOT_AVAILABLE;
michael@0 329 }
michael@0 330
michael@0 331 nsresult rv = CloseOutputStream();
michael@0 332 if (NS_FAILED(rv))
michael@0 333 NS_WARNING("CloseOutputStream() failed");
michael@0 334
michael@0 335 mozilla::Telemetry::ID id;
michael@0 336 if (NS_IsMainThread())
michael@0 337 id = mozilla::Telemetry::NETWORK_DISK_CACHE_STREAMIO_CLOSE_MAIN_THREAD;
michael@0 338 else
michael@0 339 id = mozilla::Telemetry::NETWORK_DISK_CACHE_STREAMIO_CLOSE;
michael@0 340 mozilla::Telemetry::AccumulateTimeDelta(id, start);
michael@0 341
michael@0 342 return rv;
michael@0 343 }
michael@0 344
michael@0 345 nsresult
michael@0 346 nsDiskCacheStreamIO::CloseOutputStream()
michael@0 347 {
michael@0 348 NS_ASSERTION(mBinding, "oops");
michael@0 349
michael@0 350 CACHE_LOG_DEBUG(("CACHE: CloseOutputStream [%x doomed=%u]\n",
michael@0 351 mBinding->mRecord.HashNumber(), mBinding->mDoomed));
michael@0 352
michael@0 353 // Mark outputstream as closed, even if saving the stream fails
michael@0 354 mOutputStreamIsOpen = false;
michael@0 355
michael@0 356 // When writing to a file, just close the file
michael@0 357 if (mFD) {
michael@0 358 (void) PR_Close(mFD);
michael@0 359 mFD = nullptr;
michael@0 360 return NS_OK;
michael@0 361 }
michael@0 362
michael@0 363 // write data to cache blocks, or flush mBuffer to file
michael@0 364 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "stream is bigger than buffer");
michael@0 365
michael@0 366 nsDiskCacheMap *cacheMap = mDevice->CacheMap(); // get map reference
michael@0 367 nsDiskCacheRecord * record = &mBinding->mRecord;
michael@0 368 nsresult rv = NS_OK;
michael@0 369
michael@0 370 // delete existing storage
michael@0 371 if (record->DataLocationInitialized()) {
michael@0 372 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
michael@0 373 NS_ENSURE_SUCCESS(rv, rv);
michael@0 374
michael@0 375 // Only call UpdateRecord when there is no data to write,
michael@0 376 // because WriteDataCacheBlocks / FlushBufferToFile calls it.
michael@0 377 if ((mStreamEnd == 0) && (!mBinding->mDoomed)) {
michael@0 378 rv = cacheMap->UpdateRecord(record);
michael@0 379 if (NS_FAILED(rv)) {
michael@0 380 NS_WARNING("cacheMap->UpdateRecord() failed.");
michael@0 381 return rv; // XXX doom cache entry
michael@0 382 }
michael@0 383 }
michael@0 384 }
michael@0 385
michael@0 386 if (mStreamEnd == 0) return NS_OK; // nothing to write
michael@0 387
michael@0 388 // try to write to the cache blocks
michael@0 389 rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
michael@0 390 if (NS_FAILED(rv)) {
michael@0 391 NS_WARNING("WriteDataCacheBlocks() failed.");
michael@0 392
michael@0 393 // failed to store in cacheblocks, save as separate file
michael@0 394 rv = FlushBufferToFile(); // initializes DataFileLocation() if necessary
michael@0 395 if (mFD) {
michael@0 396 UpdateFileSize();
michael@0 397 (void) PR_Close(mFD);
michael@0 398 mFD = nullptr;
michael@0 399 }
michael@0 400 else
michael@0 401 NS_WARNING("no file descriptor");
michael@0 402 }
michael@0 403
michael@0 404 return rv;
michael@0 405 }
michael@0 406
michael@0 407
michael@0 408 // assumptions:
michael@0 409 // only one thread writing at a time
michael@0 410 // never have both output and input streams open
michael@0 411 // OnDataSizeChanged() will have already been called to update entry->DataSize()
michael@0 412
michael@0 413 NS_IMETHODIMP
michael@0 414 nsDiskCacheStreamIO::Write( const char * buffer,
michael@0 415 uint32_t count,
michael@0 416 uint32_t * bytesWritten)
michael@0 417 {
michael@0 418 NS_ENSURE_ARG_POINTER(buffer);
michael@0 419 NS_ENSURE_ARG_POINTER(bytesWritten);
michael@0 420 if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED;
michael@0 421
michael@0 422 *bytesWritten = 0; // always initialize to zero in case of errors
michael@0 423
michael@0 424 NS_ASSERTION(count, "Write called with count of zero");
michael@0 425 if (count == 0) {
michael@0 426 return NS_OK; // nothing to write
michael@0 427 }
michael@0 428
michael@0 429 // grab service lock
michael@0 430 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_WRITE));
michael@0 431 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
michael@0 432
michael@0 433 if (mInStreamCount) {
michael@0 434 // we have open input streams already
michael@0 435 // this is an error until we support overlapped I/O
michael@0 436 NS_WARNING("Attempting to write to cache entry with open input streams.\n");
michael@0 437 return NS_ERROR_NOT_AVAILABLE;
michael@0 438 }
michael@0 439
michael@0 440 // Not writing to file, and it will fit in the cachedatablocks?
michael@0 441 if (!mFD && (mStreamEnd + count <= kMaxBufferSize)) {
michael@0 442
michael@0 443 // We have more data than the current buffer size?
michael@0 444 if ((mStreamEnd + count > mBufSize) && (mBufSize < kMaxBufferSize)) {
michael@0 445 // Increase buffer to the maximum size.
michael@0 446 mBuffer = (char *) moz_xrealloc(mBuffer, kMaxBufferSize);
michael@0 447 mBufSize = kMaxBufferSize;
michael@0 448 }
michael@0 449
michael@0 450 // Store in the buffer but only if it fits
michael@0 451 if (mStreamEnd + count <= mBufSize) {
michael@0 452 memcpy(mBuffer + mStreamEnd, buffer, count);
michael@0 453 mStreamEnd += count;
michael@0 454 *bytesWritten = count;
michael@0 455 return NS_OK;
michael@0 456 }
michael@0 457 }
michael@0 458
michael@0 459 // There are more bytes than fit in the buffer/cacheblocks, switch to file
michael@0 460 if (!mFD) {
michael@0 461 // Opens a cache file and write the buffer to it
michael@0 462 nsresult rv = FlushBufferToFile();
michael@0 463 if (NS_FAILED(rv)) {
michael@0 464 return rv;
michael@0 465 }
michael@0 466 }
michael@0 467 // Write directly to the file
michael@0 468 if (PR_Write(mFD, buffer, count) != (int32_t)count) {
michael@0 469 NS_WARNING("failed to write all data");
michael@0 470 return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
michael@0 471 }
michael@0 472 mStreamEnd += count;
michael@0 473 *bytesWritten = count;
michael@0 474
michael@0 475 UpdateFileSize();
michael@0 476 NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream");
michael@0 477
michael@0 478 return NS_OK;
michael@0 479 }
michael@0 480
michael@0 481
michael@0 482 void
michael@0 483 nsDiskCacheStreamIO::UpdateFileSize()
michael@0 484 {
michael@0 485 NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called");
michael@0 486
michael@0 487 nsDiskCacheRecord * record = &mBinding->mRecord;
michael@0 488 const uint32_t oldSizeK = record->DataFileSize();
michael@0 489 uint32_t newSizeK = (mStreamEnd + 0x03FF) >> 10;
michael@0 490
michael@0 491 // make sure the size won't overflow (bug #651100)
michael@0 492 if (newSizeK > kMaxDataSizeK)
michael@0 493 newSizeK = kMaxDataSizeK;
michael@0 494
michael@0 495 if (newSizeK == oldSizeK) return;
michael@0 496
michael@0 497 record->SetDataFileSize(newSizeK);
michael@0 498
michael@0 499 // update cache size totals
michael@0 500 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
michael@0 501 cacheMap->DecrementTotalSize(oldSizeK); // decrement old size
michael@0 502 cacheMap->IncrementTotalSize(newSizeK); // increment new size
michael@0 503
michael@0 504 if (!mBinding->mDoomed) {
michael@0 505 nsresult rv = cacheMap->UpdateRecord(record);
michael@0 506 if (NS_FAILED(rv)) {
michael@0 507 NS_WARNING("cacheMap->UpdateRecord() failed.");
michael@0 508 // XXX doom cache entry?
michael@0 509 }
michael@0 510 }
michael@0 511 }
michael@0 512
michael@0 513
michael@0 514 nsresult
michael@0 515 nsDiskCacheStreamIO::OpenCacheFile(int flags, PRFileDesc ** fd)
michael@0 516 {
michael@0 517 NS_ENSURE_ARG_POINTER(fd);
michael@0 518
michael@0 519 CACHE_LOG_DEBUG(("nsDiskCacheStreamIO::OpenCacheFile"));
michael@0 520
michael@0 521 nsresult rv;
michael@0 522 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
michael@0 523 nsCOMPtr<nsIFile> localFile;
michael@0 524
michael@0 525 rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord,
michael@0 526 nsDiskCache::kData,
michael@0 527 !!(flags & PR_CREATE_FILE),
michael@0 528 getter_AddRefs(localFile));
michael@0 529 if (NS_FAILED(rv)) return rv;
michael@0 530
michael@0 531 // create PRFileDesc for input stream - the 00600 is just for consistency
michael@0 532 return localFile->OpenNSPRFileDesc(flags, 00600, fd);
michael@0 533 }
michael@0 534
michael@0 535
michael@0 536 nsresult
michael@0 537 nsDiskCacheStreamIO::ReadCacheBlocks(uint32_t bufferSize)
michael@0 538 {
michael@0 539 mozilla::eventtracer::AutoEventTracer readCacheBlocks(
michael@0 540 mBinding->mCacheEntry,
michael@0 541 mozilla::eventtracer::eExec,
michael@0 542 mozilla::eventtracer::eDone,
michael@0 543 "net::cache::ReadCacheBlocks");
michael@0 544
michael@0 545 NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream");
michael@0 546 NS_ASSERTION(bufferSize <= kMaxBufferSize, "bufferSize too large for buffer");
michael@0 547 NS_ASSERTION(mStreamEnd <= bufferSize, "data too large for buffer");
michael@0 548
michael@0 549 nsDiskCacheRecord * record = &mBinding->mRecord;
michael@0 550 if (!record->DataLocationInitialized()) return NS_OK;
michael@0 551
michael@0 552 NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file");
michael@0 553
michael@0 554 if (!mBuffer) {
michael@0 555 mBuffer = (char *) moz_xmalloc(bufferSize);
michael@0 556 mBufSize = bufferSize;
michael@0 557 }
michael@0 558
michael@0 559 // read data stored in cache block files
michael@0 560 nsDiskCacheMap *map = mDevice->CacheMap(); // get map reference
michael@0 561 return map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
michael@0 562 }
michael@0 563
michael@0 564
michael@0 565 nsresult
michael@0 566 nsDiskCacheStreamIO::FlushBufferToFile()
michael@0 567 {
michael@0 568 mozilla::eventtracer::AutoEventTracer flushBufferToFile(
michael@0 569 mBinding->mCacheEntry,
michael@0 570 mozilla::eventtracer::eExec,
michael@0 571 mozilla::eventtracer::eDone,
michael@0 572 "net::cache::FlushBufferToFile");
michael@0 573
michael@0 574 nsresult rv;
michael@0 575 nsDiskCacheRecord * record = &mBinding->mRecord;
michael@0 576
michael@0 577 if (!mFD) {
michael@0 578 if (record->DataLocationInitialized() && (record->DataFile() > 0)) {
michael@0 579 // remove cache block storage
michael@0 580 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
michael@0 581 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
michael@0 582 if (NS_FAILED(rv)) return rv;
michael@0 583 }
michael@0 584 record->SetDataFileGeneration(mBinding->mGeneration);
michael@0 585
michael@0 586 // allocate file
michael@0 587 rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
michael@0 588 if (NS_FAILED(rv)) return rv;
michael@0 589
michael@0 590 int64_t dataSize = mBinding->mCacheEntry->PredictedDataSize();
michael@0 591 if (dataSize != -1)
michael@0 592 mozilla::fallocate(mFD, std::min<int64_t>(dataSize, kPreallocateLimit));
michael@0 593 }
michael@0 594
michael@0 595 // write buffer to the file when there is data in it
michael@0 596 if (mStreamEnd > 0) {
michael@0 597 if (!mBuffer) {
michael@0 598 NS_RUNTIMEABORT("Fix me!");
michael@0 599 }
michael@0 600 if (PR_Write(mFD, mBuffer, mStreamEnd) != (int32_t)mStreamEnd) {
michael@0 601 NS_WARNING("failed to flush all data");
michael@0 602 return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
michael@0 603 }
michael@0 604 }
michael@0 605
michael@0 606 // buffer is no longer valid
michael@0 607 DeleteBuffer();
michael@0 608
michael@0 609 return NS_OK;
michael@0 610 }
michael@0 611
michael@0 612
michael@0 613 void
michael@0 614 nsDiskCacheStreamIO::DeleteBuffer()
michael@0 615 {
michael@0 616 if (mBuffer) {
michael@0 617 free(mBuffer);
michael@0 618 mBuffer = nullptr;
michael@0 619 mBufSize = 0;
michael@0 620 }
michael@0 621 }
michael@0 622
michael@0 623 size_t
michael@0 624 nsDiskCacheStreamIO::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
michael@0 625 {
michael@0 626 size_t usage = aMallocSizeOf(this);
michael@0 627
michael@0 628 usage += aMallocSizeOf(mFD);
michael@0 629 usage += aMallocSizeOf(mBuffer);
michael@0 630
michael@0 631 return usage;
michael@0 632 }
michael@0 633
michael@0 634 nsresult
michael@0 635 nsDiskCacheStreamIO::SeekAndTruncate(uint32_t offset)
michael@0 636 {
michael@0 637 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
michael@0 638
michael@0 639 if (uint32_t(offset) > mStreamEnd) return NS_ERROR_FAILURE;
michael@0 640
michael@0 641 // Set the current end to the desired offset
michael@0 642 mStreamEnd = offset;
michael@0 643
michael@0 644 // Currently stored in file?
michael@0 645 if (mBinding->mRecord.DataLocationInitialized() &&
michael@0 646 (mBinding->mRecord.DataFile() == 0)) {
michael@0 647 if (!mFD) {
michael@0 648 // we need an mFD, we better open it now
michael@0 649 nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
michael@0 650 if (NS_FAILED(rv)) return rv;
michael@0 651 }
michael@0 652 if (offset) {
michael@0 653 if (PR_Seek(mFD, offset, PR_SEEK_SET) == -1)
michael@0 654 return NS_ErrorAccordingToNSPR();
michael@0 655 }
michael@0 656 nsDiskCache::Truncate(mFD, offset);
michael@0 657 UpdateFileSize();
michael@0 658
michael@0 659 // When we starting at zero again, close file and start with buffer.
michael@0 660 // If offset is non-zero (and within buffer) an option would be
michael@0 661 // to read the file into the buffer, but chance is high that it is
michael@0 662 // rewritten to the file anyway.
michael@0 663 if (offset == 0) {
michael@0 664 // close file descriptor
michael@0 665 (void) PR_Close(mFD);
michael@0 666 mFD = nullptr;
michael@0 667 }
michael@0 668 return NS_OK;
michael@0 669 }
michael@0 670
michael@0 671 // read data into mBuffer if not read yet.
michael@0 672 if (offset && !mBuffer) {
michael@0 673 nsresult rv = ReadCacheBlocks(kMaxBufferSize);
michael@0 674 if (NS_FAILED(rv)) return rv;
michael@0 675 }
michael@0 676
michael@0 677 // stream buffer sanity check
michael@0 678 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "bad stream");
michael@0 679 return NS_OK;
michael@0 680 }
michael@0 681
michael@0 682
michael@0 683 NS_IMETHODIMP
michael@0 684 nsDiskCacheStreamIO::Flush()
michael@0 685 {
michael@0 686 if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED;
michael@0 687 return NS_OK;
michael@0 688 }
michael@0 689
michael@0 690
michael@0 691 NS_IMETHODIMP
michael@0 692 nsDiskCacheStreamIO::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_t *bytesWritten)
michael@0 693 {
michael@0 694 NS_NOTREACHED("WriteFrom");
michael@0 695 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 696 }
michael@0 697
michael@0 698
michael@0 699 NS_IMETHODIMP
michael@0 700 nsDiskCacheStreamIO::WriteSegments( nsReadSegmentFun reader,
michael@0 701 void * closure,
michael@0 702 uint32_t count,
michael@0 703 uint32_t * bytesWritten)
michael@0 704 {
michael@0 705 NS_NOTREACHED("WriteSegments");
michael@0 706 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 707 }
michael@0 708
michael@0 709
michael@0 710 NS_IMETHODIMP
michael@0 711 nsDiskCacheStreamIO::IsNonBlocking(bool * nonBlocking)
michael@0 712 {
michael@0 713 *nonBlocking = false;
michael@0 714 return NS_OK;
michael@0 715 }

mercurial