Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtemporaryfile.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2017 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
7
8#include "qplatformdefs.h"
9#include "qrandom.h"
10#include "private/qtemporaryfile_p.h"
11#include "private/qfile_p.h"
12#include "private/qsystemerror_p.h"
13
14#if !defined(Q_OS_WIN)
15#include "private/qcore_unix_p.h" // overrides QT_OPEN
16#include <errno.h>
17#endif
18
19#if defined(QT_BUILD_CORE_LIB)
20#include "qcoreapplication.h"
21#else
22#define tr(X) QString::fromLatin1(X)
23#endif
24
26
27using namespace Qt::StringLiterals;
28
29#if defined(Q_OS_WIN)
30typedef ushort Char;
31
32static inline Char Latin1Char(char ch)
33{
34 return ushort(uchar(ch));
35}
36
37typedef HANDLE NativeFileHandle;
38
39#else // POSIX
40typedef char Char;
41typedef char Latin1Char;
42typedef int NativeFileHandle;
43#endif
44
45QTemporaryFileName::QTemporaryFileName(const QString &templateName)
46{
47 // Ensure there is a placeholder mask
48 QString qfilename = QDir::fromNativeSeparators(templateName);
49 qsizetype phPos = qfilename.size();
50 qsizetype phLength = 0;
51
52 while (phPos != 0) {
53 --phPos;
54
55 if (qfilename[phPos] == u'X') {
56 ++phLength;
57 continue;
58 }
59
60 if (phLength >= 6
61 || qfilename[phPos] == u'/') {
62 ++phPos;
63 break;
64 }
65
66 // start over
67 phLength = 0;
68 }
69
70 if (phLength < 6)
71 qfilename.append(".XXXXXX"_L1);
72
73 // "Nativify" :-)
74 QFileSystemEntry::NativePath filename =
75 QFileSystemEntry(QDir::cleanPath(qfilename)).nativeFilePath();
76
77 // Find mask in native path
78 phPos = filename.size();
79 phLength = 0;
80 while (phPos != 0) {
81 --phPos;
82
83 if (filename[phPos] == Latin1Char('X')) {
84 ++phLength;
85 continue;
86 }
87
88 if (phLength >= 6) {
89 ++phPos;
90 break;
91 }
92
93 // start over
94 phLength = 0;
95 }
96
97 Q_ASSERT(phLength >= 6);
98 path = filename;
99 pos = phPos;
100 length = phLength;
101}
102
103/*!
104 \internal
105
106 Generates a unique file path from the template \a templ and returns it.
107 The path in \c templ.path is modified.
108*/
109QFileSystemEntry::NativePath QTemporaryFileName::generateNext()
110{
111 Q_ASSERT(length != 0);
112 Q_ASSERT(pos < path.size());
113 Q_ASSERT(length <= path.size() - pos);
114
115 Char *const placeholderStart = (Char *)path.data() + pos;
116 Char *const placeholderEnd = placeholderStart + length;
117
118 // Replace placeholder with random chars.
119 {
120 // Since our dictionary is 26+26 characters, it would seem we only need
121 // a random number from 0 to 63 to select a character. However, due to
122 // the limited range, that would mean 12 (64-52) characters have double
123 // the probability of the others: 1 in 32 instead of 1 in 64.
124 //
125 // To overcome this limitation, we use more bits per character. With 10
126 // bits, there are 16 characters with probability 19/1024 and the rest
127 // at 20/1024 (i.e, less than .1% difference). This allows us to do 3
128 // characters per 32-bit random number, which is also half the typical
129 // placeholder length.
130 enum { BitsPerCharacter = 10 };
131
132 Char *rIter = placeholderEnd;
133 while (rIter != placeholderStart) {
134 quint32 rnd = QRandomGenerator::global()->generate();
135 auto applyOne = [&]() {
136 quint32 v = rnd & ((1 << BitsPerCharacter) - 1);
137 rnd >>= BitsPerCharacter;
138 char ch = char((26 + 26) * v / (1 << BitsPerCharacter));
139 if (ch < 26)
140 *--rIter = Latin1Char(ch + 'A');
141 else
142 *--rIter = Latin1Char(ch - 26 + 'a');
143 };
144
145 applyOne();
146 if (rIter == placeholderStart)
147 break;
148
149 applyOne();
150 if (rIter == placeholderStart)
151 break;
152
153 applyOne();
154 }
155 }
156
157 return path;
158}
159
160#if QT_CONFIG(temporaryfile)
161
162/*!
163 \internal
164
165 Generates a unique file path from the template \a templ and creates a new
166 file based on those parameters: the \c templ.length characters in \c
167 templ.path starting at \c templ.pos will be replaced by a random sequence of
168 characters. \a mode specifies the file mode bits (not used on Windows).
169
170 Returns true on success and sets the file handle on \a file. On error,
171 returns false, sets an invalid handle on \a handle and sets the error
172 condition in \a error. In both cases, the string in \a templ will be
173 changed and contain the generated path name.
174*/
175static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
176 quint32 mode, int flags, QSystemError &error)
177{
178 const int maxAttempts = 16;
179 for (int attempt = 0; attempt < maxAttempts; ++attempt) {
180 // Atomically create file and obtain handle
181 const QFileSystemEntry::NativePath &path = templ.generateNext();
182
183#if defined(Q_OS_WIN)
184 Q_UNUSED(mode);
185 const DWORD shareMode = (flags & QTemporaryFileEngine::Win32NonShared)
186 ? 0u : (FILE_SHARE_READ | FILE_SHARE_WRITE);
187
188 const DWORD extraAccessFlags = (flags & QTemporaryFileEngine::Win32NonShared) ? DELETE : 0;
189 file = CreateFile((const wchar_t *)path.constData(),
190 GENERIC_READ | GENERIC_WRITE | extraAccessFlags,
191 shareMode, NULL, CREATE_NEW,
192 FILE_ATTRIBUTE_NORMAL, NULL);
193
194 if (file != INVALID_HANDLE_VALUE)
195 return true;
196
197 DWORD err = GetLastError();
198 if (err == ERROR_ACCESS_DENIED) {
199 WIN32_FILE_ATTRIBUTE_DATA attributes;
200 if (!GetFileAttributesEx((const wchar_t *)path.constData(),
201 GetFileExInfoStandard, &attributes)
202 || attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
203 // Potential write error (read-only parent directory, etc.).
204 error = QSystemError(err, QSystemError::NativeError);
205 return false;
206 } // else file already exists as a directory.
207 } else if (err != ERROR_FILE_EXISTS) {
208 error = QSystemError(err, QSystemError::NativeError);
209 return false;
210 }
211#else // POSIX
212 Q_UNUSED(flags);
213 file = QT_OPEN(path.constData(),
214 QT_OPEN_CREAT | QT_OPEN_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
215 static_cast<mode_t>(mode));
216
217 if (file != -1)
218 return true;
219
220 int err = errno;
221 if (err != EEXIST) {
222 error = QSystemError(err, QSystemError::NativeError);
223 return false;
224 }
225#endif
226 }
227
228 return false;
229}
230
231enum class CreateUnnamedFileStatus {
232 Success = 0,
233 NotSupported,
234 OtherError
235};
236
237static CreateUnnamedFileStatus
238createUnnamedFile(NativeFileHandle &file, QTemporaryFileName &tfn, quint32 mode, QSystemError *error)
239{
240#ifdef LINUX_UNNAMED_TMPFILE
241 // first, check if we have /proc, otherwise can't make the file exist later
242 // (no error message set, as caller will try regular temporary file)
243 if (!qt_haveLinuxProcfs())
244 return CreateUnnamedFileStatus::NotSupported;
245
246 const char *p = ".";
247 QByteArray::size_type lastSlash = tfn.path.lastIndexOf('/');
248 if (lastSlash >= 0) {
249 if (lastSlash == 0)
250 lastSlash = 1;
251 tfn.path[lastSlash] = '\0';
252 p = tfn.path.data();
253 }
254
255 file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
256 static_cast<mode_t>(mode));
257 if (file != -1)
258 return CreateUnnamedFileStatus::Success;
259
260 if (errno == EOPNOTSUPP || errno == EISDIR) {
261 // fs or kernel doesn't support O_TMPFILE, so
262 // put the slash back so we may try a regular file
263 if (lastSlash != -1)
264 tfn.path[lastSlash] = '/';
265 return CreateUnnamedFileStatus::NotSupported;
266 }
267
268 // real error
269 *error = QSystemError(errno, QSystemError::NativeError);
270 return CreateUnnamedFileStatus::OtherError;
271#else
272 Q_UNUSED(file);
273 Q_UNUSED(tfn);
274 Q_UNUSED(mode);
275 Q_UNUSED(error);
276 return CreateUnnamedFileStatus::NotSupported;
277#endif
278}
279
280//************* QTemporaryFileEngine
281QTemporaryFileEngine::~QTemporaryFileEngine()
282{
283 Q_D(QFSFileEngine);
284 d->unmapAll();
285 QFSFileEngine::close();
286}
287
288bool QTemporaryFileEngine::isReallyOpen() const
289{
290 Q_D(const QFSFileEngine);
291
292 if (!((nullptr == d->fh) && (-1 == d->fd)
293#if defined Q_OS_WIN
294 && (INVALID_HANDLE_VALUE == d->fileHandle)
295#endif
296 ))
297 return true;
298
299 return false;
300
301}
302
303void QTemporaryFileEngine::setFileName(const QString &file)
304{
305 // Really close the file, so we don't leak
306 QFSFileEngine::close();
307 QFSFileEngine::setFileName(file);
308}
309
310bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode,
311 std::optional<QFile::Permissions> permissions)
312{
313 Q_D(QFSFileEngine);
314 Q_ASSERT(!isReallyOpen());
315
316 openMode |= QIODevice::ReadWrite;
317
318 if (!filePathIsTemplate)
319 return QFSFileEngine::open(openMode, permissions);
320
321 QTemporaryFileName tfn(templateName);
322
323 QSystemError error;
324#if defined(Q_OS_WIN)
325 NativeFileHandle &file = d->fileHandle;
326#else // POSIX
327 NativeFileHandle &file = d->fd;
328#endif
329
330 CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
331 if (st == CreateUnnamedFileStatus::Success) {
332 unnamedFile = true;
333 d->fileEntry.clear();
334 } else if (st == CreateUnnamedFileStatus::NotSupported &&
335 createFileFromTemplate(file, tfn, fileMode, flags, error)) {
336 filePathIsTemplate = false;
337 unnamedFile = false;
338 d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
339 } else {
340 setError(QFile::OpenError, error.toString());
341 return false;
342 }
343
344#if !defined(Q_OS_WIN)
345 d->closeFileHandle = true;
346#endif
347
348 d->openMode = openMode;
349 d->lastFlushFailed = false;
350 d->tried_stat = 0;
351
352 return true;
353}
354
355bool QTemporaryFileEngine::remove()
356{
357 Q_D(QFSFileEngine);
358 // Since the QTemporaryFileEngine::close() does not really close the file,
359 // we must explicitly call QFSFileEngine::close() before we remove it.
360 d->unmapAll();
361 QFSFileEngine::close();
362 if (isUnnamedFile())
363 return true;
364 if (!filePathIsTemplate && QFSFileEngine::remove()) {
365 d->fileEntry.clear();
366 // If a QTemporaryFile is constructed using a template file path, the path
367 // is generated in QTemporaryFileEngine::open() and then filePathIsTemplate
368 // is set to false. If remove() and then open() are called on the same
369 // QTemporaryFile, the path is not regenerated. Here we ensure that if the
370 // file path was generated, it will be generated again in the scenario above.
371 filePathIsTemplate = filePathWasTemplate;
372 return true;
373 }
374 return false;
375}
376
377bool QTemporaryFileEngine::rename(const QString &newName)
378{
379 if (isUnnamedFile()) {
380 bool ok = materializeUnnamedFile(newName, DontOverwrite);
381 QFSFileEngine::close();
382 return ok;
383 }
384 QFSFileEngine::close();
385 return QFSFileEngine::rename(newName);
386}
387
388bool QTemporaryFileEngine::renameOverwrite(const QString &newName)
389{
390 if (isUnnamedFile()) {
391 bool ok = materializeUnnamedFile(newName, Overwrite);
392 QFSFileEngine::close();
393 return ok;
394 }
395#ifdef Q_OS_WIN
396 if (flags & Win32NonShared) {
397 QFileSystemEntry newEntry(newName, QFileSystemEntry::FromInternalPath());
398 bool ok = d_func()->nativeRenameOverwrite(newEntry);
399 QFSFileEngine::close();
400 if (ok) {
401 // Match what QFSFileEngine::renameOverwrite() does
402 setFileEntry(std::move(newEntry));
403 }
404 return ok;
405 }
406#endif
407 QFSFileEngine::close();
408 return QFSFileEngine::renameOverwrite(newName);
409}
410
411bool QTemporaryFileEngine::close()
412{
413 // Don't close the file, just seek to the front.
414 seek(0);
415 setError(QFile::UnspecifiedError, QString());
416 return true;
417}
418
419QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file) const
420{
421 if (isUnnamedFile()) {
422 if (file == AbsoluteLinkTarget || file == RawLinkPath) {
423 // we know our file isn't (won't be) a symlink
424 return QString();
425 }
426
427 // for all other cases, materialize the file
428 const_cast<QTemporaryFileEngine *>(this)->materializeUnnamedFile(templateName, NameIsTemplate);
429 }
430 return QFSFileEngine::fileName(file);
431}
432
433bool QTemporaryFileEngine::materializeUnnamedFile(const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
434{
435 Q_ASSERT(isUnnamedFile());
436
437#ifdef LINUX_UNNAMED_TMPFILE
438 Q_D(QFSFileEngine);
439 const QByteArray src = "/proc/self/fd/" + QByteArray::number(d->fd);
440 auto materializeAt = [=](const QFileSystemEntry &dst) {
441 return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
442 };
443#else
444 auto materializeAt = [](const QFileSystemEntry &) { return false; };
445#endif
446
447 auto success = [this](const QFileSystemEntry &entry) {
448 filePathIsTemplate = false;
449 unnamedFile = false;
450 d_func()->fileEntry = entry;
451 return true;
452 };
453
454 auto materializeAsTemplate = [=](const QString &newName) {
455 QTemporaryFileName tfn(newName);
456 static const int maxAttempts = 16;
457 for (int attempt = 0; attempt < maxAttempts; ++attempt) {
458 tfn.generateNext();
459 QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
460 if (materializeAt(entry))
461 return success(entry);
462 }
463 return false;
464 };
465
466 if (mode == NameIsTemplate) {
467 if (materializeAsTemplate(newName))
468 return true;
469 } else {
470 // Use linkat to materialize the file
471 QFileSystemEntry dst(newName);
472 if (materializeAt(dst))
473 return success(dst);
474
475 if (errno == EEXIST && mode == Overwrite) {
476 // retry by first creating a temporary file in the right dir
477 if (!materializeAsTemplate(templateName))
478 return false;
479
480 // then rename the materialized file to target (same as renameOverwrite)
481 QFSFileEngine::close();
482 return QFSFileEngine::renameOverwrite(newName);
483 }
484 }
485
486 // failed
487 setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
488 return false;
489}
490
491bool QTemporaryFileEngine::isUnnamedFile() const
492{
493#ifdef LINUX_UNNAMED_TMPFILE
494 if (unnamedFile) {
495 Q_ASSERT(d_func()->fileEntry.isEmpty());
496 Q_ASSERT(filePathIsTemplate);
497 }
498 return unnamedFile;
499#else
500 return false;
501#endif
502}
503
504//************* QTemporaryFilePrivate
505
506QTemporaryFilePrivate::QTemporaryFilePrivate()
507{
508}
509
510QTemporaryFilePrivate::QTemporaryFilePrivate(const QString &templateNameIn)
511 : templateName(templateNameIn)
512{
513}
514
515QTemporaryFilePrivate::~QTemporaryFilePrivate()
516{
517}
518
519QAbstractFileEngine *QTemporaryFilePrivate::engine() const
520{
521 if (!fileEngine) {
522 fileEngine.reset(new QTemporaryFileEngine(&templateName));
523 resetFileEngine();
524 }
525 return fileEngine.get();
526}
527
528void QTemporaryFilePrivate::resetFileEngine() const
529{
530 if (!fileEngine)
531 return;
532
533 QTemporaryFileEngine *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
534 if (fileName.isEmpty())
535 tef->initialize(templateName, 0600);
536 else
537 tef->initialize(fileName, 0600, false);
538}
539
540void QTemporaryFilePrivate::materializeUnnamedFile()
541{
542#ifdef LINUX_UNNAMED_TMPFILE
543 if (!fileName.isEmpty() || !fileEngine)
544 return;
545
546 auto *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
547 fileName = tef->fileName(QAbstractFileEngine::DefaultName);
548#endif
549}
550
551QString QTemporaryFilePrivate::defaultTemplateName()
552{
553 QString baseName;
554#if defined(QT_BUILD_CORE_LIB)
555 baseName = QCoreApplication::applicationName();
556 if (baseName.isEmpty())
557#endif
558 baseName = "qt_temp"_L1;
559
560 return QDir::tempPath() + u'/' + baseName + ".XXXXXX"_L1;
561}
562
563//************* QTemporaryFile
564
565/*!
566 \class QTemporaryFile
567 \inmodule QtCore
568 \reentrant
569 \brief The QTemporaryFile class is an I/O device that operates on temporary files.
570
571 \ingroup io
572
573
574 QTemporaryFile is used to create unique temporary files safely.
575 The file itself is created by calling open(). The name of the
576 temporary file is guaranteed to be unique (i.e., you are
577 guaranteed to not overwrite an existing file), and the file will
578 subsequently be removed upon destruction of the QTemporaryFile
579 object. This is an important technique that avoids data
580 corruption for applications that store data in temporary files.
581 The file name is either auto-generated, or created based on a
582 template, which is passed to QTemporaryFile's constructor.
583
584 Example:
585
586 \snippet code/src_corelib_io_qtemporaryfile.cpp 0
587
588 Reopening a QTemporaryFile after calling close() is safe. For as long as
589 the QTemporaryFile object itself is not destroyed, the unique temporary
590 file will exist and be kept open internally by QTemporaryFile.
591
592 The file name of the temporary file can be found by calling fileName().
593 Note that this is only defined after the file is first opened; the function
594 returns an empty string before this.
595
596 A temporary file will have some static part of the name and some
597 part that is calculated to be unique. The default filename will be
598 determined from QCoreApplication::applicationName() (otherwise \c qt_temp) and will
599 be placed into the temporary path as returned by QDir::tempPath().
600 If you specify your own filename, a relative file path will not be placed in the
601 temporary directory by default, but be relative to the current working directory.
602
603//! [note-about-rename-method]
604 It is important to specify the correct directory if the rename() function will be
605 called, as QTemporaryFile can only rename files within the same volume / filesystem
606 as the temporary file itself was created on.
607//! [note-about-rename-method]
608
609 The file name (the part after the last directory path separator in the
610 specified file template) can contain the special sequence \c {"XXXXXX"}
611 (at least six upper case \c "X" characters), which will be replaced with
612 the auto-generated portion of the file name. If the file name doesn't
613 contain \c {"XXXXXX"}, QTemporaryFile will append the generated part to the
614 file name. Only the last occurrence of \c {"XXXXXX"} will be considered.
615
616 \note On Linux, QTemporaryFile will attempt to create unnamed temporary
617 files. If that succeeds, open() will return true but exists() will be
618 false. If you call fileName() or any function that calls it,
619 QTemporaryFile will give the file a name, so most applications will
620 not see a difference.
621
622 \sa QDir::tempPath(), QFile
623*/
624
625#ifdef QT_NO_QOBJECT
626QTemporaryFile::QTemporaryFile()
627 : QFile(*new QTemporaryFilePrivate)
628{
629}
630
631QTemporaryFile::QTemporaryFile(const QString &templateName)
632 : QFile(*new QTemporaryFilePrivate(templateName))
633{
634}
635
636#else
637/*!
638 Constructs a QTemporaryFile.
639
640//! [default-file-name-template]
641 \keyword Default File Name Template
642 The default file name template is determined from the application name as
643 returned by QCoreApplication::applicationName() (or \c {"qt_temp"} if the
644 application name is empty), followed by \c {".XXXXXX"}. The file is stored
645 in the system's temporary directory, as returned by QDir::tempPath().
646//! [default-file-name-template]
647
648 \sa setFileTemplate(), fileTemplate(), fileName(), QDir::tempPath()
649*/
650QTemporaryFile::QTemporaryFile()
651 : QTemporaryFile(nullptr)
652{
653}
654
655/*!
656 \fn QTemporaryFile::QTemporaryFile(const std::filesystem::path &templateName, QObject *parent)
657 \overload
658 \since 6.7
659*/
660
661/*!
662 Constructs a QTemporaryFile with \a templateName as the file name template.
663
664//! [file-created-on-open]
665 Upon opening the temporary file, \a templateName will be used to create
666 a unique filename.
667//! [file-created-on-open]
668
669//! [dynamic-part-of-filename]
670 If the file name (the part after the last directory path separator in
671 \a templateName) doesn't contain \c {"XXXXXX"}, it will be added
672 automatically.
673
674 \c {"XXXXXX"} will be replaced with the dynamic part of the file name,
675 which is calculated to be unique.
676//! [dynamic-part-of-filename]
677
678//! [filename-relative-or-absolute-path]
679 If \a templateName is a relative path, the path will be relative to the
680 current working directory. You can use QDir::tempPath() to construct \a
681 templateName if you want use the system's temporary directory.
682//! [filename-relative-or-absolute-path]
683
684 \include qtemporaryfile.cpp note-about-rename-method
685
686 \sa open(), fileTemplate()
687*/
688QTemporaryFile::QTemporaryFile(const QString &templateName)
689 : QTemporaryFile(templateName, nullptr)
690{
691}
692
693/*!
694 Constructs a QTemporaryFile with the given \a parent.
695
696 \include qtemporaryfile.cpp default-file-name-template
697
698 \sa setFileTemplate()
699*/
700QTemporaryFile::QTemporaryFile(QObject *parent)
701 : QFile(*new QTemporaryFilePrivate, parent)
702{
703}
704
705/*!
706 Constructs a QTemporaryFile with the specified \a parent, and
707 \a templateName as the file name template.
708
709 \include qtemporaryfile.cpp file-created-on-open
710
711 \include qtemporaryfile.cpp dynamic-part-of-filename
712
713 \include qtemporaryfile.cpp filename-relative-or-absolute-path
714 \include qtemporaryfile.cpp note-about-rename-method
715
716 \sa open(), fileTemplate()
717*/
718QTemporaryFile::QTemporaryFile(const QString &templateName, QObject *parent)
719 : QFile(*new QTemporaryFilePrivate(templateName), parent)
720{
721}
722#endif
723
724/*!
725 Destroys the temporary file object, the file is automatically
726 closed if necessary and if in auto remove mode it will
727 automatically delete the file.
728
729 \sa autoRemove()
730*/
731QTemporaryFile::~QTemporaryFile()
732{
733 Q_D(QTemporaryFile);
734 close();
735 if (!d->fileName.isEmpty() && d->autoRemove)
736 remove();
737}
738
739/*!
740 \fn bool QTemporaryFile::open()
741
742 Opens a unique temporary file in the file system in
743 \l QIODeviceBase::ReadWrite mode.
744 Returns \c true if the file was successfully opened, or was already open.
745 Otherwise returns \c false.
746
747 If called for the first time, open() will create a unique file name
748 based on \l fileTemplate(). The file is guaranteed to have been created
749 by this function (that is, it has never existed before).
750
751 If a file is reopened after calling \l close(), the same file will be
752 opened again.
753
754 \sa setFileTemplate(), QT_USE_NODISCARD_FILE_OPEN
755*/
756
757/*!
758 Returns \c true if the QTemporaryFile is in auto remove
759 mode. Auto-remove mode will automatically delete the filename from
760 disk upon destruction. This makes it very easy to create your
761 QTemporaryFile object on the stack, fill it with data, read from
762 it, and finally on function return it will automatically clean up
763 after itself.
764
765 Auto-remove is on by default.
766
767 \sa setAutoRemove(), remove()
768*/
769bool QTemporaryFile::autoRemove() const
770{
771 Q_D(const QTemporaryFile);
772 return d->autoRemove;
773}
774
775/*!
776 Sets the QTemporaryFile into auto-remove mode if \a b is \c true.
777
778 Auto-remove is on by default.
779
780 If you set this property to \c false, ensure the application provides a way
781 to remove the file once it is no longer needed, including passing the
782 responsibility on to another process. Always use the fileName() function to
783 obtain the name and never try to guess the name that QTemporaryFile has
784 generated.
785
786 On some systems, if fileName() is not called before closing the file, the
787 temporary file may be removed regardless of the state of this property.
788 This behavior should not be relied upon, so application code should either
789 call fileName() or leave the auto removal functionality enabled.
790
791 \sa autoRemove(), remove()
792*/
793void QTemporaryFile::setAutoRemove(bool b)
794{
795 Q_D(QTemporaryFile);
796 d->autoRemove = b;
797}
798
799/*!
800 Returns the complete unique filename backing the QTemporaryFile
801 object. This string is null before the QTemporaryFile is opened,
802 afterwards it will contain the fileTemplate() plus
803 additional characters to make it unique.
804
805 The file name returned by this method is relative or absolute depending on
806 the file name template used to construct this object (or passed to
807 setFileTemplate()) being relative or absolute, respectively.
808
809 \sa fileTemplate()
810*/
811
812QString QTemporaryFile::fileName() const
813{
814 Q_D(const QTemporaryFile);
815 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
816 if (tef && tef->isReallyOpen())
817 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
818
819 if (d->fileName.isEmpty())
820 return QString();
821 return d->engine()->fileName(QAbstractFileEngine::DefaultName);
822}
823
824/*!
825 Returns the file name template.
826
827 The file name template returned by this method, will be relative or
828 absolute depending on the file name template used to construct this object
829 (or passed to setFileTemplate()) being relative or absolute, respectively.
830
831 \sa setFileTemplate(), fileName(), {Default File Name Template}
832*/
833QString QTemporaryFile::fileTemplate() const
834{
835 Q_D(const QTemporaryFile);
836 return d->templateName;
837}
838
839/*!
840 \fn void QTemporaryFile::setFileTemplate(const std::filesystem::path &name)
841 \overload
842 \since 6.7
843*/
844
845/*!
846 \fn void QTemporaryFile::setFileTemplate(const QString &templateName)
847
848 Sets the file name template to \a templateName.
849
850 \include qtemporaryfile.cpp dynamic-part-of-filename
851
852 \include qtemporaryfile.cpp filename-relative-or-absolute-path
853 \include qtemporaryfile.cpp note-about-rename-method
854
855 \sa fileTemplate(), fileName()
856*/
857void QTemporaryFile::setFileTemplate(const QString &name)
858{
859 Q_D(QTemporaryFile);
860 d->templateName = name;
861}
862
863/*!
864 \fn bool QTemporaryFile::rename(const std::filesystem::path &newName)
865 \overload
866 \since 6.7
867*/
868
869/*!
870 Renames the current temporary file to \a newName and returns true if it
871 succeeded.
872
873 This function has an important difference compared to QFile::rename(): it
874 will not perform a copy+delete if the low-level system call to rename the
875 file fails, something that could happen if \a newName specifies a file in a
876 different volume or filesystem than the temporary file was created on. In
877 other words, QTemporaryFile only supports atomic file renaming.
878
879 This functionality is intended to support materializing the destination
880 file with all contents already present, so another process cannot see an
881 incomplete file in the process of being written. The \l QSaveFile class can
882 be used for a similar purpose too, particularly if the destination file is
883 not temporary.
884
885 \note Calling rename() does not disable autoRemove. If you want the renamed
886 file to persist, you must call setAutoRemove and set it to \c false after
887 calling rename(). Otherwise, the file will be deleted when the QTemporaryFile
888 object is destroyed.
889
890 This function will fail if \a newName already exists. To replace it, use
891 renameOverwrite() instead.
892
893 \sa renameOverwrite(), QSaveFile, QSaveFile::commit(), QFile::rename()
894*/
895bool QTemporaryFile::rename(const QString &newName)
896{
897 Q_D(QTemporaryFile);
898 return d->rename(newName, false);
899}
900
901bool QTemporaryFilePrivate::rename(const QString &newName, bool overwrite)
902{
903 Q_Q(QTemporaryFile);
904 auto tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
905 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
906 return q->QFile::rename(newName);
907
908 q->unsetError();
909 q->close();
910 if (q->error() == QFile::NoError) {
911 if (overwrite ? tef->renameOverwrite(newName) : tef->rename(newName)) {
912 q->unsetError();
913 // engine was able to handle the new name so we just reset it
914 fileName = newName;
915 return true;
916 }
917
918 setError(QFile::RenameError, tef->errorString());
919 }
920 return false;
921}
922
923/*!
924 \fn bool QTemporaryFile::renameOverwrite(const std::filesystem::path &newName)
925 \overload
926 \since 6.11
927*/
928
929/*!
930 \since 6.11
931
932 This is the same as rename(), except that it atomically replaces \a newName
933 if it already exists, like QSaveFile::commit() does, too.
934
935 Returns \c{false} if the rename could not performed atomically
936 (for example, the temporary file and the target file name live on
937 different file systems / volumes / drives.
938
939 \sa rename(), QSaveFile, QSaveFile::commit(), QFile::rename()
940*/
941bool QTemporaryFile::renameOverwrite(const QString &newName)
942{
943 Q_D(QTemporaryFile);
944 return d->rename(newName, true);
945}
946
947/*!
948 \fn QTemporaryFile *QTemporaryFile::createNativeFile(const QString &fileName)
949 \overload
950
951 Works on the given \a fileName rather than an existing QFile
952 object.
953*/
954/*!
955 \fn QTemporaryFile *QTemporaryFile::createNativeFile(const std::filesystem::path &fileName)
956 \overload
957 \since 6.7
958*/
959
960/*!
961 If \a file is not already a native file, then a QTemporaryFile is created
962 in QDir::tempPath(), the contents of \a file is copied into it, and a pointer
963 to the temporary file is returned. Does nothing and returns \c 0 if \a file
964 is already a native file.
965
966 For example:
967
968 \snippet code/src_corelib_io_qtemporaryfile.cpp 1
969
970 \sa QFileInfo::isNativePath()
971*/
972
973QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
974{
975 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
976 if (engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
977 return nullptr; // native already
978 //cache
979 bool wasOpen = file.isOpen();
980 qint64 old_off = 0;
981 if (wasOpen)
982 old_off = file.pos();
983 else if (!file.open(QIODevice::ReadOnly))
984 return nullptr;
985 //dump data
986 QTemporaryFile *ret = new QTemporaryFile;
987 if (ret->open()) {
988 file.seek(0);
989 char buffer[1024];
990 while (true) {
991 qint64 len = file.read(buffer, 1024);
992 if (len < 1)
993 break;
994 ret->write(buffer, len);
995 }
996 ret->seek(0);
997 } else {
998 delete ret;
999 ret = nullptr;
1000 }
1001 //restore
1002 if (wasOpen)
1003 file.seek(old_off);
1004 else
1005 file.close();
1006 //done
1007 return ret;
1008 }
1009 return nullptr;
1010}
1011
1012/*!
1013 \reimp
1014
1015 Opens a unique temporary file in the file system with \a mode flags.
1016 Returns \c true if the file was successfully opened, or was already open.
1017 Otherwise returns \c false.
1018
1019 If called for the first time, open() will create a unique file name
1020 based on \l fileTemplate(), and open it with \a mode flags.
1021 The file is guaranteed to have been created by this function (that is,
1022 it has never existed before).
1023
1024 If a file is reopened after calling \l close(), the same file will be
1025 opened again with \a mode flags.
1026
1027 \sa setFileTemplate(), QT_USE_NODISCARD_FILE_OPEN
1028*/
1029bool QTemporaryFile::open(OpenMode mode)
1030{
1031 Q_D(QTemporaryFile);
1032 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1033 if (tef && tef->isReallyOpen()) {
1034 setOpenMode(mode);
1035 return true;
1036 }
1037
1038 // reset the engine state so it creates a new, unique file name from the template;
1039 // equivalent to:
1040 // delete d->fileEngine;
1041 // d->fileEngine = 0;
1042 // d->engine();
1043 d->resetFileEngine();
1044
1045 if (QFile::open(mode)) {
1046 tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1047 if (tef->isUnnamedFile())
1048 d->fileName.clear();
1049 else
1050 d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
1051 return true;
1052 }
1053 return false;
1054}
1055
1056#endif // QT_CONFIG(temporaryfile)
1057
1058QT_END_NAMESPACE
1059
1060#ifndef QT_NO_QOBJECT
1061#include "moc_qtemporaryfile.cpp"
1062#endif
char Char
int NativeFileHandle
char Latin1Char