From fd70af45528d59a00eb3190ef6706cb299488fcd Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Tue, 10 Jul 2018 16:14:45 +0200 Subject: Add the statx function --- io/Makefile | 5 +- io/Versions | 1 + io/bits/statx.h | 91 +++++++++++++++++++++++++++++++ io/fcntl.h | 4 ++ io/statx.c | 29 ++++++++++ io/statx_generic.c | 81 +++++++++++++++++++++++++++ io/sys/stat.h | 4 ++ io/tst-statx.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 io/bits/statx.h create mode 100644 io/statx.c create mode 100644 io/statx_generic.c create mode 100644 io/tst-statx.c (limited to 'io') diff --git a/io/Makefile b/io/Makefile index 4a0d8fea09..f5b1b61d4e 100644 --- a/io/Makefile +++ b/io/Makefile @@ -31,7 +31,7 @@ routines := \ utime \ mkfifo mkfifoat \ stat fstat lstat stat64 fstat64 lstat64 fstatat fstatat64 \ - xstat fxstat lxstat xstat64 fxstat64 lxstat64 \ + xstat fxstat lxstat xstat64 fxstat64 lxstat64 statx \ mknod mknodat xmknod xmknodat \ fxstatat fxstatat64 \ statfs fstatfs statfs64 fstatfs64 \ @@ -78,6 +78,9 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ tests-static += tst-copy_file_range-compat tests-internal += tst-copy_file_range-compat +# Likewise for statx, but we do not need static linking here. +tests-internal += tst-statx + ifeq ($(run-built-tests),yes) tests-special += $(objpfx)ftwtest.out endif diff --git a/io/Versions b/io/Versions index 98037fbbfc..f7e5dbe49e 100644 --- a/io/Versions +++ b/io/Versions @@ -130,6 +130,7 @@ libc { } GLIBC_2.28 { fcntl64; + statx; } GLIBC_PRIVATE { __libc_fcntl64; diff --git a/io/bits/statx.h b/io/bits/statx.h new file mode 100644 index 0000000000..e31254e361 --- /dev/null +++ b/io/bits/statx.h @@ -0,0 +1,91 @@ +/* statx-related definitions and declarations. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This interface is based on in Linux. */ + +#ifndef _SYS_STAT_H +# error Never include directly, include instead. +#endif + +struct statx_timestamp +{ + __int64_t tv_sec; + __uint32_t tv_nsec; + __int32_t __statx_timestamp_pad1[1]; +}; + +/* Warning: The kernel may add additional fields to this struct in the + future. Only use this struct for calling the statx function, not + for storing data. (Expansion will be controlled by the mask + argument of the statx function.) */ +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 __statx_pad1[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 __statx_pad2[14]; +}; + +#define STATX_TYPE 0x0001U +#define STATX_MODE 0x0002U +#define STATX_NLINK 0x0004U +#define STATX_UID 0x0008U +#define STATX_GID 0x0010U +#define STATX_ATIME 0x0020U +#define STATX_MTIME 0x0040U +#define STATX_CTIME 0x0080U +#define STATX_INO 0x0100U +#define STATX_SIZE 0x0200U +#define STATX_BLOCKS 0x0400U +#define STATX_BASIC_STATS 0x07ffU +#define STATX_ALL 0x0fffU +#define STATX_BTIME 0x0800U +#define STATX__RESERVED 0x80000000U + +#define STATX_ATTR_COMPRESSED 0x0004 +#define STATX_ATTR_IMMUTABLE 0x0010 +#define STATX_ATTR_APPEND 0x0020 +#define STATX_ATTR_NODUMP 0x0040 +#define STATX_ATTR_ENCRYPTED 0x0800 +#define STATX_ATTR_AUTOMOUNT 0x1000 + +__BEGIN_DECLS + +/* Fill *BUF with information about PATH in DIRFD. */ +int statx (int __dirfd, const char *__restrict __path, int __flags, + unsigned int __mask, struct statx *__restrict __buf) + __THROW __nonnull ((2, 5)); + +__END_DECLS diff --git a/io/fcntl.h b/io/fcntl.h index 3afc62011a..6b0e9fa1fa 100644 --- a/io/fcntl.h +++ b/io/fcntl.h @@ -157,6 +157,10 @@ typedef __pid_t pid_t; # define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal. */ # define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname. */ +# define AT_STATX_SYNC_TYPE 0x6000 +# define AT_STATX_SYNC_AS_STAT 0x0000 +# define AT_STATX_FORCE_SYNC 0x2000 +# define AT_STATX_DONT_SYNC 0x4000 # endif # define AT_EACCESS 0x200 /* Test access permitted for effective IDs, not real IDs. */ diff --git a/io/statx.c b/io/statx.c new file mode 100644 index 0000000000..0b90cd00d1 --- /dev/null +++ b/io/statx.c @@ -0,0 +1,29 @@ +/* Generic statx implementation. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +#include "statx_generic.c" + +int +statx (int fd, const char *path, int flags, + unsigned int mask, struct statx *buf) +{ + return statx_generic (fd, path, flags, mask, buf); +} diff --git a/io/statx_generic.c b/io/statx_generic.c new file mode 100644 index 0000000000..df327f8c52 --- /dev/null +++ b/io/statx_generic.c @@ -0,0 +1,81 @@ +/* Generic implementation of statx based on fstatat64. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static inline struct statx_timestamp +statx_convert_timestamp (struct timespec tv) +{ + return (struct statx_timestamp) { tv.tv_sec, tv.tv_nsec }; +} + +/* Approximate emulation of statx. This will always fill in + POSIX-mandated attributes even if the underlying file system does + not actually support it (for example, GID and UID on file systems + without UNIX-style permissions). */ +static __attribute__ ((unused)) int +statx_generic (int fd, const char *path, int flags, + unsigned int mask, struct statx *buf) +{ + /* Flags which need to be cleared before passing them to + fstatat64. */ + static const int clear_flags = AT_STATX_SYNC_AS_STAT; + + /* Flags supported by our emulation. */ + static const int supported_flags + = AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | clear_flags; + + if (__glibc_unlikely ((flags & ~supported_flags) != 0)) + { + __set_errno (EINVAL); + return -1; + } + + struct stat64 st; + int ret = __fstatat64 (fd, path, &st, flags & ~clear_flags); + if (ret != 0) + return ret; + + /* The interface is defined in such a way that unused (padding) + fields have to be cleared. STATX_BASIC_STATS corresponds to the + data which is available via fstatat64. */ + *buf = (struct statx) + { + .stx_mask = STATX_BASIC_STATS, + .stx_blksize = st.st_blksize, + .stx_nlink = st.st_nlink, + .stx_uid = st.st_uid, + .stx_gid = st.st_gid, + .stx_mode = st.st_mode, + .stx_ino = st.st_ino, + .stx_size = st.st_size, + .stx_blocks = st.st_blocks, + .stx_atime = statx_convert_timestamp (st.st_atim), + .stx_ctime = statx_convert_timestamp (st.st_ctim), + .stx_mtime = statx_convert_timestamp (st.st_mtim), + .stx_rdev_major = major (st.st_rdev), + .stx_rdev_minor = minor (st.st_rdev), + .stx_dev_major = major (st.st_dev), + .stx_dev_minor = minor (st.st_dev), + }; + + return 0; +} diff --git a/io/sys/stat.h b/io/sys/stat.h index 90c403cfd6..762c8538ba 100644 --- a/io/sys/stat.h +++ b/io/sys/stat.h @@ -442,6 +442,10 @@ extern int __xmknodat (int __ver, int __fd, const char *__path, __mode_t __mode, __dev_t *__dev) __THROW __nonnull ((3, 5)); +#ifdef __USE_GNU +# include +#endif + #ifdef __USE_EXTERN_INLINES /* Inlined versions of the real stat and mknod functions. */ diff --git a/io/tst-statx.c b/io/tst-statx.c new file mode 100644 index 0000000000..61bf31d177 --- /dev/null +++ b/io/tst-statx.c @@ -0,0 +1,157 @@ +/* Basic test of statx system call. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Ensure that the types have the kernel-expected layout. */ +_Static_assert (sizeof (struct statx_timestamp) == 16, "statx_timestamp size"); +_Static_assert (sizeof (struct statx) == 256, "statx size"); +_Static_assert (offsetof (struct statx, stx_nlink) == 16, "statx nlink"); +_Static_assert (offsetof (struct statx, stx_ino) == 32, "statx ino"); +_Static_assert (offsetof (struct statx, stx_atime) == 64, "statx atime"); +_Static_assert (offsetof (struct statx, stx_rdev_major) == 128, "statx rdev"); +_Static_assert (offsetof (struct statx, __statx_pad2) == 144, "statx pad2"); + +#include "statx_generic.c" + +typedef int (*statx_function) (int, const char *, int, unsigned int, + struct statx *); + +/* Return true if we have a real implementation of statx. */ +static bool +kernel_supports_statx (void) +{ +#ifdef __NR_statx + struct statx buf; + return syscall (__NR_statx, 0, "", AT_EMPTY_PATH, 0, &buf) == 0 + || errno != ENOSYS; +#else + return false; +#endif +} + +/* Tests which apply to both implementations. */ +static void +both_implementations_tests (statx_function impl, const char *path, int fd) +{ + uint64_t ino; + { + struct statx buf = { 0, }; + TEST_COMPARE (statx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &buf), 0); + TEST_COMPARE (buf.stx_size, 3); + ino = buf.stx_ino; + } + { + struct statx buf = { 0, }; + TEST_COMPARE (statx (AT_FDCWD, path, 0, STATX_BASIC_STATS, &buf), 0); + TEST_COMPARE (buf.stx_size, 3); + TEST_COMPARE (buf.stx_ino, ino); + } + { + struct statx stx = { 0, }; + TEST_COMPARE (statx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &stx), 0); + struct stat64 st; + xfstat (fd, &st); + TEST_COMPARE (stx.stx_mode, st.st_mode); + TEST_COMPARE (stx.stx_dev_major, major (st.st_dev)); + TEST_COMPARE (stx.stx_dev_minor, minor (st.st_dev)); + } + { + struct statx stx = { 0, }; + TEST_COMPARE (statx (AT_FDCWD, "/dev/null", 0, STATX_BASIC_STATS, &stx), + 0); + struct stat64 st; + xstat ("/dev/null", &st); + TEST_COMPARE (stx.stx_mode, st.st_mode); + TEST_COMPARE (stx.stx_dev_major, major (st.st_dev)); + TEST_COMPARE (stx.stx_dev_minor, minor (st.st_dev)); + TEST_COMPARE (stx.stx_rdev_major, major (st.st_rdev)); + TEST_COMPARE (stx.stx_rdev_minor, minor (st.st_rdev)); + } +} + +/* Tests which apply only to the non-kernel (generic) + implementation. */ +static void +non_kernel_tests (statx_function impl, int fd) +{ + /* The non-kernel implementation must always fail for explicit sync + flags. */ + struct statx buf; + errno = 0; + TEST_COMPARE (impl (fd, "", AT_EMPTY_PATH | AT_STATX_FORCE_SYNC, + STATX_BASIC_STATS, &buf), -1); + TEST_COMPARE (errno, EINVAL); + errno = 0; + TEST_COMPARE (impl (fd, "", AT_EMPTY_PATH | AT_STATX_DONT_SYNC, + STATX_BASIC_STATS, &buf), -1); + TEST_COMPARE (errno, EINVAL); +} + +static int +do_test (void) +{ + char *path; + int fd = create_temp_file ("tst-statx-", &path); + TEST_VERIFY_EXIT (fd >= 0); + support_write_file_string (path, "abc"); + + both_implementations_tests (&statx, path, fd); + both_implementations_tests (&statx_generic, path, fd); + + if (kernel_supports_statx ()) + { + puts ("info: kernel supports statx"); + struct statx buf; + buf.stx_size = 0; + TEST_COMPARE (statx (fd, "", AT_EMPTY_PATH | AT_STATX_FORCE_SYNC, + STATX_BASIC_STATS, &buf), + 0); + TEST_COMPARE (buf.stx_size, 3); + buf.stx_size = 0; + TEST_COMPARE (statx (fd, "", AT_EMPTY_PATH | AT_STATX_DONT_SYNC, + STATX_BASIC_STATS, &buf), + 0); + TEST_COMPARE (buf.stx_size, 3); + } + else + { + puts ("info: kernel does not support statx"); + non_kernel_tests (&statx, fd); + } + non_kernel_tests (&statx_generic, fd); + + xclose (fd); + free (path); + + return 0; +} + +#include -- cgit v1.2.3