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