Re: [PATCH 34/34] vfs: Add a sample program for the new mount API [ver #12]

From: Anders Roxell
Date: Mon Dec 17 2018 - 09:13:11 EST


On Fri, 21 Sep 2018 at 18:34, David Howells <dhowells@xxxxxxxxxx> wrote:
>
> Add a sample program to demonstrate fsopen/fsmount/move_mount to mount
> something.
>
> Signed-off-by: David Howells <dhowells@xxxxxxxxxx>

I was trying to build today linux-next tag next-20181217, I ran into
the build error below
when I was trying to build a allmodconfig kernel. I think this patch
triggers the build error.

In file included from /usr/include/x86_64-linux-gnu/sys/stat.h:446,
from ../samples/vfs/test-statx.c:28:
/usr/include/x86_64-linux-gnu/bits/statx.h:25:8: error: redefinition
of âstruct statx_timestampâ
struct statx_timestamp
^~~~~~~~~~~~~~~
In file included from ../samples/vfs/test-statx.c:26:
./usr/include/linux/stat.h:56:8: note: originally defined here
struct statx_timestamp {
^~~~~~~~~~~~~~~
In file included from /usr/include/x86_64-linux-gnu/sys/stat.h:446,
from ../samples/vfs/test-statx.c:28:
/usr/include/x86_64-linux-gnu/bits/statx.h:36:8: error: redefinition
of âstruct statxâ
struct statx
^~~~~
In file included from ../samples/vfs/test-statx.c:26:
./usr/include/linux/stat.h:99:8: note: originally defined here
struct statx {
^~~~~
../samples/vfs/test-statx.c:40:9: error: conflicting types for âstatxâ
ssize_t statx(int dfd, const char *filename, unsigned flags,
^~~~~
In file included from /usr/include/x86_64-linux-gnu/sys/stat.h:446,
from ../samples/vfs/test-statx.c:28:
/usr/include/x86_64-linux-gnu/bits/statx.h:87:5: note: previous
declaration of âstatxâ was here
int statx (int __dirfd, const char *__restrict __path, int __flags,
^~~~~
make[3]: *** [scripts/Makefile.host:90: samples/vfs/test-statx] Error 1
make[3]: Target '__build' not remade because of errors.
make[2]: *** [../scripts/Makefile.build:492: samples/vfs] Error 2
make[2]: Target '__build' not remade because of errors.
make[1]: *** [/srv/src/kernel/next/Makefile:1065: samples] Error 2
make[1]: Target 'bzImage' not remade because of errors.
make: *** [Makefile:152: sub-make] Error 2
make: Target 'bzImage' not remade because of errors.

My libc version:
$ dpkg -l libc6
ii libc6:amd64 2.28-2 amd64 GNU C Library: Shared libraries

Any idea what I do wrong?

Cheers,
Anders

> ---
>
> samples/Kconfig | 10 +-
> samples/Makefile | 2
> samples/statx/Makefile | 7 -
> samples/statx/test-statx.c | 258 --------------------------------------------
> samples/vfs/Makefile | 10 ++
> samples/vfs/test-fsmount.c | 118 ++++++++++++++++++++
> samples/vfs/test-statx.c | 258 ++++++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 393 insertions(+), 270 deletions(-)
> delete mode 100644 samples/statx/Makefile
> delete mode 100644 samples/statx/test-statx.c
> create mode 100644 samples/vfs/Makefile
> create mode 100644 samples/vfs/test-fsmount.c
> create mode 100644 samples/vfs/test-statx.c
>
> diff --git a/samples/Kconfig b/samples/Kconfig
> index bd133efc1a56..8df1c012820f 100644
> --- a/samples/Kconfig
> +++ b/samples/Kconfig
> @@ -146,10 +146,12 @@ config SAMPLE_VFIO_MDEV_MBOCHS
> Specifically it does *not* include any legacy vga stuff.
> Device looks a lot like "qemu -device secondary-vga".
>
> -config SAMPLE_STATX
> - bool "Build example extended-stat using code"
> - depends on BROKEN
> +config SAMPLE_VFS
> + bool "Build example programs that use new VFS system calls"
> + depends on X86
> help
> - Build example userspace program to use the new extended-stat syscall.
> + Build example userspace programs that use new VFS system calls such
> + as mount API and statx(). Note that this is restricted to the x86
> + arch whilst it accesses system calls that aren't yet in all arches.
>
> endif # SAMPLES
> diff --git a/samples/Makefile b/samples/Makefile
> index bd601c038b86..c5a6175c2d3f 100644
> --- a/samples/Makefile
> +++ b/samples/Makefile
> @@ -3,4 +3,4 @@
> obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \
> hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
> configfs/ connector/ v4l/ trace_printk/ \
> - vfio-mdev/ statx/ qmi/
> + vfio-mdev/ vfs/ qmi/
> diff --git a/samples/statx/Makefile b/samples/statx/Makefile
> deleted file mode 100644
> index 59df7c25a9d1..000000000000
> --- a/samples/statx/Makefile
> +++ /dev/null
> @@ -1,7 +0,0 @@
> -# List of programs to build
> -hostprogs-$(CONFIG_SAMPLE_STATX) := test-statx
> -
> -# Tell kbuild to always build the programs
> -always := $(hostprogs-y)
> -
> -HOSTCFLAGS_test-statx.o += -I$(objtree)/usr/include
> diff --git a/samples/statx/test-statx.c b/samples/statx/test-statx.c
> deleted file mode 100644
> index d4d77b09412c..000000000000
> --- a/samples/statx/test-statx.c
> +++ /dev/null
> @@ -1,258 +0,0 @@
> -/* Test the statx() system call.
> - *
> - * Note that the output of this program is intended to look like the output of
> - * /bin/stat where possible.
> - *
> - * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved.
> - * Written by David Howells (dhowells@xxxxxxxxxx)
> - *
> - * This program is free software; you can redistribute it and/or
> - * modify it under the terms of the GNU General Public Licence
> - * as published by the Free Software Foundation; either version
> - * 2 of the Licence, or (at your option) any later version.
> - */
> -
> -#define _GNU_SOURCE
> -#define _ATFILE_SOURCE
> -#include <stdio.h>
> -#include <stdlib.h>
> -#include <string.h>
> -#include <unistd.h>
> -#include <ctype.h>
> -#include <errno.h>
> -#include <time.h>
> -#include <sys/syscall.h>
> -#include <sys/types.h>
> -#include <linux/stat.h>
> -#include <linux/fcntl.h>
> -#include <sys/stat.h>
> -
> -#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
> -
> -static __attribute__((unused))
> -ssize_t statx(int dfd, const char *filename, unsigned flags,
> - unsigned int mask, struct statx *buffer)
> -{
> - return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
> -}
> -
> -static void print_time(const char *field, struct statx_timestamp *ts)
> -{
> - struct tm tm;
> - time_t tim;
> - char buffer[100];
> - int len;
> -
> - tim = ts->tv_sec;
> - if (!localtime_r(&tim, &tm)) {
> - perror("localtime_r");
> - exit(1);
> - }
> - len = strftime(buffer, 100, "%F %T", &tm);
> - if (len == 0) {
> - perror("strftime");
> - exit(1);
> - }
> - printf("%s", field);
> - fwrite(buffer, 1, len, stdout);
> - printf(".%09u", ts->tv_nsec);
> - len = strftime(buffer, 100, "%z", &tm);
> - if (len == 0) {
> - perror("strftime2");
> - exit(1);
> - }
> - fwrite(buffer, 1, len, stdout);
> - printf("\n");
> -}
> -
> -static void dump_statx(struct statx *stx)
> -{
> - char buffer[256], ft = '?';
> -
> - printf("results=%x\n", stx->stx_mask);
> -
> - printf(" ");
> - if (stx->stx_mask & STATX_SIZE)
> - printf(" Size: %-15llu", (unsigned long long)stx->stx_size);
> - if (stx->stx_mask & STATX_BLOCKS)
> - printf(" Blocks: %-10llu", (unsigned long long)stx->stx_blocks);
> - printf(" IO Block: %-6llu", (unsigned long long)stx->stx_blksize);
> - if (stx->stx_mask & STATX_TYPE) {
> - switch (stx->stx_mode & S_IFMT) {
> - case S_IFIFO: printf(" FIFO\n"); ft = 'p'; break;
> - case S_IFCHR: printf(" character special file\n"); ft = 'c'; break;
> - case S_IFDIR: printf(" directory\n"); ft = 'd'; break;
> - case S_IFBLK: printf(" block special file\n"); ft = 'b'; break;
> - case S_IFREG: printf(" regular file\n"); ft = '-'; break;
> - case S_IFLNK: printf(" symbolic link\n"); ft = 'l'; break;
> - case S_IFSOCK: printf(" socket\n"); ft = 's'; break;
> - default:
> - printf(" unknown type (%o)\n", stx->stx_mode & S_IFMT);
> - break;
> - }
> - } else {
> - printf(" no type\n");
> - }
> -
> - sprintf(buffer, "%02x:%02x", stx->stx_dev_major, stx->stx_dev_minor);
> - printf("Device: %-15s", buffer);
> - if (stx->stx_mask & STATX_INO)
> - printf(" Inode: %-11llu", (unsigned long long) stx->stx_ino);
> - if (stx->stx_mask & STATX_NLINK)
> - printf(" Links: %-5u", stx->stx_nlink);
> - if (stx->stx_mask & STATX_TYPE) {
> - switch (stx->stx_mode & S_IFMT) {
> - case S_IFBLK:
> - case S_IFCHR:
> - printf(" Device type: %u,%u",
> - stx->stx_rdev_major, stx->stx_rdev_minor);
> - break;
> - }
> - }
> - printf("\n");
> -
> - if (stx->stx_mask & STATX_MODE)
> - printf("Access: (%04o/%c%c%c%c%c%c%c%c%c%c) ",
> - stx->stx_mode & 07777,
> - ft,
> - stx->stx_mode & S_IRUSR ? 'r' : '-',
> - stx->stx_mode & S_IWUSR ? 'w' : '-',
> - stx->stx_mode & S_IXUSR ? 'x' : '-',
> - stx->stx_mode & S_IRGRP ? 'r' : '-',
> - stx->stx_mode & S_IWGRP ? 'w' : '-',
> - stx->stx_mode & S_IXGRP ? 'x' : '-',
> - stx->stx_mode & S_IROTH ? 'r' : '-',
> - stx->stx_mode & S_IWOTH ? 'w' : '-',
> - stx->stx_mode & S_IXOTH ? 'x' : '-');
> - if (stx->stx_mask & STATX_UID)
> - printf("Uid: %5d ", stx->stx_uid);
> - if (stx->stx_mask & STATX_GID)
> - printf("Gid: %5d\n", stx->stx_gid);
> -
> - if (stx->stx_mask & STATX_ATIME)
> - print_time("Access: ", &stx->stx_atime);
> - if (stx->stx_mask & STATX_MTIME)
> - print_time("Modify: ", &stx->stx_mtime);
> - if (stx->stx_mask & STATX_CTIME)
> - print_time("Change: ", &stx->stx_ctime);
> - if (stx->stx_mask & STATX_BTIME)
> - print_time(" Birth: ", &stx->stx_btime);
> -
> - if (stx->stx_attributes_mask) {
> - unsigned char bits, mbits;
> - int loop, byte;
> -
> - static char attr_representation[64 + 1] =
> - /* STATX_ATTR_ flags: */
> - "????????" /* 63-56 */
> - "????????" /* 55-48 */
> - "????????" /* 47-40 */
> - "????????" /* 39-32 */
> - "????????" /* 31-24 0x00000000-ff000000 */
> - "????????" /* 23-16 0x00000000-00ff0000 */
> - "???me???" /* 15- 8 0x00000000-0000ff00 */
> - "?dai?c??" /* 7- 0 0x00000000-000000ff */
> - ;
> -
> - printf("Attributes: %016llx (", stx->stx_attributes);
> - for (byte = 64 - 8; byte >= 0; byte -= 8) {
> - bits = stx->stx_attributes >> byte;
> - mbits = stx->stx_attributes_mask >> byte;
> - for (loop = 7; loop >= 0; loop--) {
> - int bit = byte + loop;
> -
> - if (!(mbits & 0x80))
> - putchar('.'); /* Not supported */
> - else if (bits & 0x80)
> - putchar(attr_representation[63 - bit]);
> - else
> - putchar('-'); /* Not set */
> - bits <<= 1;
> - mbits <<= 1;
> - }
> - if (byte)
> - putchar(' ');
> - }
> - printf(")\n");
> - }
> -}
> -
> -static void dump_hex(unsigned long long *data, int from, int to)
> -{
> - unsigned offset, print_offset = 1, col = 0;
> -
> - from /= 8;
> - to = (to + 7) / 8;
> -
> - for (offset = from; offset < to; offset++) {
> - if (print_offset) {
> - printf("%04x: ", offset * 8);
> - print_offset = 0;
> - }
> - printf("%016llx", data[offset]);
> - col++;
> - if ((col & 3) == 0) {
> - printf("\n");
> - print_offset = 1;
> - } else {
> - printf(" ");
> - }
> - }
> -
> - if (!print_offset)
> - printf("\n");
> -}
> -
> -int main(int argc, char **argv)
> -{
> - struct statx stx;
> - int ret, raw = 0, atflag = AT_SYMLINK_NOFOLLOW;
> -
> - unsigned int mask = STATX_ALL;
> -
> - for (argv++; *argv; argv++) {
> - if (strcmp(*argv, "-F") == 0) {
> - atflag &= ~AT_STATX_SYNC_TYPE;
> - atflag |= AT_STATX_FORCE_SYNC;
> - continue;
> - }
> - if (strcmp(*argv, "-D") == 0) {
> - atflag &= ~AT_STATX_SYNC_TYPE;
> - atflag |= AT_STATX_DONT_SYNC;
> - continue;
> - }
> - if (strcmp(*argv, "-L") == 0) {
> - atflag &= ~AT_SYMLINK_NOFOLLOW;
> - continue;
> - }
> - if (strcmp(*argv, "-O") == 0) {
> - mask &= ~STATX_BASIC_STATS;
> - continue;
> - }
> - if (strcmp(*argv, "-A") == 0) {
> - atflag |= AT_NO_AUTOMOUNT;
> - continue;
> - }
> - if (strcmp(*argv, "-R") == 0) {
> - raw = 1;
> - continue;
> - }
> -
> - memset(&stx, 0xbf, sizeof(stx));
> - ret = statx(AT_FDCWD, *argv, atflag, mask, &stx);
> - printf("statx(%s) = %d\n", *argv, ret);
> - if (ret < 0) {
> - perror(*argv);
> - exit(1);
> - }
> -
> - if (raw)
> - dump_hex((unsigned long long *)&stx, 0, sizeof(stx));
> -
> - dump_statx(&stx);
> - }
> - return 0;
> -}
> diff --git a/samples/vfs/Makefile b/samples/vfs/Makefile
> new file mode 100644
> index 000000000000..4ac9690fb3c4
> --- /dev/null
> +++ b/samples/vfs/Makefile
> @@ -0,0 +1,10 @@
> +# List of programs to build
> +hostprogs-$(CONFIG_SAMPLE_VFS) := \
> + test-fsmount \
> + test-statx
> +
> +# Tell kbuild to always build the programs
> +always := $(hostprogs-y)
> +
> +HOSTCFLAGS_test-fsmount.o += -I$(objtree)/usr/include
> +HOSTCFLAGS_test-statx.o += -I$(objtree)/usr/include
> diff --git a/samples/vfs/test-fsmount.c b/samples/vfs/test-fsmount.c
> new file mode 100644
> index 000000000000..74124025ade0
> --- /dev/null
> +++ b/samples/vfs/test-fsmount.c
> @@ -0,0 +1,118 @@
> +/* fd-based mount test.
> + *
> + * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@xxxxxxxxxx)
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <sys/prctl.h>
> +#include <sys/wait.h>
> +#include <linux/fs.h>
> +#include <linux/unistd.h>
> +
> +#define E(x) do { if ((x) == -1) { perror(#x); exit(1); } } while(0)
> +
> +static void check_messages(int fd)
> +{
> + char buf[4096];
> + int err, n;
> +
> + err = errno;
> +
> + for (;;) {
> + n = read(fd, buf, sizeof(buf));
> + if (n < 0)
> + break;
> + n -= 2;
> +
> + switch (buf[0]) {
> + case 'e':
> + fprintf(stderr, "Error: %*.*s\n", n, n, buf + 2);
> + break;
> + case 'w':
> + fprintf(stderr, "Warning: %*.*s\n", n, n, buf + 2);
> + break;
> + case 'i':
> + fprintf(stderr, "Info: %*.*s\n", n, n, buf + 2);
> + break;
> + }
> + }
> +
> + errno = err;
> +}
> +
> +static __attribute__((noreturn))
> +void mount_error(int fd, const char *s)
> +{
> + check_messages(fd);
> + fprintf(stderr, "%s: %m\n", s);
> + exit(1);
> +}
> +
> +static inline int fsopen(const char *fs_name, unsigned int flags)
> +{
> + return syscall(__NR_fsopen, fs_name, flags);
> +}
> +
> +static inline int fsmount(int fsfd, unsigned int flags, unsigned int ms_flags)
> +{
> + return syscall(__NR_fsmount, fsfd, flags, ms_flags);
> +}
> +
> +static inline int fsconfig(int fsfd, unsigned int cmd,
> + const char *key, const void *val, int aux)
> +{
> + return syscall(__NR_fsconfig, fsfd, cmd, key, val, aux);
> +}
> +
> +static inline int move_mount(int from_dfd, const char *from_pathname,
> + int to_dfd, const char *to_pathname,
> + unsigned int flags)
> +{
> + return syscall(__NR_move_mount,
> + from_dfd, from_pathname,
> + to_dfd, to_pathname, flags);
> +}
> +
> +#define E_fsconfig(fd, cmd, key, val, aux) \
> + do { \
> + if (fsconfig(fd, cmd, key, val, aux) == -1) \
> + mount_error(fd, key ?: "create"); \
> + } while (0)
> +
> +int main(int argc, char *argv[])
> +{
> + int fsfd, mfd;
> +
> + /* Mount a publically available AFS filesystem */
> + fsfd = fsopen("afs", 0);
> + if (fsfd == -1) {
> + perror("fsopen");
> + exit(1);
> + }
> +
> + E_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "#grand.central.org:root.cell.", 0);
> + E_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
> +
> + mfd = fsmount(fsfd, 0, MS_RDONLY);
> + if (mfd < 0)
> + mount_error(fsfd, "fsmount");
> + E(close(fsfd));
> +
> + if (move_mount(mfd, "", AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH) < 0) {
> + perror("move_mount");
> + exit(1);
> + }
> +
> + E(close(mfd));
> + exit(0);
> +}
> diff --git a/samples/vfs/test-statx.c b/samples/vfs/test-statx.c
> new file mode 100644
> index 000000000000..d4d77b09412c
> --- /dev/null
> +++ b/samples/vfs/test-statx.c
> @@ -0,0 +1,258 @@
> +/* Test the statx() system call.
> + *
> + * Note that the output of this program is intended to look like the output of
> + * /bin/stat where possible.
> + *
> + * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@xxxxxxxxxx)
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#define _GNU_SOURCE
> +#define _ATFILE_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <ctype.h>
> +#include <errno.h>
> +#include <time.h>
> +#include <sys/syscall.h>
> +#include <sys/types.h>
> +#include <linux/stat.h>
> +#include <linux/fcntl.h>
> +#include <sys/stat.h>
> +
> +#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
> +
> +static __attribute__((unused))
> +ssize_t statx(int dfd, const char *filename, unsigned flags,
> + unsigned int mask, struct statx *buffer)
> +{
> + return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
> +}
> +
> +static void print_time(const char *field, struct statx_timestamp *ts)
> +{
> + struct tm tm;
> + time_t tim;
> + char buffer[100];
> + int len;
> +
> + tim = ts->tv_sec;
> + if (!localtime_r(&tim, &tm)) {
> + perror("localtime_r");
> + exit(1);
> + }
> + len = strftime(buffer, 100, "%F %T", &tm);
> + if (len == 0) {
> + perror("strftime");
> + exit(1);
> + }
> + printf("%s", field);
> + fwrite(buffer, 1, len, stdout);
> + printf(".%09u", ts->tv_nsec);
> + len = strftime(buffer, 100, "%z", &tm);
> + if (len == 0) {
> + perror("strftime2");
> + exit(1);
> + }
> + fwrite(buffer, 1, len, stdout);
> + printf("\n");
> +}
> +
> +static void dump_statx(struct statx *stx)
> +{
> + char buffer[256], ft = '?';
> +
> + printf("results=%x\n", stx->stx_mask);
> +
> + printf(" ");
> + if (stx->stx_mask & STATX_SIZE)
> + printf(" Size: %-15llu", (unsigned long long)stx->stx_size);
> + if (stx->stx_mask & STATX_BLOCKS)
> + printf(" Blocks: %-10llu", (unsigned long long)stx->stx_blocks);
> + printf(" IO Block: %-6llu", (unsigned long long)stx->stx_blksize);
> + if (stx->stx_mask & STATX_TYPE) {
> + switch (stx->stx_mode & S_IFMT) {
> + case S_IFIFO: printf(" FIFO\n"); ft = 'p'; break;
> + case S_IFCHR: printf(" character special file\n"); ft = 'c'; break;
> + case S_IFDIR: printf(" directory\n"); ft = 'd'; break;
> + case S_IFBLK: printf(" block special file\n"); ft = 'b'; break;
> + case S_IFREG: printf(" regular file\n"); ft = '-'; break;
> + case S_IFLNK: printf(" symbolic link\n"); ft = 'l'; break;
> + case S_IFSOCK: printf(" socket\n"); ft = 's'; break;
> + default:
> + printf(" unknown type (%o)\n", stx->stx_mode & S_IFMT);
> + break;
> + }
> + } else {
> + printf(" no type\n");
> + }
> +
> + sprintf(buffer, "%02x:%02x", stx->stx_dev_major, stx->stx_dev_minor);
> + printf("Device: %-15s", buffer);
> + if (stx->stx_mask & STATX_INO)
> + printf(" Inode: %-11llu", (unsigned long long) stx->stx_ino);
> + if (stx->stx_mask & STATX_NLINK)
> + printf(" Links: %-5u", stx->stx_nlink);
> + if (stx->stx_mask & STATX_TYPE) {
> + switch (stx->stx_mode & S_IFMT) {
> + case S_IFBLK:
> + case S_IFCHR:
> + printf(" Device type: %u,%u",
> + stx->stx_rdev_major, stx->stx_rdev_minor);
> + break;
> + }
> + }
> + printf("\n");
> +
> + if (stx->stx_mask & STATX_MODE)
> + printf("Access: (%04o/%c%c%c%c%c%c%c%c%c%c) ",
> + stx->stx_mode & 07777,
> + ft,
> + stx->stx_mode & S_IRUSR ? 'r' : '-',
> + stx->stx_mode & S_IWUSR ? 'w' : '-',
> + stx->stx_mode & S_IXUSR ? 'x' : '-',
> + stx->stx_mode & S_IRGRP ? 'r' : '-',
> + stx->stx_mode & S_IWGRP ? 'w' : '-',
> + stx->stx_mode & S_IXGRP ? 'x' : '-',
> + stx->stx_mode & S_IROTH ? 'r' : '-',
> + stx->stx_mode & S_IWOTH ? 'w' : '-',
> + stx->stx_mode & S_IXOTH ? 'x' : '-');
> + if (stx->stx_mask & STATX_UID)
> + printf("Uid: %5d ", stx->stx_uid);
> + if (stx->stx_mask & STATX_GID)
> + printf("Gid: %5d\n", stx->stx_gid);
> +
> + if (stx->stx_mask & STATX_ATIME)
> + print_time("Access: ", &stx->stx_atime);
> + if (stx->stx_mask & STATX_MTIME)
> + print_time("Modify: ", &stx->stx_mtime);
> + if (stx->stx_mask & STATX_CTIME)
> + print_time("Change: ", &stx->stx_ctime);
> + if (stx->stx_mask & STATX_BTIME)
> + print_time(" Birth: ", &stx->stx_btime);
> +
> + if (stx->stx_attributes_mask) {
> + unsigned char bits, mbits;
> + int loop, byte;
> +
> + static char attr_representation[64 + 1] =
> + /* STATX_ATTR_ flags: */
> + "????????" /* 63-56 */
> + "????????" /* 55-48 */
> + "????????" /* 47-40 */
> + "????????" /* 39-32 */
> + "????????" /* 31-24 0x00000000-ff000000 */
> + "????????" /* 23-16 0x00000000-00ff0000 */
> + "???me???" /* 15- 8 0x00000000-0000ff00 */
> + "?dai?c??" /* 7- 0 0x00000000-000000ff */
> + ;
> +
> + printf("Attributes: %016llx (", stx->stx_attributes);
> + for (byte = 64 - 8; byte >= 0; byte -= 8) {
> + bits = stx->stx_attributes >> byte;
> + mbits = stx->stx_attributes_mask >> byte;
> + for (loop = 7; loop >= 0; loop--) {
> + int bit = byte + loop;
> +
> + if (!(mbits & 0x80))
> + putchar('.'); /* Not supported */
> + else if (bits & 0x80)
> + putchar(attr_representation[63 - bit]);
> + else
> + putchar('-'); /* Not set */
> + bits <<= 1;
> + mbits <<= 1;
> + }
> + if (byte)
> + putchar(' ');
> + }
> + printf(")\n");
> + }
> +}
> +
> +static void dump_hex(unsigned long long *data, int from, int to)
> +{
> + unsigned offset, print_offset = 1, col = 0;
> +
> + from /= 8;
> + to = (to + 7) / 8;
> +
> + for (offset = from; offset < to; offset++) {
> + if (print_offset) {
> + printf("%04x: ", offset * 8);
> + print_offset = 0;
> + }
> + printf("%016llx", data[offset]);
> + col++;
> + if ((col & 3) == 0) {
> + printf("\n");
> + print_offset = 1;
> + } else {
> + printf(" ");
> + }
> + }
> +
> + if (!print_offset)
> + printf("\n");
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct statx stx;
> + int ret, raw = 0, atflag = AT_SYMLINK_NOFOLLOW;
> +
> + unsigned int mask = STATX_ALL;
> +
> + for (argv++; *argv; argv++) {
> + if (strcmp(*argv, "-F") == 0) {
> + atflag &= ~AT_STATX_SYNC_TYPE;
> + atflag |= AT_STATX_FORCE_SYNC;
> + continue;
> + }
> + if (strcmp(*argv, "-D") == 0) {
> + atflag &= ~AT_STATX_SYNC_TYPE;
> + atflag |= AT_STATX_DONT_SYNC;
> + continue;
> + }
> + if (strcmp(*argv, "-L") == 0) {
> + atflag &= ~AT_SYMLINK_NOFOLLOW;
> + continue;
> + }
> + if (strcmp(*argv, "-O") == 0) {
> + mask &= ~STATX_BASIC_STATS;
> + continue;
> + }
> + if (strcmp(*argv, "-A") == 0) {
> + atflag |= AT_NO_AUTOMOUNT;
> + continue;
> + }
> + if (strcmp(*argv, "-R") == 0) {
> + raw = 1;
> + continue;
> + }
> +
> + memset(&stx, 0xbf, sizeof(stx));
> + ret = statx(AT_FDCWD, *argv, atflag, mask, &stx);
> + printf("statx(%s) = %d\n", *argv, ret);
> + if (ret < 0) {
> + perror(*argv);
> + exit(1);
> + }
> +
> + if (raw)
> + dump_hex((unsigned long long *)&stx, 0, sizeof(stx));
> +
> + dump_statx(&stx);
> + }
> + return 0;
> +}
>