X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Ffile-has-acl.c;h=53ac317c0ba854903da29d75450a2c706e31dbea;hb=e7086a9a301ffcfef17edbcba9e7c0312c33f7a8;hp=ddae4fe0d9518e36dae15011e2917789d902425f;hpb=797d7f58f18107976336fdcde9fd20cd97a0d483;p=gnulib.git diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c index ddae4fe0d..53ac317c0 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -1,6 +1,6 @@ /* Test whether a file has a nontrivial access control list. - Copyright (C) 2002-2003, 2005-2011 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 @@ -17,6 +17,12 @@ Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ +/* Without this pragma, gcc 4.7.0 20120126 may suggest that the + file_has_acl function might be candidate for attribute 'const' */ +#if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__ +# pragma GCC diagnostic ignored "-Wsuggest-attribute=const" +#endif + #include #include "acl.h" @@ -26,7 +32,7 @@ #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. @@ -69,8 +75,7 @@ acl_access_nontrivial (acl_t acl) } return got_one; -# else /* IRIX, Tru64 */ -# if HAVE_ACL_TO_SHORT_TEXT /* IRIX */ +# elif HAVE_ACL_TO_SHORT_TEXT /* IRIX */ /* Don't use acl_get_entry: it is undocumented. */ int count = acl->acl_cnt; @@ -87,8 +92,7 @@ acl_access_nontrivial (acl_t acl) } return 0; -# endif -# if HAVE_ACL_FREE_TEXT /* Tru64 */ +# elif HAVE_ACL_FREE_TEXT /* Tru64 */ /* Don't use acl_get_entry: it takes only one argument and does not work. */ int count = acl->acl_num; @@ -111,7 +115,10 @@ acl_access_nontrivial (acl_t acl) } return 0; -# endif +# else + + errno = ENOSYS; + return -1; # endif } @@ -148,6 +155,9 @@ acl_nontrivial (int count, aclent_t *entries) # ifdef ACE_GETACL +/* A shortcut for a bitmask. */ +# define NEW_ACE_WRITEA_DATA (NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA) + /* 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. */ @@ -217,7 +227,7 @@ acl_ace_nontrivial (int count, ace_t *entries) index2 = 0; else if (ace->a_flags == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP)) index2 = 2; - else if (ace->a_flags == ACE_EVERYONE) + else if (ace->a_flags == NEW_ACE_EVERYONE) index2 = 4; else return 1; @@ -244,16 +254,6 @@ acl_ace_nontrivial (int count, ace_t *entries) | NEW_ACE_WRITE_ATTRIBUTES | NEW_ACE_WRITE_ACL | NEW_ACE_WRITE_OWNER); - if ((NEW_ACE_WRITE_NAMED_ATTRS - | NEW_ACE_WRITE_ATTRIBUTES - | NEW_ACE_WRITE_ACL - | NEW_ACE_WRITE_OWNER) - & ~ access_masks[4]) - return 1; - access_masks[4] &= ~(NEW_ACE_WRITE_NAMED_ATTRS - | NEW_ACE_WRITE_ATTRIBUTES - | NEW_ACE_WRITE_ACL - | NEW_ACE_WRITE_OWNER); if ((NEW_ACE_READ_NAMED_ATTRS | NEW_ACE_READ_ATTRIBUTES | NEW_ACE_READ_ACL @@ -266,21 +266,60 @@ acl_ace_nontrivial (int count, ace_t *entries) | NEW_ACE_SYNCHRONIZE); /* Check the allowed or denied bits. */ - if ((access_masks[0] | access_masks[1]) - != (NEW_ACE_READ_DATA - | NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA - | NEW_ACE_EXECUTE)) - return 1; - if ((access_masks[2] | access_masks[3]) - != (NEW_ACE_READ_DATA - | NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA - | NEW_ACE_EXECUTE)) - return 1; - if ((access_masks[4] | access_masks[5]) - != (NEW_ACE_READ_DATA - | NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA - | NEW_ACE_EXECUTE)) - return 1; + switch ((access_masks[0] | access_masks[1]) + & ~(NEW_ACE_READ_NAMED_ATTRS + | NEW_ACE_READ_ATTRIBUTES + | NEW_ACE_READ_ACL + | NEW_ACE_SYNCHRONIZE)) + { + case 0: + case NEW_ACE_READ_DATA: + case NEW_ACE_WRITEA_DATA: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: + case NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + break; + default: + return 1; + } + switch ((access_masks[2] | access_masks[3]) + & ~(NEW_ACE_READ_NAMED_ATTRS + | NEW_ACE_READ_ATTRIBUTES + | NEW_ACE_READ_ACL + | NEW_ACE_SYNCHRONIZE)) + { + case 0: + case NEW_ACE_READ_DATA: + case NEW_ACE_WRITEA_DATA: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: + case NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + break; + default: + return 1; + } + switch ((access_masks[4] | access_masks[5]) + & ~(NEW_ACE_WRITE_NAMED_ATTRS + | NEW_ACE_WRITE_ATTRIBUTES + | NEW_ACE_WRITE_ACL + | NEW_ACE_WRITE_OWNER)) + { + case 0: + case NEW_ACE_READ_DATA: + case NEW_ACE_WRITEA_DATA: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: + case NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: + break; + default: + return 1; + } /* Check that the NEW_ACE_WRITE_DATA and NEW_ACE_APPEND_DATA bits are either both allowed or both denied. */ @@ -439,7 +478,8 @@ acl_nontrivial (int count, struct acl *entries) /* Return 1 if NAME has a nontrivial access control list, 0 if NAME only has no or a base access control list, and -1 (setting errno) - on error. SB must be set to the stat buffer of FILE. */ + on error. SB must be set to the stat buffer of NAME, obtained + through stat() or lstat(). */ int file_has_acl (char const *name, struct stat const *sb) @@ -450,28 +490,20 @@ file_has_acl (char const *name, struct stat const *sb) # 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 || HAVE_ACL_EXTENDED_FILE_NOFOLLOW) /* Linux */ + if (HAVE_ACL_EXTENDED_FILE) /* Linux */ { -# if HAVE_ACL_EXTENDED_FILE_NOFOLLOW - /* acl_extended_file_nofollow() uses lgetxattr() in order to prevent - unnecessary mounts, but it returns the same result as we already - know that NAME is not a symbolic link at this point (modulo the - TOCTTOU race condition). */ - ret = acl_extended_file_nofollow (name); -# else /* 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); -# endif } - 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 @@ -522,10 +554,10 @@ file_has_acl (char const *name, struct stat const *sb) # 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 @@ -539,75 +571,135 @@ 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 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) + { + /* 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; + } + break; + } + if (count < 0) + { + if (errno == ENOSYS || errno == ENOTSUP) + ; + else { - if (errno == ENOSYS || errno == ENOTSUP) - break; - else - return -1; + int saved_errno = errno; + free (malloced); + errno = saved_errno; + return -1; } - - if (count == 0) - break; - + } + 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 (;;) { - 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. @@ -617,28 +709,18 @@ file_has_acl (char const *name, struct stat const *sb) 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; } - if (acl (name, ACE_GETACL, count, entries) == count) + + if (acl_ace_nontrivial (count, entries)) { - if (acl_ace_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); } # endif @@ -647,38 +729,36 @@ file_has_acl (char const *name, struct stat const *sb) # 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; @@ -687,47 +767,43 @@ file_has_acl (char const *name, struct stat const *sb) 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 @@ -804,39 +880,36 @@ file_has_acl (char const *name, struct stat const *sb) # 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 }