QtBase  v6.3.1
qfile.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Copyright (C) 2017 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qplatformdefs.h"
42 #include "qdebug.h"
43 #include "qfile.h"
44 #include "qfsfileengine_p.h"
45 #include "qtemporaryfile.h"
46 #include "qtemporaryfile_p.h"
47 #include "qlist.h"
48 #include "qfileinfo.h"
49 #include "private/qiodevice_p.h"
50 #include "private/qfile_p.h"
51 #include "private/qfilesystemengine_p.h"
52 #include "private/qsystemerror_p.h"
53 #include "private/qtemporaryfile_p.h"
54 #if defined(QT_BUILD_CORE_LIB)
55 # include "qcoreapplication.h"
56 #endif
57 
58 #ifdef QT_NO_QOBJECT
59 #define tr(X) QString::fromLatin1(X)
60 #endif
61 
63 
65 static bool file_already_open(QFile &file, const char *where = nullptr)
66 {
67  qWarning("QFile::%s: File (%ls) already open", where ? where : "open", qUtf16Printable(file.fileName()));
68  return false;
69 }
70 
71 //************* QFilePrivate
73 {
74 }
75 
77 {
78 }
79 
80 bool
81 QFilePrivate::openExternalFile(QIODevice::OpenMode flags, int fd, QFile::FileHandleFlags handleFlags)
82 {
83 #ifdef QT_NO_FSFILEENGINE
84  Q_UNUSED(flags);
85  Q_UNUSED(fd);
86  return false;
87 #else
88  auto fs = std::make_unique<QFSFileEngine>();
89  auto fe = fs.get();
90  fileEngine = std::move(fs);
91  return fe->open(flags, fd, handleFlags);
92 #endif
93 }
94 
95 bool
96 QFilePrivate::openExternalFile(QIODevice::OpenMode flags, FILE *fh, QFile::FileHandleFlags handleFlags)
97 {
98 #ifdef QT_NO_FSFILEENGINE
99  Q_UNUSED(flags);
100  Q_UNUSED(fh);
101  return false;
102 #else
103  auto fs = std::make_unique<QFSFileEngine>();
104  auto fe = fs.get();
105  fileEngine = std::move(fs);
106  return fe->open(flags, fh, handleFlags);
107 #endif
108 }
109 
111 {
112  if (!fileEngine)
114  return fileEngine.get();
115 }
116 
117 //************* QFile
118 
232 #ifdef QT_NO_QOBJECT
233 QFile::QFile()
234  : QFileDevice(*new QFilePrivate)
235 {
236 }
237 QFile::QFile(const QString &name)
238  : QFileDevice(*new QFilePrivate)
239 {
240  d_func()->fileName = name;
241 }
243  : QFileDevice(dd)
244 {
245 }
246 #else
252 {
253 }
259 {
260 }
266 {
267  Q_D(QFile);
268  d->fileName = name;
269 }
276 {
277  Q_D(QFile);
278  d->fileName = name;
279 }
284  : QFileDevice(dd, parent)
285 {
286 }
287 #endif
288 
293 {
294 }
295 
303 {
304  Q_D(const QFile);
305  return d->engine()->fileName(QAbstractFileEngine::DefaultName);
306 }
307 
326 void
328 {
329  Q_D(QFile);
330  if (isOpen()) {
331  file_already_open(*this, "setFileName");
332  close();
333  }
334  d->fileEngine.reset(); //get a new file engine later
335  d->fileName = name;
336 }
337 
375 bool
377 {
378  Q_D(const QFile);
379  // 0x1000000 = QAbstractFileEngine::Refresh, forcing an update
380  return d->engine()->fileFlags(QAbstractFileEngine::FlagsMask
382 }
383 
392 bool
394 {
395  return QFileInfo::exists(fileName);
396 }
397 
413 {
414  Q_D(const QFile);
415  return d->engine()->fileName(QAbstractFileEngine::AbsoluteLinkTarget);
416 }
417 
430 {
431  return QFileInfo(fileName).symLinkTarget();
432 }
433 
443 bool
445 {
446  Q_D(QFile);
447  if (d->fileName.isEmpty() &&
448  !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
449  qWarning("QFile::remove: Empty or null file name");
450  return false;
451  }
452  unsetError();
453  close();
454  if (error() == QFile::NoError) {
455  if (d->engine()->remove()) {
456  unsetError();
457  return true;
458  }
459  d->setError(QFile::RemoveError, d->fileEngine->errorString());
460  }
461  return false;
462 }
463 
474 bool
476 {
477  return QFile(fileName).remove();
478 }
479 
491 bool
493 {
494  Q_D(QFile);
495  if (d->fileName.isEmpty() &&
496  !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
497  qWarning("QFile::remove: Empty or null file name");
498  return false;
499  }
500  unsetError();
501  close();
502  if (error() == QFile::NoError) {
503  QFileSystemEntry fileEntry(d->fileName);
504  QFileSystemEntry trashEntry;
506  if (QFileSystemEngine::moveFileToTrash(fileEntry, trashEntry, error)) {
507  setFileName(trashEntry.filePath());
508  unsetError();
509  return true;
510  }
511  d->setError(QFile::RenameError, error.toString());
512  }
513  return false;
514 }
515 
528 bool
530 {
532  if (file.moveToTrash()) {
533  if (pathInTrash)
534  *pathInTrash = file.fileName();
535  return true;
536  }
537  return false;
538 }
539 
557 bool
558 QFile::rename(const QString &newName)
559 {
560  Q_D(QFile);
561 
562  // if this is a QTemporaryFile, the virtual fileName() call here may do something
563  if (fileName().isEmpty()) {
564  qWarning("QFile::rename: Empty or null file name");
565  return false;
566  }
567  if (d->fileName == newName) {
568  d->setError(QFile::RenameError, tr("Destination file is the same file."));
569  return false;
570  }
571  if (!exists()) {
572  d->setError(QFile::RenameError, tr("Source file does not exist."));
573  return false;
574  }
575 
576  // If the file exists and it is a case-changing rename ("foo" -> "Foo"),
577  // compare Ids to make sure it really is a different file.
578  // Note: this does not take file engines into account.
579  bool changingCase = false;
581  if (!targetId.isNull()) {
582  QByteArray fileId = d->fileEngine ?
583  d->fileEngine->id() :
585  changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0);
586  if (!changingCase) {
587  d->setError(QFile::RenameError, tr("Destination file exists"));
588  return false;
589  }
590 
591 #ifdef Q_OS_LINUX
592  // rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive
593  // FS, such as FAT32. Move the file away and rename in 2 steps to work around.
594  QTemporaryFileName tfn(d->fileName);
595  QFileSystemEntry src(d->fileName);
597  for (int attempt = 0; attempt < 16; ++attempt) {
599 
600  // rename to temporary name
602  continue;
603 
604  // rename to final name
606  d->fileEngine->setFileName(newName);
607  d->fileName = newName;
608  return true;
609  }
610 
611  // We need to restore the original file.
612  QSystemError error2;
613  if (QFileSystemEngine::renameFile(tmp, src, error2))
614  break; // report the original error, below
615 
616  // report both errors
617  d->setError(QFile::RenameError,
618  tr("Error while renaming: %1").arg(error.toString())
619  + QLatin1Char('\n')
620  + tr("Unable to restore from %1: %2").
621  arg(QDir::toNativeSeparators(tmp.filePath()), error2.toString()));
622  return false;
623  }
624  d->setError(QFile::RenameError,
625  tr("Error while renaming: %1").arg(error.toString()));
626  return false;
627 #endif // Q_OS_LINUX
628  }
629  unsetError();
630  close();
631  if (error() == QFile::NoError) {
632  if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) {
633  unsetError();
634  // engine was able to handle the new name so we just reset it
635  d->fileEngine->setFileName(newName);
636  d->fileName = newName;
637  return true;
638  }
639 
640  if (isSequential()) {
641  d->setError(QFile::RenameError, tr("Will not rename sequential file using block copy"));
642  return false;
643  }
644 
645  QFile out(newName);
646  if (open(QIODevice::ReadOnly)) {
648  bool error = false;
649  char block[4096];
650  qint64 bytes;
651  while ((bytes = read(block, sizeof(block))) > 0) {
652  if (bytes != out.write(block, bytes)) {
653  d->setError(QFile::RenameError, out.errorString());
654  error = true;
655  break;
656  }
657  }
658  if (bytes == -1) {
659  d->setError(QFile::RenameError, errorString());
660  error = true;
661  }
662  if (!error) {
663  if (!remove()) {
664  d->setError(QFile::RenameError, tr("Cannot remove source file"));
665  error = true;
666  }
667  }
668  if (error) {
669  out.remove();
670  } else {
671  d->fileEngine->setFileName(newName);
673  unsetError();
674  setFileName(newName);
675  }
676  close();
677  return !error;
678  }
679  close();
680  d->setError(QFile::RenameError,
681  tr("Cannot open destination file: %1").arg(out.errorString()));
682  } else {
683  d->setError(QFile::RenameError, errorString());
684  }
685  }
686  return false;
687 }
688 
701 bool
702 QFile::rename(const QString &oldName, const QString &newName)
703 {
704  return QFile(oldName).rename(newName);
705 }
706 
723 bool
724 QFile::link(const QString &linkName)
725 {
726  Q_D(QFile);
727  if (fileName().isEmpty()) {
728  qWarning("QFile::link: Empty or null file name");
729  return false;
730  }
731  QFileInfo fi(linkName);
732  if (d->engine()->link(fi.absoluteFilePath())) {
733  unsetError();
734  return true;
735  }
736  d->setError(QFile::RenameError, d->fileEngine->errorString());
737  return false;
738 }
739 
751 bool
752 QFile::link(const QString &fileName, const QString &linkName)
753 {
754  return QFile(fileName).link(linkName);
755 }
756 
765 bool
766 QFile::copy(const QString &newName)
767 {
768  Q_D(QFile);
769  if (fileName().isEmpty()) {
770  qWarning("QFile::copy: Empty or null file name");
771  return false;
772  }
773  if (QFile::exists(newName)) {
774  // ### Race condition. If a file is moved in after this, it /will/ be
775  // overwritten. On Unix, the proper solution is to use hardlinks:
776  // return ::link(old, new) && ::remove(old); See also rename().
777  d->setError(QFile::CopyError, tr("Destination file exists"));
778  return false;
779  }
780  unsetError();
781  close();
782  if (error() == QFile::NoError) {
783  if (d->engine()->copy(newName)) {
784  unsetError();
785  return true;
786  } else {
787  bool error = false;
788  if (!open(QFile::ReadOnly)) {
789  error = true;
790  d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName));
791  } else {
792  const auto fileTemplate = QLatin1String("%1/qt_temp.XXXXXX");
793 #ifdef QT_NO_TEMPORARYFILE
794  QFile out(fileTemplate.arg(QFileInfo(newName).path()));
795  if (!out.open(QIODevice::ReadWrite))
796  error = true;
797 #else
798  QTemporaryFile out(fileTemplate.arg(QFileInfo(newName).path()));
799  if (!out.open()) {
800  out.setFileTemplate(fileTemplate.arg(QDir::tempPath()));
801  if (!out.open())
802  error = true;
803  }
804 #endif
805  if (error) {
806  out.close();
807  close();
808  d->setError(QFile::CopyError, tr("Cannot open for output: %1").arg(out.errorString()));
809  } else {
810  if (!d->engine()->cloneTo(out.d_func()->engine())) {
811  char block[4096];
812  qint64 totalRead = 0;
813  while (!atEnd()) {
814  qint64 in = read(block, sizeof(block));
815  if (in <= 0)
816  break;
817  totalRead += in;
818  if (in != out.write(block, in)) {
819  close();
820  d->setError(QFile::CopyError, tr("Failure to write block: %1")
821  .arg(out.errorString()));
822  error = true;
823  break;
824  }
825  }
826 
827  if (totalRead != size()) {
828  // Unable to read from the source. The error string is
829  // already set from read().
830  error = true;
831  }
832  }
833 
834  if (!error) {
835  // Sync to disk if possible. Ignore errors (e.g. not supported).
836  out.d_func()->fileEngine->syncToDisk();
837 
838  if (!out.rename(newName)) {
839  error = true;
840  close();
841  d->setError(QFile::CopyError, tr("Cannot create %1 for output: %2")
842  .arg(newName, out.errorString()));
843  }
844  }
845 #ifdef QT_NO_TEMPORARYFILE
846  if (error)
847  out.remove();
848 #else
849  if (!error)
850  out.setAutoRemove(false);
851 #endif
852  }
853  }
854  if (!error) {
856  close();
857  unsetError();
858  return true;
859  }
860  }
861  }
862  return false;
863 }
864 
875 bool
876 QFile::copy(const QString &fileName, const QString &newName)
877 {
878  return QFile(fileName).copy(newName);
879 }
880 
897 bool QFile::open(OpenMode mode)
898 {
899  Q_D(QFile);
900  if (isOpen())
901  return file_already_open(*this);
902  // Either Append or NewOnly implies WriteOnly
903  if (mode & (Append | NewOnly))
904  mode |= WriteOnly;
905  unsetError();
906  if ((mode & (ReadOnly | WriteOnly)) == 0) {
907  qWarning("QIODevice::open: File access not specified");
908  return false;
909  }
910 
911  // QIODevice provides the buffering, so there's no need to request it from the file engine.
912  if (d->engine()->open(mode | QIODevice::Unbuffered)) {
914  if (mode & Append)
915  seek(size());
916  return true;
917  }
918  QFile::FileError err = d->fileEngine->error();
919  if (err == QFile::UnspecifiedError)
920  err = QFile::OpenError;
921  d->setError(err, d->fileEngine->errorString());
922  return false;
923 }
924 
942 bool QFile::open(OpenMode mode, QFile::Permissions permissions)
943 {
944  Q_D(QFile);
945  if (isOpen())
946  return file_already_open(*this);
947  // Either Append or NewOnly implies WriteOnly
948  if (mode & (Append | NewOnly))
949  mode |= WriteOnly;
950  unsetError();
951  if ((mode & (ReadOnly | WriteOnly)) == 0) {
952  qWarning("QIODevice::open: File access not specified");
953  return false;
954  }
955 
956  // QIODevice provides the buffering, so there's no need to request it from the file engine.
957  if (d->engine()->open(mode | QIODevice::Unbuffered, permissions)) {
959  if (mode & Append)
960  seek(size());
961  return true;
962  }
963  QFile::FileError err = d->fileEngine->error();
964  if (err == QFile::UnspecifiedError)
965  err = QFile::OpenError;
966  d->setError(err, d->fileEngine->errorString());
967  return false;
968 }
969 
1012 bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
1013 {
1014  Q_D(QFile);
1015  if (isOpen())
1016  return file_already_open(*this);
1017  // Either Append or NewOnly implies WriteOnly
1018  if (mode & (Append | NewOnly))
1019  mode |= WriteOnly;
1020  unsetError();
1021  if ((mode & (ReadOnly | WriteOnly)) == 0) {
1022  qWarning("QFile::open: File access not specified");
1023  return false;
1024  }
1025 
1026  // QIODevice provides the buffering, so request unbuffered file engines
1027  if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) {
1029  if (!(mode & Append) && !isSequential()) {
1030  qint64 pos = (qint64)QT_FTELL(fh);
1031  if (pos != -1) {
1032  // Skip redundant checks in QFileDevice::seek().
1034  }
1035  }
1036  return true;
1037  }
1038  return false;
1039 }
1040 
1064 bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags)
1065 {
1066  Q_D(QFile);
1067  if (isOpen())
1068  return file_already_open(*this);
1069  // Either Append or NewOnly implies WriteOnly
1070  if (mode & (Append | NewOnly))
1071  mode |= WriteOnly;
1072  unsetError();
1073  if ((mode & (ReadOnly | WriteOnly)) == 0) {
1074  qWarning("QFile::open: File access not specified");
1075  return false;
1076  }
1077 
1078  // QIODevice provides the buffering, so request unbuffered file engines
1079  if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) {
1081  if (!(mode & Append) && !isSequential()) {
1083  if (pos != -1) {
1084  // Skip redundant checks in QFileDevice::seek().
1086  }
1087  }
1088  return true;
1089  }
1090  return false;
1091 }
1092 
1097 {
1098  return QFileDevice::resize(sz); // for now
1099 }
1100 
1114 bool
1116 {
1117  return QFile(fileName).resize(sz);
1118 }
1119 
1123 QFile::Permissions QFile::permissions() const
1124 {
1125  return QFileDevice::permissions(); // for now
1126 }
1127 
1135 QFile::Permissions
1137 {
1138  return QFile(fileName).permissions();
1139 }
1140 
1152 bool QFile::setPermissions(Permissions permissions)
1153 {
1154  return QFileDevice::setPermissions(permissions); // for now
1155 }
1156 
1163 bool
1164 QFile::setPermissions(const QString &fileName, Permissions permissions)
1165 {
1166  return QFile(fileName).setPermissions(permissions);
1167 }
1168 
1173 {
1174  return QFileDevice::size(); // for now
1175 }
1176 
1268 
1269 #ifndef QT_NO_QOBJECT
1270 #include "moc_qfile.cpp"
1271 #endif
#define QT_FTELL
#define QT_OFF_T
#define QT_LSEEK
Definition: qplatformdefs.h:97
static QAbstractFileEngine * create(const QString &fileName)
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
bool isNull() const noexcept
static QString tempPath()
Definition: qdir.cpp:2085
static QString toNativeSeparators(const QString &pathName)
Definition: qdir.cpp:916
The QFSFileEngine class implements Qt's default file engine.
virtual bool isUnnamedFile() const
The QFileDevice class provides an interface for reading from and writing to open files.
Definition: qfiledevice.h:52
qint64 pos() const override
bool seek(qint64 offset) override
qint64 size() const override
void unsetError()
virtual bool resize(qint64 sz)
bool atEnd() const override
FileError error() const
bool isSequential() const override
virtual QString fileName() const
@ UnspecifiedError
Definition: qfiledevice.h:68
virtual bool setPermissions(Permissions permissionSpec)
void close() override
virtual Permissions permissions() const
std::unique_ptr< QAbstractFileEngine > fileEngine
Definition: qfiledevice_p.h:94
QFileDevice::FileHandleFlags handleFlags
Definition: qfiledevice_p.h:97
The QFile class provides an interface for reading from and writing to files.
Definition: qfile.h:94
bool open(OpenMode flags) override
Definition: qfile.cpp:897
bool setPermissions(Permissions permissionSpec) override
Definition: qfile.cpp:1152
QFile()
Definition: qfile.cpp:250
QString symLinkTarget() const
Definition: qfile.cpp:412
bool link(const QString &newName)
Definition: qfile.cpp:724
bool copy(const QString &newName)
Definition: qfile.cpp:766
bool remove()
Definition: qfile.cpp:444
void setFileName(const QString &name)
Definition: qfile.cpp:327
QString fileName() const override
Definition: qfile.cpp:302
bool moveToTrash()
Definition: qfile.cpp:492
Permissions permissions() const override
Definition: qfile.cpp:1123
~QFile()
Definition: qfile.cpp:292
bool rename(const QString &newName)
Definition: qfile.cpp:558
bool exists() const
Definition: qfile.cpp:376
qint64 size() const override
Definition: qfile.cpp:1172
bool resize(qint64 sz) override
Definition: qfile.cpp:1096
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:57
QString symLinkTarget() const
Definition: qfileinfo.cpp:1226
QString absoluteFilePath() const
Definition: qfileinfo.cpp:556
bool exists() const
Definition: qfileinfo.cpp:697
QString fileName
Definition: qfile_p.h:75
~QFilePrivate()
Definition: qfile.cpp:76
QAbstractFileEngine * engine() const override
Definition: qfile.cpp:110
QFilePrivate()
Definition: qfile.cpp:72
bool openExternalFile(QIODevice::OpenMode flags, int fd, QFile::FileHandleFlags handleFlags)
Definition: qfile.cpp:81
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)
QString filePath() const
virtual bool open(QIODeviceBase::OpenMode mode)
Definition: qiodevice.cpp:798
bool isOpen() const
Definition: qiodevice.cpp:602
QString errorString() const
Definition: qiodevice.cpp:2169
virtual bool seek(qint64 pos)
Definition: qiodevice.cpp:891
qint64 read(char *data, qint64 maxlen)
Definition: qiodevice.cpp:1030
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
The QObject class is the base class of all Qt objects.
Definition: qobject.h:125
The QString class provides a Unicode character string.
Definition: qstring.h:388
QString toString() const
The QTemporaryFile class is an I/O device that operates on temporary files.
#define SEEK_CUR
Definition: ftzconf.h:250
JOCTET JCOEFPTR block
Definition: jsimd.h:109
@ CaseInsensitive
Definition: qnamespace.h:1283
#define Q_DECL_COLD_FUNCTION
long long qint64
Definition: qglobal.h:298
#define qWarning
Definition: qlogging.h:179
GLenum mode
GLenum src
GLbitfield flags
GLuint64 GLenum GLint fd
GLuint name
GLuint in
Definition: qopenglext.h:8870
GLsizei const GLchar *const * path
Definition: qopenglext.h:4283
SSL_CTX int(*) void arg)
#define tr(X)
Q_UNUSED(salary)
[21]
QFile file
[0]
QFileInfo fi("c:/temp/foo")
[newstuff]
QTextStream out(stdout)
[7]
QObject::connect nullptr
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:53
QFileSystemEntry::NativePath generateNext()
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent