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