/* Test whether a file has a nontrivial access control list.
- Copyright (C) 2002-2003, 2005-2012 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2005-2013 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
#if USE_ACL && HAVE_ACL_GET_FILE
-# if HAVE_ACL_TYPE_EXTENDED /* MacOS X */
+# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
Return 1 if the given ACL is non-trivial.
# if HAVE_ACL_GET_FILE
/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
- /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
+ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
int ret;
if (HAVE_ACL_EXTENDED_FILE) /* Linux */
ACL_TYPE_DEFAULT. */
ret = acl_extended_file (name);
}
- else /* FreeBSD, MacOS X, IRIX, Tru64 */
+ else /* FreeBSD, Mac OS X, IRIX, Tru64 */
{
-# if HAVE_ACL_TYPE_EXTENDED /* MacOS X */
- /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
+# 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
# endif
}
if (ret < 0)
- return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
+ return - acl_errno_valid (errno);
return ret;
-# elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
+# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
# if defined ACL_NO_TRIVIAL
/* 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 count;
{
- aclent_t *entries;
+ /* Initially, try to read the entries into a stack-allocated buffer.
+ Use malloc if it does not fit. */
+ enum
+ {
+ alloc_init = 4000 / sizeof (aclent_t), /* >= 3 */
+ alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (aclent_t))
+ };
+ aclent_t buf[alloc_init];
+ size_t alloc = alloc_init;
+ aclent_t *entries = buf;
+ aclent_t *malloced = NULL;
+ int count;
for (;;)
{
- count = acl (name, GETACLCNT, 0, NULL);
-
- if (count < 0)
+ count = acl (name, GETACL, alloc, entries);
+ if (count < 0 && errno == ENOSPC)
{
- if (errno == ENOSYS || errno == ENOTSUP)
- break;
- else
- return -1;
+ /* Increase the size of the buffer. */
+ free (malloced);
+ if (alloc > alloc_max / 2)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ alloc = 2 * alloc; /* <= alloc_max */
+ entries = malloced =
+ (aclent_t *) malloc (alloc * sizeof (aclent_t));
+ if (entries == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ continue;
}
-
- if (count == 0)
- break;
-
+ break;
+ }
+ if (count < 0)
+ {
+ if (errno == ENOSYS || errno == ENOTSUP)
+ ;
+ else
+ {
+ int saved_errno = errno;
+ free (malloced);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+ else if (count == 0)
+ ;
+ else
+ {
/* 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;
+ free (malloced);
+ return 1;
}
- if (acl (name, GETACL, count, entries) == count)
+
+ if (acl_nontrivial (count, entries))
{
- if (acl_nontrivial (count, entries))
- {
- free (entries);
- return 1;
- }
- free (entries);
- break;
+ free (malloced);
+ return 1;
}
- /* Huh? The number of ACL entries changed since the last call.
- Repeat. */
- free (entries);
}
+ free (malloced);
}
# 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;
+ /* Initially, try to read the entries into a stack-allocated buffer.
+ Use malloc if it does not fit. */
+ enum
+ {
+ alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
+ alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
+ };
+ ace_t buf[alloc_init];
+ size_t alloc = alloc_init;
+ ace_t *entries = buf;
+ ace_t *malloced = NULL;
+ int count;
for (;;)
{
- int ret;
-
- count = acl (name, ACE_GETACLCNT, 0, NULL);
-
- if (count < 0)
+ count = acl (name, ACE_GETACL, alloc, entries);
+ if (count < 0 && errno == ENOSPC)
+ {
+ /* Increase the size of the buffer. */
+ free (malloced);
+ if (alloc > alloc_max / 2)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ alloc = 2 * alloc; /* <= alloc_max */
+ entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
+ if (entries == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ continue;
+ }
+ break;
+ }
+ if (count < 0)
+ {
+ if (errno == ENOSYS || errno == EINVAL)
+ ;
+ else
{
- if (errno == ENOSYS || errno == EINVAL)
- break;
- else
- return -1;
+ int saved_errno = errno;
+ free (malloced);
+ errno = saved_errno;
+ return -1;
}
-
- if (count == 0)
- break;
-
+ }
+ else if (count == 0)
+ ;
+ else
+ {
/* In the old (original Solaris 10) convention:
If there are more than 3 entries, there cannot be only the
ACE_OWNER, ACE_GROUP, ACE_OTHER entries.
NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with
NEW_ACE_ACCESS_DENIED_ACE_TYPE. */
if (count > 6)
- return 1;
-
- entries = (ace_t *) malloc (count * sizeof (ace_t));
- if (entries == NULL)
{
- errno = ENOMEM;
- return -1;
+ free (malloced);
+ return 1;
}
- ret = acl (name, ACE_GETACL, count, entries);
- if (ret < 0)
+
+ if (acl_ace_nontrivial (count, entries))
{
- free (entries);
- if (errno == ENOSYS || errno == EINVAL)
- break;
- else
- return -1;
+ free (malloced);
+ return 1;
}
- if (ret == 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);
}
+ free (malloced);
}
# endif
# elif HAVE_GETACL /* HP-UX */
- for (;;)
- {
- int count;
- struct acl_entry entries[NACLENTRIES];
-
- count = getacl (name, 0, NULL);
-
- if (count < 0)
- {
- /* ENOSYS is seen on newer HP-UX versions.
- EOPNOTSUPP is typically seen on NFS mounts.
- ENOTSUP was seen on Quantum StorNext file systems (cvfs). */
- if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
- break;
- else
- return -1;
- }
-
- if (count == 0)
- return 0;
+ {
+ struct acl_entry entries[NACLENTRIES];
+ int count;
- if (count > NACLENTRIES)
- /* If NACLENTRIES cannot be trusted, use dynamic memory
- allocation. */
- abort ();
+ count = getacl (name, NACLENTRIES, entries);
- /* If there are more than 3 entries, there cannot be only the
- (uid,%), (%,gid), (%,%) entries. */
- if (count > 3)
- return 1;
+ if (count < 0)
+ {
+ /* ENOSYS is seen on newer HP-UX versions.
+ EOPNOTSUPP is typically seen on NFS mounts.
+ ENOTSUP was seen on Quantum StorNext file systems (cvfs). */
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
+ ;
+ else
+ return -1;
+ }
+ else if (count == 0)
+ return 0;
+ else /* count > 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;
return acl_nontrivial (count, entries, &statbuf);
}
- /* Huh? The number of ACL entries changed since the last call.
- Repeat. */
- }
+ }
+ }
# if HAVE_ACLV_H /* HP-UX >= 11.11 */
- for (;;)
- {
- int count;
- struct acl entries[NACLVENTRIES];
-
- count = acl ((char *) name, ACL_CNT, NACLVENTRIES, entries);
-
- if (count < 0)
- {
- /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23.
- EINVAL is seen on NFS in HP-UX 11.31. */
- if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
- break;
- else
- return -1;
- }
+ {
+ struct acl entries[NACLVENTRIES];
+ int count;
- if (count == 0)
- return 0;
+ count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
- if (count > NACLVENTRIES)
- /* If NACLVENTRIES cannot be trusted, use dynamic memory
- allocation. */
- abort ();
+ if (count < 0)
+ {
+ /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23.
+ EINVAL is seen on NFS in HP-UX 11.31. */
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ ;
+ else
+ return -1;
+ }
+ else if (count == 0)
+ return 0;
+ else /* count > 0 */
+ {
+ if (count > NACLVENTRIES)
+ /* If NACLVENTRIES cannot be trusted, use dynamic memory
+ allocation. */
+ abort ();
- /* If there are more than 4 entries, there cannot be only the
- four base ACL entries. */
- if (count > 4)
- return 1;
+ /* If there are more than 4 entries, there cannot be only the
+ four base ACL entries. */
+ if (count > 4)
+ return 1;
- if (acl ((char *) name, ACL_GET, count, entries) == count)
return aclv_nontrivial (count, entries);
- /* Huh? The number of ACL entries changed since the last call.
- Repeat. */
- }
+ }
+ }
# endif
# elif HAVE_ACLSORT /* NonStop Kernel */
- int count;
- struct acl entries[NACLENTRIES];
-
- for (;;)
- {
- count = acl ((char *) name, ACL_CNT, NACLENTRIES, NULL);
-
- if (count < 0)
- {
- if (errno == ENOSYS || errno == ENOTSUP)
- break;
- else
- return -1;
- }
+ {
+ struct acl entries[NACLENTRIES];
+ int count;
- if (count == 0)
- return 0;
+ count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
- if (count > NACLENTRIES)
- /* If NACLENTRIES cannot be trusted, use dynamic memory
- allocation. */
- abort ();
+ if (count < 0)
+ {
+ if (errno == ENOSYS || errno == ENOTSUP)
+ ;
+ else
+ return -1;
+ }
+ else if (count == 0)
+ return 0;
+ else /* count > 0 */
+ {
+ if (count > NACLENTRIES)
+ /* If NACLENTRIES cannot be trusted, use dynamic memory
+ allocation. */
+ abort ();
- /* If there are more than 4 entries, there cannot be only the
- four base ACL entries. */
- if (count > 4)
- return 1;
+ /* If there are more than 4 entries, there cannot be only the
+ four base ACL entries. */
+ if (count > 4)
+ return 1;
- if (acl ((char *) name, ACL_GET, count, entries) == count)
return acl_nontrivial (count, entries);
- /* Huh? The number of ACL entries changed since the last call.
- Repeat. */
- }
+ }
+ }
# endif
}