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
481
486bool
488{
489 Q_D(QFile);
490 if (d->fileName.isEmpty() &&
491 !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
492 qWarning("QFile::remove: Empty or null file name");
493 return false;
494 }
495 unsetError();
496 close();
497 if (error() == QFile::NoError) {
498 QFileSystemEntry fileEntry(d->fileName);
499 QFileSystemEntry trashEntry;
501 if (QFileSystemEngine::moveFileToTrash(fileEntry, trashEntry, error)) {
502 setFileName(trashEntry.filePath());
503 unsetError();
504 return true;
505 }
506 d->setError(QFile::RenameError, error.toString());
507 }
508 return false;
509}
510
526bool
528{
530 if (file.moveToTrash()) {
531 if (pathInTrash)
532 *pathInTrash = file.fileName();
533 return true;
534 }
535 return false;
536}
537
555bool
556QFile::rename(const QString &newName)
557{
558 Q_D(QFile);
559
560 // if this is a QTemporaryFile, the virtual fileName() call here may do something
561 if (fileName().isEmpty()) {
562 qWarning("QFile::rename: Empty or null file name");
563 return false;
564 }
565 if (d->fileName == newName) {
566 d->setError(QFile::RenameError, tr("Destination file is the same file."));
567 return false;
568 }
569 if (!exists()) {
570 d->setError(QFile::RenameError, tr("Source file does not exist."));
571 return false;
572 }
573
574 // If the file exists and it is a case-changing rename ("foo" -> "Foo"),
575 // compare Ids to make sure it really is a different file.
576 // Note: this does not take file engines into account.
577 bool changingCase = false;
579 if (!targetId.isNull()) {
580 QByteArray fileId = d->fileEngine ?
581 d->fileEngine->id() :
583 changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0);
584 if (!changingCase) {
585 d->setError(QFile::RenameError, tr("Destination file exists"));
586 return false;
587 }
588
589#if defined(Q_OS_LINUX) && QT_CONFIG(temporaryfile)
590 // rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive
591 // FS, such as FAT32. Move the file away and rename in 2 steps to work around.
592 QTemporaryFileName tfn(d->fileName);
593 QFileSystemEntry src(d->fileName);
595 for (int attempt = 0; attempt < 16; ++attempt) {
596 QFileSystemEntry tmp(tfn.generateNext(), QFileSystemEntry::FromNativePath());
597
598 // rename to temporary name
600 continue;
601
602 // rename to final name
604 d->fileEngine->setFileName(newName);
605 d->fileName = newName;
606 return true;
607 }
608
609 // We need to restore the original file.
610 QSystemError error2;
611 if (QFileSystemEngine::renameFile(tmp, src, error2))
612 break; // report the original error, below
613
614 // report both errors
615 d->setError(QFile::RenameError,
616 tr("Error while renaming: %1").arg(error.toString())
617 + u'\n'
618 + tr("Unable to restore from %1: %2").
619 arg(QDir::toNativeSeparators(tmp.filePath()), error2.toString()));
620 return false;
621 }
622 d->setError(QFile::RenameError,
623 tr("Error while renaming: %1").arg(error.toString()));
624 return false;
625#endif // Q_OS_LINUX
626 }
627 unsetError();
628 close();
629 if (error() == QFile::NoError) {
630 if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) {
631 unsetError();
632 // engine was able to handle the new name so we just reset it
633 d->fileEngine->setFileName(newName);
634 d->fileName = newName;
635 return true;
636 }
637
638 if (isSequential()) {
639 d->setError(QFile::RenameError, tr("Will not rename sequential file using block copy"));
640 return false;
641 }
642
643 QFile out(newName);
646 bool error = false;
647 char block[4096];
648 qint64 bytes;
649 while ((bytes = read(block, sizeof(block))) > 0) {
650 if (bytes != out.write(block, bytes)) {
651 d->setError(QFile::RenameError, out.errorString());
652 error = true;
653 break;
654 }
655 }
656 if (bytes == -1) {
657 d->setError(QFile::RenameError, errorString());
658 error = true;
659 }
660 if (!error) {
661 if (!remove()) {
662 d->setError(QFile::RenameError, tr("Cannot remove source file"));
663 error = true;
664 }
665 }
666 if (error) {
667 out.remove();
668 } else {
669 d->fileEngine->setFileName(newName);
671 unsetError();
672 setFileName(newName);
673 }
674 close();
675 return !error;
676 }
677 close();
678 d->setError(QFile::RenameError,
679 tr("Cannot open destination file: %1").arg(out.errorString()));
680 } else {
681 d->setError(QFile::RenameError, errorString());
682 }
683 }
684 return false;
685}
686
699bool
700QFile::rename(const QString &oldName, const QString &newName)
701{
702 return QFile(oldName).rename(newName);
703}
704
721bool
722QFile::link(const QString &linkName)
723{
724 Q_D(QFile);
725 if (fileName().isEmpty()) {
726 qWarning("QFile::link: Empty or null file name");
727 return false;
728 }
729 QFileInfo fi(linkName);
730 if (d->engine()->link(fi.absoluteFilePath())) {
731 unsetError();
732 return true;
733 }
734 d->setError(QFile::RenameError, d->fileEngine->errorString());
735 return false;
736}
737
749bool
750QFile::link(const QString &fileName, const QString &linkName)
751{
752 return QFile(fileName).link(linkName);
753}
754
766bool
767QFile::copy(const QString &newName)
768{
769 Q_D(QFile);
770 if (fileName().isEmpty()) {
771 qWarning("QFile::copy: Empty or null file name");
772 return false;
773 }
774 if (QFile::exists(newName)) {
775 // ### Race condition. If a file is moved in after this, it /will/ be
776 // overwritten. On Unix, the proper solution is to use hardlinks:
777 // return ::link(old, new) && ::remove(old); See also rename().
778 d->setError(QFile::CopyError, tr("Destination file exists"));
779 return false;
780 }
781 unsetError();
782 close();
783 if (error() == QFile::NoError) {
784 if (d->engine()->copy(newName)) {
785 unsetError();
786 return true;
787 } else {
788 bool error = false;
789 if (!open(QFile::ReadOnly)) {
790 error = true;
791 d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName));
792 } else {
793 const auto fileTemplate = "%1/qt_temp.XXXXXX"_L1;
794#if !QT_CONFIG(temporaryfile)
795 QFile out(fileTemplate.arg(QFileInfo(newName).path()));
796 if (!out.open(QIODevice::ReadWrite))
797 error = true;
798#else
799 QTemporaryFile out(fileTemplate.arg(QFileInfo(newName).path()));
800 if (!out.open()) {
801 out.setFileTemplate(fileTemplate.arg(QDir::tempPath()));
802 if (!out.open())
803 error = true;
804 }
805#endif
806 if (error) {
807 d->setError(QFile::CopyError, tr("Cannot open for output: %1").arg(out.errorString()));
808 out.close();
809 close();
810 } else {
811 if (!d->engine()->cloneTo(out.d_func()->engine())) {
812 char block[4096];
813 qint64 totalRead = 0;
814 while (!atEnd()) {
815 qint64 in = read(block, sizeof(block));
816 if (in <= 0)
817 break;
818 totalRead += in;
819 if (in != out.write(block, in)) {
820 close();
821 d->setError(QFile::CopyError, tr("Failure to write block: %1")
822 .arg(out.errorString()));
823 error = true;
824 break;
825 }
826 }
827
828 if (totalRead != size()) {
829 // Unable to read from the source. The error string is
830 // already set from read().
831 error = true;
832 }
833 }
834
835 if (!error) {
836 // Sync to disk if possible. Ignore errors (e.g. not supported).
837 out.d_func()->fileEngine->syncToDisk();
838
839 if (!out.rename(newName)) {
840 error = true;
841 close();
842 d->setError(QFile::CopyError, tr("Cannot create %1 for output: %2")
843 .arg(newName, out.errorString()));
844 }
845 }
846#if !QT_CONFIG(temporaryfile)
847 if (error)
848 out.remove();
849#else
850 if (!error)
851 out.setAutoRemove(false);
852#endif
853 }
854 }
855 if (!error) {
857 close();
858 unsetError();
859 return true;
860 }
861 }
862 }
863 return false;
864}
865
879bool
880QFile::copy(const QString &fileName, const QString &newName)
881{
882 return QFile(fileName).copy(newName);
883}
884
906bool QFile::open(OpenMode mode)
907{
908 Q_D(QFile);
909 if (isOpen())
910 return file_already_open(*this);
911 // Either Append or NewOnly implies WriteOnly
912 if (mode & (Append | NewOnly))
913 mode |= WriteOnly;
914 unsetError();
915 if ((mode & (ReadOnly | WriteOnly)) == 0) {
916 qWarning("QIODevice::open: File access not specified");
917 return false;
918 }
919
920 // QIODevice provides the buffering, so there's no need to request it from the file engine.
921 if (d->engine()->open(mode | QIODevice::Unbuffered)) {
923 if (mode & Append)
924 seek(size());
925 return true;
926 }
927 QFile::FileError err = d->fileEngine->error();
928 if (err == QFile::UnspecifiedError)
929 err = QFile::OpenError;
930 d->setError(err, d->fileEngine->errorString());
931 return false;
932}
933
951bool QFile::open(OpenMode mode, QFile::Permissions permissions)
952{
953 Q_D(QFile);
954 if (isOpen())
955 return file_already_open(*this);
956 // Either Append or NewOnly implies WriteOnly
957 if (mode & (Append | NewOnly))
958 mode |= WriteOnly;
959 unsetError();
960 if ((mode & (ReadOnly | WriteOnly)) == 0) {
961 qWarning("QIODevice::open: File access not specified");
962 return false;
963 }
964
965 // QIODevice provides the buffering, so there's no need to request it from the file engine.
966 if (d->engine()->open(mode | QIODevice::Unbuffered, permissions)) {
968 if (mode & Append)
969 seek(size());
970 return true;
971 }
972 QFile::FileError err = d->fileEngine->error();
973 if (err == QFile::UnspecifiedError)
974 err = QFile::OpenError;
975 d->setError(err, d->fileEngine->errorString());
976 return false;
977}
978
1021bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
1022{
1023 Q_D(QFile);
1024 if (isOpen())
1025 return file_already_open(*this);
1026 // Either Append or NewOnly implies WriteOnly
1027 if (mode & (Append | NewOnly))
1028 mode |= WriteOnly;
1029 unsetError();
1030 if ((mode & (ReadOnly | WriteOnly)) == 0) {
1031 qWarning("QFile::open: File access not specified");
1032 return false;
1033 }
1034
1035 // QIODevice provides the buffering, so request unbuffered file engines
1036 if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) {
1038 if (!(mode & Append) && !isSequential()) {
1039 qint64 pos = (qint64)QT_FTELL(fh);
1040 if (pos != -1) {
1041 // Skip redundant checks in QFileDevice::seek().
1043 }
1044 }
1045 return true;
1046 }
1047 return false;
1048}
1049
1073bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags)
1074{
1075 Q_D(QFile);
1076 if (isOpen())
1077 return file_already_open(*this);
1078 // Either Append or NewOnly implies WriteOnly
1079 if (mode & (Append | NewOnly))
1080 mode |= WriteOnly;
1081 unsetError();
1082 if ((mode & (ReadOnly | WriteOnly)) == 0) {
1083 qWarning("QFile::open: File access not specified");
1084 return false;
1085 }
1086
1087 // QIODevice provides the buffering, so request unbuffered file engines
1088 if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) {
1090 if (!(mode & Append) && !isSequential()) {
1091 qint64 pos = (qint64)QT_LSEEK(fd, QT_OFF_T(0), SEEK_CUR);
1092 if (pos != -1) {
1093 // Skip redundant checks in QFileDevice::seek().
1095 }
1096 }
1097 return true;
1098 }
1099 return false;
1100}
1101
1106{
1107 return QFileDevice::resize(sz); // for now
1108}
1109
1123bool
1125{
1126 return QFile(fileName).resize(sz);
1127}
1128
1132QFile::Permissions QFile::permissions() const
1133{
1134 return QFileDevice::permissions(); // for now
1135}
1136
1144QFile::Permissions
1146{
1147 return QFile(fileName).permissions();
1148}
1149
1161bool QFile::setPermissions(Permissions permissions)
1162{
1163 return QFileDevice::setPermissions(permissions); // for now
1164}
1165
1172bool
1173QFile::setPermissions(const QString &fileName, Permissions permissions)
1174{
1175 return QFile(fileName).setPermissions(permissions);
1176}
1177
1182{
1183 return QFileDevice::size(); // for now
1184}
1185
1380
1381#ifndef QT_NO_QOBJECT
1382#include "moc_qfile.cpp"
1383#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:665
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:906
bool setPermissions(Permissions permissionSpec) override
Sets the permissions for the file to the permissions specified.
Definition qfile.cpp:1161
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:722
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:767
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:487
Permissions permissions() const override
\reimp
Definition qfile.cpp:1132
~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:556
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:1181
bool resize(qint64 sz) override
\reimp
Definition qfile.cpp:1105
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:167
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