[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 2/4] stat: use statx instead of stat if it's available
From: |
Jeff Layton |
Subject: |
[PATCH v2 2/4] stat: use statx instead of stat if it's available |
Date: |
Sat, 13 Apr 2019 08:50:13 -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 | 565 ++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 381 insertions(+), 184 deletions(-)
diff --git a/src/stat.c b/src/stat.c
index a0f32b32b958..38bb227835dd 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,386 @@ 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;
+ stat->st_blocks = stx->stx_blocks;
+ 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;
+ struct printarg pa = {
+ .stx = &stx,
+ .st = &st
+ };
+
+ if (AT_FDCWD != fd)
+ {
+ filename = "";
+ flags = AT_EMPTY_PATH;
+ }
+ else if (!follow_links)
+ {
+ flags = AT_SYMLINK_NOFOLLOW;
+ }
+
+ /* FIXME: set request mask based on format */
+ fd = statx(fd, filename, 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 +1612,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.20.1