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 = QFileSystemEngine::absoluteName(
74 entry: QFileSystemEntry(qfilename, QFileSystemEntry::FromInternalPath()))
75 .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#ifndef QT_NO_TEMPORARYFILE
161
162/*!
163 \internal
164
165 Generates a unique file path from the template \a templ and creates a new
166 file based on those parameters: the \c templ.length characters in \c
167 templ.path starting at \c templ.pos will be replaced by a random sequence of
168 characters. \a mode specifies the file mode bits (not used on Windows).
169
170 Returns true on success and sets the file handle on \a file. On error,
171 returns false, sets an invalid handle on \a handle and sets the error
172 condition in \a error. In both cases, the string in \a templ will be
173 changed and contain the generated path name.
174*/
175static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
176 quint32 mode, int flags, QSystemError &error)
177{
178 const int maxAttempts = 16;
179 for (int attempt = 0; attempt < maxAttempts; ++attempt) {
180 // Atomically create file and obtain handle
181 const QFileSystemEntry::NativePath &path = templ.generateNext();
182
183#if defined(Q_OS_WIN)
184 Q_UNUSED(mode);
185 const DWORD shareMode = (flags & QTemporaryFileEngine::Win32NonShared)
186 ? 0u : (FILE_SHARE_READ | FILE_SHARE_WRITE);
187
188 file = CreateFile((const wchar_t *)path.constData(),
189 GENERIC_READ | GENERIC_WRITE,
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 QFSFileEngine::close();
395 return QFSFileEngine::renameOverwrite(newName);
396}
397
398bool QTemporaryFileEngine::close()
399{
400 // Don't close the file, just seek to the front.
401 seek(0);
402 setError(error: QFile::UnspecifiedError, str: QString());
403 return true;
404}
405
406QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file) const
407{
408 if (isUnnamedFile()) {
409 if (file == AbsoluteLinkTarget || file == RawLinkPath) {
410 // we know our file isn't (won't be) a symlink
411 return QString();
412 }
413
414 // for all other cases, materialize the file
415 const_cast<QTemporaryFileEngine *>(this)->materializeUnnamedFile(newName: templateName, mode: NameIsTemplate);
416 }
417 return QFSFileEngine::fileName(file);
418}
419
420bool QTemporaryFileEngine::materializeUnnamedFile(const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
421{
422 Q_ASSERT(isUnnamedFile());
423
424#ifdef LINUX_UNNAMED_TMPFILE
425 Q_D(QFSFileEngine);
426 const QByteArray src = "/proc/self/fd/" + QByteArray::number(d->fd);
427 auto materializeAt = [=](const QFileSystemEntry &dst) {
428 return ::linkat(AT_FDCWD, from: src, AT_FDCWD, to: dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
429 };
430#else
431 auto materializeAt = [](const QFileSystemEntry &) { return false; };
432#endif
433
434 auto success = [this](const QFileSystemEntry &entry) {
435 filePathIsTemplate = false;
436 unnamedFile = false;
437 d_func()->fileEntry = entry;
438 return true;
439 };
440
441 auto materializeAsTemplate = [=](const QString &newName) {
442 QTemporaryFileName tfn(newName);
443 static const int maxAttempts = 16;
444 for (int attempt = 0; attempt < maxAttempts; ++attempt) {
445 tfn.generateNext();
446 QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
447 if (materializeAt(entry))
448 return success(entry);
449 }
450 return false;
451 };
452
453 if (mode == NameIsTemplate) {
454 if (materializeAsTemplate(newName))
455 return true;
456 } else {
457 // Use linkat to materialize the file
458 QFileSystemEntry dst(newName);
459 if (materializeAt(dst))
460 return success(dst);
461
462 if (errno == EEXIST && mode == Overwrite) {
463 // retry by first creating a temporary file in the right dir
464 if (!materializeAsTemplate(templateName))
465 return false;
466
467 // then rename the materialized file to target (same as renameOverwrite)
468 QFSFileEngine::close();
469 return QFSFileEngine::renameOverwrite(newName);
470 }
471 }
472
473 // failed
474 setError(error: QFile::RenameError, str: QSystemError(errno, QSystemError::NativeError).toString());
475 return false;
476}
477
478bool QTemporaryFileEngine::isUnnamedFile() const
479{
480#ifdef LINUX_UNNAMED_TMPFILE
481 if (unnamedFile) {
482 Q_ASSERT(d_func()->fileEntry.isEmpty());
483 Q_ASSERT(filePathIsTemplate);
484 }
485 return unnamedFile;
486#else
487 return false;
488#endif
489}
490
491//************* QTemporaryFilePrivate
492
493QTemporaryFilePrivate::QTemporaryFilePrivate()
494{
495}
496
497QTemporaryFilePrivate::QTemporaryFilePrivate(const QString &templateNameIn)
498 : templateName(templateNameIn)
499{
500}
501
502QTemporaryFilePrivate::~QTemporaryFilePrivate()
503{
504}
505
506QAbstractFileEngine *QTemporaryFilePrivate::engine() const
507{
508 if (!fileEngine) {
509 fileEngine.reset(p: new QTemporaryFileEngine(&templateName));
510 resetFileEngine();
511 }
512 return fileEngine.get();
513}
514
515void QTemporaryFilePrivate::resetFileEngine() const
516{
517 if (!fileEngine)
518 return;
519
520 QTemporaryFileEngine *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
521 if (fileName.isEmpty())
522 tef->initialize(file: templateName, mode: 0600);
523 else
524 tef->initialize(file: fileName, mode: 0600, nameIsTemplate: false);
525}
526
527void QTemporaryFilePrivate::materializeUnnamedFile()
528{
529#ifdef LINUX_UNNAMED_TMPFILE
530 if (!fileName.isEmpty() || !fileEngine)
531 return;
532
533 auto *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
534 fileName = tef->fileName(file: QAbstractFileEngine::DefaultName);
535#endif
536}
537
538QString QTemporaryFilePrivate::defaultTemplateName()
539{
540 QString baseName;
541#if defined(QT_BUILD_CORE_LIB)
542 baseName = QCoreApplication::applicationName();
543 if (baseName.isEmpty())
544#endif
545 baseName = "qt_temp"_L1;
546
547 return QDir::tempPath() + u'/' + baseName + ".XXXXXX"_L1;
548}
549
550//************* QTemporaryFile
551
552/*!
553 \class QTemporaryFile
554 \inmodule QtCore
555 \reentrant
556 \brief The QTemporaryFile class is an I/O device that operates on temporary files.
557
558 \ingroup io
559
560
561 QTemporaryFile is used to create unique temporary files safely.
562 The file itself is created by calling open(). The name of the
563 temporary file is guaranteed to be unique (i.e., you are
564 guaranteed to not overwrite an existing file), and the file will
565 subsequently be removed upon destruction of the QTemporaryFile
566 object. This is an important technique that avoids data
567 corruption for applications that store data in temporary files.
568 The file name is either auto-generated, or created based on a
569 template, which is passed to QTemporaryFile's constructor.
570
571 Example:
572
573 \snippet code/src_corelib_io_qtemporaryfile.cpp 0
574
575 Reopening a QTemporaryFile after calling close() is safe. For as long as
576 the QTemporaryFile object itself is not destroyed, the unique temporary
577 file will exist and be kept open internally by QTemporaryFile.
578
579 The file name of the temporary file can be found by calling fileName().
580 Note that this is only defined after the file is first opened; the function
581 returns an empty string before this.
582
583 A temporary file will have some static part of the name and some
584 part that is calculated to be unique. The default filename will be
585 determined from QCoreApplication::applicationName() (otherwise \c qt_temp) and will
586 be placed into the temporary path as returned by QDir::tempPath().
587 If you specify your own filename, a relative file path will not be placed in the
588 temporary directory by default, but be relative to the current working directory.
589 It is important to specify the correct directory if the rename() function will be
590 called, as QTemporaryFile can only rename files within the same volume / filesystem
591 as the temporary file itself was created on.
592
593 Specified filenames can contain the following template \c XXXXXX
594 (six upper case "X" characters), which will be replaced by the
595 auto-generated portion of the filename. Note that the template is
596 case sensitive. If the template is not present in the filename,
597 QTemporaryFile appends the generated part to the filename given.
598
599 \note On Linux, QTemporaryFile will attempt to create unnamed temporary
600 files. If that succeeds, open() will return true but exists() will be
601 false. If you call fileName() or any function that calls it,
602 QTemporaryFile will give the file a name, so most applications will
603 not see a difference.
604
605 \sa QDir::tempPath(), QFile
606*/
607
608#ifdef QT_NO_QOBJECT
609QTemporaryFile::QTemporaryFile()
610 : QFile(*new QTemporaryFilePrivate)
611{
612}
613
614QTemporaryFile::QTemporaryFile(const QString &templateName)
615 : QFile(*new QTemporaryFilePrivate(templateName))
616{
617}
618
619#else
620/*!
621 Constructs a QTemporaryFile using as file template
622 the application name returned by QCoreApplication::applicationName()
623 (otherwise \c qt_temp) followed by ".XXXXXX".
624 The file is stored in the system's temporary directory, QDir::tempPath().
625
626 \sa setFileTemplate(), QDir::tempPath()
627*/
628QTemporaryFile::QTemporaryFile()
629 : QTemporaryFile(nullptr)
630{
631}
632
633/*!
634 Constructs a QTemporaryFile with a template filename of \a
635 templateName. Upon opening the temporary file this will be used to create
636 a unique filename.
637
638 If the \a templateName does not contain XXXXXX it will automatically be
639 appended and used as the dynamic portion of the filename.
640
641 If \a templateName is a relative path, the path will be relative to the
642 current working directory. You can use QDir::tempPath() to construct \a
643 templateName if you want use the system's temporary directory. It is
644 important to specify the correct directory if the rename() function will be
645 called, as QTemporaryFile can only rename files within the same volume /
646 filesystem as the temporary file itself was created on.
647
648
649 \sa open(), fileTemplate()
650*/
651QTemporaryFile::QTemporaryFile(const QString &templateName)
652 : QTemporaryFile(templateName, nullptr)
653{
654}
655
656/*!
657 Constructs a QTemporaryFile (with the given \a parent)
658 using as file template the application name returned by QCoreApplication::applicationName()
659 (otherwise \c qt_temp) followed by ".XXXXXX".
660 The file is stored in the system's temporary directory, QDir::tempPath().
661
662 \sa setFileTemplate()
663*/
664QTemporaryFile::QTemporaryFile(QObject *parent)
665 : QFile(*new QTemporaryFilePrivate, parent)
666{
667}
668
669/*!
670 Constructs a QTemporaryFile with a template filename of \a
671 templateName and the specified \a parent.
672 Upon opening the temporary file this will be used to
673 create a unique filename.
674
675 If the \a templateName does not contain XXXXXX it will automatically be
676 appended and used as the dynamic portion of the filename.
677
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. It is
681 important to specify the correct directory if the rename() function will be
682 called, as QTemporaryFile can only rename files within the same volume /
683 filesystem as the temporary file itself was created on.
684
685 \sa open(), fileTemplate()
686*/
687QTemporaryFile::QTemporaryFile(const QString &templateName, QObject *parent)
688 : QFile(*new QTemporaryFilePrivate(templateName), parent)
689{
690}
691#endif
692
693/*!
694 Destroys the temporary file object, the file is automatically
695 closed if necessary and if in auto remove mode it will
696 automatically delete the file.
697
698 \sa autoRemove()
699*/
700QTemporaryFile::~QTemporaryFile()
701{
702 Q_D(QTemporaryFile);
703 close();
704 if (!d->fileName.isEmpty() && d->autoRemove)
705 remove();
706}
707
708/*!
709 \fn bool QTemporaryFile::open()
710
711 A QTemporaryFile will always be opened in QIODevice::ReadWrite mode,
712 this allows easy access to the data in the file. This function will
713 return true upon success and will set the fileName() to the unique
714 filename used.
715
716 \sa fileName()
717*/
718
719/*!
720 Returns \c true if the QTemporaryFile is in auto remove
721 mode. Auto-remove mode will automatically delete the filename from
722 disk upon destruction. This makes it very easy to create your
723 QTemporaryFile object on the stack, fill it with data, read from
724 it, and finally on function return it will automatically clean up
725 after itself.
726
727 Auto-remove is on by default.
728
729 \sa setAutoRemove(), remove()
730*/
731bool QTemporaryFile::autoRemove() const
732{
733 Q_D(const QTemporaryFile);
734 return d->autoRemove;
735}
736
737/*!
738 Sets the QTemporaryFile into auto-remove mode if \a b is \c true.
739
740 Auto-remove is on by default.
741
742 If you set this property to \c false, ensure the application provides a way
743 to remove the file once it is no longer needed, including passing the
744 responsibility on to another process. Always use the fileName() function to
745 obtain the name and never try to guess the name that QTemporaryFile has
746 generated.
747
748 On some systems, if fileName() is not called before closing the file, the
749 temporary file may be removed regardless of the state of this property.
750 This behavior should not be relied upon, so application code should either
751 call fileName() or leave the auto removal functionality enabled.
752
753 \sa autoRemove(), remove()
754*/
755void QTemporaryFile::setAutoRemove(bool b)
756{
757 Q_D(QTemporaryFile);
758 d->autoRemove = b;
759}
760
761/*!
762 Returns the complete unique filename backing the QTemporaryFile
763 object. This string is null before the QTemporaryFile is opened,
764 afterwards it will contain the fileTemplate() plus
765 additional characters to make it unique.
766
767 \sa fileTemplate()
768*/
769
770QString QTemporaryFile::fileName() const
771{
772 Q_D(const QTemporaryFile);
773 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
774 if (tef && tef->isReallyOpen())
775 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
776
777 if (d->fileName.isEmpty())
778 return QString();
779 return d->engine()->fileName(file: QAbstractFileEngine::DefaultName);
780}
781
782/*!
783 Returns the set file template. The default file template will be
784 called qcoreappname.XXXXXX and be placed in QDir::tempPath().
785
786 \sa setFileTemplate()
787*/
788QString QTemporaryFile::fileTemplate() const
789{
790 Q_D(const QTemporaryFile);
791 return d->templateName;
792}
793
794/*!
795 Sets the static portion of the file name to \a name. If the file
796 template contains XXXXXX that will automatically be replaced with
797 the unique part of the filename, otherwise a filename will be
798 determined automatically based on the static portion specified.
799
800 If \a name contains a relative file path, the path will be relative to the
801 current working directory. You can use QDir::tempPath() to construct \a
802 name if you want use the system's temporary directory. It is important to
803 specify the correct directory if the rename() function will be called, as
804 QTemporaryFile can only rename files within the same volume / filesystem as
805 the temporary file itself was created on.
806
807 \sa fileTemplate()
808*/
809void QTemporaryFile::setFileTemplate(const QString &name)
810{
811 Q_D(QTemporaryFile);
812 d->templateName = name;
813}
814
815/*!
816 Renames the current temporary file to \a newName and returns true if it
817 succeeded.
818
819 This function has an important difference compared to QFile::rename(): it
820 will not perform a copy+delete if the low-level system call to rename the
821 file fails, something that could happen if \a newName specifies a file in a
822 different volume or filesystem than the temporary file was created on. In
823 other words, QTemporaryFile only supports atomic file renaming.
824
825 This functionality is intended to support materializing the destination
826 file with all contents already present, so another process cannot see an
827 incomplete file in the process of being written. The \l QSaveFile class can
828 be used for a similar purpose too, particularly if the destination file is
829 not temporary.
830
831 \sa QSaveFile, QSaveFile::commit(), QFile::rename()
832*/
833bool QTemporaryFile::rename(const QString &newName)
834{
835 Q_D(QTemporaryFile);
836 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
837 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
838 return QFile::rename(newName);
839
840 unsetError();
841 close();
842 if (error() == QFile::NoError) {
843 if (tef->rename(newName)) {
844 unsetError();
845 // engine was able to handle the new name so we just reset it
846 tef->setFileName(newName);
847 d->fileName = newName;
848 return true;
849 }
850
851 d->setError(err: QFile::RenameError, errorString: tef->errorString());
852 }
853 return false;
854}
855
856/*!
857 \fn QTemporaryFile *QTemporaryFile::createNativeFile(const QString &fileName)
858 \overload
859
860 Works on the given \a fileName rather than an existing QFile
861 object.
862*/
863
864
865/*!
866 If \a file is not already a native file, then a QTemporaryFile is created
867 in QDir::tempPath(), the contents of \a file is copied into it, and a pointer
868 to the temporary file is returned. Does nothing and returns \c 0 if \a file
869 is already a native file.
870
871 For example:
872
873 \snippet code/src_corelib_io_qtemporaryfile.cpp 1
874
875 \sa QFileInfo::isNativePath()
876*/
877
878QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
879{
880 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
881 if (engine->fileFlags(type: QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
882 return nullptr; // native already
883 //cache
884 bool wasOpen = file.isOpen();
885 qint64 old_off = 0;
886 if (wasOpen)
887 old_off = file.pos();
888 else if (!file.open(flags: QIODevice::ReadOnly))
889 return nullptr;
890 //dump data
891 QTemporaryFile *ret = new QTemporaryFile;
892 if (ret->open()) {
893 file.seek(offset: 0);
894 char buffer[1024];
895 while (true) {
896 qint64 len = file.read(data: buffer, maxlen: 1024);
897 if (len < 1)
898 break;
899 ret->write(data: buffer, len);
900 }
901 ret->seek(offset: 0);
902 } else {
903 delete ret;
904 ret = nullptr;
905 }
906 //restore
907 if (wasOpen)
908 file.seek(offset: old_off);
909 else
910 file.close();
911 //done
912 return ret;
913 }
914 return nullptr;
915}
916
917/*!
918 \reimp
919
920 Creates a unique file name for the temporary file, and opens it. You can
921 get the unique name later by calling fileName(). The file is guaranteed to
922 have been created by this function (i.e., it has never existed before).
923*/
924bool QTemporaryFile::open(OpenMode flags)
925{
926 Q_D(QTemporaryFile);
927 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
928 if (tef && tef->isReallyOpen()) {
929 setOpenMode(flags);
930 return true;
931 }
932
933 // reset the engine state so it creates a new, unique file name from the template;
934 // equivalent to:
935 // delete d->fileEngine;
936 // d->fileEngine = 0;
937 // d->engine();
938 d->resetFileEngine();
939
940 if (QFile::open(flags)) {
941 tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
942 if (tef->isUnnamedFile())
943 d->fileName.clear();
944 else
945 d->fileName = tef->fileName(file: QAbstractFileEngine::DefaultName);
946 return true;
947 }
948 return false;
949}
950
951#endif // QT_NO_TEMPORARYFILE
952
953QT_END_NAMESPACE
954
955#ifndef QT_NO_QOBJECT
956#include "moc_qtemporaryfile.cpp"
957#endif
958

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