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