diff mbox series

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

Message ID 153754768644.17872.4707695831889551662.stgit@warthog.procyon.org.uk (mailing list archive)
State New, archived
Headers show
Series VFS: Introduce filesystem context [ver #12] | expand

Commit Message

David Howells Sept. 21, 2018, 4:34 p.m. UTC
Add a sample program to demonstrate fsopen/fsmount/move_mount to mount
something.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 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

Comments

Anders Roxell Dec. 17, 2018, 2:12 p.m. UTC | #1
On Fri, 21 Sep 2018 at 18:34, David Howells <dhowells@redhat.com> wrote:
>
> Add a sample program to demonstrate fsopen/fsmount/move_mount to mount
> something.
>
> Signed-off-by: David Howells <dhowells@redhat.com>

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@redhat.com)
> - *
> - * 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@redhat.com)
> + *
> + * 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@redhat.com)
> + *
> + * 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;
> +}
>
David Howells Dec. 20, 2018, 4:36 p.m. UTC | #2
Anders Roxell <anders.roxell@linaro.org> wrote:

> 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’

Yeah - the problem is that statx has now made it into glibc, but the sample
program doesn't deal with it turning up in the system headers.  I've passed a
patch to Al already.

David
diff mbox series

Patch

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@redhat.com)
- *
- * 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@redhat.com)
+ *
+ * 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@redhat.com)
+ *
+ * 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;
+}