7#include "qplatformdefs.h"
9#include "private/qtemporaryfile_p.h"
10#include "private/qfile_p.h"
11#include "private/qsystemerror_p.h"
14#include "private/qcore_unix_p.h"
18#if defined(QT_BUILD_CORE_LIB)
19#include "qcoreapplication.h"
21#define tr(X) QString::fromLatin1(X)
26using namespace Qt::StringLiterals;
31static inline Char Latin1Char(
char ch)
33 return ushort(uchar(ch));
36typedef HANDLE NativeFileHandle;
44QTemporaryFileName::QTemporaryFileName(
const QString &templateName)
47 QString qfilename = QDir::fromNativeSeparators(templateName);
48 qsizetype phPos = qfilename.size();
49 qsizetype phLength = 0;
54 if (qfilename[phPos] == u'X') {
60 || qfilename[phPos] == u'/') {
70 qfilename.append(
".XXXXXX"_L1);
73 QFileSystemEntry::NativePath filename =
74 QFileSystemEntry(QDir::cleanPath(qfilename)).nativeFilePath();
77 phPos = filename.size();
82 if (filename[phPos] == Latin1Char(
'X')) {
96 Q_ASSERT(phLength >= 6);
103
104
105
106
107
108QFileSystemEntry::NativePath QTemporaryFileName::generateNext()
110 Q_ASSERT(length != 0);
111 Q_ASSERT(pos < path.size());
112 Q_ASSERT(length <= path.size() - pos);
114 Char *
const placeholderStart = (Char *)path.data() + pos;
115 Char *
const placeholderEnd = placeholderStart + length;
129 enum { BitsPerCharacter = 10 };
131 Char *rIter = placeholderEnd;
132 while (rIter != placeholderStart) {
133 quint32 rnd = QRandomGenerator::global()->generate();
134 auto applyOne = [&]() {
135 quint32 v = rnd & ((1 << BitsPerCharacter) - 1);
136 rnd >>= BitsPerCharacter;
137 char ch =
char((26 + 26) * v / (1 << BitsPerCharacter));
139 *--rIter = Latin1Char(ch +
'A');
141 *--rIter = Latin1Char(ch - 26 +
'a');
145 if (rIter == placeholderStart)
149 if (rIter == placeholderStart)
159#if QT_CONFIG(temporaryfile)
162
163
164
165
166
167
168
169
170
171
172
173
174static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
175 quint32 mode,
int flags, QSystemError &error)
177 const int maxAttempts = 16;
178 for (
int attempt = 0; attempt < maxAttempts; ++attempt) {
180 const QFileSystemEntry::NativePath &path = templ.generateNext();
184 const DWORD shareMode = (flags & QTemporaryFileEngine::Win32NonShared)
185 ? 0u : (FILE_SHARE_READ | FILE_SHARE_WRITE);
187 const DWORD extraAccessFlags = (flags & QTemporaryFileEngine::Win32NonShared) ? DELETE : 0;
188 file = CreateFile((
const wchar_t *)path.constData(),
189 GENERIC_READ | GENERIC_WRITE | extraAccessFlags,
190 shareMode, NULL, CREATE_NEW,
191 FILE_ATTRIBUTE_NORMAL, NULL);
193 if (file != INVALID_HANDLE_VALUE)
196 DWORD err = GetLastError();
197 if (err == ERROR_ACCESS_DENIED) {
198 WIN32_FILE_ATTRIBUTE_DATA attributes;
199 if (!GetFileAttributesEx((
const wchar_t *)path.constData(),
200 GetFileExInfoStandard, &attributes)
201 || attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
203 error = QSystemError(err, QSystemError::NativeError);
206 }
else if (err != ERROR_FILE_EXISTS) {
207 error = QSystemError(err, QSystemError::NativeError);
212 file = QT_OPEN(path.constData(),
213 QT_OPEN_CREAT | QT_OPEN_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
214 static_cast<mode_t>(mode));
221 error = QSystemError(err, QSystemError::NativeError);
230enum class CreateUnnamedFileStatus {
236static CreateUnnamedFileStatus
237createUnnamedFile(NativeFileHandle &file, QTemporaryFileName &tfn, quint32 mode, QSystemError *error)
239#ifdef LINUX_UNNAMED_TMPFILE
242 if (!qt_haveLinuxProcfs())
243 return CreateUnnamedFileStatus::NotSupported;
246 QByteArray::size_type lastSlash = tfn.path.lastIndexOf(
'/');
247 if (lastSlash >= 0) {
250 tfn.path[lastSlash] =
'\0';
254 file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
255 static_cast<mode_t>(mode));
257 return CreateUnnamedFileStatus::Success;
259 if (errno == EOPNOTSUPP || errno == EISDIR) {
263 tfn.path[lastSlash] =
'/';
264 return CreateUnnamedFileStatus::NotSupported;
268 *error = QSystemError(errno, QSystemError::NativeError);
269 return CreateUnnamedFileStatus::OtherError;
275 return CreateUnnamedFileStatus::NotSupported;
280QTemporaryFileEngine::~QTemporaryFileEngine()
284 QFSFileEngine::close();
287bool QTemporaryFileEngine::isReallyOpen()
const
289 Q_D(
const QFSFileEngine);
291 if (!((
nullptr == d->fh) && (-1 == d->fd)
293 && (INVALID_HANDLE_VALUE == d->fileHandle)
302void QTemporaryFileEngine::setFileName(
const QString &file)
305 QFSFileEngine::close();
306 QFSFileEngine::setFileName(file);
309bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode,
310 std::optional<QFile::Permissions> permissions)
313 Q_ASSERT(!isReallyOpen());
315 openMode |= QIODevice::ReadWrite;
317 if (!filePathIsTemplate)
318 return QFSFileEngine::open(openMode, permissions);
320 QTemporaryFileName tfn(templateName);
324 NativeFileHandle &file = d->fileHandle;
326 NativeFileHandle &file = d->fd;
329 CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
330 if (st == CreateUnnamedFileStatus::Success) {
332 d->fileEntry.clear();
333 }
else if (st == CreateUnnamedFileStatus::NotSupported &&
334 createFileFromTemplate(file, tfn, fileMode, flags, error)) {
335 filePathIsTemplate =
false;
337 d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
339 setError(QFile::OpenError, error.toString());
343#if !defined(Q_OS_WIN)
344 d->closeFileHandle =
true;
347 d->openMode = openMode;
348 d->lastFlushFailed =
false;
354bool QTemporaryFileEngine::remove()
360 QFSFileEngine::close();
363 if (!filePathIsTemplate && QFSFileEngine::remove()) {
364 d->fileEntry.clear();
370 filePathIsTemplate = filePathWasTemplate;
376bool QTemporaryFileEngine::rename(
const QString &newName)
378 if (isUnnamedFile()) {
379 bool ok = materializeUnnamedFile(newName, DontOverwrite);
380 QFSFileEngine::close();
383 QFSFileEngine::close();
384 return QFSFileEngine::rename(newName);
387bool QTemporaryFileEngine::renameOverwrite(
const QString &newName)
389 if (isUnnamedFile()) {
390 bool ok = materializeUnnamedFile(newName, Overwrite);
391 QFSFileEngine::close();
395 if (flags & Win32NonShared) {
396 QFileSystemEntry newEntry(newName, QFileSystemEntry::FromInternalPath());
397 bool ok = d_func()->nativeRenameOverwrite(newEntry);
398 QFSFileEngine::close();
401 setFileEntry(std::move(newEntry));
406 QFSFileEngine::close();
407 return QFSFileEngine::renameOverwrite(newName);
410bool QTemporaryFileEngine::close()
414 setError(QFile::UnspecifiedError, QString());
418QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file)
const
420 if (isUnnamedFile()) {
421 if (file == AbsoluteLinkTarget || file == RawLinkPath) {
427 const_cast<QTemporaryFileEngine *>(
this)->materializeUnnamedFile(templateName, NameIsTemplate);
429 return QFSFileEngine::fileName(file);
432bool QTemporaryFileEngine::materializeUnnamedFile(
const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
434 Q_ASSERT(isUnnamedFile());
436#ifdef LINUX_UNNAMED_TMPFILE
438 const QByteArray src =
"/proc/self/fd/" + QByteArray::number(d->fd);
439 auto materializeAt = [=](
const QFileSystemEntry &dst) {
440 return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
443 auto materializeAt = [](
const QFileSystemEntry &) {
return false; };
446 auto success = [
this](
const QFileSystemEntry &entry) {
447 filePathIsTemplate =
false;
449 d_func()->fileEntry = entry;
453 auto materializeAsTemplate = [=](
const QString &newName) {
454 QTemporaryFileName tfn(newName);
455 static const int maxAttempts = 16;
456 for (
int attempt = 0; attempt < maxAttempts; ++attempt) {
458 QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
459 if (materializeAt(entry))
460 return success(entry);
465 if (mode == NameIsTemplate) {
466 if (materializeAsTemplate(newName))
470 QFileSystemEntry dst(newName);
471 if (materializeAt(dst))
474 if (errno == EEXIST && mode == Overwrite) {
476 if (!materializeAsTemplate(templateName))
480 QFSFileEngine::close();
481 return QFSFileEngine::renameOverwrite(newName);
486 setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
490bool QTemporaryFileEngine::isUnnamedFile()
const
492#ifdef LINUX_UNNAMED_TMPFILE
494 Q_ASSERT(d_func()->fileEntry.isEmpty());
495 Q_ASSERT(filePathIsTemplate);
505QTemporaryFilePrivate::QTemporaryFilePrivate()
509QTemporaryFilePrivate::QTemporaryFilePrivate(
const QString &templateNameIn)
510 : templateName(templateNameIn)
514QTemporaryFilePrivate::~QTemporaryFilePrivate()
518QAbstractFileEngine *QTemporaryFilePrivate::engine()
const
521 fileEngine.reset(
new QTemporaryFileEngine(&templateName));
524 return fileEngine.get();
527void QTemporaryFilePrivate::resetFileEngine()
const
532 QTemporaryFileEngine *tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
533 if (fileName.isEmpty())
534 tef->initialize(templateName, 0600);
536 tef->initialize(fileName, 0600,
false);
539void QTemporaryFilePrivate::materializeUnnamedFile()
541#ifdef LINUX_UNNAMED_TMPFILE
542 if (!fileName.isEmpty() || !fileEngine)
545 auto *tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
546 fileName = tef->fileName(QAbstractFileEngine::DefaultName);
550QString QTemporaryFilePrivate::defaultTemplateName()
553#if defined(QT_BUILD_CORE_LIB)
554 baseName = QCoreApplication::applicationName();
555 if (baseName.isEmpty())
557 baseName =
"qt_temp"_L1;
559 return QDir::tempPath() + u'/' + baseName +
".XXXXXX"_L1;
565
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
625QTemporaryFile::QTemporaryFile()
626 : QFile(*
new QTemporaryFilePrivate)
630QTemporaryFile::QTemporaryFile(
const QString &templateName)
631 : QFile(*
new QTemporaryFilePrivate(templateName))
637
638
639
640
641
642
643
644
645
646
647
648
649QTemporaryFile::QTemporaryFile()
650 : QTemporaryFile(
nullptr)
655
656
657
658
661
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
687QTemporaryFile::QTemporaryFile(
const QString &templateName)
688 : QTemporaryFile(templateName,
nullptr)
693
694
695
696
697
698
699QTemporaryFile::QTemporaryFile(QObject *parent)
700 : QFile(*
new QTemporaryFilePrivate, parent)
705
706
707
708
709
710
711
712
713
714
715
716
717QTemporaryFile::QTemporaryFile(
const QString &templateName, QObject *parent)
718 : QFile(*
new QTemporaryFilePrivate(templateName), parent)
724
725
726
727
728
729
730QTemporaryFile::~QTemporaryFile()
734 if (!d->fileName.isEmpty() && d->autoRemove)
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
757
758
759
760
761
762
763
764
765
766
767
768bool QTemporaryFile::autoRemove()
const
770 Q_D(
const QTemporaryFile);
771 return d->autoRemove;
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792void QTemporaryFile::setAutoRemove(
bool b)
799
800
801
802
803
804
805
806
807
808
809
811QString QTemporaryFile::fileName()
const
813 Q_D(
const QTemporaryFile);
814 auto tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
815 if (tef && tef->isReallyOpen())
816 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
818 if (d->fileName.isEmpty())
820 return d->engine()->fileName(QAbstractFileEngine::DefaultName);
824
825
826
827
828
829
830
831
832QString QTemporaryFile::fileTemplate()
const
834 Q_D(
const QTemporaryFile);
835 return d->templateName;
839
840
841
842
845
846
847
848
849
850
851
852
853
854
855
856void QTemporaryFile::setFileTemplate(
const QString &name)
859 d->templateName = name;
863
864
865
866
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886bool QTemporaryFile::rename(
const QString &newName)
889 return d->rename(newName,
false);
892bool QTemporaryFilePrivate::rename(
const QString &newName,
bool overwrite)
895 auto tef =
static_cast<QTemporaryFileEngine *>(fileEngine.get());
896 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
897 return q->QFile::rename(newName);
901 if (q->error() == QFile::NoError) {
902 if (overwrite ? tef->renameOverwrite(newName) : tef->rename(newName)) {
909 setError(QFile::RenameError, tef->errorString());
915
916
917
918
919
920
922
923
924
925
928
929
930
931
932
933
934
935
936
937
938
940QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
942 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
943 if (engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
946 bool wasOpen = file.isOpen();
949 old_off = file.pos();
950 else if (!file.open(QIODevice::ReadOnly))
953 QTemporaryFile *ret =
new QTemporaryFile;
958 qint64 len = file.read(buffer, 1024);
961 ret->write(buffer, len);
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996bool QTemporaryFile::open(OpenMode mode)
999 auto tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1000 if (tef && tef->isReallyOpen()) {
1010 d->resetFileEngine();
1012 if (QFile::open(mode)) {
1013 tef =
static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
1014 if (tef->isUnnamedFile())
1015 d->fileName.clear();
1017 d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
1027#ifndef QT_NO_QOBJECT
1028#include "moc_qtemporaryfile.cpp"
Combined button and popup list for selecting options.