static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) { acl_permset_t permset; acl_entry_type_t entry_type; acl_flagset_t flagset; int have_entry; uid_t *id, *id_new; have_entry = 0; id = acl_get_qualifier(*entry); if (id == NULL) err(1, "acl_get_qualifier() failed"); id_new = acl_get_qualifier(*entry_new); if (id_new == NULL) err(1, "acl_get_qualifier() failed"); if (*id == *id_new) { /* any other matches */ if (acl_get_permset(*entry, &permset) == -1) err(1, "acl_get_permset() failed"); if (acl_set_permset(*entry_new, permset) == -1) err(1, "acl_set_permset() failed"); if (acl_brand == ACL_BRAND_NFS4) { if (acl_get_entry_type_np(*entry, &entry_type)) err(1, "acl_get_entry_type_np() failed"); if (acl_set_entry_type_np(*entry_new, entry_type)) err(1, "acl_set_entry_type_np() failed"); if (acl_get_flagset_np(*entry, &flagset)) err(1, "acl_get_flagset_np() failed"); if (acl_set_flagset_np(*entry_new, flagset)) err(1, "acl_set_flagset_np() failed"); } have_entry = 1; } acl_free(id); acl_free(id_new); return (have_entry); }
/* merge two acl entries together */ static int merge_acl_entries(acl_entry_t *entry1, acl_entry_t *entry2) { acl_permset_t permset; acl_entry_type_t entry_type; acl_flagset_t flagset; if (acl_get_permset(*entry1, &permset) < 0) err(EX_OSERR, "acl_get_permset() failed"); if (acl_set_permset(*entry2, permset) < 0) err(EX_OSERR, "acl_set_permset() failed"); if (acl_get_entry_type_np(*entry1, &entry_type) < 0) err(EX_OSERR, "acl_get_entry_type_np() failed"); if (acl_set_entry_type_np(*entry2, entry_type) < 0) err(EX_OSERR, "acl_set_entry_type_np() failed"); if (acl_get_flagset_np(*entry1, &flagset) < 0) err(EX_OSERR, "acl_get_flagset_np() failed"); if (acl_set_flagset_np(*entry2, flagset) < 0) err(EX_OSERR, "acl_set_flagset_np() failed"); return (0); }
/* * merge an ACL into existing file's ACL */ int merge_acl(acl_t acl, acl_t *prev_acl, const char *filename) { acl_entry_t entry, entry_new; acl_permset_t permset; acl_t acl_new; acl_tag_t tag, tag_new; acl_entry_type_t entry_type, entry_type_new; acl_flagset_t flagset; int entry_id, entry_id_new, have_entry, had_entry, entry_number = 0; int acl_brand, prev_acl_brand; acl_get_brand_np(acl, &acl_brand); acl_get_brand_np(*prev_acl, &prev_acl_brand); if (branding_mismatch(acl_brand, prev_acl_brand)) { warnx("%s: branding mismatch; existing ACL is %s, " "entry to be merged is %s", filename, brand_name(prev_acl_brand), brand_name(acl_brand)); return (-1); } acl_new = acl_dup(*prev_acl); if (acl_new == NULL) err(1, "%s: acl_dup() failed", filename); entry_id = ACL_FIRST_ENTRY; while (acl_get_entry(acl, entry_id, &entry) == 1) { entry_id = ACL_NEXT_ENTRY; have_entry = 0; had_entry = 0; /* keep track of existing ACL_MASK entries */ if (acl_get_tag_type(entry, &tag) == -1) err(1, "%s: acl_get_tag_type() failed - " "invalid ACL entry", filename); if (tag == ACL_MASK) have_mask = 1; /* check against the existing ACL entries */ entry_id_new = ACL_FIRST_ENTRY; while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { entry_id_new = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) err(1, "%s: acl_get_tag_type() failed", filename); if (acl_get_tag_type(entry_new, &tag_new) == -1) err(1, "%s: acl_get_tag_type() failed", filename); if (tag != tag_new) continue; /* * For NFSv4, in addition to "tag" and "id" we also * compare "entry_type". */ if (acl_brand == ACL_BRAND_NFS4) { if (acl_get_entry_type_np(entry, &entry_type)) err(1, "%s: acl_get_entry_type_np() " "failed", filename); if (acl_get_entry_type_np(entry_new, &entry_type_new)) err(1, "%s: acl_get_entry_type_np() " "failed", filename); if (entry_type != entry_type_new) continue; } switch(tag) { case ACL_USER: case ACL_GROUP: have_entry = merge_user_group(&entry, &entry_new, acl_brand); if (have_entry == 0) break; /* FALLTHROUGH */ case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_OTHER: case ACL_MASK: case ACL_EVERYONE: if (acl_get_permset(entry, &permset) == -1) err(1, "%s: acl_get_permset() failed", filename); if (acl_set_permset(entry_new, permset) == -1) err(1, "%s: acl_set_permset() failed", filename); if (acl_brand == ACL_BRAND_NFS4) { if (acl_get_entry_type_np(entry, &entry_type)) err(1, "%s: acl_get_entry_type_np() failed", filename); if (acl_set_entry_type_np(entry_new, entry_type)) err(1, "%s: acl_set_entry_type_np() failed", filename); if (acl_get_flagset_np(entry, &flagset)) err(1, "%s: acl_get_flagset_np() failed", filename); if (acl_set_flagset_np(entry_new, flagset)) err(1, "%s: acl_set_flagset_np() failed", filename); } had_entry = have_entry = 1; break; default: /* should never be here */ errx(1, "%s: invalid tag type: %i", filename, tag); break; } } /* if this entry has not been found, it must be new */ if (had_entry == 0) { /* * NFSv4 ACL entries must be prepended to the ACL. * Appending them at the end makes no sense, since * in most cases they wouldn't even get evaluated. */ if (acl_brand == ACL_BRAND_NFS4) { if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { warn("%s: acl_create_entry_np() failed", filename); acl_free(acl_new); return (-1); } /* * Without this increment, adding several * entries at once, for example * "setfacl -m user:1:r:allow,user:2:r:allow", * would make them appear in reverse order. */ entry_number++; } else { if (acl_create_entry(&acl_new, &entry_new) == -1) { warn("%s: acl_create_entry() failed", filename); acl_free(acl_new); return (-1); } } if (acl_copy_entry(entry_new, entry) == -1) err(1, "%s: acl_copy_entry() failed", filename); } } acl_free(*prev_acl); *prev_acl = acl_new; return (0); }
static int set_acl(struct archive *a, int fd, const char *name, struct archive_acl *abstract_acl, acl_type_t acl_type, int ae_requested_type, const char *tname) { acl_t acl; acl_entry_t acl_entry; acl_permset_t acl_permset; #ifdef ACL_TYPE_NFS4 acl_flagset_t acl_flagset; int r; #endif int ret; int ae_type, ae_permset, ae_tag, ae_id; uid_t ae_uid; gid_t ae_gid; const char *ae_name; int entries; int i; ret = ARCHIVE_OK; entries = archive_acl_reset(abstract_acl, ae_requested_type); if (entries == 0) return (ARCHIVE_OK); acl = acl_init(entries); while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { acl_create_entry(&acl, &acl_entry); switch (ae_tag) { case ARCHIVE_ENTRY_ACL_USER: acl_set_tag_type(acl_entry, ACL_USER); ae_uid = archive_write_disk_uid(a, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_uid); break; case ARCHIVE_ENTRY_ACL_GROUP: acl_set_tag_type(acl_entry, ACL_GROUP); ae_gid = archive_write_disk_gid(a, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_gid); break; case ARCHIVE_ENTRY_ACL_USER_OBJ: acl_set_tag_type(acl_entry, ACL_USER_OBJ); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); break; case ARCHIVE_ENTRY_ACL_MASK: acl_set_tag_type(acl_entry, ACL_MASK); break; case ARCHIVE_ENTRY_ACL_OTHER: acl_set_tag_type(acl_entry, ACL_OTHER); break; #ifdef ACL_TYPE_NFS4 case ARCHIVE_ENTRY_ACL_EVERYONE: acl_set_tag_type(acl_entry, ACL_EVERYONE); break; #endif default: /* XXX */ break; } #ifdef ACL_TYPE_NFS4 switch (ae_type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM); break; case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: // These don't translate directly into the system ACL. break; default: // XXX error handling here. break; } #endif acl_get_permset(acl_entry, &acl_permset); acl_clear_perms(acl_permset); for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { if (ae_permset & acl_perm_map[i].archive_perm) acl_add_perm(acl_permset, acl_perm_map[i].platform_perm); } #ifdef ACL_TYPE_NFS4 // XXX acl_get_flagset_np on FreeBSD returns EINVAL for // non-NFSv4 ACLs r = acl_get_flagset_np(acl_entry, &acl_flagset); if (r == 0) { acl_clear_flags_np(acl_flagset); for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { if (ae_permset & acl_inherit_map[i].archive_inherit) acl_add_flag_np(acl_flagset, acl_inherit_map[i].platform_inherit); } } #endif } /* Try restoring the ACL through 'fd' if we can. */ #if HAVE_ACL_SET_FD if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) ret = ARCHIVE_OK; else #else #if HAVE_ACL_SET_FD_NP if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) ret = ARCHIVE_OK; else #endif #endif #if HAVE_ACL_SET_LINK_NP if (acl_set_link_np(name, acl_type, acl) != 0) { archive_set_error(a, errno, "Failed to set %s acl", tname); ret = ARCHIVE_WARN; } #else /* TODO: Skip this if 'name' is a symlink. */ if (acl_set_file(name, acl_type, acl) != 0) { archive_set_error(a, errno, "Failed to set %s acl", tname); ret = ARCHIVE_WARN; } #endif acl_free(acl); return (ret); }