Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qfile.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2017 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qplatformdefs.h"
6#include "qdebug.h"
7#include "qfile.h"
8#include "qfsfileengine_p.h"
9#include "qtemporaryfile.h"
10#include "qtemporaryfile_p.h"
11#include "qlist.h"
12#include "qfileinfo.h"
13#include "private/qiodevice_p.h"
14#include "private/qfile_p.h"
15#include "private/qfilesystemengine_p.h"
16#include "private/qsystemerror_p.h"
17#include "private/qtemporaryfile_p.h"
18#if defined(QT_BUILD_CORE_LIB)
19# include "qcoreapplication.h"
20#endif
21
22#ifdef QT_NO_QOBJECT
23#define tr(X) QString::fromLatin1(X)
24#endif
25
27
28using namespace Qt::StringLiterals;
29
31static bool file_already_open(QFile &file, const char *where = nullptr)
32{
33 qWarning("QFile::%s: File (%ls) already open", where ? where : "open", qUtf16Printable(file.fileName()));
34 return false;
35}
36
37//************* QFilePrivate
41
45
46bool
47QFilePrivate::openExternalFile(QIODevice::OpenMode flags, int fd, QFile::FileHandleFlags handleFlags)
48{
49#ifdef QT_NO_FSFILEENGINE
51 Q_UNUSED(fd);
52 return false;
53#else
54 auto fs = std::make_unique<QFSFileEngine>();
55 auto fe = fs.get();
56 fileEngine = std::move(fs);
57 return fe->open(flags, fd, handleFlags);
58#endif
59}
60
61bool
62QFilePrivate::openExternalFile(QIODevice::OpenMode flags, FILE *fh, QFile::FileHandleFlags handleFlags)
63{
64#ifdef QT_NO_FSFILEENGINE
66 Q_UNUSED(fh);
67 return false;
68#else
69 auto fs = std::make_unique<QFSFileEngine>();
70 auto fe = fs.get();
71 fileEngine = std::move(fs);
72 return fe->open(flags, fh, handleFlags);
73#endif
74}
75
82
83//************* QFile
84
198#ifdef QT_NO_QOBJECT
201{
202}
205{
206 d_func()->fileName = name;
207}
209 : QFileDevice(dd)
210{
211}
212#else
224 : QFileDevice(*new QFilePrivate, parent)
225{
226}
231
238
241{
242 Q_D(QFile);
243 d->fileName = name;
244}
250 : QFileDevice(*new QFilePrivate, parent)
251{
252 Q_D(QFile);
253 d->fileName = name;
254}
259 : QFileDevice(dd, parent)
260{
261}
262#endif
263
268{
269}
270
278{
279 Q_D(const QFile);
280 return d->engine()->fileName(QAbstractFileEngine::DefaultName);
281}
282
301void
303{
304 Q_D(QFile);
305 if (isOpen()) {
306 file_already_open(*this, "setFileName");
307 close();
308 }
309 d->fileEngine.reset(); //get a new file engine later
310 d->fileName = name;
311}
312
350bool
352{
353 Q_D(const QFile);
354 // 0x1000000 = QAbstractFileEngine::Refresh, forcing an update
355 return d->engine()->fileFlags(QAbstractFileEngine::FlagsMask
357}
358
367bool
372
388{
389 Q_D(const QFile);
390 return d->engine()->fileName(QAbstractFileEngine::AbsoluteLinkTarget);
391}
392
408
418bool
420{
421 Q_D(QFile);
422 if (d->fileName.isEmpty() &&
423 !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
424 qWarning("QFile::remove: Empty or null file name");
425 return false;
426 }
427 unsetError();
428 close();
429 if (error() == QFile::NoError) {
430 if (d->engine()->remove()) {
431 unsetError();
432 return true;
433 }
434 d->setError(QFile::RemoveError, d->fileEngine->errorString());
435 }
436 return false;
437}
438
449bool
451{
452 return QFile(fileName).remove();
453}
454
463
479
484bool
486{
487 Q_D(QFile);
488 if (d->fileName.isEmpty() &&
489 !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
490 qWarning("QFile::remove: Empty or null file name");
491 return false;
492 }
493 unsetError();
494 close();
495 if (error() == QFile::NoError) {
496 QFileSystemEntry fileEntry(d->fileName);
497 QFileSystemEntry trashEntry;
499 if (QFileSystemEngine::moveFileToTrash(fileEntry, trashEntry, error)) {
500 setFileName(trashEntry.filePath());
501 unsetError();
502 return true;
503 }
504 d->setError(QFile::RenameError, error.toString());
505 }
506 return false;
507}
508
524bool
526{
528 if (file.moveToTrash()) {
529 if (pathInTrash)
530 *pathInTrash = file.fileName();
531 return true;
532 }
533 return false;
534}
535
553bool
554QFile::rename(const QString &newName)
555{
556 Q_D(QFile);
557
558 // if this is a QTemporaryFile, the virtual fileName() call here may do something
559 if (fileName().isEmpty()) {
560 qWarning("QFile::rename: Empty or null file name");
561 return false;
562 }
563 if (d->fileName == newName) {
564 d->setError(QFile::RenameError, tr("Destination file is the same file."));
565 return false;
566 }
567 if (!exists()) {
568 d->setError(QFile::RenameError, tr("Source file does not exist."));
569 return false;
570 }
571
572 // If the file exists and it is a case-changing rename ("foo" -> "Foo"),
573 // compare Ids to make sure it really is a different file.
574 // Note: this does not take file engines into account.
575 bool changingCase = false;
577 if (!targetId.isNull()) {
578 QByteArray fileId = d->fileEngine ?
579 d->fileEngine->id() :
581 changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0);
582 if (!changingCase) {
583 d->setError(QFile::RenameError, tr("Destination file exists"));
584 return false;
585 }
586
587#if defined(Q_OS_LINUX) && QT_CONFIG(temporaryfile)
588 // rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive
589 // FS, such as FAT32. Move the file away and rename in 2 steps to work around.
590 QTemporaryFileName tfn(d->fileName);
591 QFileSystemEntry src(d->fileName);
593 for (int attempt = 0; attempt < 16; ++attempt) {
594 QFileSystemEntry tmp(tfn.generateNext(), QFileSystemEntry::FromNativePath());
595
596 // rename to temporary name
598 continue;
599
600 // rename to final name
602 d->fileEngine->setFileName(newName);
603 d->fileName = newName;
604 return true;
605 }
606
607 // We need to restore the original file.
608 QSystemError error2;
609 if (QFileSystemEngine::renameFile(tmp, src, error2))
610 break; // report the original error, below
611
612 // report both errors
613 d->setError(QFile::RenameError,
614 tr("Error while renaming: %1").arg(error.toString())
615 + u'\n'
616 + tr("Unable to restore from %1: %2").
617 arg(QDir::toNativeSeparators(tmp.filePath()), error2.toString()));
618 return false;
619 }
620 d->setError(QFile::RenameError,
621 tr("Error while renaming: %1").arg(error.toString()));
622 return false;
623#endif // Q_OS_LINUX
624 }
625 unsetError();
626 close();
627 if (error() == QFile::NoError) {
628 if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) {
629 unsetError();
630 // engine was able to handle the new name so we just reset it
631 d->fileEngine->setFileName(newName);
632 d->fileName = newName;
633 return true;
634 }
635
636 if (isSequential()) {
637 d->setError(QFile::RenameError, tr("Will not rename sequential file using block copy"));
638 return false;
639 }
640
641 QFile out(newName);
644 bool error = false;
645 char block[4096];
646 qint64 bytes;
647 while ((bytes = read(block, sizeof(block))) > 0) {
648 if (bytes != out.write(block, bytes)) {
649 d->setError(QFile::RenameError, out.errorString());
650 error = true;
651 break;
652 }
653 }
654 if (bytes == -1) {
655 d->setError(QFile::RenameError, errorString());
656 error = true;
657 }
658 if (!error) {
659 if (!remove()) {
660 d->setError(QFile::RenameError, tr("Cannot remove source file"));
661 error = true;
662 }
663 }
664 if (error) {
665 out.remove();
666 } else {
667 d->fileEngine->setFileName(newName);
669 unsetError();
670 setFileName(newName);
671 }
672 close();
673 return !error;
674 }
675 close();
676 d->setError(QFile::RenameError,
677 tr("Cannot open destination file: %1").arg(out.errorString()));
678 } else {
679 d->setError(QFile::RenameError, errorString());
680 }
681 }
682 return false;
683}
684
697bool
698QFile::rename(const QString &oldName, const QString &newName)
699{
700 return QFile(oldName).rename(newName);
701}
702
719bool
720QFile::link(const QString &linkName)
721{
722 Q_D(QFile);
723 if (fileName().isEmpty()) {
724 qWarning("QFile::link: Empty or null file name");
725 return false;
726 }
727 QFileInfo fi(linkName);
728 if (d->engine()->link(fi.absoluteFilePath())) {
729 unsetError();
730 return true;
731 }
732 d->setError(QFile::RenameError, d->fileEngine->errorString());
733 return false;
734}
735
747bool
748QFile::link(const QString &fileName, const QString &linkName)
749{
750 return QFile(fileName).link(linkName);
751}
752
764bool
765QFile::copy(const QString &newName)
766{
767 Q_D(QFile);
768 if (fileName().isEmpty()) {
769 qWarning("QFile::copy: Empty or null file name");
770 return false;
771 }
772 if (QFile::exists(newName)) {
773 // ### Race condition. If a file is moved in after this, it /will/ be
774 // overwritten. On Unix, the proper solution is to use hardlinks:
775 // return ::link(old, new) && ::remove(old); See also rename().
776 d->setError(QFile::CopyError, tr("Destination file exists"));
777 return false;
778 }
779 unsetError();
780 close();
781 if (error() == QFile::NoError) {
782 if (d->engine()->copy(newName)) {
783 unsetError();
784 return true;
785 } else {
786 bool error = false;
787 if (!open(QFile::ReadOnly)) {
788 error = true;
789 d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName));
790 } else {
791 const auto fileTemplate = "%1/qt_temp.XXXXXX"_L1;
792#if !QT_CONFIG(temporaryfile)
793 QFile out(fileTemplate.arg(QFileInfo(newName).path()));
794 if (!out.open(QIODevice::ReadWrite))
795 error = true;
796#else
797 QTemporaryFile out(fileTemplate.arg(QFileInfo(newName).path()));
798 if (!out.open()) {
799 out.setFileTemplate(fileTemplate.arg(QDir::tempPath()));
800 if (!out.open())
801 error = true;
802 }
803#endif
804 if (error) {
805 d->setError(QFile::CopyError, tr("Cannot open for output: %1").arg(out.errorString()));
806 out.close();
807 close();
808 } else {
809 if (!d->engine()->cloneTo(out.d_func()->engine())) {
810 char block[4096];
811 qint64 totalRead = 0;
812 while (!atEnd()) {
813 qint64 in = read(block, sizeof(block));
814 if (in <= 0)
815 break;
816 totalRead += in;
817 if (in != out.write(block, in)) {
818 close();
819 d->setError(QFile::CopyError, tr("Failure to write block: %1")
820 .arg(out.errorString()));
821 error = true;
822 break;
823 }
824 }
825
826 if (totalRead != size()) {
827 // Unable to read from the source. The error string is
828 // already set from read().
829 error = true;
830 }
831 }
832
833 if (!error) {
834 // Sync to disk if possible. Ignore errors (e.g. not supported).
835 out.d_func()->fileEngine->syncToDisk();
836
837 if (!out.rename(newName)) {
838 error = true;
839 close();
840 d->setError(QFile::CopyError, tr("Cannot create %1 for output: %2")
841 .arg(newName, out.errorString()));
842 }
843 }
844#if !QT_CONFIG(temporaryfile)
845 if (error)
846 out.remove();
847#else
848 if (!error)
849 out.setAutoRemove(false);
850#endif
851 }
852 }
853 if (!error) {
855 close();
856 unsetError();
857 return true;
858 }
859 }
860 }
861 return false;
862}
863
877bool
878QFile::copy(const QString &fileName, const QString &newName)
879{
880 return QFile(fileName).copy(newName);
881}
882
904bool QFile::open(OpenMode mode)
905{
906 Q_D(QFile);
907 if (isOpen())
908 return file_already_open(*this);
909 // Either Append or NewOnly implies WriteOnly
910 if (mode & (Append | NewOnly))
911 mode |= WriteOnly;
912 unsetError();
913 if ((mode & (ReadOnly | WriteOnly)) == 0) {
914 qWarning("QIODevice::open: File access not specified");
915 return false;
916 }
917
918 // QIODevice provides the buffering, so there's no need to request it from the file engine.
919 if (d->engine()->open(mode | QIODevice::Unbuffered)) {
921 if (mode & Append)
922 seek(size());
923 return true;
924 }
925 QFile::FileError err = d->fileEngine->error();
926 if (err == QFile::UnspecifiedError)
927 err = QFile::OpenError;
928 d->setError(err, d->fileEngine->errorString());
929 return false;
930}
931
949bool QFile::open(OpenMode mode, QFile::Permissions permissions)
950{
951 Q_D(QFile);
952 if (isOpen())
953 return file_already_open(*this);
954 // Either Append or NewOnly implies WriteOnly
955 if (mode & (Append | NewOnly))
956 mode |= WriteOnly;
957 unsetError();
958 if ((mode & (ReadOnly | WriteOnly)) == 0) {
959 qWarning("QIODevice::open: File access not specified");
960 return false;
961 }
962
963 // QIODevice provides the buffering, so there's no need to request it from the file engine.
964 if (d->engine()->open(mode | QIODevice::Unbuffered, permissions)) {
966 if (mode & Append)
967 seek(size());
968 return true;
969 }
970 QFile::FileError err = d->fileEngine->error();
971 if (err == QFile::UnspecifiedError)
972 err = QFile::OpenError;
973 d->setError(err, d->fileEngine->errorString());
974 return false;
975}
976
1019bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
1020{
1021 Q_D(QFile);
1022 if (isOpen())
1023 return file_already_open(*this);
1024 // Either Append or NewOnly implies WriteOnly
1025 if (mode & (Append | NewOnly))
1026 mode |= WriteOnly;
1027 unsetError();
1028 if ((mode & (ReadOnly | WriteOnly)) == 0) {
1029 qWarning("QFile::open: File access not specified");
1030 return false;
1031 }
1032
1033 // QIODevice provides the buffering, so request unbuffered file engines
1034 if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) {
1036 if (!(mode & Append) && !isSequential()) {
1037 qint64 pos = (qint64)QT_FTELL(fh);
1038 if (pos != -1) {
1039 // Skip redundant checks in QFileDevice::seek().
1041 }
1042 }
1043 return true;
1044 }
1045 return false;
1046}
1047
1071bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags)
1072{
1073 Q_D(QFile);
1074 if (isOpen())
1075 return file_already_open(*this);
1076 // Either Append or NewOnly implies WriteOnly
1077 if (mode & (Append | NewOnly))
1078 mode |= WriteOnly;
1079 unsetError();
1080 if ((mode & (ReadOnly | WriteOnly)) == 0) {
1081 qWarning("QFile::open: File access not specified");
1082 return false;
1083 }
1084
1085 // QIODevice provides the buffering, so request unbuffered file engines
1086 if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) {
1088 if (!(mode & Append) && !isSequential()) {
1089 qint64 pos = (qint64)QT_LSEEK(fd, QT_OFF_T(0), SEEK_CUR);
1090 if (pos != -1) {
1091 // Skip redundant checks in QFileDevice::seek().
1093 }
1094 }
1095 return true;
1096 }
1097 return false;
1098}
1099
1104{
1105 return QFileDevice::resize(sz); // for now
1106}
1107
1121bool
1123{
1124 return QFile(fileName).resize(sz);
1125}
1126
1130QFile::Permissions QFile::permissions() const
1131{
1132 return QFileDevice::permissions(); // for now
1133}
1134
1142QFile::Permissions
1144{
1145 return QFile(fileName).permissions();
1146}
1147
1159bool QFile::setPermissions(Permissions permissions)
1160{
1161 return QFileDevice::setPermissions(permissions); // for now
1162}
1163
1170bool
1171QFile::setPermissions(const QString &fileName, Permissions permissions)
1172{
1173 return QFile(fileName).setPermissions(permissions);
1174}
1175
1180{
1181 return QFileDevice::size(); // for now
1182}
1183
1378
1379#ifndef QT_NO_QOBJECT
1380#include "moc_qfile.cpp"
1381#endif
\inmodule QtCore \reentrant
static std::unique_ptr< QAbstractFileEngine > create(const QString &fileName)
Creates and returns a QAbstractFileEngine suitable for processing fileName.
\inmodule QtCore
Definition qbytearray.h:57
int compare(QByteArrayView a, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qbytearray.h:664
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2133
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
\inmodule QtCore
virtual bool isUnnamedFile() const
std::unique_ptr< QAbstractFileEngine > fileEngine
QFileDevice::FileHandleFlags handleFlags
\inmodule QtCore
Definition qfiledevice.h:32
qint64 pos() const override
\reimp
bool seek(qint64 offset) override
For random-access devices, this function sets the current position to pos, returning true on success,...
qint64 size() const override
Returns the size of the file.
void unsetError()
Sets the file's error to QFileDevice::NoError.
virtual bool resize(qint64 sz)
Sets the file size (in bytes) sz.
bool atEnd() const override
Returns true if the end of the file has been reached; otherwise returns false.
FileError error() const
Returns the file error status.
bool isSequential() const override
Returns true if the file can only be manipulated sequentially; otherwise returns false.
virtual QString fileName() const
Returns the name of the file.
FileError
This enum describes the errors that may be returned by the error() function.
Definition qfiledevice.h:39
virtual bool setPermissions(Permissions permissionSpec)
Sets the permissions for the file to the permissions specified.
void close() override
Calls QFileDevice::flush() and closes the file.
virtual Permissions permissions() const
Returns the complete OR-ed together combination of QFile::Permission for the file.
QString symLinkTarget() const
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
QString fileName
Definition qfile_p.h:39
~QFilePrivate()
Definition qfile.cpp:42
QAbstractFileEngine * engine() const override
Definition qfile.cpp:76
bool openExternalFile(QIODevice::OpenMode flags, int fd, QFile::FileHandleFlags handleFlags)
Definition qfile.cpp:47
static QByteArray id(const QFileSystemEntry &entry)
static bool moveFileToTrash(const QFileSystemEntry &source, QFileSystemEntry &newLocation, QSystemError &error)
static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
Q_AUTOTEST_EXPORT QString filePath() const
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
bool setPermissions(Permissions permissionSpec) override
Sets the permissions for the file to the permissions specified.
Definition qfile.cpp:1159
QFile()
Constructs a QFile object.
Definition qfile.cpp:216
QString symLinkTarget() const
Definition qfile.cpp:387
bool link(const QString &newName)
Creates a link named linkName that points to the file currently specified by fileName().
Definition qfile.cpp:720
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:765
bool remove()
Removes the file specified by fileName().
Definition qfile.cpp:419
void setFileName(const QString &name)
Sets the name of the file.
Definition qfile.cpp:302
QString fileName() const override
Returns the name set by setFileName() or to the QFile constructors.
Definition qfile.cpp:277
bool moveToTrash()
Definition qfile.cpp:485
Permissions permissions() const override
\reimp
Definition qfile.cpp:1130
~QFile()
Destroys the file object, closing it if necessary.
Definition qfile.cpp:267
bool rename(const QString &newName)
Renames the file currently specified by fileName() to newName.
Definition qfile.cpp:554
friend class QTemporaryFile
Definition qfile.h:323
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
qint64 size() const override
\reimp
Definition qfile.cpp:1179
bool resize(qint64 sz) override
\reimp
Definition qfile.cpp:1103
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
bool isOpen() const
Returns true if the device is open; otherwise returns false.
QString errorString() const
Returns a human-readable description of the last device error that occurred.
virtual bool seek(qint64 pos)
For random-access devices, this function sets the current position to pos, returning true on success,...
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
\inmodule QtCore
Definition qobject.h:103
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
Combined button and popup list for selecting options.
@ CaseInsensitive
#define Q_DECL_COLD_FUNCTION
static Q_DECL_COLD_FUNCTION bool file_already_open(QFile &file, const char *where=nullptr)
Definition qfile.cpp:31
static QByteArray fileId(HANDLE handle)
#define qWarning
Definition qlogging.h:166
GLenum mode
GLenum src
GLbitfield flags
GLuint64 GLenum GLint fd
GLuint name
GLuint in
GLsizei const GLchar *const * path
SSL_CTX int void * arg
#define qUtf16Printable(string)
Definition qstring.h:1543
#define tr(X)
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:60
QFile file
[0]
QTextStream out(stdout)
[7]
QObject::connect nullptr