From f338c12bfa958ff994bec168cb8f6c83eb0b40f3 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 2 Jan 2023 10:37:21 +0100 Subject: [PATCH 1/3] Resolves: #2137866 - basic support for checking NFSv4 ACLs --- coreutils-nfsv4-acls.patch | 625 +++++++++++++++++++++++++++++++++++++ coreutils.spec | 10 +- 2 files changed, 633 insertions(+), 2 deletions(-) create mode 100644 coreutils-nfsv4-acls.patch diff --git a/coreutils-nfsv4-acls.patch b/coreutils-nfsv4-acls.patch new file mode 100644 index 0000000..7dd036d --- /dev/null +++ b/coreutils-nfsv4-acls.patch @@ -0,0 +1,625 @@ +From 5a6af47c3db45b6303bac4dcd6da186fd5cd178c Mon Sep 17 00:00:00 2001 +From: Ondrej Valousek +Date: Fri, 2 Dec 2022 13:40:19 +0100 +Subject: [PATCH 1/3] file-has-acl: Basic support for checking NFSv4 ACLs in + Linux. + +* lib/acl-internal.h (acl_nfs4_nontrivial): New declaration. +* lib/acl-internal.c (acl_nfs4_nontrivial): New function. +* lib/file-has-acl.c: Include . +(XATTR_NAME_NFSV4_ACL, TRIVIAL_NFS4_ACL_MAX_LENGTH): New macros. +(file_has_acl): Test for NFSv4 ACLs. +* doc/acl-nfsv4.txt: New file. + +Upstream-commit: b0604a8e134dbcc307c0ffdd5ebd3693e9de7081 +Signed-off-by: Kamil Dudka +--- + doc/acl-nfsv4.txt | 17 ++++++++ + lib/acl-internal.c | 100 +++++++++++++++++++++++++++++++++++++++++++++ + lib/acl-internal.h | 3 ++ + lib/file-has-acl.c | 21 ++++++++++ + 4 files changed, 141 insertions(+) + create mode 100644 doc/acl-nfsv4.txt + +diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt +new file mode 100644 +index 0000000..71352f5 +--- /dev/null ++++ b/doc/acl-nfsv4.txt +@@ -0,0 +1,17 @@ ++General introduction: ++ https://linux.die.net/man/5/nfs4_acl ++ ++The NFSv4 acls are defined in RFC7530 and as such, every NFSv4 server supporting ACLs ++will support this kind of ACLs (note the difference from POSIX draft ACLs) ++ ++The ACLs can be obtained via the nfsv4-acl-tools, i.e. ++ ++$ nfs4_getfacl ++ ++# file: ++A::OWNER@:rwaDxtTnNcCy ++A::GROUP@:rwaDxtTnNcy ++A::EVERYONE@:rwaDxtTnNcy ++ ++Gnulib is aiming to only provide a basic support of these, i.e. recognize trivial ++and non-trivial ACLs +diff --git a/lib/acl-internal.c b/lib/acl-internal.c +index be244c6..4c65dff 100644 +--- a/lib/acl-internal.c ++++ b/lib/acl-internal.c +@@ -25,6 +25,9 @@ + + #if USE_ACL && HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ + ++# include ++# include ++ + # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ + + /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. +@@ -122,6 +125,103 @@ acl_default_nontrivial (acl_t acl) + return (acl_entries (acl) > 0); + } + ++# define ACE4_WHO_OWNER "OWNER@" ++# define ACE4_WHO_GROUP "GROUP@" ++# define ACE4_WHO_EVERYONE "EVERYONE@" ++ ++# define ACE4_ACCESS_ALLOWED_ACE_TYPE 0 ++# define ACE4_ACCESS_DENIED_ACE_TYPE 1 ++ ++/* ACE flag values */ ++# define ACE4_IDENTIFIER_GROUP 0x00000040 ++# define ROUNDUP(x, y) (((x) + (y) - 1) & - (y)) ++ ++int ++acl_nfs4_nontrivial (char *xattr, int len) ++{ ++ int bufs = len; ++ uint32_t num_aces = ntohl (*((uint32_t*)(xattr))), /* Grab the number of aces in the acl */ ++ num_a_aces = 0, ++ num_d_aces = 0; ++ char *bufp = xattr; ++ ++ bufp += 4; /* sizeof(uint32_t); */ ++ bufs -= 4; ++ ++ for (uint32_t ace_n = 0; num_aces > ace_n ; ace_n++) ++ { ++ int d_ptr; ++ uint32_t flag, ++ wholen, ++ type; ++ ++ /* Get the acl type */ ++ if (bufs <= 0) ++ return -1; ++ ++ type = ntohl (*((uint32_t*)bufp)); ++ ++ bufp += 4; ++ bufs -= 4; ++ if (bufs <= 0) ++ return -1; ++ ++ flag = ntohl (*((uint32_t*)bufp)); ++ /* As per RFC 7530, the flag should be 0, but we are just generous to Netapp ++ * and also accept the Group flag ++ */ ++ if (flag & ~ACE4_IDENTIFIER_GROUP) ++ return 1; ++ ++ /* we skip mask - ++ * it's too risky to test it and it does not seem to be actually needed */ ++ bufp += 2*4; ++ bufs -= 2*4; ++ ++ if (bufs <= 0) ++ return -1; ++ ++ wholen = ntohl (*((uint32_t*)bufp)); ++ ++ bufp += 4; ++ bufs -= 4; ++ ++ /* Get the who string */ ++ if (bufs <= 0) ++ return -1; ++ ++ /* for trivial ACL, we expect max 5 (typically 3) ACES, 3 Allow, 2 deny */ ++ if (((strncmp (bufp, ACE4_WHO_OWNER, wholen) == 0) ++ || (strncmp (bufp, ACE4_WHO_GROUP, wholen) == 0)) ++ && wholen == 6) ++ { ++ if (type == ACE4_ACCESS_ALLOWED_ACE_TYPE) ++ num_a_aces++; ++ if (type == ACE4_ACCESS_DENIED_ACE_TYPE) ++ num_d_aces++; ++ } ++ else ++ if ((strncmp (bufp, ACE4_WHO_EVERYONE, wholen) == 0) ++ && (type == ACE4_ACCESS_ALLOWED_ACE_TYPE) ++ && (wholen == 9)) ++ num_a_aces++; ++ else ++ return 1; ++ ++ d_ptr = ROUNDUP (wholen, 4); ++ bufp += d_ptr; ++ bufs -= d_ptr; ++ ++ /* Make sure we aren't outside our domain */ ++ if (bufs < 0) ++ return -1; ++ ++ } ++ return !((num_a_aces <= 3) && (num_d_aces <= 2) ++ && (num_a_aces + num_d_aces == num_aces)); ++ ++} ++ + # endif + + #elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */ +diff --git a/lib/acl-internal.h b/lib/acl-internal.h +index 9353376..2a249ff 100644 +--- a/lib/acl-internal.h ++++ b/lib/acl-internal.h +@@ -147,6 +147,9 @@ rpl_acl_set_fd (int fd, acl_t acl) + # define acl_entries rpl_acl_entries + extern int acl_entries (acl_t); + # endif ++/* Return 1 if given ACL in XDR format is non-trivial ++ * Return 0 if it is trivial */ ++extern int acl_nfs4_nontrivial (char *, int); + + # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ + /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index e02f062..1710234 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -32,6 +32,11 @@ + #if GETXATTR_WITH_POSIX_ACLS + # include + # include ++# include ++# ifndef XATTR_NAME_NFSV4_ACL ++# define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" ++# endif ++# define TRIVIAL_NFS4_ACL_MAX_LENGTH 128 + #endif + + /* Return 1 if NAME has a nontrivial access control list, +@@ -67,6 +72,22 @@ file_has_acl (char const *name, struct stat const *sb) + return 1; + } + ++ if (ret < 0) ++ { /* we might be on NFS, so try to check NFSv4 ACLs too */ ++ char xattr[TRIVIAL_NFS4_ACL_MAX_LENGTH]; ++ ++ errno = 0; /* we need to reset errno set by the previous getxattr() */ ++ ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, TRIVIAL_NFS4_ACL_MAX_LENGTH); ++ if (ret < 0 && errno == ENODATA) ++ ret = 0; ++ else ++ if (ret < 0 && errno == ERANGE) ++ return 1; /* we won't fit into the buffer, so non-trivial ACL is presented */ ++ else ++ if (ret > 0) ++ /* looks like trivial ACL, but we need to investigate further */ ++ return acl_nfs4_nontrivial (xattr, ret); ++ } + if (ret < 0) + return - acl_errno_valid (errno); + return ret; +-- +2.38.1 + + +From c5266d204a446bea619fa18da8520dceb0a54192 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Fri, 23 Dec 2022 15:18:29 -0800 +Subject: [PATCH 2/3] file-has-acl: improve recent NFSv4 support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This fixes a link failure with emacsclient on GNU/Linux. This +program wants file_has_acl but none of the other ACL primitives, +so it doesn’t link acl-internal.o; this way it doesn’t need to +link with -lacl. While I was at it I reviewed the recent changes, +fixed some unlikely overflow bugs, and adjusted to GNU style. +* doc/acl-nfsv4.txt: Remove. Its contents are now in a +comment in lib/file-has-acl.c. +* lib/acl-internal.c, lib/acl-internal.h: Move recent changes +relating to acl_nfs4_nontrivial to lib/file-has-acl.c, so that +there is no trouble linking programs that need only file_has_acl. +* lib/file-has-acl.c (acl_nfs4_nontrivial): Move here from +lib/acl-internal.c, so that we needn't link -lacl in +programs that want only file_has_acl, such as emacsclient. +Do not assume a char buffer is aligned for uint32_t. +Check more carefully for buffer read overrun. +Allow up to 6 ACEs, since other code does; but check +that they’re distinct. Avoid integer overflow. +Use memcmp rather than strncmp to compare memory blocks. +(file_has_acl): Preserve initial errno instead of setting to 0. +Allocate a bit more room for trivial ACL buffer. +Use EINVAL for botchedk NFSv4 ACLs (which shouldn’t happen). + +Upstream-commit: 35bd46f0c816948dc1a0430c8ba8b10a01167320 +Signed-off-by: Kamil Dudka +--- + doc/acl-nfsv4.txt | 17 ------ + lib/acl-internal.c | 100 ----------------------------------- + lib/acl-internal.h | 3 -- + lib/file-has-acl.c | 129 +++++++++++++++++++++++++++++++++++++++------ + 4 files changed, 113 insertions(+), 136 deletions(-) + delete mode 100644 doc/acl-nfsv4.txt + +diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt +deleted file mode 100644 +index 71352f5..0000000 +--- a/doc/acl-nfsv4.txt ++++ /dev/null +@@ -1,17 +0,0 @@ +-General introduction: +- https://linux.die.net/man/5/nfs4_acl +- +-The NFSv4 acls are defined in RFC7530 and as such, every NFSv4 server supporting ACLs +-will support this kind of ACLs (note the difference from POSIX draft ACLs) +- +-The ACLs can be obtained via the nfsv4-acl-tools, i.e. +- +-$ nfs4_getfacl +- +-# file: +-A::OWNER@:rwaDxtTnNcCy +-A::GROUP@:rwaDxtTnNcy +-A::EVERYONE@:rwaDxtTnNcy +- +-Gnulib is aiming to only provide a basic support of these, i.e. recognize trivial +-and non-trivial ACLs +diff --git a/lib/acl-internal.c b/lib/acl-internal.c +index 4c65dff..be244c6 100644 +--- a/lib/acl-internal.c ++++ b/lib/acl-internal.c +@@ -25,9 +25,6 @@ + + #if USE_ACL && HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ + +-# include +-# include +- + # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ + + /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. +@@ -125,103 +122,6 @@ acl_default_nontrivial (acl_t acl) + return (acl_entries (acl) > 0); + } + +-# define ACE4_WHO_OWNER "OWNER@" +-# define ACE4_WHO_GROUP "GROUP@" +-# define ACE4_WHO_EVERYONE "EVERYONE@" +- +-# define ACE4_ACCESS_ALLOWED_ACE_TYPE 0 +-# define ACE4_ACCESS_DENIED_ACE_TYPE 1 +- +-/* ACE flag values */ +-# define ACE4_IDENTIFIER_GROUP 0x00000040 +-# define ROUNDUP(x, y) (((x) + (y) - 1) & - (y)) +- +-int +-acl_nfs4_nontrivial (char *xattr, int len) +-{ +- int bufs = len; +- uint32_t num_aces = ntohl (*((uint32_t*)(xattr))), /* Grab the number of aces in the acl */ +- num_a_aces = 0, +- num_d_aces = 0; +- char *bufp = xattr; +- +- bufp += 4; /* sizeof(uint32_t); */ +- bufs -= 4; +- +- for (uint32_t ace_n = 0; num_aces > ace_n ; ace_n++) +- { +- int d_ptr; +- uint32_t flag, +- wholen, +- type; +- +- /* Get the acl type */ +- if (bufs <= 0) +- return -1; +- +- type = ntohl (*((uint32_t*)bufp)); +- +- bufp += 4; +- bufs -= 4; +- if (bufs <= 0) +- return -1; +- +- flag = ntohl (*((uint32_t*)bufp)); +- /* As per RFC 7530, the flag should be 0, but we are just generous to Netapp +- * and also accept the Group flag +- */ +- if (flag & ~ACE4_IDENTIFIER_GROUP) +- return 1; +- +- /* we skip mask - +- * it's too risky to test it and it does not seem to be actually needed */ +- bufp += 2*4; +- bufs -= 2*4; +- +- if (bufs <= 0) +- return -1; +- +- wholen = ntohl (*((uint32_t*)bufp)); +- +- bufp += 4; +- bufs -= 4; +- +- /* Get the who string */ +- if (bufs <= 0) +- return -1; +- +- /* for trivial ACL, we expect max 5 (typically 3) ACES, 3 Allow, 2 deny */ +- if (((strncmp (bufp, ACE4_WHO_OWNER, wholen) == 0) +- || (strncmp (bufp, ACE4_WHO_GROUP, wholen) == 0)) +- && wholen == 6) +- { +- if (type == ACE4_ACCESS_ALLOWED_ACE_TYPE) +- num_a_aces++; +- if (type == ACE4_ACCESS_DENIED_ACE_TYPE) +- num_d_aces++; +- } +- else +- if ((strncmp (bufp, ACE4_WHO_EVERYONE, wholen) == 0) +- && (type == ACE4_ACCESS_ALLOWED_ACE_TYPE) +- && (wholen == 9)) +- num_a_aces++; +- else +- return 1; +- +- d_ptr = ROUNDUP (wholen, 4); +- bufp += d_ptr; +- bufs -= d_ptr; +- +- /* Make sure we aren't outside our domain */ +- if (bufs < 0) +- return -1; +- +- } +- return !((num_a_aces <= 3) && (num_d_aces <= 2) +- && (num_a_aces + num_d_aces == num_aces)); +- +-} +- + # endif + + #elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */ +diff --git a/lib/acl-internal.h b/lib/acl-internal.h +index 2a249ff..9353376 100644 +--- a/lib/acl-internal.h ++++ b/lib/acl-internal.h +@@ -147,9 +147,6 @@ rpl_acl_set_fd (int fd, acl_t acl) + # define acl_entries rpl_acl_entries + extern int acl_entries (acl_t); + # endif +-/* Return 1 if given ACL in XDR format is non-trivial +- * Return 0 if it is trivial */ +-extern int acl_nfs4_nontrivial (char *, int); + + # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ + /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 1710234..676523b 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -29,14 +29,97 @@ + + #include "acl-internal.h" + +-#if GETXATTR_WITH_POSIX_ACLS ++#if USE_ACL && GETXATTR_WITH_POSIX_ACLS ++# include ++# include + # include + # include +-# include + # ifndef XATTR_NAME_NFSV4_ACL + # define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" + # endif +-# define TRIVIAL_NFS4_ACL_MAX_LENGTH 128 ++ ++enum { ++ /* ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000, */ ++ ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001, ++ ACE4_IDENTIFIER_GROUP = 0x00000040 ++}; ++ ++/* 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. ++ ++ See . The NFSv4 acls are ++ defined in Internet RFC 7530 and as such, every NFSv4 server ++ supporting ACLs should support NFSv4 ACLs (they differ from from ++ POSIX draft ACLs). The ACLs can be obtained via the ++ nfsv4-acl-tools, e.g., the nfs4_getfacl command. Gnulib provides ++ only basic support of NFSv4 ACLs, i.e., recognize trivial vs ++ nontrivial ACLs. */ ++ ++static int ++acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) ++{ ++ enum { BYTES_PER_NETWORK_UINT = 4}; ++ ++ /* Grab the number of aces in the acl. */ ++ nbytes -= BYTES_PER_NETWORK_UINT; ++ if (nbytes < 0) ++ return -1; ++ uint32_t num_aces = ntohl (*xattr++); ++ if (6 < num_aces) ++ return 1; ++ int ace_found = 0; ++ ++ for (int ace_n = 0; ace_n < num_aces; ace_n++) ++ { ++ /* Get the acl type and flag. Skip the mask; it's too risky to ++ test it and it does not seem to be needed. Get the wholen. */ ++ nbytes -= 4 * BYTES_PER_NETWORK_UINT; ++ if (nbytes < 0) ++ return -1; ++ uint32_t type = ntohl (xattr[0]); ++ uint32_t flag = ntohl (xattr[1]); ++ uint32_t wholen = ntohl (xattr[3]); ++ xattr += 4; ++ int64_t wholen4 = wholen; ++ wholen4 = ((wholen4 + (BYTES_PER_NETWORK_UINT)) ++ & ~ (BYTES_PER_NETWORK_UINT - 1)); ++ ++ /* Trivial ACLs have only ACE4_ACCESS_ALLOWED_ACE_TYPE or ++ ACE4_ACCESS_DENIED_ACE_TYPE. */ ++ if (ACE4_ACCESS_DENIED_ACE_TYPE < type) ++ return 1; ++ ++ /* RFC 7530 says FLAG should be 0, but be generous to NetApp and ++ also accept the group flag. */ ++ if (flag & ~ACE4_IDENTIFIER_GROUP) ++ return 1; ++ ++ /* Get the who string. Check NBYTES - WHOLEN4 before storing ++ into NBYTES, to avoid truncation on conversion. */ ++ if (nbytes - wholen4 < 0) ++ return -1; ++ nbytes -= wholen4; ++ ++ /* For a trivial ACL, max 6 (typically 3) ACEs, 3 allow, 3 deny. ++ Check that there is at most one ACE of each TYPE and WHO. */ ++ int who2 ++ = (wholen == 6 && memcmp (xattr, "OWNER@", 6) == 0 ? 0 ++ : wholen == 6 && memcmp (xattr, "GROUP@", 6) == 0 ? 2 ++ : wholen == 9 && memcmp (xattr, "EVERYONE@", 9) == 0 ? 4 ++ : -1); ++ if (who2 < 0) ++ return 1; ++ int ace_found_bit = 1 << (who2 | type); ++ if (ace_found & ace_found_bit) ++ return 1; ++ ace_found |= ace_found_bit; ++ ++ xattr = (uint32_t *) ((char *) xattr + wholen4); ++ } ++ ++ return 0; ++} + #endif + + /* Return 1 if NAME has a nontrivial access control list, +@@ -56,6 +139,7 @@ file_has_acl (char const *name, struct stat const *sb) + # if GETXATTR_WITH_POSIX_ACLS + + ssize_t ret; ++ int initial_errno = errno; + + ret = getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0); + if (ret < 0 && errno == ENODATA) +@@ -73,20 +157,33 @@ file_has_acl (char const *name, struct stat const *sb) + } + + if (ret < 0) +- { /* we might be on NFS, so try to check NFSv4 ACLs too */ +- char xattr[TRIVIAL_NFS4_ACL_MAX_LENGTH]; +- +- errno = 0; /* we need to reset errno set by the previous getxattr() */ +- ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, TRIVIAL_NFS4_ACL_MAX_LENGTH); +- if (ret < 0 && errno == ENODATA) +- ret = 0; ++ { ++ /* Check for NFSv4 ACLs. The max length of a trivial ++ 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 xattr[2 * (6 + 6 + 7)]; ++ ++ ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, sizeof xattr); ++ if (ret < 0) ++ switch (errno) ++ { ++ case ENODATA: return 0; ++ case ERANGE : return 1; /* ACL must be nontrivial. */ ++ } + else +- if (ret < 0 && errno == ERANGE) +- return 1; /* we won't fit into the buffer, so non-trivial ACL is presented */ +- else +- if (ret > 0) +- /* looks like trivial ACL, but we need to investigate further */ +- return acl_nfs4_nontrivial (xattr, ret); ++ { ++ /* It looks like a trivial ACL, but investigate further. */ ++ ret = acl_nfs4_nontrivial (xattr, ret); ++ if (ret < 0) ++ { ++ errno = EINVAL; ++ return ret; ++ } ++ errno = initial_errno; ++ } + } + if (ret < 0) + return - acl_errno_valid (errno); +-- +2.38.1 + + +From faf965110372c82cd99e9f44f0c64f03cdabb2c1 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 27 Dec 2022 20:00:58 -0800 +Subject: [PATCH 3/3] file-has-acl: fix recently-introduced NFSv4 bug + +* lib/file-has-acl.c (acl_nfs4_nontrivial): Fix off-by-one +error when rounding WHOLEN up to next multiple of 4. +Pacify GCC 12.2.1 -Wcast-align. + +Upstream-commit: d65e5a8ba77595a598c9ddb8dfa09c4aea732659 +Signed-off-by: Kamil Dudka +--- + lib/file-has-acl.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index 676523b..7876edc 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -81,9 +81,10 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + uint32_t flag = ntohl (xattr[1]); + uint32_t wholen = ntohl (xattr[3]); + xattr += 4; +- int64_t wholen4 = wholen; +- wholen4 = ((wholen4 + (BYTES_PER_NETWORK_UINT)) +- & ~ (BYTES_PER_NETWORK_UINT - 1)); ++ int whowords = (wholen / BYTES_PER_NETWORK_UINT ++ + (wholen % BYTES_PER_NETWORK_UINT != 0)); ++ int64_t wholen4 = whowords; ++ wholen4 *= BYTES_PER_NETWORK_UINT; + + /* Trivial ACLs have only ACE4_ACCESS_ALLOWED_ACE_TYPE or + ACE4_ACCESS_DENIED_ACE_TYPE. */ +@@ -115,7 +116,7 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes) + return 1; + ace_found |= ace_found_bit; + +- xattr = (uint32_t *) ((char *) xattr + wholen4); ++ xattr += whowords; + } + + return 0; +-- +2.38.1 + diff --git a/coreutils.spec b/coreutils.spec index ec92f0a..4ff2360 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -1,7 +1,7 @@ Summary: A set of basic GNU tools commonly used in shell scripts Name: coreutils Version: 9.1 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv3+ Url: https://www.gnu.org/software/coreutils/ Source0: https://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.xz @@ -20,11 +20,14 @@ Source106: coreutils-colorls.csh # Make simple backups in correct dir; broken in 9.1 Patch1: gnulib-simple-backup-fix.patch +# basic support for checking NFSv4 ACLs (#2137866) +Patch2: coreutils-nfsv4-acls.patch + # disable the test-lock gnulib test prone to deadlock Patch100: coreutils-8.26-test-lock.patch # require_selinux_(): use selinuxenabled(8) if available -Patch105: coreutils-8.26-selinuxenable.patch +Patch101: coreutils-8.26-selinuxenable.patch # downstream changes to default DIR_COLORS Patch102: coreutils-8.32-DIR_COLORS.patch @@ -265,6 +268,9 @@ rm -f $RPM_BUILD_ROOT%{_infodir}/dir %license COPYING %changelog +* Mon Jan 02 2023 Kamil Dudka - 9.1-7 +- basic support for checking NFSv4 ACLs (#2137866) + * Mon Aug 08 2022 Kamil Dudka - 9.1-6 - improve wording of a comment in /etc/DIR_COLORS (#2112593) From e1a1745d0cab2fca0f224921ec0966ce5a3bdb3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Thu, 4 May 2023 19:40:43 +0100 Subject: [PATCH 2/3] further fixes to backups; broken in 9.1 From https://bugs.gnu.org/55029 From https://bugs.gnu.org/62607 --- coreutils.spec | 5 ++- gnulib-simple-backup-fix.patch | 64 +++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/coreutils.spec b/coreutils.spec index 4ff2360..41d11e1 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -1,7 +1,7 @@ Summary: A set of basic GNU tools commonly used in shell scripts Name: coreutils Version: 9.1 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv3+ Url: https://www.gnu.org/software/coreutils/ Source0: https://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.xz @@ -268,6 +268,9 @@ rm -f $RPM_BUILD_ROOT%{_infodir}/dir %license COPYING %changelog +* Thu May 04 2023 Pádraig Brady - 9.1-8 +- further fixes to backups; broken in 9.1 + * Mon Jan 02 2023 Kamil Dudka - 9.1-7 - basic support for checking NFSv4 ACLs (#2137866) diff --git a/gnulib-simple-backup-fix.patch b/gnulib-simple-backup-fix.patch index 9c2d8a7..090ae21 100644 --- a/gnulib-simple-backup-fix.patch +++ b/gnulib-simple-backup-fix.patch @@ -1,36 +1,60 @@ -commit 7347caeb9d902d3fca2c11f69a55a3e578d93bfe -Author: Paul Eggert -Date: Wed Apr 20 19:34:57 2022 -0700 - - backupfile: fix bug when renaming simple backups - - * lib/backupfile.c (backupfile_internal): Fix bug when RENAME - and when doing simple backups. Problem reported by Steve Ward in: - https://bugs.gnu.org/55029 - -diff --git a/lib/backupfile.c b/lib/backupfile.c -index 1e9290a187..d9f465a3e0 100644 ---- a/lib/backupfile.c -+++ b/lib/backupfile.c -@@ -332,7 +332,7 @@ backupfile_internal (int dir_fd, char const *file, +diff -Naur coreutils-9.1.orig/lib/backupfile.c coreutils-9.1/lib/backupfile.c +--- coreutils-9.1.orig/lib/backupfile.c 2022-04-08 11:22:26.000000000 +0000 ++++ coreutils-9.1/lib/backupfile.c 2023-05-04 17:07:20.784911071 +0000 +@@ -332,7 +332,7 @@ return s; DIR *dirp = NULL; - int sdir = AT_FDCWD; -+ int sdir = dir_fd; ++ int sdir = -1; idx_t base_max = 0; while (true) { -@@ -371,10 +371,9 @@ backupfile_internal (int dir_fd, char const *file, +@@ -371,10 +371,10 @@ if (! rename) break; - int olddirfd = sdir < 0 ? dir_fd : sdir; -- idx_t offset = sdir < 0 ? 0 : base_offset; -+ idx_t offset = backup_type == simple_backups ? 0 : base_offset; ++ dir_fd = sdir < 0 ? dir_fd : sdir; + idx_t offset = sdir < 0 ? 0 : base_offset; unsigned flags = backup_type == simple_backups ? 0 : RENAME_NOREPLACE; - if (renameatu (olddirfd, file + offset, sdir, s + offset, flags) == 0) -+ if (renameatu (sdir, file + offset, sdir, s + offset, flags) == 0) ++ if (renameatu (dir_fd, file + offset, dir_fd, s + offset, flags) == 0) break; int e = errno; if (! (e == EEXIST && extended)) +diff -Naur coreutils-9.1.orig/tests/cp/backup-dir.sh coreutils-9.1/tests/cp/backup-dir.sh +--- coreutils-9.1.orig/tests/cp/backup-dir.sh 2022-04-08 11:22:18.000000000 +0000 ++++ coreutils-9.1/tests/cp/backup-dir.sh 2023-05-04 17:07:24.851960384 +0000 +@@ -1,5 +1,5 @@ + #!/bin/sh +-# Ensure that cp -b doesn't back up directories. ++# Ensure that cp -b handles directories appropriately + + # Copyright (C) 2006-2022 Free Software Foundation, Inc. + +@@ -29,4 +29,10 @@ + test -d y/x || fail=1 + test -d y/x~ && fail=1 + ++# Bug 62607. ++# This would fail to backup using rename, and thus fail to replace the file ++mkdir -p src/foo dst/foo || framework_failure_ ++touch src/foo/bar dst/foo/bar || framework_failure_ ++cp --recursive --backup src/* dst || fail=1 ++ + Exit $fail +diff -Naur coreutils-9.1.orig/tests/mv/backup-dir.sh coreutils-9.1/tests/mv/backup-dir.sh +--- coreutils-9.1.orig/tests/mv/backup-dir.sh 2022-04-08 11:22:18.000000000 +0000 ++++ coreutils-9.1/tests/mv/backup-dir.sh 2023-05-04 17:03:29.593098230 +0000 +@@ -36,4 +36,10 @@ + mv -T --backup=numbered C E/ || fail=1 + mv -T --backup=numbered D E/ || fail=1 + ++# Bug#55029 ++mkdir F && echo 1 >1 && echo 2 >2 && cp 1 F/X && cp 2 X || framework_failure_ ++mv --backup=simple X F/ || fail=1 ++compare 1 F/X~ || fail=1 ++compare 2 F/X || fail=1 ++ + Exit $fail From 3332d143e3197bcc4d1f17a2f49342f2e8d5c0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Thu, 4 May 2023 22:14:24 +0100 Subject: [PATCH 3/3] fix some directory-relative syscalls; broken in 9.1 From https://bugs.gnu.org/55910 From https://bugs.gnu.org/63245 --- coreutils-9.1-copy-relative-dir.patch | 254 ++++++++++++++++++++++++++ coreutils.spec | 4 + 2 files changed, 258 insertions(+) create mode 100644 coreutils-9.1-copy-relative-dir.patch diff --git a/coreutils-9.1-copy-relative-dir.patch b/coreutils-9.1-copy-relative-dir.patch new file mode 100644 index 0000000..a75d162 --- /dev/null +++ b/coreutils-9.1-copy-relative-dir.patch @@ -0,0 +1,254 @@ +diff -Naur coreutils-9.1.orig/src/copy.c coreutils-9.1/src/copy.c +--- coreutils-9.1.orig/src/copy.c 2022-04-15 13:53:28.000000000 +0000 ++++ coreutils-9.1/src/copy.c 2023-05-04 21:09:46.290253081 +0000 +@@ -1954,6 +1954,7 @@ + bool restore_dst_mode = false; + char *earlier_file = NULL; + char *dst_backup = NULL; ++ char const *drelname = *dst_relname ? dst_relname : "."; + bool delayed_ok; + bool copied_as_regular = false; + bool dest_is_symlink = false; +@@ -1971,7 +1972,7 @@ + if (x->move_mode) + { + if (rename_errno < 0) +- rename_errno = (renameatu (AT_FDCWD, src_name, dst_dirfd, dst_relname, ++ rename_errno = (renameatu (AT_FDCWD, src_name, dst_dirfd, drelname, + RENAME_NOREPLACE) + ? errno : 0); + nonexistent_dst = *rename_succeeded = new_dst = rename_errno == 0; +@@ -1983,7 +1984,7 @@ + { + char const *name = rename_errno == 0 ? dst_name : src_name; + int dirfd = rename_errno == 0 ? dst_dirfd : AT_FDCWD; +- char const *relname = rename_errno == 0 ? dst_relname : src_name; ++ char const *relname = rename_errno == 0 ? drelname : src_name; + int fstatat_flags + = x->dereference == DEREF_NEVER ? AT_SYMLINK_NOFOLLOW : 0; + if (follow_fstatat (dirfd, relname, &src_sb, fstatat_flags) != 0) +@@ -2051,8 +2052,7 @@ + int fstatat_flags = use_lstat ? AT_SYMLINK_NOFOLLOW : 0; + if (!use_lstat && nonexistent_dst < 0) + new_dst = true; +- else if (follow_fstatat (dst_dirfd, dst_relname, &dst_sb, +- fstatat_flags) ++ else if (follow_fstatat (dst_dirfd, drelname, &dst_sb, fstatat_flags) + == 0) + { + have_dst_lstat = use_lstat; +@@ -2077,7 +2077,7 @@ + bool return_now = false; + + if (x->interactive != I_ALWAYS_NO +- && ! same_file_ok (src_name, &src_sb, dst_dirfd, dst_relname, ++ && ! same_file_ok (src_name, &src_sb, dst_dirfd, drelname, + &dst_sb, x, &return_now)) + { + error (0, 0, _("%s and %s are the same file"), +@@ -2140,7 +2140,7 @@ + cp and mv treat -i and -f differently. */ + if (x->move_mode) + { +- if (abandon_move (x, dst_name, dst_dirfd, dst_relname, &dst_sb)) ++ if (abandon_move (x, dst_name, dst_dirfd, drelname, &dst_sb)) + { + /* Pretend the rename succeeded, so the caller (mv) + doesn't end up removing the source file. */ +@@ -2321,14 +2321,11 @@ + Otherwise, use AT_SYMLINK_NOFOLLOW, in case dst_name is a symlink. */ + if (have_dst_lstat) + dst_lstat_sb = &dst_sb; ++ else if (fstatat (dst_dirfd, drelname, &tmp_buf, AT_SYMLINK_NOFOLLOW) ++ == 0) ++ dst_lstat_sb = &tmp_buf; + else +- { +- if (fstatat (dst_dirfd, dst_relname, &tmp_buf, +- AT_SYMLINK_NOFOLLOW) == 0) +- dst_lstat_sb = &tmp_buf; +- else +- lstat_ok = false; +- } ++ lstat_ok = false; + + /* Never copy through a symlink we've just created. */ + if (lstat_ok +@@ -2475,8 +2472,7 @@ + if (x->move_mode) + { + if (rename_errno == EEXIST) +- rename_errno = ((renameat (AT_FDCWD, src_name, dst_dirfd, dst_relname) +- == 0) ++ rename_errno = (renameat (AT_FDCWD, src_name, dst_dirfd, drelname) == 0 + ? 0 : errno); + + if (rename_errno == 0) +@@ -2576,7 +2572,7 @@ + or not, and this is enforced above. Therefore we check the src_mode + and operate on dst_name here as a tighter constraint and also because + src_mode is readily available here. */ +- if ((unlinkat (dst_dirfd, dst_relname, ++ if ((unlinkat (dst_dirfd, drelname, + S_ISDIR (src_mode) ? AT_REMOVEDIR : 0) + != 0) + && errno != ENOENT) +@@ -2646,7 +2642,7 @@ + to ask mkdir to copy all the CHMOD_MODE_BITS, letting mkdir + decide what to do with S_ISUID | S_ISGID | S_ISVTX. */ + mode_t mode = dst_mode_bits & ~omitted_permissions; +- if (mkdirat (dst_dirfd, dst_relname, mode) != 0) ++ if (mkdirat (dst_dirfd, drelname, mode) != 0) + { + error (0, errno, _("cannot create directory %s"), + quoteaf (dst_name)); +@@ -2657,8 +2653,7 @@ + for writing the directory's contents. Check if these + permissions are there. */ + +- if (fstatat (dst_dirfd, dst_relname, &dst_sb, +- AT_SYMLINK_NOFOLLOW) != 0) ++ if (fstatat (dst_dirfd, drelname, &dst_sb, AT_SYMLINK_NOFOLLOW) != 0) + { + error (0, errno, _("cannot stat %s"), quoteaf (dst_name)); + goto un_backup; +@@ -2670,7 +2665,7 @@ + dst_mode = dst_sb.st_mode; + restore_dst_mode = true; + +- if (lchmodat (dst_dirfd, dst_relname, dst_mode | S_IRWXU) != 0) ++ if (lchmodat (dst_dirfd, drelname, dst_mode | S_IRWXU) != 0) + { + error (0, errno, _("setting permissions for %s"), + quoteaf (dst_name)); +@@ -2924,7 +2919,7 @@ + /* Now that the destination file is very likely to exist, + add its info to the set. */ + struct stat sb; +- if (fstatat (dst_dirfd, dst_relname, &sb, AT_SYMLINK_NOFOLLOW) == 0) ++ if (fstatat (dst_dirfd, drelname, &sb, AT_SYMLINK_NOFOLLOW) == 0) + record_file (x->dest_info, dst_relname, &sb); + } + +@@ -2957,7 +2952,7 @@ + timespec[1] = get_stat_mtime (&src_sb); + + int utimensat_flags = dest_is_symlink ? AT_SYMLINK_NOFOLLOW : 0; +- if (utimensat (dst_dirfd, dst_relname, timespec, utimensat_flags) != 0) ++ if (utimensat (dst_dirfd, drelname, timespec, utimensat_flags) != 0) + { + error (0, errno, _("preserving times for %s"), quoteaf (dst_name)); + if (x->require_preserve) +@@ -2969,7 +2964,7 @@ + if (!dest_is_symlink && x->preserve_ownership + && (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb))) + { +- switch (set_owner (x, dst_name, dst_dirfd, dst_relname, -1, ++ switch (set_owner (x, dst_name, dst_dirfd, drelname, -1, + &src_sb, new_dst, &dst_sb)) + { + case -1: +@@ -3024,8 +3019,9 @@ + the lstat, but deducing the current destination mode + is tricky in the presence of implementation-defined + rules for special mode bits. */ +- if (new_dst && fstatat (dst_dirfd, dst_relname, &dst_sb, +- AT_SYMLINK_NOFOLLOW) != 0) ++ if (new_dst && (fstatat (dst_dirfd, drelname, &dst_sb, ++ AT_SYMLINK_NOFOLLOW) ++ != 0)) + { + error (0, errno, _("cannot stat %s"), quoteaf (dst_name)); + return false; +@@ -3038,7 +3034,7 @@ + + if (restore_dst_mode) + { +- if (lchmodat (dst_dirfd, dst_relname, dst_mode | omitted_permissions) ++ if (lchmodat (dst_dirfd, drelname, dst_mode | omitted_permissions) + != 0) + { + error (0, errno, _("preserving permissions for %s"), +@@ -3068,7 +3064,7 @@ + if (dst_backup) + { + char const *dst_relbackup = &dst_backup[dst_relname - dst_name]; +- if (renameat (dst_dirfd, dst_relbackup, dst_dirfd, dst_relname) != 0) ++ if (renameat (dst_dirfd, dst_relbackup, dst_dirfd, drelname) != 0) + error (0, errno, _("cannot un-backup %s"), quoteaf (dst_name)); + else + { +diff -Naur coreutils-9.1.orig/src/cp.c coreutils-9.1/src/cp.c +--- coreutils-9.1.orig/src/cp.c 2023-05-04 21:08:46.747627135 +0000 ++++ coreutils-9.1/src/cp.c 2023-05-04 21:09:50.067286480 +0000 +@@ -273,15 +273,19 @@ + when done. */ + + static bool +-re_protect (char const *const_dst_name, int dst_dirfd, char const *dst_relname, ++re_protect (char const *const_dst_name, int dst_dirfd, char const *dst_fullname, + struct dir_attr *attr_list, const struct cp_options *x) + { + struct dir_attr *p; + char *dst_name; /* A copy of CONST_DST_NAME we can change. */ +- char *src_name; /* The source name in 'dst_name'. */ ++ char *src_name; /* The relative source name in 'dst_name'. */ ++ char *full_src_name; /* The full source name in 'dst_name'. */ + + ASSIGN_STRDUPA (dst_name, const_dst_name); +- src_name = dst_name + (dst_relname - const_dst_name); ++ full_src_name = dst_name + (dst_fullname - const_dst_name); ++ src_name = full_src_name; ++ while (*src_name == '/') ++ src_name++; + + for (p = attr_list; p; p = p->next) + { +@@ -324,7 +328,7 @@ + + if (x->preserve_mode) + { +- if (copy_acl (src_name, -1, dst_name, -1, p->st.st_mode) != 0) ++ if (copy_acl (full_src_name, -1, dst_name, -1, p->st.st_mode) != 0) + return false; + } + else if (p->restore_mode) +@@ -664,6 +668,7 @@ + bool parent_exists = true; /* True if dir_name (dst_name) exists. */ + struct dir_attr *attr_list; + char *arg_in_concat = NULL; ++ char *full_arg_in_concat = NULL; + char *arg = file[i]; + + /* Trailing slashes are meaningful (i.e., maybe worth preserving) +@@ -696,6 +701,7 @@ + (x->verbose ? "%s -> %s\n" : NULL), + &attr_list, &new_dst, x)); + ++ full_arg_in_concat = arg_in_concat; + while (*arg_in_concat == '/') + arg_in_concat++; + } +@@ -724,7 +730,7 @@ + new_dst, x, ©_into_self, NULL); + + if (parents_option) +- ok &= re_protect (dst_name, target_dirfd, arg_in_concat, ++ ok &= re_protect (dst_name, target_dirfd, full_arg_in_concat, + attr_list, x); + } + +diff -Naur coreutils-9.1.orig/tests/cp/cp-parents.sh coreutils-9.1/tests/cp/cp-parents.sh +--- coreutils-9.1.orig/tests/cp/cp-parents.sh 2022-04-08 11:22:18.000000000 +0000 ++++ coreutils-9.1/tests/cp/cp-parents.sh 2023-05-04 21:09:50.067286480 +0000 +@@ -66,4 +66,10 @@ + cp --parents --no-preserve=mode np/b/file np_dest/ || fail=1 + p=$(ls -ld np_dest/np|cut -b-10); case $p in drwxr-xr-x);; *) fail=1;; esac + ++# coreutils 9.1-9.3 inclusive would fail to copy acls for absolute dirs ++mkdir dest || framework_failure_ ++if test -f /bin/ls; then ++ cp -t dest --parents -p /bin/ls || fail=1 ++fi ++ + Exit $fail diff --git a/coreutils.spec b/coreutils.spec index 41d11e1..506bb6d 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -23,6 +23,9 @@ Patch1: gnulib-simple-backup-fix.patch # basic support for checking NFSv4 ACLs (#2137866) Patch2: coreutils-nfsv4-acls.patch +# Fix directory-relative syscalls in copy code; broken in 9.1 +Patch3: coreutils-9.1-copy-relative-dir.patch + # disable the test-lock gnulib test prone to deadlock Patch100: coreutils-8.26-test-lock.patch @@ -270,6 +273,7 @@ rm -f $RPM_BUILD_ROOT%{_infodir}/dir %changelog * Thu May 04 2023 Pádraig Brady - 9.1-8 - further fixes to backups; broken in 9.1 +- fix some directory-relative syscalls; broken in 9.1 * Mon Jan 02 2023 Kamil Dudka - 9.1-7 - basic support for checking NFSv4 ACLs (#2137866)