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# 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
271enum class CreateUnnamedFileStatus {
272 Success = 0,
273 NotSupported,
274 OtherError
275};
276
277static CreateUnnamedFileStatus
278createUnnamedFile(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 int lastSlash = tfn.path.lastIndexOf('/');
288 if (lastSlash != -1) {
289 tfn.path[lastSlash] = '\0';
290 p = tfn.path.data();
291 }
292
293 file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
294 static_cast<mode_t>(mode));
295 if (file != -1)
296 return CreateUnnamedFileStatus::Success;
297
298 if (errno == EOPNOTSUPP || errno == EISDIR) {
299 // fs or kernel doesn't support O_TMPFILE, so
300 // put the slash back so we may try a regular file
301 if (lastSlash != -1)
302 tfn.path[lastSlash] = '/';
303 return CreateUnnamedFileStatus::NotSupported;
304 }
305
306 // real error
307 *error = QSystemError(errno, QSystemError::NativeError);
308 return CreateUnnamedFileStatus::OtherError;
309#else
310 Q_UNUSED(file);
311 Q_UNUSED(tfn);
312 Q_UNUSED(mode);
313 Q_UNUSED(error);
314 return CreateUnnamedFileStatus::NotSupported;
315#endif
316}
317
318//************* QTemporaryFileEngine
319QTemporaryFileEngine::~QTemporaryFileEngine()
320{
321 Q_D(QFSFileEngine);
322 d->unmapAll();
323 QFSFileEngine::close();
324}
325
326bool QTemporaryFileEngine::isReallyOpen() const
327{
328 Q_D(const QFSFileEngine);
329
330 if (!((nullptr == d->fh) && (-1 == d->fd)
331#if defined Q_OS_WIN
332 && (INVALID_HANDLE_VALUE == d->fileHandle)
333#endif
334 ))
335 return true;
336
337 return false;
338
339}
340
341void QTemporaryFileEngine::setFileName(const QString &file)
342{
343 // Really close the file, so we don't leak
344 QFSFileEngine::close();
345 QFSFileEngine::setFileName(file);
346}
347
348bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
349{
350 Q_D(QFSFileEngine);
351 Q_ASSERT(!isReallyOpen());
352
353 openMode |= QIODevice::ReadWrite;
354
355 if (!filePathIsTemplate)
356 return QFSFileEngine::open(openMode);
357
358 QTemporaryFileName tfn(templateName);
359
360 QSystemError error;
361#if defined(Q_OS_WIN)
362 NativeFileHandle &file = d->fileHandle;
363#else // POSIX
364 NativeFileHandle &file = d->fd;
365#endif
366
367 CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
368 if (st == CreateUnnamedFileStatus::Success) {
369 unnamedFile = true;
370 d->fileEntry.clear();
371 } else if (st == CreateUnnamedFileStatus::NotSupported &&
372 createFileFromTemplate(file, tfn, fileMode, flags, error)) {
373 filePathIsTemplate = false;
374 unnamedFile = false;
375 d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
376 } else {
377 setError(QFile::OpenError, error.toString());
378 return false;
379 }
380
381#if !defined(Q_OS_WIN) || defined(Q_OS_WINRT)
382 d->closeFileHandle = true;
383#endif
384
385 d->openMode = openMode;
386 d->lastFlushFailed = false;
387 d->tried_stat = 0;
388
389 return true;
390}
391
392bool QTemporaryFileEngine::remove()
393{
394 Q_D(QFSFileEngine);
395 // Since the QTemporaryFileEngine::close() does not really close the file,
396 // we must explicitly call QFSFileEngine::close() before we remove it.
397 d->unmapAll();
398 QFSFileEngine::close();
399 if (isUnnamedFile())
400 return true;
401 if (!filePathIsTemplate && QFSFileEngine::remove()) {
402 d->fileEntry.clear();
403 // If a QTemporaryFile is constructed using a template file path, the path
404 // is generated in QTemporaryFileEngine::open() and then filePathIsTemplate
405 // is set to false. If remove() and then open() are called on the same
406 // QTemporaryFile, the path is not regenerated. Here we ensure that if the
407 // file path was generated, it will be generated again in the scenario above.
408 filePathIsTemplate = filePathWasTemplate;
409 return true;
410 }
411 return false;
412}
413
414bool QTemporaryFileEngine::rename(const QString &newName)
415{
416 if (isUnnamedFile()) {
417 bool ok = materializeUnnamedFile(newName, DontOverwrite);
418 QFSFileEngine::close();
419 return ok;
420 }
421 QFSFileEngine::close();
422 return QFSFileEngine::rename(newName);
423}
424
425bool QTemporaryFileEngine::renameOverwrite(const QString &newName)
426{
427 if (isUnnamedFile()) {
428 bool ok = materializeUnnamedFile(newName, Overwrite);
429 QFSFileEngine::close();
430 return ok;
431 }
432 QFSFileEngine::close();
433 return QFSFileEngine::renameOverwrite(newName);
434}
435
436bool QTemporaryFileEngine::close()
437{
438 // Don't close the file, just seek to the front.
439 seek(0);
440 setError(QFile::UnspecifiedError, QString());
441 return true;
442}
443
444QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file) const
445{
446 if (isUnnamedFile()) {
447 if (file == LinkName) {
448 // we know our file isn't (won't be) a symlink
449 return QString();
450 }
451
452 // for all other cases, materialize the file
453 const_cast<QTemporaryFileEngine *>(this)->materializeUnnamedFile(templateName, NameIsTemplate);
454 }
455 return QFSFileEngine::fileName(file);
456}
457
458bool QTemporaryFileEngine::materializeUnnamedFile(const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
459{
460 Q_ASSERT(isUnnamedFile());
461
462#ifdef LINUX_UNNAMED_TMPFILE
463 Q_D(QFSFileEngine);
464 const QByteArray src = "/proc/self/fd/" + QByteArray::number(d->fd);
465 auto materializeAt = [=](const QFileSystemEntry &dst) {
466 return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
467 };
468#else
469 auto materializeAt = [](const QFileSystemEntry &) { return false; };
470#endif
471
472 auto success = [this](const QFileSystemEntry &entry) {
473 filePathIsTemplate = false;
474 unnamedFile = false;
475 d_func()->fileEntry = entry;
476 return true;
477 };
478
479 auto materializeAsTemplate = [=](const QString &newName) {
480 QTemporaryFileName tfn(newName);
481 static const int maxAttempts = 16;
482 for (int attempt = 0; attempt < maxAttempts; ++attempt) {
483 tfn.generateNext();
484 QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
485 if (materializeAt(entry))
486 return success(entry);
487 }
488 return false;
489 };
490
491 if (mode == NameIsTemplate) {
492 if (materializeAsTemplate(newName))
493 return true;
494 } else {
495 // Use linkat to materialize the file
496 QFileSystemEntry dst(newName);
497 if (materializeAt(dst))
498 return success(dst);
499
500 if (errno == EEXIST && mode == Overwrite) {
501 // retry by first creating a temporary file in the right dir
502 if (!materializeAsTemplate(templateName))
503 return false;
504
505 // then rename the materialized file to target (same as renameOverwrite)
506 QFSFileEngine::close();
507 return QFSFileEngine::renameOverwrite(newName);
508 }
509 }
510
511 // failed
512 setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
513 return false;
514}
515
516bool QTemporaryFileEngine::isUnnamedFile() const
517{
518#ifdef LINUX_UNNAMED_TMPFILE
519 if (unnamedFile) {
520 Q_ASSERT(d_func()->fileEntry.isEmpty());
521 Q_ASSERT(filePathIsTemplate);
522 }
523 return unnamedFile;
524#else
525 return false;
526#endif
527}
528
529//************* QTemporaryFilePrivate
530
531QTemporaryFilePrivate::QTemporaryFilePrivate()
532{
533}
534
535QTemporaryFilePrivate::QTemporaryFilePrivate(const QString &templateNameIn)
536 : templateName(templateNameIn)
537{
538}
539
540QTemporaryFilePrivate::~QTemporaryFilePrivate()
541{
542}
543
544QAbstractFileEngine *QTemporaryFilePrivate::engine() const
545{
546 if (!fileEngine) {
547 fileEngine.reset(new QTemporaryFileEngine(&templateName));
548 resetFileEngine();
549 }
550 return fileEngine.get();
551}
552
553void QTemporaryFilePrivate::resetFileEngine() const
554{
555 if (!fileEngine)
556 return;
557
558 QTemporaryFileEngine *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
559 if (fileName.isEmpty())
560 tef->initialize(templateName, 0600);
561 else
562 tef->initialize(fileName, 0600, false);
563}
564
565void QTemporaryFilePrivate::materializeUnnamedFile()
566{
567#ifdef LINUX_UNNAMED_TMPFILE
568 if (!fileName.isEmpty() || !fileEngine)
569 return;
570
571 auto *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
572 fileName = tef->fileName(QAbstractFileEngine::DefaultName);
573#endif
574}
575
576QString QTemporaryFilePrivate::defaultTemplateName()
577{
578 QString baseName;
579#if defined(QT_BUILD_CORE_LIB)
580 baseName = QCoreApplication::applicationName();
581 if (baseName.isEmpty())
582#endif
583 baseName = QLatin1String("qt_temp");
584
585 return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String(".XXXXXX");
586}
587
588//************* QTemporaryFile
589
590/*!
591 \class QTemporaryFile
592 \inmodule QtCore
593 \reentrant
594 \brief The QTemporaryFile class is an I/O device that operates on temporary files.
595
596 \ingroup io
597
598
599 QTemporaryFile is used to create unique temporary files safely.
600 The file itself is created by calling open(). The name of the
601 temporary file is guaranteed to be unique (i.e., you are
602 guaranteed to not overwrite an existing file), and the file will
603 subsequently be removed upon destruction of the QTemporaryFile
604 object. This is an important technique that avoids data
605 corruption for applications that store data in temporary files.
606 The file name is either auto-generated, or created based on a
607 template, which is passed to QTemporaryFile's constructor.
608
609 Example:
610
611 \snippet code/src_corelib_io_qtemporaryfile.cpp 0
612
613 Reopening a QTemporaryFile after calling close() is safe. For as long as
614 the QTemporaryFile object itself is not destroyed, the unique temporary
615 file will exist and be kept open internally by QTemporaryFile.
616
617 The file name of the temporary file can be found by calling fileName().
618 Note that this is only defined after the file is first opened; the function
619 returns an empty string before this.
620
621 A temporary file will have some static part of the name and some
622 part that is calculated to be unique. The default filename will be
623 determined from QCoreApplication::applicationName() (otherwise \c qt_temp) and will
624 be placed into the temporary path as returned by QDir::tempPath().
625 If you specify your own filename, a relative file path will not be placed in the
626 temporary directory by default, but be relative to the current working directory.
627
628 Specified filenames can contain the following template \c XXXXXX
629 (six upper case "X" characters), which will be replaced by the
630 auto-generated portion of the filename. Note that the template is
631 case sensitive. If the template is not present in the filename,
632 QTemporaryFile appends the generated part to the filename given.
633
634 \sa QDir::tempPath(), QFile
635*/
636
637#ifdef QT_NO_QOBJECT
638QTemporaryFile::QTemporaryFile()
639 : QFile(*new QTemporaryFilePrivate)
640{
641}
642
643QTemporaryFile::QTemporaryFile(const QString &templateName)
644 : QFile(*new QTemporaryFilePrivate(templateName))
645{
646}
647
648#else
649/*!
650 Constructs a QTemporaryFile using as file template
651 the application name returned by QCoreApplication::applicationName()
652 (otherwise \c qt_temp) followed by ".XXXXXX".
653 The file is stored in the system's temporary directory, QDir::tempPath().
654
655 \sa setFileTemplate(), QDir::tempPath()
656*/
657QTemporaryFile::QTemporaryFile()
658 : QTemporaryFile(nullptr)
659{
660}
661
662/*!
663 Constructs a QTemporaryFile with a template filename of \a
664 templateName. Upon opening the temporary file this will be used to create
665 a unique filename.
666
667 If the \a templateName does not contain XXXXXX it will automatically be
668 appended and used as the dynamic portion of the filename.
669
670 If \a templateName is a relative path, the path will be relative to the
671 current working directory. You can use QDir::tempPath() to construct \a
672 templateName if you want use the system's temporary directory.
673
674 \sa open(), fileTemplate()
675*/
676QTemporaryFile::QTemporaryFile(const QString &templateName)
677 : QTemporaryFile(templateName, nullptr)
678{
679}
680
681/*!
682 Constructs a QTemporaryFile (with the given \a parent)
683 using as file template the application name returned by QCoreApplication::applicationName()
684 (otherwise \c qt_temp) followed by ".XXXXXX".
685 The file is stored in the system's temporary directory, QDir::tempPath().
686
687 \sa setFileTemplate()
688*/
689QTemporaryFile::QTemporaryFile(QObject *parent)
690 : QFile(*new QTemporaryFilePrivate, parent)
691{
692}
693
694/*!
695 Constructs a QTemporaryFile with a template filename of \a
696 templateName and the specified \a parent.
697 Upon opening the temporary file this will be used to
698 create a unique filename.
699
700 If the \a templateName does not contain XXXXXX it will automatically be
701 appended and used as the dynamic portion of the filename.
702
703 If \a templateName is a relative path, the path will be relative to the
704 current working directory. You can use QDir::tempPath() to construct \a
705 templateName if you want use the system's temporary directory.
706
707 \sa open(), fileTemplate()
708*/
709QTemporaryFile::QTemporaryFile(const QString &templateName, QObject *parent)
710 : QFile(*new QTemporaryFilePrivate(templateName), parent)
711{
712}
713#endif
714
715/*!
716 Destroys the temporary file object, the file is automatically
717 closed if necessary and if in auto remove mode it will
718 automatically delete the file.
719
720 \sa autoRemove()
721*/
722QTemporaryFile::~QTemporaryFile()
723{
724 Q_D(QTemporaryFile);
725 close();
726 if (!d->fileName.isEmpty() && d->autoRemove)
727 remove();
728}
729
730/*!
731 \fn bool QTemporaryFile::open()
732
733 A QTemporaryFile will always be opened in QIODevice::ReadWrite mode,
734 this allows easy access to the data in the file. This function will
735 return true upon success and will set the fileName() to the unique
736 filename used.
737
738 \sa fileName()
739*/
740
741/*!
742 Returns \c true if the QTemporaryFile is in auto remove
743 mode. Auto-remove mode will automatically delete the filename from
744 disk upon destruction. This makes it very easy to create your
745 QTemporaryFile object on the stack, fill it with data, read from
746 it, and finally on function return it will automatically clean up
747 after itself.
748
749 Auto-remove is on by default.
750
751 \sa setAutoRemove(), remove()
752*/
753bool QTemporaryFile::autoRemove() const
754{
755 Q_D(const QTemporaryFile);
756 return d->autoRemove;
757}
758
759/*!
760 Sets the QTemporaryFile into auto-remove mode if \a b is \c true.
761
762 Auto-remove is on by default.
763
764 If you set this property to \c false, ensure the application provides a way
765 to remove the file once it is no longer needed, including passing the
766 responsibility on to another process. Always use the fileName() function to
767 obtain the name and never try to guess the name that QTemporaryFile has
768 generated.
769
770 On some systems, if fileName() is not called before closing the file, the
771 temporary file may be removed regardless of the state of this property.
772 This behavior should not be relied upon, so application code should either
773 call fileName() or leave the auto removal functionality enabled.
774
775 \sa autoRemove(), remove()
776*/
777void QTemporaryFile::setAutoRemove(bool b)
778{
779 Q_D(QTemporaryFile);
780 d->autoRemove = b;
781}
782
783/*!
784 Returns the complete unique filename backing the QTemporaryFile
785 object. This string is null before the QTemporaryFile is opened,
786 afterwards it will contain the fileTemplate() plus
787 additional characters to make it unique.
788
789 \sa fileTemplate()
790*/
791
792QString QTemporaryFile::fileName() const
793{
794 Q_D(const QTemporaryFile);
795 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
796 if (tef && tef->isReallyOpen())
797 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
798
799 if(d->fileName.isEmpty())
800 return QString();
801 return d->engine()->fileName(QAbstractFileEngine::DefaultName);
802}
803
804/*!
805 Returns the set file template. The default file template will be
806 called qcoreappname.XXXXXX and be placed in QDir::tempPath().
807
808 \sa setFileTemplate()
809*/
810QString QTemporaryFile::fileTemplate() const
811{
812 Q_D(const QTemporaryFile);
813 return d->templateName;
814}
815
816/*!
817 Sets the static portion of the file name to \a name. If the file
818 template contains XXXXXX that will automatically be replaced with
819 the unique part of the filename, otherwise a filename will be
820 determined automatically based on the static portion specified.
821
822 If \a name contains a relative file path, the path will be relative to the
823 current working directory. You can use QDir::tempPath() to construct \a
824 name if you want use the system's temporary directory.
825
826 \sa fileTemplate()
827*/
828void QTemporaryFile::setFileTemplate(const QString &name)
829{
830 Q_D(QTemporaryFile);
831 d->templateName = name;
832}
833
834/*!
835 \internal
836
837 This is just a simplified version of QFile::rename() because we know a few
838 extra details about what kind of file we have. The documentation is hidden
839 from the user because QFile::rename() should be enough.
840*/
841bool QTemporaryFile::rename(const QString &newName)
842{
843 Q_D(QTemporaryFile);
844 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
845 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
846 return QFile::rename(newName);
847
848 unsetError();
849 close();
850 if (error() == QFile::NoError) {
851 if (tef->rename(newName)) {
852 unsetError();
853 // engine was able to handle the new name so we just reset it
854 tef->setFileName(newName);
855 d->fileName = newName;
856 return true;
857 }
858
859 d->setError(QFile::RenameError, tef->errorString());
860 }
861 return false;
862}
863
864/*!
865 \fn QTemporaryFile *QTemporaryFile::createLocalFile(const QString &fileName)
866 \overload
867 \obsolete
868
869 Use QTemporaryFile::createNativeFile(const QString &fileName) instead.
870*/
871
872/*!
873 \fn QTemporaryFile *QTemporaryFile::createLocalFile(QFile &file)
874 \obsolete
875
876 Use QTemporaryFile::createNativeFile(QFile &file) instead.
877*/
878
879/*!
880 \fn QTemporaryFile *QTemporaryFile::createNativeFile(const QString &fileName)
881 \overload
882
883 Works on the given \a fileName rather than an existing QFile
884 object.
885*/
886
887
888/*!
889 If \a file is not already a native file, then a QTemporaryFile is created
890 in QDir::tempPath(), the contents of \a file is copied into it, and a pointer
891 to the temporary file is returned. Does nothing and returns \c 0 if \a file
892 is already a native file.
893
894 For example:
895
896 \snippet code/src_corelib_io_qtemporaryfile.cpp 1
897
898 \sa QFileInfo::isNativePath()
899*/
900
901QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
902{
903 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
904 if(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
905 return nullptr; // native already
906 //cache
907 bool wasOpen = file.isOpen();
908 qint64 old_off = 0;
909 if(wasOpen)
910 old_off = file.pos();
911 else if (!file.open(QIODevice::ReadOnly))
912 return nullptr;
913 //dump data
914 QTemporaryFile *ret = new QTemporaryFile;
915 if (ret->open()) {
916 file.seek(0);
917 char buffer[1024];
918 while (true) {
919 qint64 len = file.read(buffer, 1024);
920 if (len < 1)
921 break;
922 ret->write(buffer, len);
923 }
924 ret->seek(0);
925 } else {
926 delete ret;
927 ret = nullptr;
928 }
929 //restore
930 if(wasOpen)
931 file.seek(old_off);
932 else
933 file.close();
934 //done
935 return ret;
936 }
937 return nullptr;
938}
939
940/*!
941 \reimp
942
943 Creates a unique file name for the temporary file, and opens it. You can
944 get the unique name later by calling fileName(). The file is guaranteed to
945 have been created by this function (i.e., it has never existed before).
946*/
947bool QTemporaryFile::open(OpenMode flags)
948{
949 Q_D(QTemporaryFile);
950 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
951 if (tef && tef->isReallyOpen()) {
952 setOpenMode(flags);
953 return true;
954 }
955
956 // reset the engine state so it creates a new, unique file name from the template;
957 // equivalent to:
958 // delete d->fileEngine;
959 // d->fileEngine = 0;
960 // d->engine();
961 d->resetFileEngine();
962
963 if (QFile::open(flags)) {
964 tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
965 if (tef->isUnnamedFile())
966 d->fileName.clear();
967 else
968 d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
969 return true;
970 }
971 return false;
972}
973
974#endif // QT_NO_TEMPORARYFILE
975
976QT_END_NAMESPACE
977
978#ifndef QT_NO_QOBJECT
979#include "moc_qtemporaryfile.cpp"
980#endif
981