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/. */
6 #include "ipc/IPCMessageUtils.h"
12 # include "nsILocalFileWin.h"
14 // XXX add necessary include file for ftruncate (or equivalent)
17 #include "private/pprio.h"
19 #include "nsFileStreams.h"
21 #include "nsReadLine.h"
22 #include "nsIClassInfoImpl.h"
23 #include "mozilla/ipc/InputStreamUtils.h"
24 #include "mozilla/ipc/RandomAccessStreamParams.h"
25 #include "mozilla/Unused.h"
26 #include "mozilla/FileUtils.h"
27 #include "mozilla/UniquePtr.h"
29 #include "nsNetUtil.h"
30 #include "nsXULAppAPI.h"
32 using FileHandleType
= mozilla::ipc::FileDescriptor::PlatformHandleType
;
34 using namespace mozilla::ipc
;
36 using mozilla::DebugOnly
;
38 using mozilla::Nothing
;
41 ////////////////////////////////////////////////////////////////////////////////
44 nsFileStreamBase::~nsFileStreamBase() {
45 // We don't want to try to rewrind the stream when shutting down.
46 mBehaviorFlags
&= ~nsIFileInputStream::REOPEN_ON_REWIND
;
51 NS_IMPL_ISUPPORTS(nsFileStreamBase
, nsISeekableStream
, nsITellableStream
,
55 nsFileStreamBase::Seek(int32_t whence
, int64_t offset
) {
56 nsresult rv
= DoPendingOpen();
57 NS_ENSURE_SUCCESS(rv
, rv
);
59 int64_t cnt
= PR_Seek64(mFD
, offset
, (PRSeekWhence
)whence
);
60 if (cnt
== int64_t(-1)) {
61 return NS_ErrorAccordingToNSPR();
67 nsFileStreamBase::Tell(int64_t* result
) {
68 if (mState
== eDeferredOpen
&& !(mOpenParams
.ioFlags
& PR_APPEND
)) {
73 nsresult rv
= DoPendingOpen();
74 NS_ENSURE_SUCCESS(rv
, rv
);
76 int64_t cnt
= PR_Seek64(mFD
, 0, PR_SEEK_CUR
);
77 if (cnt
== int64_t(-1)) {
78 return NS_ErrorAccordingToNSPR();
85 nsFileStreamBase::SetEOF() {
86 nsresult rv
= DoPendingOpen();
87 NS_ENSURE_SUCCESS(rv
, rv
);
90 // Some system calls require an EOF offset.
93 if (NS_FAILED(rv
)) return rv
;
97 if (ftruncate(PR_FileDesc2NativeHandle(mFD
), offset
) != 0) {
98 NS_ERROR("ftruncate failed");
99 return NS_ERROR_FAILURE
;
101 #elif defined(XP_WIN)
102 if (!SetEndOfFile((HANDLE
)PR_FileDesc2NativeHandle(mFD
))) {
103 NS_ERROR("SetEndOfFile failed");
104 return NS_ERROR_FAILURE
;
107 // XXX not implemented
114 nsFileStreamBase::GetSize(int64_t* _retval
) {
115 nsresult rv
= DoPendingOpen();
116 NS_ENSURE_SUCCESS(rv
, rv
);
119 if (PR_GetOpenFileInfo64(mFD
, &info
) == PR_FAILURE
) {
120 return NS_BASE_STREAM_OSERROR
;
123 *_retval
= int64_t(info
.size
);
129 nsFileStreamBase::GetLastModified(int64_t* _retval
) {
130 nsresult rv
= DoPendingOpen();
131 NS_ENSURE_SUCCESS(rv
, rv
);
134 if (PR_GetOpenFileInfo64(mFD
, &info
) == PR_FAILURE
) {
135 return NS_BASE_STREAM_OSERROR
;
138 int64_t modTime
= int64_t(info
.modifyTime
);
142 *_retval
= modTime
/ int64_t(PR_USEC_PER_MSEC
);
149 nsFileStreamBase::GetFileDescriptor(PRFileDesc
** _retval
) {
150 nsresult rv
= DoPendingOpen();
151 NS_ENSURE_SUCCESS(rv
, rv
);
157 nsresult
nsFileStreamBase::Close() {
158 if (mState
== eClosed
) {
166 if (PR_Close(mFD
) == PR_FAILURE
) rv
= NS_BASE_STREAM_OSERROR
;
173 nsresult
nsFileStreamBase::Available(uint64_t* aResult
) {
174 nsresult rv
= DoPendingOpen();
175 NS_ENSURE_SUCCESS(rv
, rv
);
177 // PR_Available with files over 4GB returns an error, so we have to
178 // use the 64-bit version of PR_Available.
179 int64_t avail
= PR_Available64(mFD
);
181 return NS_ErrorAccordingToNSPR();
184 // If available is greater than 4GB, return 4GB
185 *aResult
= (uint64_t)avail
;
189 nsresult
nsFileStreamBase::Read(char* aBuf
, uint32_t aCount
,
191 nsresult rv
= DoPendingOpen();
192 if (rv
== NS_BASE_STREAM_CLOSED
) {
201 int32_t bytesRead
= PR_Read(mFD
, aBuf
, aCount
);
202 if (bytesRead
== -1) {
203 return NS_ErrorAccordingToNSPR();
206 *aResult
= bytesRead
;
210 nsresult
nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter
,
211 void* aClosure
, uint32_t aCount
,
213 // ReadSegments is not implemented because it would be inefficient when
214 // the writer does not consume all data. If you want to call ReadSegments,
215 // wrap a BufferedInputStream around the file stream. That will call
218 return NS_ERROR_NOT_IMPLEMENTED
;
221 nsresult
nsFileStreamBase::IsNonBlocking(bool* aNonBlocking
) {
222 *aNonBlocking
= false;
226 nsresult
nsFileStreamBase::Flush(void) {
227 nsresult rv
= DoPendingOpen();
228 NS_ENSURE_SUCCESS(rv
, rv
);
230 int32_t cnt
= PR_Sync(mFD
);
232 return NS_ErrorAccordingToNSPR();
237 nsresult
nsFileStreamBase::StreamStatus() {
240 MOZ_CRASH("This should not happen.");
241 return NS_ERROR_FAILURE
;
248 if (NS_WARN_IF(!mFD
)) {
249 return NS_ERROR_FAILURE
;
255 return NS_BASE_STREAM_CLOSED
;
261 MOZ_CRASH("Invalid mState value.");
262 return NS_ERROR_FAILURE
;
265 nsresult
nsFileStreamBase::Write(const char* buf
, uint32_t count
,
267 nsresult rv
= DoPendingOpen();
268 NS_ENSURE_SUCCESS(rv
, rv
);
270 int32_t cnt
= PR_Write(mFD
, buf
, count
);
272 return NS_ErrorAccordingToNSPR();
278 nsresult
nsFileStreamBase::WriteFrom(nsIInputStream
* inStr
, uint32_t count
,
280 MOZ_ASSERT_UNREACHABLE("WriteFrom (see source comment)");
281 return NS_ERROR_NOT_IMPLEMENTED
;
282 // File streams intentionally do not support this method.
283 // If you need something like this, then you should wrap
284 // the file stream using nsIBufferedOutputStream
287 nsresult
nsFileStreamBase::WriteSegments(nsReadSegmentFun reader
, void* closure
,
288 uint32_t count
, uint32_t* _retval
) {
289 return NS_ERROR_NOT_IMPLEMENTED
;
290 // File streams intentionally do not support this method.
291 // If you need something like this, then you should wrap
292 // the file stream using nsIBufferedOutputStream
295 nsresult
nsFileStreamBase::MaybeOpen(nsIFile
* aFile
, int32_t aIoFlags
,
296 int32_t aPerm
, bool aDeferred
) {
297 NS_ENSURE_STATE(aFile
);
299 mOpenParams
.ioFlags
= aIoFlags
;
300 mOpenParams
.perm
= aPerm
;
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
= std::move(file
);
309 NS_ENSURE_TRUE(mOpenParams
.localFile
, NS_ERROR_UNEXPECTED
);
311 mState
= eDeferredOpen
;
315 mOpenParams
.localFile
= aFile
;
317 // Following call open() at main thread.
318 // Main thread might be blocked, while open a remote file.
322 void nsFileStreamBase::CleanUpOpen() { mOpenParams
.localFile
= nullptr; }
324 nsresult
nsFileStreamBase::DoOpen() {
325 MOZ_ASSERT(mState
== eDeferredOpen
|| mState
== eUnitialized
||
327 NS_ASSERTION(!mFD
, "Already have a file descriptor!");
328 NS_ASSERTION(mOpenParams
.localFile
, "Must have a file to open");
333 if (mOpenParams
.ioFlags
& PR_CREATE_FILE
) {
334 nsCOMPtr
<nsIFile
> parent
;
335 mOpenParams
.localFile
->GetParent(getter_AddRefs(parent
));
337 // Result doesn't need to be checked. If the file's parent path does not
338 // exist, make it. If it does exist, do nothing.
340 mozilla::Unused
<< parent
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
345 if (mBehaviorFlags
& nsIFileInputStream::SHARE_DELETE
) {
346 nsCOMPtr
<nsILocalFileWin
> file
= do_QueryInterface(mOpenParams
.localFile
);
349 rv
= file
->OpenNSPRFileDescShareDelete(mOpenParams
.ioFlags
,
350 mOpenParams
.perm
, &fd
);
354 rv
= mOpenParams
.localFile
->OpenNSPRFileDesc(mOpenParams
.ioFlags
,
355 mOpenParams
.perm
, &fd
);
372 nsresult
nsFileStreamBase::DoPendingOpen() {
375 MOZ_CRASH("This should not happen.");
376 return NS_ERROR_FAILURE
;
383 if (NS_WARN_IF(!mFD
)) {
384 return NS_ERROR_FAILURE
;
390 return NS_BASE_STREAM_CLOSED
;
396 MOZ_CRASH("Invalid mState value.");
397 return NS_ERROR_FAILURE
;
400 ////////////////////////////////////////////////////////////////////////////////
403 NS_IMPL_ADDREF_INHERITED(nsFileInputStream
, nsFileStreamBase
)
404 NS_IMPL_RELEASE_INHERITED(nsFileInputStream
, nsFileStreamBase
)
406 NS_IMPL_CLASSINFO(nsFileInputStream
, nullptr, nsIClassInfo::THREADSAFE
,
407 NS_LOCALFILEINPUTSTREAM_CID
)
409 NS_INTERFACE_MAP_BEGIN(nsFileInputStream
)
410 NS_INTERFACE_MAP_ENTRY(nsIInputStream
)
411 NS_INTERFACE_MAP_ENTRY(nsIFileInputStream
)
412 NS_INTERFACE_MAP_ENTRY(nsILineInputStream
)
413 NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream
)
414 NS_IMPL_QUERY_CLASSINFO(nsFileInputStream
)
415 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream
, IsCloneable())
416 NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase
)
418 NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream
, nsIInputStream
,
419 nsIFileInputStream
, nsISeekableStream
,
420 nsITellableStream
, nsILineInputStream
)
422 nsresult
nsFileInputStream::Create(REFNSIID aIID
, void** aResult
) {
423 RefPtr
<nsFileInputStream
> stream
= new nsFileInputStream();
424 return stream
->QueryInterface(aIID
, aResult
);
427 nsresult
nsFileInputStream::Open(nsIFile
* aFile
, int32_t aIOFlags
,
431 // If the previous file is open, close it
434 if (NS_FAILED(rv
)) return rv
;
438 if (aIOFlags
== -1) aIOFlags
= PR_RDONLY
;
439 if (aPerm
== -1) aPerm
= 0;
441 return MaybeOpen(aFile
, aIOFlags
, aPerm
,
442 mBehaviorFlags
& nsIFileInputStream::DEFER_OPEN
);
446 nsFileInputStream::Init(nsIFile
* aFile
, int32_t aIOFlags
, int32_t aPerm
,
447 int32_t aBehaviorFlags
) {
448 NS_ENSURE_TRUE(!mFD
, NS_ERROR_ALREADY_INITIALIZED
);
449 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
450 NS_ERROR_ALREADY_INITIALIZED
);
452 mBehaviorFlags
= aBehaviorFlags
;
453 mState
= eUnitialized
;
459 return Open(aFile
, aIOFlags
, aPerm
);
463 nsFileInputStream::Close() {
464 // If this stream has already been closed, do nothing.
465 if (mState
== eClosed
) {
469 // Get the cache position at the time the file was close. This allows
470 // NS_SEEK_CUR on a closed file that has been opened with
472 if (mBehaviorFlags
& REOPEN_ON_REWIND
) {
473 // Get actual position. Not one modified by subclasses
474 nsFileStreamBase::Tell(&mCachedPosition
);
477 // explicitly clear mLineBuffer in case this stream is reopened
478 mLineBuffer
= nullptr;
479 return nsFileStreamBase::Close();
483 nsFileInputStream::Read(char* aBuf
, uint32_t aCount
, uint32_t* _retval
) {
484 nsresult rv
= nsFileStreamBase::Read(aBuf
, aCount
, _retval
);
485 if (rv
== NS_ERROR_FILE_NOT_FOUND
) {
486 // Don't warn if this is a deffered file not found.
494 // Check if we're at the end of file and need to close
495 if (mBehaviorFlags
& CLOSE_ON_EOF
&& *_retval
== 0) {
503 nsFileInputStream::ReadLine(nsACString
& aLine
, bool* aResult
) {
505 mLineBuffer
= mozilla::MakeUnique
<nsLineBuffer
<char>>();
507 return NS_ReadLine(this, mLineBuffer
.get(), aLine
, aResult
);
511 nsFileInputStream::Seek(int32_t aWhence
, int64_t aOffset
) {
512 return SeekInternal(aWhence
, aOffset
);
515 nsresult
nsFileInputStream::SeekInternal(int32_t aWhence
, int64_t aOffset
,
517 nsresult rv
= DoPendingOpen();
518 if (rv
!= NS_OK
&& rv
!= NS_BASE_STREAM_CLOSED
) {
523 mLineBuffer
= nullptr;
526 if (rv
== NS_BASE_STREAM_CLOSED
) {
527 if (mBehaviorFlags
& REOPEN_ON_REWIND
) {
528 rv
= Open(mFile
, mIOFlags
, mPerm
);
529 NS_ENSURE_SUCCESS(rv
, rv
);
531 // If the file was closed, and we do a relative seek, use the
532 // position we cached when we closed the file to seek to the right
534 if (aWhence
== NS_SEEK_CUR
) {
535 aWhence
= NS_SEEK_SET
;
536 aOffset
+= mCachedPosition
;
538 // If we're trying to seek to the start then we're done, so
539 // return early to avoid Seek from calling DoPendingOpen and
540 // opening the underlying file earlier than necessary.
541 if (aWhence
== NS_SEEK_SET
&& aOffset
== 0) {
545 return NS_BASE_STREAM_CLOSED
;
549 return nsFileStreamBase::Seek(aWhence
, aOffset
);
553 nsFileInputStream::Tell(int64_t* aResult
) {
554 return nsFileStreamBase::Tell(aResult
);
558 nsFileInputStream::Available(uint64_t* aResult
) {
559 return nsFileStreamBase::Available(aResult
);
563 nsFileInputStream::StreamStatus() { return nsFileStreamBase::StreamStatus(); }
565 void nsFileInputStream::SerializedComplexity(uint32_t aMaxSize
,
568 uint32_t* aTransferables
) {
572 void nsFileInputStream::Serialize(InputStreamParams
& aParams
, uint32_t aMaxSize
,
573 uint32_t* aSizeUsed
) {
574 MOZ_ASSERT(aSizeUsed
);
577 FileInputStreamParams params
;
579 if (NS_SUCCEEDED(DoPendingOpen())) {
581 FileHandleType fd
= FileHandleType(PR_FileDesc2NativeHandle(mFD
));
582 NS_ASSERTION(fd
, "This should never be null!");
584 params
.fileDescriptor() = FileDescriptor(fd
);
589 "This file has not been opened (or could not be opened). "
590 "Sending an invalid file descriptor to the other process!");
592 params
.fileDescriptor() = FileDescriptor();
595 int32_t behaviorFlags
= mBehaviorFlags
;
597 // The receiving process (or thread) is going to have an open file
598 // descriptor automatically so transferring this flag is meaningless.
599 behaviorFlags
&= ~nsIFileInputStream::DEFER_OPEN
;
601 params
.behaviorFlags() = behaviorFlags
;
602 params
.ioFlags() = mIOFlags
;
607 bool nsFileInputStream::Deserialize(const InputStreamParams
& aParams
) {
608 NS_ASSERTION(!mFD
, "Already have a file descriptor?!");
609 NS_ASSERTION(mState
== nsFileStreamBase::eUnitialized
, "Deferring open?!");
610 NS_ASSERTION(!mFile
, "Should never have a file here!");
611 NS_ASSERTION(!mPerm
, "This should always be 0!");
613 if (aParams
.type() != InputStreamParams::TFileInputStreamParams
) {
614 NS_WARNING("Received unknown parameters from the other process!");
618 const FileInputStreamParams
& params
= aParams
.get_FileInputStreamParams();
620 const FileDescriptor
& fd
= params
.fileDescriptor();
623 auto rawFD
= fd
.ClonePlatformHandle();
624 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
626 NS_WARNING("Failed to import file handle!");
632 NS_WARNING("Received an invalid file descriptor!");
634 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
637 mBehaviorFlags
= params
.behaviorFlags();
639 if (!XRE_IsParentProcess()) {
640 // A child process shouldn't close when it reads the end because it will
641 // not be able to reopen the file later.
642 mBehaviorFlags
&= ~nsIFileInputStream::CLOSE_ON_EOF
;
644 // A child process will not be able to reopen the file so this flag is
646 mBehaviorFlags
&= ~nsIFileInputStream::REOPEN_ON_REWIND
;
649 mIOFlags
= params
.ioFlags();
654 bool nsFileInputStream::IsCloneable() const {
655 // This inputStream is cloneable only if has been created using Init() and
656 // it owns a nsIFile. This is not true when it is deserialized from IPC.
657 return XRE_IsParentProcess() && mFile
;
661 nsFileInputStream::GetCloneable(bool* aCloneable
) {
662 *aCloneable
= IsCloneable();
667 nsFileInputStream::Clone(nsIInputStream
** aResult
) {
668 MOZ_ASSERT(IsCloneable());
669 return NS_NewLocalFileInputStream(aResult
, mFile
, mIOFlags
, mPerm
,
673 ////////////////////////////////////////////////////////////////////////////////
674 // nsFileOutputStream
676 NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream
, nsFileStreamBase
,
677 nsIOutputStream
, nsIFileOutputStream
)
679 nsresult
nsFileOutputStream::Create(REFNSIID aIID
, void** aResult
) {
680 RefPtr
<nsFileOutputStream
> stream
= new nsFileOutputStream();
681 return stream
->QueryInterface(aIID
, aResult
);
685 nsFileOutputStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
686 int32_t behaviorFlags
) {
687 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
688 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
689 NS_ERROR_ALREADY_INITIALIZED
);
691 mBehaviorFlags
= behaviorFlags
;
692 mState
= eUnitialized
;
694 if (ioFlags
== -1) ioFlags
= PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
;
695 if (perm
<= 0) perm
= 0664;
697 return MaybeOpen(file
, ioFlags
, perm
,
698 mBehaviorFlags
& nsIFileOutputStream::DEFER_OPEN
);
701 nsresult
nsFileOutputStream::InitWithFileDescriptor(
702 const mozilla::ipc::FileDescriptor
& aFd
) {
703 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
704 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
705 NS_ERROR_ALREADY_INITIALIZED
);
708 auto rawFD
= aFd
.ClonePlatformHandle();
709 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
711 NS_WARNING("Failed to import file handle!");
712 return NS_ERROR_FAILURE
;
718 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
725 nsFileOutputStream::Preallocate(int64_t aLength
) {
727 return NS_ERROR_NOT_INITIALIZED
;
730 if (!mozilla::fallocate(mFD
, aLength
)) {
731 return NS_ERROR_FAILURE
;
737 ////////////////////////////////////////////////////////////////////////////////
738 // nsAtomicFileOutputStream
740 NS_IMPL_ISUPPORTS_INHERITED(nsAtomicFileOutputStream
, nsFileOutputStream
,
741 nsISafeOutputStream
, nsIOutputStream
,
745 nsAtomicFileOutputStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
746 int32_t behaviorFlags
) {
747 // While `PR_APPEND` is not supported, `-1` is used as `ioFlags` parameter
748 // in some places, and `PR_APPEND | PR_TRUNCATE` does not require appending
749 // to existing file. So, throw an exception only if `PR_APPEND` is
750 // explicitly specified without `PR_TRUNCATE`.
751 if ((ioFlags
& PR_APPEND
) && !(ioFlags
& PR_TRUNCATE
)) {
752 return NS_ERROR_INVALID_ARG
;
754 return nsFileOutputStream::Init(file
, ioFlags
, perm
, behaviorFlags
);
757 nsresult
nsAtomicFileOutputStream::DoOpen() {
758 // Make sure mOpenParams.localFile will be empty if we bail somewhere in
760 nsCOMPtr
<nsIFile
> file
;
761 file
.swap(mOpenParams
.localFile
);
764 return NS_ERROR_NOT_INITIALIZED
;
766 nsresult rv
= file
->Exists(&mTargetFileExists
);
768 NS_ERROR("Can't tell if target file exists");
770 true; // Safer to assume it exists - we just do more work.
773 // follow symlinks, for two reasons:
774 // 1) if a user has deliberately set up a profile file as a symlink, we
776 // 2) to make the MoveToNative() in Finish() an atomic operation (which may
777 // not be the case if moving across directories on different
779 nsCOMPtr
<nsIFile
> tempResult
;
780 rv
= file
->Clone(getter_AddRefs(tempResult
));
781 if (NS_SUCCEEDED(rv
) && mTargetFileExists
) {
782 tempResult
->Normalize();
785 if (NS_SUCCEEDED(rv
) && mTargetFileExists
) {
786 // Abort if |file| is not writable; it won't work as an output stream.
788 if (NS_SUCCEEDED(file
->IsWritable(&isWritable
)) && !isWritable
) {
789 return NS_ERROR_FILE_ACCESS_DENIED
;
793 if (NS_FAILED(file
->GetPermissions(&origPerm
))) {
794 NS_ERROR("Can't get permissions of target file");
795 origPerm
= mOpenParams
.perm
;
798 // XXX What if |perm| is more restrictive then |origPerm|?
799 // This leaves the user supplied permissions as they were.
800 rv
= tempResult
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, origPerm
);
802 if (NS_SUCCEEDED(rv
)) {
803 // nsFileOutputStream::DoOpen will work on the temporary file, so we
804 // prepare it and place it in mOpenParams.localFile.
805 mOpenParams
.localFile
= tempResult
;
806 mTempFile
= tempResult
;
808 rv
= nsFileOutputStream::DoOpen();
814 nsAtomicFileOutputStream::Close() {
815 nsresult rv
= nsFileOutputStream::Close();
817 // the consumer doesn't want the original file overwritten -
818 // so clean up by removing the temp file.
820 mTempFile
->Remove(false);
828 nsAtomicFileOutputStream::Finish() {
829 nsresult rv
= nsFileOutputStream::Close();
831 // if there is no temp file, don't try to move it over the original target.
832 // It would destroy the targetfile if close() is called twice.
833 if (!mTempFile
) return rv
;
835 // Only overwrite if everything was ok, and the temp file could be closed.
836 if (NS_SUCCEEDED(mWriteResult
) && NS_SUCCEEDED(rv
)) {
837 NS_ENSURE_STATE(mTargetFile
);
839 if (!mTargetFileExists
) {
840 // If the target file did not exist when we were initialized, then the
841 // temp file we gave out was actually a reference to the target file.
842 // since we succeeded in writing to the temp file (and hence succeeded
843 // in writing to the target file), there is nothing more to do.
846 if (NS_FAILED(mTargetFile
->Equals(mTempFile
, &equal
)) || !equal
) {
847 NS_WARNING("mTempFile not equal to mTargetFile");
851 nsAutoString targetFilename
;
852 rv
= mTargetFile
->GetLeafName(targetFilename
);
853 if (NS_SUCCEEDED(rv
)) {
854 // This will replace target.
855 rv
= mTempFile
->MoveTo(nullptr, targetFilename
);
856 if (NS_FAILED(rv
)) mTempFile
->Remove(false);
860 mTempFile
->Remove(false);
862 // if writing failed, propagate the failure code to the caller.
863 if (NS_FAILED(mWriteResult
)) rv
= mWriteResult
;
870 nsAtomicFileOutputStream::Write(const char* buf
, uint32_t count
,
872 nsresult rv
= nsFileOutputStream::Write(buf
, count
, result
);
873 if (NS_SUCCEEDED(mWriteResult
)) {
876 } else if (count
!= *result
) {
877 mWriteResult
= NS_ERROR_LOSS_OF_SIGNIFICANT_DATA
;
880 if (NS_FAILED(mWriteResult
) && count
> 0) {
881 NS_WARNING("writing to output stream failed! data may be lost");
887 ////////////////////////////////////////////////////////////////////////////////
888 // nsSafeFileOutputStream
891 nsSafeFileOutputStream::Finish() {
893 return nsAtomicFileOutputStream::Finish();
896 ////////////////////////////////////////////////////////////////////////////////
897 // nsFileRandomAccessStream
899 nsresult
nsFileRandomAccessStream::Create(REFNSIID aIID
, void** aResult
) {
900 RefPtr
<nsFileRandomAccessStream
> stream
= new nsFileRandomAccessStream();
901 return stream
->QueryInterface(aIID
, aResult
);
904 NS_IMPL_ISUPPORTS_INHERITED(nsFileRandomAccessStream
, nsFileStreamBase
,
905 nsIRandomAccessStream
, nsIFileRandomAccessStream
,
906 nsIInputStream
, nsIOutputStream
)
909 nsFileRandomAccessStream::GetInputStream(nsIInputStream
** aInputStream
) {
910 nsCOMPtr
<nsIInputStream
> inputStream(this);
912 inputStream
.forget(aInputStream
);
917 nsFileRandomAccessStream::GetOutputStream(nsIOutputStream
** aOutputStream
) {
918 nsCOMPtr
<nsIOutputStream
> outputStream(this);
920 outputStream
.forget(aOutputStream
);
924 nsIInputStream
* nsFileRandomAccessStream::InputStream() { return this; }
926 nsIOutputStream
* nsFileRandomAccessStream::OutputStream() { return this; }
928 RandomAccessStreamParams
nsFileRandomAccessStream::Serialize(
929 nsIInterfaceRequestor
* aCallbacks
) {
930 FileRandomAccessStreamParams params
;
932 if (NS_SUCCEEDED(DoPendingOpen())) {
934 FileHandleType fd
= FileHandleType(PR_FileDesc2NativeHandle(mFD
));
935 MOZ_ASSERT(fd
, "This should never be null!");
937 params
.fileDescriptor() = FileDescriptor(fd
);
942 "This file has not been opened (or could not be opened). "
943 "Sending an invalid file descriptor to the other process!");
945 params
.fileDescriptor() = FileDescriptor();
948 int32_t behaviorFlags
= mBehaviorFlags
;
950 // The receiving process (or thread) is going to have an open file
951 // descriptor automatically so transferring this flag is meaningless.
952 behaviorFlags
&= ~nsIFileInputStream::DEFER_OPEN
;
954 params
.behaviorFlags() = behaviorFlags
;
959 bool nsFileRandomAccessStream::Deserialize(
960 RandomAccessStreamParams
& aStreamParams
) {
961 MOZ_ASSERT(!mFD
, "Already have a file descriptor?!");
962 MOZ_ASSERT(mState
== nsFileStreamBase::eUnitialized
, "Deferring open?!");
964 if (aStreamParams
.type() !=
965 RandomAccessStreamParams::TFileRandomAccessStreamParams
) {
966 NS_WARNING("Received unknown parameters from the other process!");
970 const FileRandomAccessStreamParams
& params
=
971 aStreamParams
.get_FileRandomAccessStreamParams();
973 const FileDescriptor
& fd
= params
.fileDescriptor();
976 auto rawFD
= fd
.ClonePlatformHandle();
977 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
979 NS_WARNING("Failed to import file handle!");
985 NS_WARNING("Received an invalid file descriptor!");
987 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
990 mBehaviorFlags
= params
.behaviorFlags();
996 nsFileRandomAccessStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
997 int32_t behaviorFlags
) {
998 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
999 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
1000 NS_ERROR_ALREADY_INITIALIZED
);
1002 mBehaviorFlags
= behaviorFlags
;
1003 mState
= eUnitialized
;
1005 if (ioFlags
== -1) ioFlags
= PR_RDWR
;
1006 if (perm
<= 0) perm
= 0;
1008 return MaybeOpen(file
, ioFlags
, perm
,
1009 mBehaviorFlags
& nsIFileRandomAccessStream::DEFER_OPEN
);
1012 ////////////////////////////////////////////////////////////////////////////////