diff options
Diffstat (limited to 'libarchive/archive_read_disk_entry_from_file.c')
-rw-r--r-- | libarchive/archive_read_disk_entry_from_file.c | 293 |
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, ©file_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; } |