diff --git a/SOURCES/coreutils-8.30-statx.patch b/SOURCES/coreutils-8.30-statx.patch new file mode 100644 index 0000000..7cda82d --- /dev/null +++ b/SOURCES/coreutils-8.30-statx.patch @@ -0,0 +1,1358 @@ +From b6a4efe4347a061161054698857b6dde0f3ed67c Mon Sep 17 00:00:00 2001 +From: Martin Bukatovic +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 +--- + 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 +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 +--- + 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 + #include + #include +@@ -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 +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 +--- + 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 + #include + #include +@@ -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 +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 +--- + 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 +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 +--- + 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 last to avoid a clash of + 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 . */ ++ ++#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 + diff --git a/SOURCES/coreutils-8.32-rm-stray-skip.patch b/SOURCES/coreutils-8.32-rm-stray-skip.patch new file mode 100644 index 0000000..66a1efc --- /dev/null +++ b/SOURCES/coreutils-8.32-rm-stray-skip.patch @@ -0,0 +1,109 @@ +From 11b37b65d08c2a8b6d967fd866ebbdbe7e864949 Mon Sep 17 00:00:00 2001 +From: Nishant Nayan +Date: Thu, 26 Nov 2020 14:35:17 +0000 +Subject: [PATCH] rm: do not skip files upon failure to remove an empty dir + +When removing a directory fails for some reason, and that directory +is empty, the rm_fts code gets the return value of the excise call +confused with the return value of its earlier call to prompt, +causing fts_skip_tree to be called again and the next file +that rm would otherwise have deleted to survive. + +* src/remove.c (rm_fts): Ensure we only skip a single fts entry, +when processing empty dirs. I.e. only skip the entry +having successfully removed it. +* tests/rm/empty-immutable-skip.sh: New root-only test. +* tests/local.mk: Add it. +* NEWS: Mention the bug fix. +Fixes https://bugs.gnu.org/44883 + +Upstream-commit: 6bf108358a6104ec1c694c9530b3cd56b95f4b57 +Signed-off-by: Kamil Dudka +--- + src/remove.c | 3 ++- + tests/local.mk | 1 + + tests/rm/empty-immutable-skip.sh | 46 ++++++++++++++++++++++++++++++++ + 3 files changed, 49 insertions(+), 1 deletion(-) + create mode 100755 tests/rm/empty-immutable-skip.sh + +diff --git a/src/remove.c b/src/remove.c +index 2d40c55..adf9489 100644 +--- a/src/remove.c ++++ b/src/remove.c +@@ -506,7 +506,8 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x) + /* When we know (from prompt when in interactive mode) + that this is an empty directory, don't prompt twice. */ + s = excise (fts, ent, x, true); +- fts_skip_tree (fts, ent); ++ if (s == RM_OK) ++ fts_skip_tree (fts, ent); + } + + if (s != RM_OK) +diff --git a/tests/local.mk b/tests/local.mk +index 5f7f775..2aeff2b 100644 +--- a/tests/local.mk ++++ b/tests/local.mk +@@ -136,6 +136,7 @@ all_root_tests = \ + tests/rm/no-give-up.sh \ + tests/rm/one-file-system.sh \ + tests/rm/read-only.sh \ ++ tests/rm/empty-immutable-skip.sh \ + tests/tail-2/append-only.sh \ + tests/tail-2/end-of-device.sh \ + tests/touch/now-owned-by-other.sh +diff --git a/tests/rm/empty-immutable-skip.sh b/tests/rm/empty-immutable-skip.sh +new file mode 100755 +index 0000000..c91d8d4 +--- /dev/null ++++ b/tests/rm/empty-immutable-skip.sh +@@ -0,0 +1,46 @@ ++#!/bin/sh ++# Ensure that rm does not skip extra files after hitting an empty immutable dir. ++# Requires root access to do chattr +i, as well as an ext[23] or xfs file system ++ ++# Copyright (C) 2020 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 . ++ ++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src ++print_ver_ rm ++require_root_ ++ ++# These simple one-file operations are expected to work even in the ++# presence of this bug, and we need them to set up the rest of the test. ++chattr_i_works=1 ++touch f ++chattr +i f 2>/dev/null || chattr_i_works=0 ++rm f 2>/dev/null ++test -f f || chattr_i_works=0 ++chattr -i f 2>/dev/null || chattr_i_works=0 ++rm f 2>/dev/null || chattr_i_works=0 ++test -f f && chattr_i_works=0 ++ ++if test $chattr_i_works = 0; then ++ skip_ "chattr +i doesn't work on this file system" ++fi ++ ++mkdir empty || framework_failure_ ++touch x y || framework_failure_ ++chattr +i empty || framework_failure_ ++rm -rf empty x y ++{ test -f x || test -f y; } && fail=1 ++chattr -i empty ++ ++Exit $fail +-- +2.26.2 + diff --git a/SOURCES/coreutils-8.32-split-number.patch b/SOURCES/coreutils-8.32-split-number.patch new file mode 100644 index 0000000..89541bf --- /dev/null +++ b/SOURCES/coreutils-8.32-split-number.patch @@ -0,0 +1,100 @@ +From bb0e7fabcaed9a7e71e30f05e638e9f243cdb13e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Mon, 25 Jan 2021 14:12:48 +0000 +Subject: [PATCH] split: fix --number=K/N to output correct part of file + +This functionality regressed with the adjustments +in commit v8.25-4-g62e7af032 + +* src/split.c (bytes_chunk_extract): Account for already read data +when seeking into the file. +* tests/split/b-chunk.sh: Use the hidden ---io-blksize option, +to test this functionality. +Fixes https://bugs.gnu.org/46048 + +Upstream-commit: bb21daa125aeb4e32546309d370918ca47e612db +Signed-off-by: Kamil Dudka +--- + src/split.c | 2 +- + tests/split/b-chunk.sh | 45 ++++++++++++++++++++++++------------------ + 2 files changed, 27 insertions(+), 20 deletions(-) + +diff --git a/src/split.c b/src/split.c +index 09e610b..19248f6 100644 +--- a/src/split.c ++++ b/src/split.c +@@ -1001,7 +1001,7 @@ bytes_chunk_extract (uintmax_t k, uintmax_t n, char *buf, size_t bufsize, + } + else + { +- if (lseek (STDIN_FILENO, start, SEEK_CUR) < 0) ++ if (lseek (STDIN_FILENO, start - initial_read, SEEK_CUR) < 0) + die (EXIT_FAILURE, errno, "%s", quotef (infile)); + initial_read = SIZE_MAX; + } +diff --git a/tests/split/b-chunk.sh b/tests/split/b-chunk.sh +index 864ce55..39a6799 100755 +--- a/tests/split/b-chunk.sh ++++ b/tests/split/b-chunk.sh +@@ -35,32 +35,39 @@ split -e -n 10 /dev/null || fail=1 + returns_ 1 stat x?? 2>/dev/null || fail=1 + + printf '1\n2\n3\n4\n5\n' > input || framework_failure_ ++printf '1\n2' > exp-1 || framework_failure_ ++printf '\n3\n' > exp-2 || framework_failure_ ++printf '4\n5\n' > exp-3 || framework_failure_ + + for file in input /proc/version /sys/kernel/profiling; do + test -f $file || continue + +- split -n 3 $file > out || fail=1 +- split -n 1/3 $file > b1 || fail=1 +- split -n 2/3 $file > b2 || fail=1 +- split -n 3/3 $file > b3 || fail=1 ++ for blksize in 1 2 4096; do ++ if ! test "$file" = 'input'; then ++ # For /proc like files we must be able to read all ++ # into the internal buffer to be able to determine size. ++ test "$blksize" = 4096 || continue ++ fi + +- case $file in +- input) +- printf '1\n2' > exp-1 +- printf '\n3\n' > exp-2 +- printf '4\n5\n' > exp-3 ++ split -n 3 ---io-blksize=$blksize $file > out || fail=1 ++ split -n 1/3 ---io-blksize=$blksize $file > b1 || fail=1 ++ split -n 2/3 ---io-blksize=$blksize $file > b2 || fail=1 ++ split -n 3/3 ---io-blksize=$blksize $file > b3 || fail=1 + +- compare exp-1 xaa || fail=1 +- compare exp-2 xab || fail=1 +- compare exp-3 xac || fail=1 +- ;; +- esac ++ case $file in ++ input) ++ compare exp-1 xaa || fail=1 ++ compare exp-2 xab || fail=1 ++ compare exp-3 xac || fail=1 ++ ;; ++ esac + +- compare xaa b1 || fail=1 +- compare xab b2 || fail=1 +- compare xac b3 || fail=1 +- cat xaa xab xac | compare - $file || fail=1 +- test -f xad && fail=1 ++ compare xaa b1 || fail=1 ++ compare xab b2 || fail=1 ++ compare xac b3 || fail=1 ++ cat xaa xab xac | compare - $file || fail=1 ++ test -f xad && fail=1 ++ done + done + + Exit $fail +-- +2.26.2 + diff --git a/SPECS/coreutils.spec b/SPECS/coreutils.spec index 7402268..a2e9775 100644 --- a/SPECS/coreutils.spec +++ b/SPECS/coreutils.spec @@ -1,7 +1,7 @@ Summary: A set of basic GNU tools commonly used in shell scripts Name: coreutils Version: 8.30 -Release: 8%{?dist} +Release: 9%{?dist} License: GPLv3+ Group: System Environment/Base Url: https://www.gnu.org/software/coreutils/ @@ -35,6 +35,15 @@ Patch6: coreutils-8.31-sums-man-pages.patch # df --local: recognize afs, auristorfs, and smb3 as remote fs (#1798030) Patch7: coreutils-8.30-df-local-fs.patch +# use statx instead of stat when available (#1760300) +Patch8: coreutils-8.30-statx.patch + +# rm: do not skip files upon failure to remove an empty dir (#1905481) +Patch9: coreutils-8.32-rm-stray-skip.patch + +# split: fix --number=K/N to output correct part of file (#1921246) +Patch10: coreutils-8.32-split-number.patch + # disable the test-lock gnulib test prone to deadlock Patch100: coreutils-8.26-test-lock.patch @@ -268,6 +277,11 @@ fi %license COPYING %changelog +* Fri Mar 26 2021 Kamil Dudka - 8.30-9 +- split: fix --number=K/N to output correct part of file (#1921246) +- rm: do not skip files upon failure to remove an empty dir (#1905481) +- use statx instead of stat when available (#1760300) + * Tue Apr 14 2020 Kamil Dudka - 8.30-8 - df --local: recognize afs, auristorfs, and smb3 as remote fs (#1798030) - fix formatting of sha512sum(1) man page (#1688744)