Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsavefile.cpp
Go to the documentation of this file.
1// Copyright (C) 2012 David Faure <faure@kde.org>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsavefile.h"
5
6#if QT_CONFIG(temporaryfile)
7
8#include "qplatformdefs.h"
9#include "private/qsavefile_p.h"
10#include "qfileinfo.h"
12#include "qdebug.h"
13#include "qtemporaryfile.h"
14#include "private/qiodevice_p.h"
15#include "private/qtemporaryfile_p.h"
16#ifdef Q_OS_UNIX
17#include <errno.h>
18#endif
19
21
22using namespace Qt::StringLiterals;
23
24QSaveFilePrivate::QSaveFilePrivate()
25 : writeError(QFileDevice::NoError),
26 useTemporaryFile(true),
27 directWriteFallback(false)
28{
29}
30
31QSaveFilePrivate::~QSaveFilePrivate()
32{
33}
34
73#ifdef QT_NO_QOBJECT
74QSaveFile::QSaveFile(const QString &name)
75 : QFileDevice(*new QSaveFilePrivate)
76{
77 Q_D(QSaveFile);
78 d->fileName = name;
79}
80#else
84QSaveFile::QSaveFile(const QString &name)
85 : QFileDevice(*new QSaveFilePrivate, nullptr)
86{
87 Q_D(QSaveFile);
88 d->fileName = name;
89}
90
94QSaveFile::QSaveFile(QObject *parent)
95 : QFileDevice(*new QSaveFilePrivate, parent)
96{
97}
102QSaveFile::QSaveFile(const QString &name, QObject *parent)
103 : QFileDevice(*new QSaveFilePrivate, parent)
104{
105 Q_D(QSaveFile);
106 d->fileName = name;
107}
108#endif
109
113QSaveFile::~QSaveFile()
114{
115 Q_D(QSaveFile);
116 if (isOpen()) {
118 Q_ASSERT(d->fileEngine);
119 d->fileEngine->remove();
120 }
121}
122
129QString QSaveFile::fileName() const
130{
131 return d_func()->fileName;
132}
133
140void QSaveFile::setFileName(const QString &name)
141{
142 d_func()->fileName = name;
143}
144
157bool QSaveFile::open(OpenMode mode)
158{
159 Q_D(QSaveFile);
160 if (isOpen()) {
161 qWarning("QSaveFile::open: File (%ls) already open", qUtf16Printable(fileName()));
162 return false;
163 }
164 unsetError();
165 d->writeError = QFileDevice::NoError;
166 if ((mode & (ReadOnly | WriteOnly)) == 0) {
167 qWarning("QSaveFile::open: Open mode not specified");
168 return false;
169 }
170 // In the future we could implement ReadWrite by copying from the existing file to the temp file...
171 // The implications of NewOnly and ExistingOnly when used with QSaveFile need to be considered carefully...
172 if (mode & (ReadOnly | Append | NewOnly | ExistingOnly)) {
173 qWarning("QSaveFile::open: Unsupported open mode 0x%x", uint(mode.toInt()));
174 return false;
175 }
176
177 // check if existing file is writable
178 QFileInfo existingFile(d->fileName);
179 if (existingFile.exists() && !existingFile.isWritable()) {
180 d->setError(QFileDevice::WriteError, QSaveFile::tr("Existing file %1 is not writable").arg(d->fileName));
181 d->writeError = QFileDevice::WriteError;
182 return false;
183 }
184
185 if (existingFile.isDir()) {
186 d->setError(QFileDevice::WriteError, QSaveFile::tr("Filename refers to a directory"));
187 d->writeError = QFileDevice::WriteError;
188 return false;
189 }
190
191 // Resolve symlinks. Don't use QFileInfo::canonicalFilePath so it still give the expected
192 // target even if the file does not exist
193 d->finalFileName = d->fileName;
194 if (existingFile.isSymLink()) {
195 int maxDepth = 128;
196 while (--maxDepth && existingFile.isSymLink())
197 existingFile.setFile(existingFile.symLinkTarget());
198 if (maxDepth > 0)
199 d->finalFileName = existingFile.filePath();
200 }
201
202 auto openDirectly = [&]() {
203 d->fileEngine = QAbstractFileEngine::create(d->finalFileName);
204 if (d->fileEngine->open(mode | QIODevice::Unbuffered)) {
205 d->useTemporaryFile = false;
207 return true;
208 }
209 return false;
210 };
211
212 bool requiresDirectWrite = false;
213#ifdef Q_OS_WIN
214 // check if it is an Alternate Data Stream
215 requiresDirectWrite = d->finalFileName == d->fileName && d->fileName.indexOf(u':', 2) > 1;
216#elif defined(Q_OS_ANDROID)
217 // check if it is a content:// URL
218 requiresDirectWrite = d->fileName.startsWith("content://"_L1);
219#endif
220 if (requiresDirectWrite) {
221 // yes, we can't rename onto it...
222 if (d->directWriteFallback) {
223 if (openDirectly())
224 return true;
225 d->setError(d->fileEngine->error(), d->fileEngine->errorString());
226 d->fileEngine.reset();
227 } else {
228 QString msg =
229 QSaveFile::tr("QSaveFile cannot open '%1' without direct write fallback enabled.")
230 .arg(QDir::toNativeSeparators(d->fileName));
231 d->setError(QFileDevice::OpenError, msg);
232 }
233 return false;
234 }
235
236 d->fileEngine.reset(new QTemporaryFileEngine(&d->finalFileName, QTemporaryFileEngine::Win32NonShared));
237 // if the target file exists, we'll copy its permissions below,
238 // but until then, let's ensure the temporary file is not accessible
239 // to a third party
240 int perm = (existingFile.exists() ? 0600 : 0666);
241 static_cast<QTemporaryFileEngine *>(d->fileEngine.get())->initialize(d->finalFileName, perm);
242 // Same as in QFile: QIODevice provides the buffering, so there's no need to request it from the file engine.
243 if (!d->fileEngine->open(mode | QIODevice::Unbuffered)) {
244 QFileDevice::FileError err = d->fileEngine->error();
245#ifdef Q_OS_UNIX
246 if (d->directWriteFallback && err == QFileDevice::OpenError && errno == EACCES) {
247 if (openDirectly())
248 return true;
249 err = d->fileEngine->error();
250 }
251#endif
254 d->setError(err, d->fileEngine->errorString());
255 d->fileEngine.reset();
256 return false;
257 }
258
259 d->useTemporaryFile = true;
261 if (existingFile.exists())
262 setPermissions(existingFile.permissions());
263 return true;
264}
265
272void QSaveFile::close()
273{
274 qFatal("QSaveFile::close called");
275}
276
289bool QSaveFile::commit()
290{
291 Q_D(QSaveFile);
292 if (!d->fileEngine)
293 return false;
294
295 if (!isOpen()) {
296 qWarning("QSaveFile::commit: File (%ls) is not open", qUtf16Printable(fileName()));
297 return false;
298 }
299 QFileDevice::close(); // calls flush()
300
301 const auto &fe = d->fileEngine;
302
303 // Sync to disk if possible. Ignore errors (e.g. not supported).
304 fe->syncToDisk();
305
306 if (d->useTemporaryFile) {
307 if (d->writeError != QFileDevice::NoError) {
308 fe->remove();
309 d->writeError = QFileDevice::NoError;
310 return false;
311 }
312 // atomically replace old file with new file
313 // Can't use QFile::rename for that, must use the file engine directly
314 Q_ASSERT(fe);
315 if (!fe->renameOverwrite(d->finalFileName)) {
316 d->setError(fe->error(), fe->errorString());
317 fe->remove();
318 return false;
319 }
320 }
321 return true;
322}
323
342void QSaveFile::cancelWriting()
343{
344 Q_D(QSaveFile);
345 if (!isOpen())
346 return;
347 d->setError(QFileDevice::WriteError, QSaveFile::tr("Writing canceled by application"));
348 d->writeError = QFileDevice::WriteError;
349}
350
354qint64 QSaveFile::writeData(const char *data, qint64 len)
355{
356 Q_D(QSaveFile);
357 if (d->writeError != QFileDevice::NoError)
358 return -1;
359
361
362 if (d->error != QFileDevice::NoError)
363 d->writeError = d->error;
364 return ret;
365}
366
391void QSaveFile::setDirectWriteFallback(bool enabled)
392{
393 Q_D(QSaveFile);
394 d->directWriteFallback = enabled;
395}
396
403bool QSaveFile::directWriteFallback() const
404{
405 Q_D(const QSaveFile);
406 return d->directWriteFallback;
407}
408
410
411#ifndef QT_NO_QOBJECT
412#include "moc_qsavefile.cpp"
413#endif
414
415#endif // QT_CONFIG(temporaryfile)
static std::unique_ptr< QAbstractFileEngine > create(const QString &fileName)
Creates and returns a QAbstractFileEngine suitable for processing fileName.
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
\inmodule QtCore
Definition qfiledevice.h:32
qint64 writeData(const char *data, qint64 len) override
\reimp
FileError
This enum describes the errors that may be returned by the error() function.
Definition qfiledevice.h:39
void close() override
Calls QFileDevice::flush() and closes the file.
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
\inmodule QtCore
Definition qobject.h:103
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
Combined button and popup list for selecting options.
static bool initialize()
Definition qctf.cpp:94
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
return ret
GLenum mode
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLuint name
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
#define qUtf16Printable(string)
Definition qstring.h:1543
@ NoError
Definition main.cpp:34
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
#define enabled
QObject::connect nullptr