Qt 6.x
The Qt SDK
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
39{
40}
41
43{
44}
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
77{
78 if (!fileEngine)
80 return fileEngine.get();
81}
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
218{
219}
225{
226}
231
238
241{
242 Q_D(QFile);
243 d->fileName = name;
244}
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
369{
371}
372
388{
389 Q_D(const QFile);
390 return d->engine()->fileName(QAbstractFileEngine::AbsoluteLinkTarget);
391}
392
405{
407}
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
466bool
468{
469 Q_D(QFile);
470 if (d->fileName.isEmpty() &&
471 !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
472 qWarning("QFile::remove: Empty or null file name");
473 return false;
474 }
475 unsetError();
476 close();
477 if (error() == QFile::NoError) {
478 QFileSystemEntry fileEntry(d->fileName);
479 QFileSystemEntry trashEntry;
481 if (QFileSystemEngine::moveFileToTrash(fileEntry, trashEntry, error)) {
482 setFileName(trashEntry.filePath());
483 unsetError();
484 return true;
485 }
486 d->setError(QFile::RenameError, error.toString());
487 }
488 return false;
489}
490
503bool
505{
507 if (file.moveToTrash()) {
508 if (pathInTrash)
509 *pathInTrash = file.fileName();
510 return true;
511 }
512 return false;
513}
514
532bool
533QFile::rename(const QString &newName)
534{
535 Q_D(QFile);
536
537 // if this is a QTemporaryFile, the virtual fileName() call here may do something
538 if (fileName().isEmpty()) {
539 qWarning("QFile::rename: Empty or null file name");
540 return false;
541 }
542 if (d->fileName == newName) {
543 d->setError(QFile::RenameError, tr("Destination file is the same file."));
544 return false;
545 }
546 if (!exists()) {
547 d->setError(QFile::RenameError, tr("Source file does not exist."));
548 return false;
549 }
550
551 // If the file exists and it is a case-changing rename ("foo" -> "Foo"),
552 // compare Ids to make sure it really is a different file.
553 // Note: this does not take file engines into account.
554 bool changingCase = false;
556 if (!targetId.isNull()) {
557 QByteArray fileId = d->fileEngine ?
558 d->fileEngine->id() :
560 changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0);
561 if (!changingCase) {
562 d->setError(QFile::RenameError, tr("Destination file exists"));
563 return false;
564 }
565
566#ifdef Q_OS_LINUX
567 // rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive
568 // FS, such as FAT32. Move the file away and rename in 2 steps to work around.
569 QTemporaryFileName tfn(d->fileName);
570 QFileSystemEntry src(d->fileName);
572 for (int attempt = 0; attempt < 16; ++attempt) {
574
575 // rename to temporary name
577 continue;
578
579 // rename to final name
581 d->fileEngine->setFileName(newName);
582 d->fileName = newName;
583 return true;
584 }
585
586 // We need to restore the original file.
587 QSystemError error2;
588 if (QFileSystemEngine::renameFile(tmp, src, error2))
589 break; // report the original error, below
590
591 // report both errors
592 d->setError(QFile::RenameError,
593 tr("Error while renaming: %1").arg(error.toString())
594 + u'\n'
595 + tr("Unable to restore from %1: %2").
597 return false;
598 }
599 d->setError(QFile::RenameError,
600 tr("Error while renaming: %1").arg(error.toString()));
601 return false;
602#endif // Q_OS_LINUX
603 }
604 unsetError();
605 close();
606 if (error() == QFile::NoError) {
607 if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) {
608 unsetError();
609 // engine was able to handle the new name so we just reset it
610 d->fileEngine->setFileName(newName);
611 d->fileName = newName;
612 return true;
613 }
614
615 if (isSequential()) {
616 d->setError(QFile::RenameError, tr("Will not rename sequential file using block copy"));
617 return false;
618 }
619
620 QFile out(newName);
623 bool error = false;
624 char block[4096];
625 qint64 bytes;
626 while ((bytes = read(block, sizeof(block))) > 0) {
627 if (bytes != out.write(block, bytes)) {
628 d->setError(QFile::RenameError, out.errorString());
629 error = true;
630 break;
631 }
632 }
633 if (bytes == -1) {
634 d->setError(QFile::RenameError, errorString());
635 error = true;
636 }
637 if (!error) {
638 if (!remove()) {
639 d->setError(QFile::RenameError, tr("Cannot remove source file"));
640 error = true;
641 }
642 }
643 if (error) {
644 out.remove();
645 } else {
646 d->fileEngine->setFileName(newName);
648 unsetError();
649 setFileName(newName);
650 }
651 close();
652 return !error;
653 }
654 close();
655 d->setError(QFile::RenameError,
656 tr("Cannot open destination file: %1").arg(out.errorString()));
657 } else {
658 d->setError(QFile::RenameError, errorString());
659 }
660 }
661 return false;
662}
663
676bool
677QFile::rename(const QString &oldName, const QString &newName)
678{
679 return QFile(oldName).rename(newName);
680}
681
698bool
699QFile::link(const QString &linkName)
700{
701 Q_D(QFile);
702 if (fileName().isEmpty()) {
703 qWarning("QFile::link: Empty or null file name");
704 return false;
705 }
706 QFileInfo fi(linkName);
707 if (d->engine()->link(fi.absoluteFilePath())) {
708 unsetError();
709 return true;
710 }
711 d->setError(QFile::RenameError, d->fileEngine->errorString());
712 return false;
713}
714
726bool
727QFile::link(const QString &fileName, const QString &linkName)
728{
729 return QFile(fileName).link(linkName);
730}
731
743bool
744QFile::copy(const QString &newName)
745{
746 Q_D(QFile);
747 if (fileName().isEmpty()) {
748 qWarning("QFile::copy: Empty or null file name");
749 return false;
750 }
751 if (QFile::exists(newName)) {
752 // ### Race condition. If a file is moved in after this, it /will/ be
753 // overwritten. On Unix, the proper solution is to use hardlinks:
754 // return ::link(old, new) && ::remove(old); See also rename().
755 d->setError(QFile::CopyError, tr("Destination file exists"));
756 return false;
757 }
758 unsetError();
759 close();
760 if (error() == QFile::NoError) {
761 if (d->engine()->copy(newName)) {
762 unsetError();
763 return true;
764 } else {
765 bool error = false;
766 if (!open(QFile::ReadOnly)) {
767 error = true;
768 d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName));
769 } else {
770 const auto fileTemplate = "%1/qt_temp.XXXXXX"_L1;
771#ifdef QT_NO_TEMPORARYFILE
772 QFile out(fileTemplate.arg(QFileInfo(newName).path()));
773 if (!out.open(QIODevice::ReadWrite))
774 error = true;
775#else
776 QTemporaryFile out(fileTemplate.arg(QFileInfo(newName).path()));
777 if (!out.open()) {
778 out.setFileTemplate(fileTemplate.arg(QDir::tempPath()));
779 if (!out.open())
780 error = true;
781 }
782#endif
783 if (error) {
784 out.close();
785 close();
786 d->setError(QFile::CopyError, tr("Cannot open for output: %1").arg(out.errorString()));
787 } else {
788 if (!d->engine()->cloneTo(out.d_func()->engine())) {
789 char block[4096];
790 qint64 totalRead = 0;
791 while (!atEnd()) {
792 qint64 in = read(block, sizeof(block));
793 if (in <= 0)
794 break;
795 totalRead += in;
796 if (in != out.write(block, in)) {
797 close();
798 d->setError(QFile::CopyError, tr("Failure to write block: %1")
799 .arg(out.errorString()));
800 error = true;
801 break;
802 }
803 }
804
805 if (totalRead != size()) {
806 // Unable to read from the source. The error string is
807 // already set from read().
808 error = true;
809 }
810 }
811
812 if (!error) {
813 // Sync to disk if possible. Ignore errors (e.g. not supported).
814 out.d_func()->fileEngine->syncToDisk();
815
816 if (!out.rename(newName)) {
817 error = true;
818 close();
819 d->setError(QFile::CopyError, tr("Cannot create %1 for output: %2")
820 .arg(newName, out.errorString()));
821 }
822 }
823#ifdef QT_NO_TEMPORARYFILE
824 if (error)
825 out.remove();
826#else
827 if (!error)
828 out.setAutoRemove(false);
829#endif
830 }
831 }
832 if (!error) {
834 close();
835 unsetError();
836 return true;
837 }
838 }
839 }
840 return false;
841}
842
856bool
857QFile::copy(const QString &fileName, const QString &newName)
858{
859 return QFile(fileName).copy(newName);
860}
861
881bool QFile::open(OpenMode mode)
882{
883 Q_D(QFile);
884 if (isOpen())
885 return file_already_open(*this);
886 // Either Append or NewOnly implies WriteOnly
887 if (mode & (Append | NewOnly))
888 mode |= WriteOnly;
889 unsetError();
890 if ((mode & (ReadOnly | WriteOnly)) == 0) {
891 qWarning("QIODevice::open: File access not specified");
892 return false;
893 }
894
895 // QIODevice provides the buffering, so there's no need to request it from the file engine.
896 if (d->engine()->open(mode | QIODevice::Unbuffered)) {
898 if (mode & Append)
899 seek(size());
900 return true;
901 }
902 QFile::FileError err = d->fileEngine->error();
903 if (err == QFile::UnspecifiedError)
904 err = QFile::OpenError;
905 d->setError(err, d->fileEngine->errorString());
906 return false;
907}
908
926bool QFile::open(OpenMode mode, QFile::Permissions permissions)
927{
928 Q_D(QFile);
929 if (isOpen())
930 return file_already_open(*this);
931 // Either Append or NewOnly implies WriteOnly
932 if (mode & (Append | NewOnly))
933 mode |= WriteOnly;
934 unsetError();
935 if ((mode & (ReadOnly | WriteOnly)) == 0) {
936 qWarning("QIODevice::open: File access not specified");
937 return false;
938 }
939
940 // QIODevice provides the buffering, so there's no need to request it from the file engine.
941 if (d->engine()->open(mode | QIODevice::Unbuffered, permissions)) {
943 if (mode & Append)
944 seek(size());
945 return true;
946 }
947 QFile::FileError err = d->fileEngine->error();
948 if (err == QFile::UnspecifiedError)
949 err = QFile::OpenError;
950 d->setError(err, d->fileEngine->errorString());
951 return false;
952}
953
996bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
997{
998 Q_D(QFile);
999 if (isOpen())
1000 return file_already_open(*this);
1001 // Either Append or NewOnly implies WriteOnly
1002 if (mode & (Append | NewOnly))
1003 mode |= WriteOnly;
1004 unsetError();
1005 if ((mode & (ReadOnly | WriteOnly)) == 0) {
1006 qWarning("QFile::open: File access not specified");
1007 return false;
1008 }
1009
1010 // QIODevice provides the buffering, so request unbuffered file engines
1011 if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) {
1013 if (!(mode & Append) && !isSequential()) {
1014 qint64 pos = (qint64)QT_FTELL(fh);
1015 if (pos != -1) {
1016 // Skip redundant checks in QFileDevice::seek().
1018 }
1019 }
1020 return true;
1021 }
1022 return false;
1023}
1024
1048bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags)
1049{
1050 Q_D(QFile);
1051 if (isOpen())
1052 return file_already_open(*this);
1053 // Either Append or NewOnly implies WriteOnly
1054 if (mode & (Append | NewOnly))
1055 mode |= WriteOnly;
1056 unsetError();
1057 if ((mode & (ReadOnly | WriteOnly)) == 0) {
1058 qWarning("QFile::open: File access not specified");
1059 return false;
1060 }
1061
1062 // QIODevice provides the buffering, so request unbuffered file engines
1063 if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) {
1065 if (!(mode & Append) && !isSequential()) {
1066 qint64 pos = (qint64)QT_LSEEK(fd, QT_OFF_T(0), SEEK_CUR);
1067 if (pos != -1) {
1068 // Skip redundant checks in QFileDevice::seek().
1070 }
1071 }
1072 return true;
1073 }
1074 return false;
1075}
1076
1081{
1082 return QFileDevice::resize(sz); // for now
1083}
1084
1098bool
1100{
1101 return QFile(fileName).resize(sz);
1102}
1103
1107QFile::Permissions QFile::permissions() const
1108{
1109 return QFileDevice::permissions(); // for now
1110}
1111
1119QFile::Permissions
1121{
1122 return QFile(fileName).permissions();
1123}
1124
1136bool QFile::setPermissions(Permissions permissions)
1137{
1138 return QFileDevice::setPermissions(permissions); // for now
1139}
1140
1147bool
1148QFile::setPermissions(const QString &fileName, Permissions permissions)
1149{
1150 return QFile(fileName).setPermissions(permissions);
1151}
1152
1157{
1158 return QFileDevice::size(); // for now
1159}
1160
1254
1255#ifndef QT_NO_QOBJECT
1256#include "moc_qfile.cpp"
1257#endif
\inmodule QtCore \reentrant
static QAbstractFileEngine * create(const QString &fileName)
Creates and returns a QAbstractFileEngine suitable for processing fileName.
\inmodule QtCore
Definition qbytearray.h:57
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:2130
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:16
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:23
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.
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString symLinkTarget() const
QString absoluteFilePath() const
Returns an absolute path including the file name.
bool exists() const
Returns true if the file 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
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
bool setPermissions(Permissions permissionSpec) override
Sets the permissions for the file to the permissions specified.
Definition qfile.cpp:1136
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:699
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:744
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:467
Permissions permissions() const override
\reimp
Definition qfile.cpp:1107
~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:533
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:1156
bool resize(qint64 sz) override
\reimp
Definition qfile.cpp:1080
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:90
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString toString() const
\inmodule QtCore \reentrant
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:162
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:1403
#define tr(X)
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:55
QFile file
[0]
QFileInfo fi("c:/temp/foo")
[newstuff]
QTextStream out(stdout)
[7]
QObject::connect nullptr
QFileSystemEntry::NativePath generateNext()
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent