summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorDavid Faure <faure+bluesystems@kde.org>2013-03-24 11:10:21 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-04-05 11:04:35 +0200
commitb3a505dc924fb26fcf68bb2016b6f5ea206a946b (patch)
tree2d30bcf1a307de8b6c0b967585e9fd241649286d /src/corelib
parentef061b76b1f4eb4e9657933039873221d6380541 (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.cpp101
-rw-r--r--src/corelib/io/qsavefile.h3
-rw-r--r--src/corelib/io/qsavefile_p.h3
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