netwerk/base/src/nsFileStreams.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.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     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/. */
     6 #include "ipc/IPCMessageUtils.h"
     8 #if defined(XP_UNIX) || defined(XP_BEOS)
     9 #include <unistd.h>
    10 #elif defined(XP_WIN)
    11 #include <windows.h>
    12 #else
    13 // XXX add necessary include file for ftruncate (or equivalent)
    14 #endif
    16 #include "private/pprio.h"
    18 #include "nsFileStreams.h"
    19 #include "nsIFile.h"
    20 #include "nsReadLine.h"
    21 #include "nsIClassInfoImpl.h"
    22 #include "mozilla/ipc/InputStreamUtils.h"
    23 #include "nsNetCID.h"
    25 #define NS_NO_INPUT_BUFFERING 1 // see http://bugzilla.mozilla.org/show_bug.cgi?id=41067
    27 typedef mozilla::ipc::FileDescriptor::PlatformHandleType FileHandleType;
    29 using namespace mozilla::ipc;
    30 using mozilla::DebugOnly;
    32 ////////////////////////////////////////////////////////////////////////////////
    33 // nsFileStreamBase
    35 nsFileStreamBase::nsFileStreamBase()
    36     : mFD(nullptr)
    37     , mBehaviorFlags(0)
    38     , mDeferredOpen(false)
    39 {
    40 }
    42 nsFileStreamBase::~nsFileStreamBase()
    43 {
    44     Close();
    45 }
    47 NS_IMPL_ISUPPORTS(nsFileStreamBase,
    48                   nsISeekableStream,
    49                   nsIFileMetadata)
    51 NS_IMETHODIMP
    52 nsFileStreamBase::Seek(int32_t whence, int64_t offset)
    53 {
    54     nsresult rv = DoPendingOpen();
    55     NS_ENSURE_SUCCESS(rv, rv);
    57     if (mFD == nullptr)
    58         return NS_BASE_STREAM_CLOSED;
    60     int64_t cnt = PR_Seek64(mFD, offset, (PRSeekWhence)whence);
    61     if (cnt == int64_t(-1)) {
    62         return NS_ErrorAccordingToNSPR();
    63     }
    64     return NS_OK;
    65 }
    67 NS_IMETHODIMP
    68 nsFileStreamBase::Tell(int64_t *result)
    69 {
    70     nsresult rv = DoPendingOpen();
    71     NS_ENSURE_SUCCESS(rv, rv);
    73     if (mFD == nullptr)
    74         return NS_BASE_STREAM_CLOSED;
    76     int64_t cnt = PR_Seek64(mFD, 0, PR_SEEK_CUR);
    77     if (cnt == int64_t(-1)) {
    78         return NS_ErrorAccordingToNSPR();
    79     }
    80     *result = cnt;
    81     return NS_OK;
    82 }
    84 NS_IMETHODIMP
    85 nsFileStreamBase::SetEOF()
    86 {
    87     nsresult rv = DoPendingOpen();
    88     NS_ENSURE_SUCCESS(rv, rv);
    90     if (mFD == nullptr)
    91         return NS_BASE_STREAM_CLOSED;
    93 #if defined(XP_UNIX) || defined(XP_BEOS)
    94     // Some system calls require an EOF offset.
    95     int64_t offset;
    96     rv = Tell(&offset);
    97     if (NS_FAILED(rv)) return rv;
    98 #endif
   100 #if defined(XP_UNIX) || defined(XP_BEOS)
   101     if (ftruncate(PR_FileDesc2NativeHandle(mFD), offset) != 0) {
   102         NS_ERROR("ftruncate failed");
   103         return NS_ERROR_FAILURE;
   104     }
   105 #elif defined(XP_WIN)
   106     if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(mFD))) {
   107         NS_ERROR("SetEndOfFile failed");
   108         return NS_ERROR_FAILURE;
   109     }
   110 #else
   111     // XXX not implemented
   112 #endif
   114     return NS_OK;
   115 }
   117 NS_IMETHODIMP
   118 nsFileStreamBase::GetSize(int64_t* _retval)
   119 {
   120     nsresult rv = DoPendingOpen();
   121     NS_ENSURE_SUCCESS(rv, rv);
   123     if (!mFD) {
   124         return NS_BASE_STREAM_CLOSED;
   125     }
   127     PRFileInfo64 info;
   128     if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
   129         return NS_BASE_STREAM_OSERROR;
   130     }
   132     *_retval = int64_t(info.size);
   134     return NS_OK;
   135 }
   137 NS_IMETHODIMP
   138 nsFileStreamBase::GetLastModified(int64_t* _retval)
   139 {
   140     nsresult rv = DoPendingOpen();
   141     NS_ENSURE_SUCCESS(rv, rv);
   143     if (!mFD) {
   144         return NS_BASE_STREAM_CLOSED;
   145     }
   147     PRFileInfo64 info;
   148     if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
   149         return NS_BASE_STREAM_OSERROR;
   150     }
   152     int64_t modTime = int64_t(info.modifyTime);
   153     if (modTime == 0) {
   154         *_retval = 0;
   155     }
   156     else {
   157         *_retval = modTime / int64_t(PR_USEC_PER_MSEC);
   158     }
   160     return NS_OK;
   161 }
   163 nsresult
   164 nsFileStreamBase::Close()
   165 {
   166     CleanUpOpen();
   168     nsresult rv = NS_OK;
   169     if (mFD) {
   170         if (PR_Close(mFD) == PR_FAILURE)
   171             rv = NS_BASE_STREAM_OSERROR;
   172         mFD = nullptr;
   173     }
   174     return rv;
   175 }
   177 nsresult
   178 nsFileStreamBase::Available(uint64_t* aResult)
   179 {
   180     nsresult rv = DoPendingOpen();
   181     NS_ENSURE_SUCCESS(rv, rv);
   183     if (!mFD) {
   184         return NS_BASE_STREAM_CLOSED;
   185     }
   187     // PR_Available with files over 4GB returns an error, so we have to
   188     // use the 64-bit version of PR_Available.
   189     int64_t avail = PR_Available64(mFD);
   190     if (avail == -1) {
   191         return NS_ErrorAccordingToNSPR();
   192     }
   194     // If available is greater than 4GB, return 4GB
   195     *aResult = (uint64_t)avail;
   196     return NS_OK;
   197 }
   199 nsresult
   200 nsFileStreamBase::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
   201 {
   202     nsresult rv = DoPendingOpen();
   203     NS_ENSURE_SUCCESS(rv, rv);
   205     if (!mFD) {
   206         *aResult = 0;
   207         return NS_OK;
   208     }
   210     int32_t bytesRead = PR_Read(mFD, aBuf, aCount);
   211     if (bytesRead == -1) {
   212         return NS_ErrorAccordingToNSPR();
   213     }
   215     *aResult = bytesRead;
   216     return NS_OK;
   217 }
   219 nsresult
   220 nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
   221                                uint32_t aCount, uint32_t* aResult)
   222 {
   223     // ReadSegments is not implemented because it would be inefficient when
   224     // the writer does not consume all data.  If you want to call ReadSegments,
   225     // wrap a BufferedInputStream around the file stream.  That will call
   226     // Read().
   228     // If this is ever implemented you might need to modify
   229     // nsPartialFileInputStream::ReadSegments
   231     return NS_ERROR_NOT_IMPLEMENTED;
   232 }
   234 nsresult
   235 nsFileStreamBase::IsNonBlocking(bool *aNonBlocking)
   236 {
   237     *aNonBlocking = false;
   238     return NS_OK;
   239 }
   241 nsresult
   242 nsFileStreamBase::Flush(void)
   243 {
   244     nsresult rv = DoPendingOpen();
   245     NS_ENSURE_SUCCESS(rv, rv);
   247     if (mFD == nullptr)
   248         return NS_BASE_STREAM_CLOSED;
   250     int32_t cnt = PR_Sync(mFD);
   251     if (cnt == -1) {
   252         return NS_ErrorAccordingToNSPR();
   253     }
   254     return NS_OK;
   255 }
   257 nsresult
   258 nsFileStreamBase::Write(const char *buf, uint32_t count, uint32_t *result)
   259 {
   260     nsresult rv = DoPendingOpen();
   261     NS_ENSURE_SUCCESS(rv, rv);
   263     if (mFD == nullptr)
   264         return NS_BASE_STREAM_CLOSED;
   266     int32_t cnt = PR_Write(mFD, buf, count);
   267     if (cnt == -1) {
   268         return NS_ErrorAccordingToNSPR();
   269     }
   270     *result = cnt;
   271     return NS_OK;
   272 }
   274 nsresult
   275 nsFileStreamBase::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
   276 {
   277     NS_NOTREACHED("WriteFrom (see source comment)");
   278     return NS_ERROR_NOT_IMPLEMENTED;
   279     // File streams intentionally do not support this method.
   280     // If you need something like this, then you should wrap
   281     // the file stream using nsIBufferedOutputStream
   282 }
   284 nsresult
   285 nsFileStreamBase::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
   286 {
   287     return NS_ERROR_NOT_IMPLEMENTED;
   288     // File streams intentionally do not support this method.
   289     // If you need something like this, then you should wrap
   290     // the file stream using nsIBufferedOutputStream
   291 }
   293 nsresult
   294 nsFileStreamBase::MaybeOpen(nsIFile* aFile, int32_t aIoFlags,
   295                             int32_t aPerm, bool aDeferred)
   296 {
   297     NS_ENSURE_STATE(aFile);
   299     mOpenParams.ioFlags = aIoFlags;
   300     mOpenParams.perm = aPerm;
   302     if (aDeferred) {
   303         // Clone the file, as it may change between now and the deferred open
   304         nsCOMPtr<nsIFile> file;
   305         nsresult rv = aFile->Clone(getter_AddRefs(file));
   306         NS_ENSURE_SUCCESS(rv, rv);
   308         mOpenParams.localFile = do_QueryInterface(file);
   309         NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED);
   311         mDeferredOpen = true;
   312         return NS_OK;
   313     }
   315     mOpenParams.localFile = aFile;
   317     return DoOpen();
   318 }
   320 void
   321 nsFileStreamBase::CleanUpOpen()
   322 {
   323     mOpenParams.localFile = nullptr;
   324     mDeferredOpen = false;
   325 }
   327 nsresult
   328 nsFileStreamBase::DoOpen()
   329 {
   330     NS_ASSERTION(!mFD, "Already have a file descriptor!");
   331     NS_ASSERTION(mOpenParams.localFile, "Must have a file to open");
   333     PRFileDesc* fd;
   334     nsresult rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags,
   335                                                           mOpenParams.perm,
   336                                                           &fd);
   337     CleanUpOpen();
   338     if (NS_FAILED(rv))
   339         return rv;
   340     mFD = fd;
   342     return NS_OK;
   343 }
   345 nsresult
   346 nsFileStreamBase::DoPendingOpen()
   347 {
   348     if (!mDeferredOpen) {
   349         return NS_OK;
   350     }
   352     return DoOpen();
   353 }
   355 ////////////////////////////////////////////////////////////////////////////////
   356 // nsFileInputStream
   358 NS_IMPL_ADDREF_INHERITED(nsFileInputStream, nsFileStreamBase)
   359 NS_IMPL_RELEASE_INHERITED(nsFileInputStream, nsFileStreamBase)
   361 NS_IMPL_CLASSINFO(nsFileInputStream, nullptr, nsIClassInfo::THREADSAFE,
   362                   NS_LOCALFILEINPUTSTREAM_CID)
   364 NS_INTERFACE_MAP_BEGIN(nsFileInputStream)
   365     NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   366     NS_INTERFACE_MAP_ENTRY(nsIFileInputStream)
   367     NS_INTERFACE_MAP_ENTRY(nsILineInputStream)
   368     NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   369     NS_IMPL_QUERY_CLASSINFO(nsFileInputStream)
   370 NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase)
   372 NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream,
   373                             nsIInputStream,
   374                             nsIFileInputStream,
   375                             nsISeekableStream,
   376                             nsILineInputStream)
   378 nsresult
   379 nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
   380 {
   381     NS_ENSURE_NO_AGGREGATION(aOuter);
   383     nsFileInputStream* stream = new nsFileInputStream();
   384     if (stream == nullptr)
   385         return NS_ERROR_OUT_OF_MEMORY;
   386     NS_ADDREF(stream);
   387     nsresult rv = stream->QueryInterface(aIID, aResult);
   388     NS_RELEASE(stream);
   389     return rv;
   390 }
   392 nsresult
   393 nsFileInputStream::Open(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm)
   394 {   
   395     nsresult rv = NS_OK;
   397     // If the previous file is open, close it
   398     if (mFD) {
   399         rv = Close();
   400         if (NS_FAILED(rv)) return rv;
   401     }
   403     // Open the file
   404     if (aIOFlags == -1)
   405         aIOFlags = PR_RDONLY;
   406     if (aPerm == -1)
   407         aPerm = 0;
   409     rv = MaybeOpen(aFile, aIOFlags, aPerm,
   410                    mBehaviorFlags & nsIFileInputStream::DEFER_OPEN);
   411     if (NS_FAILED(rv)) return rv;
   413     if (mBehaviorFlags & DELETE_ON_CLOSE) {
   414         // POSIX compatible filesystems allow a file to be unlinked while a
   415         // file descriptor is still referencing the file.  since we've already
   416         // opened the file descriptor, we'll try to remove the file.  if that
   417         // fails, then we'll just remember the nsIFile and remove it after we
   418         // close the file descriptor.
   419         rv = aFile->Remove(false);
   420         if (NS_SUCCEEDED(rv)) {
   421           // No need to remove it later. Clear the flag.
   422           mBehaviorFlags &= ~DELETE_ON_CLOSE;
   423         }
   424     }
   426     return NS_OK;
   427 }
   429 NS_IMETHODIMP
   430 nsFileInputStream::Init(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm,
   431                         int32_t aBehaviorFlags)
   432 {
   433     NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED);
   434     NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
   436     mBehaviorFlags = aBehaviorFlags;
   438     mFile = aFile;
   439     mIOFlags = aIOFlags;
   440     mPerm = aPerm;
   442     return Open(aFile, aIOFlags, aPerm);
   443 }
   445 NS_IMETHODIMP
   446 nsFileInputStream::Close()
   447 {
   448     // Get the cache position at the time the file was close. This allows
   449     // NS_SEEK_CUR on a closed file that has been opened with
   450     // REOPEN_ON_REWIND.
   451     if (mBehaviorFlags & REOPEN_ON_REWIND) {
   452         // Get actual position. Not one modified by subclasses
   453         nsFileStreamBase::Tell(&mCachedPosition);
   454     }
   456     // null out mLineBuffer in case Close() is called again after failing
   457     mLineBuffer = nullptr;
   458     nsresult rv = nsFileStreamBase::Close();
   459     if (NS_FAILED(rv)) return rv;
   460     if (mFile && (mBehaviorFlags & DELETE_ON_CLOSE)) {
   461         rv = mFile->Remove(false);
   462         NS_ASSERTION(NS_SUCCEEDED(rv), "failed to delete file");
   463         // If we don't need to save the file for reopening, free it up
   464         if (!(mBehaviorFlags & REOPEN_ON_REWIND)) {
   465           mFile = nullptr;
   466         }
   467     }
   468     return rv;
   469 }
   471 NS_IMETHODIMP
   472 nsFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
   473 {
   474     nsresult rv = nsFileStreamBase::Read(aBuf, aCount, _retval);
   475     NS_ENSURE_SUCCESS(rv, rv);
   477     // Check if we're at the end of file and need to close
   478     if (mBehaviorFlags & CLOSE_ON_EOF && *_retval == 0) {
   479         Close();
   480     }
   482     return NS_OK;
   483 }
   485 NS_IMETHODIMP
   486 nsFileInputStream::ReadLine(nsACString& aLine, bool* aResult)
   487 {
   488     nsresult rv = DoPendingOpen();
   489     NS_ENSURE_SUCCESS(rv, rv);
   491     if (!mLineBuffer) {
   492       mLineBuffer = new nsLineBuffer<char>;
   493     }
   494     return NS_ReadLine(this, mLineBuffer.get(), aLine, aResult);
   495 }
   497 NS_IMETHODIMP
   498 nsFileInputStream::Seek(int32_t aWhence, int64_t aOffset)
   499 {
   500     nsresult rv = DoPendingOpen();
   501     NS_ENSURE_SUCCESS(rv, rv);
   503     mLineBuffer = nullptr;
   504     if (!mFD) {
   505         if (mBehaviorFlags & REOPEN_ON_REWIND) {
   506             rv = Open(mFile, mIOFlags, mPerm);
   507             NS_ENSURE_SUCCESS(rv, rv);
   509             // If the file was closed, and we do a relative seek, use the
   510             // position we cached when we closed the file to seek to the right
   511             // location.
   512             if (aWhence == NS_SEEK_CUR) {
   513                 aWhence = NS_SEEK_SET;
   514                 aOffset += mCachedPosition;
   515             }
   516         } else {
   517             return NS_BASE_STREAM_CLOSED;
   518         }
   519     }
   521     return nsFileStreamBase::Seek(aWhence, aOffset);
   522 }
   524 NS_IMETHODIMP
   525 nsFileInputStream::Tell(int64_t *aResult)
   526 {
   527     return nsFileStreamBase::Tell(aResult);
   528 }
   530 NS_IMETHODIMP
   531 nsFileInputStream::Available(uint64_t *aResult)
   532 {
   533     return nsFileStreamBase::Available(aResult);
   534 }
   536 void
   537 nsFileInputStream::Serialize(InputStreamParams& aParams,
   538                              FileDescriptorArray& aFileDescriptors)
   539 {
   540     FileInputStreamParams params;
   542     if (mFD) {
   543         FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
   544         NS_ASSERTION(fd, "This should never be null!");
   546         DebugOnly<FileDescriptor*> dbgFD = aFileDescriptors.AppendElement(fd);
   547         NS_ASSERTION(dbgFD->IsValid(), "Sending an invalid file descriptor!");
   549         params.fileDescriptorIndex() = aFileDescriptors.Length() - 1;
   550     } else {
   551         NS_WARNING("This file has not been opened (or could not be opened). "
   552                    "Sending an invalid file descriptor to the other process!");
   553     }
   555     int32_t behaviorFlags = mBehaviorFlags;
   557     // The other process shouldn't close when it reads the end because it will
   558     // not be able to reopen the file later.
   559     behaviorFlags &= ~nsIFileInputStream::CLOSE_ON_EOF;
   561     // The other process will not be able to reopen the file so transferring
   562     // this flag is meaningless.
   563     behaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
   565     // The other process is going to have an open file descriptor automatically
   566     // so transferring this flag is meaningless.
   567     behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN;
   569     params.behaviorFlags() = behaviorFlags;
   570     params.ioFlags() = mIOFlags;
   572     aParams = params;
   573 }
   575 bool
   576 nsFileInputStream::Deserialize(const InputStreamParams& aParams,
   577                                const FileDescriptorArray& aFileDescriptors)
   578 {
   579     NS_ASSERTION(!mFD, "Already have a file descriptor?!");
   580     NS_ASSERTION(!mDeferredOpen, "Deferring open?!");
   581     NS_ASSERTION(!mFile, "Should never have a file here!");
   582     NS_ASSERTION(!mPerm, "This should always be 0!");
   584     if (aParams.type() != InputStreamParams::TFileInputStreamParams) {
   585         NS_WARNING("Received unknown parameters from the other process!");
   586         return false;
   587     }
   589     const FileInputStreamParams& params = aParams.get_FileInputStreamParams();
   591     uint32_t fileDescriptorIndex = params.fileDescriptorIndex();
   593     FileDescriptor fd;
   594     if (fileDescriptorIndex < aFileDescriptors.Length()) {
   595         fd = aFileDescriptors[fileDescriptorIndex];
   596         NS_WARN_IF_FALSE(fd.IsValid(), "Received an invalid file descriptor!");
   597     } else {
   598         NS_WARNING("Received a bad file descriptor index!");
   599     }
   601     if (fd.IsValid()) {
   602         PRFileDesc* fileDesc = PR_ImportFile(PROsfd(fd.PlatformHandle()));
   603         if (!fileDesc) {
   604             NS_WARNING("Failed to import file handle!");
   605             return false;
   606         }
   607         mFD = fileDesc;
   608     }
   610     mBehaviorFlags = params.behaviorFlags();
   611     mIOFlags = params.ioFlags();
   613     return true;
   614 }
   616 ////////////////////////////////////////////////////////////////////////////////
   617 // nsPartialFileInputStream
   619 NS_IMPL_ADDREF_INHERITED(nsPartialFileInputStream, nsFileStreamBase)
   620 NS_IMPL_RELEASE_INHERITED(nsPartialFileInputStream, nsFileStreamBase)
   622 NS_IMPL_CLASSINFO(nsPartialFileInputStream, nullptr, nsIClassInfo::THREADSAFE,
   623                   NS_PARTIALLOCALFILEINPUTSTREAM_CID)
   625 // Don't forward to nsFileInputStream as we don't want to QI to
   626 // nsIFileInputStream
   627 NS_INTERFACE_MAP_BEGIN(nsPartialFileInputStream)
   628     NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   629     NS_INTERFACE_MAP_ENTRY(nsIPartialFileInputStream)
   630     NS_INTERFACE_MAP_ENTRY(nsILineInputStream)
   631     NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   632     NS_IMPL_QUERY_CLASSINFO(nsPartialFileInputStream)
   633 NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase)
   635 NS_IMPL_CI_INTERFACE_GETTER(nsPartialFileInputStream,
   636                             nsIInputStream,
   637                             nsIPartialFileInputStream,
   638                             nsISeekableStream,
   639                             nsILineInputStream)
   641 nsresult
   642 nsPartialFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID,
   643                                  void **aResult)
   644 {
   645     NS_ENSURE_NO_AGGREGATION(aOuter);
   647     nsPartialFileInputStream* stream = new nsPartialFileInputStream();
   649     NS_ADDREF(stream);
   650     nsresult rv = stream->QueryInterface(aIID, aResult);
   651     NS_RELEASE(stream);
   652     return rv;
   653 }
   655 NS_IMETHODIMP
   656 nsPartialFileInputStream::Init(nsIFile* aFile, uint64_t aStart,
   657                                uint64_t aLength, int32_t aIOFlags,
   658                                int32_t aPerm, int32_t aBehaviorFlags)
   659 {
   660     mStart = aStart;
   661     mLength = aLength;
   662     mPosition = 0;
   664     nsresult rv = nsFileInputStream::Init(aFile, aIOFlags, aPerm,
   665                                           aBehaviorFlags);
   666     NS_ENSURE_SUCCESS(rv, rv);
   668     return nsFileInputStream::Seek(NS_SEEK_SET, mStart);
   669 }
   671 NS_IMETHODIMP
   672 nsPartialFileInputStream::Tell(int64_t *aResult)
   673 {
   674     int64_t tell = 0;
   675     nsresult rv = nsFileInputStream::Tell(&tell);
   676     if (NS_SUCCEEDED(rv)) {
   677         *aResult = tell - mStart;
   678     }
   679     return rv;
   680 }
   682 NS_IMETHODIMP
   683 nsPartialFileInputStream::Available(uint64_t* aResult)
   684 {
   685     uint64_t available = 0;
   686     nsresult rv = nsFileInputStream::Available(&available);
   687     if (NS_SUCCEEDED(rv)) {
   688         *aResult = TruncateSize(available);
   689     }
   690     return rv;
   691 }
   693 NS_IMETHODIMP
   694 nsPartialFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
   695 {
   696     uint32_t readsize = (uint32_t) TruncateSize(aCount);
   697     if (readsize == 0 && mBehaviorFlags & CLOSE_ON_EOF) {
   698         Close();
   699         *aResult = 0;
   700         return NS_OK;
   701     }
   703     nsresult rv = nsFileInputStream::Read(aBuf, readsize, aResult);
   704     if (NS_SUCCEEDED(rv)) {
   705         mPosition += readsize;
   706     }
   707     return rv;
   708 }
   710 NS_IMETHODIMP
   711 nsPartialFileInputStream::Seek(int32_t aWhence, int64_t aOffset)
   712 {
   713     int64_t offset;
   714     switch (aWhence) {
   715         case NS_SEEK_SET:
   716             offset = mStart + aOffset;
   717             break;
   718         case NS_SEEK_CUR:
   719             offset = mStart + mPosition + aOffset;
   720             break;
   721         case NS_SEEK_END:
   722             offset = mStart + mLength + aOffset;
   723             break;
   724         default:
   725             return NS_ERROR_ILLEGAL_VALUE;
   726     }
   728     if (offset < (int64_t)mStart || offset > (int64_t)(mStart + mLength)) {
   729         return NS_ERROR_INVALID_ARG;
   730     }
   732     nsresult rv = nsFileInputStream::Seek(NS_SEEK_SET, offset);
   733     if (NS_SUCCEEDED(rv)) {
   734         mPosition = offset - mStart;
   735     }
   736     return rv;
   737 }
   739 void
   740 nsPartialFileInputStream::Serialize(InputStreamParams& aParams,
   741                                     FileDescriptorArray& aFileDescriptors)
   742 {
   743     // Serialize the base class first.
   744     InputStreamParams fileParams;
   745     nsFileInputStream::Serialize(fileParams, aFileDescriptors);
   747     PartialFileInputStreamParams params;
   749     params.fileStreamParams() = fileParams.get_FileInputStreamParams();
   750     params.begin() = mStart;
   751     params.length() = mLength;
   753     aParams = params;
   754 }
   756 bool
   757 nsPartialFileInputStream::Deserialize(
   758                                     const InputStreamParams& aParams,
   759                                     const FileDescriptorArray& aFileDescriptors)
   760 {
   761     NS_ASSERTION(!mFD, "Already have a file descriptor?!");
   762     NS_ASSERTION(!mStart, "Already have a start?!");
   763     NS_ASSERTION(!mLength, "Already have a length?!");
   764     NS_ASSERTION(!mPosition, "Already have a position?!");
   766     if (aParams.type() != InputStreamParams::TPartialFileInputStreamParams) {
   767         NS_WARNING("Received unknown parameters from the other process!");
   768         return false;
   769     }
   771     const PartialFileInputStreamParams& params =
   772         aParams.get_PartialFileInputStreamParams();
   774     // Deserialize the base class first.
   775     InputStreamParams fileParams(params.fileStreamParams());
   776     if (!nsFileInputStream::Deserialize(fileParams, aFileDescriptors)) {
   777         NS_WARNING("Base class deserialize failed!");
   778         return false;
   779     }
   781     NS_ASSERTION(mFD, "Must have a file descriptor now!");
   783     mStart = params.begin();
   784     mLength = params.length();
   785     mPosition = 0;
   787     if (!mStart) {
   788       return true;
   789     }
   791     // XXX This is so broken. Main thread IO alert.
   792     return NS_SUCCEEDED(nsFileInputStream::Seek(NS_SEEK_SET, mStart));
   793 }
   795 ////////////////////////////////////////////////////////////////////////////////
   796 // nsFileOutputStream
   798 NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream,
   799                             nsFileStreamBase,
   800                             nsIOutputStream,
   801                             nsIFileOutputStream)
   803 nsresult
   804 nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
   805 {
   806     NS_ENSURE_NO_AGGREGATION(aOuter);
   808     nsFileOutputStream* stream = new nsFileOutputStream();
   809     if (stream == nullptr)
   810         return NS_ERROR_OUT_OF_MEMORY;
   811     NS_ADDREF(stream);
   812     nsresult rv = stream->QueryInterface(aIID, aResult);
   813     NS_RELEASE(stream);
   814     return rv;
   815 }
   817 NS_IMETHODIMP
   818 nsFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
   819                          int32_t behaviorFlags)
   820 {
   821     NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
   822     NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
   824     mBehaviorFlags = behaviorFlags;
   826     if (ioFlags == -1)
   827         ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
   828     if (perm <= 0)
   829         perm = 0664;
   831     return MaybeOpen(file, ioFlags, perm,
   832                      mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN);
   833 }
   835 ////////////////////////////////////////////////////////////////////////////////
   836 // nsAtomicFileOutputStream
   838 NS_IMPL_ISUPPORTS_INHERITED(nsAtomicFileOutputStream,
   839                             nsFileOutputStream,
   840                             nsISafeOutputStream,
   841                             nsIOutputStream,
   842                             nsIFileOutputStream)
   844 NS_IMETHODIMP
   845 nsAtomicFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
   846                              int32_t behaviorFlags)
   847 {
   848     return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
   849 }
   851 nsresult
   852 nsAtomicFileOutputStream::DoOpen()
   853 {
   854     // Make sure mOpenParams.localFile will be empty if we bail somewhere in
   855     // this function
   856     nsCOMPtr<nsIFile> file;
   857     file.swap(mOpenParams.localFile);
   859     nsresult rv = file->Exists(&mTargetFileExists);
   860     if (NS_FAILED(rv)) {
   861         NS_ERROR("Can't tell if target file exists");
   862         mTargetFileExists = true; // Safer to assume it exists - we just do more work.
   863     }
   865     // follow symlinks, for two reasons:
   866     // 1) if a user has deliberately set up a profile file as a symlink, we honor it
   867     // 2) to make the MoveToNative() in Finish() an atomic operation (which may not
   868     //    be the case if moving across directories on different filesystems).
   869     nsCOMPtr<nsIFile> tempResult;
   870     rv = file->Clone(getter_AddRefs(tempResult));
   871     if (NS_SUCCEEDED(rv)) {
   872         tempResult->SetFollowLinks(true);
   874         // XP_UNIX ignores SetFollowLinks(), so we have to normalize.
   875         tempResult->Normalize();
   876     }
   878     if (NS_SUCCEEDED(rv) && mTargetFileExists) {
   879         uint32_t origPerm;
   880         if (NS_FAILED(file->GetPermissions(&origPerm))) {
   881             NS_ERROR("Can't get permissions of target file");
   882             origPerm = mOpenParams.perm;
   883         }
   884         // XXX What if |perm| is more restrictive then |origPerm|?
   885         // This leaves the user supplied permissions as they were.
   886         rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
   887     }
   888     if (NS_SUCCEEDED(rv)) {
   889         // nsFileOutputStream::DoOpen will work on the temporary file, so we
   890         // prepare it and place it in mOpenParams.localFile.
   891         mOpenParams.localFile = tempResult;
   892         mTempFile = tempResult;
   893         mTargetFile = file;
   894         rv = nsFileOutputStream::DoOpen();
   895     }
   896     return rv;
   897 }
   899 NS_IMETHODIMP
   900 nsAtomicFileOutputStream::Close()
   901 {
   902     nsresult rv = nsFileOutputStream::Close();
   904     // the consumer doesn't want the original file overwritten -
   905     // so clean up by removing the temp file.
   906     if (mTempFile) {
   907         mTempFile->Remove(false);
   908         mTempFile = nullptr;
   909     }
   911     return rv;
   912 }
   914 NS_IMETHODIMP
   915 nsAtomicFileOutputStream::Finish()
   916 {
   917     nsresult rv = nsFileOutputStream::Close();
   919     // if there is no temp file, don't try to move it over the original target.
   920     // It would destroy the targetfile if close() is called twice.
   921     if (!mTempFile)
   922         return rv;
   924     // Only overwrite if everything was ok, and the temp file could be closed.
   925     if (NS_SUCCEEDED(mWriteResult) && NS_SUCCEEDED(rv)) {
   926         NS_ENSURE_STATE(mTargetFile);
   928         if (!mTargetFileExists) {
   929             // If the target file did not exist when we were initialized, then the
   930             // temp file we gave out was actually a reference to the target file.
   931             // since we succeeded in writing to the temp file (and hence succeeded
   932             // in writing to the target file), there is nothing more to do.
   933 #ifdef DEBUG
   934             bool equal;
   935             if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal)
   936                 NS_ERROR("mTempFile not equal to mTargetFile");
   937 #endif
   938         }
   939         else {
   940             nsAutoString targetFilename;
   941             rv = mTargetFile->GetLeafName(targetFilename);
   942             if (NS_SUCCEEDED(rv)) {
   943                 // This will replace target.
   944                 rv = mTempFile->MoveTo(nullptr, targetFilename);
   945                 if (NS_FAILED(rv))
   946                     mTempFile->Remove(false);
   947             }
   948         }
   949     }
   950     else {
   951         mTempFile->Remove(false);
   953         // if writing failed, propagate the failure code to the caller.
   954         if (NS_FAILED(mWriteResult))
   955             rv = mWriteResult;
   956     }
   957     mTempFile = nullptr;
   958     return rv;
   959 }
   961 NS_IMETHODIMP
   962 nsAtomicFileOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
   963 {
   964     nsresult rv = nsFileOutputStream::Write(buf, count, result);
   965     if (NS_SUCCEEDED(mWriteResult)) {
   966         if (NS_FAILED(rv))
   967             mWriteResult = rv;
   968         else if (count != *result)
   969             mWriteResult = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
   971         if (NS_FAILED(mWriteResult) && count > 0)
   972             NS_WARNING("writing to output stream failed! data may be lost");
   973     }
   974     return rv;
   975 }
   977 ////////////////////////////////////////////////////////////////////////////////
   978 // nsSafeFileOutputStream
   980 NS_IMETHODIMP
   981 nsSafeFileOutputStream::Finish()
   982 {
   983     (void) Flush();
   984     return nsAtomicFileOutputStream::Finish();
   985 }
   987 ////////////////////////////////////////////////////////////////////////////////
   988 // nsFileStream
   990 NS_IMPL_ISUPPORTS_INHERITED(nsFileStream,
   991                             nsFileStreamBase,
   992                             nsIInputStream,
   993                             nsIOutputStream,
   994                             nsIFileStream)
   996 NS_IMETHODIMP
   997 nsFileStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
   998                    int32_t behaviorFlags)
   999 {
  1000     NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
  1001     NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
  1003     mBehaviorFlags = behaviorFlags;
  1005     if (ioFlags == -1)
  1006         ioFlags = PR_RDWR;
  1007     if (perm <= 0)
  1008         perm = 0;
  1010     return MaybeOpen(file, ioFlags, perm,
  1011                      mBehaviorFlags & nsIFileStream::DEFER_OPEN);
  1014 ////////////////////////////////////////////////////////////////////////////////

mercurial