tor-browser

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

nsFileStreams.cpp (28913B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "ipc/IPCMessageUtils.h"
      7 
      8 #if defined(XP_UNIX)
      9 #  include <unistd.h>
     10 #elif defined(XP_WIN)
     11 #  include <windows.h>
     12 #  include "nsILocalFileWin.h"
     13 #else
     14 // XXX add necessary include file for ftruncate (or equivalent)
     15 #endif
     16 
     17 #include "private/pprio.h"
     18 
     19 #include "nsFileStreams.h"
     20 #include "nsIFile.h"
     21 #include "nsReadLine.h"
     22 #include "nsIClassInfoImpl.h"
     23 #include "mozilla/ipc/InputStreamUtils.h"
     24 #include "mozilla/ipc/RandomAccessStreamParams.h"
     25 #include "mozilla/FileUtils.h"
     26 #include "mozilla/UniquePtr.h"
     27 #include "nsNetCID.h"
     28 #include "nsNetUtil.h"
     29 #include "nsXULAppAPI.h"
     30 
     31 using FileHandleType = mozilla::ipc::FileDescriptor::PlatformHandleType;
     32 
     33 using namespace mozilla::ipc;
     34 
     35 using mozilla::DebugOnly;
     36 using mozilla::Maybe;
     37 using mozilla::Nothing;
     38 using mozilla::Some;
     39 
     40 ////////////////////////////////////////////////////////////////////////////////
     41 // nsFileStreamBase
     42 
     43 nsFileStreamBase::~nsFileStreamBase() {
     44  // We don't want to try to rewrind the stream when shutting down.
     45  mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
     46 
     47  Close();
     48 }
     49 
     50 NS_IMPL_ISUPPORTS(nsFileStreamBase, nsISeekableStream, nsITellableStream,
     51                  nsIFileMetadata)
     52 
     53 NS_IMETHODIMP
     54 nsFileStreamBase::Seek(int32_t whence, int64_t offset) {
     55  nsresult rv = DoPendingOpen();
     56  NS_ENSURE_SUCCESS(rv, rv);
     57 
     58  int64_t cnt = PR_Seek64(mFD, offset, (PRSeekWhence)whence);
     59  if (cnt == int64_t(-1)) {
     60    return NS_ErrorAccordingToNSPR();
     61  }
     62  return NS_OK;
     63 }
     64 
     65 NS_IMETHODIMP
     66 nsFileStreamBase::Tell(int64_t* result) {
     67  if (mState == eDeferredOpen && !(mOpenParams.ioFlags & PR_APPEND)) {
     68    *result = 0;
     69    return NS_OK;
     70  }
     71 
     72  nsresult rv = DoPendingOpen();
     73  NS_ENSURE_SUCCESS(rv, rv);
     74 
     75  int64_t cnt = PR_Seek64(mFD, 0, PR_SEEK_CUR);
     76  if (cnt == int64_t(-1)) {
     77    return NS_ErrorAccordingToNSPR();
     78  }
     79  *result = cnt;
     80  return NS_OK;
     81 }
     82 
     83 NS_IMETHODIMP
     84 nsFileStreamBase::SetEOF() {
     85  nsresult rv = DoPendingOpen();
     86  NS_ENSURE_SUCCESS(rv, rv);
     87 
     88 #if defined(XP_UNIX)
     89  // Some system calls require an EOF offset.
     90  int64_t offset;
     91  rv = Tell(&offset);
     92  if (NS_FAILED(rv)) return rv;
     93 #endif
     94 
     95 #if defined(XP_UNIX)
     96  if (ftruncate(PR_FileDesc2NativeHandle(mFD), offset) != 0) {
     97    NS_ERROR("ftruncate failed");
     98    return NS_ERROR_FAILURE;
     99  }
    100 #elif defined(XP_WIN)
    101  if (!SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(mFD))) {
    102    NS_ERROR("SetEndOfFile failed");
    103    return NS_ERROR_FAILURE;
    104  }
    105 #else
    106  // XXX not implemented
    107 #endif
    108 
    109  return NS_OK;
    110 }
    111 
    112 NS_IMETHODIMP
    113 nsFileStreamBase::GetSize(int64_t* _retval) {
    114  nsresult rv = DoPendingOpen();
    115  NS_ENSURE_SUCCESS(rv, rv);
    116 
    117  PRFileInfo64 info;
    118  if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
    119    return NS_BASE_STREAM_OSERROR;
    120  }
    121 
    122  *_retval = int64_t(info.size);
    123 
    124  return NS_OK;
    125 }
    126 
    127 NS_IMETHODIMP
    128 nsFileStreamBase::GetLastModified(int64_t* _retval) {
    129  nsresult rv = DoPendingOpen();
    130  NS_ENSURE_SUCCESS(rv, rv);
    131 
    132  PRFileInfo64 info;
    133  if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
    134    return NS_BASE_STREAM_OSERROR;
    135  }
    136 
    137  int64_t modTime = int64_t(info.modifyTime);
    138  if (modTime == 0) {
    139    *_retval = 0;
    140  } else {
    141    *_retval = modTime / int64_t(PR_USEC_PER_MSEC);
    142  }
    143 
    144  return NS_OK;
    145 }
    146 
    147 NS_IMETHODIMP
    148 nsFileStreamBase::GetFileDescriptor(PRFileDesc** _retval) {
    149  nsresult rv = DoPendingOpen();
    150  NS_ENSURE_SUCCESS(rv, rv);
    151 
    152  *_retval = mFD;
    153  return NS_OK;
    154 }
    155 
    156 nsresult nsFileStreamBase::Close() {
    157  if (mState == eClosed) {
    158    return NS_OK;
    159  }
    160 
    161  CleanUpOpen();
    162 
    163  nsresult rv = NS_OK;
    164  if (mFD) {
    165    if (PR_Close(mFD) == PR_FAILURE) rv = NS_BASE_STREAM_OSERROR;
    166    mFD = nullptr;
    167    mState = eClosed;
    168  }
    169  return rv;
    170 }
    171 
    172 nsresult nsFileStreamBase::Available(uint64_t* aResult) {
    173  nsresult rv = DoPendingOpen();
    174  NS_ENSURE_SUCCESS(rv, rv);
    175 
    176  // PR_Available with files over 4GB returns an error, so we have to
    177  // use the 64-bit version of PR_Available.
    178  int64_t avail = PR_Available64(mFD);
    179  if (avail == -1) {
    180    return NS_ErrorAccordingToNSPR();
    181  }
    182 
    183  // If available is greater than 4GB, return 4GB
    184  *aResult = (uint64_t)avail;
    185  return NS_OK;
    186 }
    187 
    188 nsresult nsFileStreamBase::Read(char* aBuf, uint32_t aCount,
    189                                uint32_t* aResult) {
    190  nsresult rv = DoPendingOpen();
    191  if (rv == NS_BASE_STREAM_CLOSED) {
    192    *aResult = 0;
    193    return NS_OK;
    194  }
    195 
    196  if (NS_FAILED(rv)) {
    197    return rv;
    198  }
    199 
    200  int32_t bytesRead = PR_Read(mFD, aBuf, aCount);
    201  if (bytesRead == -1) {
    202    return NS_ErrorAccordingToNSPR();
    203  }
    204 
    205  *aResult = bytesRead;
    206  return NS_OK;
    207 }
    208 
    209 nsresult nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter,
    210                                        void* aClosure, uint32_t aCount,
    211                                        uint32_t* aResult) {
    212  // ReadSegments is not implemented because it would be inefficient when
    213  // the writer does not consume all data.  If you want to call ReadSegments,
    214  // wrap a BufferedInputStream around the file stream.  That will call
    215  // Read().
    216 
    217  return NS_ERROR_NOT_IMPLEMENTED;
    218 }
    219 
    220 nsresult nsFileStreamBase::IsNonBlocking(bool* aNonBlocking) {
    221  *aNonBlocking = false;
    222  return NS_OK;
    223 }
    224 
    225 nsresult nsFileStreamBase::Flush(void) {
    226  nsresult rv = DoPendingOpen();
    227  NS_ENSURE_SUCCESS(rv, rv);
    228 
    229  int32_t cnt = PR_Sync(mFD);
    230  if (cnt == -1) {
    231    return NS_ErrorAccordingToNSPR();
    232  }
    233  return NS_OK;
    234 }
    235 
    236 nsresult nsFileStreamBase::StreamStatus() {
    237  switch (mState) {
    238    case eUnitialized:
    239      MOZ_CRASH("This should not happen.");
    240      return NS_ERROR_FAILURE;
    241 
    242    case eDeferredOpen:
    243      return NS_OK;
    244 
    245    case eOpened:
    246      MOZ_ASSERT(mFD);
    247      if (NS_WARN_IF(!mFD)) {
    248        return NS_ERROR_FAILURE;
    249      }
    250      return NS_OK;
    251 
    252    case eClosed:
    253      MOZ_ASSERT(!mFD);
    254      return NS_BASE_STREAM_CLOSED;
    255 
    256    case eError:
    257      return mErrorValue;
    258  }
    259 
    260  MOZ_CRASH("Invalid mState value.");
    261  return NS_ERROR_FAILURE;
    262 }
    263 
    264 nsresult nsFileStreamBase::Write(const char* buf, uint32_t count,
    265                                 uint32_t* result) {
    266  nsresult rv = DoPendingOpen();
    267  NS_ENSURE_SUCCESS(rv, rv);
    268 
    269  int32_t cnt = PR_Write(mFD, buf, count);
    270  if (cnt == -1) {
    271    return NS_ErrorAccordingToNSPR();
    272  }
    273  *result = cnt;
    274  return NS_OK;
    275 }
    276 
    277 nsresult nsFileStreamBase::WriteFrom(nsIInputStream* inStr, uint32_t count,
    278                                     uint32_t* _retval) {
    279  MOZ_ASSERT_UNREACHABLE("WriteFrom (see source comment)");
    280  return NS_ERROR_NOT_IMPLEMENTED;
    281  // File streams intentionally do not support this method.
    282  // If you need something like this, then you should wrap
    283  // the file stream using nsIBufferedOutputStream
    284 }
    285 
    286 nsresult nsFileStreamBase::WriteSegments(nsReadSegmentFun reader, void* closure,
    287                                         uint32_t count, uint32_t* _retval) {
    288  return NS_ERROR_NOT_IMPLEMENTED;
    289  // File streams intentionally do not support this method.
    290  // If you need something like this, then you should wrap
    291  // the file stream using nsIBufferedOutputStream
    292 }
    293 
    294 nsresult nsFileStreamBase::MaybeOpen(nsIFile* aFile, int32_t aIoFlags,
    295                                     int32_t aPerm, bool aDeferred) {
    296  NS_ENSURE_STATE(aFile);
    297 
    298  mOpenParams.ioFlags = aIoFlags;
    299  mOpenParams.perm = aPerm;
    300 
    301  if (aDeferred) {
    302    // Clone the file, as it may change between now and the deferred open
    303    nsCOMPtr<nsIFile> file;
    304    nsresult rv = aFile->Clone(getter_AddRefs(file));
    305    NS_ENSURE_SUCCESS(rv, rv);
    306 
    307    mOpenParams.localFile = std::move(file);
    308    NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED);
    309 
    310    mState = eDeferredOpen;
    311    return NS_OK;
    312  }
    313 
    314  mOpenParams.localFile = aFile;
    315 
    316  // Following call open() at main thread.
    317  // Main thread might be blocked, while open a remote file.
    318  return DoOpen();
    319 }
    320 
    321 void nsFileStreamBase::CleanUpOpen() { mOpenParams.localFile = nullptr; }
    322 
    323 nsresult nsFileStreamBase::DoOpen() {
    324  MOZ_ASSERT(mState == eDeferredOpen || mState == eUnitialized ||
    325             mState == eClosed);
    326  NS_ASSERTION(!mFD, "Already have a file descriptor!");
    327  NS_ASSERTION(mOpenParams.localFile, "Must have a file to open");
    328 
    329  PRFileDesc* fd;
    330  nsresult rv;
    331 
    332  if (mOpenParams.ioFlags & PR_CREATE_FILE) {
    333    nsCOMPtr<nsIFile> parent;
    334    mOpenParams.localFile->GetParent(getter_AddRefs(parent));
    335 
    336    // Result doesn't need to be checked. If the file's parent path does not
    337    // exist, make it. If it does exist, do nothing.
    338    if (parent) {
    339      (void)parent->Create(nsIFile::DIRECTORY_TYPE, 0755);
    340    }
    341  }
    342 
    343 #ifdef XP_WIN
    344  if (mBehaviorFlags & nsIFileInputStream::SHARE_DELETE) {
    345    nsCOMPtr<nsILocalFileWin> file = do_QueryInterface(mOpenParams.localFile);
    346    MOZ_ASSERT(file);
    347 
    348    rv = file->OpenNSPRFileDescShareDelete(mOpenParams.ioFlags,
    349                                           mOpenParams.perm, &fd);
    350  } else
    351 #endif  // XP_WIN
    352  {
    353    rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags,
    354                                                 mOpenParams.perm, &fd);
    355  }
    356 
    357  CleanUpOpen();
    358 
    359  if (NS_FAILED(rv)) {
    360    mState = eError;
    361    mErrorValue = rv;
    362    return rv;
    363  }
    364 
    365  mFD = fd;
    366  mState = eOpened;
    367 
    368  return NS_OK;
    369 }
    370 
    371 nsresult nsFileStreamBase::DoPendingOpen() {
    372  switch (mState) {
    373    case eUnitialized:
    374      MOZ_CRASH("This should not happen.");
    375      return NS_ERROR_FAILURE;
    376 
    377    case eDeferredOpen:
    378      return DoOpen();
    379 
    380    case eOpened:
    381      MOZ_ASSERT(mFD);
    382      if (NS_WARN_IF(!mFD)) {
    383        return NS_ERROR_FAILURE;
    384      }
    385      return NS_OK;
    386 
    387    case eClosed:
    388      MOZ_ASSERT(!mFD);
    389      return NS_BASE_STREAM_CLOSED;
    390 
    391    case eError:
    392      return mErrorValue;
    393  }
    394 
    395  MOZ_CRASH("Invalid mState value.");
    396  return NS_ERROR_FAILURE;
    397 }
    398 
    399 ////////////////////////////////////////////////////////////////////////////////
    400 // nsFileInputStream
    401 
    402 NS_IMPL_ADDREF_INHERITED(nsFileInputStream, nsFileStreamBase)
    403 NS_IMPL_RELEASE_INHERITED(nsFileInputStream, nsFileStreamBase)
    404 
    405 NS_IMPL_CLASSINFO(nsFileInputStream, nullptr, nsIClassInfo::THREADSAFE,
    406                  NS_LOCALFILEINPUTSTREAM_CID)
    407 
    408 NS_INTERFACE_MAP_BEGIN(nsFileInputStream)
    409  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
    410  NS_INTERFACE_MAP_ENTRY(nsIFileInputStream)
    411  NS_INTERFACE_MAP_ENTRY(nsILineInputStream)
    412  NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
    413  NS_IMPL_QUERY_CLASSINFO(nsFileInputStream)
    414  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, IsCloneable())
    415 NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase)
    416 
    417 NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream, nsIInputStream,
    418                            nsIFileInputStream, nsISeekableStream,
    419                            nsITellableStream, nsILineInputStream)
    420 
    421 nsresult nsFileInputStream::Create(REFNSIID aIID, void** aResult) {
    422  RefPtr<nsFileInputStream> stream = new nsFileInputStream();
    423  return stream->QueryInterface(aIID, aResult);
    424 }
    425 
    426 nsresult nsFileInputStream::Open(nsIFile* aFile, int32_t aIOFlags,
    427                                 int32_t aPerm) {
    428  nsresult rv = NS_OK;
    429 
    430  // If the previous file is open, close it
    431  if (mFD) {
    432    rv = Close();
    433    if (NS_FAILED(rv)) return rv;
    434  }
    435 
    436  // Open the file
    437  if (aIOFlags == -1) aIOFlags = PR_RDONLY;
    438  if (aPerm == -1) aPerm = 0;
    439 
    440  return MaybeOpen(aFile, aIOFlags, aPerm,
    441                   mBehaviorFlags & nsIFileInputStream::DEFER_OPEN);
    442 }
    443 
    444 NS_IMETHODIMP
    445 nsFileInputStream::Init(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm,
    446                        int32_t aBehaviorFlags) {
    447  NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED);
    448  NS_ENSURE_TRUE(mState == eUnitialized || mState == eClosed,
    449                 NS_ERROR_ALREADY_INITIALIZED);
    450 
    451  mBehaviorFlags = aBehaviorFlags;
    452  mState = eUnitialized;
    453 
    454  mFile = aFile;
    455  mIOFlags = aIOFlags;
    456  mPerm = aPerm;
    457 
    458  return Open(aFile, aIOFlags, aPerm);
    459 }
    460 
    461 NS_IMETHODIMP
    462 nsFileInputStream::Close() {
    463  // If this stream has already been closed, do nothing.
    464  if (mState == eClosed) {
    465    return NS_OK;
    466  }
    467 
    468  // Get the cache position at the time the file was close. This allows
    469  // NS_SEEK_CUR on a closed file that has been opened with
    470  // REOPEN_ON_REWIND.
    471  if (mBehaviorFlags & REOPEN_ON_REWIND) {
    472    // Get actual position. Not one modified by subclasses
    473    nsFileStreamBase::Tell(&mCachedPosition);
    474  }
    475 
    476  // explicitly clear mLineBuffer in case this stream is reopened
    477  mLineBuffer = nullptr;
    478  return nsFileStreamBase::Close();
    479 }
    480 
    481 NS_IMETHODIMP
    482 nsFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
    483  nsresult rv = nsFileStreamBase::Read(aBuf, aCount, _retval);
    484  if (rv == NS_ERROR_FILE_NOT_FOUND) {
    485    // Don't warn if this is a deffered file not found.
    486    return rv;
    487  }
    488 
    489  if (NS_FAILED(rv)) {
    490    return rv;
    491  }
    492 
    493  // Check if we're at the end of file and need to close
    494  if (mBehaviorFlags & CLOSE_ON_EOF && *_retval == 0) {
    495    Close();
    496  }
    497 
    498  return NS_OK;
    499 }
    500 
    501 NS_IMETHODIMP
    502 nsFileInputStream::ReadLine(nsACString& aLine, bool* aResult) {
    503  if (!mLineBuffer) {
    504    mLineBuffer = mozilla::MakeUnique<nsLineBuffer<char>>();
    505  }
    506  return NS_ReadLine(this, mLineBuffer.get(), aLine, aResult);
    507 }
    508 
    509 NS_IMETHODIMP
    510 nsFileInputStream::Seek(int32_t aWhence, int64_t aOffset) {
    511  return SeekInternal(aWhence, aOffset);
    512 }
    513 
    514 nsresult nsFileInputStream::SeekInternal(int32_t aWhence, int64_t aOffset,
    515                                         bool aClearBuf) {
    516  nsresult rv = DoPendingOpen();
    517  if (rv != NS_OK && rv != NS_BASE_STREAM_CLOSED) {
    518    return rv;
    519  }
    520 
    521  if (aClearBuf) {
    522    mLineBuffer = nullptr;
    523  }
    524 
    525  if (rv == NS_BASE_STREAM_CLOSED) {
    526    if (mBehaviorFlags & REOPEN_ON_REWIND) {
    527      rv = Open(mFile, mIOFlags, mPerm);
    528      NS_ENSURE_SUCCESS(rv, rv);
    529 
    530      // If the file was closed, and we do a relative seek, use the
    531      // position we cached when we closed the file to seek to the right
    532      // location.
    533      if (aWhence == NS_SEEK_CUR) {
    534        aWhence = NS_SEEK_SET;
    535        aOffset += mCachedPosition;
    536      }
    537      // If we're trying to seek to the start then we're done, so
    538      // return early to avoid Seek from calling DoPendingOpen and
    539      // opening the underlying file earlier than necessary.
    540      if (aWhence == NS_SEEK_SET && aOffset == 0) {
    541        return NS_OK;
    542      }
    543    } else {
    544      return NS_BASE_STREAM_CLOSED;
    545    }
    546  }
    547 
    548  return nsFileStreamBase::Seek(aWhence, aOffset);
    549 }
    550 
    551 NS_IMETHODIMP
    552 nsFileInputStream::Tell(int64_t* aResult) {
    553  return nsFileStreamBase::Tell(aResult);
    554 }
    555 
    556 NS_IMETHODIMP
    557 nsFileInputStream::Available(uint64_t* aResult) {
    558  return nsFileStreamBase::Available(aResult);
    559 }
    560 
    561 NS_IMETHODIMP
    562 nsFileInputStream::StreamStatus() { return nsFileStreamBase::StreamStatus(); }
    563 
    564 void nsFileInputStream::SerializedComplexity(uint32_t aMaxSize,
    565                                             uint32_t* aSizeUsed,
    566                                             uint32_t* aPipes,
    567                                             uint32_t* aTransferables) {
    568  *aTransferables = 1;
    569 }
    570 
    571 void nsFileInputStream::Serialize(InputStreamParams& aParams, uint32_t aMaxSize,
    572                                  uint32_t* aSizeUsed) {
    573  MOZ_ASSERT(aSizeUsed);
    574  *aSizeUsed = 0;
    575 
    576  FileInputStreamParams params;
    577 
    578  if (NS_SUCCEEDED(DoPendingOpen())) {
    579    MOZ_ASSERT(mFD);
    580    FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
    581    NS_ASSERTION(fd, "This should never be null!");
    582 
    583    params.fileDescriptor() = FileDescriptor(fd);
    584 
    585    Close();
    586  } else {
    587    NS_WARNING(
    588        "This file has not been opened (or could not be opened). "
    589        "Sending an invalid file descriptor to the other process!");
    590 
    591    params.fileDescriptor() = FileDescriptor();
    592  }
    593 
    594  int32_t behaviorFlags = mBehaviorFlags;
    595 
    596  // The receiving process (or thread) is going to have an open file
    597  // descriptor automatically so transferring this flag is meaningless.
    598  behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN;
    599 
    600  params.behaviorFlags() = behaviorFlags;
    601  params.ioFlags() = mIOFlags;
    602 
    603  aParams = params;
    604 }
    605 
    606 bool nsFileInputStream::Deserialize(const InputStreamParams& aParams) {
    607  NS_ASSERTION(!mFD, "Already have a file descriptor?!");
    608  NS_ASSERTION(mState == nsFileStreamBase::eUnitialized, "Deferring open?!");
    609  NS_ASSERTION(!mFile, "Should never have a file here!");
    610  NS_ASSERTION(!mPerm, "This should always be 0!");
    611 
    612  if (aParams.type() != InputStreamParams::TFileInputStreamParams) {
    613    NS_WARNING("Received unknown parameters from the other process!");
    614    return false;
    615  }
    616 
    617  const FileInputStreamParams& params = aParams.get_FileInputStreamParams();
    618 
    619  const FileDescriptor& fd = params.fileDescriptor();
    620 
    621  if (fd.IsValid()) {
    622    auto rawFD = fd.ClonePlatformHandle();
    623    PRFileDesc* fileDesc = PR_ImportFile(PROsfd(rawFD.release()));
    624    if (!fileDesc) {
    625      NS_WARNING("Failed to import file handle!");
    626      return false;
    627    }
    628    mFD = fileDesc;
    629    mState = eOpened;
    630  } else {
    631    NS_WARNING("Received an invalid file descriptor!");
    632    mState = eError;
    633    mErrorValue = NS_ERROR_FILE_NOT_FOUND;
    634  }
    635 
    636  mBehaviorFlags = params.behaviorFlags();
    637 
    638  if (!XRE_IsParentProcess()) {
    639    // A child process shouldn't close when it reads the end because it will
    640    // not be able to reopen the file later.
    641    mBehaviorFlags &= ~nsIFileInputStream::CLOSE_ON_EOF;
    642 
    643    // A child process will not be able to reopen the file so this flag is
    644    // meaningless.
    645    mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
    646  }
    647 
    648  mIOFlags = params.ioFlags();
    649 
    650  return true;
    651 }
    652 
    653 bool nsFileInputStream::IsCloneable() const {
    654  // This inputStream is cloneable only if has been created using Init() and
    655  // it owns a nsIFile. This is not true when it is deserialized from IPC.
    656  return XRE_IsParentProcess() && mFile;
    657 }
    658 
    659 NS_IMETHODIMP
    660 nsFileInputStream::GetCloneable(bool* aCloneable) {
    661  *aCloneable = IsCloneable();
    662  return NS_OK;
    663 }
    664 
    665 NS_IMETHODIMP
    666 nsFileInputStream::Clone(nsIInputStream** aResult) {
    667  MOZ_ASSERT(IsCloneable());
    668  return NS_NewLocalFileInputStream(aResult, mFile, mIOFlags, mPerm,
    669                                    mBehaviorFlags);
    670 }
    671 
    672 ////////////////////////////////////////////////////////////////////////////////
    673 // nsFileOutputStream
    674 
    675 NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream, nsFileStreamBase,
    676                            nsIOutputStream, nsIFileOutputStream)
    677 
    678 nsresult nsFileOutputStream::Create(REFNSIID aIID, void** aResult) {
    679  RefPtr<nsFileOutputStream> stream = new nsFileOutputStream();
    680  return stream->QueryInterface(aIID, aResult);
    681 }
    682 
    683 NS_IMETHODIMP
    684 nsFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
    685                         int32_t behaviorFlags) {
    686  NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
    687  NS_ENSURE_TRUE(mState == eUnitialized || mState == eClosed,
    688                 NS_ERROR_ALREADY_INITIALIZED);
    689 
    690  mBehaviorFlags = behaviorFlags;
    691  mState = eUnitialized;
    692 
    693  if (ioFlags == -1) ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
    694  if (perm <= 0) perm = 0664;
    695 
    696  return MaybeOpen(file, ioFlags, perm,
    697                   mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN);
    698 }
    699 
    700 nsresult nsFileOutputStream::InitWithFileDescriptor(
    701    const mozilla::ipc::FileDescriptor& aFd) {
    702  NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
    703  NS_ENSURE_TRUE(mState == eUnitialized || mState == eClosed,
    704                 NS_ERROR_ALREADY_INITIALIZED);
    705 
    706  if (aFd.IsValid()) {
    707    auto rawFD = aFd.ClonePlatformHandle();
    708    PRFileDesc* fileDesc = PR_ImportFile(PROsfd(rawFD.release()));
    709    if (!fileDesc) {
    710      NS_WARNING("Failed to import file handle!");
    711      return NS_ERROR_FAILURE;
    712    }
    713    mFD = fileDesc;
    714    mState = eOpened;
    715  } else {
    716    mState = eError;
    717    mErrorValue = NS_ERROR_FILE_NOT_FOUND;
    718  }
    719 
    720  return NS_OK;
    721 }
    722 
    723 NS_IMETHODIMP
    724 nsFileOutputStream::Preallocate(int64_t aLength) {
    725  if (!mFD) {
    726    return NS_ERROR_NOT_INITIALIZED;
    727  }
    728 
    729  if (!mozilla::fallocate(mFD, aLength)) {
    730    return NS_ERROR_FAILURE;
    731  }
    732 
    733  return NS_OK;
    734 }
    735 
    736 ////////////////////////////////////////////////////////////////////////////////
    737 // nsAtomicFileOutputStream
    738 
    739 NS_IMPL_ISUPPORTS_INHERITED(nsAtomicFileOutputStream, nsFileOutputStream,
    740                            nsISafeOutputStream, nsIOutputStream,
    741                            nsIFileOutputStream)
    742 
    743 NS_IMETHODIMP
    744 nsAtomicFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
    745                               int32_t behaviorFlags) {
    746  // While `PR_APPEND` is not supported, `-1` is used as `ioFlags` parameter
    747  // in some places, and `PR_APPEND | PR_TRUNCATE` does not require appending
    748  // to existing file. So, throw an exception only if `PR_APPEND` is
    749  // explicitly specified without `PR_TRUNCATE`.
    750  if ((ioFlags & PR_APPEND) && !(ioFlags & PR_TRUNCATE)) {
    751    return NS_ERROR_INVALID_ARG;
    752  }
    753  return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
    754 }
    755 
    756 nsresult nsAtomicFileOutputStream::DoOpen() {
    757  // Make sure mOpenParams.localFile will be empty if we bail somewhere in
    758  // this function
    759  nsCOMPtr<nsIFile> file;
    760  file.swap(mOpenParams.localFile);
    761 
    762  if (!file) {
    763    return NS_ERROR_NOT_INITIALIZED;
    764  }
    765  nsresult rv = file->Exists(&mTargetFileExists);
    766  if (NS_FAILED(rv)) {
    767    NS_ERROR("Can't tell if target file exists");
    768    mTargetFileExists =
    769        true;  // Safer to assume it exists - we just do more work.
    770  }
    771 
    772  // follow symlinks, for two reasons:
    773  // 1) if a user has deliberately set up a profile file as a symlink, we
    774  //    honor it
    775  // 2) to make the MoveToNative() in Finish() an atomic operation (which may
    776  //    not be the case if moving across directories on different
    777  //    filesystems).
    778  nsCOMPtr<nsIFile> tempResult;
    779  rv = file->Clone(getter_AddRefs(tempResult));
    780  if (NS_SUCCEEDED(rv) && mTargetFileExists) {
    781    tempResult->Normalize();
    782  }
    783 
    784  if (NS_SUCCEEDED(rv) && mTargetFileExists) {
    785    // Abort if |file| is not writable; it won't work as an output stream.
    786    bool isWritable;
    787    if (NS_SUCCEEDED(file->IsWritable(&isWritable)) && !isWritable) {
    788      return NS_ERROR_FILE_ACCESS_DENIED;
    789    }
    790 
    791    uint32_t origPerm;
    792    if (NS_FAILED(file->GetPermissions(&origPerm))) {
    793      NS_ERROR("Can't get permissions of target file");
    794      origPerm = mOpenParams.perm;
    795    }
    796 
    797    // XXX What if |perm| is more restrictive then |origPerm|?
    798    // This leaves the user supplied permissions as they were.
    799    rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
    800  }
    801  if (NS_SUCCEEDED(rv)) {
    802    // nsFileOutputStream::DoOpen will work on the temporary file, so we
    803    // prepare it and place it in mOpenParams.localFile.
    804    mOpenParams.localFile = tempResult;
    805    mTempFile = tempResult;
    806    mTargetFile = file;
    807    rv = nsFileOutputStream::DoOpen();
    808  }
    809  return rv;
    810 }
    811 
    812 NS_IMETHODIMP
    813 nsAtomicFileOutputStream::Close() {
    814  nsresult rv = nsFileOutputStream::Close();
    815 
    816  // the consumer doesn't want the original file overwritten -
    817  // so clean up by removing the temp file.
    818  if (mTempFile) {
    819    mTempFile->Remove(false);
    820    mTempFile = nullptr;
    821  }
    822 
    823  return rv;
    824 }
    825 
    826 NS_IMETHODIMP
    827 nsAtomicFileOutputStream::Finish() {
    828  nsresult rv = nsFileOutputStream::Close();
    829 
    830  // if there is no temp file, don't try to move it over the original target.
    831  // It would destroy the targetfile if close() is called twice.
    832  if (!mTempFile) return rv;
    833 
    834  // Only overwrite if everything was ok, and the temp file could be closed.
    835  if (NS_SUCCEEDED(mWriteResult) && NS_SUCCEEDED(rv)) {
    836    NS_ENSURE_STATE(mTargetFile);
    837 
    838    if (!mTargetFileExists) {
    839      // If the target file did not exist when we were initialized, then the
    840      // temp file we gave out was actually a reference to the target file.
    841      // since we succeeded in writing to the temp file (and hence succeeded
    842      // in writing to the target file), there is nothing more to do.
    843 #ifdef DEBUG
    844      bool equal;
    845      if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal) {
    846        NS_WARNING("mTempFile not equal to mTargetFile");
    847      }
    848 #endif
    849    } else {
    850      nsAutoString targetFilename;
    851      rv = mTargetFile->GetLeafName(targetFilename);
    852      if (NS_SUCCEEDED(rv)) {
    853        // This will replace target.
    854        rv = mTempFile->MoveTo(nullptr, targetFilename);
    855        if (NS_FAILED(rv)) mTempFile->Remove(false);
    856      }
    857    }
    858  } else {
    859    mTempFile->Remove(false);
    860 
    861    // if writing failed, propagate the failure code to the caller.
    862    if (NS_FAILED(mWriteResult)) rv = mWriteResult;
    863  }
    864  mTempFile = nullptr;
    865  return rv;
    866 }
    867 
    868 NS_IMETHODIMP
    869 nsAtomicFileOutputStream::Write(const char* buf, uint32_t count,
    870                                uint32_t* result) {
    871  nsresult rv = nsFileOutputStream::Write(buf, count, result);
    872  if (NS_SUCCEEDED(mWriteResult)) {
    873    if (NS_FAILED(rv)) {
    874      mWriteResult = rv;
    875    } else if (count != *result) {
    876      mWriteResult = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
    877    }
    878 
    879    if (NS_FAILED(mWriteResult) && count > 0) {
    880      NS_WARNING("writing to output stream failed! data may be lost");
    881    }
    882  }
    883  return rv;
    884 }
    885 
    886 ////////////////////////////////////////////////////////////////////////////////
    887 // nsSafeFileOutputStream
    888 
    889 NS_IMETHODIMP
    890 nsSafeFileOutputStream::Finish() {
    891  (void)Flush();
    892  return nsAtomicFileOutputStream::Finish();
    893 }
    894 
    895 ////////////////////////////////////////////////////////////////////////////////
    896 // nsFileRandomAccessStream
    897 
    898 nsresult nsFileRandomAccessStream::Create(REFNSIID aIID, void** aResult) {
    899  RefPtr<nsFileRandomAccessStream> stream = new nsFileRandomAccessStream();
    900  return stream->QueryInterface(aIID, aResult);
    901 }
    902 
    903 NS_IMPL_ISUPPORTS_INHERITED(nsFileRandomAccessStream, nsFileStreamBase,
    904                            nsIRandomAccessStream, nsIFileRandomAccessStream,
    905                            nsIInputStream, nsIOutputStream)
    906 
    907 NS_IMETHODIMP
    908 nsFileRandomAccessStream::GetInputStream(nsIInputStream** aInputStream) {
    909  nsCOMPtr<nsIInputStream> inputStream(this);
    910 
    911  inputStream.forget(aInputStream);
    912  return NS_OK;
    913 }
    914 
    915 NS_IMETHODIMP
    916 nsFileRandomAccessStream::GetOutputStream(nsIOutputStream** aOutputStream) {
    917  nsCOMPtr<nsIOutputStream> outputStream(this);
    918 
    919  outputStream.forget(aOutputStream);
    920  return NS_OK;
    921 }
    922 
    923 nsIInputStream* nsFileRandomAccessStream::InputStream() { return this; }
    924 
    925 nsIOutputStream* nsFileRandomAccessStream::OutputStream() { return this; }
    926 
    927 RandomAccessStreamParams nsFileRandomAccessStream::Serialize(
    928    nsIInterfaceRequestor* aCallbacks) {
    929  FileRandomAccessStreamParams params;
    930 
    931  if (NS_SUCCEEDED(DoPendingOpen())) {
    932    MOZ_ASSERT(mFD);
    933    FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
    934    MOZ_ASSERT(fd, "This should never be null!");
    935 
    936    params.fileDescriptor() = FileDescriptor(fd);
    937 
    938    Close();
    939  } else {
    940    NS_WARNING(
    941        "This file has not been opened (or could not be opened). "
    942        "Sending an invalid file descriptor to the other process!");
    943 
    944    params.fileDescriptor() = FileDescriptor();
    945  }
    946 
    947  int32_t behaviorFlags = mBehaviorFlags;
    948 
    949  // The receiving process (or thread) is going to have an open file
    950  // descriptor automatically so transferring this flag is meaningless.
    951  behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN;
    952 
    953  params.behaviorFlags() = behaviorFlags;
    954 
    955  return params;
    956 }
    957 
    958 bool nsFileRandomAccessStream::Deserialize(
    959    RandomAccessStreamParams& aStreamParams) {
    960  MOZ_ASSERT(!mFD, "Already have a file descriptor?!");
    961  MOZ_ASSERT(mState == nsFileStreamBase::eUnitialized, "Deferring open?!");
    962 
    963  if (aStreamParams.type() !=
    964      RandomAccessStreamParams::TFileRandomAccessStreamParams) {
    965    NS_WARNING("Received unknown parameters from the other process!");
    966    return false;
    967  }
    968 
    969  const FileRandomAccessStreamParams& params =
    970      aStreamParams.get_FileRandomAccessStreamParams();
    971 
    972  const FileDescriptor& fd = params.fileDescriptor();
    973 
    974  if (fd.IsValid()) {
    975    auto rawFD = fd.ClonePlatformHandle();
    976    PRFileDesc* fileDesc = PR_ImportFile(PROsfd(rawFD.release()));
    977    if (!fileDesc) {
    978      NS_WARNING("Failed to import file handle!");
    979      return false;
    980    }
    981    mFD = fileDesc;
    982    mState = eOpened;
    983  } else {
    984    NS_WARNING("Received an invalid file descriptor!");
    985    mState = eError;
    986    mErrorValue = NS_ERROR_FILE_NOT_FOUND;
    987  }
    988 
    989  mBehaviorFlags = params.behaviorFlags();
    990 
    991  return true;
    992 }
    993 
    994 NS_IMETHODIMP
    995 nsFileRandomAccessStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
    996                               int32_t behaviorFlags) {
    997  NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
    998  NS_ENSURE_TRUE(mState == eUnitialized || mState == eClosed,
    999                 NS_ERROR_ALREADY_INITIALIZED);
   1000 
   1001  mBehaviorFlags = behaviorFlags;
   1002  mState = eUnitialized;
   1003 
   1004  if (ioFlags == -1) ioFlags = PR_RDWR;
   1005  if (perm <= 0) perm = 0;
   1006 
   1007  return MaybeOpen(file, ioFlags, perm,
   1008                   mBehaviorFlags & nsIFileRandomAccessStream::DEFER_OPEN);
   1009 }
   1010 
   1011 ////////////////////////////////////////////////////////////////////////////////