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();
186 const DWORD shareMode = (flags & QTemporaryFileEngine::Win32NonShared)
187 ? 0u : (FILE_SHARE_READ | FILE_SHARE_WRITE);
189 const DWORD extraAccessFlags = (flags & QTemporaryFileEngine::Win32NonShared) ? DELETE : 0;
190 file = CreateFile((
const wchar_t *)path.constData(),
191 GENERIC_READ | GENERIC_WRITE | extraAccessFlags,
192 shareMode, NULL, CREATE_NEW,
193 FILE_ATTRIBUTE_NORMAL, NULL);
195 if (file != INVALID_HANDLE_VALUE)
198 DWORD err = GetLastError();
199 if (err == ERROR_ACCESS_DENIED) {
200 WIN32_FILE_ATTRIBUTE_DATA attributes;
201 if (!GetFileAttributesEx((
const wchar_t *)path.constData(),
202 GetFileExInfoStandard, &attributes)
203 || attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
205 error = QSystemError(err, QSystemError::NativeError);
208 }
else if (err != ERROR_FILE_EXISTS) {
209 error = QSystemError(err, QSystemError::NativeError);
214 file = QT_OPEN(path.constData(),
215 QT_OPEN_CREAT | QT_OPEN_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
216 static_cast<mode_t>(mode));
223 error = QSystemError(err, QSystemError::NativeError);
232enum class CreateUnnamedFileStatus {
238static CreateUnnamedFileStatus
239createUnnamedFile(NativeFileHandle &file, QTemporaryFileName &tfn, quint32 mode, QSystemError *error)
241#ifdef LINUX_UNNAMED_TMPFILE
244 if (!qt_haveLinuxProcfs())
245 return CreateUnnamedFileStatus::NotSupported;
248 QByteArray::size_type lastSlash = tfn.path.lastIndexOf(
'/');
249 if (lastSlash >= 0) {
252 tfn.path[lastSlash] =
'\0';
256 file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
257 static_cast<mode_t>(mode));
259 return CreateUnnamedFileStatus::Success;
261 if (errno == EOPNOTSUPP || errno == EISDIR) {
265 tfn.path[lastSlash] =
'/';
266 return CreateUnnamedFileStatus::NotSupported;
270 *error = QSystemError(errno, QSystemError::NativeError);
271 return CreateUnnamedFileStatus::OtherError;
277 return CreateUnnamedFileStatus::NotSupported;
282QTemporaryFileEngine::~QTemporaryFileEngine()
286 QFSFileEngine::close();
289bool QTemporaryFileEngine::isReallyOpen()
const
291 Q_D(
const QFSFileEngine);
293 if (!((
nullptr == d->fh) && (-1 == d->fd)
295 && (INVALID_HANDLE_VALUE == d->fileHandle)
304void QTemporaryFileEngine::setFileName(
const QString &file)
307 QFSFileEngine::close();
308 QFSFileEngine::setFileName(file);
311bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode,
312 std::optional<QFile::Permissions> permissions)
315 Q_ASSERT(!isReallyOpen());
317 openMode |= QIODevice::ReadWrite;
319 if (!filePathIsTemplate)
320 return QFSFileEngine::open(openMode, permissions);
322 QTemporaryFileName tfn(templateName);
326 NativeFileHandle &file = d->fileHandle;
328 NativeFileHandle &file = d->fd;
331 CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
332 if (st == CreateUnnamedFileStatus::Success) {
334 d->fileEntry.clear();
335 }
else if (st == CreateUnnamedFileStatus::NotSupported &&
336 createFileFromTemplate(file, tfn, fileMode, flags, error)) {
337 filePathIsTemplate =
false;
339 d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
341 setError(QFile::OpenError, error.toString());
345#if !defined(Q_OS_WIN)
346 d->closeFileHandle =
true;
349 d->openMode = openMode;
350 d->lastFlushFailed =
false;
356bool QTemporaryFileEngine::remove()
362 QFSFileEngine::close();
365 if (!filePathIsTemplate && QFSFileEngine::remove()) {
366 d->fileEntry.clear();
372 filePathIsTemplate = filePathWasTemplate;
378bool QTemporaryFileEngine::rename(
const QString &newName)
380 if (isUnnamedFile()) {
381 bool ok = materializeUnnamedFile(newName, DontOverwrite);
382 QFSFileEngine::close();
385 QFSFileEngine::close();
386 return QFSFileEngine::rename(newName);
389bool QTemporaryFileEngine::renameOverwrite(
const QString &newName)
391 if (isUnnamedFile()) {
392 bool ok = materializeUnnamedFile(newName, Overwrite);
393 QFSFileEngine::close();
397 if (flags & Win32NonShared) {
398 QFileSystemEntry newEntry(newName, QFileSystemEntry::FromInternalPath());
399 bool ok = d_func()->nativeRenameOverwrite(newEntry);
400 QFSFileEngine::close();
403 setFileEntry(std::move(newEntry));
408 QFSFileEngine::close();
409 return QFSFileEngine::renameOverwrite(newName);
412bool QTemporaryFileEngine::close()
416 setError(QFile::UnspecifiedError, QString());
420QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file)
const
422 if (isUnnamedFile()) {
423 if (file == AbsoluteLinkTarget || file == RawLinkPath) {
429 const_cast<QTemporaryFileEngine *>(
this)->materializeUnnamedFile(templateName, NameIsTemplate);
431 return QFSFileEngine::fileName(file);
434bool QTemporaryFileEngine::materializeUnnamedFile(
const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
436 Q_ASSERT(isUnnamedFile());
438#ifdef LINUX_UNNAMED_TMPFILE
440 const QByteArray src =
"/proc/self/fd/" + QByteArray::number(d->fd);
441 auto materializeAt = [=](
const QFileSystemEntry &dst) {
442 return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
445 auto materializeAt = [](
const QFileSystemEntry &) {
return false; };
448 auto success = [
this](
const QFileSystemEntry &entry) {
449 filePathIsTemplate =
false;
451 d_func()->fileEntry = entry;
455 auto materializeAsTemplate = [=](
const QString &newName) {
456 QTemporaryFileName tfn(newName);
457 static const int maxAttempts = 16;
458 for (
int attempt = 0; attempt < maxAttempts; ++attempt) {
460 QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
461 if (materializeAt(entry))
462 return success(entry);
467 if (mode == NameIsTemplate) {
468 if (materializeAsTemplate(newName))
472 QFileSystemEntry dst(newName);
473 if (materializeAt(dst))
476 if (errno == EEXIST && mode == Overwrite) {
478 if (!materializeAsTemplate(templateName))
482 QFSFileEngine::close();
483 return QFSFileEngine::renameOverwrite(newName);
488 setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
492bool QTemporaryFileEngine::isUnnamedFile()
const
494#ifdef LINUX_UNNAMED_TMPFILE
496 Q_ASSERT(d_func()->fileEntry.isEmpty());
497 Q_ASSERT(filePathIsTemplate);
507QTemporaryFilePrivate::QTemporaryFilePrivate()
511QTemporaryFilePrivate::QTemporaryFilePrivate(
const QString &templateNameIn)
512 : templateName(templateNameIn)
516QTemporaryFilePrivate::~QTemporaryFilePrivate()
520QAbstractFileEngine *QTemporaryFilePrivate::engine()
const
523 fileEngine.reset(
new QTemporaryFileEngine(&templateName));
526 return fileEngine.get();
529void QTemporaryFilePrivate::resetFileEngine()
const
534 QTemporaryFileEngine *tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
535 if (fileName.isEmpty())
536 tef->initialize(templateName, 0600);
538 tef->initialize(fileName, 0600,
false);
541void QTemporaryFilePrivate::materializeUnnamedFile()
543#ifdef LINUX_UNNAMED_TMPFILE
544 if (!fileName.isEmpty() || !fileEngine)
547 auto *tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
548 fileName = tef->fileName(QAbstractFileEngine::DefaultName);
552QString QTemporaryFilePrivate::defaultTemplateName()
555#if defined(QT_BUILD_CORE_LIB)
556 baseName = QCoreApplication::applicationName();
557 if (baseName.isEmpty())
559 baseName =
"qt_temp"_L1;
561 return QDir::tempPath() + u'/' + baseName +
".XXXXXX"_L1;
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
624
627QTemporaryFile::QTemporaryFile()
628 : QFile(*
new QTemporaryFilePrivate)
632QTemporaryFile::QTemporaryFile(
const QString &templateName)
633 : QFile(*
new QTemporaryFilePrivate(templateName))
639
640
641
642
643
644
645
646
647
648
649
650
651QTemporaryFile::QTemporaryFile()
652 : QTemporaryFile(
nullptr)
657
658
659
660
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
688
689QTemporaryFile::QTemporaryFile(
const QString &templateName)
690 : QTemporaryFile(templateName,
nullptr)
695
696
697
698
699
700
701QTemporaryFile::QTemporaryFile(QObject *parent)
702 : QFile(*
new QTemporaryFilePrivate, parent)
707
708
709
710
711
712
713
714
715
716
717
718
719QTemporaryFile::QTemporaryFile(
const QString &templateName, QObject *parent)
720 : QFile(*
new QTemporaryFilePrivate(templateName), parent)
726
727
728
729
730
731
732QTemporaryFile::~QTemporaryFile()
736 if (!d->fileName.isEmpty() && d->autoRemove)
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
759
760
761
762
763
764
765
766
767
768
769
770bool QTemporaryFile::autoRemove()
const
772 Q_D(
const QTemporaryFile);
773 return d->autoRemove;
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794void QTemporaryFile::setAutoRemove(
bool b)
801
802
803
804
805
806
807
808
809
810
811
813QString QTemporaryFile::fileName()
const
815 Q_D(
const QTemporaryFile);
816 auto tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
817 if (tef && tef->isReallyOpen())
818 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
820 if (d->fileName.isEmpty())
822 return d->engine()->fileName(QAbstractFileEngine::DefaultName);
826
827
828
829
830
831
832
833
834QString QTemporaryFile::fileTemplate()
const
836 Q_D(
const QTemporaryFile);
837 return d->templateName;
841
842
843
844
847
848
849
850
851
852
853
854
855
856
857
858void QTemporaryFile::setFileTemplate(
const QString &name)
861 d->templateName = name;
865
866
867
868
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896bool QTemporaryFile::rename(
const QString &newName)
899 return d->rename(newName,
false);
902bool QTemporaryFilePrivate::rename(
const QString &newName,
bool overwrite)
905 auto tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
906 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
907 return q->QFile::rename(newName);
911 if (q->error() == QFile::NoError) {
912 if (overwrite ? tef->renameOverwrite(newName) : tef->rename(newName)) {
919 setError(QFile::RenameError, tef->errorString());
925
926
927
928
931
932
933
934
935
936
937
938
939
940
941
942bool QTemporaryFile::renameOverwrite(
const QString &newName)
945 return d->rename(newName,
true);
949
950
951
952
953
954
956
957
958
959
962
963
964
965
966
967
968
969
970
971
972
974QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
976 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
977 if (engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
980 bool wasOpen = file.isOpen();
983 old_off = file.pos();
984 else if (!file.open(QIODevice::ReadOnly))
987 QTemporaryFile *ret =
new QTemporaryFile;
992 qint64 len = file.read(buffer, 1024);
995 ret->write(buffer, len);
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030bool QTemporaryFile::open(OpenMode mode)
1032 Q_D(QTemporaryFile);
1033 auto tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1034 if (tef && tef->isReallyOpen()) {
1044 d->resetFileEngine();
1046 if (QFile::open(mode)) {
1047 tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1048 if (tef->isUnnamedFile())
1049 d->fileName.clear();
1051 d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
1061#ifndef QT_NO_QOBJECT
1062#include "moc_qtemporaryfile.cpp"
Combined button and popup list for selecting options.