int qset_acl (char const *name, int desc, mode_t mode) { #if USE_ACL # if HAVE_ACL_GET_FILE /* POSIX 1003.1e draft 17 (abandoned) specific version. */ /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */ # if !HAVE_ACL_TYPE_EXTENDED /* Linux, FreeBSD, IRIX, Tru64 */ /* We must also have acl_from_text and acl_delete_def_file. (acl_delete_def_file could be emulated with acl_init followed by acl_set_file, but acl_set_file with an empty acl is unspecified.) */ # ifndef HAVE_ACL_FROM_TEXT # error Must have acl_from_text (see POSIX 1003.1e draft 17). # endif # ifndef HAVE_ACL_DELETE_DEF_FILE # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). # endif acl_t acl; int ret; if (HAVE_ACL_FROM_MODE) /* Linux */ { acl = acl_from_mode (mode); if (!acl) return -1; } else /* FreeBSD, IRIX, Tru64 */ { /* If we were to create the ACL using the functions acl_init(), acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(), acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we would need to create a qualifier. I don't know how to do this. So create it using acl_from_text(). */ # if HAVE_ACL_FREE_TEXT /* Tru64 */ char acl_text[] = "u::---,g::---,o::---,"; # else /* FreeBSD, IRIX */ char acl_text[] = "u::---,g::---,o::---"; # endif if (mode & S_IRUSR) acl_text[ 3] = 'r'; if (mode & S_IWUSR) acl_text[ 4] = 'w'; if (mode & S_IXUSR) acl_text[ 5] = 'x'; if (mode & S_IRGRP) acl_text[10] = 'r'; if (mode & S_IWGRP) acl_text[11] = 'w'; if (mode & S_IXGRP) acl_text[12] = 'x'; if (mode & S_IROTH) acl_text[17] = 'r'; if (mode & S_IWOTH) acl_text[18] = 'w'; if (mode & S_IXOTH) acl_text[19] = 'x'; acl = acl_from_text (acl_text); if (!acl) return -1; } if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, acl); else ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); if (ret != 0) { int saved_errno = errno; acl_free (acl); if (ACL_NOT_WELL_SUPPORTED (errno)) return chmod_or_fchmod (name, desc, mode); else { errno = saved_errno; return -1; } } else acl_free (acl); if (S_ISDIR (mode) && acl_delete_def_file (name)) return -1; if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) { /* We did not call chmod so far, and either the mode and the ACL are separate or special bits are to be set which don't fit into ACLs. */ return chmod_or_fchmod (name, desc, mode); } return 0; # else /* 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. You have to use acl_get_file (name, ACL_TYPE_EXTENDED) or acl_get_fd (open (name, ...)) to retrieve an ACL. On the other hand, acl_set_file (name, ACL_TYPE_ACCESS, acl) and acl_set_file (name, ACL_TYPE_DEFAULT, acl) have the same effect as acl_set_file (name, ACL_TYPE_EXTENDED, acl): Each of these calls sets the file's ACL. */ acl_t acl; int ret; /* Remove the ACL if the file has ACLs. */ if (HAVE_ACL_GET_FD && desc != -1) acl = acl_get_fd (desc); else acl = acl_get_file (name, ACL_TYPE_EXTENDED); if (acl) { acl_free (acl); acl = acl_init (0); if (acl) { if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, acl); else ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl); if (ret != 0) { int saved_errno = errno; acl_free (acl); if (ACL_NOT_WELL_SUPPORTED (saved_errno)) return chmod_or_fchmod (name, desc, mode); else { errno = saved_errno; return -1; } } acl_free (acl); } } /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ return chmod_or_fchmod (name, desc, mode); # endif # elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */ int done_setacl = 0; # 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). */ /* 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? We fetch the existing ACL. 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 convention; { int count; ace_t *entries; for (;;) { 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; } if ((desc != -1 ? facl (desc, ACE_GETACL, count, entries) : acl (name, ACE_GETACL, count, entries)) == 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) { ace_t entries[6]; int count; int ret; if (convention) { /* Running on Solaris 10. */ entries[0].a_type = OLD_ALLOW; entries[0].a_flags = OLD_ACE_OWNER; entries[0].a_who = 0; /* irrelevant */ entries[0].a_access_mask = (mode >> 6) & 7; entries[1].a_type = OLD_ALLOW; entries[1].a_flags = OLD_ACE_GROUP; entries[1].a_who = 0; /* irrelevant */ entries[1].a_access_mask = (mode >> 3) & 7; entries[2].a_type = OLD_ALLOW; entries[2].a_flags = OLD_ACE_OTHER; entries[2].a_who = 0; entries[2].a_access_mask = mode & 7; count = 3; } else {
int qcopy_acl (const char *src_name, int source_desc, const char *dst_name, int dest_desc, mode_t mode) { #if USE_ACL && HAVE_ACL_GET_FILE /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ # if !HAVE_ACL_TYPE_EXTENDED /* Linux, FreeBSD, IRIX, Tru64 */ acl_t acl; int ret; if (HAVE_ACL_GET_FD && source_desc != -1) acl = acl_get_fd (source_desc); else acl = acl_get_file (src_name, ACL_TYPE_ACCESS); if (acl == NULL) { if (! acl_errno_valid (errno)) return qset_acl (dst_name, dest_desc, mode); else return -2; } if (HAVE_ACL_SET_FD && dest_desc != -1) ret = acl_set_fd (dest_desc, acl); else ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl); if (ret != 0) { int saved_errno = errno; if (! acl_errno_valid (errno) && !acl_access_nontrivial (acl)) { acl_free (acl); return chmod_or_fchmod (dst_name, dest_desc, mode); } else { acl_free (acl); chmod_or_fchmod (dst_name, dest_desc, mode); errno = saved_errno; return -1; } } else acl_free (acl); if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) { /* We did not call chmod so far, and either the mode and the ACL are separate or special bits are to be set which don't fit into ACLs. */ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) return -1; } if (S_ISDIR (mode)) { acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); if (acl == NULL) return -2; if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) { int saved_errno = errno; acl_free (acl); errno = saved_errno; return -1; } else acl_free (acl); } return 0; # else /* 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. You have to use acl_get_file (name, ACL_TYPE_EXTENDED) or acl_get_fd (open (name, ...)) to retrieve an ACL. On the other hand, acl_set_file (name, ACL_TYPE_ACCESS, acl) and acl_set_file (name, ACL_TYPE_DEFAULT, acl) have the same effect as acl_set_file (name, ACL_TYPE_EXTENDED, acl): Each of these calls sets the file's ACL. */ acl_t acl; int ret; if (HAVE_ACL_GET_FD && source_desc != -1) acl = acl_get_fd (source_desc); else acl = acl_get_file (src_name, ACL_TYPE_EXTENDED); if (acl == NULL) { if (!acl_errno_valid (errno)) return qset_acl (dst_name, dest_desc, mode); else return -2; } if (HAVE_ACL_SET_FD && dest_desc != -1) ret = acl_set_fd (dest_desc, acl); else ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl); if (ret != 0) { int saved_errno = errno; if (!acl_errno_valid (saved_errno) && !acl_extended_nontrivial (acl)) { acl_free (acl); return chmod_or_fchmod (dst_name, dest_desc, mode); } else { acl_free (acl); chmod_or_fchmod (dst_name, dest_desc, mode); errno = saved_errno; return -1; } } else acl_free (acl); /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ return chmod_or_fchmod (dst_name, dest_desc, mode); # endif #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions 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; # 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). There is an API pathconf (name, _PC_ACL_ENABLED) fpathconf (desc, _PC_ACL_ENABLED) that allows to determine which of the two kinds of ACLs is supported for the given file. But some file systems may implement this call incorrectly, so better not use it. When fetching the source ACL, we simply fetch both ACL types. When setting the destination ACL, we try either ACL types, assuming that the kernel will translate the ACL from one form to the other. (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view> 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 */ if (!MODE_INSIDE_ACL) { /* On Cygwin, it is necessary to call chmod before acl, because chmod can change the contents of the ACL (in ways that don't change the allowed accesses, but still visible). */ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) saved_errno = errno; did_chmod = 1; } /* If both ace_entries and entries are available, try SETACL before ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL can. */ if (count > 0) { ret = (dest_desc != -1 ? facl (dest_desc, SETACL, count, entries) : acl (dst_name, SETACL, count, entries)); if (ret < 0 && saved_errno == 0) { saved_errno = errno; if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) && !acl_nontrivial (count, entries)) saved_errno = 0; } else did_chmod = 1; } free (entries); # ifdef ACE_GETACL if (ace_count > 0) { ret = (dest_desc != -1 ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries) : acl (dst_name, ACE_SETACL, ace_count, ace_entries)); if (ret < 0 && saved_errno == 0) { saved_errno = errno; if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP) && !acl_ace_nontrivial (ace_count, ace_entries)) saved_errno = 0; } } free (ace_entries); # endif if (MODE_INSIDE_ACL && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) { /* We did not call chmod so far, and either the mode and the ACL are separate or special bits are to be set which don't fit into ACLs. */ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) { if (saved_errno == 0) saved_errno = errno; } } if (saved_errno) { errno = saved_errno; return -1; } return 0; #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 if (count == 0) # if HAVE_ACLV_H if (aclv_count == 0) # endif 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 */ if (count > 0) { ret = (dest_desc != -1 ? fsetacl (dest_desc, count, entries) : setacl (dst_name, count, entries)); if (ret < 0 && saved_errno == 0) { saved_errno = errno; if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) { struct stat source_statbuf; if ((source_desc != -1 ? fstat (source_desc, &source_statbuf) : stat (src_name, &source_statbuf)) == 0) { if (!acl_nontrivial (count, entries, &source_statbuf)) saved_errno = 0; } else saved_errno = errno; } } else did_chmod = 1; } # if HAVE_ACLV_H if (aclv_count > 0) { ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries); if (ret < 0 && saved_errno == 0) { saved_errno = errno; if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) { if (!aclv_nontrivial (aclv_count, aclv_entries)) saved_errno = 0; } } else did_chmod = 1; } # endif if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) { /* We did not call chmod so far, and special bits are to be set which don't fit into ACLs. */ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) { if (saved_errno == 0) saved_errno = errno; } } if (saved_errno) { errno = saved_errno; return -1; } return 0; #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */ /* TODO */ #elif USE_ACL && HAVE_STATACL /* older AIX */ union { struct acl a; char room[4096]; } u; int ret; if ((source_desc != -1 ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u)) : statacl (src_name, STX_NORMAL, &u.a, sizeof (u))) < 0) return -2; ret = (dest_desc != -1 ? fchacl (dest_desc, &u.a, u.a.acl_len) : chacl (dst_name, &u.a, u.a.acl_len)); if (ret < 0) { int saved_errno = errno; chmod_or_fchmod (dst_name, dest_desc, mode); errno = saved_errno; return -1; } /* No need to call chmod_or_fchmod at this point, since the mode bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */ return 0; #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) return qset_acl (dst_name, dest_desc, mode); ret = acl ((char *) dst_name, ACL_SET, count, entries); if (ret < 0) { int saved_errno = errno; if (0) { if (!acl_nontrivial (count, entries)) return chmod_or_fchmod (dst_name, dest_desc, mode); } chmod_or_fchmod (dst_name, dest_desc, mode); errno = saved_errno; return -1; } if (mode & (S_ISUID | S_ISGID | S_ISVTX)) { /* We did not call chmod so far, and either the mode and the ACL are separate or special bits are to be set which don't fit into ACLs. */ return chmod_or_fchmod (dst_name, dest_desc, mode); } return 0; #else return qset_acl (dst_name, dest_desc, mode); #endif }
int set_acl (char const *name, int desc, mode_t mode) { #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE /* POSIX 1003.1e draft 17 (abandoned) specific version. */ /* We must also have have_acl_from_text and acl_delete_def_file. (acl_delete_def_file could be emulated with acl_init followed by acl_set_file, but acl_set_file with an empty acl is unspecified.) */ # ifndef HAVE_ACL_FROM_TEXT # error Must have acl_from_text (see POSIX 1003.1e draft 17). # endif # ifndef HAVE_ACL_DELETE_DEF_FILE # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). # endif acl_t acl; int ret; if (HAVE_ACL_FROM_MODE) { acl = acl_from_mode (mode); if (!acl) { error (0, errno, "%s", quote (name)); return -1; } } else { char acl_text[] = "u::---,g::---,o::---"; if (mode & S_IRUSR) acl_text[ 3] = 'r'; if (mode & S_IWUSR) acl_text[ 4] = 'w'; if (mode & S_IXUSR) acl_text[ 5] = 'x'; if (mode & S_IRGRP) acl_text[10] = 'r'; if (mode & S_IWGRP) acl_text[11] = 'w'; if (mode & S_IXGRP) acl_text[12] = 'x'; if (mode & S_IROTH) acl_text[17] = 'r'; if (mode & S_IWOTH) acl_text[18] = 'w'; if (mode & S_IXOTH) acl_text[19] = 'x'; acl = acl_from_text (acl_text); if (!acl) { error (0, errno, "%s", quote (name)); return -1; } } if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, acl); else ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); if (ret != 0) { int saved_errno = errno; acl_free (acl); if (ACL_NOT_WELL_SUPPORTED (errno)) { if (chmod_or_fchmod (name, desc, mode) != 0) saved_errno = errno; else return 0; } error (0, saved_errno, _("setting permissions for %s"), quote (name)); return -1; } else acl_free (acl); if (S_ISDIR (mode) && acl_delete_def_file (name)) { error (0, errno, _("setting permissions for %s"), quote (name)); return -1; } if (mode & (S_ISUID | S_ISGID | S_ISVTX)) { /* We did not call chmod so far, so the special bits have not yet been set. */ if (chmod_or_fchmod (name, desc, mode)) { error (0, errno, _("preserving permissions for %s"), quote (name)); return -1; } } return 0; #else int ret = chmod_or_fchmod (name, desc, mode); if (ret) error (0, errno, _("setting permissions for %s"), quote (name)); return ret; #endif }
int copy_acl (const char *src_name, int source_desc, const char *dst_name, int dest_desc, mode_t mode) { int ret; #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ acl_t acl; if (HAVE_ACL_GET_FD && source_desc != -1) acl = acl_get_fd (source_desc); else acl = acl_get_file (src_name, ACL_TYPE_ACCESS); if (acl == NULL) { if (ACL_NOT_WELL_SUPPORTED (errno)) return set_acl (dst_name, dest_desc, mode); else { error (0, errno, "%s", quote (src_name)); return -1; } } if (HAVE_ACL_SET_FD && dest_desc != -1) ret = acl_set_fd (dest_desc, acl); else ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl); if (ret != 0) { int saved_errno = errno; if (ACL_NOT_WELL_SUPPORTED (errno)) { int n = acl_entries (acl); acl_free (acl); if (n == 3) { if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) saved_errno = errno; else return 0; } else chmod_or_fchmod (dst_name, dest_desc, mode); } else { acl_free (acl); chmod_or_fchmod (dst_name, dest_desc, mode); } error (0, saved_errno, _("preserving permissions for %s"), quote (dst_name)); return -1; } else acl_free (acl); if (mode & (S_ISUID | S_ISGID | S_ISVTX)) { /* We did not call chmod so far, so the special bits have not yet been set. */ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) { error (0, errno, _("preserving permissions for %s"), quote (dst_name)); return -1; } } if (S_ISDIR (mode)) { acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); if (acl == NULL) { error (0, errno, "%s", quote (src_name)); return -1; } if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) { error (0, errno, _("preserving permissions for %s"), quote (dst_name)); acl_free (acl); return -1; } else acl_free (acl); } return 0; #else ret = chmod_or_fchmod (dst_name, dest_desc, mode); if (ret != 0) error (0, errno, _("preserving permissions for %s"), quote (dst_name)); return ret; #endif }
int copy_acl (const char *src_name, int source_desc, const char *dst_name, int dest_desc, mode_t mode) { int ret; #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */ acl_t acl; if (HAVE_ACL_GET_FD && source_desc != -1) acl = acl_get_fd (source_desc); else acl = acl_get_file (src_name, ACL_TYPE_ACCESS); if (acl == NULL) { if (ACL_NOT_WELL_SUPPORTED (errno)) return set_acl (dst_name, dest_desc, mode); else { error (0, errno, "%s", quote (src_name)); return -1; } } if (HAVE_ACL_SET_FD && dest_desc != -1) ret = acl_set_fd (dest_desc, acl); else ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl); if (ret != 0) { int saved_errno = errno; if (ACL_NOT_WELL_SUPPORTED (errno)) { int n = acl_entries (acl); acl_free (acl); /* On most hosts with MODE_INSIDE_ACL an ACL is trivial if n == 3, and it cannot be less than 3. On IRIX 6.5 it is also trivial if n == -1. For simplicity and safety, assume the ACL is trivial if n <= 3. Also see file-has-acl.c for some of the other possibilities; it's not clear whether that complexity is needed here. */ if (n <= 3 * MODE_INSIDE_ACL) { if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) saved_errno = errno; else return 0; } else chmod_or_fchmod (dst_name, dest_desc, mode); } else { acl_free (acl); chmod_or_fchmod (dst_name, dest_desc, mode); } error (0, saved_errno, _("preserving permissions for %s"), quote (dst_name)); return -1; } else acl_free (acl); if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) { /* We did not call chmod so far, and either the mode and the ACL are separate or special bits are to be set which don't fit into ACLs. */ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) { error (0, errno, _("preserving permissions for %s"), quote (dst_name)); return -1; } } if (S_ISDIR (mode)) { acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); if (acl == NULL) { error (0, errno, "%s", quote (src_name)); return -1; } if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) { error (0, errno, _("preserving permissions for %s"), quote (dst_name)); acl_free (acl); return -1; } else acl_free (acl); } return 0; #else # if USE_ACL && defined ACL_NO_TRIVIAL /* Solaris 10 NFSv4 ACLs. */ acl_t *aclp = NULL; ret = (source_desc < 0 ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp) : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp)); if (ret != 0 && errno != ENOSYS) { error (0, errno, "%s", quote (src_name)); return ret; } # endif ret = qset_acl (dst_name, dest_desc, mode); if (ret != 0) error (0, errno, _("preserving permissions for %s"), quote (dst_name)); # if USE_ACL && defined ACL_NO_TRIVIAL if (ret == 0 && aclp) { ret = (dest_desc < 0 ? acl_set (dst_name, aclp) : facl_set (dest_desc, aclp)); if (ret != 0) error (0, errno, _("preserving permissions for %s"), quote (dst_name)); acl_free (aclp); } # endif return ret; #endif }