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 | 561 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 377 insertions(+), 184 deletions(-) diff --git a/src/stat.c b/src/stat.c index a0f32b32b958..e5bd61f5fc3e 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,382 @@ do_statfs (char const *filename, char const *format) return ! fail; } +#if HAVE_STATX && defined STATX_INO +/* 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.tv_sec = stx->stx_atime.tv_sec; + stat->st_atim.tv_nsec = stx->stx_atime.tv_nsec; + stat->st_ctim.tv_sec = stx->stx_ctime.tv_sec; + stat->st_ctim.tv_nsec = stx->stx_ctime.tv_nsec; + stat->st_mtim.tv_sec = stx->stx_mtime.tv_sec; + stat->st_mtim.tv_nsec = stx->stx_mtime.tv_nsec; +} + +/* 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 statx *stx = (struct statx *) data; + struct stat statbuf; + struct passwd *pw_ent; + struct group *gw_ent; + bool fail = false; + + statx_to_stat(stx, &statbuf); + 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, statbuf.st_dev); + break; + case 'D': + out_uint_x (pformat, prefix_len, statbuf.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 (&statbuf)); + break; + case 'f': + out_uint_x (pformat, prefix_len, stx->stx_mode); + break; + case 'F': + out_string (pformat, prefix_len, file_type (&statbuf)); + 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, &statbuf); + 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_attributes & 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_attributes & 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 (statx_timestamp_to_timespec(stx->stx_atime))); + break; + case 'X': + out_epoch_sec (pformat, prefix_len, + statx_timestamp_to_timespec(stx->stx_atime)); + break; + case 'y': + out_string (pformat, prefix_len, + human_time (statx_timestamp_to_timespec(stx->stx_mtime))); + break; + case 'Y': + out_epoch_sec (pformat, prefix_len, + statx_timestamp_to_timespec(stx->stx_mtime)); + break; + case 'z': + out_string (pformat, prefix_len, + human_time (statx_timestamp_to_timespec(stx->stx_ctime))); + break; + case 'Z': + out_epoch_sec (pformat, prefix_len, + statx_timestamp_to_timespec(stx->stx_ctime)); + 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 statx stx; + + 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; + + bool fail = print_it (format, fd, filename, print_statx, &stx); + 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 +1608,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