coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v5 2/4] stat: use statx instead of stat if it's available


From: Jeff Layton
Subject: [PATCH v5 2/4] stat: use statx instead of stat if it's available
Date: Mon, 13 May 2019 09:18:29 -0400

If HAVE_STATX is available, then use statx instead of stat/lstat/fstat.
This necessitates some copying due to the fact that we need a struct
stat to pass to some of the gnulib printing routines.

* src/stat.c: more comprehensive use of statx when available
---
 src/stat.c | 566 ++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 382 insertions(+), 184 deletions(-)

diff --git a/src/stat.c b/src/stat.c
index a0f32b32b958..527df32aa84b 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -979,57 +979,6 @@ print_mount_point:
   return fail;
 }
 
-static struct timespec
-get_birthtime (int fd, char const *filename, struct stat const *st)
-{
-  struct timespec ts = get_stat_birthtime (st);
-
-#if HAVE_GETATTRAT
-  if (ts.tv_nsec < 0)
-    {
-      nvlist_t *response;
-      if ((fd < 0
-           ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
-           : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
-          == 0)
-        {
-          uint64_t *val;
-          uint_t n;
-          if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
-              && 2 <= n
-              && val[0] <= TYPE_MAXIMUM (time_t)
-              && val[1] < 1000000000 * 2 /* for leap seconds */)
-            {
-              ts.tv_sec = val[0];
-              ts.tv_nsec = val[1];
-            }
-          nvlist_free (response);
-        }
-    }
-#endif
-
-#if HAVE_STATX && defined STATX_BTIME
-  if (ts.tv_nsec < 0)
-    {
-      struct statx stx;
-      if ((fd < 0
-           ? statx (AT_FDCWD, filename,
-                    follow_links ? 0 : AT_SYMLINK_NOFOLLOW,
-                    STATX_BTIME, &stx)
-           : statx (fd, "", AT_EMPTY_PATH, STATX_BTIME, &stx)) == 0)
-        {
-          if ((stx.stx_mask & STATX_BTIME) && stx.stx_btime.tv_sec != 0)
-            {
-              ts.tv_sec = stx.stx_btime.tv_sec;
-              ts.tv_nsec = stx.stx_btime.tv_nsec;
-            }
-        }
-    }
-#endif
-
-  return ts;
-}
-
 /* Map a TS with negative TS.tv_nsec to {0,0}.  */
 static inline struct timespec
 neg_to_zero (struct timespec ts)
@@ -1066,139 +1015,6 @@ getenv_quoting_style (void)
 /* Equivalent to quotearg(), but explicit to avoid syntax checks.  */
 #define quoteN(x) quotearg_style (get_quoting_style (NULL), x)
 
-/* Print stat info.  Return zero upon success, nonzero upon failure.  */
-static bool
-print_stat (char *pformat, size_t prefix_len, unsigned int m,
-            int fd, char const *filename, void const *data)
-{
-  struct stat *statbuf = (struct stat *) data;
-  struct passwd *pw_ent;
-  struct group *gw_ent;
-  bool fail = false;
-
-  switch (m)
-    {
-    case 'n':
-      out_string (pformat, prefix_len, filename);
-      break;
-    case 'N':
-      out_string (pformat, prefix_len, quoteN (filename));
-      if (S_ISLNK (statbuf->st_mode))
-        {
-          char *linkname = areadlink_with_size (filename, statbuf->st_size);
-          if (linkname == NULL)
-            {
-              error (0, errno, _("cannot read symbolic link %s"),
-                     quoteaf (filename));
-              return true;
-            }
-          printf (" -> ");
-          out_string (pformat, prefix_len, quoteN (linkname));
-          free (linkname);
-        }
-      break;
-    case 'd':
-      out_uint (pformat, prefix_len, statbuf->st_dev);
-      break;
-    case 'D':
-      out_uint_x (pformat, prefix_len, statbuf->st_dev);
-      break;
-    case 'i':
-      out_uint (pformat, prefix_len, statbuf->st_ino);
-      break;
-    case 'a':
-      out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
-      break;
-    case 'A':
-      out_string (pformat, prefix_len, human_access (statbuf));
-      break;
-    case 'f':
-      out_uint_x (pformat, prefix_len, statbuf->st_mode);
-      break;
-    case 'F':
-      out_string (pformat, prefix_len, file_type (statbuf));
-      break;
-    case 'h':
-      out_uint (pformat, prefix_len, statbuf->st_nlink);
-      break;
-    case 'u':
-      out_uint (pformat, prefix_len, statbuf->st_uid);
-      break;
-    case 'U':
-      pw_ent = getpwuid (statbuf->st_uid);
-      out_string (pformat, prefix_len,
-                  pw_ent ? pw_ent->pw_name : "UNKNOWN");
-      break;
-    case 'g':
-      out_uint (pformat, prefix_len, statbuf->st_gid);
-      break;
-    case 'G':
-      gw_ent = getgrgid (statbuf->st_gid);
-      out_string (pformat, prefix_len,
-                  gw_ent ? gw_ent->gr_name : "UNKNOWN");
-      break;
-    case 't':
-      out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
-      break;
-    case 'm':
-      fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
-      break;
-    case 'T':
-      out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
-      break;
-    case 's':
-      out_int (pformat, prefix_len, statbuf->st_size);
-      break;
-    case 'B':
-      out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
-      break;
-    case 'b':
-      out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
-      break;
-    case 'o':
-      out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
-      break;
-    case 'w':
-      {
-        struct timespec t = get_birthtime (fd, filename, statbuf);
-        if (t.tv_nsec < 0)
-          out_string (pformat, prefix_len, "-");
-        else
-          out_string (pformat, prefix_len, human_time (t));
-      }
-      break;
-    case 'W':
-      out_epoch_sec (pformat, prefix_len,
-                     neg_to_zero (get_birthtime (fd, filename, statbuf)));
-      break;
-    case 'x':
-      out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
-      break;
-    case 'X':
-      out_epoch_sec (pformat, prefix_len, get_stat_atime (statbuf));
-      break;
-    case 'y':
-      out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
-      break;
-    case 'Y':
-      out_epoch_sec (pformat, prefix_len, get_stat_mtime (statbuf));
-      break;
-    case 'z':
-      out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
-      break;
-    case 'Z':
-      out_epoch_sec (pformat, prefix_len, get_stat_ctime (statbuf));
-      break;
-    case 'C':
-      fail |= out_file_context (pformat, prefix_len, filename);
-      break;
-    default:
-      fputc ('?', stdout);
-      break;
-    }
-  return fail;
-}
-
 /* Output a single-character \ escape.  */
 
 static void
@@ -1383,6 +1199,387 @@ do_statfs (char const *filename, char const *format)
   return ! fail;
 }
 
+#if HAVE_STATX && defined STATX_INO
+struct printarg {
+  struct statx *stx;
+  struct stat *st;
+};
+
+/* Much of the format printing requires a struct stat or timespec */
+static struct timespec
+statx_timestamp_to_timespec (struct statx_timestamp tsx)
+{
+  struct timespec ts;
+
+  ts.tv_sec = tsx.tv_sec;
+  ts.tv_nsec = tsx.tv_nsec;
+  return ts;
+}
+
+static void
+statx_to_stat (struct statx *stx, struct stat *stat)
+{
+  stat->st_dev = makedev (stx->stx_dev_major, stx->stx_dev_minor);
+  stat->st_ino = stx->stx_ino;
+  stat->st_mode = stx->stx_mode;
+  stat->st_nlink = stx->stx_nlink;
+  stat->st_uid = stx->stx_uid;
+  stat->st_gid = stx->stx_gid;
+  stat->st_dev = makedev (stx->stx_rdev_major, stx->stx_rdev_minor);
+  stat->st_size = stx->stx_size;
+  stat->st_blksize = stx->stx_blksize;
+  /* Skip setting st_blocks to avoid sc_prohibit_stat_st_blocks warning */
+  stat->st_atim = statx_timestamp_to_timespec (stx->stx_atime);
+  stat->st_mtim = statx_timestamp_to_timespec (stx->stx_mtime);
+  stat->st_ctim = statx_timestamp_to_timespec (stx->stx_ctime);
+}
+
+/* Print statx info.  Return zero upon success, nonzero upon failure.  */
+static bool
+print_statx (char *pformat, size_t prefix_len, unsigned int m,
+             int fd, char const *filename, void const *data)
+{
+  struct printarg *parg = (struct printarg *) data;
+  struct statx *stx = parg->stx;
+  struct stat *st = parg->st;
+  struct passwd *pw_ent;
+  struct group *gw_ent;
+  bool fail = false;
+
+  switch (m)
+    {
+    case 'n':
+      out_string (pformat, prefix_len, filename);
+      break;
+    case 'N':
+      out_string (pformat, prefix_len, quoteN (filename));
+      if (S_ISLNK (stx->stx_mode))
+        {
+          char *linkname = areadlink_with_size (filename, stx->stx_size);
+          if (linkname == NULL)
+            {
+              error (0, errno, _("cannot read symbolic link %s"),
+                     quoteaf (filename));
+              return true;
+            }
+          printf (" -> ");
+          out_string (pformat, prefix_len, quoteN (linkname));
+          free (linkname);
+        }
+      break;
+    case 'd':
+      out_uint (pformat, prefix_len, st->st_dev);
+      break;
+    case 'D':
+      out_uint_x (pformat, prefix_len, st->st_dev);
+      break;
+    case 'i':
+      out_uint (pformat, prefix_len, stx->stx_ino);
+      break;
+    case 'a':
+      out_uint_o (pformat, prefix_len, stx->stx_mode & CHMOD_MODE_BITS);
+      break;
+    case 'A':
+      out_string (pformat, prefix_len, human_access (st));
+      break;
+    case 'f':
+      out_uint_x (pformat, prefix_len, stx->stx_mode);
+      break;
+    case 'F':
+      out_string (pformat, prefix_len, file_type (st));
+      break;
+    case 'h':
+      out_uint (pformat, prefix_len, stx->stx_nlink);
+      break;
+    case 'u':
+      out_uint (pformat, prefix_len, stx->stx_uid);
+      break;
+    case 'U':
+      pw_ent = getpwuid (stx->stx_uid);
+      out_string (pformat, prefix_len,
+                  pw_ent ? pw_ent->pw_name : "UNKNOWN");
+      break;
+    case 'g':
+      out_uint (pformat, prefix_len, stx->stx_gid);
+      break;
+    case 'G':
+      gw_ent = getgrgid (stx->stx_gid);
+      out_string (pformat, prefix_len,
+                  gw_ent ? gw_ent->gr_name : "UNKNOWN");
+      break;
+    case 'm':
+      fail |= out_mount_point (filename, pformat, prefix_len, st);
+      break;
+    case 's':
+      out_int (pformat, prefix_len, stx->stx_size);
+      break;
+    case 't':
+      out_uint_x (pformat, prefix_len, stx->stx_rdev_major);
+      break;
+    case 'T':
+      out_uint_x (pformat, prefix_len, stx->stx_rdev_minor);
+      break;
+    case 'B':
+      out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
+      break;
+    case 'b':
+      out_uint (pformat, prefix_len, stx->stx_blocks);
+      break;
+    case 'o':
+      out_uint (pformat, prefix_len, stx->stx_blksize);
+      break;
+    case 'w':
+      if (stx->stx_mask & STATX_BTIME)
+        out_string (pformat, prefix_len,
+                    human_time (statx_timestamp_to_timespec (stx->stx_btime)));
+      else
+        out_string (pformat, prefix_len, "-");
+      break;
+    case 'W':
+      if (stx->stx_mask & STATX_BTIME)
+        out_epoch_sec (pformat, prefix_len,
+                       statx_timestamp_to_timespec (stx->stx_btime));
+      else
+        out_string (pformat, prefix_len, "0");
+      break;
+    case 'x':
+      out_string (pformat, prefix_len, human_time (st->st_atim));
+      break;
+    case 'X':
+      out_epoch_sec (pformat, prefix_len, st->st_atim);
+      break;
+    case 'y':
+      out_string (pformat, prefix_len, human_time (st->st_mtim));
+      break;
+    case 'Y':
+      out_epoch_sec (pformat, prefix_len, st->st_mtim);
+      break;
+    case 'z':
+      out_string (pformat, prefix_len, human_time (st->st_ctim));
+      break;
+    case 'Z':
+      out_epoch_sec (pformat, prefix_len, st->st_ctim);
+      break;
+    case 'C':
+      fail |= out_file_context (pformat, prefix_len, filename);
+      break;
+    default:
+      fputc ('?', stdout);
+      break;
+    }
+  return fail;
+}
+
+/* statx the file and print what we find */
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+do_stat (char const *filename, char const *format, char const *format2)
+{
+  int fd = STREQ (filename, "-") ? 0 : AT_FDCWD;
+  int flags = 0;
+  struct stat st;
+  struct statx stx;
+  const char *pathname = filename;
+  struct printarg pa = {
+                         .stx = &stx,
+                         .st  = &st
+                       };
+
+  if (AT_FDCWD != fd)
+    {
+      pathname = "";
+      flags = AT_EMPTY_PATH;
+    }
+  else if (!follow_links)
+    {
+      flags = AT_SYMLINK_NOFOLLOW;
+    }
+
+  /* FIXME: set request mask based on format */
+  fd = statx (fd, pathname, flags, STATX_BASIC_STATS|STATX_BTIME, &stx);
+  if (fd < 0)
+    {
+      if (flags & AT_EMPTY_PATH)
+        error (0, errno, _("cannot stat standard input"));
+      else
+error (0, errno, _("cannot statx %s"), quoteaf (filename));
+      return false;
+    }
+
+  if (S_ISBLK (stx.stx_mode) || S_ISCHR (stx.stx_mode))
+    format = format2;
+
+  /* Many of the gnulib routines require a struct stat */
+  statx_to_stat (&stx, &st);
+
+  bool fail = print_it (format, fd, filename, print_statx, &pa);
+  return ! fail;
+}
+#else /* HAVE_STATX && defined STATX_INO */
+static struct timespec
+get_birthtime (int fd, char const *filename, struct stat const *st)
+{
+  struct timespec ts = get_stat_birthtime (st);
+
+#if HAVE_GETATTRAT
+  if (ts.tv_nsec < 0)
+    {
+      nvlist_t *response;
+      if ((fd < 0
+           ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
+           : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
+          == 0)
+        {
+          uint64_t *val;
+          uint_t n;
+          if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
+              && 2 <= n
+              && val[0] <= TYPE_MAXIMUM (time_t)
+              && val[1] < 1000000000 * 2 /* for leap seconds */)
+            {
+              ts.tv_sec = val[0];
+              ts.tv_nsec = val[1];
+            }
+          nvlist_free (response);
+        }
+    }
+#endif
+
+  return ts;
+}
+
+/* Print stat info.  Return zero upon success, nonzero upon failure.  */
+static bool
+print_stat (char *pformat, size_t prefix_len, unsigned int m,
+            int fd, char const *filename, void const *data)
+{
+  struct stat *statbuf = (struct stat *) data;
+  struct passwd *pw_ent;
+  struct group *gw_ent;
+  bool fail = false;
+
+  switch (m)
+    {
+    case 'n':
+      out_string (pformat, prefix_len, filename);
+      break;
+    case 'N':
+      out_string (pformat, prefix_len, quoteN (filename));
+      if (S_ISLNK (statbuf->st_mode))
+        {
+          char *linkname = areadlink_with_size (filename, statbuf->st_size);
+          if (linkname == NULL)
+            {
+              error (0, errno, _("cannot read symbolic link %s"),
+                     quoteaf (filename));
+              return true;
+            }
+          printf (" -> ");
+          out_string (pformat, prefix_len, quoteN (linkname));
+          free (linkname);
+        }
+      break;
+    case 'd':
+      out_uint (pformat, prefix_len, statbuf->st_dev);
+      break;
+    case 'D':
+      out_uint_x (pformat, prefix_len, statbuf->st_dev);
+      break;
+    case 'i':
+      out_uint (pformat, prefix_len, statbuf->st_ino);
+      break;
+    case 'a':
+      out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
+      break;
+    case 'A':
+      out_string (pformat, prefix_len, human_access (statbuf));
+      break;
+    case 'f':
+      out_uint_x (pformat, prefix_len, statbuf->st_mode);
+      break;
+    case 'F':
+      out_string (pformat, prefix_len, file_type (statbuf));
+      break;
+    case 'h':
+      out_uint (pformat, prefix_len, statbuf->st_nlink);
+      break;
+    case 'u':
+      out_uint (pformat, prefix_len, statbuf->st_uid);
+      break;
+    case 'U':
+      pw_ent = getpwuid (statbuf->st_uid);
+      out_string (pformat, prefix_len,
+                  pw_ent ? pw_ent->pw_name : "UNKNOWN");
+      break;
+    case 'g':
+      out_uint (pformat, prefix_len, statbuf->st_gid);
+      break;
+    case 'G':
+      gw_ent = getgrgid (statbuf->st_gid);
+      out_string (pformat, prefix_len,
+                  gw_ent ? gw_ent->gr_name : "UNKNOWN");
+      break;
+    case 't':
+      out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
+      break;
+    case 'm':
+      fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
+      break;
+    case 'T':
+      out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
+      break;
+    case 's':
+      out_int (pformat, prefix_len, statbuf->st_size);
+      break;
+    case 'B':
+      out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
+      break;
+    case 'b':
+      out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
+      break;
+    case 'o':
+      out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
+      break;
+    case 'w':
+      {
+        struct timespec t = get_birthtime (fd, filename, statbuf);
+        if (t.tv_nsec < 0)
+          out_string (pformat, prefix_len, "-");
+        else
+          out_string (pformat, prefix_len, human_time (t));
+      }
+      break;
+    case 'W':
+      out_epoch_sec (pformat, prefix_len,
+                     neg_to_zero (get_birthtime (fd, filename, statbuf)));
+      break;
+    case 'x':
+      out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
+      break;
+    case 'X':
+      out_epoch_sec (pformat, prefix_len, get_stat_atime (statbuf));
+      break;
+    case 'y':
+      out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
+      break;
+    case 'Y':
+      out_epoch_sec (pformat, prefix_len, get_stat_mtime (statbuf));
+      break;
+    case 'z':
+      out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
+      break;
+    case 'Z':
+      out_epoch_sec (pformat, prefix_len, get_stat_ctime (statbuf));
+      break;
+    case 'C':
+      fail |= out_file_context (pformat, prefix_len, filename);
+      break;
+    default:
+      fputc ('?', stdout);
+      break;
+    }
+  return fail;
+}
+
 /* stat the file and print what we find */
 static bool ATTRIBUTE_WARN_UNUSED_RESULT
 do_stat (char const *filename, char const *format,
@@ -1416,6 +1613,7 @@ do_stat (char const *filename, char const *format,
   bool fail = print_it (format, fd, filename, print_stat, &statbuf);
   return ! fail;
 }
+#endif /* HAVE_STATX && defined STATX_INO */
 
 /* Return an allocated format string in static storage that
    corresponds to whether FS and TERSE options were declared.  */
-- 
2.21.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]