chiark / gitweb /
xattr-util: use crtime/btime if statx() is available for implementation of fd_setcrti...
authorLennart Poettering <lennart@poettering.net>
Tue, 20 Feb 2018 11:48:33 +0000 (12:48 +0100)
committerSven Eden <yamakuzure@gmx.net>
Wed, 30 May 2018 05:59:00 +0000 (07:59 +0200)
The Linux kernel exposes the birth time now for files through statx()
hence make use of it where available. We keep the xattr logic in place
for this however, since only a subset of file systems on Linux currently
expose the birth time. NFS and tmpfs for example do not support it. OTOH
there are other file systems that do support the birth time but might
not support xattrs (smb…), hence make the best of the two, in particular
in order to deal with journal files copied between file system types and
to maintain compatibility with older file systems that are updated to
newer version of the file system.

meson.build
src/basic/missing.h
src/basic/missing_syscall.h
src/basic/xattr-util.c

index 696223298f355dc084eb888e9b0a7f339583af9f..f2128b9d0a4b2fe559f8529b0788592ace3ae0fa 100644 (file)
@@ -513,6 +513,8 @@ decl_headers = '''
 #include <uchar.h>
 #include <linux/ethtool.h>
 #include <linux/fib_rules.h>
+//#include <linux/stat.h>
+//#include <sys/stat.h>
 '''
 # FIXME: key_serial_t is only defined in keyutils.h, this is bound to fail
 
@@ -521,6 +523,7 @@ foreach decl : ['char16_t',
                 'key_serial_t',
                 'struct ethtool_link_settings',
                 'struct fib_rule_uid_range',
+                'struct statx',
                ]
 
         # We get -1 if the size cannot be determined
@@ -566,7 +569,7 @@ endforeach
 foreach ident : [
         ['memfd_create',      '''#include <sys/mman.h>'''],
         ['gettid',            '''#include <sys/types.h>
-//                                 #include <unistd.h>'''],
+                                 #include <unistd.h>'''],
         ['pivot_root',        '''#include <stdlib.h>
                                  #include <unistd.h>'''],     # no known header declares pivot_root
         ['name_to_handle_at', '''#include <sys/types.h>
@@ -583,6 +586,9 @@ foreach ident : [
         ['bpf',               '''#include <sys/syscall.h>
                                  #include <unistd.h>'''],
         ['explicit_bzero' ,   '''#include <string.h>'''],
+        ['statx',             '''#include <sys/types.h>
+                                 #include <sys/stat.h>
+//                                 #include <unistd.h>'''],
 ]
 
         have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')
index bbddfe6fbd41d79807c0c1fd2f5b4ed0d5beebf9..3708742c223a5b1024b4239a745782bcd70877be 100644 (file)
 #include <inttypes.h>
 #include <linux/audit.h>
 #include <linux/capability.h>
+//#include <linux/falloc.h>
 #include <linux/if_link.h>
 #include <linux/input.h>
 #include <linux/loop.h>
 #include <linux/neighbour.h>
 #include <linux/oom.h>
 #include <linux/rtnetlink.h>
+//#include <linux/stat.h>
 #include <net/ethernet.h>
 #include <stdlib.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
+//#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <uchar.h>
 #include <unistd.h>
@@ -522,6 +525,10 @@ struct btrfs_ioctl_quota_ctl_args {
 #define BPF_FS_MAGIC 0xcafe4a11
 #endif
 
+#ifndef OCFS2_SUPER_MAGIC
+#define OCFS2_SUPER_MAGIC 0x7461636f
+#endif
+
 #ifndef MS_MOVE
 #define MS_MOVE 8192
 #endif
@@ -1364,8 +1371,55 @@ struct fib_rule_uid_range {
 #define NS_GET_NSTYPE _IO(0xb7, 0x3)
 #endif
 
+#ifndef FALLOC_FL_KEEP_SIZE
+#define FALLOC_FL_KEEP_SIZE 0x01
+#endif
+
+#ifndef FALLOC_FL_PUNCH_HOLE
+#define FALLOC_FL_PUNCH_HOLE 0x02
+#endif
+
 #ifndef PF_KTHREAD
 #define PF_KTHREAD 0x00200000
 #endif
 
+#if ! HAVE_STRUCT_STATX
+struct statx_timestamp {
+        int64_t tv_sec;
+        uint32_t tv_nsec;
+        uint32_t __reserved;
+};
+struct statx {
+        uint32_t stx_mask;
+        uint32_t stx_blksize;
+        uint64_t stx_attributes;
+        uint32_t stx_nlink;
+        uint32_t stx_uid;
+        uint32_t stx_gid;
+        uint16_t stx_mode;
+        uint16_t __spare0[1];
+        uint64_t stx_ino;
+        uint64_t stx_size;
+        uint64_t stx_blocks;
+        uint64_t stx_attributes_mask;
+        struct statx_timestamp stx_atime;
+        struct statx_timestamp stx_btime;
+        struct statx_timestamp stx_ctime;
+        struct statx_timestamp stx_mtime;
+        uint32_t stx_rdev_major;
+        uint32_t stx_rdev_minor;
+        uint32_t stx_dev_major;
+        uint32_t stx_dev_minor;
+        uint64_t __spare2[14];
+};
+#endif
+
+#ifndef STATX_BTIME
+#define STATX_BTIME 0x00000800U
+#endif
+
+#ifndef AT_STATX_DONT_SYNC
+#define AT_STATX_DONT_SYNC 0x4000
+#endif
+
 #include "missing_syscall.h"
index 1c8c84ea30c851ba0206ac77687c7903285e730e..8bc75d4e93ccb20421724a9befdcb8984648da45 100644 (file)
 #include <sys/types.h>
 
 #if !HAVE_PIVOT_ROOT
-static inline int missing_pivot_root(const char *new_root, const char *put_old) {
+static inline int pivot_root(const char *new_root, const char *put_old) {
         return syscall(SYS_pivot_root, new_root, put_old);
 }
-
-#  define pivot_root missing_pivot_root
 #endif
 #endif // 0
 
@@ -66,7 +64,7 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old)
 #    endif
 #  endif
 
-static inline int missing_memfd_create(const char *name, unsigned int flags) {
+static inline int memfd_create(const char *name, unsigned int flags) {
 #  ifdef __NR_memfd_create
         return syscall(__NR_memfd_create, name, flags);
 #  else
@@ -74,8 +72,6 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
         return -1;
 #  endif
 }
-
-#  define memfd_create missing_memfd_create
 #endif
 
 /* ======================================================================= */
@@ -115,7 +111,7 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
 #    endif
 #  endif
 
-static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) {
+static inline int getrandom(void *buffer, size_t count, unsigned flags) {
 #  ifdef __NR_getrandom
         return syscall(__NR_getrandom, buffer, count, flags);
 #  else
@@ -123,18 +119,14 @@ static inline int missing_getrandom(void *buffer, size_t count, unsigned flags)
         return -1;
 #  endif
 }
-
-#  define getrandom missing_getrandom
 #endif
 
 /* ======================================================================= */
 
 #if !HAVE_GETTID
-static inline pid_t missing_gettid(void) {
+static inline pid_t gettid(void) {
         return (pid_t) syscall(SYS_gettid);
 }
-
-#  define gettid missing_gettid
 #endif
 
 /* ======================================================================= */
@@ -162,7 +154,7 @@ struct file_handle {
         unsigned char f_handle[0];
 };
 
-static inline int missing_name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
+static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
 #  ifdef __NR_name_to_handle_at
         return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags);
 #  else
@@ -170,8 +162,6 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil
         return -1;
 #  endif
 }
-
-#  define name_to_handle_at missing_name_to_handle_at
 #endif
 
 /* ======================================================================= */
@@ -189,7 +179,7 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil
 #    endif
 #  endif
 
-static inline int missing_setns(int fd, int nstype) {
+static inline int setns(int fd, int nstype) {
 #  ifdef __NR_setns
         return syscall(__NR_setns, fd, nstype);
 #  else
@@ -197,8 +187,6 @@ static inline int missing_setns(int fd, int nstype) {
         return -1;
 #  endif
 }
-
-#  define setns missing_setns
 #endif
 
 /* ======================================================================= */
@@ -244,7 +232,7 @@ static inline pid_t raw_getpid(void) {
 #    endif
 #  endif
 
-static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
+static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
 #  ifdef __NR_renameat2
         return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
 #  else
@@ -252,14 +240,12 @@ static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, c
         return -1;
 #  endif
 }
-
-#  define renameat2 missing_renameat2
 #endif
 
 /* ======================================================================= */
 
 #if !HAVE_KCMP
-static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
+static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
 #  ifdef __NR_kcmp
         return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
 #  else
@@ -267,45 +253,36 @@ static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long i
         return -1;
 #  endif
 }
-
-#  define kcmp missing_kcmp
 #endif
 
-
 /* ======================================================================= */
 
 #if !HAVE_KEYCTL
-static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) {
+static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) {
 #  ifdef __NR_keyctl
         return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
 #  else
         errno = ENOSYS;
         return -1;
 #  endif
-
-#  define keyctl missing_keyctl
 }
 
-static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
+static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
 #  ifdef __NR_add_key
         return syscall(__NR_add_key, type, description, payload, plen, ringid);
 #  else
         errno = ENOSYS;
         return -1;
 #  endif
-
-#  define add_key missing_add_key
 }
 
-static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
+static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
 #  ifdef __NR_request_key
         return syscall(__NR_request_key, type, description, callout_info, destringid);
 #  else
         errno = ENOSYS;
         return -1;
 #  endif
-
-#  define request_key missing_request_key
 }
 #endif
 
@@ -332,10 +309,10 @@ static inline key_serial_t missing_request_key(const char *type, const char *des
 #    endif
 #  endif
 
-static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
-                                              int fd_out, loff_t *off_out,
-                                              size_t len,
-                                              unsigned int flags) {
+static inline ssize_t copy_file_range(int fd_in, loff_t *off_in,
+                                      int fd_out, loff_t *off_out,
+                                      size_t len,
+                                      unsigned int flags) {
 #  ifdef __NR_copy_file_range
         return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags);
 #  else
@@ -343,8 +320,6 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
         return -1;
 #  endif
 }
-
-#  define copy_file_range missing_copy_file_range
 #endif
 
 /* ======================================================================= */
@@ -372,7 +347,7 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
 
 union bpf_attr;
 
-static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
+static inline int bpf(int cmd, union bpf_attr *attr, size_t size) {
 #ifdef __NR_bpf
         return (int) syscall(__NR_bpf, cmd, attr, size);
 #else
@@ -381,7 +356,6 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
 #endif
 }
 
-#  define bpf missing_bpf
 #endif
 
 /* ======================================================================= */
@@ -411,3 +385,26 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
 #    endif
 #  endif
 #endif
+
+#if !HAVE_STATX
+#  ifndef __NR_statx
+#    if defined __i386__
+#      define __NR_bpf 383
+#    elif defined __x86_64__
+#      define __NR_bpf 332
+#    else
+#      warning "__NR_statx not defined for your architecture"
+#    endif
+#  endif
+
+struct statx;
+
+static inline ssize_t statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) {
+#  ifdef __NR_statx
+        return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
+#  else
+        errno = ENOSYS;
+        return -1;
+#  endif
+}
+#endif
index 97387579da77bc24b1b7a3f61230ce02f1ca42e0..160af2f6770bf6907aa792c83cf37d7495da30c8 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+//#include <linux/stat.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -29,6 +30,7 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "macro.h"
+//#include "missing.h"
 #include "sparse-endian.h"
 #include "stdio-util.h"
 //#include "string-util.h"
@@ -149,52 +151,66 @@ static int parse_crtime(le64_t le, usec_t *usec) {
         return 0;
 }
 
-int fd_getcrtime(int fd, usec_t *usec) {
+int fd_getcrtime_at(int dirfd, const char *name, usec_t *ret, int flags) {
+        struct statx sx;
+        usec_t a, b;
         le64_t le;
         ssize_t n;
+        int r;
 
-        assert(fd >= 0);
-        assert(usec);
-
-        /* Until Linux gets a real concept of birthtime/creation time,
-         * let's fake one with xattrs */
-
-        n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le));
-        if (n < 0)
-                return -errno;
-        if (n != sizeof(le))
-                return -EIO;
+        assert(ret);
 
-        return parse_crtime(le, usec);
-}
+        if (flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW))
+                return -EINVAL;
 
-int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
-        le64_t le;
-        ssize_t n;
+        /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
+         * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
+         * implemented on various file systems on the lower level since a while, but never was accessible). However, we
+         * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
+         * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
+         * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
+         * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
+         * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
+         * most sense. */
+
+        if (statx(dirfd, strempty(name), flags|AT_STATX_DONT_SYNC, STATX_BTIME, &sx) >= 0 &&
+            (sx.stx_mask & STATX_BTIME) &&
+            sx.stx_btime.tv_sec != 0)
+                a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
+                        (usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
+        else
+                a = USEC_INFINITY;
 
         n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags);
         if (n < 0)
-                return -errno;
-        if (n != sizeof(le))
-                return -EIO;
+                r = -errno;
+        else if (n != sizeof(le))
+                r = -EIO;
+        else
+                r = parse_crtime(le, &b);
+        if (r < 0) {
+                if (a != USEC_INFINITY) {
+                        *ret = a;
+                        return 0;
+                }
 
-        return parse_crtime(le, usec);
-}
+                return r;
+        }
 
-int path_getcrtime(const char *p, usec_t *usec) {
-        le64_t le;
-        ssize_t n;
+        if (a != USEC_INFINITY)
+                *ret = MIN(a, b);
+        else
+                *ret = b;
 
-        assert(p);
-        assert(usec);
+        return 0;
+}
 
-        n = getxattr(p, "user.crtime_usec", &le, sizeof(le));
-        if (n < 0)
-                return -errno;
-        if (n != sizeof(le))
-                return -EIO;
+int fd_getcrtime(int fd, usec_t *ret) {
+        return fd_getcrtime_at(fd, NULL, ret, AT_EMPTY_PATH);
+}
 
-        return parse_crtime(le, usec);
+int path_getcrtime(const char *p, usec_t *ret) {
+        return fd_getcrtime_at(AT_FDCWD, p, ret, 0);
 }
 
 int fd_setcrtime(int fd, usec_t usec) {
@@ -202,7 +218,7 @@ int fd_setcrtime(int fd, usec_t usec) {
 
         assert(fd >= 0);
 
-        if (usec <= 0)
+        if (IN_SET(usec, 0, USEC_INFINITY))
                 usec = now(CLOCK_REALTIME);
 
         le = htole64((uint64_t) usec);