Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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);
1012 }
1014 ////////////////////////////////////////////////////////////////////////////////