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