Skip to content

Commit

Permalink
Resuscitate the old NFS4 support code and make it work on FreeBSD-CUR…
Browse files Browse the repository at this point in the history
…RENT.

This includes a new test that archive_write_disk and archive_read_disk
consistently read/write ACL information to/from disk.
This new test currently passes on FreeBSD-CURRENT.
  • Loading branch information
kientzle committed Apr 9, 2012
1 parent 7387569 commit f67370d
Show file tree
Hide file tree
Showing 10 changed files with 1,003 additions and 191 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,8 @@ IF(ENABLE_ACL)
CHECK_FUNCTION_EXISTS(acl_get_perm_np HAVE_ACL_GET_PERM_NP)
CHECK_FUNCTION_EXISTS(acl_get_link HAVE_ACL_GET_LINK)
CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP)
CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP)
CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP)

# MacOS has an acl.h that isn't POSIX. It can be detected by
# checking for ACL_USER
Expand Down
1 change: 1 addition & 0 deletions libarchive/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ SET(libarchive_SOURCES
archive_util.c
archive_virtual.c
archive_write.c
archive_write_disk_acl.c
archive_write_disk_posix.c
archive_write_disk_private.h
archive_write_disk_set_standard_lookup.c
Expand Down
227 changes: 179 additions & 48 deletions libarchive/archive_read_disk_entry_from_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
#define ACL_GET_PERM acl_get_perm_np
#endif

static int setup_acls_posix1e(struct archive_read_disk *,
static int setup_acls(struct archive_read_disk *,
struct archive_entry *, int *fd);
static int setup_mac_metadata(struct archive_read_disk *,
struct archive_entry *, int *fd);
Expand Down Expand Up @@ -168,15 +168,16 @@ archive_read_disk_entry_from_file(struct archive *_a,
st = &s;
}
archive_entry_copy_stat(entry, st);
/* Lookup uname/gname */
name = archive_read_disk_uname(_a, archive_entry_uid(entry));
if (name != NULL)
archive_entry_copy_uname(entry, name);
name = archive_read_disk_gname(_a, archive_entry_gid(entry));
if (name != NULL)
archive_entry_copy_gname(entry, name);
}

/* Lookup uname/gname */
name = archive_read_disk_uname(_a, archive_entry_uid(entry));
if (name != NULL)
archive_entry_copy_uname(entry, name);
name = archive_read_disk_gname(_a, archive_entry_gid(entry));
if (name != NULL)
archive_entry_copy_gname(entry, name);

#ifdef HAVE_STRUCT_STAT_ST_FLAGS
/* On FreeBSD, we get flags for free with the stat. */
/* TODO: Does this belong in copy_stat()? */
Expand Down Expand Up @@ -244,7 +245,7 @@ archive_read_disk_entry_from_file(struct archive *_a,
}
#endif /* HAVE_READLINK || HAVE_READLINKAT */

r = setup_acls_posix1e(a, entry, &fd);
r = setup_acls(a, entry, &fd);
r1 = setup_xattrs(a, entry, &fd);
if (r1 < r)
r = r1;
Expand Down Expand Up @@ -388,11 +389,11 @@ setup_mac_metadata(struct archive_read_disk *a,


#ifdef HAVE_POSIX_ACL
static void setup_acl_posix1e(struct archive_read_disk *a,
static int translate_acl(struct archive_read_disk *a,
struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);

static int
setup_acls_posix1e(struct archive_read_disk *a,
setup_acls(struct archive_read_disk *a,
struct archive_entry *entry, int *fd)
{
const char *accpath;
Expand All @@ -404,15 +405,33 @@ setup_acls_posix1e(struct archive_read_disk *a,

archive_entry_acl_clear(entry);

if (*fd < 0 && a->tree != NULL &&
(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) {
archive_set_error(&a->archive, errno,
"Couldn't access %s", accpath);
return (ARCHIVE_FAILED);
}
/* 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. */
Expand All @@ -431,7 +450,7 @@ setup_acls_posix1e(struct archive_read_disk *a,
else
acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
if (acl != NULL) {
setup_acl_posix1e(a, entry, acl,
translate_acl(a, entry, acl,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
acl_free(acl);
}
Expand All @@ -440,7 +459,7 @@ setup_acls_posix1e(struct archive_read_disk *a,
if (S_ISDIR(archive_entry_mode(entry))) {
acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
if (acl != NULL) {
setup_acl_posix1e(a, entry, acl,
translate_acl(a, entry, acl,
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
acl_free(acl);
}
Expand All @@ -449,69 +468,181 @@ setup_acls_posix1e(struct archive_read_disk *a,
}

/*
* Translate POSIX.1e ACL into libarchive internal structure.
* Translate system ACL into libarchive internal structure.
*/
static void
setup_acl_posix1e(struct archive_read_disk *a,
struct archive_entry *entry, acl_t acl, int archive_entry_acl_type)

static struct {
int archive_perm;
int platform_perm;
} acl_perm_map[] = {
{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
};

static struct {
int archive_inherit;
int platform_inherit;
} acl_inherit_map[] = {
{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}
};

static int
translate_acl(struct archive_read_disk *a,
struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
{
acl_tag_t acl_tag;
acl_entry_type_t acl_type;
acl_flagset_t acl_flagset;
acl_entry_t acl_entry;
acl_permset_t acl_permset;
int brand, i, r, entry_acl_type;
int s, ae_id, ae_tag, ae_perm;
const char *ae_name;


// FreeBSD "brands" ACLs as POSIX.1e or NFSv4
// Make sure the "brand" on this ACL is consistent
// with the default_entry_acl_type bits provided.
acl_get_brand_np(acl, &brand);
switch (brand) {
case ACL_BRAND_POSIX:
switch (default_entry_acl_type) {
case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
entry_acl_type = default_entry_acl_type;
break;
default:
// XXX set warning message?
return ARCHIVE_FAILED;
}
break;
case ACL_BRAND_NFS4:
if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
// XXX set warning message?
return ARCHIVE_FAILED;
}
break;
default:
// XXX set warning message?
return ARCHIVE_FAILED;
break;
}


s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
while (s == 1) {
ae_id = -1;
ae_name = NULL;
ae_perm = 0;

acl_get_tag_type(acl_entry, &acl_tag);
if (acl_tag == ACL_USER) {
switch (acl_tag) {
case ACL_USER:
ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
ae_name = archive_read_disk_uname(&a->archive, ae_id);
ae_tag = ARCHIVE_ENTRY_ACL_USER;
} else if (acl_tag == ACL_GROUP) {
break;
case ACL_GROUP:
ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
ae_name = archive_read_disk_gname(&a->archive, ae_id);
ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
} else if (acl_tag == ACL_MASK) {
break;
case ACL_MASK:
ae_tag = ARCHIVE_ENTRY_ACL_MASK;
} else if (acl_tag == ACL_USER_OBJ) {
break;
case ACL_USER_OBJ:
ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
} else if (acl_tag == ACL_GROUP_OBJ) {
break;
case ACL_GROUP_OBJ:
ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
} else if (acl_tag == ACL_OTHER) {
break;
case ACL_OTHER:
ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
} else {
break;
case ACL_EVERYONE:
ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
break;
default:
/* Skip types that libarchive can't support. */
continue;
}

acl_get_permset(acl_entry, &acl_permset);
ae_perm = 0;
// XXX acl type maps to allow/deny/audit/YYYY bits
// XXX acl_get_entry_type_np on FreeBSD returns EINVAL for
// non-NFSv4 ACLs
entry_acl_type = default_entry_acl_type;
r = acl_get_entry_type_np(acl_entry, &acl_type);
if (r == 0) {
switch (acl_type) {
case ACL_ENTRY_TYPE_ALLOW:
entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
break;
case ACL_ENTRY_TYPE_DENY:
entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
break;
case ACL_ENTRY_TYPE_AUDIT:
entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
break;
case ACL_ENTRY_TYPE_ALARM:
entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
break;
}
}

/*
* acl_get_perm() is spelled differently on different
* platforms; see above.
* Libarchive stores "flag" (NFSv4 inheritance bits)
* in the ae_perm bitmap.
*/
if (ACL_GET_PERM(acl_permset, ACL_EXECUTE))
ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
if (ACL_GET_PERM(acl_permset, ACL_READ))
ae_perm |= ARCHIVE_ENTRY_ACL_READ;
if (ACL_GET_PERM(acl_permset, ACL_WRITE))
ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
acl_get_flagset_np(acl_entry, &acl_flagset);
for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
if (acl_get_flag_np(acl_flagset,
acl_inherit_map[i].platform_inherit))
ae_perm |= acl_inherit_map[i].archive_inherit;

archive_entry_acl_add_entry(entry,
archive_entry_acl_type, ae_perm, ae_tag,
ae_id, ae_name);
}

acl_get_permset(acl_entry, &acl_permset);
for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) {
/*
* acl_get_perm() is spelled differently on different
* platforms; see above.
*/
if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm))
ae_perm |= acl_perm_map[i].archive_perm;
}

archive_entry_acl_add_entry(entry, entry_acl_type,
ae_perm, ae_tag,
ae_id, ae_name);

s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
}
return (ARCHIVE_OK);
}
#else
static int
setup_acls_posix1e(struct archive_read_disk *a,
struct archive_entry *entry, int *fd)
setup_acls(struct archive_read_disk *a,
struct archive_entry *entry, int fd)
{
(void)a; /* UNUSED */
(void)entry; /* UNUSED */
Expand Down
Loading

0 comments on commit f67370d

Please sign in to comment.