Hi Bruno, Just sending the modified patch (fixed formatting + few suggestions from Andreas included) Does it look OK now? Thanks, Ondrej
--- doc/acl-nfsv4.txt | 17 +++++++++ lib/acl-internal.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++ lib/acl-internal.h | 3 ++ lib/file-has-acl.c | 24 ++++++++---- m4/acl.m4 | 5 ++- 5 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 doc/acl-nfsv4.txt diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt new file mode 100644 index 000000000..552a62ca3 --- /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> + +# file: <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 be244c67a..86df38854 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 <string.h> +# include <arpa/inet.h> + # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. @@ -122,6 +125,98 @@ 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 ret = 0,wholen,bufs = len; + uint32_t flag,type,num_aces = ntohl(*((uint32_t*)(xattr))); /* Grab the number of aces in the acl */ + char *bufp = xattr; + int num_a_aces = 0, num_d_aces = 0; + + ret = 0; + bufp += 4; /* sizeof(uint32_t); */ + bufs -= 4; + + for(uint32_t ace_n = 0; num_aces > ace_n ; ace_n++) { + int d_ptr; + + /* 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) ? 0 : 1; + +} + # 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 94553fab2..88f1d8f1d 100644 --- a/lib/acl-internal.h +++ b/lib/acl-internal.h @@ -146,6 +146,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 e02f0626a..8e43dd70f 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -32,6 +32,11 @@ #if GETXATTR_WITH_POSIX_ACLS # include <sys/xattr.h> # include <linux/xattr.h> +# include <arpa/inet.h> +#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) + return acl_nfs4_nontrivial(xattr,ret); + /* looks like trivial ACL, but we need to investigate further */ + } if (ret < 0) return - acl_errno_valid (errno); return ret; diff --git a/m4/acl.m4 b/m4/acl.m4 index 8909442d7..9445edf9c 100644 --- a/m4/acl.m4 +++ b/m4/acl.m4 @@ -197,7 +197,10 @@ AC_DEFUN([gl_FILE_HAS_ACL], [gl_cv_getxattr_with_posix_acls=yes])]) fi if test "$gl_cv_getxattr_with_posix_acls" = yes; then - LIB_HAS_ACL= + dnl We need to pull libacl back to make linker happy, but strictly + dnl speaking, we do not need it + LIB_HAS_ACL=$LIB_ACL + gl_need_lib_has_acl=1 AC_DEFINE([GETXATTR_WITH_POSIX_ACLS], 1, [Define to 1 if getxattr works with XATTR_NAME_POSIX_ACL_ACCESS and XATTR_NAME_POSIX_ACL_DEFAULT.])