/* Copy the permissions of src_path to dst_path. This includes the file mode permission bits and ACLs. File ownership is not copied. */ int perm_copy_fd (const char *src_path, int src_fd, const char *dst_path, int dst_fd, struct error_context *ctx) { #if defined(HAVE_ACL_GET_FD) && defined(HAVE_ACL_SET_FD) acl_t acl; #endif struct stat st; int ret = 0; ret = fstat(src_fd, &st); if (ret != 0) { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); return -1; } #if defined(HAVE_ACL_GET_FD) && defined(HAVE_ACL_SET_FD) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl = acl_get_fd (src_fd); if (acl == NULL) { ret = -1; if (errno == ENOSYS || errno == ENOTSUP) ret = set_acl_fd (dst_path, dst_fd, st.st_mode, ctx); else { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); } return ret; } if (acl_set_fd (dst_fd, acl) != 0) { int saved_errno = errno; __apply_mask_to_mode(&st.st_mode, acl); ret = fchmod (dst_fd, st.st_mode); if ((errno != ENOSYS && errno != ENOTSUP) || acl_entries (acl) != 3) { const char *qpath = quote (ctx, dst_path); errno = saved_errno; error (ctx, _("preserving permissions for %s"), qpath); quote_free (ctx, qpath); ret = -1; } } (void) acl_free (acl); return ret; #else /* POSIX.1 version. */ ret = fchmod (dst_fd, st.st_mode); if (ret != 0) { const char *qpath = quote (ctx, dst_path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; #endif }
SMB_ACL_T posixacl_sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { struct smb_acl_t *result; acl_t acl = acl_get_fd(fsp->fh->fd); if (acl == NULL) { return NULL; } result = smb_acl_to_internal(acl); acl_free(acl); return result; }
static int setup_acls_posix1e(struct archive_read_disk *a, struct archive_entry *entry, int fd) { const char *accpath; acl_t acl; accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); archive_entry_acl_clear(entry); /* Retrieve access ACL from file. */ if (fd >= 0) acl = acl_get_fd(fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_ACCESS); if (acl != NULL) { setup_acl_posix1e(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); acl_free(acl); } /* Only directories can have default ACLs. */ if (S_ISDIR(archive_entry_mode(entry))) { acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); if (acl != NULL) { setup_acl_posix1e(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); acl_free(acl); } } return (ARCHIVE_OK); }
static int fix_acl(int fd, uid_t uid) { #ifdef HAVE_ACL _cleanup_(acl_freep) acl_t acl = NULL; acl_entry_t entry; acl_permset_t permset; assert(fd >= 0); if (uid <= SYSTEM_UID_MAX) return 0; /* Make sure normal users can read (but not write or delete) * their own coredumps */ acl = acl_get_fd(fd); if (!acl) { log_error("Failed to get ACL: %m"); return -errno; } if (acl_create_entry(&acl, &entry) < 0 || acl_set_tag_type(entry, ACL_USER) < 0 || acl_set_qualifier(entry, &uid) < 0) { log_error("Failed to patch ACL: %m"); return -errno; } if (acl_get_permset(entry, &permset) < 0 || acl_add_perm(permset, ACL_READ) < 0 || calc_acl_mask_if_needed(&acl) < 0) { log_warning("Failed to patch ACL: %m"); return -errno; } if (acl_set_fd(fd, acl) < 0) { log_error("Failed to apply ACL: %m"); return -errno; } #endif return 0; }
static int setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { const char *accpath; acl_t acl; int r; accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); if (*fd < 0 && a->tree != NULL) { if (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK) *fd = a->open_on_current_dir(a->tree, accpath, O_RDONLY | O_NONBLOCK); if (*fd < 0) { if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't access %s", accpath); return (ARCHIVE_FAILED); } } } archive_entry_acl_clear(entry); acl = NULL; #ifdef ACL_TYPE_NFS4 /* Try NFS4 ACL first. */ if (*fd >= 0) #if HAVE_ACL_GET_FD_NP acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4); #else acl = acl_get_fd(*fd); #endif #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks)
static int fix_acl(int fd, uid_t uid) { #if HAVE_ACL _cleanup_(acl_freep) acl_t acl = NULL; acl_entry_t entry; acl_permset_t permset; int r; assert(fd >= 0); if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) return 0; /* Make sure normal users can read (but not write or delete) * their own coredumps */ acl = acl_get_fd(fd); if (!acl) return log_error_errno(errno, "Failed to get ACL: %m"); if (acl_create_entry(&acl, &entry) < 0 || acl_set_tag_type(entry, ACL_USER) < 0 || acl_set_qualifier(entry, &uid) < 0) return log_error_errno(errno, "Failed to patch ACL: %m"); if (acl_get_permset(entry, &permset) < 0 || acl_add_perm(permset, ACL_READ) < 0) return log_warning_errno(errno, "Failed to patch ACL: %m"); r = calc_acl_mask_if_needed(&acl); if (r < 0) return log_warning_errno(r, "Failed to patch ACL: %m"); if (acl_set_fd(fd, acl) < 0) return log_error_errno(errno, "Failed to apply ACL: %m"); #endif return 0; }
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 {
static int check_facl(pool *p, const char *path, int mode, void *acl, int nents, struct stat *st, uid_t uid, gid_t gid, array_header *suppl_gids) { # if defined(HAVE_BSD_POSIX_ACL) || defined(HAVE_LINUX_POSIX_ACL) register unsigned int i; int have_access_entry = FALSE, res = -1; pool *acl_pool; acl_t facl = acl; acl_entry_t ae; acl_tag_t ae_type; acl_entry_t acl_user_entry = NULL; acl_entry_t acl_group_entry = NULL; acl_entry_t acl_other_entry = NULL; acl_entry_t acl_mask_entry = NULL; array_header *acl_groups; array_header *acl_users; /* Iterate through all of the ACL entries, sorting them for later * checking. */ res = acl_get_entry(facl, ACL_FIRST_ENTRY, &ae); if (res < 0) { pr_log_debug(DEBUG10, "FS: unable to retrieve first ACL entry for '%s': %s", path, strerror(errno)); errno = EACCES; return -1; } if (res == 0) { pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s' has no entries!", path); errno = EACCES; return -1; } acl_pool = make_sub_pool(p); acl_groups = make_array(acl_pool, 1, sizeof(acl_entry_t)); acl_users = make_array(acl_pool, 1, sizeof(acl_entry_t)); while (res > 0) { if (acl_get_tag_type(ae, &ae_type) < 0) { pr_log_debug(DEBUG5, "FS: error retrieving type of ACL entry for '%s': %s", path, strerror(errno)); res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae); continue; } if (ae_type & ACL_USER_OBJ) { acl_copy_entry(acl_user_entry, ae); } else if (ae_type & ACL_USER) { acl_entry_t *ae_dup = push_array(acl_users); acl_copy_entry(*ae_dup, ae); } else if (ae_type & ACL_GROUP_OBJ) { acl_copy_entry(acl_group_entry, ae); } else if (ae_type & ACL_GROUP) { acl_entry_t *ae_dup = push_array(acl_groups); acl_copy_entry(*ae_dup, ae); } else if (ae_type & ACL_OTHER) { acl_copy_entry(acl_other_entry, ae); } else if (ae_type & ACL_MASK) { acl_copy_entry(acl_mask_entry, ae); } res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae); } /* Select the ACL entry that determines access. */ res = -1; /* 1. If the given user ID matches the file owner, use that entry for * access. */ if (uid == st->st_uid) { /* Check the acl_user_entry for access. */ acl_copy_entry(ae, acl_user_entry); ae_type = ACL_USER_OBJ; have_access_entry = TRUE; } /* 2. If not matched above, and f the given user ID matches one of the * named user entries, use that entry for access. */ for (i = 0; !have_access_entry && i < acl_users->nelts; i++) { acl_entry_t e = ((acl_entry_t *) acl_users->elts)[i]; if (uid == *((uid_t *) acl_get_qualifier(e))) { /* Check this entry for access. Note that it'll need to * be modified by the mask, if any, later. */ acl_copy_entry(ae, e); ae_type = ACL_USER; have_access_entry = TRUE; break; } } /* 3. If not matched above, and if one of the group IDs matches the * group owner entry, and the group owner entry contains the * requested permissions, use that entry for access. */ if (!have_access_entry && gid == st->st_gid) { /* Check the acl_group_entry for access. First though, we need to * see if the acl_group_entry contains the requested permissions. */ acl_permset_t perms; acl_get_permset(acl_group_entry, &perms); # if defined(HAVE_BSD_POSIX_ACL) if (acl_get_perm_np(perms, mode) == 1) { # elif defined(HAVE_LINUX_POSIX_ACL) if (acl_get_perm(perms, mode) == 1) { # endif acl_copy_entry(ae, acl_group_entry); ae_type = ACL_GROUP_OBJ; have_access_entry = TRUE; } } if (suppl_gids) { for (i = 0; !have_access_entry && i < suppl_gids->nelts; i++) { gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[i]; if (suppl_gid == st->st_gid) { /* Check the acl_group_entry for access. First though, we need to * see if the acl_group_entry contains the requested permissions. */ acl_permset_t perms; acl_get_permset(acl_group_entry, &perms); # if defined(HAVE_BSD_POSIX_ACL) if (acl_get_perm_np(perms, mode) == 1) { # elif defined(HAVE_LINUX_POSIX_ACL) if (acl_get_perm(perms, mode) == 1) { # endif acl_copy_entry(ae, acl_group_entry); ae_type = ACL_GROUP_OBJ; have_access_entry = TRUE; break; } } } } /* 5. If not matched above, and if one of the group IDs matches one * of the named group entries, and that entry contains the requested * permissions, use that entry for access. */ for (i = 0; !have_access_entry && i < acl_groups->nelts; i++) { acl_entry_t e = ((acl_entry_t *) acl_groups->elts)[i]; if (gid == *((gid_t *) acl_get_qualifier(e))) { /* Check this entry for access. Note that it'll need to * be modified by the mask, if any, later. */ acl_permset_t perms; acl_get_permset(e, &perms); # if defined(HAVE_BSD_POSIX_ACL) if (acl_get_perm_np(perms, mode) == 1) { # elif defined(HAVE_LINUX_POSIX_ACL) if (acl_get_perm(perms, mode) == 1) { # endif acl_copy_entry(ae, e); ae_type = ACL_GROUP; have_access_entry = TRUE; break; } } if (suppl_gids) { register unsigned int j; for (j = 0; !have_access_entry && j < suppl_gids->nelts; j++) { gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[j]; if (suppl_gid == *((gid_t *) acl_get_qualifier(e))) { /* Check this entry for access. Note that it'll need to * be modified by the mask, if any, later. */ acl_permset_t perms; acl_get_permset(e, &perms); # if defined(HAVE_BSD_POSIX_ACL) if (acl_get_perm_np(perms, mode) == 1) { # elif defined(HAVE_LINUX_POSIX_ACL) if (acl_get_perm(perms, mode) == 1) { # endif acl_copy_entry(ae, e); ae_type = ACL_GROUP; have_access_entry = TRUE; break; } } } } } /* 6. If not matched above, and if one of the group IDs matches * the group owner or any of the named group entries, but neither * the group owner entry nor any of the named group entries contains * the requested permissions, access is denied. */ /* 7. If not matched above, the other entry determines access. */ if (!have_access_entry) { acl_copy_entry(ae, acl_other_entry); ae_type = ACL_OTHER; have_access_entry = TRUE; } /* Access determination: * * If either the user owner entry or other entry were used, and the * entry contains the requested permissions, access is permitted. * * Otherwise, if the selected entry and the mask entry both contain * the requested permissions, access is permitted. * * Otherwise, access is denied. */ switch (ae_type) { case ACL_USER_OBJ: case ACL_OTHER: { acl_permset_t perms; acl_get_permset(ae, &perms); # if defined(HAVE_BSD_POSIX_ACL) if (acl_get_perm_np(perms, mode) == 1) { # elif defined(HAVE_LINUX_POSIX_ACL) if (acl_get_perm(perms, mode) == 1) { # endif res = 0; } break; } default: { acl_permset_t ent_perms, mask_perms; acl_get_permset(ae, &ent_perms); acl_get_permset(acl_mask_entry, &mask_perms); # if defined(HAVE_BSD_POSIX_ACL) if (acl_get_perm_np(ent_perms, mode) == 1 && acl_get_perm_np(mask_perms, mode) == 1) { # elif defined(HAVE_LINUX_POSIX_ACL) if (acl_get_perm(ent_perms, mode) == 1 && acl_get_perm(mask_perms, mode) == 1) { # endif res = 0; } break; } } destroy_pool(acl_pool); if (res < 0) errno = EACCES; return res; # elif defined(HAVE_SOLARIS_POSIX_ACL) register unsigned int i; int have_access_entry = FALSE, idx, res = -1; pool *acl_pool; aclent_t *acls = acl; aclent_t ae; int ae_type = 0; aclent_t acl_user_entry; aclent_t acl_group_entry; aclent_t acl_other_entry; aclent_t acl_mask_entry; array_header *acl_groups; array_header *acl_users; /* In the absence of any clear documentation, I'll assume that * Solaris ACLs follow the same selection and checking algorithm * as do BSD and Linux. */ res = aclcheck(acls, nents, &idx); switch (res) { case 0: break; case GRP_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "too many GROUP entries"); errno = EACCES; return -1; case USER_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "too many USER entries"); errno = EACCES; return -1; case OTHER_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "too many OTHER entries"); errno = EACCES; return -1; case CLASS_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "too many CLASS entries"); errno = EACCES; return -1; case DUPLICATE_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "duplicate entries"); errno = EACCES; return -1; case MISS_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "missing required entry"); errno = EACCES; return -1; case MEM_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "Out of memory!"); errno = EACCES; return -1; case ENTRY_ERROR: pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path, "invalid entry type"); errno = EACCES; return -1; } /* Iterate through all of the ACL entries, sorting them for later * checking. */ acl_pool = make_sub_pool(p); acl_groups = make_array(acl_pool, 1, sizeof(aclent_t)); acl_users = make_array(acl_pool, 1, sizeof(aclent_t)); for (i = 0; i < nents; i++) { if (acls[i].a_type & USER_OBJ) { memcpy(&acl_user_entry, &(acls[i]), sizeof(aclent_t)); } else if (acls[i].a_type & USER) { aclent_t *ae_dup = push_array(acl_users); memcpy(ae_dup, &(acls[i]), sizeof(aclent_t)); } else if (acls[i].a_type & GROUP_OBJ) { memcpy(&acl_group_entry, &(acls[i]), sizeof(aclent_t)); } else if (acls[i].a_type & GROUP) { aclent_t *ae_dup = push_array(acl_groups); memcpy(ae_dup, &(acls[i]), sizeof(aclent_t)); } else if (acls[i].a_type & OTHER_OBJ) { memcpy(&acl_other_entry, &(acls[i]), sizeof(aclent_t)); } else if (acls[i].a_type & CLASS_OBJ) { memcpy(&acl_mask_entry, &(acls[i]), sizeof(aclent_t)); } } /* Select the ACL entry that determines access. */ res = -1; /* 1. If the given user ID matches the file owner, use that entry for * access. */ if (uid == st->st_uid) { /* Check the acl_user_entry for access. */ memcpy(&ae, &acl_user_entry, sizeof(aclent_t)); ae_type = USER_OBJ; have_access_entry = TRUE; } /* 2. If not matched above, and f the given user ID matches one of the * named user entries, use that entry for access. */ for (i = 0; !have_access_entry && i < acl_users->nelts; i++) { aclent_t e; memcpy(&e, &(((aclent_t *) acl_users->elts)[i]), sizeof(aclent_t)); if (uid == e.a_id) { /* Check this entry for access. Note that it'll need to * be modified by the mask, if any, later. */ memcpy(&ae, &e, sizeof(aclent_t)); ae_type = USER; have_access_entry = TRUE; break; } } /* 3. If not matched above, and if one of the group IDs matches the * group owner entry, and the group owner entry contains the * requested permissions, use that entry for access. */ if (!have_access_entry && gid == st->st_gid) { /* Check the acl_group_entry for access. First though, we need to * see if the acl_group_entry contains the requested permissions. */ if (acl_group_entry.a_perm & mode) { memcpy(&ae, &acl_group_entry, sizeof(aclent_t)); ae_type = GROUP_OBJ; have_access_entry = TRUE; } } if (suppl_gids) { for (i = 0; !have_access_entry && i < suppl_gids->nelts; i++) { gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[i]; if (suppl_gid == st->st_gid) { /* Check the acl_group_entry for access. First though, we need to * see if the acl_group_entry contains the requested permissions. */ if (acl_group_entry.a_perm & mode) { memcpy(&ae, &acl_group_entry, sizeof(aclent_t)); ae_type = GROUP_OBJ; have_access_entry = TRUE; break; } } } } /* 5. If not matched above, and if one of the group IDs matches one * of the named group entries, and that entry contains the requested * permissions, use that entry for access. */ for (i = 0; !have_access_entry && i < acl_groups->nelts; i++) { aclent_t e; memcpy(&e, &(((aclent_t *) acl_groups->elts)[i]), sizeof(aclent_t)); if (gid == e.a_id) { /* Check this entry for access. Note that it'll need to * be modified by the mask, if any, later. */ if (e.a_perm & mode) { memcpy(&ae, &e, sizeof(aclent_t)); ae_type = GROUP; have_access_entry = TRUE; break; } } if (suppl_gids) { register unsigned int j; for (j = 0; !have_access_entry && j < suppl_gids->nelts; j++) { gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[j]; if (suppl_gid == e.a_id) { /* Check this entry for access. Note that it'll need to * be modified by the mask, if any, later. */ if (e.a_perm & mode) { memcpy(&ae, &e, sizeof(aclent_t)); ae_type = GROUP; have_access_entry = TRUE; break; } } } } } /* 6. If not matched above, and if one of the group IDs matches * the group owner or any of the named group entries, but neither * the group owner entry nor any of the named group entries contains * the requested permissions, access is denied. */ /* 7. If not matched above, the other entry determines access. */ if (!have_access_entry) { memcpy(&ae, &acl_other_entry, sizeof(aclent_t)); ae_type = OTHER_OBJ; have_access_entry = TRUE; } /* Access determination: * * If either the user owner entry or other entry were used, and the * entry contains the requested permissions, access is permitted. * * Otherwise, if the selected entry and the mask entry both contain * the requested permissions, access is permitted. * * Otherwise, access is denied. */ switch (ae_type) { case USER_OBJ: case OTHER_OBJ: if (ae.a_perm & mode) res = 0; break; default: if ((ae.a_perm & mode) && (acl_mask_entry.a_perm & mode)) res = 0; break; } destroy_pool(acl_pool); if (res < 0) errno = EACCES; return res; # endif /* HAVE_SOLARIS_POSIX_ACL */ } /* FSIO handlers */ static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode, uid_t uid, gid_t gid, array_header *suppl_gids) { int nents = 0; struct stat st; void *acls; pr_fs_clear_cache(); if (pr_fsio_stat(path, &st) < 0) return -1; /* Look up the acl for this path. */ # if defined(HAVE_BSD_POSIX_ACL) || defined(HAVE_LINUX_POSIX_ACL) acls = acl_get_file(path, ACL_TYPE_ACCESS); if (!acls) { pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s", path, strerror(errno)); return -1; } # elif defined(HAVE_SOLARIS_POSIX_ACL) nents = acl(path, GETACLCNT, 0, NULL); if (nents < 0) { pr_log_debug(DEBUG10, "FS: unable to retrieve ACL count for '%s': %s", path, strerror(errno)); return -1; } acls = pcalloc(fs->fs_pool, nents * sizeof(aclent_t)); nents = acl(path, GETACL, nents, acls); if (nents < 0) { pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s", path, strerror(errno)); return -1; } # endif return check_facl(fs->fs_pool, path, mode, acls, nents, &st, uid, gid, suppl_gids); } static int facl_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid, array_header *suppl_gids) { int nents = 0; struct stat st; void *acls; pr_fs_clear_cache(); if (pr_fsio_fstat(fh, &st) < 0) return -1; /* Look up the acl for this fd. */ # if defined(HAVE_BSD_POSIX_ACL) || defined(HAVE_LINUX_POSIX_ACL) acls = acl_get_fd(PR_FH_FD(fh)); if (!acls) { pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s", fh->fh_path, strerror(errno)); return -1; } # elif defined(HAVE_SOLARIS_POSIX_ACL) nents = facl(PR_FH_FD(fh), GETACLCNT, 0, NULL); if (nents < 0) { pr_log_debug(DEBUG10, "FS: unable to retrieve ACL count for '%s': %s", fh->fh_path, strerror(errno)); return -1; } acls = pcalloc(fh->fh_fs->fs_pool, nents * sizeof(aclent_t)); nents = facl(PR_FH_FD(fh), GETACL, nents, acls); if (nents < 0) { pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s", fh->fh_path, strerror(errno)); return -1; } # endif return check_facl(fh->fh_fs->fs_pool, fh->fh_path, mode, acls, nents, &st, uid, gid, suppl_gids); } #endif /* HAVE_POSIX_ACL */ /* Initialization routines */ static int facl_init(void) { #if defined(PR_USE_FACL) && defined(HAVE_POSIX_ACL) pr_fs_t *fs = pr_register_fs(permanent_pool, "facl", "/"); if (!fs) { pr_log_pri(PR_LOG_ERR, MOD_FACL_VERSION ": error registering fs: %s", strerror(errno)); return -1; } /* Ensure that our ACL-checking handlers are used. */ fs->access = facl_fsio_access; fs->faccess = facl_fsio_faccess; #endif /* PR_USE_FACL and HAVE_POSIX_ACL */ return 0; } /* Module Tables */ module facl_module = { /* Always NULL */ NULL, NULL, /* Module API version */ 0x20, /* Module name */ "facl", /* Module configuration directive handlers */ NULL, /* Module command handlers */ NULL, /* Module authentication handlers */ NULL, /* Module initialization */ facl_init, /* Session initialization */ NULL, /* Module version */ MOD_FACL_VERSION };
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 }
static int setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { const char *accpath; acl_t acl; #if HAVE_ACL_IS_TRIVIAL_NP int r; #endif accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); archive_entry_acl_clear(entry); /* Try NFS4 ACL first. */ if (*fd >= 0) acl = acl_get_fd(*fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_NFS4); #if HAVE_ACL_IS_TRIVIAL_NP /* Ignore "trivial" ACLs that just mirror the file mode. */ acl_is_trivial_np(acl, &r); if (r) { acl_free(acl); acl = NULL; } #endif if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); acl_free(acl); return (ARCHIVE_OK); } /* Retrieve access ACL from file. */ if (*fd >= 0) acl = acl_get_fd(*fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_ACCESS); if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); acl_free(acl); } /* Only directories can have default ACLs. */ if (S_ISDIR(archive_entry_mode(entry))) { acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); acl_free(acl); } } return (ARCHIVE_OK); }
int main(int argc, char **argv) { int c; char *file; char *acl_text; int getaccess = 0; int getdefault = 0; int usefd = 0; int fd = -1; acl_t acl; prog = basename(argv[0]); while ((c = getopt(argc, argv, "adf")) != -1) { switch (c) { case 'a': getaccess = 1; break; case 'd': getdefault = 1; break; case 'f': usefd = 1; break; case '?': usage(); return 1; } } if (getdefault && usefd) { fprintf(stderr, "%s: -f and -d are not compatible\n", prog); return 1; } /* need path */ if (optind == argc) { usage(); exit(1); } else { file = argv[optind]; } if (usefd) { fd = open(file, O_RDONLY); if (fd < 0) { fprintf (stderr, "%s: error opening \"%s\": %s\n", prog, file, strerror(errno)); usage(); return 1; } } if (getaccess) { if (usefd) { acl = acl_get_fd(fd); } else { acl = acl_get_file(file, ACL_TYPE_ACCESS); } if (acl == NULL) { fprintf(stderr, "%s: error getting access ACL on \"%s\": %s\n", prog, file, strerror(errno)); return 0; } acl_text = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE); if (acl_text == NULL) { fprintf(stderr, "%s: cannot get access ACL text on '%s': %s\n", prog, file, strerror(errno)); return 0; } printf("%s: access %s", file, acl_text); acl_free(acl_text); acl_free(acl); } if (getdefault) { acl = acl_get_file(file, ACL_TYPE_DEFAULT); if (acl == NULL) { fprintf(stderr, "%s: error getting default ACL on \"%s\": %s\n", prog, file, strerror(errno)); return 0; } acl_text = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE); if (acl_text == NULL) { fprintf(stderr, "%s: cannot get default ACL text on '%s': %s\n", prog, file, strerror(errno)); return 0; } printf("%s: default %s", file, acl_text); acl_free(acl_text); acl_free(acl); } return 0; }
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 }