diff options
author | Tim Kientzle <kientzle@FreeBSD.org> | 2004-04-05 21:12:29 +0000 |
---|---|---|
committer | Tim Kientzle <kientzle@FreeBSD.org> | 2004-04-05 21:12:29 +0000 |
commit | 71b44796d9533309abbe22e7b26d3ee5bb3d0251 (patch) | |
tree | 65c99eaf5f048beaa6a7d9973eb8410048b1f15d /lib | |
parent | 7f8a436ff29ebeb1ce2ae2434add4505d5e7e2ca (diff) | |
download | src-71b44796d9533309abbe22e7b26d3ee5bb3d0251.tar.gz src-71b44796d9533309abbe22e7b26d3ee5bb3d0251.zip |
Overhauled ACL support. This makes us compatible
with 'star' ACL handling, though there's still a
bit more work needed in this area.
Added 'write_open_fd' and 'read_open_fd' to simplify, e.g.,
tar's u and r modes. Eliminated old 'write_open_file_position'
as a bad idea. (It required closing/reopening files to
do updates, which led to unpleasant implications.)
Various other minor fixes, API tweaks, etc.
Notes
Notes:
svn path=/head/; revision=127912
Diffstat (limited to 'lib')
21 files changed, 1239 insertions, 194 deletions
diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index f1ff70e5b92e..c5d625bb74e6 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -1,13 +1,13 @@ # Makefile for libarchive. # # $FreeBSD$ - +DEBUG_FLAGS=-g LIB= archive SHLIB_MAJOR= 1 # I'm not yet ready for a shared version of this library, as -# there are still a couple of API changes still in the works. -NOSHLIBS= 1 +# there are a couple of API changes still in the works. +NOPIC= 1 SRCS= archive_check_magic.c \ archive_entry.c \ @@ -15,6 +15,7 @@ SRCS= archive_check_magic.c \ archive_read_data_into_buffer.c \ archive_read_data_into_fd.c \ archive_read_extract.c \ + archive_read_open_fd.c \ archive_read_open_file.c \ archive_read_support_compression_all.c \ archive_read_support_compression_bzip2.c \ @@ -28,6 +29,7 @@ SRCS= archive_check_magic.c \ archive_string_sprintf.c \ archive_util.c \ archive_write.c \ + archive_write_open_fd.c \ archive_write_open_file.c \ archive_write_set_compression_bzip2.c \ archive_write_set_compression_gzip.c \ @@ -80,8 +82,8 @@ MLINKS += archive_read.3 archive_read_finish.3 MLINKS += archive_read.3 archive_read_new.3 MLINKS += archive_read.3 archive_read_next_header.3 MLINKS += archive_read.3 archive_read_open.3 +MLINKS += archive_read.3 archive_read_open_fd.3 MLINKS += archive_read.3 archive_read_open_file.3 -MLINKS += archive_read.3 archive_read_open_tar.3 MLINKS += archive_read.3 archive_read_set_bytes_per_block.3 MLINKS += archive_read.3 archive_read_support_compression_all.3 MLINKS += archive_read.3 archive_read_support_compression_bzip2.3 @@ -102,6 +104,7 @@ MLINKS += archive_write.3 archive_write_finish.3 MLINKS += archive_write.3 archive_write_header.3 MLINKS += archive_write.3 archive_write_new.3 MLINKS += archive_write.3 archive_write_open.3 +MLINKS += archive_write.3 archive_write_open_fd.3 MLINKS += archive_write.3 archive_write_open_file.3 MLINKS += archive_write.3 archive_write_prepare.3 MLINKS += archive_write.3 archive_write_set_bytes_per_block.3 @@ -120,7 +123,7 @@ DEBUG_FLAGS+= -DDEBUG -g CFLAGS+= -DHAVE_DMALLOC -I/usr/local/include LDFLAGS+= -L/usr/local/lib -ldmalloc .endif -CFLAGS+= -O3 +#CFLAGS+= -O3 WARNS?= 6 diff --git a/lib/libarchive/Makefile.freebsd b/lib/libarchive/Makefile.freebsd index f1ff70e5b92e..c5d625bb74e6 100644 --- a/lib/libarchive/Makefile.freebsd +++ b/lib/libarchive/Makefile.freebsd @@ -1,13 +1,13 @@ # Makefile for libarchive. # # $FreeBSD$ - +DEBUG_FLAGS=-g LIB= archive SHLIB_MAJOR= 1 # I'm not yet ready for a shared version of this library, as -# there are still a couple of API changes still in the works. -NOSHLIBS= 1 +# there are a couple of API changes still in the works. +NOPIC= 1 SRCS= archive_check_magic.c \ archive_entry.c \ @@ -15,6 +15,7 @@ SRCS= archive_check_magic.c \ archive_read_data_into_buffer.c \ archive_read_data_into_fd.c \ archive_read_extract.c \ + archive_read_open_fd.c \ archive_read_open_file.c \ archive_read_support_compression_all.c \ archive_read_support_compression_bzip2.c \ @@ -28,6 +29,7 @@ SRCS= archive_check_magic.c \ archive_string_sprintf.c \ archive_util.c \ archive_write.c \ + archive_write_open_fd.c \ archive_write_open_file.c \ archive_write_set_compression_bzip2.c \ archive_write_set_compression_gzip.c \ @@ -80,8 +82,8 @@ MLINKS += archive_read.3 archive_read_finish.3 MLINKS += archive_read.3 archive_read_new.3 MLINKS += archive_read.3 archive_read_next_header.3 MLINKS += archive_read.3 archive_read_open.3 +MLINKS += archive_read.3 archive_read_open_fd.3 MLINKS += archive_read.3 archive_read_open_file.3 -MLINKS += archive_read.3 archive_read_open_tar.3 MLINKS += archive_read.3 archive_read_set_bytes_per_block.3 MLINKS += archive_read.3 archive_read_support_compression_all.3 MLINKS += archive_read.3 archive_read_support_compression_bzip2.3 @@ -102,6 +104,7 @@ MLINKS += archive_write.3 archive_write_finish.3 MLINKS += archive_write.3 archive_write_header.3 MLINKS += archive_write.3 archive_write_new.3 MLINKS += archive_write.3 archive_write_open.3 +MLINKS += archive_write.3 archive_write_open_fd.3 MLINKS += archive_write.3 archive_write_open_file.3 MLINKS += archive_write.3 archive_write_prepare.3 MLINKS += archive_write.3 archive_write_set_bytes_per_block.3 @@ -120,7 +123,7 @@ DEBUG_FLAGS+= -DDEBUG -g CFLAGS+= -DHAVE_DMALLOC -I/usr/local/include LDFLAGS+= -L/usr/local/lib -ldmalloc .endif -CFLAGS+= -O3 +#CFLAGS+= -O3 WARNS?= 6 diff --git a/lib/libarchive/archive.h b/lib/libarchive/archive.h index 18a8bdb5e5fc..1ac0b2b12071 100644 --- a/lib/libarchive/archive.h +++ b/lib/libarchive/archive.h @@ -149,6 +149,8 @@ int archive_read_open(struct archive *, void *_client_data, */ int archive_read_open_file(struct archive *, const char *_file, size_t _block_size); +int archive_read_open_fd(struct archive *, int _fd, + size_t _block_size); /* Parses and returns next entry header. */ int archive_read_next_header(struct archive *, @@ -239,10 +241,8 @@ int archive_write_set_format_ustar(struct archive *); int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); +int archive_write_open_fd(struct archive *, int _fd); int archive_write_open_file(struct archive *, const char *_file); -int archive_write_open_file_position(struct archive *, - const char *_filename, int64_t offset); -int archive_write_open_tar(struct archive *, const char *_file); /* * Note that the library will truncate writes beyond the size provided diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in index 18a8bdb5e5fc..1ac0b2b12071 100644 --- a/lib/libarchive/archive.h.in +++ b/lib/libarchive/archive.h.in @@ -149,6 +149,8 @@ int archive_read_open(struct archive *, void *_client_data, */ int archive_read_open_file(struct archive *, const char *_file, size_t _block_size); +int archive_read_open_fd(struct archive *, int _fd, + size_t _block_size); /* Parses and returns next entry header. */ int archive_read_next_header(struct archive *, @@ -239,10 +241,8 @@ int archive_write_set_format_ustar(struct archive *); int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); +int archive_write_open_fd(struct archive *, int _fd); int archive_write_open_file(struct archive *, const char *_file); -int archive_write_open_file_position(struct archive *, - const char *_filename, int64_t offset); -int archive_write_open_tar(struct archive *, const char *_file); /* * Note that the library will truncate writes beyond the size provided diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index b9a86c1c090c..80e3da1c5afc 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -32,12 +32,18 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_DMALLOC #include <dmalloc.h> #endif +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wchar.h> +#include "archive.h" #include "archive_entry.h" +#include "archive_private.h" + +#undef max +#define max(a, b) ((a)>(b)?(a):(b)) /* * Handle wide character (i.e., Unicode) and non-wide character @@ -52,20 +58,44 @@ struct aes { wchar_t *aes_wcs_alloc; }; -void aes_clean(struct aes *); -void aes_copy(struct aes *dest, struct aes *src); -const char * aes_get_mbs(struct aes *); -const wchar_t * aes_get_wcs(struct aes *); -void aes_set_mbs(struct aes *, const char *mbs); -void aes_set_wcs(struct aes *, const wchar_t *wcs); -void aes_copy_wcs(struct aes *, const wchar_t *wcs); +struct ae_acl { + struct ae_acl *next; + int type; /* E.g., access or default */ + int tag; /* E.g., user/group/other/mask */ + int permset; /* r/w/x bits */ + int id; /* uid/gid for user/group */ + struct aes name; /* uname/gname */ +}; + +static void aes_clean(struct aes *); +static void aes_copy(struct aes *dest, struct aes *src); +static const char * aes_get_mbs(struct aes *); +static const wchar_t * aes_get_wcs(struct aes *); +static void aes_set_mbs(struct aes *, const char *mbs); +static void aes_copy_mbs(struct aes *, const char *mbs); +/* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */ +static void aes_copy_wcs(struct aes *, const wchar_t *wcs); + +static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, + const wchar_t *wname, int perm, int id); +static void append_id_w(wchar_t **wp, int id); + +static int acl_special(struct archive_entry *entry, + int type, int permset, int tag); +static struct ae_acl *acl_new_entry(struct archive_entry *entry, + int type, int permset, int tag, int id); +static void next_field_w(const wchar_t **wp, const wchar_t **start, + const wchar_t **end, wchar_t *sep); +static int prefix_w(const wchar_t *start, const wchar_t *end, + const wchar_t *test); + /* * Description of an archive entry. * * Basically, this is a "struct stat" with a few text fields added in. * - * TODO: Add "comment", "charset", "acl", and possibly other entries + * TODO: Add "comment", "charset", and possibly other entries * that are supported by "pax interchange" format. However, GNU, ustar, * cpio, and other variants don't support these features, so they're not an * excruciatingly high priority right now. @@ -95,14 +125,17 @@ struct archive_entry { /* * Use aes here so that we get transparent mbs<->wcs conversions. */ - struct aes ae_acl; /* ACL text */ - struct aes ae_acl_default; /* default ACL */ struct aes ae_fflags; /* Text fflags per fflagstostr(3) */ struct aes ae_gname; /* Name of owning group */ struct aes ae_hardlink; /* Name of target for hardlink */ struct aes ae_pathname; /* Name of entry */ struct aes ae_symlink; /* symlink contents */ struct aes ae_uname; /* Name of owner */ + + struct ae_acl *acl_head; + struct ae_acl *acl_p; + int acl_state; /* See acl_next for details. */ + wchar_t *acl_text_w; }; void @@ -189,6 +222,24 @@ aes_set_mbs(struct aes *aes, const char *mbs) } void +aes_copy_mbs(struct aes *aes, const char *mbs) +{ + if (aes->aes_mbs_alloc) { + free(aes->aes_mbs_alloc); + aes->aes_mbs_alloc = NULL; + } + if (aes->aes_wcs_alloc) { + free(aes->aes_wcs_alloc); + aes->aes_wcs_alloc = NULL; + } + aes->aes_mbs_alloc = malloc((strlen(mbs) + 1) * sizeof(char)); + strcpy(aes->aes_mbs_alloc, mbs); + aes->aes_mbs = aes->aes_mbs_alloc; + aes->aes_wcs = NULL; +} + +#if 0 +void aes_set_wcs(struct aes *aes, const wchar_t *wcs) { if (aes->aes_mbs_alloc) { @@ -202,6 +253,7 @@ aes_set_wcs(struct aes *aes, const wchar_t *wcs) aes->aes_mbs = NULL; aes->aes_wcs = wcs; } +#endif void aes_copy_wcs(struct aes *aes, const wchar_t *wcs) @@ -223,14 +275,13 @@ aes_copy_wcs(struct aes *aes, const wchar_t *wcs) struct archive_entry * archive_entry_clear(struct archive_entry *entry) { - aes_clean(&entry->ae_acl); - aes_clean(&entry->ae_acl_default); aes_clean(&entry->ae_fflags); aes_clean(&entry->ae_gname); aes_clean(&entry->ae_hardlink); aes_clean(&entry->ae_pathname); aes_clean(&entry->ae_symlink); aes_clean(&entry->ae_uname); + archive_entry_acl_clear(entry); memset(entry, 0, sizeof(*entry)); entry->ae_tartype = -1; return entry; @@ -243,11 +294,12 @@ archive_entry_clone(struct archive_entry *entry) /* Allocate new structure and copy over all of the fields. */ entry2 = malloc(sizeof(*entry2)); + if(entry2 == NULL) + return (NULL); + memset(entry2, 0, sizeof(*entry2)); entry2->ae_stat = entry->ae_stat; entry2->ae_tartype = entry->ae_tartype; - aes_copy(&entry2->ae_acl ,&entry->ae_acl); - aes_copy(&entry2->ae_acl_default ,&entry->ae_acl_default); aes_copy(&entry2->ae_fflags ,&entry->ae_fflags); aes_copy(&entry2->ae_gname ,&entry->ae_gname); aes_copy(&entry2->ae_hardlink ,&entry->ae_hardlink); @@ -282,19 +334,6 @@ archive_entry_new(void) * Functions for reading fields from an archive_entry. */ -const char * -archive_entry_acl(struct archive_entry *entry) -{ - return (aes_get_mbs(&entry->ae_acl)); -} - - -const char * -archive_entry_acl_default(struct archive_entry *entry) -{ - return (aes_get_mbs(&entry->ae_acl_default)); -} - dev_t archive_entry_devmajor(struct archive_entry *entry) { @@ -332,6 +371,20 @@ archive_entry_mode(struct archive_entry *entry) return (entry->ae_stat.st_mode); } + +time_t +archive_entry_mtime(struct archive_entry *entry) +{ + return (entry->ae_stat.st_mtime); +} + + +long +archive_entry_mtime_nsec(struct archive_entry *entry) +{ + return (entry->ae_stat.st_mtimespec.tv_nsec); +} + const char * archive_entry_pathname(struct archive_entry *entry) { @@ -389,30 +442,6 @@ archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st) } void -archive_entry_set_acl(struct archive_entry *entry, const char *acl) -{ - aes_set_mbs(&entry->ae_acl, acl); -} - -void -archive_entry_copy_acl_w(struct archive_entry *entry, const wchar_t *acl) -{ - aes_copy_wcs(&entry->ae_acl, acl); -} - -void -archive_entry_set_acl_default(struct archive_entry *entry, const char *acl) -{ - aes_set_mbs(&entry->ae_acl_default, acl); -} - -void -archive_entry_copy_acl_default_w(struct archive_entry *entry, const wchar_t *acl) -{ - aes_copy_wcs(&entry->ae_acl_default, acl); -} - -void archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) { dev_t d; @@ -532,6 +561,622 @@ archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) aes_copy_wcs(&entry->ae_uname, name); } +/* + * ACL management. The following would, of course, be a lot simpler + * if: 1) the last draft of POSIX.1e were a really thorough and + * complete standard that addressed the needs of ACL archiving and 2) + * everyone followed it faithfully. Alas, neither is true, so the + * following is a lot more complex than might seem necessary to the + * uninitiated. + */ + +void +archive_entry_acl_clear(struct archive_entry *entry) +{ + struct ae_acl *ap; + + while (entry->acl_head != NULL) { + ap = entry->acl_head->next; + aes_clean(&entry->acl_head->name); + free(entry->acl_head); + entry->acl_head = ap; + } + if (entry->acl_text_w != NULL) { + free(entry->acl_text_w); + entry->acl_text_w = NULL; + } + entry->acl_p = NULL; + entry->acl_state = 0; /* Not counting. */ +} + +/* + * Add a single ACL entry to the internal list of ACL data. + */ +void +archive_entry_acl_add_entry(struct archive_entry *entry, + int type, int permset, int tag, int id, const char *name) +{ + struct ae_acl *ap; + + if (acl_special(entry, type, permset, tag) == 0) + return; + ap = acl_new_entry(entry, type, permset, tag, id); + if (ap == NULL) { + /* XXX Error XXX */ + return; + } + if (name != NULL && *name != '\0') + aes_copy_mbs(&ap->name, name); + else + aes_clean(&ap->name); +} + +/* + * As above, but with a wide-character name. + */ +void +archive_entry_acl_add_entry_w(struct archive_entry *entry, + int type, int permset, int tag, int id, const wchar_t *name) +{ + struct ae_acl *ap; + + if (acl_special(entry, type, permset, tag) == 0) + return; + ap = acl_new_entry(entry, type, permset, tag, id); + if (ap == NULL) { + /* XXX Error XXX */ + return; + } + if (name != NULL && *name != L'\0') + aes_copy_wcs(&ap->name, name); + else + aes_clean(&ap->name); +} + +/* + * If this ACL entry is part of the standard POSIX permissions set, + * store the permissions in the stat structure and return zero. + */ +static int +acl_special(struct archive_entry *entry, int type, int permset, int tag) +{ + if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { + switch (tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + entry->ae_stat.st_mode &= 0077; + entry->ae_stat.st_mode |= (permset & 7) << 6; + return (0); + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + entry->ae_stat.st_mode &= 0707; + entry->ae_stat.st_mode |= (permset & 7) << 3; + return (0); + case ARCHIVE_ENTRY_ACL_OTHER: + entry->ae_stat.st_mode &= 0770; + entry->ae_stat.st_mode |= permset & 7; + return (0); + } + } + return (1); +} + +/* + * Allocate and populate a new ACL entry with everything but the + * name. + */ +static struct ae_acl * +acl_new_entry(struct archive_entry *entry, + int type, int permset, int tag, int id) +{ + struct ae_acl *ap; + + if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS && + type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) + return (NULL); + if (entry->acl_text_w != NULL) { + free(entry->acl_text_w); + entry->acl_text_w = NULL; + } + + /* XXX TODO: More sanity-checks on the arguments XXX */ + + /* If there's a matching entry already in the list, overwrite it. */ + for (ap = entry->acl_head; ap != NULL; ap = ap->next) { + if (ap->type == type && ap->tag == tag && ap->id == id) { + ap->permset = permset; + return (ap); + } + } + + /* Add a new entry to the list. */ + ap = malloc(sizeof(*ap)); + memset(ap, 0, sizeof(*ap)); + ap->next = entry->acl_head; + entry->acl_head = ap; + ap->type = type; + ap->tag = tag; + ap->id = id; + ap->permset = permset; + return (ap); +} + +/* + * Return a count of entries matching "want_type". + */ +int +archive_entry_acl_count(struct archive_entry *entry, int want_type) +{ + int count; + struct ae_acl *ap; + + count = 0; + ap = entry->acl_head; + while (ap != NULL) { + if ((ap->type & want_type) != 0) + count++; + ap = ap->next; + } + + if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) + count += 3; + return (count); +} + +/* + * Prepare for reading entries from the ACL data. Returns a count + * of entries matching "want_type", or zero if there are no + * non-extended ACL entries of that type. + */ +int +archive_entry_acl_reset(struct archive_entry *entry, int want_type) +{ + int count, cutoff; + + count = archive_entry_acl_count(entry, want_type); + + /* + * If the only entries are the three standard ones, + * then don't return any ACL data. (In this case, + * client can just use chmod(2) to set permissions.) + */ + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) + cutoff = 3; + else + cutoff = 0; + + if (count > cutoff) + entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; + else + entry->acl_state = 0; + entry->acl_p = NULL; + return (count); +} + +/* + * Return the next ACL entry in the list. Fake entries for the + * standard permissions and include them in the returned list. + */ + +int +archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, + int *permset, int *tag, int *id, const char **name) +{ + *name = NULL; + *id = -1; + + /* + * The acl_state is either zero (no entries available), -1 + * (reading from list), or an entry type (retrieve that type + * from ae_stat.st_mode). + */ + + if (entry->acl_state == 0) + return (ARCHIVE_WARN); + + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 && + entry->acl_state > 0) { + *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + *tag = entry->acl_state; + switch (entry->acl_state) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + *permset = (entry->ae_stat.st_mode >> 6) & 7; + entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + *permset = (entry->ae_stat.st_mode >> 3) & 7; + entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER; + break; + case ARCHIVE_ENTRY_ACL_OTHER: + *permset = entry->ae_stat.st_mode & 7; + entry->acl_state = -1; + entry->acl_p = entry->acl_head; + break; + } + return (ARCHIVE_OK); + } + + while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0) + entry->acl_p = entry->acl_p->next; + if (entry->acl_p == NULL) { + entry->acl_state = 0; + return (ARCHIVE_WARN); + } + *type = entry->acl_p->type; + *permset = entry->acl_p->permset; + *tag = entry->acl_p->tag; + *id = entry->acl_p->id; + *name = aes_get_mbs(&entry->acl_p->name); + entry->acl_p = entry->acl_p->next; + return (ARCHIVE_OK); +} + +/* + * Generate a text version of the ACL. The format here varies + * from POSIX.1e in a couple of useful ways: + * + * * An additional colon-delimited field holds the numeric uid + * or gid. For proper archiving, it is essential to have both + * the uname/gname and the uid/gid. + * + * * You can request a single text holding both access and default + * entries. In this case, each default entry is prefixed with + * "default:". + */ +const wchar_t * +__archive_entry_acl_text_w(struct archive_entry *entry, int type) +{ + int count; + int length; + const wchar_t *wname; + struct ae_acl *ap; + wchar_t *wp; + + if (entry->acl_text_w != NULL) { + free (entry->acl_text_w); + entry->acl_text_w = NULL; + } + + count = 0; + length = 0; + ap = entry->acl_head; + while (ap != NULL) { + if ((ap->type & type) != 0) { + count++; + if (type & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS | + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) + length += 8; /* "default:" */ + length += 5; /* tag name */ + length += 1; /* colon */ + wname = aes_get_wcs(&ap->name); + if (wname != NULL) + length += wcslen(wname); + length ++; /* colon */ + length += 3; /* rwx */ + length += 1; /* colon */ + length += max(sizeof(uid_t),sizeof(gid_t)) * 3 + 1; + length ++; /* newline */ + } + ap = ap->next; + } + + if (count > 0 && ((type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { + length += 10; /* "user::rwx\n" */ + length += 11; /* "group::rwx\n" */ + length += 11; /* "other::rwx\n" */ + } + + if (count == 0) + return (NULL); + + /* Now, allocate the string and actually populate it. */ + wp = entry->acl_text_w = malloc(length * sizeof(wchar_t)); + count = 0; + if ((type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, + entry->ae_stat.st_mode & 0700, -1); + *wp++ = ','; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, + entry->ae_stat.st_mode & 0070, -1); + *wp++ = ','; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, + entry->ae_stat.st_mode & 0007, -1); + count += 3; + + ap = entry->acl_head; + while (ap != NULL) { + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + wname = aes_get_wcs(&ap->name); + *wp++ = ','; + append_entry_w(&wp, NULL, ap->tag, wname, + ap->permset, ap->id); + count++; + } + ap = ap->next; + } + } + + if ((type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { + ap = entry->acl_head; + while (ap != NULL) { + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { + wname = aes_get_wcs(&ap->name); + if (count > 0) + *wp++ = ','; + append_entry_w(&wp, L"default:", ap->tag, + wname, ap->permset, ap->id); + } + ap = ap->next; + } + } + + return (entry->acl_text_w); +} + +static void +append_id_w(wchar_t **wp, int id) +{ + if (id > 9) + append_id_w(wp, id / 10); + *(*wp)++ = L"0123456789"[id % 10]; +} + +static void +append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, + const wchar_t *wname, int perm, int id) +{ + if (prefix != NULL) { + wcscpy(*wp, prefix); + *wp += wcslen(*wp); + } + switch (tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + wname = NULL; + id = -1; + /* FALL THROUGH */ + case ARCHIVE_ENTRY_ACL_USER: + wcscpy(*wp, L"user"); + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + wname = NULL; + id = -1; + /* FALL THROUGH */ + case ARCHIVE_ENTRY_ACL_GROUP: + wcscpy(*wp, L"group"); + break; + case ARCHIVE_ENTRY_ACL_MASK: + wcscpy(*wp, L"mask"); + wname = NULL; + id = -1; + break; + case ARCHIVE_ENTRY_ACL_OTHER: + wcscpy(*wp, L"other"); + wname = NULL; + id = -1; + break; + } + *wp += wcslen(*wp); + *(*wp)++ = L':'; + if (wname != NULL) + wcscpy(*wp, wname); + *wp += wcslen(*wp); + *(*wp)++ = L':'; + *(*wp)++ = (perm & 0444) ? L'r' : L'-'; + *(*wp)++ = (perm & 0222) ? L'w' : L'-'; + *(*wp)++ = (perm & 0111) ? L'x' : L'-'; + if (id != -1) { + *(*wp)++ = L':'; + append_id_w(wp, id); + } + **wp = L'\0'; +} + +/* + * Parse a textual ACL. This automatically recognizes and supports + * extensions described above. The 'type' argument is used to + * indicate the type that should be used for any entries not + * explicitly marked as "default:". + */ +int +__archive_entry_acl_parse_w(struct archive_entry *entry, + const wchar_t *text, int default_type) +{ + int type, tag, permset, id; + const wchar_t *start, *end; + const wchar_t *name_start, *name_end; + wchar_t sep; + wchar_t *namebuff; + int namebuff_length; + + name_start = name_end = NULL; + namebuff = NULL; + namebuff_length = 0; + + archive_entry_acl_clear(entry); + + while (text != NULL && *text != L'\0') { + next_field_w(&text, &start, &end, &sep); + if (sep != L':') + goto fail; + + if (prefix_w(start, end, L"default")) { + type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + next_field_w(&text, &start, &end, &sep); + if (sep != L':') + goto fail; + } else + type = default_type; + + if (prefix_w(start, end, L"user")) { + next_field_w(&text, &start, &end, &sep); + if (sep != L':') + goto fail; + if (end > start) { + tag = ARCHIVE_ENTRY_ACL_USER; + name_start = start; + name_end = end; + } else + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (prefix_w(start, end, L"group")) { + next_field_w(&text, &start, &end, &sep); + if (sep != L':') + goto fail; + if (end > start) { + tag = ARCHIVE_ENTRY_ACL_GROUP; + name_start = start; + name_end = end; + } else + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (prefix_w(start, end, L"other")) { + next_field_w(&text, &start, &end, &sep); + if (sep != L':') + goto fail; + if (end > start) + goto fail; + tag = ARCHIVE_ENTRY_ACL_OTHER; + } else if (prefix_w(start, end, L"mask")) { + next_field_w(&text, &start, &end, &sep); + if (sep != L':') + goto fail; + if (end > start) + goto fail; + tag = ARCHIVE_ENTRY_ACL_MASK; + } else + goto fail; + + next_field_w(&text, &start, &end, &sep); + permset = 0; + while (start < end) { + switch (*start++) { + case 'r': case 'R': + permset |= ARCHIVE_ENTRY_ACL_READ; + break; + case 'w': case 'W': + permset |= ARCHIVE_ENTRY_ACL_WRITE; + break; + case 'x': case 'X': + permset |= ARCHIVE_ENTRY_ACL_EXECUTE; + break; + case '-': + break; + default: + goto fail; + } + } + + /* + * Support star-compatible numeric UID/GID extension. + * This extension adds a ":" followed by the numeric + * ID so that "group:groupname:rwx", for example, + * becomes "group:groupname:rwx:999", where 999 is the + * numeric GID. This extension makes it possible, for + * example, to correctly restore ACLs on a system that + * might have a damaged passwd file or be disconnected + * from a central NIS server. This extension is compatible + * with POSIX.1e draft 17. + */ + if (sep == L':' && (tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP)) { + next_field_w(&text, &start, &end, &sep); + + id = 0; + while (start < end && *start >= '0' && *start <= '9') { + if (id > (INT_MAX / 10)) + id = INT_MAX; + else { + id *= 10; + id += *start - '0'; + start++; + } + } + } else + id = -1; /* No id specified. */ + + /* Skip any additional entries. */ + while (sep == L':') { + next_field_w(&text, &start, &end, &sep); + } + + /* Add entry to the internal list. */ + if (name_end == name_start) { + archive_entry_acl_add_entry_w(entry, type, permset, + tag, id, NULL); + } else { + if (namebuff_length <= name_end - name_start) { + if (namebuff != NULL) + free(namebuff); + namebuff_length = name_end - name_start + 256; + namebuff = + malloc(namebuff_length * sizeof(wchar_t)); + } + wmemcpy(namebuff, start, end-start); + archive_entry_acl_add_entry_w(entry, type, + permset, tag, id, namebuff); + } + } + return (ARCHIVE_OK); + +fail: + fprintf(stderr, "ACL error\n"); + if (namebuff != NULL) + free(namebuff); + return (ARCHIVE_WARN); +} + +/* + * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated + * to point to just after the separator. *start points to the first + * character of the matched text and *end just after the last + * character of the matched identifier. In particular *end - *start + * is the length of the field body, not including leading or trailing + * whitespace. + */ +static void +next_field_w(const wchar_t **wp, const wchar_t **start, + const wchar_t **end, wchar_t *sep) +{ + /* Skip leading whitespace to find start of field. */ + while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { + (*wp)++; + } + *start = *wp; + + /* Scan for the separator. */ + while (**wp != L'\0' && **wp != L',' && **wp != L':' && + **wp != L'\n') { + (*wp)++; + } + *sep = **wp; + + /* Trim trailing whitespace to locate end of field. */ + *end = *wp - 1; + while (**end == L' ' || **end == L'\t' || **end == L'\n') { + (*end)--; + } + (*end)++; + + /* Adjust scanner location. */ + if (**wp != L'\0') + (*wp)++; +} + +static int +prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) +{ + if (start == end) + return (0); + + if (*start++ != *test++) + return (0); + + while (start < end && *start++ == *test++) + ; + + if (start < end) + return (0); + + return (1); +} + + #if TEST int main(int argc, char **argv) diff --git a/lib/libarchive/archive_entry.h b/lib/libarchive/archive_entry.h index db0241822bdf..0471f011fe1b 100644 --- a/lib/libarchive/archive_entry.h +++ b/lib/libarchive/archive_entry.h @@ -66,14 +66,14 @@ struct archive_entry *archive_entry_new(void); * Retrieve fields from an archive_entry. */ -const char *archive_entry_acl(struct archive_entry *); -const char *archive_entry_acl_default(struct archive_entry *); dev_t archive_entry_devmajor(struct archive_entry *); dev_t archive_entry_devminor(struct archive_entry *); const char *archive_entry_fflags(struct archive_entry *); const char *archive_entry_gname(struct archive_entry *); const char *archive_entry_hardlink(struct archive_entry *); mode_t archive_entry_mode(struct archive_entry *); +time_t archive_entry_mtime(struct archive_entry *); +long archive_entry_mtime_nsec(struct archive_entry *); const char *archive_entry_pathname(struct archive_entry *); const wchar_t *archive_entry_pathname_w(struct archive_entry *); int64_t archive_entry_size(struct archive_entry *); @@ -90,10 +90,6 @@ const char *archive_entry_uname(struct archive_entry *); */ void archive_entry_copy_stat(struct archive_entry *, const struct stat *); -void archive_entry_set_acl(struct archive_entry *, const char *); -void archive_entry_copy_acl_w(struct archive_entry *, const wchar_t *); -void archive_entry_set_acl_default(struct archive_entry *, const char *); -void archive_entry_copy_acl_default_w(struct archive_entry *, const wchar_t *); void archive_entry_set_fflags(struct archive_entry *, const char *); void archive_entry_copy_fflags_w(struct archive_entry *, const wchar_t *); void archive_entry_set_devmajor(struct archive_entry *, dev_t); @@ -114,4 +110,66 @@ void archive_entry_set_uid(struct archive_entry *, uid_t); void archive_entry_set_uname(struct archive_entry *, const char *); void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); +/* + * ACL routines. This used to simply store and return text-format ACL + * strings, but that proved insufficient. The intent here is to allow + * libarchive internals to fetch/store text-format strings, but + * clients use the more involved interface that allows them control + * over uid/uname/gid/gname lookups. + */ + +/* + * Permission bits mimic POSIX.1e. Note that I've not followed POSIX.1e's + * "permset"/"perm" abstract type nonsense. A permset is just a simple + * bitmap, following long-standing Unix tradition. + */ +#define ARCHIVE_ENTRY_ACL_EXECUTE 1 +#define ARCHIVE_ENTRY_ACL_WRITE 2 +#define ARCHIVE_ENTRY_ACL_READ 4 + +/* We need to be able to specify either or both of these. */ +#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 256 +#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 512 + +/* Tag values mimic POSIX.1e */ +#define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */ +#define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */ +#define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */ +#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */ +#define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access. */ +#define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public. */ + + +/* + * Set the ACL by clearing it and adding entries one at a time. + * Unlike the POSIX.1e ACL routines, you must specify the type + * (access/default) for each entry. Internally, the ACL data is just + * a soup of entries. API calls here allow you to retrieve just the + * entries of interest. This design (which goes against the spirit of + * POSIX.1e) is useful for handling archive formats that combine + * default and access information in a single ACL list. + */ +void archive_entry_acl_clear(struct archive_entry *); +void archive_entry_acl_add_entry(struct archive_entry *, + int type, int permset, int tag, int qual, const char *name); +void archive_entry_acl_add_entry_w(struct archive_entry *, + int type, int permset, int tag, int qual, const wchar_t *name); + +/* + * To retrieve the ACL, first "reset", then repeatedly ask for the + * "next" entry. The want_type parameter allows you to request only + * access entries or only default entries. + */ +int archive_entry_acl_reset(struct archive_entry *, int want_type); +int archive_entry_acl_next(struct archive_entry *, int want_type, + int *type, int *permset, int *tag, int *qual, const char **name); +int archive_entry_acl_next_w(struct archive_entry *, int want_type, + int *type, int *permset, int *tag, int *qual, + const wchar_t **name); + +/* Return a count of entries matching 'want_type' */ +int archive_entry_acl_count(struct archive_entry *, int want_type); + + + #endif /* !ARCHIVE_ENTRY_H_INCLUDED */ diff --git a/lib/libarchive/archive_private.h b/lib/libarchive/archive_private.h index 24e52f41bf6a..21655e7a2614 100644 --- a/lib/libarchive/archive_private.h +++ b/lib/libarchive/archive_private.h @@ -29,6 +29,8 @@ #ifndef ARCHIVE_PRIVATE_H_INCLUDED #define ARCHIVE_PRIVATE_H_INCLUDED +#include <wchar.h> + #include "archive.h" #include "archive_string.h" @@ -229,4 +231,18 @@ int __archive_read_register_compression(struct archive *a, #define err_combine(a,b) ((a) < (b) ? (a) : (b)) + +/* + * Private ACL handling: parse and generate ACL strings. + * These are private because they handle a lot of very weird formats + * that clients should not be messing with. Clients should only + * deal with their platform-native formats. Because of the need to + * support many formats cleanly, new arguments are likely to get added + * on a regular basis. Clients who try to use this interface are + * likely to be surprised when it changes. + */ +int __archive_entry_acl_parse_w(struct archive_entry *, + const wchar_t *, int type); +const wchar_t *__archive_entry_acl_text_w(struct archive_entry *, int type); + #endif diff --git a/lib/libarchive/archive_read.3 b/lib/libarchive/archive_read.3 index c5bd04e716c8..28131cd4cb07 100644 --- a/lib/libarchive/archive_read.3 +++ b/lib/libarchive/archive_read.3 @@ -39,6 +39,7 @@ .Nm archive_read_support_format_cpio , .Nm archive_read_support_format_all , .Nm archive_read_open , +.Nm archive_read_open_fd , .Nm archive_read_open_file , .Nm archive_read_next_header , .Nm archive_read_data , @@ -73,6 +74,8 @@ .Ft int .Fn archive_read_open "struct archive *" "void *client_data" "archive_read_archive_callback *" "archive_open_archive_callback *" "archive_close_archive_callback *" .Ft int +.Fn archive_read_open_fd "struct archive *" "int fd" +.Ft int .Fn archive_read_open_file "struct archive *" "const char *filename" .Ft int .Fn archive_read_next_header "struct archive *" "struct archive_entry **" @@ -133,6 +136,21 @@ This is the most generic version of this call, which accepts three callback functions. The library invokes these client-provided functions to obtain raw bytes from the archive. +Note: The API permits a decompression method to fork and invoke the +callbacks from another process. +Although none of the current decompression methods use this technique, +future decompression methods may utilize this technique. +If the decompressor forks, it will ensure that the open and close +callbacks are invoked within the same process as the read callback. +In particular, clients should not attempt to use shared variables to +communicate between the open/read/close callbacks and the mainline code. +.It Fn archive_read_open_fd +Like +.Fn archive_read_open , +except that it accepts a file descriptor rather than +a trio of function pointers. +Note that the file descriptor will not be automatically closed at +end-of-archive. .It Fn archive_read_open_file Like .Fn archive_read_open , diff --git a/lib/libarchive/archive_read.c b/lib/libarchive/archive_read.c index a6da35a60fd8..bb89eeb1d7ea 100644 --- a/lib/libarchive/archive_read.c +++ b/lib/libarchive/archive_read.c @@ -405,6 +405,9 @@ archive_read_data_skip(struct archive *a) void archive_read_finish(struct archive *a) { + int i; + int slots; + archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY); a->state = ARCHIVE_STATE_CLOSED; @@ -418,23 +421,21 @@ archive_read_finish(struct archive *a) if (a->compression_finish != NULL) (a->compression_finish)(a); - /*- - * Release allocated strings. - * - * TODO: Add a "cleanup" column to the "formats" array and - * use that to cleanup format-specific data. E.g., - * - * for (i=0; i< slots; i++) { - * if (a->formats[i].cleanup) - * (a->formats[i].cleanup)(a); - * } - */ + /* Cleanup format-specific data. */ + slots = sizeof(a->formats) / sizeof(a->formats[0]); + for (i = 0; i < slots; i++) { + a->pformat_data = &(a->formats[i].format_data); + if (a->formats[i].cleanup) + (a->formats[i].cleanup)(a); + } + /* Casting a pointer to int allows us to remove 'const.' */ free((void *)(uintptr_t)(const void *)a->nulls); if (a->extract_mkdirpath.s != NULL) free(a->extract_mkdirpath.s); if (a->entry) archive_entry_free(a->entry); + a->magic = 0; free(a); } diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index 80a4aa12e8a7..f1e38cf7377c 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -74,12 +74,14 @@ static int archive_read_extract_regular_open(struct archive *, const char *name, int mode, int flags); static int archive_read_extract_symbolic_link(struct archive *, struct archive_entry *, int); +static gid_t lookup_gid(struct archive *, const char *uname, gid_t); +static uid_t lookup_uid(struct archive *, const char *uname, uid_t); static int mkdirpath(struct archive *, const char *); static int mkdirpath_recursive(char *path); static int mksubdir(char *path); #ifdef HAVE_POSIX_ACL -static int set_acl(struct archive *a, const char *acl_text, - acl_type_t type, const char *pathname); +static int set_acl(struct archive *, struct archive_entry *, + acl_type_t, int archive_entry_acl_type); #endif static int set_acls(struct archive *, struct archive_entry *); static int set_extended_perm(struct archive *, struct archive_entry *, @@ -673,6 +675,9 @@ mksubdir(char *path) static int set_ownership(struct archive *a, struct archive_entry *entry, int flags) { + uid_t uid; + gid_t gid; + /* If UID/GID are already correct, return 0. */ /* TODO: Fix this; need to stat() to find on-disk GID <sigh> */ if (a->user_uid == archive_entry_stat(entry)->st_uid) @@ -682,21 +687,23 @@ set_ownership(struct archive *a, struct archive_entry *entry, int flags) if ((flags & ARCHIVE_EXTRACT_OWNER) == 0) return (ARCHIVE_WARN); + uid = lookup_uid(a, archive_entry_uname(entry), + archive_entry_stat(entry)->st_uid); + gid = lookup_gid(a, archive_entry_gname(entry), + archive_entry_stat(entry)->st_gid); + /* * Root can change owner/group; owner can change group; * otherwise, bail out now. */ - if ((a->user_uid != 0) - && (a->user_uid != archive_entry_stat(entry)->st_uid)) + if (a->user_uid != 0 && a->user_uid != uid) { + /* XXXX archive_set_error( XXXX ) ; XXX */ return (ARCHIVE_WARN); + } - if (lchown(archive_entry_pathname(entry), - archive_entry_stat(entry)->st_uid, - archive_entry_stat(entry)->st_gid)) { + if (lchown(archive_entry_pathname(entry), uid, gid)) { archive_set_error(a, errno, - "Can't set user=%d/group=%d for %s", - archive_entry_stat(entry)->st_uid, - archive_entry_stat(entry)->st_gid, + "Can't set user=%d/group=%d for %s", uid, gid, archive_entry_pathname(entry)); return (ARCHIVE_WARN); } @@ -817,64 +824,130 @@ set_fflags(struct archive *a, struct archive_entry *entry) return (ret); } +#ifndef HAVE_POSIX_ACL +/* Default empty function body to satisfy mainline code. */ +static int +set_acls(struct archive *a, struct archive_entry *entry) +{ + (void)a; + (void)entry; + return (ARCHIVE_OK); +} + +#else + /* * XXX TODO: What about ACL types other than ACCESS and DEFAULT? */ static int set_acls(struct archive *a, struct archive_entry *entry) { -#ifdef HAVE_POSIX_ACL - const char *acl_text; - const char *name; int ret; - ret = ARCHIVE_OK; - - name = archive_entry_pathname(entry); - acl_text = archive_entry_acl(entry); - ret = set_acl(a, acl_text, ACL_TYPE_ACCESS, name); - if (ret == ARCHIVE_OK) { - acl_text = archive_entry_acl_default(entry); - ret = set_acl(a, acl_text, ACL_TYPE_DEFAULT, name); - } - + ret = set_acl(a, entry, ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + if (ret != ARCHIVE_OK) + return (ret); + ret = set_acl(a, entry, ACL_TYPE_DEFAULT, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); return (ret); -#else - /* Default empty function body to satisfy mainline code. */ - (void)a; - (void)entry; - return (ARCHIVE_OK); -#endif } -#ifdef HAVE_POSIX_ACL + static int -set_acl(struct archive *a, const char *acl_text, acl_type_t type, - const char *name) +set_acl(struct archive *a, struct archive_entry *entry, acl_type_t acl_type, + int ae_requested_type) { acl_t acl; + acl_entry_t acl_entry; + acl_permset_t acl_permset; 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; + const char *name; ret = ARCHIVE_OK; + entries = archive_entry_acl_reset(entry, ae_requested_type); + if (entries == 0) + return (ARCHIVE_OK); + acl = acl_init(entries); + while (archive_entry_acl_next(entry, ae_requested_type, &ae_type, + &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { + acl_create_entry(&acl, &acl_entry); + + if (ae_tag == ARCHIVE_ENTRY_ACL_USER) { + acl_set_tag_type(acl_entry, ACL_USER); + ae_uid = lookup_uid(a, ae_name, ae_id); + acl_set_qualifier(acl_entry, &ae_uid); + } else if (ae_tag == ARCHIVE_ENTRY_ACL_GROUP) { + acl_set_tag_type(acl_entry, ACL_GROUP); + ae_gid = lookup_gid(a, ae_name, ae_id); + acl_set_qualifier(acl_entry, &ae_gid); + } else if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ) + acl_set_tag_type(acl_entry, ACL_USER_OBJ); + else if (ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) + acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); + else if (ae_tag == ARCHIVE_ENTRY_ACL_MASK) + acl_set_tag_type(acl_entry, ACL_MASK); + else if (ae_tag == ARCHIVE_ENTRY_ACL_OTHER) + acl_set_tag_type(acl_entry, ACL_OTHER); + + acl_get_permset(acl_entry, &acl_permset); + acl_clear_perms(acl_permset); + if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE) + acl_add_perm(acl_permset, ACL_EXECUTE); + if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE) + acl_add_perm(acl_permset, ACL_WRITE); + if (ae_permset & ARCHIVE_ENTRY_ACL_READ) + acl_add_perm(acl_permset, ACL_READ); + } - if (acl_text == NULL) - return (ret); - - acl = acl_from_text(acl_text); - if (acl == NULL) { - archive_set_error(a, errno, "Error parsing acl '%s'", - acl_text); + name = archive_entry_pathname(entry); + if (acl_set_file(name, acl_type, acl) != 0) { + archive_set_error(a, errno, "Failed to set acl"); ret = ARCHIVE_WARN; - } else { - if (acl_set_file(name, type, acl) != 0) { - archive_set_error(a, errno, - "Failed to set acl '%s' (type %d)", - acl_text, type); - ret = ARCHIVE_WARN; - } - acl_free(acl); } - + acl_free(acl); return (ret); } #endif + +/* + * XXX The following gid/uid lookups can be a performance bottleneck. + * Some form of caching would probably be very effective, though + * I have concerns about staleness. + */ +static gid_t +lookup_gid(struct archive *a, const char *gname, gid_t gid) +{ + struct group *grent; + + (void)a; /* UNUSED */ + + /* Look up gid from gname. */ + if (gname != NULL && *gname != '\0') { + grent = getgrnam(gname); + if (grent != NULL) + gid = grent->gr_gid; + } + return (gid); +} + +static uid_t +lookup_uid(struct archive *a, const char *uname, uid_t uid) +{ + struct passwd *pwent; + + (void)a; /* UNUSED */ + + /* Look up uid from uname. */ + if (uname != NULL && *uname != '\0') { + pwent = getpwnam(uname); + if (pwent != NULL) + uid = pwent->pw_uid; + } + return (uid); +} diff --git a/lib/libarchive/archive_read_open_fd.c b/lib/libarchive/archive_read_open_fd.c new file mode 100644 index 000000000000..13349165d75c --- /dev/null +++ b/lib/libarchive/archive_read_open_fd.c @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 2003-2004 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_DMALLOC +#include <dmalloc.h> +#endif +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "archive.h" + +struct read_fd_data { + int fd; + size_t block_size; + void *buffer; +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_read(struct archive *, void *, const void **buff); + +int +archive_read_open_fd(struct archive *a, int fd, size_t block_size) +{ + struct read_fd_data *mine; + + mine = malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->block_size = block_size; + mine->buffer = malloc(mine->block_size); + mine->fd = fd; + return (archive_read_open(a, mine, file_open, file_read, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + (void)client_data; /* UNUSED */ + (void)a; /* UNUSED */ + + return (ARCHIVE_OK); +} + +static ssize_t +file_read(struct archive *a, void *client_data, const void **buff) +{ + struct read_fd_data *mine = client_data; + + (void)a; /* UNUSED */ + *buff = mine->buffer; + return (read(mine->fd, mine->buffer, mine->block_size)); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct read_fd_data *mine = client_data; + + (void)a; /* UNUSED */ + free(mine); + return (ARCHIVE_OK); +} diff --git a/lib/libarchive/archive_read_open_file.c b/lib/libarchive/archive_read_open_file.c index a07ff2957078..772e1cc21d73 100644 --- a/lib/libarchive/archive_read_open_file.c +++ b/lib/libarchive/archive_read_open_file.c @@ -55,12 +55,19 @@ archive_read_open_file(struct archive *a, const char *filename, { struct read_file_data *mine; - /* XXX detect and report malloc failure XXX */ if (filename == NULL) { mine = malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } mine->filename[0] = 0; } else { mine = malloc(sizeof(*mine) + strlen(filename)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } strcpy(mine->filename, filename); } mine->block_size = block_size; diff --git a/lib/libarchive/archive_read_open_filename.c b/lib/libarchive/archive_read_open_filename.c index a07ff2957078..772e1cc21d73 100644 --- a/lib/libarchive/archive_read_open_filename.c +++ b/lib/libarchive/archive_read_open_filename.c @@ -55,12 +55,19 @@ archive_read_open_file(struct archive *a, const char *filename, { struct read_file_data *mine; - /* XXX detect and report malloc failure XXX */ if (filename == NULL) { mine = malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } mine->filename[0] = 0; } else { mine = malloc(sizeof(*mine) + strlen(filename)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } strcpy(mine->filename, filename); } mine->block_size = block_size; diff --git a/lib/libarchive/archive_read_support_compression_none.c b/lib/libarchive/archive_read_support_compression_none.c index 0704b4e4c192..12416b711076 100644 --- a/lib/libarchive/archive_read_support_compression_none.c +++ b/lib/libarchive/archive_read_support_compression_none.c @@ -241,6 +241,7 @@ archive_decompressor_none_read_consume(struct archive *a, size_t request) state->client_next += request; state->client_avail -= request; } + a->file_position += request; return (request); } diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index 47bfc55898e9..dda925f21dfa 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -831,9 +831,11 @@ pax_attribute(struct archive_entry *entry, struct stat *st, case 'S': /* We support some keys used by the "star" archiver */ if (wcscmp(key, L"SCHILY.acl.access")==0) - archive_entry_copy_acl_w(entry, value); + __archive_entry_acl_parse_w(entry, value, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); else if (wcscmp(key, L"SCHILY.acl.default")==0) - archive_entry_copy_acl_default_w(entry, value); + __archive_entry_acl_parse_w(entry, value, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); else if (wcscmp(key, L"SCHILY.devmajor")==0) st->st_rdev = makedev(tar_atol10(value, wcslen(value)), minor(st->st_dev)); diff --git a/lib/libarchive/archive_write.3 b/lib/libarchive/archive_write.3 index 2cebaab94bae..a4a135f7d17e 100644 --- a/lib/libarchive/archive_write.3 +++ b/lib/libarchive/archive_write.3 @@ -40,8 +40,8 @@ .Nm archive_write_set_compressor_gzip , .Nm archive_write_set_compressor_bzip2 , .Nm archive_write_open , +.Nm archive_write_open_fd , .Nm archive_write_open_file , -.Nm archive_write_open_tar , .Nm archive_write_prepare , .Nm archive_write_header , .Nm archive_write_data , @@ -74,9 +74,9 @@ .Ft int .Fn archive_write_open "struct archive *" "void *client_data" "archive_write_archive_callback *" "archive_open_archive_callback *" "archive_close_archive_callback *" .Ft int -.Fn archive_write_open_file "struct archive *" "const char *filename" +.Fn archive_write_open_fd "struct archive *" "int fd" .Ft int -.Fn archive_write_open_tar "struct archive *" "const char *archive_name" +.Fn archive_write_open_file "struct archive *" "const char *filename" .Ft int .Fn archive_write_header "struct archive *" .Ft int @@ -117,7 +117,7 @@ is applied only after the compression. The uncompressed data is always unpadded. The default is to pad the last block to the full block size (note that .Fn archive_write_open_file -affects this). +will set this based on the file type). Unlike the other .Dq set functions, this function can be called after the archive is opened. @@ -153,6 +153,16 @@ Freeze the settings, open the archive, and prepare for writing entries. This is the most generic form of this function, which accepts pointers to three callback functions which will be invoked by the library to write the constructed archive. +Note: Internally, the callbacks are invoked by the compression layer. +In order to support external compression programs, the compression +is permitted to fork and invoke the callbacks from a separate process. +In particular, clients should not assume that they can communicate +between the callbacks and the mainline code using shared variables. +Note that the standard gzip, bzip2, and "none" compression methods do not fork. +.It Fn archive_write_open_fd +A convenience form of +.Fn archive_write_open +that accepts a file descriptor. .It Fn archive_write_open_file A convenience form of .Fn archive_write_open @@ -172,14 +182,6 @@ You can override this by manually invoking .Fn archive_write_set_bytes_in_last_block either before or after calling .Fn archive_write_open . -.It Fn archive_write_open_tar -A convenience form of -.Fn archive_write_open -that accepts an archive name in the same formats accepted by -.Xr tar 1 . -In particular, a -.Pa - -argument indicates that the output should be written to standard output. .It Fn archive_write_header Build and write a header using the data in the provided .Tn struct archive_entry diff --git a/lib/libarchive/archive_write.c b/lib/libarchive/archive_write.c index eac741395b5e..239728a30618 100644 --- a/lib/libarchive/archive_write.c +++ b/lib/libarchive/archive_write.c @@ -170,6 +170,7 @@ archive_write_finish(struct archive *a) free((void *)(uintptr_t)(const void *)a->nulls); if (a->extract_mkdirpath.s != NULL) free(a->extract_mkdirpath.s); + a->magic = 0; free(a); } diff --git a/lib/libarchive/archive_write_open_fd.c b/lib/libarchive/archive_write_open_fd.c new file mode 100644 index 000000000000..33e7f4593994 --- /dev/null +++ b/lib/libarchive/archive_write_open_fd.c @@ -0,0 +1,121 @@ +/*- + * Copyright (c) 2003-2004 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#include <sys/stat.h> +#ifdef HAVE_DMALLOC +#include <dmalloc.h> +#endif +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "archive.h" +#include "archive_private.h" + +struct write_fd_data { + off_t offset; + int fd; +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_write(struct archive *, void *, void *buff, size_t); + +int +archive_write_open_fd(struct archive *a, int fd) +{ + struct write_fd_data *mine; + + mine = malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->fd = fd; + return (archive_write_open(a, mine, + file_open, file_write, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + struct write_fd_data *mine; + struct stat st; + + mine = client_data; + + /* + * If client hasn't explicitly set the last block handling, + * then set it here: If the output is a block or character + * device, pad the last block, otherwise leave it unpadded. + */ + if (mine->fd >= 0 && a->bytes_in_last_block < 0) { + /* Last block will be fully padded. */ + fstat(mine->fd, &st); + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || + S_ISFIFO(st.st_mode)) + archive_write_set_bytes_in_last_block(a, 0); + else + archive_write_set_bytes_in_last_block(a, 1); + } + + if (mine->fd == 1) { + if (a->bytes_in_last_block < 0) /* Still default? */ + /* Last block will be fully padded. */ + archive_write_set_bytes_in_last_block(a, 0); + } + + if (mine->fd < 0) { + archive_set_error(a, errno, "Failed to open"); + return (ARCHIVE_FATAL); + } + + return (ARCHIVE_OK); +} + +static ssize_t +file_write(struct archive *a, void *client_data, void *buff, size_t length) +{ + struct write_fd_data *mine; + + (void)a; /* UNUSED */ + mine = client_data; + return (write(mine->fd, buff, length)); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct write_fd_data *mine = client_data; + + (void)a; /* UNUSED */ + free(mine); + return (ARCHIVE_OK); +} diff --git a/lib/libarchive/archive_write_open_file.c b/lib/libarchive/archive_write_open_file.c index 92fa9d8a9ce3..19fdebaff2bb 100644 --- a/lib/libarchive/archive_write_open_file.c +++ b/lib/libarchive/archive_write_open_file.c @@ -41,7 +41,6 @@ __FBSDID("$FreeBSD$"); #include "archive_private.h" struct write_file_data { - off_t offset; int fd; char filename[1]; }; @@ -53,23 +52,23 @@ static ssize_t file_write(struct archive *, void *, void *buff, size_t); int archive_write_open_file(struct archive *a, const char *filename) { - return (archive_write_open_file_position(a, filename, 0)); -} - -int -archive_write_open_file_position(struct archive *a, const char *filename, - int64_t offset) -{ struct write_file_data *mine; if (filename == NULL) { mine = malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } mine->filename[0] = 0; } else { mine = malloc(sizeof(*mine) + strlen(filename)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } strcpy(mine->filename, filename); } - mine->offset = offset; mine->fd = -1; return (archive_write_open(a, mine, file_open, file_write, file_close)); @@ -83,10 +82,7 @@ file_open(struct archive *a, void *client_data) struct stat st; mine = client_data; - if (mine->offset == 0) - flags = O_WRONLY | O_CREAT | O_TRUNC; - else - flags = O_WRONLY; + flags = O_WRONLY | O_CREAT | O_TRUNC; if (*mine->filename != 0) { mine->fd = open(mine->filename, flags, 0666); @@ -115,12 +111,7 @@ file_open(struct archive *a, void *client_data) if (mine->fd < 0) { archive_set_error(a, errno, "Failed to open"); - return -1; - } - - if (mine->offset > 0) { - lseek(mine->fd, mine->offset, SEEK_SET); - ftruncate(mine->fd, mine->offset); + return (ARCHIVE_FATAL); } return (ARCHIVE_OK); diff --git a/lib/libarchive/archive_write_open_filename.c b/lib/libarchive/archive_write_open_filename.c index 92fa9d8a9ce3..19fdebaff2bb 100644 --- a/lib/libarchive/archive_write_open_filename.c +++ b/lib/libarchive/archive_write_open_filename.c @@ -41,7 +41,6 @@ __FBSDID("$FreeBSD$"); #include "archive_private.h" struct write_file_data { - off_t offset; int fd; char filename[1]; }; @@ -53,23 +52,23 @@ static ssize_t file_write(struct archive *, void *, void *buff, size_t); int archive_write_open_file(struct archive *a, const char *filename) { - return (archive_write_open_file_position(a, filename, 0)); -} - -int -archive_write_open_file_position(struct archive *a, const char *filename, - int64_t offset) -{ struct write_file_data *mine; if (filename == NULL) { mine = malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } mine->filename[0] = 0; } else { mine = malloc(sizeof(*mine) + strlen(filename)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } strcpy(mine->filename, filename); } - mine->offset = offset; mine->fd = -1; return (archive_write_open(a, mine, file_open, file_write, file_close)); @@ -83,10 +82,7 @@ file_open(struct archive *a, void *client_data) struct stat st; mine = client_data; - if (mine->offset == 0) - flags = O_WRONLY | O_CREAT | O_TRUNC; - else - flags = O_WRONLY; + flags = O_WRONLY | O_CREAT | O_TRUNC; if (*mine->filename != 0) { mine->fd = open(mine->filename, flags, 0666); @@ -115,12 +111,7 @@ file_open(struct archive *a, void *client_data) if (mine->fd < 0) { archive_set_error(a, errno, "Failed to open"); - return -1; - } - - if (mine->offset > 0) { - lseek(mine->fd, mine->offset, SEEK_SET); - ftruncate(mine->fd, mine->offset); + return (ARCHIVE_FATAL); } return (ARCHIVE_OK); diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c index 441c190895a7..d0cec2c16a0b 100644 --- a/lib/libarchive/archive_write_set_format_pax.c +++ b/lib/libarchive/archive_write_set_format_pax.c @@ -248,7 +248,7 @@ add_pax_attr_w(struct archive_string *as, const char *key, const wchar_t *wval) p += 6; } } - + *p = '\0'; add_pax_attr(as, key, utf8_value); free(utf8_value); } @@ -488,6 +488,16 @@ archive_write_pax_header(struct archive *a, if ((st_main->st_mtime < 0) || (st_main->st_mtime >= 0x7fffffff)) need_extension = 1; + /* If there are non-trivial ACL entries, we need an extension. */ + if (archive_entry_acl_count(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0) + need_extension = 1; + + /* If there are non-trivial ACL entries, we need an extension. */ + if (archive_entry_acl_count(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) > 0) + need_extension = 1; + /* * The following items are handled differently in "pax * restricted" format. In particular, in "pax restricted" @@ -520,13 +530,15 @@ archive_write_pax_header(struct archive *a, add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p); /* I use star-compatible ACL attributes. */ - p = archive_entry_acl(entry_main); - if (p != NULL && *p != '\0') - add_pax_attr(&(pax->pax_header), "SCHILY.acl.access", p); - p = archive_entry_acl_default(entry_main); - if (p != NULL && *p != '\0') - add_pax_attr(&(pax->pax_header), "SCHILY.acl.default", - p); + wp = __archive_entry_acl_text_w(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + if (wp != NULL && *wp != L'\0') + add_pax_attr_w(&(pax->pax_header), "SCHILY.acl.access", wp); + wp = __archive_entry_acl_text_w(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + if (wp != NULL && *wp != L'\0') + add_pax_attr_w(&(pax->pax_header), "SCHILY.acl.default", + wp); /* Include star-compatible metadata info. */ add_pax_attr_int(&(pax->pax_header), "SCHILY.dev", |