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
5#include "qtemporaryfile.h"
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
24QT_BEGIN_NAMESPACE
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(pathName: 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(s: ".XXXXXX"_L1);
71
72 // "Nativify" :-)
73 QFileSystemEntry::NativePath filename =
74 QFileSystemEntry(QDir::cleanPath(path: 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(pathname: path.constData(),
213 QT_OPEN_CREAT | QT_OPEN_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
214 mode: 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(c: '/');
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(pathname: p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
255 mode: 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, mode: fileMode, error: &error);
330 if (st == CreateUnnamedFileStatus::Success) {
331 unnamedFile = true;
332 d->fileEntry.clear();
333 } else if (st == CreateUnnamedFileStatus::NotSupported &&
334 createFileFromTemplate(file, templ&: tfn, mode: fileMode, flags, error)) {
335 filePathIsTemplate = false;
336 unnamedFile = false;
337 d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
338 } else {
339 setError(error: QFile::OpenError, str: 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, mode: 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, mode: 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(error: QFile::UnspecifiedError, str: 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(newName: templateName, mode: 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, from: src, AT_FDCWD, to: 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(error: QFile::RenameError, str: 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(p: 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(file: templateName, mode: 0600);
535 else
536 tef->initialize(file: fileName, mode: 0600, nameIsTemplate: 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(file: 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 first 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 A QTemporaryFile will always be opened in QIODevice::ReadWrite mode,
742 this allows easy access to the data in the file. This function will
743 return true upon success and will set the fileName() to the unique
744 filename used.
745
746 \sa fileName(), QT_USE_NODISCARD_FILE_OPEN
747*/
748
749/*!
750 Returns \c true if the QTemporaryFile is in auto remove
751 mode. Auto-remove mode will automatically delete the filename from
752 disk upon destruction. This makes it very easy to create your
753 QTemporaryFile object on the stack, fill it with data, read from
754 it, and finally on function return it will automatically clean up
755 after itself.
756
757 Auto-remove is on by default.
758
759 \sa setAutoRemove(), remove()
760*/
761bool QTemporaryFile::autoRemove() const
762{
763 Q_D(const QTemporaryFile);
764 return d->autoRemove;
765}
766
767/*!
768 Sets the QTemporaryFile into auto-remove mode if \a b is \c true.
769
770 Auto-remove is on by default.
771
772 If you set this property to \c false, ensure the application provides a way
773 to remove the file once it is no longer needed, including passing the
774 responsibility on to another process. Always use the fileName() function to
775 obtain the name and never try to guess the name that QTemporaryFile has
776 generated.
777
778 On some systems, if fileName() is not called before closing the file, the
779 temporary file may be removed regardless of the state of this property.
780 This behavior should not be relied upon, so application code should either
781 call fileName() or leave the auto removal functionality enabled.
782
783 \sa autoRemove(), remove()
784*/
785void QTemporaryFile::setAutoRemove(bool b)
786{
787 Q_D(QTemporaryFile);
788 d->autoRemove = b;
789}
790
791/*!
792 Returns the complete unique filename backing the QTemporaryFile
793 object. This string is null before the QTemporaryFile is opened,
794 afterwards it will contain the fileTemplate() plus
795 additional characters to make it unique.
796
797 The file name returned by this method is relative or absolute depending on
798 the file name template used to construct this object (or passed to
799 setFileTemplate()) being relative or absolute, respectively.
800
801 \sa fileTemplate()
802*/
803
804QString QTemporaryFile::fileName() const
805{
806 Q_D(const QTemporaryFile);
807 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
808 if (tef && tef->isReallyOpen())
809 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
810
811 if (d->fileName.isEmpty())
812 return QString();
813 return d->engine()->fileName(file: QAbstractFileEngine::DefaultName);
814}
815
816/*!
817 Returns the file name template.
818
819 The file name template returned by this method, will be relative or
820 absolute depending on the file name template used to construct this object
821 (or passed to setFileTemplate()) being relative or absolute, respectively.
822
823 \sa setFileTemplate(), fileName(), {Default File Name Template}
824*/
825QString QTemporaryFile::fileTemplate() const
826{
827 Q_D(const QTemporaryFile);
828 return d->templateName;
829}
830
831/*!
832 \fn void QTemporaryFile::setFileTemplate(const std::filesystem::path &name)
833 \overload
834 \since 6.7
835*/
836
837/*!
838 \fn void QTemporaryFile::setFileTemplate(const QString &templateName)
839
840 Sets the file name template to \a templateName.
841
842 \include qtemporaryfile.cpp dynamic-part-of-filename
843
844 \include qtemporaryfile.cpp filename-relative-or-absolute-path
845 \include qtemporaryfile.cpp note-about-rename-method
846
847 \sa fileTemplate(), fileName()
848*/
849void QTemporaryFile::setFileTemplate(const QString &name)
850{
851 Q_D(QTemporaryFile);
852 d->templateName = name;
853}
854
855/*!
856 \fn bool QTemporaryFile::rename(const std::filesystem::path &newName)
857 \overload
858 \since 6.7
859*/
860
861/*!
862 Renames the current temporary file to \a newName and returns true if it
863 succeeded.
864
865 This function has an important difference compared to QFile::rename(): it
866 will not perform a copy+delete if the low-level system call to rename the
867 file fails, something that could happen if \a newName specifies a file in a
868 different volume or filesystem than the temporary file was created on. In
869 other words, QTemporaryFile only supports atomic file renaming.
870
871 This functionality is intended to support materializing the destination
872 file with all contents already present, so another process cannot see an
873 incomplete file in the process of being written. The \l QSaveFile class can
874 be used for a similar purpose too, particularly if the destination file is
875 not temporary.
876
877 \sa QSaveFile, QSaveFile::commit(), QFile::rename()
878*/
879bool QTemporaryFile::rename(const QString &newName)
880{
881 Q_D(QTemporaryFile);
882 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
883 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
884 return QFile::rename(newName);
885
886 unsetError();
887 close();
888 if (error() == QFile::NoError) {
889 if (tef->rename(newName)) {
890 unsetError();
891 // engine was able to handle the new name so we just reset it
892 d->fileName = newName;
893 return true;
894 }
895
896 d->setError(err: QFile::RenameError, errorString: tef->errorString());
897 }
898 return false;
899}
900
901/*!
902 \fn QTemporaryFile *QTemporaryFile::createNativeFile(const QString &fileName)
903 \overload
904
905 Works on the given \a fileName rather than an existing QFile
906 object.
907*/
908/*!
909 \fn QTemporaryFile *QTemporaryFile::createNativeFile(const std::filesystem::path &fileName)
910 \overload
911 \since 6.7
912*/
913
914/*!
915 If \a file is not already a native file, then a QTemporaryFile is created
916 in QDir::tempPath(), the contents of \a file is copied into it, and a pointer
917 to the temporary file is returned. Does nothing and returns \c 0 if \a file
918 is already a native file.
919
920 For example:
921
922 \snippet code/src_corelib_io_qtemporaryfile.cpp 1
923
924 \sa QFileInfo::isNativePath()
925*/
926
927QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
928{
929 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
930 if (engine->fileFlags(type: QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
931 return nullptr; // native already
932 //cache
933 bool wasOpen = file.isOpen();
934 qint64 old_off = 0;
935 if (wasOpen)
936 old_off = file.pos();
937 else if (!file.open(flags: QIODevice::ReadOnly))
938 return nullptr;
939 //dump data
940 QTemporaryFile *ret = new QTemporaryFile;
941 if (ret->open()) {
942 file.seek(offset: 0);
943 char buffer[1024];
944 while (true) {
945 qint64 len = file.read(data: buffer, maxlen: 1024);
946 if (len < 1)
947 break;
948 ret->write(data: buffer, len);
949 }
950 ret->seek(offset: 0);
951 } else {
952 delete ret;
953 ret = nullptr;
954 }
955 //restore
956 if (wasOpen)
957 file.seek(offset: old_off);
958 else
959 file.close();
960 //done
961 return ret;
962 }
963 return nullptr;
964}
965
966/*!
967 \reimp
968
969 Creates a unique file name for the temporary file, and opens it. You can
970 get the unique name later by calling fileName(). The file is guaranteed to
971 have been created by this function (i.e., it has never existed before).
972*/
973bool QTemporaryFile::open(OpenMode flags)
974{
975 Q_D(QTemporaryFile);
976 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
977 if (tef && tef->isReallyOpen()) {
978 setOpenMode(flags);
979 return true;
980 }
981
982 // reset the engine state so it creates a new, unique file name from the template;
983 // equivalent to:
984 // delete d->fileEngine;
985 // d->fileEngine = 0;
986 // d->engine();
987 d->resetFileEngine();
988
989 if (QFile::open(flags)) {
990 tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
991 if (tef->isUnnamedFile())
992 d->fileName.clear();
993 else
994 d->fileName = tef->fileName(file: QAbstractFileEngine::DefaultName);
995 return true;
996 }
997 return false;
998}
999
1000#endif // QT_CONFIG(temporaryfile)
1001
1002QT_END_NAMESPACE
1003
1004#ifndef QT_NO_QOBJECT
1005#include "moc_qtemporaryfile.cpp"
1006#endif
1007

source code of qtbase/src/corelib/io/qtemporaryfile.cpp