Blame SOURCES/coreutils-8.30-statx.patch

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