Hi Paul, > The patch you sent looks like it'll fix that particular bug, but while > looking into this I discovered so many bugs in this area, mostly > hard-to-test race conditions and overflows, that I thought it better > to rewrite the affected code than to try to fix each bug one at a time. > I didn't write this up very well, and my first cut at doing this could > stand some improvements of its own. Here's a revised patch that tries > to do a better job at all this. Thanks for this rework. I apologize for the time it took me to review it: I had a week-long private outage. > + - There's a race condition in this kind of code: > + > + n = acl (f, GETACLCNT, 0, NULL); > + [ allocate an array A of size N ] > + if (acl (f, GETACL, n, a) == n) > + return ok; > + > + The ACL can grow in size between the first and second calls to > + 'acl', which means that the second 'acl' returns a truncated > + ACL but thinks that it has the whole thing. To avoid this, the > + following pattern should be used instead: > + > + n = acl (f, GETACLCNT, 0, NULL); > + [ allocate an array A of size N + 1 ] > + n1 = acl (f, GETACL, n + 1, a); > + if (0 < n1 && n1 <= n) > + return ok; Have you tested that the acl() call works like this? The manual page http://docs.oracle.com/cd/E23824_01/html/821-1463/acl-2.html says that rather than returning a truncated result, the acl() call will fail with error code ENOSPC. And on HP-UX, the documentation http://h20000.www2.hp.com/bc/docs/support/SupportManual/c02261503/c02261503.pdf says that the argument nentries is "never greater than" NACLENTRIES. Therefore, passing NACLENTRIES + 1, like your patch does, is invalid. > + - There were several instances of this pattern: > + > + for (;;) { > + n = acl (f, GETACLCNT, 0, NULL); > + [ allocate an array A of size N ] > + if (acl (f, GETACL, n, a) == n) > + break; > + } > + > + This loop might never terminate if some other process is constantly > + manipulating the file's ACL. The loop should be rewritten to > + terminate. The loop terminates with probability 1, because the probability that the competing process is able to change the size of the file's ACL right in time to provoke an endless loop is 0. > + - The acl (... GETACLNT ...) call is merely an optimization; its value > + is merely a hint as to how big to make the array. A better > + optimization is to avoid the acl (... GETACLNT ...) call entirely, > + and just guess a reasonably-big size, growing the size and trying > + again if it's not large enough. This guarantees termination, and > + saves a system call. Good point. I agree with this one. > + - With this approach, for ports like HP-UX that have an upper bound > + for the ACL length, there's no longer any need to loop reading the > + ACL. Just read it once and you're done reading it. Yes. > + - For ports like Solaris that do need to loop (because the ACL length > + is not known a priori), it's faster to allocate a reasonably-sized > + buffer on the stack and use that, and to allocate something on the > + heap only if the ACL is unusually large. This avoids malloc in the > + usual case. Yes. However, beware against premature optimizations: So far, the code does not handles ACLs on directories, only on files. The limit of "trivial" ACLs on files and on directories may be different. > + - The code that calculated sizes of these arrays did not check for > + overflow in size calculations; it should. > + > + - There are some memory leaks. For example, in qcopy_acl, if acl > + (src_name, GETACLCNT, 0, NULL) < 0 && errno == EPERM, then the > + storage for ace_entries leaks. Similarly, if acl (src_name, > + ACE_GETACL, ace_count, ace_entries) returns -1, the storage for > + ace_entries leaks. Oops, indeed! > + - In qset_acl, there's sometimes no need to read the entire ACL > + to determine the convention; this can save system calls when > + looking at very large ACLs. You assume that when you pass less room to the acl() call than needed, it will fill the buffer. But this is not clear from the documentation at (cf. ENOSPC). Even if that's how it behaves in a specific version of Solaris, it's a risky assumption. > + Rather than fix these bugs one at a time I thought it more efficient > + to rewrite the affected code. Yup. But the code in tests/test-sameacls.c should be kept in sync if we decide to not use GETACLCNT any more. Also, one change per commit -- otherwise it gets too complicated. I hope you agree if I commit the parts on which we agree. =============================================================================== 2012-02-19 Bruno Haible acl: Update doc references. * doc/acl-resources.txt: Update links to Solaris documentation. --- doc/acl-resources.txt.orig Sun Feb 19 16:02:46 2012 +++ doc/acl-resources.txt Sun Feb 19 16:01:54 2012 @@ -72,10 +72,10 @@ http://www.softpanorama.org/Solaris/ACL/index.shtml http://www.cs.duke.edu/csl/faqs/solaris-acls.php Manual pages: - http://docs.sun.com/app/docs/doc/816-5167/acl-2?l=en&a=view - http://docs.sun.com/app/docs/doc/816-5165/getfacl-1?l=en&a=view - http://docs.sun.com/app/docs/doc/816-5165/setfacl-1?l=en&a=view - http://docs.sun.com/app/docs/doc/816-5167/pathconf-2?l=en&a=view + http://docs.oracle.com/cd/E23823_01/html/816-5167/acl-2.html + http://docs.oracle.com/cd/E23823_01/html/816-5165/getfacl-1.html + http://docs.oracle.com/cd/E23823_01/html/816-5165/setfacl-1.html + http://docs.oracle.com/cd/E23823_01/html/816-5167/pathconf-2.html Includes: Library: =============================================================================== Already before any modification of the code, I'm seeing a test failure on Solaris 11: test-copy-acl: preserving permissions for 'tmpfile1': Operation not applicable FAIL: test-copy-acl.sh The reason is that the function acl_ace_nontrivial returns 1 when it should better return 0. The original code for acl_ace_nontrivial was made to match the code in qset_acl, but this code is apparently more specialized than the various situations that occur on the file systems. This fixes it (without introducing regressions on Solaris 10). =============================================================================== 2012-02-19 Bruno Haible acl: Fix copy-acl test failure on Solaris 11 2011-11. * lib/file-has-acl.c (NEW_ACE_WRITEA_DATA): New macro. (acl_ace_nontrivial): Relax the restrictions on access_masks[] so that this function returns 0 in some more cases. --- lib/file-has-acl.c.orig Sun Feb 19 21:56:14 2012 +++ lib/file-has-acl.c Sun Feb 19 21:27:12 2012 @@ -154,6 +154,9 @@ # 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. */ @@ -250,16 +253,6 @@ | 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 @@ -272,21 +265,60 @@ | 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. */ =============================================================================== After eliminating this test failure, let me add first the minimal workaround to the problem reported in . =============================================================================== 2012-02-19 Bruno Haible acl: Fix endless loop on Solaris with vxfs. * lib/file-has-acl.c (file_has_acl) [Solaris]: Treat a failing acl()/facl() call for ACE_GETACL like a failing call for ACE_GETACLCNT. * lib/set-mode-acl.c (qset_acl) [Solaris]: Likewise. * lib/copy-acl.c (qcopy_acl)[Solaris]: Likewise. * tests/test-sameacls.c (main)[Solaris]: Likewise. Reported by Bill Jones in , via Paul Eggert. --- lib/copy-acl.c.orig Sun Feb 19 22:04:57 2012 +++ lib/copy-acl.c Sun Feb 19 22:00:03 2012 @@ -235,10 +235,22 @@ return -2; } - if ((source_desc != -1 - ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) - : acl (src_name, ACE_GETACL, ace_count, ace_entries)) - == ace_count) + ret = (source_desc != -1 + ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) + : acl (src_name, ACE_GETACL, ace_count, ace_entries)); + if (ret < 0) + { + free (ace_entries); + if (errno == ENOSYS || errno == EINVAL) + { + ace_count = 0; + ace_entries = NULL; + break; + } + else + return -2; + } + if (ret == ace_count) break; /* Huh? The number of ACL entries changed since the last call. Repeat. */ --- lib/file-has-acl.c.orig Sun Feb 19 22:04:57 2012 +++ lib/file-has-acl.c Sun Feb 19 22:00:03 2012 @@ -626,6 +626,8 @@ for (;;) { + int ret; + count = acl (name, ACE_GETACLCNT, 0, NULL); if (count < 0) @@ -656,7 +658,16 @@ errno = ENOMEM; return -1; } - if (acl (name, ACE_GETACL, count, entries) == count) + ret = acl (name, ACE_GETACL, count, entries); + if (ret < 0) + { + free (entries); + if (errno == ENOSYS || errno == EINVAL) + break; + else + return -1; + } + if (ret == count) { if (acl_ace_nontrivial (count, entries)) { --- lib/set-mode-acl.c.orig Sun Feb 19 22:04:57 2012 +++ lib/set-mode-acl.c Sun Feb 19 22:00:03 2012 @@ -219,6 +219,8 @@ for (;;) { + int ret; + if (desc != -1) count = facl (desc, ACE_GETACLCNT, 0, NULL); else @@ -234,10 +236,16 @@ errno = ENOMEM; return -1; } - if ((desc != -1 - ? facl (desc, ACE_GETACL, count, entries) - : acl (name, ACE_GETACL, count, entries)) - == count) + ret = (desc != -1 + ? facl (desc, ACE_GETACL, count, entries) + : acl (name, ACE_GETACL, count, entries)); + if (ret < 0) + { + free (entries); + convention = -1; + break; + } + if (ret == count) { int i; --- tests/test-sameacls.c.orig Sun Feb 19 22:04:57 2012 +++ tests/test-sameacls.c Sun Feb 19 22:00:03 2012 @@ -310,58 +310,66 @@ fflush (stderr); abort (); } - if (count1 != count2) - { - fprintf (stderr, "files %s and %s have different number of ACE-ACLs: %d and %d\n", - file1, file2, count1, count2); - return 1; - } - else if (count1 > 0) - { - ace_t *entries1 = XNMALLOC (count1, ace_t); - ace_t *entries2 = XNMALLOC (count2, ace_t); - int i; + { + ace_t *entries1 = XNMALLOC (count1, ace_t); + ace_t *entries2 = XNMALLOC (count2, ace_t); + int ret; + int i; - if (acl (file1, ACE_GETACL, count1, entries1) < count1) - { - fprintf (stderr, "error retrieving the ACE-ACLs of file %s\n", file1); - fflush (stderr); - abort (); - } - if (acl (file2, ACE_GETACL, count2, entries2) < count1) - { - fprintf (stderr, "error retrieving the ACE-ACLs of file %s\n", file2); - fflush (stderr); - abort (); - } - for (i = 0; i < count1; i++) - { - if (entries1[i].a_type != entries2[i].a_type) - { - fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different types %d and %d\n", - file1, file2, i, entries1[i].a_type, entries2[i].a_type); - return 1; - } - if (entries1[i].a_who != entries2[i].a_who) - { - fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different ids %d and %d\n", - file1, file2, i, (int)entries1[i].a_who, (int)entries2[i].a_who); - return 1; - } - if (entries1[i].a_access_mask != entries2[i].a_access_mask) - { - fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different access masks %03o and %03o\n", - file1, file2, i, (unsigned int) entries1[i].a_access_mask, (unsigned int) entries2[i].a_access_mask); - return 1; - } - if (entries1[i].a_flags != entries2[i].a_flags) - { - fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different flags 0x%x and 0x%x\n", - file1, file2, i, (unsigned int) entries1[i].a_flags, (unsigned int) entries2[i].a_flags); - return 1; - } - } - } + ret = acl (file1, ACE_GETACL, count1, entries1); + if (ret < 0 && errno == EINVAL) + count1 = 0; + else if (ret < count1) + { + fprintf (stderr, "error retrieving the ACE-ACLs of file %s\n", file1); + fflush (stderr); + abort (); + } + ret = acl (file2, ACE_GETACL, count2, entries2); + if (ret < 0 && errno == EINVAL) + count2 = 0; + else if (ret < count2) + { + fprintf (stderr, "error retrieving the ACE-ACLs of file %s\n", file2); + fflush (stderr); + abort (); + } + + if (count1 != count2) + { + fprintf (stderr, "files %s and %s have different number of ACE-ACLs: %d and %d\n", + file1, file2, count1, count2); + return 1; + } + + for (i = 0; i < count1; i++) + { + if (entries1[i].a_type != entries2[i].a_type) + { + fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different types %d and %d\n", + file1, file2, i, entries1[i].a_type, entries2[i].a_type); + return 1; + } + if (entries1[i].a_who != entries2[i].a_who) + { + fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different ids %d and %d\n", + file1, file2, i, (int)entries1[i].a_who, (int)entries2[i].a_who); + return 1; + } + if (entries1[i].a_access_mask != entries2[i].a_access_mask) + { + fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different access masks %03o and %03o\n", + file1, file2, i, (unsigned int) entries1[i].a_access_mask, (unsigned int) entries2[i].a_access_mask); + return 1; + } + if (entries1[i].a_flags != entries2[i].a_flags) + { + fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different flags 0x%x and 0x%x\n", + file1, file2, i, (unsigned int) entries1[i].a_flags, (unsigned int) entries2[i].a_flags); + return 1; + } + } + } # endif #elif HAVE_GETACL /* HP-UX */ int count1; =============================================================================== Then comes the main part of your rewrite. First the part for Solaris: =============================================================================== 2012-02-19 Paul Eggert Bruno Haible acl: Don't use GETACLCNT and similar ops, since they are unreliable. - There were several instances of this pattern: for (;;) { n = acl (f, GETACLCNT, 0, NULL); [ allocate an array A of size N ] if (acl (f, GETACL, n, a) == n) break; } This loop might never terminate if some other process is constantly manipulating the file's ACL. The loop should be rewritten to terminate. - The acl (... GETACLNT ...) call is merely an optimization; its value is merely a hint as to how big to make the array. A better optimization is to avoid the acl (... GETACLNT ...) call entirely, and just guess a reasonably-big size, growing the size and trying again if it's not large enough. This guarantees termination, and saves a system call. * lib/acl-internal.h: Include . (MIN, SIZE_MAX): New macros. * lib/file-has-acl.c (file_has_acl) [Solaris]: Read the entries into a stack-allocated buffer, and use malloc if it does not fit. Don't use GETACLCNT. * lib/set-mode-acl.c (qset_acl) [Solaris]: Likewise. * lib/copy-acl.c (qcopy_acl) [Solaris]: Likewise. *** lib/acl-internal.h.orig Mon Feb 20 01:10:13 2012 --- lib/acl-internal.h Sun Feb 19 22:59:06 2012 *************** *** 55,60 **** --- 55,69 ---- # define ENOTSUP (-1) #endif + #include + #ifndef MIN + # define MIN(a,b) ((a) < (b) ? (a) : (b)) + #endif + + #ifndef SIZE_MAX + # define SIZE_MAX ((size_t) -1) + #endif + #ifndef HAVE_FCHMOD # define HAVE_FCHMOD false # define fchmod(fd, mode) (-1) *** lib/copy-acl.c.orig Mon Feb 20 01:10:13 2012 --- lib/copy-acl.c Mon Feb 20 01:04:36 2012 *************** *** 181,191 **** of Unixware. The acl() call returns the access and default ACL both at once. */ # ifdef ACE_GETACL int ace_count; - ace_t *ace_entries; # endif int count; - aclent_t *entries; int did_chmod; int saved_errno; int ret; --- 181,207 ---- of Unixware. The acl() call returns the access and default ACL both at once. */ # ifdef ACE_GETACL + enum + { + ace_alloc_init = 4000 / sizeof (ace_t), /* >= 3 */ + ace_alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t)) + }; + ace_t ace_buf[ace_alloc_init]; + size_t ace_alloc = ace_alloc_init; + ace_t *ace_entries = ace_buf; + ace_t *ace_malloced = NULL; int ace_count; # endif + 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; int did_chmod; int saved_errno; int ret; *************** *** 204,308 **** that the kernel will translate the ACL from one form to the other. (See in the description of ENOTSUP.) */ for (;;) { ace_count = (source_desc != -1 ! ? facl (source_desc, ACE_GETACLCNT, 0, NULL) ! : acl (src_name, ACE_GETACLCNT, 0, NULL)); ! ! if (ace_count < 0) ! { ! if (errno == ENOSYS || errno == EINVAL) { ! ace_count = 0; ! ace_entries = NULL; ! break; } ! else ! return -2; } ! ! if (ace_count == 0) { ace_entries = NULL; - break; } ! ! ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t)); ! if (ace_entries == NULL) { ! errno = ENOMEM; return -2; } - - ret = (source_desc != -1 - ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) - : acl (src_name, ACE_GETACL, ace_count, ace_entries)); - if (ret < 0) - { - free (ace_entries); - if (errno == ENOSYS || errno == EINVAL) - { - ace_count = 0; - ace_entries = NULL; - break; - } - else - return -2; - } - if (ret == ace_count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ } # endif for (;;) { count = (source_desc != -1 ! ? facl (source_desc, GETACLCNT, 0, NULL) ! : acl (src_name, GETACLCNT, 0, NULL)); ! ! if (count < 0) ! { ! if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) { ! count = 0; ! entries = NULL; ! break; } ! else ! return -2; } ! ! if (count == 0) { entries = NULL; - break; } ! ! entries = (aclent_t *) malloc (count * sizeof (aclent_t)); ! if (entries == NULL) { ! errno = ENOMEM; return -2; } - - if ((source_desc != -1 - ? facl (source_desc, GETACL, count, entries) - : acl (src_name, GETACL, count, entries)) - == count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ } /* Is there an ACL of either kind? */ # ifdef ACE_GETACL if (ace_count == 0) # endif if (count == 0) ! return qset_acl (dst_name, dest_desc, mode); did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ saved_errno = 0; /* the first non-ignorable error code */ --- 220,339 ---- that the kernel will translate the ACL from one form to the other. (See in the description of ENOTSUP.) */ + + /* Initially, try to read the entries into a stack-allocated buffer. + Use malloc if it does not fit. */ for (;;) { ace_count = (source_desc != -1 ! ? facl (source_desc, ACE_GETACL, ace_alloc, ace_entries) ! : acl (src_name, ACE_GETACL, ace_alloc, ace_entries)); ! if (ace_count < 0 && errno == ENOSPC) ! { ! /* Increase the size of the buffer. */ ! free (ace_malloced); ! if (ace_alloc > ace_alloc_max / 2) { ! errno = ENOMEM; ! return -2; } ! ace_alloc = 2 * ace_alloc; /* <= ace_alloc_max */ ! ace_entries = ace_malloced = ! (ace_t *) malloc (ace_alloc * sizeof (ace_t)); ! if (ace_entries == NULL) ! { ! errno = ENOMEM; ! return -2; ! } ! continue; } ! break; ! } ! if (ace_count < 0) ! { ! if (errno == ENOSYS || errno == EINVAL) { + ace_count = 0; ace_entries = NULL; } ! else { ! int saved_errno = errno; ! free (ace_malloced); ! errno = saved_errno; return -2; } } + else if (ace_count == 0) + ace_entries = NULL; # endif + /* Initially, try to read the entries into a stack-allocated buffer. + Use malloc if it does not fit. */ for (;;) { count = (source_desc != -1 ! ? facl (source_desc, GETACL, alloc, entries) ! : acl (src_name, GETACL, alloc, entries)); ! if (count < 0 && errno == ENOSPC) ! { ! /* Increase the size of the buffer. */ ! free (malloced); ! if (alloc > alloc_max / 2) { ! # ifdef ACE_GETACL ! free (ace_malloced); ! # endif ! errno = ENOMEM; ! return -2; } ! alloc = 2 * alloc; /* <= alloc_max */ ! entries = malloced = (aclent_t *) malloc (alloc * sizeof (aclent_t)); ! if (entries == NULL) ! { ! # ifdef ACE_GETACL ! free (ace_malloced); ! # endif ! errno = ENOMEM; ! return -2; ! } ! continue; } ! break; ! } ! if (count < 0) ! { ! if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) { + count = 0; entries = NULL; } ! else { ! int saved_errno = errno; ! # ifdef ACE_GETACL ! free (ace_malloced); ! # endif ! free (malloced); ! errno = saved_errno; return -2; } } + else if (count == 0) + entries = NULL; /* Is there an ACL of either kind? */ # ifdef ACE_GETACL if (ace_count == 0) # endif if (count == 0) ! { ! # ifdef ACE_GETACL ! free (ace_malloced); ! # endif ! free (malloced); ! return qset_acl (dst_name, dest_desc, mode); ! } did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ saved_errno = 0; /* the first non-ignorable error code */ *************** *** 336,342 **** else did_chmod = 1; } ! free (entries); # ifdef ACE_GETACL if (ace_count > 0) --- 367,373 ---- else did_chmod = 1; } ! free (malloced); # ifdef ACE_GETACL if (ace_count > 0) *************** *** 352,358 **** saved_errno = 0; } } ! free (ace_entries); # endif if (MODE_INSIDE_ACL --- 383,389 ---- saved_errno = 0; } } ! free (ace_malloced); # endif if (MODE_INSIDE_ACL *** lib/file-has-acl.c.orig Mon Feb 20 01:10:13 2012 --- lib/file-has-acl.c Mon Feb 20 00:14:37 2012 *************** *** 556,562 **** return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1; return ret; ! # elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */ # if defined ACL_NO_TRIVIAL --- 556,562 ---- return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1; return ret; ! # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ # if defined ACL_NO_TRIVIAL *************** *** 570,646 **** /* 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; 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 (;;) { ! int ret; ! ! count = acl (name, ACE_GETACLCNT, 0, NULL); ! ! if (count < 0) { ! if (errno == ENOSYS || errno == EINVAL) ! break; ! else ! return -1; } ! ! if (count == 0) ! break; ! /* 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. --- 570,704 ---- /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions of Unixware. The acl() call returns the access and default ACL both at once. */ { ! /* 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, 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 ! { ! 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) { ! free (malloced); ! return 1; } ! ! if (acl_nontrivial (count, entries)) { ! free (malloced); ! return 1; } } + 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). */ { ! /* 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_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 ! { ! int saved_errno = errno; ! free (malloced); ! errno = saved_errno; ! return -1; ! } ! } ! 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. *************** *** 650,686 **** 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; - } - ret = acl (name, ACE_GETACL, count, entries); - if (ret < 0) { ! free (entries); ! if (errno == ENOSYS || errno == EINVAL) ! break; ! else ! 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); } } # endif --- 708,725 ---- NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with NEW_ACE_ACCESS_DENIED_ACE_TYPE. */ if (count > 6) { ! free (malloced); ! return 1; } ! ! if (acl_ace_nontrivial (count, entries)) { ! free (malloced); ! return 1; } } + free (malloced); } # endif *** lib/set-mode-acl.c.orig Mon Feb 20 01:10:13 2012 --- lib/set-mode-acl.c Mon Feb 20 00:14:51 2012 *************** *** 197,203 **** return chmod_or_fchmod (name, desc, mode); # endif ! # elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */ int done_setacl = 0; --- 197,203 ---- return chmod_or_fchmod (name, desc, mode); # endif ! # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ int done_setacl = 0; *************** *** 214,268 **** int convention; { int count; - ace_t *entries; for (;;) { ! int ret; ! ! if (desc != -1) ! count = facl (desc, ACE_GETACLCNT, 0, NULL); ! else ! count = acl (name, ACE_GETACLCNT, 0, NULL); ! if (count <= 0) ! { ! convention = -1; ! break; ! } ! entries = (ace_t *) malloc (count * sizeof (ace_t)); ! if (entries == NULL) ! { ! errno = ENOMEM; ! return -1; ! } ! ret = (desc != -1 ! ? facl (desc, ACE_GETACL, count, entries) ! : acl (name, ACE_GETACL, count, entries)); ! if (ret < 0) { ! free (entries); ! convention = -1; ! break; } ! if (ret == count) ! { ! int i; ! convention = 0; ! for (i = 0; i < count; i++) ! if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) ! { ! convention = 1; ! break; ! } ! free (entries); ! break; ! } ! /* Huh? The number of ACL entries changed since the last call. ! Repeat. */ ! free (entries); } } if (convention >= 0) --- 214,273 ---- int convention; { + /* 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 = (desc != -1 ! ? facl (desc, ACE_GETACL, alloc, entries) ! : 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) ! convention = -1; ! else ! { ! int i; ! ! convention = 0; ! for (i = 0; i < count; i++) ! if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) ! { ! convention = 1; ! break; ! } } + free (malloced); } if (convention >= 0) =============================================================================== You would think that commit is fine, right? But it introduces a test failure on Solaris 10 over NFS: files tmpfile0 and tmpfile2: different ACE-ACL entry #0: different access masks 1400207 and 5400607 FAIL: test-copy-acl-2.sh What's happening, is that the test-copy-acl copies wrong ACL entries. In the debugger, I can see the following values. Old code (with ACE_GETACLCNT): (gdb) print ace_entries[0] $3 = {a_who = 1088, a_access_mask = 1442183, a_flags = 4096, a_type = 0} (gdb) print ace_entries[1] $4 = {a_who = 1088, a_access_mask = 32, a_flags = 4096, a_type = 1} (gdb) print ace_entries[2] $5 = {a_who = 1, a_access_mask = 262400, a_flags = 0, a_type = 1} (gdb) print ace_entries[3] $6 = {a_who = 1, a_access_mask = 1179808, a_flags = 0, a_type = 0} (gdb) print ace_entries[4] $7 = {a_who = 1, a_access_mask = 262407, a_flags = 0, a_type = 1} (gdb) print ace_entries[5] $8 = {a_who = 1088, a_access_mask = 262400, a_flags = 8256, a_type = 1} (gdb) print ace_entries[6] $9 = {a_who = 1088, a_access_mask = 1179776, a_flags = 8256, a_type = 0} (gdb) print ace_entries[7] $10 = {a_who = 1088, a_access_mask = 262439, a_flags = 8256, a_type = 1} (gdb) print ace_entries[8] $11 = {a_who = 0, a_access_mask = 1179776, a_flags = 16384, a_type = 0} (gdb) print ace_entries[9] $12 = {a_who = 0, a_access_mask = 262439, a_flags = 16384, a_type = 1} New code (without use of ACE_GETACLCNT): (gdb) print ace_entries[0] $8 = {a_who = 1088, a_access_mask = 393351, a_flags = 4096, a_type = 0} (gdb) print ace_entries[1] $9 = {a_who = 1088, a_access_mask = 32, a_flags = 4096, a_type = 1} (gdb) print ace_entries[2] $10 = {a_who = 1, a_access_mask = 262144, a_flags = 0, a_type = 1} (gdb) print ace_entries[3] $11 = {a_who = 1, a_access_mask = 131232, a_flags = 0, a_type = 0} (gdb) print ace_entries[4] $12 = {a_who = 1, a_access_mask = 262151, a_flags = 0, a_type = 1} (gdb) print ace_entries[5] $13 = {a_who = 1088, a_access_mask = 262144, a_flags = 8256, a_type = 1} (gdb) print ace_entries[6] $14 = {a_who = 1088, a_access_mask = 131200, a_flags = 8256, a_type = 0} (gdb) print ace_entries[7] $15 = {a_who = 1088, a_access_mask = 262183, a_flags = 8256, a_type = 1} (gdb) print ace_entries[8] $16 = {a_who = 0, a_access_mask = 131200, a_flags = 16384, a_type = 0} (gdb) print ace_entries[9] $17 = {a_who = 0, a_access_mask = 262183, a_flags = 16384, a_type = 1} As you can see, most of the a_access_mask values are broken. I haven't yet found out the real cause, but I know that the modification of copy-acl.c triggers it. Therefore I'm not committing this part of the patch today. I attach it, in case you want to experiment further with it. Then the part for HP-UX and NSK: =============================================================================== 2012-02-19 Paul Eggert Bruno Haible acl: Don't use ACL_CNT and similar ops, since they are unreliable. * lib/file-has-acl.c (file_has_acl) [HP-UX, NonStop Kernel]: Read the entries into a stack-allocated buffer directly. * lib/copy-acl.c (qcopy_acl) [HP-UX, NonStop Kernel]: Likewise. *** lib/copy-acl.c.orig Mon Feb 20 01:51:49 2012 --- lib/copy-acl.c Mon Feb 20 01:29:02 2012 *************** *** 408,484 **** #elif USE_ACL && HAVE_GETACL /* HP-UX */ - int count; struct acl_entry entries[NACLENTRIES]; # if HAVE_ACLV_H - int aclv_count; struct acl aclv_entries[NACLVENTRIES]; # endif int did_chmod; int saved_errno; int ret; ! for (;;) ! { ! count = (source_desc != -1 ! ? fgetacl (source_desc, 0, NULL) ! : getacl (src_name, 0, NULL)); ! ! if (count < 0) ! { ! if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) ! { ! count = 0; ! break; ! } ! else ! return -2; ! } ! ! if (count == 0) ! break; if (count > NACLENTRIES) /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); - - if ((source_desc != -1 - ? fgetacl (source_desc, count, entries) - : getacl (src_name, count, entries)) - == count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ } # if HAVE_ACLV_H ! for (;;) ! { ! aclv_count = acl ((char *) src_name, ACL_CNT, NACLVENTRIES, aclv_entries); ! ! if (aclv_count < 0) ! { ! if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) ! { ! count = 0; ! break; ! } ! else ! return -2; ! } ! ! if (aclv_count == 0) ! break; if (aclv_count > NACLVENTRIES) /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); - - if (acl ((char *) src_name, ACL_GET, aclv_count, aclv_entries) - == aclv_count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ } # endif --- 408,456 ---- #elif USE_ACL && HAVE_GETACL /* HP-UX */ struct acl_entry entries[NACLENTRIES]; + int count; # if HAVE_ACLV_H struct acl aclv_entries[NACLVENTRIES]; + int aclv_count; # endif int did_chmod; int saved_errno; int ret; ! count = (source_desc != -1 ! ? fgetacl (source_desc, NACLENTRIES, entries) ! : getacl (src_name, NACLENTRIES, entries)); + if (count < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) + count = 0; + else + return -2; + } + else if (count > 0) + { if (count > NACLENTRIES) /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); } # if HAVE_ACLV_H ! aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries); + if (aclv_count < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + count = 0; + else + return -2; + } + else if (aclv_count > 0) + { if (aclv_count > NACLVENTRIES) /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); } # endif *************** *** 589,624 **** #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ - int count; struct acl entries[NACLENTRIES]; int ret; ! for (;;) ! { ! count = acl ((char *) src_name, ACL_CNT, NACLENTRIES, NULL); ! ! if (count < 0) ! { ! if (0) ! { ! count = 0; ! break; ! } ! else ! return -2; ! } ! ! if (count == 0) ! break; if (count > NACLENTRIES) /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); - - if (acl ((char *) src_name, ACL_GET, count, entries) == count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ } if (count == 0) --- 561,584 ---- #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ struct acl entries[NACLENTRIES]; + int count; int ret; ! count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries); + if (count < 0) + { + if (0) + count = 0; + else + return -2; + } + else if (count > 0) + { if (count > NACLENTRIES) /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); } if (count == 0) *** lib/file-has-acl.c.orig Mon Feb 20 01:51:49 2012 --- lib/file-has-acl.c Mon Feb 20 01:49:41 2012 *************** *** 728,765 **** # 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; ! ! 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; --- 728,763 ---- # elif HAVE_GETACL /* HP-UX */ ! { ! struct acl_entry entries[NACLENTRIES]; ! int count; ! ! count = getacl (name, NACLENTRIES, entries); ! ! 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; { struct stat statbuf; *************** *** 768,814 **** 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; ! } ! ! if (count == 0) ! return 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 (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 --- 766,808 ---- return acl_nontrivial (count, entries, &statbuf); } ! } ! } # if HAVE_ACLV_H /* HP-UX >= 11.11 */ ! { ! struct acl entries[NACLVENTRIES]; ! int count; ! ! count = acl ((char *) name, ACL_GET, 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) ! ; ! 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; return aclv_nontrivial (count, entries); ! } ! } # endif *************** *** 885,923 **** # 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; ! } ! ! if (count == 0) ! return 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 (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 } --- 879,914 ---- # elif HAVE_ACLSORT /* NonStop Kernel */ ! { ! struct acl entries[NACLENTRIES]; ! int count; ! ! count = acl ((char *) name, ACL_GET, NACLENTRIES, entries); ! ! 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; return acl_nontrivial (count, entries); ! } ! } # endif } =============================================================================== Bruno