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