summaryrefslogtreecommitdiffstats
path: root/src/corelib/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io')
-rw-r--r--src/corelib/io/io.pri8
-rw-r--r--src/corelib/io/qabstractfileengine.cpp47
-rw-r--r--src/corelib/io/qabstractfileengine_p.h9
-rw-r--r--src/corelib/io/qdatastream.cpp32
-rw-r--r--src/corelib/io/qdatastream.h5
-rw-r--r--src/corelib/io/qdebug.cpp15
-rw-r--r--src/corelib/io/qdebug.h3
-rw-r--r--src/corelib/io/qfile.cpp125
-rw-r--r--src/corelib/io/qfile.h1
-rw-r--r--src/corelib/io/qfiledevice.cpp70
-rw-r--r--src/corelib/io/qfiledevice.h11
-rw-r--r--src/corelib/io/qfileinfo.cpp341
-rw-r--r--src/corelib/io/qfileinfo.h11
-rw-r--r--src/corelib/io/qfileinfo_p.h48
-rw-r--r--src/corelib/io/qfilesystemengine.cpp228
-rw-r--r--src/corelib/io/qfilesystemengine_p.h10
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp948
-rwxr-xr-xsrc/corelib/io/qfilesystemengine_win.cpp317
-rw-r--r--src/corelib/io/qfilesystementry.cpp5
-rw-r--r--src/corelib/io/qfilesystementry_p.h5
-rw-r--r--src/corelib/io/qfilesystemmetadata_p.h64
-rw-r--r--src/corelib/io/qfsfileengine.cpp35
-rw-r--r--src/corelib/io/qfsfileengine_p.h5
-rw-r--r--src/corelib/io/qfsfileengine_unix.cpp148
-rw-r--r--src/corelib/io/qfsfileengine_win.cpp58
-rw-r--r--src/corelib/io/qiodevice.cpp124
-rw-r--r--src/corelib/io/qiodevice.h1
-rw-r--r--src/corelib/io/qiodevice_p.h3
-rw-r--r--src/corelib/io/qlockfile.cpp71
-rw-r--r--src/corelib/io/qlockfile_p.h8
-rw-r--r--src/corelib/io/qlockfile_unix.cpp167
-rw-r--r--src/corelib/io/qlockfile_win.cpp60
-rw-r--r--src/corelib/io/qprocess.cpp105
-rw-r--r--src/corelib/io/qprocess.h1
-rw-r--r--src/corelib/io/qprocess_p.h6
-rw-r--r--src/corelib/io/qprocess_unix.cpp129
-rw-r--r--src/corelib/io/qprocess_win.cpp134
-rw-r--r--src/corelib/io/qresource.cpp50
-rw-r--r--src/corelib/io/qsavefile.cpp39
-rw-r--r--src/corelib/io/qsettings.cpp82
-rw-r--r--src/corelib/io/qsettings.h2
-rw-r--r--src/corelib/io/qsettings_p.h3
-rw-r--r--src/corelib/io/qstandardpaths.cpp35
-rw-r--r--src/corelib/io/qstandardpaths_unix.cpp8
-rw-r--r--src/corelib/io/qstandardpaths_win.cpp10
-rw-r--r--src/corelib/io/qstorageinfo.cpp35
-rw-r--r--src/corelib/io/qstorageinfo.h7
-rw-r--r--src/corelib/io/qstorageinfo_unix.cpp18
-rw-r--r--src/corelib/io/qtemporarydir.cpp83
-rw-r--r--src/corelib/io/qtemporaryfile.cpp520
-rw-r--r--src/corelib/io/qtemporaryfile.h4
-rw-r--r--src/corelib/io/qtemporaryfile_p.h52
-rw-r--r--src/corelib/io/qtextstream.cpp3
-rw-r--r--src/corelib/io/qtldurl.cpp42
-rw-r--r--src/corelib/io/qwindowspipereader.cpp13
-rw-r--r--src/corelib/io/qwindowspipewriter.cpp8
-rw-r--r--src/corelib/io/qwindowspipewriter_p.h1
-rw-r--r--src/corelib/io/qwinoverlappedionotifier.cpp428
-rw-r--r--src/corelib/io/qwinoverlappedionotifier_p.h90
59 files changed, 2857 insertions, 2034 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri
index d24c290508..4614fe2a6b 100644
--- a/src/corelib/io/io.pri
+++ b/src/corelib/io/io.pri
@@ -121,18 +121,16 @@ win32 {
!winrt {
HEADERS += \
io/qwindowspipereader_p.h \
- io/qwindowspipewriter_p.h \
- io/qwinoverlappedionotifier_p.h
+ io/qwindowspipewriter_p.h
SOURCES += \
io/qsettings_win.cpp \
io/qstandardpaths_win.cpp \
io/qstorageinfo_win.cpp \
io/qwindowspipereader.cpp \
- io/qwindowspipewriter.cpp \
- io/qwinoverlappedionotifier.cpp
+ io/qwindowspipewriter.cpp
- LIBS += -lmpr
+ LIBS += -lmpr -lnetapi32 -luserenv
} else {
SOURCES += \
io/qstandardpaths_winrt.cpp \
diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp
index 89727673d3..f2a895bbb8 100644
--- a/src/corelib/io/qabstractfileengine.cpp
+++ b/src/corelib/io/qabstractfileengine.cpp
@@ -328,7 +328,8 @@ QAbstractFileEngine *QAbstractFileEngine::create(const QString &fileName)
These are used by the fileTime() function.
- \value CreationTime When the file was created.
+ \value BirthTime When the file was born (created).
+ \value MetadataChangeTime When the file's metadata was last changed.
\value ModificationTime When the file was most recently modified.
\value AccessTime When the file was most recently accessed (e.g.
read or written to).
@@ -737,13 +738,31 @@ QString QAbstractFileEngine::owner(FileOwner owner) const
return QString();
}
+
+/*!
+ \since 5.10
+
+ Sets the file \a time to \a newDate, returning true if successful;
+ otherwise returns false.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa fileTime()
+*/
+bool QAbstractFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
+{
+ Q_UNUSED(newDate);
+ Q_UNUSED(time);
+ return false;
+}
+
/*!
- If \a time is \c CreationTime, return when the file was created.
- If \a time is \c ModificationTime, return when the file was most
- recently modified. If \a time is \c AccessTime, return when the
- file was most recently accessed (e.g. read or written).
- If the time cannot be determined return QDateTime() (an invalid
- date time).
+ If \a time is \c BirthTime, return when the file was born (created). If \a
+ time is \c MetadataChangeTime, return when the file's metadata was last
+ changed. If \a time is \c ModificationTime, return when the file was most
+ recently modified. If \a time is \c AccessTime, return when the file was
+ most recently accessed (e.g. read or written). If the time cannot be
+ determined return QDateTime() (an invalid date time).
This virtual function must be reimplemented by all subclasses.
@@ -843,6 +862,20 @@ bool QAbstractFileEngine::unmap(uchar *address)
}
/*!
+ \since 5.10
+
+ Duplicates the contents of this file (starting from the current position)
+ to the file specified by the engine \a target.
+
+ Returns \c true on success; otherwise, \c false is returned.
+ */
+bool QAbstractFileEngine::cloneTo(QAbstractFileEngine *target)
+{
+ Q_UNUSED(target);
+ return false;
+}
+
+/*!
\since 4.3
\class QAbstractFileEngineIterator
\inmodule QtCore
diff --git a/src/corelib/io/qabstractfileengine_p.h b/src/corelib/io/qabstractfileengine_p.h
index 58fa776e49..00c415b521 100644
--- a/src/corelib/io/qabstractfileengine_p.h
+++ b/src/corelib/io/qabstractfileengine_p.h
@@ -113,9 +113,10 @@ public:
OwnerGroup
};
enum FileTime {
- CreationTime,
- ModificationTime,
- AccessTime
+ AccessTime,
+ BirthTime,
+ MetadataChangeTime,
+ ModificationTime
};
virtual ~QAbstractFileEngine();
@@ -145,9 +146,11 @@ public:
virtual QString fileName(FileName file=DefaultName) const;
virtual uint ownerId(FileOwner) const;
virtual QString owner(FileOwner) const;
+ virtual bool setFileTime(const QDateTime &newDate, FileTime time);
virtual QDateTime fileTime(FileTime time) const;
virtual void setFileName(const QString &file);
virtual int handle() const;
+ virtual bool cloneTo(QAbstractFileEngine *target);
bool atEnd() const;
uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
bool unmap(uchar *ptr);
diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp
index 9a42fb4a37..1486d3630c 100644
--- a/src/corelib/io/qdatastream.cpp
+++ b/src/corelib/io/qdatastream.cpp
@@ -562,6 +562,7 @@ void QDataStream::setByteOrder(ByteOrder bo)
\value Qt_5_7 Same as Qt_5_6
\value Qt_5_8 Same as Qt_5_6
\value Qt_5_9 Same as Qt_5_6
+ \value Qt_5_10 Same as Qt_5_6
\omitvalue Qt_DefaultCompiledVersion
\sa setVersion(), version()
@@ -1384,32 +1385,13 @@ int QDataStream::writeRawData(const char *s, int len)
int QDataStream::skipRawData(int len)
{
CHECK_STREAM_PRECOND(-1)
+ if (q_status != Ok && dev->isTransactionStarted())
+ return -1;
- if (dev->isSequential()) {
- char buf[4096];
- int sumRead = 0;
-
- while (len > 0) {
- int blockSize = qMin(len, (int)sizeof(buf));
- int n = readBlock(buf, blockSize);
- if (n == -1)
- return -1;
- if (n == 0)
- return sumRead;
-
- sumRead += n;
- len -= blockSize;
- }
- return sumRead;
- } else {
- qint64 pos = dev->pos();
- qint64 size = dev->size();
- if (pos + len > size)
- len = size - pos;
- if (!dev->seek(pos + len))
- return -1;
- return len;
- }
+ const int skipResult = dev->skip(len);
+ if (skipResult != len)
+ setStatus(ReadPastEnd);
+ return skipResult;
}
QT_END_NAMESPACE
diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h
index 575607e147..0a429d091a 100644
--- a/src/corelib/io/qdatastream.h
+++ b/src/corelib/io/qdatastream.h
@@ -96,10 +96,11 @@ public:
Qt_5_7 = Qt_5_6,
Qt_5_8 = Qt_5_7,
Qt_5_9 = Qt_5_8,
-#if QT_VERSION >= 0x050a00
+ Qt_5_10 = Qt_5_9,
+#if QT_VERSION >= 0x050b00
#error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion
#endif
- Qt_DefaultCompiledVersion = Qt_5_9
+ Qt_DefaultCompiledVersion = Qt_5_10
};
enum ByteOrder {
diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp
index be33ec2d23..341400fd93 100644
--- a/src/corelib/io/qdebug.cpp
+++ b/src/corelib/io/qdebug.cpp
@@ -648,6 +648,21 @@ QDebug &QDebug::resetFormat()
*/
/*!
+ \since 5.10
+ \fn QDebug &QDebug::operator<<(QStringView s)
+
+ Writes the string view, \a s, to the stream and returns a reference to the
+ stream. Normally, QDebug prints the string inside quotes and transforms
+ non-printable characters to their Unicode values (\\u1234).
+
+ To print non-printable characters without transformation, enable the
+ noquote() functionality. Note that some QDebug backends might not be 8-bit
+ clean.
+
+ See the QString overload for examples.
+*/
+
+/*!
\fn QDebug &QDebug::operator<<(QLatin1String s)
Writes the string, \a s, to the stream and returns a reference to the
diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h
index 186722b69b..ee8ef679a9 100644
--- a/src/corelib/io/qdebug.h
+++ b/src/corelib/io/qdebug.h
@@ -151,8 +151,11 @@ public:
inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); }
inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); }
inline QDebug &operator<<(const char* t) { stream->ts << QString::fromUtf8(t); return maybeSpace(); }
+#if QT_STRINGVIEW_LEVEL < 2
inline QDebug &operator<<(const QString & t) { putString(t.constData(), uint(t.length())); return maybeSpace(); }
inline QDebug &operator<<(const QStringRef & t) { putString(t.constData(), uint(t.length())); return maybeSpace(); }
+#endif
+ inline QDebug &operator<<(QStringView s) { putString(s.data(), size_t(s.size())); return maybeSpace(); }
inline QDebug &operator<<(QLatin1String t) { putByteArray(t.latin1(), t.size(), ContainsLatin1); return maybeSpace(); }
inline QDebug &operator<<(const QByteArray & t) { putByteArray(t.constData(), t.size(), ContainsBinary); return maybeSpace(); }
inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); }
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp
index 64d34aec59..e4888e9523 100644
--- a/src/corelib/io/qfile.cpp
+++ b/src/corelib/io/qfile.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -42,12 +43,14 @@
#include "qfile.h"
#include "qfsfileengine_p.h"
#include "qtemporaryfile.h"
+#include "qtemporaryfile_p.h"
#include "qlist.h"
#include "qfileinfo.h"
#include "private/qiodevice_p.h"
#include "private/qfile_p.h"
#include "private/qfilesystemengine_p.h"
#include "private/qsystemerror_p.h"
+#include "private/qtemporaryfile_p.h"
#if defined(QT_BUILD_CORE_LIB)
# include "qcoreapplication.h"
#endif
@@ -420,7 +423,7 @@ QFile::exists() const
Q_D(const QFile);
// 0x1000000 = QAbstractFileEngine::Refresh, forcing an update
return (d->engine()->fileFlags(QAbstractFileEngine::FlagsMask
- | QAbstractFileEngine::FileFlag(0x1000000)) & QAbstractFileEngine::ExistsFlag);
+ | QAbstractFileEngine::Refresh) & QAbstractFileEngine::ExistsFlag);
}
/*!
@@ -500,7 +503,8 @@ bool
QFile::remove()
{
Q_D(QFile);
- if (d->fileName.isEmpty()) {
+ if (d->fileName.isEmpty() &&
+ !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
qWarning("QFile::remove: Empty or null file name");
return false;
}
@@ -553,7 +557,9 @@ bool
QFile::rename(const QString &newName)
{
Q_D(QFile);
- if (d->fileName.isEmpty()) {
+
+ // if this is a QTemporaryFile, the virtual fileName() call here may do something
+ if (fileName().isEmpty()) {
qWarning("QFile::rename: Empty or null file name");
return false;
}
@@ -565,57 +571,64 @@ QFile::rename(const QString &newName)
d->setError(QFile::RenameError, tr("Source file does not exist."));
return false;
}
+
// If the file exists and it is a case-changing rename ("foo" -> "Foo"),
// compare Ids to make sure it really is a different file.
// Note: this does not take file engines into account.
+ bool changingCase = false;
QByteArray targetId = QFileSystemEngine::id(QFileSystemEntry(newName));
if (!targetId.isNull()) {
QByteArray fileId = d->fileEngine ?
d->fileEngine->id() :
QFileSystemEngine::id(QFileSystemEntry(d->fileName));
- if (fileId != targetId || d->fileName.compare(newName, Qt::CaseInsensitive)) {
- // ### Race condition. If a file is moved in after this, it /will/ be
- // overwritten. On Unix, the proper solution is to use hardlinks:
- // return ::link(old, new) && ::remove(old);
+ changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0);
+ if (!changingCase) {
d->setError(QFile::RenameError, tr("Destination file exists"));
return false;
}
-#ifndef QT_NO_TEMPORARYFILE
- // This #ifndef disables the workaround it encloses. Therefore, this configuration is not recommended.
+
#ifdef Q_OS_LINUX
// rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive
// FS, such as FAT32. Move the file away and rename in 2 steps to work around.
- QTemporaryFile tempFile(d->fileName + QLatin1String(".XXXXXX"));
- tempFile.setAutoRemove(false);
- if (!tempFile.open(QIODevice::ReadWrite)) {
- d->setError(QFile::RenameError, tempFile.errorString());
- return false;
- }
- tempFile.close();
- if (!d->engine()->rename(tempFile.fileName())) {
- d->setError(QFile::RenameError, tr("Error while renaming."));
- return false;
- }
- if (tempFile.rename(newName)) {
- d->fileEngine->setFileName(newName);
- d->fileName = newName;
- return true;
- }
- d->setError(QFile::RenameError, tempFile.errorString());
- // We need to restore the original file.
- if (!tempFile.rename(d->fileName)) {
- d->setError(QFile::RenameError, errorString() + QLatin1Char('\n')
+ QTemporaryFileName tfn(d->fileName);
+ QFileSystemEntry src(d->fileName);
+ QSystemError error;
+ for (int attempt = 0; attempt < 16; ++attempt) {
+ QFileSystemEntry tmp(tfn.generateNext(), QFileSystemEntry::FromNativePath());
+
+ // rename to temporary name
+ if (!QFileSystemEngine::renameFile(src, tmp, error))
+ continue;
+
+ // rename to final name
+ if (QFileSystemEngine::renameFile(tmp, QFileSystemEntry(newName), error)) {
+ d->fileEngine->setFileName(newName);
+ d->fileName = newName;
+ return true;
+ }
+
+ // We need to restore the original file.
+ QSystemError error2;
+ if (QFileSystemEngine::renameFile(tmp, src, error2))
+ break; // report the original error, below
+
+ // report both errors
+ d->setError(QFile::RenameError,
+ tr("Error while renaming: %1").arg(error.toString())
+ + QLatin1Char('\n')
+ tr("Unable to restore from %1: %2").
- arg(QDir::toNativeSeparators(tempFile.fileName()), tempFile.errorString()));
+ arg(QDir::toNativeSeparators(tmp.filePath()), error2.toString()));
+ return false;
}
+ d->setError(QFile::RenameError,
+ tr("Error while renaming: %1").arg(error.toString()));
return false;
#endif // Q_OS_LINUX
-#endif // QT_NO_TEMPORARYFILE
}
unsetError();
close();
if(error() == QFile::NoError) {
- if (d->engine()->rename(newName)) {
+ if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) {
unsetError();
// engine was able to handle the new name so we just reset it
d->fileEngine->setFileName(newName);
@@ -707,7 +720,7 @@ bool
QFile::link(const QString &linkName)
{
Q_D(QFile);
- if (d->fileName.isEmpty()) {
+ if (fileName().isEmpty()) {
qWarning("QFile::link: Empty or null file name");
return false;
}
@@ -753,7 +766,7 @@ bool
QFile::copy(const QString &newName)
{
Q_D(QFile);
- if (d->fileName.isEmpty()) {
+ if (fileName().isEmpty()) {
qWarning("QFile::copy: Empty or null file name");
return false;
}
@@ -794,25 +807,27 @@ QFile::copy(const QString &newName)
close();
d->setError(QFile::CopyError, tr("Cannot open for output"));
} else {
- char block[4096];
- qint64 totalRead = 0;
- while(!atEnd()) {
- qint64 in = read(block, sizeof(block));
- if (in <= 0)
- break;
- totalRead += in;
- if(in != out.write(block, in)) {
- close();
- d->setError(QFile::CopyError, tr("Failure to write block"));
- error = true;
- break;
+ if (!d->engine()->cloneTo(out.d_func()->engine())) {
+ char block[4096];
+ qint64 totalRead = 0;
+ while (!atEnd()) {
+ qint64 in = read(block, sizeof(block));
+ if (in <= 0)
+ break;
+ totalRead += in;
+ if (in != out.write(block, in)) {
+ close();
+ d->setError(QFile::CopyError, tr("Failure to write block"));
+ error = true;
+ break;
+ }
}
- }
- if (totalRead != size()) {
- // Unable to read from the source. The error string is
- // already set from read().
- error = true;
+ if (totalRead != size()) {
+ // Unable to read from the source. The error string is
+ // already set from read().
+ error = true;
+ }
}
if (!error && !out.rename(newName)) {
error = true;
@@ -957,7 +972,9 @@ bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
qWarning("QFile::open: File access not specified");
return false;
}
- if (d->openExternalFile(mode, fh, handleFlags)) {
+
+ // QIODevice provides the buffering, so request unbuffered file engines
+ if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) {
QIODevice::open(mode);
if (!(mode & Append) && !isSequential()) {
qint64 pos = (qint64)QT_FTELL(fh);
@@ -1013,7 +1030,9 @@ bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags)
qWarning("QFile::open: File access not specified");
return false;
}
- if (d->openExternalFile(mode, fd, handleFlags)) {
+
+ // QIODevice provides the buffering, so request unbuffered file engines
+ if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) {
QIODevice::open(mode);
if (!(mode & Append) && !isSequential()) {
qint64 pos = (qint64)QT_LSEEK(fd, QT_OFF_T(0), SEEK_CUR);
diff --git a/src/corelib/io/qfile.h b/src/corelib/io/qfile.h
index a2a1b25248..c5819e8076 100644
--- a/src/corelib/io/qfile.h
+++ b/src/corelib/io/qfile.h
@@ -81,6 +81,7 @@ public:
}
static QString decodeName(const QByteArray &localFileName)
{
+ // note: duplicated in qglobal.cpp (qEnvironmentVariable)
return QString::fromUtf8(localFileName).normalized(QString::NormalizationForm_C);
}
#else
diff --git a/src/corelib/io/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp
index f50949e6c1..2f99775c65 100644
--- a/src/corelib/io/qfiledevice.cpp
+++ b/src/corelib/io/qfiledevice.cpp
@@ -740,6 +740,76 @@ bool QFileDevice::unmap(uchar *address)
return false;
}
+/*!
+ \enum QFileDevice::FileTime
+ \since 5.10
+
+ This enum is used by the fileTime() and setFileTime() functions.
+
+ \value FileAccessTime When the file was most recently accessed
+ (e.g. read or written to).
+ \value FileBirthTime When the file was created (may not be not
+ supported on UNIX).
+ \value FileMetadataChangeTime When the file's metadata was last changed.
+ \value FileModificationTime When the file was most recently modified.
+
+ \sa setFileTime(), fileTime(), QFileInfo::fileTime()
+*/
+
+static inline QAbstractFileEngine::FileTime FileDeviceTimeToAbstractFileEngineTime(QFileDevice::FileTime time)
+{
+ Q_STATIC_ASSERT(int(QFileDevice::FileAccessTime) == int(QAbstractFileEngine::AccessTime));
+ Q_STATIC_ASSERT(int(QFileDevice::FileBirthTime) == int(QAbstractFileEngine::BirthTime));
+ Q_STATIC_ASSERT(int(QFileDevice::FileMetadataChangeTime) == int(QAbstractFileEngine::MetadataChangeTime));
+ Q_STATIC_ASSERT(int(QFileDevice::FileModificationTime) == int(QAbstractFileEngine::ModificationTime));
+ return QAbstractFileEngine::FileTime(time);
+}
+
+/*!
+ \since 5.10
+ Returns the file time specified by \a time.
+ If the time cannot be determined return QDateTime() (an invalid
+ date time).
+
+ \sa setFileTime(), FileTime, QDateTime::isValid()
+*/
+QDateTime QFileDevice::fileTime(QFileDevice::FileTime time) const
+{
+ Q_D(const QFileDevice);
+
+ if (d->engine())
+ return d->engine()->fileTime(FileDeviceTimeToAbstractFileEngineTime(time));
+
+ return QDateTime();
+}
+
+/*!
+ \since 5.10
+ Sets the file time specified by \a fileTime to \a newDate, returning true
+ if successful; otherwise returns false.
+
+ \note The file must be open to use this function.
+
+ \sa fileTime(), FileTime
+*/
+bool QFileDevice::setFileTime(const QDateTime &newDate, QFileDevice::FileTime fileTime)
+{
+ Q_D(QFileDevice);
+
+ if (!d->engine()) {
+ d->setError(QFileDevice::UnspecifiedError, tr("No file engine available"));
+ return false;
+ }
+
+ if (!d->fileEngine->setFileTime(newDate, FileDeviceTimeToAbstractFileEngineTime(fileTime))) {
+ d->setError(d->fileEngine->error(), d->fileEngine->errorString());
+ return false;
+ }
+
+ unsetError();
+ return true;
+}
+
QT_END_NAMESPACE
#ifndef QT_NO_QOBJECT
diff --git a/src/corelib/io/qfiledevice.h b/src/corelib/io/qfiledevice.h
index 4cfda1b044..e941781c25 100644
--- a/src/corelib/io/qfiledevice.h
+++ b/src/corelib/io/qfiledevice.h
@@ -45,6 +45,7 @@
QT_BEGIN_NAMESPACE
+class QDateTime;
class QFileDevicePrivate;
class Q_CORE_EXPORT QFileDevice : public QIODevice
@@ -73,6 +74,13 @@ public:
CopyError = 14
};
+ enum FileTime {
+ FileAccessTime,
+ FileBirthTime,
+ FileMetadataChangeTime,
+ FileModificationTime
+ };
+
enum Permission {
ReadOwner = 0x4000, WriteOwner = 0x2000, ExeOwner = 0x1000,
ReadUser = 0x0400, WriteUser = 0x0200, ExeUser = 0x0100,
@@ -119,6 +127,9 @@ public:
uchar *map(qint64 offset, qint64 size, MemoryMapFlags flags = NoOptions);
bool unmap(uchar *address);
+ QDateTime fileTime(QFileDevice::FileTime time) const;
+ bool setFileTime(const QDateTime &newDate, QFileDevice::FileTime fileTime);
+
protected:
QFileDevice();
#ifdef QT_NO_QOBJECT
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index 907574b6dd..26078a6c71 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -42,6 +42,7 @@
#include "qglobal.h"
#include "qdir.h"
#include "qfileinfo_p.h"
+#include "qdebug.h"
QT_BEGIN_NAMESPACE
@@ -185,17 +186,25 @@ uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) cons
QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) const
{
Q_ASSERT(fileEngine); // should never be called when using the native FS
- if (fileTimes.size() != 3)
- fileTimes.resize(3);
if (!cache_enabled)
clearFlags();
- uint cf;
- if (request == QAbstractFileEngine::CreationTime)
- cf = CachedCTime;
- else if (request == QAbstractFileEngine::ModificationTime)
- cf = CachedMTime;
- else
+
+ uint cf = 0;
+ switch (request) {
+ case QAbstractFileEngine::AccessTime:
cf = CachedATime;
+ break;
+ case QAbstractFileEngine::BirthTime:
+ cf = CachedBTime;
+ break;
+ case QAbstractFileEngine::MetadataChangeTime:
+ cf = CachedMCTime;
+ break;
+ case QAbstractFileEngine::ModificationTime:
+ cf = CachedMTime;
+ break;
+ }
+
if (!getCachedFlag(cf)) {
fileTimes[request] = fileEngine->fileTime(request);
setCachedFlag(cf);
@@ -262,8 +271,8 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request)
info objects, just append one to the file name given to the constructors
or setFile().
- The file's dates are returned by created(), lastModified() and
- lastRead(). Information about the file's access permissions is
+ The file's dates are returned by created(), lastModified(), lastRead() and
+ fileTime(). Information about the file's access permissions is
obtained with isReadable(), isWritable() and isExecutable(). The
file's ownership is available from owner(), ownerId(), group() and
groupId(). You can examine a file's permissions and ownership in a
@@ -582,9 +591,6 @@ QString QFileInfo::absolutePath() const
if (d->isDefaultConstructed) {
return QLatin1String("");
- } else if (d->fileEntry.isEmpty()) {
- qWarning("QFileInfo::absolutePath: Constructed with empty filename");
- return QLatin1String("");
}
return d->getFileName(QAbstractFileEngine::AbsolutePathName);
}
@@ -904,14 +910,10 @@ QDir QFileInfo::absoluteDir() const
bool QFileInfo::isReadable() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserReadPermission))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserReadPermission);
- return (d->metaData.permissions() & QFile::ReadUser) != 0;
- }
- return d->getFileFlags(QAbstractFileEngine::ReadUserPerm);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::UserReadPermission,
+ [d]() { return (d->metaData.permissions() & QFile::ReadUser) != 0; },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::ReadUserPerm); });
}
/*!
@@ -925,14 +927,10 @@ bool QFileInfo::isReadable() const
bool QFileInfo::isWritable() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserWritePermission))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserWritePermission);
- return (d->metaData.permissions() & QFile::WriteUser) != 0;
- }
- return d->getFileFlags(QAbstractFileEngine::WriteUserPerm);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::UserWritePermission,
+ [d]() { return (d->metaData.permissions() & QFile::WriteUser) != 0; },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::WriteUserPerm); });
}
/*!
@@ -943,14 +941,10 @@ bool QFileInfo::isWritable() const
bool QFileInfo::isExecutable() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserExecutePermission))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserExecutePermission);
- return (d->metaData.permissions() & QFile::ExeUser) != 0;
- }
- return d->getFileFlags(QAbstractFileEngine::ExeUserPerm);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::UserExecutePermission,
+ [d]() { return (d->metaData.permissions() & QFile::ExeUser) != 0; },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::ExeUserPerm); });
}
/*!
@@ -962,14 +956,10 @@ bool QFileInfo::isExecutable() const
bool QFileInfo::isHidden() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::HiddenAttribute))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::HiddenAttribute);
- return d->metaData.isHidden();
- }
- return d->getFileFlags(QAbstractFileEngine::HiddenFlag);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::HiddenAttribute,
+ [d]() { return d->metaData.isHidden(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::HiddenFlag); });
}
/*!
@@ -1005,14 +995,10 @@ bool QFileInfo::isNativePath() const
bool QFileInfo::isFile() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::FileType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::FileType);
- return d->metaData.isFile();
- }
- return d->getFileFlags(QAbstractFileEngine::FileType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::FileType,
+ [d]() { return d->metaData.isFile(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::FileType); });
}
/*!
@@ -1024,14 +1010,10 @@ bool QFileInfo::isFile() const
bool QFileInfo::isDir() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::DirectoryType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::DirectoryType);
- return d->metaData.isDirectory();
- }
- return d->getFileFlags(QAbstractFileEngine::DirectoryType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::DirectoryType,
+ [d]() { return d->metaData.isDirectory(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::DirectoryType); });
}
@@ -1045,14 +1027,10 @@ bool QFileInfo::isDir() const
bool QFileInfo::isBundle() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::BundleType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::BundleType);
- return d->metaData.isBundle();
- }
- return d->getFileFlags(QAbstractFileEngine::BundleType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::BundleType,
+ [d]() { return d->metaData.isBundle(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::BundleType); });
}
/*!
@@ -1079,14 +1057,10 @@ bool QFileInfo::isBundle() const
bool QFileInfo::isSymLink() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::LegacyLinkType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::LegacyLinkType);
- return d->metaData.isLegacyLink();
- }
- return d->getFileFlags(QAbstractFileEngine::LinkType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::LegacyLinkType,
+ [d]() { return d->metaData.isLegacyLink(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); });
}
/*!
@@ -1098,7 +1072,7 @@ bool QFileInfo::isRoot() const
{
Q_D(const QFileInfo);
if (d->isDefaultConstructed)
- return true;
+ return false;
if (d->fileEngine == 0) {
if (d->fileEntry.isRoot()) {
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
@@ -1174,14 +1148,10 @@ QString QFileInfo::owner() const
uint QFileInfo::ownerId() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserId))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserId);
- return d->metaData.userId();
- }
- return d->fileEngine->ownerId(QAbstractFileEngine::OwnerUser);
+ return d->checkAttribute(uint(-2),
+ QFileSystemMetaData::UserId,
+ [d]() { return d->metaData.userId(); },
+ [d]() { return d->fileEngine->ownerId(QAbstractFileEngine::OwnerUser); });
}
/*!
@@ -1213,14 +1183,10 @@ QString QFileInfo::group() const
uint QFileInfo::groupId() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::GroupId))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::GroupId);
- return d->metaData.groupId();
- }
- return d->fileEngine->ownerId(QAbstractFileEngine::OwnerGroup);
+ return d->checkAttribute(uint(-2),
+ QFileSystemMetaData::GroupId,
+ [d]() { return d->metaData.groupId(); },
+ [d]() { return d->fileEngine->ownerId(QAbstractFileEngine::OwnerGroup); });
}
/*!
@@ -1242,16 +1208,15 @@ uint QFileInfo::groupId() const
bool QFileInfo::permission(QFile::Permissions permissions) const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- // the QFileSystemMetaData::MetaDataFlag and QFile::Permissions overlap, so just static cast.
- QFileSystemMetaData::MetaDataFlag permissionFlags = static_cast<QFileSystemMetaData::MetaDataFlag>((int)permissions);
- if (!d->cache_enabled || !d->metaData.hasFlags(permissionFlags))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, permissionFlags);
- return (d->metaData.permissions() & permissions) == permissions;
- }
- return d->getFileFlags(QAbstractFileEngine::FileFlags((int)permissions)) == (uint)permissions;
+ // the QFileSystemMetaData::MetaDataFlag and QFile::Permissions overlap, so just cast.
+ auto fseFlags = QFileSystemMetaData::MetaDataFlag(int(permissions));
+ auto feFlags = QAbstractFileEngine::FileFlags(int(permissions));
+ return d->checkAttribute<bool>(
+ fseFlags,
+ [=]() { return (d->metaData.permissions() & permissions) == permissions; },
+ [=]() {
+ return d->getFileFlags(feFlags) == uint(permissions);
+ });
}
/*!
@@ -1264,14 +1229,12 @@ bool QFileInfo::permission(QFile::Permissions permissions) const
QFile::Permissions QFileInfo::permissions() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::Permissions))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::Permissions);
- return d->metaData.permissions();
- }
- return QFile::Permissions(d->getFileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask);
+ return d->checkAttribute<QFile::Permissions>(
+ QFileSystemMetaData::Permissions,
+ [d]() { return d->metaData.permissions(); },
+ [d]() {
+ return QFile::Permissions(d->getFileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask);
+ });
}
@@ -1284,64 +1247,77 @@ QFile::Permissions QFileInfo::permissions() const
qint64 QFileInfo::size() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::SizeAttribute))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::SizeAttribute);
- return d->metaData.size();
- }
- if (!d->getCachedFlag(QFileInfoPrivate::CachedSize)) {
- d->setCachedFlag(QFileInfoPrivate::CachedSize);
- d->fileSize = d->fileEngine->size();
- }
- return d->fileSize;
+ return d->checkAttribute<qint64>(
+ QFileSystemMetaData::SizeAttribute,
+ [d]() { return d->metaData.size(); },
+ [d]() {
+ if (!d->getCachedFlag(QFileInfoPrivate::CachedSize)) {
+ d->setCachedFlag(QFileInfoPrivate::CachedSize);
+ d->fileSize = d->fileEngine->size();
+ }
+ return d->fileSize;
+ });
}
+#if QT_DEPRECATED_SINCE(5, 10)
/*!
- Returns the date and local time when the file was created.
+ \deprecated
- On most Unix systems, this function returns the time of the last
- status change. A status change occurs when the file is created,
- but it also occurs whenever the user writes or sets inode
- information (for example, changing the file permissions).
+ Returns the date and time when the file was created, the time its metadata
+ was last changed or the time of last modification, whichever one of the
+ three is available (in that order).
- If neither creation time nor "last status change" time are not
- available, returns the same as lastModified().
+ This function is deprecated. Instead, use the birthTime() function to get
+ the time the file was created, metadataChangeTime() to get the time its
+ metadata was last changed, or lastModified() to get the time it was last modified.
- \sa lastModified(), lastRead()
+ \sa birthTime(), metadataChangeTime(), lastModified(), lastRead()
*/
QDateTime QFileInfo::created() const
{
- Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return QDateTime();
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::CreationTime))
- if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::CreationTime))
- return QDateTime();
- return d->metaData.creationTime().toLocalTime();
- }
- return d->getFileTime(QAbstractFileEngine::CreationTime).toLocalTime();
+ QDateTime d = fileTime(QFile::FileBirthTime);
+ if (d.isValid())
+ return d;
+ return fileTime(QFile::FileMetadataChangeTime);
+}
+#endif
+
+/*!
+ \since 5.10
+ Returns the date and time when the file was created / born.
+
+ If the file birth time is not available, this function returns an invalid
+ QDateTime.
+
+ \sa lastModified(), lastRead(), metadataChangeTime()
+*/
+QDateTime QFileInfo::birthTime() const
+{
+ return fileTime(QFile::FileBirthTime);
+}
+
+/*!
+ \since 5.10
+ Returns the date and time when the file metadata was changed. A metadata
+ change occurs when the file is created, but it also occurs whenever the
+ user writes or sets inode information (for example, changing the file
+ permissions).
+
+ \sa lastModified(), lastRead()
+*/
+QDateTime QFileInfo::metadataChangeTime() const
+{
+ return fileTime(QFile::FileMetadataChangeTime);
}
/*!
Returns the date and local time when the file was last modified.
- \sa created(), lastRead()
+ \sa birthTime(), lastRead(), metadataChangeTime(), fileTime()
*/
QDateTime QFileInfo::lastModified() const
{
- Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return QDateTime();
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ModificationTime))
- if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ModificationTime))
- return QDateTime();
- return d->metaData.modificationTime().toLocalTime();
- }
- return d->getFileTime(QAbstractFileEngine::ModificationTime).toLocalTime();
+ return fileTime(QFile::FileModificationTime);
}
/*!
@@ -1350,20 +1326,50 @@ QDateTime QFileInfo::lastModified() const
On platforms where this information is not available, returns the
same as lastModified().
- \sa created(), lastModified()
+ \sa birthTime(), lastModified(), metadataChangeTime(), fileTime()
*/
QDateTime QFileInfo::lastRead() const
{
+ return fileTime(QFile::FileAccessTime);
+}
+
+/*!
+ \since 5.10
+
+ Returns the file time specified by \a time. If the time cannot be
+ determined, an invalid date time is returned.
+
+ \sa QFile::FileTime, QDateTime::isValid()
+*/
+QDateTime QFileInfo::fileTime(QFile::FileTime time) const
+{
+ Q_STATIC_ASSERT(int(QFile::FileAccessTime) == int(QAbstractFileEngine::AccessTime));
+ Q_STATIC_ASSERT(int(QFile::FileBirthTime) == int(QAbstractFileEngine::BirthTime));
+ Q_STATIC_ASSERT(int(QFile::FileMetadataChangeTime) == int(QAbstractFileEngine::MetadataChangeTime));
+ Q_STATIC_ASSERT(int(QFile::FileModificationTime) == int(QAbstractFileEngine::ModificationTime));
+
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return QDateTime();
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::AccessTime))
- if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::AccessTime))
- return QDateTime();
- return d->metaData.accessTime().toLocalTime();
+ auto fetime = QAbstractFileEngine::FileTime(time);
+ QFileSystemMetaData::MetaDataFlags flag;
+ switch (time) {
+ case QFile::FileAccessTime:
+ flag = QFileSystemMetaData::AccessTime;
+ break;
+ case QFile::FileBirthTime:
+ flag = QFileSystemMetaData::BirthTime;
+ break;
+ case QFile::FileMetadataChangeTime:
+ flag = QFileSystemMetaData::MetadataChangeTime;
+ break;
+ case QFile::FileModificationTime:
+ flag = QFileSystemMetaData::ModificationTime;
+ break;
}
- return d->getFileTime(QAbstractFileEngine::AccessTime).toLocalTime();
+
+ return d->checkAttribute<QDateTime>(
+ flag,
+ [=]() { return d->metaData.fileTime(fetime).toLocalTime(); },
+ [=]() { return d->getFileTime(fetime).toLocalTime(); });
}
/*!
@@ -1410,4 +1416,15 @@ void QFileInfo::setCaching(bool enable)
Synonym for QList<QFileInfo>.
*/
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QFileInfo &fi)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg.noquote();
+ dbg << "QFileInfo(" << QDir::toNativeSeparators(fi.filePath()) << ')';
+ return dbg;
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h
index a160e8463e..f295a86015 100644
--- a/src/corelib/io/qfileinfo.h
+++ b/src/corelib/io/qfileinfo.h
@@ -129,9 +129,16 @@ public:
qint64 size() const;
+ // ### Qt6: inline these functions
+#if QT_DEPRECATED_SINCE(5, 10)
+ QT_DEPRECATED_X("Use either birthTime() or metadataChangeTime()")
QDateTime created() const;
+#endif
+ QDateTime birthTime() const;
+ QDateTime metadataChangeTime() const;
QDateTime lastModified() const;
QDateTime lastRead() const;
+ QDateTime fileTime(QFile::FileTime time) const;
bool caching() const;
void setCaching(bool on);
@@ -151,6 +158,10 @@ Q_DECLARE_SHARED(QFileInfo)
typedef QList<QFileInfo> QFileInfoList;
+#ifndef QT_NO_DEBUG_STREAM
+Q_CORE_EXPORT QDebug operator<<(QDebug, const QFileInfo &);
+#endif
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QFileInfo)
diff --git a/src/corelib/io/qfileinfo_p.h b/src/corelib/io/qfileinfo_p.h
index 806df179e8..e4b28f4519 100644
--- a/src/corelib/io/qfileinfo_p.h
+++ b/src/corelib/io/qfileinfo_p.h
@@ -56,7 +56,6 @@
#include "qatomic.h"
#include "qshareddata.h"
#include "qfilesystemengine_p.h"
-#include "qvector.h"
#include <QtCore/private/qabstractfileengine_p.h>
#include <QtCore/private/qfilesystementry_p.h>
@@ -67,9 +66,18 @@ QT_BEGIN_NAMESPACE
class QFileInfoPrivate : public QSharedData
{
public:
- enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04,
- CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40,
- CachedSize =0x08, CachedPerms=0x80 };
+ enum {
+ // note: cachedFlags is only 30-bits wide
+ CachedFileFlags = 0x01,
+ CachedLinkTypeFlag = 0x02,
+ CachedBundleTypeFlag = 0x04,
+ CachedSize = 0x08,
+ CachedATime = 0x10,
+ CachedBTime = 0x20,
+ CachedMCTime = 0x40,
+ CachedMTime = 0x80,
+ CachedPerms = 0x100
+ };
inline QFileInfoPrivate()
: QSharedData(), fileEngine(0),
@@ -95,7 +103,7 @@ public:
fileEngine(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(fileEntry, metaData)),
cachedFlags(0),
#ifndef QT_NO_FSFILEENGINE
- isDefaultConstructed(false),
+ isDefaultConstructed(file.isEmpty()),
#else
isDefaultConstructed(!fileEngine),
#endif
@@ -158,24 +166,40 @@ public:
QScopedPointer<QAbstractFileEngine> const fileEngine;
mutable QString fileNames[QAbstractFileEngine::NFileNames];
- mutable QString fileOwners[2];
+ mutable QString fileOwners[2]; // QAbstractFileEngine::FileOwner: OwnerUser and OwnerGroup
+ mutable QDateTime fileTimes[4]; // QAbstractFileEngine::FileTime: BirthTime, MetadataChangeTime, ModificationTime, AccessTime
mutable uint cachedFlags : 30;
bool const isDefaultConstructed : 1; // QFileInfo is a default constructed instance
bool cache_enabled : 1;
mutable uint fileFlags;
mutable qint64 fileSize;
- // ### Qt6: FIXME: This vector is essentially a plain array
- // mutable QDateTime fileTimes[3], but the array is slower
- // to initialize than the QVector as QDateTime has a pimpl.
- // In Qt 6, QDateTime should inline its data members,
- // and this here can be an array again.
- mutable QVector<QDateTime> fileTimes;
inline bool getCachedFlag(uint c) const
{ return cache_enabled ? (cachedFlags & c) : 0; }
inline void setCachedFlag(uint c) const
{ if (cache_enabled) cachedFlags |= c; }
+ template <typename Ret, typename FSLambda, typename EngineLambda>
+ Ret checkAttribute(Ret defaultValue, QFileSystemMetaData::MetaDataFlags fsFlags, const FSLambda &fsLambda,
+ const EngineLambda &engineLambda) const
+ {
+ if (isDefaultConstructed)
+ return defaultValue;
+ if (fileEngine)
+ return engineLambda();
+ if (!cache_enabled || !metaData.hasFlags(fsFlags)) {
+ QFileSystemEngine::fillMetaData(fileEntry, metaData, fsFlags);
+ // ignore errors, fillMetaData will have cleared the flags
+ }
+ return fsLambda();
+ }
+
+ template <typename Ret, typename FSLambda, typename EngineLambda>
+ Ret checkAttribute(QFileSystemMetaData::MetaDataFlags fsFlags, const FSLambda &fsLambda,
+ const EngineLambda &engineLambda) const
+ {
+ return checkAttribute(Ret(), fsFlags, fsLambda, engineLambda);
+ }
};
QT_END_NAMESPACE
diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp
index ccbcdb1037..7abdf90bf5 100644
--- a/src/corelib/io/qfilesystemengine.cpp
+++ b/src/corelib/io/qfilesystemengine.cpp
@@ -86,8 +86,6 @@ QString QFileSystemEngine::slowCanonicalized(const QString &path)
fi.setFile(prefix);
if (fi.isSymLink()) {
QString target = fi.symLinkTarget();
- if(QFileInfo(target).isRelative())
- target = fi.absolutePath() + slash + target;
if (separatorPos != -1) {
if (fi.isDir() && !target.endsWith(slash))
target.append(slash);
@@ -204,228 +202,6 @@ QAbstractFileEngine *QFileSystemEngine::resolveEntryAndCreateLegacyEngine(
return engine;
}
-//these unix functions are in this file, because they are shared by symbian port
-//for open C file handles.
-#ifdef Q_OS_UNIX
-//static
-bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data)
-{
- data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
- data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
-
- QT_STATBUF statBuffer;
- if (QT_FSTAT(fd, &statBuffer) == 0) {
- data.fillFromStatBuf(statBuffer);
- return true;
- }
-
- return false;
-}
-
-#if defined(_DEXTRA_FIRST)
-static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32)
-{
- statBuf64->st_mode = statBuf32.st_mode;
- statBuf64->st_size = statBuf32.st_size;
-#if _POSIX_VERSION >= 200809L
- statBuf64->st_ctim = statBuf32.st_ctim;
- statBuf64->st_mtim = statBuf32.st_mtim;
- statBuf64->st_atim = statBuf32.st_atim;
-#else
- statBuf64->st_ctime = statBuf32.st_ctime;
- statBuf64->st_mtime = statBuf32.st_mtime;
- statBuf64->st_atime = statBuf32.st_atime;
-#endif
- statBuf64->st_uid = statBuf32.st_uid;
- statBuf64->st_gid = statBuf32.st_gid;
-}
-#endif
-
-#if _POSIX_VERSION >= 200809L
-static qint64 timespecToMSecs(const timespec &spec)
-{
- return (qint64(spec.tv_sec) * 1000) + (spec.tv_nsec / 1000000);
-}
-#endif
-
-void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
-{
- // Permissions
- if (statBuffer.st_mode & S_IRUSR)
- entryFlags |= QFileSystemMetaData::OwnerReadPermission;
- if (statBuffer.st_mode & S_IWUSR)
- entryFlags |= QFileSystemMetaData::OwnerWritePermission;
- if (statBuffer.st_mode & S_IXUSR)
- entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
-
- if (statBuffer.st_mode & S_IRGRP)
- entryFlags |= QFileSystemMetaData::GroupReadPermission;
- if (statBuffer.st_mode & S_IWGRP)
- entryFlags |= QFileSystemMetaData::GroupWritePermission;
- if (statBuffer.st_mode & S_IXGRP)
- entryFlags |= QFileSystemMetaData::GroupExecutePermission;
-
- if (statBuffer.st_mode & S_IROTH)
- entryFlags |= QFileSystemMetaData::OtherReadPermission;
- if (statBuffer.st_mode & S_IWOTH)
- entryFlags |= QFileSystemMetaData::OtherWritePermission;
- if (statBuffer.st_mode & S_IXOTH)
- entryFlags |= QFileSystemMetaData::OtherExecutePermission;
-
- // Type
- if ((statBuffer.st_mode & S_IFMT) == S_IFREG)
- entryFlags |= QFileSystemMetaData::FileType;
- else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR)
- entryFlags |= QFileSystemMetaData::DirectoryType;
- else if ((statBuffer.st_mode & S_IFMT) != S_IFBLK)
- entryFlags |= QFileSystemMetaData::SequentialType;
-
- // Attributes
- entryFlags |= QFileSystemMetaData::ExistsAttribute;
- size_ = statBuffer.st_size;
-#if defined(Q_OS_DARWIN)
- if (statBuffer.st_flags & UF_HIDDEN) {
- entryFlags |= QFileSystemMetaData::HiddenAttribute;
- knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
- }
-#endif
-
- // Times
-#if _POSIX_VERSION >= 200809L
- modificationTime_ = timespecToMSecs(statBuffer.st_mtim);
- creationTime_ = timespecToMSecs(statBuffer.st_ctim);
- if (!creationTime_)
- creationTime_ = modificationTime_;
- accessTime_ = timespecToMSecs(statBuffer.st_atim);
-#else
- creationTime_ = qint64(statBuffer.st_ctime ? statBuffer.st_ctime : statBuffer.st_mtime) * 1000;
- modificationTime_ = qint64(statBuffer.st_mtime) * 1000;
- accessTime_ = qint64(statBuffer.st_atime) * 1000;
-#endif
- userId_ = statBuffer.st_uid;
- groupId_ = statBuffer.st_gid;
-}
-
-void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
-{
-#if defined(_DEXTRA_FIRST)
- knownFlagsMask = 0;
- entryFlags = 0;
- for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
- extra = _DEXTRA_NEXT(extra)) {
- if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
-
- const struct dirent_extra_stat * const extra_stat =
- reinterpret_cast<struct dirent_extra_stat *>(extra);
-
- // Remember whether this was a link or not, this saves an lstat() call later.
- if (extra->d_type == _DTYPE_LSTAT) {
- knownFlagsMask |= QFileSystemMetaData::LinkType;
- if (S_ISLNK(extra_stat->d_stat.st_mode))
- entryFlags |= QFileSystemMetaData::LinkType;
- }
-
- // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data,
- // as we need the stat() information there, not the lstat() information.
- // In this case, don't use the extra information.
- // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for
- // symlinks, we always incur the cost of an extra stat() call later.
- if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
- continue;
-
-#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
- // Even with large file support, d_stat is always of type struct stat, not struct stat64,
- // so it needs to be converted
- struct stat64 statBuf;
- fillStat64fromStat32(&statBuf, extra_stat->d_stat);
- fillFromStatBuf(statBuf);
-#else
- fillFromStatBuf(extra_stat->d_stat);
-#endif
- knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
- if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
- knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
- entryFlags |= QFileSystemMetaData::ExistsAttribute;
- }
- }
- }
-#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
- // BSD4 includes OS X and iOS
-
- // ### This will clear all entry flags and knownFlagsMask
- switch (entry.d_type)
- {
- case DT_DIR:
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_BLK:
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType
- | QFileSystemMetaData::AliasType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_CHR:
- case DT_FIFO:
- case DT_SOCK:
- // ### System attribute
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType
- | QFileSystemMetaData::AliasType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_LNK:
- knownFlagsMask = QFileSystemMetaData::LinkType;
- entryFlags = QFileSystemMetaData::LinkType;
- break;
-
- case DT_REG:
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::FileType
- | QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_UNKNOWN:
- default:
- clear();
- }
-#else
- Q_UNUSED(entry)
-#endif
-}
-
-#endif
-
//static
QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
{
@@ -435,6 +211,8 @@ QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileS
#else //(Q_OS_UNIX)
if (!metaData.hasFlags(QFileSystemMetaData::UserId))
QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::UserId);
+ if (!metaData.exists())
+ return QString();
return resolveUserName(metaData.userId());
#endif
}
@@ -448,6 +226,8 @@ QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFile
#else //(Q_OS_UNIX)
if (!metaData.hasFlags(QFileSystemMetaData::GroupId))
QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::GroupId);
+ if (!metaData.exists())
+ return QString();
return resolveGroupName(metaData.groupId());
#endif
}
diff --git a/src/corelib/io/qfilesystemengine_p.h b/src/corelib/io/qfilesystemengine_p.h
index e3e52f6eaa..09ec2d6a10 100644
--- a/src/corelib/io/qfilesystemengine_p.h
+++ b/src/corelib/io/qfilesystemengine_p.h
@@ -91,8 +91,11 @@ public:
static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what);
#if defined(Q_OS_UNIX)
+ static bool cloneFile(int srcfd, int dstfd, const QFileSystemMetaData &knownData);
static bool fillMetaData(int fd, QFileSystemMetaData &data); // what = PosixStatFlags
static QByteArray id(int fd);
+ static bool setFileTime(int fd, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime whatTime, QSystemError &error);
static bool setPermissions(int fd, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data = nullptr);
#endif
@@ -106,6 +109,8 @@ public:
static bool fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what);
static QByteArray id(HANDLE fHandle);
+ static bool setFileTime(HANDLE fHandle, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime whatTime, QSystemError &error);
static QString owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own);
static QString nativeAbsoluteFilePath(const QString &path);
#endif
@@ -121,11 +126,16 @@ public:
static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
+ static bool renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool removeFile(const QFileSystemEntry &entry, QSystemError &error);
static bool setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data = 0);
+ // unused, therefore not implemented
+ static bool setFileTime(const QFileSystemEntry &entry, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime whatTime, QSystemError &error);
+
static bool setCurrentPath(const QFileSystemEntry &entry);
static QFileSystemEntry currentPath();
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 7fed54f733..b974af80dc 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -43,6 +43,8 @@
#include "qfilesystemengine_p.h"
#include "qfile.h"
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/private/qcore_unix_p.h>
#include <QtCore/qvarlengtharray.h>
#include <pwd.h>
@@ -53,6 +55,12 @@
#include <stdio.h>
#include <errno.h>
+#if QT_HAS_INCLUDE(<paths.h>)
+# include <paths.h>
+#endif
+#ifndef _PATH_TMP // from <paths.h>
+# define _PATH_TMP "/tmp"
+#endif
#if defined(Q_OS_MAC)
# include <QtCore/private/qcore_mac_p.h>
@@ -68,14 +76,62 @@
#endif
#if defined(Q_OS_DARWIN)
+# if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000)
+# include <sys/clonefile.h>
+# endif
+# include <copyfile.h>
// We cannot include <Foundation/Foundation.h> (it's an Objective-C header), but
// we need these declarations:
Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
extern "C" NSString *NSTemporaryDirectory();
#endif
+#if defined(Q_OS_LINUX)
+# include <sys/ioctl.h>
+# include <sys/syscall.h>
+# include <sys/sendfile.h>
+# include <linux/fs.h>
+
+// in case linux/fs.h is too old and doesn't define it:
+#ifndef FICLONE
+# define FICLONE _IOW(0x94, 9, int)
+#endif
+
+# if !QT_CONFIG(renameat2) && defined(SYS_renameat2)
+static int renameat2(int oldfd, const char *oldpath, int newfd, const char *newpath, unsigned flags)
+{ return syscall(SYS_renameat2, oldfd, oldpath, newfd, newpath, flags); }
+# endif
+
+# if !QT_CONFIG(statx) && defined(SYS_statx) && QT_HAS_INCLUDE(<linux/stat.h>)
+# include <linux/stat.h>
+static int statx(int dirfd, const char *pathname, int flag, unsigned mask, struct statx *statxbuf)
+{ return syscall(SYS_statx, dirfd, pathname, flag, mask, statxbuf); }
+# endif
+#endif
+
+#ifndef STATX_BASIC_STATS
+struct statx { mode_t stx_mode; };
+#endif
+
QT_BEGIN_NAMESPACE
+enum {
+#ifdef Q_OS_ANDROID
+ // On Android, the link(2) system call has been observed to always fail
+ // with EACCES, regardless of whether there are permission problems or not.
+ SupportsHardlinking = false
+#else
+ SupportsHardlinking = true
+#endif
+};
+
+#define emptyFileEntryWarning() emptyFileEntryWarning_(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC)
+static void emptyFileEntryWarning_(const char *file, int line, const char *function)
+{
+ QMessageLogger(file, line, function).warning("Empty filename passed to function");
+ errno = EINVAL;
+}
+
#if defined(Q_OS_DARWIN)
static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data,
const QFileSystemEntry &entry,
@@ -144,33 +200,457 @@ static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &e
}
#endif
+namespace {
+namespace GetFileTimes {
+#if !QT_CONFIG(futimens) && (QT_CONFIG(futimes))
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atim, &T::st_mtim, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atim.tv_sec;
+ access->tv_usec = p->st_atim.tv_nsec / 1000;
+
+ modification->tv_sec = p->st_mtim.tv_sec;
+ modification->tv_usec = p->st_mtim.tv_nsec / 1000;
+}
+
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atimespec, &T::st_mtimespec, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atimespec.tv_sec;
+ access->tv_usec = p->st_atimespec.tv_nsec / 1000;
+
+ modification->tv_sec = p->st_mtimespec.tv_sec;
+ modification->tv_usec = p->st_mtimespec.tv_nsec / 1000;
+}
+
+# ifndef st_atimensec
+// if "st_atimensec" is defined, this would expand to invalid C++
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atimensec, &T::st_mtimensec, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atime;
+ access->tv_usec = p->st_atimensec / 1000;
+
+ modification->tv_sec = p->st_mtime;
+ modification->tv_usec = p->st_mtimensec / 1000;
+}
+# endif
+#endif
+
+qint64 timespecToMSecs(const timespec &spec)
+{
+ return (qint64(spec.tv_sec) * 1000) + (spec.tv_nsec / 1000000);
+}
+
+// fallback set
+Q_DECL_UNUSED qint64 atime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_atime) * 1000; }
+Q_DECL_UNUSED qint64 birthtime(const QT_STATBUF &, ulong) { return Q_INT64_C(0); }
+Q_DECL_UNUSED qint64 ctime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_ctime) * 1000; }
+Q_DECL_UNUSED qint64 mtime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_mtime) * 1000; }
+
+// Xtim, POSIX.1-2008
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atim, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_atim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtim, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_birthtim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctim, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_ctim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtim, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_mtim); }
+
+#ifndef st_mtimespec
+// Xtimespec
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimespec, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_atimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimespec, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_birthtimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimespec, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_ctimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimespec, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_mtimespec); }
+#endif
+
+#ifndef st_mtimensec
+// Xtimensec
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimensec, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return statBuffer.st_atime * Q_INT64_C(1000) + statBuffer.st_atimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimensec, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return statBuffer.st_birthtime * Q_INT64_C(1000) + statBuffer.st_birthtimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimensec, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return statBuffer.st_ctime * Q_INT64_C(1000) + statBuffer.st_ctimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimensec, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return statBuffer.st_mtime * Q_INT64_C(1000) + statBuffer.st_mtimensec / 1000000; }
+#endif
+} // namespace GetFileTimes
+} // unnamed namespace
+
+#ifdef STATX_BASIC_STATS
+static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer)
+{
+#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
+ static QBasicAtomicInteger<qint8> statxTested = Q_BASIC_ATOMIC_INITIALIZER(0);
+#else
+ static QBasicAtomicInt statxTested = Q_BASIC_ATOMIC_INITIALIZER(0);
+#endif
+
+ if (statxTested.load() == -1)
+ return -ENOSYS;
+
+ unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
+ int ret = statx(fd, pathname, flags, mask, statxBuffer);
+ if (ret == -1 && errno == ENOSYS) {
+ statxTested.store(-1);
+ return -ENOSYS;
+ }
+ statxTested.store(1);
+ return ret == -1 ? -errno : 0;
+}
+
+static int qt_statx(const char *pathname, struct statx *statxBuffer)
+{
+ return qt_real_statx(AT_FDCWD, pathname, 0, statxBuffer);
+}
+
+static int qt_lstatx(const char *pathname, struct statx *statxBuffer)
+{
+ return qt_real_statx(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, statxBuffer);
+}
+
+static int qt_fstatx(int fd, struct statx *statxBuffer)
+{
+ return qt_real_statx(fd, "", AT_EMPTY_PATH, statxBuffer);
+}
+
+inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &statxBuffer)
+{
+ // Permissions
+ if (statxBuffer.stx_mode & S_IRUSR)
+ entryFlags |= QFileSystemMetaData::OwnerReadPermission;
+ if (statxBuffer.stx_mode & S_IWUSR)
+ entryFlags |= QFileSystemMetaData::OwnerWritePermission;
+ if (statxBuffer.stx_mode & S_IXUSR)
+ entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
+
+ if (statxBuffer.stx_mode & S_IRGRP)
+ entryFlags |= QFileSystemMetaData::GroupReadPermission;
+ if (statxBuffer.stx_mode & S_IWGRP)
+ entryFlags |= QFileSystemMetaData::GroupWritePermission;
+ if (statxBuffer.stx_mode & S_IXGRP)
+ entryFlags |= QFileSystemMetaData::GroupExecutePermission;
+
+ if (statxBuffer.stx_mode & S_IROTH)
+ entryFlags |= QFileSystemMetaData::OtherReadPermission;
+ if (statxBuffer.stx_mode & S_IWOTH)
+ entryFlags |= QFileSystemMetaData::OtherWritePermission;
+ if (statxBuffer.stx_mode & S_IXOTH)
+ entryFlags |= QFileSystemMetaData::OtherExecutePermission;
+
+ // Type
+ if (S_ISLNK(statxBuffer.stx_mode))
+ entryFlags |= QFileSystemMetaData::LinkType;
+ if ((statxBuffer.stx_mode & S_IFMT) == S_IFREG)
+ entryFlags |= QFileSystemMetaData::FileType;
+ else if ((statxBuffer.stx_mode & S_IFMT) == S_IFDIR)
+ entryFlags |= QFileSystemMetaData::DirectoryType;
+ else if ((statxBuffer.stx_mode & S_IFMT) != S_IFBLK)
+ entryFlags |= QFileSystemMetaData::SequentialType;
+
+ // Attributes
+ entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
+ if (statxBuffer.stx_nlink == 0)
+ entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
+ size_ = qint64(statxBuffer.stx_size);
+
+ // Times
+ auto toMSecs = [](struct statx_timestamp ts)
+ {
+ return qint64(ts.tv_sec) * 1000 + (ts.tv_nsec / 1000000);
+ };
+ accessTime_ = toMSecs(statxBuffer.stx_atime);
+ metadataChangeTime_ = toMSecs(statxBuffer.stx_ctime);
+ modificationTime_ = toMSecs(statxBuffer.stx_mtime);
+ if (statxBuffer.stx_mask & STATX_BTIME)
+ birthTime_ = toMSecs(statxBuffer.stx_btime);
+ else
+ birthTime_ = 0;
+
+ userId_ = statxBuffer.stx_uid;
+ groupId_ = statxBuffer.stx_gid;
+}
+#else
+static int qt_statx(const char *, struct statx *)
+{ return -ENOSYS; }
+
+static int qt_lstatx(const char *, struct statx *)
+{ return -ENOSYS; }
+
+static int qt_fstatx(int, struct statx *)
+{ return -ENOSYS; }
+
+inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &)
+{ }
+#endif
+
//static
-QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
+bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data)
{
-#if defined(__GLIBC__) && !defined(PATH_MAX)
-#define PATH_CHUNK_SIZE 256
- char *s = 0;
- int len = -1;
- int size = PATH_CHUNK_SIZE;
-
- while (1) {
- s = (char *) ::realloc(s, size);
- Q_CHECK_PTR(s);
- len = ::readlink(link.nativeFilePath().constData(), s, size);
- if (len < 0) {
- ::free(s);
- break;
+ data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
+
+ union {
+ struct statx statxBuffer;
+ QT_STATBUF statBuffer;
+ };
+
+ int ret = qt_fstatx(fd, &statxBuffer);
+ if (ret != -ENOSYS) {
+ if (ret == 0) {
+ data.fillFromStatxBuf(statxBuffer);
+ return true;
}
- if (len < size) {
- break;
+ return false;
+ }
+
+ if (QT_FSTAT(fd, &statBuffer) == 0) {
+ data.fillFromStatBuf(statBuffer);
+ return true;
+ }
+
+ return false;
+}
+
+#if defined(_DEXTRA_FIRST)
+static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32)
+{
+ statBuf64->st_mode = statBuf32.st_mode;
+ statBuf64->st_size = statBuf32.st_size;
+#if _POSIX_VERSION >= 200809L
+ statBuf64->st_ctim = statBuf32.st_ctim;
+ statBuf64->st_mtim = statBuf32.st_mtim;
+ statBuf64->st_atim = statBuf32.st_atim;
+#else
+ statBuf64->st_ctime = statBuf32.st_ctime;
+ statBuf64->st_mtime = statBuf32.st_mtime;
+ statBuf64->st_atime = statBuf32.st_atime;
+#endif
+ statBuf64->st_uid = statBuf32.st_uid;
+ statBuf64->st_gid = statBuf32.st_gid;
+}
+#endif
+
+void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
+{
+ // Permissions
+ if (statBuffer.st_mode & S_IRUSR)
+ entryFlags |= QFileSystemMetaData::OwnerReadPermission;
+ if (statBuffer.st_mode & S_IWUSR)
+ entryFlags |= QFileSystemMetaData::OwnerWritePermission;
+ if (statBuffer.st_mode & S_IXUSR)
+ entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
+
+ if (statBuffer.st_mode & S_IRGRP)
+ entryFlags |= QFileSystemMetaData::GroupReadPermission;
+ if (statBuffer.st_mode & S_IWGRP)
+ entryFlags |= QFileSystemMetaData::GroupWritePermission;
+ if (statBuffer.st_mode & S_IXGRP)
+ entryFlags |= QFileSystemMetaData::GroupExecutePermission;
+
+ if (statBuffer.st_mode & S_IROTH)
+ entryFlags |= QFileSystemMetaData::OtherReadPermission;
+ if (statBuffer.st_mode & S_IWOTH)
+ entryFlags |= QFileSystemMetaData::OtherWritePermission;
+ if (statBuffer.st_mode & S_IXOTH)
+ entryFlags |= QFileSystemMetaData::OtherExecutePermission;
+
+ // Type
+ if ((statBuffer.st_mode & S_IFMT) == S_IFREG)
+ entryFlags |= QFileSystemMetaData::FileType;
+ else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR)
+ entryFlags |= QFileSystemMetaData::DirectoryType;
+ else if ((statBuffer.st_mode & S_IFMT) != S_IFBLK)
+ entryFlags |= QFileSystemMetaData::SequentialType;
+
+ // Attributes
+ entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
+ if (statBuffer.st_nlink == 0)
+ entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
+ size_ = statBuffer.st_size;
+#ifdef UF_HIDDEN
+ if (statBuffer.st_flags & UF_HIDDEN) {
+ entryFlags |= QFileSystemMetaData::HiddenAttribute;
+ knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
+ }
+#endif
+
+ // Times
+ accessTime_ = GetFileTimes::atime(statBuffer, 0);
+ birthTime_ = GetFileTimes::birthtime(statBuffer, 0);
+ metadataChangeTime_ = GetFileTimes::ctime(statBuffer, 0);
+ modificationTime_ = GetFileTimes::mtime(statBuffer, 0);
+
+ userId_ = statBuffer.st_uid;
+ groupId_ = statBuffer.st_gid;
+}
+
+void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
+{
+#if defined(_DEXTRA_FIRST)
+ knownFlagsMask = 0;
+ entryFlags = 0;
+ for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
+ extra = _DEXTRA_NEXT(extra)) {
+ if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
+
+ const struct dirent_extra_stat * const extra_stat =
+ reinterpret_cast<struct dirent_extra_stat *>(extra);
+
+ // Remember whether this was a link or not, this saves an lstat() call later.
+ if (extra->d_type == _DTYPE_LSTAT) {
+ knownFlagsMask |= QFileSystemMetaData::LinkType;
+ if (S_ISLNK(extra_stat->d_stat.st_mode))
+ entryFlags |= QFileSystemMetaData::LinkType;
+ }
+
+ // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data,
+ // as we need the stat() information there, not the lstat() information.
+ // In this case, don't use the extra information.
+ // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for
+ // symlinks, we always incur the cost of an extra stat() call later.
+ if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
+ continue;
+
+#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
+ // Even with large file support, d_stat is always of type struct stat, not struct stat64,
+ // so it needs to be converted
+ struct stat64 statBuf;
+ fillStat64fromStat32(&statBuf, extra_stat->d_stat);
+ fillFromStatBuf(statBuf);
+#else
+ fillFromStatBuf(extra_stat->d_stat);
+#endif
+ knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
+ if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
+ knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
+ entryFlags |= QFileSystemMetaData::ExistsAttribute;
+ }
}
- size *= 2;
+ }
+#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
+ // BSD4 includes OS X and iOS
+
+ // ### This will clear all entry flags and knownFlagsMask
+ switch (entry.d_type)
+ {
+ case DT_DIR:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_BLK:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::AliasType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_CHR:
+ case DT_FIFO:
+ case DT_SOCK:
+ // ### System attribute
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::AliasType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_LNK:
+ knownFlagsMask = QFileSystemMetaData::LinkType;
+ entryFlags = QFileSystemMetaData::LinkType;
+ break;
+
+ case DT_REG:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::FileType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_UNKNOWN:
+ default:
+ clear();
}
#else
- char s[PATH_MAX+1];
- int len = readlink(link.nativeFilePath().constData(), s, PATH_MAX);
+ Q_UNUSED(entry)
#endif
- if (len > 0) {
+}
+
+//static
+QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
+{
+ if (Q_UNLIKELY(link.isEmpty()))
+ return emptyFileEntryWarning(), link;
+
+ QByteArray s = qt_readlink(link.nativeFilePath().constData());
+ if (s.length() > 0) {
QString ret;
if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
fillMetaData(link, data, QFileSystemMetaData::DirectoryType);
@@ -181,19 +661,10 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
ret += QLatin1Char('/');
}
- s[len] = '\0';
- ret += QFile::decodeName(QByteArray(s));
-#if defined(__GLIBC__) && !defined(PATH_MAX)
- ::free(s);
-#endif
+ ret += QFile::decodeName(s);
- if (!ret.startsWith(QLatin1Char('/'))) {
- const QString linkPath = link.path();
- if (linkPath.startsWith(QLatin1Char('/')))
- ret.prepend(linkPath + QLatin1Char('/'));
- else
- ret.prepend(QDir::currentPath() + QLatin1Char('/') + linkPath + QLatin1Char('/'));
- }
+ if (!ret.startsWith(QLatin1Char('/')))
+ ret.prepend(absoluteName(link).path() + QLatin1Char('/'));
ret = QDir::cleanPath(ret);
if (ret.size() > 1 && ret.endsWith(QLatin1Char('/')))
ret.chop(1);
@@ -235,7 +706,9 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{
- if (entry.isEmpty() || entry.isRoot())
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), entry;
+ if (entry.isRoot())
return entry;
#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
@@ -302,6 +775,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), entry;
if (entry.isAbsolute() && entry.isClean())
return entry;
@@ -335,6 +810,9 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
//static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), QByteArray();
+
QT_STATBUF statResult;
if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
if (errno != ENOENT)
@@ -442,56 +920,105 @@ QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry)
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
#if defined(Q_OS_DARWIN)
if (what & QFileSystemMetaData::BundleType) {
if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
what |= QFileSystemMetaData::DirectoryType;
}
+#endif
+#ifdef UF_HIDDEN
if (what & QFileSystemMetaData::HiddenAttribute) {
// OS X >= 10.5: st_flags & UF_HIDDEN
what |= QFileSystemMetaData::PosixStatFlags;
}
#endif // defined(Q_OS_DARWIN)
+ // if we're asking for any of the stat(2) flags, then we're getting them all
if (what & QFileSystemMetaData::PosixStatFlags)
what |= QFileSystemMetaData::PosixStatFlags;
- if (what & QFileSystemMetaData::ExistsAttribute) {
- // FIXME: Would other queries being performed provide this bit?
- what |= QFileSystemMetaData::PosixStatFlags;
- }
-
data.entryFlags &= ~what;
const QByteArray nativeFilePath = entry.nativeFilePath();
- bool entryExists = true; // innocent until proven otherwise
-
- QT_STATBUF statBuffer;
- bool statBufferValid = false;
+ int entryErrno = 0; // innocent until proven otherwise
+
+ // first, we may try lstat(2). Possible outcomes:
+ // - success and is a symlink: filesystem entry exists, but we need stat(2)
+ // -> statResult = -1;
+ // - success and is not a symlink: filesystem entry exists and we're done
+ // -> statResult = 0
+ // - failure: really non-existent filesystem entry
+ // -> entryExists = false; statResult = 0;
+ // both stat(2) and lstat(2) may generate a number of different errno
+ // conditions, but of those, the only ones that could happen and the
+ // entry still exist are EACCES, EFAULT, ENOMEM and EOVERFLOW. If we get
+ // EACCES or ENOMEM, then we have no choice on how to proceed, so we may
+ // as well conclude it doesn't exist; EFAULT can't happen and EOVERFLOW
+ // shouldn't happen because we build in _LARGEFIE64.
+ union {
+ QT_STATBUF statBuffer;
+ struct statx statxBuffer;
+ };
+ int statResult = -1;
if (what & QFileSystemMetaData::LinkType) {
- if (QT_LSTAT(nativeFilePath, &statBuffer) == 0) {
- if (S_ISLNK(statBuffer.st_mode)) {
+ mode_t mode = 0;
+ statResult = qt_lstatx(nativeFilePath, &statxBuffer);
+ if (statResult == -ENOSYS) {
+ // use lstst(2)
+ statResult = QT_LSTAT(nativeFilePath, &statBuffer);
+ if (statResult == 0)
+ mode = statBuffer.st_mode;
+ } else if (statResult == 0) {
+ statResult = 1; // record it was statx(2) that succeeded
+ mode = statxBuffer.stx_mode;
+ }
+
+ if (statResult >= 0) {
+ if (S_ISLNK(mode)) {
+ // it's a symlink, we don't know if the file "exists"
data.entryFlags |= QFileSystemMetaData::LinkType;
+ statResult = -1; // force stat(2) below
} else {
- statBufferValid = true;
- data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ // it's a reagular file and it exists
+ if (statResult)
+ data.fillFromStatxBuf(statxBuffer);
+ else
+ data.fillFromStatBuf(statBuffer);
+ data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags
+ | QFileSystemMetaData::ExistsAttribute;
+ data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
}
} else {
- entryExists = false;
+ // it doesn't exist
+ entryErrno = errno;
+ data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
}
data.knownFlagsMask |= QFileSystemMetaData::LinkType;
}
- if (statBufferValid || (what & QFileSystemMetaData::PosixStatFlags)) {
- if (entryExists && !statBufferValid)
- statBufferValid = (QT_STAT(nativeFilePath, &statBuffer) == 0);
+ // second, we try a regular stat(2)
+ if (statResult == -1 && (what & QFileSystemMetaData::PosixStatFlags)) {
+ if (entryErrno == 0 && statResult == -1) {
+ data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ statResult = qt_statx(nativeFilePath, &statxBuffer);
+ if (statResult == -ENOSYS) {
+ // use stat(2)
+ statResult = QT_STAT(nativeFilePath, &statBuffer);
+ if (statResult == 0)
+ data.fillFromStatBuf(statBuffer);
+ } else if (statResult == 0) {
+ data.fillFromStatxBuf(statxBuffer);
+ }
+ }
- if (statBufferValid)
- data.fillFromStatBuf(statBuffer);
- else {
- entryExists = false;
- data.creationTime_ = 0;
+ if (statResult != 0) {
+ entryErrno = errno;
+ data.birthTime_ = 0;
+ data.metadataChangeTime_ = 0;
data.modificationTime_ = 0;
data.accessTime_ = 0;
data.size_ = 0;
@@ -504,60 +1031,135 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
| QFileSystemMetaData::ExistsAttribute;
}
+ // third, we try access(2)
+ if (what & (QFileSystemMetaData::UserPermissions | QFileSystemMetaData::ExistsAttribute)) {
+ // calculate user permissions
+ auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag, int mode) {
+ if (entryErrno != 0 || (what & flag) == 0)
+ return;
+ if (QT_ACCESS(nativeFilePath, mode) == 0) {
+ // access ok (and file exists)
+ data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
+ } else if (errno != EACCES && errno != EROFS) {
+ entryErrno = errno;
+ }
+ };
+
+ checkAccess(QFileSystemMetaData::UserReadPermission, R_OK);
+ checkAccess(QFileSystemMetaData::UserWritePermission, W_OK);
+ checkAccess(QFileSystemMetaData::UserExecutePermission, X_OK);
+
+ // if we still haven't found out if the file exists, try F_OK
+ if (entryErrno == 0 && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
+ if (QT_ACCESS(nativeFilePath, F_OK) == -1)
+ entryErrno = errno;
+ else
+ data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
+ }
+
+ data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
+ QFileSystemMetaData::ExistsAttribute;
+ }
+
#if defined(Q_OS_DARWIN)
- if (what & QFileSystemMetaData::AliasType)
- {
- if (entryExists && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey))
+ if (what & QFileSystemMetaData::AliasType) {
+ if (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey))
data.entryFlags |= QFileSystemMetaData::AliasType;
data.knownFlagsMask |= QFileSystemMetaData::AliasType;
}
-#endif
- if (what & QFileSystemMetaData::UserPermissions) {
- // calculate user permissions
+ if (what & QFileSystemMetaData::BundleType) {
+ if (entryErrno == 0 && isPackage(data, entry))
+ data.entryFlags |= QFileSystemMetaData::BundleType;
- if (entryExists) {
- if (what & QFileSystemMetaData::UserReadPermission) {
- if (QT_ACCESS(nativeFilePath, R_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserReadPermission;
- }
- if (what & QFileSystemMetaData::UserWritePermission) {
- if (QT_ACCESS(nativeFilePath, W_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserWritePermission;
- }
- if (what & QFileSystemMetaData::UserExecutePermission) {
- if (QT_ACCESS(nativeFilePath, X_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
- }
- }
- data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions);
+ data.knownFlagsMask |= QFileSystemMetaData::BundleType;
}
+#endif
if (what & QFileSystemMetaData::HiddenAttribute
&& !data.isHidden()) {
QString fileName = entry.fileName();
if ((fileName.size() > 0 && fileName.at(0) == QLatin1Char('.'))
#if defined(Q_OS_DARWIN)
- || (entryExists && hasResourcePropertyFlag(data, entry, kCFURLIsHiddenKey))
+ || (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsHiddenKey))
#endif
)
data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
}
-#if defined(Q_OS_DARWIN)
- if (what & QFileSystemMetaData::BundleType) {
- if (entryExists && isPackage(data, entry))
- data.entryFlags |= QFileSystemMetaData::BundleType;
+ if (entryErrno != 0) {
+ what &= ~QFileSystemMetaData::LinkType; // don't clear link: could be broken symlink
+ data.clearFlags(what);
+ return false;
+ }
+ return true;
+}
- data.knownFlagsMask |= QFileSystemMetaData::BundleType;
+// static
+bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaData &knownData)
+{
+ QT_STATBUF statBuffer;
+ if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
+ knownData.isFile()) {
+ statBuffer.st_size = knownData.size();
+ statBuffer.st_mode = S_IFREG;
+ } else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
+ knownData.isDirectory()) {
+ return false; // fcopyfile(3) returns success on directories
+ } else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
+ return false;
+ } else if (!S_ISREG((statBuffer.st_mode))) {
+ // not a regular file, let QFile do the copy
+ return false;
}
-#endif
- if (!entryExists) {
- data.clearFlags(what);
+
+#if defined(Q_OS_LINUX)
+ if (statBuffer.st_size == 0) {
+ // empty file? we're done.
+ return true;
+ }
+
+ // first, try FICLONE (only works on regular files and only on certain fs)
+ if (::ioctl(dstfd, FICLONE, srcfd) == 0)
+ return true;
+
+ // Second, try sendfile (it can send to some special types too).
+ // sendfile(2) is limited in the kernel to 2G - 4k
+ auto sendfileSize = [](QT_OFF_T size) { return size_t(qMin<qint64>(0x7ffff000, size)); };
+
+ ssize_t n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
+ if (n == -1) {
+ // if we got an error here, give up and try at an upper layer
return false;
}
- return data.hasFlags(what);
+
+ statBuffer.st_size -= n;
+ while (statBuffer.st_size) {
+ n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
+ if (n == 0) {
+ // uh oh, this is probably a real error (like ENOSPC), but we have
+ // no way to notify QFile of partial success, so just erase any work
+ // done (hopefully we won't get any errors, because there's nothing
+ // we can do about them)
+ n = ftruncate(dstfd, 0);
+ n = lseek(srcfd, 0, SEEK_SET);
+ n = lseek(dstfd, 0, SEEK_SET);
+ return false;
+ }
+ if (n == 0)
+ return true;
+ statBuffer.st_size -= n;
+ }
+
+ return true;
+#elif defined(Q_OS_DARWIN)
+ // try fcopyfile
+ return fcopyfile(srcfd, dstfd, nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0;
+#else
+ Q_UNUSED(dstfd);
+ return false;
+#endif
}
// Note: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
@@ -598,6 +1200,8 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{
QString dirName = entry.filePath();
+ if (Q_UNLIKELY(dirName.isEmpty()))
+ return emptyFileEntryWarning(), false;
// Darwin doesn't support trailing /'s, so remove for everyone
while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/')))
@@ -616,6 +1220,9 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
//static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
if (removeEmptyParents) {
QString dirName = QDir::cleanPath(entry.filePath());
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
@@ -639,6 +1246,8 @@ bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool remo
//static
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+ if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -648,8 +1257,18 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000)
+ if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
+ if (::clonefile(source.nativeFilePath().constData(),
+ target.nativeFilePath().constData(), 0) == 0)
+ return true;
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+#else
Q_UNUSED(source);
Q_UNUSED(target);
+#endif
error = QSystemError(ENOSYS, QSystemError::StandardLibraryError); //Function not implemented
return false;
}
@@ -657,6 +1276,83 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst
//static
bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+ QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
+ QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
+ if (Q_UNLIKELY(srcPath.isEmpty() || tgtPath.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
+#if defined(RENAME_NOREPLACE) && (QT_CONFIG(renameat2) || defined(SYS_renameat2))
+ if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
+ return true;
+
+ // If we're using syscall(), check for ENOSYS;
+ // if renameat2 came from libc, we don't accept ENOSYS.
+ // We can also get EINVAL for some non-local filesystems.
+ if ((QT_CONFIG(renameat2) || errno != ENOSYS) && errno != EINVAL) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+#endif
+#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
+ if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
+ if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
+ return true;
+ if (errno != ENOTSUP) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+ }
+#endif
+
+ if (SupportsHardlinking && ::link(srcPath, tgtPath) == 0) {
+ if (::unlink(srcPath) == 0)
+ return true;
+
+ // if we managed to link but can't unlink the source, it's likely
+ // it's in a directory we don't have write access to; fail the
+ // renaming instead
+ int savedErrno = errno;
+
+ // this could fail too, but there's nothing we can do about it now
+ ::unlink(tgtPath);
+
+ error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
+ return false;
+ } else if (!SupportsHardlinking) {
+ // man 2 link on Linux has:
+ // EPERM The filesystem containing oldpath and newpath does not
+ // support the creation of hard links.
+ errno = EPERM;
+ }
+
+ switch (errno) {
+ case EACCES:
+ case EEXIST:
+ case ENAMETOOLONG:
+ case ENOENT:
+ case ENOTDIR:
+ case EROFS:
+ case EXDEV:
+ // accept the error from link(2) (especially EEXIST) and don't retry
+ break;
+
+ default:
+ // fall back to rename()
+ // ### Race condition. If a file is moved in after this, it /will/ be
+ // overwritten.
+ if (::rename(srcPath, tgtPath) == 0)
+ return true;
+ }
+
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+}
+
+//static
+bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+{
+ if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -666,6 +1362,8 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (unlink(entry.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -700,8 +1398,10 @@ static mode_t toMode_t(QFile::Permissions permissions)
//static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data)
{
- mode_t mode = toMode_t(permissions);
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+ mode_t mode = toMode_t(permissions);
bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
if (success && data) {
data->entryFlags &= ~QFileSystemMetaData::Permissions;
@@ -729,6 +1429,71 @@ bool QFileSystemEngine::setPermissions(int fd, QFile::Permissions permissions, Q
return success;
}
+//static
+bool QFileSystemEngine::setFileTime(int fd, const QDateTime &newDate, QAbstractFileEngine::FileTime time, QSystemError &error)
+{
+ if (!newDate.isValid() || time == QAbstractFileEngine::BirthTime ||
+ time == QAbstractFileEngine::MetadataChangeTime) {
+ error = QSystemError(EINVAL, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+#if QT_CONFIG(futimens)
+ struct timespec ts[2];
+
+ ts[0].tv_sec = ts[1].tv_sec = 0;
+ ts[0].tv_nsec = ts[1].tv_nsec = UTIME_OMIT;
+
+ const qint64 msecs = newDate.toMSecsSinceEpoch();
+
+ if (time == QAbstractFileEngine::AccessTime) {
+ ts[0].tv_sec = msecs / 1000;
+ ts[0].tv_nsec = (msecs % 1000) * 1000000;
+ } else if (time == QAbstractFileEngine::ModificationTime) {
+ ts[1].tv_sec = msecs / 1000;
+ ts[1].tv_nsec = (msecs % 1000) * 1000000;
+ }
+
+ if (futimens(fd, ts) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ return true;
+#elif QT_CONFIG(futimes)
+ struct timeval tv[2];
+ QT_STATBUF st;
+
+ if (QT_FSTAT(fd, &st) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ GetFileTimes::get(&st, &tv[0], &tv[1]);
+
+ const qint64 msecs = newDate.toMSecsSinceEpoch();
+
+ if (time == QAbstractFileEngine::AccessTime) {
+ tv[0].tv_sec = msecs / 1000;
+ tv[0].tv_usec = (msecs % 1000) * 1000;
+ } else if (time == QAbstractFileEngine::ModificationTime) {
+ tv[1].tv_sec = msecs / 1000;
+ tv[1].tv_usec = (msecs % 1000) * 1000;
+ }
+
+ if (futimes(fd, tv) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ return true;
+#else
+ Q_UNUSED(fd);
+ error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
+ return false;
+#endif
+}
+
QString QFileSystemEngine::homePath()
{
QString home = QFile::decodeName(qgetenv("HOME"));
@@ -749,14 +1514,13 @@ QString QFileSystemEngine::tempPath()
#else
QString temp = QFile::decodeName(qgetenv("TMPDIR"));
if (temp.isEmpty()) {
+ if (false) {
#if defined(Q_OS_DARWIN) && !defined(QT_BOOTSTRAPPED)
- if (NSString *nsPath = NSTemporaryDirectory()) {
+ } else if (NSString *nsPath = NSTemporaryDirectory()) {
temp = QString::fromCFString((CFStringRef)nsPath);
- } else {
-#else
- {
#endif
- temp = QLatin1String("/tmp");
+ } else {
+ temp = QLatin1String(_PATH_TMP);
}
}
return QDir::cleanPath(temp);
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index c9a56a81cc..fadc058110 100755
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -48,7 +48,6 @@
#include "qfile.h"
#include "qdir.h"
-#include "private/qmutexpool_p.h"
#include "qvarlengtharray.h"
#include "qdatetime.h"
#include "qt_windows.h"
@@ -60,6 +59,7 @@
#include <objbase.h>
#ifndef Q_OS_WINRT
# include <shlobj.h>
+# include <lm.h>
# include <accctrl.h>
#endif
#include <initguid.h>
@@ -147,49 +147,31 @@ typedef struct _REPARSE_DATA_BUFFER {
# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
-QT_BEGIN_NAMESPACE
-
-Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
-
-#if defined(Q_OS_WINRT)
-// As none of the functions we try to resolve do exist on WinRT we
-// avoid library loading on WinRT in general to shorten everything
-// up a little bit.
+#if defined(Q_OS_WINRT) || defined(QT_BOOTSTRAPPED)
# define QT_FEATURE_fslibs -1
#else
-# define QT_FEATURE_fslibs QT_FEATURE_library
+# define QT_FEATURE_fslibs 1
#endif // Q_OS_WINRT
#if QT_CONFIG(fslibs)
-QT_BEGIN_INCLUDE_NAMESPACE
-typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*);
-static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0;
-typedef BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE);
-static PtrLookupAccountSidW ptrLookupAccountSidW = 0;
-typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID);
-static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0;
-typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK);
-static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0;
-typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
-static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0;
-QT_END_INCLUDE_NAMESPACE
-
+#include <aclapi.h>
+#include <userenv.h>
static TRUSTEE_W currentUserTrusteeW;
static TRUSTEE_W worldTrusteeW;
static PSID currentUserSID = 0;
static PSID worldSID = 0;
static HANDLE currentUserImpersonatedToken = nullptr;
-/*
- Deletes the allocated SIDs during global static cleanup
-*/
-class SidCleanup
+QT_BEGIN_NAMESPACE
+
+namespace {
+struct GlobalSid
{
-public:
- ~SidCleanup();
+ GlobalSid();
+ ~GlobalSid();
};
-SidCleanup::~SidCleanup()
+GlobalSid::~GlobalSid()
{
free(currentUserSID);
currentUserSID = 0;
@@ -206,39 +188,13 @@ SidCleanup::~SidCleanup()
}
}
-Q_GLOBAL_STATIC(SidCleanup, initSidCleanup)
-
-static void resolveLibs()
+GlobalSid::GlobalSid()
{
- static bool triedResolve = false;
- if (!triedResolve) {
- // need to resolve the security info functions
-
- // protect initialization
-#ifndef QT_NO_THREAD
- QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
- // check triedResolve again, since another thread may have already
- // done the initialization
- if (triedResolve) {
- // another thread did initialize the security function pointers,
- // so we shouldn't do it again.
- return;
- }
-#endif
-
- triedResolve = true;
- HINSTANCE advapiHnd = QSystemLibrary::load(L"advapi32");
- if (advapiHnd) {
- ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW");
- ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW");
- ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW");
- ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW");
- }
- if (ptrBuildTrusteeWithSidW) {
+ {
+ {
// Create TRUSTEE for current user
HANDLE hnd = ::GetCurrentProcess();
HANDLE token = 0;
- initSidCleanup();
if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
DWORD retsize = 0;
// GetTokenInformation requires a buffer big enough for the TOKEN_USER struct and
@@ -253,7 +209,7 @@ static void resolveLibs()
DWORD sidLen = ::GetLengthSid(tokenSid);
currentUserSID = reinterpret_cast<PSID>(malloc(sidLen));
if (::CopySid(sidLen, currentUserSID, tokenSid))
- ptrBuildTrusteeWithSidW(&currentUserTrusteeW, currentUserSID);
+ BuildTrusteeWithSid(&currentUserTrusteeW, currentUserSID);
}
free(tokenBuffer);
}
@@ -266,54 +222,62 @@ static void resolveLibs()
::CloseHandle(token);
}
- typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
- PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid");
- if (ptrAllocateAndInitializeSid) {
+ {
// Create TRUSTEE for Everyone (World)
SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
- if (ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID))
- ptrBuildTrusteeWithSidW(&worldTrusteeW, worldSID);
+ if (AllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID))
+ BuildTrusteeWithSid(&worldTrusteeW, worldSID);
}
}
- HINSTANCE userenvHnd = QSystemLibrary::load(L"userenv");
- if (userenvHnd)
- ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
}
}
+
+Q_GLOBAL_STATIC(GlobalSid, initGlobalSid)
+
+QT_END_NAMESPACE
+
+} // anonymous namespace
#endif // QT_CONFIG(fslibs)
-typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD);
-static PtrNetShareEnum ptrNetShareEnum = 0;
-typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID);
-static PtrNetApiBufferFree ptrNetApiBufferFree = 0;
-typedef struct _SHARE_INFO_1 {
- LPWSTR shi1_netname;
- DWORD shi1_type;
- LPWSTR shi1_remark;
-} SHARE_INFO_1;
+QT_BEGIN_NAMESPACE
+Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
-static bool resolveUNCLibs()
+static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
{
- static bool triedResolve = false;
- if (!triedResolve) {
-#ifndef QT_NO_THREAD
- QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
- if (triedResolve) {
- return ptrNetShareEnum && ptrNetApiBufferFree;
- }
-#endif
- triedResolve = true;
-#if !defined(Q_OS_WINRT)
- HINSTANCE hLib = QSystemLibrary::load(L"Netapi32");
- if (hLib) {
- ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum");
- if (ptrNetShareEnum)
- ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree");
- }
-#endif // !Q_OS_WINRT
+ SYSTEMTIME sTime;
+ if (date.timeSpec() == Qt::LocalTime) {
+ SYSTEMTIME lTime;
+ const QDate d = date.date();
+ const QTime t = date.time();
+
+ lTime.wYear = d.year();
+ lTime.wMonth = d.month();
+ lTime.wDay = d.day();
+ lTime.wHour = t.hour();
+ lTime.wMinute = t.minute();
+ lTime.wSecond = t.second();
+ lTime.wMilliseconds = t.msec();
+ lTime.wDayOfWeek = d.dayOfWeek() % 7;
+
+ if (!::TzSpecificLocalTimeToSystemTime(0, &lTime, &sTime))
+ return false;
+ } else {
+ QDateTime utcDate = date.toUTC();
+ const QDate d = utcDate.date();
+ const QTime t = utcDate.time();
+
+ sTime.wYear = d.year();
+ sTime.wMonth = d.month();
+ sTime.wDay = d.day();
+ sTime.wHour = t.hour();
+ sTime.wMinute = t.minute();
+ sTime.wSecond = t.second();
+ sTime.wMilliseconds = t.msec();
+ sTime.wDayOfWeek = d.dayOfWeek() % 7;
}
- return ptrNetShareEnum && ptrNetApiBufferFree;
+
+ return ::SystemTimeToFileTime(&sTime, fileTime);
}
static QString readSymLink(const QFileSystemEntry &link)
@@ -351,7 +315,7 @@ static QString readSymLink(const QFileSystemEntry &link)
CloseHandle(handle);
#if QT_CONFIG(fslibs)
- resolveLibs();
+ initGlobalSid();
QRegExp matchVolName(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), Qt::CaseInsensitive);
if (matchVolName.indexIn(result) == 0) {
DWORD len;
@@ -448,32 +412,35 @@ static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
{
- if (resolveUNCLibs()) {
- SHARE_INFO_1 *BufPtr, *p;
- DWORD res;
- DWORD er = 0, tr = 0, resume = 0, i;
- do {
- res = ptrNetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
- if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
- p = BufPtr;
- for (i = 1; i <= er; ++i) {
- if (list && p->shi1_type == 0)
- list->append(QString::fromWCharArray(p->shi1_netname));
- p++;
- }
+ DWORD res = ERROR_NOT_SUPPORTED;
+#ifndef Q_OS_WINRT
+ SHARE_INFO_1 *BufPtr, *p;
+ DWORD er = 0, tr = 0, resume = 0, i;
+ do {
+ res = NetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
+ if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
+ p = BufPtr;
+ for (i = 1; i <= er; ++i) {
+ if (list && p->shi1_type == 0)
+ list->append(QString::fromWCharArray(p->shi1_netname));
+ p++;
}
- ptrNetApiBufferFree(BufPtr);
- } while (res == ERROR_MORE_DATA);
- return res == ERROR_SUCCESS;
- }
- return false;
+ }
+ NetApiBufferFree(BufPtr);
+ } while (res == ERROR_MORE_DATA);
+#else
+ Q_UNUSED(server);
+ Q_UNUSED(list);
+#endif
+ return res == ERROR_SUCCESS;
}
void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
{
data.size_ = 0;
data.fileAttribute_ = 0;
- data.creationTime_ = FILETIME();
+ data.birthTime_ = FILETIME();
+ data.changeTime_ = FILETIME();
data.lastAccessTime_ = FILETIME();
data.lastWriteTime_ = FILETIME();
}
@@ -662,36 +629,72 @@ QByteArray QFileSystemEngine::id(HANDLE fHandle)
}
//static
+bool QFileSystemEngine::setFileTime(HANDLE fHandle, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime time, QSystemError &error)
+{
+ FILETIME fTime;
+ FILETIME *pLastWrite = NULL;
+ FILETIME *pLastAccess = NULL;
+ FILETIME *pCreationTime = NULL;
+
+ switch (time) {
+ case QAbstractFileEngine::ModificationTime:
+ pLastWrite = &fTime;
+ break;
+
+ case QAbstractFileEngine::AccessTime:
+ pLastAccess = &fTime;
+ break;
+
+ case QAbstractFileEngine::BirthTime:
+ pCreationTime = &fTime;
+ break;
+
+ default:
+ error = QSystemError(ERROR_INVALID_PARAMETER, QSystemError::NativeError);
+ return false;
+ }
+
+ if (!toFileTime(newDate, &fTime))
+ return false;
+
+ if (!::SetFileTime(fHandle, pCreationTime, pLastAccess, pLastWrite)) {
+ error = QSystemError(::GetLastError(), QSystemError::NativeError);
+ return false;
+ }
+ return true;
+}
+
QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own)
{
QString name;
#if QT_CONFIG(fslibs)
extern int qt_ntfs_permission_lookup;
- if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
- resolveLibs();
- if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
+ if (qt_ntfs_permission_lookup > 0) {
+ initGlobalSid();
+ {
PSID pOwner = 0;
PSECURITY_DESCRIPTOR pSD;
- if (ptrGetNamedSecurityInfoW((wchar_t*)entry.nativeFilePath().utf16(), SE_FILE_OBJECT,
- own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
- own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::OwnerGroup ? &pOwner : 0,
- 0, 0, &pSD) == ERROR_SUCCESS) {
+ if (GetNamedSecurityInfo(reinterpret_cast<const wchar_t*>(entry.nativeFilePath().utf16()), SE_FILE_OBJECT,
+ own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
+ own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::OwnerGroup ? &pOwner : 0,
+ 0, 0, &pSD) == ERROR_SUCCESS) {
DWORD lowner = 64;
DWORD ldomain = 64;
QVarLengthArray<wchar_t, 64> owner(lowner);
QVarLengthArray<wchar_t, 64> domain(ldomain);
SID_NAME_USE use = SidTypeUnknown;
// First call, to determine size of the strings (with '\0').
- if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
- (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) {
+ if (!LookupAccountSid(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
+ domain.data(), &ldomain, &use)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (lowner > (DWORD)owner.size())
owner.resize(lowner);
if (ldomain > (DWORD)domain.size())
domain.resize(ldomain);
// Second call, try on resized buf-s
- if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
- (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) {
+ if (!LookupAccountSid(NULL, pOwner, owner.data(), &lowner,
+ domain.data(), &ldomain, &use)) {
lowner = 0;
}
} else {
@@ -716,9 +719,9 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
QFileSystemMetaData::MetaDataFlags what)
{
#if QT_CONFIG(fslibs)
- if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
- resolveLibs();
- if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) {
+ if (qt_ntfs_permission_lookup > 0) {
+ initGlobalSid();
+ {
enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
QString fname = entry.nativeFilePath();
@@ -726,9 +729,9 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
PSID pGroup = 0;
PACL pDacl;
PSECURITY_DESCRIPTOR pSD;
- DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT,
- OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
- &pOwner, &pGroup, &pDacl, 0, &pSD);
+ DWORD res = GetNamedSecurityInfo(reinterpret_cast<const wchar_t*>(fname.utf16()), SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+ &pOwner, &pGroup, &pDacl, 0, &pSD);
if(res == ERROR_SUCCESS) {
ACCESS_MASK access_mask;
TRUSTEE_W trustee;
@@ -767,7 +770,7 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
}
} else { // fallback to GetEffectiveRightsFromAcl
data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
- if (ptrGetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
+ if (GetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
access_mask = ACCESS_MASK(-1);
if (access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::UserReadPermission;
@@ -779,8 +782,8 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
}
if (what & QFileSystemMetaData::OwnerPermissions) { // owner
data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions;
- ptrBuildTrusteeWithSidW(&trustee, pOwner);
- if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
+ BuildTrusteeWithSid(&trustee, pOwner);
+ if (GetEffectiveRightsFromAcl(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
access_mask = (ACCESS_MASK)-1;
if(access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::OwnerReadPermission;
@@ -791,8 +794,8 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
}
if (what & QFileSystemMetaData::GroupPermissions) { // group
data.knownFlagsMask |= QFileSystemMetaData::GroupPermissions;
- ptrBuildTrusteeWithSidW(&trustee, pGroup);
- if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
+ BuildTrusteeWithSid(&trustee, pGroup);
+ if (GetEffectiveRightsFromAcl(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
access_mask = (ACCESS_MASK)-1;
if(access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::GroupReadPermission;
@@ -803,7 +806,7 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
}
if (what & QFileSystemMetaData::OtherPermissions) { // other (world)
data.knownFlagsMask |= QFileSystemMetaData::OtherPermissions;
- if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS)
+ if (GetEffectiveRightsFromAcl(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS)
access_mask = (ACCESS_MASK)-1; // ###
if(access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::OtherReadPermission;
@@ -956,8 +959,10 @@ bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data,
FILE_BASIC_INFO fileBasicInfo;
if (GetFileInformationByHandleEx(fHandle, FileBasicInfo, &fileBasicInfo, sizeof(fileBasicInfo))) {
data.fillFromFileAttribute(fileBasicInfo.FileAttributes);
- data.creationTime_.dwHighDateTime = fileBasicInfo.CreationTime.HighPart;
- data.creationTime_.dwLowDateTime = fileBasicInfo.CreationTime.LowPart;
+ data.birthTime_.dwHighDateTime = fileBasicInfo.CreationTime.HighPart;
+ data.birthTime_.dwLowDateTime = fileBasicInfo.CreationTime.LowPart;
+ data.changeTime_.dwHighDateTime = fileBasicInfo.ChangeTime.HighPart;
+ data.changeTime_.dwLowDateTime = fileBasicInfo.ChangeTime.LowPart;
data.lastAccessTime_.dwHighDateTime = fileBasicInfo.LastAccessTime.HighPart;
data.lastAccessTime_.dwLowDateTime = fileBasicInfo.LastAccessTime.LowPart;
data.lastWriteTime_.dwHighDateTime = fileBasicInfo.LastWriteTime.HighPart;
@@ -1214,19 +1219,19 @@ QString QFileSystemEngine::homePath()
{
QString ret;
#if QT_CONFIG(fslibs)
- resolveLibs();
- if (ptrGetUserProfileDirectoryW) {
+ initGlobalSid();
+ {
HANDLE hnd = ::GetCurrentProcess();
HANDLE token = 0;
BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
if (ok) {
DWORD dwBufferSize = 0;
// First call, to determine size of the strings (with '\0').
- ok = ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize);
+ ok = GetUserProfileDirectory(token, NULL, &dwBufferSize);
if (!ok && dwBufferSize != 0) { // We got the required buffer size
wchar_t *userDirectory = new wchar_t[dwBufferSize];
// Second call, now we can fill the allocated buffer.
- ok = ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize);
+ ok = GetUserProfileDirectory(token, userDirectory, &dwBufferSize);
if (ok)
ret = QString::fromWCharArray(userDirectory);
delete [] userDirectory;
@@ -1372,6 +1377,17 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
}
//static
+bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+{
+ bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()),
+ reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()),
+ MOVEFILE_REPLACE_EXISTING) != 0;
+ if (!ret)
+ error = QSystemError(::GetLastError(), QSystemError::NativeError);
+ return ret;
+}
+
+//static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{
bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
@@ -1403,6 +1419,9 @@ bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Per
static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
{
+ if (time->dwHighDateTime == 0 && time->dwLowDateTime == 0)
+ return QDateTime();
+
SYSTEMTIME sTime;
FileTimeToSystemTime(time, &sTime);
return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
@@ -1410,9 +1429,13 @@ static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
Qt::UTC);
}
-QDateTime QFileSystemMetaData::creationTime() const
+QDateTime QFileSystemMetaData::birthTime() const
+{
+ return fileTimeToQDateTime(&birthTime_);
+}
+QDateTime QFileSystemMetaData::metadataChangeTime() const
{
- return fileTimeToQDateTime(&creationTime_);
+ return fileTimeToQDateTime(&changeTime_);
}
QDateTime QFileSystemMetaData::modificationTime() const
{
diff --git a/src/corelib/io/qfilesystementry.cpp b/src/corelib/io/qfilesystementry.cpp
index cbff17d0f1..3ff90bd0a3 100644
--- a/src/corelib/io/qfilesystementry.cpp
+++ b/src/corelib/io/qfilesystementry.cpp
@@ -331,11 +331,6 @@ bool QFileSystemEntry::isRoot() const
return isRootPath(m_filePath);
}
-bool QFileSystemEntry::isEmpty() const
-{
- return m_filePath.isEmpty() && m_nativeFilePath.isEmpty();
-}
-
// private methods
void QFileSystemEntry::findLastSeparator() const
diff --git a/src/corelib/io/qfilesystementry_p.h b/src/corelib/io/qfilesystementry_p.h
index 700696d09e..251eca553a 100644
--- a/src/corelib/io/qfilesystementry_p.h
+++ b/src/corelib/io/qfilesystementry_p.h
@@ -98,7 +98,10 @@ public:
#endif
bool isRoot() const;
- bool isEmpty() const;
+ bool isEmpty() const
+ {
+ return m_filePath.isEmpty() && m_nativeFilePath.isEmpty();
+ }
void clear()
{
*this = QFileSystemEntry();
diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h
index b09223d656..4d2a5acb9b 100644
--- a/src/corelib/io/qfilesystemmetadata_p.h
+++ b/src/corelib/io/qfilesystemmetadata_p.h
@@ -64,6 +64,10 @@
# endif
#endif
+#ifdef Q_OS_UNIX
+struct statx;
+#endif
+
QT_BEGIN_NAMESPACE
class QFileSystemEngine;
@@ -120,16 +124,22 @@ public:
// Attributes
HiddenAttribute = 0x00100000,
SizeAttribute = 0x00200000, // Note: overlaps with QAbstractFileEngine::LocalDiskFlag
- ExistsAttribute = 0x00400000,
+ ExistsAttribute = 0x00400000, // For historical reasons, indicates existence of data, not the file
+#if defined(Q_OS_WIN)
+ WasDeletedAttribute = 0x0,
+#else
+ WasDeletedAttribute = 0x40000000, // Indicates the file was deleted
+#endif
- Attributes = HiddenAttribute | SizeAttribute | ExistsAttribute,
+ Attributes = HiddenAttribute | SizeAttribute | ExistsAttribute | WasDeletedAttribute,
- // Times
- CreationTime = 0x01000000, // Note: overlaps with QAbstractFileEngine::Refresh
+ // Times - if we know one of them, we know them all
+ AccessTime = 0x02000000,
+ BirthTime = 0x02000000,
+ MetadataChangeTime = 0x02000000,
ModificationTime = 0x02000000,
- AccessTime = 0x04000000,
- Times = CreationTime | ModificationTime | AccessTime,
+ Times = AccessTime | BirthTime | MetadataChangeTime | ModificationTime,
// Owner IDs
UserId = 0x10000000,
@@ -144,6 +154,7 @@ public:
| QFileSystemMetaData::DirectoryType
| QFileSystemMetaData::SequentialType
| QFileSystemMetaData::SizeAttribute
+ | QFileSystemMetaData::WasDeletedAttribute
| QFileSystemMetaData::Times
| QFileSystemMetaData::OwnerIds,
@@ -191,6 +202,7 @@ public:
bool isLegacyLink() const { return (entryFlags & LegacyLinkType); }
bool isSequential() const { return (entryFlags & SequentialType); }
bool isHidden() const { return (entryFlags & HiddenAttribute); }
+ bool wasDeleted() const { return (entryFlags & WasDeletedAttribute); }
#if defined(Q_OS_WIN)
bool isLnkFile() const { return (entryFlags & WinLnkType); }
#else
@@ -201,9 +213,10 @@ public:
QFile::Permissions permissions() const { return QFile::Permissions(Permissions & entryFlags); }
- QDateTime creationTime() const;
- QDateTime modificationTime() const;
QDateTime accessTime() const;
+ QDateTime birthTime() const;
+ QDateTime metadataChangeTime() const;
+ QDateTime modificationTime() const;
QDateTime fileTime(QAbstractFileEngine::FileTime time) const;
uint userId() const;
@@ -211,6 +224,7 @@ public:
uint ownerId(QAbstractFileEngine::FileOwner owner) const;
#ifdef Q_OS_UNIX
+ void fillFromStatxBuf(const struct statx &statBuffer);
void fillFromStatBuf(const QT_STATBUF &statBuffer);
void fillFromDirEnt(const QT_DIRENT &statBuffer);
#endif
@@ -233,14 +247,16 @@ private:
// Platform-specific data goes here:
#if defined(Q_OS_WIN)
DWORD fileAttribute_;
- FILETIME creationTime_;
+ FILETIME birthTime_;
+ FILETIME changeTime_;
FILETIME lastAccessTime_;
FILETIME lastWriteTime_;
#else
// msec precision
- qint64 creationTime_;
- qint64 modificationTime_;
qint64 accessTime_;
+ qint64 birthTime_;
+ qint64 metadataChangeTime_;
+ qint64 modificationTime_;
uint userId_;
uint groupId_;
@@ -268,8 +284,11 @@ inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime tim
case QAbstractFileEngine::AccessTime:
return accessTime();
- case QAbstractFileEngine::CreationTime:
- return creationTime();
+ case QAbstractFileEngine::BirthTime:
+ return birthTime();
+
+ case QAbstractFileEngine::MetadataChangeTime:
+ return metadataChangeTime();
}
return QDateTime();
@@ -277,9 +296,14 @@ inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime tim
#endif
#if defined(Q_OS_UNIX)
-inline QDateTime QFileSystemMetaData::creationTime() const { return QDateTime::fromMSecsSinceEpoch(creationTime_); }
-inline QDateTime QFileSystemMetaData::modificationTime() const { return QDateTime::fromMSecsSinceEpoch(modificationTime_); }
-inline QDateTime QFileSystemMetaData::accessTime() const { return QDateTime::fromMSecsSinceEpoch(accessTime_); }
+inline QDateTime QFileSystemMetaData::birthTime() const
+{ return birthTime_ ? QDateTime::fromMSecsSinceEpoch(birthTime_) : QDateTime(); }
+inline QDateTime QFileSystemMetaData::metadataChangeTime() const
+{ return metadataChangeTime_ ? QDateTime::fromMSecsSinceEpoch(metadataChangeTime_) : QDateTime(); }
+inline QDateTime QFileSystemMetaData::modificationTime() const
+{ return modificationTime_ ? QDateTime::fromMSecsSinceEpoch(modificationTime_) : QDateTime(); }
+inline QDateTime QFileSystemMetaData::accessTime() const
+{ return accessTime_ ? QDateTime::fromMSecsSinceEpoch(accessTime_) : QDateTime(); }
inline uint QFileSystemMetaData::userId() const { return userId_; }
inline uint QFileSystemMetaData::groupId() const { return groupId_; }
@@ -318,9 +342,9 @@ inline void QFileSystemMetaData::fillFromFileAttribute(DWORD fileAttribute,bool
inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, bool setLinkType, bool isDriveRoot)
{
fillFromFileAttribute(findData.dwFileAttributes, isDriveRoot);
- creationTime_ = findData.ftCreationTime;
+ birthTime_ = findData.ftCreationTime;
lastAccessTime_ = findData.ftLastAccessTime;
- lastWriteTime_ = findData.ftLastWriteTime;
+ changeTime_ = lastWriteTime_ = findData.ftLastWriteTime;
if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) {
size_ = 0;
} else {
@@ -343,9 +367,9 @@ inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, boo
inline void QFileSystemMetaData::fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo)
{
fillFromFileAttribute(fileInfo.dwFileAttributes);
- creationTime_ = fileInfo.ftCreationTime;
+ birthTime_ = fileInfo.ftCreationTime;
lastAccessTime_ = fileInfo.ftLastAccessTime;
- lastWriteTime_ = fileInfo.ftLastWriteTime;
+ changeTime_ = lastWriteTime_ = fileInfo.ftLastWriteTime;
if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) {
size_ = 0;
} else {
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp
index 88e8baedca..b7a5440224 100644
--- a/src/corelib/io/qfsfileengine.cpp
+++ b/src/corelib/io/qfsfileengine.cpp
@@ -195,6 +195,9 @@ void QFSFileEngine::setFileName(const QString &file)
*/
bool QFSFileEngine::open(QIODevice::OpenMode openMode)
{
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
+
Q_D(QFSFileEngine);
if (d->fileEntry.isEmpty()) {
qWarning("QFSFileEngine::open: No file name specified");
@@ -230,6 +233,9 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags)
{
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
+
Q_D(QFSFileEngine);
// Append implies WriteOnly.
@@ -255,6 +261,9 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHand
*/
bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
{
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
+
Q_Q(QFSFileEngine);
this->fh = fh;
fd = -1;
@@ -516,6 +525,25 @@ bool QFSFileEngine::seek(qint64 pos)
}
/*!
+ \reimp
+*/
+QDateTime QFSFileEngine::fileTime(FileTime time) const
+{
+ Q_D(const QFSFileEngine);
+
+ if (time == AccessTime) {
+ // always refresh for the access time
+ d->metaData.clearFlags(QFileSystemMetaData::AccessTime);
+ }
+
+ if (d->doStat(QFileSystemMetaData::Times))
+ return d->metaData.fileTime(time);
+
+ return QDateTime();
+}
+
+
+/*!
\internal
*/
bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
@@ -699,6 +727,7 @@ qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen)
qint64 QFSFileEngine::write(const char *data, qint64 len)
{
Q_D(QFSFileEngine);
+ d->metaData.clearFlags(QFileSystemMetaData::Times);
// On Windows' stdlib implementation, the results of calling fread and
// fwrite are undefined if not called either in sequence, or if preceded
@@ -860,9 +889,9 @@ bool QFSFileEngine::supportsExtension(Extension extension) const
/*! \fn bool QFSFileEngine::copy(const QString &copyName)
- For windows, copy the file to file \a copyName.
+ For Windows or Apple platforms, copy the file to file \a copyName.
- Not implemented for Unix.
+ Not implemented for other Unix platforms.
*/
/*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
@@ -890,7 +919,7 @@ bool QFSFileEngine::supportsExtension(Extension extension) const
\reimp
*/
-/*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const
+/*! \fn bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
\reimp
*/
diff --git a/src/corelib/io/qfsfileengine_p.h b/src/corelib/io/qfsfileengine_p.h
index ab9ad3aa6b..faef84cbe2 100644
--- a/src/corelib/io/qfsfileengine_p.h
+++ b/src/corelib/io/qfsfileengine_p.h
@@ -97,6 +97,7 @@ public:
QString fileName(FileName file) const Q_DECL_OVERRIDE;
uint ownerId(FileOwner) const Q_DECL_OVERRIDE;
QString owner(FileOwner) const Q_DECL_OVERRIDE;
+ bool setFileTime(const QDateTime &newDate, FileTime time) Q_DECL_OVERRIDE;
QDateTime fileTime(FileTime time) const Q_DECL_OVERRIDE;
void setFileName(const QString &file) Q_DECL_OVERRIDE;
int handle() const Q_DECL_OVERRIDE;
@@ -109,6 +110,10 @@ public:
qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
qint64 readLine(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
qint64 write(const char *data, qint64 len) Q_DECL_OVERRIDE;
+ bool cloneTo(QAbstractFileEngine *target) override;
+
+ virtual bool isUnnamedFile() const
+ { return false; }
bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0) Q_DECL_OVERRIDE;
bool supportsExtension(Extension extension) const Q_DECL_OVERRIDE;
diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp
index c613368a91..c040d67862 100644
--- a/src/corelib/io/qfsfileengine_unix.cpp
+++ b/src/corelib/io/qfsfileengine_unix.cpp
@@ -65,46 +65,6 @@ QT_BEGIN_NAMESPACE
/*!
\internal
- Returns the stdlib open string corresponding to a QIODevice::OpenMode.
-*/
-static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QFileSystemEntry &fileEntry,
- QFileSystemMetaData &metaData)
-{
- QByteArray mode;
- if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
- mode = "rb";
- if (flags & QIODevice::WriteOnly) {
- metaData.clearFlags(QFileSystemMetaData::FileType);
- if (!fileEntry.isEmpty()
- && QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::FileType)
- && metaData.isFile()) {
- mode += '+';
- } else {
- mode = "wb+";
- }
- }
- } else if (flags & QIODevice::WriteOnly) {
- mode = "wb";
- if (flags & QIODevice::ReadOnly)
- mode += '+';
- }
- if (flags & QIODevice::Append) {
- mode = "ab";
- if (flags & QIODevice::ReadOnly)
- mode += '+';
- }
-
-#if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0207
- // must be glibc >= 2.7
- mode += 'e';
-#endif
-
- return mode;
-}
-
-/*!
- \internal
-
Returns the stdio open flags corresponding to a QIODevice::OpenMode.
*/
static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
@@ -130,17 +90,6 @@ static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
return oflags;
}
-/*!
- \internal
-
- Sets the file descriptor to close on exec. That is, the file
- descriptor is not inherited by child processes.
-*/
-static inline bool setCloseOnExec(int fd)
-{
- return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
-}
-
static inline QString msgOpenDirectory()
{
const char message[] = QT_TRANSLATE_NOOP("QIODevice", "file to open is a directory");
@@ -158,6 +107,8 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
{
Q_Q(QFSFileEngine);
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
if (openMode & QIODevice::Unbuffered) {
int flags = openModeToOpenFlags(openMode);
@@ -199,49 +150,6 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
}
fh = 0;
- } else {
- QByteArray fopenMode = openModeToFopenMode(openMode, fileEntry, metaData);
-
- // Try to open the file in buffered mode.
- do {
- fh = QT_FOPEN(fileEntry.nativeFilePath().constData(), fopenMode.constData());
- } while (!fh && errno == EINTR);
-
- // On failure, return and report the error.
- if (!fh) {
- q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
- qt_error_string(int(errno)));
- return false;
- }
-
- if (!(openMode & QIODevice::WriteOnly)) {
- // we don't need this check if we tried to open for writing because then
- // we had received EISDIR anyway.
- if (QFileSystemEngine::fillMetaData(QT_FILENO(fh), metaData)
- && metaData.isDirectory()) {
- q->setError(QFile::OpenError, msgOpenDirectory());
- fclose(fh);
- return false;
- }
- }
-
- setCloseOnExec(fileno(fh)); // ignore failure
-
- // Seek to the end when in Append mode.
- if (openMode & QIODevice::Append) {
- int ret;
- do {
- ret = QT_FSEEK(fh, 0, SEEK_END);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
- qt_error_string(int(errno)));
- return false;
- }
- }
-
- fd = -1;
}
closeFileHandle = true;
@@ -416,8 +324,14 @@ bool QFSFileEngine::copy(const QString &newName)
bool QFSFileEngine::renameOverwrite(const QString &newName)
{
- // On Unix, rename() overwrites.
- return rename(newName);
+ Q_D(QFSFileEngine);
+ QSystemError error;
+ bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error);
+
+ if (!ret)
+ setError(QFile::RenameError, error.toString());
+
+ return ret;
}
bool QFSFileEngine::rename(const QString &newName)
@@ -549,11 +463,14 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
| QFileSystemMetaData::LinkType
| QFileSystemMetaData::FileType
| QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType;
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::WasDeletedAttribute;
if (type & FlagsMask)
queryFlags |= QFileSystemMetaData::HiddenAttribute
| QFileSystemMetaData::ExistsAttribute;
+ else if (type & ExistsFlag)
+ queryFlags |= QFileSystemMetaData::WasDeletedAttribute;
queryFlags |= QFileSystemMetaData::LinkType;
@@ -585,7 +502,8 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
}
if (type & FlagsMask) {
- if (exists)
+ // the inode existing does not mean the file exists
+ if (!d->metaData.wasDeleted())
ret |= ExistsFlag;
if (d->fileEntry.isRoot())
ret |= RootFlag;
@@ -689,14 +607,23 @@ bool QFSFileEngine::setSize(qint64 size)
return ret;
}
-QDateTime QFSFileEngine::fileTime(FileTime time) const
+bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
{
- Q_D(const QFSFileEngine);
+ Q_D(QFSFileEngine);
+
+ if (d->openMode == QIODevice::NotOpen) {
+ setError(QFile::PermissionsError, qt_error_string(EACCES));
+ return false;
+ }
- if (d->doStat(QFileSystemMetaData::Times))
- return d->metaData.fileTime(time);
+ QSystemError error;
+ if (!QFileSystemEngine::setFileTime(d->nativeHandle(), newDate, time, error)) {
+ setError(QFile::PermissionsError, error.toString());
+ return false;
+ }
- return QDateTime();
+ d->metaData.clearFlags(QFileSystemMetaData::Times);
+ return true;
}
uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
@@ -715,7 +642,6 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla
#endif
Q_Q(QFSFileEngine);
- Q_UNUSED(flags);
if (openMode == QIODevice::NotOpen) {
q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
return 0;
@@ -806,6 +732,20 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr)
#endif
}
+/*!
+ \reimp
+*/
+bool QFSFileEngine::cloneTo(QAbstractFileEngine *target)
+{
+ Q_D(QFSFileEngine);
+ if ((target->fileFlags(LocalDiskFlag) & LocalDiskFlag) == 0)
+ return false;
+
+ int srcfd = d->nativeHandle();
+ int dstfd = target->handle();
+ return QFileSystemEngine::cloneFile(srcfd, dstfd, d->metaData);
+}
+
QT_END_NAMESPACE
#endif // QT_NO_FSFILEENGINE
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
index c99a25f30d..759effe632 100644
--- a/src/corelib/io/qfsfileengine_win.cpp
+++ b/src/corelib/io/qfsfileengine_win.cpp
@@ -474,11 +474,10 @@ bool QFSFileEngine::rename(const QString &newName)
bool QFSFileEngine::renameOverwrite(const QString &newName)
{
Q_D(QFSFileEngine);
- bool ret = ::MoveFileEx((wchar_t*)d->fileEntry.nativeFilePath().utf16(),
- (wchar_t*)QFileSystemEntry(newName).nativeFilePath().utf16(),
- MOVEFILE_REPLACE_EXISTING) != 0;
+ QSystemError error;
+ bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error);
if (!ret)
- setError(QFile::RenameError, QSystemError(::GetLastError(), QSystemError::NativeError).toString());
+ setError(QFile::RenameError, error.toString());
return ret;
}
@@ -694,6 +693,8 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::Fil
}
if (type & FlagsMask) {
if (d->metaData.exists()) {
+ // if we succeeded in querying, then the file exists: a file on
+ // Windows cannot be deleted if we have an open handle to it
ret |= ExistsFlag;
if (d->fileEntry.isRoot())
ret |= RootFlag;
@@ -854,15 +855,41 @@ bool QFSFileEngine::setSize(qint64 size)
return false;
}
-
-QDateTime QFSFileEngine::fileTime(FileTime time) const
+bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
{
- Q_D(const QFSFileEngine);
+ Q_D(QFSFileEngine);
+
+ if (d->openMode == QFile::NotOpen) {
+ setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
+ return false;
+ }
+
+ if (!newDate.isValid() || time == QAbstractFileEngine::MetadataChangeTime) {
+ setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
+ return false;
+ }
+
+ HANDLE handle = d->fileHandle;
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (d->fh)
+ handle = reinterpret_cast<HANDLE>(::_get_osfhandle(QT_FILENO(d->fh)));
+ else if (d->fd != -1)
+ handle = reinterpret_cast<HANDLE>(::_get_osfhandle(d->fd));
+ }
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
+ return false;
+ }
- if (d->doStat(QFileSystemMetaData::Times))
- return d->metaData.fileTime(time);
+ QSystemError error;
+ if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) {
+ setError(QFile::PermissionsError, error.toString());
+ return false;
+ }
- return QDateTime();
+ d->metaData.clearFlags(QFileSystemMetaData::Times);
+ return true;
}
uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
@@ -1002,4 +1029,15 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr)
return true;
}
+/*!
+ \reimp
+*/
+bool QFSFileEngine::cloneTo(QAbstractFileEngine *target)
+{
+ // There's some Windows Server 2016 API, but we won't
+ // bother with it.
+ Q_UNUSED(target);
+ return false;
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index 80122eac5e..7d46898911 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -775,6 +775,7 @@ bool QIODevice::open(OpenMode mode)
d->writeBuffers.clear();
d->setReadChannelCount(isReadable() ? 1 : 0);
d->setWriteChannelCount(isWritable() ? 1 : 0);
+ d->errorString.clear();
#if defined QIODEVICE_DEBUG
printf("%p QIODevice::open(0x%x)\n", this, quint32(mode));
#endif
@@ -801,7 +802,6 @@ void QIODevice::close()
emit aboutToClose();
#endif
d->openMode = NotOpen;
- d->errorString.clear();
d->pos = 0;
d->transactionStarted = false;
d->transactionPos = 0;
@@ -1879,6 +1879,128 @@ QByteArray QIODevice::peek(qint64 maxSize)
}
/*!
+ \since 5.10
+
+ Skips up to \a maxSize bytes from the device. Returns the number of bytes
+ actually skipped, or -1 on error.
+
+ This function does not wait and only discards the data that is already
+ available for reading.
+
+ If the device is opened in text mode, end-of-line terminators are
+ translated to '\n' symbols and count as a single byte identically to the
+ read() and peek() behavior.
+
+ This function works for all devices, including sequential ones that cannot
+ seek(). It is optimized to skip unwanted data after a peek() call.
+
+ For random-access devices, skip() can be used to seek forward from the
+ current position. Negative \a maxSize values are not allowed.
+
+ \sa peek(), seek(), read()
+*/
+qint64 QIODevice::skip(qint64 maxSize)
+{
+ Q_D(QIODevice);
+ CHECK_MAXLEN(skip, qint64(-1));
+ CHECK_READABLE(skip, qint64(-1));
+
+ const bool sequential = d->isSequential();
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::skip(%lld), d->pos = %lld, d->buffer.size() = %lld\n",
+ this, maxSize, d->pos, d->buffer.size());
+#endif
+
+ if ((sequential && d->transactionStarted) || (d->openMode & QIODevice::Text) != 0)
+ return d->skipByReading(maxSize);
+
+ // First, skip over any data in the internal buffer.
+ qint64 skippedSoFar = 0;
+ if (!d->buffer.isEmpty()) {
+ skippedSoFar = d->buffer.skip(maxSize);
+#if defined QIODEVICE_DEBUG
+ printf("%p \tskipping %lld bytes in buffer\n", this, skippedSoFar);
+#endif
+ if (!sequential)
+ d->pos += skippedSoFar;
+ if (d->buffer.isEmpty())
+ readData(nullptr, 0);
+ if (skippedSoFar == maxSize)
+ return skippedSoFar;
+
+ maxSize -= skippedSoFar;
+ }
+
+ // Try to seek on random-access device. At this point,
+ // the internal read buffer is empty.
+ if (!sequential) {
+ const qint64 bytesToSkip = qMin(size() - d->pos, maxSize);
+
+ // If the size is unknown or file position is at the end,
+ // fall back to reading below.
+ if (bytesToSkip > 0) {
+ if (!seek(d->pos + bytesToSkip))
+ return skippedSoFar ? skippedSoFar : Q_INT64_C(-1);
+ if (bytesToSkip == maxSize)
+ return skippedSoFar + bytesToSkip;
+
+ skippedSoFar += bytesToSkip;
+ maxSize -= bytesToSkip;
+ }
+ }
+
+ const qint64 skipResult = d->skip(maxSize);
+ if (skippedSoFar == 0)
+ return skipResult;
+
+ if (skipResult == -1)
+ return skippedSoFar;
+
+ return skippedSoFar + skipResult;
+}
+
+/*!
+ \internal
+*/
+qint64 QIODevicePrivate::skipByReading(qint64 maxSize)
+{
+ qint64 readSoFar = 0;
+ do {
+ char dummy[4096];
+ const qint64 readBytes = qMin<qint64>(maxSize, sizeof(dummy));
+ const qint64 readResult = read(dummy, readBytes);
+
+ // Do not try again, if we got less data.
+ if (readResult != readBytes) {
+ if (readSoFar == 0)
+ return readResult;
+
+ if (readResult == -1)
+ return readSoFar;
+
+ return readSoFar + readResult;
+ }
+
+ readSoFar += readResult;
+ maxSize -= readResult;
+ } while (maxSize > 0);
+
+ return readSoFar;
+}
+
+/*!
+ \internal
+*/
+qint64 QIODevicePrivate::skip(qint64 maxSize)
+{
+ // Base implementation discards the data by reading into the dummy buffer.
+ // It's slow, but this works for all types of devices. Subclasses can
+ // reimplement this function to improve on that.
+ return skipByReading(maxSize);
+}
+
+/*!
Blocks until new data is available for reading and the readyRead()
signal has been emitted, or until \a msecs milliseconds have
passed. If msecs is -1, this function will not time out.
diff --git a/src/corelib/io/qiodevice.h b/src/corelib/io/qiodevice.h
index 162480d22f..e64a4d0bb1 100644
--- a/src/corelib/io/qiodevice.h
+++ b/src/corelib/io/qiodevice.h
@@ -136,6 +136,7 @@ public:
qint64 peek(char *data, qint64 maxlen);
QByteArray peek(qint64 maxlen);
+ qint64 skip(qint64 maxSize);
virtual bool waitForReadyRead(int msecs);
virtual bool waitForBytesWritten(int msecs);
diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h
index 71a326dd53..de2aa1597e 100644
--- a/src/corelib/io/qiodevice_p.h
+++ b/src/corelib/io/qiodevice_p.h
@@ -174,6 +174,9 @@ public:
qint64 read(char *data, qint64 maxSize, bool peeking = false);
virtual qint64 peek(char *data, qint64 maxSize);
virtual QByteArray peek(qint64 maxSize);
+ qint64 skipByReading(qint64 maxSize);
+ // ### Qt6: consider replacing with a protected virtual QIODevice::skipData().
+ virtual qint64 skip(qint64 maxSize);
#ifdef QT_NO_QOBJECT
QIODevice *q_ptr;
diff --git a/src/corelib/io/qlockfile.cpp b/src/corelib/io/qlockfile.cpp
index 48317d07e0..129cf01b63 100644
--- a/src/corelib/io/qlockfile.cpp
+++ b/src/corelib/io/qlockfile.cpp
@@ -2,6 +2,7 @@
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -42,12 +43,34 @@
#include "qlockfile_p.h"
#include <QtCore/qthread.h>
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qfileinfo.h>
QT_BEGIN_NAMESPACE
+namespace {
+struct LockFileInfo
+{
+ qint64 pid;
+ QString appname;
+ QString hostname;
+};
+}
+
+static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info);
+
+static QString machineName()
+{
+#ifdef Q_OS_WIN
+ // we don't use QSysInfo because it tries to do name resolution
+ return qEnvironmentVariable("COMPUTERNAME");
+#else
+ return QSysInfo::machineHostName();
+#endif
+}
+
/*!
\class QLockFile
\inmodule QtCore
@@ -291,10 +314,27 @@ bool QLockFile::tryLock(int timeout)
bool QLockFile::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const
{
Q_D(const QLockFile);
- return d->getLockInfo(pid, hostname, appname);
+ LockFileInfo info;
+ if (!getLockInfo_helper(d->fileName, &info))
+ return false;
+ if (pid)
+ *pid = info.pid;
+ if (hostname)
+ *hostname = info.hostname;
+ if (appname)
+ *appname = info.appname;
+ return true;
}
-bool QLockFilePrivate::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const
+QByteArray QLockFilePrivate::lockFileContents() const
+{
+ // Use operator% from the fast builder to avoid multiple memory allocations.
+ return QByteArray::number(QCoreApplication::applicationPid()) % '\n'
+ % processNameByPid(QCoreApplication::applicationPid()).toUtf8() % '\n'
+ % machineName().toUtf8() % '\n';
+}
+
+static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info)
{
QFile reader(fileName);
if (!reader.open(QIODevice::ReadOnly))
@@ -309,14 +349,25 @@ bool QLockFilePrivate::getLockInfo(qint64 *pid, QString *hostname, QString *appn
QByteArray hostNameLine = reader.readLine();
hostNameLine.chop(1);
- qint64 thePid = pidLine.toLongLong();
- if (pid)
- *pid = thePid;
- if (appname)
- *appname = QString::fromUtf8(appNameLine);
- if (hostname)
- *hostname = QString::fromUtf8(hostNameLine);
- return thePid > 0;
+ bool ok;
+ info->appname = QString::fromUtf8(appNameLine);
+ info->hostname = QString::fromUtf8(hostNameLine);
+ info->pid = pidLine.toLongLong(&ok);
+ return ok && info->pid > 0;
+}
+
+bool QLockFilePrivate::isApparentlyStale() const
+{
+ LockFileInfo info;
+ if (getLockInfo_helper(fileName, &info)) {
+ if (info.hostname.isEmpty() || info.hostname == machineName()) {
+ if (!isProcessRunning(info.pid, info.appname))
+ return true;
+ }
+ }
+
+ const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTimeUtc());
+ return staleLockTime > 0 && qAbs(age) > staleLockTime;
}
/*!
diff --git a/src/corelib/io/qlockfile_p.h b/src/corelib/io/qlockfile_p.h
index 86a606ec00..b41dfb38ad 100644
--- a/src/corelib/io/qlockfile_p.h
+++ b/src/corelib/io/qlockfile_p.h
@@ -78,16 +78,14 @@ public:
}
QLockFile::LockError tryLock_sys();
bool removeStaleLock();
- bool getLockInfo(qint64 *pid, QString *hostname, QString *appname) const;
+ QByteArray lockFileContents() const;
// Returns \c true if the lock belongs to dead PID, or is old.
// The attempt to delete it will tell us if it was really stale or not, though.
bool isApparentlyStale() const;
+
// used in dbusmenu
Q_CORE_EXPORT static QString processNameByPid(qint64 pid);
-
-#ifdef Q_OS_UNIX
- static int checkFcntlWorksAfterFlock(const QString &fn);
-#endif
+ static bool isProcessRunning(qint64 pid, const QString &appname);
QString fileName;
#ifdef Q_OS_WIN
diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp
index ccc607afd5..fc01f83e80 100644
--- a/src/corelib/io/qlockfile_unix.cpp
+++ b/src/corelib/io/qlockfile_unix.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2017 Intel Corporation.
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -42,7 +42,6 @@
#include "private/qlockfile_p.h"
#include "QtCore/qtemporaryfile.h"
-#include "QtCore/qcoreapplication.h"
#include "QtCore/qfileinfo.h"
#include "QtCore/qdebug.h"
#include "QtCore/qdatetime.h"
@@ -94,91 +93,59 @@ static qint64 qt_write_loop(int fd, const char *data, qint64 len)
return pos;
}
-int QLockFilePrivate::checkFcntlWorksAfterFlock(const QString &fn)
-{
-#ifndef QT_NO_TEMPORARYFILE
- QTemporaryFile file(fn);
- if (!file.open())
- return 0;
- const int fd = file.d_func()->engine()->handle();
-#if defined(LOCK_EX) && defined(LOCK_NB)
- if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs
- return 0;
-#endif
- struct flock flockData;
- flockData.l_type = F_WRLCK;
- flockData.l_whence = SEEK_SET;
- flockData.l_start = 0;
- flockData.l_len = 0; // 0 = entire file
- flockData.l_pid = getpid();
- if (fcntl(fd, F_SETLK, &flockData) == -1) // for networked filesystems
- return 0;
- return 1;
-#else
- Q_UNUSED(fn);
- return 0;
-#endif
-}
-
-// Cache the result of checkFcntlWorksAfterFlock for each directory a lock
-// file is created in because in some filesystems, like NFS, both locks
-// are the same. This does not take into account a filesystem changing.
-// QCache is set to hold a maximum of 10 entries, this is to avoid unbounded
-// growth, this is caching directories of files and it is assumed a low number
-// will be sufficient.
-typedef QCache<QString, bool> CacheType;
-Q_GLOBAL_STATIC_WITH_ARGS(CacheType, fcntlOK, (10));
-static QBasicMutex fcntlLock;
+/*
+ * Details about file locking on Unix.
+ *
+ * There are three types of advisory locks on Unix systems:
+ * 1) POSIX process-wide locks using fcntl(F_SETLK)
+ * 2) BSD flock(2) system call
+ * 3) Linux-specific file descriptor locks using fcntl(F_OFD_SETLK)
+ * There's also a mandatory locking feature by POSIX, which is deprecated on
+ * Linux and users are advised not to use it.
+ *
+ * The first problem is that the POSIX API is braindead. POSIX.1-2008 says:
+ *
+ * All locks associated with a file for a given process shall be removed when
+ * a file descriptor for that file is closed by that process or the process
+ * holding that file descriptor terminates.
+ *
+ * The Linux manpage is clearer:
+ *
+ * * If a process closes _any_ file descriptor referring to a file, then all
+ * of the process's locks on that file are released, regardless of the file
+ * descriptor(s) on which the locks were obtained. This is bad: [...]
+ *
+ * * The threads in a process share locks. In other words, a multithreaded
+ * program can't use record locking to ensure that threads don't
+ * simultaneously access the same region of a file.
+ *
+ * So in order to use POSIX locks, we'd need a global mutex that stays locked
+ * while the QLockFile is locked. For that reason, Qt does not use POSIX
+ * advisory locks anymore.
+ *
+ * The next problem is that POSIX leaves undefined the relationship between
+ * locks with fcntl(), flock() and lockf(). In some systems (like the BSDs),
+ * all three use the same record set, while on others (like Linux) the locks
+ * are independent, except if locking over NFS mounts, in which case they're
+ * actually the same. Therefore, it's a very bad idea to mix them in the same
+ * process.
+ *
+ * We therefore use only flock(2).
+ */
-/*!
- \internal
- Checks that the OS isn't using POSIX locks to emulate flock().
- \macos is one of those.
-*/
-static bool fcntlWorksAfterFlock(const QString &fn)
-{
- QMutexLocker lock(&fcntlLock);
- if (fcntlOK.isDestroyed())
- return QLockFilePrivate::checkFcntlWorksAfterFlock(fn);
- bool *worksPtr = fcntlOK->object(fn);
- if (worksPtr)
- return *worksPtr;
-
- const bool val = QLockFilePrivate::checkFcntlWorksAfterFlock(fn);
- worksPtr = new bool(val);
- fcntlOK->insert(fn, worksPtr);
-
- return val;
-}
-
-static bool setNativeLocks(const QString &fileName, int fd)
+static bool setNativeLocks(int fd)
{
#if defined(LOCK_EX) && defined(LOCK_NB)
if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs
return false;
+#else
+ Q_UNUSED(fd);
#endif
- struct flock flockData;
- flockData.l_type = F_WRLCK;
- flockData.l_whence = SEEK_SET;
- flockData.l_start = 0;
- flockData.l_len = 0; // 0 = entire file
- flockData.l_pid = getpid();
- if (fcntlWorksAfterFlock(QDir::cleanPath(QFileInfo(fileName).absolutePath()) + QString('/'))
- && fcntl(fd, F_SETLK, &flockData) == -1) { // for networked filesystems
- return false;
- }
return true;
}
QLockFile::LockError QLockFilePrivate::tryLock_sys()
{
- // Assemble data, to write in a single call to write
- // (otherwise we'd have to check every write call)
- // Use operator% from the fast builder to avoid multiple memory allocations.
- QByteArray fileData = QByteArray::number(QCoreApplication::applicationPid()) % '\n'
- % QCoreApplication::applicationName().toUtf8() % '\n'
- % QSysInfo::machineHostName().toUtf8() % '\n';
-
const QByteArray lockFileName = QFile::encodeName(fileName);
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0) {
@@ -193,13 +160,14 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
}
}
// Ensure nobody else can delete the file while we have it
- if (!setNativeLocks(fileName, fd)) {
+ if (!setNativeLocks(fd)) {
const int errnoSaved = errno;
qWarning() << "setNativeLocks failed:" << qt_error_string(errnoSaved);
}
+ QByteArray fileData = lockFileContents();
if (qt_write_loop(fd, fileData.constData(), fileData.size()) < fileData.size()) {
- close(fd);
+ qt_safe_close(fd);
if (!QFile::remove(fileName))
qWarning("QLockFile: Could not remove our own lock file %s.", qPrintable(fileName));
return QLockFile::UnknownError; // partition full
@@ -224,31 +192,26 @@ bool QLockFilePrivate::removeStaleLock()
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY, 0666);
if (fd < 0) // gone already?
return false;
- bool success = setNativeLocks(fileName, fd) && (::unlink(lockFileName) == 0);
+ bool success = setNativeLocks(fd) && (::unlink(lockFileName) == 0);
close(fd);
return success;
}
-bool QLockFilePrivate::isApparentlyStale() const
+bool QLockFilePrivate::isProcessRunning(qint64 pid, const QString &appname)
{
- qint64 pid;
- QString hostname, appname;
- if (getLockInfo(&pid, &hostname, &appname)) {
- if (hostname.isEmpty() || hostname == QSysInfo::machineHostName()) {
- if (::kill(pid, 0) == -1 && errno == ESRCH)
- return true; // PID doesn't exist anymore
- const QString processName = processNameByPid(pid);
- if (!processName.isEmpty()) {
- QFileInfo fi(appname);
- if (fi.isSymLink())
- fi.setFile(fi.symLinkTarget());
- if (processName != fi.fileName())
- return true; // PID got reused by a different application.
- }
- }
+ if (::kill(pid, 0) == -1 && errno == ESRCH)
+ return false; // PID doesn't exist anymore
+
+ const QString processName = processNameByPid(pid);
+ if (!processName.isEmpty()) {
+ QFileInfo fi(appname);
+ if (fi.isSymLink())
+ fi.setFile(fi.symLinkTarget());
+ if (processName != fi.fileName())
+ return false; // PID got reused by a different application.
}
- const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
- return staleLockTime > 0 && qAbs(age) > staleLockTime;
+
+ return true;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)
@@ -258,17 +221,17 @@ QString QLockFilePrivate::processNameByPid(qint64 pid)
proc_name(pid, name, sizeof(name) / sizeof(char));
return QFile::decodeName(name);
#elif defined(Q_OS_LINUX)
- if (!QFile::exists(QStringLiteral("/proc/version")))
+ if (!qt_haveLinuxProcfs())
return QString();
+
char exePath[64];
- char buf[PATH_MAX + 1];
sprintf(exePath, "/proc/%lld/exe", pid);
- size_t len = (size_t)readlink(exePath, buf, sizeof(buf));
- if (len >= sizeof(buf)) {
+
+ QByteArray buf = qt_readlink(exePath);
+ if (buf.isEmpty()) {
// The pid is gone. Return some invalid process name to fail the test.
return QStringLiteral("/ERROR/");
}
- buf[len] = 0;
return QFileInfo(QFile::decodeName(buf)).fileName();
#elif defined(Q_OS_HAIKU)
thread_info info;
diff --git a/src/corelib/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp
index 4b43181686..de64ec0432 100644
--- a/src/corelib/io/qlockfile_win.cpp
+++ b/src/corelib/io/qlockfile_win.cpp
@@ -2,6 +2,7 @@
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -42,7 +43,6 @@
#include "private/qfilesystementry_p.h"
#include <qt_windows.h>
-#include "QtCore/qcoreapplication.h"
#include "QtCore/qfileinfo.h"
#include "QtCore/qdatetime.h"
#include "QtCore/qdebug.h"
@@ -50,11 +50,6 @@
QT_BEGIN_NAMESPACE
-static inline QByteArray localHostName()
-{
- return qgetenv("COMPUTERNAME");
-}
-
static inline bool fileExists(const wchar_t *fileName)
{
WIN32_FILE_ATTRIBUTE_DATA data;
@@ -107,15 +102,7 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
// We hold the lock, continue.
fileHandle = fh;
- // Assemble data, to write in a single call to write
- // (otherwise we'd have to check every write call)
- QByteArray fileData;
- fileData += QByteArray::number(QCoreApplication::applicationPid());
- fileData += '\n';
- fileData += QCoreApplication::applicationName().toUtf8();
- fileData += '\n';
- fileData += localHostName();
- fileData += '\n';
+ QByteArray fileData = lockFileContents();
DWORD bytesWritten = 0;
QLockFile::LockError error = QLockFile::NoError;
if (!WriteFile(fh, fileData.constData(), fileData.size(), &bytesWritten, NULL) || !FlushFileBuffers(fh))
@@ -129,38 +116,33 @@ bool QLockFilePrivate::removeStaleLock()
return QFile::remove(fileName);
}
-bool QLockFilePrivate::isApparentlyStale() const
+bool QLockFilePrivate::isProcessRunning(qint64 pid, const QString &appname)
{
- qint64 pid;
- QString hostname, appname;
-
// On WinRT there seems to be no way of obtaining information about other
// processes due to sandboxing
#ifndef Q_OS_WINRT
- if (getLockInfo(&pid, &hostname, &appname)) {
- if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) {
- HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
- if (!procHandle)
- return true;
- // We got a handle but check if process is still alive
- DWORD exitCode = 0;
- if (!::GetExitCodeProcess(procHandle, &exitCode))
- exitCode = 0;
- ::CloseHandle(procHandle);
- if (exitCode != STILL_ACTIVE)
- return true;
- const QString processName = processNameByPid(pid);
- if (!processName.isEmpty() && processName != appname)
- return true; // PID got reused by a different application.
- }
- }
+ HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+ if (!procHandle)
+ return false;
+
+ // We got a handle but check if process is still alive
+ DWORD exitCode = 0;
+ if (!::GetExitCodeProcess(procHandle, &exitCode))
+ exitCode = 0;
+ ::CloseHandle(procHandle);
+ if (exitCode != STILL_ACTIVE)
+ return false;
+
+ const QString processName = processNameByPid(pid);
+ if (!processName.isEmpty() && processName != appname)
+ return false; // PID got reused by a different application.
+
#else // !Q_OS_WINRT
Q_UNUSED(pid);
- Q_UNUSED(hostname);
Q_UNUSED(appname);
#endif // Q_OS_WINRT
- const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
- return staleLockTime > 0 && qAbs(age) > staleLockTime;
+
+ return true;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 88a54f275f..64a59bc2c3 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -99,6 +99,10 @@ QT_END_NAMESPACE
#include <private/qcore_unix_p.h>
#endif
+#if QT_HAS_INCLUDE(<paths.h>)
+#include <paths.h>
+#endif
+
QT_BEGIN_NAMESPACE
/*!
@@ -450,11 +454,6 @@ void QProcessPrivate::Channel::clear()
process = 0;
}
-/*! \fn bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
-
-\internal
- */
-
/*!
\class QProcess
\inmodule QtCore
@@ -1058,10 +1057,7 @@ bool QProcessPrivate::tryReadFromChannel(Channel *channel)
readBuffer.chop(available - readBytes);
bool didRead = false;
- if (readBytes == 0) {
- if (channel->notifier)
- channel->notifier->setEnabled(false);
- } else if (currentReadChannel == channelIdx) {
+ if (currentReadChannel == channelIdx) {
didRead = true;
if (!emittedReadyRead) {
emittedReadyRead = true;
@@ -2114,6 +2110,64 @@ void QProcess::start(OpenMode mode)
}
/*!
+ \since 5.10
+
+ Starts the program set by setProgram() with arguments set by setArguments()
+ in a new process, and detaches from it. Returns \c true on success;
+ otherwise returns \c false. If the calling process exits, the
+ detached process will continue to run unaffected.
+
+ \b{Unix:} The started process will run in its own session and act
+ like a daemon.
+
+ The process will be started in the directory set by setWorkingDirectory().
+ If workingDirectory() is empty, the working directory is inherited
+ from the calling process.
+
+ \note On QNX, this may cause all application threads to
+ temporarily freeze.
+
+ If the function is successful then *\a pid is set to the process identifier
+ of the started process. Note that the child process may exit and the PID
+ may become invalid without notice. Furthermore, after the child process
+ exits, the same PID may be recycled and used by a completely different
+ process. User code should be careful when using this variable, especially
+ if one intends to forcibly terminate the process by operating system means.
+
+ Only the following property setters are supported by startDetached():
+ \list
+ \li setArguments()
+ \li setCreateProcessArgumentsModifier()
+ \li setNativeArguments()
+ \li setProcessEnvironment()
+ \li setProgram()
+ \li setStandardErrorFile()
+ \li setStandardInputFile()
+ \li setStandardOutputFile()
+ \li setWorkingDirectory()
+ \endlist
+ All other properties of the QProcess object are ignored.
+
+ \sa start()
+ \sa startDetached(const QString &program, const QStringList &arguments,
+ const QString &workingDirectory, qint64 *pid)
+ \sa startDetached(const QString &command)
+*/
+bool QProcess::startDetached(qint64 *pid)
+{
+ Q_D(QProcess);
+ if (d->processState != NotRunning) {
+ qWarning("QProcess::startDetached: Process is already running");
+ return false;
+ }
+ if (d->program.isEmpty()) {
+ d->setErrorAndEmit(QProcess::FailedToStart, tr("No program defined"));
+ return false;
+ }
+ return d->startDetached(pid);
+}
+
+/*!
Starts the program set by setProgram() with arguments set by setArguments().
The OpenMode is set to \a mode.
@@ -2447,6 +2501,8 @@ int QProcess::execute(const QString &command)
}
/*!
+ \overload startDetached()
+
Starts the program \a program with the arguments \a arguments in a
new process, and detaches from it. Returns \c true on success;
otherwise returns \c false. If the calling process exits, the
@@ -2454,16 +2510,10 @@ int QProcess::execute(const QString &command)
Argument handling is identical to the respective start() overload.
- \b{Unix:} The started process will run in its own session and act
- like a daemon.
-
The process will be started in the directory \a workingDirectory.
If \a workingDirectory is empty, the working directory is inherited
from the calling process.
- \note On QNX, this may cause all application threads to
- temporarily freeze.
-
If the function is successful then *\a pid is set to the process
identifier of the started process.
@@ -2474,10 +2524,11 @@ bool QProcess::startDetached(const QString &program,
const QString &workingDirectory,
qint64 *pid)
{
- return QProcessPrivate::startDetached(program,
- arguments,
- workingDirectory,
- pid);
+ QProcess process;
+ process.setProgram(program);
+ process.setArguments(arguments);
+ process.setWorkingDirectory(workingDirectory);
+ return process.startDetached(pid);
}
/*!
@@ -2486,11 +2537,14 @@ bool QProcess::startDetached(const QString &program,
bool QProcess::startDetached(const QString &program,
const QStringList &arguments)
{
- return QProcessPrivate::startDetached(program, arguments);
+ QProcess process;
+ process.setProgram(program);
+ process.setArguments(arguments);
+ return process.startDetached();
}
/*!
- \overload
+ \overload startDetached()
Starts the command \a command in a new process, and detaches from it.
Returns \c true on success; otherwise returns \c false.
@@ -2508,9 +2562,10 @@ bool QProcess::startDetached(const QString &command)
if (args.isEmpty())
return false;
- const QString prog = args.takeFirst();
-
- return QProcessPrivate::startDetached(prog, args);
+ QProcess process;
+ process.setProgram(args.takeFirst());
+ process.setArguments(args);
+ return process.startDetached();
}
QT_BEGIN_INCLUDE_NAMESPACE
@@ -2587,6 +2642,8 @@ QString QProcess::nullDevice()
{
#ifdef Q_OS_WIN
return QStringLiteral("\\\\.\\NUL");
+#elif defined(_PATH_DEVNULL)
+ return QStringLiteral(_PATH_DEVNULL);
#else
return QStringLiteral("/dev/null");
#endif
diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h
index 19157bdd02..c8aef2f0b1 100644
--- a/src/corelib/io/qprocess.h
+++ b/src/corelib/io/qprocess.h
@@ -163,6 +163,7 @@ public:
void start(const QString &command, OpenMode mode = ReadWrite);
#endif
void start(OpenMode mode = ReadWrite);
+ bool startDetached(qint64 *pid = nullptr);
bool open(OpenMode mode = ReadWrite) Q_DECL_OVERRIDE;
QString program() const;
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index c5abf7b762..deb29dca0a 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -350,7 +350,7 @@ public:
void start(QIODevice::OpenMode mode);
void startProcess();
#if defined(Q_OS_UNIX)
- void execChild(const char *workingDirectory, char **path, char **argv, char **envp);
+ void execChild(const char *workingDirectory, char **argv, char **envp);
#endif
bool processStarted(QString *errorMessage = Q_NULLPTR);
void terminateProcess();
@@ -360,13 +360,13 @@ public:
bool waitForDeadChild();
#endif
#ifdef Q_OS_WIN
+ bool callCreateProcess(QProcess::CreateProcessArguments *cpargs);
bool drainOutputPipes();
void flushPipeWriter();
qint64 pipeWriterBytesToWrite() const;
#endif
- static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(),
- qint64 *pid = 0);
+ bool startDetached(qint64 *pPid);
int exitCode;
QProcess::ExitStatus exitStatus;
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
index 318c633017..15752f84b2 100644
--- a/src/corelib/io/qprocess_unix.cpp
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -87,6 +87,7 @@ QT_END_NAMESPACE
#include "qprocess.h"
#include "qprocess_p.h"
+#include "qstandardpaths.h"
#include "private/qcore_unix_p.h"
#ifdef Q_OS_MAC
@@ -420,8 +421,16 @@ void QProcessPrivate::startProcess()
#endif
// Add the program name to the argument list.
- char *dupProgramName = ::strdup(encodedProgramName.constData());
- argv[0] = dupProgramName;
+ argv[0] = nullptr;
+ if (!program.contains(QLatin1Char('/'))) {
+ const QString &exeFilePath = QStandardPaths::findExecutable(program);
+ if (!exeFilePath.isEmpty()) {
+ const QByteArray &tmp = QFile::encodeName(exeFilePath);
+ argv[0] = ::strdup(tmp.constData());
+ }
+ }
+ if (!argv[0])
+ argv[0] = ::strdup(encodedProgramName.constData());
// Add every argument to the list
for (int i = 0; i < arguments.count(); ++i)
@@ -443,29 +452,6 @@ void QProcessPrivate::startProcess()
workingDirPtr = encodedWorkingDirectory.constData();
}
- // If the program does not specify a path, generate a list of possible
- // locations for the binary using the PATH environment variable.
- char **path = 0;
- int pathc = 0;
- if (!program.contains(QLatin1Char('/'))) {
- const QString pathEnv = QString::fromLocal8Bit(qgetenv("PATH"));
- if (!pathEnv.isEmpty()) {
- QStringList pathEntries = pathEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
- if (!pathEntries.isEmpty()) {
- pathc = pathEntries.size();
- path = new char *[pathc + 1];
- path[pathc] = 0;
-
- for (int k = 0; k < pathEntries.size(); ++k) {
- QByteArray tmp = QFile::encodeName(pathEntries.at(k));
- if (!tmp.endsWith('/')) tmp += '/';
- tmp += encodedProgramName;
- path[k] = ::strdup(tmp.constData());
- }
- }
- }
- }
-
// Start the process manager, and fork off the child process.
pid_t childPid;
forkfd = ::forkfd(FFD_CLOEXEC, &childPid);
@@ -473,16 +459,12 @@ void QProcessPrivate::startProcess()
if (forkfd != FFD_CHILD_PROCESS) {
// Parent process.
// Clean up duplicated memory.
- free(dupProgramName);
- for (int i = 1; i <= arguments.count(); ++i)
+ for (int i = 0; i <= arguments.count(); ++i)
free(argv[i]);
for (int i = 0; i < envc; ++i)
free(envp[i]);
- for (int i = 0; i < pathc; ++i)
- free(path[i]);
delete [] argv;
delete [] envp;
- delete [] path;
}
// On QNX, if spawnChild failed, childPid will be -1 but forkfd is still 0.
@@ -503,7 +485,7 @@ void QProcessPrivate::startProcess()
// Start the child.
if (forkfd == FFD_CHILD_PROCESS) {
- execChild(workingDirPtr, path, argv, envp);
+ execChild(workingDirPtr, argv, envp);
::_exit(-1);
}
@@ -550,7 +532,7 @@ struct ChildError
char function[8];
};
-void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp)
+void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp)
{
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
@@ -574,7 +556,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
}
}
- // make sure this fd is closed if execvp() succeeds
+ // make sure this fd is closed if execv() succeeds
qt_safe_close(childStartedPipe[0]);
// enter the working directory
@@ -589,25 +571,13 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
// execute the process
if (!envp) {
- qt_safe_execvp(argv[0], argv);
+ qt_safe_execv(argv[0], argv);
strcpy(error.function, "execvp");
} else {
- if (path) {
- char **arg = path;
- while (*arg) {
- argv[0] = *arg;
#if defined (QPROCESS_DEBUG)
- fprintf(stderr, "QProcessPrivate::execChild() searching / starting %s\n", argv[0]);
+ fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);
#endif
- qt_safe_execve(argv[0], argv, envp);
- ++arg;
- }
- } else {
-#if defined (QPROCESS_DEBUG)
- fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);
-#endif
- qt_safe_execve(argv[0], argv, envp);
- }
+ qt_safe_execve(argv[0], argv, envp);
strcpy(error.function, "execve");
}
@@ -933,7 +903,7 @@ bool QProcessPrivate::waitForDeadChild()
return true;
}
-bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+bool QProcessPrivate::startDetached(qint64 *pid)
{
QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory);
@@ -949,6 +919,17 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
return false;
}
+ if ((stdinChannel.type == Channel::Redirect && !openChannel(stdinChannel))
+ || (stdoutChannel.type == Channel::Redirect && !openChannel(stdoutChannel))
+ || (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))) {
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
+ qt_safe_close(startedPipe[0]);
+ qt_safe_close(startedPipe[1]);
+ return false;
+ }
+
pid_t childPid = fork();
if (childPid == 0) {
struct sigaction noaction;
@@ -965,6 +946,18 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
if (doubleForkPid == 0) {
qt_safe_close(pidPipe[1]);
+ // copy the stdin socket if asked to (without closing on exec)
+ if (inputChannelMode != QProcess::ForwardedInputChannel)
+ qt_safe_dup2(stdinChannel.pipe[0], STDIN_FILENO, 0);
+
+ // copy the stdout and stderr if asked to
+ if (processChannelMode != QProcess::ForwardedChannels) {
+ if (processChannelMode != QProcess::ForwardedOutputChannel)
+ qt_safe_dup2(stdoutChannel.pipe[1], STDOUT_FILENO, 0);
+ if (processChannelMode != QProcess::ForwardedErrorChannel)
+ qt_safe_dup2(stderrChannel.pipe[1], STDERR_FILENO, 0);
+ }
+
if (!encodedWorkingDirectory.isEmpty()) {
if (QT_CHDIR(encodedWorkingDirectory.constData()) == -1)
qWarning("QProcessPrivate::startDetached: failed to chdir to %s", encodedWorkingDirectory.constData());
@@ -975,23 +968,28 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData());
argv[arguments.size() + 1] = 0;
+ // Duplicate the environment.
+ int envc = 0;
+ char **envp = nullptr;
+ if (environment.d.constData()) {
+ QProcessEnvironmentPrivate::MutexLocker locker(environment.d);
+ envp = _q_dupEnvironment(environment.d.constData()->vars, &envc);
+ }
+
+ QByteArray tmp;
if (!program.contains(QLatin1Char('/'))) {
- const QString path = QString::fromLocal8Bit(qgetenv("PATH"));
- if (!path.isEmpty()) {
- QStringList pathEntries = path.split(QLatin1Char(':'));
- for (int k = 0; k < pathEntries.size(); ++k) {
- QByteArray tmp = QFile::encodeName(pathEntries.at(k));
- if (!tmp.endsWith('/')) tmp += '/';
- tmp += QFile::encodeName(program);
- argv[0] = tmp.data();
- qt_safe_execv(argv[0], argv);
- }
- }
- } else {
- QByteArray tmp = QFile::encodeName(program);
- argv[0] = tmp.data();
- qt_safe_execv(argv[0], argv);
+ const QString &exeFilePath = QStandardPaths::findExecutable(program);
+ if (!exeFilePath.isEmpty())
+ tmp = QFile::encodeName(exeFilePath);
}
+ if (tmp.isEmpty())
+ tmp = QFile::encodeName(program);
+ argv[0] = tmp.data();
+
+ if (envp)
+ qt_safe_execve(argv[0], argv, envp);
+ else
+ qt_safe_execv(argv[0], argv);
struct sigaction noaction;
memset(&noaction, 0, sizeof(noaction));
@@ -1021,6 +1019,9 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
::_exit(1);
}
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
qt_safe_close(startedPipe[1]);
qt_safe_close(pidPipe[1]);
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index 05c9d6594c..8da6d6b16e 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -48,6 +48,7 @@
#include <qdir.h>
#include <qelapsedtimer.h>
#include <qfileinfo.h>
+#include <qrandom.h>
#include <qregexp.h>
#include <qwineventnotifier.h>
#include <private/qsystemlibrary_p.h>
@@ -99,10 +100,8 @@ static void qt_create_pipe(Q_PIPE *pipe, bool isInputPipe)
wchar_t pipeName[256];
unsigned int attempts = 1000;
forever {
- // ### The user must make sure to call qsrand() to make the pipe names less predictable.
- // ### Replace the call to qrand() with a secure version, once we have it in Qt.
_snwprintf(pipeName, sizeof(pipeName) / sizeof(pipeName[0]),
- L"\\\\.\\pipe\\qt-%X", qrand());
+ L"\\\\.\\pipe\\qt-%X", QRandomGenerator::global()->generate());
DWORD dwOpenMode = FILE_FLAG_OVERLAPPED;
DWORD dwOutputBufferSize = 0;
@@ -358,7 +357,8 @@ void QProcessPrivate::closeChannel(Channel *channel)
destroyPipe(channel->pipe);
}
-static QString qt_create_commandline(const QString &program, const QStringList &arguments)
+static QString qt_create_commandline(const QString &program, const QStringList &arguments,
+ const QString &nativeArguments)
{
QString args;
if (!program.isEmpty()) {
@@ -387,6 +387,13 @@ static QString qt_create_commandline(const QString &program, const QStringList &
}
args += QLatin1Char(' ') + tmp;
}
+
+ if (!nativeArguments.isEmpty()) {
+ if (!args.isEmpty())
+ args += QLatin1Char(' ');
+ args += nativeArguments;
+ }
+
return args;
}
@@ -450,6 +457,30 @@ static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Map &e
return envlist;
}
+bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs)
+{
+ if (modifyCreateProcessArgs)
+ modifyCreateProcessArgs(cpargs);
+ bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
+ cpargs->processAttributes, cpargs->threadAttributes,
+ cpargs->inheritHandles, cpargs->flags, cpargs->environment,
+ cpargs->currentDirectory, cpargs->startupInfo,
+ cpargs->processInformation);
+ if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
+ CloseHandle(stdinChannel.pipe[0]);
+ stdinChannel.pipe[0] = INVALID_Q_PIPE;
+ }
+ if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stdoutChannel.pipe[1]);
+ stdoutChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stderrChannel.pipe[1]);
+ stderrChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ return success;
+}
+
void QProcessPrivate::startProcess()
{
Q_Q(QProcess);
@@ -472,15 +503,10 @@ void QProcessPrivate::startProcess()
!openChannel(stderrChannel))
return;
- QString args = qt_create_commandline(program, arguments);
+ const QString args = qt_create_commandline(program, arguments, nativeArguments);
QByteArray envlist;
if (environment.d.constData())
envlist = qt_create_environment(environment.d.constData()->vars);
- if (!nativeArguments.isEmpty()) {
- if (!args.isEmpty())
- args += QLatin1Char(' ');
- args += nativeArguments;
- }
#if defined QPROCESS_DEBUG
qDebug("Creating process");
@@ -507,18 +533,14 @@ void QProcessPrivate::startProcess()
const QString nativeWorkingDirectory = QDir::toNativeSeparators(workingDirectory);
QProcess::CreateProcessArguments cpargs = {
- 0, (wchar_t*)args.utf16(),
- 0, 0, TRUE, dwCreationFlags,
- environment.isEmpty() ? 0 : envlist.data(),
- nativeWorkingDirectory.isEmpty() ? Q_NULLPTR : (wchar_t*)nativeWorkingDirectory.utf16(),
+ nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
+ nullptr, nullptr, true, dwCreationFlags,
+ environment.isEmpty() ? nullptr : envlist.data(),
+ nativeWorkingDirectory.isEmpty()
+ ? nullptr : reinterpret_cast<const wchar_t *>(nativeWorkingDirectory.utf16()),
&startupInfo, pid
};
- if (modifyCreateProcessArgs)
- modifyCreateProcessArgs(&cpargs);
- success = CreateProcess(cpargs.applicationName, cpargs.arguments, cpargs.processAttributes,
- cpargs.threadAttributes, cpargs.inheritHandles, cpargs.flags,
- cpargs.environment, cpargs.currentDirectory, cpargs.startupInfo,
- cpargs.processInformation);
+ success = callCreateProcess(&cpargs);
QString errorString;
if (!success) {
@@ -526,19 +548,6 @@ void QProcessPrivate::startProcess()
errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
}
- if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
- CloseHandle(stdinChannel.pipe[0]);
- stdinChannel.pipe[0] = INVALID_Q_PIPE;
- }
- if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
- CloseHandle(stdoutChannel.pipe[1]);
- stdoutChannel.pipe[1] = INVALID_Q_PIPE;
- }
- if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
- CloseHandle(stderrChannel.pipe[1]);
- stderrChannel.pipe[1] = INVALID_Q_PIPE;
- }
-
if (!success) {
cleanup();
setErrorAndEmit(QProcess::FailedToStart, errorString);
@@ -826,6 +835,7 @@ bool QProcessPrivate::writeToStdin()
// Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails
// with ERROR_ELEVATION_REQUIRED.
static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
+ const QString &nativeArguments,
const QString &workingDir, qint64 *pid)
{
typedef BOOL (WINAPI *ShellExecuteExType)(SHELLEXECUTEINFOW *);
@@ -836,7 +846,8 @@ static bool startDetachedUacPrompt(const QString &programIn, const QStringList &
if (!shellExecuteEx)
return false;
- const QString args = qt_create_commandline(QString(), arguments); // needs arguments only
+ const QString args = qt_create_commandline(QString(), // needs arguments only
+ arguments, nativeArguments);
SHELLEXECUTEINFOW shellExecuteExInfo;
memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW));
shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
@@ -859,25 +870,52 @@ static bool startDetachedUacPrompt(const QString &programIn, const QStringList &
return true;
}
-bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
+bool QProcessPrivate::startDetached(qint64 *pid)
{
static const DWORD errorElevationRequired = 740;
- QString args = qt_create_commandline(program, arguments);
+ if ((stdinChannel.type == Channel::Redirect && !openChannel(stdinChannel))
+ || (stdoutChannel.type == Channel::Redirect && !openChannel(stdoutChannel))
+ || (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))) {
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
+ return false;
+ }
+
+ QString args = qt_create_commandline(program, arguments, nativeArguments);
bool success = false;
PROCESS_INFORMATION pinfo;
+ void *envPtr = nullptr;
+ QByteArray envlist;
+ if (environment.d.constData()) {
+ envlist = qt_create_environment(environment.d.constData()->vars);
+ envPtr = envlist.data();
+ }
+
DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0,
+ STARTF_USESTDHANDLES,
+ 0, 0, 0,
+ stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
};
- success = CreateProcess(0, (wchar_t*)args.utf16(),
- 0, 0, FALSE, dwCreationFlags, 0,
- workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
- &startupInfo, &pinfo);
+
+ const bool inheritHandles = stdinChannel.type == Channel::Redirect
+ || stdoutChannel.type == Channel::Redirect
+ || stderrChannel.type == Channel::Redirect;
+ QProcess::CreateProcessArguments cpargs = {
+ nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
+ nullptr, nullptr, inheritHandles, dwCreationFlags, envPtr,
+ workingDirectory.isEmpty()
+ ? nullptr : reinterpret_cast<const wchar_t *>(workingDirectory.utf16()),
+ &startupInfo, &pinfo
+ };
+ success = callCreateProcess(&cpargs);
if (success) {
CloseHandle(pinfo.hThread);
@@ -885,9 +923,19 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
if (pid)
*pid = pinfo.dwProcessId;
} else if (GetLastError() == errorElevationRequired) {
- success = startDetachedUacPrompt(program, arguments, workingDir, pid);
+ if (envPtr)
+ qWarning("QProcess: custom environment will be ignored for detached elevated process.");
+ if (!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()
+ || !stderrChannel.file.isEmpty()) {
+ qWarning("QProcess: file redirection is unsupported for detached elevated processes.");
+ }
+ success = startDetachedUacPrompt(program, arguments, nativeArguments,
+ workingDirectory, pid);
}
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
return success;
}
diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp
index 4e7120a3b8..31f02e977d 100644
--- a/src/corelib/io/qresource.cpp
+++ b/src/corelib/io/qresource.cpp
@@ -67,10 +67,9 @@ QT_BEGIN_NAMESPACE
class QStringSplitter
{
public:
- QStringSplitter(const QString &s)
- : m_string(s), m_data(m_string.constData()), m_len(s.length()), m_pos(0)
+ explicit QStringSplitter(QStringView sv)
+ : m_data(sv.data()), m_len(sv.size())
{
- m_splitChar = QLatin1Char('/');
}
inline bool hasNext() {
@@ -79,18 +78,17 @@ public:
return m_pos < m_len;
}
- inline QStringRef next() {
+ inline QStringView next() {
int start = m_pos;
while (m_pos < m_len && m_data[m_pos] != m_splitChar)
++m_pos;
- return QStringRef(&m_string, start, m_pos - start);
+ return QStringView(m_data + start, m_pos - start);
}
- QString m_string;
const QChar *m_data;
- QChar m_splitChar;
- int m_len;
- int m_pos;
+ qsizetype m_len;
+ qsizetype m_pos = 0;
+ QChar m_splitChar = QLatin1Char('/');
};
@@ -679,7 +677,7 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
QStringSplitter splitter(path);
while (child_count && splitter.hasNext()) {
- QStringRef segment = splitter.next();
+ QStringView segment = splitter.next();
#ifdef DEBUG_RESOURCE_MATCH
qDebug() << " CHILDREN" << segment;
@@ -836,24 +834,24 @@ QStringList QResourceRoot::children(int node) const
bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
{
const QString root = mappingRoot();
- if(!root.isEmpty()) {
- const QVector<QStringRef> root_segments = root.splitRef(QLatin1Char('/'), QString::SkipEmptyParts),
- path_segments = path.splitRef(QLatin1Char('/'), QString::SkipEmptyParts);
- if(path_segments.size() <= root_segments.size()) {
- int matched = 0;
- for(int i = 0; i < path_segments.size(); ++i) {
- if(root_segments[i] != path_segments[i])
- break;
- ++matched;
- }
- if(matched == path_segments.size()) {
- if(match && root_segments.size() > matched)
- *match = root_segments.at(matched).toString();
- return true;
- }
+ if (root.isEmpty())
+ return false;
+
+ QStringSplitter rootIt(root);
+ QStringSplitter pathIt(path);
+ while (rootIt.hasNext()) {
+ if (pathIt.hasNext()) {
+ if (rootIt.next() != pathIt.next()) // mismatch
+ return false;
+ } else {
+ // end of path, but not of root:
+ if (match)
+ *match = rootIt.next().toString();
+ return true;
}
}
- return false;
+ // end of root
+ return !pathIt.hasNext();
}
Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp
index 3f45ca5f91..63f2284ef5 100644
--- a/src/corelib/io/qsavefile.cpp
+++ b/src/corelib/io/qsavefile.cpp
@@ -231,7 +231,38 @@ bool QSaveFile::open(OpenMode mode)
d->finalFileName = existingFile.filePath();
}
- d->fileEngine = new QTemporaryFileEngine;
+ auto openDirectly = [&]() {
+ d->fileEngine = QAbstractFileEngine::create(d->finalFileName);
+ if (d->fileEngine->open(mode | QIODevice::Unbuffered)) {
+ d->useTemporaryFile = false;
+ QFileDevice::open(mode);
+ return true;
+ }
+ return false;
+ };
+
+#ifdef Q_OS_WIN
+ // check if it is an Alternate Data Stream
+ if (d->finalFileName == d->fileName && d->fileName.indexOf(QLatin1Char(':'), 2) > 1) {
+ // yes, we can't rename onto it...
+ if (d->directWriteFallback) {
+ if (openDirectly())
+ return true;
+ d->setError(d->fileEngine->error(), d->fileEngine->errorString());
+ delete d->fileEngine;
+ d->fileEngine = 0;
+ } else {
+ QString msg =
+ QSaveFile::tr("QSaveFile cannot open '%1' without direct write fallback "
+ "enabled: path contains an Alternate Data Stream specifier")
+ .arg(QDir::toNativeSeparators(d->fileName));
+ d->setError(QFileDevice::OpenError, msg);
+ }
+ return false;
+ }
+#endif
+
+ d->fileEngine = new QTemporaryFileEngine(&d->finalFileName);
// if the target file exists, we'll copy its permissions below,
// but until then, let's ensure the temporary file is not accessible
// to a third party
@@ -243,12 +274,8 @@ bool QSaveFile::open(OpenMode mode)
#ifdef Q_OS_UNIX
if (d->directWriteFallback && err == QFileDevice::OpenError && errno == EACCES) {
delete d->fileEngine;
- d->fileEngine = QAbstractFileEngine::create(d->finalFileName);
- if (d->fileEngine->open(mode | QIODevice::Unbuffered)) {
- d->useTemporaryFile = false;
- QFileDevice::open(mode);
+ if (openDirectly())
return true;
- }
err = d->fileEngine->error();
}
#endif
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp
index d5460238ec..bbc66120b5 100644
--- a/src/corelib/io/qsettings.cpp
+++ b/src/corelib/io/qsettings.cpp
@@ -1405,6 +1405,11 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
return;
}
+ if (!readOnly && !confFile->isWritable()) {
+ setStatus(QSettings::AccessError);
+ return;
+ }
+
#ifndef QT_BOOTSTRAPPED
/*
Use a lockfile in order to protect us against other QSettings instances
@@ -1414,17 +1419,11 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
Concurrent read and write are not a problem because the writing operation is atomic.
*/
QLockFile lockFile(confFile->name + QLatin1String(".lock"));
-#endif
- if (!readOnly) {
- if (!confFile->isWritable()
-#ifndef QT_BOOTSTRAPPED
- || !lockFile.lock()
-#endif
- ) {
- setStatus(QSettings::AccessError);
- return;
- }
+ if (!readOnly && !lockFile.lock() && atomicSyncOnly) {
+ setStatus(QSettings::AccessError);
+ return;
}
+#endif
/*
We hold the lock. Let's reread the file if it has changed
@@ -1452,7 +1451,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
Files that we can't read (because of permissions or
because they don't exist) are treated as empty files.
*/
- if (file.isReadable() && fileInfo.size() != 0) {
+ if (file.isReadable() && file.size() != 0) {
bool ok = false;
#ifdef Q_OS_MAC
if (format == QSettings::NativeFormat) {
@@ -1496,6 +1495,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
QSaveFile sf(confFile->name);
+ sf.setDirectWriteFallback(!atomicSyncOnly);
#else
QFile sf(confFile->name);
#endif
@@ -1802,6 +1802,8 @@ struct QSettingsIniSection
inline QSettingsIniSection() : position(-1) {}
};
+Q_DECLARE_TYPEINFO(QSettingsIniSection, Q_MOVABLE_TYPE);
+
typedef QMap<QString, QSettingsIniSection> IniMap;
/*
@@ -2199,10 +2201,16 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
QSettings can safely be used from different processes (which can
be different instances of your application running at the same
time or different applications altogether) to read and write to
- the same system locations. It uses advisory file locking and a
- smart merging algorithm to ensure data integrity. Note that sync()
- imports changes made by other processes (in addition to writing
- the changes from this QSettings).
+ the same system locations, provided certain conditions are met. For
+ QSettings::IniFormat, it uses advisory file locking and a smart merging
+ algorithm to ensure data integrity. The condition for that to work is that
+ the writeable configuration file must be a regular file and must reside in
+ a directory that the current user can create new, temporary files in. If
+ that is not the case, then one must use setAtomicSyncRequired() to turn the
+ safety off.
+
+ Note that sync() imports changes made by other processes (in addition to
+ writing the changes from this QSettings).
\section1 Platform-Specific Notes
@@ -2883,6 +2891,50 @@ QSettings::Status QSettings::status() const
}
/*!
+ \since 5.10
+
+ Returns \c true if QSettings is only allowed to perform atomic saving and
+ reloading (synchronization) of the settings. Returns \c false if it is
+ allowed to save the settings contents directly to the configuration file.
+
+ The default is \c true.
+
+ \sa setAtomicSyncRequired(), QSaveFile
+*/
+bool QSettings::isAtomicSyncRequired() const
+{
+ Q_D(const QSettings);
+ return d->atomicSyncOnly;
+}
+
+/*!
+ \since 5.10
+
+ Configures whether QSettings is required to perform atomic saving and
+ reloading (synchronization) of the settings. If the \a enable argument is
+ \c true (the default), sync() will only perform synchronization operations
+ that are atomic. If this is not possible, sync() will fail and status()
+ will be an error condition.
+
+ Setting this property to \c false will allow QSettings to write directly to
+ the configuration file and ignore any errors trying to lock it against
+ other processes trying to write at the same time. Because of the potential
+ for corruption, this option should be used with care, but is required in
+ certain conditions, like a QSettings::IniFormat configuration file that
+ exists in an otherwise non-writeable directory or NTFS Alternate Data
+ Streams.
+
+ See \l QSaveFile for more information on the feature.
+
+ \sa isAtomicSyncRequired(), QSaveFile
+*/
+void QSettings::setAtomicSyncRequired(bool enable)
+{
+ Q_D(QSettings);
+ d->atomicSyncOnly = enable;
+}
+
+/*!
Appends \a prefix to the current group.
The current group is automatically prepended to all keys
diff --git a/src/corelib/io/qsettings.h b/src/corelib/io/qsettings.h
index edd59026ed..da5502e5ca 100644
--- a/src/corelib/io/qsettings.h
+++ b/src/corelib/io/qsettings.h
@@ -146,6 +146,8 @@ public:
void clear();
void sync();
Status status() const;
+ bool isAtomicSyncRequired() const;
+ void setAtomicSyncRequired(bool enable);
void beginGroup(const QString &prefix);
void endGroup();
diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h
index 639605d8c4..7923c24770 100644
--- a/src/corelib/io/qsettings_p.h
+++ b/src/corelib/io/qsettings_p.h
@@ -109,6 +109,8 @@ private:
};
#endif
+Q_DECLARE_TYPEINFO(QSettingsKey, Q_MOVABLE_TYPE);
+
typedef QMap<QSettingsKey, QByteArray> UnparsedSettingsMap;
typedef QMap<QSettingsKey, QVariant> ParsedSettingsMap;
@@ -247,6 +249,7 @@ protected:
QString groupPrefix;
bool fallbacks;
bool pendingChanges;
+ bool atomicSyncOnly = true;
mutable QSettings::Status status;
};
diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp
index f2368c3b23..b3a5bd797a 100644
--- a/src/corelib/io/qstandardpaths.cpp
+++ b/src/corelib/io/qstandardpaths.cpp
@@ -48,6 +48,14 @@
#include <qcoreapplication.h>
#endif
+#if QT_HAS_INCLUDE(<paths.h>)
+#include <paths.h>
+#endif
+
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#endif
+
#ifndef QT_NO_STANDARDPATHS
QT_BEGIN_NAMESPACE
@@ -183,7 +191,7 @@ QT_BEGIN_NAMESPACE
\li "C:/Users/<USER>"
\row \li DataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
- \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data", "<APPDIR>/data/<APPNAME>"
\row \li CacheLocation
\li "~/Library/Caches/<APPNAME>", "/Library/Caches/<APPNAME>"
\li "C:/Users/<USER>/AppData/Local/<APPNAME>/cache"
@@ -207,10 +215,10 @@ QT_BEGIN_NAMESPACE
\li "C:/Users/<USER>/AppData/Local/cache"
\row \li AppDataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
- \li "C:/Users/<USER>/AppData/Roaming/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
+ \li "C:/Users/<USER>/AppData/Roaming/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data", "<APPDIR>/data/<APPNAME>"
\row \li AppLocalDataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
- \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data", "<APPDIR>/data/<APPNAME>"
\row \li AppConfigLocation
\li "~/Library/Preferences/<APPNAME>"
\li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>"
@@ -509,6 +517,27 @@ QString QStandardPaths::findExecutable(const QString &executableName, const QStr
QStringList searchPaths = paths;
if (paths.isEmpty()) {
QByteArray pEnv = qgetenv("PATH");
+ if (Q_UNLIKELY(pEnv.isNull())) {
+ // Get a default path. POSIX.1 does not actually require this, but
+ // most Unix libc fall back to confstr(_CS_PATH) if the PATH
+ // environment variable isn't set. Let's try to do the same.
+#if defined(_PATH_DEFPATH)
+ // BSD API.
+ pEnv = _PATH_DEFPATH;
+#elif defined(_CS_PATH)
+ // POSIX API.
+ size_t n = confstr(_CS_PATH, nullptr, 0);
+ if (n) {
+ pEnv.resize(n);
+ // size()+1 is ok because QByteArray always has an extra NUL-terminator
+ confstr(_CS_PATH, pEnv.data(), pEnv.size() + 1);
+ }
+#else
+ // Windows SDK's execvpe() does not have a fallback, so we won't
+ // apply one either.
+#endif
+ }
+
// Remove trailing slashes, which occur on Windows.
const QStringList rawPaths = QString::fromLocal8Bit(pEnv.constData()).split(QDir::listSeparator(), QString::SkipEmptyParts);
searchPaths.reserve(rawPaths.size());
diff --git a/src/corelib/io/qstandardpaths_unix.cpp b/src/corelib/io/qstandardpaths_unix.cpp
index f0ff46fe7f..d06778e2a1 100644
--- a/src/corelib/io/qstandardpaths_unix.cpp
+++ b/src/corelib/io/qstandardpaths_unix.cpp
@@ -42,6 +42,7 @@
#include <qfile.h>
#include <qhash.h>
#include <qtextstream.h>
+#include <qregularexpression.h>
#include <private/qfilesystemengine_p.h>
#include <errno.h>
#include <stdlib.h>
@@ -179,11 +180,12 @@ QString QStandardPaths::writableLocation(StandardLocation type)
QHash<QString, QString> lines;
QTextStream stream(&file);
// Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop"
- QRegExp exp(QLatin1String("^XDG_(.*)_DIR=(.*)$"));
+ QRegularExpression exp(QLatin1String("^XDG_(.*)_DIR=(.*)$"));
while (!stream.atEnd()) {
const QString &line = stream.readLine();
- if (exp.indexIn(line) != -1) {
- const QStringList lst = exp.capturedTexts();
+ QRegularExpressionMatch match = exp.match(line);
+ if (match.hasMatch()) {
+ const QStringList lst = match.capturedTexts();
const QString key = lst.at(1);
QString value = lst.at(2);
if (value.length() > 2
diff --git a/src/corelib/io/qstandardpaths_win.cpp b/src/corelib/io/qstandardpaths_win.cpp
index a06b204da7..eeb02419c3 100644
--- a/src/corelib/io/qstandardpaths_win.cpp
+++ b/src/corelib/io/qstandardpaths_win.cpp
@@ -227,7 +227,15 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
QString applicationDirPath = qApp ? QCoreApplication::applicationDirPath()
: QFileInfo(qAppFileName()).path();
dirs.append(applicationDirPath);
- dirs.append(applicationDirPath + QLatin1String("/data"));
+ const QString dataDir = applicationDirPath + QLatin1String("/data");
+ dirs.append(dataDir);
+
+ if (!isGenericConfigLocation(type)) {
+ QString appDataDir = dataDir;
+ appendOrganizationAndApp(appDataDir);
+ if (appDataDir != dataDir)
+ dirs.append(appDataDir);
+ }
#endif // !QT_BOOTSTRAPPED
} // isConfigLocation()
diff --git a/src/corelib/io/qstorageinfo.cpp b/src/corelib/io/qstorageinfo.cpp
index 27f0552a31..e2c1f0232f 100644
--- a/src/corelib/io/qstorageinfo.cpp
+++ b/src/corelib/io/qstorageinfo.cpp
@@ -40,6 +40,8 @@
#include "qstorageinfo.h"
#include "qstorageinfo_p.h"
+#include "qdebug.h"
+
QT_BEGIN_NAMESPACE
/*!
@@ -431,4 +433,37 @@ QStorageInfo QStorageInfo::root()
volume than the \a second; otherwise returns false.
*/
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QStorageInfo &s)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug.noquote();
+ debug << "QStorageInfo(";
+ if (s.isValid()) {
+ const QStorageInfoPrivate *d = s.d.constData();
+ debug << '"' << d->rootPath << '"';
+ if (!d->fileSystemType.isEmpty())
+ debug << ", type=" << d->fileSystemType;
+ if (!d->name.isEmpty())
+ debug << ", name=\"" << d->name << '"';
+ if (!d->device.isEmpty())
+ debug << ", device=\"" << d->device << '"';
+ if (!d->subvolume.isEmpty())
+ debug << ", subvolume=\"" << d->subvolume << '"';
+ if (d->readOnly)
+ debug << " [read only]";
+ debug << (d->ready ? " [ready]" : " [not ready]");
+ if (d->bytesTotal > 0) {
+ debug << ", bytesTotal=" << d->bytesTotal << ", bytesFree=" << d->bytesFree
+ << ", bytesAvailable=" << d->bytesAvailable;
+ }
+ } else {
+ debug << "invalid";
+ }
+ debug<< ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
diff --git a/src/corelib/io/qstorageinfo.h b/src/corelib/io/qstorageinfo.h
index e2d9747ceb..4ab7a353ef 100644
--- a/src/corelib/io/qstorageinfo.h
+++ b/src/corelib/io/qstorageinfo.h
@@ -49,6 +49,8 @@
QT_BEGIN_NAMESPACE
+class QDebug;
+
class QStorageInfoPrivate;
class Q_CORE_EXPORT QStorageInfo
{
@@ -94,6 +96,7 @@ public:
private:
friend class QStorageInfoPrivate;
friend bool operator==(const QStorageInfo &first, const QStorageInfo &second);
+ friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QStorageInfo &);
QExplicitlySharedDataPointer<QStorageInfoPrivate> d;
};
@@ -114,6 +117,10 @@ inline bool QStorageInfo::isRoot() const
Q_DECLARE_SHARED(QStorageInfo)
+#ifndef QT_NO_DEBUG_STREAM
+Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QStorageInfo &);
+#endif
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QStorageInfo)
diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp
index 1fc32e0f2d..c2d31e7677 100644
--- a/src/corelib/io/qstorageinfo_unix.cpp
+++ b/src/corelib/io/qstorageinfo_unix.cpp
@@ -107,6 +107,13 @@
# endif // QT_LARGEFILE_SUPPORT
#endif // Q_OS_BSD4
+#if QT_HAS_INCLUDE(<paths.h>)
+# include <paths.h>
+#endif
+#ifndef _PATH_MOUNTED
+# define _PATH_MOUNTED "/etc/mnttab"
+#endif
+
QT_BEGIN_NAMESPACE
class QStorageIterator
@@ -241,11 +248,9 @@ inline QByteArray QStorageIterator::options() const
#elif defined(Q_OS_SOLARIS)
-static const char pathMounted[] = "/etc/mnttab";
-
inline QStorageIterator::QStorageIterator()
{
- const int fd = qt_safe_open(pathMounted, O_RDONLY);
+ const int fd = qt_safe_open(_PATH_MOUNTED, O_RDONLY);
fp = ::fdopen(fd, "r");
}
@@ -282,11 +287,9 @@ inline QByteArray QStorageIterator::device() const
#elif defined(Q_OS_ANDROID)
-static const QLatin1String pathMounted("/proc/mounts");
-
inline QStorageIterator::QStorageIterator()
{
- file.setFileName(pathMounted);
+ file.setFileName(_PATH_MOUNTED);
file.open(QIODevice::ReadOnly | QIODevice::Text);
}
@@ -339,14 +342,13 @@ inline QByteArray QStorageIterator::options() const
#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD)
-static const char pathMounted[] = "/etc/mtab";
static const int bufferSize = 1024; // 2 paths (mount point+device) and metainfo;
// should be enough
inline QStorageIterator::QStorageIterator() :
buffer(QByteArray(bufferSize, 0))
{
- fp = ::setmntent(pathMounted, "r");
+ fp = ::setmntent(_PATH_MOUNTED, "r");
}
inline QStorageIterator::~QStorageIterator()
diff --git a/src/corelib/io/qtemporarydir.cpp b/src/corelib/io/qtemporarydir.cpp
index ffaee9c683..2779246c5b 100644
--- a/src/corelib/io/qtemporarydir.cpp
+++ b/src/corelib/io/qtemporarydir.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -44,22 +44,15 @@
#include "qdiriterator.h"
#include "qplatformdefs.h"
+#include "private/qtemporaryfile_p.h"
#include <QDebug>
#include <QPair>
+#include <QRandomGenerator>
#if defined(QT_BUILD_CORE_LIB)
#include "qcoreapplication.h"
#endif
-#if !defined(Q_OS_QNX) && !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY)
-# define USE_SYSTEM_MKDTEMP
-#endif
-
-#include <stdlib.h> // mkdtemp
-#ifndef USE_SYSTEM_MKDTEMP
-#include <private/qfilesystemengine_p.h>
-#endif
-
#if !defined(Q_OS_WIN)
#include <errno.h>
#endif
@@ -102,38 +95,12 @@ static QString defaultTemplateName()
return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String("-XXXXXX");
}
-#ifndef USE_SYSTEM_MKDTEMP
-static int nextRand(int &v)
-{
- int r = v % 62;
- v /= 62;
- if (v < 62)
- v = qrand();
- return r;
-}
-
-QPair<QString, bool> q_mkdtemp(QString templateName)
+void QTemporaryDirPrivate::create(const QString &templateName)
{
- Q_ASSERT(templateName.endsWith(QLatin1String("XXXXXX")));
-
- static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
- const int length = templateName.size();
-
- QChar *XXXXXX = templateName.data() + length - 6;
-
+ QTemporaryFileName tfn(templateName);
for (int i = 0; i < 256; ++i) {
- int v = qrand();
-
- /* Fill in the random bits. */
- XXXXXX[0] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[1] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[2] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[3] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[4] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[5] = QLatin1Char(letters[v % 62]);
-
- QFileSystemEntry fileSystemEntry(templateName);
+ tfn.generateNext();
+ QFileSystemEntry fileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
if (QFileSystemEngine::createDirectory(fileSystemEntry, false)) {
QSystemError error;
QFileSystemEngine::setPermissions(fileSystemEntry,
@@ -145,7 +112,9 @@ QPair<QString, bool> q_mkdtemp(QString templateName)
qWarning() << "Unable to remove unused directory" << templateName;
continue;
}
- return qMakePair(templateName, true);
+ success = true;
+ pathOrError = fileSystemEntry.filePath();
+ return;
}
# ifdef Q_OS_WIN
const int exists = ERROR_ALREADY_EXISTS;
@@ -155,36 +124,10 @@ QPair<QString, bool> q_mkdtemp(QString templateName)
int code = errno;
# endif
if (code != exists)
- return qMakePair(qt_error_string(code), false);
+ break;
}
- return qMakePair(qt_error_string(), false);
-}
-
-#else // !USE_SYSTEM_MKDTEMP
-
-QPair<QString, bool> q_mkdtemp(char *templateName)
-{
- bool ok = (mkdtemp(templateName) != 0);
- return qMakePair(ok ? QFile::decodeName(templateName) : qt_error_string(), ok);
-}
-
-#endif // USE_SYSTEM_MKDTEMP
-
-void QTemporaryDirPrivate::create(const QString &templateName)
-{
-#ifndef USE_SYSTEM_MKDTEMP
- QString buffer = templateName;
- if (!buffer.endsWith(QLatin1String("XXXXXX")))
- buffer += QLatin1String("XXXXXX");
- const QPair<QString, bool> result = q_mkdtemp(buffer);
-#else // !USE_SYSTEM_MKDTEMP
- QByteArray buffer = QFile::encodeName(templateName);
- if (!buffer.endsWith("XXXXXX"))
- buffer += "XXXXXX";
- QPair<QString, bool> result = q_mkdtemp(buffer.data()); // modifies buffer
-#endif // USE_SYSTEM_MKDTEMP
- pathOrError = result.first;
- success = result.second;
+ pathOrError = qt_error_string();
+ success = false;
}
//************* QTemporaryDir
diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp
index 8a99873fee..35699d52df 100644
--- a/src/corelib/io/qtemporaryfile.cpp
+++ b/src/corelib/io/qtemporaryfile.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -39,9 +40,8 @@
#include "qtemporaryfile.h"
-#ifndef QT_NO_TEMPORARYFILE
-
#include "qplatformdefs.h"
+#include "qrandom.h"
#include "private/qtemporaryfile_p.h"
#include "private/qfile_p.h"
#include "private/qsystemerror_p.h"
@@ -53,6 +53,8 @@
#if defined(QT_BUILD_CORE_LIB)
#include "qcoreapplication.h"
+#else
+#define tr(X) QString::fromLatin1(X)
#endif
QT_BEGIN_NAMESPACE
@@ -73,82 +75,145 @@ typedef char Latin1Char;
typedef int NativeFileHandle;
#endif
-/*
- * Copyright (c) 1987, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
+QTemporaryFileName::QTemporaryFileName(const QString &templateName)
+{
+ // Ensure there is a placeholder mask
+ QString qfilename = templateName;
+ uint phPos = qfilename.length();
+ uint phLength = 0;
+
+ while (phPos != 0) {
+ --phPos;
+
+ if (qfilename[phPos] == QLatin1Char('X')) {
+ ++phLength;
+ continue;
+ }
+
+ if (phLength >= 6
+ || qfilename[phPos] == QLatin1Char('/')) {
+ ++phPos;
+ break;
+ }
+
+ // start over
+ phLength = 0;
+ }
+
+ if (phLength < 6)
+ qfilename.append(QLatin1String(".XXXXXX"));
+
+ // "Nativify" :-)
+ QFileSystemEntry::NativePath filename = QFileSystemEngine::absoluteName(
+ QFileSystemEntry(qfilename, QFileSystemEntry::FromInternalPath()))
+ .nativeFilePath();
+
+ // Find mask in native path
+ phPos = filename.length();
+ phLength = 0;
+ while (phPos != 0) {
+ --phPos;
+
+ if (filename[phPos] == Latin1Char('X')) {
+ ++phLength;
+ continue;
+ }
+
+ if (phLength >= 6) {
+ ++phPos;
+ break;
+ }
+
+ // start over
+ phLength = 0;
+ }
+
+ Q_ASSERT(phLength >= 6);
+ path = filename;
+ pos = phPos;
+ length = phLength;
+}
/*!
\internal
- Generates a unique file path and returns a native handle to the open file.
- \a path is used as a template when generating unique paths, \a pos
- identifies the position of the first character that will be replaced in the
- template and \a length the number of characters that may be substituted.
- \a mode specifies the file mode bits (not used on Windows).
-
- Returns an open handle to the newly created file if successful, an invalid
- handle otherwise. In both cases, the string in \a path will be changed and
- contain the generated path name.
+ Generates a unique file path from the template \a templ and returns it.
+ The path in \c templ.path is modified.
*/
-static bool createFileFromTemplate(NativeFileHandle &file,
- QFileSystemEntry::NativePath &path, size_t pos, size_t length, quint32 mode,
- QSystemError &error)
+QFileSystemEntry::NativePath QTemporaryFileName::generateNext()
{
Q_ASSERT(length != 0);
- Q_ASSERT(pos < size_t(path.length()));
- Q_ASSERT(length <= size_t(path.length()) - pos);
+ Q_ASSERT(pos < path.length());
+ Q_ASSERT(length <= path.length() - pos);
Char *const placeholderStart = (Char *)path.data() + pos;
Char *const placeholderEnd = placeholderStart + length;
- // Initialize placeholder with random chars + PID.
+ // Replace placeholder with random chars.
{
+ // Since our dictionary is 26+26 characters, it would seem we only need
+ // a random number from 0 to 63 to select a character. However, due to
+ // the limited range, that would mean 12 (64-52) characters have double
+ // the probability of the others: 1 in 32 instead of 1 in 64.
+ //
+ // To overcome this limitation, we use more bits per character. With 10
+ // bits, there are 16 characters with probability 19/1024 and the rest
+ // at 20/1024 (i.e, less than .1% difference). This allows us to do 3
+ // characters per 32-bit random number, which is also half the typical
+ // placeholder length.
+ enum { BitsPerCharacter = 10 };
+
Char *rIter = placeholderEnd;
+ while (rIter != placeholderStart) {
+ quint32 rnd = QRandomGenerator::global()->generate();
+ auto applyOne = [&]() {
+ quint32 v = rnd & ((1 << BitsPerCharacter) - 1);
+ rnd >>= BitsPerCharacter;
+ char ch = char((26 + 26) * v / (1 << BitsPerCharacter));
+ if (ch < 26)
+ *--rIter = Latin1Char(ch + 'A');
+ else
+ *--rIter = Latin1Char(ch - 26 + 'a');
+ };
+
+ applyOne();
+ if (rIter == placeholderStart)
+ break;
-#if defined(QT_BUILD_CORE_LIB)
- quint64 pid = quint64(QCoreApplication::applicationPid());
- do {
- *--rIter = Latin1Char((pid % 10) + '0');
- pid /= 10;
- } while (rIter != placeholderStart && pid != 0);
-#endif
+ applyOne();
+ if (rIter == placeholderStart)
+ break;
- while (rIter != placeholderStart) {
- char ch = char((qrand() & 0xffff) % (26 + 26));
- if (ch < 26)
- *--rIter = Latin1Char(ch + 'A');
- else
- *--rIter = Latin1Char(ch - 26 + 'a');
+ applyOne();
}
}
- for (;;) {
+ return path;
+}
+
+#ifndef QT_NO_TEMPORARYFILE
+
+/*!
+ \internal
+
+ Generates a unique file path from the template \a templ and creates a new
+ file based based on those parameters: the \c templ.length characters in \c
+ templ.path starting at \c templ.pos will be replacd by a random sequence of
+ characters. \a mode specifies the file mode bits (not used on Windows).
+
+ Returns true on success and sets the file handle on \a file. On error,
+ returns false, sets an invalid handle on \a handle and sets the error
+ condition in \a error. In both cases, the string in \a templ will be
+ changed and contain the generated path name.
+*/
+static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
+ quint32 mode, QSystemError &error)
+{
+ const int maxAttempts = 16;
+ for (int attempt = 0; attempt < maxAttempts; ++attempt) {
// Atomically create file and obtain handle
+ const QFileSystemEntry::NativePath &path = templ.generateNext();
+
#if defined(Q_OS_WIN)
Q_UNUSED(mode);
@@ -195,45 +260,58 @@ static bool createFileFromTemplate(NativeFileHandle &file,
return false;
}
#endif
-
- /* tricky little algorwwithm for backward compatibility */
- for (Char *iter = placeholderStart;;) {
- // Character progression: [0-9] => 'a' ... 'z' => 'A' .. 'Z'
- // String progression: "ZZaiC" => "aabiC"
- switch (char(*iter)) {
- case 'Z':
- // Rollover, advance next character
- *iter = Latin1Char('a');
- if (++iter == placeholderEnd) {
- // Out of alternatives. Return file exists error, previously set.
- error = QSystemError(err, QSystemError::NativeError);
- return false;
- }
-
- continue;
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- *iter = Latin1Char('a');
- break;
-
- case 'z':
- // increment 'z' to 'A'
- *iter = Latin1Char('A');
- break;
-
- default:
- ++*iter;
- break;
- }
- break;
- }
}
- Q_ASSERT(false);
return false;
}
+enum class CreateUnnamedFileStatus {
+ Success = 0,
+ NotSupported,
+ OtherError
+};
+
+static CreateUnnamedFileStatus
+createUnnamedFile(NativeFileHandle &file, QTemporaryFileName &tfn, quint32 mode, QSystemError *error)
+{
+#ifdef LINUX_UNNAMED_TMPFILE
+ // first, check if we have /proc, otherwise can't make the file exist later
+ // (no error message set, as caller will try regular temporary file)
+ if (!qt_haveLinuxProcfs())
+ return CreateUnnamedFileStatus::NotSupported;
+
+ const char *p = ".";
+ int lastSlash = tfn.path.lastIndexOf('/');
+ if (lastSlash != -1) {
+ tfn.path[lastSlash] = '\0';
+ p = tfn.path.data();
+ }
+
+ file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
+ static_cast<mode_t>(mode));
+ if (file != -1)
+ return CreateUnnamedFileStatus::Success;
+
+ if (errno == EOPNOTSUPP || errno == EISDIR) {
+ // fs or kernel doesn't support O_TMPFILE, so
+ // put the slash back so we may try a regular file
+ if (lastSlash != -1)
+ tfn.path[lastSlash] = '/';
+ return CreateUnnamedFileStatus::NotSupported;
+ }
+
+ // real error
+ *error = QSystemError(errno, QSystemError::NativeError);
+ return CreateUnnamedFileStatus::OtherError;
+#else
+ Q_UNUSED(file);
+ Q_UNUSED(tfn);
+ Q_UNUSED(mode);
+ Q_UNUSED(error);
+ return CreateUnnamedFileStatus::NotSupported;
+#endif
+}
+
//************* QTemporaryFileEngine
QTemporaryFileEngine::~QTemporaryFileEngine()
{
@@ -264,13 +342,6 @@ void QTemporaryFileEngine::setFileName(const QString &file)
QFSFileEngine::setFileName(file);
}
-void QTemporaryFileEngine::setFileTemplate(const QString &fileTemplate)
-{
- Q_D(QFSFileEngine);
- if (filePathIsTemplate)
- d->fileEntry = QFileSystemEntry(fileTemplate);
-}
-
bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
{
Q_D(QFSFileEngine);
@@ -281,59 +352,7 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
if (!filePathIsTemplate)
return QFSFileEngine::open(openMode);
- QString qfilename = d->fileEntry.filePath();
-
- // Ensure there is a placeholder mask
- uint phPos = qfilename.length();
- uint phLength = 0;
-
- while (phPos != 0) {
- --phPos;
-
- if (qfilename[phPos] == QLatin1Char('X')) {
- ++phLength;
- continue;
- }
-
- if (phLength >= 6
- || qfilename[phPos] == QLatin1Char('/')) {
- ++phPos;
- break;
- }
-
- // start over
- phLength = 0;
- }
-
- if (phLength < 6)
- qfilename.append(QLatin1String(".XXXXXX"));
-
- // "Nativify" :-)
- QFileSystemEntry::NativePath filename = QFileSystemEngine::absoluteName(
- QFileSystemEntry(qfilename, QFileSystemEntry::FromInternalPath()))
- .nativeFilePath();
-
- // Find mask in native path
- phPos = filename.length();
- phLength = 0;
- while (phPos != 0) {
- --phPos;
-
- if (filename[phPos] == Latin1Char('X')) {
- ++phLength;
- continue;
- }
-
- if (phLength >= 6) {
- ++phPos;
- break;
- }
-
- // start over
- phLength = 0;
- }
-
- Q_ASSERT(phLength >= 6);
+ QTemporaryFileName tfn(templateName);
QSystemError error;
#if defined(Q_OS_WIN)
@@ -342,19 +361,24 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
NativeFileHandle &file = d->fd;
#endif
- if (!createFileFromTemplate(file, filename, phPos, phLength, fileMode, error)) {
+ CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
+ if (st == CreateUnnamedFileStatus::Success) {
+ unnamedFile = true;
+ d->fileEntry.clear();
+ } else if (st == CreateUnnamedFileStatus::NotSupported &&
+ createFileFromTemplate(file, tfn, fileMode, error)) {
+ filePathIsTemplate = false;
+ unnamedFile = false;
+ d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
+ } else {
setError(QFile::OpenError, error.toString());
return false;
}
- d->fileEntry = QFileSystemEntry(filename, QFileSystemEntry::FromNativePath());
-
#if !defined(Q_OS_WIN) || defined(Q_OS_WINRT)
d->closeFileHandle = true;
#endif
- filePathIsTemplate = false;
-
d->openMode = openMode;
d->lastFlushFailed = false;
d->tried_stat = 0;
@@ -369,7 +393,9 @@ bool QTemporaryFileEngine::remove()
// we must explicitly call QFSFileEngine::close() before we remove it.
d->unmapAll();
QFSFileEngine::close();
- if (QFSFileEngine::remove()) {
+ if (isUnnamedFile())
+ return true;
+ if (!filePathIsTemplate && QFSFileEngine::remove()) {
d->fileEntry.clear();
// If a QTemporaryFile is constructed using a template file path, the path
// is generated in QTemporaryFileEngine::open() and then filePathIsTemplate
@@ -384,12 +410,22 @@ bool QTemporaryFileEngine::remove()
bool QTemporaryFileEngine::rename(const QString &newName)
{
+ if (isUnnamedFile()) {
+ bool ok = materializeUnnamedFile(newName, DontOverwrite);
+ QFSFileEngine::close();
+ return ok;
+ }
QFSFileEngine::close();
return QFSFileEngine::rename(newName);
}
bool QTemporaryFileEngine::renameOverwrite(const QString &newName)
{
+ if (isUnnamedFile()) {
+ bool ok = materializeUnnamedFile(newName, Overwrite);
+ QFSFileEngine::close();
+ return ok;
+ }
QFSFileEngine::close();
return QFSFileEngine::renameOverwrite(newName);
}
@@ -402,6 +438,91 @@ bool QTemporaryFileEngine::close()
return true;
}
+QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file) const
+{
+ if (isUnnamedFile()) {
+ if (file == LinkName) {
+ // we know our file isn't (won't be) a symlink
+ return QString();
+ }
+
+ // for all other cases, materialize the file
+ const_cast<QTemporaryFileEngine *>(this)->materializeUnnamedFile(templateName, NameIsTemplate);
+ }
+ return QFSFileEngine::fileName(file);
+}
+
+bool QTemporaryFileEngine::materializeUnnamedFile(const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
+{
+ Q_ASSERT(isUnnamedFile());
+
+#ifdef LINUX_UNNAMED_TMPFILE
+ Q_D(QFSFileEngine);
+ const QByteArray src = "/proc/self/fd/" + QByteArray::number(d->fd);
+ auto materializeAt = [=](const QFileSystemEntry &dst) {
+ return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
+ };
+#else
+ auto materializeAt = [](const QFileSystemEntry &) { return false; };
+#endif
+
+ auto success = [this](const QFileSystemEntry &entry) {
+ filePathIsTemplate = false;
+ unnamedFile = false;
+ d_func()->fileEntry = entry;
+ return true;
+ };
+
+ auto materializeAsTemplate = [=](const QString &newName) {
+ QTemporaryFileName tfn(newName);
+ static const int maxAttempts = 16;
+ for (int attempt = 0; attempt < maxAttempts; ++attempt) {
+ tfn.generateNext();
+ QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
+ if (materializeAt(entry))
+ return success(entry);
+ }
+ return false;
+ };
+
+ if (mode == NameIsTemplate) {
+ if (materializeAsTemplate(newName))
+ return true;
+ } else {
+ // Use linkat to materialize the file
+ QFileSystemEntry dst(newName);
+ if (materializeAt(dst))
+ return success(dst);
+
+ if (errno == EEXIST && mode == Overwrite) {
+ // retry by first creating a temporary file in the right dir
+ if (!materializeAsTemplate(templateName))
+ return false;
+
+ // then rename the materialized file to target (same as renameOverwrite)
+ QFSFileEngine::close();
+ return QFSFileEngine::renameOverwrite(newName);
+ }
+ }
+
+ // failed
+ setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
+ return false;
+}
+
+bool QTemporaryFileEngine::isUnnamedFile() const
+{
+#ifdef LINUX_UNNAMED_TMPFILE
+ if (unnamedFile) {
+ Q_ASSERT(d_func()->fileEntry.isEmpty());
+ Q_ASSERT(filePathIsTemplate);
+ }
+ return unnamedFile;
+#else
+ return false;
+#endif
+}
+
//************* QTemporaryFilePrivate
QTemporaryFilePrivate::QTemporaryFilePrivate()
@@ -420,7 +541,7 @@ QTemporaryFilePrivate::~QTemporaryFilePrivate()
QAbstractFileEngine *QTemporaryFilePrivate::engine() const
{
if (!fileEngine) {
- fileEngine = new QTemporaryFileEngine;
+ fileEngine = new QTemporaryFileEngine(&templateName);
resetFileEngine();
}
return fileEngine;
@@ -438,6 +559,17 @@ void QTemporaryFilePrivate::resetFileEngine() const
tef->initialize(fileName, 0600, false);
}
+void QTemporaryFilePrivate::materializeUnnamedFile()
+{
+#ifdef LINUX_UNNAMED_TMPFILE
+ if (!fileName.isEmpty() || !fileEngine)
+ return;
+
+ auto *tef = static_cast<QTemporaryFileEngine *>(fileEngine);
+ fileName = tef->fileName(QAbstractFileEngine::DefaultName);
+#endif
+}
+
QString QTemporaryFilePrivate::defaultTemplateName()
{
QString baseName;
@@ -622,10 +754,21 @@ bool QTemporaryFile::autoRemove() const
}
/*!
- Sets the QTemporaryFile into auto-remove mode if \a b is true.
+ Sets the QTemporaryFile into auto-remove mode if \a b is \c true.
Auto-remove is on by default.
+ If you set this property to \c false, ensure the application provides a way
+ to remove the file once it is no longer needed, including passing the
+ responsibility on to another process. Always use the fileName() function to
+ obtain the name and never try to guess the name that QTemporaryFile has
+ generated.
+
+ On some systems, if fileName() is not called before closing the file, the
+ temporary file may be removed regardless of the state of this property.
+ This behavior should not be relied upon, so application code should either
+ call fileName() or leave the auto removal functionality enabled.
+
\sa autoRemove(), remove()
*/
void QTemporaryFile::setAutoRemove(bool b)
@@ -646,6 +789,10 @@ void QTemporaryFile::setAutoRemove(bool b)
QString QTemporaryFile::fileName() const
{
Q_D(const QTemporaryFile);
+ auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (tef && tef->isReallyOpen())
+ const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
+
if(d->fileName.isEmpty())
return QString();
return d->engine()->fileName(QAbstractFileEngine::DefaultName);
@@ -679,8 +826,36 @@ void QTemporaryFile::setFileTemplate(const QString &name)
{
Q_D(QTemporaryFile);
d->templateName = name;
- if (d->fileEngine)
- static_cast<QTemporaryFileEngine*>(d->fileEngine)->setFileTemplate(name);
+}
+
+/*!
+ \internal
+
+ This is just a simplified version of QFile::rename() because we know a few
+ extra details about what kind of file we have. The documentation is hidden
+ from the user because QFile::rename() should be enough.
+*/
+bool QTemporaryFile::rename(const QString &newName)
+{
+ Q_D(QTemporaryFile);
+ auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
+ return QFile::rename(newName);
+
+ unsetError();
+ close();
+ if (error() == QFile::NoError) {
+ if (tef->rename(newName)) {
+ unsetError();
+ // engine was able to handle the new name so we just reset it
+ tef->setFileName(newName);
+ d->fileName = newName;
+ return true;
+ }
+
+ d->setError(QFile::RenameError, tef->errorString());
+ }
+ return false;
}
/*!
@@ -771,11 +946,10 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
bool QTemporaryFile::open(OpenMode flags)
{
Q_D(QTemporaryFile);
- if (!d->fileName.isEmpty()) {
- if (static_cast<QTemporaryFileEngine*>(d->engine())->isReallyOpen()) {
- setOpenMode(flags);
- return true;
- }
+ auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (tef && tef->isReallyOpen()) {
+ setOpenMode(flags);
+ return true;
}
// reset the engine state so it creates a new, unique file name from the template;
@@ -786,16 +960,20 @@ bool QTemporaryFile::open(OpenMode flags)
d->resetFileEngine();
if (QFile::open(flags)) {
- d->fileName = d->fileEngine->fileName(QAbstractFileEngine::DefaultName);
+ tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (tef->isUnnamedFile())
+ d->fileName.clear();
+ else
+ d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
return true;
}
return false;
}
-QT_END_NAMESPACE
-
#endif // QT_NO_TEMPORARYFILE
+QT_END_NAMESPACE
+
#ifndef QT_NO_QOBJECT
#include "moc_qtemporaryfile.cpp"
#endif
diff --git a/src/corelib/io/qtemporaryfile.h b/src/corelib/io/qtemporaryfile.h
index 3dc2e75f50..7fc5a299fc 100644
--- a/src/corelib/io/qtemporaryfile.h
+++ b/src/corelib/io/qtemporaryfile.h
@@ -80,6 +80,10 @@ public:
QString fileName() const Q_DECL_OVERRIDE;
QString fileTemplate() const;
void setFileTemplate(const QString &name);
+
+ // Hides QFile::rename
+ bool rename(const QString &newName);
+
#if QT_DEPRECATED_SINCE(5,1)
QT_DEPRECATED inline static QTemporaryFile *createLocalFile(const QString &fileName)
{ return createNativeFile(fileName); }
diff --git a/src/corelib/io/qtemporaryfile_p.h b/src/corelib/io/qtemporaryfile_p.h
index 7f365f0e8a..fb8887af53 100644
--- a/src/corelib/io/qtemporaryfile_p.h
+++ b/src/corelib/io/qtemporaryfile_p.h
@@ -53,25 +53,48 @@
#include <QtCore/qglobal.h>
-#ifndef QT_NO_TEMPORARYFILE
-
#include "private/qfsfileengine_p.h"
#include "private/qfilesystemengine_p.h"
#include "private/qfile_p.h"
+#include "qtemporaryfile.h"
+
+#if defined(Q_OS_LINUX) && QT_CONFIG(linkat)
+# include <fcntl.h>
+# ifdef O_TMPFILE
+// some early libc support had the wrong values for O_TMPFILE
+// (see https://bugzilla.gnome.org/show_bug.cgi?id=769453#c18)
+# if (O_TMPFILE & O_DIRECTORY) == O_DIRECTORY
+# define LINUX_UNNAMED_TMPFILE
+# endif
+# endif
+#endif
QT_BEGIN_NAMESPACE
+struct QTemporaryFileName
+{
+ QFileSystemEntry::NativePath path;
+ qsizetype pos;
+ qsizetype length;
+
+ QTemporaryFileName(const QString &templateName);
+ QFileSystemEntry::NativePath generateNext();
+};
+
+#ifndef QT_NO_TEMPORARYFILE
+
class QTemporaryFilePrivate : public QFilePrivate
{
Q_DECLARE_PUBLIC(QTemporaryFile)
-protected:
+public:
QTemporaryFilePrivate();
explicit QTemporaryFilePrivate(const QString &templateNameIn);
~QTemporaryFilePrivate();
QAbstractFileEngine *engine() const override;
void resetFileEngine() const;
+ void materializeUnnamedFile();
bool autoRemove = true;
QString templateName = defaultTemplateName();
@@ -85,37 +108,50 @@ class QTemporaryFileEngine : public QFSFileEngine
{
Q_DECLARE_PRIVATE(QFSFileEngine)
public:
+ QTemporaryFileEngine(const QString *templateName)
+ : templateName(*templateName)
+ {}
+
void initialize(const QString &file, quint32 mode, bool nameIsTemplate = true)
{
Q_D(QFSFileEngine);
Q_ASSERT(!isReallyOpen());
fileMode = mode;
filePathIsTemplate = filePathWasTemplate = nameIsTemplate;
- d->fileEntry = QFileSystemEntry(file);
- if (!filePathIsTemplate)
+ if (filePathIsTemplate) {
+ d->fileEntry.clear();
+ } else {
+ d->fileEntry = QFileSystemEntry(file);
QFSFileEngine::setFileName(file);
+ }
}
~QTemporaryFileEngine();
bool isReallyOpen() const;
void setFileName(const QString &file) override;
- void setFileTemplate(const QString &fileTemplate);
bool open(QIODevice::OpenMode flags) override;
bool remove() override;
bool rename(const QString &newName) override;
bool renameOverwrite(const QString &newName) override;
bool close() override;
+ QString fileName(FileName file) const override;
+ enum MaterializationMode { Overwrite, DontOverwrite, NameIsTemplate };
+ bool materializeUnnamedFile(const QString &newName, MaterializationMode mode);
+ bool isUnnamedFile() const override final;
+
+ const QString &templateName;
quint32 fileMode;
bool filePathIsTemplate;
bool filePathWasTemplate;
+ bool unnamedFile = false;
};
-QT_END_NAMESPACE
-
#endif // QT_NO_TEMPORARYFILE
+QT_END_NAMESPACE
+
#endif /* QTEMPORARYFILE_P_H */
diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp
index 5144ac0ec9..ee3cb4efcb 100644
--- a/src/corelib/io/qtextstream.cpp
+++ b/src/corelib/io/qtextstream.cpp
@@ -954,8 +954,7 @@ QTextStreamPrivate::PaddingResult QTextStreamPrivate::padding(int len) const
right = padSize - padSize/2;
break;
}
- const PaddingResult result = { left, right };
- return result;
+ return { left, right };
}
/*!
diff --git a/src/corelib/io/qtldurl.cpp b/src/corelib/io/qtldurl.cpp
index 96543bbbfd..a934d19fa2 100644
--- a/src/corelib/io/qtldurl.cpp
+++ b/src/corelib/io/qtldurl.cpp
@@ -50,9 +50,21 @@
QT_BEGIN_NAMESPACE
-static bool containsTLDEntry(const QStringRef &entry)
+enum TLDMatchType {
+ ExactMatch,
+ SuffixMatch,
+ ExceptionMatch,
+};
+
+static bool containsTLDEntry(QStringView entry, TLDMatchType match)
{
- int index = qt_hash(entry) % tldCount;
+ const QStringView matchSymbols[] = {
+ QStringViewLiteral(""),
+ QStringViewLiteral("*"),
+ QStringViewLiteral("!"),
+ };
+ const auto symbol = matchSymbols[match];
+ int index = qt_hash(entry, qt_hash(symbol)) % tldCount;
// select the right chunk from the big table
short chunk = 0;
@@ -65,19 +77,14 @@ static bool containsTLDEntry(const QStringRef &entry)
// check all the entries from the given index
while (chunkIndex < tldIndices[index+1] - offset) {
- QString currentEntry = QString::fromUtf8(tldData[chunk] + chunkIndex);
- if (currentEntry == entry)
+ const auto utf8 = tldData[chunk] + chunkIndex;
+ if ((symbol.isEmpty() || QLatin1Char(*utf8) == symbol) && entry == QString::fromUtf8(utf8 + symbol.size()))
return true;
- chunkIndex += qstrlen(tldData[chunk] + chunkIndex) + 1; // +1 for the ending \0
+ chunkIndex += qstrlen(utf8) + 1; // +1 for the ending \0
}
return false;
}
-static inline bool containsTLDEntry(const QString &entry)
-{
- return containsTLDEntry(QStringRef(&entry));
-}
-
/*!
\internal
@@ -111,19 +118,16 @@ Q_CORE_EXPORT bool qIsEffectiveTLD(const QStringRef &domain)
{
// for domain 'foo.bar.com':
// 1. return if TLD table contains 'foo.bar.com'
- if (containsTLDEntry(domain))
+ // 2. else if table contains '*.bar.com',
+ // 3. test that table does not contain '!foo.bar.com'
+
+ if (containsTLDEntry(domain, ExactMatch)) // 1
return true;
const int dot = domain.indexOf(QLatin1Char('.'));
if (dot >= 0) {
- int count = domain.size() - dot;
- QString wildCardDomain = QLatin1Char('*') + domain.right(count);
- // 2. if table contains '*.bar.com',
- // test if table contains '!foo.bar.com'
- if (containsTLDEntry(wildCardDomain)) {
- QString exceptionDomain = QLatin1Char('!') + domain;
- return (! containsTLDEntry(exceptionDomain));
- }
+ if (containsTLDEntry(domain.mid(dot), SuffixMatch)) // 2
+ return !containsTLDEntry(domain, ExceptionMatch); // 3
}
return false;
}
diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp
index b93fed5ba8..ef5ff2d827 100644
--- a/src/corelib/io/qwindowspipereader.cpp
+++ b/src/corelib/io/qwindowspipereader.cpp
@@ -132,17 +132,8 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
actualReadBufferSize--;
readSoFar = 1;
} else {
- qint64 bytesToRead = qMin(actualReadBufferSize, maxlen);
- readSoFar = 0;
- while (readSoFar < bytesToRead) {
- const char *ptr = readBuffer.readPointer();
- qint64 bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar,
- readBuffer.nextDataBlockSize());
- memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
- readSoFar += bytesToReadFromThisBlock;
- readBuffer.free(bytesToReadFromThisBlock);
- actualReadBufferSize -= bytesToReadFromThisBlock;
- }
+ readSoFar = readBuffer.read(data, qMin(actualReadBufferSize, maxlen));
+ actualReadBufferSize -= readSoFar;
}
if (!pipeBroken) {
diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp
index 846891102f..75cb8a7ede 100644
--- a/src/corelib/io/qwindowspipewriter.cpp
+++ b/src/corelib/io/qwindowspipewriter.cpp
@@ -57,7 +57,6 @@ QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
: QObject(parent),
handle(pipeWriteEnd),
overlapped(nullptr),
- numberOfBytesToWrite(0),
pendingBytesWrittenValue(0),
stopped(true),
writeSequenceStarted(false),
@@ -98,7 +97,7 @@ bool QWindowsPipeWriter::waitForWrite(int msecs)
qint64 QWindowsPipeWriter::bytesToWrite() const
{
- return numberOfBytesToWrite + pendingBytesWrittenValue;
+ return buffer.size() + pendingBytesWrittenValue;
}
void QWindowsPipeWriter::emitPendingBytesWrittenValue()
@@ -137,7 +136,6 @@ void QWindowsPipeWriter::notified(DWORD errorCode, DWORD numberOfBytesWritten)
{
notifiedCalled = true;
writeSequenceStarted = false;
- numberOfBytesToWrite = 0;
Q_ASSERT(errorCode != ERROR_SUCCESS || numberOfBytesWritten == DWORD(buffer.size()));
buffer.clear();
@@ -193,13 +191,11 @@ bool QWindowsPipeWriter::write(const QByteArray &ba)
overlapped = new Overlapped(this);
overlapped->clear();
buffer = ba;
- numberOfBytesToWrite = buffer.size();
stopped = false;
writeSequenceStarted = true;
- if (!WriteFileEx(handle, buffer.constData(), numberOfBytesToWrite,
+ if (!WriteFileEx(handle, buffer.constData(), buffer.size(),
overlapped, &writeFileCompleted)) {
writeSequenceStarted = false;
- numberOfBytesToWrite = 0;
buffer.clear();
const DWORD errorCode = GetLastError();
diff --git a/src/corelib/io/qwindowspipewriter_p.h b/src/corelib/io/qwindowspipewriter_p.h
index c8252e2a4b..d6671c3f27 100644
--- a/src/corelib/io/qwindowspipewriter_p.h
+++ b/src/corelib/io/qwindowspipewriter_p.h
@@ -145,7 +145,6 @@ private:
HANDLE handle;
Overlapped *overlapped;
QByteArray buffer;
- qint64 numberOfBytesToWrite;
qint64 pendingBytesWrittenValue;
bool stopped;
bool writeSequenceStarted;
diff --git a/src/corelib/io/qwinoverlappedionotifier.cpp b/src/corelib/io/qwinoverlappedionotifier.cpp
deleted file mode 100644
index d7745ae1b6..0000000000
--- a/src/corelib/io/qwinoverlappedionotifier.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qwinoverlappedionotifier_p.h"
-#include <qdebug.h>
-#include <qatomic.h>
-#include <qelapsedtimer.h>
-#include <qmutex.h>
-#include <qpointer.h>
-#include <qqueue.h>
-#include <qset.h>
-#include <qthread.h>
-#include <qt_windows.h>
-#include <private/qobject_p.h>
-#include <private/qiodevice_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QWinOverlappedIoNotifier
- \inmodule QtCore
- \brief The QWinOverlappedIoNotifier class provides support for overlapped I/O notifications on Windows.
- \since 5.0
- \internal
-
- The QWinOverlappedIoNotifier class makes it possible to use efficient
- overlapped (asynchronous) I/O notifications on Windows by using an
- I/O completion port.
-
- Once you have obtained a file handle, you can use setHandle() to get
- notifications for I/O operations. Whenever an I/O operation completes,
- the notified() signal is emitted which will pass the number of transferred
- bytes, the operation's error code and a pointer to the operation's
- OVERLAPPED object to the receiver.
-
- Every handle that supports overlapped I/O can be used by
- QWinOverlappedIoNotifier. That includes file handles, TCP sockets
- and named pipes.
-
- Note that you must not use ReadFileEx() and WriteFileEx() together
- with QWinOverlappedIoNotifier. They are not supported as they use a
- different I/O notification mechanism.
-
- The hEvent member in the OVERLAPPED structure passed to ReadFile()
- or WriteFile() is ignored and can be used for other purposes.
-
- \warning This class is only available on Windows.
-
- Due to peculiarities of the Windows I/O completion port API, users of
- QWinOverlappedIoNotifier must pay attention to the following restrictions:
- \list
- \li File handles with a QWinOverlappedIoNotifer are assigned to an I/O
- completion port until the handle is closed. It is impossible to
- disassociate the file handle from the I/O completion port.
- \li There can be only one QWinOverlappedIoNotifer per file handle. Creating
- another QWinOverlappedIoNotifier for that file, even with a duplicated
- handle, will fail.
- \li Certain Windows API functions are unavailable for file handles that are
- assigned to an I/O completion port. This includes the functions
- \c{ReadFileEx} and \c{WriteFileEx}.
- \endlist
- See also the remarks in the MSDN documentation for the
- \c{CreateIoCompletionPort} function.
-*/
-
-struct IOResult
-{
- IOResult(DWORD n = 0, DWORD e = 0, OVERLAPPED *p = 0)
- : numberOfBytes(n), errorCode(e), overlapped(p)
- {}
-
- DWORD numberOfBytes;
- DWORD errorCode;
- OVERLAPPED *overlapped;
-};
-
-
-class QWinIoCompletionPort;
-
-class QWinOverlappedIoNotifierPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QWinOverlappedIoNotifier)
-public:
- QWinOverlappedIoNotifierPrivate()
- : hHandle(INVALID_HANDLE_VALUE)
- {
- }
-
- OVERLAPPED *waitForAnyNotified(int msecs);
- void notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped);
- void _q_notified();
- OVERLAPPED *dispatchNextIoResult();
-
- static QWinIoCompletionPort *iocp;
- static HANDLE iocpInstanceLock;
- static unsigned int iocpInstanceRefCount;
- HANDLE hHandle;
- HANDLE hSemaphore;
- HANDLE hResultsMutex;
- QAtomicInt waiting;
- QQueue<IOResult> results;
-};
-
-QWinIoCompletionPort *QWinOverlappedIoNotifierPrivate::iocp = 0;
-HANDLE QWinOverlappedIoNotifierPrivate::iocpInstanceLock = CreateMutex(NULL, FALSE, NULL);
-unsigned int QWinOverlappedIoNotifierPrivate::iocpInstanceRefCount = 0;
-
-
-class QWinIoCompletionPort : protected QThread
-{
-public:
- QWinIoCompletionPort()
- : finishThreadKey(reinterpret_cast<ULONG_PTR>(this)),
- drainQueueKey(reinterpret_cast<ULONG_PTR>(this + 1)),
- hPort(INVALID_HANDLE_VALUE)
- {
- setObjectName(QLatin1String("I/O completion port thread"));
- HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
- if (!hIOCP) {
- qErrnoWarning("CreateIoCompletionPort failed.");
- return;
- }
- hPort = hIOCP;
- hQueueDrainedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (!hQueueDrainedEvent) {
- qErrnoWarning("CreateEvent failed.");
- return;
- }
- }
-
- ~QWinIoCompletionPort()
- {
- PostQueuedCompletionStatus(hPort, 0, finishThreadKey, NULL);
- QThread::wait();
- CloseHandle(hPort);
- CloseHandle(hQueueDrainedEvent);
- }
-
- void registerNotifier(QWinOverlappedIoNotifierPrivate *notifier)
- {
- const HANDLE hHandle = notifier->hHandle;
- HANDLE hIOCP = CreateIoCompletionPort(hHandle, hPort,
- reinterpret_cast<ULONG_PTR>(notifier), 0);
- if (!hIOCP) {
- qErrnoWarning("Can't associate file handle %x with I/O completion port.", hHandle);
- return;
- }
- mutex.lock();
- notifiers += notifier;
- mutex.unlock();
- if (!QThread::isRunning())
- QThread::start();
- }
-
- void unregisterNotifier(QWinOverlappedIoNotifierPrivate *notifier)
- {
- mutex.lock();
- notifiers.remove(notifier);
- mutex.unlock();
- }
-
- void drainQueue()
- {
- QMutexLocker locker(&drainQueueMutex);
- ResetEvent(hQueueDrainedEvent);
- PostQueuedCompletionStatus(hPort, 0, drainQueueKey, NULL);
- WaitForSingleObject(hQueueDrainedEvent, INFINITE);
- }
-
- using QThread::isRunning;
-
-protected:
- void run()
- {
- DWORD dwBytesRead;
- ULONG_PTR pulCompletionKey;
- OVERLAPPED *overlapped;
- DWORD msecs = INFINITE;
-
- forever {
- BOOL success = GetQueuedCompletionStatus(hPort,
- &dwBytesRead,
- &pulCompletionKey,
- &overlapped,
- msecs);
-
- DWORD errorCode = success ? ERROR_SUCCESS : GetLastError();
- if (!success && !overlapped) {
- if (!msecs) {
- // Time out in drain mode. The completion status queue is empty.
- msecs = INFINITE;
- SetEvent(hQueueDrainedEvent);
- continue;
- }
- qErrnoWarning(errorCode, "GetQueuedCompletionStatus failed.");
- return;
- }
-
- if (pulCompletionKey == finishThreadKey)
- return;
- if (pulCompletionKey == drainQueueKey) {
- // Enter drain mode.
- Q_ASSERT(msecs == INFINITE);
- msecs = 0;
- continue;
- }
-
- QWinOverlappedIoNotifierPrivate *notifier
- = reinterpret_cast<QWinOverlappedIoNotifierPrivate *>(pulCompletionKey);
- mutex.lock();
- if (notifiers.contains(notifier))
- notifier->notify(dwBytesRead, errorCode, overlapped);
- mutex.unlock();
- }
- }
-
-private:
- const ULONG_PTR finishThreadKey;
- const ULONG_PTR drainQueueKey;
- HANDLE hPort;
- QSet<QWinOverlappedIoNotifierPrivate *> notifiers;
- QMutex mutex;
- QMutex drainQueueMutex;
- HANDLE hQueueDrainedEvent;
-};
-
-
-QWinOverlappedIoNotifier::QWinOverlappedIoNotifier(QObject *parent)
- : QObject(*new QWinOverlappedIoNotifierPrivate, parent)
-{
- Q_D(QWinOverlappedIoNotifier);
- WaitForSingleObject(d->iocpInstanceLock, INFINITE);
- if (!d->iocp)
- d->iocp = new QWinIoCompletionPort;
- d->iocpInstanceRefCount++;
- ReleaseMutex(d->iocpInstanceLock);
-
- d->hSemaphore = CreateSemaphore(NULL, 0, 255, NULL);
- d->hResultsMutex = CreateMutex(NULL, FALSE, NULL);
- connect(this, SIGNAL(_q_notify()), this, SLOT(_q_notified()), Qt::QueuedConnection);
-}
-
-QWinOverlappedIoNotifier::~QWinOverlappedIoNotifier()
-{
- Q_D(QWinOverlappedIoNotifier);
- setEnabled(false);
- CloseHandle(d->hResultsMutex);
- CloseHandle(d->hSemaphore);
-
- WaitForSingleObject(d->iocpInstanceLock, INFINITE);
- if (!--d->iocpInstanceRefCount) {
- delete d->iocp;
- d->iocp = 0;
- }
- ReleaseMutex(d->iocpInstanceLock);
-}
-
-void QWinOverlappedIoNotifier::setHandle(Qt::HANDLE h)
-{
- Q_D(QWinOverlappedIoNotifier);
- d->hHandle = h;
-}
-
-Qt::HANDLE QWinOverlappedIoNotifier::handle() const
-{
- Q_D(const QWinOverlappedIoNotifier);
- return d->hHandle;
-}
-
-void QWinOverlappedIoNotifier::setEnabled(bool enabled)
-{
- Q_D(QWinOverlappedIoNotifier);
- if (enabled)
- d->iocp->registerNotifier(d);
- else
- d->iocp->unregisterNotifier(d);
-}
-
-OVERLAPPED *QWinOverlappedIoNotifierPrivate::waitForAnyNotified(int msecs)
-{
- if (!iocp->isRunning()) {
- qWarning("Called QWinOverlappedIoNotifier::waitForAnyNotified on inactive notifier.");
- return 0;
- }
-
- if (msecs == 0)
- iocp->drainQueue();
-
- const DWORD wfso = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs));
- switch (wfso) {
- case WAIT_OBJECT_0:
- return dispatchNextIoResult();
- case WAIT_TIMEOUT:
- return 0;
- default:
- qErrnoWarning("QWinOverlappedIoNotifier::waitForAnyNotified: WaitForSingleObject failed.");
- return 0;
- }
-}
-
-class QScopedAtomicIntIncrementor
-{
-public:
- QScopedAtomicIntIncrementor(QAtomicInt &i)
- : m_int(i)
- {
- ++m_int;
- }
-
- ~QScopedAtomicIntIncrementor()
- {
- --m_int;
- }
-
-private:
- QAtomicInt &m_int;
-};
-
-/*!
- * Wait synchronously for any notified signal.
- *
- * The function returns a pointer to the OVERLAPPED object corresponding to the completed I/O
- * operation. In case no I/O operation was completed during the \a msec timeout, this function
- * returns a null pointer.
- */
-OVERLAPPED *QWinOverlappedIoNotifier::waitForAnyNotified(int msecs)
-{
- Q_D(QWinOverlappedIoNotifier);
- QScopedAtomicIntIncrementor saii(d->waiting);
- OVERLAPPED *result = d->waitForAnyNotified(msecs);
- return result;
-}
-
-/*!
- * Wait synchronously for the notified signal.
- *
- * The function returns true if the notified signal was emitted for
- * the I/O operation that corresponds to the OVERLAPPED object.
- */
-bool QWinOverlappedIoNotifier::waitForNotified(int msecs, OVERLAPPED *overlapped)
-{
- Q_D(QWinOverlappedIoNotifier);
- QScopedAtomicIntIncrementor saii(d->waiting);
- int t = msecs;
- QElapsedTimer stopWatch;
- stopWatch.start();
- forever {
- OVERLAPPED *triggeredOverlapped = waitForAnyNotified(t);
- if (!triggeredOverlapped)
- return false;
- if (triggeredOverlapped == overlapped)
- return true;
- t = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
- if (t == 0)
- return false;
- }
-}
-
-/*!
- * Note: This function runs in the I/O completion port thread.
- */
-void QWinOverlappedIoNotifierPrivate::notify(DWORD numberOfBytes, DWORD errorCode,
- OVERLAPPED *overlapped)
-{
- Q_Q(QWinOverlappedIoNotifier);
- WaitForSingleObject(hResultsMutex, INFINITE);
- results.enqueue(IOResult(numberOfBytes, errorCode, overlapped));
- ReleaseMutex(hResultsMutex);
- ReleaseSemaphore(hSemaphore, 1, NULL);
- if (!waiting)
- emit q->_q_notify();
-}
-
-void QWinOverlappedIoNotifierPrivate::_q_notified()
-{
- if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0)
- dispatchNextIoResult();
-}
-
-OVERLAPPED *QWinOverlappedIoNotifierPrivate::dispatchNextIoResult()
-{
- Q_Q(QWinOverlappedIoNotifier);
- WaitForSingleObject(hResultsMutex, INFINITE);
- IOResult ioresult = results.dequeue();
- ReleaseMutex(hResultsMutex);
- emit q->notified(ioresult.numberOfBytes, ioresult.errorCode, ioresult.overlapped);
- return ioresult.overlapped;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qwinoverlappedionotifier_p.cpp"
diff --git a/src/corelib/io/qwinoverlappedionotifier_p.h b/src/corelib/io/qwinoverlappedionotifier_p.h
deleted file mode 100644
index 276a1d861e..0000000000
--- a/src/corelib/io/qwinoverlappedionotifier_p.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QWINOVERLAPPEDIONOTIFIER_P_H
-#define QWINOVERLAPPEDIONOTIFIER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/private/qglobal_p.h>
-#include <qobject.h>
-
-typedef struct _OVERLAPPED OVERLAPPED;
-
-QT_BEGIN_NAMESPACE
-
-class QWinOverlappedIoNotifierPrivate;
-
-class Q_CORE_EXPORT QWinOverlappedIoNotifier : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(QWinOverlappedIoNotifier)
- Q_DECLARE_PRIVATE(QWinOverlappedIoNotifier)
- Q_PRIVATE_SLOT(d_func(), void _q_notified())
- friend class QWinIoCompletionPort;
-public:
- QWinOverlappedIoNotifier(QObject *parent = 0);
- ~QWinOverlappedIoNotifier();
-
- void setHandle(Qt::HANDLE h);
- Qt::HANDLE handle() const;
-
- void setEnabled(bool enabled);
- OVERLAPPED *waitForAnyNotified(int msecs);
- bool waitForNotified(int msecs, OVERLAPPED *overlapped);
-
-Q_SIGNALS:
- void notified(quint32 numberOfBytes, quint32 errorCode, OVERLAPPED *overlapped);
-#if !defined(Q_QDOC)
- void _q_notify();
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif // QWINOVERLAPPEDIONOTIFIER_P_H