diff --git a/backport-acl-First-step-towards-more-consistent-function-name.patch b/backport-acl-First-step-towards-more-consistent-function-name.patch new file mode 100644 index 0000000000000000000000000000000000000000..735178fe5866eb0a0dc6eb9cf20c21219007c3be --- /dev/null +++ b/backport-acl-First-step-towards-more-consistent-function-name.patch @@ -0,0 +1,135 @@ +From 8a71833114c376212ecdd56495604905f6aa218d Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Sat, 24 Aug 2024 17:58:48 +0200 +Subject: [PATCH] acl: First step towards more consistent function names. + +* lib/acl.h (xset_acl): New declaration. +(set_acl): Mark deprecated. +(xcopy_acl): New declaration. +(copy_acl): Mark deprecated. +* lib/set-acl.c (xset_acl): Renamed from set_acl. +(set_acl): New function. +* lib/copy-acl.c (xcopy_acl): Renamed from copy_acl. +(copy_acl): New function. +* tests/test-set-mode-acl.c (main): Test xset_acl instead of set_acl. +* tests/test-copy-acl.c (main): Test xcopy_acl instead of copy_acl. + + +--- + gnulib-tests/test-copy-acl.c | 2 +- + gnulib-tests/test-set-mode-acl.c | 2 +- + lib/acl.h | 14 +++++++++++--- + lib/copy-acl.c | 11 +++++++++-- + lib/set-acl.c | 8 +++++++- + 5 files changed, 29 insertions(+), 8 deletions(-) + +diff --git a/gnulib-tests/test-copy-acl.c b/gnulib-tests/test-copy-acl.c +index de47b9a..e71fb10 100644 +--- a/gnulib-tests/test-copy-acl.c ++++ b/gnulib-tests/test-copy-acl.c +@@ -60,7 +60,7 @@ main (int argc, char *argv[]) + } + + #if USE_ACL +- if (copy_acl (file1, fd1, file2, fd2, mode)) ++ if (xcopy_acl (file1, fd1, file2, fd2, mode)) + exit (EXIT_FAILURE); + #else + chmod (file2, mode); +diff --git a/gnulib-tests/test-set-mode-acl.c b/gnulib-tests/test-set-mode-acl.c +index 6c112db..1365402 100644 +--- a/gnulib-tests/test-set-mode-acl.c ++++ b/gnulib-tests/test-set-mode-acl.c +@@ -35,7 +35,7 @@ main (int argc, char *argv[]) + file = argv[1]; + mode = strtol (argv[2], NULL, 8); + +- set_acl (file, -1, mode); ++ xset_acl (file, -1, mode); + + return 0; + } +diff --git a/lib/acl.h b/lib/acl.h +index 0d02120..5f17512 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -20,7 +20,7 @@ + #ifndef _GL_ACL_H + #define _GL_ACL_H 1 + +-/* This file uses _GL_ATTRIBUTE_CONST. */ ++/* This file uses _GL_ATTRIBUTE_CONST, _GL_ATTRIBUTE_DEPRECATED. */ + #if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." + #endif +@@ -30,10 +30,18 @@ + + bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST; + int file_has_acl (char const *, struct stat const *); ++ + int qset_acl (char const *, int, mode_t); +-int set_acl (char const *, int, mode_t); ++int xset_acl (char const *, int, mode_t); ++/* Old name of xset_acl. */ ++_GL_ATTRIBUTE_DEPRECATED int set_acl (char const *, int, mode_t); ++ + int qcopy_acl (char const *, int, char const *, int, mode_t); +-int copy_acl (char const *, int, char const *, int, mode_t); ++int xcopy_acl (char const *, int, char const *, int, mode_t); ++/* Old name of xcopy_acl. */ ++_GL_ATTRIBUTE_DEPRECATED int copy_acl (char const *, int, char const *, int, ++ mode_t); ++ + int chmod_or_fchmod (char const *, int, mode_t); + + #endif +diff --git a/lib/copy-acl.c b/lib/copy-acl.c +index 3606a50..3cbf966 100644 +--- a/lib/copy-acl.c ++++ b/lib/copy-acl.c +@@ -40,8 +40,8 @@ + negative error code. */ + + int +-copy_acl (const char *src_name, int source_desc, const char *dst_name, +- int dest_desc, mode_t mode) ++xcopy_acl (const char *src_name, int source_desc, const char *dst_name, ++ int dest_desc, mode_t mode) + { + int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode); + switch (ret) +@@ -59,3 +59,10 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name, + } + return ret; + } ++ ++int ++copy_acl (const char *src_name, int source_desc, const char *dst_name, ++ int dest_desc, mode_t mode) ++{ ++ return xcopy_acl (src_name, source_desc, dst_name, dest_desc, mode); ++} +diff --git a/lib/set-acl.c b/lib/set-acl.c +index fac5665..955c5fa 100644 +--- a/lib/set-acl.c ++++ b/lib/set-acl.c +@@ -39,10 +39,16 @@ + return -1. */ + + int +-set_acl (char const *name, int desc, mode_t mode) ++xset_acl (char const *name, int desc, mode_t mode) + { + int ret = qset_acl (name, desc, mode); + if (ret != 0) + error (0, errno, _("setting permissions for %s"), quote (name)); + return ret; + } ++ ++int ++set_acl (char const *name, int desc, mode_t mode) ++{ ++ return xset_acl (name, desc, mode); ++} +-- +2.43.0 \ No newline at end of file diff --git a/backport-acl-tests-link-with-FILE_HAS_ACL_LIB.patch b/backport-acl-tests-link-with-FILE_HAS_ACL_LIB.patch new file mode 100644 index 0000000000000000000000000000000000000000..afb8ae1f2629a6d9ccd39e173bf052cc6f29ab26 --- /dev/null +++ b/backport-acl-tests-link-with-FILE_HAS_ACL_LIB.patch @@ -0,0 +1,48 @@ +From 955360a66c99bdd9ac3688519a8b521b06958fd3 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Fri, 9 May 2025 18:48:03 -0700 +Subject: [PATCH] acl-tests: link with $(FILE_HAS_ACL_LIB) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* modules/acl-tests (test_copy_acl_LDADD): Add +$(FILE_HAS_ACL_LIB), since qcopy-acl depends on file-has-acl. +Although this suggests that QCOPY_ACL_LIB should contain +FILE_HAS_ACL_LIB, I’m not sure whether that’s the right course of +action and anyway this is good enough for coreutils. + + +--- + gnulib-tests/Makefile.in | 2 +- + gnulib-tests/gnulib.mk | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/gnulib-tests/Makefile.in b/gnulib-tests/Makefile.in +index 334d765..ad3230e 100644 +--- a/gnulib-tests/Makefile.in ++++ b/gnulib-tests/Makefile.in +@@ -5874,7 +5874,7 @@ EXTRA_libtests_a_SOURCES = strerror_r.c + AM_LIBTOOLFLAGS = --preserve-dup-deps + test_accept_LDADD = $(LDADD) @LIBSOCKET@ + test_set_mode_acl_LDADD = $(LDADD) $(LIB_ACL) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) $(LIBC32CONV) +-test_copy_acl_LDADD = $(LDADD) $(LIB_ACL) $(QCOPY_ACL_LIB) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) $(LIBC32CONV) ++test_copy_acl_LDADD = $(LDADD) $(LIB_ACL) $(QCOPY_ACL_LIB) $(FILE_HAS_ACL_LIB) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) $(LIBC32CONV) + test_sameacls_LDADD = $(LDADD) $(LIB_ACL) @LIBINTL@ $(MBRTOWC_LIB) + test_areadlinkat_LDADD = $(LDADD) @LIBINTL@ + test_areadlinkat_with_size_LDADD = $(LDADD) @LIBINTL@ +diff --git a/gnulib-tests/gnulib.mk b/gnulib-tests/gnulib.mk +index eef5c25..761b699 100644 +--- a/gnulib-tests/gnulib.mk ++++ b/gnulib-tests/gnulib.mk +@@ -89,7 +89,7 @@ TESTS += \ + TESTS_ENVIRONMENT += USE_ACL=$(USE_ACL) + check_PROGRAMS += test-set-mode-acl test-copy-acl test-sameacls + test_set_mode_acl_LDADD = $(LDADD) $(LIB_ACL) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) $(LIBC32CONV) +-test_copy_acl_LDADD = $(LDADD) $(LIB_ACL) $(QCOPY_ACL_LIB) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) $(LIBC32CONV) ++test_copy_acl_LDADD = $(LDADD) $(LIB_ACL) $(QCOPY_ACL_LIB) $(FILE_HAS_ACL_LIB) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) $(LIBC32CONV) + test_sameacls_LDADD = $(LDADD) $(LIB_ACL) @LIBINTL@ $(MBRTOWC_LIB) + EXTRA_DIST += test-set-mode-acl.sh test-set-mode-acl-1.sh test-set-mode-acl-2.sh test-copy-acl.sh test-copy-acl-1.sh test-copy-acl-2.sh test-set-mode-acl.c test-copy-acl.c test-sameacls.c macros.h + +-- +2.43.0 \ No newline at end of file diff --git a/backport-build-update-gnulib-submodule-to-latest.patch b/backport-build-update-gnulib-submodule-to-latest.patch new file mode 100644 index 0000000000000000000000000000000000000000..5f808b5a6d2b3647e92ab1b2b1b2d4374f089524 --- /dev/null +++ b/backport-build-update-gnulib-submodule-to-latest.patch @@ -0,0 +1,39 @@ +From 87b887f96862f552e118f0e6ce5301f05a1e03d3 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 11 Nov 2024 08:52:12 -0800 +Subject: [PATCH] build: update gnulib submodule to latest +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This also fixes a problem with ls -Z when configured with +--disable-acl, reported by Pádraig Brady +. +* src/ls.c (gobble_file): Pass ACL_GET_SCONTEXT to +file_has_aclinfo, if -Z is used. + + +--- + src/ls.c | 7 ++++--- + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 317a641ad..6ff0f410f 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3501,9 +3501,10 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + if ((format == long_format) | print_scontext | check_capability) + { + struct aclinfo ai; +- int n = file_has_aclinfo_cache (full_name, f, &ai, +- ((do_deref ? ACL_SYMLINK_FOLLOW : 0) +- | filetype_d_type[type])); ++ int aclinfo_flags = ((do_deref ? ACL_SYMLINK_FOLLOW : 0) ++ | (print_scontext ? ACL_GET_SCONTEXT : 0) ++ | filetype_d_type[type]); ++ int n = file_has_aclinfo_cache (full_name, f, &ai, aclinfo_flags); + bool have_acl = 0 < n; + bool have_scontext = !ai.scontext_err; + f->acl_type = (!have_scontext && !have_acl +-- +2.43.0 \ No newline at end of file diff --git a/backport-cp-mv-ensure-i-f-are-not-overridden-by-u.patch b/backport-cp-mv-ensure-i-f-are-not-overridden-by-u.patch new file mode 100644 index 0000000000000000000000000000000000000000..8cc07365f253c5d4e820ad9be97d87089c2d2d06 --- /dev/null +++ b/backport-cp-mv-ensure-i-f-are-not-overridden-by-u.patch @@ -0,0 +1,191 @@ +From a6ab944e19684d0ccd0568fa8c06f71921c1e6c3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sun, 12 May 2024 12:21:19 +0100 +Subject: [PATCH] cp,mv: ensure -i,f are not overridden by -u + +Since coreutils 9.3 we had --update={all,older} override -i. +In coreutils 9.5 this was expanded to -u +(to make it consistent with --update=older). + +This patch reinstates things so that -i combines with -u instead. +I.e. have -i be protective, rather than selective (like -u). + +The -f option of mv is similarly adjusted in this patch, +so now --update does not override any of -f,-i,-n. + +* src/cp.c (main): Don't have -u disable prompting. +* src/mv.c (main): Likewise. +* tests/cp/cp-i.sh: Add a test case for -i. +* tests/mv/update.sh: Likewise. +* tests/mv/i-3.sh. Add a test case for -f. +Fixes https://bugs.gnu.org/70887 +--- + src/cp.c | 10 +++++++--- + src/mv.c | 20 ++++++++++++++++---- + tests/cp/cp-i.sh | 19 +++++++++++++++++++ + tests/mv/i-3.sh | 17 +++++++++++++---- + tests/mv/update.sh | 3 +++ + 5 files changed, 58 insertions(+), 11 deletions(-) + +diff --git a/src/cp.c b/src/cp.c +index 215f810bd5..23c25a9838 100644 +--- a/src/cp.c ++++ b/src/cp.c +@@ -1063,7 +1063,9 @@ main (int argc, char **argv) + break; + + case 'i': +- x.interactive = I_ASK_USER; ++ /* -i overrides -n, but not --update={none,none-fail}. */ ++ if (no_clobber || x.interactive == I_UNSPECIFIED) ++ x.interactive = I_ASK_USER; + break; + + case 'l': +@@ -1151,7 +1153,8 @@ main (int argc, char **argv) + { + /* Default cp operation. */ + x.update = false; +- x.interactive = I_UNSPECIFIED; ++ if (x.interactive != I_ASK_USER) ++ x.interactive = I_UNSPECIFIED; + } + else if (update_opt == UPDATE_NONE) + { +@@ -1166,7 +1169,8 @@ main (int argc, char **argv) + else if (update_opt == UPDATE_OLDER) + { + x.update = true; +- x.interactive = I_UNSPECIFIED; ++ if (x.interactive != I_ASK_USER) ++ x.interactive = I_UNSPECIFIED; + } + } + break; +diff --git a/src/mv.c b/src/mv.c +index 46893a25f5..bbf1e60348 100644 +--- a/src/mv.c ++++ b/src/mv.c +@@ -353,10 +353,18 @@ main (int argc, char **argv) + version_control_string = optarg; + break; + case 'f': +- x.interactive = I_ALWAYS_YES; ++ /* -f overrides -n, or -i, but not --update={none,none-fail}. */ ++ if (no_clobber ++ || x.interactive == I_ASK_USER ++ || x.interactive == I_UNSPECIFIED) ++ x.interactive = I_ALWAYS_YES; + break; + case 'i': +- x.interactive = I_ASK_USER; ++ /* -i overrides -n, or -f, but not --update={none,none-fail}. */ ++ if (no_clobber ++ || x.interactive == I_ALWAYS_YES ++ || x.interactive == I_UNSPECIFIED) ++ x.interactive = I_ASK_USER; + break; + case 'n': + x.interactive = I_ALWAYS_SKIP; +@@ -394,7 +402,9 @@ main (int argc, char **argv) + { + /* Default mv operation. */ + x.update = false; +- x.interactive = I_UNSPECIFIED; ++ if (x.interactive != I_ASK_USER ++ && x.interactive != I_ALWAYS_YES) ++ x.interactive = I_UNSPECIFIED; + } + else if (update_opt == UPDATE_NONE) + { +@@ -409,7 +419,9 @@ main (int argc, char **argv) + else if (update_opt == UPDATE_OLDER) + { + x.update = true; +- x.interactive = I_UNSPECIFIED; ++ if (x.interactive != I_ASK_USER ++ && x.interactive != I_ALWAYS_YES) ++ x.interactive = I_UNSPECIFIED; + } + } + break; +diff --git a/tests/cp/cp-i.sh b/tests/cp/cp-i.sh +index 08da4b31b5..2fd5abea8f 100755 +--- a/tests/cp/cp-i.sh ++++ b/tests/cp/cp-i.sh +@@ -70,4 +70,23 @@ returns_ 1 cp -bn c d 2>/dev/null || fail=1 + # options --backup and --no-clobber are mutually exclusive + returns_ 1 cp -bn c d 2>/dev/null || fail=1 + ++# Verify -i combines with -u, ++echo old > old || framework_failure_ ++touch -d yesterday old || framework_failure_ ++echo new > new || framework_failure_ ++# coreutils 9.3 had --update={all,older} ignore -i ++echo n | returns_ 1 cp -vi --update=older new old 2>/dev/null >out8 || fail=1 ++compare /dev/null out8 || fail=1 ++echo n | returns_ 1 cp -vi --update=all new old 2>/dev/null >out8 || fail=1 ++compare /dev/null out8 || fail=1 ++# coreutils 9.5 also had -u ignore -i ++echo n | returns_ 1 cp -vi -u new old 2>/dev/null >out8 || fail=1 ++compare /dev/null out8 || fail=1 ++# Don't prompt as not updating ++cp -v -i --update=none new old 2>/dev/null >out8 /dev/null >out8 /dev/null 2>&1 \ +@@ -59,11 +59,20 @@ retry_delay_ check_overwrite_prompt .1 7 || { fail=1; cat out; } + + cleanup_ + +-mv -f h i > out 2>&1 || fail=1 ++# Make sure there was no prompt with -f ++timeout 10 mv -f h i > out 2>&1 || fail=1 + test -f i || fail=1 + test -f h && fail=1 ++case "$(cat out)" in ++ '') ;; ++ *) fail=1 ;; ++esac + +-# Make sure there was no prompt. ++# Likewise make sure there was no prompt with -f -u ++# coreutils 9.3-9.5 mistakenly did prompt. ++timeout 10 mv -f --update=all j k > out 2>&1 || fail=1 ++test -f k || fail=1 ++test -f j && fail=1 + case "$(cat out)" in + '') ;; + *) fail=1 ;; +diff --git a/tests/mv/update.sh b/tests/mv/update.sh +index b0b4d4acb7..cc4214724a 100755 +--- a/tests/mv/update.sh ++++ b/tests/mv/update.sh +@@ -38,6 +38,9 @@ for interactive in '' -i; do + done + done + ++# This should prompt. coreutils 9.3-9.5 mistakenly did not ++echo n | returns_ 1 mv -vi -u new old >/dev/null 2>&1 || fail=1 ++ + # These should perform the rename / copy + for update_option in '--update' '--update=older' '--update=all' \ + '--update=none --update=all'; do diff --git a/backport-cp-mv-reinstate-that-n-exits-with-success-if-files-s.patch b/backport-cp-mv-reinstate-that-n-exits-with-success-if-files-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..33678805ce9f304c23402460149293ec7a96a49e --- /dev/null +++ b/backport-cp-mv-reinstate-that-n-exits-with-success-if-files-s.patch @@ -0,0 +1,251 @@ +From 9907b6f5dc3faf370c916c2f81bfcc3d06028e83 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sat, 24 Feb 2024 19:51:56 +0000 +Subject: [PATCH] cp,mv: reinstate that -n exits with success if files skipped + +* src/cp.c (main): Adjust so that -n will exit success if skipped files. +* src/mv.c (main): Likewise. +* doc/coreutils.texi (cp invocation): Adjust the description of -n. +* src/system.h (emit_update_parameters_note): Adjust --update=none +comparison. +* tests/cp/cp-i.sh: Adjust -n exit status checks. +* tests/mv/mv-n.sh: Likewise. +Fixes https://bugs.gnu.org/62572 +--- + doc/coreutils.texi | 14 ++++++-------- + src/cp.c | 14 ++++++++------ + src/mv.c | 10 ++++++---- + src/system.h | 4 ++-- + tests/cp/cp-i.sh | 11 +++++------ + tests/mv/mv-n.sh | 11 +++++------ + 6 files changed, 32 insertions(+), 32 deletions(-) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 255261f85e..7b7e8bcb00 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -9057,12 +9057,10 @@ a regular file in the destination tree. + @itemx --no-clobber + @opindex -n + @opindex --no-clobber +-Do not overwrite an existing file; silently fail instead. +-This option overrides a previous +-@option{-i} option. This option is mutually exclusive with @option{-b} or +-@option{--backup} option. +-See also the @option{--update=none} option which will +-skip existing files but not fail. ++Do not overwrite an existing file; silently skip instead. ++This option overrides a previous @option{-i} option. ++This option is mutually exclusive with @option{-b} or @option{--backup} option. ++See also the @option{--update} option. + + @item -P + @itemx --no-dereference +@@ -9333,8 +9331,8 @@ This is the default operation when an @option{--update} option is not specified, + and results in all existing files in the destination being replaced. + + @item none +-This is similar to the @option{--no-clobber} option, in that no files in the +-destination are replaced, but also skipping a file does not induce a failure. ++This is like the deprecated @option{--no-clobber} option, where no files in the ++destination are replaced, and also skipping a file does not induce a failure. + + @item older + This is the default operation when @option{--update} is specified, and results +diff --git a/src/cp.c b/src/cp.c +index 0355ed97f6..36ae4fb66f 100644 +--- a/src/cp.c ++++ b/src/cp.c +@@ -195,8 +195,8 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ + -L, --dereference always follow symbolic links in SOURCE\n\ + "), stdout); + fputs (_("\ +- -n, --no-clobber ensure no existing files overwritten, and fail\n\ +- silently instead. See also --update\n\ ++ -n, --no-clobber silently skip existing files.\n\ ++ See also --update\n\ + "), stdout); + fputs (_("\ + -P, --no-dereference never follow symbolic links in SOURCE\n\ +@@ -984,6 +984,7 @@ main (int argc, char **argv) + char *target_directory = nullptr; + bool no_target_directory = false; + char const *scontext = nullptr; ++ bool no_clobber = false; + + initialize_main (&argc, &argv); + set_program_name (argv[0]); +@@ -1074,7 +1075,8 @@ main (int argc, char **argv) + break; + + case 'n': +- x.interactive = I_ALWAYS_NO; ++ x.interactive = I_ALWAYS_SKIP; ++ no_clobber = true; + break; + + case 'P': +@@ -1140,7 +1142,7 @@ main (int argc, char **argv) + case 'u': + if (optarg == nullptr) + x.update = true; +- else if (x.interactive != I_ALWAYS_NO) /* -n takes precedence. */ ++ else if (! no_clobber) /* -n takes precedence. */ + { + enum Update_type update_opt; + update_opt = XARGMATCH ("--update", optarg, +@@ -1225,10 +1227,10 @@ main (int argc, char **argv) + usage (EXIT_FAILURE); + } + +- if (x.interactive == I_ALWAYS_NO) ++ if (x.interactive == I_ALWAYS_SKIP) + x.update = false; + +- if (make_backups && x.interactive == I_ALWAYS_NO) ++ if (make_backups && x.interactive == I_ALWAYS_SKIP) + { + error (0, 0, + _("options --backup and --no-clobber are mutually exclusive")); +diff --git a/src/mv.c b/src/mv.c +index 8a6fef41a7..2a8c977a78 100644 +--- a/src/mv.c ++++ b/src/mv.c +@@ -322,6 +322,7 @@ main (int argc, char **argv) + int n_files; + char **file; + bool selinux_enabled = (0 < is_selinux_enabled ()); ++ bool no_clobber = false; + + initialize_main (&argc, &argv); + set_program_name (argv[0]); +@@ -353,7 +354,8 @@ main (int argc, char **argv) + x.interactive = I_ASK_USER; + break; + case 'n': +- x.interactive = I_ALWAYS_NO; ++ x.interactive = I_ALWAYS_SKIP; ++ no_clobber = true; + break; + case DEBUG_OPTION: + x.debug = x.verbose = true; +@@ -375,7 +377,7 @@ main (int argc, char **argv) + case 'u': + if (optarg == nullptr) + x.update = true; +- else if (x.interactive != I_ALWAYS_NO) /* -n takes precedence. */ ++ else if (! no_clobber) /* -n takes precedence. */ + { + enum Update_type update_opt; + update_opt = XARGMATCH ("--update", optarg, +@@ -506,10 +508,10 @@ main (int argc, char **argv) + for (int i = 0; i < n_files; i++) + strip_trailing_slashes (file[i]); + +- if (x.interactive == I_ALWAYS_NO) ++ if (x.interactive == I_ALWAYS_SKIP) + x.update = false; + +- if (make_backups && x.interactive == I_ALWAYS_NO) ++ if (make_backups && x.interactive == I_ALWAYS_SKIP) + { + error (0, 0, + _("options --backup and --no-clobber are mutually exclusive")); +diff --git a/src/system.h b/src/system.h +index a074b65010..d9e169c6ed 100644 +--- a/src/system.h ++++ b/src/system.h +@@ -596,8 +596,8 @@ emit_update_parameters_note (void) + UPDATE controls which existing files in the destination are replaced.\n\ + 'all' is the default operation when an --update option is not specified,\n\ + and results in all existing files in the destination being replaced.\n\ +-'none' is similar to the --no-clobber option, in that no files in the\n\ +-destination are replaced, but also skipped files do not induce a failure.\n\ ++'none' is like the --no-clobber option, in that no files in the\n\ ++destination are replaced, and skipped files do not induce a failure.\n\ + 'older' is the default operation when --update is specified, and results\n\ + in files being replaced if they're older than the corresponding source file.\n\ + "), stdout); +diff --git a/tests/cp/cp-i.sh b/tests/cp/cp-i.sh +index d382684039..02a992c3a0 100755 +--- a/tests/cp/cp-i.sh ++++ b/tests/cp/cp-i.sh +@@ -29,7 +29,6 @@ echo n | returns_ 1 cp -iR a b 2>/dev/null || fail=1 + # test miscellaneous combinations of -f -i -n parameters + touch c d || framework_failure_ + echo "'c' -> 'd'" > out_copy || framework_failure_ +-echo "cp: not replacing 'd'" > err_skip || framework_failure_ + touch out_empty || framework_failure_ + + # ask for overwrite, answer no +@@ -45,12 +44,12 @@ echo y | cp -vni c d 2>/dev/null > out3 || fail=1 + compare out3 out_copy || fail=1 + + # -n wins over -i +-echo y | returns_ 1 cp -vin c d 2>/dev/null > out4 || fail=1 ++echo y | cp -vin c d 2>/dev/null > out4 || fail=1 + compare out4 out_empty || fail=1 + + # -n wins over -i non verbose +-echo y | returns_ 1 cp -in c d 2>err4 > out4 || fail=1 +-compare err4 err_skip || fail=1 ++echo y | cp -in c d 2>err4 > out4 || fail=1 ++compare /dev/null err4 || fail=1 + compare out4 out_empty || fail=1 + + # ask for overwrite, answer yes +@@ -58,11 +57,11 @@ echo y | cp -vfi c d 2>/dev/null > out5 || fail=1 + compare out5 out_copy || fail=1 + + # do not ask, prevent from overwrite +-echo n | returns_ 1 cp -vfn c d 2>/dev/null > out6 || fail=1 ++echo n | cp -vfn c d 2>/dev/null > out6 || fail=1 + compare out6 out_empty || fail=1 + + # do not ask, prevent from overwrite +-echo n | returns_ 1 cp -vnf c d 2>/dev/null > out7 || fail=1 ++echo n | cp -vnf c d 2>/dev/null > out7 || fail=1 + compare out7 out_empty || fail=1 + + # options --backup and --no-clobber are mutually exclusive +diff --git a/tests/mv/mv-n.sh b/tests/mv/mv-n.sh +index f484c21637..9bd3866ccb 100755 +--- a/tests/mv/mv-n.sh ++++ b/tests/mv/mv-n.sh +@@ -23,7 +23,6 @@ print_ver_ mv + # test miscellaneous combinations of -f -i -n parameters + touch a b || framework_failure_ + echo "renamed 'a' -> 'b'" > out_move +-echo "mv: not replacing 'b'" > err_skip || framework_failure_ + > out_empty + + # ask for overwrite, answer no +@@ -38,23 +37,23 @@ compare out2 out_move || fail=1 + + # -n wins (as the last option) + touch a b || framework_failure_ +-echo y | returns_ 1 mv -vin a b 2>/dev/null > out3 || fail=1 ++echo y | mv -vin a b 2>/dev/null > out3 || fail=1 + compare out3 out_empty || fail=1 + + # -n wins (non verbose) + touch a b || framework_failure_ +-echo y | returns_ 1 mv -in a b 2>err3 > out3 || fail=1 ++echo y | mv -in a b 2>err3 > out3 || fail=1 + compare out3 out_empty || fail=1 +-compare err3 err_skip || fail=1 ++compare /dev/null err3 || fail=1 + + # -n wins (as the last option) + touch a b || framework_failure_ +-echo y | returns_ 1 mv -vfn a b 2>/dev/null > out4 || fail=1 ++echo y | mv -vfn a b 2>/dev/null > out4 || fail=1 + compare out4 out_empty || fail=1 + + # -n wins (as the last option) + touch a b || framework_failure_ +-echo y | returns_ 1 mv -vifn a b 2>/dev/null > out5 || fail=1 ++echo y | mv -vifn a b 2>/dev/null > out5 || fail=1 + compare out5 out_empty || fail=1 + + # options --backup and --no-clobber are mutually exclusive diff --git a/backport-dirent-define-DT_-macros-on-all-platforms.patch b/backport-dirent-define-DT_-macros-on-all-platforms.patch new file mode 100644 index 0000000000000000000000000000000000000000..660dc8d82455b2ebe8e0b8e8a3f05faa5b93c0bd --- /dev/null +++ b/backport-dirent-define-DT_-macros-on-all-platforms.patch @@ -0,0 +1,125 @@ +From a6de9f388441c4389e2ee3a8e98486f195d67198 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 2 Oct 2024 15:50:10 -0700 +Subject: [PATCH] dirent: define DT_* macros on all platforms + +* lib/dirent.in.h (DT_UNKNOWN, DT_FIFO, DT_CHR, DT_DIR, DT_BLK) +(DT_REG, DT_LNK, DT_SOCK, DT_WHT): Define these on all platforms, +if the system does not already define them. Check that they +have distinct values. +(_GL_DIRENT_S_ISWHT, _GL_DIRENT_S_IFWHT) [!(IFTODT && DTTOIF)]: +New macros. +(IFTODT, DTTOIF): Define if not already defined. +* modules/dirent (Depends-on): Add assert-h, extensions. + + +--- + lib/dirent.in.h | 89 +++++++++++++++++++++++++++++++---- + 4 files changed, 114 insertions(+), 12 deletions(-) + +diff --git a/lib/dirent.in.h b/lib/dirent.in.h +index 7ba8fc64..c8c39c3e 100644 +--- a/lib/dirent.in.h ++++ b/lib/dirent.in.h +@@ -46,20 +46,89 @@ struct dirent + char d_type; + char d_name[1]; + }; +-/* Possible values for 'd_type'. */ +-# define DT_UNKNOWN 0 +-# define DT_FIFO 1 /* FIFO */ +-# define DT_CHR 2 /* character device */ +-# define DT_DIR 4 /* directory */ +-# define DT_BLK 6 /* block device */ +-# define DT_REG 8 /* regular file */ +-# define DT_LNK 10 /* symbolic link */ +-# define DT_SOCK 12 /* socket */ +-# define DT_WHT 14 /* whiteout */ + # define GNULIB_defined_struct_dirent 1 + # endif + #endif + ++/* 'd_type' macros specified in GNU, i.e., POSIX.1-2024 plus DT_WHT, ++ but not (yet) DT_MQ, DT_SEM, DT_SHM, DT_TMO. ++ These macros can be useful even on platforms that do not support ++ d_type or the corresponding file types. ++ Default to the Linux values which are also popular elsewhere, ++ and check that all macros have distinct values. */ ++#ifndef DT_UNKNOWN ++# define DT_UNKNOWN 0 ++#endif ++#ifndef DT_FIFO ++# define DT_FIFO 1 /* FIFO */ ++#endif ++#ifndef DT_CHR ++# define DT_CHR 2 /* character device */ ++#endif ++#ifndef DT_DIR ++# define DT_DIR 4 /* directory */ ++#endif ++#ifndef DT_BLK ++# define DT_BLK 6 /* block device */ ++#endif ++#ifndef DT_REG ++# define DT_REG 8 /* regular file */ ++#endif ++#ifndef DT_LNK ++# define DT_LNK 10 /* symbolic link */ ++#endif ++#ifndef DT_SOCK ++# define DT_SOCK 12 /* socket */ ++#endif ++#ifndef DT_WHT ++# define DT_WHT 14 /* whiteout */ ++#endif ++static_assert (DT_UNKNOWN != DT_FIFO && DT_UNKNOWN != DT_CHR ++ && DT_UNKNOWN != DT_BLK && DT_UNKNOWN != DT_REG ++ && DT_UNKNOWN != DT_LNK && DT_UNKNOWN != DT_SOCK ++ && DT_UNKNOWN != DT_WHT ++ && DT_FIFO != DT_CHR && DT_FIFO != DT_BLK && DT_FIFO != DT_REG ++ && DT_FIFO != DT_LNK && DT_FIFO != DT_SOCK && DT_FIFO != DT_WHT ++ && DT_CHR != DT_BLK && DT_CHR != DT_REG && DT_CHR != DT_LNK ++ && DT_CHR != DT_SOCK && DT_CHR != DT_WHT ++ && DT_BLK != DT_REG && DT_BLK != DT_LNK && DT_BLK != DT_SOCK ++ && DT_BLK != DT_WHT ++ && DT_REG != DT_LNK && DT_REG != DT_SOCK && DT_REG != DT_WHT ++ && DT_LNK != DT_SOCK && DT_LNK != DT_WHT ++ && DT_SOCK != DT_WHT); ++ ++/* Conversion between S_IF* and DT_* file types. */ ++#if ! (defined IFTODT && defined DTTOIF) ++# include ++# ifdef S_ISWHT ++# define _GL_DIRENT_S_ISWHT(mode) S_ISWHT(mode) ++# else ++# define _GL_DIRENT_S_ISWHT(mode) 0 ++# endif ++# ifdef S_IFWHT ++# define _GL_DIRENT_S_IFWHT S_IFWHT ++# else ++# define _GL_DIRENT_S_IFWHT (DT_WHT << 12) /* just a guess */ ++# endif ++#endif ++#ifndef IFTODT ++# define IFTODT(mode) \ ++ (S_ISREG (mode) ? DT_REG : S_ISDIR (mode) ? DT_DIR \ ++ : S_ISLNK (mode) ? DT_LNK : S_ISBLK (mode) ? DT_BLK \ ++ : S_ISCHR (mode) ? DT_CHR : S_ISFIFO (mode) ? DT_FIFO \ ++ : S_ISSOCK (mode) ? DT_SOCK \ ++ : _GL_DIRENT_S_ISWHT (mode) ? DT_WHT : DT_UNKNOWN) ++#endif ++#ifndef DTTOIF ++# define DTTOIF(dirtype) \ ++ ((dirtype) == DT_REG ? S_IFREG : (dirtype) == DT_DIR ? S_IFDIR \ ++ : (dirtype) == DT_LNK ? S_IFLNK : (dirtype) == DT_BLK ? S_IFBLK \ ++ : (dirtype) == DT_CHR ? S_IFCHR : dirtype == DT_FIFO ? S_IFIFO \ ++ : (dirtype) == DT_SOCK ? S_IFSOCK \ ++ : (dirtype) == DT_WHT ? _GL_DIRENT_S_IFWHT \ ++ : (dirtype) << 12 /* just a guess */) ++#endif ++ + #if !@DIR_HAS_FD_MEMBER@ + # if !GNULIB_defined_DIR + /* struct gl_directory is a type with a field 'int fd_to_close'. +-- +2.43.0 \ No newline at end of file diff --git a/backport-doc-cp-no-clobber-improve-documentation.patch b/backport-doc-cp-no-clobber-improve-documentation.patch new file mode 100644 index 0000000000000000000000000000000000000000..3c0831b3ab32d6a3af5f7acdb6cfc4488817d50c --- /dev/null +++ b/backport-doc-cp-no-clobber-improve-documentation.patch @@ -0,0 +1,42 @@ +From 63e9f1ae61e4b48ab8fe505a7d9bd1ae092c1a32 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sun, 17 Dec 2023 14:35:36 +0000 +Subject: [PATCH] doc: cp --no-clobber: improve documentation + +* doc/coreutils.texi (cp invocation): Reference the related --update +option, like we had already done in mv invocation. +* src/cp.c (usage): State clearly what --no-clobber does, +indicating it's protection focused, rather than being update focused. +--- + doc/coreutils.texi | 2 ++ + src/cp.c | 4 ++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 1f8b356d12..bf0f424d34 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -9057,6 +9057,8 @@ Do not overwrite an existing file; silently fail instead. + This option overrides a previous + @option{-i} option. This option is mutually exclusive with @option{-b} or + @option{--backup} option. ++See also the @option{--update=none} option which will ++skip existing files but not fail. + + @item -P + @itemx --no-dereference +diff --git a/src/cp.c b/src/cp.c +index 04a5cbee38..3ccc4c4e60 100644 +--- a/src/cp.c ++++ b/src/cp.c +@@ -192,8 +192,8 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ + -L, --dereference always follow symbolic links in SOURCE\n\ + "), stdout); + fputs (_("\ +- -n, --no-clobber do not overwrite an existing file (overrides a\n\ +- -u or previous -i option). See also --update\n\ ++ -n, --no-clobber ensure no existing files overwritten, and fail\n\ ++ silently instead. See also --update\n\ + "), stdout); + fputs (_("\ + -P, --no-dereference never follow symbolic links in SOURCE\n\ diff --git a/backport-file-has-acl-Fix-comments.patch b/backport-file-has-acl-Fix-comments.patch new file mode 100644 index 0000000000000000000000000000000000000000..e7dd23adf3e411fadbe9fec33b378a68430d892d --- /dev/null +++ b/backport-file-has-acl-Fix-comments.patch @@ -0,0 +1,30 @@ +From fda640572909c9416cbfe76c385a4d48281561ca Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Mon, 7 Oct 2024 13:52:59 +0200 +Subject: [PATCH] file-has-acl: Fix comments. + +* lib/file-has-acl.c (file_has_aclinfo): Fix typo in comment. + + +--- + lib/file-has-acl.c | 4 ++-- + 2 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 9f421370..1fc54a76 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -328,9 +328,9 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + if ACLs are not supported as errno is set in that case also. + Set *AI to ACL info regardless of return value. + FLAGS should be a d_type value, optionally ORed with +- AT_SYMLINK_FOLLOW; if the d_type value is not known, ++ ACL_SYMLINK_FOLLOW; if the d_type value is not known, + use DT_UNKNOWN though this may be less efficient. +- If FLAGS & AT_SYMLINK_FOLLOW, follow symlinks when retrieving ACL info; ++ If FLAGS & ACL_SYMLINK_FOLLOW, follow symlinks when retrieving ACL info; + otherwise do not follow them if possible. */ + int + file_has_aclinfo (MAYBE_UNUSED char const *restrict name, +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-Fix-compilation-error-regression-yester.patch b/backport-file-has-acl-Fix-compilation-error-regression-yester.patch new file mode 100644 index 0000000000000000000000000000000000000000..cf13bb25e01a4676dda6ea7761f486b9c8e62329 --- /dev/null +++ b/backport-file-has-acl-Fix-compilation-error-regression-yester.patch @@ -0,0 +1,37 @@ +From c4e6d695b9f970eb312fa6664a7876dd693e7f06 Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Mon, 30 Sep 2024 14:16:28 +0200 +Subject: [PATCH] file-has-acl: Fix compilation error (regression yesterday). + +* m4/acl.m4 (gl_FILE_HAS_ACL): Require, not invoke, +gl_CHECK_HEADER_SELINUX_SELINUX_H. + + +--- + m4/acl.m4 | 6 +++--- + 2 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/m4/acl.m4 b/m4/acl.m4 +index 4328bd20..69135f98 100644 +--- a/m4/acl.m4 ++++ b/m4/acl.m4 +@@ -1,5 +1,5 @@ + # acl.m4 - check for access control list (ACL) primitives +-# serial 32 ++# serial 33 + + # Copyright (C) 2002, 2004-2023 Free Software Foundation, Inc. + # This file is free software; the Free Software Foundation +@@ -214,8 +214,8 @@ AC_DEFUN([gl_FILE_HAS_ACL], + AS_CASE([$enable_acl,$with_selinux,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr], + [no,* | *,no,*], [], + [*,*,yes,yes], +- [gl_CHECK_HEADER_SELINUX_SELINUX_H +- AS_IF([test "$USE_SELINUX_SELINUX_H" -ne 0 ], ++ [AC_REQUIRE([gl_CHECK_HEADER_SELINUX_SELINUX_H]) ++ AS_IF([test $USE_SELINUX_SELINUX_H = 1], + [FILE_HAS_ACL_LIB="$FILE_HAS_ACL_LIB $LIB_SELINUX" + gl_file_has_acl_uses_selinux=yes])]) + +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-Fix-compilation-errors-on-macOS-et-al.-.patch b/backport-file-has-acl-Fix-compilation-errors-on-macOS-et-al.-.patch new file mode 100644 index 0000000000000000000000000000000000000000..623706c14a3d7718bcdf864ee72f0411f05eb853 --- /dev/null +++ b/backport-file-has-acl-Fix-compilation-errors-on-macOS-et-al.-.patch @@ -0,0 +1,103 @@ +From c56c270c03eede5a3342e323f0469d5fdbc09cf0 Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Mon, 11 Nov 2024 07:25:00 +0100 +Subject: [PATCH] file-has-acl: Fix compilation errors on macOS et al. (regr. + 2024-11-09). + +* m4/acl.m4 (gl_FUNC_ACL): Test whether acl_get_link_np exists. +* lib/file-has-acl.c (file_has_aclinfo): Use ACL_SYMLINK_FOLLOW, not +AC_SYMLINK_FOLLOW. Don't use acl_get_link_np if this function does not +exist. + + +--- + lib/file-has-acl.c | 22 +++++++++++++--------- + m4/acl.m4 | 7 ++++--- + 2 files changed, 17 insertions(+), 12 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 9c02d02..7f9ea02 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -432,7 +432,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + + { + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ +- /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ ++ /* Linux, FreeBSD, NetBSD >= 10, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ + int ret; + + # if HAVE_ACL_EXTENDED_FILE /* Linux */ +@@ -449,7 +449,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + always return NULL / EINVAL. There is no point in making + these two useless calls. The real ACL is retrieved through + acl_get_file (name, ACL_TYPE_EXTENDED). */ +- acl_t acl = ((flags & AC_SYMLINK_FOLLOW ++ acl_t acl = ((flags & ACL_SYMLINK_FOLLOW + ? acl_get_file + : acl_get_link_np) + (name, ACL_TYPE_EXTENDED)); +@@ -460,11 +460,14 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + } + else + ret = -1; +-# else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */ +- acl_t acl = ((flags & AC_SYMLINK_FOLLOW +- ? acl_get_file +- : acl_get_link_np) +- (name, ACL_TYPE_ACCESS)); ++# else /* FreeBSD, NetBSD >= 10, IRIX, Tru64, Cygwin >= 2.5 */ ++ acl_t acl; ++# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10 */ ++ if (!(flags & ACL_SYMLINK_FOLLOW)) ++ acl = acl_get_link_np (name, ACL_TYPE_ACCESS); ++ else ++# endif ++ acl = acl_get_file (name, ACL_TYPE_ACCESS); + if (acl) + { + ret = acl_access_nontrivial (acl); +@@ -475,8 +478,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always + returns NULL with errno not set. There is no point in + making this call. */ +-# else /* FreeBSD, IRIX, Cygwin >= 2.5 */ +- /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS) ++# else /* FreeBSD, NetBSD >= 10, IRIX, Cygwin >= 2.5 */ ++ /* On Linux, FreeBSD, NetBSD, IRIX, ++ acl_get_file (name, ACL_TYPE_ACCESS) + and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory + either both succeed or both fail; it depends on the + file system. Therefore there is no point in making the second +diff --git a/m4/acl.m4 b/m4/acl.m4 +index 88bb4d9..74ac74f 100644 +--- a/m4/acl.m4 ++++ b/m4/acl.m4 +@@ -1,5 +1,5 @@ + # acl.m4 - check for access control list (ACL) primitives +-# serial 33 ++# serial 34 + + # Copyright (C) 2002, 2004-2023 Free Software Foundation, Inc. + # This file is free software; the Free Software Foundation +@@ -32,8 +32,8 @@ AC_DEFUN_ONCE([gl_FUNC_ACL], + if test $ac_cv_header_sys_acl_h = yes; then + ac_save_LIBS=$LIBS + +- dnl Test for POSIX-draft-like API (GNU/Linux, FreeBSD, Mac OS X, +- dnl IRIX, Tru64, Cygwin >= 2.5). ++ dnl Test for POSIX-draft-like API (GNU/Linux, FreeBSD, NetBSD >= 10, ++ dnl Mac OS X, IRIX, Tru64, Cygwin >= 2.5). + dnl -lacl is needed on GNU/Linux, -lpacl on OSF/1. + if test $use_acl = 0; then + AC_SEARCH_LIBS([acl_get_file], [acl pacl], +@@ -42,6 +42,7 @@ AC_DEFUN_ONCE([gl_FUNC_ACL], + fi + AC_CHECK_FUNCS( + [acl_get_file acl_get_fd acl_set_file acl_set_fd \ ++ acl_get_link_np \ + acl_free acl_from_mode acl_from_text \ + acl_delete_def_file acl_extended_file \ + acl_delete_fd_np acl_delete_file_np \ +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-Fix-performance-regression-on-FreeBSD-C.patch b/backport-file-has-acl-Fix-performance-regression-on-FreeBSD-C.patch new file mode 100644 index 0000000000000000000000000000000000000000..c2c3b641470dc6361c5f7695f6a054cd660df178 --- /dev/null +++ b/backport-file-has-acl-Fix-performance-regression-on-FreeBSD-C.patch @@ -0,0 +1,118 @@ +From c3fe68e0a802db61d46e4cfd67a04e1476a47407 Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Mon, 7 Oct 2024 14:30:52 +0200 +Subject: [PATCH] file-has-acl: Fix performance regression on FreeBSD, Cygwin. + +* lib/dirent.in.h (_GL_DT_NOTDIR): New macro. +* lib/acl.h (ACL_SYMLINK_FOLLOW): Increase value. +* lib/file-has-acl.c (file_has_aclinfo): Don't call +acl_get_file (name, ACL_TYPE_DEFAULT) if we know that name does not +denote a directory. +(file_has_acl): Extract from *SB the information that NAME is not a +directory. + + +--- + lib/acl.h | 6 +++--- + lib/dirent.in.h | 6 ++++++ + lib/file-has-acl.c | 15 +++++++++++---- + 4 files changed, 31 insertions(+), 7 deletions(-) + +diff --git a/lib/acl.h b/lib/acl.h +index 29ec2496..68a421a0 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -32,9 +32,9 @@ + #include + #include + +-/* Follow symlinks when getting an ACL. This is greater than any +- unsigned char so that it can be ORed with any DT_* value. */ +-enum { ACL_SYMLINK_FOLLOW = 1 + (unsigned char) -1 }; ++/* Follow symlinks when getting an ACL. This is a bitmask that is guaranteed ++ not to collide with any DT_* or _GL_DT_* value. */ ++enum { ACL_SYMLINK_FOLLOW = 0x10000 }; + + /* Information about an ACL. */ + struct aclinfo +diff --git a/lib/dirent.in.h b/lib/dirent.in.h +index c8c39c3e..a0ac39b4 100644 +--- a/lib/dirent.in.h ++++ b/lib/dirent.in.h +@@ -54,6 +54,7 @@ struct dirent + but not (yet) DT_MQ, DT_SEM, DT_SHM, DT_TMO. + These macros can be useful even on platforms that do not support + d_type or the corresponding file types. ++ The values of these macros are all in the 'unsigned char' range. + Default to the Linux values which are also popular elsewhere, + and check that all macros have distinct values. */ + #ifndef DT_UNKNOWN +@@ -97,6 +98,9 @@ static_assert (DT_UNKNOWN != DT_FIFO && DT_UNKNOWN != DT_CHR + && DT_LNK != DT_SOCK && DT_LNK != DT_WHT + && DT_SOCK != DT_WHT); + ++/* Other optional information about a directory entry. */ ++#define _GL_DT_NOTDIR 0x100 /* Not a directory */ ++ + /* Conversion between S_IF* and DT_* file types. */ + #if ! (defined IFTODT && defined DTTOIF) + # include +@@ -111,6 +115,7 @@ static_assert (DT_UNKNOWN != DT_FIFO && DT_UNKNOWN != DT_CHR + # define _GL_DIRENT_S_IFWHT (DT_WHT << 12) /* just a guess */ + # endif + #endif ++/* Conversion from a 'stat' mode to a DT_* value. */ + #ifndef IFTODT + # define IFTODT(mode) \ + (S_ISREG (mode) ? DT_REG : S_ISDIR (mode) ? DT_DIR \ +@@ -119,6 +124,7 @@ static_assert (DT_UNKNOWN != DT_FIFO && DT_UNKNOWN != DT_CHR + : S_ISSOCK (mode) ? DT_SOCK \ + : _GL_DIRENT_S_ISWHT (mode) ? DT_WHT : DT_UNKNOWN) + #endif ++/* Conversion from a DT_* value to a 'stat' mode. */ + #ifndef DTTOIF + # define DTTOIF(dirtype) \ + ((dirtype) == DT_REG ? S_IFREG : (dirtype) == DT_DIR ? S_IFDIR \ +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 1fc54a76..0dfd25b5 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -328,8 +328,10 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + if ACLs are not supported as errno is set in that case also. + Set *AI to ACL info regardless of return value. + FLAGS should be a d_type value, optionally ORed with +- ACL_SYMLINK_FOLLOW; if the d_type value is not known, +- use DT_UNKNOWN though this may be less efficient. ++ - _GL_DT_NOTDIR if it is known that NAME is not a directory, ++ - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link. ++ If the d_type value is not known, use DT_UNKNOWN though this may be less ++ efficient. + If FLAGS & ACL_SYMLINK_FOLLOW, follow symlinks when retrieving ACL info; + otherwise do not follow them if possible. */ + int +@@ -463,7 +465,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + either both succeed or both fail; it depends on the + file system. Therefore there is no point in making the second + call if the first one already failed. */ +- if (ret == 0 && (d_type == DT_DIR || d_type == DT_UNKNOWN)) ++ if (ret == 0 ++ && (d_type == DT_DIR ++ || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR)))) + { + acl = acl_get_file (name, ACL_TYPE_DEFAULT); + if (acl) +@@ -851,8 +855,11 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + int + file_has_acl (char const *name, struct stat const *sb) + { ++ int flags = IFTODT (sb->st_mode); ++ if (!S_ISDIR (sb->st_mode)) ++ flags |= _GL_DT_NOTDIR; + struct aclinfo ai; +- int r = file_has_aclinfo (name, &ai, IFTODT (sb->st_mode)); ++ int r = file_has_aclinfo (name, &ai, flags); + aclinfo_free (&ai); + return r; + } +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-Fix-test-failure-on-Cygwin.patch b/backport-file-has-acl-Fix-test-failure-on-Cygwin.patch new file mode 100644 index 0000000000000000000000000000000000000000..1931bdd8753181a438fa687041f47bcdd3a49a2a --- /dev/null +++ b/backport-file-has-acl-Fix-test-failure-on-Cygwin.patch @@ -0,0 +1,36 @@ +From 6e398bee5adc9a88a0023767c457621e596e44c4 Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Mon, 7 Oct 2024 14:59:00 +0200 +Subject: [PATCH] file-has-acl: Fix test failure on Cygwin. + +* lib/file-has-acl.c (file_has_aclinfo): On Cygwin, don't fail if +acl_get_file (name, ACL_TYPE_DEFAULT) returns NULL on a file of unknown +type. + + +--- + lib/file-has-acl.c | 9 ++++++++- + 2 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 0dfd25b5..64291e02 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -483,7 +483,14 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + # endif + } + else +- ret = -1; ++ { ++# ifdef __CYGWIN__ /* Cygwin >= 2.5 */ ++ if (d_type == DT_UNKNOWN) ++ ret = 0; ++ else ++# endif ++ ret = -1; ++ } + } + # endif + } +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-_GL_UNUSED-MAYBE_UNUSED.patch b/backport-file-has-acl-_GL_UNUSED-MAYBE_UNUSED.patch new file mode 100644 index 0000000000000000000000000000000000000000..50012122d6062e2255f3dafe0b5bc236b9f3d03f --- /dev/null +++ b/backport-file-has-acl-_GL_UNUSED-MAYBE_UNUSED.patch @@ -0,0 +1,29 @@ +From 6a018d0492239d01c2cc8fd56a1acec4d6fcd44d Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sun, 10 Nov 2024 21:18:02 -0800 +Subject: [PATCH] =?UTF-8?q?file-has-acl:=20=5FGL=5FUNUSED=20=E2=86=92=20MA?= + =?UTF-8?q?YBE=5FUNUSED?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + + +--- + lib/file-has-acl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 1cd97954..3cfff7ec 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -341,7 +341,7 @@ int + file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + struct aclinfo *restrict ai, int flags) + { +- _GL_UNUSED unsigned char d_type = flags & UCHAR_MAX; ++ MAYBE_UNUSED unsigned char d_type = flags & UCHAR_MAX; + + #if USE_LINUX_XATTR + int initial_errno = errno; +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-another-nofollow-fix-for-FreeBSD.patch b/backport-file-has-acl-another-nofollow-fix-for-FreeBSD.patch new file mode 100644 index 0000000000000000000000000000000000000000..bea3d52c83fca3ce8f8432f6b924b80abb9680cd --- /dev/null +++ b/backport-file-has-acl-another-nofollow-fix-for-FreeBSD.patch @@ -0,0 +1,70 @@ +From a1615477559c4b93939b073b33f6d900d2fbed0c Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 11 Nov 2024 10:57:35 -0800 +Subject: [PATCH] file-has-acl: another nofollow fix for FreeBSD +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* lib/file-has-acl.c (file_has_aclinfo): On FreeBSD, NetBSD >= 10, +if we don’t follow symlinks the first time, also don’t follow +them the second time, when it is typically a directory - so it +doesn’t matter whether symlinks are followed - but it might not be. + + +--- + lib/file-has-acl.c | 23 +++++++++++------------ + 2 files changed, 17 insertions(+), 12 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 3cfff7ec..7c29f201 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -331,12 +331,11 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + Set *AI to ACL info regardless of return value. + FLAGS should be a d_type value, optionally ORed with + - _GL_DT_NOTDIR if it is known that NAME is not a directory, +- - ACL_GET_SCONTEXT to retrieve extended attributes, +- - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link. ++ - ACL_GET_SCONTEXT to retrieve security context and return 1 if present, ++ - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link; ++ otherwise do not follow them if possible. + If the d_type value is not known, use DT_UNKNOWN though this may be less +- efficient. +- If FLAGS & ACL_SYMLINK_FOLLOW, follow symlinks when retrieving ACL info; +- otherwise do not follow them if possible. */ ++ efficient. */ + int + file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + struct aclinfo *restrict ai, int flags) +@@ -429,13 +428,13 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + else + ret = -1; + # else /* FreeBSD, NetBSD >= 10, IRIX, Tru64, Cygwin >= 2.5 */ +-# if !HAVE_ACL_GET_LINK_NP /* IRIX, Tru64, Cygwin >= 2.5 */ +-# define acl_get_link_np acl_get_file ++ acl_t (*acl_get_file_or_link) (char const *, acl_type_t) = acl_get_file; ++# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10 */ ++ if (! (flags & ACL_SYMLINK_FOLLOW)) ++ acl_get_file_or_link = acl_get_link_np; + # endif +- acl_t acl = ((flags & AC_SYMLINK_FOLLOW +- ? acl_get_file +- : acl_get_link_np) +- (name, ACL_TYPE_ACCESS)); ++ ++ acl_t acl = acl_get_file_or_link (name, ACL_TYPE_ACCESS); + if (acl) + { + ret = acl_access_nontrivial (acl); +@@ -457,7 +456,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + && (d_type == DT_DIR + || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR)))) + { +- acl = acl_get_file (name, ACL_TYPE_DEFAULT); ++ acl = acl_get_file_or_link (name, ACL_TYPE_DEFAULT); + if (acl) + { + # ifdef __CYGWIN__ /* Cygwin >= 2.5 */ +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-avoid-Werror-unused-variable-with-disab.patch b/backport-file-has-acl-avoid-Werror-unused-variable-with-disab.patch new file mode 100644 index 0000000000000000000000000000000000000000..3e8930e84c4f5b2be31ee0bced1c8e4cc6485911 --- /dev/null +++ b/backport-file-has-acl-avoid-Werror-unused-variable-with-disab.patch @@ -0,0 +1,29 @@ +From 10197d9c2c7d55c3b4eb999c7670cf8ec7d14c17 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sun, 10 Nov 2024 12:56:10 +0000 +Subject: [PATCH] file-has-acl: avoid -Werror=unused-variable with + --disable-acl + +* lib/file-has-acl.c (file_has_aclinfo): Mark D_TYPE as unused +in certain configurations. + + +--- + lib/file-has-acl.c | 2 +- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 9a89e2de..468ac36f 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -334,7 +334,7 @@ int + file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + struct aclinfo *restrict ai, int flags) + { +- unsigned char d_type = flags & UCHAR_MAX; ++ _GL_UNUSED unsigned char d_type = flags & UCHAR_MAX; + + #if USE_LINUX_XATTR + int initial_errno = errno; +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-handle-NFSv4-ACLs-with-listxattr-return.patch b/backport-file-has-acl-handle-NFSv4-ACLs-with-listxattr-return.patch new file mode 100644 index 0000000000000000000000000000000000000000..70b228db1773868fa426df7d7e8c8bf49260a350 --- /dev/null +++ b/backport-file-has-acl-handle-NFSv4-ACLs-with-listxattr-return.patch @@ -0,0 +1,99 @@ +From 05c63bc908a67a316fea29ddf4c702d89cf5bdec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sat, 11 Jan 2025 16:40:33 +0000 +Subject: [PATCH] file-has-acl: handle NFSv4 ACLs with listxattr returning + EACCES + +* lib/file-has-acl.c (has_xattr): A new helper function to +lookup aclinfo for the xattr or fallback to getxattr() if appropriate. +(get_aclinfo): Use has_xattr() rather than aclinfo_has_xattr(). +Discussed at + + +--- + lib/file-has-acl.c | 46 ++++++++++++++++++++++++++++++++++++---------- + 2 files changed, 44 insertions(+), 10 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 5ec6b256..e8413f8f 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -99,6 +99,36 @@ enum { + ACE4_IDENTIFIER_GROUP = 0x00000040 + }; + ++/* AI indicates XATTR may be present but wasn't accessible. ++ This is the case when [l]listxattr failed with E2BIG, ++ or failed with EACCES which in Linux kernel 6.12 NFS can mean merely ++ that we lack read access. ++*/ ++ ++static bool ++aclinfo_may_indicate_xattr (struct aclinfo const *ai) ++{ ++ return ai->size < 0 && (ai->u.err == EACCES || ai->u.err == E2BIG); ++} ++ ++/* Does NAME have XATTR? */ ++ ++static bool ++has_xattr (char const *xattr, struct aclinfo const *ai, ++ MAYBE_UNUSED char const *restrict name, MAYBE_UNUSED int flags) ++{ ++ if (ai && aclinfo_has_xattr (ai, xattr)) ++ return true; ++ else if (!ai || aclinfo_may_indicate_xattr (ai)) ++ { ++ int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) ++ (name, xattr, NULL, 0)); ++ if (0 <= ret || (errno == ERANGE || errno == E2BIG)) ++ return true; ++ } ++ return false; ++} ++ + /* Does AI's xattr set contain XATTR? */ + + bool +@@ -176,13 +206,9 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + } + } + +- /* A security context can exist only if extended attributes do: i.e., +- [l]listxattr either returned a positive number, or failed with E2BIG, +- or failed with EACCES which in Linux kernel 6.12 NFS can mean merely +- that we lack read access. */ ++ /* A security context can exist only if extended attributes do. */ + if (flags & ACL_GET_SCONTEXT +- && (0 < ai->size +- || (ai->size < 0 && (ai->u.err == E2BIG || ai->u.err == EACCES)))) ++ && (0 < ai->size || aclinfo_may_indicate_xattr (ai))) + { + if (is_smack_enabled ()) + { +@@ -358,7 +384,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + int initial_errno = errno; + get_aclinfo (name, ai, flags); + +- if (ai->size <= 0) ++ if (!aclinfo_may_indicate_xattr (ai) && ai->size <= 0) + { + errno = ai->size < 0 ? ai->u.err : initial_errno; + return ai->size; +@@ -369,11 +395,11 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + In earlier Fedora the two types of ACLs were mutually exclusive. + Attempt to work correctly on both kinds of systems. */ + +- if (!aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL)) ++ if (!has_xattr (XATTR_NAME_NFSV4_ACL, ai, name, flags)) + return +- (aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) ++ (has_xattr (XATTR_NAME_POSIX_ACL_ACCESS, ai, name, flags) + || ((d_type == DT_DIR || d_type == DT_UNKNOWN) +- && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))); ++ && has_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ai, name, flags))); + + /* A buffer large enough to hold any trivial NFSv4 ACL. + The max length of a trivial NFSv4 ACL is 6 words for owner, +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-handle-listxattr-returning-ENOTSUP.patch b/backport-file-has-acl-handle-listxattr-returning-ENOTSUP.patch new file mode 100644 index 0000000000000000000000000000000000000000..86b51b3c9e5eb96944d00087ad8b804a307e1b70 --- /dev/null +++ b/backport-file-has-acl-handle-listxattr-returning-ENOTSUP.patch @@ -0,0 +1,42 @@ +From caf768863e2a411ede373164e861b0bf6b707bcc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Wed, 15 Jan 2025 23:30:24 +0000 +Subject: [PATCH] file-has-acl: handle listxattr returning ENOTSUP + +listxattr() was seen to return ENOTSUP on virtiofs, +which resulted in ls outputting "Not supported" errors. + +* lib/file-has-acl.c (aclinfo_may_indicate_xattr): Treat any +non valid acl errno as being inconclusive as to whether there +are xattrs available. + + +--- + lib/file-has-acl.c | 7 ++++--- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index e8413f8f..c02cfee8 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -101,14 +101,15 @@ enum { + + /* AI indicates XATTR may be present but wasn't accessible. + This is the case when [l]listxattr failed with E2BIG, +- or failed with EACCES which in Linux kernel 6.12 NFS can mean merely +- that we lack read access. ++ or is not supported (!acl_errno_valid()), or failed with EACCES ++ which in Linux kernel 6.12 NFS can mean merely that we lack read access. + */ + + static bool + aclinfo_may_indicate_xattr (struct aclinfo const *ai) + { +- return ai->size < 0 && (ai->u.err == EACCES || ai->u.err == E2BIG); ++ return ai->size < 0 && (!acl_errno_valid (ai->u.err) ++ || ai->u.err == EACCES || ai->u.err == E2BIG); + } + + /* Does NAME have XATTR? */ +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-improve-acl.h-comment.patch b/backport-file-has-acl-improve-acl.h-comment.patch new file mode 100644 index 0000000000000000000000000000000000000000..5f1a5264638c6ef5cb32d3b64c1889759dc6fad5 --- /dev/null +++ b/backport-file-has-acl-improve-acl.h-comment.patch @@ -0,0 +1,48 @@ +From 0c55c3d7f74d42d273e0fffc8b774344eed65458 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 30 Sep 2024 09:24:09 -0700 +Subject: [PATCH] file-has-acl: improve acl.h comment +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* lib/acl.h: Improve comment about ‘152’. + + +--- + lib/acl.h | 5 ++++- + lib/file-has-acl.c | 3 +-- + 2 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/lib/acl.h b/lib/acl.h +index 07ab0425..1a627323 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -65,7 +65,10 @@ struct aclinfo + int err; + + /* A small array of char, big enough for most listxattr results. +- The size is somewhat arbitrary. For internal use only. */ ++ The size is somewhat arbitrary; it equals the max length of a ++ trivial NFSv4 ACL (a size used by file-has-acl.c in 2023-2024 ++ but no longer relevant now), and a different value might be ++ better once experience is gained. For internal use only. */ + char __gl_acl_ch[152]; + } u; + }; +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 89faba56..6924a616 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -349,8 +349,7 @@ file_has_aclinfo (char const *name, struct stat const *sb, + || (S_ISDIR (sb->st_mode) + && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT)))); + +- /* If there is an NFSv4 ACL, follow up with a getxattr syscall +- to see whether the NFSv4 ACL is nontrivial. */ ++ /* If there is an NFSv4 ACL, check whether it is nontrivial. */ + if (nfsv4_acl) + { + /* A buffer large enough to hold any trivial NFSv4 ACL. +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-improve-performance-on-Linux-symlinks.patch b/backport-file-has-acl-improve-performance-on-Linux-symlinks.patch new file mode 100644 index 0000000000000000000000000000000000000000..aa91eb61eb052c0c185831512dca9fd2cb0010af --- /dev/null +++ b/backport-file-has-acl-improve-performance-on-Linux-symlinks.patch @@ -0,0 +1,210 @@ +From 276260e7ec35181b0ea9fddf5bab397cf1061eca Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 30 Sep 2024 10:46:57 -0700 +Subject: [PATCH] file-has-acl: improve performance on Linux symlinks + +* lib/file-has-acl.c (USE_LINUX_XATTR): Now can be used outside #if. +(file_has_aclinfo): As an optimization on Linux, do not attempt to +get extended attributes on symlinks, as this will always fail. +Also, use lgetxattr (not getxattr) and acl_extended_file_nofollow +(not acl_extended_file) when not following symlinks, to avoid some +races. + + +--- + lib/file-has-acl.c | 143 ++++++++++++++++++++++++++------------------- + 2 files changed, 92 insertions(+), 61 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 6924a616..944f0d22 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -33,7 +33,11 @@ + + static char const UNKNOWN_SECURITY_CONTEXT[] = "?"; + +-#define USE_LINUX_XATTR (USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR) ++#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR ++# define USE_LINUX_XATTR true ++#else ++# define USE_LINUX_XATTR false ++#endif + + #if USE_LINUX_XATTR + # if USE_SELINUX_SELINUX_H +@@ -320,80 +324,95 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + SB must be set to the stat buffer of NAME, + obtained through stat() or lstat(). + Set *AI to the ACL info if available. +- If FLAGS & AT_SYMLINK_FOLLOW, SB was gotten via stat and follow symlinks; +- otherwise, SB was gotten vi lstat and do not follow them. */ ++ If FLAGS & AT_SYMLINK_FOLLOW, SB was gotten via stat, otherwise lstat. ++ Also, if FLAGS & AT_SYMLINK_FOLLOW, follow symlinks when retrieving ++ ACL info, otherwise do not follow them if possible. */ + int + file_has_aclinfo (char const *name, struct stat const *sb, + struct aclinfo *ai, int flags) + { +-#if USE_LINUX_XATTR +- int initial_errno = errno; +- get_aclinfo (name, ai, flags); ++ /* Symbolic links lack extended attributes and ACLs on all supported ++ platforms, so don't bother trying to fetch them. If the platform ++ supports not following symlinks this defends against some races ++ and avoids a syscall; otherwise this is essential. ++ ++ First, initialize *AI for cases known to be unsupported. */ + +- if (ai->size <= 0) ++ if (!USE_LINUX_XATTR || S_ISLNK (sb->st_mode)) + { +- errno = ai->size < 0 ? ai->u.err : initial_errno; +- return ai->size; ++ ai->buf = ai->u.__gl_acl_ch; ++ ai->size = -1; ++ ai->u.err = ENOTSUP; ++ ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT; ++ ai->scontext_err = ENOTSUP; + } +- else ++ ++ /* Now set *AI for cases that might be supported, then check for ACLs. */ ++ ++#if USE_ACL ++ if (! S_ISLNK (sb->st_mode)) + { +- /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs, +- but if it has an NFSv4 ACL that's the one that matters. +- In earlier Fedora the two types of ACLs were mutually exclusive. +- Attempt to work correctly on both kinds of systems. */ +- bool nfsv4_acl = aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL); +- int ret +- = (ai->size <= 0 ? ai->size +- : (nfsv4_acl +- || aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) +- || (S_ISDIR (sb->st_mode) +- && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT)))); +- +- /* If there is an NFSv4 ACL, check whether it is nontrivial. */ +- if (nfsv4_acl) ++ ++# if USE_LINUX_XATTR ++ int initial_errno = errno; ++ get_aclinfo (name, ai, flags); ++ ++ if (ai->size <= 0) + { +- /* A buffer large enough to hold any trivial NFSv4 ACL. +- The max length of a trivial NFSv4 ACL is 6 words for owner, +- 6 for group, 7 for everyone, all times 2 because there are both +- allow and deny ACEs. There are 6 words for owner because of +- type, flag, mask, wholen, "OWNER@"+pad and similarly for group; +- everyone is another word to hold "EVERYONE@". */ +- uint32_t buf[2 * (6 + 6 + 7)]; +- +- ret = getxattr (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf); +- if (ret < 0) +- switch (errno) +- { +- case ENODATA: return 0; +- case ERANGE : return 1; /* ACL must be nontrivial. */ +- } +- else ++ errno = ai->size < 0 ? ai->u.err : initial_errno; ++ return ai->size; ++ } ++ else ++ { ++ /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs, ++ but if it has an NFSv4 ACL that's the one that matters. ++ In earlier Fedora the two types of ACLs were mutually exclusive. ++ Attempt to work correctly on both kinds of systems. */ ++ bool nfsv4_acl = aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL); ++ int ret ++ = (ai->size <= 0 ? ai->size ++ : (nfsv4_acl ++ || aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) ++ || (S_ISDIR (sb->st_mode) ++ && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT)))); ++ ++ /* If there is an NFSv4 ACL, check whether it is nontrivial. */ ++ if (nfsv4_acl) + { +- /* It looks like a trivial ACL, but investigate further. */ +- ret = acl_nfs4_nontrivial (buf, ret); ++ /* A buffer large enough to hold any trivial NFSv4 ACL. ++ The max length of a trivial NFSv4 ACL is 6 words for owner, ++ 6 for group, 7 for everyone, all times 2 because there are both ++ allow and deny ACEs. There are 6 words for owner because of ++ type, flag, mask, wholen, "OWNER@"+pad and similarly for group; ++ everyone is another word to hold "EVERYONE@". */ ++ uint32_t buf[2 * (6 + 6 + 7)]; ++ ++ ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) ++ (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf)); + if (ret < 0) ++ switch (errno) ++ { ++ case ENODATA: return 0; ++ case ERANGE : return 1; /* ACL must be nontrivial. */ ++ } ++ else + { +- errno = EINVAL; +- return ret; ++ /* It looks like a trivial ACL, but investigate further. */ ++ ret = acl_nfs4_nontrivial (buf, ret); ++ if (ret < 0) ++ { ++ errno = EINVAL; ++ return ret; ++ } ++ errno = initial_errno; + } +- errno = initial_errno; + } ++ if (ret < 0) ++ return - acl_errno_valid (errno); ++ return ret; + } +- if (ret < 0) +- return - acl_errno_valid (errno); +- return ret; +- } +-#else +- ai->size = -1; +- ai->u.err = ENOTSUP; +- ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT; +- ai->scontext_err = ENOTSUP; +-#endif + +-#if USE_ACL +- if (! S_ISLNK (sb->st_mode)) +- { +-# if HAVE_ACL_GET_FILE ++# elif HAVE_ACL_GET_FILE + + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ +@@ -404,7 +423,10 @@ file_has_aclinfo (char const *name, struct stat const *sb, + /* On Linux, acl_extended_file is an optimized function: It only + makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for + ACL_TYPE_DEFAULT. */ +- ret = acl_extended_file (name); ++ ret = ((flags & ACL_SYMLINK_FOLLOW ++ ? acl_extended_file ++ : acl_extended_file_nofollow) ++ (name)); + } + else /* FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ + { +@@ -815,7 +837,6 @@ file_has_aclinfo (char const *name, struct stat const *sb, + return acl_nontrivial (count, entries); + } + } +- + # endif + } + #endif +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-minor-ENOMEM-fixes.patch b/backport-file-has-acl-minor-ENOMEM-fixes.patch new file mode 100644 index 0000000000000000000000000000000000000000..5ecfb9386548fbacdb9e9760e2f911e051fcf7e5 --- /dev/null +++ b/backport-file-has-acl-minor-ENOMEM-fixes.patch @@ -0,0 +1,92 @@ +From 407d038993aa2e1894c7312b94d0798d4cd96f02 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 7 Nov 2024 09:37:17 -0800 +Subject: [PATCH] file-has-acl: minor ENOMEM fixes + +* lib/file-has-acl.c: Include limits.h. +(get_aclinfo): Use a saturating add rather than reporting +overflow, in the very unlikely case that the size overflows. +This is simpler and is good enough here. +Also, defend against implausible system that fails with ERANGE +even when the buffer size is SSIZE_MAX. +(file_has_aclinfo): Since we already assume elsewhere that +malloc sets errno, rely on that here too. +* modules/file-has-acl (Depends-on): Add limits.h. +Also add malloc-posix, since we rely on malloc errno. + + +--- + lib/file-has-acl.c | 23 ++++++----------------- + 3 files changed, 18 insertions(+), 17 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index a4367933..980c2024 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -28,6 +28,7 @@ + #include "acl.h" + + #include ++#include + + #include "acl-internal.h" + #include "attribute.h" +@@ -125,7 +126,7 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + if (0 < ai->size) + break; + ai->u.err = ai->size < 0 ? errno : 0; +- if (! (ai->size < 0 && ai->u.err == ERANGE)) ++ if (! (ai->size < 0 && ai->u.err == ERANGE && acl_alloc < SSIZE_MAX)) + break; + + /* The buffer was too small. Find how large it should have been. */ +@@ -146,10 +147,7 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + ai->buf = ai->u.__gl_acl_ch; + } + if (ckd_add (&acl_alloc, acl_alloc, acl_alloc >> 1)) +- { +- ai->u.err = ENOMEM; +- break; +- } ++ acl_alloc = SSIZE_MAX; + if (acl_alloc < size) + acl_alloc = size; + if (SIZE_MAX < acl_alloc) +@@ -543,10 +541,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + entries = malloced = + (aclent_t *) malloc (alloc * sizeof (aclent_t)); + if (entries == NULL) +- { +- errno = ENOMEM; +- return -1; +- } ++ return -1; + continue; + } + break; +@@ -616,10 +611,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + alloc = 2 * alloc; /* <= alloc_max */ + entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t)); + if (entries == NULL) +- { +- errno = ENOMEM; +- return -1; +- } ++ return -1; + continue; + } + break; +@@ -773,10 +765,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + free (acl); + acl = malloc (aclsize); + if (acl == NULL) +- { +- errno = ENOMEM; +- return -1; +- } ++ return -1; + } + + if (type.u64 == ACL_AIXC) +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-minor-refactor-of-acl_get_link_np-fix.patch b/backport-file-has-acl-minor-refactor-of-acl_get_link_np-fix.patch new file mode 100644 index 0000000000000000000000000000000000000000..f7abb0d3e8bb96f49b5190d403737e19b63f0be9 --- /dev/null +++ b/backport-file-has-acl-minor-refactor-of-acl_get_link_np-fix.patch @@ -0,0 +1,41 @@ +From 9dac7a63cb074ab459405cadc1b135b11af3e71b Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 11 Nov 2024 07:52:32 -0800 +Subject: [PATCH] file-has-acl: minor refactor of acl_get_link_np fix +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* lib/file-has-acl.c (file_has_aclinfo): Redo to avoid ‘else #endif’. + + +--- + lib/file-has-acl.c | 12 ++++++------ + 2 files changed, 9 insertions(+), 6 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 2cbc20b3..1cd97954 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -429,13 +429,13 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + else + ret = -1; + # else /* FreeBSD, NetBSD >= 10, IRIX, Tru64, Cygwin >= 2.5 */ +- acl_t acl; +-# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10 */ +- if (!(flags & ACL_SYMLINK_FOLLOW)) +- acl = acl_get_link_np (name, ACL_TYPE_ACCESS); +- else ++# if !HAVE_ACL_GET_LINK_NP /* IRIX, Tru64, Cygwin >= 2.5 */ ++# define acl_get_link_np acl_get_file + # endif +- acl = acl_get_file (name, ACL_TYPE_ACCESS); ++ acl_t acl = ((flags & AC_SYMLINK_FOLLOW ++ ? acl_get_file ++ : acl_get_link_np) ++ (name, ACL_TYPE_ACCESS)); + if (acl) + { + ret = acl_access_nontrivial (acl); +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-new-function-file_has_aclinfo.patch b/backport-file-has-acl-new-function-file_has_aclinfo.patch new file mode 100644 index 0000000000000000000000000000000000000000..c74707192db2a52447135c4af3ad15f911ac64f2 --- /dev/null +++ b/backport-file-has-acl-new-function-file_has_aclinfo.patch @@ -0,0 +1,712 @@ +From ccc26add4e1dca76e7a3ded465c94db4064bc20d Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sun, 29 Sep 2024 16:13:53 -0700 +Subject: [PATCH] file-has-acl: new function file_has_aclinfo +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is for GNU coreutils, whose ‘ls’ can be made faster with the +new function because it means (for example) that ‘ls’ needn’t call +both listxattr and lgetxattr in the common case where the file has +no extended attributes. +* lib/acl.h (ACL_SYMLINK_FOLLOW): New constant. +(struct aclinfo): New type. +* lib/file-has-acl.c (UNKNOWN_SECURITY_CONTEXT): New constant. +(USE_LINUX_XATTR): New macro. When set: +Include stdint.h (for uint32_t). If also USE_SELINUX_SELINUX_H, +include selinux/selinux.h (for getfilecon). +If HAVE_SMACK, include , otherwise provide substitutes +for smack_smackfs_path and smack_new_label_from_path. +(is_smack_enabled): New function. +(aclinfo_has_xattr): Rename from have_xattr and make it extern. +Use struct aclinfo const * arg instead of char const * and ssize_t. +Work even if ai->size < 0. Supply a no-op macro on platforms +lacking xattr. +(get_aclinfo): New static function, much of it taken from +coreutils/src/ls.c. +(file_has_aclinfo): New function, which generalizes file_has_acl +to provide more info. Its body is taken from the old file_has_acl +with new stuff from ls.c. +(file_has_acl): Use it. +(aclinfo_scontext_free, aclinfo_free): Nwew functions, if not +already no-op macros. +* m4/acl.m4 (gl_FUNC_ACL_ARG): Add --without-libsmack option. +(gl_FILE_HAS_ACL): Check for libsmack, selinux. +* m4/selinux-selinux-h.m4 (gl_CHECK_HEADER_SELINUX_SELINUX_H): +New macro, for use by file-has-acl. Rename HAVE_SELINUX_SELINUX_H +to USE_SELINUX_SELINUX_H to avoid confusion as to whether we have +; all uses changed. +(gl_HEADERS_SELINUX_SELINUX_H): Use the new macro. +* modules/file-has-acl (Files): Add m4/selinux-selinux-h.m4. +(Depends-on): Add errno, ssize_t. +* tests/test-file-has-acl.c (main): Add a little test for +file_has_aclinfo. + + +--- + Makefile.in | 2 +- + gnulib-tests/test-file-has-acl.c | 11 ++ + lib/acl.h | 54 ++++++ + lib/file-has-acl.c | 310 +++++++++++++++++++++++-------- + lib/gnulib.mk | 2 +- + lib/se-selinux.in.h | 2 +- + m4/acl.m4 | 39 +++- + m4/selinux-selinux-h.m4 | 34 ++-- + 8 files changed, 357 insertions(+), 97 deletions(-) + +diff --git a/Makefile.in b/Makefile.in +index 873a127..2ed8d00 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -25953,11 +25953,11 @@ lib/selinux/selinux.h: lib/se-selinux.in.h $(top_builddir)/config.status + $(AM_V_GEN)$(MKDIR_P) 'lib/selinux' + $(AM_V_at)$(SED_HEADER_STDOUT) \ + -e 's|@''GUARD_PREFIX''@|GL|g' \ +- -e 's/@''HAVE_SELINUX_SELINUX_H''@/$(HAVE_SELINUX_SELINUX_H)/g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_SELINUX_SELINUX_H''@|$(NEXT_SELINUX_SELINUX_H)|g' \ ++ -e 's/@''USE_SELINUX_SELINUX_H''@/$(USE_SELINUX_SELINUX_H)/g' \ + $(top_srcdir)/lib/se-selinux.in.h > $@-t + $(AM_V_at)mv $@-t $@ + @GL_GENERATE_SELINUX_CONTEXT_H_TRUE@lib/selinux/context.h: lib/se-context.in.h $(top_builddir)/config.status +diff --git a/gnulib-tests/test-file-has-acl.c b/gnulib-tests/test-file-has-acl.c +index 79e45cf..912d6de 100644 +--- a/gnulib-tests/test-file-has-acl.c ++++ b/gnulib-tests/test-file-has-acl.c +@@ -22,6 +22,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -64,6 +65,16 @@ main (int argc, char *argv[]) + fprintf (stderr, "could not access the ACL of file \"%s\"\n", file); + exit (EXIT_FAILURE); + } ++ ++ struct aclinfo ai; ++ int reti = file_has_aclinfo (file, &statbuf, &ai, 0); ++ if (reti != ret) ++ { ++ fprintf (stderr, "file_has_aclinfo failed for \"%s\"\n", file); ++ exit (EXIT_FAILURE); ++ } ++ aclinfo_free (&ai); ++ + printf ("%s\n", ret ? "yes" : "no"); + } + #else +diff --git a/lib/acl.h b/lib/acl.h +index 5f17512..e2d8640 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -28,8 +28,62 @@ + #include + #include + ++/* Follow symlinks when getting an ACL. */ ++enum { ACL_SYMLINK_FOLLOW = 1 }; ++ ++/* Information about an ACL. */ ++struct aclinfo ++{ ++ /* If 'size' is nonnegative, a buffer holding the concatenation ++ of extended attribute names, each terminated by NUL ++ (either u.__gl_acl_ch, or heap-allocated). */ ++ char *buf; ++ ++ /* The number of useful bytes at the start of buf, counting trailing NULs. ++ If negative, there was an error in getting the ACL info, ++ and u.err is the corresponding errno. */ ++ ssize_t size; ++ ++ /* The allocated size of buf. This is sizeof u.__gl_acl_ch if the ++ buffer is not heap-allocated, and is larger otherwise. ++ For internal use only. */ ++ ssize_t __gl_acl_alloc; ++ ++ /* Security context string. Do not modify its contents. */ ++ char *scontext; ++ /* Security context errno value. It is zero if there was no ++ error getting the security context. When nonzero, scontext is "?". */ ++ int scontext_err; ++ ++ union ++ { ++ /* An errno value, when there was an error getting the ACL info. */ ++ int err; ++ ++ /* A small array of char, big enough for most listxattr results. ++ The size is somewhat arbitrary. For internal use only. */ ++ char __gl_acl_ch[152]; ++ } u; ++}; ++ + bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST; + int file_has_acl (char const *, struct stat const *); ++int file_has_aclinfo (char const *, struct stat const *, struct aclinfo *, int); ++ ++#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR ++bool aclinfo_has_xattr (struct aclinfo const *, char const *) ++ _GL_ATTRIBUTE_PURE; ++void aclinfo_free (struct aclinfo *); ++#else ++# define aclinfo_has_xattr(ai, xattr) false ++# define aclinfo_free(ai) ((void) 0) ++#endif ++#if (USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR \ ++ && (HAVE_SMACK || USE_SELINUX_SELINUX_H)) ++void aclinfo_scontext_free (char *); ++#else ++# define aclinfo_scontext_free(s) ((void) 0) ++#endif + + int qset_acl (char const *, int, mode_t); + int xset_acl (char const *, int, mode_t); +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 3eeaf9c..3e31a5a 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -31,8 +31,16 @@ + #include "attribute.h" + #include "minmax.h" + +-#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR ++static char const UNKNOWN_SECURITY_CONTEXT[] = "?"; ++ ++#define USE_LINUX_XATTR (USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR) ++ ++#if USE_LINUX_XATTR ++# if USE_SELINUX_SELINUX_H ++# include ++# endif + # include ++# include + # include + # include + # include +@@ -47,26 +55,185 @@ + # define XATTR_NAME_POSIX_ACL_DEFAULT "system.posix_acl_default" + # endif + ++# ifdef HAVE_SMACK ++# include ++# else ++static char const * ++smack_smackfs_path (void) ++{ ++ return NULL; ++} ++static ssize_t ++smack_new_label_from_path (MAYBE_UNUSED const char *path, ++ MAYBE_UNUSED const char *xattr, ++ MAYBE_UNUSED int follow, MAYBE_UNUSED char **label) ++{ ++ return -1; ++} ++# endif ++static bool ++is_smack_enabled (void) ++{ ++ return !!smack_smackfs_path (); ++} ++ + enum { + /* ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000, */ + ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001, + ACE4_IDENTIFIER_GROUP = 0x00000040 + }; + +-/* Return true if ATTR is in the set represented by the NUL-terminated +- strings in LISTBUF, which is of size LISTSIZE. */ ++/* Does AI's xattr set contain XATTR? */ + +-ATTRIBUTE_PURE static bool +-have_xattr (char const *attr, char const *listbuf, ssize_t listsize) ++bool ++aclinfo_has_xattr (struct aclinfo const *ai, char const *xattr) + { +- char const *blim = listbuf + listsize; +- for (char const *b = listbuf; b < blim; b += strlen (b) + 1) +- for (char const *a = attr; *a == *b; a++, b++) +- if (!*a) +- return true; ++ if (0 < ai->size) ++ { ++ char const *blim = ai->buf + ai->size; ++ for (char const *b = ai->buf; b < blim; b += strlen (b) + 1) ++ for (char const *a = xattr; *a == *b; a++, b++) ++ if (!*a) ++ return true; ++ } + return false; + } + ++/* Get attributes of the file NAME into AI. ++ If FLAGS & ACL_SYMLINK_FOLLOW, follow symbolic links. */ ++static void ++get_aclinfo (char const *name, struct aclinfo *ai, int flags) ++{ ++ int scontext_err = ENOTSUP; ++ ai->buf = ai->u.__gl_acl_ch; ++ ai->__gl_acl_alloc = sizeof ai->u.__gl_acl_ch; ++ ++ ssize_t (*lsxattr) (char const *, char *, size_t) ++ = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr); ++ while (true) ++ { ++ ai->size = lsxattr (name, ai->buf, ai->__gl_acl_alloc); ++ if (0 < ai->size) ++ break; ++ ai->u.err = ai->size < 0 ? errno : 0; ++ if (! (ai->size < 0 && ai->u.err == ERANGE)) ++ break; ++ ++ /* The buffer was too small. Find how large it should have been. */ ++ ssize_t size = lsxattr (name, NULL, 0); ++ if (size <= 0) ++ { ++ ai->size = size; ++ ai->u.err = size < 0 ? errno : 0; ++ break; ++ } ++ ++ /* Grow allocation to at least 'size'. Grow it by a nontrivial ++ amount, to defend against denial of service by an adversary ++ that fiddles with ACLs. */ ++ ssize_t larger_alloc; ++ if (ckd_add (&larger_alloc, ai->__gl_acl_alloc, ai->__gl_acl_alloc >> 1)) ++ { ++ ai->u.err = ENOMEM; ++ break; ++ } ++ if (ai->buf != ai->u.__gl_acl_ch) ++ { ++ free (ai->buf); ++ ai->buf = ai->u.__gl_acl_ch; ++ ai->__gl_acl_alloc = sizeof ai->u.__gl_acl_ch; ++ } ++ ssize_t newalloc = MAX (size, larger_alloc); ++ if (SIZE_MAX < newalloc) ++ { ++ ai->u.err = ENOMEM; ++ break; ++ } ++ char *newbuf = malloc (newalloc); ++ if (!newbuf) ++ { ++ ai->u.err = errno; ++ break; ++ } ++ ai->buf = newbuf; ++ ai->__gl_acl_alloc = newalloc; ++ } ++ ++ if (0 < ai->size) ++ { ++ if (is_smack_enabled ()) ++ { ++ if (aclinfo_has_xattr (ai, XATTR_NAME_SMACK)) ++ { ++ ssize_t r = smack_new_label_from_path (name, "security.SMACK64", ++ flags & ACL_SYMLINK_FOLLOW, ++ &ai->scontext); ++ scontext_err = r < 0 ? errno : 0; ++ } ++ } ++ else ++ { ++# if USE_SELINUX_SELINUX_H ++ if (aclinfo_has_xattr (ai, XATTR_NAME_SELINUX)) ++ { ++ ssize_t r = ++ ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon) ++ (name, &ai->scontext)); ++ scontext_err = r < 0 ? errno : 0; ++# ifndef SE_SELINUX_INLINE ++ /* Gnulib's selinux-h module is not in use, so getfilecon and ++ lgetfilecon can misbehave, be it via an old version of ++ libselinux where these would return 0 and set the result ++ context to NULL, or via a modern kernel+lib operating on a ++ file from a disk whose attributes were set by a kernel from ++ around 2006. In that latter case, the functions return a ++ length of 10 for the "unlabeled" context. Map both failures ++ to a return value of -1, and set errno to ENOTSUP in the ++ first case, and ENODATA in the latter. */ ++ if (r == 0) ++ scontext_err = ENOTSUP; ++ if (r == 10 && memcmp (ai->scontext, "unlabeled", 10) == 0) ++ { ++ freecon (ai->scontext); ++ scontext_err = ENODATA; ++ } ++# endif ++ } ++# endif ++ } ++ } ++ ai->scontext_err = scontext_err; ++ if (scontext_err) ++ ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT; ++} ++ ++# ifndef aclinfo_scontext_free ++/* Free the pointer that file_has_aclinfo put into scontext. ++ However, do nothing if the argument is a null pointer; ++ This lets the caller replace the scontext member with a null pointer if it ++ is willing to own the member and call this function later. */ ++void ++aclinfo_scontext_free (char *scontext) ++{ ++ if (scontext != UNKNOWN_SECURITY_CONTEXT) ++ { ++ if (is_smack_enabled ()) ++ free (scontext); ++ else if (scontext) ++ freecon (scontext); ++ } ++} ++# endif ++ ++/* Free AI's heap storage. */ ++void ++aclinfo_free (struct aclinfo *ai) ++{ ++ if (ai->buf != ai->u.__gl_acl_ch) ++ free (ai->buf); ++ aclinfo_scontext_free (ai->scontext); ++} ++ + /* Return 1 if given ACL in XDR format is non-trivial, 0 if it is trivial. + -1 upon failure to determine it. Possibly change errno. Assume that + the ACL is valid, except avoid undefined behavior even if invalid. +@@ -151,86 +318,50 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + and -1 (setting errno) on error. Note callers can determine + if ACLs are not supported as errno is set in that case also. + SB must be set to the stat buffer of NAME, +- obtained through stat() or lstat(). */ +- ++ obtained through stat() or lstat(). ++ Set *AI to the ACL info if available. ++ If FLAGS & AT_SYMLINK_FOLLOW, SB was gotten via stat and follow symlinks; ++ otherwise, SB was gotten vi lstat and do not follow them. */ + int +-file_has_acl (char const *name, struct stat const *sb) ++file_has_aclinfo (char const *name, struct stat const *sb, ++ struct aclinfo *ai, int flags) + { +-#if USE_ACL +- if (! S_ISLNK (sb->st_mode)) +- { +- +-# if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR +- int initial_errno = errno; +- +- /* The max length of a trivial NFSv4 ACL is 6 words for owner, +- 6 for group, 7 for everyone, all times 2 because there are +- both allow and deny ACEs. There are 6 words for owner +- because of type, flag, mask, wholen, "OWNER@"+pad and +- similarly for group; everyone is another word to hold +- "EVERYONE@". */ +- typedef uint32_t trivial_NFSv4_xattr_buf[2 * (6 + 6 + 7)]; +- +- /* A buffer large enough to hold any trivial NFSv4 ACL, +- and also useful as a small array of char. */ +- union { +- trivial_NFSv4_xattr_buf xattr; +- char ch[sizeof (trivial_NFSv4_xattr_buf)]; +- } stackbuf; +- +- char *listbuf = stackbuf.ch; +- ssize_t listbufsize = sizeof stackbuf.ch; +- char *heapbuf = NULL; +- ssize_t listsize; +- +- /* Use listxattr first, as this means just one syscall in the +- typical case where the file lacks an ACL. Try stackbuf +- first, falling back on malloc if stackbuf is too small. */ +- while ((listsize = listxattr (name, listbuf, listbufsize)) < 0 +- && errno == ERANGE) +- { +- free (heapbuf); +- ssize_t newsize = listxattr (name, NULL, 0); +- if (newsize <= 0) +- return newsize; +- +- /* Grow LISTBUFSIZE to at least NEWSIZE. Grow it by a +- nontrivial amount too, to defend against denial of +- service by an adversary that fiddles with ACLs. */ +- bool overflow = ckd_add (&listbufsize, listbufsize, listbufsize >> 1); +- listbufsize = MAX (listbufsize, newsize); +- if (overflow || SIZE_MAX < listbufsize) +- { +- errno = ENOMEM; +- return -1; +- } +- +- listbuf = heapbuf = malloc (listbufsize); +- if (!listbuf) +- return -1; +- } ++#if USE_LINUX_XATTR ++ int initial_errno = errno; ++ get_aclinfo (name, ai, flags); + ++ if (ai->size <= 0) ++ { ++ errno = ai->size < 0 ? ai->u.err : initial_errno; ++ return ai->size; ++ } ++ else ++ { + /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs, + but if it has an NFSv4 ACL that's the one that matters. + In earlier Fedora the two types of ACLs were mutually exclusive. + Attempt to work correctly on both kinds of systems. */ +- bool nfsv4_acl +- = 0 < listsize && have_xattr (XATTR_NAME_NFSV4_ACL, listbuf, listsize); ++ bool nfsv4_acl = aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL); + int ret +- = (listsize <= 0 ? listsize ++ = (ai->size <= 0 ? ai->size + : (nfsv4_acl +- || have_xattr (XATTR_NAME_POSIX_ACL_ACCESS, listbuf, listsize) ++ || aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) + || (S_ISDIR (sb->st_mode) +- && have_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, +- listbuf, listsize)))); +- free (heapbuf); ++ && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT)))); + + /* If there is an NFSv4 ACL, follow up with a getxattr syscall + to see whether the NFSv4 ACL is nontrivial. */ + if (nfsv4_acl) + { +- ret = getxattr (name, XATTR_NAME_NFSV4_ACL, +- stackbuf.xattr, sizeof stackbuf.xattr); ++ /* A buffer large enough to hold any trivial NFSv4 ACL. ++ The max length of a trivial NFSv4 ACL is 6 words for owner, ++ 6 for group, 7 for everyone, all times 2 because there are both ++ allow and deny ACEs. There are 6 words for owner because of ++ type, flag, mask, wholen, "OWNER@"+pad and similarly for group; ++ everyone is another word to hold "EVERYONE@". */ ++ uint32_t buf[2 * (6 + 6 + 7)]; ++ ++ ret = getxattr (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf); + if (ret < 0) + switch (errno) + { +@@ -240,7 +371,7 @@ file_has_acl (char const *name, struct stat const *sb) + else + { + /* It looks like a trivial ACL, but investigate further. */ +- ret = acl_nfs4_nontrivial (stackbuf.xattr, ret); ++ ret = acl_nfs4_nontrivial (buf, ret); + if (ret < 0) + { + errno = EINVAL; +@@ -252,8 +383,18 @@ file_has_acl (char const *name, struct stat const *sb) + if (ret < 0) + return - acl_errno_valid (errno); + return ret; ++ } ++#else ++ ai->size = -1; ++ ai->u.err = ENOTSUP; ++ ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT; ++ ai->scontext_err = ENOTSUP; ++#endif + +-# elif HAVE_ACL_GET_FILE ++#if USE_ACL ++ if (! S_ISLNK (sb->st_mode)) ++ { ++# if HAVE_ACL_GET_FILE + + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ +@@ -682,3 +823,18 @@ file_has_acl (char const *name, struct stat const *sb) + + return 0; + } ++ ++/* Return 1 if NAME has a nontrivial access control list, ++ 0 if ACLs are not supported, or if NAME has no or only a base ACL, ++ and -1 (setting errno) on error. Note callers can determine ++ if ACLs are not supported as errno is set in that case also. ++ SB must be set to the stat buffer of NAME, ++ obtained through stat() or lstat(). */ ++int ++file_has_acl (char const *name, struct stat const *sb) ++{ ++ struct aclinfo ai; ++ int r = file_has_aclinfo (name, sb, &ai, 0); ++ aclinfo_free (&ai); ++ return r; ++} +diff --git a/lib/gnulib.mk b/lib/gnulib.mk +index 3b5c372..93d3252 100644 +--- a/lib/gnulib.mk ++++ b/lib/gnulib.mk +@@ -4564,11 +4564,11 @@ lib/selinux/selinux.h: lib/se-selinux.in.h $(top_builddir)/config.status + $(AM_V_GEN)$(MKDIR_P) '%reldir%/selinux' + $(AM_V_at)$(SED_HEADER_STDOUT) \ + -e 's|@''GUARD_PREFIX''@|GL|g' \ +- -e 's/@''HAVE_SELINUX_SELINUX_H''@/$(HAVE_SELINUX_SELINUX_H)/g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_SELINUX_SELINUX_H''@|$(NEXT_SELINUX_SELINUX_H)|g' \ ++ -e 's/@''USE_SELINUX_SELINUX_H''@/$(USE_SELINUX_SELINUX_H)/g' \ + $(top_srcdir)/lib/se-selinux.in.h > $@-t + $(AM_V_at)mv $@-t $@ + MOSTLYCLEANFILES += lib/selinux/selinux.h lib/selinux/selinux.h-t +diff --git a/lib/se-selinux.in.h b/lib/se-selinux.in.h +index baf2154..4eab05a 100644 +--- a/lib/se-selinux.in.h ++++ b/lib/se-selinux.in.h +@@ -19,7 +19,7 @@ + #endif + @PRAGMA_COLUMNS@ + +-#if @HAVE_SELINUX_SELINUX_H@ ++#if @USE_SELINUX_SELINUX_H@ + + #@INCLUDE_NEXT@ @NEXT_SELINUX_SELINUX_H@ + +diff --git a/m4/acl.m4 b/m4/acl.m4 +index 2492582..bf988f6 100644 +--- a/m4/acl.m4 ++++ b/m4/acl.m4 +@@ -1,5 +1,5 @@ + # acl.m4 - check for access control list (ACL) primitives +-# serial 31 ++# serial 32 + + # Copyright (C) 2002, 2004-2023 Free Software Foundation, Inc. + # This file is free software; the Free Software Foundation +@@ -14,9 +14,12 @@ AC_DEFUN([gl_FUNC_ACL_ARG], + AC_ARG_ENABLE([acl], + AS_HELP_STRING([[--disable-acl]], [do not support ACLs]), + , [enable_acl=auto]) ++ AC_ARG_WITH([libsmack], ++ [AS_HELP_STRING([--without-libsmack], ++ [do not use libsmack, even on systems that have it])] ++ [], [with_libsmack=maybe]) + ]) + +- + AC_DEFUN_ONCE([gl_FUNC_ACL], + [ + AC_REQUIRE([gl_FUNC_ACL_ARG]) +@@ -188,9 +191,35 @@ AC_DEFUN([gl_FILE_HAS_ACL], + AC_CHECK_HEADERS_ONCE([linux/xattr.h]) + AC_CHECK_FUNCS_ONCE([listxattr]) + FILE_HAS_ACL_LIB= +- AS_CASE([$enable_acl,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr], +- [no,*,*], [], +- [*,yes,yes], [], ++ ++ gl_file_has_acl_uses_smack=no ++ AS_CASE([$enable_acl,$with_libsmack,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr], ++ [no,* | *,no,*], [], ++ [*,*,yes,yes], ++ [AC_CHECK_HEADER([sys/smack.h], ++ [gl_saved_LIBS=$LIBS ++ AC_SEARCH_LIBS([smack_new_label_from_path], [smack], ++ [AC_DEFINE([HAVE_SMACK], [1], ++ [Define to 1 if libsmack is usable.]) ++ AS_CASE([$ac_cv_search_smack_new_label_from_path], ++ ["none required"], [], ++ [FILE_HAS_ACL_LIB=$ac_cv_search_new_label_from_path]) ++ gl_file_has_acl_uses_smack=yes], ++ [AS_CASE([$with_libsmack], ++ [yes], [AC_MSG_ERROR([libsmack not found or unusable])])]) ++ LIBS=$gl_saved_LIBS])]) ++ ++ gl_file_has_acl_uses_selinux=no ++ AS_CASE([$enable_acl,$with_selinux,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr], ++ [no,* | *,no,*], [], ++ [*,*,yes,yes], ++ [gl_CHECK_HEADER_SELINUX_SELINUX_H ++ AS_IF([test "$USE_SELINUX_SELINUX_H" -ne 0 ], ++ [FILE_HAS_ACL_LIB="$FILE_HAS_ACL_LIB $LIB_SELINUX" ++ gl_file_has_acl_uses_selinux=yes])]) ++ ++ AS_CASE([$enable_acl,$gl_file_has_acl_uses_selinux,$gl_file_has_acl_uses_smack], ++ [no,* | *,yes,* | *,yes], [], + [*], + [dnl Set gl_need_lib_has_acl to a nonempty value, so that any + dnl later gl_FUNC_ACL call will set FILE_HAS_ACL_LIB=$LIB_ACL. +diff --git a/m4/selinux-selinux-h.m4 b/m4/selinux-selinux-h.m4 +index 2c943f1..c85a4ed 100644 +--- a/m4/selinux-selinux-h.m4 ++++ b/m4/selinux-selinux-h.m4 +@@ -1,4 +1,4 @@ +-# serial 7 -*- Autoconf -*- ++# serial 9 -*- Autoconf -*- + # Copyright (C) 2006-2007, 2009-2023 Free Software Foundation, Inc. + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -11,16 +11,8 @@ + + AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H], + [ +- AC_REQUIRE([gl_LIBSELINUX]) ++ AC_REQUIRE([gl_CHECK_HEADER_SELINUX_SELINUX_H]) + if test "$with_selinux" != no; then +- AC_CHECK_HEADERS([selinux/selinux.h]) +- +- if test $ac_cv_header_selinux_selinux_h = yes; then +- HAVE_SELINUX_SELINUX_H=1 +- else +- HAVE_SELINUX_SELINUX_H=0 +- fi +- + if test "$ac_cv_header_selinux_selinux_h" = yes; then + # We do have , so do compile getfilecon.c + # and arrange to use its wrappers. +@@ -32,6 +24,22 @@ AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H], + AC_DEFINE([fgetfilecon], [rpl_fgetfilecon], + [Always use our fgetfilecon wrapper.]) + fi ++ fi ++]) ++ ++# Check for , if necessary. ++ ++AC_DEFUN([gl_CHECK_HEADER_SELINUX_SELINUX_H], ++[ ++ AC_REQUIRE([gl_LIBSELINUX]) ++ if test "$with_selinux" != no; then ++ AC_CHECK_HEADERS_ONCE([selinux/selinux.h]) ++ ++ if test $ac_cv_header_selinux_selinux_h = yes; then ++ USE_SELINUX_SELINUX_H=1 ++ else ++ USE_SELINUX_SELINUX_H=0 ++ fi + + case "$ac_cv_search_setfilecon:$ac_cv_header_selinux_selinux_h" in + no:*) # already warned +@@ -43,9 +51,11 @@ AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H], + else + # Do as if does not exist, even if + # AC_CHECK_HEADERS_ONCE has already determined that it exists. +- HAVE_SELINUX_SELINUX_H=0 ++ USE_SELINUX_SELINUX_H=0 + fi +- AC_SUBST([HAVE_SELINUX_SELINUX_H]) ++ AC_SUBST([USE_SELINUX_SELINUX_H]) ++ AC_DEFINE_UNQUOTED([USE_SELINUX_SELINUX_H], [$USE_SELINUX_SELINUX_H], ++ [Define to 1 if should be used, to 0 otherwise.])]) + ]) + + AC_DEFUN([gl_LIBSELINUX], +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-no-need-for-struct-stat.patch b/backport-file-has-acl-no-need-for-struct-stat.patch new file mode 100644 index 0000000000000000000000000000000000000000..079f98a12dc2a620737d8c0b29b12344d5a5ac71 --- /dev/null +++ b/backport-file-has-acl-no-need-for-struct-stat.patch @@ -0,0 +1,256 @@ +From 8682f4d3c2aa0daff2c39935f4a3938d3db108d2 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 2 Oct 2024 17:40:34 -0700 +Subject: [PATCH] file-has-acl: no need for struct stat +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Change the API of the new file_has_aclinfo function so that it no +longer needs a struct stat *. In some cases this can help GNU ls +avoid an unnecessary ‘stat’ call for each file it lists, which +can be a significant win. +* lib/acl.h (ACL_SYMLINK_NOFOLLOW): Change from 1 to UCHAR_MAX+1 +so that it can be ORed with any d_type value. +* lib/file-has-acl.c: Include dirent.h, for the DT_* macros. +Check that ACL_SYMLINK_NOFOLLOW is outside unsigned char range. +(file_has_aclinfo): Change API so that struct stat is no longer +needed. Instead, the file type (if known) is passed as part of +the flags. All callers changed. Simplify NFSv4 code. +* modules/file-has-acl (Depends-on): Add assert-h for static_assert, +and dirent for DT_* macros. +* tests/test-file-has-acl.c: Include dirent.h. +(main): Adjust to file_has_aclinfo API change. +Briefly test ACL_SYMLINK_FOLLOW. + + +--- + lib/acl.h | 7 +-- + lib/file-has-acl.c | 95 ++++++++++++++++++++------------------- + gnulib-tests/test-file-has-acl.c | 13 +++++- + 6 files changed, 88 insertions(+), 50 deletions(-) + +diff --git a/lib/acl.h b/lib/acl.h +index 1a627323..29ec2496 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -32,8 +32,9 @@ + #include + #include + +-/* Follow symlinks when getting an ACL. */ +-enum { ACL_SYMLINK_FOLLOW = 1 }; ++/* Follow symlinks when getting an ACL. This is greater than any ++ unsigned char so that it can be ORed with any DT_* value. */ ++enum { ACL_SYMLINK_FOLLOW = 1 + (unsigned char) -1 }; + + /* Information about an ACL. */ + struct aclinfo +@@ -75,7 +76,7 @@ struct aclinfo + + bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST; + int file_has_acl (char const *, struct stat const *); +-int file_has_aclinfo (char const *, struct stat const *, struct aclinfo *, int); ++int file_has_aclinfo (char const *restrict, struct aclinfo *restrict, int); + + #if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR + bool aclinfo_has_xattr (struct aclinfo const *, char const *) +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index f29414de..9f421370 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -27,10 +27,15 @@ + + #include "acl.h" + ++#include ++ + #include "acl-internal.h" + #include "attribute.h" + #include "minmax.h" + ++/* Check the assumption that UCHAR_MAX < INT_MAX. */ ++static_assert (ACL_SYMLINK_FOLLOW & ~ (unsigned char) -1); ++ + static char const UNKNOWN_SECURITY_CONTEXT[] = "?"; + + #if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR +@@ -321,16 +326,18 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + 0 if ACLs are not supported, or if NAME has no or only a base ACL, + and -1 (setting errno) on error. Note callers can determine + if ACLs are not supported as errno is set in that case also. +- SB must be set to the stat buffer of NAME, +- obtained through stat() or lstat(). +- Set *AI to the ACL info if available. +- If FLAGS & AT_SYMLINK_FOLLOW, SB was gotten via stat, otherwise lstat. +- Also, if FLAGS & AT_SYMLINK_FOLLOW, follow symlinks when retrieving +- ACL info, otherwise do not follow them if possible. */ ++ Set *AI to ACL info regardless of return value. ++ FLAGS should be a d_type value, optionally ORed with ++ AT_SYMLINK_FOLLOW; if the d_type value is not known, ++ use DT_UNKNOWN though this may be less efficient. ++ If FLAGS & AT_SYMLINK_FOLLOW, follow symlinks when retrieving ACL info; ++ otherwise do not follow them if possible. */ + int +-file_has_aclinfo (char const *name, struct stat const *sb, +- struct aclinfo *ai, int flags) ++file_has_aclinfo (MAYBE_UNUSED char const *restrict name, ++ struct aclinfo *restrict ai, int flags) + { ++ unsigned char d_type = flags & UCHAR_MAX; ++ + /* Symbolic links lack extended attributes and ACLs on all supported + platforms, so don't bother trying to fetch them. If the platform + supports not following symlinks this defends against some races +@@ -338,7 +345,7 @@ file_has_aclinfo (char const *name, struct stat const *sb, + + First, initialize *AI for cases known to be unsupported. */ + +- if (!USE_LINUX_XATTR || S_ISLNK (sb->st_mode)) ++ if (!USE_LINUX_XATTR || d_type == DT_LNK) + { + ai->buf = ai->u.__gl_acl_ch; + ai->size = -1; +@@ -350,7 +357,7 @@ file_has_aclinfo (char const *name, struct stat const *sb, + /* Now set *AI for cases that might be supported, then check for ACLs. */ + + #if USE_ACL +- if (! S_ISLNK (sb->st_mode)) ++ if (d_type != DT_LNK) + { + + # if USE_LINUX_XATTR +@@ -368,45 +375,41 @@ file_has_aclinfo (char const *name, struct stat const *sb, + but if it has an NFSv4 ACL that's the one that matters. + In earlier Fedora the two types of ACLs were mutually exclusive. + Attempt to work correctly on both kinds of systems. */ +- bool nfsv4_acl = aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL); +- int ret +- = (ai->size <= 0 ? ai->size +- : (nfsv4_acl +- || aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) +- || (S_ISDIR (sb->st_mode) +- && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT)))); +- +- /* If there is an NFSv4 ACL, check whether it is nontrivial. */ +- if (nfsv4_acl) +- { +- /* A buffer large enough to hold any trivial NFSv4 ACL. +- The max length of a trivial NFSv4 ACL is 6 words for owner, +- 6 for group, 7 for everyone, all times 2 because there are both +- allow and deny ACEs. There are 6 words for owner because of +- type, flag, mask, wholen, "OWNER@"+pad and similarly for group; +- everyone is another word to hold "EVERYONE@". */ +- uint32_t buf[2 * (6 + 6 + 7)]; +- +- ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) ++ ++ if (!aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL)) ++ return ++ (aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) ++ || ((d_type == DT_DIR || d_type == DT_UNKNOWN) ++ && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))); ++ ++ /* A buffer large enough to hold any trivial NFSv4 ACL. ++ The max length of a trivial NFSv4 ACL is 6 words for owner, ++ 6 for group, 7 for everyone, all times 2 because there are both ++ allow and deny ACEs. There are 6 words for owner because of ++ type, flag, mask, wholen, "OWNER@"+pad and similarly for group; ++ everyone is another word to hold "EVERYONE@". */ ++ uint32_t buf[2 * (6 + 6 + 7)]; ++ ++ int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) + (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf)); ++ if (ret < 0) ++ switch (errno) ++ { ++ case ENODATA: return 0; ++ case ERANGE : return 1; /* ACL must be nontrivial. */ ++ } ++ else ++ { ++ /* It looks like a trivial ACL, but investigate further. */ ++ ret = acl_nfs4_nontrivial (buf, ret); + if (ret < 0) +- switch (errno) +- { +- case ENODATA: return 0; +- case ERANGE : return 1; /* ACL must be nontrivial. */ +- } +- else + { +- /* It looks like a trivial ACL, but investigate further. */ +- ret = acl_nfs4_nontrivial (buf, ret); +- if (ret < 0) +- { +- errno = EINVAL; +- return ret; +- } +- errno = initial_errno; ++ errno = EINVAL; ++ return ret; + } ++ errno = initial_errno; + } ++ + if (ret < 0) + return - acl_errno_valid (errno); + return ret; +@@ -460,7 +463,7 @@ file_has_aclinfo (char const *name, struct stat const *sb, + either both succeed or both fail; it depends on the + file system. Therefore there is no point in making the second + call if the first one already failed. */ +- if (ret == 0 && S_ISDIR (sb->st_mode)) ++ if (ret == 0 && (d_type == DT_DIR || d_type == DT_UNKNOWN)) + { + acl = acl_get_file (name, ACL_TYPE_DEFAULT); + if (acl) +@@ -849,7 +852,7 @@ int + file_has_acl (char const *name, struct stat const *sb) + { + struct aclinfo ai; +- int r = file_has_aclinfo (name, sb, &ai, 0); ++ int r = file_has_aclinfo (name, &ai, IFTODT (sb->st_mode)); + aclinfo_free (&ai); + return r; + } +diff --git a/gnulib-tests/test-file-has-acl.c b/gnulib-tests/test-file-has-acl.c +index 79e80c1a..1cced639 100644 +--- a/gnulib-tests/test-file-has-acl.c ++++ b/gnulib-tests/test-file-has-acl.c +@@ -20,6 +20,7 @@ + + #include "acl.h" + ++#include + #include + #include + #include +@@ -66,7 +67,7 @@ main (int argc, char *argv[]) + } + + struct aclinfo ai; +- int reti = file_has_aclinfo (file, &statbuf, &ai, 0); ++ int reti = file_has_aclinfo (file, &ai, DT_UNKNOWN); + if (reti != ret) + { + fprintf (stderr, "file_has_aclinfo failed for \"%s\"\n", file); +@@ -74,6 +75,16 @@ main (int argc, char *argv[]) + } + aclinfo_free (&ai); + ++ int retj = file_has_aclinfo (file, &ai, ACL_SYMLINK_FOLLOW | DT_UNKNOWN); ++ if (retj != ret) ++ { ++ fprintf (stderr, ++ "file_has_aclinfo with ACL_SYMLINK_FOLLOW failed for \"%s\"\n", ++ file); ++ exit (EXIT_FAILURE); ++ } ++ aclinfo_free (&ai); ++ + printf ("%s\n", ret ? "yes" : "no"); + } + #else +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-port-symlink-code-to-Cygwin.patch b/backport-file-has-acl-port-symlink-code-to-Cygwin.patch new file mode 100644 index 0000000000000000000000000000000000000000..755088797a4a04037a078890217c626cb752f7f2 --- /dev/null +++ b/backport-file-has-acl-port-symlink-code-to-Cygwin.patch @@ -0,0 +1,61 @@ +From 41e7b7e0d159d8ac0eb385964119f350ac9dfc3f Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 7 Apr 2025 01:45:17 -0700 +Subject: [PATCH] file-has-acl: port symlink code to Cygwin + +Problem reported by Corinna Vinschen in: +https://lists.gnu.org/r/bug-gnulib/2025-03/msg00112.html +* lib/file-has-acl.c (acl_get_link_np): New static function, +defined only if needed; include if needed for this. +(HAVE_ACL_GET_LINK_NP): Define this if defining acl_get_link_np. + + +--- + lib/file-has-acl.c | 25 ++++++++++++++++++++++++- + 2 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 179e805b..66b920c1 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -362,6 +362,29 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + } + #endif + ++#if (!USE_LINUX_XATTR && USE_ACL && HAVE_ACL_GET_FD \ ++ && !HAVE_ACL_EXTENDED_FILE && !HAVE_ACL_TYPE_EXTENDED \ ++ && !HAVE_ACL_GET_LINK_NP) ++# include ++# ifdef O_PATH ++ ++/* Like acl_get_file, but do not follow symbolic links. */ ++static acl_t ++acl_get_link_np (char const *name, acl_type_t type) ++{ ++ int fd = open (name, O_PATH | O_NOFOLLOW); ++ if (fd < 0) ++ return NULL; ++ acl_t r = acl_get_fd (fd); ++ int err = errno; ++ close (fd); ++ errno = err; ++ return r; ++} ++# define HAVE_ACL_GET_LINK_NP 1 ++# endif ++#endif ++ + /* Return 1 if NAME has a nontrivial access control list, + 0 if ACLs are not supported, or if NAME has no or only a base ACL, + and -1 (setting errno) on error. Note callers can determine +@@ -467,7 +490,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + ret = -1; + # else /* FreeBSD, NetBSD >= 10, IRIX, Tru64, Cygwin >= 2.5 */ + acl_t (*acl_get_file_or_link) (char const *, acl_type_t) = acl_get_file; +-# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10 */ ++# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10, Cygwin >= 2.5 */ + if (! (flags & ACL_SYMLINK_FOLLOW)) + acl_get_file_or_link = acl_get_link_np; + # endif +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-port-to-Linux-6.12-NFS-listxattr.patch b/backport-file-has-acl-port-to-Linux-6.12-NFS-listxattr.patch new file mode 100644 index 0000000000000000000000000000000000000000..73c379fdef4182b461888623afa893377c78c261 --- /dev/null +++ b/backport-file-has-acl-port-to-Linux-6.12-NFS-listxattr.patch @@ -0,0 +1,53 @@ +From 64ce046c046563bce51e9a5ed4cf2422ee376c8b Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 9 Jan 2025 20:37:13 -0800 +Subject: [PATCH] file-has-acl: port to Linux 6.12 + NFS listxattr +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* lib/file-has-acl.c (get_aclinfo): Try the getxattr-related calls +even if [l]listxattr fails with EACCES. Problem reported by +Pádraig Brady . Also, treat E2BIG +like EACCES. + + +--- + lib/file-has-acl.c | 12 +++++++++--- + 2 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 35dcc19f..5ec6b256 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -176,11 +176,17 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + } + } + +- if (0 < ai->size && flags & ACL_GET_SCONTEXT) ++ /* A security context can exist only if extended attributes do: i.e., ++ [l]listxattr either returned a positive number, or failed with E2BIG, ++ or failed with EACCES which in Linux kernel 6.12 NFS can mean merely ++ that we lack read access. */ ++ if (flags & ACL_GET_SCONTEXT ++ && (0 < ai->size ++ || (ai->size < 0 && (ai->u.err == E2BIG || ai->u.err == EACCES)))) + { + if (is_smack_enabled ()) + { +- if (aclinfo_has_xattr (ai, XATTR_NAME_SMACK)) ++ if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SMACK)) + { + ssize_t r = smack_new_label_from_path (name, "security.SMACK64", + flags & ACL_SYMLINK_FOLLOW, +@@ -191,7 +197,7 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + else + { + # if USE_SELINUX_SELINUX_H +- if (aclinfo_has_xattr (ai, XATTR_NAME_SELINUX)) ++ if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SELINUX)) + { + ssize_t r = + ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon) +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-remove-__gl_acl_alloc-member.patch b/backport-file-has-acl-remove-__gl_acl_alloc-member.patch new file mode 100644 index 0000000000000000000000000000000000000000..4496bff6aea78877bd627a80cde3e1f8e7e502e2 --- /dev/null +++ b/backport-file-has-acl-remove-__gl_acl_alloc-member.patch @@ -0,0 +1,97 @@ +From a44d8b6c280e55873aadc88354a353abc8eac188 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 7 Nov 2024 09:25:43 -0800 +Subject: [PATCH] file-has-acl: remove __gl_acl_alloc member + +It may have been needed in earlier versions of this code, +but it is no longer needed. +* lib/acl.h (struct aclinfo): Remove __gl_acl_alloc. All uses removed. +* lib/file-has-acl.c (get_aclinfo): +Use local, not the removed struct aclinfo slot. + + +--- + lib/acl.h | 5 ----- + lib/file-has-acl.c | 24 +++++++++++------------- + 3 files changed, 20 insertions(+), 18 deletions(-) + +diff --git a/lib/acl.h b/lib/acl.h +index 68a421a0..ca74fe6d 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -49,11 +49,6 @@ struct aclinfo + and u.err is the corresponding errno. */ + ssize_t size; + +- /* The allocated size of buf. This is sizeof u.__gl_acl_ch if the +- buffer is not heap-allocated, and is larger otherwise. +- For internal use only. */ +- ssize_t __gl_acl_alloc; +- + /* Security context string. Do not modify its contents. */ + char *scontext; + /* Security context errno value. It is zero if there was no +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 64291e02..a4367933 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -115,13 +115,13 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + { + int scontext_err = ENOTSUP; + ai->buf = ai->u.__gl_acl_ch; +- ai->__gl_acl_alloc = sizeof ai->u.__gl_acl_ch; ++ ssize_t acl_alloc = sizeof ai->u.__gl_acl_ch; + + ssize_t (*lsxattr) (char const *, char *, size_t) + = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr); + while (true) + { +- ai->size = lsxattr (name, ai->buf, ai->__gl_acl_alloc); ++ ai->size = lsxattr (name, ai->buf, acl_alloc); + if (0 < ai->size) + break; + ai->u.err = ai->size < 0 ? errno : 0; +@@ -140,32 +140,30 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + /* Grow allocation to at least 'size'. Grow it by a nontrivial + amount, to defend against denial of service by an adversary + that fiddles with ACLs. */ +- ssize_t larger_alloc; +- if (ckd_add (&larger_alloc, ai->__gl_acl_alloc, ai->__gl_acl_alloc >> 1)) +- { +- ai->u.err = ENOMEM; +- break; +- } + if (ai->buf != ai->u.__gl_acl_ch) + { + free (ai->buf); + ai->buf = ai->u.__gl_acl_ch; +- ai->__gl_acl_alloc = sizeof ai->u.__gl_acl_ch; + } +- ssize_t newalloc = MAX (size, larger_alloc); +- if (SIZE_MAX < newalloc) ++ if (ckd_add (&acl_alloc, acl_alloc, acl_alloc >> 1)) ++ { ++ ai->u.err = ENOMEM; ++ break; ++ } ++ if (acl_alloc < size) ++ acl_alloc = size; ++ if (SIZE_MAX < acl_alloc) + { + ai->u.err = ENOMEM; + break; + } +- char *newbuf = malloc (newalloc); ++ char *newbuf = malloc (acl_alloc); + if (!newbuf) + { + ai->u.err = errno; + break; + } + ai->buf = newbuf; +- ai->__gl_acl_alloc = newalloc; + } + + if (0 < ai->size) +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-scontext-even-if-disable-acl.patch b/backport-file-has-acl-scontext-even-if-disable-acl.patch new file mode 100644 index 0000000000000000000000000000000000000000..e91d1a574e08af9b11c4a4ec8a65f437443122bc --- /dev/null +++ b/backport-file-has-acl-scontext-even-if-disable-acl.patch @@ -0,0 +1,201 @@ +From 771fe316ce8041663a84fdb38ccc7419e6231e0c Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 11 Nov 2024 07:35:50 -0800 +Subject: [PATCH] file-has-acl: scontext even if --disable-acl +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Be able to get the security context even if configured with +--disable-acl, as security contexts are not ACLs, and the +main reason for --disable-acl was for efficiency with GNU ls -l, +a concern that does not apply to security contexts (which are +needed only with ls -Z). Problem reported by Pádraig Brady +. +* lib/acl.h (ACL_GET_SCONTEXT): New constant. +(aclinfo_free, aclinfo_scontext_free): Declare even if !USE_ACL. +* lib/file-has-acl.c (USE_LINUX_XATTR): No longer false merely +because !USE_ACL, because we need xattr to get scontext. +(get_aclinfo): Support new ACL_GET_SCONTEXT flag. + + +--- + lib/acl.h | 17 ++++++--- + lib/file-has-acl.c | 93 +++++++++++++++++++++++++--------------------- + 3 files changed, 77 insertions(+), 48 deletions(-) + +diff --git a/lib/acl.h b/lib/acl.h +index ca74fe6d..1d52345c 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -32,9 +32,16 @@ + #include + #include + +-/* Follow symlinks when getting an ACL. This is a bitmask that is guaranteed +- not to collide with any DT_* or _GL_DT_* value. */ +-enum { ACL_SYMLINK_FOLLOW = 0x10000 }; ++/* file_has_acl flags guaranteed to not collide with any ++ DT_* or _GL_DT_* value. */ ++enum ++ { ++ /* Get scontext information as well. */ ++ ACL_GET_SCONTEXT = 0x10000, ++ ++ /* Follow symlinks. */ ++ ACL_SYMLINK_FOLLOW = 0x20000, ++ }; + + /* Information about an ACL. */ + struct aclinfo +@@ -73,7 +80,7 @@ bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST; + int file_has_acl (char const *, struct stat const *); + int file_has_aclinfo (char const *restrict, struct aclinfo *restrict, int); + +-#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR ++#if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR + bool aclinfo_has_xattr (struct aclinfo const *, char const *) + _GL_ATTRIBUTE_PURE; + void aclinfo_free (struct aclinfo *); +@@ -81,7 +88,7 @@ void aclinfo_free (struct aclinfo *); + # define aclinfo_has_xattr(ai, xattr) false + # define aclinfo_free(ai) ((void) 0) + #endif +-#if (USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR \ ++#if (HAVE_LINUX_XATTR_H && HAVE_LISTXATTR \ + && (HAVE_SMACK || USE_SELINUX_SELINUX_H)) + void aclinfo_scontext_free (char *); + #else +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 7e05d391..2cbc20b3 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -39,7 +39,7 @@ static_assert (ACL_SYMLINK_FOLLOW & ~ (unsigned char) -1); + + static char const UNKNOWN_SECURITY_CONTEXT[] = "?"; + +-#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR ++#if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR + # define USE_LINUX_XATTR true + #else + # define USE_LINUX_XATTR false +@@ -109,7 +109,8 @@ aclinfo_has_xattr (struct aclinfo const *ai, char const *xattr) + return false; + } + +-/* Get attributes of the file NAME into AI. ++/* Get attributes of the file NAME into AI, if USE_ACL. ++ If FLAGS & ACL_GET_SCONTEXT, also get security context. + If FLAGS & ACL_SYMLINK_FOLLOW, follow symbolic links. */ + static void + get_aclinfo (char const *name, struct aclinfo *ai, int flags) +@@ -118,53 +119,58 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + ai->buf = ai->u.__gl_acl_ch; + ssize_t acl_alloc = sizeof ai->u.__gl_acl_ch; + +- ssize_t (*lsxattr) (char const *, char *, size_t) +- = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr); +- while (true) ++ if (! (USE_ACL || flags & ACL_GET_SCONTEXT)) ++ ai->size = 0; ++ else + { +- ai->size = lsxattr (name, ai->buf, acl_alloc); +- if (0 < ai->size) +- break; +- ai->u.err = ai->size < 0 ? errno : 0; +- if (! (ai->size < 0 && ai->u.err == ERANGE && acl_alloc < SSIZE_MAX)) +- break; +- +- /* The buffer was too small. Find how large it should have been. */ +- ssize_t size = lsxattr (name, NULL, 0); +- if (size <= 0) ++ ssize_t (*lsxattr) (char const *, char *, size_t) ++ = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr); ++ while (true) + { +- ai->size = size; +- ai->u.err = size < 0 ? errno : 0; +- break; +- } ++ ai->size = lsxattr (name, ai->buf, acl_alloc); ++ if (0 < ai->size) ++ break; ++ ai->u.err = ai->size < 0 ? errno : 0; ++ if (! (ai->size < 0 && ai->u.err == ERANGE && acl_alloc < SSIZE_MAX)) ++ break; + +- /* Grow allocation to at least 'size'. Grow it by a nontrivial +- amount, to defend against denial of service by an adversary +- that fiddles with ACLs. */ +- if (ai->buf != ai->u.__gl_acl_ch) +- { +- free (ai->buf); +- ai->buf = ai->u.__gl_acl_ch; +- } +- if (ckd_add (&acl_alloc, acl_alloc, acl_alloc >> 1)) +- acl_alloc = SSIZE_MAX; +- if (acl_alloc < size) +- acl_alloc = size; +- if (SIZE_MAX < acl_alloc) +- { +- ai->u.err = ENOMEM; +- break; +- } +- char *newbuf = malloc (acl_alloc); +- if (!newbuf) +- { +- ai->u.err = errno; +- break; ++ /* The buffer was too small. Find how large it should have been. */ ++ ssize_t size = lsxattr (name, NULL, 0); ++ if (size <= 0) ++ { ++ ai->size = size; ++ ai->u.err = size < 0 ? errno : 0; ++ break; ++ } ++ ++ /* Grow allocation to at least 'size'. Grow it by a nontrivial ++ amount, to defend against denial of service by an adversary ++ that fiddles with ACLs. */ ++ if (ai->buf != ai->u.__gl_acl_ch) ++ { ++ free (ai->buf); ++ ai->buf = ai->u.__gl_acl_ch; ++ } ++ if (ckd_add (&acl_alloc, acl_alloc, acl_alloc >> 1)) ++ acl_alloc = SSIZE_MAX; ++ if (acl_alloc < size) ++ acl_alloc = size; ++ if (SIZE_MAX < acl_alloc) ++ { ++ ai->u.err = ENOMEM; ++ break; ++ } ++ char *newbuf = malloc (acl_alloc); ++ if (!newbuf) ++ { ++ ai->u.err = errno; ++ break; ++ } ++ ai->buf = newbuf; + } +- ai->buf = newbuf; + } + +- if (0 < ai->size) ++ if (0 < ai->size && flags & ACL_GET_SCONTEXT) + { + if (is_smack_enabled ()) + { +@@ -325,6 +331,7 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + Set *AI to ACL info regardless of return value. + FLAGS should be a d_type value, optionally ORed with + - _GL_DT_NOTDIR if it is known that NAME is not a directory, ++ - ACL_GET_SCONTEXT to retrieve extended attributes, + - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link. + If the d_type value is not known, use DT_UNKNOWN though this may be less + efficient. +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-symlinks-can-have-scontext.patch b/backport-file-has-acl-symlinks-can-have-scontext.patch new file mode 100644 index 0000000000000000000000000000000000000000..e834e9794c3816ed2396cf58d47828f2c2318faf --- /dev/null +++ b/backport-file-has-acl-symlinks-can-have-scontext.patch @@ -0,0 +1,421 @@ +From 74b6a2a7386cf8586c1244297d4d87dde123db42 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 9 Nov 2024 14:52:05 -0800 +Subject: [PATCH] file-has-acl: symlinks can have scontext +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Problem reported by Pádraig Brady . +* lib/file-has-acl.c (file_has_aclinfo): +With SELinux, symbolic links can have security contexts, +so omit incorrect optimizations assuming that they lack them. +When ! (flags & AC_SYMLINK_FOLLOW), prefer acl_get_link_np to +acl_get_file on platforms that have both APIs, +as this can avoid some races. + + +--- + lib/file-has-acl.c | 314 ++++++++++++++++++++++----------------------- + 2 files changed, 163 insertions(+), 162 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 980c2024..9a89e2de 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -336,177 +336,165 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + { + unsigned char d_type = flags & UCHAR_MAX; + +- /* Symbolic links lack extended attributes and ACLs on all supported +- platforms, so don't bother trying to fetch them. If the platform +- supports not following symlinks this defends against some races +- and avoids a syscall; otherwise this is essential. +- +- First, initialize *AI for cases known to be unsupported. */ ++#if USE_LINUX_XATTR ++ int initial_errno = errno; ++ get_aclinfo (name, ai, flags); + +- if (!USE_LINUX_XATTR || d_type == DT_LNK) ++ if (ai->size <= 0) + { +- ai->buf = ai->u.__gl_acl_ch; +- ai->size = -1; +- ai->u.err = ENOTSUP; +- ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT; +- ai->scontext_err = ENOTSUP; ++ errno = ai->size < 0 ? ai->u.err : initial_errno; ++ return ai->size; + } + +- /* Now set *AI for cases that might be supported, then check for ACLs. */ ++ /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs, ++ but if it has an NFSv4 ACL that's the one that matters. ++ In earlier Fedora the two types of ACLs were mutually exclusive. ++ Attempt to work correctly on both kinds of systems. */ ++ ++ if (!aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL)) ++ return ++ (aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) ++ || ((d_type == DT_DIR || d_type == DT_UNKNOWN) ++ && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))); ++ ++ /* A buffer large enough to hold any trivial NFSv4 ACL. ++ The max length of a trivial NFSv4 ACL is 6 words for owner, ++ 6 for group, 7 for everyone, all times 2 because there are both ++ allow and deny ACEs. There are 6 words for owner because of ++ type, flag, mask, wholen, "OWNER@"+pad and similarly for group; ++ everyone is another word to hold "EVERYONE@". */ ++ uint32_t buf[2 * (6 + 6 + 7)]; ++ ++ int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) ++ (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf)); ++ if (ret < 0) ++ switch (errno) ++ { ++ case ENODATA: return 0; ++ case ERANGE : return 1; /* ACL must be nontrivial. */ ++ default: return - acl_errno_valid (errno); ++ } + +-#if USE_ACL +- if (d_type != DT_LNK) +- { ++ /* It looks like a trivial ACL, but investigate further. */ ++ ret = acl_nfs4_nontrivial (buf, ret); ++ errno = ret < 0 ? EINVAL : initial_errno; ++ return ret; + +-# if USE_LINUX_XATTR +- int initial_errno = errno; +- get_aclinfo (name, ai, flags); ++#else /* !USE_LINUX_XATTR */ + +- if (ai->size <= 0) +- { +- errno = ai->size < 0 ? ai->u.err : initial_errno; +- return ai->size; +- } +- else +- { +- /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs, +- but if it has an NFSv4 ACL that's the one that matters. +- In earlier Fedora the two types of ACLs were mutually exclusive. +- Attempt to work correctly on both kinds of systems. */ +- +- if (!aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL)) +- return +- (aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS) +- || ((d_type == DT_DIR || d_type == DT_UNKNOWN) +- && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))); +- +- /* A buffer large enough to hold any trivial NFSv4 ACL. +- The max length of a trivial NFSv4 ACL is 6 words for owner, +- 6 for group, 7 for everyone, all times 2 because there are both +- allow and deny ACEs. There are 6 words for owner because of +- type, flag, mask, wholen, "OWNER@"+pad and similarly for group; +- everyone is another word to hold "EVERYONE@". */ +- uint32_t buf[2 * (6 + 6 + 7)]; +- +- int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) +- (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf)); +- if (ret < 0) +- switch (errno) ++ ai->buf = ai->u.__gl_acl_ch; ++ ai->size = -1; ++ ai->u.err = ENOTSUP; ++ ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT; ++ ai->scontext_err = ENOTSUP; ++ ++# if USE_ACL ++# if HAVE_ACL_GET_FILE ++ ++ { ++ /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ ++ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ ++ int ret; ++ ++# if HAVE_ACL_EXTENDED_FILE /* Linux */ ++ /* On Linux, acl_extended_file is an optimized function: It only ++ makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for ++ ACL_TYPE_DEFAULT. */ ++ ret = ((flags & ACL_SYMLINK_FOLLOW ++ ? acl_extended_file ++ : acl_extended_file_nofollow) ++ (name)); ++# elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ ++ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) ++ always return NULL / EINVAL. There is no point in making ++ these two useless calls. The real ACL is retrieved through ++ acl_get_file (name, ACL_TYPE_EXTENDED). */ ++ acl_t acl = ((flags & AC_SYMLINK_FOLLOW ++ ? acl_get_file ++ : acl_get_link_np) ++ (name, ACL_TYPE_EXTENDED)); ++ if (acl) ++ { ++ ret = acl_extended_nontrivial (acl); ++ acl_free (acl); ++ } ++ else ++ ret = -1; ++# else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */ ++ acl_t acl = ((flags & AC_SYMLINK_FOLLOW ++ ? acl_get_file ++ : acl_get_link_np) ++ (name, ACL_TYPE_ACCESS)); ++ if (acl) ++ { ++ ret = acl_access_nontrivial (acl); ++ int saved_errno = errno; ++ acl_free (acl); ++ errno = saved_errno; ++# if HAVE_ACL_FREE_TEXT /* Tru64 */ ++ /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always ++ returns NULL with errno not set. There is no point in ++ making this call. */ ++# else /* FreeBSD, IRIX, Cygwin >= 2.5 */ ++ /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory ++ either both succeed or both fail; it depends on the ++ file system. Therefore there is no point in making the second ++ call if the first one already failed. */ ++ if (ret == 0 ++ && (d_type == DT_DIR ++ || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR)))) ++ { ++ acl = acl_get_file (name, ACL_TYPE_DEFAULT); ++ if (acl) + { +- case ENODATA: return 0; +- case ERANGE : return 1; /* ACL must be nontrivial. */ ++# ifdef __CYGWIN__ /* Cygwin >= 2.5 */ ++ ret = acl_access_nontrivial (acl); ++ saved_errno = errno; ++ acl_free (acl); ++ errno = saved_errno; ++# else ++ ret = (0 < acl_entries (acl)); ++ acl_free (acl); ++# endif + } +- else +- { +- /* It looks like a trivial ACL, but investigate further. */ +- ret = acl_nfs4_nontrivial (buf, ret); +- if (ret < 0) +- { +- errno = EINVAL; +- return ret; +- } +- errno = initial_errno; +- } +- +- if (ret < 0) +- return - acl_errno_valid (errno); +- return ret; +- } +- +-# elif HAVE_ACL_GET_FILE +- +- /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ +- /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ +- int ret; +- +-# if HAVE_ACL_EXTENDED_FILE /* Linux */ +- /* On Linux, acl_extended_file is an optimized function: It only +- makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for +- ACL_TYPE_DEFAULT. */ +- ret = ((flags & ACL_SYMLINK_FOLLOW +- ? acl_extended_file +- : acl_extended_file_nofollow) +- (name)); +-# elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ +- /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) +- and acl_get_file (name, ACL_TYPE_DEFAULT) +- always return NULL / EINVAL. There is no point in making +- these two useless calls. The real ACL is retrieved through +- acl_get_file (name, ACL_TYPE_EXTENDED). */ +- acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED); +- if (acl) +- { +- ret = acl_extended_nontrivial (acl); +- acl_free (acl); +- } +- else +- ret = -1; +-# else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */ +- acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); +- if (acl) +- { +- int saved_errno; +- +- ret = acl_access_nontrivial (acl); +- saved_errno = errno; +- acl_free (acl); +- errno = saved_errno; +-# if HAVE_ACL_FREE_TEXT /* Tru64 */ +- /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always +- returns NULL with errno not set. There is no point in +- making this call. */ +-# else /* FreeBSD, IRIX, Cygwin >= 2.5 */ +- /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS) +- and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory +- either both succeed or both fail; it depends on the +- file system. Therefore there is no point in making the second +- call if the first one already failed. */ +- if (ret == 0 +- && (d_type == DT_DIR +- || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR)))) +- { +- acl = acl_get_file (name, ACL_TYPE_DEFAULT); +- if (acl) +- { +-# ifdef __CYGWIN__ /* Cygwin >= 2.5 */ +- ret = acl_access_nontrivial (acl); +- saved_errno = errno; +- acl_free (acl); +- errno = saved_errno; +-# else +- ret = (0 < acl_entries (acl)); +- acl_free (acl); +-# endif +- } +- else +- { +-# ifdef __CYGWIN__ /* Cygwin >= 2.5 */ +- if (d_type == DT_UNKNOWN) +- ret = 0; +- else ++ else ++ { ++ ret = -1; ++# ifdef __CYGWIN__ /* Cygwin >= 2.5 */ ++ if (d_type == DT_UNKNOWN) ++ ret = 0; ++# endif ++ } ++ } + # endif +- ret = -1; +- } +- } ++ } ++ else ++ ret = -1; + # endif +- } +- else +- ret = -1; +-# endif +- if (ret < 0) +- return - acl_errno_valid (errno); +- return ret; + +-# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */ ++ return ret < 0 ? - acl_errno_valid (errno) : ret; ++ } + +-# if defined ACL_NO_TRIVIAL ++# else /* !HAVE_ACL_GET_FILE */ ++ ++ /* The remaining APIs always follow symlinks and operate on ++ platforms where symlinks do not have ACLs, so skip the APIs if ++ NAME is known to be a symlink. */ ++ if (d_type != DT_LNK) ++ { ++ ++# if HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */ ++ ++# ifdef ACL_NO_TRIVIAL + + /* Solaris 10 (newer version), which has additional API declared in + (acl_t) and implemented in libsec (acl_set, acl_trivial, + acl_fromtext, ...). */ + return acl_trivial (name); + +-# else /* Solaris, Cygwin, general case */ ++# else /* Solaris, Cygwin, general case */ + + /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions + of Unixware. The acl() call returns the access and default ACL both +@@ -579,7 +567,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + free (malloced); + } + +-# ifdef ACE_GETACL ++# ifdef ACE_GETACL + /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 + file systems (whereas the other ones are used in UFS file systems). */ + { +@@ -652,12 +640,12 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + } + free (malloced); + } +-# endif ++# endif + + return 0; +-# endif ++# endif + +-# elif HAVE_GETACL /* HP-UX */ ++# elif HAVE_GETACL /* HP-UX */ + + { + struct acl_entry entries[NACLENTRIES]; +@@ -700,7 +688,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + } + } + +-# if HAVE_ACLV_H /* HP-UX >= 11.11 */ ++# if HAVE_ACLV_H /* HP-UX >= 11.11 */ + + { + struct acl entries[NACLVENTRIES]; +@@ -735,9 +723,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + } + } + +-# endif ++# endif + +-# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ ++# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ + + acl_type_t type; + char aclbuf[1024]; +@@ -792,7 +780,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + return -1; + } + +-# elif HAVE_STATACL /* older AIX */ ++# elif HAVE_STATACL /* older AIX */ + + union { struct acl a; char room[4096]; } u; + +@@ -801,7 +789,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + + return acl_nontrivial (&u.a); + +-# elif HAVE_ACLSORT /* NonStop Kernel */ ++# elif HAVE_ACLSORT /* NonStop Kernel */ + + { + struct acl entries[NACLENTRIES]; +@@ -833,8 +821,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + return acl_nontrivial (count, entries); + } + } +-# endif ++# endif + } ++# endif ++# endif + #endif + + return 0; +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl-tests-omit-string.h-include.patch b/backport-file-has-acl-tests-omit-string.h-include.patch new file mode 100644 index 0000000000000000000000000000000000000000..a83687fe005ccecbb3a384ec287a9d268e3aa88d --- /dev/null +++ b/backport-file-has-acl-tests-omit-string.h-include.patch @@ -0,0 +1,31 @@ +From fc6d90e012e3f872a758272b2cd869b1fe91abdf Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 1 Oct 2024 19:56:18 -0700 +Subject: [PATCH] file-has-acl-tests: omit string.h include +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Problem reported by Bruno Haible in: +https://lists.gnu.org/r/bug-gnulib/2024-10/msg00001.html +* tests/test-file-has-acl.c: Don’t include string.h. + + +--- + gnulib-tests/test-file-has-acl.c | 1 - + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/gnulib-tests/test-file-has-acl.c b/gnulib-tests/test-file-has-acl.c +index c0766609..79e80c1a 100644 +--- a/gnulib-tests/test-file-has-acl.c ++++ b/gnulib-tests/test-file-has-acl.c +@@ -22,7 +22,6 @@ + + #include + #include +-#include + #include + #include + #include +-- +2.43.0 \ No newline at end of file diff --git a/backport-file-has-acl.c-port-back-to-macOS.patch b/backport-file-has-acl.c-port-back-to-macOS.patch new file mode 100644 index 0000000000000000000000000000000000000000..db27f714313b163491cb38ed9c08ea6eaf019643 --- /dev/null +++ b/backport-file-has-acl.c-port-back-to-macOS.patch @@ -0,0 +1,155 @@ +From 9256d97e074bbae33bbd188e742daccfb72022de Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 1 Oct 2024 19:51:50 -0700 +Subject: [PATCH] file-has-acl.c: port back to macOS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Problem reported by Bruno Haible in: +https://lists.gnu.org/r/bug-gnulib/2024-10/msg00000.html +* lib/file-has-acl.c (file_has_aclinfo): Don’t attempt to link to +acl_extended_file if !HAVE_ACL_EXTENDED_FILE. Most of this patch +consists of indenting changes; the real change is to use ‘#if’ +rather than ‘if’. + + +--- + lib/file-has-acl.c | 109 +++++++++++++++++++++------------------------ + 2 files changed, 62 insertions(+), 57 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 944f0d22..f29414de 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -418,76 +418,71 @@ file_has_aclinfo (char const *name, struct stat const *sb, + /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ + int ret; + +- if (HAVE_ACL_EXTENDED_FILE) /* Linux */ ++# if HAVE_ACL_EXTENDED_FILE /* Linux */ ++ /* On Linux, acl_extended_file is an optimized function: It only ++ makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for ++ ACL_TYPE_DEFAULT. */ ++ ret = ((flags & ACL_SYMLINK_FOLLOW ++ ? acl_extended_file ++ : acl_extended_file_nofollow) ++ (name)); ++# elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ ++ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) ++ always return NULL / EINVAL. There is no point in making ++ these two useless calls. The real ACL is retrieved through ++ acl_get_file (name, ACL_TYPE_EXTENDED). */ ++ acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED); ++ if (acl) + { +- /* On Linux, acl_extended_file is an optimized function: It only +- makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for +- ACL_TYPE_DEFAULT. */ +- ret = ((flags & ACL_SYMLINK_FOLLOW +- ? acl_extended_file +- : acl_extended_file_nofollow) +- (name)); ++ ret = acl_extended_nontrivial (acl); ++ acl_free (acl); + } +- else /* FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ +- { +-# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ +- /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) +- and acl_get_file (name, ACL_TYPE_DEFAULT) +- always return NULL / EINVAL. There is no point in making +- these two useless calls. The real ACL is retrieved through +- acl_get_file (name, ACL_TYPE_EXTENDED). */ +- acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED); +- if (acl) +- { +- ret = acl_extended_nontrivial (acl); +- acl_free (acl); +- } +- else +- ret = -1; ++ else ++ ret = -1; + # else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */ +- acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); +- if (acl) +- { +- int saved_errno; ++ acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); ++ if (acl) ++ { ++ int saved_errno; + +- ret = acl_access_nontrivial (acl); +- saved_errno = errno; +- acl_free (acl); +- errno = saved_errno; ++ ret = acl_access_nontrivial (acl); ++ saved_errno = errno; ++ acl_free (acl); ++ errno = saved_errno; + # if HAVE_ACL_FREE_TEXT /* Tru64 */ +- /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always +- returns NULL with errno not set. There is no point in +- making this call. */ ++ /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always ++ returns NULL with errno not set. There is no point in ++ making this call. */ + # else /* FreeBSD, IRIX, Cygwin >= 2.5 */ +- /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS) +- and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory +- either both succeed or both fail; it depends on the +- file system. Therefore there is no point in making the second +- call if the first one already failed. */ +- if (ret == 0 && S_ISDIR (sb->st_mode)) ++ /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory ++ either both succeed or both fail; it depends on the ++ file system. Therefore there is no point in making the second ++ call if the first one already failed. */ ++ if (ret == 0 && S_ISDIR (sb->st_mode)) ++ { ++ acl = acl_get_file (name, ACL_TYPE_DEFAULT); ++ if (acl) + { +- acl = acl_get_file (name, ACL_TYPE_DEFAULT); +- if (acl) +- { + # ifdef __CYGWIN__ /* Cygwin >= 2.5 */ +- ret = acl_access_nontrivial (acl); +- saved_errno = errno; +- acl_free (acl); +- errno = saved_errno; ++ ret = acl_access_nontrivial (acl); ++ saved_errno = errno; ++ acl_free (acl); ++ errno = saved_errno; + # else +- ret = (0 < acl_entries (acl)); +- acl_free (acl); ++ ret = (0 < acl_entries (acl)); ++ acl_free (acl); + # endif +- } +- else +- ret = -1; + } +-# endif ++ else ++ ret = -1; + } +- else +- ret = -1; +-# endif ++# endif + } ++ else ++ ret = -1; ++# endif + if (ret < 0) + return - acl_errno_valid (errno); + return ret; +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-check-FILETYPE_INDICATORS-cardinality.patch b/backport-ls-check-FILETYPE_INDICATORS-cardinality.patch new file mode 100644 index 0000000000000000000000000000000000000000..d563e914910330a4867805b81c7ae61eb0406918 --- /dev/null +++ b/backport-ls-check-FILETYPE_INDICATORS-cardinality.patch @@ -0,0 +1,61 @@ +From a89896f8e7be81733f2edc3f830f72183a272461 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 1 Oct 2024 17:12:31 -0700 +Subject: [PATCH] ls: check FILETYPE_INDICATORS cardinality + +* src/ls.c (filetype_cardinality): New constant. +(filetype_letter): Omit unnecessary trailing NUL. +(FILETYPE_INDICATORS): Remove, moving definiens to ... +(get_color_indicator): ... here, and check its cardinality too. + + +--- + src/ls.c | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 86e6de3d4..12af32794 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -172,20 +172,13 @@ enum filetype + whiteout, + arg_directory + }; ++enum { filetype_cardinality = arg_directory + 1 }; + + /* Display letters and indicators for each filetype. + Keep these in sync with enum filetype. */ +-static char const filetype_letter[] = "?pcdb-lswd"; +- +-/* Ensure that filetype and filetype_letter have the same +- number of elements. */ +-static_assert (sizeof filetype_letter - 1 == arg_directory + 1); +- +-#define FILETYPE_INDICATORS \ +- { \ +- C_ORPHAN, C_FIFO, C_CHR, C_DIR, C_BLK, C_FILE, \ +- C_LINK, C_SOCK, C_FILE, C_DIR \ +- } ++static char const filetype_letter[] = ++ {'?', 'p', 'c', 'd', 'b', '-', 'l', 's', 'w', 'd'}; ++static_assert (ARRAY_CARDINALITY (filetype_letter) == filetype_cardinality); + + enum acl_type + { +@@ -4956,7 +4949,13 @@ get_color_indicator (const struct fileinfo *f, bool symlink_target) + type = C_MISSING; + else if (!f->stat_ok) + { +- static enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS; ++ static enum indicator_no const filetype_indicator[] = ++ { ++ C_ORPHAN, C_FIFO, C_CHR, C_DIR, C_BLK, C_FILE, ++ C_LINK, C_SOCK, C_FILE, C_DIR ++ }; ++ static_assert (ARRAY_CARDINALITY (filetype_indicator) ++ == filetype_cardinality); + type = filetype_indicator[f->filetype]; + } + else +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-fix-aclinfo-cache-bug.patch b/backport-ls-fix-aclinfo-cache-bug.patch new file mode 100644 index 0000000000000000000000000000000000000000..76678badab2eb2e29f0f797c1526ddb3a2710531 --- /dev/null +++ b/backport-ls-fix-aclinfo-cache-bug.patch @@ -0,0 +1,61 @@ +From b857d66b51e34568b4fdab07053433df1413243b Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 7 Nov 2024 12:32:21 -0800 +Subject: [PATCH] ls: fix aclinfo cache bug + +Found when testing on a new platform with a new file system. +* src/ls.c (file_has_aclinfo_cache): For failures, also cache +return value, scontext, and scontext_err, and when using cached +values make sure buf and size have reasonable values for +aclinfo_free etc. + + +--- + src/ls.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 962737432..dd61fbe62 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3284,19 +3284,33 @@ static int + file_has_aclinfo_cache (char const *file, struct fileinfo *f, + struct aclinfo *ai, int flags) + { +- /* st_dev of the most recently processed device for which we've +- found that file_has_acl fails indicating lack of support. */ ++ /* st_dev and associated info for the most recently processed device ++ for which file_has_acl failed indicating lack of support. */ ++ static int unsupported_return; ++ static char *unsupported_scontext; ++ static int unsupported_scontext_err; + static dev_t unsupported_device; + + if (f->stat.st_dev == unsupported_device) + { ++ ai->buf = ai->u.__gl_acl_ch; ++ ai->size = 0; ++ ai->scontext = unsupported_scontext; ++ ai->scontext_err = unsupported_scontext_err; + errno = ENOTSUP; +- return 0; ++ return unsupported_return; + } + ++ errno = 0; + int n = file_has_aclinfo (file, ai, flags); +- if (n <= 0 && !acl_errno_valid (ai->u.err)) +- unsupported_device = f->stat.st_dev; ++ int err = errno; ++ if (n <= 0 && !acl_errno_valid (err)) ++ { ++ unsupported_return = n; ++ unsupported_scontext = ai->scontext; ++ unsupported_scontext_err = ai->scontext_err; ++ unsupported_device = f->stat.st_dev; ++ } + return n; + } + +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-fix-crash-of-ls-Z-.-on-OpenBSD-s-dev-wd0a-disk.patch b/backport-ls-fix-crash-of-ls-Z-.-on-OpenBSD-s-dev-wd0a-disk.patch new file mode 100644 index 0000000000000000000000000000000000000000..e855f42f0e9ea5a8986f2f5bc4cdb95181063e19 --- /dev/null +++ b/backport-ls-fix-crash-of-ls-Z-.-on-OpenBSD-s-dev-wd0a-disk.patch @@ -0,0 +1,42 @@ +From 0954e91fcff2bd224dc14c9b22bf221fe88b078c Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Tue, 8 Apr 2025 12:14:29 +0200 +Subject: [PATCH] ls: fix crash of "ls -Z ." on OpenBSD's /dev/wd0a disk + +* src/ls.c (file_has_aclinfo_cache): Add new static variable +'unsupported_cached'. Don't assume that device 0 never occurs. + + +--- + src/ls.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/ls.c b/src/ls.c +index 6690f7747..de5a1ae2b 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3309,12 +3309,13 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + { + /* st_dev and associated info for the most recently processed device + for which file_has_aclinfo failed indicating lack of support. */ ++ static bool unsupported_cached /* = false */; + static int unsupported_return; + static char *unsupported_scontext; + static int unsupported_scontext_err; + static dev_t unsupported_device; + +- if (f->stat_ok && f->stat.st_dev == unsupported_device) ++ if (f->stat_ok && unsupported_cached && f->stat.st_dev == unsupported_device) + { + ai->buf = ai->u.__gl_acl_ch; + ai->size = 0; +@@ -3330,6 +3331,7 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + if (f->stat_ok && n <= 0 && !acl_errno_valid (err) + && (!(flags & ACL_GET_SCONTEXT) || !acl_errno_valid (ai->scontext_err))) + { ++ unsupported_cached = true; + unsupported_return = n; + unsupported_scontext = ai->scontext; + unsupported_scontext_err = ai->scontext_err; +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-fix-crash-on-systems-with-SELinux-but-without-xat.patch b/backport-ls-fix-crash-on-systems-with-SELinux-but-without-xat.patch new file mode 100644 index 0000000000000000000000000000000000000000..cbe9838f1f9ebf84326c230e27a6798213903642 --- /dev/null +++ b/backport-ls-fix-crash-on-systems-with-SELinux-but-without-xat.patch @@ -0,0 +1,36 @@ +From cb2abbac7f9e40e0f0d6183bf9b11e80b0cad8ef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Thu, 20 Mar 2025 18:40:01 +0000 +Subject: [PATCH] ls: fix crash on systems with SELinux but without xattr + support + +This was seen on termux on Android with ./configure --disable-xattr +where listxattr() and getxattr() returned ENOTSUP. +Then the valid security context obtained by file_has_aclinfo() +was discounted, and problematically then freed multiple times. +Reported at https://github.com/termux/termux-packages/issues/23752 + +* src/ls.c (file_has_aclinfo_cache): Only discount the returned +acl info when all components are defaulted due to being unsupported. + + +--- + src/ls.c | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 23309b812..244484439 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3327,7 +3327,8 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + errno = 0; + int n = file_has_aclinfo (file, ai, flags); + int err = errno; +- if (f->stat_ok && n <= 0 && !acl_errno_valid (err)) ++ if (f->stat_ok && n <= 0 && !acl_errno_valid (err) ++ && (!(flags & ACL_GET_SCONTEXT) || !acl_errno_valid (ai->scontext_err))) + { + unsupported_return = n; + unsupported_scontext = ai->scontext; +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-fix-crash-with-context.patch b/backport-ls-fix-crash-with-context.patch new file mode 100644 index 0000000000000000000000000000000000000000..e16f78f3d01065952389ff3af77d06c647021a97 --- /dev/null +++ b/backport-ls-fix-crash-with-context.patch @@ -0,0 +1,82 @@ +From 915004f403cb25fadb207ddfdbe6a2f43bd44fac Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Fri, 17 Jan 2025 17:29:34 +0000 +Subject: [PATCH] ls: fix crash with --context + +* src/ls.c (main): Flag that we need to stat() +if we're going to get security context (call file_has_aclinfo_cache). +(file_has_aclinfo_cache): Be defensive and only lookup the device +for the file if the stat has been performed. +(has_capability_cache): Likewise. +* tests/ls/selinux-segfault.sh: Add a test case. +* NEWS: Mention the bug fix. +Reported by Bruno Haible. + + +--- + src/ls.c | 10 +++++----- + tests/ls/selinux-segfault.sh | 3 +++ + 3 files changed, 13 insertions(+), 5 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 321536021..f67167f16 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -1768,7 +1768,7 @@ main (int argc, char **argv) + + format_needs_stat = ((sort_type == sort_time) | (sort_type == sort_size) + | (format == long_format) +- | print_block_size | print_hyperlink); ++ | print_block_size | print_hyperlink | print_scontext); + format_needs_type = ((! format_needs_stat) + & (recursive | print_with_color | print_scontext + | directories_first +@@ -3309,7 +3309,7 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + static int unsupported_scontext_err; + static dev_t unsupported_device; + +- if (f->stat.st_dev == unsupported_device) ++ if (f->stat_ok && f->stat.st_dev == unsupported_device) + { + ai->buf = ai->u.__gl_acl_ch; + ai->size = 0; +@@ -3322,7 +3322,7 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + errno = 0; + int n = file_has_aclinfo (file, ai, flags); + int err = errno; +- if (n <= 0 && !acl_errno_valid (err)) ++ if (f->stat_ok && n <= 0 && !acl_errno_valid (err)) + { + unsupported_return = n; + unsupported_scontext = ai->scontext; +@@ -3342,14 +3342,14 @@ has_capability_cache (char const *file, struct fileinfo *f) + found that has_capability fails indicating lack of support. */ + static dev_t unsupported_device; + +- if (f->stat.st_dev == unsupported_device) ++ if (f->stat_ok && f->stat.st_dev == unsupported_device) + { + errno = ENOTSUP; + return 0; + } + + bool b = has_capability (file); +- if ( !b && !acl_errno_valid (errno)) ++ if (f->stat_ok && !b && !acl_errno_valid (errno)) + unsupported_device = f->stat.st_dev; + return b; + } +diff --git a/tests/ls/selinux-segfault.sh b/tests/ls/selinux-segfault.sh +index 11623acb3..1cac2b5fc 100755 +--- a/tests/ls/selinux-segfault.sh ++++ b/tests/ls/selinux-segfault.sh +@@ -30,4 +30,7 @@ mkdir sedir || framework_failure_ + ln -sf missing sedir/broken || framework_failure_ + returns_ 1 ls -L -R -Z -m sedir > out || fail=1 + ++# ls 9.6 would segfault with the following ++ls -Z . > out || fail=1 ++ + Exit $fail +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-fix-security-context-indication-in-long-mode.patch b/backport-ls-fix-security-context-indication-in-long-mode.patch new file mode 100644 index 0000000000000000000000000000000000000000..76e939915ab909c45a9e930981c1918dbdf00fd7 --- /dev/null +++ b/backport-ls-fix-security-context-indication-in-long-mode.patch @@ -0,0 +1,30 @@ +From 00c45be7875e159b35ab6cd9fcfb2733fde95d41 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Mon, 11 Nov 2024 18:55:19 +0000 +Subject: [PATCH] ls: fix security context indication in --long mode + +* src/ls.c (gobble_file): Always get the security context with -l +so that we can indicate a context with '.' if present. + + +--- + src/ls.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/ls.c b/src/ls.c +index 6ff0f410f..6422022d2 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3501,8 +3501,9 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + if ((format == long_format) | print_scontext | check_capability) + { + struct aclinfo ai; ++ bool get_scontext = (format == long_format) | print_scontext; + int aclinfo_flags = ((do_deref ? ACL_SYMLINK_FOLLOW : 0) +- | (print_scontext ? ACL_GET_SCONTEXT : 0) ++ | (get_scontext ? ACL_GET_SCONTEXT : 0) + | filetype_d_type[type]); + int n = file_has_aclinfo_cache (full_name, f, &ai, aclinfo_flags); + bool have_acl = 0 < n; +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-fix-spurious-output-with-Z.patch b/backport-ls-fix-spurious-output-with-Z.patch new file mode 100644 index 0000000000000000000000000000000000000000..4693d4f8237a1e9e29dff9af212bea04e1a81188 --- /dev/null +++ b/backport-ls-fix-spurious-output-with-Z.patch @@ -0,0 +1,28 @@ +From a94a551ee031e0ff07d707f867cb08a8be83e78c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Thu, 3 Oct 2024 15:15:48 +0100 +Subject: [PATCH] ls: fix spurious output with -Z + +* src/ls.c (gobble_file): Only output an error if there actually +was an error. + + +--- + src/ls.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/ls.c b/src/ls.c +index 101ffa818..290ce35d2 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3518,7 +3518,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + ls fail just because the file (even a command line argument) + isn't on the right type of file system. I.e., a getfilecon + failure isn't in the same class as a stat failure. */ +- if (print_scontext ++ if (print_scontext && ai.scontext_err + && (! (is_ENOTSUP (ai.scontext_err) + || ai.scontext_err == ENODATA))) + error (0, ai.scontext_err, "%s", quotef (full_name)); +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-omit-cast-from-gobble_file.patch b/backport-ls-omit-cast-from-gobble_file.patch new file mode 100644 index 0000000000000000000000000000000000000000..0979229ee5c6d180a894cb41d6bde36226c5177d --- /dev/null +++ b/backport-ls-omit-cast-from-gobble_file.patch @@ -0,0 +1,59 @@ +From 8fbb1076d5bdf84f84da391a0e3377ffe014de5a Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 2 Oct 2024 11:22:03 -0700 +Subject: [PATCH] ls: omit cast from gobble_file + +* src/ls.c (gobble_file): Minor refactoring. Last arg is now null +pointer, not "", for no directory. All callers changed. +Avoid need for cast from char const * to char *. + + +--- + src/ls.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 12af32794..57f1621ff 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -1770,13 +1770,13 @@ main (int argc, char **argv) + if (n_files <= 0) + { + if (immediate_dirs) +- gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, ""); ++ gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, nullptr); + else + queue_directory (".", nullptr, true); + } + else + do +- gobble_file (argv[i++], unknown, NOT_AN_INODE_NUMBER, true, ""); ++ gobble_file (argv[i++], unknown, NOT_AN_INODE_NUMBER, true, nullptr); + while (i < argc); + + if (cwd_n_used) +@@ -3405,16 +3405,17 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + + { + /* Absolute name of this file. */ +- char *full_name; ++ char const *full_name; + bool do_deref; + int err; + +- if (name[0] == '/' || dirname[0] == 0) +- full_name = (char *) name; ++ if (name[0] == '/' || !dirname) ++ full_name = name; + else + { +- full_name = alloca (strlen (name) + strlen (dirname) + 2); +- attach (full_name, dirname, name); ++ char *p = alloca (strlen (name) + strlen (dirname) + 2); ++ attach (p, dirname, name); ++ full_name = p; + } + + if (print_hyperlink) +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-omit-unnecessary-test.patch b/backport-ls-omit-unnecessary-test.patch new file mode 100644 index 0000000000000000000000000000000000000000..db1eb0aa7e770129e7178954a91655a3d0d27ba3 --- /dev/null +++ b/backport-ls-omit-unnecessary-test.patch @@ -0,0 +1,28 @@ +From 65c58007f77122c5c0d8b858cad0d9d44bc525c1 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 1 Oct 2024 12:10:10 -0700 +Subject: [PATCH] ls: omit unnecessary test + +* src/ls.c (gobble_file): Omit redundant test of variable that +must be false here. + + +--- + src/ls.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/ls.c b/src/ls.c +index 9f3f21400..8599368ef 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3384,7 +3384,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + assertion that the inode number is not yet known. */ + || (print_inode && inode == NOT_AN_INODE_NUMBER) + || (format_needs_type +- && (type == unknown || command_line_arg ++ && (type == unknown + /* --indicator-style=classify (aka -F) + requires that we stat each regular file + to see if it's executable. */ +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-prefer-xpalloc-to-xnrealloc.patch b/backport-ls-prefer-xpalloc-to-xnrealloc.patch new file mode 100644 index 0000000000000000000000000000000000000000..a763a9e3c2d9e19b40320f8604bc810c2b6b238a --- /dev/null +++ b/backport-ls-prefer-xpalloc-to-xnrealloc.patch @@ -0,0 +1,352 @@ +From 4cb726e4a6e3b839ceb3ed84cc66adf401f7ebad Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 7 Nov 2024 22:30:09 -0800 +Subject: [PATCH] ls: prefer xpalloc to xnrealloc + +* src/ls.c (cwd_n_alloc, cwd_n_used, clear_files) +(extract_dirs_from_files, initialize_ordering_vector) +(update_current_files_info, sort_files, print_current_files) +(print_many_per_line, print_horizontal, print_with_separator) +(init_column_info, calculate_columns): +Prefer idx_t to size_t for sizes related to xpalloc. +(main): Let the compiler fold constants. +(gobble_file, init_column_info): Use xpalloc, not xnrealloc. +(print_many_per_line): Fix very-unlikely integer overflow. + + +--- + src/ls.c | 129 ++++++++++++++++++++----------------------------------- + 1 file changed, 47 insertions(+), 82 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index dd61fbe62..317a641ad 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -289,7 +289,7 @@ static void extract_dirs_from_files (char const *dirname, + static void get_link_name (char const *filename, struct fileinfo *f, + bool command_line_arg); + static void indent (size_t from, size_t to); +-static size_t calculate_columns (bool by_columns); ++static idx_t calculate_columns (bool by_columns); + static void print_current_files (void); + static void print_dir (char const *name, char const *realname, + bool command_line_arg); +@@ -344,10 +344,10 @@ static Hash_table *active_dir_set; + static struct fileinfo *cwd_file; + + /* Length of block that 'cwd_file' points to, measured in files. */ +-static size_t cwd_n_alloc; ++static idx_t cwd_n_alloc; + + /* Index of first unused slot in 'cwd_file'. */ +-static size_t cwd_n_used; ++static idx_t cwd_n_used; + + /* Whether files needs may need padding due to quoting. */ + static bool cwd_some_quoted; +@@ -1779,7 +1779,7 @@ main (int argc, char **argv) + } + + cwd_n_alloc = 100; +- cwd_file = xnmalloc (cwd_n_alloc, sizeof *cwd_file); ++ cwd_file = xmalloc (cwd_n_alloc * sizeof *cwd_file); + cwd_n_used = 0; + + clear_files (); +@@ -3256,7 +3256,7 @@ free_ent (struct fileinfo *f) + static void + clear_files (void) + { +- for (size_t i = 0; i < cwd_n_used; i++) ++ for (idx_t i = 0; i < cwd_n_used; i++) + { + struct fileinfo *f = sorted_file[i]; + free_ent (f); +@@ -3360,10 +3360,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + affirm (! command_line_arg || inode == NOT_AN_INODE_NUMBER); + + if (cwd_n_used == cwd_n_alloc) +- { +- cwd_file = xnrealloc (cwd_file, cwd_n_alloc, 2 * sizeof *cwd_file); +- cwd_n_alloc *= 2; +- } ++ cwd_file = xpalloc (cwd_file, &cwd_n_alloc, 1, -1, sizeof *cwd_file); + + f = &cwd_file[cwd_n_used]; + memset (f, '\0', sizeof *f); +@@ -3702,8 +3699,7 @@ basename_is_dot_or_dotdot (char const *name) + static void + extract_dirs_from_files (char const *dirname, bool command_line_arg) + { +- size_t i; +- size_t j; ++ idx_t i, j; + bool ignore_dot_and_dot_dot = (dirname != nullptr); + + if (dirname && LOOP_DETECT) +@@ -3716,8 +3712,9 @@ extract_dirs_from_files (char const *dirname, bool command_line_arg) + + /* Queue the directories last one first, because queueing reverses the + order. */ +- for (i = cwd_n_used; i-- != 0; ) ++ for (i = cwd_n_used; 0 < i; ) + { ++ i--; + struct fileinfo *f = sorted_file[i]; + + if (is_directory (f) +@@ -4020,7 +4017,7 @@ static_assert (ARRAY_CARDINALITY (sort_functions) + static void + initialize_ordering_vector (void) + { +- for (size_t i = 0; i < cwd_n_used; i++) ++ for (idx_t i = 0; i < cwd_n_used; i++) + sorted_file[i] = &cwd_file[i]; + } + +@@ -4033,8 +4030,7 @@ update_current_files_info (void) + if (sort_type == sort_width + || (line_length && (format == many_per_line || format == horizontal))) + { +- size_t i; +- for (i = 0; i < cwd_n_used; i++) ++ for (idx_t i = 0; i < cwd_n_used; i++) + { + struct fileinfo *f = sorted_file[i]; + f->width = fileinfo_name_width (f); +@@ -4049,10 +4045,10 @@ sort_files (void) + { + bool use_strcmp; + +- if (sorted_file_alloc < cwd_n_used + cwd_n_used / 2) ++ if (sorted_file_alloc < cwd_n_used + (cwd_n_used >> 1)) + { + free (sorted_file); +- sorted_file = xnmalloc (cwd_n_used, 3 * sizeof *sorted_file); ++ sorted_file = xinmalloc (cwd_n_used, 3 * sizeof *sorted_file); + sorted_file_alloc = 3 * cwd_n_used; + } + +@@ -4089,12 +4085,10 @@ sort_files (void) + static void + print_current_files (void) + { +- size_t i; +- + switch (format) + { + case one_per_line: +- for (i = 0; i < cwd_n_used; i++) ++ for (idx_t i = 0; i < cwd_n_used; i++) + { + print_file_name_and_frills (sorted_file[i], 0); + putchar (eolbyte); +@@ -4120,7 +4114,7 @@ print_current_files (void) + break; + + case long_format: +- for (i = 0; i < cwd_n_used; i++) ++ for (idx_t i = 0; i < cwd_n_used; i++) + { + set_normal_color (); + print_long_format (sorted_file[i]); +@@ -5107,18 +5101,17 @@ length_of_file_name_and_frills (const struct fileinfo *f) + static void + print_many_per_line (void) + { +- size_t row; /* Current row. */ +- size_t cols = calculate_columns (true); ++ idx_t cols = calculate_columns (true); + struct column_info const *line_fmt = &column_info[cols - 1]; + + /* Calculate the number of rows that will be in each column except possibly + for a short column on the right. */ +- size_t rows = cwd_n_used / cols + (cwd_n_used % cols != 0); ++ idx_t rows = cwd_n_used / cols + (cwd_n_used % cols != 0); + +- for (row = 0; row < rows; row++) ++ for (idx_t row = 0; row < rows; row++) + { + size_t col = 0; +- size_t filesno = row; ++ idx_t filesno = row; + size_t pos = 0; + + /* Print the next row. */ +@@ -5129,9 +5122,9 @@ print_many_per_line (void) + size_t max_name_length = line_fmt->col_arr[col++]; + print_file_name_and_frills (f, pos); + +- filesno += rows; +- if (filesno >= cwd_n_used) ++ if (cwd_n_used - rows <= filesno) + break; ++ filesno += rows; + + indent (pos + name_length, pos + max_name_length); + pos += max_name_length; +@@ -5143,9 +5136,8 @@ print_many_per_line (void) + static void + print_horizontal (void) + { +- size_t filesno; + size_t pos = 0; +- size_t cols = calculate_columns (false); ++ idx_t cols = calculate_columns (false); + struct column_info const *line_fmt = &column_info[cols - 1]; + struct fileinfo const *f = sorted_file[0]; + size_t name_length = length_of_file_name_and_frills (f); +@@ -5155,9 +5147,9 @@ print_horizontal (void) + print_file_name_and_frills (f, 0); + + /* Now the rest. */ +- for (filesno = 1; filesno < cwd_n_used; ++filesno) ++ for (idx_t filesno = 1; filesno < cwd_n_used; filesno++) + { +- size_t col = filesno % cols; ++ idx_t col = filesno % cols; + + if (col == 0) + { +@@ -5184,10 +5176,9 @@ print_horizontal (void) + static void + print_with_separator (char sep) + { +- size_t filesno; + size_t pos = 0; + +- for (filesno = 0; filesno < cwd_n_used; filesno++) ++ for (idx_t filesno = 0; filesno < cwd_n_used; filesno++) + { + struct fileinfo const *f = sorted_file[filesno]; + size_t len = line_length ? length_of_file_name_and_frills (f) : 0; +@@ -5268,65 +5259,41 @@ attach (char *dest, char const *dirname, char const *name) + narrowest possible columns. */ + + static void +-init_column_info (size_t max_cols) ++init_column_info (idx_t max_cols) + { +- size_t i; +- + /* Currently allocated columns in column_info. */ +- static size_t column_info_alloc; ++ static idx_t column_info_alloc; + + if (column_info_alloc < max_cols) + { +- size_t new_column_info_alloc; +- size_t *p; +- +- if (!max_idx || max_cols < max_idx / 2) +- { +- /* The number of columns is far less than the display width +- allows. Grow the allocation, but only so that it's +- double the current requirements. If the display is +- extremely wide, this avoids allocating a lot of memory +- that is never needed. */ +- column_info = xnrealloc (column_info, max_cols, +- 2 * sizeof *column_info); +- new_column_info_alloc = 2 * max_cols; +- } +- else +- { +- column_info = xnrealloc (column_info, max_idx, sizeof *column_info); +- new_column_info_alloc = max_idx; +- } ++ idx_t old_column_info_alloc = column_info_alloc; ++ column_info = xpalloc (column_info, &column_info_alloc, ++ max_cols - column_info_alloc, -1, ++ sizeof *column_info); + + /* Allocate the new size_t objects by computing the triangle + formula n * (n + 1) / 2, except that we don't need to + allocate the part of the triangle that we've already + allocated. Check for address arithmetic overflow. */ +- { +- size_t column_info_growth = new_column_info_alloc - column_info_alloc; +- size_t s = column_info_alloc + 1 + new_column_info_alloc; +- size_t t = s * column_info_growth; +- if (s < new_column_info_alloc || t / column_info_growth != s) +- xalloc_die (); +- p = xnmalloc (t / 2, sizeof *p); +- } ++ idx_t column_info_growth = column_info_alloc - old_column_info_alloc, s; ++ if (ckd_add (&s, old_column_info_alloc + 1, column_info_alloc) ++ || ckd_mul (&s, s, column_info_growth)) ++ xalloc_die (); ++ size_t *p = xinmalloc (s >> 1, sizeof *p); + + /* Grow the triangle by parceling out the cells just allocated. */ +- for (i = column_info_alloc; i < new_column_info_alloc; i++) ++ for (idx_t i = old_column_info_alloc; i < column_info_alloc; i++) + { + column_info[i].col_arr = p; + p += i + 1; + } +- +- column_info_alloc = new_column_info_alloc; + } + +- for (i = 0; i < max_cols; ++i) ++ for (idx_t i = 0; i < max_cols; ++i) + { +- size_t j; +- + column_info[i].valid_len = true; + column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH; +- for (j = 0; j <= i; ++j) ++ for (idx_t j = 0; j <= i; ++j) + column_info[i].col_arr[j] = MIN_COLUMN_WIDTH; + } + } +@@ -5334,32 +5301,29 @@ init_column_info (size_t max_cols) + /* Calculate the number of columns needed to represent the current set + of files in the current display width. */ + +-static size_t ++static idx_t + calculate_columns (bool by_columns) + { +- size_t filesno; /* Index into cwd_file. */ +- size_t cols; /* Number of files across. */ +- + /* Normally the maximum number of columns is determined by the + screen width. But if few files are available this might limit it + as well. */ +- size_t max_cols = 0 < max_idx && max_idx < cwd_n_used ? max_idx : cwd_n_used; ++ idx_t max_cols = 0 < max_idx && max_idx < cwd_n_used ? max_idx : cwd_n_used; + + init_column_info (max_cols); + + /* Compute the maximum number of possible columns. */ +- for (filesno = 0; filesno < cwd_n_used; ++filesno) ++ for (idx_t filesno = 0; filesno < cwd_n_used; ++filesno) + { + struct fileinfo const *f = sorted_file[filesno]; + size_t name_length = length_of_file_name_and_frills (f); + +- for (size_t i = 0; i < max_cols; ++i) ++ for (idx_t i = 0; i < max_cols; ++i) + { + if (column_info[i].valid_len) + { +- size_t idx = (by_columns +- ? filesno / ((cwd_n_used + i) / (i + 1)) +- : filesno % (i + 1)); ++ idx_t idx = (by_columns ++ ? filesno / ((cwd_n_used + i) / (i + 1)) ++ : filesno % (i + 1)); + size_t real_length = name_length + (idx == i ? 0 : 2); + + if (column_info[i].col_arr[idx] < real_length) +@@ -5375,6 +5339,7 @@ calculate_columns (bool by_columns) + } + + /* Find maximum allowed columns. */ ++ idx_t cols; + for (cols = max_cols; 1 < cols; --cols) + { + if (column_info[cols - 1].valid_len) +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-reinstate-capability-checking-in-more-cases.patch b/backport-ls-reinstate-capability-checking-in-more-cases.patch new file mode 100644 index 0000000000000000000000000000000000000000..fa305901d68d89ae792fc77de660c9bd3555382a --- /dev/null +++ b/backport-ls-reinstate-capability-checking-in-more-cases.patch @@ -0,0 +1,61 @@ +From 647a8b8cf9184b12e15cca3b62b8647d8d8447e0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Wed, 2 Oct 2024 22:57:16 +0100 +Subject: [PATCH] ls: reinstate capability checking in more cases + +The recent commit v9.5-119-g4ce432ad8 restricted capability checking +to only files with XATTR_NAME_CAPS set. If this is done then we need +to adjust tests/ls/no-cap.sh so that it doesn't always skip. More +problematically XATTR_NAME_CAPS was only determined in long listing +mode, thus breaking capability coloring in short listing mode +as evidenced by the failing tests/ls/capability.sh test. + +Note capability checking does have a large overhead, but we've +disabled capability checking by default anyway through the default +color configuration since v9.0-187-g6b5134770 + +So for these reasons revert to checking capabilities as before. + +* src/ls.c (gobble_file): Check for capabilities in all modes +if enabled in color config. + + +--- + src/ls.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 099893f86..9f3f21400 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3484,6 +3484,13 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + + f->stat_ok = true; + ++ /* has_capability adds around 30% runtime to 'ls --color', ++ so call it only if really needed. Note capability coloring ++ is disabled in the default color config. */ ++ if ((type == normal || S_ISREG (f->stat.st_mode)) ++ && print_with_color && is_colored (C_CAP)) ++ f->has_capability = has_capability_cache (full_name, f); ++ + if (format == long_format || print_scontext) + { + struct aclinfo ai; +@@ -3512,14 +3519,6 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + error (0, ai.scontext_err, "%s", quotef (full_name)); + } + +- /* has_capability adds around 30% runtime to 'ls --color', +- so call it only if really needed. */ +- if (0 < ai.size +- && (type == normal || S_ISREG (f->stat.st_mode)) +- && print_with_color && is_colored (C_CAP) +- && aclinfo_has_xattr (&ai, XATTR_NAME_CAPS)) +- f->has_capability = has_capability_cache (full_name, f); +- + f->scontext = ai.scontext; + ai.scontext = nullptr; + aclinfo_free (&ai); +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-tune-indicator_name.patch b/backport-ls-tune-indicator_name.patch new file mode 100644 index 0000000000000000000000000000000000000000..64adcaef299b9ae25cb1ccc4debae24346a9f4bd --- /dev/null +++ b/backport-ls-tune-indicator_name.patch @@ -0,0 +1,111 @@ +From 75b34c77e43a4d462ec8b0d4649489a572ca0727 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 1 Oct 2024 16:54:36 -0700 +Subject: [PATCH] ls: tune indicator_name + +* src/ls.c (indicator_name): Simplify type; it is now merely +a 2-dimensional array of char. All uses changed. + + +--- + src/ls.c | 34 +++++++++++++++++++--------------- + 1 file changed, 19 insertions(+), 15 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index d9ac970f2..86e6de3d4 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -602,11 +602,15 @@ enum indicator_no + C_CLR_TO_EOL + }; + +-static char const *const indicator_name[]= ++static char const indicator_name[][2]= + { +- "lc", "rc", "ec", "rs", "no", "fi", "di", "ln", "pi", "so", +- "bd", "cd", "mi", "or", "ex", "do", "su", "sg", "st", +- "ow", "tw", "ca", "mh", "cl", nullptr ++ {'l','c'}, {'r','c'}, {'e','c'}, {'r','s'}, {'n','o'}, ++ {'f','i'}, {'d','i'}, {'l','n'}, ++ {'p','i'}, {'s','o'}, ++ {'b','d'}, {'c','d'}, {'m','i'}, {'o','r'}, {'e','x'}, ++ {'d','o'}, {'s','u'}, {'s','g'}, ++ {'s','t'}, {'o','w'}, {'t','w'}, {'c','a'}, {'m','h'}, ++ {'c','l'} + }; + + struct color_ext_type +@@ -1675,7 +1679,7 @@ main (int argc, char **argv) + initialize_exit_failure (LS_FAILURE); + atexit (close_stdout); + +- static_assert (ARRAY_CARDINALITY (color_indicator) + 1 ++ static_assert (ARRAY_CARDINALITY (color_indicator) + == ARRAY_CARDINALITY (indicator_name)); + + exit_status = EXIT_SUCCESS; +@@ -2731,8 +2735,7 @@ parse_ls_color (void) + { + char const *p; /* Pointer to character being parsed */ + char *buf; /* color_buf buffer pointer */ +- int ind_no; /* Indicator number */ +- char label[3]; /* Indicator label */ ++ char label0, label1; /* Indicator label */ + struct color_ext_type *ext; /* Extension we are working on */ + + if ((p = getenv ("LS_COLORS")) == nullptr || *p == '\0') +@@ -2748,7 +2751,6 @@ parse_ls_color (void) + } + + ext = nullptr; +- strcpy (label, "??"); + + /* This is an overly conservative estimate, but any possible + LS_COLORS string will *not* generate a color_buf longer than +@@ -2791,7 +2793,7 @@ parse_ls_color (void) + goto done; + + default: /* Assume it is file type label */ +- label[0] = *(p++); ++ label0 = *p++; + state = PS_2; + break; + } +@@ -2800,7 +2802,7 @@ parse_ls_color (void) + case PS_2: /* Second label character */ + if (*p) + { +- label[1] = *(p++); ++ label1 = *p++; + state = PS_3; + } + else +@@ -2811,19 +2813,21 @@ parse_ls_color (void) + state = PS_FAIL; /* Assume failure... */ + if (*(p++) == '=')/* It *should* be... */ + { +- for (ind_no = 0; indicator_name[ind_no] != nullptr; ++ind_no) ++ for (int i = 0; i < ARRAY_CARDINALITY (indicator_name); i++) + { +- if (STREQ (label, indicator_name[ind_no])) ++ if ((label0 == indicator_name[i][0]) ++ && (label1 == indicator_name[i][1])) + { +- color_indicator[ind_no].string = buf; ++ color_indicator[i].string = buf; + state = (get_funky_string (&buf, &p, false, +- &color_indicator[ind_no].len) ++ &color_indicator[i].len) + ? PS_START : PS_FAIL); + break; + } + } + if (state == PS_FAIL) +- error (0, 0, _("unrecognized prefix: %s"), quote (label)); ++ error (0, 0, _("unrecognized prefix: %s"), ++ quote ((char []) {label0, label1, '\0'})); + } + break; + +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-tune-is_colored.patch b/backport-ls-tune-is_colored.patch new file mode 100644 index 0000000000000000000000000000000000000000..70e3894cbda800db835ae268aa2c253a0b475ec3 --- /dev/null +++ b/backport-ls-tune-is_colored.patch @@ -0,0 +1,37 @@ +From ec0fe0d2b84c041edae97bfd291757e9d62b7fff Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 1 Oct 2024 13:18:19 -0700 +Subject: [PATCH] ls: tune is_colored + +* src/ls.c (is_colored): Tune. +This shrinks the machine code considerably on x86-64. + + +--- + src/ls.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 8599368ef..46f8e8e41 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -1464,11 +1464,14 @@ free_pending_ent (struct pending *p) + static bool + is_colored (enum indicator_no type) + { ++ /* Return true unless the string is "", "0" or "00"; try to be efficient. */ + size_t len = color_indicator[type].len; ++ if (len == 0) ++ return false; ++ if (2 < len) ++ return true; + char const *s = color_indicator[type].string; +- return ! (len == 0 +- || (len == 1 && STRNCMP_LIT (s, "0") == 0) +- || (len == 2 && STRNCMP_LIT (s, "00") == 0)); ++ return (s[0] != '0') | (s[len - 1] != '0'); + } + + static void +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-tune-usage-of-getxattr-stat-syscalls.patch b/backport-ls-tune-usage-of-getxattr-stat-syscalls.patch new file mode 100644 index 0000000000000000000000000000000000000000..d69a5ef5f73dc9496ae279bf0471f02b8a2e2f9d --- /dev/null +++ b/backport-ls-tune-usage-of-getxattr-stat-syscalls.patch @@ -0,0 +1,521 @@ +From 2a6bed9332bd58260923f17a9fcfa372b2349db1 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 2 Oct 2024 21:11:02 -0700 +Subject: [PATCH] ls: tune usage of getxattr/stat syscalls +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Update gnulib submodule to latest. This changes the file_has_aclinfo +API, so at the same time do the following changes to ls.c, which +adjusts to these changes among other things. +* src/ls.c (filetype_d_type, d_type_filetype): New static constants. +(format_needs_capability): New static var. +(main): Set and use it. Don’t set format_needs_stat merely +because print_scontext, as we needn’t call stat to get the +scontext. Instead, set format_needs_type if print_scontext but +not format_needs_stat. +(print_dir): Use new static tables to determine filetype +more efficiently. +(file_has_aclinfo_cache): Adjust to Gnulib file_has_aclinfo API change. +(gobble_file): Check stat if format_needs_type but the type is +unknown. Be conservative, and when deciding whether to check stat +but the type is unknown, assume it might be directory. Similarly +for normal files when classifying; if the type is unknown assume +it might be normal. Use new static constants and IFTODT to +compute filetype more straightforwardly. Get ACLs and check for +capability less often. +(get_color_indicator): Omit unnecessary call to is_colored (C_CAP), +since f->has_capability can be true only if is_colored (C_CAP). + + +--- + src/ls.c | 373 +++++++++++++++++++++++++++---------------------------- + 1 file changed, 184 insertions(+), 189 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 5c75c22..ffd1487 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -180,6 +180,22 @@ static char const filetype_letter[] = + {'?', 'p', 'c', 'd', 'b', '-', 'l', 's', 'w', 'd'}; + static_assert (ARRAY_CARDINALITY (filetype_letter) == filetype_cardinality); + ++/* Map enum filetype to d_type values. */ ++static unsigned char const filetype_d_type[] = ++ { ++ DT_UNKNOWN, DT_FIFO, DT_CHR, DT_DIR, DT_BLK, DT_REG, DT_LNK, DT_SOCK, ++ DT_WHT, DT_DIR ++ }; ++static_assert (ARRAY_CARDINALITY (filetype_d_type) == filetype_cardinality); ++ ++/* Map d_type values to enum filetype. */ ++static char const d_type_filetype[UCHAR_MAX + 1] = ++ { ++ [DT_BLK] = blockdev, [DT_CHR] = chardev, [DT_DIR] = directory, ++ [DT_FIFO] = fifo, [DT_LNK] = symbolic_link, [DT_REG] = normal, ++ [DT_SOCK] = sock, [DT_WHT] = whiteout ++ }; ++ + enum acl_type + { + ACL_T_NONE, +@@ -745,6 +761,10 @@ static bool format_needs_stat; + + static bool format_needs_type; + ++/* Like 'format_needs_stat', but set only if capability colors are needed. */ ++ ++static bool format_needs_capability; ++ + /* An arbitrary limit on the number of bytes in a printed timestamp. + This is set to a relatively small value to avoid the need to worry + about denial-of-service attacks on servers that run "ls" on behalf +@@ -1711,15 +1731,14 @@ main (int argc, char **argv) + + localtz = tzalloc (getenv ("TZ")); + +- format_needs_stat = sort_type == sort_time || sort_type == sort_size +- || format == long_format +- || print_scontext +- || print_block_size; +- format_needs_type = (! format_needs_stat +- && (recursive +- || print_with_color +- || indicator_style != none +- || directories_first)); ++ format_needs_stat = ((sort_type == sort_time) | (sort_type == sort_size) ++ | (format == long_format) ++ | print_block_size | print_hyperlink); ++ format_needs_type = ((! format_needs_stat) ++ & (recursive | print_with_color | print_scontext ++ | directories_first ++ | (indicator_style != none))); ++ format_needs_capability = print_with_color && is_colored (C_CAP); + + if (dired) + { +@@ -3056,22 +3075,11 @@ print_dir (char const *name, char const *realname, bool command_line_arg) + { + if (! file_ignored (next->d_name)) + { +- enum filetype type = unknown; +- ++ enum filetype type; + #if HAVE_STRUCT_DIRENT_D_TYPE +- switch (next->d_type) +- { +- case DT_BLK: type = blockdev; break; +- case DT_CHR: type = chardev; break; +- case DT_DIR: type = directory; break; +- case DT_FIFO: type = fifo; break; +- case DT_LNK: type = symbolic_link; break; +- case DT_REG: type = normal; break; +- case DT_SOCK: type = sock; break; +-# ifdef DT_WHT +- case DT_WHT: type = whiteout; break; +-# endif +- } ++ type = d_type_filetype[next->d_type]; ++#else ++ type = unknown; + #endif + total_blocks += gobble_file (next->d_name, type, + RELIABLE_D_INO (next), +@@ -3284,7 +3292,7 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + return 0; + } + +- int n = file_has_aclinfo (file, &f->stat, ai, flags); ++ int n = file_has_aclinfo (file, ai, flags); + if (n <= 0 && errno_unsupported (ai->u.err)) + unsupported_device = f->stat.st_dev; + return n; +@@ -3345,6 +3353,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + memset (f, '\0', sizeof *f); + f->stat.st_ino = inode; + f->filetype = type; ++ f->scontext = UNKNOWN_SECURITY_CONTEXT; + + f->quoted = -1; + if ((! cwd_some_quoted) && align_variable_outer_quotes) +@@ -3355,13 +3364,14 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + cwd_some_quoted = 1; + } + +- if (command_line_arg ++ bool check_stat = ++ (command_line_arg + || print_hyperlink + || format_needs_stat +- /* When coloring a directory (we may know the type from +- direct.d_type), we have to stat it in order to indicate ++ || (format_needs_type && type == unknown) ++ /* When coloring a directory, stat it to indicate + sticky and/or other-writable attributes. */ +- || (type == directory && print_with_color ++ || ((type == directory || type == unknown) && print_with_color + && (is_colored (C_OTHER_WRITABLE) + || is_colored (C_STICKY) + || is_colored (C_STICKY_OTHER_WRITABLE))) +@@ -3374,40 +3384,34 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + /* Command line dereferences are already taken care of by the above + assertion that the inode number is not yet known. */ + || (print_inode && inode == NOT_AN_INODE_NUMBER) +- || (format_needs_type +- && (type == unknown +- /* --indicator-style=classify (aka -F) +- requires that we stat each regular file +- to see if it's executable. */ +- || (type == normal && (indicator_style == classify +- /* This is so that --color ends up +- highlighting files with these mode +- bits set even when options like -F are +- not specified. Note we do a redundant +- stat in the very unlikely case where +- C_CAP is set but not the others. */ +- || (print_with_color +- && (is_colored (C_EXEC) +- || is_colored (C_SETUID) +- || is_colored (C_SETGID) +- || is_colored (C_CAP))) +- ))))) ++ /* --indicator-style=classify (aka -F) requires statting each ++ regular file to see whether it's executable. */ ++ || ((type == normal || type == unknown) ++ && (indicator_style == classify ++ /* This is so that --color ends up highlighting files with these ++ mode bits set even when options like -F are not specified. */ ++ || (print_with_color && (is_colored (C_EXEC) ++ || is_colored (C_SETUID) ++ || is_colored (C_SETGID)))))); ++ ++ /* Absolute name of this file, if needed. */ ++ char const *full_name = name; ++ if (check_stat | print_scontext | format_needs_capability ++ && name[0] != '/' && dirname) ++ { ++ char *p = alloca (strlen (name) + strlen (dirname) + 2); ++ attach (p, dirname, name); ++ full_name = p; ++ } ++ ++ bool do_deref; + ++ if (!check_stat) ++ do_deref = dereference == DEREF_ALWAYS; ++ else + { +- /* Absolute name of this file. */ +- char const *full_name; +- bool do_deref; + int err; + +- if (name[0] == '/' || !dirname) +- full_name = name; +- else +- { +- char *p = alloca (strlen (name) + strlen (dirname) + 2); +- attach (p, dirname, name); +- full_name = p; +- } +- + if (print_hyperlink) + { + f->absolute_name = canonicalize_filename_mode (full_name, +@@ -3463,8 +3467,6 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + file_failure (command_line_arg, + _("cannot access %s"), full_name); + +- f->scontext = UNKNOWN_SECURITY_CONTEXT; +- + if (command_line_arg) + return 0; + +@@ -3475,155 +3477,148 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + } + + f->stat_ok = true; ++ f->filetype = type = d_type_filetype[IFTODT (f->stat.st_mode)]; ++ } + +- /* has_capability adds around 30% runtime to 'ls --color', +- so call it only if really needed. Note capability coloring +- is disabled in the default color config. */ +- if ((type == normal || S_ISREG (f->stat.st_mode)) +- && print_with_color && is_colored (C_CAP)) +- f->has_capability = has_capability_cache (full_name, f); ++ if (type == directory && command_line_arg && !immediate_dirs) ++ f->filetype = type = arg_directory; + +- if (format == long_format || print_scontext) +- { +- struct aclinfo ai; +- int n = file_has_aclinfo_cache (full_name, f, &ai, +- do_deref ? ACL_SYMLINK_FOLLOW : 0); +- bool have_acl = 0 < n; +- bool have_scontext = !ai.scontext_err; +- f->acl_type = (!have_scontext && !have_acl +- ? ACL_T_NONE +- : (have_scontext && !have_acl +- ? ACL_T_LSM_CONTEXT_ONLY +- : ACL_T_YES)); +- any_has_acl |= f->acl_type != ACL_T_NONE; +- +- if (format == long_format && n < 0) +- error (0, ai.u.err, "%s", quotef (full_name)); +- else +- { +- /* When requesting security context information, don't make +- ls fail just because the file (even a command line argument) +- isn't on the right type of file system. I.e., a getfilecon +- failure isn't in the same class as a stat failure. */ +- if (print_scontext +- && (! (is_ENOTSUP (ai.scontext_err) +- || ai.scontext_err == ENODATA))) +- error (0, ai.scontext_err, "%s", quotef (full_name)); +- } ++ bool check_capability = format_needs_capability & (type == normal); + +- f->scontext = ai.scontext; +- ai.scontext = nullptr; +- aclinfo_free (&ai); ++ if ((format == long_format) | print_scontext | check_capability) ++ { ++ struct aclinfo ai; ++ int n = file_has_aclinfo_cache (full_name, f, &ai, ++ ((do_deref ? ACL_SYMLINK_FOLLOW : 0) ++ | filetype_d_type[type])); ++ bool have_acl = 0 < n; ++ bool have_scontext = !ai.scontext_err; ++ f->acl_type = (!have_scontext && !have_acl ++ ? ACL_T_NONE ++ : (have_scontext && !have_acl ++ ? ACL_T_LSM_CONTEXT_ONLY ++ : ACL_T_YES)); ++ any_has_acl |= f->acl_type != ACL_T_NONE; ++ ++ if (format == long_format && n < 0) ++ error (0, ai.u.err, "%s", quotef (full_name)); ++ else ++ { ++ /* When requesting security context information, don't make ++ ls fail just because the file (even a command line argument) ++ isn't on the right type of file system. I.e., a getfilecon ++ failure isn't in the same class as a stat failure. */ ++ if (print_scontext ++ && (! (is_ENOTSUP (ai.scontext_err) ++ || ai.scontext_err == ENODATA))) ++ error (0, ai.scontext_err, "%s", quotef (full_name)); + } + +- if (S_ISLNK (f->stat.st_mode) +- && (format == long_format || check_symlink_mode)) +- { +- struct stat linkstats; ++ /* has_capability adds around 30% runtime to 'ls --color', ++ so call it only if really needed. Note capability coloring ++ is disabled in the default color config. */ ++ if (check_capability && aclinfo_has_xattr (&ai, XATTR_NAME_CAPS)) ++ f->has_capability = has_capability_cache (full_name, f); ++ ++ f->scontext = ai.scontext; ++ ai.scontext = nullptr; ++ aclinfo_free (&ai); ++ } + +- get_link_name (full_name, f, command_line_arg); ++ if ((type == symbolic_link) ++ & ((format == long_format) | check_symlink_mode)) ++ { ++ struct stat linkstats; + +- /* Use the slower quoting path for this entry, though +- don't update CWD_SOME_QUOTED since alignment not affected. */ +- if (f->linkname && f->quoted == 0 && needs_quoting (f->linkname)) +- f->quoted = -1; ++ get_link_name (full_name, f, command_line_arg); + +- /* Avoid following symbolic links when possible, i.e., when +- they won't be traced and when no indicator is needed. */ +- if (f->linkname +- && (file_type <= indicator_style || check_symlink_mode) +- && stat_for_mode (full_name, &linkstats) == 0) +- { +- f->linkok = true; +- f->linkmode = linkstats.st_mode; +- } +- } ++ /* Use the slower quoting path for this entry, though ++ don't update CWD_SOME_QUOTED since alignment not affected. */ ++ if (f->linkname && f->quoted == 0 && needs_quoting (f->linkname)) ++ f->quoted = -1; + +- if (S_ISLNK (f->stat.st_mode)) +- f->filetype = symbolic_link; +- else if (S_ISDIR (f->stat.st_mode)) ++ /* Avoid following symbolic links when possible, i.e., when ++ they won't be traced and when no indicator is needed. */ ++ if (f->linkname ++ && (file_type <= indicator_style || check_symlink_mode) ++ && stat_for_mode (full_name, &linkstats) == 0) + { +- if (command_line_arg && !immediate_dirs) +- f->filetype = arg_directory; +- else +- f->filetype = directory; ++ f->linkok = true; ++ f->linkmode = linkstats.st_mode; + } +- else +- f->filetype = normal; ++ } + +- blocks = ST_NBLOCKS (f->stat); +- if (format == long_format || print_block_size) ++ blocks = ST_NBLOCKS (f->stat); ++ if (format == long_format || print_block_size) ++ { ++ char buf[LONGEST_HUMAN_READABLE + 1]; ++ int len = mbswidth (human_readable (blocks, buf, human_output_opts, ++ ST_NBLOCKSIZE, output_block_size), ++ 0); ++ if (block_size_width < len) ++ block_size_width = len; ++ } ++ ++ if (format == long_format) ++ { ++ if (print_owner) + { +- char buf[LONGEST_HUMAN_READABLE + 1]; +- int len = mbswidth (human_readable (blocks, buf, human_output_opts, +- ST_NBLOCKSIZE, output_block_size), +- 0); +- if (block_size_width < len) +- block_size_width = len; ++ int len = format_user_width (f->stat.st_uid); ++ if (owner_width < len) ++ owner_width = len; ++ } ++ if (print_group) ++ { ++ int len = format_group_width (f->stat.st_gid); ++ if (group_width < len) ++ group_width = len; + } + +- if (format == long_format) ++ if (print_author) + { +- if (print_owner) +- { +- int len = format_user_width (f->stat.st_uid); +- if (owner_width < len) +- owner_width = len; +- } ++ int len = format_user_width (f->stat.st_author); ++ if (author_width < len) ++ author_width = len; ++ } ++ } + +- if (print_group) +- { +- int len = format_group_width (f->stat.st_gid); +- if (group_width < len) +- group_width = len; +- } ++ if (print_scontext) ++ { ++ int len = strlen (f->scontext); ++ if (scontext_width < len) ++ scontext_width = len; ++ } + +- if (print_author) +- { +- int len = format_user_width (f->stat.st_author); +- if (author_width < len) +- author_width = len; +- } +- } ++ if (format == long_format) ++ { ++ char b[INT_BUFSIZE_BOUND (uintmax_t)]; ++ int b_len = strlen (umaxtostr (f->stat.st_nlink, b)); ++ if (nlink_width < b_len) ++ nlink_width = b_len; + +- if (print_scontext) ++ if ((type == chardev) | (type == blockdev)) + { +- int len = strlen (f->scontext); +- if (scontext_width < len) +- scontext_width = len; ++ char buf[INT_BUFSIZE_BOUND (uintmax_t)]; ++ int len = strlen (umaxtostr (major (f->stat.st_rdev), buf)); ++ if (major_device_number_width < len) ++ major_device_number_width = len; ++ len = strlen (umaxtostr (minor (f->stat.st_rdev), buf)); ++ if (minor_device_number_width < len) ++ minor_device_number_width = len; ++ len = major_device_number_width + 2 + minor_device_number_width; ++ if (file_size_width < len) ++ file_size_width = len; + } +- +- if (format == long_format) ++ else + { +- char b[INT_BUFSIZE_BOUND (uintmax_t)]; +- int b_len = strlen (umaxtostr (f->stat.st_nlink, b)); +- if (nlink_width < b_len) +- nlink_width = b_len; +- +- if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode)) +- { +- char buf[INT_BUFSIZE_BOUND (uintmax_t)]; +- int len = strlen (umaxtostr (major (f->stat.st_rdev), buf)); +- if (major_device_number_width < len) +- major_device_number_width = len; +- len = strlen (umaxtostr (minor (f->stat.st_rdev), buf)); +- if (minor_device_number_width < len) +- minor_device_number_width = len; +- len = major_device_number_width + 2 + minor_device_number_width; +- if (file_size_width < len) +- file_size_width = len; +- } +- else +- { +- char buf[LONGEST_HUMAN_READABLE + 1]; +- uintmax_t size = unsigned_file_size (f->stat.st_size); +- int len = mbswidth (human_readable (size, buf, +- file_human_output_opts, +- 1, file_output_block_size), +- 0); +- if (file_size_width < len) +- file_size_width = len; +- } ++ char buf[LONGEST_HUMAN_READABLE + 1]; ++ uintmax_t size = unsigned_file_size (f->stat.st_size); ++ int len = mbswidth (human_readable (size, buf, ++ file_human_output_opts, ++ 1, file_output_block_size), ++ 0); ++ if (file_size_width < len) ++ file_size_width = len; + } + } + +@@ -4955,7 +4950,7 @@ get_color_indicator (const struct fileinfo *f, bool symlink_target) + type = C_SETUID; + else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) + type = C_SETGID; +- else if (is_colored (C_CAP) && f->has_capability) ++ else if (f->has_capability) + type = C_CAP; + else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC)) + type = C_EXEC; +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-update-comment.patch b/backport-ls-update-comment.patch new file mode 100644 index 0000000000000000000000000000000000000000..8e7c713f4cabcc807d3d1f5b2b30380e0e259329 --- /dev/null +++ b/backport-ls-update-comment.patch @@ -0,0 +1,27 @@ +From f169dc8b68610030ee8d00907fa4b3290a1ae204 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 8 Jan 2025 11:13:54 -0800 +Subject: [PATCH] ls: update comment + +* src/ls.c (file_has_aclinfo_cache): Fix comment. + + +--- + src/ls.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/ls.c b/src/ls.c +index 0cdaacb93..a42c7707a 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3295,7 +3295,7 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + struct aclinfo *ai, int flags) + { + /* st_dev and associated info for the most recently processed device +- for which file_has_acl failed indicating lack of support. */ ++ for which file_has_aclinfo failed indicating lack of support. */ + static int unsupported_return; + static char *unsupported_scontext; + static int unsupported_scontext_err; +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-use-Gnulib-s-unsupported-errno-list.patch b/backport-ls-use-Gnulib-s-unsupported-errno-list.patch new file mode 100644 index 0000000000000000000000000000000000000000..7171a04430d474130119e32ab12e3ee7d82a4d56 --- /dev/null +++ b/backport-ls-use-Gnulib-s-unsupported-errno-list.patch @@ -0,0 +1,72 @@ +From e9d294afb987d692a133458c14ca4b2c8eed5d77 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 7 Nov 2024 08:57:42 -0800 +Subject: [PATCH] =?UTF-8?q?ls:=20use=20Gnulib=E2=80=99s=20unsupported=20er?= + =?UTF-8?q?rno=20list?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* bootstrap.conf (gnulib_modules): Add acl-permissions, which +supplies acl_errno_valid, and which we are already using +indirectly via file-has-acl. +* src/ls.c (errno_unsupported): Remove. All calls replaced +by !acl_errno_valid. + + +--- + bootstrap.conf | 1 + + src/ls.c | 12 ++---------- + 2 files changed, 3 insertions(+), 10 deletions(-) + +diff --git a/bootstrap.conf b/bootstrap.conf +index be9b8b7fd..480e02931 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -28,6 +28,7 @@ avoided_gnulib_modules=' + gnulib_modules=" + $avoided_gnulib_modules + acl ++ acl-permissions + alignalloc + alignasof + alloca +diff --git a/src/ls.c b/src/ls.c +index 290ce35d2..962737432 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3277,14 +3277,6 @@ clear_files (void) + file_size_width = 0; + } + +-/* Return true if ERR implies lack-of-support failure by a +- getxattr-calling function like file_has_acl. */ +-static bool +-errno_unsupported (int err) +-{ +- return (err == EINVAL || err == ENOSYS || is_ENOTSUP (err)); +-} +- + /* Cache file_has_aclinfo failure, when it's trivial to do. + Like file_has_aclinfo, but when F's st_dev says it's on a file + system lacking ACL support, return 0 with ENOTSUP immediately. */ +@@ -3303,7 +3295,7 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, + } + + int n = file_has_aclinfo (file, ai, flags); +- if (n <= 0 && errno_unsupported (ai->u.err)) ++ if (n <= 0 && !acl_errno_valid (ai->u.err)) + unsupported_device = f->stat.st_dev; + return n; + } +@@ -3325,7 +3317,7 @@ has_capability_cache (char const *file, struct fileinfo *f) + } + + bool b = has_capability (file); +- if ( !b && errno_unsupported (errno)) ++ if ( !b && !acl_errno_valid (errno)) + unsupported_device = f->stat.st_dev; + return b; + } +-- +2.43.0 \ No newline at end of file diff --git a/backport-ls-use-fewer-xattr-related-syscalls.patch b/backport-ls-use-fewer-xattr-related-syscalls.patch new file mode 100644 index 0000000000000000000000000000000000000000..1c42c6cc050c228970caea9e1f6ead0deef67fff --- /dev/null +++ b/backport-ls-use-fewer-xattr-related-syscalls.patch @@ -0,0 +1,219 @@ +From 4ce432ad8738387f1b2e80e883dc7080df3afabe Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sun, 29 Sep 2024 22:04:02 -0700 +Subject: [PATCH] ls: use fewer xattr-related syscalls + +* src/ls.c: Do not include or "smack.h". +Include if HAVE_LINUX_ATTR_H, for XATTR_NAME_CAPS. +(free_ent): Use aclinfo_scontext_free to free f->scontext. +(getfilecon_cache): Remove; no longer needed. +(file_has_aclinfo_cache): Rename from file_has_acl_cache, +and use file_has_aclinfo instead of file_has_acl. All uses changed. +(gobble_file): Use file_has_aclinfo instead of file_has_acl, so +that we get more info about the file before deciding whether to +issue further syscalls for it. Let file_has_aclinfo worry about +smack and SELinux. Call has_capability only if the xattr list +mentions XATTR_NAME_CAPS. + + +--- + src/ls.c | 123 ++++++++++++++++++------------------------------------- + 1 file changed, 40 insertions(+), 83 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 465f35d19..099893f86 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -55,7 +55,6 @@ + #include + #include + #include +-#include + #include + + #if HAVE_LANGINFO_CODESET +@@ -101,7 +100,6 @@ + #include "mpsort.h" + #include "obstack.h" + #include "quote.h" +-#include "smack.h" + #include "stat-size.h" + #include "stat-time.h" + #include "strftime.h" +@@ -122,6 +120,10 @@ + # include + #endif + ++#if HAVE_LINUX_XATTR_H ++# include ++#endif ++ + #define PROGRAM_NAME (ls_mode == LS_LS ? "ls" \ + : (ls_mode == LS_MULTI_COL \ + ? "dir" : "vdir")) +@@ -3238,12 +3240,7 @@ free_ent (struct fileinfo *f) + free (f->linkname); + free (f->absolute_name); + if (f->scontext != UNKNOWN_SECURITY_CONTEXT) +- { +- if (is_smack_enabled ()) +- free (f->scontext); +- else +- freecon (f->scontext); +- } ++ aclinfo_scontext_free (f->scontext); + } + + /* Empty the table of files. */ +@@ -3272,48 +3269,19 @@ clear_files (void) + } + + /* Return true if ERR implies lack-of-support failure by a +- getxattr-calling function like getfilecon or file_has_acl. */ ++ getxattr-calling function like file_has_acl. */ + static bool + errno_unsupported (int err) + { + return (err == EINVAL || err == ENOSYS || is_ENOTSUP (err)); + } + +-/* Cache *getfilecon failure, when it's trivial to do so. +- Like getfilecon/lgetfilecon, but when F's st_dev says it's doesn't +- support getting the security context, fail with ENOTSUP immediately. */ +-static int +-getfilecon_cache (char const *file, struct fileinfo *f, bool deref) +-{ +- /* st_dev of the most recently processed device for which we've +- found that [l]getfilecon fails indicating lack of support. */ +- static dev_t unsupported_device; +- +- if (f->stat.st_dev == unsupported_device) +- { +- errno = ENOTSUP; +- return -1; +- } +- int r = 0; +-#ifdef HAVE_SMACK +- if (is_smack_enabled ()) +- r = smack_new_label_from_path (file, "security.SMACK64", deref, +- &f->scontext); +- else +-#endif +- r = (deref +- ? getfilecon (file, &f->scontext) +- : lgetfilecon (file, &f->scontext)); +- if (r < 0 && errno_unsupported (errno)) +- unsupported_device = f->stat.st_dev; +- return r; +-} +- +-/* Cache file_has_acl failure, when it's trivial to do. +- Like file_has_acl, but when F's st_dev says it's on a file ++/* Cache file_has_aclinfo failure, when it's trivial to do. ++ Like file_has_aclinfo, but when F's st_dev says it's on a file + system lacking ACL support, return 0 with ENOTSUP immediately. */ + static int +-file_has_acl_cache (char const *file, struct fileinfo *f) ++file_has_aclinfo_cache (char const *file, struct fileinfo *f, ++ struct aclinfo *ai, int flags) + { + /* st_dev of the most recently processed device for which we've + found that file_has_acl fails indicating lack of support. */ +@@ -3325,11 +3293,8 @@ file_has_acl_cache (char const *file, struct fileinfo *f) + return 0; + } + +- /* Zero errno so that we can distinguish between two 0-returning cases: +- "has-ACL-support, but only a default ACL" and "no ACL support". */ +- errno = 0; +- int n = file_has_acl (file, &f->stat); +- if (n <= 0 && errno_unsupported (errno)) ++ int n = file_has_aclinfo (file, &f->stat, ai, flags); ++ if (n <= 0 && errno_unsupported (ai->u.err)) + unsupported_device = f->stat.st_dev; + return n; + } +@@ -3519,53 +3484,45 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + + f->stat_ok = true; + +- /* Note has_capability() adds around 30% runtime to 'ls --color' */ +- if ((type == normal || S_ISREG (f->stat.st_mode)) +- && print_with_color && is_colored (C_CAP)) +- f->has_capability = has_capability_cache (full_name, f); +- + if (format == long_format || print_scontext) + { +- bool have_scontext = false; +- bool have_acl = false; +- int attr_len = getfilecon_cache (full_name, f, do_deref); +- err = (attr_len < 0); ++ struct aclinfo ai; ++ int n = file_has_aclinfo_cache (full_name, f, &ai, ++ do_deref ? ACL_SYMLINK_FOLLOW : 0); ++ bool have_acl = 0 < n; ++ bool have_scontext = !ai.scontext_err; ++ f->acl_type = (!have_scontext && !have_acl ++ ? ACL_T_NONE ++ : (have_scontext && !have_acl ++ ? ACL_T_LSM_CONTEXT_ONLY ++ : ACL_T_YES)); ++ any_has_acl |= f->acl_type != ACL_T_NONE; + +- if (err == 0) +- { +- if (is_smack_enabled ()) +- have_scontext = ! STREQ ("_", f->scontext); +- else +- have_scontext = ! STREQ ("unlabeled", f->scontext); +- } ++ if (format == long_format && n < 0) ++ error (0, ai.u.err, "%s", quotef (full_name)); + else + { +- f->scontext = UNKNOWN_SECURITY_CONTEXT; +- + /* When requesting security context information, don't make + ls fail just because the file (even a command line argument) + isn't on the right type of file system. I.e., a getfilecon + failure isn't in the same class as a stat failure. */ +- if (is_ENOTSUP (errno) || errno == ENODATA) +- err = 0; ++ if (print_scontext ++ && (! (is_ENOTSUP (ai.scontext_err) ++ || ai.scontext_err == ENODATA))) ++ error (0, ai.scontext_err, "%s", quotef (full_name)); + } + +- if (err == 0 && format == long_format) +- { +- int n = file_has_acl_cache (full_name, f); +- err = (n < 0); +- have_acl = (0 < n); +- } +- +- f->acl_type = (!have_scontext && !have_acl +- ? ACL_T_NONE +- : (have_scontext && !have_acl +- ? ACL_T_LSM_CONTEXT_ONLY +- : ACL_T_YES)); +- any_has_acl |= f->acl_type != ACL_T_NONE; +- +- if (err) +- error (0, errno, "%s", quotef (full_name)); ++ /* has_capability adds around 30% runtime to 'ls --color', ++ so call it only if really needed. */ ++ if (0 < ai.size ++ && (type == normal || S_ISREG (f->stat.st_mode)) ++ && print_with_color && is_colored (C_CAP) ++ && aclinfo_has_xattr (&ai, XATTR_NAME_CAPS)) ++ f->has_capability = has_capability_cache (full_name, f); ++ ++ f->scontext = ai.scontext; ++ ai.scontext = nullptr; ++ aclinfo_free (&ai); + } + + if (S_ISLNK (f->stat.st_mode) +-- +2.43.0 \ No newline at end of file diff --git a/backport-od-don-t-assume-no-holes-in-wide-unsigned.patch b/backport-od-don-t-assume-no-holes-in-wide-unsigned.patch new file mode 100644 index 0000000000000000000000000000000000000000..07df407f556d9f08778b787d1d9ac909f86c716c --- /dev/null +++ b/backport-od-don-t-assume-no-holes-in-wide-unsigned.patch @@ -0,0 +1,55 @@ +From dbe4e2f42830fd5f90391472802309bbc9d1a8ef Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 24 Jun 2025 20:01:04 -0700 +Subject: [PATCH] =?UTF-8?q?od:=20don=E2=80=99t=20assume=20no=20holes=20in?= + =?UTF-8?q?=20wide=20unsigned?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Also, fix minor related typos. +* src/od.c (MAX_INTEGRAL_TYPE_SIZE, MAX_ADDRESS_LENGTH): +Now a constant, not a macro. +(MAX_INTEGRAL_TYPE_WIDTH): New constant. Use it instead of +CHAR_BIT, so as not to assume that uintmax_t and unsigned long +long int are hole-free. This doesn’t matter on practical porting +targets, though there is still a mainframe or two that have holes. +(FMT_BYTES_ALLOCATED): Fix typo by changing "jd" to "jo". +Fix off-by-one typo in static assertion. +--- + src/od.c | 8 +++++----- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/od.c b/src/od.c +index c1cdd310a4..8781a2258b 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -75,7 +75,8 @@ enum output_format + CHARACTER + }; + +-#define MAX_INTEGRAL_TYPE_SIZE sizeof (unsigned_long_long_int) ++enum { MAX_INTEGRAL_TYPE_SIZE = sizeof (unsigned_long_long_int) }; ++enum { MAX_INTEGRAL_TYPE_WIDTH = ULLONG_WIDTH }; + + /* The maximum number of bytes needed for a format string, including + the trailing nul. Each format string expects a variable amount of +@@ -93,7 +94,7 @@ enum + }; + + /* Ensure that our choice for FMT_BYTES_ALLOCATED is reasonable. */ +-static_assert (MAX_INTEGRAL_TYPE_SIZE * CHAR_BIT / 3 <= 99); ++static_assert (MAX_INTEGRAL_TYPE_WIDTH <= 3 * 99); + + /* Each output format specification (from '-t spec' or from + old-style options) is represented by one of these structures. */ +@@ -180,8 +181,7 @@ static int address_base; + + /* The number of octal digits required to represent the largest + address value. */ +-#define MAX_ADDRESS_LENGTH \ +- ((sizeof (uintmax_t) * CHAR_BIT + CHAR_BIT - 1) / 3) ++enum { MAX_ADDRESS_LENGTH = UINTMAX_WIDTH / 3 + (UINTMAX_WIDTH % 3 != 0) }; + + /* Width of a normal address. */ + static int address_pad_len; diff --git a/backport-od-fix-N.-bug.patch b/backport-od-fix-N.-bug.patch new file mode 100644 index 0000000000000000000000000000000000000000..9e4a5e838bc939a10e99e9cc80fa104e25662401 --- /dev/null +++ b/backport-od-fix-N.-bug.patch @@ -0,0 +1,88 @@ +From 66464e61f549e9f2fd35f82567345721798288f9 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 28 Jun 2025 17:29:22 -0700 +Subject: [PATCH] od: fix '+N.' bug + +* src/od.c (parse_old_offset): First arg is now char *, +not char const *. If a decimal number, temporarily +modify the string so that xstrtoumax does not complain +about the '.'. +* tests/od/od.pl: Test for the bug. +--- + src/od.c | 31 +++++++++++++++++++++++++++---- + tests/od/od.pl | 4 ++++ + 2 files changed, 31 insertions(+), 4 deletions(-) + +diff --git a/src/od.c b/src/od.c +index 8bb463ca8b..fd5fc45bd4 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -1402,7 +1402,7 @@ get_lcm (void) + leading '+' return true and set *OFFSET to the offset it denotes. */ + + static bool +-parse_old_offset (char const *s, uintmax_t *offset) ++parse_old_offset (char *s, uintmax_t *offset) + { + int radix; + +@@ -1414,10 +1414,24 @@ parse_old_offset (char const *s, uintmax_t *offset) + ++s; + + /* Determine the radix we'll use to interpret S. If there is a '.', ++ optionally followed by 'B' or 'b' and then end of string, + it's decimal, otherwise, if the string begins with '0X'or '0x', + it's hexadecimal, else octal. */ +- if (strchr (s, '.') != nullptr) +- radix = 10; ++ char *dot = strchr (s, '.'); ++ if (dot) ++ { ++ bool b = dot[1] == 'B' || dot[1] == 'b'; ++ if (dot[b + 1]) ++ dot = nullptr; ++ } ++ ++ if (dot) ++ { ++ /* Temporarily remove the '.' from the decimal string. */ ++ dot[0] = dot[1]; ++ dot[1] = '\0'; ++ radix = 10; ++ } + else + { + if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) +@@ -1426,7 +1440,16 @@ parse_old_offset (char const *s, uintmax_t *offset) + radix = 8; + } + +- return xstrtoumax (s, nullptr, radix, offset, "Bb") == LONGINT_OK; ++ enum strtol_error s_err = xstrtoumax (s, nullptr, radix, offset, "Bb"); ++ ++ if (dot) ++ { ++ /* Restore the decimal string's original value. */ ++ dot[1] = dot[0]; ++ dot[0] = '.'; ++ } ++ ++ return s_err == LONGINT_OK; + } + + /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the +diff --git a/tests/od/od.pl b/tests/od/od.pl +index 5bb271e607..9688607c6e 100755 +--- a/tests/od/od.pl ++++ b/tests/od/od.pl +@@ -60,6 +60,10 @@ + ['j-proc', "-An -c -j $proc_file_byte_count $proc_file", + {IN=>{f2=>'e'}}, {OUT=>" e\n"}], + ++ # Check that the traditional form '+N.' works, as per POSIX. ++ ['trad-dot1', '+1.', {IN_PIPE=>'a'}, {OUT=>"0000001\n"}], ++ ['trad-dot512', '+1.b', {IN_PIPE => 'a' x 512}, {OUT=>"0001000\n"}], ++ + # Ensure that a large width does not cause trouble. + # From coreutils-7.0 through coreutils-8.21, these would print + # approximately 128KiB of padding. diff --git a/backport-od-fix-another-off-by-one-issue-with-strings.patch b/backport-od-fix-another-off-by-one-issue-with-strings.patch new file mode 100644 index 0000000000000000000000000000000000000000..c0110f193984f3291f1758e3e41849ab2ab0ede6 --- /dev/null +++ b/backport-od-fix-another-off-by-one-issue-with-strings.patch @@ -0,0 +1,28 @@ +From 69b07cc58de0a86f7b06d6709049077c6bd486ea Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 28 Jun 2025 08:07:54 -0700 +Subject: [PATCH] od: fix another off-by-one issue with --strings + +* src/od.c (main): Fix off-by-one error in string_min limit. +--- + src/od.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/od.c b/src/od.c +index 7306b0f9ad..439f71e5b5 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -1738,10 +1738,10 @@ main (int argc, char **argv) + if (s_err != LONGINT_OK) + xstrtol_fatal (s_err, oi, c, long_options, optarg); + +- /* The minimum string length may be no larger than ++ /* The minimum string length must be less than + MIN (IDX_MAX, SIZE_MAX), since we may allocate a +- buffer of this size. */ +- if (MIN (IDX_MAX, SIZE_MAX) < tmp) ++ buffer of this size + 1. */ ++ if (MIN (IDX_MAX, SIZE_MAX) <= tmp) + error (EXIT_FAILURE, 0, _("%s is too large"), quote (optarg)); + + string_min = tmp; diff --git a/backport-od-fix-integer-overflow-with-large-pseudos.patch b/backport-od-fix-integer-overflow-with-large-pseudos.patch new file mode 100644 index 0000000000000000000000000000000000000000..b9fe88da5ee29e1586894218d57a0e1e46bb12de --- /dev/null +++ b/backport-od-fix-integer-overflow-with-large-pseudos.patch @@ -0,0 +1,28 @@ +From d5ea5e8aed304337489a4dbbaf0f00d40bd92fc6 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 28 Jun 2025 17:36:04 -0700 +Subject: [PATCH] od: fix integer overflow with large pseudos + +* src/od.c (format_address_label): Diagnose overflow. +--- + src/od.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/od.c b/src/od.c +index b3af4c72f7..c3c76cc866 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -1227,7 +1227,12 @@ static void + format_address_label (intmax_t address, char c) + { + format_address_std (address, ' '); +- format_address_paren (address + pseudo_offset, c); ++ ++ intmax_t addr; ++ if (ckd_add (&addr, address, pseudo_offset)) ++ error (EXIT_FAILURE, 0, _("pseudo address too large for input")); ++ ++ format_address_paren (addr, c); + } + + /* Write N_BYTES bytes from CURR_BLOCK to standard output once for each diff --git a/backport-od-fix-some-unlikely-integer-overflows.patch b/backport-od-fix-some-unlikely-integer-overflows.patch new file mode 100644 index 0000000000000000000000000000000000000000..8beeba1312984ec6529c2f63e63d09570641b2da --- /dev/null +++ b/backport-od-fix-some-unlikely-integer-overflows.patch @@ -0,0 +1,314 @@ +From 88f30ee0a5c355701914d4446dc7ec729a344fa2 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 25 Jun 2025 23:22:37 -0700 +Subject: [PATCH] od: fix some unlikely integer overflows + +* src/od.c (print_n_spaces, pad_at, pad_at_overflow): +New static functions. +(struct tspec, PRINT_FIELDS, print_named_ascii, print_ascii) +(decode_one_format, write_block, main): +Use idx_t, not int, for counts that depend on the number +of bytes in an object. +(decode_one_format): Use print_n_spaces to output spaces. +(PRINT_FIELDS, print_named_ascii, print_ascii): +Use pad_at to avoid integer overflow. +(write_block): Do not use %*s to pad, as the total pad might +exceed INT_MAX. Instead, pad by hand with putchar (' '). +(main): Use pad_at_overflow to report integer overflow due to +oversize -w. Use better way to tell whether -w is used, +without needing IF_LINT. +* tests/od/big-w.sh: New test. +* tests/local.mk (all_tests): Add it. +--- + src/od.c | 97 +++++++++++++++++++++++++++++++---------------- + tests/local.mk | 1 + + tests/od/big-w.sh | 43 +++++++++++++++++++++ + 3 files changed, 108 insertions(+), 33 deletions(-) + create mode 100755 tests/od/big-w.sh + +diff --git a/src/od.c b/src/od.c +index f385d6ce54..8bb463ca8b 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -107,11 +107,11 @@ struct tspec + leading space, and PAD is total pad to divide among FIELDS. + PAD is at least as large as FIELDS. */ + void (*print_function) (idx_t fields, idx_t blank, void const *data, +- char const *fmt, int width, int pad); ++ char const *fmt, int width, idx_t pad); + char fmt_string[FMT_BYTES_ALLOCATED]; /* Of the style "%*d". */ + bool hexl_mode_trailer; + int field_width; /* Minimum width of a field, excluding leading space. */ +- int pad_width; /* Total padding to be divided among fields. */ ++ idx_t pad_width; /* Total padding to be divided among fields. */ + }; + + /* Convert the number of 8-bit bytes of a binary representation to +@@ -428,16 +428,44 @@ Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\ + + /* Define the print functions. */ + ++/* Print N spaces, where 0 <= N. ++ Do not rely on printf ("%*s", N, "") as N may exceed INT_MAX. */ ++static void ++print_n_spaces (intmax_t n) ++{ ++ for (; 0 < n; n--) ++ putchar (' '); ++} ++ ++/* If there are FIELDS fields, return the total padding up to the ++ start of field I, where I < FIELDS. PAD is the total padding for ++ all fields. The result equals (PAD * I) / FIELDS, except it does ++ not suffer from internal overflow. */ ++static idx_t ++pad_at (idx_t fields, idx_t i, idx_t pad) ++{ ++ /* This implementation assumes that (FIELDS - 1)^2 does not overflow ++ intmax_t, an assumption checked by pad_at_overflow. */ ++ intmax_t m = pad % fields; ++ return pad / fields * i + m * i / fields; ++} ++static bool ++pad_at_overflow (idx_t fields) ++{ ++ intmax_t product; ++ return ckd_mul (&product, fields - 1, fields - 1); ++} ++ + #define PRINT_FIELDS(N, T, FMT_STRING_DECL, ACTION) \ + static void \ + N (idx_t fields, idx_t blank, void const *block, \ +- FMT_STRING_DECL, int width, int pad) \ ++ FMT_STRING_DECL, int width, idx_t pad) \ + { \ + T const *p = block; \ +- int pad_remaining = pad; \ ++ idx_t pad_remaining = pad; \ + for (idx_t i = fields; blank < i; i--) \ + { \ +- int next_pad = pad * (i - 1) / fields; \ ++ idx_t next_pad = pad_at (fields, i - 1, pad); \ + int adjusted_width = pad_remaining - next_pad + width; \ + T x; \ + if (input_swap && sizeof (T) > 1) \ +@@ -499,13 +527,12 @@ dump_hexl_mode_trailer (idx_t n_bytes, char const *block) + static void + print_named_ascii (idx_t fields, idx_t blank, void const *block, + MAYBE_UNUSED char const *unused_fmt_string, +- int width, int pad) ++ int width, idx_t pad) + { + unsigned char const *p = block; +- int pad_remaining = pad; ++ idx_t pad_remaining = pad; + for (idx_t i = fields; blank < i; i--) + { +- int next_pad = pad * (i - 1) / fields; + int masked_c = *p++ & 0x7f; + char const *s; + char buf[2]; +@@ -521,7 +548,9 @@ print_named_ascii (idx_t fields, idx_t blank, void const *block, + s = buf; + } + +- xprintf ("%*s", pad_remaining - next_pad + width, s); ++ idx_t next_pad = pad_at (fields, i - 1, pad); ++ int adjusted_width = pad_remaining - next_pad + width; ++ xprintf ("%*s", adjusted_width, s); + pad_remaining = next_pad; + } + } +@@ -529,13 +558,12 @@ print_named_ascii (idx_t fields, idx_t blank, void const *block, + static void + print_ascii (idx_t fields, idx_t blank, void const *block, + MAYBE_UNUSED char const *unused_fmt_string, int width, +- int pad) ++ idx_t pad) + { + unsigned char const *p = block; +- int pad_remaining = pad; ++ idx_t pad_remaining = pad; + for (idx_t i = fields; blank < i; i--) + { +- int next_pad = pad * (i - 1) / fields; + unsigned char c = *p++; + char const *s; + char buf[4]; +@@ -579,7 +607,9 @@ print_ascii (idx_t fields, idx_t blank, void const *block, + s = buf; + } + +- xprintf ("%*s", pad_remaining - next_pad + width, s); ++ idx_t next_pad = pad_at (fields, i - 1, pad); ++ int adjusted_width = pad_remaining - next_pad + width; ++ xprintf ("%*s", adjusted_width, s); + pad_remaining = next_pad; + } + } +@@ -632,7 +662,7 @@ decode_one_format (char const *s_orig, char const *s, char const **next, + int size; + enum output_format fmt; + void (*print_function) (idx_t, idx_t, void const *, char const *, +- int, int); ++ int, idx_t); + char const *p; + char c; + int field_width; +@@ -1204,22 +1234,24 @@ write_block (intmax_t current_offset, idx_t n_bytes, + for (idx_t i = 0; i < n_specs; i++) + { + int datum_width = width_bytes[spec[i].size]; +- int fields_per_block = bytes_per_block / datum_width; +- int blank_fields = (bytes_per_block - n_bytes) / datum_width; ++ idx_t fields_per_block = bytes_per_block / datum_width; ++ idx_t blank_fields = (bytes_per_block - n_bytes) / datum_width; + if (i == 0) + format_address (current_offset, '\0'); + else +- printf ("%*s", address_pad_len, ""); ++ print_n_spaces (address_pad_len); + (*spec[i].print_function) (fields_per_block, blank_fields, + curr_block, spec[i].fmt_string, + spec[i].field_width, spec[i].pad_width); + if (spec[i].hexl_mode_trailer) + { +- /* space-pad out to full line width, then dump the trailer */ ++ /* Space-pad out to full line width, then dump the trailer. */ + int field_width = spec[i].field_width; +- int pad_width = (spec[i].pad_width * blank_fields +- / fields_per_block); +- printf ("%*s", blank_fields * field_width + pad_width, ""); ++ for (idx_t f = 0; f < blank_fields; f++) ++ print_n_spaces (field_width); ++ idx_t pad_width = pad_at (fields_per_block, blank_fields, ++ spec[i].pad_width); ++ print_n_spaces (pad_width); + dump_hexl_mode_trailer (n_bytes, curr_block); + } + putchar ('\n'); +@@ -1578,9 +1610,8 @@ main (int argc, char **argv) + { + int n_files; + int l_c_m; +- idx_t desired_width IF_LINT ( = 0); ++ idx_t desired_width = 0; + bool modern = false; +- bool width_specified = false; + bool ok = true; + idx_t width_per_block = 0; + static char const multipliers[] = "bEGKkMmPQRTYZ0"; +@@ -1769,7 +1800,6 @@ main (int argc, char **argv) + + case 'w': + modern = true; +- width_specified = true; + if (optarg == nullptr) + { + desired_width = 32; +@@ -1935,9 +1965,9 @@ main (int argc, char **argv) + /* Compute output block length. */ + l_c_m = get_lcm (); + +- if (width_specified) ++ if (desired_width != 0) + { +- if (desired_width != 0 && desired_width % l_c_m == 0) ++ if (desired_width % l_c_m == 0) + bytes_per_block = desired_width; + else + { +@@ -1957,24 +1987,25 @@ main (int argc, char **argv) + /* Compute padding necessary to align output block. */ + for (idx_t i = 0; i < n_specs; i++) + { +- int fields_per_block = bytes_per_block / width_bytes[spec[i].size]; +- int block_width = (spec[i].field_width + 1) * fields_per_block; ++ idx_t fields_per_block = bytes_per_block / width_bytes[spec[i].size]; ++ if (pad_at_overflow (fields_per_block)) ++ error (EXIT_FAILURE, 0, _("%td is too large"), desired_width); ++ idx_t block_width = (spec[i].field_width + 1) * fields_per_block; + if (width_per_block < block_width) + width_per_block = block_width; + } + for (idx_t i = 0; i < n_specs; i++) + { +- int fields_per_block = bytes_per_block / width_bytes[spec[i].size]; +- int block_width = spec[i].field_width * fields_per_block; ++ idx_t fields_per_block = bytes_per_block / width_bytes[spec[i].size]; ++ idx_t block_width = spec[i].field_width * fields_per_block; + spec[i].pad_width = width_per_block - block_width; + } + + #ifdef DEBUG +- printf ("lcm=%d, width_per_block=%"PRIuMAX"\n", l_c_m, +- (uintmax_t) width_per_block); ++ printf ("lcm=%d, width_per_block=%td\n", l_c_m, width_per_block); + for (idx_t i = 0; i < n_specs; i++) + { +- int fields_per_block = bytes_per_block / width_bytes[spec[i].size]; ++ idx_t fields_per_block = bytes_per_block / width_bytes[spec[i].size]; + affirm (bytes_per_block % width_bytes[spec[i].size] == 0); + affirm (1 <= spec[i].pad_width / fields_per_block); + printf ("%d: fmt=\"%s\" in_width=%d out_width=%d pad=%d\n", +diff --git a/tests/local.mk b/tests/local.mk +index b68df41f76..03114f7592 100644 +--- a/tests/local.mk ++++ b/tests/local.mk +@@ -269,6 +269,7 @@ all_tests = \ + tests/misc/xstrtol.pl \ + tests/tail/overlay-headers.sh \ + tests/tail/pid.sh \ ++ tests/od/big-w.sh \ + tests/od/od.pl \ + tests/od/od-endian.sh \ + tests/od/od-float.sh \ +diff --git a/tests/od/big-w.sh b/tests/od/big-w.sh +new file mode 100755 +index 0000000000..27c125c693 +--- /dev/null ++++ b/tests/od/big-w.sh +@@ -0,0 +1,43 @@ ++#!/bin/sh ++# Check whether od -wN works with big N ++ ++# Copyright 2025 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_ od ++very_expensive_ ++ ++export LC_ALL=C ++ ++cat >exp <<'EOF' || framework_failure_ ++0000000 x >x< ++0000001 ++EOF ++ ++# Try values near sqrt(2**31) and sqrt(2**63). ++for w in 46340 46341 3037000500 3037000501; do ++ printf x | od -w$w -tcz 2>err | tr -s ' ' ' ' >out ++ if test -s err; then ++ test ! -s out || fail=1 ++ else ++ compare exp out || fail=1 ++ outbytes=$(printf x | od -w$w -tcz | wc -c) ++ expbytes=$((4*$w + 21)) ++ test $expbytes -eq $outbytes || fail=1 ++ fi ++done ++ ++Exit $fail diff --git a/backport-od-fix-theoretical-size_t-malloc-overflow.patch b/backport-od-fix-theoretical-size_t-malloc-overflow.patch new file mode 100644 index 0000000000000000000000000000000000000000..d29258a1fc7bdc725a074f7bff0ab613bd56475b --- /dev/null +++ b/backport-od-fix-theoretical-size_t-malloc-overflow.patch @@ -0,0 +1,34 @@ +From 0d1c25d1cb6d0ce119775368a0fabc7644393f6e Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 28 Jun 2025 08:15:42 -0700 +Subject: [PATCH] od: fix theoretical size_t malloc overflow + +* src/od.c (dump, dump_strings): Use idx_t allocators +rather than size_t allocators, to avoid unchecked integer +overflow on theoretical platforms where SIZE_MAX < IDX_MAX. +--- + src/od.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/od.c b/src/od.c +index 6b5c8675f1..7306b0f9ad 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -1432,7 +1432,7 @@ dump (void) + bool ok = true; + size_t n_bytes_read; + +- block[0] = xnmalloc (2, bytes_per_block); ++ block[0] = xinmalloc (2, bytes_per_block); + block[1] = block[0] + bytes_per_block; + + current_offset = n_bytes_to_skip; +@@ -1514,7 +1514,7 @@ static bool + dump_strings (void) + { + idx_t bufsize = MAX (100, string_min + 1); +- char *buf = xmalloc (bufsize); ++ char *buf = ximalloc (bufsize); + uintmax_t address = n_bytes_to_skip; + bool ok = true; + diff --git a/backport-od-fix-various-off-by-one-issues-with-strings-with-N.patch b/backport-od-fix-various-off-by-one-issues-with-strings-with-N.patch new file mode 100644 index 0000000000000000000000000000000000000000..35393868533a42d8870f773b2ff02e282c8a7d77 --- /dev/null +++ b/backport-od-fix-various-off-by-one-issues-with-strings-with-N.patch @@ -0,0 +1,193 @@ +From 44809c3379d64b9e580042124ccea6665d746d9a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Tue, 24 Jun 2025 01:17:12 +0100 +Subject: [PATCH] od: fix various off-by-one issues with --strings with -N + +* src/od.c (dump_strings): There are three related issues here +due to not accounting for the terminating NUL char appropriately. + +1. Ensure BUF always has enough space for the terminating NUL. +This avoids CWE-122: Heap-based Buffer Overflow, +where we wrote a single NUL byte directly after the allocated buffer. +I.e., there should be no buffer overflow with: + printf '%100s' | od -N100 -S1 + +2. Ensure we support -S == -N (END_OFFSET - STRING_MIN == ADDRESS): +I.e., there should be output with: + printf '%100s' | od -N10 -S10 + +3. Ensure we always output a valid address by ensuring +the ADDRESS and I variables are kept in sync. +I.e., this should output address 0000000 not 1777777777777777777777: + printf '%100s' | od -N10 -S1 + +As well as fixing these we simplify by using a single loop +to read the data, rather than two. + +* doc/coreutils.texi (od invocation): Clarify that -N +implicitly NUL terminates strings. +* tests/od/od-N.sh: Add test cases. +* NEWS: Mention the bug fixes. + +Fixes https://bugs.gnu.org/78880 +--- + doc/coreutils.texi | 2 ++ + src/od.c | 56 ++++++++++++++++++---------------------------- + tests/od/od-N.sh | 29 ++++++++++++++++++++++-- + 3 files changed, 51 insertions(+), 36 deletions(-) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index a1d45fb301..865ceff050 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -2075,6 +2075,8 @@ least @var{bytes} consecutive printable characters, + followed by a zero byte (ASCII NUL). + Prefixes and suffixes on @var{bytes} are interpreted as for the + @option{-j} option. ++If combined with the @option{-N} option, truncated strings ++are considered ASCII NUL terminated. + + If @var{bytes} is omitted with @option{--strings}, the default is 3. + +diff --git a/src/od.c b/src/od.c +index 88d467c73c..32e8498960 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -1505,70 +1505,58 @@ dump (void) + } + + /* STRINGS mode. Find each "string constant" in the input. +- A string constant is a run of at least 'string_min' ASCII +- graphic (or formatting) characters terminated by a null. ++ A string constant is a run of at least STRING_MIN ++ printable characters terminated by a NUL or END_OFFSET. + Based on a function written by Richard Stallman for a + traditional version of od. Return true if successful. */ + + static bool + dump_strings (void) + { +- idx_t bufsize = MAX (100, string_min); ++ idx_t bufsize = MAX (100, string_min + 1); + char *buf = xmalloc (bufsize); + uintmax_t address = n_bytes_to_skip; + bool ok = true; + + while (true) + { +- idx_t i; +- int c; +- +- /* See if the next 'string_min' chars are all printing chars. */ +- tryline: ++ idx_t i = 0; ++ int c = 1; /* Init to 1 so can distinguish if NUL read. */ + + if (limit_bytes_to_format +- && (end_offset < string_min || end_offset - string_min <= address)) ++ && (end_offset < string_min || end_offset - string_min < address)) + break; + +- for (i = 0; i < string_min; i++) +- { +- ok &= read_char (&c); +- address++; +- if (c < 0) +- { +- free (buf); +- return ok; +- } +- if (! isprint (c)) +- /* Found a non-printing. Try again starting with next char. */ +- goto tryline; +- buf[i] = c; +- } +- +- /* We found a run of 'string_min' printable characters. +- Now see if it is terminated with a null byte. */ ++ /* Store consecutive printable characters to BUF. */ + while (!limit_bytes_to_format || address < end_offset) + { +- if (i == bufsize) ++ if (i == bufsize - 1) + buf = xpalloc (buf, &bufsize, 1, -1, sizeof *buf); + ok &= read_char (&c); +- address++; + if (c < 0) + { + free (buf); + return ok; + } ++ address++; ++ buf[i++] = c; + if (c == '\0') +- break; /* It is; print this string. */ ++ break; /* Print this string. */ + if (! isprint (c)) +- goto tryline; /* It isn't; give up on this string. */ +- buf[i++] = c; /* String continues; store it all. */ ++ { ++ c = -1; /* Give up on this string. */ ++ break; ++ } + } + +- /* If we get here, the string is all printable and null-terminated, +- so print it. It is all in 'buf' and 'i' is its length. */ ++ if (c == -1 || i - !c < string_min) ++ continue; ++ + buf[i] = 0; +- format_address (address - i - 1, ' '); ++ ++ /* If we get here, the string is all printable, so print it. */ ++ ++ format_address (address - i, ' '); + + for (i = 0; (c = buf[i]); i++) + { +diff --git a/tests/od/od-N.sh b/tests/od/od-N.sh +index b057c9b83b..7c97eeee2a 100755 +--- a/tests/od/od-N.sh ++++ b/tests/od/od-N.sh +@@ -20,8 +20,6 @@ + print_ver_ od + + echo abcdefg > in || framework_failure_ +- +- + (od -An -N3 -c; od -An -N3 -c) < in > out + cat < exp || framework_failure_ + a b c +@@ -29,4 +27,31 @@ cat < exp || framework_failure_ + EOF + compare exp out || fail=1 + ++# coreutils <= 9.7 would buffer overflow with ++# a single NUL byte after the heap buffer ++printf '%100s' | od -N100 -S1 > out || fail=1 ++printf '%07o %100s\n' 0 '' > exp || framework_failure_ ++compare exp out || fail=1 ++ ++# coreutils <= 9.7 would output nothing ++printf '%100s' | od -N10 -S10 > out || fail=1 ++printf '%07o %10s\n' 0 '' > exp || framework_failure_ ++compare exp out || fail=1 ++ ++# coreutils <= 9.7 would output an invalid address ++printf '%100s' | od -N10 -S1 > out || fail=1 ++printf '%07o %10s\n' 0 '' > exp || framework_failure_ ++compare exp out || fail=1 ++ ++# Ensure -S limits appropriately ++printf '%10s\000' | od -N11 -S11 > out || fail=1 ++compare /dev/null out || fail=1 ++printf '%10s\000' | od -S11 > out || fail=1 ++compare /dev/null out || fail=1 ++printf '%10s' | od -S10 > out || fail=1 # Ignore unterminated at EOF? ++compare /dev/null out || fail=1 ++printf '\001%10s\000%10s\000' | od -S10 > out || fail=1 ++printf '%07o %10s\n' 1 '' 12 '' > exp || framework_failure_ ++compare exp out || fail=1 ++ + Exit $fail diff --git a/backport-od-output-standard-diagnostics-for-invalid-w-argumen.patch b/backport-od-output-standard-diagnostics-for-invalid-w-argumen.patch new file mode 100644 index 0000000000000000000000000000000000000000..e3540e0747f813539ece983a4615d591de37db72 --- /dev/null +++ b/backport-od-output-standard-diagnostics-for-invalid-w-argumen.patch @@ -0,0 +1,65 @@ +From 2b7f3621f8b994db9436318fbe818a34ee87859a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Tue, 24 Jun 2025 15:47:48 +0100 +Subject: [PATCH] od: output standard diagnostics for invalid -w arguments + +* src/od.c (main): Don't pass LONGINT_OK to xstrtol_fatal(), +as otherwise it will abort(). +* tests/od/od.pl: Add test cases. +* NEWS: Mention the bug fix. + +Addresses https://bugs.gnu.org/78879 +--- + src/od.c | 4 +++- + tests/od/od.pl | 12 +++++++++++- + 2 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/src/od.c b/src/od.c +index 32e8498960..6b5c8675f1 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -1820,7 +1820,9 @@ main (int argc, char **argv) + { + intmax_t w_tmp; + s_err = xstrtoimax (optarg, nullptr, 10, &w_tmp, ""); +- if (s_err != LONGINT_OK || w_tmp <= 0) ++ if (s_err == LONGINT_OK && w_tmp <= 0) ++ s_err = LONGINT_INVALID; ++ if (s_err != LONGINT_OK) + xstrtol_fatal (s_err, oi, c, long_options, optarg); + if (ckd_add (&desired_width, w_tmp, 0)) + error (EXIT_FAILURE, 0, _("%s is too large"), quote (optarg)); +diff --git a/tests/od/od.pl b/tests/od/od.pl +index affdc75aed..5bb271e607 100755 +--- a/tests/od/od.pl ++++ b/tests/od/od.pl +@@ -23,6 +23,8 @@ + # Turn off localization of executable's output. + @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + ++my $prog = 'od'; ++ + # Use a file in /proc whose size is not likely to + # change between the wc and od invocations. + my $proc_file = '/proc/version'; +@@ -64,11 +66,19 @@ + ['wide-a', '-a -w65537 -An', {IN=>{g=>'x'}}, {OUT=>" x\n"}], + ['wide-c', '-c -w65537 -An', {IN=>{g=>'x'}}, {OUT=>" x\n"}], + ['wide-x', '-tx1 -w65537 -An', {IN=>{g=>'B'}}, {OUT=>" 42\n"}], ++ ++ # Ensure that invalid widths do not cause trouble. ++ # From coreutils-9.3 through coreutils-9.7, these would abort ++ ['invalid-w-1', '-w0 -An', {IN=>""}, {EXIT=>1}, ++ {ERR=>"$prog: invalid -w argument '0'\n"}], ++ ['invalid-w-2', '-w-1 -An', {IN=>""}, {EXIT=>1}, ++ {ERR=>"$prog: invalid -w argument '-1'\n"}], ++ ['invalid-w-3', '-ww -An', {IN=>""}, {EXIT=>1}, ++ {ERR=>"$prog: invalid -w argument 'w'\n"}], + ); + + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + +-my $prog = 'od'; + my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); + exit $fail; diff --git a/backport-od-prefer-idx_t-to-size_t.patch b/backport-od-prefer-idx_t-to-size_t.patch new file mode 100644 index 0000000000000000000000000000000000000000..2ca4ac99f86d39cf5a1868fad0568a3ac9c10a39 --- /dev/null +++ b/backport-od-prefer-idx_t-to-size_t.patch @@ -0,0 +1,216 @@ +From 35d9bf6915bff5d88dd2246ae5a157db838c64f1 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 24 Jun 2025 20:34:29 -0700 +Subject: [PATCH] od: prefer idx_t to size_t + +This helps find overflow bugs when compiling with -fsanitize=undefined. +* src/od.c (struct tspec, bytes_per_block, PRINT_FIELDS) +(PRINT_TYPE, print_named_ascii, print_ascii, decode_one_format) +(skip, write_block, read_block, dump, main): +Use idx_t, not size_t. +--- + src/od.c | 57 ++++++++++++++++++++++++-------------------------------- + 1 file changed, 24 insertions(+), 33 deletions(-) + +diff --git a/src/od.c b/src/od.c +index 8781a2258b..f385d6ce54 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -123,7 +123,7 @@ struct tspec + fields to leave blank. WIDTH is width of one field, excluding + leading space, and PAD is total pad to divide among FIELDS. + PAD is at least as large as FIELDS. */ +- void (*print_function) (size_t fields, size_t blank, void const *data, ++ void (*print_function) (idx_t fields, idx_t blank, void const *data, + char const *fmt, int width, int pad); + char fmt_string[FMT_BYTES_ALLOCATED]; /* Of the style "%*d". */ + bool hexl_mode_trailer; +@@ -261,7 +261,7 @@ static idx_t n_specs_allocated; + a multiple of the least common multiple of the sizes associated with + the specified output types. It should be as large as possible, but + no larger than 16 -- unless specified with the -w option. */ +-static size_t bytes_per_block; ++static idx_t bytes_per_block; + + /* Human-readable representation of *file_list (for error messages). + It differs from file_list[-1] only when file_list[-1] is "-". */ +@@ -452,25 +452,23 @@ Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\ + + #define PRINT_FIELDS(N, T, FMT_STRING_DECL, ACTION) \ + static void \ +-N (size_t fields, size_t blank, void const *block, \ ++N (idx_t fields, idx_t blank, void const *block, \ + FMT_STRING_DECL, int width, int pad) \ + { \ + T const *p = block; \ +- uintmax_t i; \ + int pad_remaining = pad; \ +- for (i = fields; blank < i; i--) \ ++ for (idx_t i = fields; blank < i; i--) \ + { \ + int next_pad = pad * (i - 1) / fields; \ + int adjusted_width = pad_remaining - next_pad + width; \ + T x; \ + if (input_swap && sizeof (T) > 1) \ + { \ +- size_t j; \ + union { \ + T x; \ + char b[sizeof (T)]; \ + } u; \ +- for (j = 0; j < sizeof (T); j++) \ ++ for (idx_t j = 0; j < sizeof (T); j++) \ + u.b[j] = ((char const *) p)[sizeof (T) - 1 - j]; \ + x = u.x; \ + } \ +@@ -510,10 +508,10 @@ PRINT_FLOATTYPE (print_long_double, long double, ldtoastr, LDBL_BUFSIZE_BOUND) + #undef PRINT_FLOATTYPE + + static void +-dump_hexl_mode_trailer (size_t n_bytes, char const *block) ++dump_hexl_mode_trailer (idx_t n_bytes, char const *block) + { + fputs (" >", stdout); +- for (size_t i = n_bytes; i > 0; i--) ++ for (idx_t i = n_bytes; i > 0; i--) + { + unsigned char c = *block++; + unsigned char c2 = (isprint (c) ? c : '.'); +@@ -523,14 +521,13 @@ dump_hexl_mode_trailer (size_t n_bytes, char const *block) + } + + static void +-print_named_ascii (size_t fields, size_t blank, void const *block, ++print_named_ascii (idx_t fields, idx_t blank, void const *block, + MAYBE_UNUSED char const *unused_fmt_string, + int width, int pad) + { + unsigned char const *p = block; +- uintmax_t i; + int pad_remaining = pad; +- for (i = fields; blank < i; i--) ++ for (idx_t i = fields; blank < i; i--) + { + int next_pad = pad * (i - 1) / fields; + int masked_c = *p++ & 0x7f; +@@ -554,14 +551,13 @@ print_named_ascii (size_t fields, size_t blank, void const *block, + } + + static void +-print_ascii (size_t fields, size_t blank, void const *block, ++print_ascii (idx_t fields, idx_t blank, void const *block, + MAYBE_UNUSED char const *unused_fmt_string, int width, + int pad) + { + unsigned char const *p = block; +- uintmax_t i; + int pad_remaining = pad; +- for (i = fields; blank < i; i--) ++ for (idx_t i = fields; blank < i; i--) + { + int next_pad = pad * (i - 1) / fields; + unsigned char c = *p++; +@@ -659,7 +655,7 @@ decode_one_format (char const *s_orig, char const *s, char const **next, + enum size_spec size_spec; + int size; + enum output_format fmt; +- void (*print_function) (size_t, size_t, void const *, char const *, ++ void (*print_function) (idx_t, idx_t, void const *, char const *, + int, int); + char const *p; + char c; +@@ -868,7 +864,7 @@ decode_one_format (char const *s_orig, char const *s, char const **next, + + { + struct lconv const *locale = localeconv (); +- size_t decimal_point_len = ++ idx_t decimal_point_len = + (locale->decimal_point[0] ? strlen (locale->decimal_point) : 1); + + switch (size_spec) +@@ -1111,7 +1107,7 @@ skip (uintmax_t n_skip) + else + { + char buf[BUFSIZ]; +- size_t n_bytes_read, n_bytes_to_read = BUFSIZ; ++ idx_t n_bytes_read, n_bytes_to_read = BUFSIZ; + + while (0 < n_skip) + { +@@ -1228,7 +1224,7 @@ format_address_label (uintmax_t address, char c) + That condition may be false only for the last input block. */ + + static void +-write_block (uintmax_t current_offset, size_t n_bytes, ++write_block (uintmax_t current_offset, idx_t n_bytes, + char const *prev_block, char const *curr_block) + { + static bool first = true; +@@ -1329,7 +1325,7 @@ read_char (int *c) + as usual and return false. Otherwise return true. */ + + static bool +-read_block (size_t n, char *block, size_t *n_bytes_in_buffer) ++read_block (idx_t n, char *block, idx_t *n_bytes_in_buffer) + { + bool ok = true; + +@@ -1339,11 +1335,9 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer) + + while (in_stream != nullptr) /* EOF. */ + { +- size_t n_needed; +- size_t n_read; +- +- n_needed = n - *n_bytes_in_buffer; +- n_read = fread (block + *n_bytes_in_buffer, 1, n_needed, in_stream); ++ idx_t n_needed = n - *n_bytes_in_buffer; ++ idx_t n_read = fread (block + *n_bytes_in_buffer, ++ 1, n_needed, in_stream); + + *n_bytes_in_buffer += n_read; + +@@ -1422,7 +1416,7 @@ dump (void) + uintmax_t current_offset; + bool idx = false; + bool ok = true; +- size_t n_bytes_read; ++ idx_t n_bytes_read; + + block[0] = xinmalloc (2, bytes_per_block); + block[1] = block[0] + bytes_per_block; +@@ -1433,7 +1427,7 @@ dump (void) + { + while (ok) + { +- size_t n_needed; ++ idx_t n_needed; + if (current_offset >= end_offset) + { + n_bytes_read = 0; +@@ -1472,14 +1466,11 @@ dump (void) + + if (n_bytes_read > 0) + { +- int l_c_m; +- size_t bytes_to_write; +- +- l_c_m = get_lcm (); ++ int l_c_m = get_lcm (); + + /* Ensure zero-byte padding up to the smallest multiple of l_c_m that + is at least as large as n_bytes_read. */ +- bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m); ++ idx_t bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m); + + memset (block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); + write_block (current_offset, n_bytes_read, block[!idx], block[idx]); +@@ -1607,7 +1598,7 @@ main (int argc, char **argv) + bool modern = false; + bool width_specified = false; + bool ok = true; +- size_t width_per_block = 0; ++ idx_t width_per_block = 0; + static char const multipliers[] = "bEGKkMmPQRTYZ0"; + + /* The old-style 'pseudo starting address' to be printed in parentheses diff --git a/backport-od-prefer-intmax_t-to-uintmax_t.patch b/backport-od-prefer-intmax_t-to-uintmax_t.patch new file mode 100644 index 0000000000000000000000000000000000000000..6aacad760f5332f6cf23690d0438bc5394e22df8 --- /dev/null +++ b/backport-od-prefer-intmax_t-to-uintmax_t.patch @@ -0,0 +1,306 @@ +From 4d527dda583f6d3b0bd66364549472a5d98cef67 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 26 Jun 2025 08:45:47 -0700 +Subject: [PATCH] od: prefer intmax_t to uintmax_t + +* src/od.c (MAX_ADDRESS_LENGTH, pseudo_offset, n_bytes_to_skip) +(max_bytes_to_format, end_offset, skip, format_address_none) +(format_address_std, format_address_paren, format_address_label) +(write_block, parse_old_offset, dump, dump_strings, main): +Prefer intmax_t to uintmax_t. This makes no practical difference, +and lets -fsanitize=undefined check for signed integer overflow. +(skip, dump): Remove no-longer-needed casts. +(xstr2nonneg): New static function. All callers of xstrtoumax +now call this function instead. +(main): Use ckd_add to detect signed integer overflow, since +the unsigned trick no longer works reliably. +Let xstrtol_fatal report the overflow, instead of doing +it by hand ourselves. +--- + src/od.c | 103 +++++++++++++++++++++++++++++-------------------------- + 1 file changed, 55 insertions(+), 48 deletions(-) + +diff --git a/src/od.c b/src/od.c +index fd5fc45bd4..b8137fa8a1 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -203,7 +203,8 @@ static int address_base; + + /* The number of octal digits required to represent the largest + address value. */ +-enum { MAX_ADDRESS_LENGTH = UINTMAX_WIDTH / 3 + (UINTMAX_WIDTH % 3 != 0) }; ++enum { MAX_ADDRESS_LENGTH = ((INTMAX_WIDTH - 1) / 3 ++ + ((INTMAX_WIDTH - 1) % 3 != 0)) }; + + /* Width of a normal address. */ + static int address_pad_len; +@@ -224,24 +225,24 @@ static bool flag_pseudo_start; + + /* The difference between the old-style pseudo starting address and + the number of bytes to skip. */ +-static uintmax_t pseudo_offset; ++static intmax_t pseudo_offset; + + /* Function that accepts an address and an optional following char, + and prints the address and char to stdout. */ +-static void (*format_address) (uintmax_t, char); ++static void (*format_address) (intmax_t, char); + + /* The number of input bytes to skip before formatting and writing. */ +-static uintmax_t n_bytes_to_skip = 0; ++static intmax_t n_bytes_to_skip = 0; + + /* When false, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all + input is formatted. */ + static bool limit_bytes_to_format = false; + + /* The maximum number of bytes that will be formatted. */ +-static uintmax_t max_bytes_to_format; ++static intmax_t max_bytes_to_format; + + /* The offset of the first byte after the last byte to be formatted. */ +-static uintmax_t end_offset; ++static intmax_t end_offset; + + /* When true and two or more consecutive blocks are equal, format + only the first block and output an asterisk alone on the following +@@ -1079,7 +1080,7 @@ decode_format_string (char const *s) + advance IN_STREAM. */ + + static bool +-skip (uintmax_t n_skip) ++skip (intmax_t n_skip) + { + bool ok = true; + int in_errno = 0; +@@ -1114,7 +1115,7 @@ skip (uintmax_t n_skip) + proc-like file systems. */ + if (usable_size && ST_BLKSIZE (file_stats) < file_stats.st_size) + { +- if ((uintmax_t) file_stats.st_size < n_skip) ++ if (file_stats.st_size < n_skip) + n_skip -= file_stats.st_size; + else + { +@@ -1182,13 +1183,13 @@ skip (uintmax_t n_skip) + } + + static void +-format_address_none (MAYBE_UNUSED uintmax_t address, ++format_address_none (MAYBE_UNUSED intmax_t address, + MAYBE_UNUSED char c) + { + } + + static void +-format_address_std (uintmax_t address, char c) ++format_address_std (intmax_t address, char c) + { + char buf[MAX_ADDRESS_LENGTH + 2]; + char *p = buf + sizeof buf; +@@ -1228,7 +1229,7 @@ format_address_std (uintmax_t address, char c) + } + + static void +-format_address_paren (uintmax_t address, char c) ++format_address_paren (intmax_t address, char c) + { + putchar ('('); + format_address_std (address, ')'); +@@ -1237,7 +1238,7 @@ format_address_paren (uintmax_t address, char c) + } + + static void +-format_address_label (uintmax_t address, char c) ++format_address_label (intmax_t address, char c) + { + format_address_std (address, ' '); + format_address_paren (address + pseudo_offset, c); +@@ -1254,7 +1255,7 @@ format_address_label (uintmax_t address, char c) + That condition may be false only for the last input block. */ + + static void +-write_block (uintmax_t current_offset, idx_t n_bytes, ++write_block (intmax_t current_offset, idx_t n_bytes, + char const *prev_block, char const *curr_block) + { + static bool first = true; +@@ -1398,11 +1399,23 @@ get_lcm (void) + return l_c_m; + } + ++/* Act like xstrtoimax (NPTR, nullptr, BASE, VAL, VALID_SUFFIXES), ++ except reject negative values, and *VAL may be set if ++ LONGINT_INVALID is returned. */ ++static strtol_error ++xstr2nonneg (char const *restrict nptr, int base, intmax_t *val, ++ char const *restrict valid_suffixes) ++{ ++ strtol_error s_err = xstrtoimax (nptr, nullptr, base, val, valid_suffixes); ++ return s_err != LONGINT_INVALID && *val < 0 ? LONGINT_INVALID : s_err; ++} ++ + /* If S is a valid traditional offset specification with an optional +- leading '+' return true and set *OFFSET to the offset it denotes. */ ++ leading '+' return true and set *OFFSET to the offset it denotes. ++ Otherwise return false and possibly set *OFFSET. */ + + static bool +-parse_old_offset (char *s, uintmax_t *offset) ++parse_old_offset (char *s, intmax_t *offset) + { + int radix; + +@@ -1440,7 +1453,7 @@ parse_old_offset (char *s, uintmax_t *offset) + radix = 8; + } + +- enum strtol_error s_err = xstrtoumax (s, nullptr, radix, offset, "Bb"); ++ enum strtol_error s_err = xstr2nonneg (s, radix, offset, "Bb"); + + if (dot) + { +@@ -1468,7 +1481,7 @@ static bool + dump (void) + { + char *block[2]; +- uintmax_t current_offset; ++ intmax_t current_offset; + bool idx = false; + bool ok = true; + idx_t n_bytes_read; +@@ -1482,14 +1495,12 @@ dump (void) + { + while (ok) + { +- idx_t n_needed; + if (current_offset >= end_offset) + { + n_bytes_read = 0; + break; + } +- n_needed = MIN (end_offset - current_offset, +- (uintmax_t) bytes_per_block); ++ idx_t n_needed = MIN (end_offset - current_offset, bytes_per_block); + ok &= read_block (n_needed, block[idx], &n_bytes_read); + if (n_bytes_read < bytes_per_block) + break; +@@ -1553,7 +1564,7 @@ dump_strings (void) + { + idx_t bufsize = MAX (100, string_min + 1); + char *buf = ximalloc (bufsize); +- uintmax_t address = n_bytes_to_skip; ++ intmax_t address = n_bytes_to_skip; + bool ok = true; + + while (true) +@@ -1657,7 +1668,7 @@ main (int argc, char **argv) + + /* The old-style 'pseudo starting address' to be printed in parentheses + after any true address. */ +- uintmax_t pseudo_start IF_LINT ( = 0); ++ intmax_t pseudo_start IF_LINT ( = 0); + + initialize_main (&argc, &argv); + set_program_name (argv[0]); +@@ -1704,7 +1715,7 @@ main (int argc, char **argv) + + while (true) + { +- uintmax_t tmp; ++ intmax_t tmp; + enum strtol_error s_err; + int oi = -1; + int c = getopt_long (argc, argv, short_options, long_options, &oi); +@@ -1747,8 +1758,7 @@ main (int argc, char **argv) + + case 'j': + modern = true; +- s_err = xstrtoumax (optarg, nullptr, 0, +- &n_bytes_to_skip, multipliers); ++ s_err = xstr2nonneg (optarg, 0, &n_bytes_to_skip, multipliers); + if (s_err != LONGINT_OK) + xstrtol_fatal (s_err, oi, c, long_options, optarg); + break; +@@ -1757,8 +1767,7 @@ main (int argc, char **argv) + modern = true; + limit_bytes_to_format = true; + +- s_err = xstrtoumax (optarg, nullptr, 0, &max_bytes_to_format, +- multipliers); ++ s_err = xstr2nonneg (optarg, 0, &max_bytes_to_format, multipliers); + if (s_err != LONGINT_OK) + xstrtol_fatal (s_err, oi, c, long_options, optarg); + break; +@@ -1769,16 +1778,14 @@ main (int argc, char **argv) + string_min = 3; + else + { +- s_err = xstrtoumax (optarg, nullptr, 0, &tmp, multipliers); ++ s_err = xstr2nonneg (optarg, 0, &tmp, multipliers); ++ /* The minimum string length + 1 must fit in idx_t, ++ since we may allocate a buffer of this size + 1. */ ++ idx_t i; ++ if (s_err == LONGINT_OK && ckd_add (&i, tmp, 1)) ++ s_err = LONGINT_OVERFLOW; + if (s_err != LONGINT_OK) + xstrtol_fatal (s_err, oi, c, long_options, optarg); +- +- /* The minimum string length must be less than +- MIN (IDX_MAX, SIZE_MAX), since we may allocate a +- buffer of this size + 1. */ +- if (MIN (IDX_MAX, SIZE_MAX) <= tmp) +- error (EXIT_FAILURE, 0, _("%s is too large"), quote (optarg)); +- + string_min = tmp; + } + flag_dump_strings = true; +@@ -1853,13 +1860,16 @@ main (int argc, char **argv) + else + { + intmax_t w_tmp; +- s_err = xstrtoimax (optarg, nullptr, 10, &w_tmp, ""); +- if (s_err == LONGINT_OK && w_tmp <= 0) +- s_err = LONGINT_INVALID; ++ s_err = xstr2nonneg (optarg, 10, &w_tmp, ""); ++ if (s_err == LONGINT_OK) ++ { ++ if (ckd_add (&desired_width, w_tmp, 0)) ++ s_err = LONGINT_OVERFLOW; ++ else if (desired_width == 0) ++ s_err = LONGINT_INVALID; ++ } + if (s_err != LONGINT_OK) + xstrtol_fatal (s_err, oi, c, long_options, optarg); +- if (ckd_add (&desired_width, w_tmp, 0)) +- error (EXIT_FAILURE, 0, _("%s is too large"), quote (optarg)); + } + break; + +@@ -1896,8 +1906,8 @@ main (int argc, char **argv) + + if (!modern || traditional) + { +- uintmax_t o1; +- uintmax_t o2; ++ intmax_t o1; ++ intmax_t o2; + + switch (n_files) + { +@@ -1970,12 +1980,9 @@ main (int argc, char **argv) + format_address = format_address_label; + } + +- if (limit_bytes_to_format) +- { +- end_offset = n_bytes_to_skip + max_bytes_to_format; +- if (end_offset < n_bytes_to_skip) +- error (EXIT_FAILURE, 0, _("skip-bytes + read-bytes is too large")); +- } ++ if (limit_bytes_to_format ++ && ckd_add (&end_offset, n_bytes_to_skip, max_bytes_to_format)) ++ error (EXIT_FAILURE, 0, _("skip-bytes + read-bytes is too large")); + + if (n_specs == 0) + decode_format_string ("oS"); diff --git a/backport-od-prefer-xpalloc-to-x2nrealloc.patch b/backport-od-prefer-xpalloc-to-x2nrealloc.patch new file mode 100644 index 0000000000000000000000000000000000000000..9f54c3bbabeaa5066836ac48f49eaf7df9a0bc09 --- /dev/null +++ b/backport-od-prefer-xpalloc-to-x2nrealloc.patch @@ -0,0 +1,109 @@ +From a824f50d96e4a9f5ee7f4ec3f04339015926f6e2 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 7 Nov 2024 12:58:49 -0800 +Subject: [PATCH] od: prefer xpalloc to x2nrealloc + +* src/od.c (n_specs, n_specs_allocated, write_block, get_lcm, main): +Use idx_t instead of size_t for some sizes, so that we can +use xpalloc. +(decode_format_string): Prefer xpalloc to X2NREALLOC. +--- + src/od.c | 21 ++++++++++----------- + 1 file changed, 10 insertions(+), 11 deletions(-) + +diff --git a/src/od.c b/src/od.c +index d23df2c5e1..3d3d9a35b2 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -259,10 +259,10 @@ static bool abbreviate_duplicate_blocks = true; + static struct tspec *spec; + + /* The number of format specs. */ +-static size_t n_specs; ++static idx_t n_specs; + + /* The allocated length of SPEC. */ +-static size_t n_specs_allocated; ++static idx_t n_specs_allocated; + + /* The number of input bytes formatted per output line. It must be + a multiple of the least common multiple of the sizes associated with +@@ -1039,7 +1039,7 @@ decode_format_string (char const *s) + char const *next; + + if (n_specs_allocated <= n_specs) +- spec = X2NREALLOC (spec, &n_specs_allocated); ++ spec = xpalloc (spec, &n_specs_allocated, 1, -1, sizeof *spec); + + if (! decode_one_format (s_orig, s, &next, &spec[n_specs])) + return false; +@@ -1261,7 +1261,7 @@ write_block (uintmax_t current_offset, size_t n_bytes, + else + { + prev_pair_equal = false; +- for (size_t i = 0; i < n_specs; i++) ++ for (idx_t i = 0; i < n_specs; i++) + { + int datum_width = width_bytes[spec[i].size]; + int fields_per_block = bytes_per_block / datum_width; +@@ -1374,7 +1374,7 @@ get_lcm (void) + { + int l_c_m = 1; + +- for (size_t i = 0; i < n_specs; i++) ++ for (idx_t i = 0; i < n_specs; i++) + l_c_m = lcm (l_c_m, width_bytes[spec[i].size]); + return l_c_m; + } +@@ -1623,7 +1623,6 @@ int + main (int argc, char **argv) + { + int n_files; +- size_t i; + int l_c_m; + idx_t desired_width IF_LINT ( = 0); + bool modern = false; +@@ -1644,7 +1643,7 @@ main (int argc, char **argv) + + atexit (close_stdout); + +- for (i = 0; i <= MAX_INTEGRAL_TYPE_SIZE; i++) ++ for (idx_t i = 0; i <= MAX_INTEGRAL_TYPE_SIZE; i++) + integral_type_size[i] = NO_SIZE; + + integral_type_size[sizeof (char)] = CHAR; +@@ -1657,7 +1656,7 @@ main (int argc, char **argv) + integral_type_size[sizeof (unsigned_long_long_int)] = LONG_LONG; + #endif + +- for (i = 0; i <= MAX_FP_TYPE_SIZE; i++) ++ for (idx_t i = 0; i <= MAX_FP_TYPE_SIZE; i++) + fp_type_size[i] = NO_SIZE; + + fp_type_size[sizeof (float)] = FLOAT_SINGLE; +@@ -2008,14 +2007,14 @@ main (int argc, char **argv) + } + + /* Compute padding necessary to align output block. */ +- for (i = 0; i < n_specs; i++) ++ for (idx_t i = 0; i < n_specs; i++) + { + int fields_per_block = bytes_per_block / width_bytes[spec[i].size]; + int block_width = (spec[i].field_width + 1) * fields_per_block; + if (width_per_block < block_width) + width_per_block = block_width; + } +- for (i = 0; i < n_specs; i++) ++ for (idx_t i = 0; i < n_specs; i++) + { + int fields_per_block = bytes_per_block / width_bytes[spec[i].size]; + int block_width = spec[i].field_width * fields_per_block; +@@ -2024,7 +2023,7 @@ main (int argc, char **argv) + #ifdef DEBUG + printf ("lcm=%d, width_per_block=%"PRIuMAX"\n", l_c_m, + (uintmax_t) width_per_block); +- for (i = 0; i < n_specs; i++) ++ for (idx_t i = 0; i < n_specs; i++) + { + int fields_per_block = bytes_per_block / width_bytes[spec[i].size]; + affirm (bytes_per_block % width_bytes[spec[i].size] == 0); diff --git a/backport-od-prefer-xpalloc-to-x2realloc.patch b/backport-od-prefer-xpalloc-to-x2realloc.patch new file mode 100644 index 0000000000000000000000000000000000000000..b674f5977ce90a82d51def067ac7d902748266e4 --- /dev/null +++ b/backport-od-prefer-xpalloc-to-x2realloc.patch @@ -0,0 +1,66 @@ +From 91a743bb852f870aedbe510a70a71d6ab12ad3cc Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 7 Nov 2024 15:11:37 -0800 +Subject: [PATCH] od: prefer xpalloc to x2realloc + +* src/od.c (string_min, dump_strings): Prefer idx_t to size_t for +sizes. Use xpalloc, not x2realloc. +--- + src/od.c | 17 ++++++++--------- + 1 file changed, 8 insertions(+), 9 deletions(-) + +diff --git a/src/od.c b/src/od.c +index 3d3d9a35b2..37a934a781 100644 +--- a/src/od.c ++++ b/src/od.c +@@ -216,7 +216,7 @@ static int address_base; + static int address_pad_len; + + /* Minimum length when detecting --strings. */ +-static size_t string_min; ++static idx_t string_min; + + /* True when in --strings mode. */ + static bool flag_dump_strings; +@@ -1512,14 +1512,14 @@ dump (void) + static bool + dump_strings (void) + { +- size_t bufsize = MAX (100, string_min); ++ idx_t bufsize = MAX (100, string_min); + char *buf = xmalloc (bufsize); + uintmax_t address = n_bytes_to_skip; + bool ok = true; + + while (true) + { +- size_t i; ++ idx_t i; + int c; + + /* See if the next 'string_min' chars are all printing chars. */ +@@ -1549,9 +1549,7 @@ dump_strings (void) + while (!limit_bytes_to_format || address < end_offset) + { + if (i == bufsize) +- { +- buf = X2REALLOC (buf, &bufsize); +- } ++ buf = xpalloc (buf, &bufsize, 1, -1, sizeof *buf); + ok &= read_char (&c); + address++; + if (c < 0) +@@ -1751,9 +1749,10 @@ main (int argc, char **argv) + if (s_err != LONGINT_OK) + xstrtol_fatal (s_err, oi, c, long_options, optarg); + +- /* The minimum string length may be no larger than SIZE_MAX, +- since we may allocate a buffer of this size. */ +- if (SIZE_MAX < tmp) ++ /* The minimum string length may be no larger than ++ MIN (IDX_MAX, SIZE_MAX), since we may allocate a ++ buffer of this size. */ ++ if (MIN (IDX_MAX, SIZE_MAX) < tmp) + error (EXIT_FAILURE, 0, _("%s is too large"), quote (optarg)); + + string_min = tmp; diff --git a/backport-qcopy-acl-Fix-copying-of-ACLs-on-CentOS-7-regression.patch b/backport-qcopy-acl-Fix-copying-of-ACLs-on-CentOS-7-regression.patch new file mode 100644 index 0000000000000000000000000000000000000000..c210c7176f91573e9610396dfa11266b78dcf2de --- /dev/null +++ b/backport-qcopy-acl-Fix-copying-of-ACLs-on-CentOS-7-regression.patch @@ -0,0 +1,130 @@ +From 47947855dda53fd12bbae2a0fccecb2280577a60 Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Mon, 15 Jul 2024 14:48:41 +0200 +Subject: [PATCH] qcopy-acl: Fix copying of ACLs on CentOS 7 (regression + 2023-01-12). + +* lib/qcopy-acl.c: Include , . +(XATTR_NAME_NFSV4_ACL, XATTR_NAME_POSIX_ACL_ACCESS, +XATTR_NAME_POSIX_ACL_DEFAULT): New macros, from file-has-acl.c. +(is_attr_permissions): Test for these names explicitly. +* m4/acl.m4 (gl_QCOPY_ACL): New macro. +* modules/qcopy-acl (Files): Add m4/acl.m4. +(configure.ac): Invoke gl_QCOPY_ACL. + + +--- + lib/qcopy-acl.c | 21 ++++++++++++++++++++- + m4/acl.m4 | 25 ++++++++++++++++++++----- + m4/gnulib-comp.m4 | 9 +-------- + 3 files changed, 41 insertions(+), 14 deletions(-) + +diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c +index 0f4159b..d03869c 100644 +--- a/lib/qcopy-acl.c ++++ b/lib/qcopy-acl.c +@@ -26,6 +26,20 @@ + #if USE_XATTR + + # include ++# include ++ ++# if HAVE_LINUX_XATTR_H ++# include ++# endif ++# ifndef XATTR_NAME_NFSV4_ACL ++# define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" ++# endif ++# ifndef XATTR_NAME_POSIX_ACL_ACCESS ++# define XATTR_NAME_POSIX_ACL_ACCESS "system.posix_acl_access" ++# endif ++# ifndef XATTR_NAME_POSIX_ACL_DEFAULT ++# define XATTR_NAME_POSIX_ACL_DEFAULT "system.posix_acl_default" ++# endif + + /* Returns 1 if NAME is the name of an extended attribute that is related + to permissions, i.e. ACLs. Returns 0 otherwise. */ +@@ -33,7 +47,12 @@ + static int + is_attr_permissions (const char *name, struct error_context *ctx) + { +- return attr_copy_action (name, ctx) == ATTR_ACTION_PERMISSIONS; ++ /* We need to explicitly test for the known extended attribute names, ++ because at least on CentOS 7, attr_copy_action does not do it. */ ++ return strcmp (name, XATTR_NAME_POSIX_ACL_ACCESS) == 0 ++ || strcmp (name, XATTR_NAME_POSIX_ACL_DEFAULT) == 0 ++ || strcmp (name, XATTR_NAME_NFSV4_ACL) == 0 ++ || attr_copy_action (name, ctx) == ATTR_ACTION_PERMISSIONS; + } + + #endif /* USE_XATTR */ +diff --git a/m4/acl.m4 b/m4/acl.m4 +index 38b1dc6..2492582 100644 +--- a/m4/acl.m4 ++++ b/m4/acl.m4 +@@ -1,5 +1,5 @@ + # acl.m4 - check for access control list (ACL) primitives +-# serial 29 ++# serial 31 + + # Copyright (C) 2002, 2004-2023 Free Software Foundation, Inc. + # This file is free software; the Free Software Foundation +@@ -177,13 +177,14 @@ AC_DEFUN([gl_ACL_GET_FILE], + AS_IF([test "$gl_cv_func_working_acl_get_file" != no], [$1], [$2]) + ]) + +-# On GNU/Linux, testing if a file has an acl can be done with the +-# listxattr and getxattr syscalls, which don't require linking +-# against additional libraries. Assume this works if linux/attr.h +-# and listxattr are present. ++# Prerequisites of module file-has-acl. + AC_DEFUN([gl_FILE_HAS_ACL], + [ + AC_REQUIRE([gl_FUNC_ACL_ARG]) ++ # On GNU/Linux, testing if a file has an acl can be done with the ++ # listxattr and getxattr syscalls, which don't require linking ++ # against additional libraries. Assume this works if linux/attr.h ++ # and listxattr are present. + AC_CHECK_HEADERS_ONCE([linux/xattr.h]) + AC_CHECK_FUNCS_ONCE([listxattr]) + FILE_HAS_ACL_LIB= +@@ -197,3 +198,17 @@ AC_DEFUN([gl_FILE_HAS_ACL], + FILE_HAS_ACL_LIB=$LIB_ACL]) + AC_SUBST([FILE_HAS_ACL_LIB]) + ]) ++ ++# Prerequisites of module qcopy-acl. ++AC_DEFUN([gl_QCOPY_ACL], ++[ ++ AC_REQUIRE([gl_FUNC_ACL]) ++ AC_CHECK_HEADERS_ONCE([linux/xattr.h]) ++ gl_FUNC_XATTR ++ if test "$use_xattr" = yes; then ++ QCOPY_ACL_LIB="$LIB_XATTR" ++ else ++ QCOPY_ACL_LIB="$LIB_ACL" ++ fi ++ AC_SUBST([QCOPY_ACL_LIB]) ++]) +diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 +index 7ee104a..487b44d 100644 +--- a/m4/gnulib-comp.m4 ++++ b/m4/gnulib-comp.m4 +@@ -2092,14 +2092,7 @@ AC_DEFUN([gl_INIT], + gl_PREREQ_PUTENV + ]) + gl_STDLIB_MODULE_INDICATOR([putenv]) +- gl_FUNC_XATTR +- AC_REQUIRE([gl_FUNC_ACL]) +- if test "$use_xattr" = yes; then +- QCOPY_ACL_LIB="$LIB_XATTR" +- else +- QCOPY_ACL_LIB="$LIB_ACL" +- fi +- AC_SUBST([QCOPY_ACL_LIB]) ++ gl_QCOPY_ACL + gl_QUOTE + gl_QUOTEARG + gl_FUNC_RAISE +-- +2.43.0 \ No newline at end of file diff --git a/backport-qcopy-acl-port-better-to-NFSv4-on-GNU-Linux.patch b/backport-qcopy-acl-port-better-to-NFSv4-on-GNU-Linux.patch new file mode 100644 index 0000000000000000000000000000000000000000..ea8e55ca8cc1a946a6d7f609dd25762a186d658e --- /dev/null +++ b/backport-qcopy-acl-port-better-to-NFSv4-on-GNU-Linux.patch @@ -0,0 +1,484 @@ +From 8a356b77717a2e4f735ec06e326880ca1f61aadb Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Fri, 9 May 2025 18:02:29 -0700 +Subject: [PATCH] qcopy-acl: port better to NFSv4 on GNU/Linux +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Problem reported by Ian Dall in +and by Thomas Clark in . +* lib/file-has-acl.c (smack_new_label_from_file) [!HAVE_SMACK]: +New dummy function. +(has_xattr, get_aclinfo): New arg FD. All callers changed. +Remove some unnecessary MAYBE_UNUSEDs. +(acl_get_fd_np): Fall back on acl_get_fd if this function is +needed but not available. +(acl_get_fdfile): New function, if needed. +(file_has_aclinfo): Reimplement in terms of ... +(fdfile_has_aclinfo): ... this new function, +which also has an FD argument. +* lib/qcopy-acl.c [USE_XATTR]: Include dirent.h, for DT_DIR etc. +(qcopy_acl): If attr_copy_file or attr_copy_fd fail with EOPNOTSUPP, +don’t fail if the source has a trivial ACL (this is the part +that fixes the bug; the rest is optimization). +* modules/qcopy-acl (Depends-on): Depend on dirent-h and +file-has-acl if $use_xattr. Also, depend on acl-permissions +unconditionally, since qcopy-acl.c includes acl.h unconditionally. + + +--- + lib/acl.h | 2 + + lib/copy-acl.c | 1 + + lib/file-has-acl.c | 172 ++++++++++++++++++++++++++++++++------------- + lib/qcopy-acl.c | 29 ++++++-- + 7 files changed, 182 insertions(+), 53 deletions(-) + +diff --git a/lib/acl.h b/lib/acl.h +index 90fd24e1..e3c134fb 100644 +--- a/lib/acl.h ++++ b/lib/acl.h +@@ -79,6 +79,8 @@ struct aclinfo + bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST; + int file_has_acl (char const *, struct stat const *); + int file_has_aclinfo (char const *restrict, struct aclinfo *restrict, int); ++int fdfile_has_aclinfo (int, char const *restrict, ++ struct aclinfo *restrict, int); + + #if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR + bool aclinfo_has_xattr (struct aclinfo const *, char const *) +diff --git a/lib/copy-acl.c b/lib/copy-acl.c +index c36f64e5..2fce6c7d 100644 +--- a/lib/copy-acl.c ++++ b/lib/copy-acl.c +@@ -33,6 +33,7 @@ + a valid file descriptor, use file descriptor operations, else use + filename based operations on SRC_NAME. Likewise for DEST_DESC and + DST_NAME. ++ MODE should be the source file's st_mode. + If access control lists are not available, fchmod the target file to + MODE. Also sets the non-permission bits of the destination file + (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set. +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 66b920c1..a356ee0d 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -85,6 +85,13 @@ smack_new_label_from_path (MAYBE_UNUSED const char *path, + { + return -1; + } ++static ssize_t ++smack_new_label_from_file (MAYBE_UNUSED int fd, ++ MAYBE_UNUSED const char *xattr, ++ MAYBE_UNUSED char **label) ++{ ++ return -1; ++} + # endif + static bool + is_smack_enabled (void) +@@ -115,14 +122,16 @@ aclinfo_may_indicate_xattr (struct aclinfo const *ai) + + static bool + has_xattr (char const *xattr, struct aclinfo const *ai, +- MAYBE_UNUSED char const *restrict name, MAYBE_UNUSED int flags) ++ int fd, char const *restrict name, int flags) + { + if (ai && aclinfo_has_xattr (ai, xattr)) + return true; + else if (!ai || aclinfo_may_indicate_xattr (ai)) + { +- int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) +- (name, xattr, NULL, 0)); ++ int ret = (fd < 0 ++ ? ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) ++ (name, xattr, NULL, 0)) ++ : fgetxattr (fd, xattr, NULL, 0)); + if (0 <= ret || (errno == ERANGE || errno == E2BIG)) + return true; + } +@@ -145,11 +154,12 @@ aclinfo_has_xattr (struct aclinfo const *ai, char const *xattr) + return false; + } + +-/* Get attributes of the file NAME into AI, if USE_ACL. ++/* Get attributes of the file FD aka NAME into AI, if USE_ACL. ++ Ignore FD if it is negative. + If FLAGS & ACL_GET_SCONTEXT, also get security context. + If FLAGS & ACL_SYMLINK_FOLLOW, follow symbolic links. */ + static void +-get_aclinfo (char const *name, struct aclinfo *ai, int flags) ++get_aclinfo (int fd, char const *name, struct aclinfo *ai, int flags) + { + int scontext_err = ENOTSUP; + ai->buf = ai->u.__gl_acl_ch; +@@ -163,7 +173,9 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr); + while (true) + { +- ai->size = lsxattr (name, ai->buf, acl_alloc); ++ ai->size = (fd < 0 ++ ? lsxattr (name, ai->buf, acl_alloc) ++ : flistxattr (fd, ai->buf, acl_alloc)); + if (0 < ai->size) + break; + ai->u.err = ai->size < 0 ? errno : 0; +@@ -171,7 +183,9 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + break; + + /* The buffer was too small. Find how large it should have been. */ +- ssize_t size = lsxattr (name, NULL, 0); ++ ssize_t size = (fd < 0 ++ ? lsxattr (name, NULL, 0) ++ : flistxattr (fd, NULL, 0)); + if (size <= 0) + { + ai->size = size; +@@ -214,9 +228,13 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + { + if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SMACK)) + { +- ssize_t r = smack_new_label_from_path (name, "security.SMACK64", +- flags & ACL_SYMLINK_FOLLOW, +- &ai->scontext); ++ static char const SMACK64[] = "security.SMACK64"; ++ ssize_t r = ++ (fd < 0 ++ ? smack_new_label_from_path (name, SMACK64, ++ flags & ACL_SYMLINK_FOLLOW, ++ &ai->scontext) ++ : smack_new_label_from_file (fd, SMACK64, &ai->scontext)); + scontext_err = r < 0 ? errno : 0; + } + } +@@ -226,8 +244,10 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags) + if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SELINUX)) + { + ssize_t r = +- ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon) +- (name, &ai->scontext)); ++ (fd < 0 ++ ? ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon) ++ (name, &ai->scontext)) ++ : fgetfilecon (fd, &ai->scontext)); + scontext_err = r < 0 ? errno : 0; + # ifndef SE_SELINUX_INLINE + /* Gnulib's selinux-h module is not in use, so getfilecon and +@@ -362,11 +382,13 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + } + #endif + +-#if (!USE_LINUX_XATTR && USE_ACL && HAVE_ACL_GET_FD \ +- && !HAVE_ACL_EXTENDED_FILE && !HAVE_ACL_TYPE_EXTENDED \ +- && !HAVE_ACL_GET_LINK_NP) +-# include +-# ifdef O_PATH ++#if (!USE_LINUX_XATTR && USE_ACL && !HAVE_ACL_EXTENDED_FILE \ ++ && !HAVE_ACL_TYPE_EXTENDED) ++ ++# if HAVE_ACL_GET_FD && !HAVE_ACL_GET_LINK_NP ++# include ++# ifdef O_PATH ++# define acl_get_fd_np(fd, type) acl_get_fd (fd) + + /* Like acl_get_file, but do not follow symbolic links. */ + static acl_t +@@ -381,8 +403,24 @@ acl_get_link_np (char const *name, acl_type_t type) + errno = err; + return r; + } +-# define HAVE_ACL_GET_LINK_NP 1 ++# define HAVE_ACL_GET_LINK_NP 1 ++# endif + # endif ++ ++static acl_t ++acl_get_fdfile (int fd, char const *name, acl_type_t type, int flags) ++{ ++ acl_t (*get) (char const *, acl_type_t) = acl_get_file; ++# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10, Cygwin >= 2.5 */ ++ if (0 <= fd) ++ return acl_get_fd_np (fd, type); ++ if (! (flags & ACL_SYMLINK_FOLLOW)) ++ get = acl_get_link_np; ++# else ++ /* Ignore FD and FLAGS, unfortunately. */ ++# endif ++ return get (name, type); ++} + #endif + + /* Return 1 if NAME has a nontrivial access control list, +@@ -398,14 +436,35 @@ acl_get_link_np (char const *name, acl_type_t type) + If the d_type value is not known, use DT_UNKNOWN though this may be less + efficient. */ + int +-file_has_aclinfo (MAYBE_UNUSED char const *restrict name, ++file_has_aclinfo (char const *restrict name, + struct aclinfo *restrict ai, int flags) ++{ ++ return fdfile_has_aclinfo (-1, name, ai, flags); ++} ++ ++/* Return 1 if FD aka NAME has a nontrivial access control list, ++ 0 if ACLs are not supported, or if NAME has no or only a base ACL, ++ and -1 (setting errno) on error. Note callers can determine ++ if ACLs are not supported as errno is set in that case also. ++ Ignore FD if it is negative. ++ Set *AI to ACL info regardless of return value. ++ FLAGS should be a d_type value, optionally ORed with ++ - _GL_DT_NOTDIR if it is known that NAME is not a directory, ++ - ACL_GET_SCONTEXT to retrieve security context and return 1 if present, ++ - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link; ++ otherwise do not follow them if possible. ++ If the d_type value is not known, use DT_UNKNOWN though this may be less ++ efficient. */ ++int ++fdfile_has_aclinfo (MAYBE_UNUSED int fd, ++ MAYBE_UNUSED char const *restrict name, ++ struct aclinfo *restrict ai, int flags) + { + MAYBE_UNUSED unsigned char d_type = flags & UCHAR_MAX; + + #if USE_LINUX_XATTR + int initial_errno = errno; +- get_aclinfo (name, ai, flags); ++ get_aclinfo (fd, name, ai, flags); + + if (!aclinfo_may_indicate_xattr (ai) && ai->size <= 0) + { +@@ -418,11 +477,11 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + In earlier Fedora the two types of ACLs were mutually exclusive. + Attempt to work correctly on both kinds of systems. */ + +- if (!has_xattr (XATTR_NAME_NFSV4_ACL, ai, name, flags)) ++ if (!has_xattr (XATTR_NAME_NFSV4_ACL, ai, fd, name, flags)) + return +- (has_xattr (XATTR_NAME_POSIX_ACL_ACCESS, ai, name, flags) ++ (has_xattr (XATTR_NAME_POSIX_ACL_ACCESS, ai, fd, name, flags) + || ((d_type == DT_DIR || d_type == DT_UNKNOWN) +- && has_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ai, name, flags))); ++ && has_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ai, fd, name, flags))); + + /* A buffer large enough to hold any trivial NFSv4 ACL. + The max length of a trivial NFSv4 ACL is 6 words for owner, +@@ -432,8 +491,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + everyone is another word to hold "EVERYONE@". */ + uint32_t buf[2 * (6 + 6 + 7)]; + +- int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) +- (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf)); ++ int ret = (fd < 0 ++ ? ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr) ++ (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf)) ++ : fgetxattr (fd, XATTR_NAME_NFSV4_ACL, buf, sizeof buf)); + if (ret < 0) + switch (errno) + { +@@ -467,20 +528,23 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + /* On Linux, acl_extended_file is an optimized function: It only + makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for + ACL_TYPE_DEFAULT. */ +- ret = ((flags & ACL_SYMLINK_FOLLOW +- ? acl_extended_file +- : acl_extended_file_nofollow) +- (name)); ++ ret = (fd < 0 ++ ? ((flags & ACL_SYMLINK_FOLLOW ++ ? acl_extended_file ++ : acl_extended_file_nofollow) ++ (name)) ++ : acl_extended_fd (fd)); + # elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ + /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) + and acl_get_file (name, ACL_TYPE_DEFAULT) + always return NULL / EINVAL. There is no point in making + these two useless calls. The real ACL is retrieved through +- acl_get_file (name, ACL_TYPE_EXTENDED). */ +- acl_t acl = ((flags & ACL_SYMLINK_FOLLOW +- ? acl_get_file +- : acl_get_link_np) +- (name, ACL_TYPE_EXTENDED)); ++ ACL_TYPE_EXTENDED. */ ++ acl_t acl = ++ (fd < 0 ++ ? ((flags & ACL_SYMLINK_FOLLOW ? acl_get_file : acl_get_link_np) ++ (name, ACL_TYPE_EXTENDED)) ++ : acl_get_fd_np (fd, ACL_TYPE_EXTENDED)); + if (acl) + { + ret = acl_extended_nontrivial (acl); +@@ -489,13 +553,8 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + else + ret = -1; + # else /* FreeBSD, NetBSD >= 10, IRIX, Tru64, Cygwin >= 2.5 */ +- acl_t (*acl_get_file_or_link) (char const *, acl_type_t) = acl_get_file; +-# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10, Cygwin >= 2.5 */ +- if (! (flags & ACL_SYMLINK_FOLLOW)) +- acl_get_file_or_link = acl_get_link_np; +-# endif + +- acl_t acl = acl_get_file_or_link (name, ACL_TYPE_ACCESS); ++ acl_t acl = acl_get_fdfile (fd, name, ACL_TYPE_ACCESS, flags); + if (acl) + { + ret = acl_access_nontrivial (acl); +@@ -517,7 +576,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + && (d_type == DT_DIR + || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR)))) + { +- acl = acl_get_file_or_link (name, ACL_TYPE_DEFAULT); ++ acl = acl_get_fdfile (fd, name, ACL_TYPE_DEFAULT, flags); + if (acl) + { + # ifdef __CYGWIN__ /* Cygwin >= 2.5 */ +@@ -562,7 +621,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + + /* Solaris 10 (newer version), which has additional API declared in + (acl_t) and implemented in libsec (acl_set, acl_trivial, +- acl_fromtext, ...). */ ++ acl_fromtext, ...). ++ ++ Ignore FD, unfortunately. That is better than mishandling ++ ZFS-style ACLs, as the general case code does. */ + return acl_trivial (name); + + # else /* Solaris, Cygwin, general case */ +@@ -586,7 +648,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + + for (;;) + { +- count = acl (name, GETACL, alloc, entries); ++ count = (fd < 0 ++ ? acl (name, GETACL, alloc, entries) ++ : facl (fd, GETACL, alloc, entries)); + if (count < 0 && errno == ENOSPC) + { + /* Increase the size of the buffer. */ +@@ -657,7 +721,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + + for (;;) + { +- count = acl (name, ACE_GETACL, alloc, entries); ++ count = (fd < 0 ++ ? acl (name, ACE_GETACL, alloc, entries) ++ : facl (fd, ACE_GETACL, alloc, entries)); + if (count < 0 && errno == ENOSPC) + { + /* Increase the size of the buffer. */ +@@ -722,7 +788,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + struct acl_entry entries[NACLENTRIES]; + int count; + +- count = getacl (name, NACLENTRIES, entries); ++ count = (fd < 0 ++ ? getacl (name, NACLENTRIES, entries) ++ : fgetacl (fd, NACLENTRIES, entries)); + + if (count < 0) + { +@@ -751,7 +819,8 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + { + struct stat statbuf; + +- if (stat (name, &statbuf) == -1 && errno != EOVERFLOW) ++ if ((fd < 0 ? stat (name, &statbuf) : fstat (fd, &statbuf)) < 0 ++ && errno != EOVERFLOW) + return -1; + + return acl_nontrivial (count, entries); +@@ -765,6 +834,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + struct acl entries[NACLVENTRIES]; + int count; + ++ /* Ignore FD, unfortunately. */ + count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries); + + if (count < 0) +@@ -809,7 +879,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + /* The docs say that type being 0 is equivalent to ACL_ANY, but it + is not true, in AIX 5.3. */ + type.u64 = ACL_ANY; +- if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0) ++ if (0 <= (fd < 0 ++ ? aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) ++ : aclx_fget (fd, 0, &type, aclbuf, &aclsize, &mode))) + break; + if (errno == ENOSYS) + return 0; +@@ -855,7 +927,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + + union { struct acl a; char room[4096]; } u; + +- if (statacl ((char *) name, STX_NORMAL, &u.a, sizeof (u)) < 0) ++ if ((fd < 0 ++ ? statacl ((char *) name, STX_NORMAL, &u.a, sizeof u) ++ : fstatacl (fd, STX_NORMAL, &u.a, sizeof u)) ++ < 0) + return -1; + + return acl_nontrivial (&u.a); +@@ -866,6 +941,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name, + struct acl entries[NACLENTRIES]; + int count; + ++ /* Ignore FD, unfortunately. */ + count = acl ((char *) name, ACL_GET, NACLENTRIES, entries); + + if (count < 0) +diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c +index ad796615..282f4b2d 100644 +--- a/lib/qcopy-acl.c ++++ b/lib/qcopy-acl.c +@@ -26,6 +26,7 @@ + #if USE_XATTR + + # include ++# include + # include + + # if HAVE_LINUX_XATTR_H +@@ -61,6 +62,7 @@ is_attr_permissions (const char *name, struct error_context *ctx) + a valid file descriptor, use file descriptor operations, else use + filename based operations on SRC_NAME. Likewise for DEST_DESC and + DST_NAME. ++ MODE should be the source file's st_mode. + If access control lists are not available, fchmod the target file to + MODE. Also sets the non-permission bits of the destination file + (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set. +@@ -86,10 +88,29 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, + Functions attr_copy_* return 0 in case we copied something OR nothing + to copy */ + if (ret == 0) +- ret = source_desc <= 0 || dest_desc <= 0 +- ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL) +- : attr_copy_fd (src_name, source_desc, dst_name, dest_desc, +- is_attr_permissions, NULL); ++ { ++ ret = source_desc <= 0 || dest_desc <= 0 ++ ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL) ++ : attr_copy_fd (src_name, source_desc, dst_name, dest_desc, ++ is_attr_permissions, NULL); ++ ++ /* Copying can fail with EOPNOTSUPP even when the source ++ permissions are trivial (Bug#78328). Don't report an error ++ in this case, as the chmod_or_fchmod suffices. */ ++ if (ret < 0 && errno == EOPNOTSUPP) ++ { ++ /* fdfile_has_aclinfo cares only about DT_DIR, _GL_DT_NOTDIR, ++ and DT_LNK (but DT_LNK is not possible here), ++ so use _GL_DT_NOTDIR | DT_UNKNOWN for other file types. */ ++ int flags = S_ISDIR (mode) ? DT_DIR : _GL_DT_NOTDIR | DT_UNKNOWN; ++ ++ struct aclinfo ai; ++ if (!fdfile_has_aclinfo (source_desc, src_name, &ai, flags)) ++ ret = 0; ++ aclinfo_free (&ai); ++ errno = EOPNOTSUPP; ++ } ++ } + #else + /* no XATTR, so we proceed the old dusty way */ + struct permission_context ctx; +-- +2.43.0 \ No newline at end of file diff --git a/coreutils.spec b/coreutils.spec index 18529900a94dbffaebcb08db1e087ae9ec907d37..be7c31e176327f0bd9455b93e85a3138bb08ee5a 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -1,6 +1,6 @@ Name: coreutils Version: 9.4 -Release: 18 +Release: 19 License: GPLv3+ Summary: A set of basic GNU tools commonly used in shell scripts Url: https://www.gnu.org/software/coreutils/ @@ -46,6 +46,67 @@ Patch31: backport-cat-fix-plain-cat-bug.patch Patch32: backport-tail-avoid-infloop-with-c-on-dev-zero.patch Patch33: backport-CVE-2025-5278.patch Patch34: backport-posixtm-pacify-clang-18.patch +Patch35: backport-doc-cp-no-clobber-improve-documentation.patch +Patch36: backport-cp-mv-reinstate-that-n-exits-with-success-if-files-s.patch +Patch37: backport-cp-mv-ensure-i-f-are-not-overridden-by-u.patch +Patch38: backport-od-don-t-assume-no-holes-in-wide-unsigned.patch +Patch39: backport-od-prefer-xpalloc-to-x2realloc.patch +Patch40: backport-od-prefer-xpalloc-to-x2nrealloc.patch +Patch41: backport-od-fix-various-off-by-one-issues-with-strings-with-N.patch +Patch42: backport-od-fix-N.-bug.patch +Patch43: backport-od-fix-another-off-by-one-issue-with-strings.patch +Patch44: backport-od-fix-theoretical-size_t-malloc-overflow.patch +Patch45: backport-od-prefer-idx_t-to-size_t.patch +Patch46: backport-od-output-standard-diagnostics-for-invalid-w-argumen.patch +Patch47: backport-od-prefer-intmax_t-to-uintmax_t.patch +Patch48: backport-od-fix-integer-overflow-with-large-pseudos.patch +Patch49: backport-od-fix-some-unlikely-integer-overflows.patch +Patch50: backport-qcopy-acl-Fix-copying-of-ACLs-on-CentOS-7-regression.patch +Patch51: backport-acl-First-step-towards-more-consistent-function-name.patch +Patch52: backport-file-has-acl-new-function-file_has_aclinfo.patch +Patch53: backport-file-has-acl-tests-omit-string.h-include.patch +Patch54: backport-file-has-acl-improve-acl.h-comment.patch +Patch55: backport-file-has-acl-improve-performance-on-Linux-symlinks.patch +Patch56: backport-file-has-acl.c-port-back-to-macOS.patch +Patch57: backport-file-has-acl-no-need-for-struct-stat.patch +Patch58: backport-file-has-acl-Fix-comments.patch +Patch59: backport-dirent-define-DT_-macros-on-all-platforms.patch +Patch60: backport-file-has-acl-Fix-performance-regression-on-FreeBSD-C.patch +Patch61: backport-file-has-acl-remove-__gl_acl_alloc-member.patch +Patch62: backport-file-has-acl-minor-ENOMEM-fixes.patch +Patch63: backport-file-has-acl-scontext-even-if-disable-acl.patch +Patch64: backport-file-has-acl-port-to-Linux-6.12-NFS-listxattr.patch +Patch65: backport-file-has-acl-Fix-test-failure-on-Cygwin.patch +Patch66: backport-file-has-acl-symlinks-can-have-scontext.patch +Patch67: backport-file-has-acl-handle-NFSv4-ACLs-with-listxattr-return.patch +Patch68: backport-file-has-acl-Fix-compilation-error-regression-yester.patch +Patch69: backport-acl-tests-link-with-FILE_HAS_ACL_LIB.patch +Patch70: backport-file-has-acl-Fix-compilation-errors-on-macOS-et-al.-.patch +Patch71: backport-file-has-acl-minor-refactor-of-acl_get_link_np-fix.patch +Patch72: backport-file-has-acl-avoid-Werror-unused-variable-with-disab.patch +Patch73: backport-file-has-acl-_GL_UNUSED-MAYBE_UNUSED.patch +Patch74: backport-file-has-acl-another-nofollow-fix-for-FreeBSD.patch +Patch75: backport-file-has-acl-port-symlink-code-to-Cygwin.patch +Patch76: backport-qcopy-acl-port-better-to-NFSv4-on-GNU-Linux.patch +Patch77: backport-file-has-acl-handle-listxattr-returning-ENOTSUP.patch +Patch78: backport-ls-use-fewer-xattr-related-syscalls.patch +Patch79: backport-ls-reinstate-capability-checking-in-more-cases.patch +Patch80: backport-ls-tune-is_colored.patch +Patch81: backport-ls-omit-unnecessary-test.patch +Patch82: backport-ls-tune-indicator_name.patch +Patch83: backport-ls-check-FILETYPE_INDICATORS-cardinality.patch +Patch84: backport-ls-omit-cast-from-gobble_file.patch +Patch85: backport-ls-tune-usage-of-getxattr-stat-syscalls.patch +Patch86: backport-ls-fix-spurious-output-with-Z.patch +Patch87: backport-build-update-gnulib-submodule-to-latest.patch +Patch88: backport-ls-fix-security-context-indication-in-long-mode.patch +Patch89: backport-ls-use-Gnulib-s-unsupported-errno-list.patch +Patch90: backport-ls-fix-aclinfo-cache-bug.patch +Patch91: backport-ls-prefer-xpalloc-to-xnrealloc.patch +Patch92: backport-ls-update-comment.patch +Patch93: backport-ls-fix-crash-with-context.patch +Patch94: backport-ls-fix-crash-on-systems-with-SELinux-but-without-xat.patch +Patch95: backport-ls-fix-crash-of-ls-Z-.-on-OpenBSD-s-dev-wd0a-disk.patch Patch9001: coreutils-9.0-sw.patch @@ -190,6 +251,9 @@ fi %{_mandir}/man*/* %changelog +* Thu Aug 21 2025 yanglongkang - 9.4-19 +- sync patches from community + * Mon Jul 14 2025 jchzhou - 9.4-18 - backport a patch to address clang building issues