You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
- Written by Paul Eggert and Andreas Gruenbacher. */
+ Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
#include <config.h>
#if USE_ACL && HAVE_ACL_GET_FILE
+# if HAVE_ACL_TYPE_EXTENDED /* MacOS X */
+
+/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
+ Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial. */
+int
+acl_extended_nontrivial (acl_t acl)
+{
+ /* acl is non-trivial if it is non-empty. */
+ return (acl_entries (acl) > 0);
+}
+
+# else /* Linux, FreeBSD, IRIX, Tru64 */
+
/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS.
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_access_nontrivial (acl_t acl)
{
-# if MODE_INSIDE_ACL /* Linux, FreeBSD, IRIX, Tru64 */
/* acl is non-trivial if it has some entries other than for "user::",
"group::", and "other::". Normally these three should be present
at least, allowing us to write
# endif
# endif
-# else /* MacOS X */
+}
- /* acl is non-trivial if it is non-empty. */
- return (acl_entries (acl) > 0);
# endif
+
+
+#elif USE_ACL && HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+
+# if !defined ACL_NO_TRIVIAL /* Solaris <= 10, Cygwin */
+
+/* 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;
+
+ /* The flags in the ace_t structure changed in a binary incompatible way
+ when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
+ How to distinguish the two conventions at runtime?
+ In the old convention, usually three ACEs have a_flags = ACE_OWNER /
+ ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. In the new
+ convention, these values are not used. */
+ int old_convention = 0;
+
+ for (i = 0; i < count; i++)
+ if (entries[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_OTHER))
+ {
+ old_convention = 1;
+ break;
+ }
+
+ if (old_convention)
+ /* Running on Solaris 10. */
+ 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;
+ }
+ else
+ /* Running on Solaris 10 (newer version) or Solaris 11. */
+ for (i = 0; i < count; i++)
+ {
+ ace_t *ace = &entries[i];
+
+ if (!(ace->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE
+ && (ace->a_flags == NEW_ACE_OWNER
+ || ace->a_flags
+ == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP)
+ || ace->a_flags == ACE_EVERYONE)
+ && (ace->a_access_mask
+ & ~(NEW_ACE_READ_DATA | NEW_ACE_WRITE_DATA | NEW_ACE_EXECUTE))
+ == 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+# endif
+
+# 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;
+}
+
+#elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
+
+/* TODO */
+
+#elif USE_ACL && HAVE_STATACL /* older AIX */
+
+/* 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 (struct acl *a)
+{
+ /* The normal way to iterate through an ACL is like this:
+ struct acl_entry *ace;
+ for (ace = a->acl_ext; ace != acl_last (a); ace = acl_nxt (ace))
+ {
+ struct ace_id *aei;
+ switch (ace->ace_type)
+ {
+ case ACC_PERMIT:
+ case ACC_DENY:
+ case ACC_SPECIFY:
+ ...;
+ }
+ for (aei = ace->ace_id; aei != id_last (ace); aei = id_nxt (aei))
+ ...
+ }
+ */
+ return (acl_last (a) != a->acl_ext ? 1 : 0);
}
#endif
/* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
int ret;
- if (HAVE_ACL_EXTENDED_FILE)
- ret = acl_extended_file (name);
- else
+ 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 = acl_extended_file (name);
+ }
+ else /* FreeBSD, MacOS X, IRIX, Tru64 */
{
+# if HAVE_ACL_TYPE_EXTENDED /* MacOS X */
+ /* On MacOS 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 */
acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
if (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 */
+ /* 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
+ filesystem. 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);
else
ret = -1;
}
+# endif
}
else
ret = -1;
+# endif
}
if (ret < 0)
return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
# elif HAVE_ACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
-# if HAVE_ACL_TRIVIAL
+# if defined ACL_NO_TRIVIAL
- /* Solaris 10, which also has NFSv4 and ZFS style ACLs. */
+ /* Solaris 10 (newer version), which has additional API declared in
+ <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
+ acl_fromtext, ...). */
return acl_trivial (name);
# 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
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. */
+ }
+
+# elif HAVE_ACLX_GET && 0 /* AIX */
+
+ /* TODO: use aclx_get(), and then? */
+
+# elif HAVE_STATACL /* older AIX */
+
+ union { struct acl a; char room[4096]; } u;
+
+ if (statacl (name, STX_NORMAL, &u.a, sizeof (u)) < 0)
+ return -1;
+
+ return acl_nontrivial (&u.a);
+
# endif
}
#endif
- /* FIXME: Add support for AIX, Irix, and Tru64. Please see Samba's
- source/lib/sysacls.c file for fix-related ideas. */
-
return 0;
}