diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index d41e45659744..d73672e0ed57 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -25,6 +25,9 @@ SRCS= archive_check_magic.c \ archive_entry_link_resolver.c \ archive_read.c \ archive_read_data_into_fd.c \ + archive_read_disk.c \ + archive_read_disk_entry_from_file.c \ + archive_read_disk_set_standard_lookup.c \ archive_read_extract.c \ archive_read_open_fd.c \ archive_read_open_file.c \ diff --git a/lib/libarchive/archive.h b/lib/libarchive/archive.h index 09e0c4a9edc4..72bd8a00c105 100644 --- a/lib/libarchive/archive.h +++ b/lib/libarchive/archive.h @@ -36,6 +36,7 @@ * platform macros. */ +#include #include /* Linux requires this for off_t */ #if !defined(__WATCOMC__) && !defined(_MSC_VER) /* Header unavailable on Watcom C or MS Visual C++. */ @@ -604,6 +605,40 @@ __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, __LA_UID_T (*)(void *, const char *, __LA_UID_T), void (* /* cleanup */)(void *)); +/* + * ARCHIVE_READ_DISK API + * + * This is still evolving and somewhat experimental. + */ +__LA_DECL struct archive *archive_read_disk_new(void); +/* The names for symlink modes here correspond to an old BSD + * command-line argument convention: -L, -P, -H */ +/* Follow all symlinks. */ +__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *); +/* Follow no symlinks. */ +__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *); +/* Follow symlink initially, then not. */ +__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *); +/* TODO: Handle Linux stat32/stat64 ugliness. */ +__LA_DECL int archive_read_disk_entry_from_file(struct archive *, + struct archive_entry *, int /* fd */, const struct stat *); +/* Look up gname for gid or uname for uid. */ +/* Default implementations are very, very stupid. */ +__LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_GID_T); +__LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_UID_T); +/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the + * results for performance. */ +__LA_DECL int archive_read_disk_set_standard_lookup(struct archive *); +/* You can install your own lookups if you like. */ +__LA_DECL int archive_read_disk_set_gname_lookup(struct archive *, + void * /* private_data */, + const char *(* /* lookup_fn */)(void *, __LA_GID_T), + void (* /* cleanup_fn */)(void *)); +__LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, + void * /* private_data */, + const char *(* /* lookup_fn */)(void *, __LA_UID_T), + void (* /* cleanup_fn */)(void *)); + /* * Accessor functions to read/set various information in * the struct archive object: diff --git a/lib/libarchive/archive_private.h b/lib/libarchive/archive_private.h index 658c833ecdc1..a93344d69685 100644 --- a/lib/libarchive/archive_private.h +++ b/lib/libarchive/archive_private.h @@ -41,6 +41,7 @@ #define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU) #define ARCHIVE_READ_MAGIC (0xdeb0c5U) #define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U) +#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U) #define ARCHIVE_STATE_ANY 0xFFFFU #define ARCHIVE_STATE_NEW 1U diff --git a/lib/libarchive/archive_read_disk.c b/lib/libarchive/archive_read_disk.c new file mode 100644 index 000000000000..3c0b815af53b --- /dev/null +++ b/lib/libarchive/archive_read_disk.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2003-2009 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 "archive.h" +#include "archive_string.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_disk_private.h" + +static int _archive_read_finish(struct archive *); +static int _archive_read_close(struct archive *); +static const char *trivial_lookup_gname(void *, gid_t gid); +static const char *trivial_lookup_uname(void *, uid_t uid); + +static struct archive_vtable * +archive_read_disk_vtable(void) +{ + static struct archive_vtable av; + static int inited = 0; + + if (!inited) { + av.archive_finish = _archive_read_finish; + av.archive_close = _archive_read_close; + } + return (&av); +} + +const char * +archive_read_disk_gname(struct archive *_a, gid_t gid) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + if (a->lookup_gname != NULL) + return ((*a->lookup_gname)(a->lookup_gname_data, gid)); + return (NULL); +} + +const char * +archive_read_disk_uname(struct archive *_a, uid_t uid) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + if (a->lookup_uname != NULL) + return ((*a->lookup_uname)(a->lookup_uname_data, uid)); + return (NULL); +} + +int +archive_read_disk_set_gname_lookup(struct archive *_a, + void *private_data, + const char * (*lookup_gname)(void *private, gid_t gid), + void (*cleanup_gname)(void *private)) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); + + if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) + (a->cleanup_gname)(a->lookup_gname_data); + + a->lookup_gname = lookup_gname; + a->cleanup_gname = cleanup_gname; + a->lookup_gname_data = private_data; + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_uname_lookup(struct archive *_a, + void *private_data, + const char * (*lookup_uname)(void *private, uid_t uid), + void (*cleanup_uname)(void *private)) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); + + if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) + (a->cleanup_uname)(a->lookup_uname_data); + + a->lookup_uname = lookup_uname; + a->cleanup_uname = cleanup_uname; + a->lookup_uname_data = private_data; + return (ARCHIVE_OK); +} + +/* + * Create a new archive_read_disk object and initialize it with global state. + */ +struct archive * +archive_read_disk_new(void) +{ + struct archive_read_disk *a; + + a = (struct archive_read_disk *)malloc(sizeof(*a)); + if (a == NULL) + return (NULL); + memset(a, 0, sizeof(*a)); + a->archive.magic = ARCHIVE_READ_DISK_MAGIC; + /* We're ready to write a header immediately. */ + a->archive.state = ARCHIVE_STATE_HEADER; + a->archive.vtable = archive_read_disk_vtable(); + a->lookup_uname = trivial_lookup_uname; + a->lookup_gname = trivial_lookup_gname; + return (&a->archive); +} + +static int +_archive_read_finish(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + + if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) + (a->cleanup_gname)(a->lookup_gname_data); + if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) + (a->cleanup_uname)(a->lookup_uname_data); + archive_string_free(&a->archive.error_string); + free(a); + return (ARCHIVE_OK); +} + +static int +_archive_read_close(struct archive *_a) +{ + (void)_a; /* UNUSED */ + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_symlink_logical(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + a->symlink_mode = 'L'; + a->follow_symlinks = 1; + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_symlink_physical(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + a->symlink_mode = 'P'; + a->follow_symlinks = 0; + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_symlink_hybrid(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + a->symlink_mode = 'H'; + a->follow_symlinks = 1; /* Follow symlinks initially. */ + return (ARCHIVE_OK); +} + +/* + * Trivial implementations of gname/uname lookup functions. + * These are normally overridden by the client, but these stub + * versions ensure that we always have something that works. + */ +static const char * +trivial_lookup_gname(void *private_data, gid_t gid) +{ + (void)private_data; /* UNUSED */ + (void)gid; /* UNUSED */ + return (NULL); +} + +static const char * +trivial_lookup_uname(void *private_data, uid_t uid) +{ + (void)private_data; /* UNUSED */ + (void)uid; /* UNUSED */ + return (NULL); +} diff --git a/lib/libarchive/archive_read_disk_entry_from_file.c b/lib/libarchive/archive_read_disk_entry_from_file.c new file mode 100644 index 000000000000..ef06faa3ac74 --- /dev/null +++ b/lib/libarchive/archive_read_disk_entry_from_file.c @@ -0,0 +1,537 @@ +/*- + * Copyright (c) 2003-2009 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. + * 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_SYS_TYPES_H +/* Mac OSX requires sys/types.h before sys/acl.h. */ +#include +#endif +#ifdef HAVE_SYS_ACL_H +#include +#endif +#ifdef HAVE_SYS_EXTATTR_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ACL_LIBACL_H +#include +#endif +#ifdef HAVE_ATTR_XATTR_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_disk_private.h" + +/* + * Linux and FreeBSD plug this obvious hole in POSIX.1e in + * different ways. + */ +#if HAVE_ACL_GET_PERM +#define ACL_GET_PERM acl_get_perm +#elif HAVE_ACL_GET_PERM_NP +#define ACL_GET_PERM acl_get_perm_np +#endif + +static int setup_acls_posix1e(struct archive_read_disk *, + struct archive_entry *, int fd); +static int setup_xattrs(struct archive_read_disk *, + struct archive_entry *, int fd); + +int +archive_read_disk_entry_from_file(struct archive *_a, + struct archive_entry *entry, + int fd, const struct stat *st) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + const char *path; + struct stat s; + int initial_fd = fd; + int r, r1; + + path = archive_entry_sourcepath(entry); + if (path == NULL) + path = archive_entry_pathname(entry); + +#ifdef EXT2_IOC_GETFLAGS + /* Linux requires an extra ioctl to pull the flags. Although + * this is an extra step, it has a nice side-effect: We get an + * open file descriptor which we can use in the subsequent lookups. */ + if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { + if (fd < 0) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + unsigned long stflags; + int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); + if (r == 0 && stflags != 0) + archive_entry_set_fflags(entry, stflags, 0); + } + } +#endif + + if (st == NULL) { +#if HAVE_FSTAT + if (fd >= 0) { + if (fstat(fd, &s) != 0) + return (ARCHIVE_FATAL); + } else +#endif +#if HAVE_LSTAT + if (!a->follow_symlinks) { + if (lstat(path, &s) != 0) + return (ARCHIVE_FATAL); + } else +#endif + if (stat(path, &s) != 0) + return (ARCHIVE_FATAL); + st = &s; + } + archive_entry_copy_stat(entry, st); + +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + /* On FreeBSD, we get flags for free with the stat. */ + /* TODO: Does this belong in copy_stat()? */ + if (st->st_flags != 0) + archive_entry_set_fflags(entry, st->st_flags, 0); +#endif + +#ifdef HAVE_READLINK + if (S_ISLNK(st->st_mode)) { + char linkbuffer[PATH_MAX + 1]; + int lnklen = readlink(path, linkbuffer, PATH_MAX); + if (lnklen < 0) { + archive_set_error(&a->archive, errno, + "Couldn't read link data"); + return (ARCHIVE_WARN); + } + linkbuffer[lnklen] = 0; + archive_entry_set_symlink(entry, linkbuffer); + } +#endif + + r = setup_acls_posix1e(a, entry, fd); + r1 = setup_xattrs(a, entry, fd); + if (r1 < r) + r = r1; + /* If we opened the file earlier in this function, close it. */ + if (initial_fd != fd) + close(fd); + return (r); +} + +#ifdef HAVE_POSIX_ACL +static void setup_acl_posix1e(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, + struct archive_entry *entry, int fd) +{ + const char *accpath; + acl_t acl; + + accpath = archive_entry_sourcepath(entry); + if (accpath == NULL) + accpath = archive_entry_pathname(entry); + + archive_entry_acl_clear(entry); + + /* Retrieve access ACL from file. */ + 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_ACCESS); +#endif + else + acl = acl_get_file(accpath, ACL_TYPE_ACCESS); + if (acl != NULL) { + setup_acl_posix1e(a, entry, acl, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + acl_free(acl); + } + + /* Only directories can have default ACLs. */ + if (S_ISDIR(archive_entry_mode(entry))) { + acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); + if (acl != NULL) { + setup_acl_posix1e(a, entry, acl, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + acl_free(acl); + } + } + return (ARCHIVE_OK); +} + +/* + * Translate POSIX.1e 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) +{ + acl_tag_t acl_tag; + acl_entry_t acl_entry; + acl_permset_t acl_permset; + int s, ae_id, ae_tag, ae_perm; + const char *ae_name; + + s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); + while (s == 1) { + ae_id = -1; + ae_name = NULL; + + acl_get_tag_type(acl_entry, &acl_tag); + if (acl_tag == 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) { + 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) { + ae_tag = ARCHIVE_ENTRY_ACL_MASK; + } else if (acl_tag == ACL_USER_OBJ) { + ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (acl_tag == ACL_GROUP_OBJ) { + ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (acl_tag == ACL_OTHER) { + ae_tag = ARCHIVE_ENTRY_ACL_OTHER; + } else { + /* Skip types that libarchive can't support. */ + continue; + } + + acl_get_permset(acl_entry, &acl_permset); + ae_perm = 0; + /* + * acl_get_perm() is spelled differently on different + * platforms; see above. + */ + 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; + + archive_entry_acl_add_entry(entry, + archive_entry_acl_type, ae_perm, ae_tag, + ae_id, ae_name); + + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); + } +} +#else +static int +setup_acls_posix1e(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + (void)a; /* UNUSED */ + (void)entry; /* UNUSED */ + (void)fd; /* UNUSED */ + return (ARCHIVE_OK); +} +#endif + +#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR + +/* + * Linux extended attribute support. + * + * TODO: By using a stack-allocated buffer for the first + * call to getxattr(), we might be able to avoid the second + * call entirely. We only need the second call if the + * stack-allocated buffer is too small. But a modest buffer + * of 1024 bytes or so will often be big enough. Same applies + * to listxattr(). + */ + + +static int +setup_xattr(struct archive_read_disk *a, + struct archive_entry *entry, const char *name, int fd) +{ + ssize_t size; + void *value = NULL; + const char *accpath; + + (void)fd; /* UNUSED */ + + accpath = archive_entry_sourcepath(entry); + if (accpath == NULL) + accpath = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + size = lgetxattr(accpath, name, NULL, 0); + else + size = getxattr(accpath, name, NULL, 0); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't query extended attribute"); + return (ARCHIVE_WARN); + } + + if (size > 0 && (value = malloc(size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + size = lgetxattr(accpath, name, value, size); + else + size = getxattr(accpath, name, value, size); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't read extended attribute"); + return (ARCHIVE_WARN); + } + + archive_entry_xattr_add_entry(entry, name, value, size); + + free(value); + return (ARCHIVE_OK); +} + +static int +setup_xattrs(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + char *list, *p; + const char *path; + ssize_t list_size; + + + path = archive_entry_sourcepath(entry); + if (path == NULL) + path = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + list_size = llistxattr(path, NULL, 0); + else + list_size = listxattr(path, NULL, 0); + + if (list_size == -1) { + if (errno == ENOTSUP) + return (ARCHIVE_OK); + archive_set_error(&a->archive, errno, + "Couldn't list extended attributes"); + return (ARCHIVE_WARN); + } + + if (list_size == 0) + return (ARCHIVE_OK); + + if ((list = malloc(list_size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + list_size = llistxattr(path, list, list_size); + else + list_size = listxattr(path, list, list_size); + + if (list_size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't retrieve extended attributes"); + free(list); + return (ARCHIVE_WARN); + } + + for (p = list; (p - list) < list_size; p += strlen(p) + 1) { + if (strncmp(p, "system.", 7) == 0 || + strncmp(p, "xfsroot.", 8) == 0) + continue; + setup_xattr(a, entry, p, fd); + } + + free(list); + return (ARCHIVE_OK); +} + +#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE + +/* + * FreeBSD extattr interface. + */ + +/* TODO: Implement this. Follow the Linux model above, but + * with FreeBSD-specific system calls, of course. Be careful + * to not include the system extattrs that hold ACLs; we handle + * those separately. + */ +int +setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, + int namespace, const char *name, const char *fullname, int fd); + +int +setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, + int namespace, const char *name, const char *fullname, int fd) +{ + ssize_t size; + void *value = NULL; + const char *accpath; + + (void)fd; /* UNUSED */ + + accpath = archive_entry_sourcepath(entry); + if (accpath == NULL) + accpath = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + size = extattr_get_link(accpath, namespace, name, NULL, 0); + else + size = extattr_get_file(accpath, namespace, name, NULL, 0); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't query extended attribute"); + return (ARCHIVE_WARN); + } + + if (size > 0 && (value = malloc(size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + size = extattr_get_link(accpath, namespace, name, value, size); + else + size = extattr_get_file(accpath, namespace, name, value, size); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't read extended attribute"); + return (ARCHIVE_WARN); + } + + archive_entry_xattr_add_entry(entry, fullname, value, size); + + free(value); + return (ARCHIVE_OK); +} + +static int +setup_xattrs(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + char buff[512]; + char *list, *p; + ssize_t list_size; + const char *path; + int namespace = EXTATTR_NAMESPACE_USER; + + path = archive_entry_sourcepath(entry); + if (path == NULL) + path = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + list_size = extattr_list_link(path, namespace, NULL, 0); + else + list_size = extattr_list_file(path, namespace, NULL, 0); + + if (list_size == -1 && errno == EOPNOTSUPP) + return (ARCHIVE_OK); + if (list_size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't list extended attributes"); + return (ARCHIVE_WARN); + } + + if (list_size == 0) + return (ARCHIVE_OK); + + if ((list = malloc(list_size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + list_size = extattr_list_link(path, namespace, list, list_size); + else + list_size = extattr_list_file(path, namespace, list, list_size); + + if (list_size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't retrieve extended attributes"); + free(list); + return (ARCHIVE_WARN); + } + + p = list; + while ((p - list) < list_size) { + size_t len = 255 & (int)*p; + char *name; + + strcpy(buff, "user."); + name = buff + strlen(buff); + memcpy(name, p + 1, len); + name[len] = '\0'; + setup_xattr(a, entry, namespace, name, buff, fd); + p += 1 + len; + } + + free(list); + return (ARCHIVE_OK); +} + +#else + +/* + * Generic (stub) extended attribute support. + */ +static int +setup_xattrs(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + (void)a; /* UNUSED */ + (void)entry; /* UNUSED */ + (void)fd; /* UNUSED */ + return (ARCHIVE_OK); +} + +#endif diff --git a/lib/libarchive/archive_read_disk_private.h b/lib/libarchive/archive_read_disk_private.h new file mode 100644 index 000000000000..fd385a6b77e6 --- /dev/null +++ b/lib/libarchive/archive_read_disk_private.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 2003-2009 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. + * + * $FreeBSD$ + */ + +#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED +#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED + +struct archive_read_disk { + struct archive archive; + + /* + * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid, + * following an old BSD convention. 'L' follows all symlinks, + * 'P' follows none, 'H' follows symlinks only for the first + * item. + */ + char symlink_mode; + + /* + * Since symlink interaction changes, we need to track whether + * we're following symlinks for the current item. 'L' mode above + * sets this true, 'P' sets it false, 'H' changes it as we traverse. + */ + char follow_symlinks; /* Either 'L' or 'P'. */ + + const char * (*lookup_gname)(void *private, gid_t gid); + void (*cleanup_gname)(void *private); + void *lookup_gname_data; + const char * (*lookup_uname)(void *private, gid_t gid); + void (*cleanup_uname)(void *private); + void *lookup_uname_data; +}; + +#endif diff --git a/lib/libarchive/archive_read_disk_set_standard_lookup.c b/lib/libarchive/archive_read_disk_set_standard_lookup.c new file mode 100644 index 000000000000..cb123146b7de --- /dev/null +++ b/lib/libarchive/archive_read_disk_set_standard_lookup.c @@ -0,0 +1,229 @@ +/*- + * Copyright (c) 2003-2007 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. + * 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_SYS_TYPES_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" + +#ifdef _WIN32 +int +archive_read_disk_set_standard_lookup(struct archive *a) +{ + archive_set_error(a, -1, "Standard lookups not available on Windows"); + return (ARCHIVE_FATAL); +} +#else +#define name_cache_size 127 + +static const char * const NO_NAME = "(noname)"; + +struct name_cache { + struct archive *archive; + int probes; + int hits; + size_t size; + struct { + id_t id; + const char *name; + } cache[name_cache_size]; +}; + +static const char * lookup_gname(void *, gid_t); +static const char * lookup_uname(void *, uid_t); +static void cleanup(void *); +static const char * lookup_gname_helper(struct archive *, id_t gid); +static const char * lookup_uname_helper(struct archive *, id_t uid); + +/* + * Installs functions that use getpwuid()/getgrgid()---along with + * a simple cache to accelerate such lookups---into the archive_read_disk + * object. This is in a separate file because getpwuid()/getgrgid() + * can pull in a LOT of library code (including NIS/LDAP functions, which + * pull in DNS resolveers, etc). This can easily top 500kB, which makes + * it inappropriate for some space-constrained applications. + * + * Applications that are size-sensitive may want to just use the + * real default functions (defined in archive_read_disk.c) that just + * use the uid/gid without the lookup. Or define your own custom functions + * if you prefer. + */ +int +archive_read_disk_set_standard_lookup(struct archive *a) +{ + struct name_cache *ucache = malloc(sizeof(struct name_cache)); + struct name_cache *gcache = malloc(sizeof(struct name_cache)); + + if (ucache == NULL || gcache == NULL) { + archive_set_error(a, ENOMEM, + "Can't allocate uname/gname lookup cache"); + free(ucache); + free(gcache); + return (ARCHIVE_FATAL); + } + + memset(ucache, 0, sizeof(*ucache)); + ucache->archive = a; + ucache->size = name_cache_size; + memset(gcache, 0, sizeof(*gcache)); + gcache->archive = a; + gcache->size = name_cache_size; + + archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup); + archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup); + + return (ARCHIVE_OK); +} + +static void +cleanup(void *data) +{ + struct name_cache *cache = (struct name_cache *)data; + size_t i; + + if (cache != NULL) { + for (i = 0; i < cache->size; i++) { + if (cache->cache[i].name != NULL && + cache->cache[i].name != NO_NAME) + free((void *)(uintptr_t)cache->cache[i].name); + } + free(cache); + } +} + +/* + * Lookup uid/gid from uname/gname, return NULL if no match. + */ +static const char * +lookup_name(struct name_cache *cache, + const char * (*lookup_fn)(struct archive *, id_t), id_t id) +{ + const char *name; + int slot; + + + cache->probes++; + + slot = id % cache->size; + if (cache->cache[slot].name != NULL) { + if (cache->cache[slot].id == id) { + cache->hits++; + if (cache->cache[slot].name == NO_NAME) + return (NULL); + return (cache->cache[slot].name); + } + if (cache->cache[slot].name != NO_NAME) + free((void *)(uintptr_t)cache->cache[slot].name); + cache->cache[slot].name = NULL; + } + + name = (lookup_fn)(cache->archive, id); + if (name == NULL) { + /* Cache and return the negative response. */ + cache->cache[slot].name = NO_NAME; + cache->cache[slot].id = id; + return (NULL); + } + + cache->cache[slot].name = name; + cache->cache[slot].id = id; + return (cache->cache[slot].name); +} + +static const char * +lookup_uname(void *data, uid_t uid) +{ + struct name_cache *uname_cache = (struct name_cache *)data; + return (lookup_name(uname_cache, + &lookup_uname_helper, (id_t)uid)); +} + +static const char * +lookup_uname_helper(struct archive *a, id_t id) +{ + char buffer[64]; + struct passwd pwent, *result; + int r; + + errno = 0; + r = getpwuid_r((uid_t)id, &pwent, buffer, sizeof(buffer), &result); + if (r != 0) { + archive_set_error(a, errno, + "Can't lookup user for id %d", (int)id); + return (NULL); + } + if (result == NULL) + return (NULL); + + return strdup(pwent.pw_name); +} + +static const char * +lookup_gname(void *data, gid_t gid) +{ + struct name_cache *gname_cache = (struct name_cache *)data; + return (lookup_name(gname_cache, + &lookup_gname_helper, (id_t)gid)); +} + +static const char * +lookup_gname_helper(struct archive *a, id_t id) +{ + char buffer[64]; + struct group grent, *result; + int r; + + errno = 0; + r = getgrgid_r((gid_t)id, &grent, buffer, sizeof(buffer), &result); + if (r != 0) { + archive_set_error(a, errno, + "Can't lookup group for id %d", (int)id); + return (NULL); + } + if (result == NULL) + return (NULL); + + return strdup(grent.gr_name); +} +#endif /* _WIN32 */ diff --git a/lib/libarchive/config_freebsd.h b/lib/libarchive/config_freebsd.h index aa3aba9a9d88..54e26c89dca1 100644 --- a/lib/libarchive/config_freebsd.h +++ b/lib/libarchive/config_freebsd.h @@ -34,6 +34,12 @@ #define HAVE_ACL_SET_FD_NP 1 #define HAVE_ACL_SET_FILE 1 #define HAVE_ACL_USER 1 +#define HAVE_EXTATTR_GET_FILE 1 +#define HAVE_EXTATTR_LIST_FILE 1 +#define HAVE_EXTATTR_SET_FD 1 +#define HAVE_EXTATTR_SET_FILE 1 +#define HAVE_SYS_ACL_H 1 +#define HAVE_SYS_EXTATTR_H 1 #endif #define HAVE_BZLIB_H 1 @@ -78,6 +84,7 @@ #define HAVE_POLL 1 #define HAVE_POLL_H 1 #define HAVE_PWD_H 1 +#define HAVE_READLINK 1 #define HAVE_SELECT 1 #define HAVE_SETENV 1 #define HAVE_STDINT_H 1 diff --git a/lib/libarchive/test/Makefile b/lib/libarchive/test/Makefile index fc8e3e9206f5..634f510771d1 100644 --- a/lib/libarchive/test/Makefile +++ b/lib/libarchive/test/Makefile @@ -26,6 +26,7 @@ TESTS= \ test_pax_filename_encoding.c \ test_read_compress_program.c \ test_read_data_large.c \ + test_read_disk.c \ test_read_extract.c \ test_read_format_ar.c \ test_read_format_cpio_bin.c \ diff --git a/lib/libarchive/test/test_read_disk.c b/lib/libarchive/test/test_read_disk.c new file mode 100644 index 000000000000..e368398e4689 --- /dev/null +++ b/lib/libarchive/test/test_read_disk.c @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2003-2009 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. + * 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 "test.h" +__FBSDID("$FreeBSD$"); + +static void +gname_cleanup(void *d) +{ + int *mp = d; + assertEqualInt(*mp, 0x13579); + *mp = 0x2468; +} + +static const char * +gname_lookup(void *d, gid_t g) +{ + int *mp = d; + assertEqualInt(*mp, 0x13579); + if (g == 1) + return ("FOOGROUP"); + return ("NOTFOOGROUP"); +} + +static void +uname_cleanup(void *d) +{ + int *mp = d; + assertEqualInt(*mp, 0x1234); + *mp = 0x2345; +} + +static const char * +uname_lookup(void *d, uid_t u) +{ + int *mp = d; + assertEqualInt(*mp, 0x1234); + if (u == 1) + return ("FOO"); + return ("NOTFOO"); +} + +/* We test GID lookup by looking up the name of group number zero and + * checking it against the following list. If your system uses a + * different conventional name for group number zero, please extend + * this array and send us a patch. As always, please keep this list + * sorted alphabetically. + */ +static const char *zero_groups[] = { + "root", /* Linux */ + "wheel" /* BSD */ +}; + +DEFINE_TEST(test_read_disk) +{ + struct archive *a; + int gmagic = 0x13579, umagic = 0x1234; + const char *p; + size_t i; + + assert((a = archive_read_disk_new()) != NULL); + + /* Default uname/gname lookups always return NULL. */ + assert(archive_read_disk_gname(a, 0) == NULL); + assert(archive_read_disk_uname(a, 0) == NULL); + + /* Register some weird lookup functions. */ + assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a, + &gmagic, &gname_lookup, &gname_cleanup)); + /* Verify that our new function got called. */ + assertEqualString(archive_read_disk_gname(a, 0), "NOTFOOGROUP"); + assertEqualString(archive_read_disk_gname(a, 1), "FOOGROUP"); + + /* De-register. */ + assertEqualInt(ARCHIVE_OK, + archive_read_disk_set_gname_lookup(a, NULL, NULL, NULL)); + /* Ensure our cleanup function got called. */ + assertEqualInt(gmagic, 0x2468); + + /* Same thing with uname lookup.... */ + assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a, + &umagic, &uname_lookup, &uname_cleanup)); + assertEqualString(archive_read_disk_uname(a, 0), "NOTFOO"); + assertEqualString(archive_read_disk_uname(a, 1), "FOO"); + assertEqualInt(ARCHIVE_OK, + archive_read_disk_set_uname_lookup(a, NULL, NULL, NULL)); + assertEqualInt(umagic, 0x2345); + + /* Try the standard lookup functions. */ + if (archive_read_disk_set_standard_lookup(a) != ARCHIVE_OK) { + skipping("standard uname/gname lookup"); + } else { + /* XXX Someday, we may need to generalize this the + * same way we generalized the group name check below. + * That's needed only if we encounter a system where + * uid 0 is not "root". XXX */ + assertEqualString(archive_read_disk_uname(a, 0), "root"); + + /* Get the group name for group 0 and see if it makes sense. */ + p = archive_read_disk_gname(a, 0); + i = 0; + while (i < sizeof(zero_groups)/sizeof(zero_groups[0])) { + if (strcmp(zero_groups[i], p) == 0) + break; + ++i; + } + if (i == sizeof(zero_groups)/sizeof(zero_groups[0])) { + /* If you get a failure here, either + * archive_read_disk_gname() isn't working or + * your system uses a different name for group + * number zero. If the latter, please add a + * new entry to the zero_groups[] array above. + */ + failure("group 0 didn't have any of the expected names"); + assertEqualString(p, zero_groups[0]); + } + } + + /* Deregister again and verify the default lookups again. */ + assertEqualInt(ARCHIVE_OK, + archive_read_disk_set_gname_lookup(a, NULL, NULL, NULL)); + assertEqualInt(ARCHIVE_OK, + archive_read_disk_set_uname_lookup(a, NULL, NULL, NULL)); + assert(archive_read_disk_gname(a, 0) == NULL); + assert(archive_read_disk_uname(a, 0) == NULL); + + /* Re-register our custom handlers. */ + gmagic = 0x13579; + umagic = 0x1234; + assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a, + &gmagic, &gname_lookup, &gname_cleanup)); + assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a, + &umagic, &uname_lookup, &uname_cleanup)); + + /* Destroy the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); + + /* Verify our cleanup functions got called. */ + assertEqualInt(gmagic, 0x2468); + assertEqualInt(umagic, 0x2345); +}