8#include "qplatformdefs.h"
10#include "private/qtemporaryfile_p.h"
11#include "private/qfile_p.h"
12#include "private/qsystemerror_p.h"
15#include "private/qcore_unix_p.h"
19#if defined(QT_BUILD_CORE_LIB)
20#include "qcoreapplication.h"
22#define tr(X) QString::fromLatin1(X)
27using namespace Qt::StringLiterals;
32static inline Char Latin1Char(
char ch)
34 return ushort(uchar(ch));
37typedef HANDLE NativeFileHandle;
45QTemporaryFileName::QTemporaryFileName(
const QString &templateName)
48 QString qfilename = QDir::fromNativeSeparators(templateName);
49 qsizetype phPos = qfilename.size();
50 qsizetype phLength = 0;
55 if (qfilename[phPos] == u'X') {
61 || qfilename[phPos] == u'/') {
71 qfilename.append(
".XXXXXX"_L1);
74 QFileSystemEntry::NativePath filename =
75 QFileSystemEntry(QDir::cleanPath(qfilename)).nativeFilePath();
78 phPos = filename.size();
83 if (filename[phPos] == Latin1Char(
'X')) {
97 Q_ASSERT(phLength >= 6);
104
105
106
107
108
109QFileSystemEntry::NativePath QTemporaryFileName::generateNext()
111 Q_ASSERT(length != 0);
112 Q_ASSERT(pos < path.size());
113 Q_ASSERT(length <= path.size() - pos);
115 Char *
const placeholderStart = (Char *)path.data() + pos;
116 Char *
const placeholderEnd = placeholderStart + length;
130 enum { BitsPerCharacter = 10 };
132 Char *rIter = placeholderEnd;
133 while (rIter != placeholderStart) {
134 quint32 rnd = QRandomGenerator::global()->generate();
135 auto applyOne = [&]() {
136 quint32 v = rnd & ((1 << BitsPerCharacter) - 1);
137 rnd >>= BitsPerCharacter;
138 char ch =
char((26 + 26) * v / (1 << BitsPerCharacter));
140 *--rIter = Latin1Char(ch +
'A');
142 *--rIter = Latin1Char(ch - 26 +
'a');
146 if (rIter == placeholderStart)
150 if (rIter == placeholderStart)
160#if QT_CONFIG(temporaryfile)
163
164
165
166
167
168
169
170
171
172
173
174
175static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
176 quint32 mode,
int flags, QSystemError &error)
178 const int maxAttempts = 16;
179 for (
int attempt = 0; attempt < maxAttempts; ++attempt) {
181 const QFileSystemEntry::NativePath &path = templ.generateNext();
185 const DWORD shareMode = (flags & QTemporaryFileEngine::Win32NonShared)
186 ? 0u : (FILE_SHARE_READ | FILE_SHARE_WRITE);
188 const DWORD extraAccessFlags = (flags & QTemporaryFileEngine::Win32NonShared) ? DELETE : 0;
189 file = CreateFile((
const wchar_t *)path.constData(),
190 GENERIC_READ | GENERIC_WRITE | extraAccessFlags,
191 shareMode, NULL, CREATE_NEW,
192 FILE_ATTRIBUTE_NORMAL, NULL);
194 if (file != INVALID_HANDLE_VALUE)
197 DWORD err = GetLastError();
198 if (err == ERROR_ACCESS_DENIED) {
199 WIN32_FILE_ATTRIBUTE_DATA attributes;
200 if (!GetFileAttributesEx((
const wchar_t *)path.constData(),
201 GetFileExInfoStandard, &attributes)
202 || attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
204 error = QSystemError(err, QSystemError::NativeError);
207 }
else if (err != ERROR_FILE_EXISTS) {
208 error = QSystemError(err, QSystemError::NativeError);
213 file = QT_OPEN(path.constData(),
214 QT_OPEN_CREAT | QT_OPEN_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
215 static_cast<mode_t>(mode));
222 error = QSystemError(err, QSystemError::NativeError);
231enum class CreateUnnamedFileStatus {
237static CreateUnnamedFileStatus
238createUnnamedFile(NativeFileHandle &file, QTemporaryFileName &tfn, quint32 mode, QSystemError *error)
240#ifdef LINUX_UNNAMED_TMPFILE
243 if (!qt_haveLinuxProcfs())
244 return CreateUnnamedFileStatus::NotSupported;
247 QByteArray::size_type lastSlash = tfn.path.lastIndexOf(
'/');
248 if (lastSlash >= 0) {
251 tfn.path[lastSlash] =
'\0';
255 file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
256 static_cast<mode_t>(mode));
258 return CreateUnnamedFileStatus::Success;
260 if (errno == EOPNOTSUPP || errno == EISDIR) {
264 tfn.path[lastSlash] =
'/';
265 return CreateUnnamedFileStatus::NotSupported;
269 *error = QSystemError(errno, QSystemError::NativeError);
270 return CreateUnnamedFileStatus::OtherError;
276 return CreateUnnamedFileStatus::NotSupported;
281QTemporaryFileEngine::~QTemporaryFileEngine()
285 QFSFileEngine::close();
288bool QTemporaryFileEngine::isReallyOpen()
const
290 Q_D(
const QFSFileEngine);
292 if (!((
nullptr == d->fh) && (-1 == d->fd)
294 && (INVALID_HANDLE_VALUE == d->fileHandle)
303void QTemporaryFileEngine::setFileName(
const QString &file)
306 QFSFileEngine::close();
307 QFSFileEngine::setFileName(file);
310bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode,
311 std::optional<QFile::Permissions> permissions)
314 Q_ASSERT(!isReallyOpen());
316 openMode |= QIODevice::ReadWrite;
318 if (!filePathIsTemplate)
319 return QFSFileEngine::open(openMode, permissions);
321 QTemporaryFileName tfn(templateName);
325 NativeFileHandle &file = d->fileHandle;
327 NativeFileHandle &file = d->fd;
330 CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
331 if (st == CreateUnnamedFileStatus::Success) {
333 d->fileEntry.clear();
334 }
else if (st == CreateUnnamedFileStatus::NotSupported &&
335 createFileFromTemplate(file, tfn, fileMode, flags, error)) {
336 filePathIsTemplate =
false;
338 d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
340 setError(QFile::OpenError, error.toString());
344#if !defined(Q_OS_WIN)
345 d->closeFileHandle =
true;
348 d->openMode = openMode;
349 d->lastFlushFailed =
false;
355bool QTemporaryFileEngine::remove()
361 QFSFileEngine::close();
364 if (!filePathIsTemplate && QFSFileEngine::remove()) {
365 d->fileEntry.clear();
371 filePathIsTemplate = filePathWasTemplate;
377bool QTemporaryFileEngine::rename(
const QString &newName)
379 if (isUnnamedFile()) {
380 bool ok = materializeUnnamedFile(newName, DontOverwrite);
381 QFSFileEngine::close();
384 QFSFileEngine::close();
385 return QFSFileEngine::rename(newName);
388bool QTemporaryFileEngine::renameOverwrite(
const QString &newName)
390 if (isUnnamedFile()) {
391 bool ok = materializeUnnamedFile(newName, Overwrite);
392 QFSFileEngine::close();
396 if (flags & Win32NonShared) {
397 QFileSystemEntry newEntry(newName, QFileSystemEntry::FromInternalPath());
398 bool ok = d_func()->nativeRenameOverwrite(newEntry);
399 QFSFileEngine::close();
402 setFileEntry(std::move(newEntry));
407 QFSFileEngine::close();
408 return QFSFileEngine::renameOverwrite(newName);
411bool QTemporaryFileEngine::close()
415 setError(QFile::UnspecifiedError, QString());
419QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file)
const
421 if (isUnnamedFile()) {
422 if (file == AbsoluteLinkTarget || file == RawLinkPath) {
428 const_cast<QTemporaryFileEngine *>(
this)->materializeUnnamedFile(templateName, NameIsTemplate);
430 return QFSFileEngine::fileName(file);
433bool QTemporaryFileEngine::materializeUnnamedFile(
const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
435 Q_ASSERT(isUnnamedFile());
437#ifdef LINUX_UNNAMED_TMPFILE
439 const QByteArray src =
"/proc/self/fd/" + QByteArray::number(d->fd);
440 auto materializeAt = [=](
const QFileSystemEntry &dst) {
441 return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
444 auto materializeAt = [](
const QFileSystemEntry &) {
return false; };
447 auto success = [
this](
const QFileSystemEntry &entry) {
448 filePathIsTemplate =
false;
450 d_func()->fileEntry = entry;
454 auto materializeAsTemplate = [=](
const QString &newName) {
455 QTemporaryFileName tfn(newName);
456 static const int maxAttempts = 16;
457 for (
int attempt = 0; attempt < maxAttempts; ++attempt) {
459 QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
460 if (materializeAt(entry))
461 return success(entry);
466 if (mode == NameIsTemplate) {
467 if (materializeAsTemplate(newName))
471 QFileSystemEntry dst(newName);
472 if (materializeAt(dst))
475 if (errno == EEXIST && mode == Overwrite) {
477 if (!materializeAsTemplate(templateName))
481 QFSFileEngine::close();
482 return QFSFileEngine::renameOverwrite(newName);
487 setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
491bool QTemporaryFileEngine::isUnnamedFile()
const
493#ifdef LINUX_UNNAMED_TMPFILE
495 Q_ASSERT(d_func()->fileEntry.isEmpty());
496 Q_ASSERT(filePathIsTemplate);
506QTemporaryFilePrivate::QTemporaryFilePrivate()
510QTemporaryFilePrivate::QTemporaryFilePrivate(
const QString &templateNameIn)
511 : templateName(templateNameIn)
515QTemporaryFilePrivate::~QTemporaryFilePrivate()
519QAbstractFileEngine *QTemporaryFilePrivate::engine()
const
522 fileEngine.reset(
new QTemporaryFileEngine(&templateName));
525 return fileEngine.get();
528void QTemporaryFilePrivate::resetFileEngine()
const
533 QTemporaryFileEngine *tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
534 if (fileName.isEmpty())
535 tef->initialize(templateName, 0600);
537 tef->initialize(fileName, 0600,
false);
540void QTemporaryFilePrivate::materializeUnnamedFile()
542#ifdef LINUX_UNNAMED_TMPFILE
543 if (!fileName.isEmpty() || !fileEngine)
546 auto *tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
547 fileName = tef->fileName(QAbstractFileEngine::DefaultName);
551QString QTemporaryFilePrivate::defaultTemplateName()
554#if defined(QT_BUILD_CORE_LIB)
555 baseName = QCoreApplication::applicationName();
556 if (baseName.isEmpty())
558 baseName =
"qt_temp"_L1;
560 return QDir::tempPath() + u'/' + baseName +
".XXXXXX"_L1;
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
626QTemporaryFile::QTemporaryFile()
627 : QFile(*
new QTemporaryFilePrivate)
631QTemporaryFile::QTemporaryFile(
const QString &templateName)
632 : QFile(*
new QTemporaryFilePrivate(templateName))
638
639
640
641
642
643
644
645
646
647
648
649
650QTemporaryFile::QTemporaryFile()
651 : QTemporaryFile(
nullptr)
656
657
658
659
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688QTemporaryFile::QTemporaryFile(
const QString &templateName)
689 : QTemporaryFile(templateName,
nullptr)
694
695
696
697
698
699
700QTemporaryFile::QTemporaryFile(QObject *parent)
701 : QFile(*
new QTemporaryFilePrivate, parent)
706
707
708
709
710
711
712
713
714
715
716
717
718QTemporaryFile::QTemporaryFile(
const QString &templateName, QObject *parent)
719 : QFile(*
new QTemporaryFilePrivate(templateName), parent)
725
726
727
728
729
730
731QTemporaryFile::~QTemporaryFile()
735 if (!d->fileName.isEmpty() && d->autoRemove)
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
758
759
760
761
762
763
764
765
766
767
768
769bool QTemporaryFile::autoRemove()
const
771 Q_D(
const QTemporaryFile);
772 return d->autoRemove;
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793void QTemporaryFile::setAutoRemove(
bool b)
800
801
802
803
804
805
806
807
808
809
810
812QString QTemporaryFile::fileName()
const
814 Q_D(
const QTemporaryFile);
815 auto tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
816 if (tef && tef->isReallyOpen())
817 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
819 if (d->fileName.isEmpty())
821 return d->engine()->fileName(QAbstractFileEngine::DefaultName);
825
826
827
828
829
830
831
832
833QString QTemporaryFile::fileTemplate()
const
835 Q_D(
const QTemporaryFile);
836 return d->templateName;
840
841
842
843
846
847
848
849
850
851
852
853
854
855
856
857void QTemporaryFile::setFileTemplate(
const QString &name)
860 d->templateName = name;
864
865
866
867
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895bool QTemporaryFile::rename(
const QString &newName)
898 return d->rename(newName,
false);
901bool QTemporaryFilePrivate::rename(
const QString &newName,
bool overwrite)
904 auto tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
905 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
906 return q->QFile::rename(newName);
910 if (q->error() == QFile::NoError) {
911 if (overwrite ? tef->renameOverwrite(newName) : tef->rename(newName)) {
918 setError(QFile::RenameError, tef->errorString());
924
925
926
927
930
931
932
933
934
935
936
937
938
939
940
941bool QTemporaryFile::renameOverwrite(
const QString &newName)
944 return d->rename(newName,
true);
948
949
950
951
952
953
955
956
957
958
961
962
963
964
965
966
967
968
969
970
971
973QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
975 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
976 if (engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
979 bool wasOpen = file.isOpen();
982 old_off = file.pos();
983 else if (!file.open(QIODevice::ReadOnly))
986 QTemporaryFile *ret =
new QTemporaryFile;
991 qint64 len = file.read(buffer, 1024);
994 ret->write(buffer, len);
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029bool QTemporaryFile::open(OpenMode mode)
1031 Q_D(QTemporaryFile);
1032 auto tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1033 if (tef && tef->isReallyOpen()) {
1043 d->resetFileEngine();
1045 if (QFile::open(mode)) {
1046 tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1047 if (tef->isUnnamedFile())
1048 d->fileName.clear();
1050 d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
1060#ifndef QT_NO_QOBJECT
1061#include "moc_qtemporaryfile.cpp"