diff options
Diffstat (limited to 'src/corelib/io')
43 files changed, 979 insertions, 1082 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..7a603544f3 100644 --- a/src/corelib/io/qabstractfileengine.cpp +++ b/src/corelib/io/qabstractfileengine.cpp @@ -737,6 +737,24 @@ 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 @@ -843,6 +861,19 @@ bool QAbstractFileEngine::unmap(uchar *address) } /*! + \since 5.10 + + Copies the contents from the file specified by \a sourceHandle to this file + by cloning it. + Returns \c true on success; otherwise, \c false is returned. + */ +bool QAbstractFileEngine::clone(int sourceHandle) +{ + Q_UNUSED(sourceHandle); + 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..47b3a624fe 100644 --- a/src/corelib/io/qabstractfileengine_p.h +++ b/src/corelib/io/qabstractfileengine_p.h @@ -145,9 +145,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 clone(int sourceHandle); 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..9044ad74b1 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() 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 f7e711a7cf..2cfb718932 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -42,6 +42,7 @@ #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" @@ -420,7 +421,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); } /*! @@ -794,25 +795,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 (!out.d_func()->engine()->clone(d->engine()->handle())) { + 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 +960,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 +1018,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/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp index 715dc7293a..b642add7f6 100644 --- a/src/corelib/io/qfiledevice.cpp +++ b/src/corelib/io/qfiledevice.cpp @@ -738,6 +738,82 @@ bool QFileDevice::unmap(uchar *address) return false; } +/*! + \enum QFileDevice::FileTime + \since 5.10 + + This enum is used by the fileTime() and setFileTime() functions. + + \value FileCreationTime When the file was created (not supported on UNIX). + \value FileModificationTime When the file was most recently modified. + \value FileAccessTime When the file was most recently accessed (e.g. + read or written to). + + \sa setFileTime(), fileTime() +*/ + +static inline QAbstractFileEngine::FileTime FileDeviceTimeToAbstractFileEngineTime(QFileDevice::FileTime time) +{ + switch (time) { + case QFileDevice::FileAccessTime: + return QAbstractFileEngine::AccessTime; + + case QFileDevice::FileCreationTime: + return QAbstractFileEngine::CreationTime; + + case QFileDevice::FileModificationTime: + return QAbstractFileEngine::ModificationTime; + } + + Q_UNREACHABLE(); + return QAbstractFileEngine::AccessTime; +} + +/*! + \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..3519a049a9 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,12 @@ public: CopyError = 14 }; + enum FileTime { + FileCreationTime, + FileModificationTime, + FileAccessTime + }; + enum Permission { ReadOwner = 0x4000, WriteOwner = 0x2000, ExeOwner = 0x1000, ReadUser = 0x0400, WriteUser = 0x0200, ExeUser = 0x0100, @@ -119,6 +126,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 12fd7d3048..77349d511d 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,22 @@ 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) + + uint cf = 0; + switch (request) { + case QAbstractFileEngine::AccessTime: + cf = CachedATime; + break; + case QAbstractFileEngine::CreationTime: cf = CachedCTime; - else if (request == QAbstractFileEngine::ModificationTime) + break; + case QAbstractFileEngine::ModificationTime: cf = CachedMTime; - else - cf = CachedATime; + break; + } + if (!getCachedFlag(cf)) { fileTimes[request] = fileEngine->fileTime(request); setCachedFlag(cf); @@ -262,8 +268,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 @@ -1309,35 +1315,17 @@ qint64 QFileInfo::size() const */ 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(); + return fileTime(QFile::FileCreationTime); } /*! Returns the date and local time when the file was last modified. - \sa created(), lastRead() + \sa created(), lastRead(), 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); } /*! @@ -1346,20 +1334,51 @@ QDateTime QFileInfo::lastModified() const On platforms where this information is not available, returns the same as lastModified(). - \sa created(), lastModified() + \sa created(), lastModified(), 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 return QDateTime() (an invalid + date time). + + \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::FileCreationTime) == int(QAbstractFileEngine::CreationTime)); + Q_STATIC_ASSERT(int(QFile::FileModificationTime) == int(QAbstractFileEngine::ModificationTime)); + Q_D(const QFileInfo); + auto fetime = QAbstractFileEngine::FileTime(time); 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(); + if (d->fileEngine) + return d->getFileTime(fetime).toLocalTime(); + + QFileSystemMetaData::MetaDataFlags flag; + switch (time) { + case QFile::FileAccessTime: + flag = QFileSystemMetaData::AccessTime; + break; + case QFile::FileCreationTime: + flag = QFileSystemMetaData::CreationTime; + break; + case QFile::FileModificationTime: + flag = QFileSystemMetaData::ModificationTime; + break; } - return d->getFileTime(QAbstractFileEngine::AccessTime).toLocalTime(); + + if (!d->cache_enabled || !d->metaData.hasFlags(flag)) + if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, flag)) + return QDateTime(); + return d->metaData.fileTime(fetime).toLocalTime(); } /*! @@ -1406,4 +1425,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..845fdefac7 100644 --- a/src/corelib/io/qfileinfo.h +++ b/src/corelib/io/qfileinfo.h @@ -129,9 +129,11 @@ public: qint64 size() const; + // ### Qt6: inline these functions QDateTime created() const; QDateTime lastModified() const; QDateTime lastRead() const; + QDateTime fileTime(QFile::FileTime time) const; bool caching() const; void setCaching(bool on); @@ -151,6 +153,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..d45cf6be33 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> @@ -158,19 +157,14 @@ 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 diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp index ccbcdb1037..f3205ae6e2 100644 --- a/src/corelib/io/qfilesystemengine.cpp +++ b/src/corelib/io/qfilesystemengine.cpp @@ -281,7 +281,9 @@ void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer) entryFlags |= QFileSystemMetaData::SequentialType; // Attributes - entryFlags |= QFileSystemMetaData::ExistsAttribute; + entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists + if (statBuffer.st_nlink == 0) + entryFlags |= QFileSystemMetaData::WasDeletedAttribute; size_ = statBuffer.st_size; #if defined(Q_OS_DARWIN) if (statBuffer.st_flags & UF_HIDDEN) { diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index 6640e2b7e7..b0c23e3f82 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 <stdlib.h> // for realpath() @@ -67,6 +69,9 @@ #endif #if defined(Q_OS_DARWIN) +# if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000) +# include <sys/clonefile.h> +# endif // We cannot include <Foundation/Foundation.h> (it's an Objective-C header), but // we need these declarations: Q_FORWARD_DECLARE_OBJC_CLASS(NSString); @@ -146,30 +151,8 @@ static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &e //static QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, 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; - } - if (len < size) { - break; - } - size *= 2; - } -#else - char s[PATH_MAX+1]; - int len = readlink(link.nativeFilePath().constData(), s, PATH_MAX); -#endif - if (len > 0) { + QByteArray s = qt_readlink(link.nativeFilePath().constData()); + if (s.length() > 0) { QString ret; if (!data.hasFlags(QFileSystemMetaData::DirectoryType)) fillMetaData(link, data, QFileSystemMetaData::DirectoryType); @@ -180,11 +163,7 @@ 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 linkFilePath = link.filePath(); @@ -649,8 +628,22 @@ 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) + const auto current = QOperatingSystemVersion::current(); + if (current >= QOperatingSystemVersion::MacOSSierra || + current >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 10) || + current >= QOperatingSystemVersion(QOperatingSystemVersion::TvOS, 10) || + current >= QOperatingSystemVersion(QOperatingSystemVersion::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; } diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp index 0542d9e16c..5a9864edb2 100644 --- 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,48 +147,30 @@ 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; -/* - 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; @@ -200,39 +182,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 @@ -247,62 +203,33 @@ static void resolveLibs() DWORD sidLen = ::GetLengthSid(tokenSid); currentUserSID = reinterpret_cast<PSID>(malloc(sidLen)); if (::CopySid(sidLen, currentUserSID, tokenSid)) - ptrBuildTrusteeWithSidW(¤tUserTrusteeW, currentUserSID); + BuildTrusteeWithSid(¤tUserTrusteeW, currentUserSID); } free(tokenBuffer); } ::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"); } } -#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; +Q_GLOBAL_STATIC(GlobalSid, initGlobalSid) +QT_END_NAMESPACE -static bool resolveUNCLibs() -{ - 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 - } - return ptrNetShareEnum && ptrNetApiBufferFree; -} +} // anonymous namespace +#endif // QT_CONFIG(fslibs) + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0; static QString readSymLink(const QFileSystemEntry &link) { @@ -339,7 +266,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; @@ -436,25 +363,27 @@ 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) @@ -647,31 +576,31 @@ QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEng 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 { @@ -696,9 +625,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(); @@ -706,15 +635,15 @@ 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; if (what & QFileSystemMetaData::UserPermissions) { // user data.knownFlagsMask |= QFileSystemMetaData::UserPermissions; - if(ptrGetEffectiveRightsFromAclW(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS) + if (GetEffectiveRightsFromAcl(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS) access_mask = (ACCESS_MASK)-1; if(access_mask & ReadMask) data.entryFlags |= QFileSystemMetaData::UserReadPermission; @@ -725,8 +654,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; @@ -737,8 +666,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; @@ -749,7 +678,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; @@ -1160,19 +1089,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; diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h index b09223d656..2f25721bee 100644 --- a/src/corelib/io/qfilesystemmetadata_p.h +++ b/src/corelib/io/qfilesystemmetadata_p.h @@ -120,9 +120,14 @@ 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 @@ -144,6 +149,7 @@ public: | QFileSystemMetaData::DirectoryType | QFileSystemMetaData::SequentialType | QFileSystemMetaData::SizeAttribute + | QFileSystemMetaData::WasDeletedAttribute | QFileSystemMetaData::Times | QFileSystemMetaData::OwnerIds, @@ -191,6 +197,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 diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 88e8baedca..037deb8942 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 ©Name) - 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,6 +919,10 @@ bool QFSFileEngine::supportsExtension(Extension extension) const \reimp */ +/*! \fn bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) + \reimp +*/ + /*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const \reimp */ diff --git a/src/corelib/io/qfsfileengine_p.h b/src/corelib/io/qfsfileengine_p.h index ab9ad3aa6b..f352fcd475 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,7 @@ 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 clone(int sourceHandle) override; 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..ab762d2b4d 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"); @@ -151,6 +100,44 @@ static inline QString msgOpenDirectory() #endif } +#if !QT_CONFIG(futimens) && (QT_CONFIG(futimes) || QT_CONFIG(futimesat)) +namespace { +namespace GetFileTimes { + +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; +} + +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 + /*! \internal */ @@ -158,6 +145,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 +188,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; @@ -549,11 +495,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 +534,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 +639,81 @@ 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 (!newDate.isValid() || time == QAbstractFileEngine::CreationTime) { + setError(QFile::UnspecifiedError, qt_error_string(EINVAL)); + 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(d->nativeHandle(), ts) == -1) { + setError(QFile::PermissionsError, qt_error_string()); + return false; + } + + d->metaData.clearFlags(QFileSystemMetaData::Times); - if (d->doStat(QFileSystemMetaData::Times)) - return d->metaData.fileTime(time); + return true; +#elif QT_CONFIG(futimes) || QT_CONFIG(futimesat) + struct timeval tv[2]; + QT_STATBUF st; + + if (QT_FSTAT(d->nativeHandle(), &st) == -1) { + setError(QFile::PermissionsError, qt_error_string()); + 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 QT_CONFIG(futimes) + if (futimes(d->nativeHandle(), tv) == -1) { +#else + if (futimesat(d->nativeHandle(), NULL, tv) == -1) { +#endif + setError(QFile::PermissionsError, qt_error_string()); + return false; + } + + d->metaData.clearFlags(QFileSystemMetaData::Times); - return QDateTime(); + return true; +#else + setError(QFile::UnspecifiedError, qt_error_string(ENOSYS)); + return false; +#endif } uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags) @@ -715,7 +732,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 +822,23 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr) #endif } +/*! + \reimp +*/ +bool QFSFileEngine::clone(int sourceHandle) +{ +#if defined(Q_OS_LINUX) + Q_D(QFSFileEngine); +# if !defined FICLONE +# define FICLONE _IOW (0x94, 9, int) +# endif + return ::ioctl(d->fd, FICLONE, sourceHandle) == 0; +#else + Q_UNUSED(sourceHandle); + return false; +#endif +} + 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..0decd26179 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -80,6 +80,39 @@ static inline bool isUncPath(const QString &path) && path.size() > 2 && path.at(2) != QLatin1Char('.')); } +static inline void QDateTimeToSystemTime(const QDateTime &date, SYSTEMTIME *systemTime) +{ + const QDate d = date.date(); + const QTime t = date.time(); + + systemTime->wYear = d.year(); + systemTime->wMonth = d.month(); + systemTime->wDay = d.day(); + systemTime->wHour = t.hour(); + systemTime->wMinute = t.minute(); + systemTime->wSecond = t.second(); + systemTime->wMilliseconds = t.msec(); + systemTime->wDayOfWeek = d.dayOfWeek() % 7; +} + +static inline bool QDateTimeToFileTime(const QDateTime &date, FILETIME *fileTime) +{ + SYSTEMTIME sTime; + +#if defined(Q_OS_WINCE) + QDateTimeToSystemTime(date, &sTime); +#else + SYSTEMTIME lTime; + + QDateTimeToSystemTime(date, &lTime); + + if (!::TzSpecificLocalTimeToSystemTime(0, &lTime, &sTime)) + return false; +#endif + + return ::SystemTimeToFileTime(&sTime, fileTime); +} + /*! \internal */ @@ -694,6 +727,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 +889,68 @@ 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->doStat(QFileSystemMetaData::Times)) - return d->metaData.fileTime(time); + if (d->openMode == QFile::NotOpen) { + setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); + return false; + } - return QDateTime(); + if (!newDate.isValid()) { + setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER)); + return false; + } + + HANDLE handle = d->fileHandle; + +#ifndef Q_OS_WINCE + 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)); + } +#endif + + if (handle == INVALID_HANDLE_VALUE) { + setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); + return false; + } + + 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::CreationTime: + pCreationTime = &fTime; + break; + } + + if (!QDateTimeToFileTime(newDate, &fTime)) { + setError(QFile::UnspecifiedError, qt_error_string()); + return false; + } + + if (!::SetFileTime(handle, pCreationTime, pLastAccess, pLastWrite)) { + setError(QFile::PermissionsError, qt_error_string()); + return false; + } + + d->metaData.clearFlags(QFileSystemMetaData::Times); + + return true; } uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, @@ -1002,4 +1090,13 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr) return true; } +/*! + \reimp +*/ +bool QFSFileEngine::clone(int sourceHandle) +{ + Q_UNUSED(sourceHandle); + return false; +} + QT_END_NAMESPACE diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp index ccc607afd5..1ee8ce889c 100644 --- a/src/corelib/io/qlockfile_unix.cpp +++ b/src/corelib/io/qlockfile_unix.cpp @@ -199,7 +199,7 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys() } 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 @@ -258,17 +258,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/qprocess.cpp b/src/corelib/io/qprocess.cpp index 2226b4435b..bb16b0c427 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -450,11 +450,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 @@ -2112,6 +2107,60 @@ 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. + + 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. @@ -2445,6 +2494,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 @@ -2452,16 +2503,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. @@ -2472,10 +2517,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); } /*! @@ -2484,11 +2530,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. @@ -2506,9 +2555,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 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 6b7f187fee..cf9d38097a 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"); } @@ -921,7 +891,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); @@ -937,6 +907,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; @@ -953,6 +934,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()); @@ -963,23 +956,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)); @@ -1009,6 +1007,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..2bbc4eddd0 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -358,7 +358,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 +388,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 +458,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 +504,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 +534,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 +549,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 +836,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 +847,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 +871,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 +924,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..c1187e5145 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; + qssize_t m_len; + qssize_t 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/qsettings.cpp b/src/corelib/io/qsettings.cpp index d5460238ec..850e729987 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -1802,6 +1802,8 @@ struct QSettingsIniSection inline QSettingsIniSection() : position(-1) {} }; +Q_DECLARE_TYPEINFO(QSettingsIniSection, Q_MOVABLE_TYPE); + typedef QMap<QString, QSettingsIniSection> IniMap; /* diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index 639605d8c4..d8e91e48ce 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; diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp index f2368c3b23..c3d45caf0e 100644 --- a/src/corelib/io/qstandardpaths.cpp +++ b/src/corelib/io/qstandardpaths.cpp @@ -183,7 +183,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 +207,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>" diff --git a/src/corelib/io/qstandardpaths_win.cpp b/src/corelib/io/qstandardpaths_win.cpp index a64bde6fb4..41fd31df8b 100644 --- a/src/corelib/io/qstandardpaths_win.cpp +++ b/src/corelib/io/qstandardpaths_win.cpp @@ -218,7 +218,15 @@ QStringList QStandardPaths::standardLocations(StandardLocation type) } #ifndef QT_BOOTSTRAPPED dirs.append(QCoreApplication::applicationDirPath()); - dirs.append(QCoreApplication::applicationDirPath() + QLatin1String("/data")); + const QString dataDir = QCoreApplication::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/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index 8a99873fee..efab2a30ff 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -42,6 +42,7 @@ #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" @@ -131,15 +132,17 @@ static bool createFileFromTemplate(NativeFileHandle &file, Char *rIter = placeholderEnd; #if defined(QT_BUILD_CORE_LIB) + // don't consume more than half of the template with the PID + Char *pidStart = placeholderEnd - (placeholderEnd - placeholderStart) / 2; quint64 pid = quint64(QCoreApplication::applicationPid()); do { *--rIter = Latin1Char((pid % 10) + '0'); pid /= 10; - } while (rIter != placeholderStart && pid != 0); + } while (rIter != pidStart && pid != 0); #endif while (rIter != placeholderStart) { - char ch = char((qrand() & 0xffff) % (26 + 26)); + char ch = char(QRandomGenerator::bounded(26 + 26)); if (ch < 26) *--rIter = Latin1Char(ch + 'A'); else diff --git a/src/corelib/io/qtemporaryfile_p.h b/src/corelib/io/qtemporaryfile_p.h index 7f365f0e8a..bf40e1627a 100644 --- a/src/corelib/io/qtemporaryfile_p.h +++ b/src/corelib/io/qtemporaryfile_p.h @@ -65,7 +65,7 @@ class QTemporaryFilePrivate : public QFilePrivate { Q_DECLARE_PUBLIC(QTemporaryFile) -protected: +public: QTemporaryFilePrivate(); explicit QTemporaryFilePrivate(const QString &templateNameIn); ~QTemporaryFilePrivate(); 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 |