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"
20 #include "IOActivityMonitor.h"
21 #include "nsFileStreams.h"
23 #include "nsReadLine.h"
24 #include "nsIClassInfoImpl.h"
25 #include "nsLiteralString.h"
26 #include "nsSocketTransport2.h" // for ErrorAccordingToNSPR()
27 #include "mozilla/ipc/InputStreamUtils.h"
28 #include "mozilla/ipc/RandomAccessStreamParams.h"
29 #include "mozilla/Unused.h"
30 #include "mozilla/FileUtils.h"
32 #include "nsXULAppAPI.h"
34 using FileHandleType
= mozilla::ipc::FileDescriptor::PlatformHandleType
;
36 using namespace mozilla::ipc
;
37 using namespace mozilla::net
;
39 using mozilla::DebugOnly
;
41 using mozilla::Nothing
;
44 ////////////////////////////////////////////////////////////////////////////////
47 nsFileStreamBase::~nsFileStreamBase() {
48 // We don't want to try to rewrind the stream when shutting down.
49 mBehaviorFlags
&= ~nsIFileInputStream::REOPEN_ON_REWIND
;
54 NS_IMPL_ISUPPORTS(nsFileStreamBase
, nsISeekableStream
, nsITellableStream
,
58 nsFileStreamBase::Seek(int32_t whence
, int64_t offset
) {
59 nsresult rv
= DoPendingOpen();
60 NS_ENSURE_SUCCESS(rv
, rv
);
62 int64_t cnt
= PR_Seek64(mFD
, offset
, (PRSeekWhence
)whence
);
63 if (cnt
== int64_t(-1)) {
64 return NS_ErrorAccordingToNSPR();
70 nsFileStreamBase::Tell(int64_t* result
) {
71 if (mState
== eDeferredOpen
&& !(mOpenParams
.ioFlags
& PR_APPEND
)) {
76 nsresult rv
= DoPendingOpen();
77 NS_ENSURE_SUCCESS(rv
, rv
);
79 int64_t cnt
= PR_Seek64(mFD
, 0, PR_SEEK_CUR
);
80 if (cnt
== int64_t(-1)) {
81 return NS_ErrorAccordingToNSPR();
88 nsFileStreamBase::SetEOF() {
89 nsresult rv
= DoPendingOpen();
90 NS_ENSURE_SUCCESS(rv
, rv
);
93 // Some system calls require an EOF offset.
96 if (NS_FAILED(rv
)) return rv
;
100 if (ftruncate(PR_FileDesc2NativeHandle(mFD
), offset
) != 0) {
101 NS_ERROR("ftruncate failed");
102 return NS_ERROR_FAILURE
;
104 #elif defined(XP_WIN)
105 if (!SetEndOfFile((HANDLE
)PR_FileDesc2NativeHandle(mFD
))) {
106 NS_ERROR("SetEndOfFile failed");
107 return NS_ERROR_FAILURE
;
110 // XXX not implemented
117 nsFileStreamBase::GetSize(int64_t* _retval
) {
118 nsresult rv
= DoPendingOpen();
119 NS_ENSURE_SUCCESS(rv
, rv
);
122 if (PR_GetOpenFileInfo64(mFD
, &info
) == PR_FAILURE
) {
123 return NS_BASE_STREAM_OSERROR
;
126 *_retval
= int64_t(info
.size
);
132 nsFileStreamBase::GetLastModified(int64_t* _retval
) {
133 nsresult rv
= DoPendingOpen();
134 NS_ENSURE_SUCCESS(rv
, rv
);
137 if (PR_GetOpenFileInfo64(mFD
, &info
) == PR_FAILURE
) {
138 return NS_BASE_STREAM_OSERROR
;
141 int64_t modTime
= int64_t(info
.modifyTime
);
145 *_retval
= modTime
/ int64_t(PR_USEC_PER_MSEC
);
152 nsFileStreamBase::GetFileDescriptor(PRFileDesc
** _retval
) {
153 nsresult rv
= DoPendingOpen();
154 NS_ENSURE_SUCCESS(rv
, rv
);
160 nsresult
nsFileStreamBase::Close() {
165 if (PR_Close(mFD
) == PR_FAILURE
) rv
= NS_BASE_STREAM_OSERROR
;
172 nsresult
nsFileStreamBase::Available(uint64_t* aResult
) {
173 nsresult rv
= DoPendingOpen();
174 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
180 return NS_ErrorAccordingToNSPR();
183 // If available is greater than 4GB, return 4GB
184 *aResult
= (uint64_t)avail
;
188 nsresult
nsFileStreamBase::Read(char* aBuf
, uint32_t aCount
,
190 nsresult rv
= DoPendingOpen();
191 if (rv
== NS_BASE_STREAM_CLOSED
) {
200 int32_t bytesRead
= PR_Read(mFD
, aBuf
, aCount
);
201 if (bytesRead
== -1) {
202 return NS_ErrorAccordingToNSPR();
205 *aResult
= bytesRead
;
209 nsresult
nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter
,
210 void* aClosure
, uint32_t aCount
,
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
217 return NS_ERROR_NOT_IMPLEMENTED
;
220 nsresult
nsFileStreamBase::IsNonBlocking(bool* aNonBlocking
) {
221 *aNonBlocking
= false;
225 nsresult
nsFileStreamBase::Flush(void) {
226 nsresult rv
= DoPendingOpen();
227 NS_ENSURE_SUCCESS(rv
, rv
);
229 int32_t cnt
= PR_Sync(mFD
);
231 return NS_ErrorAccordingToNSPR();
236 nsresult
nsFileStreamBase::Write(const char* buf
, uint32_t count
,
238 nsresult rv
= DoPendingOpen();
239 NS_ENSURE_SUCCESS(rv
, rv
);
241 int32_t cnt
= PR_Write(mFD
, buf
, count
);
243 return NS_ErrorAccordingToNSPR();
249 nsresult
nsFileStreamBase::WriteFrom(nsIInputStream
* inStr
, uint32_t count
,
251 MOZ_ASSERT_UNREACHABLE("WriteFrom (see source comment)");
252 return NS_ERROR_NOT_IMPLEMENTED
;
253 // File streams intentionally do not support this method.
254 // If you need something like this, then you should wrap
255 // the file stream using nsIBufferedOutputStream
258 nsresult
nsFileStreamBase::WriteSegments(nsReadSegmentFun reader
, void* closure
,
259 uint32_t count
, uint32_t* _retval
) {
260 return NS_ERROR_NOT_IMPLEMENTED
;
261 // File streams intentionally do not support this method.
262 // If you need something like this, then you should wrap
263 // the file stream using nsIBufferedOutputStream
266 nsresult
nsFileStreamBase::MaybeOpen(nsIFile
* aFile
, int32_t aIoFlags
,
267 int32_t aPerm
, bool aDeferred
) {
268 NS_ENSURE_STATE(aFile
);
270 mOpenParams
.ioFlags
= aIoFlags
;
271 mOpenParams
.perm
= aPerm
;
274 // Clone the file, as it may change between now and the deferred open
275 nsCOMPtr
<nsIFile
> file
;
276 nsresult rv
= aFile
->Clone(getter_AddRefs(file
));
277 NS_ENSURE_SUCCESS(rv
, rv
);
279 mOpenParams
.localFile
= std::move(file
);
280 NS_ENSURE_TRUE(mOpenParams
.localFile
, NS_ERROR_UNEXPECTED
);
282 mState
= eDeferredOpen
;
286 mOpenParams
.localFile
= aFile
;
288 // Following call open() at main thread.
289 // Main thread might be blocked, while open a remote file.
293 void nsFileStreamBase::CleanUpOpen() { mOpenParams
.localFile
= nullptr; }
295 nsresult
nsFileStreamBase::DoOpen() {
296 MOZ_ASSERT(mState
== eDeferredOpen
|| mState
== eUnitialized
||
298 NS_ASSERTION(!mFD
, "Already have a file descriptor!");
299 NS_ASSERTION(mOpenParams
.localFile
, "Must have a file to open");
304 if (mOpenParams
.ioFlags
& PR_CREATE_FILE
) {
305 nsCOMPtr
<nsIFile
> parent
;
306 mOpenParams
.localFile
->GetParent(getter_AddRefs(parent
));
308 // Result doesn't need to be checked. If the file's parent path does not
309 // exist, make it. If it does exist, do nothing.
311 Unused
<< parent
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
316 if (mBehaviorFlags
& nsIFileInputStream::SHARE_DELETE
) {
317 nsCOMPtr
<nsILocalFileWin
> file
= do_QueryInterface(mOpenParams
.localFile
);
320 rv
= file
->OpenNSPRFileDescShareDelete(mOpenParams
.ioFlags
,
321 mOpenParams
.perm
, &fd
);
325 rv
= mOpenParams
.localFile
->OpenNSPRFileDesc(mOpenParams
.ioFlags
,
326 mOpenParams
.perm
, &fd
);
329 if (rv
== NS_OK
&& IOActivityMonitor::IsActive()) {
330 auto nativePath
= mOpenParams
.localFile
->NativePath();
331 if (!nativePath
.IsEmpty()) {
332 // registering the file to the activity monitor
335 IOActivityMonitor::MonitorFile(
336 fd
, NS_ConvertUTF16toUTF8(nativePath
.get()).get());
339 IOActivityMonitor::MonitorFile(fd
, nativePath
.get());
358 nsresult
nsFileStreamBase::DoPendingOpen() {
361 MOZ_CRASH("This should not happen.");
362 return NS_ERROR_FAILURE
;
369 if (NS_WARN_IF(!mFD
)) {
370 return NS_ERROR_FAILURE
;
376 return NS_BASE_STREAM_CLOSED
;
382 MOZ_CRASH("Invalid mState value.");
383 return NS_ERROR_FAILURE
;
386 ////////////////////////////////////////////////////////////////////////////////
389 NS_IMPL_ADDREF_INHERITED(nsFileInputStream
, nsFileStreamBase
)
390 NS_IMPL_RELEASE_INHERITED(nsFileInputStream
, nsFileStreamBase
)
392 NS_IMPL_CLASSINFO(nsFileInputStream
, nullptr, nsIClassInfo::THREADSAFE
,
393 NS_LOCALFILEINPUTSTREAM_CID
)
395 NS_INTERFACE_MAP_BEGIN(nsFileInputStream
)
396 NS_INTERFACE_MAP_ENTRY(nsIInputStream
)
397 NS_INTERFACE_MAP_ENTRY(nsIFileInputStream
)
398 NS_INTERFACE_MAP_ENTRY(nsILineInputStream
)
399 NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream
)
400 NS_IMPL_QUERY_CLASSINFO(nsFileInputStream
)
401 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream
, IsCloneable())
402 NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase
)
404 NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream
, nsIInputStream
,
405 nsIFileInputStream
, nsISeekableStream
,
406 nsITellableStream
, nsILineInputStream
)
408 nsresult
nsFileInputStream::Create(REFNSIID aIID
, void** aResult
) {
409 RefPtr
<nsFileInputStream
> stream
= new nsFileInputStream();
410 return stream
->QueryInterface(aIID
, aResult
);
413 nsresult
nsFileInputStream::Open(nsIFile
* aFile
, int32_t aIOFlags
,
417 // If the previous file is open, close it
420 if (NS_FAILED(rv
)) return rv
;
424 if (aIOFlags
== -1) aIOFlags
= PR_RDONLY
;
425 if (aPerm
== -1) aPerm
= 0;
427 return MaybeOpen(aFile
, aIOFlags
, aPerm
,
428 mBehaviorFlags
& nsIFileInputStream::DEFER_OPEN
);
432 nsFileInputStream::Init(nsIFile
* aFile
, int32_t aIOFlags
, int32_t aPerm
,
433 int32_t aBehaviorFlags
) {
434 NS_ENSURE_TRUE(!mFD
, NS_ERROR_ALREADY_INITIALIZED
);
435 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
436 NS_ERROR_ALREADY_INITIALIZED
);
438 mBehaviorFlags
= aBehaviorFlags
;
439 mState
= eUnitialized
;
445 return Open(aFile
, aIOFlags
, aPerm
);
449 nsFileInputStream::Close() {
450 // Get the cache position at the time the file was close. This allows
451 // NS_SEEK_CUR on a closed file that has been opened with
453 if (mBehaviorFlags
& REOPEN_ON_REWIND
) {
454 // Get actual position. Not one modified by subclasses
455 nsFileStreamBase::Tell(&mCachedPosition
);
458 // null out mLineBuffer in case Close() is called again after failing
459 mLineBuffer
= nullptr;
460 return nsFileStreamBase::Close();
464 nsFileInputStream::Read(char* aBuf
, uint32_t aCount
, uint32_t* _retval
) {
465 nsresult rv
= nsFileStreamBase::Read(aBuf
, aCount
, _retval
);
466 if (rv
== NS_ERROR_FILE_NOT_FOUND
) {
467 // Don't warn if this is a deffered file not found.
471 NS_ENSURE_SUCCESS(rv
, rv
);
473 // Check if we're at the end of file and need to close
474 if (mBehaviorFlags
& CLOSE_ON_EOF
&& *_retval
== 0) {
482 nsFileInputStream::ReadLine(nsACString
& aLine
, bool* aResult
) {
484 mLineBuffer
= MakeUnique
<nsLineBuffer
<char>>();
486 return NS_ReadLine(this, mLineBuffer
.get(), aLine
, aResult
);
490 nsFileInputStream::Seek(int32_t aWhence
, int64_t aOffset
) {
491 return SeekInternal(aWhence
, aOffset
);
494 nsresult
nsFileInputStream::SeekInternal(int32_t aWhence
, int64_t aOffset
,
496 nsresult rv
= DoPendingOpen();
497 if (rv
!= NS_OK
&& rv
!= NS_BASE_STREAM_CLOSED
) {
502 mLineBuffer
= nullptr;
505 if (rv
== NS_BASE_STREAM_CLOSED
) {
506 if (mBehaviorFlags
& REOPEN_ON_REWIND
) {
507 rv
= Open(mFile
, mIOFlags
, mPerm
);
508 NS_ENSURE_SUCCESS(rv
, rv
);
510 // If the file was closed, and we do a relative seek, use the
511 // position we cached when we closed the file to seek to the right
513 if (aWhence
== NS_SEEK_CUR
) {
514 aWhence
= NS_SEEK_SET
;
515 aOffset
+= mCachedPosition
;
517 // If we're trying to seek to the start then we're done, so
518 // return early to avoid Seek from calling DoPendingOpen and
519 // opening the underlying file earlier than necessary.
520 if (aWhence
== NS_SEEK_SET
&& aOffset
== 0) {
524 return NS_BASE_STREAM_CLOSED
;
528 return nsFileStreamBase::Seek(aWhence
, aOffset
);
532 nsFileInputStream::Tell(int64_t* aResult
) {
533 return nsFileStreamBase::Tell(aResult
);
537 nsFileInputStream::Available(uint64_t* aResult
) {
538 return nsFileStreamBase::Available(aResult
);
541 void nsFileInputStream::SerializedComplexity(uint32_t aMaxSize
,
544 uint32_t* aTransferables
) {
548 void nsFileInputStream::Serialize(InputStreamParams
& aParams
, uint32_t aMaxSize
,
549 uint32_t* aSizeUsed
) {
550 MOZ_ASSERT(aSizeUsed
);
553 FileInputStreamParams params
;
555 if (NS_SUCCEEDED(DoPendingOpen())) {
557 FileHandleType fd
= FileHandleType(PR_FileDesc2NativeHandle(mFD
));
558 NS_ASSERTION(fd
, "This should never be null!");
560 params
.fileDescriptor() = FileDescriptor(fd
);
565 "This file has not been opened (or could not be opened). "
566 "Sending an invalid file descriptor to the other process!");
568 params
.fileDescriptor() = FileDescriptor();
571 int32_t behaviorFlags
= mBehaviorFlags
;
573 // The receiving process (or thread) is going to have an open file
574 // descriptor automatically so transferring this flag is meaningless.
575 behaviorFlags
&= ~nsIFileInputStream::DEFER_OPEN
;
577 params
.behaviorFlags() = behaviorFlags
;
578 params
.ioFlags() = mIOFlags
;
583 bool nsFileInputStream::Deserialize(const InputStreamParams
& aParams
) {
584 NS_ASSERTION(!mFD
, "Already have a file descriptor?!");
585 NS_ASSERTION(mState
== nsFileStreamBase::eUnitialized
, "Deferring open?!");
586 NS_ASSERTION(!mFile
, "Should never have a file here!");
587 NS_ASSERTION(!mPerm
, "This should always be 0!");
589 if (aParams
.type() != InputStreamParams::TFileInputStreamParams
) {
590 NS_WARNING("Received unknown parameters from the other process!");
594 const FileInputStreamParams
& params
= aParams
.get_FileInputStreamParams();
596 const FileDescriptor
& fd
= params
.fileDescriptor();
599 auto rawFD
= fd
.ClonePlatformHandle();
600 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
602 NS_WARNING("Failed to import file handle!");
608 NS_WARNING("Received an invalid file descriptor!");
610 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
613 mBehaviorFlags
= params
.behaviorFlags();
615 if (!XRE_IsParentProcess()) {
616 // A child process shouldn't close when it reads the end because it will
617 // not be able to reopen the file later.
618 mBehaviorFlags
&= ~nsIFileInputStream::CLOSE_ON_EOF
;
620 // A child process will not be able to reopen the file so this flag is
622 mBehaviorFlags
&= ~nsIFileInputStream::REOPEN_ON_REWIND
;
625 mIOFlags
= params
.ioFlags();
630 bool nsFileInputStream::IsCloneable() const {
631 // This inputStream is cloneable only if has been created using Init() and
632 // it owns a nsIFile. This is not true when it is deserialized from IPC.
633 return XRE_IsParentProcess() && mFile
;
637 nsFileInputStream::GetCloneable(bool* aCloneable
) {
638 *aCloneable
= IsCloneable();
643 nsFileInputStream::Clone(nsIInputStream
** aResult
) {
644 MOZ_ASSERT(IsCloneable());
645 return NS_NewLocalFileInputStream(aResult
, mFile
, mIOFlags
, mPerm
,
649 ////////////////////////////////////////////////////////////////////////////////
650 // nsFileOutputStream
652 NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream
, nsFileStreamBase
,
653 nsIOutputStream
, nsIFileOutputStream
)
655 nsresult
nsFileOutputStream::Create(REFNSIID aIID
, void** aResult
) {
656 RefPtr
<nsFileOutputStream
> stream
= new nsFileOutputStream();
657 return stream
->QueryInterface(aIID
, aResult
);
661 nsFileOutputStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
662 int32_t behaviorFlags
) {
663 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
664 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
665 NS_ERROR_ALREADY_INITIALIZED
);
667 mBehaviorFlags
= behaviorFlags
;
668 mState
= eUnitialized
;
670 if (ioFlags
== -1) ioFlags
= PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
;
671 if (perm
<= 0) perm
= 0664;
673 return MaybeOpen(file
, ioFlags
, perm
,
674 mBehaviorFlags
& nsIFileOutputStream::DEFER_OPEN
);
677 nsresult
nsFileOutputStream::InitWithFileDescriptor(
678 const mozilla::ipc::FileDescriptor
& aFd
) {
679 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
680 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
681 NS_ERROR_ALREADY_INITIALIZED
);
684 auto rawFD
= aFd
.ClonePlatformHandle();
685 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
687 NS_WARNING("Failed to import file handle!");
688 return NS_ERROR_FAILURE
;
694 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
701 nsFileOutputStream::Preallocate(int64_t aLength
) {
703 return NS_ERROR_NOT_INITIALIZED
;
706 if (!mozilla::fallocate(mFD
, aLength
)) {
707 return NS_ERROR_FAILURE
;
713 ////////////////////////////////////////////////////////////////////////////////
714 // nsAtomicFileOutputStream
716 NS_IMPL_ISUPPORTS_INHERITED(nsAtomicFileOutputStream
, nsFileOutputStream
,
717 nsISafeOutputStream
, nsIOutputStream
,
721 nsAtomicFileOutputStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
722 int32_t behaviorFlags
) {
723 // While `PR_APPEND` is not supported, `-1` is used as `ioFlags` parameter
724 // in some places, and `PR_APPEND | PR_TRUNCATE` does not require appending
725 // to existing file. So, throw an exception only if `PR_APPEND` is
726 // explicitly specified without `PR_TRUNCATE`.
727 if ((ioFlags
& PR_APPEND
) && !(ioFlags
& PR_TRUNCATE
)) {
728 return NS_ERROR_INVALID_ARG
;
730 return nsFileOutputStream::Init(file
, ioFlags
, perm
, behaviorFlags
);
733 nsresult
nsAtomicFileOutputStream::DoOpen() {
734 // Make sure mOpenParams.localFile will be empty if we bail somewhere in
736 nsCOMPtr
<nsIFile
> file
;
737 file
.swap(mOpenParams
.localFile
);
740 return NS_ERROR_NOT_INITIALIZED
;
742 nsresult rv
= file
->Exists(&mTargetFileExists
);
744 NS_ERROR("Can't tell if target file exists");
746 true; // Safer to assume it exists - we just do more work.
749 // follow symlinks, for two reasons:
750 // 1) if a user has deliberately set up a profile file as a symlink, we
752 // 2) to make the MoveToNative() in Finish() an atomic operation (which may
753 // not be the case if moving across directories on different
755 nsCOMPtr
<nsIFile
> tempResult
;
756 rv
= file
->Clone(getter_AddRefs(tempResult
));
757 if (NS_SUCCEEDED(rv
) && mTargetFileExists
) {
758 tempResult
->Normalize();
761 if (NS_SUCCEEDED(rv
) && mTargetFileExists
) {
762 // Abort if |file| is not writable; it won't work as an output stream.
764 if (NS_SUCCEEDED(file
->IsWritable(&isWritable
)) && !isWritable
) {
765 return NS_ERROR_FILE_ACCESS_DENIED
;
769 if (NS_FAILED(file
->GetPermissions(&origPerm
))) {
770 NS_ERROR("Can't get permissions of target file");
771 origPerm
= mOpenParams
.perm
;
774 // XXX What if |perm| is more restrictive then |origPerm|?
775 // This leaves the user supplied permissions as they were.
776 rv
= tempResult
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, origPerm
);
778 if (NS_SUCCEEDED(rv
)) {
779 // nsFileOutputStream::DoOpen will work on the temporary file, so we
780 // prepare it and place it in mOpenParams.localFile.
781 mOpenParams
.localFile
= tempResult
;
782 mTempFile
= tempResult
;
784 rv
= nsFileOutputStream::DoOpen();
790 nsAtomicFileOutputStream::Close() {
791 nsresult rv
= nsFileOutputStream::Close();
793 // the consumer doesn't want the original file overwritten -
794 // so clean up by removing the temp file.
796 mTempFile
->Remove(false);
804 nsAtomicFileOutputStream::Finish() {
805 nsresult rv
= nsFileOutputStream::Close();
807 // if there is no temp file, don't try to move it over the original target.
808 // It would destroy the targetfile if close() is called twice.
809 if (!mTempFile
) return rv
;
811 // Only overwrite if everything was ok, and the temp file could be closed.
812 if (NS_SUCCEEDED(mWriteResult
) && NS_SUCCEEDED(rv
)) {
813 NS_ENSURE_STATE(mTargetFile
);
815 if (!mTargetFileExists
) {
816 // If the target file did not exist when we were initialized, then the
817 // temp file we gave out was actually a reference to the target file.
818 // since we succeeded in writing to the temp file (and hence succeeded
819 // in writing to the target file), there is nothing more to do.
822 if (NS_FAILED(mTargetFile
->Equals(mTempFile
, &equal
)) || !equal
) {
823 NS_WARNING("mTempFile not equal to mTargetFile");
827 nsAutoString targetFilename
;
828 rv
= mTargetFile
->GetLeafName(targetFilename
);
829 if (NS_SUCCEEDED(rv
)) {
830 // This will replace target.
831 rv
= mTempFile
->MoveTo(nullptr, targetFilename
);
832 if (NS_FAILED(rv
)) mTempFile
->Remove(false);
836 mTempFile
->Remove(false);
838 // if writing failed, propagate the failure code to the caller.
839 if (NS_FAILED(mWriteResult
)) rv
= mWriteResult
;
846 nsAtomicFileOutputStream::Write(const char* buf
, uint32_t count
,
848 nsresult rv
= nsFileOutputStream::Write(buf
, count
, result
);
849 if (NS_SUCCEEDED(mWriteResult
)) {
852 } else if (count
!= *result
) {
853 mWriteResult
= NS_ERROR_LOSS_OF_SIGNIFICANT_DATA
;
856 if (NS_FAILED(mWriteResult
) && count
> 0) {
857 NS_WARNING("writing to output stream failed! data may be lost");
863 ////////////////////////////////////////////////////////////////////////////////
864 // nsSafeFileOutputStream
867 nsSafeFileOutputStream::Finish() {
869 return nsAtomicFileOutputStream::Finish();
872 ////////////////////////////////////////////////////////////////////////////////
873 // nsFileRandomAccessStream
875 nsresult
nsFileRandomAccessStream::Create(REFNSIID aIID
, void** aResult
) {
876 RefPtr
<nsFileRandomAccessStream
> stream
= new nsFileRandomAccessStream();
877 return stream
->QueryInterface(aIID
, aResult
);
880 NS_IMPL_ISUPPORTS_INHERITED(nsFileRandomAccessStream
, nsFileStreamBase
,
881 nsIRandomAccessStream
, nsIFileRandomAccessStream
,
882 nsIInputStream
, nsIOutputStream
)
885 nsFileRandomAccessStream::GetInputStream(nsIInputStream
** aInputStream
) {
886 nsCOMPtr
<nsIInputStream
> inputStream(this);
888 inputStream
.forget(aInputStream
);
893 nsFileRandomAccessStream::GetOutputStream(nsIOutputStream
** aOutputStream
) {
894 nsCOMPtr
<nsIOutputStream
> outputStream(this);
896 outputStream
.forget(aOutputStream
);
900 nsIInputStream
* nsFileRandomAccessStream::InputStream() { return this; }
902 nsIOutputStream
* nsFileRandomAccessStream::OutputStream() { return this; }
904 RandomAccessStreamParams
nsFileRandomAccessStream::Serialize() {
905 FileRandomAccessStreamParams params
;
907 if (NS_SUCCEEDED(DoPendingOpen())) {
909 FileHandleType fd
= FileHandleType(PR_FileDesc2NativeHandle(mFD
));
910 MOZ_ASSERT(fd
, "This should never be null!");
912 params
.fileDescriptor() = FileDescriptor(fd
);
917 "This file has not been opened (or could not be opened). "
918 "Sending an invalid file descriptor to the other process!");
920 params
.fileDescriptor() = FileDescriptor();
923 int32_t behaviorFlags
= mBehaviorFlags
;
925 // The receiving process (or thread) is going to have an open file
926 // descriptor automatically so transferring this flag is meaningless.
927 behaviorFlags
&= ~nsIFileInputStream::DEFER_OPEN
;
929 params
.behaviorFlags() = behaviorFlags
;
934 bool nsFileRandomAccessStream::Deserialize(
935 RandomAccessStreamParams
& aStreamParams
) {
936 MOZ_ASSERT(!mFD
, "Already have a file descriptor?!");
937 MOZ_ASSERT(mState
== nsFileStreamBase::eUnitialized
, "Deferring open?!");
939 if (aStreamParams
.type() !=
940 RandomAccessStreamParams::TFileRandomAccessStreamParams
) {
941 NS_WARNING("Received unknown parameters from the other process!");
945 const FileRandomAccessStreamParams
& params
=
946 aStreamParams
.get_FileRandomAccessStreamParams();
948 const FileDescriptor
& fd
= params
.fileDescriptor();
951 auto rawFD
= fd
.ClonePlatformHandle();
952 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
954 NS_WARNING("Failed to import file handle!");
960 NS_WARNING("Received an invalid file descriptor!");
962 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
965 mBehaviorFlags
= params
.behaviorFlags();
971 nsFileRandomAccessStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
972 int32_t behaviorFlags
) {
973 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
974 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
975 NS_ERROR_ALREADY_INITIALIZED
);
977 mBehaviorFlags
= behaviorFlags
;
978 mState
= eUnitialized
;
980 if (ioFlags
== -1) ioFlags
= PR_RDWR
;
981 if (perm
<= 0) perm
= 0;
983 return MaybeOpen(file
, ioFlags
, perm
,
984 mBehaviorFlags
& nsIFileRandomAccessStream::DEFER_OPEN
);
987 ////////////////////////////////////////////////////////////////////////////////