summaryrefslogtreecommitdiffstats
path: root/libarchive/archive_read_disk_entry_from_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive/archive_read_disk_entry_from_file.c')
-rw-r--r--libarchive/archive_read_disk_entry_from_file.c293
1 files changed, 221 insertions, 72 deletions
diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c
index a43c0ad..e984aaa 100644
--- a/libarchive/archive_read_disk_entry_from_file.c
+++ b/libarchive/archive_read_disk_entry_from_file.c
@@ -49,8 +49,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
-#ifdef HAVE_SYS_XATTR_H
+#if defined(HAVE_SYS_XATTR_H)
#include <sys/xattr.h>
+#elif defined(HAVE_ATTR_XATTR_H)
+#include <attr/xattr.h>
#endif
#ifdef HAVE_SYS_EA_H
#include <sys/ea.h>
@@ -58,9 +60,6 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
#ifdef HAVE_ACL_LIBACL_H
#include <acl/libacl.h>
#endif
-#ifdef HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
-#endif
#ifdef HAVE_COPYFILE_H
#include <copyfile.h>
#endif
@@ -104,6 +103,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
#include "archive_private.h"
#include "archive_read_disk_private.h"
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
/*
* Linux and FreeBSD plug this obvious hole in POSIX.1e in
* different ways.
@@ -114,7 +117,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);
@@ -168,15 +171,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()? */
@@ -192,12 +196,14 @@ archive_read_disk_entry_from_file(struct archive *_a,
if (fd < 0) {
if (a->tree != NULL)
fd = a->open_on_current_dir(a->tree, path,
- O_RDONLY | O_NONBLOCK);
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
else
- fd = open(path, O_RDONLY | O_NONBLOCK);
+ fd = open(path, O_RDONLY | O_NONBLOCK |
+ O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
}
if (fd >= 0) {
- unsigned long stflags;
+ int stflags;
r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
if (r == 0 && stflags != 0)
archive_entry_set_fflags(entry, stflags, 0);
@@ -244,7 +250,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;
@@ -285,9 +291,10 @@ setup_mac_metadata(struct archive_read_disk *a,
int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
struct stat copyfile_stat;
int ret = ARCHIVE_OK;
- void *buff;
+ void *buff = NULL;
int have_attrs;
- const char *name, *tempdir, *tempfile = NULL;
+ const char *name, *tempdir;
+ struct archive_string tempfile;
(void)fd; /* UNUSED */
name = archive_entry_sourcepath(entry);
@@ -322,25 +329,28 @@ setup_mac_metadata(struct archive_read_disk *a,
tempdir = getenv("TMPDIR");
if (tempdir == NULL)
tempdir = _PATH_TMP;
- tempfile = tempnam(tempdir, "tar.md.");
+ archive_string_init(&tempfile);
+ archive_strcpy(&tempfile, tempdir);
+ archive_strcat(&tempfile, "tar.md.XXXXXX");
+ tempfd = mkstemp(tempfile.s);
+ if (tempfd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Could not open extended attribute file");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ __archive_ensure_cloexec_flag(tempfd);
/* XXX I wish copyfile() could pack directly to a memory
* buffer; that would avoid the temp file here. For that
* matter, it would be nice if fcopyfile() actually worked,
* that would reduce the many open/close races here. */
- if (copyfile(name, tempfile, 0, copyfile_flags | COPYFILE_PACK)) {
+ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) {
archive_set_error(&a->archive, errno,
"Could not pack extended attributes");
ret = ARCHIVE_WARN;
goto cleanup;
}
- tempfd = open(tempfile, O_RDONLY);
- if (tempfd < 0) {
- archive_set_error(&a->archive, errno,
- "Could not open extended attribute file");
- ret = ARCHIVE_WARN;
- goto cleanup;
- }
if (fstat(tempfd, &copyfile_stat)) {
archive_set_error(&a->archive, errno,
"Could not check size of extended attributes");
@@ -363,10 +373,12 @@ setup_mac_metadata(struct archive_read_disk *a,
archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
cleanup:
- if (tempfd >= 0)
+ if (tempfd >= 0) {
close(tempfd);
- if (tempfile != NULL)
- unlink(tempfile);
+ unlink(tempfile.s);
+ }
+ archive_string_free(&tempfile);
+ free(buff);
return (ret);
}
@@ -387,16 +399,19 @@ setup_mac_metadata(struct archive_read_disk *a,
#endif
-#ifdef HAVE_POSIX_ACL
-static void setup_acl_posix1e(struct archive_read_disk *a,
+#if defined(HAVE_POSIX_ACL) && defined(ACL_TYPE_NFS4)
+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;
acl_t acl;
+#if HAVE_ACL_IS_TRIVIAL_NP
+ int r;
+#endif
accpath = archive_entry_sourcepath(entry);
if (accpath == NULL)
@@ -404,15 +419,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. */
@@ -431,7 +464,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);
}
@@ -440,7 +473,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);
}
@@ -449,68 +482,180 @@ 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:
+ 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. */
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
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,
+setup_acls(struct archive_read_disk *a,
struct archive_entry *entry, int *fd)
{
(void)a; /* UNUSED */
@@ -901,16 +1046,19 @@ setup_sparse(struct archive_read_disk *a,
path = archive_entry_pathname(entry);
if (a->tree != NULL)
*fd = a->open_on_current_dir(a->tree, path,
- O_RDONLY | O_NONBLOCK);
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
else
- *fd = open(path, O_RDONLY | O_NONBLOCK);
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (*fd < 0) {
archive_set_error(&a->archive, errno,
"Can't open `%s'", path);
return (ARCHIVE_FAILED);
}
+ __archive_ensure_cloexec_flag(*fd);
}
+ /* Initialize buffer to avoid the error valgrind complains about. */
+ memset(buff, 0, sizeof(buff));
count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
fm = (struct fiemap *)buff;
fm->fm_start = 0;
@@ -1012,12 +1160,13 @@ setup_sparse(struct archive_read_disk *a,
if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
return (ARCHIVE_OK);
- *fd = open(path, O_RDONLY | O_NONBLOCK);
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (*fd < 0) {
archive_set_error(&a->archive, errno,
"Can't open `%s'", path);
return (ARCHIVE_FAILED);
}
+ __archive_ensure_cloexec_flag(*fd);
initial_off = 0;
}
OpenPOWER on IntegriCloud