8cf838
From b6a4efe4347a061161054698857b6dde0f3ed67c Mon Sep 17 00:00:00 2001
8cf838
From: Martin Bukatovic <martin.bukatovic@gmail.com>
8cf838
Date: Sat, 2 Mar 2019 19:57:17 -0800
8cf838
Subject: [PATCH 1/5] stat: print birth time on systems supporting statx
8cf838
8cf838
* configure.ac: Check for statx(), available on glibc >= 2.28.
8cf838
* src/stat.c (get_birthtime): Call statx() when available.
8cf838
* NEWS: Mention the improvement.
8cf838
8cf838
Upstream-commit: 186896d65f6182dff15cad6c1045d22ad2004962
8cf838
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
8cf838
---
8cf838
 configure.ac |  3 +++
8cf838
 src/stat.c   | 19 +++++++++++++++++++
8cf838
 2 files changed, 22 insertions(+)
8cf838
8cf838
diff --git a/configure.ac b/configure.ac
8cf838
index 9f7a8a5..c24ce2a 100644
8cf838
--- a/configure.ac
8cf838
+++ b/configure.ac
8cf838
@@ -318,6 +318,9 @@ if test $ac_cv_func_getattrat = yes; then
8cf838
   AC_SUBST([LIB_NVPAIR])
8cf838
 fi
8cf838
 
8cf838
+# glibc >= 2.28 and linux kernel >= 4.11
8cf838
+AC_CHECK_FUNCS([statx])
8cf838
+
8cf838
 # SCO-ODT-3.0 is reported to need -los to link programs using initgroups
8cf838
 AC_CHECK_FUNCS([initgroups])
8cf838
 if test $ac_cv_func_initgroups = no; then
8cf838
diff --git a/src/stat.c b/src/stat.c
8cf838
index 0a5ef3c..9e71cbe 100644
8cf838
--- a/src/stat.c
8cf838
+++ b/src/stat.c
8cf838
@@ -1007,6 +1007,25 @@ get_birthtime (int fd, char const *filename, struct stat const *st)
8cf838
     }
8cf838
 #endif
8cf838
 
8cf838
+#if HAVE_STATX
8cf838
+  if (ts.tv_nsec < 0)
8cf838
+    {
8cf838
+      struct statx stx;
8cf838
+      if ((fd < 0
8cf838
+           ? statx (AT_FDCWD, filename,
8cf838
+                    follow_links ? 0 : AT_SYMLINK_NOFOLLOW,
8cf838
+                    STATX_BTIME, &stx)
8cf838
+           : statx (fd, "", AT_EMPTY_PATH, STATX_BTIME, &stx)) == 0)
8cf838
+        {
8cf838
+          if ((stx.stx_mask & STATX_BTIME) && stx.stx_btime.tv_sec != 0)
8cf838
+            {
8cf838
+              ts.tv_sec = stx.stx_btime.tv_sec;
8cf838
+              ts.tv_nsec = stx.stx_btime.tv_nsec;
8cf838
+            }
8cf838
+        }
8cf838
+    }
8cf838
+#endif
8cf838
+
8cf838
   return ts;
8cf838
 }
8cf838
 
8cf838
-- 
8cf838
2.20.1
8cf838
8cf838
8cf838
From 21ff41c1e5ef4668669f92d04c69a258708ba20e Mon Sep 17 00:00:00 2001
8cf838
From: Jeff Layton <jlayton@kernel.org>
8cf838
Date: Tue, 28 May 2019 08:21:42 -0400
8cf838
Subject: [PATCH 2/5] stat: Use statx where available and support --cached
8cf838
8cf838
* src/stat.c: Drop statbuf argument from out_epoch_sec().
8cf838
Use statx() rather than [lf]stat() where available,
8cf838
so a separate call is not required to get birth time.
8cf838
Set STATX_* mask bits only for things we want to print,
8cf838
which can be more efficient on some file systems.
8cf838
Add a new --cache= command-line option that sets the appropriate hint
8cf838
flags in the statx call.  These are primarily used with network
8cf838
file systems to indicate what level of cache coherency is desired.
8cf838
The new option is available unconditionally for better portability,
8cf838
and ignored where not implemented.
8cf838
* doc/coreutils.texi: Add documention for --cached.
8cf838
* man/stat.x (SEE ALSO): Mention statx().
8cf838
8cf838
Upstream-commit: 6cc35de16fdc52d417602b66d5e90694d7e02994
8cf838
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
8cf838
---
8cf838
 doc/coreutils.texi |  21 ++
8cf838
 man/stat.x         |   2 +-
8cf838
 src/stat.c         | 623 ++++++++++++++++++++++++++++++---------------
8cf838
 3 files changed, 444 insertions(+), 202 deletions(-)
8cf838
8cf838
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
8cf838
index 6ac99be..547d17b 100644
8cf838
--- a/doc/coreutils.texi
8cf838
+++ b/doc/coreutils.texi
8cf838
@@ -12201,6 +12201,27 @@ Report information about the file systems where the given files are located
8cf838
 instead of information about the files themselves.
8cf838
 This option implies the @option{-L} option.
8cf838
 
8cf838
+@item --cached=@var{mode}
8cf838
+@opindex --cached=@var{mode}
8cf838
+@cindex attribute caching
8cf838
+Control how attributes are read from the file system;
8cf838
+if supported by the system.  This allows one to
8cf838
+control the trade-off between freshness and efficiency
8cf838
+of attribute access, especially useful with remote file systems.
8cf838
+@var{mode} can be:
8cf838
+
8cf838
+@table @samp
8cf838
+@item always
8cf838
+Always read the already cached attributes if available.
8cf838
+
8cf838
+@item never
8cf838
+Always sychronize with the latest file system attributes.
8cf838
+
8cf838
+@item default
8cf838
+Leave the caching behavior to the underlying file system.
8cf838
+
8cf838
+@end table
8cf838
+
8cf838
 @item -c
8cf838
 @itemx --format=@var{format}
8cf838
 @opindex -c
8cf838
diff --git a/man/stat.x b/man/stat.x
8cf838
index dc3781e..b9f8c68 100644
8cf838
--- a/man/stat.x
8cf838
+++ b/man/stat.x
8cf838
@@ -3,4 +3,4 @@ stat \- display file or file system status
8cf838
 [DESCRIPTION]
8cf838
 .\" Add any additional description here
8cf838
 [SEE ALSO]
8cf838
-stat(2), statfs(2)
8cf838
+stat(2), statfs(2), statx(2)
8cf838
diff --git a/src/stat.c b/src/stat.c
8cf838
index 9e71cbe..32ffb6d 100644
8cf838
--- a/src/stat.c
8cf838
+++ b/src/stat.c
8cf838
@@ -28,6 +28,12 @@
8cf838
 # define USE_STATVFS 0
8cf838
 #endif
8cf838
 
8cf838
+#if HAVE_STATX && defined STATX_INO
8cf838
+# define USE_STATX 1
8cf838
+#else
8cf838
+# define USE_STATX 0
8cf838
+#endif
8cf838
+
8cf838
 #include <stddef.h>
8cf838
 #include <stdio.h>
8cf838
 #include <stdalign.h>
8cf838
@@ -194,6 +200,23 @@ enum
8cf838
   PRINTF_OPTION = CHAR_MAX + 1
8cf838
 };
8cf838
 
8cf838
+enum cached_mode
8cf838
+{
8cf838
+  cached_default,
8cf838
+  cached_never,
8cf838
+  cached_always
8cf838
+};
8cf838
+
8cf838
+static char const *const cached_args[] =
8cf838
+{
8cf838
+  "default", "never", "always", NULL
8cf838
+};
8cf838
+
8cf838
+static enum cached_mode const cached_modes[] =
8cf838
+{
8cf838
+  cached_default, cached_never, cached_always
8cf838
+};
8cf838
+
8cf838
 static struct option const long_options[] =
8cf838
 {
8cf838
   {"dereference", no_argument, NULL, 'L'},
8cf838
@@ -201,6 +224,7 @@ static struct option const long_options[] =
8cf838
   {"format", required_argument, NULL, 'c'},
8cf838
   {"printf", required_argument, NULL, PRINTF_OPTION},
8cf838
   {"terse", no_argument, NULL, 't'},
8cf838
+  {"cached", required_argument, NULL, 0},
8cf838
   {GETOPT_HELP_OPTION_DECL},
8cf838
   {GETOPT_VERSION_OPTION_DECL},
8cf838
   {NULL, 0, NULL, 0}
8cf838
@@ -221,6 +245,10 @@ static char const *trailing_delim = "";
8cf838
 static char const *decimal_point;
8cf838
 static size_t decimal_point_len;
8cf838
 
8cf838
+static bool
8cf838
+print_stat (char *pformat, size_t prefix_len, unsigned int m,
8cf838
+            int fd, char const *filename, void const *data);
8cf838
+
8cf838
 /* Return the type of the specified file system.
8cf838
    Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
8cf838
    Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
8cf838
@@ -674,7 +702,6 @@ out_minus_zero (char *pformat, size_t prefix_len)
8cf838
    acts like printf's %f format.  */
8cf838
 static void
8cf838
 out_epoch_sec (char *pformat, size_t prefix_len,
8cf838
-               struct stat const *statbuf _GL_UNUSED,
8cf838
                struct timespec arg)
8cf838
 {
8cf838
   char *dot = memchr (pformat, '.', prefix_len);
8cf838
@@ -978,57 +1005,6 @@ print_mount_point:
8cf838
   return fail;
8cf838
 }
8cf838
 
8cf838
-static struct timespec
8cf838
-get_birthtime (int fd, char const *filename, struct stat const *st)
8cf838
-{
8cf838
-  struct timespec ts = get_stat_birthtime (st);
8cf838
-
8cf838
-#if HAVE_GETATTRAT
8cf838
-  if (ts.tv_nsec < 0)
8cf838
-    {
8cf838
-      nvlist_t *response;
8cf838
-      if ((fd < 0
8cf838
-           ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
8cf838
-           : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
8cf838
-          == 0)
8cf838
-        {
8cf838
-          uint64_t *val;
8cf838
-          uint_t n;
8cf838
-          if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
8cf838
-              && 2 <= n
8cf838
-              && val[0] <= TYPE_MAXIMUM (time_t)
8cf838
-              && val[1] < 1000000000 * 2 /* for leap seconds */)
8cf838
-            {
8cf838
-              ts.tv_sec = val[0];
8cf838
-              ts.tv_nsec = val[1];
8cf838
-            }
8cf838
-          nvlist_free (response);
8cf838
-        }
8cf838
-    }
8cf838
-#endif
8cf838
-
8cf838
-#if HAVE_STATX
8cf838
-  if (ts.tv_nsec < 0)
8cf838
-    {
8cf838
-      struct statx stx;
8cf838
-      if ((fd < 0
8cf838
-           ? statx (AT_FDCWD, filename,
8cf838
-                    follow_links ? 0 : AT_SYMLINK_NOFOLLOW,
8cf838
-                    STATX_BTIME, &stx)
8cf838
-           : statx (fd, "", AT_EMPTY_PATH, STATX_BTIME, &stx)) == 0)
8cf838
-        {
8cf838
-          if ((stx.stx_mask & STATX_BTIME) && stx.stx_btime.tv_sec != 0)
8cf838
-            {
8cf838
-              ts.tv_sec = stx.stx_btime.tv_sec;
8cf838
-              ts.tv_nsec = stx.stx_btime.tv_nsec;
8cf838
-            }
8cf838
-        }
8cf838
-    }
8cf838
-#endif
8cf838
-
8cf838
-  return ts;
8cf838
-}
8cf838
-
8cf838
 /* Map a TS with negative TS.tv_nsec to {0,0}.  */
8cf838
 static inline struct timespec
8cf838
 neg_to_zero (struct timespec ts)
8cf838
@@ -1065,139 +1041,6 @@ getenv_quoting_style (void)
8cf838
 /* Equivalent to quotearg(), but explicit to avoid syntax checks.  */
8cf838
 #define quoteN(x) quotearg_style (get_quoting_style (NULL), x)
8cf838
 
8cf838
-/* Print stat info.  Return zero upon success, nonzero upon failure.  */
8cf838
-static bool
8cf838
-print_stat (char *pformat, size_t prefix_len, unsigned int m,
8cf838
-            int fd, char const *filename, void const *data)
8cf838
-{
8cf838
-  struct stat *statbuf = (struct stat *) data;
8cf838
-  struct passwd *pw_ent;
8cf838
-  struct group *gw_ent;
8cf838
-  bool fail = false;
8cf838
-
8cf838
-  switch (m)
8cf838
-    {
8cf838
-    case 'n':
8cf838
-      out_string (pformat, prefix_len, filename);
8cf838
-      break;
8cf838
-    case 'N':
8cf838
-      out_string (pformat, prefix_len, quoteN (filename));
8cf838
-      if (S_ISLNK (statbuf->st_mode))
8cf838
-        {
8cf838
-          char *linkname = areadlink_with_size (filename, statbuf->st_size);
8cf838
-          if (linkname == NULL)
8cf838
-            {
8cf838
-              error (0, errno, _("cannot read symbolic link %s"),
8cf838
-                     quoteaf (filename));
8cf838
-              return true;
8cf838
-            }
8cf838
-          printf (" -> ");
8cf838
-          out_string (pformat, prefix_len, quoteN (linkname));
8cf838
-          free (linkname);
8cf838
-        }
8cf838
-      break;
8cf838
-    case 'd':
8cf838
-      out_uint (pformat, prefix_len, statbuf->st_dev);
8cf838
-      break;
8cf838
-    case 'D':
8cf838
-      out_uint_x (pformat, prefix_len, statbuf->st_dev);
8cf838
-      break;
8cf838
-    case 'i':
8cf838
-      out_uint (pformat, prefix_len, statbuf->st_ino);
8cf838
-      break;
8cf838
-    case 'a':
8cf838
-      out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
8cf838
-      break;
8cf838
-    case 'A':
8cf838
-      out_string (pformat, prefix_len, human_access (statbuf));
8cf838
-      break;
8cf838
-    case 'f':
8cf838
-      out_uint_x (pformat, prefix_len, statbuf->st_mode);
8cf838
-      break;
8cf838
-    case 'F':
8cf838
-      out_string (pformat, prefix_len, file_type (statbuf));
8cf838
-      break;
8cf838
-    case 'h':
8cf838
-      out_uint (pformat, prefix_len, statbuf->st_nlink);
8cf838
-      break;
8cf838
-    case 'u':
8cf838
-      out_uint (pformat, prefix_len, statbuf->st_uid);
8cf838
-      break;
8cf838
-    case 'U':
8cf838
-      pw_ent = getpwuid (statbuf->st_uid);
8cf838
-      out_string (pformat, prefix_len,
8cf838
-                  pw_ent ? pw_ent->pw_name : "UNKNOWN");
8cf838
-      break;
8cf838
-    case 'g':
8cf838
-      out_uint (pformat, prefix_len, statbuf->st_gid);
8cf838
-      break;
8cf838
-    case 'G':
8cf838
-      gw_ent = getgrgid (statbuf->st_gid);
8cf838
-      out_string (pformat, prefix_len,
8cf838
-                  gw_ent ? gw_ent->gr_name : "UNKNOWN");
8cf838
-      break;
8cf838
-    case 't':
8cf838
-      out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
8cf838
-      break;
8cf838
-    case 'm':
8cf838
-      fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
8cf838
-      break;
8cf838
-    case 'T':
8cf838
-      out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
8cf838
-      break;
8cf838
-    case 's':
8cf838
-      out_int (pformat, prefix_len, statbuf->st_size);
8cf838
-      break;
8cf838
-    case 'B':
8cf838
-      out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
8cf838
-      break;
8cf838
-    case 'b':
8cf838
-      out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
8cf838
-      break;
8cf838
-    case 'o':
8cf838
-      out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
8cf838
-      break;
8cf838
-    case 'w':
8cf838
-      {
8cf838
-        struct timespec t = get_birthtime (fd, filename, statbuf);
8cf838
-        if (t.tv_nsec < 0)
8cf838
-          out_string (pformat, prefix_len, "-");
8cf838
-        else
8cf838
-          out_string (pformat, prefix_len, human_time (t));
8cf838
-      }
8cf838
-      break;
8cf838
-    case 'W':
8cf838
-      out_epoch_sec (pformat, prefix_len, statbuf,
8cf838
-                     neg_to_zero (get_birthtime (fd, filename, statbuf)));
8cf838
-      break;
8cf838
-    case 'x':
8cf838
-      out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
8cf838
-      break;
8cf838
-    case 'X':
8cf838
-      out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
8cf838
-      break;
8cf838
-    case 'y':
8cf838
-      out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
8cf838
-      break;
8cf838
-    case 'Y':
8cf838
-      out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
8cf838
-      break;
8cf838
-    case 'z':
8cf838
-      out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
8cf838
-      break;
8cf838
-    case 'Z':
8cf838
-      out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
8cf838
-      break;
8cf838
-    case 'C':
8cf838
-      fail |= out_file_context (pformat, prefix_len, filename);
8cf838
-      break;
8cf838
-    default:
8cf838
-      fputc ('?', stdout);
8cf838
-      break;
8cf838
-    }
8cf838
-  return fail;
8cf838
-}
8cf838
-
8cf838
 /* Output a single-character \ escape.  */
8cf838
 
8cf838
 static void
8cf838
@@ -1239,6 +1082,17 @@ print_esc_char (char c)
8cf838
   putchar (c);
8cf838
 }
8cf838
 
8cf838
+static size_t _GL_ATTRIBUTE_PURE
8cf838
+format_code_offset (char const* directive)
8cf838
+{
8cf838
+  size_t len = strspn (directive + 1, printf_flags);
8cf838
+  char const *fmt_char = directive + len + 1;
8cf838
+  fmt_char += strspn (fmt_char, digits);
8cf838
+  if (*fmt_char == '.')
8cf838
+    fmt_char += 1 + strspn (fmt_char + 1, digits);
8cf838
+  return fmt_char - directive;
8cf838
+}
8cf838
+
8cf838
 /* Print the information specified by the format string, FORMAT,
8cf838
    calling PRINT_FUNC for each %-directive encountered.
8cf838
    Return zero upon success, nonzero upon failure.  */
8cf838
@@ -1268,33 +1122,28 @@ print_it (char const *format, int fd, char const *filename,
8cf838
         {
8cf838
         case '%':
8cf838
           {
8cf838
-            size_t len = strspn (b + 1, printf_flags);
8cf838
-            char const *fmt_char = b + len + 1;
8cf838
-            fmt_char += strspn (fmt_char, digits);
8cf838
-            if (*fmt_char == '.')
8cf838
-              fmt_char += 1 + strspn (fmt_char + 1, digits);
8cf838
-            len = fmt_char - (b + 1);
8cf838
-            unsigned int fmt_code = *fmt_char;
8cf838
-            memcpy (dest, b, len + 1);
8cf838
-
8cf838
-            b = fmt_char;
8cf838
-            switch (fmt_code)
8cf838
+            size_t len = format_code_offset (b);
8cf838
+            char const *fmt_char = b + len;
8cf838
+            memcpy (dest, b, len);
8cf838
+            b += len;
8cf838
+
8cf838
+            switch (*fmt_char)
8cf838
               {
8cf838
               case '\0':
8cf838
                 --b;
8cf838
                 FALLTHROUGH;
8cf838
               case '%':
8cf838
-                if (0 < len)
8cf838
+                if (1 < len)
8cf838
                   {
8cf838
-                    dest[len + 1] = *fmt_char;
8cf838
-                    dest[len + 2] = '\0';
8cf838
+                    dest[len] = *fmt_char;
8cf838
+                    dest[len + 1] = '\0';
8cf838
                     die (EXIT_FAILURE, 0, _("%s: invalid directive"),
8cf838
                          quote (dest));
8cf838
                   }
8cf838
                 putchar ('%');
8cf838
                 break;
8cf838
               default:
8cf838
-                fail |= print_func (dest, len + 1, fmt_code,
8cf838
+                fail |= print_func (dest, len, to_uchar (*fmt_char),
8cf838
                                     fd, filename, data);
8cf838
                 break;
8cf838
               }
8cf838
@@ -1382,6 +1231,204 @@ do_statfs (char const *filename, char const *format)
8cf838
   return ! fail;
8cf838
 }
8cf838
 
8cf838
+struct print_args {
8cf838
+  struct stat *st;
8cf838
+  struct timespec btime;
8cf838
+};
8cf838
+
8cf838
+/* Ask statx to avoid syncing? */
8cf838
+static bool dont_sync;
8cf838
+
8cf838
+/* Ask statx to force sync? */
8cf838
+static bool force_sync;
8cf838
+
8cf838
+#if USE_STATX
8cf838
+/* Much of the format printing requires a struct stat or timespec */
8cf838
+static struct timespec
8cf838
+statx_timestamp_to_timespec (struct statx_timestamp tsx)
8cf838
+{
8cf838
+  struct timespec ts;
8cf838
+
8cf838
+  ts.tv_sec = tsx.tv_sec;
8cf838
+  ts.tv_nsec = tsx.tv_nsec;
8cf838
+  return ts;
8cf838
+}
8cf838
+
8cf838
+static void
8cf838
+statx_to_stat (struct statx *stx, struct stat *stat)
8cf838
+{
8cf838
+  stat->st_dev = makedev (stx->stx_dev_major, stx->stx_dev_minor);
8cf838
+  stat->st_ino = stx->stx_ino;
8cf838
+  stat->st_mode = stx->stx_mode;
8cf838
+  stat->st_nlink = stx->stx_nlink;
8cf838
+  stat->st_uid = stx->stx_uid;
8cf838
+  stat->st_gid = stx->stx_gid;
8cf838
+  stat->st_rdev = makedev (stx->stx_rdev_major, stx->stx_rdev_minor);
8cf838
+  stat->st_size = stx->stx_size;
8cf838
+  stat->st_blksize = stx->stx_blksize;
8cf838
+/* define to avoid sc_prohibit_stat_st_blocks.  */
8cf838
+# define SC_ST_BLOCKS st_blocks
8cf838
+  stat->SC_ST_BLOCKS = stx->stx_blocks;
8cf838
+  stat->st_atim = statx_timestamp_to_timespec (stx->stx_atime);
8cf838
+  stat->st_mtim = statx_timestamp_to_timespec (stx->stx_mtime);
8cf838
+  stat->st_ctim = statx_timestamp_to_timespec (stx->stx_ctime);
8cf838
+}
8cf838
+
8cf838
+static unsigned int
8cf838
+fmt_to_mask (char fmt)
8cf838
+{
8cf838
+  switch (fmt)
8cf838
+    {
8cf838
+    case 'N':
8cf838
+      return STATX_MODE|STATX_SIZE;
8cf838
+    case 'd':
8cf838
+    case 'D':
8cf838
+      return STATX_MODE;
8cf838
+    case 'i':
8cf838
+      return STATX_INO;
8cf838
+    case 'a':
8cf838
+    case 'A':
8cf838
+      return STATX_MODE;
8cf838
+    case 'f':
8cf838
+      return STATX_MODE|STATX_TYPE;
8cf838
+    case 'F':
8cf838
+      return STATX_TYPE;
8cf838
+    case 'h':
8cf838
+      return STATX_NLINK;
8cf838
+    case 'u':
8cf838
+    case 'U':
8cf838
+      return STATX_UID;
8cf838
+    case 'g':
8cf838
+    case 'G':
8cf838
+      return STATX_GID;
8cf838
+    case 'm':
8cf838
+      return STATX_MODE|STATX_INO;
8cf838
+    case 's':
8cf838
+      return STATX_SIZE;
8cf838
+    case 't':
8cf838
+    case 'T':
8cf838
+      return STATX_MODE;
8cf838
+    case 'b':
8cf838
+      return STATX_BLOCKS;
8cf838
+    case 'w':
8cf838
+    case 'W':
8cf838
+      return STATX_BTIME;
8cf838
+    case 'x':
8cf838
+    case 'X':
8cf838
+      return STATX_ATIME;
8cf838
+    case 'y':
8cf838
+    case 'Y':
8cf838
+      return STATX_MTIME;
8cf838
+    case 'z':
8cf838
+    case 'Z':
8cf838
+      return STATX_CTIME;
8cf838
+    }
8cf838
+  return 0;
8cf838
+}
8cf838
+
8cf838
+static unsigned int _GL_ATTRIBUTE_PURE
8cf838
+format_to_mask (char const *format)
8cf838
+{
8cf838
+  unsigned int mask = 0;
8cf838
+  char const *b;
8cf838
+
8cf838
+  for (b = format; *b; b++)
8cf838
+    {
8cf838
+      if (*b != '%')
8cf838
+        continue;
8cf838
+
8cf838
+      b += format_code_offset (b);
8cf838
+      if (*b == '\0')
8cf838
+        break;
8cf838
+      mask |= fmt_to_mask (*b);
8cf838
+    }
8cf838
+  return mask;
8cf838
+}
8cf838
+
8cf838
+/* statx the file and print what we find */
8cf838
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
8cf838
+do_stat (char const *filename, char const *format, char const *format2)
8cf838
+{
8cf838
+  int fd = STREQ (filename, "-") ? 0 : AT_FDCWD;
8cf838
+  int flags = 0;
8cf838
+  struct stat st;
8cf838
+  struct statx stx;
8cf838
+  const char *pathname = filename;
8cf838
+  struct print_args pa;
8cf838
+  pa.st = &st;
8cf838
+  pa.btime = (struct timespec) {-1, -1};
8cf838
+
8cf838
+  if (AT_FDCWD != fd)
8cf838
+    {
8cf838
+      pathname = "";
8cf838
+      flags = AT_EMPTY_PATH;
8cf838
+    }
8cf838
+  else if (!follow_links)
8cf838
+    {
8cf838
+      flags = AT_SYMLINK_NOFOLLOW;
8cf838
+    }
8cf838
+
8cf838
+  if (dont_sync)
8cf838
+    flags |= AT_STATX_DONT_SYNC;
8cf838
+  else if (force_sync)
8cf838
+    flags |= AT_STATX_FORCE_SYNC;
8cf838
+
8cf838
+  fd = statx (fd, pathname, flags, format_to_mask (format), &stx;;
8cf838
+  if (fd < 0)
8cf838
+    {
8cf838
+      if (flags & AT_EMPTY_PATH)
8cf838
+        error (0, errno, _("cannot stat standard input"));
8cf838
+      else
8cf838
+        error (0, errno, _("cannot statx %s"), quoteaf (filename));
8cf838
+      return false;
8cf838
+    }
8cf838
+
8cf838
+  if (S_ISBLK (stx.stx_mode) || S_ISCHR (stx.stx_mode))
8cf838
+    format = format2;
8cf838
+
8cf838
+  statx_to_stat (&stx, &st);
8cf838
+  if (stx.stx_mask & STATX_BTIME)
8cf838
+    pa.btime = statx_timestamp_to_timespec (stx.stx_btime);
8cf838
+
8cf838
+  bool fail = print_it (format, fd, filename, print_stat, &pa);
8cf838
+  return ! fail;
8cf838
+}
8cf838
+
8cf838
+#else /* USE_STATX */
8cf838
+
8cf838
+static struct timespec
8cf838
+get_birthtime (int fd, char const *filename, struct stat const *st)
8cf838
+{
8cf838
+  struct timespec ts = get_stat_birthtime (st);
8cf838
+
8cf838
+# if HAVE_GETATTRAT
8cf838
+  if (ts.tv_nsec < 0)
8cf838
+    {
8cf838
+      nvlist_t *response;
8cf838
+      if ((fd < 0
8cf838
+           ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
8cf838
+           : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
8cf838
+          == 0)
8cf838
+        {
8cf838
+          uint64_t *val;
8cf838
+          uint_t n;
8cf838
+          if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
8cf838
+              && 2 <= n
8cf838
+              && val[0] <= TYPE_MAXIMUM (time_t)
8cf838
+              && val[1] < 1000000000 * 2 /* for leap seconds */)
8cf838
+            {
8cf838
+              ts.tv_sec = val[0];
8cf838
+              ts.tv_nsec = val[1];
8cf838
+            }
8cf838
+          nvlist_free (response);
8cf838
+        }
8cf838
+    }
8cf838
+# endif
8cf838
+
8cf838
+  return ts;
8cf838
+}
8cf838
+
8cf838
+
8cf838
 /* stat the file and print what we find */
8cf838
 static bool ATTRIBUTE_WARN_UNUSED_RESULT
8cf838
 do_stat (char const *filename, char const *format,
8cf838
@@ -1389,6 +1436,9 @@ do_stat (char const *filename, char const *format,
8cf838
 {
8cf838
   int fd = STREQ (filename, "-") ? 0 : -1;
8cf838
   struct stat statbuf;
8cf838
+  struct print_args pa;
8cf838
+  pa.st = &statbuf;
8cf838
+  pa.btime = (struct timespec) {-1, -1};
8cf838
 
8cf838
   if (0 <= fd)
8cf838
     {
8cf838
@@ -1412,9 +1462,152 @@ do_stat (char const *filename, char const *format,
8cf838
   if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
8cf838
     format = format2;
8cf838
 
8cf838
-  bool fail = print_it (format, fd, filename, print_stat, &statbuf);
8cf838
+  bool fail = print_it (format, fd, filename, print_stat, &pa);
8cf838
   return ! fail;
8cf838
 }
8cf838
+#endif /* USE_STATX */
8cf838
+
8cf838
+
8cf838
+/* Print stat info.  Return zero upon success, nonzero upon failure.  */
8cf838
+static bool
8cf838
+print_stat (char *pformat, size_t prefix_len, unsigned int m,
8cf838
+            int fd, char const *filename, void const *data)
8cf838
+{
8cf838
+  struct print_args *parg = (struct print_args *) data;
8cf838
+  struct stat *statbuf = parg->st;
8cf838
+  struct timespec btime = parg->btime;
8cf838
+  struct passwd *pw_ent;
8cf838
+  struct group *gw_ent;
8cf838
+  bool fail = false;
8cf838
+
8cf838
+  switch (m)
8cf838
+    {
8cf838
+    case 'n':
8cf838
+      out_string (pformat, prefix_len, filename);
8cf838
+      break;
8cf838
+    case 'N':
8cf838
+      out_string (pformat, prefix_len, quoteN (filename));
8cf838
+      if (S_ISLNK (statbuf->st_mode))
8cf838
+        {
8cf838
+          char *linkname = areadlink_with_size (filename, statbuf->st_size);
8cf838
+          if (linkname == NULL)
8cf838
+            {
8cf838
+              error (0, errno, _("cannot read symbolic link %s"),
8cf838
+                     quoteaf (filename));
8cf838
+              return true;
8cf838
+            }
8cf838
+          printf (" -> ");
8cf838
+          out_string (pformat, prefix_len, quoteN (linkname));
8cf838
+          free (linkname);
8cf838
+        }
8cf838
+      break;
8cf838
+    case 'd':
8cf838
+      out_uint (pformat, prefix_len, statbuf->st_dev);
8cf838
+      break;
8cf838
+    case 'D':
8cf838
+      out_uint_x (pformat, prefix_len, statbuf->st_dev);
8cf838
+      break;
8cf838
+    case 'i':
8cf838
+      out_uint (pformat, prefix_len, statbuf->st_ino);
8cf838
+      break;
8cf838
+    case 'a':
8cf838
+      out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
8cf838
+      break;
8cf838
+    case 'A':
8cf838
+      out_string (pformat, prefix_len, human_access (statbuf));
8cf838
+      break;
8cf838
+    case 'f':
8cf838
+      out_uint_x (pformat, prefix_len, statbuf->st_mode);
8cf838
+      break;
8cf838
+    case 'F':
8cf838
+      out_string (pformat, prefix_len, file_type (statbuf));
8cf838
+      break;
8cf838
+    case 'h':
8cf838
+      out_uint (pformat, prefix_len, statbuf->st_nlink);
8cf838
+      break;
8cf838
+    case 'u':
8cf838
+      out_uint (pformat, prefix_len, statbuf->st_uid);
8cf838
+      break;
8cf838
+    case 'U':
8cf838
+      pw_ent = getpwuid (statbuf->st_uid);
8cf838
+      out_string (pformat, prefix_len,
8cf838
+                  pw_ent ? pw_ent->pw_name : "UNKNOWN");
8cf838
+      break;
8cf838
+    case 'g':
8cf838
+      out_uint (pformat, prefix_len, statbuf->st_gid);
8cf838
+      break;
8cf838
+    case 'G':
8cf838
+      gw_ent = getgrgid (statbuf->st_gid);
8cf838
+      out_string (pformat, prefix_len,
8cf838
+                  gw_ent ? gw_ent->gr_name : "UNKNOWN");
8cf838
+      break;
8cf838
+    case 'm':
8cf838
+      fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
8cf838
+      break;
8cf838
+    case 's':
8cf838
+      out_int (pformat, prefix_len, statbuf->st_size);
8cf838
+      break;
8cf838
+    case 't':
8cf838
+      out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
8cf838
+      break;
8cf838
+    case 'T':
8cf838
+      out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
8cf838
+      break;
8cf838
+    case 'B':
8cf838
+      out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
8cf838
+      break;
8cf838
+    case 'b':
8cf838
+      out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
8cf838
+      break;
8cf838
+    case 'o':
8cf838
+      out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
8cf838
+      break;
8cf838
+    case 'w':
8cf838
+      {
8cf838
+#if ! USE_STATX
8cf838
+        btime = get_birthtime (fd, filename, statbuf);
8cf838
+#endif
8cf838
+        if (btime.tv_nsec < 0)
8cf838
+          out_string (pformat, prefix_len, "-");
8cf838
+        else
8cf838
+          out_string (pformat, prefix_len, human_time (btime));
8cf838
+      }
8cf838
+      break;
8cf838
+    case 'W':
8cf838
+      {
8cf838
+#if ! USE_STATX
8cf838
+        btime = get_birthtime (fd, filename, statbuf);
8cf838
+#endif
8cf838
+        out_epoch_sec (pformat, prefix_len, neg_to_zero (btime));
8cf838
+      }
8cf838
+      break;
8cf838
+    case 'x':
8cf838
+      out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
8cf838
+      break;
8cf838
+    case 'X':
8cf838
+      out_epoch_sec (pformat, prefix_len, get_stat_atime (statbuf));
8cf838
+      break;
8cf838
+    case 'y':
8cf838
+      out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
8cf838
+      break;
8cf838
+    case 'Y':
8cf838
+      out_epoch_sec (pformat, prefix_len, get_stat_mtime (statbuf));
8cf838
+      break;
8cf838
+    case 'z':
8cf838
+      out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
8cf838
+      break;
8cf838
+    case 'Z':
8cf838
+      out_epoch_sec (pformat, prefix_len, get_stat_ctime (statbuf));
8cf838
+      break;
8cf838
+    case 'C':
8cf838
+      fail |= out_file_context (pformat, prefix_len, filename);
8cf838
+      break;
8cf838
+    default:
8cf838
+      fputc ('?', stdout);
8cf838
+      break;
8cf838
+    }
8cf838
+  return fail;
8cf838
+}
8cf838
 
8cf838
 /* Return an allocated format string in static storage that
8cf838
    corresponds to whether FS and TERSE options were declared.  */
8cf838
@@ -1523,6 +1716,10 @@ Display file or file system status.\n\
8cf838
       fputs (_("\
8cf838
   -L, --dereference     follow links\n\
8cf838
   -f, --file-system     display file system status instead of file status\n\
8cf838
+"), stdout);
8cf838
+      fputs (_("\
8cf838
+      --cached=MODE     specify how to use cached attributes;\n\
8cf838
+                          useful on remote file systems. See MODE below\n\
8cf838
 "), stdout);
8cf838
       fputs (_("\
8cf838
   -c  --format=FORMAT   use the specified FORMAT instead of the default;\n\
8cf838
@@ -1535,6 +1732,13 @@ Display file or file system status.\n\
8cf838
       fputs (HELP_OPTION_DESCRIPTION, stdout);
8cf838
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
8cf838
 
8cf838
+      fputs (_("\n\
8cf838
+The --cached MODE argument can be; always, never, or default.\n\
8cf838
+`always` will use cached attributes if available, while\n\
8cf838
+`never` will try to synchronize with the latest attributes, and\n\
8cf838
+`default` will leave it up to the underlying file system.\n\
8cf838
+"), stdout);
8cf838
+
8cf838
       fputs (_("\n\
8cf838
 The valid format sequences for files (without --file-system):\n\
8cf838
 \n\
8cf838
@@ -1668,6 +1872,23 @@ main (int argc, char *argv[])
8cf838
           terse = true;
8cf838
           break;
8cf838
 
8cf838
+        case 0:
8cf838
+          switch (XARGMATCH ("--cached", optarg, cached_args, cached_modes))
8cf838
+            {
8cf838
+              case cached_never:
8cf838
+                force_sync = true;
8cf838
+                dont_sync = false;
8cf838
+                break;
8cf838
+              case cached_always:
8cf838
+                force_sync = false;
8cf838
+                dont_sync = true;
8cf838
+                break;
8cf838
+              case cached_default:
8cf838
+                force_sync = false;
8cf838
+                dont_sync = false;
8cf838
+            }
8cf838
+          break;
8cf838
+
8cf838
         case_GETOPT_HELP_CHAR;
8cf838
 
8cf838
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
8cf838
-- 
8cf838
2.20.1
8cf838
8cf838
8cf838
From b9d6d0b4902cfa5ff3edf5ac7a082138f3237847 Mon Sep 17 00:00:00 2001
8cf838
From: Jeff Layton <jlayton@kernel.org>
8cf838
Date: Fri, 14 Jun 2019 14:37:43 -0400
8cf838
Subject: [PATCH 3/5] stat: fix enabling of statx logic
8cf838
8cf838
* src/stat.c: STATX_INO isn't defined until stat.h is included.
8cf838
Move the test down so it works properly.
8cf838
8cf838
Upstream-commit: 0b9bac90d8283c1262e74f0dbda87583508de9a3
8cf838
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
8cf838
---
8cf838
 src/stat.c | 12 ++++++------
8cf838
 1 file changed, 6 insertions(+), 6 deletions(-)
8cf838
8cf838
diff --git a/src/stat.c b/src/stat.c
8cf838
index 32ffb6d..1d9d83a 100644
8cf838
--- a/src/stat.c
8cf838
+++ b/src/stat.c
8cf838
@@ -28,12 +28,6 @@
8cf838
 # define USE_STATVFS 0
8cf838
 #endif
8cf838
 
8cf838
-#if HAVE_STATX && defined STATX_INO
8cf838
-# define USE_STATX 1
8cf838
-#else
8cf838
-# define USE_STATX 0
8cf838
-#endif
8cf838
-
8cf838
 #include <stddef.h>
8cf838
 #include <stdio.h>
8cf838
 #include <stdalign.h>
8cf838
@@ -80,6 +74,12 @@
8cf838
 #include "find-mount-point.h"
8cf838
 #include "xvasprintf.h"
8cf838
 
8cf838
+#if HAVE_STATX && defined STATX_INO
8cf838
+# define USE_STATX 1
8cf838
+#else
8cf838
+# define USE_STATX 0
8cf838
+#endif
8cf838
+
8cf838
 #if USE_STATVFS
8cf838
 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
8cf838
 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
8cf838
-- 
8cf838
2.20.1
8cf838
8cf838
8cf838
From 987cb69ae212a257b5f8d1582dac03c2aa1aa399 Mon Sep 17 00:00:00 2001
8cf838
From: Andreas Dilger <adilger@whamcloud.com>
8cf838
Date: Thu, 27 Jun 2019 02:25:55 -0600
8cf838
Subject: [PATCH 4/5] stat: don't explicitly request file size for filenames
8cf838
8cf838
When calling 'stat -c %N' to print the filename, don't explicitly
8cf838
request the size of the file via statx(), as it may add overhead on
8cf838
some filesystems.  The size is only needed to optimize an allocation
8cf838
for the relatively rare case of reading a symlink name, and the worst
8cf838
effect is a somewhat-too-large temporary buffer may be allocated for
8cf838
areadlink_with_size(), or internal retries if buffer is too small.
8cf838
8cf838
The file size will be returned by statx() on most filesystems, even
8cf838
if not requested, unless the filesystem considers this to be too
8cf838
expensive for that file, in which case the tradeoff is worthwhile.
8cf838
8cf838
* src/stat.c: Don't explicitly request STATX_SIZE for filenames.
8cf838
8cf838
Upstream-commit: a1a5e9a32eb9525680edd02fd127240c27ba0999
8cf838
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
8cf838
---
8cf838
 src/stat.c | 4 ++--
8cf838
 1 file changed, 2 insertions(+), 2 deletions(-)
8cf838
8cf838
diff --git a/src/stat.c b/src/stat.c
8cf838
index 1d9d83a..32bb5f0 100644
8cf838
--- a/src/stat.c
8cf838
+++ b/src/stat.c
8cf838
@@ -1280,7 +1280,7 @@ fmt_to_mask (char fmt)
8cf838
   switch (fmt)
8cf838
     {
8cf838
     case 'N':
8cf838
-      return STATX_MODE|STATX_SIZE;
8cf838
+      return STATX_MODE;
8cf838
     case 'd':
8cf838
     case 'D':
8cf838
       return STATX_MODE;
8cf838
@@ -1352,7 +1352,7 @@ do_stat (char const *filename, char const *format, char const *format2)
8cf838
   int fd = STREQ (filename, "-") ? 0 : AT_FDCWD;
8cf838
   int flags = 0;
8cf838
   struct stat st;
8cf838
-  struct statx stx;
8cf838
+  struct statx stx = { 0, };
8cf838
   const char *pathname = filename;
8cf838
   struct print_args pa;
8cf838
   pa.st = &st;
8cf838
-- 
8cf838
2.20.1
8cf838
8cf838
8cf838
From 5da6c36dacac4fd610ddad1bc0b2547a9cdfdf2f Mon Sep 17 00:00:00 2001
8cf838
From: Jeff Layton <jlayton@kernel.org>
8cf838
Date: Thu, 19 Sep 2019 11:59:45 -0400
8cf838
Subject: [PATCH 5/5] ls: use statx instead of stat when available
8cf838
8cf838
statx allows ls to indicate interest in only certain inode metadata.
8cf838
This is potentially a win on networked/clustered/distributed
8cf838
file systems. In cases where we'd have to do a full, heavyweight stat()
8cf838
call we can now do a much lighter statx() call.
8cf838
8cf838
As a real-world example, consider a file system like CephFS where one
8cf838
client is actively writing to a file and another client does an
8cf838
ls --color in the same directory. --color means that we need to fetch
8cf838
the mode of the file.
8cf838
8cf838
Doing that with a stat() call means that we have to fetch the size and
8cf838
mtime in addition to the mode. The MDS in that situation will have to
8cf838
revoke caps in order to ensure that it has up-to-date values to report,
8cf838
which disrupts the writer.
8cf838
8cf838
This has a measurable affect on performance. I ran a fio sequential
8cf838
write test on one cephfs client and had a second client do "ls --color"
8cf838
in a tight loop on the directory that held the file:
8cf838
8cf838
Baseline -- no activity on the second client:
8cf838
8cf838
WRITE: bw=76.7MiB/s (80.4MB/s), 76.7MiB/s-76.7MiB/s (80.4MB/s-80.4MB/s),
8cf838
       io=4600MiB (4824MB), run=60016-60016msec
8cf838
8cf838
Without this patch series, we see a noticable performance hit:
8cf838
8cf838
WRITE: bw=70.4MiB/s (73.9MB/s), 70.4MiB/s-70.4MiB/s (73.9MB/s-73.9MB/s),
8cf838
       io=4228MiB (4433MB), run=60012-60012msec
8cf838
8cf838
With this patch series, we gain most of that ground back:
8cf838
8cf838
WRITE: bw=75.9MiB/s (79.6MB/s), 75.9MiB/s-75.9MiB/s (79.6MB/s-79.6MB/s),
8cf838
       io=4555MiB (4776MB), run=60019-60019msec
8cf838
8cf838
* src/stat.c: move statx to stat struct conversion to new header...
8cf838
* src/statx.h: ...here.
8cf838
* src/ls.c: Add wrapper functions for stat/lstat/fstat calls,
8cf838
and add variants for when we are only interested in specific info.
8cf838
Add statx-enabled functions and set the request mask based on the
8cf838
output format and what values are needed.
8cf838
8cf838
Upstream-commit: a99ab266110795ed94a9cb4d2765ddad9c4310da
8cf838
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
8cf838
---
8cf838
 src/local.mk |   1 +
8cf838
 src/ls.c     | 145 ++++++++++++++++++++++++++++++++++++++++++++++++---
8cf838
 src/stat.c   |  32 +-----------
8cf838
 src/statx.h  |  52 ++++++++++++++++++
8cf838
 4 files changed, 192 insertions(+), 38 deletions(-)
8cf838
 create mode 100644 src/statx.h
8cf838
8cf838
diff --git a/src/local.mk b/src/local.mk
8cf838
index 7a587bb..c013590 100644
8cf838
--- a/src/local.mk
8cf838
+++ b/src/local.mk
8cf838
@@ -58,6 +58,7 @@ noinst_HEADERS =		\
8cf838
   src/prog-fprintf.h		\
8cf838
   src/remove.h			\
8cf838
   src/set-fields.h		\
8cf838
+  src/statx.h			\
8cf838
   src/system.h			\
8cf838
   src/uname.h
8cf838
 
8cf838
diff --git a/src/ls.c b/src/ls.c
8cf838
index bf0c594..7f68e3c 100644
8cf838
--- a/src/ls.c
8cf838
+++ b/src/ls.c
8cf838
@@ -114,6 +114,7 @@
8cf838
 #include "xgethostname.h"
8cf838
 #include "c-ctype.h"
8cf838
 #include "canonicalize.h"
8cf838
+#include "statx.h"
8cf838
 
8cf838
 /* Include <sys/capability.h> last to avoid a clash of <sys/types.h>
8cf838
    include guards with some premature versions of libcap.
8cf838
@@ -1063,6 +1064,136 @@ dired_dump_obstack (const char *prefix, struct obstack *os)
8cf838
     }
8cf838
 }
8cf838
 
8cf838
+#if HAVE_STATX && defined STATX_INO
8cf838
+static unsigned int _GL_ATTRIBUTE_PURE
8cf838
+time_type_to_statx (void)
8cf838
+{
8cf838
+  switch (time_type)
8cf838
+    {
8cf838
+    case time_ctime:
8cf838
+      return STATX_CTIME;
8cf838
+    case time_mtime:
8cf838
+      return STATX_MTIME;
8cf838
+    case time_atime:
8cf838
+      return STATX_ATIME;
8cf838
+    default:
8cf838
+      abort ();
8cf838
+    }
8cf838
+    return 0;
8cf838
+}
8cf838
+
8cf838
+static unsigned int _GL_ATTRIBUTE_PURE
8cf838
+calc_req_mask (void)
8cf838
+{
8cf838
+  unsigned int mask = STATX_MODE;
8cf838
+
8cf838
+  if (print_inode)
8cf838
+    mask |= STATX_INO;
8cf838
+
8cf838
+  if (print_block_size)
8cf838
+    mask |= STATX_BLOCKS;
8cf838
+
8cf838
+  if (format == long_format) {
8cf838
+    mask |= STATX_NLINK | STATX_SIZE | time_type_to_statx ();
8cf838
+    if (print_owner || print_author)
8cf838
+      mask |= STATX_UID;
8cf838
+    if (print_group)
8cf838
+      mask |= STATX_GID;
8cf838
+  }
8cf838
+
8cf838
+  switch (sort_type)
8cf838
+    {
8cf838
+    case sort_none:
8cf838
+    case sort_name:
8cf838
+    case sort_version:
8cf838
+    case sort_extension:
8cf838
+      break;
8cf838
+    case sort_time:
8cf838
+      mask |= time_type_to_statx ();
8cf838
+      break;
8cf838
+    case sort_size:
8cf838
+      mask |= STATX_SIZE;
8cf838
+      break;
8cf838
+    default:
8cf838
+      abort ();
8cf838
+    }
8cf838
+
8cf838
+  return mask;
8cf838
+}
8cf838
+
8cf838
+static int
8cf838
+do_statx (int fd, const char *name, struct stat *st, int flags,
8cf838
+          unsigned int mask)
8cf838
+{
8cf838
+  struct statx stx;
8cf838
+  int ret = statx (fd, name, flags, mask, &stx;;
8cf838
+  if (ret >= 0)
8cf838
+    statx_to_stat (&stx, st);
8cf838
+  return ret;
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+do_stat (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return do_statx (AT_FDCWD, name, st, 0, calc_req_mask ());
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+do_lstat (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return do_statx (AT_FDCWD, name, st, AT_SYMLINK_NOFOLLOW, calc_req_mask ());
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+stat_for_mode (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return do_statx (AT_FDCWD, name, st, 0, STATX_MODE);
8cf838
+}
8cf838
+
8cf838
+/* dev+ino should be static, so no need to sync with backing store */
8cf838
+static inline int
8cf838
+stat_for_ino (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return do_statx (AT_FDCWD, name, st, 0, STATX_INO);
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+fstat_for_ino (int fd, struct stat *st)
8cf838
+{
8cf838
+  return do_statx (fd, "", st, AT_EMPTY_PATH, STATX_INO);
8cf838
+}
8cf838
+#else
8cf838
+static inline int
8cf838
+do_stat (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return stat (name, st);
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+do_lstat (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return lstat (name, st);
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+stat_for_mode (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return stat (name, st);
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+stat_for_ino (const char *name, struct stat *st)
8cf838
+{
8cf838
+  return stat (name, st);
8cf838
+}
8cf838
+
8cf838
+static inline int
8cf838
+fstat_for_ino (int fd, struct stat *st)
8cf838
+{
8cf838
+  return fstat (fd, st);
8cf838
+}
8cf838
+#endif
8cf838
+
8cf838
 /* Return the address of the first plain %b spec in FMT, or NULL if
8cf838
    there is no such spec.  %5b etc. do not match, so that user
8cf838
    widths/flags are honored.  */
8cf838
@@ -2733,10 +2864,10 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
8cf838
       struct stat dir_stat;
8cf838
       int fd = dirfd (dirp);
8cf838
 
8cf838
-      /* If dirfd failed, endure the overhead of using stat.  */
8cf838
+      /* If dirfd failed, endure the overhead of stat'ing by path  */
8cf838
       if ((0 <= fd
8cf838
-           ? fstat (fd, &dir_stat)
8cf838
-           : stat (name, &dir_stat)) < 0)
8cf838
+           ? fstat_for_ino (fd, &dir_stat)
8cf838
+           : stat_for_ino (name, &dir_stat)) < 0)
8cf838
         {
8cf838
           file_failure (command_line_arg,
8cf838
                         _("cannot determine device and inode of %s"), name);
8cf838
@@ -3198,7 +3329,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
8cf838
       switch (dereference)
8cf838
         {
8cf838
         case DEREF_ALWAYS:
8cf838
-          err = stat (full_name, &f->stat);
8cf838
+          err = do_stat (full_name, &f->stat);
8cf838
           do_deref = true;
8cf838
           break;
8cf838
 
8cf838
@@ -3207,7 +3338,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
8cf838
           if (command_line_arg)
8cf838
             {
8cf838
               bool need_lstat;
8cf838
-              err = stat (full_name, &f->stat);
8cf838
+              err = do_stat (full_name, &f->stat);
8cf838
               do_deref = true;
8cf838
 
8cf838
               if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
8cf838
@@ -3227,7 +3358,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
8cf838
           FALLTHROUGH;
8cf838
 
8cf838
         default: /* DEREF_NEVER */
8cf838
-          err = lstat (full_name, &f->stat);
8cf838
+          err = do_lstat (full_name, &f->stat);
8cf838
           do_deref = false;
8cf838
           break;
8cf838
         }
8cf838
@@ -3316,7 +3447,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
8cf838
              they won't be traced and when no indicator is needed.  */
8cf838
           if (linkname
8cf838
               && (file_type <= indicator_style || check_symlink_color)
8cf838
-              && stat (linkname, &linkstats) == 0)
8cf838
+              && stat_for_mode (linkname, &linkstats) == 0)
8cf838
             {
8cf838
               f->linkok = true;
8cf838
 
8cf838
diff --git a/src/stat.c b/src/stat.c
8cf838
index 32bb5f0..03fecc3 100644
8cf838
--- a/src/stat.c
8cf838
+++ b/src/stat.c
8cf838
@@ -73,6 +73,7 @@
8cf838
 #include "strftime.h"
8cf838
 #include "find-mount-point.h"
8cf838
 #include "xvasprintf.h"
8cf838
+#include "statx.h"
8cf838
 
8cf838
 #if HAVE_STATX && defined STATX_INO
8cf838
 # define USE_STATX 1
8cf838
@@ -1243,37 +1244,6 @@ static bool dont_sync;
8cf838
 static bool force_sync;
8cf838
 
8cf838
 #if USE_STATX
8cf838
-/* Much of the format printing requires a struct stat or timespec */
8cf838
-static struct timespec
8cf838
-statx_timestamp_to_timespec (struct statx_timestamp tsx)
8cf838
-{
8cf838
-  struct timespec ts;
8cf838
-
8cf838
-  ts.tv_sec = tsx.tv_sec;
8cf838
-  ts.tv_nsec = tsx.tv_nsec;
8cf838
-  return ts;
8cf838
-}
8cf838
-
8cf838
-static void
8cf838
-statx_to_stat (struct statx *stx, struct stat *stat)
8cf838
-{
8cf838
-  stat->st_dev = makedev (stx->stx_dev_major, stx->stx_dev_minor);
8cf838
-  stat->st_ino = stx->stx_ino;
8cf838
-  stat->st_mode = stx->stx_mode;
8cf838
-  stat->st_nlink = stx->stx_nlink;
8cf838
-  stat->st_uid = stx->stx_uid;
8cf838
-  stat->st_gid = stx->stx_gid;
8cf838
-  stat->st_rdev = makedev (stx->stx_rdev_major, stx->stx_rdev_minor);
8cf838
-  stat->st_size = stx->stx_size;
8cf838
-  stat->st_blksize = stx->stx_blksize;
8cf838
-/* define to avoid sc_prohibit_stat_st_blocks.  */
8cf838
-# define SC_ST_BLOCKS st_blocks
8cf838
-  stat->SC_ST_BLOCKS = stx->stx_blocks;
8cf838
-  stat->st_atim = statx_timestamp_to_timespec (stx->stx_atime);
8cf838
-  stat->st_mtim = statx_timestamp_to_timespec (stx->stx_mtime);
8cf838
-  stat->st_ctim = statx_timestamp_to_timespec (stx->stx_ctime);
8cf838
-}
8cf838
-
8cf838
 static unsigned int
8cf838
 fmt_to_mask (char fmt)
8cf838
 {
8cf838
diff --git a/src/statx.h b/src/statx.h
8cf838
new file mode 100644
8cf838
index 0000000..19f3e18
8cf838
--- /dev/null
8cf838
+++ b/src/statx.h
8cf838
@@ -0,0 +1,52 @@
8cf838
+/* statx -> stat conversion functions for coreutils
8cf838
+   Copyright (C) 2019 Free Software Foundation, Inc.
8cf838
+
8cf838
+   This program is free software: you can redistribute it and/or modify
8cf838
+   it under the terms of the GNU General Public License as published by
8cf838
+   the Free Software Foundation, either version 3 of the License, or
8cf838
+   (at your option) any later version.
8cf838
+
8cf838
+   This program is distributed in the hope that it will be useful,
8cf838
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
8cf838
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8cf838
+   GNU General Public License for more details.
8cf838
+
8cf838
+   You should have received a copy of the GNU General Public License
8cf838
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
8cf838
+
8cf838
+#ifndef COREUTILS_STATX_H
8cf838
+# define COREUTILS_STATX_H
8cf838
+
8cf838
+# if HAVE_STATX && defined STATX_INO
8cf838
+/* Much of the format printing requires a struct stat or timespec */
8cf838
+static inline struct timespec
8cf838
+statx_timestamp_to_timespec (struct statx_timestamp tsx)
8cf838
+{
8cf838
+  struct timespec ts;
8cf838
+
8cf838
+  ts.tv_sec = tsx.tv_sec;
8cf838
+  ts.tv_nsec = tsx.tv_nsec;
8cf838
+  return ts;
8cf838
+}
8cf838
+
8cf838
+static inline void
8cf838
+statx_to_stat (struct statx *stx, struct stat *stat)
8cf838
+{
8cf838
+  stat->st_dev = makedev (stx->stx_dev_major, stx->stx_dev_minor);
8cf838
+  stat->st_ino = stx->stx_ino;
8cf838
+  stat->st_mode = stx->stx_mode;
8cf838
+  stat->st_nlink = stx->stx_nlink;
8cf838
+  stat->st_uid = stx->stx_uid;
8cf838
+  stat->st_gid = stx->stx_gid;
8cf838
+  stat->st_rdev = makedev (stx->stx_rdev_major, stx->stx_rdev_minor);
8cf838
+  stat->st_size = stx->stx_size;
8cf838
+  stat->st_blksize = stx->stx_blksize;
8cf838
+/* define to avoid sc_prohibit_stat_st_blocks.  */
8cf838
+#  define SC_ST_BLOCKS st_blocks
8cf838
+  stat->SC_ST_BLOCKS = stx->stx_blocks;
8cf838
+  stat->st_atim = statx_timestamp_to_timespec (stx->stx_atime);
8cf838
+  stat->st_mtim = statx_timestamp_to_timespec (stx->stx_mtime);
8cf838
+  stat->st_ctim = statx_timestamp_to_timespec (stx->stx_ctime);
8cf838
+}
8cf838
+# endif /* HAVE_STATX && defined STATX_INO */
8cf838
+#endif /* COREUTILS_STATX_H */
8cf838
-- 
8cf838
2.20.1
8cf838