diff options
author | David Faure <faure+bluesystems@kde.org> | 2013-03-24 11:10:21 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-04-05 11:04:35 +0200 |
commit | b3a505dc924fb26fcf68bb2016b6f5ea206a946b (patch) | |
tree | 2d30bcf1a307de8b6c0b967585e9fd241649286d /src/corelib | |
parent | ef061b76b1f4eb4e9657933039873221d6380541 (diff) |
QSaveFile: allow saving to a writable file in a non-writable directory
The only way to make this possible is to disable the
atomic-rename-from-temp-file behavior. This is not done by default,
but only if the application allows this to happen.
https://bugs.kde.org/show_bug.cgi?id=312415
Change-Id: I71ce54ae1f7f50ab5e8379f04c0ede74ebe3136d
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/io/qsavefile.cpp | 101 | ||||
-rw-r--r-- | src/corelib/io/qsavefile.h | 3 | ||||
-rw-r--r-- | src/corelib/io/qsavefile_p.h | 3 |
3 files changed, 90 insertions, 17 deletions
diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp index fee6a4c4d8..f8b5ebcabd 100644 --- a/src/corelib/io/qsavefile.cpp +++ b/src/corelib/io/qsavefile.cpp @@ -48,11 +48,16 @@ #include "qtemporaryfile.h" #include "private/qiodevice_p.h" #include "private/qtemporaryfile_p.h" +#ifdef Q_OS_UNIX +#include <errno.h> +#endif QT_BEGIN_NAMESPACE QSaveFilePrivate::QSaveFilePrivate() - : writeError(QFileDevice::NoError) + : writeError(QFileDevice::NoError), + useTemporaryFile(true), + directWriteFallback(false) { } @@ -201,6 +206,18 @@ bool QSaveFile::open(OpenMode mode) // Same as in QFile: QIODevice provides the buffering, so there's no need to request it from the file engine. if (!d->fileEngine->open(mode | QIODevice::Unbuffered)) { QFileDevice::FileError err = d->fileEngine->error(); +#ifdef Q_OS_UNIX + if (d->directWriteFallback && err == QFileDevice::OpenError && errno == EACCES) { + delete d->fileEngine; + d->fileEngine = QAbstractFileEngine::create(d->fileName); + if (d->fileEngine->open(mode | QIODevice::Unbuffered)) { + d->useTemporaryFile = false; + QFileDevice::open(mode); + return true; + } + err = d->fileEngine->error(); + } +#endif if (err == QFileDevice::UnspecifiedError) err = QFileDevice::OpenError; d->setError(err, d->fileEngine->errorString()); @@ -209,6 +226,7 @@ bool QSaveFile::open(OpenMode mode) return false; } + d->useTemporaryFile = true; QFileDevice::open(mode); if (existingFile.exists()) setPermissions(existingFile.permissions()); @@ -253,22 +271,24 @@ bool QSaveFile::commit() // Sync to disk if possible. Ignore errors (e.g. not supported). d->fileEngine->syncToDisk(); - if (d->writeError != QFileDevice::NoError) { - d->fileEngine->remove(); - d->writeError = QFileDevice::NoError; - delete d->fileEngine; - d->fileEngine = 0; - return false; - } - // atomically replace old file with new file - // Can't use QFile::rename for that, must use the file engine directly - Q_ASSERT(d->fileEngine); - if (!d->fileEngine->renameOverwrite(d->fileName)) { - d->setError(d->fileEngine->error(), d->fileEngine->errorString()); - d->fileEngine->remove(); - delete d->fileEngine; - d->fileEngine = 0; - return false; + if (d->useTemporaryFile) { + if (d->writeError != QFileDevice::NoError) { + d->fileEngine->remove(); + d->writeError = QFileDevice::NoError; + delete d->fileEngine; + d->fileEngine = 0; + return false; + } + // atomically replace old file with new file + // Can't use QFile::rename for that, must use the file engine directly + Q_ASSERT(d->fileEngine); + if (!d->fileEngine->renameOverwrite(d->fileName)) { + d->setError(d->fileEngine->error(), d->fileEngine->errorString()); + d->fileEngine->remove(); + delete d->fileEngine; + d->fileEngine = 0; + return false; + } } delete d->fileEngine; d->fileEngine = 0; @@ -286,6 +306,11 @@ bool QSaveFile::commit() Further write operations are possible after calling this method, but none of it will have any effect, the written file will be discarded. + This method has no effect when direct write fallback is used. This is the case + when saving over an existing file in a readonly directory: no temporary file can + be created, so the existing file is overwritten no matter what, and cancelWriting() + cannot do anything about that, the contents of the existing file will be lost. + \sa commit() */ void QSaveFile::cancelWriting() @@ -313,4 +338,46 @@ qint64 QSaveFile::writeData(const char *data, qint64 len) return ret; } +/*! + Allows writing over the existing file if necessary. + + QSaveFile creates a temporary file in the same directory as the final + file and atomically renames it. However this is not possible if the + directory permissions do not allow creating new files. + In order to preserve atomicity guarantees, open() fails when it + cannot create the temporary file. + + In order to allow users to edit files with write permissions in a + directory with restricted permissions, call setDirectWriteFallback() with + \a enabled set to true, and the following calls to open() will fallback to + opening the existing file directly and writing into it, without the use of + a temporary file. + This does not have atomicity guarantees, i.e. an application crash or + for instance a power failure could lead to a partially-written file on disk. + It also means cancelWriting() has no effect, in such a case. + + Typically, to save documents edited by the user, call setDirectWriteFallback(true), + and to save application internal files (configuration files, data files, ...), keep + the default setting which ensures atomicity. + + \sa directWriteFallback() +*/ +void QSaveFile::setDirectWriteFallback(bool enabled) +{ + Q_D(QSaveFile); + d->directWriteFallback = enabled; +} + +/*! + Returns true if the fallback solution for saving files in read-only + directories is enabled. + + \sa setDirectWriteFallback() +*/ +bool QSaveFile::directWriteFallback() const +{ + Q_D(const QSaveFile); + return d->directWriteFallback; +} + QT_END_NAMESPACE diff --git a/src/corelib/io/qsavefile.h b/src/corelib/io/qsavefile.h index 32af4a708e..6d81f58d42 100644 --- a/src/corelib/io/qsavefile.h +++ b/src/corelib/io/qsavefile.h @@ -75,6 +75,9 @@ public: void cancelWriting(); + void setDirectWriteFallback(bool enabled); + bool directWriteFallback() const; + protected: qint64 writeData(const char *data, qint64 len) Q_DECL_OVERRIDE; diff --git a/src/corelib/io/qsavefile_p.h b/src/corelib/io/qsavefile_p.h index 27e687835b..53a8b5eb34 100644 --- a/src/corelib/io/qsavefile_p.h +++ b/src/corelib/io/qsavefile_p.h @@ -68,6 +68,9 @@ protected: QString fileName; QFileDevice::FileError writeError; + + bool useTemporaryFile; + bool directWriteFallback; }; QT_END_NAMESPACE |