diff options
-rw-r--r-- | src/corelib/configure.json | 18 | ||||
-rw-r--r-- | src/corelib/global/qconfig-bootstrapped.h | 1 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemengine_unix.cpp | 182 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemmetadata_p.h | 1 |
4 files changed, 185 insertions, 17 deletions
diff --git a/src/corelib/configure.json b/src/corelib/configure.json index c8fede2b8f..8b503233a0 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -383,6 +383,19 @@ "main": "renameat2(AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_NOREPLACE | RENAME_WHITEOUT);" } }, + "statx": { + "label": "statx() in libc", + "type": "compile", + "test": { + "head": "#define _ATFILE_SOURCE 1", + "include": [ "sys/types.h", "sys/stat.h", "unistd.h", "fcntl.h" ], + "main": [ + "struct statx statxbuf;", + "unsigned int mask = STATX_BASIC_STATS;", + "return statx(AT_FDCWD, \"\", AT_STATX_SYNC_AS_STAT, mask, &statxbuf);" + ] + } + }, "syslog": { "label": "syslog", "type": "compile", @@ -585,6 +598,11 @@ "condition": "libs.slog2", "output": [ "privateFeature" ] }, + "statx": { + "label": "statx() in libc", + "condition": "config.linux && tests.statx", + "output": [ "privateFeature" ] + }, "syslog": { "label": "syslog", "autoDetect": false, diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index eb752354b1..95095f4b76 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -89,6 +89,7 @@ #define QT_NO_SYSTEMLOCALE #define QT_FEATURE_renameat2 -1 #define QT_FEATURE_slog2 -1 +#define QT_FEATURE_statx -1 #define QT_FEATURE_syslog -1 #define QT_FEATURE_temporaryfile 1 #define QT_NO_THREAD diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index 7f043582e8..3047fc64f5 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -85,6 +85,12 @@ extern "C" NSString *NSTemporaryDirectory(); static int renameat2(int oldfd, const char *oldpath, int newfd, const char *newpath, unsigned flags) { return syscall(SYS_renameat2, oldfd, oldpath, newfd, newpath, flags); } # endif + +# if !QT_CONFIG(statx) && defined(SYS_statx) && QT_HAS_INCLUDE(<linux/stat.h>) +# include <linux/stat.h> +static int statx(int dirfd, const char *pathname, int flag, unsigned mask, struct statx *statxbuf) +{ return syscall(SYS_statx, dirfd, pathname, flag, mask, statxbuf); } +# endif #endif QT_BEGIN_NAMESPACE @@ -269,13 +275,131 @@ mtime(const T &statBuffer, int) } } +#ifdef STATX_BASIC_STATS +static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer) +{ +#ifdef Q_ATOMIC_INT8_IS_SUPPORTED + static QBasicAtomicInteger<qint8> statxTested = Q_BASIC_ATOMIC_INITIALIZER(0); +#else + static QBasicAtomicInt statxTested = Q_BASIC_ATOMIC_INITIALIZER(0); +#endif + + if (statxTested.load() == -1) + return -ENOSYS; + + unsigned mask = STATX_BASIC_STATS | STATX_BTIME; + int ret = statx(fd, pathname, flags, mask, statxBuffer); + if (ret == -1 && errno == ENOSYS) { + statxTested.store(-1); + return -ENOSYS; + } + statxTested.store(1); + return ret == -1 ? -errno : 0; +} + +static int qt_statx(const char *pathname, struct statx *statxBuffer) +{ + return qt_real_statx(AT_FDCWD, pathname, 0, statxBuffer); +} + +static int qt_lstatx(const char *pathname, struct statx *statxBuffer) +{ + return qt_real_statx(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, statxBuffer); +} + +static int qt_fstatx(int fd, struct statx *statxBuffer) +{ + return qt_real_statx(fd, "", AT_EMPTY_PATH, statxBuffer); +} + +inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &statxBuffer) +{ + // Permissions + if (statxBuffer.stx_mode & S_IRUSR) + entryFlags |= QFileSystemMetaData::OwnerReadPermission; + if (statxBuffer.stx_mode & S_IWUSR) + entryFlags |= QFileSystemMetaData::OwnerWritePermission; + if (statxBuffer.stx_mode & S_IXUSR) + entryFlags |= QFileSystemMetaData::OwnerExecutePermission; + + if (statxBuffer.stx_mode & S_IRGRP) + entryFlags |= QFileSystemMetaData::GroupReadPermission; + if (statxBuffer.stx_mode & S_IWGRP) + entryFlags |= QFileSystemMetaData::GroupWritePermission; + if (statxBuffer.stx_mode & S_IXGRP) + entryFlags |= QFileSystemMetaData::GroupExecutePermission; + + if (statxBuffer.stx_mode & S_IROTH) + entryFlags |= QFileSystemMetaData::OtherReadPermission; + if (statxBuffer.stx_mode & S_IWOTH) + entryFlags |= QFileSystemMetaData::OtherWritePermission; + if (statxBuffer.stx_mode & S_IXOTH) + entryFlags |= QFileSystemMetaData::OtherExecutePermission; + + // Type + if (S_ISLNK(statxBuffer.stx_mode)) + entryFlags |= QFileSystemMetaData::LinkType; + if ((statxBuffer.stx_mode & S_IFMT) == S_IFREG) + entryFlags |= QFileSystemMetaData::FileType; + else if ((statxBuffer.stx_mode & S_IFMT) == S_IFDIR) + entryFlags |= QFileSystemMetaData::DirectoryType; + else if ((statxBuffer.stx_mode & S_IFMT) != S_IFBLK) + entryFlags |= QFileSystemMetaData::SequentialType; + + // Attributes + entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists + if (statxBuffer.stx_nlink == 0) + entryFlags |= QFileSystemMetaData::WasDeletedAttribute; + size_ = qint64(statxBuffer.stx_size); + + // Times + auto toMSecs = [](struct statx_timestamp ts) + { + return qint64(ts.tv_sec) * 1000 + (ts.tv_nsec / 1000000); + }; + accessTime_ = toMSecs(statxBuffer.stx_atime); + metadataChangeTime_ = toMSecs(statxBuffer.stx_ctime); + modificationTime_ = toMSecs(statxBuffer.stx_mtime); + if (statxBuffer.stx_mask & STATX_BTIME) + birthTime_ = toMSecs(statxBuffer.stx_btime); + else + birthTime_ = 0; + + userId_ = statxBuffer.stx_uid; + groupId_ = statxBuffer.stx_gid; +} +#else +struct statx { mode_t stx_mode; }; +static int qt_statx(const char *, struct statx *) +{ return -ENOSYS; } + +static int qt_lstatx(const char *, struct statx *) +{ return -ENOSYS; } + +static int qt_fstatx(int, struct statx *) +{ return -ENOSYS; } + +inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &) +{ } +#endif + //static bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data) { data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags; data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags; - QT_STATBUF statBuffer; + union { + struct statx statxBuffer; + QT_STATBUF statBuffer; + }; + + int ret = qt_fstatx(fd, &statxBuffer); + if (ret != -ENOSYS) { + data.fillFromStatxBuf(statxBuffer); + return ret == 0; + } + if (QT_FSTAT(fd, &statBuffer) == 0) { data.fillFromStatBuf(statBuffer); return true; @@ -771,28 +895,46 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM // first, we may try lstat(2). Possible outcomes: // - success and is a symlink: filesystem entry exists, but we need stat(2) - // -> statBufferValid = false + // -> statResult = -1; // - success and is not a symlink: filesystem entry exists and we're done - // -> statBufferValid = true + // -> statResult = 0 // - failure: really non-existent filesystem entry - // -> entryExists = false; statBufferValid = true + // -> entryExists = false; statResult = 0; // both stat(2) and lstat(2) may generate a number of different errno // conditions, but of those, the only ones that could happen and the // entry still exist are EACCES, EFAULT, ENOMEM and EOVERFLOW. If we get // EACCES or ENOMEM, then we have no choice on how to proceed, so we may // as well conclude it doesn't exist; EFAULT can't happen and EOVERFLOW // shouldn't happen because we build in _LARGEFIE64. - QT_STATBUF statBuffer; - bool statBufferValid = false; + union { + QT_STATBUF statBuffer; + struct statx statxBuffer; + }; + int statResult = -1; if (what & QFileSystemMetaData::LinkType) { - if (QT_LSTAT(nativeFilePath, &statBuffer) == 0) { - if (S_ISLNK(statBuffer.st_mode)) { - // it's a symlink, we don't know if the file "exists" + mode_t mode = 0; + statResult = qt_lstatx(nativeFilePath, &statxBuffer); + if (statResult == -ENOSYS) { + // use lstst(2) + statResult = QT_LSTAT(nativeFilePath, &statBuffer); + if (statResult == 0) + mode = statBuffer.st_mode; + } else if (statResult == 0) { + statResult = 1; // record it was statx(2) that succeeded + mode = statxBuffer.stx_mode; + } + + if (statResult >= 0) { + if (S_ISLNK(mode)) { + // it's a symlink, we don't know if the file "exists" data.entryFlags |= QFileSystemMetaData::LinkType; + statResult = -1; // force stat(2) below } else { // it's a reagular file and it exists - statBufferValid = true; - data.fillFromStatBuf(statBuffer); + if (statResult) + data.fillFromStatxBuf(statxBuffer); + else + data.fillFromStatBuf(statBuffer); data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags | QFileSystemMetaData::ExistsAttribute; data.entryFlags |= QFileSystemMetaData::ExistsAttribute; @@ -807,15 +949,21 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM } // second, we try a regular stat(2) - if (statBufferValid || (what & QFileSystemMetaData::PosixStatFlags)) { - if (entryExists && !statBufferValid) { - statBufferValid = (QT_STAT(nativeFilePath, &statBuffer) == 0); + if (statResult != 0 && (what & QFileSystemMetaData::PosixStatFlags)) { + if (entryExists && statResult == -1) { data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags; - if (statBufferValid) - data.fillFromStatBuf(statBuffer); + statResult = qt_statx(nativeFilePath, &statxBuffer); + if (statResult == -ENOSYS) { + // use stat(2) + statResult = QT_STAT(nativeFilePath, &statBuffer); + if (statResult == 0) + data.fillFromStatBuf(statBuffer); + } else if (statResult == 0) { + data.fillFromStatxBuf(statxBuffer); + } } - if (!statBufferValid) { + if (statResult != 0) { entryExists = false; data.birthTime_ = 0; data.metadataChangeTime_ = 0; diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h index c4e1312c1d..55e44d52c7 100644 --- a/src/corelib/io/qfilesystemmetadata_p.h +++ b/src/corelib/io/qfilesystemmetadata_p.h @@ -220,6 +220,7 @@ public: uint ownerId(QAbstractFileEngine::FileOwner owner) const; #ifdef Q_OS_UNIX + void fillFromStatxBuf(const struct statx &statBuffer); void fillFromStatBuf(const QT_STATBUF &statBuffer); void fillFromDirEnt(const QT_DIRENT &statBuffer); #endif |