X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Ffile-has-acl.c;h=50e60d5150666b26b82e41d1727eec69e9d475f5;hb=7e766e7fc7aecffc5b37f4b799921de254a8450d;hp=9bae202f18575c79f6ea908d1555e416e8150674;hpb=feea4bcda87c7fe24c41b77f2f3f453c842bf617;p=gnulib.git diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c index 9bae202f1..50e60d515 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -117,6 +117,84 @@ acl_access_nontrivial (acl_t acl) # endif + +#elif USE_ACL && HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ + +/* Test an ACL retrieved with GETACL. + Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_nontrivial (int count, aclent_t *entries) +{ + int i; + + for (i = 0; i < count; i++) + { + aclent_t *ace = &entries[i]; + + /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). + If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). + We don't need to check ace->a_id in these cases. */ + if (!(ace->a_type == USER_OBJ + || ace->a_type == GROUP_OBJ + || ace->a_type == OTHER_OBJ + /* Note: Cygwin does not return a CLASS_OBJ ("mask:") entry + sometimes. */ + || ace->a_type == CLASS_OBJ)) + return 1; + } + return 0; +} + +# ifdef ACE_GETACL + +/* Test an ACL retrieved with ACE_GETACL. + Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_ace_nontrivial (int count, ace_t *entries) +{ + int i; + + for (i = 0; i < count; i++) + { + ace_t *ace = &entries[i]; + + /* Note: If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from + stat(). If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from + stat(). We don't need to check ace->a_who in these cases. */ + if (!(ace->a_type == ALLOW + && (ace->a_flags == ACE_OWNER + || ace->a_flags == ACE_GROUP + || ace->a_flags == ACE_OTHER))) + return 1; + } + return 0; +} + +# endif + +#elif USE_ACL && HAVE_GETACL /* HP-UX */ + +/* Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ +int +acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb) +{ + int i; + + for (i = 0; i < count; i++) + { + struct acl_entry *ace = &entries[i]; + + if (!((ace->uid == sb->st_uid && ace->gid == ACL_NSGROUP) + || (ace->uid == ACL_NSUSER && ace->gid == sb->st_gid) + || (ace->uid == ACL_NSUSER && ace->gid == ACL_NSGROUP))) + return 1; + } + return 0; +} + #endif @@ -204,7 +282,9 @@ file_has_acl (char const *name, struct stat const *sb) # if HAVE_ACL_TRIVIAL - /* Solaris 10, which also has NFSv4 and ZFS style ACLs. */ + /* 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 */ @@ -212,11 +292,144 @@ file_has_acl (char const *name, struct stat const *sb) /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions of Unixware. The acl() call returns the access and default ACL both at once. */ - int n = acl (name, GETACLCNT, 0, NULL); - return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n); + int count; + { + aclent_t *entries; + + for (;;) + { + count = acl (name, GETACLCNT, 0, NULL); + + if (count < 0) + { + if (errno == ENOSYS || errno == ENOTSUP) + break; + else + return -1; + } + + if (count == 0) + break; + + /* Don't use MIN_ACL_ENTRIES: It's set to 4 on Cygwin, but Cygwin + returns only 3 entries for files with no ACL. But this is safe: + If there are more than 4 entries, there cannot be only the + "user::", "group::", "other:", and "mask:" entries. */ + if (count > 4) + return 1; + + entries = (aclent_t *) malloc (count * sizeof (aclent_t)); + if (entries == NULL) + { + errno = ENOMEM; + return -1; + } + if (acl (name, GETACL, count, entries) == count) + { + if (acl_nontrivial (count, entries)) + { + free (entries); + return 1; + } + free (entries); + break; + } + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + free (entries); + } + } + +# 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). */ + { + ace_t *entries; + + for (;;) + { + count = acl (name, ACE_GETACLCNT, 0, NULL); + + if (count < 0) + { + if (errno == ENOSYS || errno == EINVAL) + break; + else + return -1; + } + + if (count == 0) + break; + + /* If there are more than 3 entries, there cannot be only the + ACE_OWNER, ACE_GROUP, ACE_OTHER entries. */ + if (count > 3) + return 1; + + entries = (ace_t *) malloc (count * sizeof (ace_t)); + if (entries == NULL) + { + errno = ENOMEM; + return -1; + } + if (acl (name, ACE_GETACL, count, entries) == count) + { + if (acl_ace_nontrivial (count, entries)) + { + free (entries); + return 1; + } + free (entries); + break; + } + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + free (entries); + } + } +# endif + return 0; # endif +# elif HAVE_GETACL /* HP-UX */ + + int count; + struct acl_entry entries[NACLENTRIES]; + + for (;;) + { + count = getacl (name, 0, NULL); + + if (count < 0) + return (errno == ENOSYS || errno == EOPNOTSUPP ? 0 : -1); + + if (count == 0) + return 0; + + if (count > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory + allocation. */ + abort (); + + /* If there are more than 3 entries, there cannot be only the + (uid,%), (%,gid), (%,%) entries. */ + if (count > 3) + return 1; + + if (getacl (name, count, entries) == count) + { + struct stat statbuf; + + if (stat (name, &statbuf) < 0) + return -1; + + return acl_nontrivial (count, entries, &statbuf); + } + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + } + # endif } #endif