/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include"ipc/IPCMessageUtils.h"
#ifdefined(XP_UNIX) # include <unistd.h> #elifdefined(XP_WIN) # include <windows.h> # include "nsILocalFileWin.h" #else // XXX add necessary include file for ftruncate (or equivalent) #endif
nsFileStreamBase::~nsFileStreamBase() { // We don't want to try to rewrind the stream when shutting down.
mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
// PR_Available with files over 4GB returns an error, so we have to // use the 64-bit version of PR_Available.
int64_t avail = PR_Available64(mFD); if (avail == -1) { return NS_ErrorAccordingToNSPR();
}
// If available is greater than 4GB, return 4GB
*aResult = (uint64_t)avail; return NS_OK;
}
nsresult nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
uint32_t* aResult) { // ReadSegments is not implemented because it would be inefficient when // the writer does not consume all data. If you want to call ReadSegments, // wrap a BufferedInputStream around the file stream. That will call // Read().
nsresult nsFileStreamBase::WriteFrom(nsIInputStream* inStr, uint32_t count,
uint32_t* _retval) {
MOZ_ASSERT_UNREACHABLE("WriteFrom (see source comment)"); return NS_ERROR_NOT_IMPLEMENTED; // File streams intentionally do not support this method. // If you need something like this, then you should wrap // the file stream using nsIBufferedOutputStream
}
nsresult nsFileStreamBase::WriteSegments(nsReadSegmentFun reader, void* closure,
uint32_t count, uint32_t* _retval) { return NS_ERROR_NOT_IMPLEMENTED; // File streams intentionally do not support this method. // If you need something like this, then you should wrap // the file stream using nsIBufferedOutputStream
}
if (aDeferred) { // Clone the file, as it may change between now and the deferred open
nsCOMPtr<nsIFile> file;
nsresult rv = aFile->Clone(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
nsresult nsFileStreamBase::DoOpen() {
MOZ_ASSERT(mState == eDeferredOpen || mState == eUnitialized ||
mState == eClosed);
NS_ASSERTION(!mFD, "Already have a file descriptor!");
NS_ASSERTION(mOpenParams.localFile, "Must have a file to open");
PRFileDesc* fd;
nsresult rv;
if (mOpenParams.ioFlags & PR_CREATE_FILE) {
nsCOMPtr<nsIFile> parent;
mOpenParams.localFile->GetParent(getter_AddRefs(parent));
// Result doesn't need to be checked. If the file's parent path does not // exist, make it. If it does exist, do nothing. if (parent) {
mozilla::Unused << parent->Create(nsIFile::DIRECTORY_TYPE, 0755);
}
}
NS_IMETHODIMP
nsFileInputStream::Close() { // If this stream has already been closed, do nothing. if (mState == eClosed) { return NS_OK;
}
// Get the cache position at the time the file was close. This allows // NS_SEEK_CUR on a closed file that has been opened with // REOPEN_ON_REWIND. if (mBehaviorFlags & REOPEN_ON_REWIND) { // Get actual position. Not one modified by subclasses
nsFileStreamBase::Tell(&mCachedPosition);
}
// explicitly clear mLineBuffer in case this stream is reopened
mLineBuffer = nullptr; return nsFileStreamBase::Close();
}
NS_IMETHODIMP
nsFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
nsresult rv = nsFileStreamBase::Read(aBuf, aCount, _retval); if (rv == NS_ERROR_FILE_NOT_FOUND) { // Don't warn if this is a deffered file not found. return rv;
}
if (NS_FAILED(rv)) { return rv;
}
// Check if we're at the end of file and need to close if (mBehaviorFlags & CLOSE_ON_EOF && *_retval == 0) {
Close();
}
if (rv == NS_BASE_STREAM_CLOSED) { if (mBehaviorFlags & REOPEN_ON_REWIND) {
rv = Open(mFile, mIOFlags, mPerm);
NS_ENSURE_SUCCESS(rv, rv);
// If the file was closed, and we do a relative seek, use the // position we cached when we closed the file to seek to the right // location. if (aWhence == NS_SEEK_CUR) {
aWhence = NS_SEEK_SET;
aOffset += mCachedPosition;
} // If we're trying to seek to the start then we're done, so // return early to avoid Seek from calling DoPendingOpen and // opening the underlying file earlier than necessary. if (aWhence == NS_SEEK_SET && aOffset == 0) { return NS_OK;
}
} else { return NS_BASE_STREAM_CLOSED;
}
}
if (NS_SUCCEEDED(DoPendingOpen())) {
MOZ_ASSERT(mFD);
FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
NS_ASSERTION(fd, "This should never be null!");
params.fileDescriptor() = FileDescriptor(fd);
Close();
} else {
NS_WARNING( "This file has not been opened (or could not be opened). " "Sending an invalid file descriptor to the other process!");
params.fileDescriptor() = FileDescriptor();
}
int32_t behaviorFlags = mBehaviorFlags;
// The receiving process (or thread) is going to have an open file // descriptor automatically so transferring this flag is meaningless.
behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN;
bool nsFileInputStream::Deserialize(const InputStreamParams& aParams) {
NS_ASSERTION(!mFD, "Already have a file descriptor?!");
NS_ASSERTION(mState == nsFileStreamBase::eUnitialized, "Deferring open?!");
NS_ASSERTION(!mFile, "Should never have a file here!");
NS_ASSERTION(!mPerm, "This should always be 0!");
if (aParams.type() != InputStreamParams::TFileInputStreamParams) {
NS_WARNING("Received unknown parameters from the other process!"); returnfalse;
}
if (fd.IsValid()) { auto rawFD = fd.ClonePlatformHandle();
PRFileDesc* fileDesc = PR_ImportFile(PROsfd(rawFD.release())); if (!fileDesc) {
NS_WARNING("Failed to import file handle!"); returnfalse;
}
mFD = fileDesc;
mState = eOpened;
} else {
NS_WARNING("Received an invalid file descriptor!");
mState = eError;
mErrorValue = NS_ERROR_FILE_NOT_FOUND;
}
mBehaviorFlags = params.behaviorFlags();
if (!XRE_IsParentProcess()) { // A child process shouldn't close when it reads the end because it will // not be able to reopen the file later.
mBehaviorFlags &= ~nsIFileInputStream::CLOSE_ON_EOF;
// A child process will not be able to reopen the file so this flag is // meaningless.
mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
}
mIOFlags = params.ioFlags();
returntrue;
}
bool nsFileInputStream::IsCloneable() const { // This inputStream is cloneable only if has been created using Init() and // it owns a nsIFile. This is not true when it is deserialized from IPC. return XRE_IsParentProcess() && mFile;
}
NS_IMETHODIMP
nsAtomicFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
int32_t behaviorFlags) { // While `PR_APPEND` is not supported, `-1` is used as `ioFlags` parameter // in some places, and `PR_APPEND | PR_TRUNCATE` does not require appending // to existing file. So, throw an exception only if `PR_APPEND` is // explicitly specified without `PR_TRUNCATE`. if ((ioFlags & PR_APPEND) && !(ioFlags & PR_TRUNCATE)) { return NS_ERROR_INVALID_ARG;
} return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
}
nsresult nsAtomicFileOutputStream::DoOpen() { // Make sure mOpenParams.localFile will be empty if we bail somewhere in // this function
nsCOMPtr<nsIFile> file;
file.swap(mOpenParams.localFile);
if (!file) { return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = file->Exists(&mTargetFileExists); if (NS_FAILED(rv)) {
NS_ERROR("Can't tell if target file exists");
mTargetFileExists = true; // Safer to assume it exists - we just do more work.
}
// follow symlinks, for two reasons: // 1) if a user has deliberately set up a profile file as a symlink, we // honor it // 2) to make the MoveToNative() in Finish() an atomic operation (which may // not be the case if moving across directories on different // filesystems).
nsCOMPtr<nsIFile> tempResult;
rv = file->Clone(getter_AddRefs(tempResult)); if (NS_SUCCEEDED(rv) && mTargetFileExists) {
tempResult->Normalize();
}
if (NS_SUCCEEDED(rv) && mTargetFileExists) { // Abort if |file| is not writable; it won't work as an output stream. bool isWritable; if (NS_SUCCEEDED(file->IsWritable(&isWritable)) && !isWritable) { return NS_ERROR_FILE_ACCESS_DENIED;
}
uint32_t origPerm; if (NS_FAILED(file->GetPermissions(&origPerm))) {
NS_ERROR("Can't get permissions of target file");
origPerm = mOpenParams.perm;
}
// XXX What if |perm| is more restrictive then |origPerm|? // This leaves the user supplied permissions as they were.
rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
} if (NS_SUCCEEDED(rv)) { // nsFileOutputStream::DoOpen will work on the temporary file, so we // prepare it and place it in mOpenParams.localFile.
mOpenParams.localFile = tempResult;
mTempFile = tempResult;
mTargetFile = file;
rv = nsFileOutputStream::DoOpen();
} return rv;
}
// the consumer doesn't want the original file overwritten - // so clean up by removing the temp file. if (mTempFile) {
mTempFile->Remove(false);
mTempFile = nullptr;
}
// if there is no temp file, don't try to move it over the original target. // It would destroy the targetfile if close() is called twice. if (!mTempFile) return rv;
// Only overwrite if everything was ok, and the temp file could be closed. if (NS_SUCCEEDED(mWriteResult) && NS_SUCCEEDED(rv)) {
NS_ENSURE_STATE(mTargetFile);
if (!mTargetFileExists) { // If the target file did not exist when we were initialized, then the // temp file we gave out was actually a reference to the target file. // since we succeeded in writing to the temp file (and hence succeeded // in writing to the target file), there is nothing more to do. #ifdef DEBUG bool equal; if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal) {
NS_WARNING("mTempFile not equal to mTargetFile");
} #endif
} else {
nsAutoString targetFilename;
rv = mTargetFile->GetLeafName(targetFilename); if (NS_SUCCEEDED(rv)) { // This will replace target.
rv = mTempFile->MoveTo(nullptr, targetFilename); if (NS_FAILED(rv)) mTempFile->Remove(false);
}
}
} else {
mTempFile->Remove(false);
// if writing failed, propagate the failure code to the caller. if (NS_FAILED(mWriteResult)) rv = mWriteResult;
}
mTempFile = nullptr; return rv;
}
if (NS_SUCCEEDED(DoPendingOpen())) {
MOZ_ASSERT(mFD);
FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
MOZ_ASSERT(fd, "This should never be null!");
params.fileDescriptor() = FileDescriptor(fd);
Close();
} else {
NS_WARNING( "This file has not been opened (or could not be opened). " "Sending an invalid file descriptor to the other process!");
params.fileDescriptor() = FileDescriptor();
}
int32_t behaviorFlags = mBehaviorFlags;
// The receiving process (or thread) is going to have an open file // descriptor automatically so transferring this flag is meaningless.
behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN;
params.behaviorFlags() = behaviorFlags;
return params;
}
bool nsFileRandomAccessStream::Deserialize(
RandomAccessStreamParams& aStreamParams) {
MOZ_ASSERT(!mFD, "Already have a file descriptor?!");
MOZ_ASSERT(mState == nsFileStreamBase::eUnitialized, "Deferring open?!");
if (aStreamParams.type() !=
RandomAccessStreamParams::TFileRandomAccessStreamParams) {
NS_WARNING("Received unknown parameters from the other process!"); returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.