diff --git a/lib/copy-acl.c b/lib/copy-acl.c *** a/lib/copy-acl.c --- b/lib/copy-acl.c *************** *** 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