michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ipc/IPCMessageUtils.h" michael@0: michael@0: #if defined(XP_UNIX) || defined(XP_BEOS) michael@0: #include michael@0: #elif defined(XP_WIN) michael@0: #include michael@0: #else michael@0: // XXX add necessary include file for ftruncate (or equivalent) michael@0: #endif michael@0: michael@0: #include "private/pprio.h" michael@0: michael@0: #include "nsFileStreams.h" michael@0: #include "nsIFile.h" michael@0: #include "nsReadLine.h" michael@0: #include "nsIClassInfoImpl.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: #include "nsNetCID.h" michael@0: michael@0: #define NS_NO_INPUT_BUFFERING 1 // see http://bugzilla.mozilla.org/show_bug.cgi?id=41067 michael@0: michael@0: typedef mozilla::ipc::FileDescriptor::PlatformHandleType FileHandleType; michael@0: michael@0: using namespace mozilla::ipc; michael@0: using mozilla::DebugOnly; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsFileStreamBase michael@0: michael@0: nsFileStreamBase::nsFileStreamBase() michael@0: : mFD(nullptr) michael@0: , mBehaviorFlags(0) michael@0: , mDeferredOpen(false) michael@0: { michael@0: } michael@0: michael@0: nsFileStreamBase::~nsFileStreamBase() michael@0: { michael@0: Close(); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFileStreamBase, michael@0: nsISeekableStream, michael@0: nsIFileMetadata) michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileStreamBase::Seek(int32_t whence, int64_t offset) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mFD == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: int64_t cnt = PR_Seek64(mFD, offset, (PRSeekWhence)whence); michael@0: if (cnt == int64_t(-1)) { michael@0: return NS_ErrorAccordingToNSPR(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileStreamBase::Tell(int64_t *result) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mFD == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: int64_t cnt = PR_Seek64(mFD, 0, PR_SEEK_CUR); michael@0: if (cnt == int64_t(-1)) { michael@0: return NS_ErrorAccordingToNSPR(); michael@0: } michael@0: *result = cnt; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileStreamBase::SetEOF() michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mFD == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: #if defined(XP_UNIX) || defined(XP_BEOS) michael@0: // Some system calls require an EOF offset. michael@0: int64_t offset; michael@0: rv = Tell(&offset); michael@0: if (NS_FAILED(rv)) return rv; michael@0: #endif michael@0: michael@0: #if defined(XP_UNIX) || defined(XP_BEOS) michael@0: if (ftruncate(PR_FileDesc2NativeHandle(mFD), offset) != 0) { michael@0: NS_ERROR("ftruncate failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: #elif defined(XP_WIN) michael@0: if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(mFD))) { michael@0: NS_ERROR("SetEndOfFile failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: #else michael@0: // XXX not implemented michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileStreamBase::GetSize(int64_t* _retval) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!mFD) { michael@0: return NS_BASE_STREAM_CLOSED; michael@0: } michael@0: michael@0: PRFileInfo64 info; michael@0: if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) { michael@0: return NS_BASE_STREAM_OSERROR; michael@0: } michael@0: michael@0: *_retval = int64_t(info.size); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileStreamBase::GetLastModified(int64_t* _retval) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!mFD) { michael@0: return NS_BASE_STREAM_CLOSED; michael@0: } michael@0: michael@0: PRFileInfo64 info; michael@0: if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) { michael@0: return NS_BASE_STREAM_OSERROR; michael@0: } michael@0: michael@0: int64_t modTime = int64_t(info.modifyTime); michael@0: if (modTime == 0) { michael@0: *_retval = 0; michael@0: } michael@0: else { michael@0: *_retval = modTime / int64_t(PR_USEC_PER_MSEC); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::Close() michael@0: { michael@0: CleanUpOpen(); michael@0: michael@0: nsresult rv = NS_OK; michael@0: if (mFD) { michael@0: if (PR_Close(mFD) == PR_FAILURE) michael@0: rv = NS_BASE_STREAM_OSERROR; michael@0: mFD = nullptr; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::Available(uint64_t* aResult) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!mFD) { michael@0: return NS_BASE_STREAM_CLOSED; michael@0: } michael@0: michael@0: // PR_Available with files over 4GB returns an error, so we have to michael@0: // use the 64-bit version of PR_Available. michael@0: int64_t avail = PR_Available64(mFD); michael@0: if (avail == -1) { michael@0: return NS_ErrorAccordingToNSPR(); michael@0: } michael@0: michael@0: // If available is greater than 4GB, return 4GB michael@0: *aResult = (uint64_t)avail; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::Read(char* aBuf, uint32_t aCount, uint32_t* aResult) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!mFD) { michael@0: *aResult = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t bytesRead = PR_Read(mFD, aBuf, aCount); michael@0: if (bytesRead == -1) { michael@0: return NS_ErrorAccordingToNSPR(); michael@0: } michael@0: michael@0: *aResult = bytesRead; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, michael@0: uint32_t aCount, uint32_t* aResult) michael@0: { michael@0: // ReadSegments is not implemented because it would be inefficient when michael@0: // the writer does not consume all data. If you want to call ReadSegments, michael@0: // wrap a BufferedInputStream around the file stream. That will call michael@0: // Read(). michael@0: michael@0: // If this is ever implemented you might need to modify michael@0: // nsPartialFileInputStream::ReadSegments michael@0: michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::IsNonBlocking(bool *aNonBlocking) michael@0: { michael@0: *aNonBlocking = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::Flush(void) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mFD == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: int32_t cnt = PR_Sync(mFD); michael@0: if (cnt == -1) { michael@0: return NS_ErrorAccordingToNSPR(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::Write(const char *buf, uint32_t count, uint32_t *result) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mFD == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: int32_t cnt = PR_Write(mFD, buf, count); michael@0: if (cnt == -1) { michael@0: return NS_ErrorAccordingToNSPR(); michael@0: } michael@0: *result = cnt; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) michael@0: { michael@0: NS_NOTREACHED("WriteFrom (see source comment)"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: // File streams intentionally do not support this method. michael@0: // If you need something like this, then you should wrap michael@0: // the file stream using nsIBufferedOutputStream michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: // File streams intentionally do not support this method. michael@0: // If you need something like this, then you should wrap michael@0: // the file stream using nsIBufferedOutputStream michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::MaybeOpen(nsIFile* aFile, int32_t aIoFlags, michael@0: int32_t aPerm, bool aDeferred) michael@0: { michael@0: NS_ENSURE_STATE(aFile); michael@0: michael@0: mOpenParams.ioFlags = aIoFlags; michael@0: mOpenParams.perm = aPerm; michael@0: michael@0: if (aDeferred) { michael@0: // Clone the file, as it may change between now and the deferred open michael@0: nsCOMPtr file; michael@0: nsresult rv = aFile->Clone(getter_AddRefs(file)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mOpenParams.localFile = do_QueryInterface(file); michael@0: NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED); michael@0: michael@0: mDeferredOpen = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: mOpenParams.localFile = aFile; michael@0: michael@0: return DoOpen(); michael@0: } michael@0: michael@0: void michael@0: nsFileStreamBase::CleanUpOpen() michael@0: { michael@0: mOpenParams.localFile = nullptr; michael@0: mDeferredOpen = false; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::DoOpen() michael@0: { michael@0: NS_ASSERTION(!mFD, "Already have a file descriptor!"); michael@0: NS_ASSERTION(mOpenParams.localFile, "Must have a file to open"); michael@0: michael@0: PRFileDesc* fd; michael@0: nsresult rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags, michael@0: mOpenParams.perm, michael@0: &fd); michael@0: CleanUpOpen(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: mFD = fd; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileStreamBase::DoPendingOpen() michael@0: { michael@0: if (!mDeferredOpen) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return DoOpen(); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsFileInputStream michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsFileInputStream, nsFileStreamBase) michael@0: NS_IMPL_RELEASE_INHERITED(nsFileInputStream, nsFileStreamBase) michael@0: michael@0: NS_IMPL_CLASSINFO(nsFileInputStream, nullptr, nsIClassInfo::THREADSAFE, michael@0: NS_LOCALFILEINPUTSTREAM_CID) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsFileInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIFileInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsILineInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) michael@0: NS_IMPL_QUERY_CLASSINFO(nsFileInputStream) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase) michael@0: michael@0: NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream, michael@0: nsIInputStream, michael@0: nsIFileInputStream, michael@0: nsISeekableStream, michael@0: nsILineInputStream) michael@0: michael@0: nsresult michael@0: nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: michael@0: nsFileInputStream* stream = new nsFileInputStream(); michael@0: if (stream == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(stream); michael@0: nsresult rv = stream->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(stream); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsFileInputStream::Open(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: // If the previous file is open, close it michael@0: if (mFD) { michael@0: rv = Close(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // Open the file michael@0: if (aIOFlags == -1) michael@0: aIOFlags = PR_RDONLY; michael@0: if (aPerm == -1) michael@0: aPerm = 0; michael@0: michael@0: rv = MaybeOpen(aFile, aIOFlags, aPerm, michael@0: mBehaviorFlags & nsIFileInputStream::DEFER_OPEN); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (mBehaviorFlags & DELETE_ON_CLOSE) { michael@0: // POSIX compatible filesystems allow a file to be unlinked while a michael@0: // file descriptor is still referencing the file. since we've already michael@0: // opened the file descriptor, we'll try to remove the file. if that michael@0: // fails, then we'll just remember the nsIFile and remove it after we michael@0: // close the file descriptor. michael@0: rv = aFile->Remove(false); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // No need to remove it later. Clear the flag. michael@0: mBehaviorFlags &= ~DELETE_ON_CLOSE; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileInputStream::Init(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm, michael@0: int32_t aBehaviorFlags) michael@0: { michael@0: NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED); michael@0: NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED); michael@0: michael@0: mBehaviorFlags = aBehaviorFlags; michael@0: michael@0: mFile = aFile; michael@0: mIOFlags = aIOFlags; michael@0: mPerm = aPerm; michael@0: michael@0: return Open(aFile, aIOFlags, aPerm); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileInputStream::Close() michael@0: { michael@0: // Get the cache position at the time the file was close. This allows michael@0: // NS_SEEK_CUR on a closed file that has been opened with michael@0: // REOPEN_ON_REWIND. michael@0: if (mBehaviorFlags & REOPEN_ON_REWIND) { michael@0: // Get actual position. Not one modified by subclasses michael@0: nsFileStreamBase::Tell(&mCachedPosition); michael@0: } michael@0: michael@0: // null out mLineBuffer in case Close() is called again after failing michael@0: mLineBuffer = nullptr; michael@0: nsresult rv = nsFileStreamBase::Close(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (mFile && (mBehaviorFlags & DELETE_ON_CLOSE)) { michael@0: rv = mFile->Remove(false); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "failed to delete file"); michael@0: // If we don't need to save the file for reopening, free it up michael@0: if (!(mBehaviorFlags & REOPEN_ON_REWIND)) { michael@0: mFile = nullptr; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) michael@0: { michael@0: nsresult rv = nsFileStreamBase::Read(aBuf, aCount, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Check if we're at the end of file and need to close michael@0: if (mBehaviorFlags & CLOSE_ON_EOF && *_retval == 0) { michael@0: Close(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileInputStream::ReadLine(nsACString& aLine, bool* aResult) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!mLineBuffer) { michael@0: mLineBuffer = new nsLineBuffer; michael@0: } michael@0: return NS_ReadLine(this, mLineBuffer.get(), aLine, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileInputStream::Seek(int32_t aWhence, int64_t aOffset) michael@0: { michael@0: nsresult rv = DoPendingOpen(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mLineBuffer = nullptr; michael@0: if (!mFD) { michael@0: if (mBehaviorFlags & REOPEN_ON_REWIND) { michael@0: rv = Open(mFile, mIOFlags, mPerm); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // If the file was closed, and we do a relative seek, use the michael@0: // position we cached when we closed the file to seek to the right michael@0: // location. michael@0: if (aWhence == NS_SEEK_CUR) { michael@0: aWhence = NS_SEEK_SET; michael@0: aOffset += mCachedPosition; michael@0: } michael@0: } else { michael@0: return NS_BASE_STREAM_CLOSED; michael@0: } michael@0: } michael@0: michael@0: return nsFileStreamBase::Seek(aWhence, aOffset); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileInputStream::Tell(int64_t *aResult) michael@0: { michael@0: return nsFileStreamBase::Tell(aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileInputStream::Available(uint64_t *aResult) michael@0: { michael@0: return nsFileStreamBase::Available(aResult); michael@0: } michael@0: michael@0: void michael@0: nsFileInputStream::Serialize(InputStreamParams& aParams, michael@0: FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: FileInputStreamParams params; michael@0: michael@0: if (mFD) { michael@0: FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD)); michael@0: NS_ASSERTION(fd, "This should never be null!"); michael@0: michael@0: DebugOnly dbgFD = aFileDescriptors.AppendElement(fd); michael@0: NS_ASSERTION(dbgFD->IsValid(), "Sending an invalid file descriptor!"); michael@0: michael@0: params.fileDescriptorIndex() = aFileDescriptors.Length() - 1; michael@0: } else { michael@0: NS_WARNING("This file has not been opened (or could not be opened). " michael@0: "Sending an invalid file descriptor to the other process!"); michael@0: } michael@0: michael@0: int32_t behaviorFlags = mBehaviorFlags; michael@0: michael@0: // The other process shouldn't close when it reads the end because it will michael@0: // not be able to reopen the file later. michael@0: behaviorFlags &= ~nsIFileInputStream::CLOSE_ON_EOF; michael@0: michael@0: // The other process will not be able to reopen the file so transferring michael@0: // this flag is meaningless. michael@0: behaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND; michael@0: michael@0: // The other process is going to have an open file descriptor automatically michael@0: // so transferring this flag is meaningless. michael@0: behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN; michael@0: michael@0: params.behaviorFlags() = behaviorFlags; michael@0: params.ioFlags() = mIOFlags; michael@0: michael@0: aParams = params; michael@0: } michael@0: michael@0: bool michael@0: nsFileInputStream::Deserialize(const InputStreamParams& aParams, michael@0: const FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: NS_ASSERTION(!mFD, "Already have a file descriptor?!"); michael@0: NS_ASSERTION(!mDeferredOpen, "Deferring open?!"); michael@0: NS_ASSERTION(!mFile, "Should never have a file here!"); michael@0: NS_ASSERTION(!mPerm, "This should always be 0!"); michael@0: michael@0: if (aParams.type() != InputStreamParams::TFileInputStreamParams) { michael@0: NS_WARNING("Received unknown parameters from the other process!"); michael@0: return false; michael@0: } michael@0: michael@0: const FileInputStreamParams& params = aParams.get_FileInputStreamParams(); michael@0: michael@0: uint32_t fileDescriptorIndex = params.fileDescriptorIndex(); michael@0: michael@0: FileDescriptor fd; michael@0: if (fileDescriptorIndex < aFileDescriptors.Length()) { michael@0: fd = aFileDescriptors[fileDescriptorIndex]; michael@0: NS_WARN_IF_FALSE(fd.IsValid(), "Received an invalid file descriptor!"); michael@0: } else { michael@0: NS_WARNING("Received a bad file descriptor index!"); michael@0: } michael@0: michael@0: if (fd.IsValid()) { michael@0: PRFileDesc* fileDesc = PR_ImportFile(PROsfd(fd.PlatformHandle())); michael@0: if (!fileDesc) { michael@0: NS_WARNING("Failed to import file handle!"); michael@0: return false; michael@0: } michael@0: mFD = fileDesc; michael@0: } michael@0: michael@0: mBehaviorFlags = params.behaviorFlags(); michael@0: mIOFlags = params.ioFlags(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsPartialFileInputStream michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsPartialFileInputStream, nsFileStreamBase) michael@0: NS_IMPL_RELEASE_INHERITED(nsPartialFileInputStream, nsFileStreamBase) michael@0: michael@0: NS_IMPL_CLASSINFO(nsPartialFileInputStream, nullptr, nsIClassInfo::THREADSAFE, michael@0: NS_PARTIALLOCALFILEINPUTSTREAM_CID) michael@0: michael@0: // Don't forward to nsFileInputStream as we don't want to QI to michael@0: // nsIFileInputStream michael@0: NS_INTERFACE_MAP_BEGIN(nsPartialFileInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIPartialFileInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsILineInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) michael@0: NS_IMPL_QUERY_CLASSINFO(nsPartialFileInputStream) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase) michael@0: michael@0: NS_IMPL_CI_INTERFACE_GETTER(nsPartialFileInputStream, michael@0: nsIInputStream, michael@0: nsIPartialFileInputStream, michael@0: nsISeekableStream, michael@0: nsILineInputStream) michael@0: michael@0: nsresult michael@0: nsPartialFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, michael@0: void **aResult) michael@0: { michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: michael@0: nsPartialFileInputStream* stream = new nsPartialFileInputStream(); michael@0: michael@0: NS_ADDREF(stream); michael@0: nsresult rv = stream->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(stream); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPartialFileInputStream::Init(nsIFile* aFile, uint64_t aStart, michael@0: uint64_t aLength, int32_t aIOFlags, michael@0: int32_t aPerm, int32_t aBehaviorFlags) michael@0: { michael@0: mStart = aStart; michael@0: mLength = aLength; michael@0: mPosition = 0; michael@0: michael@0: nsresult rv = nsFileInputStream::Init(aFile, aIOFlags, aPerm, michael@0: aBehaviorFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return nsFileInputStream::Seek(NS_SEEK_SET, mStart); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPartialFileInputStream::Tell(int64_t *aResult) michael@0: { michael@0: int64_t tell = 0; michael@0: nsresult rv = nsFileInputStream::Tell(&tell); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *aResult = tell - mStart; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPartialFileInputStream::Available(uint64_t* aResult) michael@0: { michael@0: uint64_t available = 0; michael@0: nsresult rv = nsFileInputStream::Available(&available); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *aResult = TruncateSize(available); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPartialFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult) michael@0: { michael@0: uint32_t readsize = (uint32_t) TruncateSize(aCount); michael@0: if (readsize == 0 && mBehaviorFlags & CLOSE_ON_EOF) { michael@0: Close(); michael@0: *aResult = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv = nsFileInputStream::Read(aBuf, readsize, aResult); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mPosition += readsize; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPartialFileInputStream::Seek(int32_t aWhence, int64_t aOffset) michael@0: { michael@0: int64_t offset; michael@0: switch (aWhence) { michael@0: case NS_SEEK_SET: michael@0: offset = mStart + aOffset; michael@0: break; michael@0: case NS_SEEK_CUR: michael@0: offset = mStart + mPosition + aOffset; michael@0: break; michael@0: case NS_SEEK_END: michael@0: offset = mStart + mLength + aOffset; michael@0: break; michael@0: default: michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: if (offset < (int64_t)mStart || offset > (int64_t)(mStart + mLength)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsresult rv = nsFileInputStream::Seek(NS_SEEK_SET, offset); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mPosition = offset - mStart; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsPartialFileInputStream::Serialize(InputStreamParams& aParams, michael@0: FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: // Serialize the base class first. michael@0: InputStreamParams fileParams; michael@0: nsFileInputStream::Serialize(fileParams, aFileDescriptors); michael@0: michael@0: PartialFileInputStreamParams params; michael@0: michael@0: params.fileStreamParams() = fileParams.get_FileInputStreamParams(); michael@0: params.begin() = mStart; michael@0: params.length() = mLength; michael@0: michael@0: aParams = params; michael@0: } michael@0: michael@0: bool michael@0: nsPartialFileInputStream::Deserialize( michael@0: const InputStreamParams& aParams, michael@0: const FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: NS_ASSERTION(!mFD, "Already have a file descriptor?!"); michael@0: NS_ASSERTION(!mStart, "Already have a start?!"); michael@0: NS_ASSERTION(!mLength, "Already have a length?!"); michael@0: NS_ASSERTION(!mPosition, "Already have a position?!"); michael@0: michael@0: if (aParams.type() != InputStreamParams::TPartialFileInputStreamParams) { michael@0: NS_WARNING("Received unknown parameters from the other process!"); michael@0: return false; michael@0: } michael@0: michael@0: const PartialFileInputStreamParams& params = michael@0: aParams.get_PartialFileInputStreamParams(); michael@0: michael@0: // Deserialize the base class first. michael@0: InputStreamParams fileParams(params.fileStreamParams()); michael@0: if (!nsFileInputStream::Deserialize(fileParams, aFileDescriptors)) { michael@0: NS_WARNING("Base class deserialize failed!"); michael@0: return false; michael@0: } michael@0: michael@0: NS_ASSERTION(mFD, "Must have a file descriptor now!"); michael@0: michael@0: mStart = params.begin(); michael@0: mLength = params.length(); michael@0: mPosition = 0; michael@0: michael@0: if (!mStart) { michael@0: return true; michael@0: } michael@0: michael@0: // XXX This is so broken. Main thread IO alert. michael@0: return NS_SUCCEEDED(nsFileInputStream::Seek(NS_SEEK_SET, mStart)); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsFileOutputStream michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream, michael@0: nsFileStreamBase, michael@0: nsIOutputStream, michael@0: nsIFileOutputStream) michael@0: michael@0: nsresult michael@0: nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: michael@0: nsFileOutputStream* stream = new nsFileOutputStream(); michael@0: if (stream == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(stream); michael@0: nsresult rv = stream->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(stream); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm, michael@0: int32_t behaviorFlags) michael@0: { michael@0: NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); michael@0: NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED); michael@0: michael@0: mBehaviorFlags = behaviorFlags; michael@0: michael@0: if (ioFlags == -1) michael@0: ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE; michael@0: if (perm <= 0) michael@0: perm = 0664; michael@0: michael@0: return MaybeOpen(file, ioFlags, perm, michael@0: mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsAtomicFileOutputStream michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsAtomicFileOutputStream, michael@0: nsFileOutputStream, michael@0: nsISafeOutputStream, michael@0: nsIOutputStream, michael@0: nsIFileOutputStream) michael@0: michael@0: NS_IMETHODIMP michael@0: nsAtomicFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm, michael@0: int32_t behaviorFlags) michael@0: { michael@0: return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags); michael@0: } michael@0: michael@0: nsresult michael@0: nsAtomicFileOutputStream::DoOpen() michael@0: { michael@0: // Make sure mOpenParams.localFile will be empty if we bail somewhere in michael@0: // this function michael@0: nsCOMPtr file; michael@0: file.swap(mOpenParams.localFile); michael@0: michael@0: nsresult rv = file->Exists(&mTargetFileExists); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("Can't tell if target file exists"); michael@0: mTargetFileExists = true; // Safer to assume it exists - we just do more work. michael@0: } michael@0: michael@0: // follow symlinks, for two reasons: michael@0: // 1) if a user has deliberately set up a profile file as a symlink, we honor it michael@0: // 2) to make the MoveToNative() in Finish() an atomic operation (which may not michael@0: // be the case if moving across directories on different filesystems). michael@0: nsCOMPtr tempResult; michael@0: rv = file->Clone(getter_AddRefs(tempResult)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: tempResult->SetFollowLinks(true); michael@0: michael@0: // XP_UNIX ignores SetFollowLinks(), so we have to normalize. michael@0: tempResult->Normalize(); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv) && mTargetFileExists) { michael@0: uint32_t origPerm; michael@0: if (NS_FAILED(file->GetPermissions(&origPerm))) { michael@0: NS_ERROR("Can't get permissions of target file"); michael@0: origPerm = mOpenParams.perm; michael@0: } michael@0: // XXX What if |perm| is more restrictive then |origPerm|? michael@0: // This leaves the user supplied permissions as they were. michael@0: rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm); michael@0: } michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // nsFileOutputStream::DoOpen will work on the temporary file, so we michael@0: // prepare it and place it in mOpenParams.localFile. michael@0: mOpenParams.localFile = tempResult; michael@0: mTempFile = tempResult; michael@0: mTargetFile = file; michael@0: rv = nsFileOutputStream::DoOpen(); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAtomicFileOutputStream::Close() michael@0: { michael@0: nsresult rv = nsFileOutputStream::Close(); michael@0: michael@0: // the consumer doesn't want the original file overwritten - michael@0: // so clean up by removing the temp file. michael@0: if (mTempFile) { michael@0: mTempFile->Remove(false); michael@0: mTempFile = nullptr; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAtomicFileOutputStream::Finish() michael@0: { michael@0: nsresult rv = nsFileOutputStream::Close(); michael@0: michael@0: // if there is no temp file, don't try to move it over the original target. michael@0: // It would destroy the targetfile if close() is called twice. michael@0: if (!mTempFile) michael@0: return rv; michael@0: michael@0: // Only overwrite if everything was ok, and the temp file could be closed. michael@0: if (NS_SUCCEEDED(mWriteResult) && NS_SUCCEEDED(rv)) { michael@0: NS_ENSURE_STATE(mTargetFile); michael@0: michael@0: if (!mTargetFileExists) { michael@0: // If the target file did not exist when we were initialized, then the michael@0: // temp file we gave out was actually a reference to the target file. michael@0: // since we succeeded in writing to the temp file (and hence succeeded michael@0: // in writing to the target file), there is nothing more to do. michael@0: #ifdef DEBUG michael@0: bool equal; michael@0: if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal) michael@0: NS_ERROR("mTempFile not equal to mTargetFile"); michael@0: #endif michael@0: } michael@0: else { michael@0: nsAutoString targetFilename; michael@0: rv = mTargetFile->GetLeafName(targetFilename); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // This will replace target. michael@0: rv = mTempFile->MoveTo(nullptr, targetFilename); michael@0: if (NS_FAILED(rv)) michael@0: mTempFile->Remove(false); michael@0: } michael@0: } michael@0: } michael@0: else { michael@0: mTempFile->Remove(false); michael@0: michael@0: // if writing failed, propagate the failure code to the caller. michael@0: if (NS_FAILED(mWriteResult)) michael@0: rv = mWriteResult; michael@0: } michael@0: mTempFile = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAtomicFileOutputStream::Write(const char *buf, uint32_t count, uint32_t *result) michael@0: { michael@0: nsresult rv = nsFileOutputStream::Write(buf, count, result); michael@0: if (NS_SUCCEEDED(mWriteResult)) { michael@0: if (NS_FAILED(rv)) michael@0: mWriteResult = rv; michael@0: else if (count != *result) michael@0: mWriteResult = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; michael@0: michael@0: if (NS_FAILED(mWriteResult) && count > 0) michael@0: NS_WARNING("writing to output stream failed! data may be lost"); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsSafeFileOutputStream michael@0: michael@0: NS_IMETHODIMP michael@0: nsSafeFileOutputStream::Finish() michael@0: { michael@0: (void) Flush(); michael@0: return nsAtomicFileOutputStream::Finish(); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsFileStream michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsFileStream, michael@0: nsFileStreamBase, michael@0: nsIInputStream, michael@0: nsIOutputStream, michael@0: nsIFileStream) michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm, michael@0: int32_t behaviorFlags) michael@0: { michael@0: NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); michael@0: NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED); michael@0: michael@0: mBehaviorFlags = behaviorFlags; michael@0: michael@0: if (ioFlags == -1) michael@0: ioFlags = PR_RDWR; michael@0: if (perm <= 0) michael@0: perm = 0; michael@0: michael@0: return MaybeOpen(file, ioFlags, perm, michael@0: mBehaviorFlags & nsIFileStream::DEFER_OPEN); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////////