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"
31 #include "mozilla/UniquePtr.h"
33 #include "nsXULAppAPI.h"
35 using FileHandleType
= mozilla::ipc::FileDescriptor::PlatformHandleType
;
37 using namespace mozilla::ipc
;
38 using namespace mozilla::net
;
40 using mozilla::DebugOnly
;
42 using mozilla::Nothing
;
45 ////////////////////////////////////////////////////////////////////////////////
48 nsFileStreamBase::~nsFileStreamBase() {
49 // We don't want to try to rewrind the stream when shutting down.
50 mBehaviorFlags
&= ~nsIFileInputStream::REOPEN_ON_REWIND
;
55 NS_IMPL_ISUPPORTS(nsFileStreamBase
, nsISeekableStream
, nsITellableStream
,
59 nsFileStreamBase::Seek(int32_t whence
, int64_t offset
) {
60 nsresult rv
= DoPendingOpen();
61 NS_ENSURE_SUCCESS(rv
, rv
);
63 int64_t cnt
= PR_Seek64(mFD
, offset
, (PRSeekWhence
)whence
);
64 if (cnt
== int64_t(-1)) {
65 return NS_ErrorAccordingToNSPR();
71 nsFileStreamBase::Tell(int64_t* result
) {
72 if (mState
== eDeferredOpen
&& !(mOpenParams
.ioFlags
& PR_APPEND
)) {
77 nsresult rv
= DoPendingOpen();
78 NS_ENSURE_SUCCESS(rv
, rv
);
80 int64_t cnt
= PR_Seek64(mFD
, 0, PR_SEEK_CUR
);
81 if (cnt
== int64_t(-1)) {
82 return NS_ErrorAccordingToNSPR();
89 nsFileStreamBase::SetEOF() {
90 nsresult rv
= DoPendingOpen();
91 NS_ENSURE_SUCCESS(rv
, rv
);
94 // Some system calls require an EOF offset.
97 if (NS_FAILED(rv
)) return rv
;
101 if (ftruncate(PR_FileDesc2NativeHandle(mFD
), offset
) != 0) {
102 NS_ERROR("ftruncate failed");
103 return NS_ERROR_FAILURE
;
105 #elif defined(XP_WIN)
106 if (!SetEndOfFile((HANDLE
)PR_FileDesc2NativeHandle(mFD
))) {
107 NS_ERROR("SetEndOfFile failed");
108 return NS_ERROR_FAILURE
;
111 // XXX not implemented
118 nsFileStreamBase::GetSize(int64_t* _retval
) {
119 nsresult rv
= DoPendingOpen();
120 NS_ENSURE_SUCCESS(rv
, rv
);
123 if (PR_GetOpenFileInfo64(mFD
, &info
) == PR_FAILURE
) {
124 return NS_BASE_STREAM_OSERROR
;
127 *_retval
= int64_t(info
.size
);
133 nsFileStreamBase::GetLastModified(int64_t* _retval
) {
134 nsresult rv
= DoPendingOpen();
135 NS_ENSURE_SUCCESS(rv
, rv
);
138 if (PR_GetOpenFileInfo64(mFD
, &info
) == PR_FAILURE
) {
139 return NS_BASE_STREAM_OSERROR
;
142 int64_t modTime
= int64_t(info
.modifyTime
);
146 *_retval
= modTime
/ int64_t(PR_USEC_PER_MSEC
);
153 nsFileStreamBase::GetFileDescriptor(PRFileDesc
** _retval
) {
154 nsresult rv
= DoPendingOpen();
155 NS_ENSURE_SUCCESS(rv
, rv
);
161 nsresult
nsFileStreamBase::Close() {
162 if (mState
== eClosed
) {
170 if (PR_Close(mFD
) == PR_FAILURE
) rv
= NS_BASE_STREAM_OSERROR
;
177 nsresult
nsFileStreamBase::Available(uint64_t* aResult
) {
178 nsresult rv
= DoPendingOpen();
179 NS_ENSURE_SUCCESS(rv
, rv
);
181 // PR_Available with files over 4GB returns an error, so we have to
182 // use the 64-bit version of PR_Available.
183 int64_t avail
= PR_Available64(mFD
);
185 return NS_ErrorAccordingToNSPR();
188 // If available is greater than 4GB, return 4GB
189 *aResult
= (uint64_t)avail
;
193 nsresult
nsFileStreamBase::Read(char* aBuf
, uint32_t aCount
,
195 nsresult rv
= DoPendingOpen();
196 if (rv
== NS_BASE_STREAM_CLOSED
) {
205 int32_t bytesRead
= PR_Read(mFD
, aBuf
, aCount
);
206 if (bytesRead
== -1) {
207 return NS_ErrorAccordingToNSPR();
210 *aResult
= bytesRead
;
214 nsresult
nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter
,
215 void* aClosure
, uint32_t aCount
,
217 // ReadSegments is not implemented because it would be inefficient when
218 // the writer does not consume all data. If you want to call ReadSegments,
219 // wrap a BufferedInputStream around the file stream. That will call
222 return NS_ERROR_NOT_IMPLEMENTED
;
225 nsresult
nsFileStreamBase::IsNonBlocking(bool* aNonBlocking
) {
226 *aNonBlocking
= false;
230 nsresult
nsFileStreamBase::Flush(void) {
231 nsresult rv
= DoPendingOpen();
232 NS_ENSURE_SUCCESS(rv
, rv
);
234 int32_t cnt
= PR_Sync(mFD
);
236 return NS_ErrorAccordingToNSPR();
241 nsresult
nsFileStreamBase::StreamStatus() {
244 MOZ_CRASH("This should not happen.");
245 return NS_ERROR_FAILURE
;
252 if (NS_WARN_IF(!mFD
)) {
253 return NS_ERROR_FAILURE
;
259 return NS_BASE_STREAM_CLOSED
;
265 MOZ_CRASH("Invalid mState value.");
266 return NS_ERROR_FAILURE
;
269 nsresult
nsFileStreamBase::Write(const char* buf
, uint32_t count
,
271 nsresult rv
= DoPendingOpen();
272 NS_ENSURE_SUCCESS(rv
, rv
);
274 int32_t cnt
= PR_Write(mFD
, buf
, count
);
276 return NS_ErrorAccordingToNSPR();
282 nsresult
nsFileStreamBase::WriteFrom(nsIInputStream
* inStr
, uint32_t count
,
284 MOZ_ASSERT_UNREACHABLE("WriteFrom (see source comment)");
285 return NS_ERROR_NOT_IMPLEMENTED
;
286 // File streams intentionally do not support this method.
287 // If you need something like this, then you should wrap
288 // the file stream using nsIBufferedOutputStream
291 nsresult
nsFileStreamBase::WriteSegments(nsReadSegmentFun reader
, void* closure
,
292 uint32_t count
, uint32_t* _retval
) {
293 return NS_ERROR_NOT_IMPLEMENTED
;
294 // File streams intentionally do not support this method.
295 // If you need something like this, then you should wrap
296 // the file stream using nsIBufferedOutputStream
299 nsresult
nsFileStreamBase::MaybeOpen(nsIFile
* aFile
, int32_t aIoFlags
,
300 int32_t aPerm
, bool aDeferred
) {
301 NS_ENSURE_STATE(aFile
);
303 mOpenParams
.ioFlags
= aIoFlags
;
304 mOpenParams
.perm
= aPerm
;
307 // Clone the file, as it may change between now and the deferred open
308 nsCOMPtr
<nsIFile
> file
;
309 nsresult rv
= aFile
->Clone(getter_AddRefs(file
));
310 NS_ENSURE_SUCCESS(rv
, rv
);
312 mOpenParams
.localFile
= std::move(file
);
313 NS_ENSURE_TRUE(mOpenParams
.localFile
, NS_ERROR_UNEXPECTED
);
315 mState
= eDeferredOpen
;
319 mOpenParams
.localFile
= aFile
;
321 // Following call open() at main thread.
322 // Main thread might be blocked, while open a remote file.
326 void nsFileStreamBase::CleanUpOpen() { mOpenParams
.localFile
= nullptr; }
328 nsresult
nsFileStreamBase::DoOpen() {
329 MOZ_ASSERT(mState
== eDeferredOpen
|| mState
== eUnitialized
||
331 NS_ASSERTION(!mFD
, "Already have a file descriptor!");
332 NS_ASSERTION(mOpenParams
.localFile
, "Must have a file to open");
337 if (mOpenParams
.ioFlags
& PR_CREATE_FILE
) {
338 nsCOMPtr
<nsIFile
> parent
;
339 mOpenParams
.localFile
->GetParent(getter_AddRefs(parent
));
341 // Result doesn't need to be checked. If the file's parent path does not
342 // exist, make it. If it does exist, do nothing.
344 mozilla::Unused
<< parent
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
349 if (mBehaviorFlags
& nsIFileInputStream::SHARE_DELETE
) {
350 nsCOMPtr
<nsILocalFileWin
> file
= do_QueryInterface(mOpenParams
.localFile
);
353 rv
= file
->OpenNSPRFileDescShareDelete(mOpenParams
.ioFlags
,
354 mOpenParams
.perm
, &fd
);
358 rv
= mOpenParams
.localFile
->OpenNSPRFileDesc(mOpenParams
.ioFlags
,
359 mOpenParams
.perm
, &fd
);
362 if (rv
== NS_OK
&& IOActivityMonitor::IsActive()) {
363 auto nativePath
= mOpenParams
.localFile
->NativePath();
364 if (!nativePath
.IsEmpty()) {
365 // registering the file to the activity monitor
368 IOActivityMonitor::MonitorFile(
369 fd
, NS_ConvertUTF16toUTF8(nativePath
.get()).get());
372 IOActivityMonitor::MonitorFile(fd
, nativePath
.get());
391 nsresult
nsFileStreamBase::DoPendingOpen() {
394 MOZ_CRASH("This should not happen.");
395 return NS_ERROR_FAILURE
;
402 if (NS_WARN_IF(!mFD
)) {
403 return NS_ERROR_FAILURE
;
409 return NS_BASE_STREAM_CLOSED
;
415 MOZ_CRASH("Invalid mState value.");
416 return NS_ERROR_FAILURE
;
419 ////////////////////////////////////////////////////////////////////////////////
422 NS_IMPL_ADDREF_INHERITED(nsFileInputStream
, nsFileStreamBase
)
423 NS_IMPL_RELEASE_INHERITED(nsFileInputStream
, nsFileStreamBase
)
425 NS_IMPL_CLASSINFO(nsFileInputStream
, nullptr, nsIClassInfo::THREADSAFE
,
426 NS_LOCALFILEINPUTSTREAM_CID
)
428 NS_INTERFACE_MAP_BEGIN(nsFileInputStream
)
429 NS_INTERFACE_MAP_ENTRY(nsIInputStream
)
430 NS_INTERFACE_MAP_ENTRY(nsIFileInputStream
)
431 NS_INTERFACE_MAP_ENTRY(nsILineInputStream
)
432 NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream
)
433 NS_IMPL_QUERY_CLASSINFO(nsFileInputStream
)
434 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream
, IsCloneable())
435 NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase
)
437 NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream
, nsIInputStream
,
438 nsIFileInputStream
, nsISeekableStream
,
439 nsITellableStream
, nsILineInputStream
)
441 nsresult
nsFileInputStream::Create(REFNSIID aIID
, void** aResult
) {
442 RefPtr
<nsFileInputStream
> stream
= new nsFileInputStream();
443 return stream
->QueryInterface(aIID
, aResult
);
446 nsresult
nsFileInputStream::Open(nsIFile
* aFile
, int32_t aIOFlags
,
450 // If the previous file is open, close it
453 if (NS_FAILED(rv
)) return rv
;
457 if (aIOFlags
== -1) aIOFlags
= PR_RDONLY
;
458 if (aPerm
== -1) aPerm
= 0;
460 return MaybeOpen(aFile
, aIOFlags
, aPerm
,
461 mBehaviorFlags
& nsIFileInputStream::DEFER_OPEN
);
465 nsFileInputStream::Init(nsIFile
* aFile
, int32_t aIOFlags
, int32_t aPerm
,
466 int32_t aBehaviorFlags
) {
467 NS_ENSURE_TRUE(!mFD
, NS_ERROR_ALREADY_INITIALIZED
);
468 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
469 NS_ERROR_ALREADY_INITIALIZED
);
471 mBehaviorFlags
= aBehaviorFlags
;
472 mState
= eUnitialized
;
478 return Open(aFile
, aIOFlags
, aPerm
);
482 nsFileInputStream::Close() {
483 // If this stream has already been closed, do nothing.
484 if (mState
== eClosed
) {
488 // Get the cache position at the time the file was close. This allows
489 // NS_SEEK_CUR on a closed file that has been opened with
491 if (mBehaviorFlags
& REOPEN_ON_REWIND
) {
492 // Get actual position. Not one modified by subclasses
493 nsFileStreamBase::Tell(&mCachedPosition
);
496 // explicitly clear mLineBuffer in case this stream is reopened
497 mLineBuffer
= nullptr;
498 return nsFileStreamBase::Close();
502 nsFileInputStream::Read(char* aBuf
, uint32_t aCount
, uint32_t* _retval
) {
503 nsresult rv
= nsFileStreamBase::Read(aBuf
, aCount
, _retval
);
504 if (rv
== NS_ERROR_FILE_NOT_FOUND
) {
505 // Don't warn if this is a deffered file not found.
513 // Check if we're at the end of file and need to close
514 if (mBehaviorFlags
& CLOSE_ON_EOF
&& *_retval
== 0) {
522 nsFileInputStream::ReadLine(nsACString
& aLine
, bool* aResult
) {
524 mLineBuffer
= mozilla::MakeUnique
<nsLineBuffer
<char>>();
526 return NS_ReadLine(this, mLineBuffer
.get(), aLine
, aResult
);
530 nsFileInputStream::Seek(int32_t aWhence
, int64_t aOffset
) {
531 return SeekInternal(aWhence
, aOffset
);
534 nsresult
nsFileInputStream::SeekInternal(int32_t aWhence
, int64_t aOffset
,
536 nsresult rv
= DoPendingOpen();
537 if (rv
!= NS_OK
&& rv
!= NS_BASE_STREAM_CLOSED
) {
542 mLineBuffer
= nullptr;
545 if (rv
== NS_BASE_STREAM_CLOSED
) {
546 if (mBehaviorFlags
& REOPEN_ON_REWIND
) {
547 rv
= Open(mFile
, mIOFlags
, mPerm
);
548 NS_ENSURE_SUCCESS(rv
, rv
);
550 // If the file was closed, and we do a relative seek, use the
551 // position we cached when we closed the file to seek to the right
553 if (aWhence
== NS_SEEK_CUR
) {
554 aWhence
= NS_SEEK_SET
;
555 aOffset
+= mCachedPosition
;
557 // If we're trying to seek to the start then we're done, so
558 // return early to avoid Seek from calling DoPendingOpen and
559 // opening the underlying file earlier than necessary.
560 if (aWhence
== NS_SEEK_SET
&& aOffset
== 0) {
564 return NS_BASE_STREAM_CLOSED
;
568 return nsFileStreamBase::Seek(aWhence
, aOffset
);
572 nsFileInputStream::Tell(int64_t* aResult
) {
573 return nsFileStreamBase::Tell(aResult
);
577 nsFileInputStream::Available(uint64_t* aResult
) {
578 return nsFileStreamBase::Available(aResult
);
582 nsFileInputStream::StreamStatus() { return nsFileStreamBase::StreamStatus(); }
584 void nsFileInputStream::SerializedComplexity(uint32_t aMaxSize
,
587 uint32_t* aTransferables
) {
591 void nsFileInputStream::Serialize(InputStreamParams
& aParams
, uint32_t aMaxSize
,
592 uint32_t* aSizeUsed
) {
593 MOZ_ASSERT(aSizeUsed
);
596 FileInputStreamParams params
;
598 if (NS_SUCCEEDED(DoPendingOpen())) {
600 FileHandleType fd
= FileHandleType(PR_FileDesc2NativeHandle(mFD
));
601 NS_ASSERTION(fd
, "This should never be null!");
603 params
.fileDescriptor() = FileDescriptor(fd
);
608 "This file has not been opened (or could not be opened). "
609 "Sending an invalid file descriptor to the other process!");
611 params
.fileDescriptor() = FileDescriptor();
614 int32_t behaviorFlags
= mBehaviorFlags
;
616 // The receiving process (or thread) is going to have an open file
617 // descriptor automatically so transferring this flag is meaningless.
618 behaviorFlags
&= ~nsIFileInputStream::DEFER_OPEN
;
620 params
.behaviorFlags() = behaviorFlags
;
621 params
.ioFlags() = mIOFlags
;
626 bool nsFileInputStream::Deserialize(const InputStreamParams
& aParams
) {
627 NS_ASSERTION(!mFD
, "Already have a file descriptor?!");
628 NS_ASSERTION(mState
== nsFileStreamBase::eUnitialized
, "Deferring open?!");
629 NS_ASSERTION(!mFile
, "Should never have a file here!");
630 NS_ASSERTION(!mPerm
, "This should always be 0!");
632 if (aParams
.type() != InputStreamParams::TFileInputStreamParams
) {
633 NS_WARNING("Received unknown parameters from the other process!");
637 const FileInputStreamParams
& params
= aParams
.get_FileInputStreamParams();
639 const FileDescriptor
& fd
= params
.fileDescriptor();
642 auto rawFD
= fd
.ClonePlatformHandle();
643 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
645 NS_WARNING("Failed to import file handle!");
651 NS_WARNING("Received an invalid file descriptor!");
653 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
656 mBehaviorFlags
= params
.behaviorFlags();
658 if (!XRE_IsParentProcess()) {
659 // A child process shouldn't close when it reads the end because it will
660 // not be able to reopen the file later.
661 mBehaviorFlags
&= ~nsIFileInputStream::CLOSE_ON_EOF
;
663 // A child process will not be able to reopen the file so this flag is
665 mBehaviorFlags
&= ~nsIFileInputStream::REOPEN_ON_REWIND
;
668 mIOFlags
= params
.ioFlags();
673 bool nsFileInputStream::IsCloneable() const {
674 // This inputStream is cloneable only if has been created using Init() and
675 // it owns a nsIFile. This is not true when it is deserialized from IPC.
676 return XRE_IsParentProcess() && mFile
;
680 nsFileInputStream::GetCloneable(bool* aCloneable
) {
681 *aCloneable
= IsCloneable();
686 nsFileInputStream::Clone(nsIInputStream
** aResult
) {
687 MOZ_ASSERT(IsCloneable());
688 return NS_NewLocalFileInputStream(aResult
, mFile
, mIOFlags
, mPerm
,
692 ////////////////////////////////////////////////////////////////////////////////
693 // nsFileOutputStream
695 NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream
, nsFileStreamBase
,
696 nsIOutputStream
, nsIFileOutputStream
)
698 nsresult
nsFileOutputStream::Create(REFNSIID aIID
, void** aResult
) {
699 RefPtr
<nsFileOutputStream
> stream
= new nsFileOutputStream();
700 return stream
->QueryInterface(aIID
, aResult
);
704 nsFileOutputStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
705 int32_t behaviorFlags
) {
706 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
707 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
708 NS_ERROR_ALREADY_INITIALIZED
);
710 mBehaviorFlags
= behaviorFlags
;
711 mState
= eUnitialized
;
713 if (ioFlags
== -1) ioFlags
= PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
;
714 if (perm
<= 0) perm
= 0664;
716 return MaybeOpen(file
, ioFlags
, perm
,
717 mBehaviorFlags
& nsIFileOutputStream::DEFER_OPEN
);
720 nsresult
nsFileOutputStream::InitWithFileDescriptor(
721 const mozilla::ipc::FileDescriptor
& aFd
) {
722 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
723 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
724 NS_ERROR_ALREADY_INITIALIZED
);
727 auto rawFD
= aFd
.ClonePlatformHandle();
728 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
730 NS_WARNING("Failed to import file handle!");
731 return NS_ERROR_FAILURE
;
737 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
744 nsFileOutputStream::Preallocate(int64_t aLength
) {
746 return NS_ERROR_NOT_INITIALIZED
;
749 if (!mozilla::fallocate(mFD
, aLength
)) {
750 return NS_ERROR_FAILURE
;
756 ////////////////////////////////////////////////////////////////////////////////
757 // nsAtomicFileOutputStream
759 NS_IMPL_ISUPPORTS_INHERITED(nsAtomicFileOutputStream
, nsFileOutputStream
,
760 nsISafeOutputStream
, nsIOutputStream
,
764 nsAtomicFileOutputStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
765 int32_t behaviorFlags
) {
766 // While `PR_APPEND` is not supported, `-1` is used as `ioFlags` parameter
767 // in some places, and `PR_APPEND | PR_TRUNCATE` does not require appending
768 // to existing file. So, throw an exception only if `PR_APPEND` is
769 // explicitly specified without `PR_TRUNCATE`.
770 if ((ioFlags
& PR_APPEND
) && !(ioFlags
& PR_TRUNCATE
)) {
771 return NS_ERROR_INVALID_ARG
;
773 return nsFileOutputStream::Init(file
, ioFlags
, perm
, behaviorFlags
);
776 nsresult
nsAtomicFileOutputStream::DoOpen() {
777 // Make sure mOpenParams.localFile will be empty if we bail somewhere in
779 nsCOMPtr
<nsIFile
> file
;
780 file
.swap(mOpenParams
.localFile
);
783 return NS_ERROR_NOT_INITIALIZED
;
785 nsresult rv
= file
->Exists(&mTargetFileExists
);
787 NS_ERROR("Can't tell if target file exists");
789 true; // Safer to assume it exists - we just do more work.
792 // follow symlinks, for two reasons:
793 // 1) if a user has deliberately set up a profile file as a symlink, we
795 // 2) to make the MoveToNative() in Finish() an atomic operation (which may
796 // not be the case if moving across directories on different
798 nsCOMPtr
<nsIFile
> tempResult
;
799 rv
= file
->Clone(getter_AddRefs(tempResult
));
800 if (NS_SUCCEEDED(rv
) && mTargetFileExists
) {
801 tempResult
->Normalize();
804 if (NS_SUCCEEDED(rv
) && mTargetFileExists
) {
805 // Abort if |file| is not writable; it won't work as an output stream.
807 if (NS_SUCCEEDED(file
->IsWritable(&isWritable
)) && !isWritable
) {
808 return NS_ERROR_FILE_ACCESS_DENIED
;
812 if (NS_FAILED(file
->GetPermissions(&origPerm
))) {
813 NS_ERROR("Can't get permissions of target file");
814 origPerm
= mOpenParams
.perm
;
817 // XXX What if |perm| is more restrictive then |origPerm|?
818 // This leaves the user supplied permissions as they were.
819 rv
= tempResult
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, origPerm
);
821 if (NS_SUCCEEDED(rv
)) {
822 // nsFileOutputStream::DoOpen will work on the temporary file, so we
823 // prepare it and place it in mOpenParams.localFile.
824 mOpenParams
.localFile
= tempResult
;
825 mTempFile
= tempResult
;
827 rv
= nsFileOutputStream::DoOpen();
833 nsAtomicFileOutputStream::Close() {
834 nsresult rv
= nsFileOutputStream::Close();
836 // the consumer doesn't want the original file overwritten -
837 // so clean up by removing the temp file.
839 mTempFile
->Remove(false);
847 nsAtomicFileOutputStream::Finish() {
848 nsresult rv
= nsFileOutputStream::Close();
850 // if there is no temp file, don't try to move it over the original target.
851 // It would destroy the targetfile if close() is called twice.
852 if (!mTempFile
) return rv
;
854 // Only overwrite if everything was ok, and the temp file could be closed.
855 if (NS_SUCCEEDED(mWriteResult
) && NS_SUCCEEDED(rv
)) {
856 NS_ENSURE_STATE(mTargetFile
);
858 if (!mTargetFileExists
) {
859 // If the target file did not exist when we were initialized, then the
860 // temp file we gave out was actually a reference to the target file.
861 // since we succeeded in writing to the temp file (and hence succeeded
862 // in writing to the target file), there is nothing more to do.
865 if (NS_FAILED(mTargetFile
->Equals(mTempFile
, &equal
)) || !equal
) {
866 NS_WARNING("mTempFile not equal to mTargetFile");
870 nsAutoString targetFilename
;
871 rv
= mTargetFile
->GetLeafName(targetFilename
);
872 if (NS_SUCCEEDED(rv
)) {
873 // This will replace target.
874 rv
= mTempFile
->MoveTo(nullptr, targetFilename
);
875 if (NS_FAILED(rv
)) mTempFile
->Remove(false);
879 mTempFile
->Remove(false);
881 // if writing failed, propagate the failure code to the caller.
882 if (NS_FAILED(mWriteResult
)) rv
= mWriteResult
;
889 nsAtomicFileOutputStream::Write(const char* buf
, uint32_t count
,
891 nsresult rv
= nsFileOutputStream::Write(buf
, count
, result
);
892 if (NS_SUCCEEDED(mWriteResult
)) {
895 } else if (count
!= *result
) {
896 mWriteResult
= NS_ERROR_LOSS_OF_SIGNIFICANT_DATA
;
899 if (NS_FAILED(mWriteResult
) && count
> 0) {
900 NS_WARNING("writing to output stream failed! data may be lost");
906 ////////////////////////////////////////////////////////////////////////////////
907 // nsSafeFileOutputStream
910 nsSafeFileOutputStream::Finish() {
912 return nsAtomicFileOutputStream::Finish();
915 ////////////////////////////////////////////////////////////////////////////////
916 // nsFileRandomAccessStream
918 nsresult
nsFileRandomAccessStream::Create(REFNSIID aIID
, void** aResult
) {
919 RefPtr
<nsFileRandomAccessStream
> stream
= new nsFileRandomAccessStream();
920 return stream
->QueryInterface(aIID
, aResult
);
923 NS_IMPL_ISUPPORTS_INHERITED(nsFileRandomAccessStream
, nsFileStreamBase
,
924 nsIRandomAccessStream
, nsIFileRandomAccessStream
,
925 nsIInputStream
, nsIOutputStream
)
928 nsFileRandomAccessStream::GetInputStream(nsIInputStream
** aInputStream
) {
929 nsCOMPtr
<nsIInputStream
> inputStream(this);
931 inputStream
.forget(aInputStream
);
936 nsFileRandomAccessStream::GetOutputStream(nsIOutputStream
** aOutputStream
) {
937 nsCOMPtr
<nsIOutputStream
> outputStream(this);
939 outputStream
.forget(aOutputStream
);
943 nsIInputStream
* nsFileRandomAccessStream::InputStream() { return this; }
945 nsIOutputStream
* nsFileRandomAccessStream::OutputStream() { return this; }
947 RandomAccessStreamParams
nsFileRandomAccessStream::Serialize(
948 nsIInterfaceRequestor
* aCallbacks
) {
949 FileRandomAccessStreamParams params
;
951 if (NS_SUCCEEDED(DoPendingOpen())) {
953 FileHandleType fd
= FileHandleType(PR_FileDesc2NativeHandle(mFD
));
954 MOZ_ASSERT(fd
, "This should never be null!");
956 params
.fileDescriptor() = FileDescriptor(fd
);
961 "This file has not been opened (or could not be opened). "
962 "Sending an invalid file descriptor to the other process!");
964 params
.fileDescriptor() = FileDescriptor();
967 int32_t behaviorFlags
= mBehaviorFlags
;
969 // The receiving process (or thread) is going to have an open file
970 // descriptor automatically so transferring this flag is meaningless.
971 behaviorFlags
&= ~nsIFileInputStream::DEFER_OPEN
;
973 params
.behaviorFlags() = behaviorFlags
;
978 bool nsFileRandomAccessStream::Deserialize(
979 RandomAccessStreamParams
& aStreamParams
) {
980 MOZ_ASSERT(!mFD
, "Already have a file descriptor?!");
981 MOZ_ASSERT(mState
== nsFileStreamBase::eUnitialized
, "Deferring open?!");
983 if (aStreamParams
.type() !=
984 RandomAccessStreamParams::TFileRandomAccessStreamParams
) {
985 NS_WARNING("Received unknown parameters from the other process!");
989 const FileRandomAccessStreamParams
& params
=
990 aStreamParams
.get_FileRandomAccessStreamParams();
992 const FileDescriptor
& fd
= params
.fileDescriptor();
995 auto rawFD
= fd
.ClonePlatformHandle();
996 PRFileDesc
* fileDesc
= PR_ImportFile(PROsfd(rawFD
.release()));
998 NS_WARNING("Failed to import file handle!");
1004 NS_WARNING("Received an invalid file descriptor!");
1006 mErrorValue
= NS_ERROR_FILE_NOT_FOUND
;
1009 mBehaviorFlags
= params
.behaviorFlags();
1015 nsFileRandomAccessStream::Init(nsIFile
* file
, int32_t ioFlags
, int32_t perm
,
1016 int32_t behaviorFlags
) {
1017 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
1018 NS_ENSURE_TRUE(mState
== eUnitialized
|| mState
== eClosed
,
1019 NS_ERROR_ALREADY_INITIALIZED
);
1021 mBehaviorFlags
= behaviorFlags
;
1022 mState
= eUnitialized
;
1024 if (ioFlags
== -1) ioFlags
= PR_RDWR
;
1025 if (perm
<= 0) perm
= 0;
1027 return MaybeOpen(file
, ioFlags
, perm
,
1028 mBehaviorFlags
& nsIFileRandomAccessStream::DEFER_OPEN
);
1031 ////////////////////////////////////////////////////////////////////////////////