Blame SOURCES/coreutils-8.30-statx.patch

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