summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2017-07-02 14:57:21 -0700
committerThiago Macieira <thiago.macieira@intel.com>2017-08-08 21:48:20 +0000
commitd3393ce25833c0afd7f0fa6b85fd6f3bd7ad520a (patch)
treec8dc63ecc5dfe4784c7327000be0b3c6e8127a26 /src
parent874837b4cc76b79a9b0f54f5c221c95d2a309c9d (diff)
Add support for statx(2) on Linux
This system call, new in Linux 4.11, gives us the file birth time. It's also extensible, representing the fourth generation of stat(2) on Linux (the original sys_stat(), sys_newstat(), sys_stat64() and now sys_statx()), not to be confused with glibc's __xstat function, which wraps a call to stat64. Anyway, the new one is designed to be extensible. Now we get birth times on ext[34] on Linux too: Name: . Path: . (/home/tjmaciei/src/qt) Size: 4096 Type: Directory Attrs: readable writable executable hidden nativepath Mode: drwxr-xr-x Owner: tjmaciei (1000) Group: users (100) Access: 2017-07-02T14:47:49.608 Birth: 2016-05-02T13:20:33.097 Change: 2017-07-01T13:37:08.737 Modified: 2017-07-01T13:37:08.737 It's not supported in any other filesystems I have (Linux sources show xfs has the feature too). Even on ext4, it depends on whether the filesystem was created with 256-byte inodes, which my /boot fs wasn't. Change-Id: I8d96dea9955d4c749b99fffd14cda23ed60d5e72 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-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