tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CacheFileOutputStream.cpp (13696B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "CacheLog.h"
      6 #include "CacheFileOutputStream.h"
      7 
      8 #include "CacheFile.h"
      9 #include "CacheEntry.h"
     10 #include "nsStreamUtils.h"
     11 #include "nsThreadUtils.h"
     12 #include "mozilla/IntegerPrintfMacros.h"
     13 #include <algorithm>
     14 
     15 namespace mozilla::net {
     16 
     17 NS_IMPL_ADDREF(CacheFileOutputStream)
     18 NS_IMETHODIMP_(MozExternalRefCountType)
     19 CacheFileOutputStream::Release() {
     20  MOZ_ASSERT(0 != mRefCnt, "dup release");
     21  nsrefcnt count = --mRefCnt;
     22  NS_LOG_RELEASE(this, count, "CacheFileOutputStream");
     23 
     24  if (0 == count) {
     25    mRefCnt = 1;
     26    {
     27      CacheFileAutoLock lock(mFile);
     28      mFile->RemoveOutput(this, mStatus);
     29    }
     30    delete (this);
     31    return 0;
     32  }
     33 
     34  return count;
     35 }
     36 
     37 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
     38  NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
     39  NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
     40  NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
     41  NS_INTERFACE_MAP_ENTRY(nsITellableStream)
     42  NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
     43  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
     44 NS_INTERFACE_MAP_END
     45 
     46 CacheFileOutputStream::CacheFileOutputStream(
     47    CacheFile* aFile, CacheOutputCloseListener* aCloseListener,
     48    bool aAlternativeData)
     49    : mFile(aFile),
     50      mCloseListener(aCloseListener),
     51      mPos(0),
     52      mClosed(false),
     53      mAlternativeData(aAlternativeData),
     54      mStatus(NS_OK),
     55      mCallbackFlags(0) {
     56  LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
     57 
     58  if (mAlternativeData) {
     59    mPos = mFile->mAltDataOffset;
     60  }
     61 }
     62 
     63 CacheFileOutputStream::~CacheFileOutputStream() {
     64  LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
     65 }
     66 
     67 // nsIOutputStream
     68 NS_IMETHODIMP
     69 CacheFileOutputStream::Close() {
     70  LOG(("CacheFileOutputStream::Close() [this=%p]", this));
     71  return CloseWithStatus(NS_OK);
     72 }
     73 
     74 NS_IMETHODIMP
     75 CacheFileOutputStream::Flush() {
     76  // TODO do we need to implement flush ???
     77  LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
     78  return NS_OK;
     79 }
     80 
     81 NS_IMETHODIMP
     82 CacheFileOutputStream::StreamStatus() {
     83  CacheFileAutoLock lock(mFile);
     84  mFile->AssertOwnsLock();  // For thread-safety analysis
     85 
     86  LOG(("CacheFileOutputStream::Close() [this=%p]", this));
     87  if (mClosed) {
     88    return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
     89  }
     90  return NS_OK;
     91 }
     92 
     93 NS_IMETHODIMP
     94 CacheFileOutputStream::Write(const char* aBuf, uint32_t aCount,
     95                             uint32_t* _retval) {
     96  CacheFileAutoLock lock(mFile);
     97  mFile->AssertOwnsLock();  // For thread-safety analysis
     98 
     99  LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
    100 
    101  if (mClosed) {
    102    LOG(
    103        ("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
    104         "status=0x%08" PRIx32 "]",
    105         this, static_cast<uint32_t>(mStatus)));
    106 
    107    return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
    108  }
    109 
    110  if (!mFile->mSkipSizeCheck &&
    111      CacheObserver::EntryIsTooBig(mPos + aCount, !mFile->mMemoryOnly)) {
    112    LOG(("CacheFileOutputStream::Write() - Entry is too big. [this=%p]", this));
    113 
    114    CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG);
    115    return NS_ERROR_FILE_TOO_BIG;
    116  }
    117 
    118  // We use 64-bit offset when accessing the file, unfortunately we use 32-bit
    119  // metadata offset, so we cannot handle data bigger than 4GB.
    120  if (mPos + aCount > PR_UINT32_MAX) {
    121    LOG(("CacheFileOutputStream::Write() - Entry's size exceeds 4GB. [this=%p]",
    122         this));
    123 
    124    CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG);
    125    return NS_ERROR_FILE_TOO_BIG;
    126  }
    127 
    128  *_retval = aCount;
    129 
    130  if (mDict) {
    131    // We need to calculate the hash for the entry as we save it
    132    // We don't necessarily need to save the data in memory, however
    133    mDict->AccumulateHash(aBuf, aCount);
    134  }
    135 
    136  while (aCount) {
    137    EnsureCorrectChunk(false);
    138    if (NS_FAILED(mStatus)) {
    139      return mStatus;
    140    }
    141 
    142    FillHole();
    143    if (NS_FAILED(mStatus)) {
    144      return mStatus;
    145    }
    146 
    147    uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
    148    uint32_t canWrite = kChunkSize - chunkOffset;
    149    uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount);
    150 
    151    CacheFileChunkWriteHandle hnd =
    152        mChunk->GetWriteHandle(chunkOffset + thisWrite);
    153    if (!hnd.Buf()) {
    154      CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY);
    155      return NS_ERROR_OUT_OF_MEMORY;
    156    }
    157 
    158    memcpy(hnd.Buf() + chunkOffset, aBuf, thisWrite);
    159    hnd.UpdateDataSize(chunkOffset, thisWrite);
    160 
    161    mPos += thisWrite;
    162    aBuf += thisWrite;
    163    aCount -= thisWrite;
    164  }
    165 
    166  EnsureCorrectChunk(true);
    167 
    168  LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]", *_retval,
    169       this));
    170 
    171  return NS_OK;
    172 }
    173 
    174 NS_IMETHODIMP
    175 CacheFileOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
    176                                 uint32_t* _retval) {
    177  LOG(
    178      ("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
    179       ", count=%d]",
    180       this, aFromStream, aCount));
    181 
    182  return NS_ERROR_NOT_IMPLEMENTED;
    183 }
    184 
    185 NS_IMETHODIMP
    186 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
    187                                     uint32_t aCount, uint32_t* _retval) {
    188  LOG(
    189      ("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
    190       "count=%d]",
    191       this, aCount));
    192 
    193  return NS_ERROR_NOT_IMPLEMENTED;
    194 }
    195 
    196 NS_IMETHODIMP
    197 CacheFileOutputStream::IsNonBlocking(bool* _retval) {
    198  *_retval = false;
    199  return NS_OK;
    200 }
    201 
    202 // nsIAsyncOutputStream
    203 NS_IMETHODIMP
    204 CacheFileOutputStream::CloseWithStatus(nsresult aStatus) {
    205  CacheFileAutoLock lock(mFile);
    206 
    207  LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32
    208       "]",
    209       this, static_cast<uint32_t>(aStatus)));
    210 
    211  return CloseWithStatusLocked(aStatus);
    212 }
    213 
    214 nsresult CacheFileOutputStream::CloseWithStatusLocked(nsresult aStatus) {
    215  LOG(
    216      ("CacheFileOutputStream::CloseWithStatusLocked() [this=%p, "
    217       "aStatus=0x%08" PRIx32 "]",
    218       this, static_cast<uint32_t>(aStatus)));
    219 
    220  if (mClosed) {
    221    MOZ_ASSERT(!mCallback);
    222    return NS_OK;
    223  }
    224 
    225  mClosed = true;
    226  mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
    227 
    228  if (mChunk) {
    229    ReleaseChunk();
    230  }
    231 
    232  if (mCallback) {
    233    NotifyListener();
    234  }
    235 
    236  mFile->RemoveOutput(this, mStatus);
    237 
    238  return NS_OK;
    239 }
    240 
    241 NS_IMETHODIMP
    242 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback* aCallback,
    243                                 uint32_t aFlags, uint32_t aRequestedCount,
    244                                 nsIEventTarget* aEventTarget) {
    245  CacheFileAutoLock lock(mFile);
    246 
    247  LOG(
    248      ("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
    249       "requestedCount=%d, eventTarget=%p]",
    250       this, aCallback, aFlags, aRequestedCount, aEventTarget));
    251 
    252  mCallback = aCallback;
    253  mCallbackFlags = aFlags;
    254  mCallbackTarget = aEventTarget;
    255 
    256  if (!mCallback) return NS_OK;
    257 
    258  // The stream is blocking so it is writable at any time
    259  if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY)) NotifyListener();
    260 
    261  return NS_OK;
    262 }
    263 
    264 // nsISeekableStream
    265 NS_IMETHODIMP
    266 CacheFileOutputStream::Seek(int32_t whence, int64_t offset) {
    267  CacheFileAutoLock lock(mFile);
    268  mFile->AssertOwnsLock();  // For thread-safety analysis
    269 
    270  LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%" PRId64 "]",
    271       this, whence, offset));
    272 
    273  if (mClosed) {
    274    LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
    275    return NS_BASE_STREAM_CLOSED;
    276  }
    277 
    278  int64_t newPos = offset;
    279  switch (whence) {
    280    case NS_SEEK_SET:
    281      if (mAlternativeData) {
    282        newPos += mFile->mAltDataOffset;
    283      }
    284      break;
    285    case NS_SEEK_CUR:
    286      newPos += mPos;
    287      break;
    288    case NS_SEEK_END:
    289      if (mAlternativeData) {
    290        newPos += mFile->mDataSize;
    291      } else {
    292        newPos += mFile->mAltDataOffset;
    293      }
    294      break;
    295    default:
    296      NS_ERROR("invalid whence");
    297      return NS_ERROR_INVALID_ARG;
    298  }
    299  mPos = newPos;
    300  EnsureCorrectChunk(true);
    301 
    302  LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
    303  return NS_OK;
    304 }
    305 
    306 NS_IMETHODIMP
    307 CacheFileOutputStream::SetEOF() {
    308  MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
    309  // Right now we don't use SetEOF(). If we ever need this method, we need
    310  // to think about what to do with input streams that already points beyond
    311  // new EOF.
    312  return NS_ERROR_NOT_IMPLEMENTED;
    313 }
    314 
    315 // nsITellableStream
    316 NS_IMETHODIMP
    317 CacheFileOutputStream::Tell(int64_t* _retval) {
    318  CacheFileAutoLock lock(mFile);
    319  mFile->AssertOwnsLock();  // For thread-safety analysis
    320 
    321  if (mClosed) {
    322    LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
    323    return NS_BASE_STREAM_CLOSED;
    324  }
    325 
    326  *_retval = mPos;
    327 
    328  if (mAlternativeData) {
    329    *_retval -= mFile->mAltDataOffset;
    330  }
    331 
    332  LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%" PRId64 "]", this,
    333       *_retval));
    334  return NS_OK;
    335 }
    336 
    337 // CacheFileChunkListener
    338 nsresult CacheFileOutputStream::OnChunkRead(nsresult aResult,
    339                                            CacheFileChunk* aChunk) {
    340  MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
    341  return NS_ERROR_UNEXPECTED;
    342 }
    343 
    344 nsresult CacheFileOutputStream::OnChunkWritten(nsresult aResult,
    345                                               CacheFileChunk* aChunk) {
    346  MOZ_CRASH("CacheFileOutputStream::OnChunkWritten should not be called!");
    347  return NS_ERROR_UNEXPECTED;
    348 }
    349 
    350 nsresult CacheFileOutputStream::OnChunkAvailable(nsresult aResult,
    351                                                 uint32_t aChunkIdx,
    352                                                 CacheFileChunk* aChunk) {
    353  MOZ_CRASH("CacheFileOutputStream::OnChunkAvailable should not be called!");
    354  return NS_ERROR_UNEXPECTED;
    355 }
    356 
    357 nsresult CacheFileOutputStream::OnChunkUpdated(CacheFileChunk* aChunk) {
    358  MOZ_CRASH("CacheFileOutputStream::OnChunkUpdated should not be called!");
    359  return NS_ERROR_UNEXPECTED;
    360 }
    361 
    362 void CacheFileOutputStream::NotifyCloseListener() {
    363  RefPtr<CacheOutputCloseListener> listener;
    364  listener.swap(mCloseListener);
    365  if (!listener) return;
    366 
    367  listener->OnOutputClosed();
    368 }
    369 
    370 void CacheFileOutputStream::ReleaseChunk() {
    371  mFile->AssertOwnsLock();
    372 
    373  LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]", this,
    374       mChunk->Index()));
    375 
    376  // If the chunk didn't write any data we need to remove hash for this chunk
    377  // that was added when the chunk was created in CacheFile::GetChunkLocked.
    378  if (mChunk->DataSize() == 0) {
    379    // It must be due to a failure, we don't create a new chunk when we don't
    380    // have data to write.
    381    MOZ_ASSERT(NS_FAILED(mChunk->GetStatus()));
    382    mFile->mMetadata->RemoveHash(mChunk->Index());
    383  }
    384 
    385  mFile->ReleaseOutsideLock(std::move(mChunk));
    386 }
    387 
    388 void CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly) {
    389  mFile->AssertOwnsLock();
    390 
    391  LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
    392       this, aReleaseOnly));
    393 
    394  uint32_t chunkIdx = mPos / kChunkSize;
    395 
    396  if (mChunk) {
    397    if (mChunk->Index() == chunkIdx) {
    398      // we have a correct chunk
    399      LOG(
    400          ("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
    401           "[this=%p, idx=%d]",
    402           this, chunkIdx));
    403 
    404      return;
    405    }
    406    ReleaseChunk();
    407  }
    408 
    409  if (aReleaseOnly) return;
    410 
    411  nsresult rv;
    412  rv = mFile->GetChunkLocked(chunkIdx, CacheFile::WRITER, nullptr,
    413                             getter_AddRefs(mChunk));
    414  if (NS_FAILED(rv)) {
    415    LOG(
    416        ("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
    417         "[this=%p, idx=%d, rv=0x%08" PRIx32 "]",
    418         this, chunkIdx, static_cast<uint32_t>(rv)));
    419    CloseWithStatusLocked(rv);
    420  }
    421 }
    422 
    423 void CacheFileOutputStream::FillHole() {
    424  mFile->AssertOwnsLock();
    425 
    426  MOZ_ASSERT(mChunk);
    427  MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
    428 
    429  uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize;
    430  if (mChunk->DataSize() >= pos) return;
    431 
    432  LOG(
    433      ("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
    434       "%d-%d [this=%p]",
    435       mChunk->Index(), mChunk->DataSize(), pos - 1, this));
    436 
    437  CacheFileChunkWriteHandle hnd = mChunk->GetWriteHandle(pos);
    438  if (!hnd.Buf()) {
    439    CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY);
    440    return;
    441  }
    442 
    443  uint32_t offset = hnd.DataSize();
    444  memset(hnd.Buf() + offset, 0, pos - offset);
    445  hnd.UpdateDataSize(offset, pos - offset);
    446 }
    447 
    448 void CacheFileOutputStream::NotifyListener() {
    449  mFile->AssertOwnsLock();
    450 
    451  LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
    452 
    453  MOZ_ASSERT(mCallback);
    454 
    455  if (!mCallbackTarget) {
    456    mCallbackTarget = CacheFileIOManager::IOTarget();
    457    if (!mCallbackTarget) {
    458      LOG(
    459          ("CacheFileOutputStream::NotifyListener() - Cannot get Cache I/O "
    460           "thread! Using main thread for callback."));
    461      mCallbackTarget = GetMainThreadSerialEventTarget();
    462    }
    463  }
    464 
    465  nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
    466      NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
    467 
    468  mCallback = nullptr;
    469  mCallbackTarget = nullptr;
    470 
    471  asyncCallback->OnOutputStreamReady(this);
    472 }
    473 
    474 // Memory reporting
    475 
    476 size_t CacheFileOutputStream::SizeOfIncludingThis(
    477    mozilla::MallocSizeOf mallocSizeOf) const {
    478  // Everything the stream keeps a reference to is already reported somewhere
    479  // else.
    480  // mFile reports itself.
    481  // mChunk reported as part of CacheFile.
    482  // mCloseListener is CacheEntry, already reported.
    483  // mCallback is usually CacheFile or a class that is reported elsewhere.
    484  return mallocSizeOf(this);
    485 }
    486 
    487 }  // namespace mozilla::net