1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache/nsDiskCacheStreams.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,715 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 + 1.11 +#include "nsCache.h" 1.12 +#include "nsDiskCache.h" 1.13 +#include "nsDiskCacheDevice.h" 1.14 +#include "nsDiskCacheStreams.h" 1.15 +#include "nsCacheService.h" 1.16 +#include "mozilla/FileUtils.h" 1.17 +#include "nsThreadUtils.h" 1.18 +#include "mozilla/MemoryReporting.h" 1.19 +#include "mozilla/Telemetry.h" 1.20 +#include "mozilla/TimeStamp.h" 1.21 +#include <algorithm> 1.22 +#include "mozilla/VisualEventTracer.h" 1.23 + 1.24 +// we pick 16k as the max buffer size because that is the threshold above which 1.25 +// we are unable to store the data in the cache block files 1.26 +// see nsDiskCacheMap.[cpp,h] 1.27 +#define kMaxBufferSize (16 * 1024) 1.28 + 1.29 +// Assumptions: 1.30 +// - cache descriptors live for life of streams 1.31 +// - streams will only be used by FileTransport, 1.32 +// they will not be directly accessible to clients 1.33 +// - overlapped I/O is NOT supported 1.34 + 1.35 + 1.36 +/****************************************************************************** 1.37 + * nsDiskCacheInputStream 1.38 + *****************************************************************************/ 1.39 +class nsDiskCacheInputStream : public nsIInputStream { 1.40 + 1.41 +public: 1.42 + 1.43 + nsDiskCacheInputStream( nsDiskCacheStreamIO * parent, 1.44 + PRFileDesc * fileDesc, 1.45 + const char * buffer, 1.46 + uint32_t endOfStream); 1.47 + 1.48 + virtual ~nsDiskCacheInputStream(); 1.49 + 1.50 + NS_DECL_THREADSAFE_ISUPPORTS 1.51 + NS_DECL_NSIINPUTSTREAM 1.52 + 1.53 +private: 1.54 + nsDiskCacheStreamIO * mStreamIO; // backpointer to parent 1.55 + PRFileDesc * mFD; 1.56 + const char * mBuffer; 1.57 + uint32_t mStreamEnd; 1.58 + uint32_t mPos; // stream position 1.59 + bool mClosed; 1.60 +}; 1.61 + 1.62 + 1.63 +NS_IMPL_ISUPPORTS(nsDiskCacheInputStream, nsIInputStream) 1.64 + 1.65 + 1.66 +nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent, 1.67 + PRFileDesc * fileDesc, 1.68 + const char * buffer, 1.69 + uint32_t endOfStream) 1.70 + : mStreamIO(parent) 1.71 + , mFD(fileDesc) 1.72 + , mBuffer(buffer) 1.73 + , mStreamEnd(endOfStream) 1.74 + , mPos(0) 1.75 + , mClosed(false) 1.76 +{ 1.77 + NS_ADDREF(mStreamIO); 1.78 + mStreamIO->IncrementInputStreamCount(); 1.79 +} 1.80 + 1.81 + 1.82 +nsDiskCacheInputStream::~nsDiskCacheInputStream() 1.83 +{ 1.84 + Close(); 1.85 + mStreamIO->DecrementInputStreamCount(); 1.86 + NS_RELEASE(mStreamIO); 1.87 +} 1.88 + 1.89 + 1.90 +NS_IMETHODIMP 1.91 +nsDiskCacheInputStream::Close() 1.92 +{ 1.93 + if (!mClosed) { 1.94 + if (mFD) { 1.95 + (void) PR_Close(mFD); 1.96 + mFD = nullptr; 1.97 + } 1.98 + mClosed = true; 1.99 + } 1.100 + return NS_OK; 1.101 +} 1.102 + 1.103 + 1.104 +NS_IMETHODIMP 1.105 +nsDiskCacheInputStream::Available(uint64_t * bytesAvailable) 1.106 +{ 1.107 + if (mClosed) return NS_BASE_STREAM_CLOSED; 1.108 + if (mStreamEnd < mPos) return NS_ERROR_UNEXPECTED; 1.109 + 1.110 + *bytesAvailable = mStreamEnd - mPos; 1.111 + return NS_OK; 1.112 +} 1.113 + 1.114 + 1.115 +NS_IMETHODIMP 1.116 +nsDiskCacheInputStream::Read(char * buffer, uint32_t count, uint32_t * bytesRead) 1.117 +{ 1.118 + *bytesRead = 0; 1.119 + 1.120 + if (mClosed) { 1.121 + CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " 1.122 + "[stream=%p] stream was closed", 1.123 + this, buffer, count)); 1.124 + return NS_OK; 1.125 + } 1.126 + 1.127 + if (mPos == mStreamEnd) { 1.128 + CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " 1.129 + "[stream=%p] stream at end of file", 1.130 + this, buffer, count)); 1.131 + return NS_OK; 1.132 + } 1.133 + if (mPos > mStreamEnd) { 1.134 + CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " 1.135 + "[stream=%p] stream past end of file (!)", 1.136 + this, buffer, count)); 1.137 + return NS_ERROR_UNEXPECTED; 1.138 + } 1.139 + 1.140 + if (count > mStreamEnd - mPos) 1.141 + count = mStreamEnd - mPos; 1.142 + 1.143 + if (mFD) { 1.144 + // just read from file 1.145 + int32_t result = PR_Read(mFD, buffer, count); 1.146 + if (result < 0) { 1.147 + nsresult rv = NS_ErrorAccordingToNSPR(); 1.148 + CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read PR_Read failed" 1.149 + "[stream=%p, rv=%d, NSPR error %s", 1.150 + this, int(rv), PR_ErrorToName(PR_GetError()))); 1.151 + return rv; 1.152 + } 1.153 + 1.154 + mPos += (uint32_t)result; 1.155 + *bytesRead = (uint32_t)result; 1.156 + 1.157 + } else if (mBuffer) { 1.158 + // read data from mBuffer 1.159 + memcpy(buffer, mBuffer + mPos, count); 1.160 + mPos += count; 1.161 + *bytesRead = count; 1.162 + } else { 1.163 + // no data source for input stream 1.164 + } 1.165 + 1.166 + CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " 1.167 + "[stream=%p, count=%ud, byteRead=%ud] ", 1.168 + this, unsigned(count), unsigned(*bytesRead))); 1.169 + return NS_OK; 1.170 +} 1.171 + 1.172 + 1.173 +NS_IMETHODIMP 1.174 +nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer, 1.175 + void * closure, 1.176 + uint32_t count, 1.177 + uint32_t * bytesRead) 1.178 +{ 1.179 + return NS_ERROR_NOT_IMPLEMENTED; 1.180 +} 1.181 + 1.182 + 1.183 +NS_IMETHODIMP 1.184 +nsDiskCacheInputStream::IsNonBlocking(bool * nonBlocking) 1.185 +{ 1.186 + *nonBlocking = false; 1.187 + return NS_OK; 1.188 +} 1.189 + 1.190 + 1.191 + 1.192 + 1.193 +/****************************************************************************** 1.194 + * nsDiskCacheStreamIO 1.195 + *****************************************************************************/ 1.196 +NS_IMPL_ISUPPORTS(nsDiskCacheStreamIO, nsIOutputStream) 1.197 + 1.198 +nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding) 1.199 + : mBinding(binding) 1.200 + , mInStreamCount(0) 1.201 + , mFD(nullptr) 1.202 + , mStreamEnd(0) 1.203 + , mBufSize(0) 1.204 + , mBuffer(nullptr) 1.205 + , mOutputStreamIsOpen(false) 1.206 +{ 1.207 + mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice(); 1.208 + 1.209 + // acquire "death grip" on cache service 1.210 + nsCacheService *service = nsCacheService::GlobalInstance(); 1.211 + NS_ADDREF(service); 1.212 +} 1.213 + 1.214 + 1.215 +nsDiskCacheStreamIO::~nsDiskCacheStreamIO() 1.216 +{ 1.217 + nsCacheService::AssertOwnsLock(); 1.218 + 1.219 + // Close the outputstream 1.220 + if (mBinding && mOutputStreamIsOpen) { 1.221 + (void)CloseOutputStream(); 1.222 + } 1.223 + 1.224 + // release "death grip" on cache service 1.225 + nsCacheService *service = nsCacheService::GlobalInstance(); 1.226 + NS_RELEASE(service); 1.227 + 1.228 + // assert streams closed 1.229 + NS_ASSERTION(!mOutputStreamIsOpen, "output stream still open"); 1.230 + NS_ASSERTION(mInStreamCount == 0, "input stream still open"); 1.231 + NS_ASSERTION(!mFD, "file descriptor not closed"); 1.232 + 1.233 + DeleteBuffer(); 1.234 +} 1.235 + 1.236 + 1.237 +// NOTE: called with service lock held 1.238 +nsresult 1.239 +nsDiskCacheStreamIO::GetInputStream(uint32_t offset, nsIInputStream ** inputStream) 1.240 +{ 1.241 + NS_ENSURE_ARG_POINTER(inputStream); 1.242 + NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED); 1.243 + 1.244 + *inputStream = nullptr; 1.245 + 1.246 + if (!mBinding) return NS_ERROR_NOT_AVAILABLE; 1.247 + 1.248 + if (mOutputStreamIsOpen) { 1.249 + NS_WARNING("already have an output stream open"); 1.250 + return NS_ERROR_NOT_AVAILABLE; 1.251 + } 1.252 + 1.253 + nsresult rv; 1.254 + PRFileDesc * fd = nullptr; 1.255 + 1.256 + mStreamEnd = mBinding->mCacheEntry->DataSize(); 1.257 + if (mStreamEnd == 0) { 1.258 + // there's no data to read 1.259 + NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size"); 1.260 + } else if (mBinding->mRecord.DataFile() == 0) { 1.261 + // open file desc for data 1.262 + rv = OpenCacheFile(PR_RDONLY, &fd); 1.263 + if (NS_FAILED(rv)) return rv; // unable to open file 1.264 + NS_ASSERTION(fd, "cache stream lacking open file."); 1.265 + 1.266 + } else if (!mBuffer) { 1.267 + // read block file for data 1.268 + rv = ReadCacheBlocks(mStreamEnd); 1.269 + if (NS_FAILED(rv)) return rv; 1.270 + } 1.271 + // else, mBuffer already contains all of the data (left over from a 1.272 + // previous block-file read or write). 1.273 + 1.274 + NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream"); 1.275 + 1.276 + // create a new input stream 1.277 + nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd); 1.278 + if (!inStream) return NS_ERROR_OUT_OF_MEMORY; 1.279 + 1.280 + NS_ADDREF(*inputStream = inStream); 1.281 + return NS_OK; 1.282 +} 1.283 + 1.284 + 1.285 +// NOTE: called with service lock held 1.286 +nsresult 1.287 +nsDiskCacheStreamIO::GetOutputStream(uint32_t offset, nsIOutputStream ** outputStream) 1.288 +{ 1.289 + NS_ENSURE_ARG_POINTER(outputStream); 1.290 + *outputStream = nullptr; 1.291 + 1.292 + if (!mBinding) return NS_ERROR_NOT_AVAILABLE; 1.293 + 1.294 + NS_ASSERTION(!mOutputStreamIsOpen, "already have an output stream open"); 1.295 + NS_ASSERTION(mInStreamCount == 0, "we already have input streams open"); 1.296 + if (mOutputStreamIsOpen || mInStreamCount) return NS_ERROR_NOT_AVAILABLE; 1.297 + 1.298 + mStreamEnd = mBinding->mCacheEntry->DataSize(); 1.299 + 1.300 + // Inits file or buffer and truncate at the desired offset 1.301 + nsresult rv = SeekAndTruncate(offset); 1.302 + if (NS_FAILED(rv)) return rv; 1.303 + 1.304 + mOutputStreamIsOpen = true; 1.305 + NS_ADDREF(*outputStream = this); 1.306 + return NS_OK; 1.307 +} 1.308 + 1.309 +nsresult 1.310 +nsDiskCacheStreamIO::ClearBinding() 1.311 +{ 1.312 + nsresult rv = NS_OK; 1.313 + if (mBinding && mOutputStreamIsOpen) 1.314 + rv = CloseOutputStream(); 1.315 + mBinding = nullptr; 1.316 + return rv; 1.317 +} 1.318 + 1.319 +NS_IMETHODIMP 1.320 +nsDiskCacheStreamIO::Close() 1.321 +{ 1.322 + if (!mOutputStreamIsOpen) return NS_OK; 1.323 + 1.324 + mozilla::TimeStamp start = mozilla::TimeStamp::Now(); 1.325 + 1.326 + // grab service lock 1.327 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_CLOSEOUTPUTSTREAM)); 1.328 + 1.329 + if (!mBinding) { // if we're severed, just clear member variables 1.330 + mOutputStreamIsOpen = false; 1.331 + return NS_ERROR_NOT_AVAILABLE; 1.332 + } 1.333 + 1.334 + nsresult rv = CloseOutputStream(); 1.335 + if (NS_FAILED(rv)) 1.336 + NS_WARNING("CloseOutputStream() failed"); 1.337 + 1.338 + mozilla::Telemetry::ID id; 1.339 + if (NS_IsMainThread()) 1.340 + id = mozilla::Telemetry::NETWORK_DISK_CACHE_STREAMIO_CLOSE_MAIN_THREAD; 1.341 + else 1.342 + id = mozilla::Telemetry::NETWORK_DISK_CACHE_STREAMIO_CLOSE; 1.343 + mozilla::Telemetry::AccumulateTimeDelta(id, start); 1.344 + 1.345 + return rv; 1.346 +} 1.347 + 1.348 +nsresult 1.349 +nsDiskCacheStreamIO::CloseOutputStream() 1.350 +{ 1.351 + NS_ASSERTION(mBinding, "oops"); 1.352 + 1.353 + CACHE_LOG_DEBUG(("CACHE: CloseOutputStream [%x doomed=%u]\n", 1.354 + mBinding->mRecord.HashNumber(), mBinding->mDoomed)); 1.355 + 1.356 + // Mark outputstream as closed, even if saving the stream fails 1.357 + mOutputStreamIsOpen = false; 1.358 + 1.359 + // When writing to a file, just close the file 1.360 + if (mFD) { 1.361 + (void) PR_Close(mFD); 1.362 + mFD = nullptr; 1.363 + return NS_OK; 1.364 + } 1.365 + 1.366 + // write data to cache blocks, or flush mBuffer to file 1.367 + NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "stream is bigger than buffer"); 1.368 + 1.369 + nsDiskCacheMap *cacheMap = mDevice->CacheMap(); // get map reference 1.370 + nsDiskCacheRecord * record = &mBinding->mRecord; 1.371 + nsresult rv = NS_OK; 1.372 + 1.373 + // delete existing storage 1.374 + if (record->DataLocationInitialized()) { 1.375 + rv = cacheMap->DeleteStorage(record, nsDiskCache::kData); 1.376 + NS_ENSURE_SUCCESS(rv, rv); 1.377 + 1.378 + // Only call UpdateRecord when there is no data to write, 1.379 + // because WriteDataCacheBlocks / FlushBufferToFile calls it. 1.380 + if ((mStreamEnd == 0) && (!mBinding->mDoomed)) { 1.381 + rv = cacheMap->UpdateRecord(record); 1.382 + if (NS_FAILED(rv)) { 1.383 + NS_WARNING("cacheMap->UpdateRecord() failed."); 1.384 + return rv; // XXX doom cache entry 1.385 + } 1.386 + } 1.387 + } 1.388 + 1.389 + if (mStreamEnd == 0) return NS_OK; // nothing to write 1.390 + 1.391 + // try to write to the cache blocks 1.392 + rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mStreamEnd); 1.393 + if (NS_FAILED(rv)) { 1.394 + NS_WARNING("WriteDataCacheBlocks() failed."); 1.395 + 1.396 + // failed to store in cacheblocks, save as separate file 1.397 + rv = FlushBufferToFile(); // initializes DataFileLocation() if necessary 1.398 + if (mFD) { 1.399 + UpdateFileSize(); 1.400 + (void) PR_Close(mFD); 1.401 + mFD = nullptr; 1.402 + } 1.403 + else 1.404 + NS_WARNING("no file descriptor"); 1.405 + } 1.406 + 1.407 + return rv; 1.408 +} 1.409 + 1.410 + 1.411 +// assumptions: 1.412 +// only one thread writing at a time 1.413 +// never have both output and input streams open 1.414 +// OnDataSizeChanged() will have already been called to update entry->DataSize() 1.415 + 1.416 +NS_IMETHODIMP 1.417 +nsDiskCacheStreamIO::Write( const char * buffer, 1.418 + uint32_t count, 1.419 + uint32_t * bytesWritten) 1.420 +{ 1.421 + NS_ENSURE_ARG_POINTER(buffer); 1.422 + NS_ENSURE_ARG_POINTER(bytesWritten); 1.423 + if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED; 1.424 + 1.425 + *bytesWritten = 0; // always initialize to zero in case of errors 1.426 + 1.427 + NS_ASSERTION(count, "Write called with count of zero"); 1.428 + if (count == 0) { 1.429 + return NS_OK; // nothing to write 1.430 + } 1.431 + 1.432 + // grab service lock 1.433 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_WRITE)); 1.434 + if (!mBinding) return NS_ERROR_NOT_AVAILABLE; 1.435 + 1.436 + if (mInStreamCount) { 1.437 + // we have open input streams already 1.438 + // this is an error until we support overlapped I/O 1.439 + NS_WARNING("Attempting to write to cache entry with open input streams.\n"); 1.440 + return NS_ERROR_NOT_AVAILABLE; 1.441 + } 1.442 + 1.443 + // Not writing to file, and it will fit in the cachedatablocks? 1.444 + if (!mFD && (mStreamEnd + count <= kMaxBufferSize)) { 1.445 + 1.446 + // We have more data than the current buffer size? 1.447 + if ((mStreamEnd + count > mBufSize) && (mBufSize < kMaxBufferSize)) { 1.448 + // Increase buffer to the maximum size. 1.449 + mBuffer = (char *) moz_xrealloc(mBuffer, kMaxBufferSize); 1.450 + mBufSize = kMaxBufferSize; 1.451 + } 1.452 + 1.453 + // Store in the buffer but only if it fits 1.454 + if (mStreamEnd + count <= mBufSize) { 1.455 + memcpy(mBuffer + mStreamEnd, buffer, count); 1.456 + mStreamEnd += count; 1.457 + *bytesWritten = count; 1.458 + return NS_OK; 1.459 + } 1.460 + } 1.461 + 1.462 + // There are more bytes than fit in the buffer/cacheblocks, switch to file 1.463 + if (!mFD) { 1.464 + // Opens a cache file and write the buffer to it 1.465 + nsresult rv = FlushBufferToFile(); 1.466 + if (NS_FAILED(rv)) { 1.467 + return rv; 1.468 + } 1.469 + } 1.470 + // Write directly to the file 1.471 + if (PR_Write(mFD, buffer, count) != (int32_t)count) { 1.472 + NS_WARNING("failed to write all data"); 1.473 + return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR() 1.474 + } 1.475 + mStreamEnd += count; 1.476 + *bytesWritten = count; 1.477 + 1.478 + UpdateFileSize(); 1.479 + NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream"); 1.480 + 1.481 + return NS_OK; 1.482 +} 1.483 + 1.484 + 1.485 +void 1.486 +nsDiskCacheStreamIO::UpdateFileSize() 1.487 +{ 1.488 + NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called"); 1.489 + 1.490 + nsDiskCacheRecord * record = &mBinding->mRecord; 1.491 + const uint32_t oldSizeK = record->DataFileSize(); 1.492 + uint32_t newSizeK = (mStreamEnd + 0x03FF) >> 10; 1.493 + 1.494 + // make sure the size won't overflow (bug #651100) 1.495 + if (newSizeK > kMaxDataSizeK) 1.496 + newSizeK = kMaxDataSizeK; 1.497 + 1.498 + if (newSizeK == oldSizeK) return; 1.499 + 1.500 + record->SetDataFileSize(newSizeK); 1.501 + 1.502 + // update cache size totals 1.503 + nsDiskCacheMap * cacheMap = mDevice->CacheMap(); 1.504 + cacheMap->DecrementTotalSize(oldSizeK); // decrement old size 1.505 + cacheMap->IncrementTotalSize(newSizeK); // increment new size 1.506 + 1.507 + if (!mBinding->mDoomed) { 1.508 + nsresult rv = cacheMap->UpdateRecord(record); 1.509 + if (NS_FAILED(rv)) { 1.510 + NS_WARNING("cacheMap->UpdateRecord() failed."); 1.511 + // XXX doom cache entry? 1.512 + } 1.513 + } 1.514 +} 1.515 + 1.516 + 1.517 +nsresult 1.518 +nsDiskCacheStreamIO::OpenCacheFile(int flags, PRFileDesc ** fd) 1.519 +{ 1.520 + NS_ENSURE_ARG_POINTER(fd); 1.521 + 1.522 + CACHE_LOG_DEBUG(("nsDiskCacheStreamIO::OpenCacheFile")); 1.523 + 1.524 + nsresult rv; 1.525 + nsDiskCacheMap * cacheMap = mDevice->CacheMap(); 1.526 + nsCOMPtr<nsIFile> localFile; 1.527 + 1.528 + rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord, 1.529 + nsDiskCache::kData, 1.530 + !!(flags & PR_CREATE_FILE), 1.531 + getter_AddRefs(localFile)); 1.532 + if (NS_FAILED(rv)) return rv; 1.533 + 1.534 + // create PRFileDesc for input stream - the 00600 is just for consistency 1.535 + return localFile->OpenNSPRFileDesc(flags, 00600, fd); 1.536 +} 1.537 + 1.538 + 1.539 +nsresult 1.540 +nsDiskCacheStreamIO::ReadCacheBlocks(uint32_t bufferSize) 1.541 +{ 1.542 + mozilla::eventtracer::AutoEventTracer readCacheBlocks( 1.543 + mBinding->mCacheEntry, 1.544 + mozilla::eventtracer::eExec, 1.545 + mozilla::eventtracer::eDone, 1.546 + "net::cache::ReadCacheBlocks"); 1.547 + 1.548 + NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream"); 1.549 + NS_ASSERTION(bufferSize <= kMaxBufferSize, "bufferSize too large for buffer"); 1.550 + NS_ASSERTION(mStreamEnd <= bufferSize, "data too large for buffer"); 1.551 + 1.552 + nsDiskCacheRecord * record = &mBinding->mRecord; 1.553 + if (!record->DataLocationInitialized()) return NS_OK; 1.554 + 1.555 + NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file"); 1.556 + 1.557 + if (!mBuffer) { 1.558 + mBuffer = (char *) moz_xmalloc(bufferSize); 1.559 + mBufSize = bufferSize; 1.560 + } 1.561 + 1.562 + // read data stored in cache block files 1.563 + nsDiskCacheMap *map = mDevice->CacheMap(); // get map reference 1.564 + return map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd); 1.565 +} 1.566 + 1.567 + 1.568 +nsresult 1.569 +nsDiskCacheStreamIO::FlushBufferToFile() 1.570 +{ 1.571 + mozilla::eventtracer::AutoEventTracer flushBufferToFile( 1.572 + mBinding->mCacheEntry, 1.573 + mozilla::eventtracer::eExec, 1.574 + mozilla::eventtracer::eDone, 1.575 + "net::cache::FlushBufferToFile"); 1.576 + 1.577 + nsresult rv; 1.578 + nsDiskCacheRecord * record = &mBinding->mRecord; 1.579 + 1.580 + if (!mFD) { 1.581 + if (record->DataLocationInitialized() && (record->DataFile() > 0)) { 1.582 + // remove cache block storage 1.583 + nsDiskCacheMap * cacheMap = mDevice->CacheMap(); 1.584 + rv = cacheMap->DeleteStorage(record, nsDiskCache::kData); 1.585 + if (NS_FAILED(rv)) return rv; 1.586 + } 1.587 + record->SetDataFileGeneration(mBinding->mGeneration); 1.588 + 1.589 + // allocate file 1.590 + rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD); 1.591 + if (NS_FAILED(rv)) return rv; 1.592 + 1.593 + int64_t dataSize = mBinding->mCacheEntry->PredictedDataSize(); 1.594 + if (dataSize != -1) 1.595 + mozilla::fallocate(mFD, std::min<int64_t>(dataSize, kPreallocateLimit)); 1.596 + } 1.597 + 1.598 + // write buffer to the file when there is data in it 1.599 + if (mStreamEnd > 0) { 1.600 + if (!mBuffer) { 1.601 + NS_RUNTIMEABORT("Fix me!"); 1.602 + } 1.603 + if (PR_Write(mFD, mBuffer, mStreamEnd) != (int32_t)mStreamEnd) { 1.604 + NS_WARNING("failed to flush all data"); 1.605 + return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR() 1.606 + } 1.607 + } 1.608 + 1.609 + // buffer is no longer valid 1.610 + DeleteBuffer(); 1.611 + 1.612 + return NS_OK; 1.613 +} 1.614 + 1.615 + 1.616 +void 1.617 +nsDiskCacheStreamIO::DeleteBuffer() 1.618 +{ 1.619 + if (mBuffer) { 1.620 + free(mBuffer); 1.621 + mBuffer = nullptr; 1.622 + mBufSize = 0; 1.623 + } 1.624 +} 1.625 + 1.626 +size_t 1.627 +nsDiskCacheStreamIO::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) 1.628 +{ 1.629 + size_t usage = aMallocSizeOf(this); 1.630 + 1.631 + usage += aMallocSizeOf(mFD); 1.632 + usage += aMallocSizeOf(mBuffer); 1.633 + 1.634 + return usage; 1.635 +} 1.636 + 1.637 +nsresult 1.638 +nsDiskCacheStreamIO::SeekAndTruncate(uint32_t offset) 1.639 +{ 1.640 + if (!mBinding) return NS_ERROR_NOT_AVAILABLE; 1.641 + 1.642 + if (uint32_t(offset) > mStreamEnd) return NS_ERROR_FAILURE; 1.643 + 1.644 + // Set the current end to the desired offset 1.645 + mStreamEnd = offset; 1.646 + 1.647 + // Currently stored in file? 1.648 + if (mBinding->mRecord.DataLocationInitialized() && 1.649 + (mBinding->mRecord.DataFile() == 0)) { 1.650 + if (!mFD) { 1.651 + // we need an mFD, we better open it now 1.652 + nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD); 1.653 + if (NS_FAILED(rv)) return rv; 1.654 + } 1.655 + if (offset) { 1.656 + if (PR_Seek(mFD, offset, PR_SEEK_SET) == -1) 1.657 + return NS_ErrorAccordingToNSPR(); 1.658 + } 1.659 + nsDiskCache::Truncate(mFD, offset); 1.660 + UpdateFileSize(); 1.661 + 1.662 + // When we starting at zero again, close file and start with buffer. 1.663 + // If offset is non-zero (and within buffer) an option would be 1.664 + // to read the file into the buffer, but chance is high that it is 1.665 + // rewritten to the file anyway. 1.666 + if (offset == 0) { 1.667 + // close file descriptor 1.668 + (void) PR_Close(mFD); 1.669 + mFD = nullptr; 1.670 + } 1.671 + return NS_OK; 1.672 + } 1.673 + 1.674 + // read data into mBuffer if not read yet. 1.675 + if (offset && !mBuffer) { 1.676 + nsresult rv = ReadCacheBlocks(kMaxBufferSize); 1.677 + if (NS_FAILED(rv)) return rv; 1.678 + } 1.679 + 1.680 + // stream buffer sanity check 1.681 + NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "bad stream"); 1.682 + return NS_OK; 1.683 +} 1.684 + 1.685 + 1.686 +NS_IMETHODIMP 1.687 +nsDiskCacheStreamIO::Flush() 1.688 +{ 1.689 + if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED; 1.690 + return NS_OK; 1.691 +} 1.692 + 1.693 + 1.694 +NS_IMETHODIMP 1.695 +nsDiskCacheStreamIO::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_t *bytesWritten) 1.696 +{ 1.697 + NS_NOTREACHED("WriteFrom"); 1.698 + return NS_ERROR_NOT_IMPLEMENTED; 1.699 +} 1.700 + 1.701 + 1.702 +NS_IMETHODIMP 1.703 +nsDiskCacheStreamIO::WriteSegments( nsReadSegmentFun reader, 1.704 + void * closure, 1.705 + uint32_t count, 1.706 + uint32_t * bytesWritten) 1.707 +{ 1.708 + NS_NOTREACHED("WriteSegments"); 1.709 + return NS_ERROR_NOT_IMPLEMENTED; 1.710 +} 1.711 + 1.712 + 1.713 +NS_IMETHODIMP 1.714 +nsDiskCacheStreamIO::IsNonBlocking(bool * nonBlocking) 1.715 +{ 1.716 + *nonBlocking = false; 1.717 + return NS_OK; 1.718 +}