Merge r399,401,402,405,415,430,440,452,453,458,506,533,536,538,544,590

from libarchive.googlecode.com:  Add a new "archive_read_disk" API
that provides the important service of reading metadata from the
disk.  In particular, this will make it possible to remove all
knowledge of extended attributes, ACLs, etc, from clients such
as bsdtar and bsdcpio.

Closely related, this API also provides pluggable uid->uname
and gid->gname lookup and caching services similar to
the uname->uid and gname->gid services provided by archive_write_disk.
Remember this is also required for correct ACL management.

Documentation is still pending...
This commit is contained in:
Tim Kientzle 2009-03-06 04:35:31 +00:00
parent 74aced1051
commit 43a8e5f098
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=189429
10 changed files with 1231 additions and 0 deletions

View file

@ -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 \

View file

@ -36,6 +36,7 @@
* platform macros.
*/
#include <sys/stat.h>
#include <sys/types.h> /* 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. <sigh> */
__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:

View file

@ -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

View file

@ -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);
}

View file

@ -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 <sys/types.h>
#endif
#ifdef HAVE_SYS_ACL_H
#include <sys/acl.h>
#endif
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ACL_LIBACL_H
#include <acl/libacl.h>
#endif
#ifdef HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#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

View file

@ -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

View file

@ -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 <sys/types.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#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 */

View file

@ -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

View file

@ -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 \

View file

@ -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);
}