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 
60 QT_BEGIN_NAMESPACE
61 
62 #if defined(Q_OS_WIN)
63 typedef ushort Char;
64 
Latin1Char(char ch)65 static inline Char Latin1Char(char ch)
66 {
67     return ushort(uchar(ch));
68 }
69 
70 typedef HANDLE NativeFileHandle;
71 
72 #else // POSIX
73 typedef char Char;
74 typedef char Latin1Char;
75 typedef int NativeFileHandle;
76 #endif
77 
QTemporaryFileName(const QString & templateName)78 QTemporaryFileName::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 */
generateNext()143 QFileSystemEntry::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 */
createFileFromTemplate(NativeFileHandle & file,QTemporaryFileName & templ,quint32 mode,int flags,QSystemError & error)209 static 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 #  ifndef Q_OS_WINRT
223         file = CreateFile((const wchar_t *)path.constData(),
224                 GENERIC_READ | GENERIC_WRITE,
225                 shareMode, NULL, CREATE_NEW,
226                 FILE_ATTRIBUTE_NORMAL, NULL);
227 #  else // !Q_OS_WINRT
228         file = CreateFile2((const wchar_t *)path.constData(),
229                 GENERIC_READ | GENERIC_WRITE,
230                 shareMode, CREATE_NEW,
231                 NULL);
232 #  endif // Q_OS_WINRT
233 
234         if (file != INVALID_HANDLE_VALUE)
235             return true;
236 
237         DWORD err = GetLastError();
238         if (err == ERROR_ACCESS_DENIED) {
239             WIN32_FILE_ATTRIBUTE_DATA attributes;
240             if (!GetFileAttributesEx((const wchar_t *)path.constData(),
241                                      GetFileExInfoStandard, &attributes)
242                     || attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
243                 // Potential write error (read-only parent directory, etc.).
244                 error = QSystemError(err, QSystemError::NativeError);
245                 return false;
246             } // else file already exists as a directory.
247         } else if (err != ERROR_FILE_EXISTS) {
248             error = QSystemError(err, QSystemError::NativeError);
249             return false;
250         }
251 #else // POSIX
252         Q_UNUSED(flags)
253         file = QT_OPEN(path.constData(),
254                 QT_OPEN_CREAT | QT_OPEN_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
255                 static_cast<mode_t>(mode));
256 
257         if (file != -1)
258             return true;
259 
260         int err = errno;
261         if (err != EEXIST) {
262             error = QSystemError(err, QSystemError::NativeError);
263             return false;
264         }
265 #endif
266     }
267 
268     return false;
269 }
270 
271 enum class CreateUnnamedFileStatus {
272     Success = 0,
273     NotSupported,
274     OtherError
275 };
276 
277 static CreateUnnamedFileStatus
createUnnamedFile(NativeFileHandle & file,QTemporaryFileName & tfn,quint32 mode,QSystemError * error)278 createUnnamedFile(NativeFileHandle &file, QTemporaryFileName &tfn, quint32 mode, QSystemError *error)
279 {
280 #ifdef LINUX_UNNAMED_TMPFILE
281     // first, check if we have /proc, otherwise can't make the file exist later
282     // (no error message set, as caller will try regular temporary file)
283     if (!qt_haveLinuxProcfs())
284         return CreateUnnamedFileStatus::NotSupported;
285 
286     const char *p = ".";
287     QByteArray::size_type lastSlash = tfn.path.lastIndexOf('/');
288     if (lastSlash >= 0) {
289         if (lastSlash == 0)
290             lastSlash = 1;
291         tfn.path[lastSlash] = '\0';
292         p = tfn.path.data();
293     }
294 
295     file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
296             static_cast<mode_t>(mode));
297     if (file != -1)
298         return CreateUnnamedFileStatus::Success;
299 
300     if (errno == EOPNOTSUPP || errno == EISDIR) {
301         // fs or kernel doesn't support O_TMPFILE, so
302         // put the slash back so we may try a regular file
303         if (lastSlash != -1)
304             tfn.path[lastSlash] = '/';
305         return CreateUnnamedFileStatus::NotSupported;
306     }
307 
308     // real error
309     *error = QSystemError(errno, QSystemError::NativeError);
310     return CreateUnnamedFileStatus::OtherError;
311 #else
312     Q_UNUSED(file);
313     Q_UNUSED(tfn);
314     Q_UNUSED(mode);
315     Q_UNUSED(error);
316     return CreateUnnamedFileStatus::NotSupported;
317 #endif
318 }
319 
320 //************* QTemporaryFileEngine
~QTemporaryFileEngine()321 QTemporaryFileEngine::~QTemporaryFileEngine()
322 {
323     Q_D(QFSFileEngine);
324     d->unmapAll();
325     QFSFileEngine::close();
326 }
327 
isReallyOpen() const328 bool QTemporaryFileEngine::isReallyOpen() const
329 {
330     Q_D(const QFSFileEngine);
331 
332     if (!((nullptr == d->fh) && (-1 == d->fd)
333 #if defined Q_OS_WIN
334                 && (INVALID_HANDLE_VALUE == d->fileHandle)
335 #endif
336             ))
337         return true;
338 
339     return false;
340 
341 }
342 
setFileName(const QString & file)343 void QTemporaryFileEngine::setFileName(const QString &file)
344 {
345     // Really close the file, so we don't leak
346     QFSFileEngine::close();
347     QFSFileEngine::setFileName(file);
348 }
349 
open(QIODevice::OpenMode openMode)350 bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
351 {
352     Q_D(QFSFileEngine);
353     Q_ASSERT(!isReallyOpen());
354 
355     openMode |= QIODevice::ReadWrite;
356 
357     if (!filePathIsTemplate)
358         return QFSFileEngine::open(openMode);
359 
360     QTemporaryFileName tfn(templateName);
361 
362     QSystemError error;
363 #if defined(Q_OS_WIN)
364     NativeFileHandle &file = d->fileHandle;
365 #else // POSIX
366     NativeFileHandle &file = d->fd;
367 #endif
368 
369     CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
370     if (st == CreateUnnamedFileStatus::Success) {
371         unnamedFile = true;
372         d->fileEntry.clear();
373     } else if (st == CreateUnnamedFileStatus::NotSupported &&
374                createFileFromTemplate(file, tfn, fileMode, flags, error)) {
375         filePathIsTemplate = false;
376         unnamedFile = false;
377         d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
378     } else {
379         setError(QFile::OpenError, error.toString());
380         return false;
381     }
382 
383 #if !defined(Q_OS_WIN) || defined(Q_OS_WINRT)
384     d->closeFileHandle = true;
385 #endif
386 
387     d->openMode = openMode;
388     d->lastFlushFailed = false;
389     d->tried_stat = 0;
390 
391     return true;
392 }
393 
remove()394 bool QTemporaryFileEngine::remove()
395 {
396     Q_D(QFSFileEngine);
397     // Since the QTemporaryFileEngine::close() does not really close the file,
398     // we must explicitly call QFSFileEngine::close() before we remove it.
399     d->unmapAll();
400     QFSFileEngine::close();
401     if (isUnnamedFile())
402         return true;
403     if (!filePathIsTemplate && QFSFileEngine::remove()) {
404         d->fileEntry.clear();
405         // If a QTemporaryFile is constructed using a template file path, the path
406         // is generated in QTemporaryFileEngine::open() and then filePathIsTemplate
407         // is set to false. If remove() and then open() are called on the same
408         // QTemporaryFile, the path is not regenerated. Here we ensure that if the
409         // file path was generated, it will be generated again in the scenario above.
410         filePathIsTemplate = filePathWasTemplate;
411         return true;
412     }
413     return false;
414 }
415 
rename(const QString & newName)416 bool QTemporaryFileEngine::rename(const QString &newName)
417 {
418     if (isUnnamedFile()) {
419         bool ok = materializeUnnamedFile(newName, DontOverwrite);
420         QFSFileEngine::close();
421         return ok;
422     }
423     QFSFileEngine::close();
424     return QFSFileEngine::rename(newName);
425 }
426 
renameOverwrite(const QString & newName)427 bool QTemporaryFileEngine::renameOverwrite(const QString &newName)
428 {
429     if (isUnnamedFile()) {
430         bool ok = materializeUnnamedFile(newName, Overwrite);
431         QFSFileEngine::close();
432         return ok;
433     }
434     QFSFileEngine::close();
435     return QFSFileEngine::renameOverwrite(newName);
436 }
437 
close()438 bool QTemporaryFileEngine::close()
439 {
440     // Don't close the file, just seek to the front.
441     seek(0);
442     setError(QFile::UnspecifiedError, QString());
443     return true;
444 }
445 
fileName(QAbstractFileEngine::FileName file) const446 QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file) const
447 {
448     if (isUnnamedFile()) {
449         if (file == LinkName) {
450             // we know our file isn't (won't be) a symlink
451             return QString();
452         }
453 
454         // for all other cases, materialize the file
455         const_cast<QTemporaryFileEngine *>(this)->materializeUnnamedFile(templateName, NameIsTemplate);
456     }
457     return QFSFileEngine::fileName(file);
458 }
459 
materializeUnnamedFile(const QString & newName,QTemporaryFileEngine::MaterializationMode mode)460 bool QTemporaryFileEngine::materializeUnnamedFile(const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
461 {
462     Q_ASSERT(isUnnamedFile());
463 
464 #ifdef LINUX_UNNAMED_TMPFILE
465     Q_D(QFSFileEngine);
466     const QByteArray src = "/proc/self/fd/" + QByteArray::number(d->fd);
467     auto materializeAt = [=](const QFileSystemEntry &dst) {
468         return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
469     };
470 #else
471     auto materializeAt = [](const QFileSystemEntry &) { return false; };
472 #endif
473 
474     auto success = [this](const QFileSystemEntry &entry) {
475         filePathIsTemplate = false;
476         unnamedFile = false;
477         d_func()->fileEntry = entry;
478         return true;
479     };
480 
481     auto materializeAsTemplate = [=](const QString &newName) {
482         QTemporaryFileName tfn(newName);
483         static const int maxAttempts = 16;
484         for (int attempt = 0; attempt < maxAttempts; ++attempt) {
485             tfn.generateNext();
486             QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
487             if (materializeAt(entry))
488                 return success(entry);
489         }
490         return false;
491     };
492 
493     if (mode == NameIsTemplate) {
494         if (materializeAsTemplate(newName))
495             return true;
496     } else {
497         // Use linkat to materialize the file
498         QFileSystemEntry dst(newName);
499         if (materializeAt(dst))
500             return success(dst);
501 
502         if (errno == EEXIST && mode == Overwrite) {
503             // retry by first creating a temporary file in the right dir
504             if (!materializeAsTemplate(templateName))
505                 return false;
506 
507             // then rename the materialized file to target (same as renameOverwrite)
508             QFSFileEngine::close();
509             return QFSFileEngine::renameOverwrite(newName);
510         }
511     }
512 
513     // failed
514     setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
515     return false;
516 }
517 
isUnnamedFile() const518 bool QTemporaryFileEngine::isUnnamedFile() const
519 {
520 #ifdef LINUX_UNNAMED_TMPFILE
521     if (unnamedFile) {
522         Q_ASSERT(d_func()->fileEntry.isEmpty());
523         Q_ASSERT(filePathIsTemplate);
524     }
525     return unnamedFile;
526 #else
527     return false;
528 #endif
529 }
530 
531 //************* QTemporaryFilePrivate
532 
QTemporaryFilePrivate()533 QTemporaryFilePrivate::QTemporaryFilePrivate()
534 {
535 }
536 
QTemporaryFilePrivate(const QString & templateNameIn)537 QTemporaryFilePrivate::QTemporaryFilePrivate(const QString &templateNameIn)
538     : templateName(templateNameIn)
539 {
540 }
541 
~QTemporaryFilePrivate()542 QTemporaryFilePrivate::~QTemporaryFilePrivate()
543 {
544 }
545 
engine() const546 QAbstractFileEngine *QTemporaryFilePrivate::engine() const
547 {
548     if (!fileEngine) {
549         fileEngine.reset(new QTemporaryFileEngine(&templateName));
550         resetFileEngine();
551     }
552     return fileEngine.get();
553 }
554 
resetFileEngine() const555 void QTemporaryFilePrivate::resetFileEngine() const
556 {
557     if (!fileEngine)
558         return;
559 
560     QTemporaryFileEngine *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
561     if (fileName.isEmpty())
562         tef->initialize(templateName, 0600);
563     else
564         tef->initialize(fileName, 0600, false);
565 }
566 
materializeUnnamedFile()567 void QTemporaryFilePrivate::materializeUnnamedFile()
568 {
569 #ifdef LINUX_UNNAMED_TMPFILE
570     if (!fileName.isEmpty() || !fileEngine)
571         return;
572 
573     auto *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
574     fileName = tef->fileName(QAbstractFileEngine::DefaultName);
575 #endif
576 }
577 
defaultTemplateName()578 QString QTemporaryFilePrivate::defaultTemplateName()
579 {
580     QString baseName;
581 #if defined(QT_BUILD_CORE_LIB)
582     baseName = QCoreApplication::applicationName();
583     if (baseName.isEmpty())
584 #endif
585         baseName = QLatin1String("qt_temp");
586 
587     return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String(".XXXXXX");
588 }
589 
590 //************* QTemporaryFile
591 
592 /*!
593     \class QTemporaryFile
594     \inmodule QtCore
595     \reentrant
596     \brief The QTemporaryFile class is an I/O device that operates on temporary files.
597 
598     \ingroup io
599 
600 
601     QTemporaryFile is used to create unique temporary files safely.
602     The file itself is created by calling open(). The name of the
603     temporary file is guaranteed to be unique (i.e., you are
604     guaranteed to not overwrite an existing file), and the file will
605     subsequently be removed upon destruction of the QTemporaryFile
606     object. This is an important technique that avoids data
607     corruption for applications that store data in temporary files.
608     The file name is either auto-generated, or created based on a
609     template, which is passed to QTemporaryFile's constructor.
610 
611     Example:
612 
613     \snippet code/src_corelib_io_qtemporaryfile.cpp 0
614 
615     Reopening a QTemporaryFile after calling close() is safe. For as long as
616     the QTemporaryFile object itself is not destroyed, the unique temporary
617     file will exist and be kept open internally by QTemporaryFile.
618 
619     The file name of the temporary file can be found by calling fileName().
620     Note that this is only defined after the file is first opened; the function
621     returns an empty string before this.
622 
623     A temporary file will have some static part of the name and some
624     part that is calculated to be unique. The default filename will be
625     determined from QCoreApplication::applicationName() (otherwise \c qt_temp) and will
626     be placed into the temporary path as returned by QDir::tempPath().
627     If you specify your own filename, a relative file path will not be placed in the
628     temporary directory by default, but be relative to the current working directory.
629 
630     Specified filenames can contain the following template \c XXXXXX
631     (six upper case "X" characters), which will be replaced by the
632     auto-generated portion of the filename. Note that the template is
633     case sensitive. If the template is not present in the filename,
634     QTemporaryFile appends the generated part to the filename given.
635 
636     \note On Linux, QTemporaryFile will attempt to create unnamed temporary
637     files. If that succeeds, open() will return true but exists() will be
638     false. If you call fileName() or any function that calls it,
639     QTemporaryFile will give the file a name, so most applications will
640     not see a difference.
641 
642     \sa QDir::tempPath(), QFile
643 */
644 
645 #ifdef QT_NO_QOBJECT
QTemporaryFile()646 QTemporaryFile::QTemporaryFile()
647     : QFile(*new QTemporaryFilePrivate)
648 {
649 }
650 
QTemporaryFile(const QString & templateName)651 QTemporaryFile::QTemporaryFile(const QString &templateName)
652     : QFile(*new QTemporaryFilePrivate(templateName))
653 {
654 }
655 
656 #else
657 /*!
658     Constructs a QTemporaryFile using as file template
659     the application name returned by QCoreApplication::applicationName()
660     (otherwise \c qt_temp) followed by ".XXXXXX".
661     The file is stored in the system's temporary directory, QDir::tempPath().
662 
663     \sa setFileTemplate(), QDir::tempPath()
664 */
QTemporaryFile()665 QTemporaryFile::QTemporaryFile()
666     : QTemporaryFile(nullptr)
667 {
668 }
669 
670 /*!
671     Constructs a QTemporaryFile with a template filename of \a
672     templateName. Upon opening the temporary file this will be used to create
673     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.
681 
682     \sa open(), fileTemplate()
683 */
QTemporaryFile(const QString & templateName)684 QTemporaryFile::QTemporaryFile(const QString &templateName)
685     : QTemporaryFile(templateName, nullptr)
686 {
687 }
688 
689 /*!
690     Constructs a QTemporaryFile (with the given \a parent)
691     using as file template the application name returned by QCoreApplication::applicationName()
692     (otherwise \c qt_temp) followed by ".XXXXXX".
693     The file is stored in the system's temporary directory, QDir::tempPath().
694 
695     \sa setFileTemplate()
696 */
QTemporaryFile(QObject * parent)697 QTemporaryFile::QTemporaryFile(QObject *parent)
698     : QFile(*new QTemporaryFilePrivate, parent)
699 {
700 }
701 
702 /*!
703     Constructs a QTemporaryFile with a template filename of \a
704     templateName and the specified \a parent.
705     Upon opening the temporary file this will be used to
706     create a unique filename.
707 
708     If the \a templateName does not contain XXXXXX it will automatically be
709     appended and used as the dynamic portion of the filename.
710 
711     If \a templateName is a relative path, the path will be relative to the
712     current working directory. You can use QDir::tempPath() to construct \a
713     templateName if you want use the system's temporary directory.
714 
715     \sa open(), fileTemplate()
716 */
QTemporaryFile(const QString & templateName,QObject * parent)717 QTemporaryFile::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 */
~QTemporaryFile()730 QTemporaryFile::~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()
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 */
autoRemove() const761 bool 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 */
setAutoRemove(bool b)785 void 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    \sa fileTemplate()
798 */
799 
fileName() const800 QString QTemporaryFile::fileName() const
801 {
802     Q_D(const QTemporaryFile);
803     auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
804     if (tef && tef->isReallyOpen())
805         const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
806 
807     if(d->fileName.isEmpty())
808         return QString();
809     return d->engine()->fileName(QAbstractFileEngine::DefaultName);
810 }
811 
812 /*!
813   Returns the set file template. The default file template will be
814   called qcoreappname.XXXXXX and be placed in QDir::tempPath().
815 
816   \sa setFileTemplate()
817 */
fileTemplate() const818 QString QTemporaryFile::fileTemplate() const
819 {
820     Q_D(const QTemporaryFile);
821     return d->templateName;
822 }
823 
824 /*!
825    Sets the static portion of the file name to \a name. If the file
826    template contains XXXXXX that will automatically be replaced with
827    the unique part of the filename, otherwise a filename will be
828    determined automatically based on the static portion specified.
829 
830     If \a name contains a relative file path, the path will be relative to the
831     current working directory. You can use QDir::tempPath() to construct \a
832     name if you want use the system's temporary directory.
833 
834    \sa fileTemplate()
835 */
setFileTemplate(const QString & name)836 void QTemporaryFile::setFileTemplate(const QString &name)
837 {
838     Q_D(QTemporaryFile);
839     d->templateName = name;
840 }
841 
842 /*!
843     \internal
844 
845     This is just a simplified version of QFile::rename() because we know a few
846     extra details about what kind of file we have. The documentation is hidden
847     from the user because QFile::rename() should be enough.
848 */
rename(const QString & newName)849 bool QTemporaryFile::rename(const QString &newName)
850 {
851     Q_D(QTemporaryFile);
852     auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
853     if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
854         return QFile::rename(newName);
855 
856     unsetError();
857     close();
858     if (error() == QFile::NoError) {
859         if (tef->rename(newName)) {
860             unsetError();
861             // engine was able to handle the new name so we just reset it
862             tef->setFileName(newName);
863             d->fileName = newName;
864             return true;
865         }
866 
867         d->setError(QFile::RenameError, tef->errorString());
868     }
869     return false;
870 }
871 
872 /*!
873   \fn QTemporaryFile *QTemporaryFile::createLocalFile(const QString &fileName)
874   \overload
875   \obsolete
876 
877   Use QTemporaryFile::createNativeFile(const QString &fileName) instead.
878 */
879 
880 /*!
881   \fn QTemporaryFile *QTemporaryFile::createLocalFile(QFile &file)
882   \obsolete
883 
884   Use QTemporaryFile::createNativeFile(QFile &file) instead.
885 */
886 
887 /*!
888   \fn QTemporaryFile *QTemporaryFile::createNativeFile(const QString &fileName)
889   \overload
890 
891   Works on the given \a fileName rather than an existing QFile
892   object.
893 */
894 
895 
896 /*!
897   If \a file is not already a native file, then a QTemporaryFile is created
898   in QDir::tempPath(), the contents of \a file is copied into it, and a pointer
899   to the temporary file is returned. Does nothing and returns \c 0 if \a file
900   is already a native file.
901 
902   For example:
903 
904   \snippet code/src_corelib_io_qtemporaryfile.cpp 1
905 
906   \sa QFileInfo::isNativePath()
907 */
908 
createNativeFile(QFile & file)909 QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
910 {
911     if (QAbstractFileEngine *engine = file.d_func()->engine()) {
912         if(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
913             return nullptr; // native already
914         //cache
915         bool wasOpen = file.isOpen();
916         qint64 old_off = 0;
917         if(wasOpen)
918             old_off = file.pos();
919         else if (!file.open(QIODevice::ReadOnly))
920             return nullptr;
921         //dump data
922         QTemporaryFile *ret = new QTemporaryFile;
923         if (ret->open()) {
924             file.seek(0);
925             char buffer[1024];
926             while (true) {
927                 qint64 len = file.read(buffer, 1024);
928                 if (len < 1)
929                     break;
930                 ret->write(buffer, len);
931             }
932             ret->seek(0);
933         } else {
934             delete ret;
935             ret = nullptr;
936         }
937         //restore
938         if(wasOpen)
939             file.seek(old_off);
940         else
941             file.close();
942         //done
943         return ret;
944     }
945     return nullptr;
946 }
947 
948 /*!
949    \reimp
950 
951     Creates a unique file name for the temporary file, and opens it.  You can
952     get the unique name later by calling fileName(). The file is guaranteed to
953     have been created by this function (i.e., it has never existed before).
954 */
open(OpenMode flags)955 bool QTemporaryFile::open(OpenMode flags)
956 {
957     Q_D(QTemporaryFile);
958     auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
959     if (tef && tef->isReallyOpen()) {
960         setOpenMode(flags);
961         return true;
962     }
963 
964     // reset the engine state so it creates a new, unique file name from the template;
965     // equivalent to:
966     //    delete d->fileEngine;
967     //    d->fileEngine = 0;
968     //    d->engine();
969     d->resetFileEngine();
970 
971     if (QFile::open(flags)) {
972         tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
973         if (tef->isUnnamedFile())
974             d->fileName.clear();
975         else
976             d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
977         return true;
978     }
979     return false;
980 }
981 
982 #endif // QT_NO_TEMPORARYFILE
983 
984 QT_END_NAMESPACE
985 
986 #ifndef QT_NO_QOBJECT
987 #include "moc_qtemporaryfile.cpp"
988 #endif
989