summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/configure.json18
-rw-r--r--src/corelib/global/qconfig-bootstrapped.h1
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp182
-rw-r--r--src/corelib/io/qfilesystemmetadata_p.h1
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