libarchive 2.2.3

* "compression_program" support uses an external program
  * Portability: no longer uses "struct stat" as a primary
    data interchange structure internally
  * Part of the above: refactor archive_entry to separate
    out copy_stat() and stat() functions
  * More complete tests for archive_entry
  * Finish archive_entry_clone()
  * Isolate major()/minor()/makedev() in archive_entry; remove
    these from everywhere else.
  * Bug fix: properly handle decompression look-ahead at end-of-data
  * Bug fixes to 'ar' support
  * Fix memory leak in ZIP reader
  * Portability: better timegm() emulation in iso9660 reader
  * New write_disk flags to suppress auto dir creation and not
    overwrite newer files (for future cpio front-end)
  * Simplify trailing-'/' fixup when writing tar and pax
  * Test enhancements:  fix various compiler warnings, improve
    portability, add lots of new tests.
  * Documentation: document new functions, first draft of
    libarchive_internals.3

MFC after: 14 days
Thanks to: Joerg Sonnenberger (compression_program)
Thanks to: Kai Wang (ar)
Thanks to: Colin Percival (many small fixes)
Thanks to: Many others who sent me various patches and problem reports.
This commit is contained in:
Tim Kientzle 2007-05-29 01:00:21 +00:00
parent 8d573cc158
commit b48b40f1f8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=170079
73 changed files with 3889 additions and 1372 deletions

View File

@ -9,7 +9,7 @@ LDADD= -lbz2 -lz
# Major: Bumped ONLY when API/ABI breakage happens (see SHLIB_MAJOR)
# Minor: Bumped when significant new features are added
# Revision: Bumped on any notable change
VERSION= 2.0.31
VERSION= 2.2.3
ARCHIVE_API_MAJOR!= echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/\..*//'
ARCHIVE_API_MINOR!= echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/[0-9]*\.//' -e 's/\..*//'
@ -46,6 +46,8 @@ CLEANFILES+= archive.h
SRCS= archive.h \
archive_check_magic.c \
archive_entry.c \
archive_entry_copy_stat.c \
archive_entry_stat.c \
archive_read.c \
archive_read_data_into_fd.c \
archive_read_extract.c \
@ -58,6 +60,7 @@ SRCS= archive.h \
archive_read_support_compression_compress.c \
archive_read_support_compression_gzip.c \
archive_read_support_compression_none.c \
archive_read_support_compression_program.c \
archive_read_support_format_all.c \
archive_read_support_format_ar.c \
archive_read_support_format_cpio.c \
@ -79,13 +82,15 @@ SRCS= archive.h \
archive_write_set_compression_bzip2.c \
archive_write_set_compression_gzip.c \
archive_write_set_compression_none.c \
archive_write_set_compression_program.c \
archive_write_set_format.c \
archive_write_set_format_ar.c \
archive_write_set_format_by_name.c \
archive_write_set_format_cpio.c \
archive_write_set_format_pax.c \
archive_write_set_format_shar.c \
archive_write_set_format_ustar.c
archive_write_set_format_ustar.c \
filter_fork.c
# Man pages to be installed.
MAN= archive_entry.3 \
@ -115,6 +120,10 @@ MLINKS+= archive_entry.3 archive_entry_copy_pathname_w.3
MLINKS+= archive_entry.3 archive_entry_copy_stat.3
MLINKS+= archive_entry.3 archive_entry_copy_symlink_w.3
MLINKS+= archive_entry.3 archive_entry_copy_uname_w.3
MLINKS+= archive_entry.3 archive_entry_dev.3
MLINKS+= archive_entry.3 archive_entry_devmajor.3
MLINKS+= archive_entry.3 archive_entry_devminor.3
MLINKS+= archive_entry.3 archive_entry_filetype.3
MLINKS+= archive_entry.3 archive_entry_fflags.3
MLINKS+= archive_entry.3 archive_entry_fflags_text.3
MLINKS+= archive_entry.3 archive_entry_free.3
@ -126,19 +135,28 @@ MLINKS+= archive_entry.3 archive_entry_ino.3
MLINKS+= archive_entry.3 archive_entry_mode.3
MLINKS+= archive_entry.3 archive_entry_mtime.3
MLINKS+= archive_entry.3 archive_entry_mtime_nsec.3
MLINKS+= archive_entry.3 archive_entry_nlink.3
MLINKS+= archive_entry.3 archive_entry_new.3
MLINKS+= archive_entry.3 archive_entry_pathname.3
MLINKS+= archive_entry.3 archive_entry_pathname_w.3
MLINKS+= archive_entry.3 archive_entry_rdev.3
MLINKS+= archive_entry.3 archive_entry_rdevmajor.3
MLINKS+= archive_entry.3 archive_entry_rdevminor.3
MLINKS+= archive_entry.3 archive_entry_set_atime.3
MLINKS+= archive_entry.3 archive_entry_set_ctime.3
MLINKS+= archive_entry.3 archive_entry_set_dev.3
MLINKS+= archive_entry.3 archive_entry_set_devmajor.3
MLINKS+= archive_entry.3 archive_entry_set_devminor.3
MLINKS+= archive_entry.3 archive_entry_set_fflags.3
MLINKS+= archive_entry.3 archive_entry_set_gid.3
MLINKS+= archive_entry.3 archive_entry_set_gname.3
MLINKS+= archive_entry.3 archive_entry_set_hardlink.3
MLINKS+= archive_entry.3 archive_entry_set_link.3
MLINKS+= archive_entry.3 archive_entry_set_mode.3
MLINKS+= archive_entry.3 archive_entry_set_mtime.3
MLINKS+= archive_entry.3 archive_entry_set_nlink.3
MLINKS+= archive_entry.3 archive_entry_set_pathname.3
MLINKS+= archive_entry.3 archive_entry_set_rdev.3
MLINKS+= archive_entry.3 archive_entry_set_rdevmajor.3
MLINKS+= archive_entry.3 archive_entry_set_rdevminor.3
MLINKS+= archive_entry.3 archive_entry_set_size.3
@ -174,6 +192,7 @@ MLINKS+= archive_read.3 archive_read_support_compression_bzip2.3
MLINKS+= archive_read.3 archive_read_support_compression_compress.3
MLINKS+= archive_read.3 archive_read_support_compression_gzip.3
MLINKS+= archive_read.3 archive_read_support_compression_none.3
MLINKS+= archive_read.3 archive_read_support_compression_program.3
MLINKS+= archive_read.3 archive_read_support_format_all.3
MLINKS+= archive_read.3 archive_read_support_format_cpio.3
MLINKS+= archive_read.3 archive_read_support_format_iso9660.3
@ -205,6 +224,8 @@ MLINKS+= archive_write.3 archive_write_set_bytes_per_block.3
MLINKS+= archive_write.3 archive_write_set_callbacks.3
MLINKS+= archive_write.3 archive_write_set_compression_bzip2.3
MLINKS+= archive_write.3 archive_write_set_compression_gzip.3
MLINKS+= archive_write.3 archive_write_set_compression_none.3
MLINKS+= archive_write.3 archive_write_set_compression_program.3
MLINKS+= archive_write.3 archive_write_set_format_pax.3
MLINKS+= archive_write.3 archive_write_set_format_shar.3
MLINKS+= archive_write.3 archive_write_set_format_ustar.3

View File

@ -141,6 +141,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data);
#define ARCHIVE_COMPRESSION_GZIP 1
#define ARCHIVE_COMPRESSION_BZIP2 2
#define ARCHIVE_COMPRESSION_COMPRESS 3
#define ARCHIVE_COMPRESSION_PROGRAM 4
/*
* Codes returned by archive_format.
@ -207,6 +208,8 @@ int archive_read_support_compression_bzip2(struct archive *);
int archive_read_support_compression_compress(struct archive *);
int archive_read_support_compression_gzip(struct archive *);
int archive_read_support_compression_none(struct archive *);
int archive_read_support_compression_program(struct archive *,
const char *command);
int archive_read_support_format_all(struct archive *);
int archive_read_support_format_ar(struct archive *);
@ -319,6 +322,10 @@ int archive_read_data_into_fd(struct archive *, int fd);
#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (256)
/* Default: Do not reject entries with '..' as path elements. */
#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (512)
/* Default: Create parent directories as needed. */
#define ARCHIVE_EXTRACT_NO_AUTODIR (1024)
/* Default: Overwrite files, even if one on disk is newer. */
#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (2048)
int archive_read_extract(struct archive *, struct archive_entry *,
int flags);
@ -373,6 +380,8 @@ int archive_write_set_skip_file(struct archive *, dev_t, ino_t);
int archive_write_set_compression_bzip2(struct archive *);
int archive_write_set_compression_gzip(struct archive *);
int archive_write_set_compression_none(struct archive *);
int archive_write_set_compression_program(struct archive *,
const char *cmd);
/* A convenience function to set the format based on the code or name. */
int archive_write_set_format(struct archive *, int format_code);
int archive_write_set_format_by_name(struct archive *,
@ -494,6 +503,7 @@ const char *archive_format_name(struct archive *);
int archive_format(struct archive *);
void archive_clear_error(struct archive *);
void archive_set_error(struct archive *, int _err, const char *fmt, ...);
void archive_copy_error(struct archive *dest, struct archive *src);
#ifdef __cplusplus
}

View File

@ -50,6 +50,9 @@
.Nm archive_entry_copy_symlink_w ,
.Nm archive_entry_copy_uname_w ,
.Nm archive_entry_dev ,
.Nm archive_entry_devmajor ,
.Nm archive_entry_devminor ,
.Nm archive_entry_filetype ,
.Nm archive_entry_fflags ,
.Nm archive_entry_fflags_text ,
.Nm archive_entry_free ,
@ -60,12 +63,19 @@
.Nm archive_entry_mode ,
.Nm archive_entry_mtime ,
.Nm archive_entry_mtime_nsec ,
.Nm archive_entry_nlink ,
.Nm archive_entry_new ,
.Nm archive_entry_pathname ,
.Nm archive_entry_pathname_w ,
.Nm archive_entry_rdev ,
.Nm archive_entry_rdevmajor ,
.Nm archive_entry_rdevminor ,
.Nm archive_entry_set_atime ,
.Nm archive_entry_set_ctime ,
.Nm archive_entry_set_dev ,
.Nm archive_entry_set_devmajor ,
.Nm archive_entry_set_devminor ,
.Nm archive_entry_set_filetype ,
.Nm archive_entry_set_fflags ,
.Nm archive_entry_set_gid ,
.Nm archive_entry_set_gname ,
@ -132,6 +142,12 @@
.Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *"
.Ft dev_t
.Fn archive_entry_dev "struct archive_entry *"
.Ft dev_t
.Fn archive_entry_devmajor "struct archive_entry *"
.Ft dev_t
.Fn archive_entry_devminor "struct archive_entry *"
.Ft mode_t
.Fn archive_entry_filetype "struct archive_entry *"
.Ft void
.Fn archive_entry_fflags "struct archive_entry *" "unsigned long *set" "unsigned long *clear"
.Ft const char *
@ -150,6 +166,8 @@
.Fn archive_entry_mtime "struct archive_entry *"
.Ft long
.Fn archive_entry_mtime_nsec "struct archive_entry *"
.Ft unsigned int
.Fn archive_entry_nlink "struct archive_entry *"
.Ft struct archive_entry *
.Fn archive_entry_new "void"
.Ft const char *
@ -163,6 +181,14 @@
.Ft dev_t
.Fn archive_entry_rdevminor "struct archive_entry *"
.Ft void
.Fn archive_entry_set_dev "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_devmajor "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_devminor "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_filetype "struct archive_entry *" "unsigned int"
.Ft void
.Fn archive_entry_set_fflags "struct archive_entry *" "unsigned long set" "unsigned long clear"
.Ft void
.Fn archive_entry_set_gid "struct archive_entry *" "gid_t"
@ -171,14 +197,20 @@
.Ft void
.Fn archive_entry_set_hardlink "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_ino "struct archive_entry *" "unsigned long"
.Ft void
.Fn archive_entry_set_link "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_mode "struct archive_entry *" "mode_t"
.Ft void
.Fn archive_entry_set_mtime "struct archive_entry *" "time_t" "long nanos"
.Ft void
.Fn archive_entry_set_nlink "struct archive_entry *" "unsigned int"
.Ft void
.Fn archive_entry_set_pathname "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_rdev "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_rdevmajor "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_rdevminor "struct archive_entry *" "dev_t"

View File

@ -34,6 +34,9 @@ __FBSDID("$FreeBSD$");
#endif
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
# define makedev mkdev
# endif
#else
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
@ -63,69 +66,14 @@ __FBSDID("$FreeBSD$");
#include <wchar.h>
#endif
#ifndef HAVE_WCSCPY
static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
{
wchar_t *dest = s1;
while ((*s1 = *s2) != L'\0')
++s1, ++s2;
return dest;
}
#endif
#ifndef HAVE_WCSLEN
static size_t wcslen(const wchar_t *s)
{
const wchar_t *p = s;
while (*p != L'\0')
++p;
return p - s;
}
#endif
#ifndef HAVE_WMEMCMP
/* Good enough for simple equality testing, but not for sorting. */
#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
#endif
#ifndef HAVE_WMEMCPY
#define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
#endif
#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_entry_private.h"
#undef max
#define max(a, b) ((a)>(b)?(a):(b))
/*
* Handle wide character (i.e., Unicode) and non-wide character
* strings transparently.
*
*/
struct aes {
const char *aes_mbs;
char *aes_mbs_alloc;
const wchar_t *aes_wcs;
wchar_t *aes_wcs_alloc;
};
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 */
};
struct ae_xattr {
struct ae_xattr *next;
char *name;
void *value;
size_t size;
};
static void aes_clean(struct aes *);
static void aes_copy(struct aes *dest, struct aes *src);
static const char * aes_get_mbs(struct aes *);
@ -157,57 +105,32 @@ archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type,
int permset, int tag, int id, const wchar_t *name, size_t);
/*
* Description of an archive entry.
*
* Basically, this is a "struct stat" with a few text fields added in.
*
* 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.
*
* TODO: "pax interchange" format allows essentially arbitrary
* key/value attributes to be attached to any entry. Supporting
* such extensions may make this library useful for special
* applications (e.g., a package manager could attach special
* package-management attributes to each entry). There are tricky
* API issues involved, so this is not going to happen until
* there's a real demand for it.
*
* TODO: Design a good API for handling sparse files.
*/
struct archive_entry {
/*
* Note that ae_stat.st_mode & S_IFMT can be 0!
*
* This occurs when the actual file type of the object is not
* in the archive. For example, 'tar' archives store
* hardlinks without marking the type of the underlying
* object.
*/
struct stat ae_stat;
#ifndef HAVE_WCSCPY
static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
{
wchar_t *dest = s1;
while ((*s1 = *s2) != L'\0')
++s1, ++s2;
return dest;
}
#endif
#ifndef HAVE_WCSLEN
static size_t wcslen(const wchar_t *s)
{
const wchar_t *p = s;
while (*p != L'\0')
++p;
return p - s;
}
#endif
#ifndef HAVE_WMEMCMP
/* Good enough for simple equality testing, but not for sorting. */
#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
#endif
#ifndef HAVE_WMEMCPY
#define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
#endif
/*
* Use aes here so that we get transparent mbs<->wcs conversions.
*/
struct aes ae_fflags_text; /* Text fflags per fflagstostr(3) */
unsigned long ae_fflags_set; /* Bitmap fflags */
unsigned long ae_fflags_clear;
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;
struct ae_xattr *xattr_head;
struct ae_xattr *xattr_p;
};
static void
aes_clean(struct aes *aes)
@ -379,6 +302,7 @@ archive_entry_clear(struct archive_entry *entry)
aes_clean(&entry->ae_uname);
archive_entry_acl_clear(entry);
archive_entry_xattr_clear(entry);
free(entry->stat);
memset(entry, 0, sizeof(*entry));
return entry;
}
@ -387,6 +311,8 @@ struct archive_entry *
archive_entry_clone(struct archive_entry *entry)
{
struct archive_entry *entry2;
struct ae_acl *ap, *ap2;
struct ae_xattr *xp;
/* Allocate new structure and copy over all of the fields. */
entry2 = (struct archive_entry *)malloc(sizeof(*entry2));
@ -404,8 +330,24 @@ archive_entry_clone(struct archive_entry *entry)
aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
aes_copy(&entry2->ae_uname, &entry->ae_uname);
/* XXX TODO: Copy ACL data over as well. XXX */
/* XXX TODO: Copy xattr data over as well. XXX */
/* Copy ACL data over. */
ap = entry->acl_head;
while (ap != NULL) {
ap2 = acl_new_entry(entry2,
ap->type, ap->permset, ap->tag, ap->id);
if (ap2 != NULL)
aes_copy(&ap2->name, &ap->name);
ap = ap->next;
}
/* Copy xattr data over. */
xp = entry->xattr_head;
while (xp != NULL) {
archive_entry_xattr_add_entry(entry2,
xp->name, xp->value, xp->size);
xp = xp->next;
}
return (entry2);
}
@ -435,33 +377,59 @@ archive_entry_new(void)
time_t
archive_entry_atime(struct archive_entry *entry)
{
return (entry->ae_stat.st_atime);
return (entry->ae_stat.aest_atime);
}
long
archive_entry_atime_nsec(struct archive_entry *entry)
{
(void)entry; /* entry can be unused here. */
return (ARCHIVE_STAT_ATIME_NANOS(&entry->ae_stat));
return (entry->ae_stat.aest_atime_nsec);
}
time_t
archive_entry_ctime(struct archive_entry *entry)
{
return (entry->ae_stat.st_ctime);
return (entry->ae_stat.aest_ctime);
}
long
archive_entry_ctime_nsec(struct archive_entry *entry)
{
(void)entry; /* entry can be unused here. */
return (ARCHIVE_STAT_CTIME_NANOS(&entry->ae_stat));
return (entry->ae_stat.aest_ctime_nsec);
}
dev_t
archive_entry_dev(struct archive_entry *entry)
{
return (entry->ae_stat.st_dev);
if (entry->ae_stat.aest_dev_is_broken_down)
return makedev(entry->ae_stat.aest_devmajor,
entry->ae_stat.aest_devminor);
else
return (entry->ae_stat.aest_dev);
}
dev_t
archive_entry_devmajor(struct archive_entry *entry)
{
if (entry->ae_stat.aest_dev_is_broken_down)
return (entry->ae_stat.aest_devmajor);
else
return major(entry->ae_stat.aest_dev);
}
dev_t
archive_entry_devminor(struct archive_entry *entry)
{
if (entry->ae_stat.aest_dev_is_broken_down)
return (entry->ae_stat.aest_devminor);
else
return minor(entry->ae_stat.aest_dev);
}
mode_t
archive_entry_filetype(struct archive_entry *entry)
{
return (AE_IFMT & entry->ae_stat.aest_mode);
}
void
@ -507,7 +475,7 @@ archive_entry_fflags_text(struct archive_entry *entry)
gid_t
archive_entry_gid(struct archive_entry *entry)
{
return (entry->ae_stat.st_gid);
return (entry->ae_stat.aest_gid);
}
const char *
@ -537,26 +505,31 @@ archive_entry_hardlink_w(struct archive_entry *entry)
ino_t
archive_entry_ino(struct archive_entry *entry)
{
return (entry->ae_stat.st_ino);
return (entry->ae_stat.aest_ino);
}
mode_t
archive_entry_mode(struct archive_entry *entry)
{
return (entry->ae_stat.st_mode);
return (entry->ae_stat.aest_mode);
}
time_t
archive_entry_mtime(struct archive_entry *entry)
{
return (entry->ae_stat.st_mtime);
return (entry->ae_stat.aest_mtime);
}
long
archive_entry_mtime_nsec(struct archive_entry *entry)
{
(void)entry; /* entry can be unused here. */
return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat));
return (entry->ae_stat.aest_mtime_nsec);
}
unsigned int
archive_entry_nlink(struct archive_entry *entry)
{
return (entry->ae_stat.aest_nlink);
}
const char *
@ -574,31 +547,35 @@ archive_entry_pathname_w(struct archive_entry *entry)
dev_t
archive_entry_rdev(struct archive_entry *entry)
{
return (entry->ae_stat.st_rdev);
if (entry->ae_stat.aest_rdev_is_broken_down)
return makedev(entry->ae_stat.aest_rdevmajor,
entry->ae_stat.aest_rdevminor);
else
return (entry->ae_stat.aest_rdev);
}
dev_t
archive_entry_rdevmajor(struct archive_entry *entry)
{
return (major(entry->ae_stat.st_rdev));
if (entry->ae_stat.aest_rdev_is_broken_down)
return (entry->ae_stat.aest_rdevmajor);
else
return major(entry->ae_stat.aest_rdev);
}
dev_t
archive_entry_rdevminor(struct archive_entry *entry)
{
return (minor(entry->ae_stat.st_rdev));
if (entry->ae_stat.aest_rdev_is_broken_down)
return (entry->ae_stat.aest_rdevminor);
else
return minor(entry->ae_stat.aest_rdev);
}
int64_t
archive_entry_size(struct archive_entry *entry)
{
return (entry->ae_stat.st_size);
}
const struct stat *
archive_entry_stat(struct archive_entry *entry)
{
return (&entry->ae_stat);
return (entry->ae_stat.aest_size);
}
const char *
@ -616,7 +593,7 @@ archive_entry_symlink_w(struct archive_entry *entry)
uid_t
archive_entry_uid(struct archive_entry *entry)
{
return (entry->ae_stat.st_uid);
return (entry->ae_stat.aest_uid);
}
const char *
@ -635,14 +612,12 @@ archive_entry_uname_w(struct archive_entry *entry)
* Functions to set archive_entry properties.
*/
/*
* Note "copy" not "set" here. The "set" functions that accept a pointer
* only store the pointer; they don't copy the underlying object.
*/
void
archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
{
entry->ae_stat = *st;
entry->stat_valid = 0;
entry->ae_stat.aest_mode &= ~AE_IFMT;
entry->ae_stat.aest_mode |= AE_IFMT & type;
}
void
@ -666,7 +641,8 @@ archive_entry_copy_fflags_text_w(struct archive_entry *entry,
void
archive_entry_set_gid(struct archive_entry *entry, gid_t g)
{
entry->ae_stat.st_gid = g;
entry->stat_valid = 0;
entry->ae_stat.aest_gid = g;
}
void
@ -681,6 +657,13 @@ archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
aes_copy_wcs(&entry->ae_gname, name);
}
void
archive_entry_set_ino(struct archive_entry *entry, unsigned long ino)
{
entry->stat_valid = 0;
entry->ae_stat.aest_ino = ino;
}
void
archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
{
@ -702,15 +685,41 @@ archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target
void
archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
{
entry->ae_stat.st_atime = t;
ARCHIVE_STAT_SET_ATIME_NANOS(&entry->ae_stat, ns);
entry->stat_valid = 0;
entry->ae_stat.aest_atime = t;
entry->ae_stat.aest_atime_nsec = ns;
}
void
archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
{
entry->ae_stat.st_ctime = t;
ARCHIVE_STAT_SET_CTIME_NANOS(&entry->ae_stat, ns);
entry->stat_valid = 0;
entry->ae_stat.aest_ctime = t;
entry->ae_stat.aest_ctime_nsec = ns;
}
void
archive_entry_set_dev(struct archive_entry *entry, dev_t d)
{
entry->stat_valid = 0;
entry->ae_stat.aest_dev_is_broken_down = 0;
entry->ae_stat.aest_dev = d;
}
void
archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_dev_is_broken_down = 1;
entry->ae_stat.aest_devmajor = m;
}
void
archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_dev_is_broken_down = 1;
entry->ae_stat.aest_devminor = m;
}
/* Set symlink if symlink is already set, else set hardlink. */
@ -727,14 +736,23 @@ archive_entry_set_link(struct archive_entry *entry, const char *target)
void
archive_entry_set_mode(struct archive_entry *entry, mode_t m)
{
entry->ae_stat.st_mode = m;
entry->stat_valid = 0;
entry->ae_stat.aest_mode = m;
}
void
archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
{
entry->ae_stat.st_mtime = m;
ARCHIVE_STAT_SET_MTIME_NANOS(&entry->ae_stat, ns);
entry->stat_valid = 0;
entry->ae_stat.aest_mtime = m;
entry->ae_stat.aest_mtime_nsec = ns;
}
void
archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
{
entry->stat_valid = 0;
entry->ae_stat.aest_nlink = nlink;
}
void
@ -755,28 +773,35 @@ archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
aes_copy_wcs(&entry->ae_pathname, name);
}
void
archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_rdev = m;
entry->ae_stat.aest_rdev_is_broken_down = 0;
}
void
archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
{
dev_t d;
d = entry->ae_stat.st_rdev;
entry->ae_stat.st_rdev = makedev(major(m), minor(d));
entry->stat_valid = 0;
entry->ae_stat.aest_rdev_is_broken_down = 1;
entry->ae_stat.aest_rdevmajor = m;
}
void
archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
{
dev_t d;
d = entry->ae_stat.st_rdev;
entry->ae_stat.st_rdev = makedev(major(d), minor(m));
entry->stat_valid = 0;
entry->ae_stat.aest_rdev_is_broken_down = 1;
entry->ae_stat.aest_rdevminor = m;
}
void
archive_entry_set_size(struct archive_entry *entry, int64_t s)
{
entry->ae_stat.st_size = s;
entry->stat_valid = 0;
entry->ae_stat.aest_size = s;
}
void
@ -800,7 +825,8 @@ archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linknam
void
archive_entry_set_uid(struct archive_entry *entry, uid_t u)
{
entry->ae_stat.st_uid = u;
entry->stat_valid = 0;
entry->ae_stat.aest_uid = u;
}
void
@ -904,16 +930,16 @@ 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 &= ~0700;
entry->ae_stat.st_mode |= (permset & 7) << 6;
entry->ae_stat.aest_mode &= ~0700;
entry->ae_stat.aest_mode |= (permset & 7) << 6;
return (0);
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
entry->ae_stat.st_mode &= ~0070;
entry->ae_stat.st_mode |= (permset & 7) << 3;
entry->ae_stat.aest_mode &= ~0070;
entry->ae_stat.aest_mode |= (permset & 7) << 3;
return (0);
case ARCHIVE_ENTRY_ACL_OTHER:
entry->ae_stat.st_mode &= ~0007;
entry->ae_stat.st_mode |= permset & 7;
entry->ae_stat.aest_mode &= ~0007;
entry->ae_stat.aest_mode |= permset & 7;
return (0);
}
}
@ -1029,7 +1055,7 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
/*
* 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).
* from ae_stat.aest_mode).
*/
if (entry->acl_state == 0)
return (ARCHIVE_WARN);
@ -1038,19 +1064,19 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
switch (entry->acl_state) {
case ARCHIVE_ENTRY_ACL_USER_OBJ:
*permset = (entry->ae_stat.st_mode >> 6) & 7;
*permset = (entry->ae_stat.aest_mode >> 6) & 7;
*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
return (ARCHIVE_OK);
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
*permset = (entry->ae_stat.st_mode >> 3) & 7;
*permset = (entry->ae_stat.aest_mode >> 3) & 7;
*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
return (ARCHIVE_OK);
case ARCHIVE_ENTRY_ACL_OTHER:
*permset = entry->ae_stat.st_mode & 7;
*permset = entry->ae_stat.aest_mode & 7;
*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
*tag = ARCHIVE_ENTRY_ACL_OTHER;
entry->acl_state = -1;
@ -1139,13 +1165,13 @@ archive_entry_acl_text_w(struct archive_entry *entry, int flags)
count = 0;
if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
entry->ae_stat.st_mode & 0700, -1);
entry->ae_stat.aest_mode & 0700, -1);
*wp++ = ',';
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
entry->ae_stat.st_mode & 0070, -1);
entry->ae_stat.aest_mode & 0070, -1);
*wp++ = ',';
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
entry->ae_stat.st_mode & 0007, -1);
entry->ae_stat.aest_mode & 0007, -1);
count += 3;
ap = entry->acl_head;

View File

@ -28,7 +28,9 @@
#ifndef ARCHIVE_ENTRY_H_INCLUDED
#define ARCHIVE_ENTRY_H_INCLUDED
#include <sys/types.h>
#include <stddef.h> /* for wchar_t */
#include <time.h>
#include <unistd.h>
#ifdef __cplusplus
@ -54,6 +56,17 @@ extern "C" {
*/
struct archive_entry;
/*
* File-type constants. These are returned from archive_entry_filetype().
*/
#define AE_IFMT 0170000
#define AE_IFREG 0100000
#define AE_IFLNK 0120000
#define AE_IFCHR 0020000
#define AE_IFBLK 0060000
#define AE_IFDIR 0040000
#define AE_IFIFO 0010000
/*
* Basic object manipulation
*/
@ -73,6 +86,9 @@ long archive_entry_atime_nsec(struct archive_entry *);
time_t archive_entry_ctime(struct archive_entry *);
long archive_entry_ctime_nsec(struct archive_entry *);
dev_t archive_entry_dev(struct archive_entry *);
dev_t archive_entry_devmajor(struct archive_entry *);
dev_t archive_entry_devminor(struct archive_entry *);
mode_t archive_entry_filetype(struct archive_entry *);
void archive_entry_fflags(struct archive_entry *,
unsigned long *set, unsigned long *clear);
const char *archive_entry_fflags_text(struct archive_entry *);
@ -85,13 +101,13 @@ ino_t archive_entry_ino(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 *);
unsigned int archive_entry_nlink(struct archive_entry *);
const char *archive_entry_pathname(struct archive_entry *);
const wchar_t *archive_entry_pathname_w(struct archive_entry *);
dev_t archive_entry_rdev(struct archive_entry *);
dev_t archive_entry_rdevmajor(struct archive_entry *);
dev_t archive_entry_rdevminor(struct archive_entry *);
int64_t archive_entry_size(struct archive_entry *);
const struct stat *archive_entry_stat(struct archive_entry *);
const char *archive_entry_symlink(struct archive_entry *);
const wchar_t *archive_entry_symlink_w(struct archive_entry *);
uid_t archive_entry_uid(struct archive_entry *);
@ -105,9 +121,12 @@ const wchar_t *archive_entry_uname_w(struct archive_entry *);
* In contrast, 'copy' functions do copy the object pointed to.
*/
void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
void archive_entry_set_atime(struct archive_entry *, time_t, long);
void archive_entry_set_ctime(struct archive_entry *, time_t, long);
void archive_entry_set_dev(struct archive_entry *, dev_t);
void archive_entry_set_devmajor(struct archive_entry *, dev_t);
void archive_entry_set_devminor(struct archive_entry *, dev_t);
void archive_entry_set_filetype(struct archive_entry *, unsigned int);
void archive_entry_set_fflags(struct archive_entry *,
unsigned long set, unsigned long clear);
/* Returns pointer to start of first invalid token, or NULL if none. */
@ -120,12 +139,15 @@ void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
void archive_entry_set_hardlink(struct archive_entry *, const char *);
void archive_entry_copy_hardlink(struct archive_entry *, const char *);
void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
void archive_entry_set_ino(struct archive_entry *, unsigned long);
void archive_entry_set_link(struct archive_entry *, const char *);
void archive_entry_set_mode(struct archive_entry *, mode_t);
void archive_entry_set_mtime(struct archive_entry *, time_t, long);
void archive_entry_set_nlink(struct archive_entry *, unsigned int);
void archive_entry_set_pathname(struct archive_entry *, const char *);
void archive_entry_copy_pathname(struct archive_entry *, const char *);
void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
void archive_entry_set_rdev(struct archive_entry *, dev_t);
void archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
void archive_entry_set_rdevminor(struct archive_entry *, dev_t);
void archive_entry_set_size(struct archive_entry *, int64_t);
@ -136,6 +158,18 @@ 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 *);
/*
* Routines to bulk copy fields to/from a platform-native "struct
* stat." Libarchive used to just store a struct stat inside of each
* archive_entry object, but this created issues when trying to
* manipulate archives on systems different than the ones they were
* created on.
*
* TODO: On Linux, provide both stat32 and stat64 versions of these functions.
*/
const struct stat *archive_entry_stat(struct archive_entry *);
void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
/*
* ACL routines. This used to simply store and return text-format ACL
* strings, but that proved insufficient for a number of reasons:

View File

@ -0,0 +1,59 @@
/*-
* 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_STAT_H
#include <sys/stat.h>
#endif
#include "archive_entry.h"
void
archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
{
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);
archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);
archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);
archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);
archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);
#else
archive_entry_set_atime(entry, st->st_atime, 0);
archive_entry_set_ctime(entry, st->st_ctime, 0);
archive_entry_set_mtime(entry, st->st_mtime, 0);
#endif
archive_entry_set_dev(entry, st->st_dev);
archive_entry_set_gid(entry, st->st_gid);
archive_entry_set_uid(entry, st->st_uid);
archive_entry_set_ino(entry, st->st_ino);
archive_entry_set_nlink(entry, st->st_nlink);
archive_entry_set_rdev(entry, st->st_rdev);
archive_entry_set_size(entry, st->st_size);
archive_entry_set_mode(entry, st->st_mode);
}

View File

@ -0,0 +1,155 @@
/*-
* 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.
*
* $FreeBSD$
*/
#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
/*
* Handle wide character (i.e., Unicode) and non-wide character
* strings transparently.
*
*/
struct aes {
const char *aes_mbs;
char *aes_mbs_alloc;
const wchar_t *aes_wcs;
wchar_t *aes_wcs_alloc;
};
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 */
};
struct ae_xattr {
struct ae_xattr *next;
char *name;
void *value;
size_t size;
};
/*
* Description of an archive entry.
*
* Basically, this is a "struct stat" with a few text fields added in.
*
* 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.
*
* TODO: "pax interchange" format allows essentially arbitrary
* key/value attributes to be attached to any entry. Supporting
* such extensions may make this library useful for special
* applications (e.g., a package manager could attach special
* package-management attributes to each entry). There are tricky
* API issues involved, so this is not going to happen until
* there's a real demand for it.
*
* TODO: Design a good API for handling sparse files.
*/
struct archive_entry {
/*
* Note that ae_stat.st_mode & AE_IFMT can be 0!
*
* This occurs when the actual file type of the object is not
* in the archive. For example, 'tar' archives store
* hardlinks without marking the type of the underlying
* object.
*/
/*
* Read archive_entry_copy_stat.c for an explanation of why I
* don't just use "struct stat" instead of "struct aest" here
* and why I have this odd pointer to a separately-allocated
* struct stat.
*/
void *stat;
int stat_valid; /* Set to 0 whenever a field in aest changes. */
struct aest {
int64_t aest_atime;
uint32_t aest_atime_nsec;
int64_t aest_ctime;
uint32_t aest_ctime_nsec;
int64_t aest_mtime;
uint32_t aest_mtime_nsec;
gid_t aest_gid;
ino_t aest_ino;
mode_t aest_mode;
uint32_t aest_nlink;
uint64_t aest_size;
uid_t aest_uid;
/*
* Because converting between device codes and
* major/minor values is platform-specific and
* inherently a bit risky, we only do that conversion
* lazily. That way, we will do a better job of
* preserving information in those cases where no
* conversion is actually required.
*/
int aest_dev_is_broken_down;
dev_t aest_dev;
dev_t aest_devmajor;
dev_t aest_devminor;
int aest_rdev_is_broken_down;
dev_t aest_rdev;
dev_t aest_rdevmajor;
dev_t aest_rdevminor;
} ae_stat;
/*
* Use aes here so that we get transparent mbs<->wcs conversions.
*/
struct aes ae_fflags_text; /* Text fflags per fflagstostr(3) */
unsigned long ae_fflags_set; /* Bitmap fflags */
unsigned long ae_fflags_clear;
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;
struct ae_xattr *xattr_head;
struct ae_xattr *xattr_p;
};
#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */

View File

@ -0,0 +1,100 @@
/*-
* 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_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include "archive_entry.h"
#include "archive_entry_private.h"
const struct stat *
archive_entry_stat(struct archive_entry *entry)
{
struct stat *st;
if (entry->stat == NULL) {
entry->stat = malloc(sizeof(*st));
if (entry->stat == NULL)
return (NULL);
entry->stat_valid = 0;
}
/*
* If none of the underlying fields have been changed, we
* don't need to regenerate. In theory, we could use a bitmap
* here to flag only those items that have changed, but the
* extra complexity probably isn't worth it. It will be very
* rare for anyone to change just one field then request a new
* stat structure.
*/
if (entry->stat_valid)
return (entry->stat);
st = entry->stat;
/*
* Use the public interfaces to extract items, so that
* the appropriate conversions get invoked.
*/
st->st_atime = archive_entry_atime(entry);
st->st_ctime = archive_entry_ctime(entry);
st->st_mtime = archive_entry_mtime(entry);
st->st_dev = archive_entry_dev(entry);
st->st_gid = archive_entry_gid(entry);
st->st_uid = archive_entry_uid(entry);
st->st_ino = archive_entry_ino(entry);
st->st_nlink = archive_entry_nlink(entry);
st->st_rdev = archive_entry_rdev(entry);
st->st_size = archive_entry_size(entry);
st->st_mode = archive_entry_mode(entry);
/*
* On systems that support high-res timestamps, copy that
* information into struct stat.
*/
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);
st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);
st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);
st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);
st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);
#endif
/*
* TODO: On Linux, store 32 or 64 here depending on whether
* the cached stat structure is a stat32 or a stat64. This
* will allow us to support both variants interchangably.
*/
entry->stat_valid = 1;
return (st);
}

View File

@ -122,33 +122,4 @@
#define ARCHIVE_ERRNO_MISC (-1)
#endif
/* Select the best way to set/get hi-res timestamps. */
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
/* FreeBSD uses "timespec" members. */
#define ARCHIVE_STAT_ATIME_NANOS(st) (st)->st_atimespec.tv_nsec
#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec
#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec
#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n)
#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n)
#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n)
#else
#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
/* Linux uses "tim" members. */
#define ARCHIVE_STAT_ATIME_NANOS(pstat) (pstat)->st_atim.tv_nsec
#define ARCHIVE_STAT_CTIME_NANOS(pstat) (pstat)->st_ctim.tv_nsec
#define ARCHIVE_STAT_MTIME_NANOS(pstat) (pstat)->st_mtim.tv_nsec
#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n)
#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n)
#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n)
#else
/* If we can't find a better way, just use stubs. */
#define ARCHIVE_STAT_ATIME_NANOS(pstat) 0
#define ARCHIVE_STAT_CTIME_NANOS(pstat) 0
#define ARCHIVE_STAT_MTIME_NANOS(pstat) 0
#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) ((void)(n))
#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) ((void)(n))
#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) ((void)(n))
#endif
#endif
#endif /* !ARCHIVE_H_INCLUDED */

View File

@ -34,6 +34,7 @@
.Nm archive_read_support_compression_compress ,
.Nm archive_read_support_compression_gzip ,
.Nm archive_read_support_compression_none ,
.Nm archive_read_support_compression_program ,
.Nm archive_read_support_format_all ,
.Nm archive_read_support_format_cpio ,
.Nm archive_read_support_format_empty ,
@ -74,6 +75,8 @@
.Ft int
.Fn archive_read_support_compression_none "struct archive *"
.Ft int
.Fn archive_read_support_compression_program "struct archive *" "const char *cmd"
.Ft int
.Fn archive_read_support_format_all "struct archive *"
.Ft int
.Fn archive_read_support_format_cpio "struct archive *"
@ -142,6 +145,11 @@ is always enabled by default.
For convenience,
.Fn archive_read_support_compression_all
enables all available decompression code.
.It Fn archive_read_support_compression_program
Data is fed through the specified external program before being dearchived.
Note that this disables automatic detection of the compression format,
so it makes no sense to specify this in conjunction with any other
decompression option.
.It Fn archive_read_support_format_all , Fn archive_read_support_format_cpio , Fn archive_read_support_format_empty , Fn archive_read_support_format_iso9660 , Fn archive_read_support_format_tar, Fn archive_read_support_format_zip
Enables support---including auto-detection code---for the
specified archive format.

View File

@ -53,7 +53,7 @@ __FBSDID("$FreeBSD$");
#include "archive_private.h"
#include "archive_read_private.h"
static int choose_decompressor(struct archive_read *, const void*, size_t);
static void choose_decompressor(struct archive_read *, const void*, size_t);
static int choose_format(struct archive_read *);
static off_t dummy_skip(struct archive_read *, off_t);
@ -131,7 +131,6 @@ archive_read_open2(struct archive *_a, void *client_data,
struct archive_read *a = (struct archive_read *)_a;
const void *buffer;
ssize_t bytes_read;
int high_bidder;
int e;
__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open");
@ -163,7 +162,7 @@ archive_read_open2(struct archive *_a, void *client_data,
}
}
/* Read first block now for format detection. */
/* Read first block now for compress format detection. */
bytes_read = (client_reader)(&a->archive, client_data, &buffer);
if (bytes_read < 0) {
@ -182,12 +181,12 @@ archive_read_open2(struct archive *_a, void *client_data,
a->client_data = client_data;
/* Select a decompression routine. */
high_bidder = choose_decompressor(a, buffer, (size_t)bytes_read);
if (high_bidder < 0)
choose_decompressor(a, buffer, (size_t)bytes_read);
if (a->decompressor == NULL)
return (ARCHIVE_FATAL);
/* Initialize decompression routine with the first block of data. */
e = (a->decompressors[high_bidder].init)(a, buffer, (size_t)bytes_read);
e = (a->decompressor->init)(a, buffer, (size_t)bytes_read);
if (e == ARCHIVE_OK)
a->archive.state = ARCHIVE_STATE_HEADER;
@ -196,8 +195,8 @@ archive_read_open2(struct archive *_a, void *client_data,
* If the decompressor didn't register a skip function, provide a
* dummy compression-layer skip function.
*/
if (a->compression_skip == NULL)
a->compression_skip = dummy_skip;
if (a->decompressor->skip == NULL)
a->decompressor->skip = dummy_skip;
return (e);
}
@ -206,33 +205,37 @@ archive_read_open2(struct archive *_a, void *client_data,
* Allow each registered decompression routine to bid on whether it
* wants to handle this stream. Return index of winning bidder.
*/
static int
static void
choose_decompressor(struct archive_read *a,
const void *buffer, size_t bytes_read)
{
int decompression_slots, i, bid, best_bid, best_bid_slot;
int decompression_slots, i, bid, best_bid;
struct decompressor_t *decompressor, *best_decompressor;
decompression_slots = sizeof(a->decompressors) /
sizeof(a->decompressors[0]);
best_bid = -1;
best_bid_slot = -1;
best_bid = 0;
a->decompressor = NULL;
best_decompressor = NULL;
decompressor = a->decompressors;
for (i = 0; i < decompression_slots; i++) {
if (a->decompressors[i].bid) {
bid = (a->decompressors[i].bid)(buffer, bytes_read);
if ((bid > best_bid) || (best_bid_slot < 0)) {
if (decompressor->bid) {
bid = (decompressor->bid)(buffer, bytes_read);
if (bid > best_bid || best_decompressor == NULL) {
best_bid = bid;
best_bid_slot = i;
best_decompressor = decompressor;
}
}
decompressor ++;
}
/*
* There were no bidders; this is a serious programmer error
* and demands a quick and definitive abort.
*/
if (best_bid_slot < 0)
if (best_decompressor == NULL)
__archive_errx(1, "No decompressors were registered; you "
"must call at least one "
"archive_read_support_compression_XXX function in order "
@ -245,10 +248,11 @@ choose_decompressor(struct archive_read *a,
if (best_bid < 1) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Unrecognized archive format");
return (ARCHIVE_FATAL);
return;
}
return (best_bid_slot);
/* Record the best decompressor for this stream. */
a->decompressor = best_decompressor;
}
/*
@ -263,7 +267,7 @@ dummy_skip(struct archive_read * a, off_t request)
off_t bytes_skipped;
for (bytes_skipped = 0; request > 0;) {
bytes_read = (a->compression_read_ahead)(a, &dummy_buffer, 1);
bytes_read = (a->decompressor->read_ahead)(a, &dummy_buffer, 1);
if (bytes_read < 0)
return (bytes_read);
if (bytes_read == 0) {
@ -275,7 +279,7 @@ dummy_skip(struct archive_read * a, off_t request)
}
if (bytes_read > request)
bytes_read = (ssize_t)request;
(a->compression_read_consume)(a, (size_t)bytes_read);
(a->decompressor->consume)(a, (size_t)bytes_read);
request -= bytes_read;
bytes_skipped += bytes_read;
}
@ -326,7 +330,6 @@ archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
return (ARCHIVE_FATAL);
}
a->format = &(a->formats[slot]);
a->pformat_data = &(a->format->format_data);
ret = (a->format->read_header)(a, entry);
/*
@ -377,7 +380,6 @@ choose_format(struct archive_read *a)
a->format = &(a->formats[0]);
for (i = 0; i < slots; i++, a->format++) {
if (a->format->bid) {
a->pformat_data = &(a->format->format_data);
bid = (a->format->bid)(a);
if (bid == ARCHIVE_FATAL)
return (ARCHIVE_FATAL);
@ -591,6 +593,7 @@ archive_read_close(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
size_t i, n;
__archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
ARCHIVE_STATE_ANY, "archive_read_close");
@ -600,11 +603,21 @@ archive_read_close(struct archive *_a)
if (a->cleanup_archive_extract != NULL)
r = (a->cleanup_archive_extract)(a);
/* TODO: Finish the format processing. */
/* TODO: Clean up the formatters. */
/* Close the input machinery. */
if (a->compression_finish != NULL) {
r1 = (a->compression_finish)(a);
/* Clean up the decompressors. */
n = sizeof(a->decompressors)/sizeof(a->decompressors[0]);
for (i = 0; i < n; i++) {
if (a->decompressors[i].finish != NULL) {
r1 = (a->decompressors[i].finish)(a);
if (r1 < r)
r = r1;
}
}
/* Close the client stream. */
if (a->client_closer != NULL) {
r1 = ((a->client_closer)(&a->archive, a->client_data));
if (r1 < r)
r = r1;
}
@ -636,7 +649,7 @@ archive_read_finish(struct archive *_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);
a->format = &(a->formats[i]);
if (a->formats[i].cleanup)
(a->formats[i].cleanup)(a);
}
@ -683,7 +696,7 @@ __archive_read_register_format(struct archive_read *a,
a->formats[i].read_data = read_data;
a->formats[i].read_data_skip = read_data_skip;
a->formats[i].cleanup = cleanup;
a->formats[i].format_data = format_data;
a->formats[i].data = format_data;
return (ARCHIVE_OK);
}
}
@ -696,7 +709,7 @@ __archive_read_register_format(struct archive_read *a,
* Used internally by decompression routines to register their bid and
* initialization functions.
*/
int
struct decompressor_t *
__archive_read_register_compression(struct archive_read *a,
int (*bid)(const void *, size_t),
int (*init)(struct archive_read *, const void *, size_t))
@ -711,14 +724,14 @@ __archive_read_register_compression(struct archive_read *a,
for (i = 0; i < number_slots; i++) {
if (a->decompressors[i].bid == bid)
return (ARCHIVE_OK); /* We've already installed */
return (a->decompressors + i);
if (a->decompressors[i].bid == NULL) {
a->decompressors[i].bid = bid;
a->decompressors[i].init = init;
return (ARCHIVE_OK);
return (a->decompressors + i);
}
}
__archive_errx(1, "Not enough slots for compression registration");
return (ARCHIVE_FATAL); /* Never actually executed. */
return (NULL); /* Never actually executed. */
}

View File

@ -99,9 +99,7 @@ archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
r = ARCHIVE_WARN;
if (r != ARCHIVE_OK)
/* If _write_header failed, copy the error. */
archive_set_error(&a->archive,
archive_errno(extract->ad),
"%s", archive_error_string(extract->ad));
archive_copy_error(&a->archive, extract->ad);
else
/* Otherwise, pour data into the entry. */
r = copy_data(_a, a->extract->ad);
@ -110,9 +108,7 @@ archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
r2 = ARCHIVE_WARN;
/* Use the first message. */
if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
archive_set_error(&a->archive,
archive_errno(extract->ad),
"%s", archive_error_string(extract->ad));
archive_copy_error(&a->archive, extract->ad);
/* Use the worst error return. */
if (r2 < r)
r = r2;

View File

@ -86,33 +86,35 @@ struct archive_read {
off_t header_position;
/*
* Detection functions for decompression: bid functions are
* given a block of data from the beginning of the stream and
* can bid on whether or not they support the data stream.
* General guideline: bid the number of bits that you actually
* test, e.g., 16 if you test a 2-byte magic value. The
* highest bidder will have their init function invoked, which
* can set up pointers to specific handlers.
* Decompressors have a very specific lifecycle:
* public setup function initializes a slot in this table
* 'config' holds minimal configuration data
* bid() examines a block of data and returns a bid [1]
* init() is called for successful bidder
* 'data' is initialized by init()
* read() returns a pointer to the next block of data
* consume() indicates how much data is used
* skip() ignores bytes of data
* finish() cleans up and frees 'data' and 'config'
*
* [1] General guideline: bid the number of bits that you actually
* test, e.g., 16 if you test a 2-byte magic value.
*/
struct {
struct decompressor_t {
void *config;
void *data;
int (*bid)(const void *buff, size_t);
int (*init)(struct archive_read *, const void *buff, size_t);
int (*init)(struct archive_read *,
const void *buff, size_t);
int (*finish)(struct archive_read *);
ssize_t (*read_ahead)(struct archive_read *,
const void **, size_t);
ssize_t (*consume)(struct archive_read *, size_t);
off_t (*skip)(struct archive_read *, off_t);
} decompressors[4];
/* Read/write data stream (with compression). */
void *compression_data; /* Data for (de)compressor. */
int (*compression_finish)(struct archive_read *);
/*
* Read uses a peek/consume I/O model: the decompression code
* returns a pointer to the requested block and advances the
* file position only when requested by a consume call. This
* reduces copying and also simplifies look-ahead for format
* detection.
*/
ssize_t (*compression_read_ahead)(struct archive_read *,
const void **, size_t request);
ssize_t (*compression_read_consume)(struct archive_read *, size_t);
off_t (*compression_skip)(struct archive_read *, off_t);
/* Pointer to current decompressor. */
struct decompressor_t *decompressor;
/*
* Format detection is mostly the same as compression
@ -130,26 +132,15 @@ struct archive_read {
*/
struct archive_format_descriptor {
void *data;
int (*bid)(struct archive_read *);
int (*read_header)(struct archive_read *, struct archive_entry *);
int (*read_data)(struct archive_read *, const void **, size_t *, off_t *);
int (*read_data_skip)(struct archive_read *);
int (*cleanup)(struct archive_read *);
void *format_data; /* Format-specific data for readers. */
} formats[8];
struct archive_format_descriptor *format; /* Active format. */
/*
* Storage for format-specific data. Note that there can be
* multiple format readers active at one time, so we need to
* allow for multiple format readers to have their data
* available. The pformat_data slot here is the solution: on
* read, it is guaranteed to always point to a void* variable
* that the format can use.
*/
void **pformat_data; /* Pointer to current format_data. */
void *format_data; /* Used by writers. */
/*
* Pointers to format-specific functions for writing. They're
* initialized by archive_write_set_format_XXX() calls.
@ -177,7 +168,8 @@ int __archive_read_register_format(struct archive_read *a,
int (*read_data_skip)(struct archive_read *),
int (*cleanup)(struct archive_read *));
int __archive_read_register_compression(struct archive_read *a,
struct decompressor_t
*__archive_read_register_compression(struct archive_read *a,
int (*bid)(const void *, size_t),
int (*init)(struct archive_read *, const void *, size_t));

View File

@ -55,6 +55,7 @@ struct private_data {
size_t uncompressed_buffer_size;
char *read_next;
int64_t total_out;
char eof; /* True = found end of compressed data. */
};
static int finish(struct archive_read *);
@ -63,7 +64,7 @@ static ssize_t read_consume(struct archive_read *, size_t);
static int drive_decompressor(struct archive_read *a, struct private_data *);
#endif
/* These two functions are defined even if we lack bzlib. See below. */
/* These two functions are defined even if we lack the library. See below. */
static int bid(const void *, size_t);
static int init(struct archive_read *, const void *, size_t);
@ -71,7 +72,9 @@ int
archive_read_support_compression_bzip2(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
return (__archive_read_register_compression(a, bid, init));
if (__archive_read_register_compression(a, bid, init) != NULL)
return (ARCHIVE_OK);
return (ARCHIVE_FATAL);
}
/*
@ -131,9 +134,9 @@ bid(const void *buff, size_t len)
#ifndef HAVE_BZLIB_H
/*
* If we don't have bzlib on this system, we can't actually do the
* decompression. We can, however, still detect bzip2-compressed
* archives and emit a useful message.
* If we don't have the library on this system, we can't actually do the
* decompression. We can, however, still detect compressed archives
* and emit a useful message.
*/
static int
init(struct archive_read *a, const void *buff, size_t n)
@ -194,10 +197,10 @@ init(struct archive_read *a, const void *buff, size_t n)
state->stream.next_in = (char *)(uintptr_t)(const void *)buff;
state->stream.avail_in = n;
a->compression_read_ahead = read_ahead;
a->compression_read_consume = read_consume;
a->compression_skip = NULL; /* not supported */
a->compression_finish = finish;
a->decompressor->read_ahead = read_ahead;
a->decompressor->consume = read_consume;
a->decompressor->skip = NULL; /* not supported */
a->decompressor->finish = finish;
/* Initialize compression library. */
ret = BZ2_bzDecompressInit(&(state->stream),
@ -212,7 +215,7 @@ init(struct archive_read *a, const void *buff, size_t n)
}
if (ret == BZ_OK) {
a->compression_data = state;
a->decompressor->data = state;
return (ARCHIVE_OK);
}
@ -231,7 +234,7 @@ init(struct archive_read *a, const void *buff, size_t n)
"invalid setup parameter");
break;
case BZ_MEM_ERROR:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
archive_set_error(&a->archive, ENOMEM,
"Internal error initializing compression library: "
"out of memory");
break;
@ -256,7 +259,7 @@ read_ahead(struct archive_read *a, const void **p, size_t min)
size_t read_avail, was_avail;
int ret;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
if (!a->client_reader) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"No read callback is registered? "
@ -278,8 +281,10 @@ read_ahead(struct archive_read *a, const void **p, size_t min)
while (read_avail < min && /* Haven't satisfied min. */
read_avail < state->uncompressed_buffer_size) { /* !full */
was_avail = read_avail;
if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK)
return (ret);
if (ret == ARCHIVE_EOF)
break; /* Break on EOF even if we haven't met min. */
read_avail = state->stream.next_out - state->read_next;
if (was_avail == read_avail) /* No progress? */
break;
@ -297,7 +302,7 @@ read_consume(struct archive_read *a, size_t n)
{
struct private_data *state;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
a->archive.file_position += n;
state->read_next += n;
if (state->read_next > state->stream.next_out)
@ -315,7 +320,7 @@ finish(struct archive_read *a)
struct private_data *state;
int ret;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
ret = ARCHIVE_OK;
switch (BZ2_bzDecompressEnd(&(state->stream))) {
case BZ_OK:
@ -330,10 +335,7 @@ finish(struct archive_read *a)
free(state->uncompressed_buffer);
free(state);
a->compression_data = NULL;
if (a->client_closer != NULL)
(a->client_closer)(&a->archive, a->client_data);
a->decompressor->data = NULL;
return (ret);
}
@ -349,6 +351,8 @@ drive_decompressor(struct archive_read *a, struct private_data *state)
char *output;
const void *read_buf;
if (state->eof)
return (ARCHIVE_EOF);
total_decompressed = 0;
for (;;) {
if (state->stream.avail_in == 0) {
@ -390,6 +394,7 @@ drive_decompressor(struct archive_read *a, struct private_data *state)
return (ARCHIVE_OK);
break;
case BZ_STREAM_END: /* Found end of stream. */
state->eof = 1;
return (ARCHIVE_OK);
default:
/* Any other return value is an error. */

View File

@ -145,7 +145,9 @@ int
archive_read_support_compression_compress(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
return (__archive_read_register_compression(a, bid, init));
if (__archive_read_register_compression(a, bid, init) != NULL)
return (ARCHIVE_OK);
return (ARCHIVE_FATAL);
}
/*
@ -197,10 +199,10 @@ init(struct archive_read *a, const void *buff, size_t n)
a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
a->archive.compression_name = "compress (.Z)";
a->compression_read_ahead = read_ahead;
a->compression_read_consume = read_consume;
a->compression_skip = NULL; /* not supported */
a->compression_finish = finish;
a->decompressor->read_ahead = read_ahead;
a->decompressor->consume = read_consume;
a->decompressor->skip = NULL; /* not supported */
a->decompressor->finish = finish;
state = (struct private_data *)malloc(sizeof(*state));
if (state == NULL) {
@ -210,7 +212,7 @@ init(struct archive_read *a, const void *buff, size_t n)
return (ARCHIVE_FATAL);
}
memset(state, 0, sizeof(*state));
a->compression_data = state;
a->decompressor->data = state;
state->uncompressed_buffer_size = 64 * 1024;
state->uncompressed_buffer = malloc(state->uncompressed_buffer_size);
@ -278,7 +280,7 @@ read_ahead(struct archive_read *a, const void **p, size_t min)
size_t read_avail;
int ret;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
if (!a->client_reader) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"No read callback is registered? "
@ -331,7 +333,7 @@ read_consume(struct archive_read *a, size_t n)
{
struct private_data *state;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
a->archive.file_position += n;
state->read_next += n;
if (state->read_next > state->next_out)
@ -349,7 +351,7 @@ finish(struct archive_read *a)
struct private_data *state;
int ret = ARCHIVE_OK;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
if (state != NULL) {
if (state->uncompressed_buffer != NULL)
@ -357,10 +359,7 @@ finish(struct archive_read *a)
free(state);
}
a->compression_data = NULL;
if (a->client_closer != NULL)
ret = (a->client_closer)(&a->archive, a->client_data);
a->decompressor->data = NULL;
return (ret);
}

View File

@ -57,6 +57,7 @@ struct private_data {
int64_t total_out;
unsigned long crc;
char header_done;
char eof; /* True = found end of compressed data. */
};
static int finish(struct archive_read *);
@ -65,7 +66,7 @@ static ssize_t read_consume(struct archive_read *, size_t);
static int drive_decompressor(struct archive_read *a, struct private_data *);
#endif
/* These two functions are defined even if we lack zlib. See below. */
/* These two functions are defined even if we lack the library. See below. */
static int bid(const void *, size_t);
static int init(struct archive_read *, const void *, size_t);
@ -73,7 +74,9 @@ int
archive_read_support_compression_gzip(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
return (__archive_read_register_compression(a, bid, init));
if (__archive_read_register_compression(a, bid, init) != NULL)
return (ARCHIVE_OK);
return (ARCHIVE_FATAL);
}
/*
@ -132,9 +135,9 @@ bid(const void *buff, size_t len)
#ifndef HAVE_ZLIB_H
/*
* If we don't have zlib on this system, we can't actually do the
* decompression. We can, however, still detect gzip-compressed
* archives and emit a useful message.
* If we don't have the library on this system, we can't actually do the
* decompression. We can, however, still detect compressed archives
* and emit a useful message.
*/
static int
init(struct archive_read *a, const void *buff, size_t n)
@ -198,10 +201,10 @@ init(struct archive_read *a, const void *buff, size_t n)
state->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
state->stream.avail_in = n;
a->compression_read_ahead = read_ahead;
a->compression_read_consume = read_consume;
a->compression_skip = NULL; /* not supported */
a->compression_finish = finish;
a->decompressor->read_ahead = read_ahead;
a->decompressor->consume = read_consume;
a->decompressor->skip = NULL; /* not supported */
a->decompressor->finish = finish;
/*
* TODO: Do I need to parse the gzip header before calling
@ -217,7 +220,7 @@ init(struct archive_read *a, const void *buff, size_t n)
ret = inflateInit2(&(state->stream),
-15 /* Don't check for zlib header */);
if (ret == Z_OK) {
a->compression_data = state;
a->decompressor->data = state;
return (ARCHIVE_OK);
}
@ -261,7 +264,7 @@ read_ahead(struct archive_read *a, const void **p, size_t min)
size_t read_avail, was_avail;
int ret;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
if (!a->client_reader) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"No read callback is registered? "
@ -283,8 +286,10 @@ read_ahead(struct archive_read *a, const void **p, size_t min)
while (read_avail < min && /* Haven't satisfied min. */
read_avail < state->uncompressed_buffer_size) { /* !full */
was_avail = read_avail;
if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK)
return (ret);
if (ret == ARCHIVE_EOF)
break; /* Break on EOF even if we haven't met min. */
read_avail = state->stream.next_out - state->read_next;
if (was_avail == read_avail) /* No progress? */
break;
@ -302,7 +307,7 @@ read_consume(struct archive_read *a, size_t n)
{
struct private_data *state;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
a->archive.file_position += n;
state->read_next += n;
if (state->read_next > state->stream.next_out)
@ -320,7 +325,7 @@ finish(struct archive_read *a)
struct private_data *state;
int ret;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->decompressor->data;
ret = ARCHIVE_OK;
switch (inflateEnd(&(state->stream))) {
case Z_OK:
@ -335,10 +340,7 @@ finish(struct archive_read *a)
free(state->uncompressed_buffer);
free(state);
a->compression_data = NULL;
if (a->client_closer != NULL)
(a->client_closer)(&a->archive, a->client_data);
a->decompressor->data = NULL;
return (ret);
}
@ -356,6 +358,8 @@ drive_decompressor(struct archive_read *a, struct private_data *state)
unsigned char b;
const void *read_buf;
if (state->eof)
return (ARCHIVE_EOF);
flags = 0;
count = 0;
header_state = 0;
@ -525,12 +529,10 @@ drive_decompressor(struct archive_read *a, struct private_data *state)
* TODO: Verify gzip trailer
* (uncompressed length and CRC).
*/
state->eof = 1;
return (ARCHIVE_OK);
default:
/* Any other return value is an error. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"gzip decompression failed (%s)",
state->stream.msg);
goto fatal;
}
}

View File

@ -88,9 +88,11 @@ int
archive_read_support_compression_none(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
return (__archive_read_register_compression(a,
archive_decompressor_none_bid,
archive_decompressor_none_init));
if (__archive_read_register_compression(a,
archive_decompressor_none_bid,
archive_decompressor_none_init) != NULL)
return (ARCHIVE_OK);
return (ARCHIVE_FATAL);
}
/*
@ -135,11 +137,11 @@ archive_decompressor_none_init(struct archive_read *a, const void *buff, size_t
state->client_next = state->client_buff;
state->client_avail = state->client_total;
a->compression_data = state;
a->compression_read_ahead = archive_decompressor_none_read_ahead;
a->compression_read_consume = archive_decompressor_none_read_consume;
a->compression_skip = archive_decompressor_none_skip;
a->compression_finish = archive_decompressor_none_finish;
a->decompressor->data = state;
a->decompressor->read_ahead = archive_decompressor_none_read_ahead;
a->decompressor->consume = archive_decompressor_none_read_consume;
a->decompressor->skip = archive_decompressor_none_skip;
a->decompressor->finish = archive_decompressor_none_finish;
return (ARCHIVE_OK);
}
@ -156,7 +158,7 @@ archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff,
struct archive_decompress_none *state;
ssize_t bytes_read;
state = (struct archive_decompress_none *)a->compression_data;
state = (struct archive_decompress_none *)a->decompressor->data;
if (state->fatal)
return (-1);
@ -253,7 +255,7 @@ archive_decompressor_none_read_consume(struct archive_read *a, size_t request)
{
struct archive_decompress_none *state;
state = (struct archive_decompress_none *)a->compression_data;
state = (struct archive_decompress_none *)a->decompressor->data;
if (state->avail > 0) {
/* Read came from copy buffer. */
state->next += request;
@ -279,7 +281,7 @@ archive_decompressor_none_skip(struct archive_read *a, off_t request)
off_t bytes_skipped, total_bytes_skipped = 0;
size_t min;
state = (struct archive_decompress_none *)a->compression_data;
state = (struct archive_decompress_none *)a->decompressor->data;
if (state->fatal)
return (-1);
/*
@ -355,11 +357,9 @@ archive_decompressor_none_finish(struct archive_read *a)
{
struct archive_decompress_none *state;
state = (struct archive_decompress_none *)a->compression_data;
state = (struct archive_decompress_none *)a->decompressor->data;
free(state->buffer);
free(state);
a->compression_data = NULL;
if (a->client_closer != NULL)
return ((a->client_closer)(&a->archive, a->client_data));
a->decompressor->data = NULL;
return (ARCHIVE_OK);
}

View File

@ -0,0 +1,312 @@
/*-
* Copyright (c) 2007 Joerg Sonnenberger
* 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_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"
#include "filter_fork.h"
struct archive_decompress_program {
char *description;
pid_t child;
int child_stdin, child_stdout;
char *child_out_buf;
char *child_out_buf_next;
size_t child_out_buf_len, child_out_buf_avail;
const char *child_in_buf;
size_t child_in_buf_avail;
};
static int archive_decompressor_program_bid(const void *, size_t);
static int archive_decompressor_program_finish(struct archive_read *);
static int archive_decompressor_program_init(struct archive_read *,
const void *, size_t);
static ssize_t archive_decompressor_program_read_ahead(struct archive_read *,
const void **, size_t);
static ssize_t archive_decompressor_program_read_consume(struct archive_read *,
size_t);
int
archive_read_support_compression_program(struct archive *_a, const char *cmd)
{
struct archive_read *a = (struct archive_read *)_a;
struct decompressor_t *decompressor;
if (cmd == NULL || *cmd == '\0')
return (ARCHIVE_WARN);
decompressor = __archive_read_register_compression(a,
archive_decompressor_program_bid,
archive_decompressor_program_init);
if (decompressor == NULL)
return (ARCHIVE_WARN);
decompressor->config = strdup(cmd);
return (ARCHIVE_OK);
}
/*
* If the user used us to register, they must really want us to
* handle it, so this module always bids INT_MAX.
*/
static int
archive_decompressor_program_bid(const void *buff, size_t len)
{
(void)buff; /* UNUSED */
(void)len; /* UNUSED */
return (INT_MAX); /* Default: We'll take it. */
}
static ssize_t
child_read(struct archive_read *a, char *buf, size_t buf_len)
{
struct archive_decompress_program *state = a->decompressor->data;
ssize_t ret, requested;
if (state->child_stdout == -1)
return (-1);
if (buf_len == 0)
return (-1);
restart_read:
requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
do {
ret = read(state->child_stdout, buf, requested);
} while (ret == -1 && errno == EINTR);
if (ret > 0)
return (ret);
if (ret == 0 || (ret == -1 && errno == EPIPE)) {
close(state->child_stdout);
state->child_stdout = -1;
return (0);
}
if (ret == -1 && errno != EAGAIN)
return (-1);
if (state->child_in_buf_avail == 0) {
ret = (a->client_reader)(&a->archive,
a->client_data, (const void **)&state->child_in_buf);
if (ret < 0) {
close(state->child_stdin);
state->child_stdin = -1;
fcntl(state->child_stdout, F_SETFL, 0);
return (-1);
}
if (ret == 0) {
close(state->child_stdin);
state->child_stdin = -1;
fcntl(state->child_stdout, F_SETFL, 0);
goto restart_read;
}
state->child_in_buf_avail = ret;
}
do {
ret = write(state->child_stdin, state->child_in_buf,
state->child_in_buf_avail);
} while (ret == -1 && errno == EINTR);
if (ret > 0) {
state->child_in_buf += ret;
state->child_in_buf_avail -= ret;
goto restart_read;
} else if (ret == -1 && errno == EAGAIN) {
__archive_check_child(state->child_stdin, state->child_stdout);
goto restart_read;
} else if (ret == 0 || (ret == -1 && errno == EPIPE)) {
close(state->child_stdin);
state->child_stdout = -1;
fcntl(state->child_stdout, F_SETFL, 0);
goto restart_read;
} else {
close(state->child_stdin);
state->child_stdin = -1;
fcntl(state->child_stdout, F_SETFL, 0);
return (-1);
}
}
static int
archive_decompressor_program_init(struct archive_read *a, const void *buff, size_t n)
{
struct archive_decompress_program *state;
const char *cmd = a->decompressor->config;
const char *prefix = "Program: ";
state = (struct archive_decompress_program *)malloc(sizeof(*state));
if (!state) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate input data");
return (ARCHIVE_FATAL);
}
a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
strcpy(state->description, prefix);
strcat(state->description, cmd);
a->archive.compression_name = state->description;
state->child_out_buf_next = state->child_out_buf = malloc(65536);
if (!state->child_out_buf) {
free(state);
archive_set_error(&a->archive, ENOMEM,
"Can't allocate filter buffer");
return (ARCHIVE_FATAL);
}
state->child_out_buf_len = 65536;
state->child_out_buf_avail = 0;
state->child_in_buf = buff;
state->child_in_buf_avail = n;
if ((state->child = __archive_create_child(cmd,
&state->child_stdin, &state->child_stdout)) == -1) {
free(state->child_out_buf);
free(state);
archive_set_error(&a->archive, EINVAL,
"Can't initialise filter");
return (ARCHIVE_FATAL);
}
a->decompressor->data = state;
a->decompressor->read_ahead = archive_decompressor_program_read_ahead;
a->decompressor->consume = archive_decompressor_program_read_consume;
a->decompressor->skip = NULL;
a->decompressor->finish = archive_decompressor_program_finish;
/* XXX Check that we can read at least one byte? */
return (ARCHIVE_OK);
}
static ssize_t
archive_decompressor_program_read_ahead(struct archive_read *a, const void **buff,
size_t min)
{
struct archive_decompress_program *state;
ssize_t bytes_read;
state = (struct archive_decompress_program *)a->decompressor->data;
if (min > state->child_out_buf_len)
min = state->child_out_buf_len;
while (state->child_stdout != -1 && min > state->child_out_buf_avail) {
if (state->child_out_buf != state->child_out_buf_next) {
memmove(state->child_out_buf, state->child_out_buf_next,
state->child_out_buf_avail);
state->child_out_buf_next = state->child_out_buf;
}
bytes_read = child_read(a,
state->child_out_buf + state->child_out_buf_avail,
state->child_out_buf_len - state->child_out_buf_avail);
if (bytes_read == -1)
return (-1);
if (bytes_read == 0)
break;
state->child_out_buf_avail += bytes_read;
a->archive.raw_position += bytes_read;
}
*buff = state->child_out_buf_next;
return (state->child_out_buf_avail);
}
static ssize_t
archive_decompressor_program_read_consume(struct archive_read *a, size_t request)
{
struct archive_decompress_program *state;
state = (struct archive_decompress_program *)a->decompressor->data;
state->child_out_buf_next += request;
state->child_out_buf_avail -= request;
a->archive.file_position += request;
return (request);
}
static int
archive_decompressor_program_finish(struct archive_read *a)
{
struct archive_decompress_program *state;
int status;
state = (struct archive_decompress_program *)a->decompressor->data;
/* Release our configuration data. */
free(a->decompressor->config);
a->decompressor->config = NULL;
/* Shut down the child. */
if (state->child_stdin != -1)
close(state->child_stdin);
if (state->child_stdout != -1)
close(state->child_stdout);
while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
continue;
/* Release our private data. */
free(state->child_out_buf);
free(state->description);
free(state);
a->decompressor->data = NULL;
return (ARCHIVE_OK);
}

View File

@ -126,10 +126,11 @@ archive_read_format_ar_cleanup(struct archive_read *a)
{
struct ar *ar;
ar = (struct ar *)*(a->pformat_data);
free(ar->strtab);
ar = (struct ar *)(a->format->data);
if (ar->strtab)
free(ar->strtab);
free(ar);
*(a->pformat_data) = NULL;
(a->format->data) = NULL;
return (ARCHIVE_OK);
}
@ -145,7 +146,7 @@ archive_read_format_ar_bid(struct archive_read *a)
ARCHIVE_FORMAT_AR)
return(0);
ar = (struct ar *)*(a->pformat_data);
ar = (struct ar *)(a->format->data);
if (ar->bid > 0)
return (ar->bid);
@ -154,7 +155,7 @@ archive_read_format_ar_bid(struct archive_read *a)
* Verify the 8-byte file signature.
* TODO: Do we need to check more than this?
*/
bytes_read = (a->compression_read_ahead)(a, &h, 8);
bytes_read = (a->decompressor->read_ahead)(a, &h, 8);
if (bytes_read < 8)
return (-1);
if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
@ -178,25 +179,25 @@ archive_read_format_ar_read_header(struct archive_read *a,
const char *h;
int r;
ar = (struct ar*)*(a->pformat_data);
ar = (struct ar*)(a->format->data);
if (a->archive.file_position == 0) {
/*
* We are now at the beginning of the archive,
* so we need first consume the ar global header.
*/
(a->compression_read_consume)(a, 8);
(a->decompressor->consume)(a, 8);
/* Set a default format code for now. */
a->archive.archive_format = ARCHIVE_FORMAT_AR;
}
/* Read the header for the next file entry. */
bytes_read = (a->compression_read_ahead)(a, &b, 60);
bytes_read = (a->decompressor->read_ahead)(a, &b, 60);
if (bytes_read < 60) {
/* Broken header. */
return (ARCHIVE_EOF);
}
(a->compression_read_consume)(a, 60);
(a->decompressor->consume)(a, 60);
h = (const char *)b;
/* Verify the magic signature on the file header. */
@ -284,7 +285,7 @@ archive_read_format_ar_read_header(struct archive_read *a,
}
entry_size = (size_t)number;
/* Read the filename table into memory. */
bytes_read = (a->compression_read_ahead)(a, &b, entry_size);
bytes_read = (a->decompressor->read_ahead)(a, &b, entry_size);
if (bytes_read <= 0)
return (ARCHIVE_FATAL);
if ((size_t)bytes_read < entry_size) {
@ -348,7 +349,7 @@ archive_read_format_ar_read_header(struct archive_read *a,
archive_entry_set_size(entry, ar->entry_bytes_remaining);
/* Read the long name into memory. */
bytes_read = (a->compression_read_ahead)(a, &b, bsd_name_length);
bytes_read = (a->decompressor->read_ahead)(a, &b, bsd_name_length);
if (bytes_read <= 0)
return (ARCHIVE_FATAL);
if ((size_t)bytes_read < bsd_name_length) {
@ -356,7 +357,7 @@ archive_read_format_ar_read_header(struct archive_read *a,
"Truncated input file");
return (ARCHIVE_FATAL);
}
(a->compression_read_consume)(a, bsd_name_length);
(a->decompressor->consume)(a, bsd_name_length);
/* Store it in the entry. */
p = (char *)malloc(bsd_name_length + 1);
@ -434,10 +435,10 @@ archive_read_format_ar_read_data(struct archive_read *a,
ssize_t bytes_read;
struct ar *ar;
ar = (struct ar *)*(a->pformat_data);
ar = (struct ar *)(a->format->data);
if (ar->entry_bytes_remaining > 0) {
bytes_read = (a->compression_read_ahead)(a, buff, 1);
bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
if (bytes_read == 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Truncated ar archive");
@ -451,16 +452,16 @@ archive_read_format_ar_read_data(struct archive_read *a,
*offset = ar->entry_offset;
ar->entry_offset += bytes_read;
ar->entry_bytes_remaining -= bytes_read;
(a->compression_read_consume)(a, (size_t)bytes_read);
(a->decompressor->consume)(a, (size_t)bytes_read);
return (ARCHIVE_OK);
} else {
while (ar->entry_padding > 0) {
bytes_read = (a->compression_read_ahead)(a, buff, 1);
bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
if (bytes_read <= 0)
return (ARCHIVE_FATAL);
if (bytes_read > ar->entry_padding)
bytes_read = (ssize_t)ar->entry_padding;
(a->compression_read_consume)(a, (size_t)bytes_read);
(a->decompressor->consume)(a, (size_t)bytes_read);
ar->entry_padding -= bytes_read;
}
*buff = NULL;
@ -480,14 +481,14 @@ archive_read_format_ar_skip(struct archive_read *a)
size_t s;
off_t o;
ar = (struct ar *)*(a->pformat_data);
if (a->compression_skip == NULL) {
ar = (struct ar *)(a->format->data);
if (a->decompressor->skip == NULL) {
while (r == ARCHIVE_OK)
r = archive_read_format_ar_read_data(a, &b, &s, &o);
return (r);
}
bytes_skipped = (a->compression_skip)(a, ar->entry_bytes_remaining +
bytes_skipped = (a->decompressor->skip)(a, ar->entry_bytes_remaining +
ar->entry_padding);
if (bytes_skipped < 0)
return (ARCHIVE_FATAL);

View File

@ -26,15 +26,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -45,9 +36,6 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "archive.h"
#include "archive_entry.h"
@ -112,7 +100,7 @@ struct links_entry {
struct cpio {
int magic;
int (*read_header)(struct archive_read *, struct cpio *,
struct stat *, size_t *, size_t *);
struct archive_entry *, size_t *, size_t *);
struct links_entry *links_head;
struct archive_string entry_name;
struct archive_string entry_linkname;
@ -130,17 +118,16 @@ static int archive_read_format_cpio_read_data(struct archive_read *,
static int archive_read_format_cpio_read_header(struct archive_read *,
struct archive_entry *);
static int be4(const unsigned char *);
static int header_bin_be(struct archive_read *, struct cpio *, struct stat *,
size_t *, size_t *);
static int header_bin_le(struct archive_read *, struct cpio *, struct stat *,
size_t *, size_t *);
static int header_newc(struct archive_read *, struct cpio *, struct stat *,
size_t *, size_t *);
static int header_odc(struct archive_read *, struct cpio *, struct stat *,
size_t *, size_t *);
static int header_bin_be(struct archive_read *, struct cpio *,
struct archive_entry *, size_t *, size_t *);
static int header_bin_le(struct archive_read *, struct cpio *,
struct archive_entry *, size_t *, size_t *);
static int header_newc(struct archive_read *, struct cpio *,
struct archive_entry *, size_t *, size_t *);
static int header_odc(struct archive_read *, struct cpio *,
struct archive_entry *, size_t *, size_t *);
static int le4(const unsigned char *);
static void record_hardlink(struct cpio *cpio, struct archive_entry *entry,
const struct stat *st);
static void record_hardlink(struct cpio *cpio, struct archive_entry *entry);
int
archive_read_support_format_cpio(struct archive *_a)
@ -179,9 +166,9 @@ archive_read_format_cpio_bid(struct archive_read *a)
const unsigned char *p;
struct cpio *cpio;
cpio = (struct cpio *)*(a->pformat_data);
cpio = (struct cpio *)(a->format->data);
bid = 0;
bytes_read = (a->compression_read_ahead)(a, &h, 6);
bytes_read = (a->decompressor->read_ahead)(a, &h, 6);
/* Convert error code into error return. */
if (bytes_read < 0)
return ((int)bytes_read);
@ -234,7 +221,6 @@ static int
archive_read_format_cpio_read_header(struct archive_read *a,
struct archive_entry *entry)
{
struct stat st;
struct cpio *cpio;
size_t bytes;
const void *h;
@ -242,33 +228,28 @@ archive_read_format_cpio_read_header(struct archive_read *a,
size_t name_pad;
int r;
memset(&st, 0, sizeof(st));
cpio = (struct cpio *)*(a->pformat_data);
r = (cpio->read_header(a, cpio, &st, &namelength, &name_pad));
cpio = (struct cpio *)(a->format->data);
r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
if (r != ARCHIVE_OK)
return (r);
/* Assign all of the 'stat' fields at once. */
archive_entry_copy_stat(entry, &st);
/* Read name from buffer. */
bytes = (a->compression_read_ahead)(a, &h, namelength + name_pad);
bytes = (a->decompressor->read_ahead)(a, &h, namelength + name_pad);
if (bytes < namelength + name_pad)
return (ARCHIVE_FATAL);
(a->compression_read_consume)(a, namelength + name_pad);
(a->decompressor->consume)(a, namelength + name_pad);
archive_strncpy(&cpio->entry_name, (const char *)h, namelength);
archive_entry_set_pathname(entry, cpio->entry_name.s);
cpio->entry_offset = 0;
/* If this is a symlink, read the link contents. */
if (S_ISLNK(st.st_mode)) {
bytes = (a->compression_read_ahead)(a, &h,
if (archive_entry_filetype(entry) == AE_IFLNK) {
bytes = (a->decompressor->read_ahead)(a, &h,
cpio->entry_bytes_remaining);
if ((off_t)bytes < cpio->entry_bytes_remaining)
return (ARCHIVE_FATAL);
(a->compression_read_consume)(a, cpio->entry_bytes_remaining);
(a->decompressor->consume)(a, cpio->entry_bytes_remaining);
archive_strncpy(&cpio->entry_linkname, (const char *)h,
cpio->entry_bytes_remaining);
archive_entry_set_symlink(entry, cpio->entry_linkname.s);
@ -283,7 +264,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
}
/* Detect and record hardlinks to previously-extracted entries. */
record_hardlink(cpio, entry, &st);
record_hardlink(cpio, entry);
return (ARCHIVE_OK);
}
@ -295,9 +276,9 @@ archive_read_format_cpio_read_data(struct archive_read *a,
ssize_t bytes_read;
struct cpio *cpio;
cpio = (struct cpio *)*(a->pformat_data);
cpio = (struct cpio *)(a->format->data);
if (cpio->entry_bytes_remaining > 0) {
bytes_read = (a->compression_read_ahead)(a, buff, 1);
bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
if (bytes_read <= 0)
return (ARCHIVE_FATAL);
if (bytes_read > cpio->entry_bytes_remaining)
@ -306,16 +287,16 @@ archive_read_format_cpio_read_data(struct archive_read *a,
*offset = cpio->entry_offset;
cpio->entry_offset += bytes_read;
cpio->entry_bytes_remaining -= bytes_read;
(a->compression_read_consume)(a, bytes_read);
(a->decompressor->consume)(a, bytes_read);
return (ARCHIVE_OK);
} else {
while (cpio->entry_padding > 0) {
bytes_read = (a->compression_read_ahead)(a, buff, 1);
bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
if (bytes_read <= 0)
return (ARCHIVE_FATAL);
if (bytes_read > cpio->entry_padding)
bytes_read = cpio->entry_padding;
(a->compression_read_consume)(a, bytes_read);
(a->decompressor->consume)(a, bytes_read);
cpio->entry_padding -= bytes_read;
}
*buff = NULL;
@ -326,20 +307,20 @@ archive_read_format_cpio_read_data(struct archive_read *a,
}
static int
header_newc(struct archive_read *a, struct cpio *cpio, struct stat *st,
size_t *namelength, size_t *name_pad)
header_newc(struct archive_read *a, struct cpio *cpio,
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
const void *h;
const struct cpio_newc_header *header;
size_t bytes;
/* Read fixed-size portion of header. */
bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_newc_header));
bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header));
if (bytes < sizeof(struct cpio_newc_header))
return (ARCHIVE_FATAL);
(a->compression_read_consume)(a, sizeof(struct cpio_newc_header));
(a->decompressor->consume)(a, sizeof(struct cpio_newc_header));
/* Parse out hex fields into struct stat. */
/* Parse out hex fields. */
header = (const struct cpio_newc_header *)h;
if (memcmp(header->c_magic, "070701", 6) == 0) {
@ -352,18 +333,16 @@ header_newc(struct archive_read *a, struct cpio *cpio, struct stat *st,
/* TODO: Abort here? */
}
st->st_dev = makedev(
atol16(header->c_devmajor, sizeof(header->c_devmajor)),
atol16(header->c_devminor, sizeof(header->c_devminor)));
st->st_ino = atol16(header->c_ino, sizeof(header->c_ino));
st->st_mode = atol16(header->c_mode, sizeof(header->c_mode));
st->st_uid = atol16(header->c_uid, sizeof(header->c_uid));
st->st_gid = atol16(header->c_gid, sizeof(header->c_gid));
st->st_nlink = atol16(header->c_nlink, sizeof(header->c_nlink));
st->st_rdev = makedev(
atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor)),
atol16(header->c_rdevminor, sizeof(header->c_rdevminor)));
st->st_mtime = atol16(header->c_mtime, sizeof(header->c_mtime));
archive_entry_set_devmajor(entry, atol16(header->c_devmajor, sizeof(header->c_devmajor)));
archive_entry_set_devminor(entry, atol16(header->c_devminor, sizeof(header->c_devminor)));
archive_entry_set_ino(entry, atol16(header->c_ino, sizeof(header->c_ino)));
archive_entry_set_mode(entry, atol16(header->c_mode, sizeof(header->c_mode)));
archive_entry_set_uid(entry, atol16(header->c_uid, sizeof(header->c_uid)));
archive_entry_set_gid(entry, atol16(header->c_gid, sizeof(header->c_gid)));
archive_entry_set_nlink(entry, atol16(header->c_nlink, sizeof(header->c_nlink)));
archive_entry_set_rdevmajor(entry, atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor)));
archive_entry_set_rdevminor(entry, atol16(header->c_rdevminor, sizeof(header->c_rdevminor)));
archive_entry_set_mtime(entry, atol16(header->c_mtime, sizeof(header->c_mtime)), 0);
*namelength = atol16(header->c_namesize, sizeof(header->c_namesize));
/* Pad name to 2 more than a multiple of 4. */
*name_pad = (2 - *namelength) & 3;
@ -371,20 +350,19 @@ header_newc(struct archive_read *a, struct cpio *cpio, struct stat *st,
/*
* Note: entry_bytes_remaining is at least 64 bits and
* therefore guaranteed to be big enough for a 33-bit file
* size. struct stat.st_size may only be 32 bits, so
* assigning there first could lose information.
* size.
*/
cpio->entry_bytes_remaining =
atol16(header->c_filesize, sizeof(header->c_filesize));
st->st_size = cpio->entry_bytes_remaining;
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
/* Pad file contents to a multiple of 4. */
cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
return (ARCHIVE_OK);
}
static int
header_odc(struct archive_read *a, struct cpio *cpio, struct stat *st,
size_t *namelength, size_t *name_pad)
header_odc(struct archive_read *a, struct cpio *cpio,
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
const void *h;
const struct cpio_odc_header *header;
@ -394,41 +372,40 @@ header_odc(struct archive_read *a, struct cpio *cpio, struct stat *st,
a->archive.archive_format_name = "POSIX octet-oriented cpio";
/* Read fixed-size portion of header. */
bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_odc_header));
bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header));
if (bytes < sizeof(struct cpio_odc_header))
return (ARCHIVE_FATAL);
(a->compression_read_consume)(a, sizeof(struct cpio_odc_header));
(a->decompressor->consume)(a, sizeof(struct cpio_odc_header));
/* Parse out octal fields into struct stat. */
/* Parse out octal fields. */
header = (const struct cpio_odc_header *)h;
st->st_dev = atol8(header->c_dev, sizeof(header->c_dev));
st->st_ino = atol8(header->c_ino, sizeof(header->c_ino));
st->st_mode = atol8(header->c_mode, sizeof(header->c_mode));
st->st_uid = atol8(header->c_uid, sizeof(header->c_uid));
st->st_gid = atol8(header->c_gid, sizeof(header->c_gid));
st->st_nlink = atol8(header->c_nlink, sizeof(header->c_nlink));
st->st_rdev = atol8(header->c_rdev, sizeof(header->c_rdev));
st->st_mtime = atol8(header->c_mtime, sizeof(header->c_mtime));
archive_entry_set_dev(entry, atol8(header->c_dev, sizeof(header->c_dev)));
archive_entry_set_ino(entry, atol8(header->c_ino, sizeof(header->c_ino)));
archive_entry_set_mode(entry, atol8(header->c_mode, sizeof(header->c_mode)));
archive_entry_set_uid(entry, atol8(header->c_uid, sizeof(header->c_uid)));
archive_entry_set_gid(entry, atol8(header->c_gid, sizeof(header->c_gid)));
archive_entry_set_nlink(entry, atol8(header->c_nlink, sizeof(header->c_nlink)));
archive_entry_set_rdev(entry, atol8(header->c_rdev, sizeof(header->c_rdev)));
archive_entry_set_mtime(entry, atol8(header->c_mtime, sizeof(header->c_mtime)), 0);
*namelength = atol8(header->c_namesize, sizeof(header->c_namesize));
*name_pad = 0; /* No padding of filename. */
/*
* Note: entry_bytes_remaining is at least 64 bits and
* therefore guaranteed to be big enough for a 33-bit file
* size. struct stat.st_size may only be 32 bits, so
* assigning there first could lose information.
* size.
*/
cpio->entry_bytes_remaining =
atol8(header->c_filesize, sizeof(header->c_filesize));
st->st_size = cpio->entry_bytes_remaining;
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
cpio->entry_padding = 0;
return (ARCHIVE_OK);
}
static int
header_bin_le(struct archive_read *a, struct cpio *cpio, struct stat *st,
size_t *namelength, size_t *name_pad)
header_bin_le(struct archive_read *a, struct cpio *cpio,
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
const void *h;
const struct cpio_bin_header *header;
@ -438,34 +415,34 @@ header_bin_le(struct archive_read *a, struct cpio *cpio, struct stat *st,
a->archive.archive_format_name = "cpio (little-endian binary)";
/* Read fixed-size portion of header. */
bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_bin_header));
bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_bin_header));
if (bytes < sizeof(struct cpio_bin_header))
return (ARCHIVE_FATAL);
(a->compression_read_consume)(a, sizeof(struct cpio_bin_header));
(a->decompressor->consume)(a, sizeof(struct cpio_bin_header));
/* Parse out binary fields into struct stat. */
/* Parse out binary fields. */
header = (const struct cpio_bin_header *)h;
st->st_dev = header->c_dev[0] + header->c_dev[1] * 256;
st->st_ino = header->c_ino[0] + header->c_ino[1] * 256;
st->st_mode = header->c_mode[0] + header->c_mode[1] * 256;
st->st_uid = header->c_uid[0] + header->c_uid[1] * 256;
st->st_gid = header->c_gid[0] + header->c_gid[1] * 256;
st->st_nlink = header->c_nlink[0] + header->c_nlink[1] * 256;
st->st_rdev = header->c_rdev[0] + header->c_rdev[1] * 256;
st->st_mtime = le4(header->c_mtime);
archive_entry_set_dev(entry, header->c_dev[0] + header->c_dev[1] * 256);
archive_entry_set_ino(entry, header->c_ino[0] + header->c_ino[1] * 256);
archive_entry_set_mode(entry, header->c_mode[0] + header->c_mode[1] * 256);
archive_entry_set_uid(entry, header->c_uid[0] + header->c_uid[1] * 256);
archive_entry_set_gid(entry, header->c_gid[0] + header->c_gid[1] * 256);
archive_entry_set_nlink(entry, header->c_nlink[0] + header->c_nlink[1] * 256);
archive_entry_set_rdev(entry, header->c_rdev[0] + header->c_rdev[1] * 256);
archive_entry_set_mtime(entry, le4(header->c_mtime), 0);
*namelength = header->c_namesize[0] + header->c_namesize[1] * 256;
*name_pad = *namelength & 1; /* Pad to even. */
cpio->entry_bytes_remaining = le4(header->c_filesize);
st->st_size = cpio->entry_bytes_remaining;
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
return (ARCHIVE_OK);
}
static int
header_bin_be(struct archive_read *a, struct cpio *cpio, struct stat *st,
size_t *namelength, size_t *name_pad)
header_bin_be(struct archive_read *a, struct cpio *cpio,
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
const void *h;
const struct cpio_bin_header *header;
@ -475,27 +452,27 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, struct stat *st,
a->archive.archive_format_name = "cpio (big-endian binary)";
/* Read fixed-size portion of header. */
bytes = (a->compression_read_ahead)(a, &h,
bytes = (a->decompressor->read_ahead)(a, &h,
sizeof(struct cpio_bin_header));
if (bytes < sizeof(struct cpio_bin_header))
return (ARCHIVE_FATAL);
(a->compression_read_consume)(a, sizeof(struct cpio_bin_header));
(a->decompressor->consume)(a, sizeof(struct cpio_bin_header));
/* Parse out binary fields into struct stat. */
/* Parse out binary fields. */
header = (const struct cpio_bin_header *)h;
st->st_dev = header->c_dev[0] * 256 + header->c_dev[1];
st->st_ino = header->c_ino[0] * 256 + header->c_ino[1];
st->st_mode = header->c_mode[0] * 256 + header->c_mode[1];
st->st_uid = header->c_uid[0] * 256 + header->c_uid[1];
st->st_gid = header->c_gid[0] * 256 + header->c_gid[1];
st->st_nlink = header->c_nlink[0] * 256 + header->c_nlink[1];
st->st_rdev = header->c_rdev[0] * 256 + header->c_rdev[1];
st->st_mtime = be4(header->c_mtime);
archive_entry_set_dev(entry, header->c_dev[0] * 256 + header->c_dev[1]);
archive_entry_set_ino(entry, header->c_ino[0] * 256 + header->c_ino[1]);
archive_entry_set_mode(entry, header->c_mode[0] * 256 + header->c_mode[1]);
archive_entry_set_uid(entry, header->c_uid[0] * 256 + header->c_uid[1]);
archive_entry_set_gid(entry, header->c_gid[0] * 256 + header->c_gid[1]);
archive_entry_set_nlink(entry, header->c_nlink[0] * 256 + header->c_nlink[1]);
archive_entry_set_rdev(entry, header->c_rdev[0] * 256 + header->c_rdev[1]);
archive_entry_set_mtime(entry, be4(header->c_mtime), 0);
*namelength = header->c_namesize[0] * 256 + header->c_namesize[1];
*name_pad = *namelength & 1; /* Pad to even. */
cpio->entry_bytes_remaining = be4(header->c_filesize);
st->st_size = cpio->entry_bytes_remaining;
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
return (ARCHIVE_OK);
}
@ -505,7 +482,7 @@ archive_read_format_cpio_cleanup(struct archive_read *a)
{
struct cpio *cpio;
cpio = (struct cpio *)*(a->pformat_data);
cpio = (struct cpio *)(a->format->data);
/* Free inode->name map */
while (cpio->links_head != NULL) {
struct links_entry *lp = cpio->links_head->next;
@ -515,9 +492,9 @@ archive_read_format_cpio_cleanup(struct archive_read *a)
free(cpio->links_head);
cpio->links_head = lp;
}
archive_string_free(&cpio->entry_name);
free(cpio);
*(a->pformat_data) = NULL;
(a->format->data) = NULL;
return (ARCHIVE_OK);
}
@ -582,17 +559,21 @@ atol16(const char *p, unsigned char_cnt)
}
static void
record_hardlink(struct cpio *cpio, struct archive_entry *entry,
const struct stat *st)
record_hardlink(struct cpio *cpio, struct archive_entry *entry)
{
struct links_entry *le;
dev_t dev;
ino_t ino;
dev = archive_entry_dev(entry);
ino = archive_entry_ino(entry);
/*
* First look in the list of multiply-linked files. If we've
* already dumped it, convert this entry to a hard link entry.
*/
for (le = cpio->links_head; le; le = le->next) {
if (le->dev == st->st_dev && le->ino == st->st_ino) {
if (le->dev == dev && le->ino == ino) {
archive_entry_set_hardlink(entry, le->name);
if (--le->links <= 0) {
@ -617,9 +598,9 @@ record_hardlink(struct cpio *cpio, struct archive_entry *entry,
le->next = cpio->links_head;
le->previous = NULL;
cpio->links_head = le;
le->dev = st->st_dev;
le->ino = st->st_ino;
le->links = st->st_nlink - 1;
le->dev = dev;
le->ino = ino;
le->links = archive_entry_nlink(entry) - 1;
le->name = strdup(archive_entry_pathname(entry));
if (le->name == NULL)
__archive_errx(1, "Out of memory adding file to list");

View File

@ -60,7 +60,7 @@ archive_read_format_empty_bid(struct archive_read *a)
int bytes_read;
const void *h;
bytes_read = (a->compression_read_ahead)(a, &h, 1);
bytes_read = (a->decompressor->read_ahead)(a, &h, 1);
if (bytes_read > 0)
return (-1);
return (1);

View File

@ -26,10 +26,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -42,9 +38,6 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#endif
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "archive.h"
#include "archive_entry.h"
@ -288,7 +281,7 @@ archive_read_format_iso9660_bid(struct archive_read *a)
const void *h;
const unsigned char *p;
iso9660 = (struct iso9660 *)*(a->pformat_data);
iso9660 = (struct iso9660 *)(a->format->data);
if (iso9660->bid >= 0)
return (iso9660->bid);
@ -298,7 +291,7 @@ archive_read_format_iso9660_bid(struct archive_read *a)
* 8 sectors of the volume descriptor table. Of course,
* if the I/O layer gives us more, we'll take it.
*/
bytes_read = (a->compression_read_ahead)(a, &h, 32768 + 8*2048);
bytes_read = (a->decompressor->read_ahead)(a, &h, 32768 + 8*2048);
if (bytes_read < 32768 + 8*2048)
return (iso9660->bid = -1);
p = (const unsigned char *)h;
@ -343,13 +336,12 @@ static int
archive_read_format_iso9660_read_header(struct archive_read *a,
struct archive_entry *entry)
{
struct stat st;
struct iso9660 *iso9660;
struct file_info *file;
ssize_t bytes_read;
int r;
iso9660 = (struct iso9660 *)*(a->pformat_data);
iso9660 = (struct iso9660 *)(a->format->data);
if (!a->archive.archive_format) {
a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
@ -365,17 +357,15 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */
/* Set up the entry structure with information about this entry. */
memset(&st, 0, sizeof(st));
st.st_mode = file->mode;
st.st_uid = file->uid;
st.st_gid = file->gid;
st.st_nlink = file->nlinks;
st.st_ino = file->inode;
st.st_mtime = file->mtime;
st.st_ctime = file->ctime;
st.st_atime = file->atime;
st.st_size = iso9660->entry_bytes_remaining;
archive_entry_copy_stat(entry, &st);
archive_entry_set_mode(entry, file->mode);
archive_entry_set_uid(entry, file->uid);
archive_entry_set_gid(entry, file->gid);
archive_entry_set_nlink(entry, file->nlinks);
archive_entry_set_ino(entry, file->inode);
archive_entry_set_mtime(entry, file->mtime, 0);
archive_entry_set_ctime(entry, file->ctime, 0);
archive_entry_set_atime(entry, file->atime, 0);
archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
archive_string_empty(&iso9660->pathname);
archive_entry_set_pathname(entry,
build_pathname(&iso9660->pathname, file));
@ -412,14 +402,14 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
archive_strcpy(&iso9660->previous_pathname, iso9660->pathname.s);
/* If this is a directory, read in all of the entries right now. */
if (S_ISDIR(st.st_mode)) {
if (archive_entry_filetype(entry) == AE_IFDIR) {
while (iso9660->entry_bytes_remaining > 0) {
const void *block;
const unsigned char *p;
ssize_t step = iso9660->logical_block_size;
if (step > iso9660->entry_bytes_remaining)
step = iso9660->entry_bytes_remaining;
bytes_read = (a->compression_read_ahead)(a, &block, step);
bytes_read = (a->decompressor->read_ahead)(a, &block, step);
if (bytes_read < step) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Failed to read full block when scanning ISO9660 directory list");
@ -428,7 +418,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
}
if (bytes_read > step)
bytes_read = step;
(a->compression_read_consume)(a, bytes_read);
(a->decompressor->consume)(a, bytes_read);
iso9660->current_position += bytes_read;
iso9660->entry_bytes_remaining -= bytes_read;
for (p = (const unsigned char *)block;
@ -476,7 +466,7 @@ archive_read_format_iso9660_read_data(struct archive_read *a,
ssize_t bytes_read;
struct iso9660 *iso9660;
iso9660 = (struct iso9660 *)*(a->pformat_data);
iso9660 = (struct iso9660 *)(a->format->data);
if (iso9660->entry_bytes_remaining <= 0) {
*buff = NULL;
*size = 0;
@ -484,7 +474,7 @@ archive_read_format_iso9660_read_data(struct archive_read *a,
return (ARCHIVE_EOF);
}
bytes_read = (a->compression_read_ahead)(a, buff, 1);
bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
if (bytes_read == 0)
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Truncated input file");
@ -497,7 +487,7 @@ archive_read_format_iso9660_read_data(struct archive_read *a,
iso9660->entry_sparse_offset += bytes_read;
iso9660->entry_bytes_remaining -= bytes_read;
iso9660->current_position += bytes_read;
(a->compression_read_consume)(a, bytes_read);
(a->decompressor->consume)(a, bytes_read);
return (ARCHIVE_OK);
}
@ -507,13 +497,15 @@ archive_read_format_iso9660_cleanup(struct archive_read *a)
struct iso9660 *iso9660;
struct file_info *file;
iso9660 = (struct iso9660 *)*(a->pformat_data);
iso9660 = (struct iso9660 *)(a->format->data);
while ((file = next_entry(iso9660)) != NULL)
release_file(iso9660, file);
archive_string_free(&iso9660->pathname);
archive_string_free(&iso9660->previous_pathname);
if (iso9660->pending_files)
free(iso9660->pending_files);
free(iso9660);
*(a->pformat_data) = NULL;
(a->format->data) = NULL;
return (ARCHIVE_OK);
}
@ -554,9 +546,9 @@ parse_file_info(struct iso9660 *iso9660, struct file_info *parent,
file->name[name_len] = '\0';
flags = *(isodirrec + DR_flags_offset);
if (flags & 0x02)
file->mode = S_IFDIR | 0700;
file->mode = AE_IFDIR | 0700;
else
file->mode = S_IFREG | 0400;
file->mode = AE_IFREG | 0400;
/* Rockridge extensions overwrite information from above. */
{
@ -920,7 +912,7 @@ fprintf(stderr, " *** Discarding CE data.\n");
if (iso9660->current_position < offset) {
off_t step = offset - iso9660->current_position;
off_t bytes_read;
bytes_read = (a->compression_skip)(a, step);
bytes_read = (a->decompressor->skip)(a, step);
if (bytes_read < 0)
return (bytes_read);
iso9660->current_position = offset;
@ -939,13 +931,13 @@ fprintf(stderr, " *** Discarding CE data.\n");
file->ce_offset = 0;
file->ce_size = 0;
bytes_read = (a->compression_read_ahead)(a, &p, size);
bytes_read = (a->decompressor->read_ahead)(a, &p, size);
if (bytes_read > size)
bytes_read = size;
rr_start = (const unsigned char *)p;
parse_rockridge(iso9660, file, rr_start,
rr_start + bytes_read);
(a->compression_read_consume)(a, bytes_read);
(a->decompressor->consume)(a, bytes_read);
iso9660->current_position += bytes_read;
add_entry(iso9660, file);
}
@ -1053,34 +1045,37 @@ time_from_tm(struct tm *t)
{
#if HAVE_TIMEGM
return (timegm(t));
#else
#elif HAVE_STRUCT_TM_TM_GMTOFF
/*
* Unfortunately, timegm() isn't standard. The standard
* mktime() function is a close match, except that it uses
* local timezone instead of GMT. Close enough for now.
* Note that it is not possible to emulate timegm() using
* completely standard interfaces:
* * ANSI C90 does not even guarantee that time_t is
* an arithmetic type, so time adjustments can only be
* done by manipulating struct tm elements. You cannot
* portably calculate time_t values.
* * POSIX does promise that time_t is an arithmetic type
* measured in seconds, so you can do time_t calculations
* while remaining POSIX-compliant.
* * Neither ANSI nor POSIX provides an easy way to measure
* the timezone offset, so you can't adjust mktime() to
* work like timegm().
* * POSIX does not promise that the epoch begins in 1970,
* so you can't write a portable timegm() function from
* scratch.
* In practice, of course, mktime() is a reasonable approximation
* and most POSIX systems do use seconds since 1970, so you
* can roll your own and have it work on all but a few pretty
* whacky systems.
* local timezone instead of GMT. You can compensate for
* this by adding the timezone and DST offsets back in, at
* the cost of two calls to mktime().
*/
time_t result = mktime(t);
/* TODO: Find a way to improve this approximation to timegm(). */
return result;
mktime(t); /* Normalize the time and get the TZ offset. */
t->tm_sec += t->tm_gmtoff; /* Try to adjust for the timezone and DST.*/
if (t->tm_isdst)
t->tm_hour -= 1;
return (mktime(t)); /* Re-convert. */
#else
/*
* If you don't have tm_gmtoff, let's try resetting the timezone
* (yecch!).
*/
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "UTC 0", 1);
tzset();
ret = mktime(t);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
#endif
}

View File

@ -26,16 +26,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#else
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -47,9 +37,6 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/* Obtain suitable wide-character manipulation functions. */
#ifdef HAVE_WCHAR_H
@ -180,25 +167,25 @@ static int gnu_read_sparse_data(struct archive_read *, struct tar *,
static void gnu_parse_sparse_data(struct archive_read *, struct tar *,
const struct gnu_sparse *sparse, int length);
static int header_Solaris_ACL(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *);
struct archive_entry *, const void *);
static int header_common(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *);
struct archive_entry *, const void *);
static int header_old_tar(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *);
struct archive_entry *, const void *);
static int header_pax_extensions(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *);
struct archive_entry *, const void *);
static int header_pax_global(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *h);
struct archive_entry *, const void *h);
static int header_longlink(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *h);
struct archive_entry *, const void *h);
static int header_longname(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *h);
struct archive_entry *, const void *h);
static int header_volume(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *h);
struct archive_entry *, const void *h);
static int header_ustar(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *h);
struct archive_entry *, const void *h);
static int header_gnutar(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, const void *h);
struct archive_entry *, const void *h);
static int archive_read_format_tar_bid(struct archive_read *);
static int archive_read_format_tar_cleanup(struct archive_read *);
static int archive_read_format_tar_read_data(struct archive_read *a,
@ -207,10 +194,10 @@ static int archive_read_format_tar_skip(struct archive_read *a);
static int archive_read_format_tar_read_header(struct archive_read *,
struct archive_entry *);
static int checksum(struct archive_read *, const void *);
static int pax_attribute(struct archive_entry *, struct stat *,
static int pax_attribute(struct archive_entry *,
wchar_t *key, wchar_t *value);
static int pax_header(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *, char *attr);
struct archive_entry *, char *attr);
static void pax_time(const wchar_t *, int64_t *sec, long *nanos);
static int read_body_to_string(struct archive_read *, struct tar *,
struct archive_string *, const void *h);
@ -219,7 +206,7 @@ static int64_t tar_atol10(const wchar_t *, unsigned);
static int64_t tar_atol256(const char *, unsigned);
static int64_t tar_atol8(const char *, unsigned);
static int tar_read_header(struct archive_read *, struct tar *,
struct archive_entry *, struct stat *);
struct archive_entry *);
static int tohex(int c);
static char *url_decode(const char *);
static int utf8_decode(wchar_t *, const char *, size_t length);
@ -264,7 +251,7 @@ archive_read_format_tar_cleanup(struct archive_read *a)
{
struct tar *tar;
tar = (struct tar *)*(a->pformat_data);
tar = (struct tar *)(a->format->data);
archive_string_free(&tar->acl_text);
archive_string_free(&tar->entry_name);
archive_string_free(&tar->entry_linkname);
@ -275,7 +262,7 @@ archive_read_format_tar_cleanup(struct archive_read *a)
if (tar->pax_entry != NULL)
free(tar->pax_entry);
free(tar);
*(a->pformat_data) = NULL;
(a->format->data) = NULL;
return (ARCHIVE_OK);
}
@ -307,8 +294,8 @@ archive_read_format_tar_bid(struct archive_read *a)
bid++;
/* Now let's look at the actual header and see if it matches. */
if (a->compression_read_ahead != NULL)
bytes_read = (a->compression_read_ahead)(a, &h, 512);
if (a->decompressor->read_ahead != NULL)
bytes_read = (a->decompressor->read_ahead)(a, &h, 512);
else
bytes_read = 0; /* Empty file. */
if (bytes_read < 0)
@ -412,26 +399,24 @@ archive_read_format_tar_read_header(struct archive_read *a,
*/
static int default_inode;
static int default_dev;
struct stat st;
struct tar *tar;
const char *p;
int r;
size_t l;
memset(&st, 0, sizeof(st));
/* Assign default device/inode values. */
st.st_dev = 1 + default_dev; /* Don't use zero. */
st.st_ino = ++default_inode; /* Don't use zero. */
archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */
/* Limit generated st_ino number to 16 bits. */
if (default_inode >= 0xffff) {
++default_dev;
default_inode = 0;
}
tar = (struct tar *)*(a->pformat_data);
tar = (struct tar *)(a->format->data);
tar->entry_offset = 0;
r = tar_read_header(a, tar, entry, &st);
r = tar_read_header(a, tar, entry);
if (r == ARCHIVE_OK) {
/*
@ -441,13 +426,9 @@ archive_read_format_tar_read_header(struct archive_read *a,
*/
p = archive_entry_pathname(entry);
l = strlen(p);
if (S_ISREG(st.st_mode) && p[l-1] == '/') {
st.st_mode &= ~S_IFMT;
st.st_mode |= S_IFDIR;
}
/* Copy the final stat data into the entry. */
archive_entry_copy_stat(entry, &st);
if (archive_entry_filetype(entry) == AE_IFREG
&& p[l-1] == '/')
archive_entry_set_filetype(entry, AE_IFDIR);
}
return (r);
}
@ -460,7 +441,7 @@ archive_read_format_tar_read_data(struct archive_read *a,
struct tar *tar;
struct sparse_block *p;
tar = (struct tar *)*(a->pformat_data);
tar = (struct tar *)(a->format->data);
if (tar->sparse_list != NULL) {
/* Remove exhausted entries from sparse list. */
while (tar->sparse_list != NULL &&
@ -476,7 +457,7 @@ archive_read_format_tar_read_data(struct archive_read *a,
}
if (tar->entry_bytes_remaining > 0) {
bytes_read = (a->compression_read_ahead)(a, buff, 1);
bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
if (bytes_read == 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Truncated tar archive");
@ -499,10 +480,10 @@ archive_read_format_tar_read_data(struct archive_read *a,
*offset = tar->entry_offset;
tar->entry_offset += bytes_read;
tar->entry_bytes_remaining -= bytes_read;
(a->compression_read_consume)(a, bytes_read);
(a->decompressor->consume)(a, bytes_read);
return (ARCHIVE_OK);
} else {
if ((a->compression_skip)(a, tar->entry_padding) < 0)
if ((a->decompressor->skip)(a, tar->entry_padding) < 0)
return (ARCHIVE_FATAL);
tar->entry_padding = 0;
*buff = NULL;
@ -519,14 +500,14 @@ archive_read_format_tar_skip(struct archive_read *a)
struct tar* tar;
struct sparse_block *p;
tar = (struct tar *)*(a->pformat_data);
tar = (struct tar *)(a->format->data);
/*
* Compression layer skip functions are required to either skip the
* length requested or fail, so we can rely upon the entire entry
* plus padding being skipped.
*/
bytes_skipped = (a->compression_skip)(a, tar->entry_bytes_remaining +
bytes_skipped = (a->decompressor->skip)(a, tar->entry_bytes_remaining +
tar->entry_padding);
if (bytes_skipped < 0)
return (ARCHIVE_FATAL);
@ -550,7 +531,7 @@ archive_read_format_tar_skip(struct archive_read *a)
*/
static int
tar_read_header(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, struct stat *st)
struct archive_entry *entry)
{
ssize_t bytes;
int err;
@ -558,7 +539,7 @@ tar_read_header(struct archive_read *a, struct tar *tar,
const struct archive_entry_header_ustar *header;
/* Read 512-byte header record */
bytes = (a->compression_read_ahead)(a, &h, 512);
bytes = (a->decompressor->read_ahead)(a, &h, 512);
if (bytes < 512) {
/*
* If we're here, it's becase the _bid function accepted
@ -567,14 +548,14 @@ tar_read_header(struct archive_read *a, struct tar *tar,
*/
return (ARCHIVE_EOF);
}
(a->compression_read_consume)(a, 512);
(a->decompressor->consume)(a, 512);
/* Check for end-of-archive mark. */
if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) {
/* Try to consume a second all-null record, as well. */
bytes = (a->compression_read_ahead)(a, &h, 512);
bytes = (a->decompressor->read_ahead)(a, &h, 512);
if (bytes > 0)
(a->compression_read_consume)(a, bytes);
(a->decompressor->consume)(a, bytes);
archive_set_error(&a->archive, 0, NULL);
return (ARCHIVE_EOF);
}
@ -602,48 +583,48 @@ tar_read_header(struct archive_read *a, struct tar *tar,
case 'A': /* Solaris tar ACL */
a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
a->archive.archive_format_name = "Solaris tar";
err = header_Solaris_ACL(a, tar, entry, st, h);
err = header_Solaris_ACL(a, tar, entry, h);
break;
case 'g': /* POSIX-standard 'g' header. */
a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
a->archive.archive_format_name = "POSIX pax interchange format";
err = header_pax_global(a, tar, entry, st, h);
err = header_pax_global(a, tar, entry, h);
break;
case 'K': /* Long link name (GNU tar, others) */
err = header_longlink(a, tar, entry, st, h);
err = header_longlink(a, tar, entry, h);
break;
case 'L': /* Long filename (GNU tar, others) */
err = header_longname(a, tar, entry, st, h);
err = header_longname(a, tar, entry, h);
break;
case 'V': /* GNU volume header */
err = header_volume(a, tar, entry, st, h);
err = header_volume(a, tar, entry, h);
break;
case 'X': /* Used by SUN tar; same as 'x'. */
a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
a->archive.archive_format_name =
"POSIX pax interchange format (Sun variant)";
err = header_pax_extensions(a, tar, entry, st, h);
err = header_pax_extensions(a, tar, entry, h);
break;
case 'x': /* POSIX-standard 'x' header. */
a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
a->archive.archive_format_name = "POSIX pax interchange format";
err = header_pax_extensions(a, tar, entry, st, h);
err = header_pax_extensions(a, tar, entry, h);
break;
default:
if (memcmp(header->magic, "ustar \0", 8) == 0) {
a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
a->archive.archive_format_name = "GNU tar format";
err = header_gnutar(a, tar, entry, st, h);
err = header_gnutar(a, tar, entry, h);
} else if (memcmp(header->magic, "ustar", 5) == 0) {
if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
a->archive.archive_format_name = "POSIX ustar format";
}
err = header_ustar(a, tar, entry, st, h);
err = header_ustar(a, tar, entry, h);
} else {
a->archive.archive_format = ARCHIVE_FORMAT_TAR;
a->archive.archive_format_name = "tar (non-POSIX)";
err = header_old_tar(a, tar, entry, st, h);
err = header_old_tar(a, tar, entry, h);
}
}
--tar->header_recursion_depth;
@ -716,14 +697,14 @@ archive_block_is_null(const unsigned char *p)
*/
static int
header_Solaris_ACL(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
struct archive_entry *entry, const void *h)
{
int err, err2;
char *p;
wchar_t *wp;
err = read_body_to_string(a, tar, &(tar->acl_text), h);
err2 = tar_read_header(a, tar, entry, st);
err2 = tar_read_header(a, tar, entry);
err = err_combine(err, err2);
/* XXX Ensure p doesn't overrun acl_text */
@ -752,12 +733,12 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar,
*/
static int
header_longlink(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
struct archive_entry *entry, const void *h)
{
int err, err2;
err = read_body_to_string(a, tar, &(tar->longlink), h);
err2 = tar_read_header(a, tar, entry, st);
err2 = tar_read_header(a, tar, entry);
if (err == ARCHIVE_OK && err2 == ARCHIVE_OK) {
/* Set symlink if symlink already set, else hardlink. */
archive_entry_set_link(entry, tar->longlink.s);
@ -770,13 +751,13 @@ header_longlink(struct archive_read *a, struct tar *tar,
*/
static int
header_longname(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
struct archive_entry *entry, const void *h)
{
int err, err2;
err = read_body_to_string(a, tar, &(tar->longname), h);
/* Read and parse "real" header, then override name. */
err2 = tar_read_header(a, tar, entry, st);
err2 = tar_read_header(a, tar, entry);
if (err == ARCHIVE_OK && err2 == ARCHIVE_OK)
archive_entry_set_pathname(entry, tar->longname.s);
return (err_combine(err, err2));
@ -788,12 +769,12 @@ header_longname(struct archive_read *a, struct tar *tar,
*/
static int
header_volume(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
struct archive_entry *entry, const void *h)
{
(void)h;
/* Just skip this and read the next header. */
return (tar_read_header(a, tar, entry, st));
return (tar_read_header(a, tar, entry));
}
/*
@ -818,12 +799,12 @@ read_body_to_string(struct archive_read *a, struct tar *tar,
padded_size = (size + 511) & ~ 511;
dest = as->s;
while (padded_size > 0) {
bytes_read = (a->compression_read_ahead)(a, &src, padded_size);
bytes_read = (a->decompressor->read_ahead)(a, &src, padded_size);
if (bytes_read < 0)
return (ARCHIVE_FATAL);
if (bytes_read > padded_size)
bytes_read = padded_size;
(a->compression_read_consume)(a, bytes_read);
(a->decompressor->consume)(a, bytes_read);
bytes_to_copy = bytes_read;
if ((off_t)bytes_to_copy > size)
bytes_to_copy = (ssize_t)size;
@ -847,8 +828,8 @@ read_body_to_string(struct archive_read *a, struct tar *tar,
* common parsing into one place.
*/
static int
header_common(struct archive_read *a, struct tar *tar, struct archive_entry *entry,
struct stat *st, const void *h)
header_common(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, const void *h)
{
const struct archive_entry_header_ustar *header;
char tartype;
@ -863,15 +844,14 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *ent
archive_string_empty(&(tar->entry_linkname));
/* Parse out the numeric fields (all are octal) */
st->st_mode = tar_atol(header->mode, sizeof(header->mode));
st->st_uid = tar_atol(header->uid, sizeof(header->uid));
st->st_gid = tar_atol(header->gid, sizeof(header->gid));
st->st_size = tar_atol(header->size, sizeof(header->size));
st->st_mtime = tar_atol(header->mtime, sizeof(header->mtime));
archive_entry_set_mode(entry, tar_atol(header->mode, sizeof(header->mode)));
archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
archive_entry_set_size(entry, tar_atol(header->size, sizeof(header->size)));
archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
/* Handle the tar type flag appropriately. */
tartype = header->typeflag[0];
st->st_mode &= ~S_IFMT;
switch (tartype) {
case '1': /* Hard link */
@ -885,8 +865,8 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *ent
* implies that the underlying entry is a regular
* file.
*/
if (st->st_size > 0)
st->st_mode |= S_IFREG;
if (archive_entry_size(entry) > 0)
archive_entry_set_filetype(entry, AE_IFREG);
/*
* A tricky point: Traditionally, tar readers have
@ -910,31 +890,31 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *ent
* we encounter a hardlink entry for a file that is
* itself an uncompressed tar archive.
*/
if (st->st_size > 0 &&
if (archive_entry_size(entry) > 0 &&
a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
archive_read_format_tar_bid(a) > 50)
st->st_size = 0;
archive_entry_set_size(entry, 0);
break;
case '2': /* Symlink */
st->st_mode |= S_IFLNK;
st->st_size = 0;
archive_entry_set_filetype(entry, AE_IFLNK);
archive_entry_set_size(entry, 0);
archive_entry_set_symlink(entry, tar->entry_linkname.s);
break;
case '3': /* Character device */
st->st_mode |= S_IFCHR;
st->st_size = 0;
archive_entry_set_filetype(entry, AE_IFCHR);
archive_entry_set_size(entry, 0);
break;
case '4': /* Block device */
st->st_mode |= S_IFBLK;
st->st_size = 0;
archive_entry_set_filetype(entry, AE_IFBLK);
archive_entry_set_size(entry, 0);
break;
case '5': /* Dir */
st->st_mode |= S_IFDIR;
st->st_size = 0;
archive_entry_set_filetype(entry, AE_IFDIR);
archive_entry_set_size(entry, 0);
break;
case '6': /* FIFO device */
st->st_mode |= S_IFIFO;
st->st_size = 0;
archive_entry_set_filetype(entry, AE_IFIFO);
archive_entry_set_size(entry, 0);
break;
case 'D': /* GNU incremental directory type */
/*
@ -942,7 +922,7 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *ent
* It might be nice someday to preprocess the file list and
* provide it to the client, though.
*/
st->st_mode |= S_IFDIR;
archive_entry_set_filetype(entry, AE_IFDIR);
break;
case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/
/*
@ -956,7 +936,7 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *ent
/* The body of this entry is a script for renaming
* previously-extracted entries. Ugh. It will never
* be supported by libarchive. */
st->st_mode |= S_IFREG;
archive_entry_set_filetype(entry, AE_IFREG);
break;
case 'S': /* GNU sparse files */
/*
@ -969,7 +949,7 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *ent
* Per POSIX: non-recognized types should always be
* treated as regular files.
*/
st->st_mode |= S_IFREG;
archive_entry_set_filetype(entry, AE_IFREG);
break;
}
return (0);
@ -979,8 +959,8 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *ent
* Parse out header elements for "old-style" tar archives.
*/
static int
header_old_tar(struct archive_read *a, struct tar *tar, struct archive_entry *entry,
struct stat *st, const void *h)
header_old_tar(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, const void *h)
{
const struct archive_entry_header_ustar *header;
@ -990,9 +970,9 @@ header_old_tar(struct archive_read *a, struct tar *tar, struct archive_entry *en
archive_entry_set_pathname(entry, tar->entry_name.s);
/* Grab rest of common fields */
header_common(a, tar, entry, st, h);
header_common(a, tar, entry, h);
tar->entry_bytes_remaining = st->st_size;
tar->entry_bytes_remaining = archive_entry_size(entry);
tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
return (0);
}
@ -1002,25 +982,25 @@ header_old_tar(struct archive_read *a, struct tar *tar, struct archive_entry *en
*/
static int
header_pax_global(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
struct archive_entry *entry, const void *h)
{
int err, err2;
err = read_body_to_string(a, tar, &(tar->pax_global), h);
err2 = tar_read_header(a, tar, entry, st);
err2 = tar_read_header(a, tar, entry);
return (err_combine(err, err2));
}
static int
header_pax_extensions(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
struct archive_entry *entry, const void *h)
{
int err, err2;
read_body_to_string(a, tar, &(tar->pax_header), h);
/* Parse the next header. */
err = tar_read_header(a, tar, entry, st);
err = tar_read_header(a, tar, entry);
/*
* TODO: Parse global/default options into 'entry' struct here
@ -1032,9 +1012,9 @@ header_pax_extensions(struct archive_read *a, struct tar *tar,
* and then skip any fields in the standard header that were
* defined in the pax header.
*/
err2 = pax_header(a, tar, entry, st, tar->pax_header.s);
err2 = pax_header(a, tar, entry, tar->pax_header.s);
err = err_combine(err, err2);
tar->entry_bytes_remaining = st->st_size;
tar->entry_bytes_remaining = archive_entry_size(entry);
tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
return (err);
}
@ -1045,8 +1025,8 @@ header_pax_extensions(struct archive_read *a, struct tar *tar,
* handles "pax" or "extended ustar" entries.
*/
static int
header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry,
struct stat *st, const void *h)
header_ustar(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, const void *h)
{
const struct archive_entry_header_ustar *header;
struct archive_string *as;
@ -1066,7 +1046,7 @@ header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entr
archive_entry_set_pathname(entry, as->s);
/* Handle rest of common fields. */
header_common(a, tar, entry, st, h);
header_common(a, tar, entry, h);
/* Handle POSIX ustar fields. */
archive_strncpy(&(tar->entry_uname), header->uname,
@ -1079,12 +1059,13 @@ header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entr
/* Parse out device numbers only for char and block specials. */
if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
st->st_rdev = makedev(
tar_atol(header->rdevmajor, sizeof(header->rdevmajor)),
archive_entry_set_rdevmajor(entry,
tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
archive_entry_set_rdevminor(entry,
tar_atol(header->rdevminor, sizeof(header->rdevminor)));
}
tar->entry_bytes_remaining = st->st_size;
tar->entry_bytes_remaining = archive_entry_size(entry);
tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
return (0);
@ -1097,8 +1078,8 @@ header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entr
* Returns non-zero if there's an error in the data.
*/
static int
pax_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry,
struct stat *st, char *attr)
pax_header(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, char *attr)
{
size_t attr_length, l, line_length;
char *line, *p;
@ -1179,7 +1160,7 @@ pax_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry,
value = wp + 1;
/* Identify this attribute and set it in the entry. */
err2 = pax_attribute(entry, st, key, value);
err2 = pax_attribute(entry, key, value);
err = err_combine(err, err2);
/* Skip to next line */
@ -1240,7 +1221,7 @@ pax_attribute_xattr(struct archive_entry *entry,
* any of them look useful.
*/
static int
pax_attribute(struct archive_entry *entry, struct stat *st,
pax_attribute(struct archive_entry *entry,
wchar_t *key, wchar_t *value)
{
int64_t s;
@ -1266,32 +1247,28 @@ pax_attribute(struct archive_entry *entry, struct stat *st,
__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_rdev));
archive_entry_set_rdevmajor(entry, tar_atol10(value, wcslen(value)));
else if (wcscmp(key, L"SCHILY.devminor")==0)
st->st_rdev = makedev(major(st->st_rdev),
tar_atol10(value, wcslen(value)));
archive_entry_set_rdevminor(entry, tar_atol10(value, wcslen(value)));
else if (wcscmp(key, L"SCHILY.fflags")==0)
archive_entry_copy_fflags_text_w(entry, value);
else if (wcscmp(key, L"SCHILY.dev")==0)
st->st_dev = tar_atol10(value, wcslen(value));
archive_entry_set_dev(entry, tar_atol10(value, wcslen(value)));
else if (wcscmp(key, L"SCHILY.ino")==0)
st->st_ino = tar_atol10(value, wcslen(value));
archive_entry_set_ino(entry, tar_atol10(value, wcslen(value)));
else if (wcscmp(key, L"SCHILY.nlink")==0)
st->st_nlink = tar_atol10(value, wcslen(value));
archive_entry_set_nlink(entry, tar_atol10(value, wcslen(value)));
break;
case 'a':
if (wcscmp(key, L"atime")==0) {
pax_time(value, &s, &n);
st->st_atime = s;
ARCHIVE_STAT_SET_ATIME_NANOS(st, n);
archive_entry_set_atime(entry, s, n);
}
break;
case 'c':
if (wcscmp(key, L"ctime")==0) {
pax_time(value, &s, &n);
st->st_ctime = s;
ARCHIVE_STAT_SET_CTIME_NANOS(st, n);
archive_entry_set_ctime(entry, s, n);
} else if (wcscmp(key, L"charset")==0) {
/* TODO: Publish charset information in entry. */
} else if (wcscmp(key, L"comment")==0) {
@ -1300,7 +1277,7 @@ pax_attribute(struct archive_entry *entry, struct stat *st,
break;
case 'g':
if (wcscmp(key, L"gid")==0)
st->st_gid = tar_atol10(value, wcslen(value));
archive_entry_set_gid(entry, tar_atol10(value, wcslen(value)));
else if (wcscmp(key, L"gname")==0)
archive_entry_copy_gname_w(entry, value);
break;
@ -1316,8 +1293,7 @@ pax_attribute(struct archive_entry *entry, struct stat *st,
case 'm':
if (wcscmp(key, L"mtime")==0) {
pax_time(value, &s, &n);
st->st_mtime = s;
ARCHIVE_STAT_SET_MTIME_NANOS(st, n);
archive_entry_set_mtime(entry, s, n);
}
break;
case 'p':
@ -1331,11 +1307,11 @@ pax_attribute(struct archive_entry *entry, struct stat *st,
/* POSIX has reserved 'security.*' */
/* Someday: if (wcscmp(key, L"security.acl")==0) { ... } */
if (wcscmp(key, L"size")==0)
st->st_size = tar_atol10(value, wcslen(value));
archive_entry_set_size(entry, tar_atol10(value, wcslen(value)));
break;
case 'u':
if (wcscmp(key, L"uid")==0)
st->st_uid = tar_atol10(value, wcslen(value));
archive_entry_set_uid(entry, tar_atol10(value, wcslen(value)));
else if (wcscmp(key, L"uname")==0)
archive_entry_copy_uname_w(entry, value);
break;
@ -1399,8 +1375,8 @@ pax_time(const wchar_t *p, int64_t *ps, long *pn)
* Parse GNU tar header
*/
static int
header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *entry,
struct stat *st, const void *h)
header_gnutar(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, const void *h)
{
const struct archive_entry_header_gnutar *header;
@ -1413,7 +1389,7 @@ header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *ent
*/
/* Grab fields common to all tar variants. */
header_common(a, tar, entry, st, h);
header_common(a, tar, entry, h);
/* Copy filename over (to ensure null termination). */
header = (const struct archive_entry_header_gnutar *)h;
@ -1434,22 +1410,25 @@ header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *ent
archive_entry_set_gname(entry, tar->entry_gname.s);
/* Parse out device numbers only for char and block specials */
if (header->typeflag[0] == '3' || header->typeflag[0] == '4')
st->st_rdev = makedev (
tar_atol(header->rdevmajor, sizeof(header->rdevmajor)),
if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
archive_entry_set_rdevmajor(entry,
tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
archive_entry_set_rdevminor(entry,
tar_atol(header->rdevminor, sizeof(header->rdevminor)));
else
st->st_rdev = 0;
} else
archive_entry_set_rdev(entry, 0);
tar->entry_bytes_remaining = st->st_size;
tar->entry_bytes_remaining = archive_entry_size(entry);
tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
/* Grab GNU-specific fields. */
st->st_atime = tar_atol(header->atime, sizeof(header->atime));
st->st_ctime = tar_atol(header->ctime, sizeof(header->ctime));
archive_entry_set_atime(entry,
tar_atol(header->atime, sizeof(header->atime)), 0);
archive_entry_set_ctime(entry,
tar_atol(header->ctime, sizeof(header->ctime)), 0);
if (header->realsize[0] != 0) {
st->st_size = tar_atol(header->realsize,
sizeof(header->realsize));
archive_entry_set_size(entry,
tar_atol(header->realsize, sizeof(header->realsize)));
}
if (header->sparse[0].offset[0] != 0) {
@ -1481,7 +1460,7 @@ gnu_read_sparse_data(struct archive_read *a, struct tar *tar,
return (ARCHIVE_OK);
do {
bytes_read = (a->compression_read_ahead)(a, &data, 512);
bytes_read = (a->decompressor->read_ahead)(a, &data, 512);
if (bytes_read < 0)
return (ARCHIVE_FATAL);
if (bytes_read < 512) {
@ -1490,7 +1469,7 @@ gnu_read_sparse_data(struct archive_read *a, struct tar *tar,
"detected while reading sparse file data");
return (ARCHIVE_FATAL);
}
(a->compression_read_consume)(a, 512);
(a->decompressor->consume)(a, 512);
ext = (const struct extended *)data;
gnu_parse_sparse_data(a, tar, ext->sparse, 21);
} while (ext->isextended[0] != 0);

View File

@ -26,9 +26,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -82,6 +79,7 @@ struct zip {
size_t uncompressed_buffer_size;
#ifdef HAVE_ZLIB_H
z_stream stream;
char stream_valid;
#endif
struct archive_string pathname;
@ -176,7 +174,7 @@ archive_read_format_zip_bid(struct archive_read *a)
if (a->archive.archive_format == ARCHIVE_FORMAT_ZIP)
bid += 1;
bytes_read = (a->compression_read_ahead)(a, &h, 4);
bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
if (bytes_read < 4)
return (-1);
p = (const char *)h;
@ -208,13 +206,13 @@ archive_read_format_zip_read_header(struct archive_read *a,
if (a->archive.archive_format_name == NULL)
a->archive.archive_format_name = "ZIP";
zip = (struct zip *)*(a->pformat_data);
zip = (struct zip *)(a->format->data);
zip->decompress_init = 0;
zip->end_of_entry = 0;
zip->end_of_entry_cleanup = 0;
zip->entry_uncompressed_bytes_read = 0;
zip->entry_compressed_bytes_read = 0;
bytes_read = (a->compression_read_ahead)(a, &h, 4);
bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
if (bytes_read < 4)
return (ARCHIVE_FATAL);
@ -263,10 +261,9 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
const struct zip_file_header *p;
const void *h;
int bytes_read;
struct stat st;
bytes_read =
(a->compression_read_ahead)(a, &h, sizeof(struct zip_file_header));
(a->decompressor->read_ahead)(a, &h, sizeof(struct zip_file_header));
if (bytes_read < (int)sizeof(struct zip_file_header)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated ZIP file header");
@ -295,11 +292,11 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
zip->uncompressed_size = u4(p->uncompressed_size);
zip->compressed_size = u4(p->compressed_size);
(a->compression_read_consume)(a, sizeof(struct zip_file_header));
(a->decompressor->consume)(a, sizeof(struct zip_file_header));
/* Read the filename. */
bytes_read = (a->compression_read_ahead)(a, &h, zip->filename_length);
bytes_read = (a->decompressor->read_ahead)(a, &h, zip->filename_length);
if (bytes_read < zip->filename_length) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated ZIP file header");
@ -307,34 +304,32 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
}
archive_string_ensure(&zip->pathname, zip->filename_length);
archive_strncpy(&zip->pathname, (const char *)h, zip->filename_length);
(a->compression_read_consume)(a, zip->filename_length);
(a->decompressor->consume)(a, zip->filename_length);
archive_entry_set_pathname(entry, zip->pathname.s);
if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/')
zip->mode = S_IFDIR | 0777;
zip->mode = AE_IFDIR | 0777;
else
zip->mode = S_IFREG | 0777;
zip->mode = AE_IFREG | 0777;
/* Read the extra data. */
bytes_read = (a->compression_read_ahead)(a, &h, zip->extra_length);
bytes_read = (a->decompressor->read_ahead)(a, &h, zip->extra_length);
if (bytes_read < zip->extra_length) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated ZIP file header");
return (ARCHIVE_FATAL);
}
process_extra(h, zip);
(a->compression_read_consume)(a, zip->extra_length);
(a->decompressor->consume)(a, zip->extra_length);
/* Populate some additional entry fields: */
memset(&st, 0, sizeof(st));
st.st_mode = zip->mode;
st.st_uid = zip->uid;
st.st_gid = zip->gid;
st.st_mtime = zip->mtime;
st.st_ctime = zip->ctime;
st.st_atime = zip->atime;
st.st_size = zip->uncompressed_size;
archive_entry_copy_stat(entry, &st);
archive_entry_set_mode(entry, zip->mode);
archive_entry_set_uid(entry, zip->uid);
archive_entry_set_gid(entry, zip->gid);
archive_entry_set_mtime(entry, zip->mtime, 0);
archive_entry_set_ctime(entry, zip->ctime, 0);
archive_entry_set_atime(entry, zip->atime, 0);
archive_entry_set_size(entry, zip->uncompressed_size);
zip->entry_bytes_remaining = zip->compressed_size;
zip->entry_offset = 0;
@ -376,7 +371,7 @@ archive_read_format_zip_read_data(struct archive_read *a,
int r;
struct zip *zip;
zip = (struct zip *)*(a->pformat_data);
zip = (struct zip *)(a->format->data);
/*
* If we hit end-of-entry last time, clean up and return
@ -388,7 +383,7 @@ archive_read_format_zip_read_data(struct archive_read *a,
const void *h;
const char *p;
int bytes_read =
(a->compression_read_ahead)(a, &h, 16);
(a->decompressor->read_ahead)(a, &h, 16);
if (bytes_read < 16) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
@ -399,7 +394,7 @@ archive_read_format_zip_read_data(struct archive_read *a,
zip->crc32 = i4(p + 4);
zip->compressed_size = u4(p + 8);
zip->uncompressed_size = u4(p + 12);
bytes_read = (a->compression_read_consume)(a, 16);
bytes_read = (a->decompressor->consume)(a, 16);
}
/* Check file size, CRC against these values. */
@ -482,7 +477,7 @@ zip_read_data_none(struct archive_read *a, const void **buff,
struct zip *zip;
ssize_t bytes_avail;
zip = (struct zip *)*(a->pformat_data);
zip = (struct zip *)(a->format->data);
if (zip->entry_bytes_remaining == 0) {
*buff = NULL;
@ -497,7 +492,7 @@ zip_read_data_none(struct archive_read *a, const void **buff,
* available bytes; asking for more than that forces the
* decompressor to combine reads by copying data.
*/
bytes_avail = (a->compression_read_ahead)(a, buff, 1);
bytes_avail = (a->decompressor->read_ahead)(a, buff, 1);
if (bytes_avail <= 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated ZIP file data");
@ -505,7 +500,7 @@ zip_read_data_none(struct archive_read *a, const void **buff,
}
if (bytes_avail > zip->entry_bytes_remaining)
bytes_avail = zip->entry_bytes_remaining;
(a->compression_read_consume)(a, bytes_avail);
(a->decompressor->consume)(a, bytes_avail);
*size = bytes_avail;
*offset = zip->entry_offset;
zip->entry_offset += *size;
@ -525,7 +520,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
const void *compressed_buff;
int r;
zip = (struct zip *)*(a->pformat_data);
zip = (struct zip *)(a->format->data);
/* If the buffer hasn't been allocated, allocate it now. */
if (zip->uncompressed_buffer == NULL) {
@ -541,13 +536,19 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
/* If we haven't yet read any data, initialize the decompressor. */
if (!zip->decompress_init) {
r = inflateInit2(&zip->stream,
-15 /* Don't check for zlib header */);
if (zip->stream_valid)
r = inflateReset(&zip->stream);
else
r = inflateInit2(&zip->stream,
-15 /* Don't check for zlib header */);
if (r != Z_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Can't initialize ZIP decompression.");
return (ARCHIVE_FATAL);
}
/* Stream structure has been set up. */
zip->stream_valid = 1;
/* We've initialized decompression for this stream. */
zip->decompress_init = 1;
}
@ -557,7 +558,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
* available bytes; asking for more than that forces the
* decompressor to combine reads by copying data.
*/
bytes_avail = (a->compression_read_ahead)(a, &compressed_buff, 1);
bytes_avail = (a->decompressor->read_ahead)(a, &compressed_buff, 1);
if (bytes_avail <= 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated ZIP file body");
@ -596,7 +597,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
/* Consume as much as the compressor actually used. */
bytes_avail = zip->stream.total_in;
(a->compression_read_consume)(a, bytes_avail);
(a->decompressor->consume)(a, bytes_avail);
zip->entry_bytes_remaining -= bytes_avail;
zip->entry_compressed_bytes_read += bytes_avail;
@ -628,7 +629,7 @@ archive_read_format_zip_read_data_skip(struct archive_read *a)
const void *buff = NULL;
ssize_t bytes_avail;
zip = (struct zip *)*(a->pformat_data);
zip = (struct zip *)(a->format->data);
/*
* If the length is at the end, we have no choice but
@ -650,7 +651,7 @@ archive_read_format_zip_read_data_skip(struct archive_read *a)
* compressed data much more quickly.
*/
while (zip->entry_bytes_remaining > 0) {
bytes_avail = (a->compression_read_ahead)(a, &buff, 1);
bytes_avail = (a->decompressor->read_ahead)(a, &buff, 1);
if (bytes_avail <= 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
@ -659,7 +660,7 @@ archive_read_format_zip_read_data_skip(struct archive_read *a)
}
if (bytes_avail > zip->entry_bytes_remaining)
bytes_avail = zip->entry_bytes_remaining;
(a->compression_read_consume)(a, bytes_avail);
(a->decompressor->consume)(a, bytes_avail);
zip->entry_bytes_remaining -= bytes_avail;
}
/* This entry is finished and done. */
@ -672,13 +673,16 @@ archive_read_format_zip_cleanup(struct archive_read *a)
{
struct zip *zip;
zip = (struct zip *)*(a->pformat_data);
if (zip->uncompressed_buffer != NULL)
free(zip->uncompressed_buffer);
zip = (struct zip *)(a->format->data);
#ifdef HAVE_ZLIB_H
if (zip->stream_valid)
inflateEnd(&zip->stream);
#endif
free(zip->uncompressed_buffer);
archive_string_free(&(zip->pathname));
archive_string_free(&(zip->extra));
free(zip);
*(a->pformat_data) = NULL;
(a->format->data) = NULL;
return (ARCHIVE_OK);
}

View File

@ -51,6 +51,15 @@ __archive_string_append(struct archive_string *as, const char *p, size_t s)
return (as);
}
void
__archive_string_copy(struct archive_string *dest, struct archive_string *src)
{
__archive_string_ensure(dest, src->length + 1);
memcpy(dest->s, src->s, src->length);
dest->length = src->length;
dest->s[dest->length] = 0;
}
void
__archive_string_free(struct archive_string *as)
{

View File

@ -74,6 +74,12 @@ __archive_strappend_int(struct archive_string *as, int d, int base);
struct archive_string *
__archive_string_append(struct archive_string *as, const char *p, size_t s);
/* Copy one archive_string to another */
void
__archive_string_copy(struct archive_string *dest, struct archive_string *src);
#define archive_string_copy(dest, src) \
__archive_string_copy(dest, src)
/* Ensure that the underlying buffer is at least as large as the request. */
struct archive_string *
__archive_string_ensure(struct archive_string *, size_t);

View File

@ -31,6 +31,7 @@
.Nm archive_clear_error ,
.Nm archive_compression ,
.Nm archive_compression_name ,
.Nm archive_copy_error ,
.Nm archive_errno ,
.Nm archive_error_string ,
.Nm archive_format ,
@ -45,6 +46,8 @@
.Fn archive_compression "struct archive *"
.Ft const char *
.Fn archive_compression_name "struct archive *"
.Ft void
.Fn archive_copy_error "struct archive *" "struct archive *"
.Ft int
.Fn archive_errno "struct archive *"
.Ft const char *
@ -71,6 +74,8 @@ This value is set by
.Fn archive_read_open .
.It Fn archive_compression_name
Returns a text description of the current compression suitable for display.
.It Fn archive_copy_error
Copies error information from one archive to another.
.It Fn archive_errno
Returns a numeric error code (see
.Xr errno 2 )

View File

@ -161,6 +161,15 @@ archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
va_end(ap);
}
void
archive_copy_error(struct archive *dest, struct archive *src)
{
dest->archive_error_number = src->archive_error_number;
archive_string_copy(&dest->error_string, &src->error_string);
dest->error = dest->error_string.s;
}
void
__archive_errx(int retvalue, const char *msg)
{

View File

@ -38,8 +38,10 @@
.Nm archive_write_get_bytes_per_block ,
.Nm archive_write_set_bytes_per_block ,
.Nm archive_write_set_bytes_in_last_block ,
.Nm archive_write_set_compressor_gzip ,
.Nm archive_write_set_compressor_bzip2 ,
.Nm archive_write_set_compression_bzip2 ,
.Nm archive_write_set_compression_gzip ,
.Nm archive_write_set_compression_none ,
.Nm archive_write_set_compression_program ,
.Nm archive_write_open ,
.Nm archive_write_open_fd ,
.Nm archive_write_open_FILE ,
@ -62,9 +64,13 @@
.Ft int
.Fn archive_write_set_bytes_in_last_block "struct archive *" "int"
.Ft int
.Fn archive_write_set_compressor_gzip "struct archive *"
.Fn archive_write_set_compression_bzip2 "struct archive *"
.Ft int
.Fn archive_write_set_compressor_bzip2 "struct archive *"
.Fn archive_write_set_compression_gzip "struct archive *"
.Ft int
.Fn archive_write_set_compression_none "struct archive *"
.Ft int
.Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
.Ft int
.Fn archive_write_set_format_cpio "struct archive *"
.Ft int
@ -168,9 +174,13 @@ filenames, linknames, uids, sizes, etc.
is the library default; this is the same as pax format, but suppresses
the pax extended header for most normal files.
In most cases, this will result in ordinary ustar archives.
.It Fn archive_write_set_compression_gzip , Fn archive_write_set_compression_bzip2
.It Fn archive_write_set_compression_bzip2 , Fn archive_write_set_compression_gzip , Fn archive_write_set_compression_none
The resulting archive will be compressed as specified.
Note that the compressed output is always properly blocked.
.It Fn archive_write_set_compression_program
The archive will be fed into the specified compression program.
The output of that program is blocked and written to the client
write callbacks.
.It Fn archive_write_open
Freeze the settings, open the archive, and prepare for writing entries.
This is the most generic form of this function, which accepts

View File

@ -99,7 +99,6 @@ archive_write_new(void)
a->archive.vtable = archive_write_vtable();
a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
a->bytes_in_last_block = -1; /* Default */
a->pformat_data = &(a->format_data);
/* Initialize a block of nulls for padding purposes. */
a->null_length = 1024;
@ -208,7 +207,7 @@ archive_write_open(struct archive *_a, void *client_data,
a->client_writer = writer;
a->client_opener = opener;
a->client_closer = closer;
ret = (a->compression_init)(a);
ret = (a->compressor.init)(a);
if (a->format_init && ret == ARCHIVE_OK)
ret = (a->format_init)(a);
return (ret);
@ -242,7 +241,7 @@ _archive_write_close(struct archive *_a)
r = r1;
}
/* Release resources. */
/* Release format resources. */
if (a->format_destroy != NULL) {
r1 = (a->format_destroy)(a);
if (r1 < r)
@ -250,8 +249,15 @@ _archive_write_close(struct archive *_a)
}
/* Finish the compression and close the stream. */
if (a->compression_finish != NULL) {
r1 = (a->compression_finish)(a);
if (a->compressor.finish != NULL) {
r1 = (a->compressor.finish)(a);
if (r1 < r)
r = r1;
}
/* Close out the client stream. */
if (a->client_closer != NULL) {
r1 = (a->client_closer)(&a->archive, a->client_data);
if (r1 < r)
r = r1;
}

View File

@ -204,6 +204,7 @@ static void edit_deep_directories(struct archive_write_disk *ad);
static int cleanup_pathname(struct archive_write_disk *);
static int create_dir(struct archive_write_disk *, char *);
static int create_parent_dir(struct archive_write_disk *, char *);
static int older(struct stat *, struct archive_entry *);
static int restore_entry(struct archive_write_disk *);
#ifdef HAVE_POSIX_ACL
static int set_acl(struct archive_write_disk *, int fd, struct archive_entry *,
@ -292,7 +293,11 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
a->pst = NULL;
a->current_fixup = NULL;
a->deferred = 0;
a->entry = entry;
if (a->entry) {
archive_entry_free(a->entry);
a->entry = NULL;
}
a->entry = archive_entry_clone(entry);
a->fd = -1;
a->offset = 0;
a->uid = a->user_uid;
@ -544,6 +549,11 @@ _archive_write_finish_entry(struct archive *_a)
close(a->fd);
a->fd = -1;
}
/* If there's an entry, we can release it now. */
if (a->entry) {
archive_entry_free(a->entry);
a->entry = NULL;
}
a->archive.state = ARCHIVE_STATE_HEADER;
return (ret);
}
@ -682,21 +692,42 @@ restore_entry(struct archive_write_disk *a)
/* Try creating it first; if this fails, we'll try to recover. */
en = create_filesystem_object(a);
if (en == ENOTDIR || en == ENOENT) {
if ((en == ENOTDIR || en == ENOENT)
&& !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
/* If the parent dir doesn't exist, try creating it. */
create_parent_dir(a, a->name);
/* Now try to create the object again. */
en = create_filesystem_object(a);
}
if (en == EEXIST) {
if ((en == EISDIR || en == EEXIST)
&& (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
/* If we're not overwriting, we're done. */
if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE) {
archive_set_error(&a->archive, en, "Already exists");
archive_set_error(&a->archive, en, "Already exists");
return (ARCHIVE_WARN);
}
/*
* Some platforms return EISDIR if you call
* open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
* return EEXIST. POSIX is ambiguous, requiring EISDIR
* for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
* on an existing item.
*/
if (en == EISDIR) {
/* A dir is in the way of a non-dir, rmdir it. */
if (rmdir(a->name) != 0) {
archive_set_error(&a->archive, errno,
"Can't remove already-existing dir");
return (ARCHIVE_WARN);
}
/* Find out what's in the way before we go any further. */
/* Try again. */
en = create_filesystem_object(a);
} else if (en == EEXIST) {
/*
* We know something is in the way, but we don't know what;
* we need to find out before we go any further.
*/
if (lstat(a->name, &a->st) != 0) {
archive_set_error(&a->archive, errno,
"Can't stat existing object");
@ -705,6 +736,14 @@ restore_entry(struct archive_write_disk *a)
/* TODO: if it's a symlink... */
if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) {
if (!older(&(a->st), a->entry)) {
archive_set_error(&a->archive, 0,
"File on disk is not older; skipping.");
return (ARCHIVE_FAILED);
}
}
/* If it's our archive, we're done. */
if (a->skip_file_dev > 0 &&
a->skip_file_ino > 0 &&
@ -1405,16 +1444,13 @@ set_ownership(struct archive_write_disk *a)
static int
set_time(struct archive_write_disk *a)
{
const struct stat *st;
struct timeval times[2];
st = archive_entry_stat(a->entry);
times[1].tv_sec = archive_entry_mtime(a->entry);
times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000;
times[1].tv_sec = st->st_mtime;
times[1].tv_usec = ARCHIVE_STAT_MTIME_NANOS(st) / 1000;
times[0].tv_sec = st->st_atime;
times[0].tv_usec = ARCHIVE_STAT_ATIME_NANOS(st) / 1000;
times[0].tv_sec = archive_entry_atime(a->entry);
times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000;
#ifdef HAVE_FUTIMES
if (a->fd >= 0 && futimes(a->fd, times) == 0) {
@ -1450,11 +1486,10 @@ set_time(struct archive_write_disk *a)
static int
set_time(struct archive_write_disk *a)
{
const struct stat *st = archive_entry_stat(a->entry);
struct utimbuf times;
times.modtime = st->st_mtime;
times.actime = st->st_atime;
times.modtime = archive_entry_mtime(a->entry);
times.actime = archive_entry_atime(a->entry);
if (!S_ISLNK(a->mode) && utime(a->name, &times) != 0) {
archive_set_error(&a->archive, errno,
"Can't update time for %s", a->name);
@ -1479,6 +1514,7 @@ static int
set_mode(struct archive_write_disk *a, int mode)
{
int r = ARCHIVE_OK;
mode &= 07777; /* Strip off file type bits. */
if (a->todo & TODO_SGID_CHECK) {
/*
@ -1539,7 +1575,8 @@ set_mode(struct archive_write_disk *a, int mode)
* impact.
*/
if (lchmod(a->name, mode) != 0) {
archive_set_error(&a->archive, errno, "Can't set permissions");
archive_set_error(&a->archive, errno,
"Can't set permissions to 0%o", (int)mode);
r = ARCHIVE_WARN;
}
#endif
@ -1554,7 +1591,7 @@ set_mode(struct archive_write_disk *a, int mode)
if (a->fd >= 0) {
if (fchmod(a->fd, mode) != 0) {
archive_set_error(&a->archive, errno,
"Can't set permissions");
"Can't set permissions to 0%o", (int)mode);
r = ARCHIVE_WARN;
}
} else
@ -1563,7 +1600,7 @@ set_mode(struct archive_write_disk *a, int mode)
* we'll just use chmod(). */
if (chmod(a->name, mode) != 0) {
archive_set_error(&a->archive, errno,
"Can't set permissions");
"Can't set permissions to 0%o", (int)mode);
r = ARCHIVE_WARN;
}
}
@ -1998,3 +2035,38 @@ trivial_lookup_uid(void *private_data, const char *uname, uid_t uid)
(void)uname; /* UNUSED */
return (uid);
}
/*
* Test if file on disk is older than entry.
*/
static int
older(struct stat *st, struct archive_entry *entry)
{
/* First, test the seconds and return if we have a definite answer. */
/* Definitely older. */
if (st->st_mtime < archive_entry_mtime(entry))
return (1);
/* Definitely younger. */
if (st->st_mtime > archive_entry_mtime(entry))
return (0);
/* If this platform supports fractional seconds, try those. */
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
/* Definitely older. */
if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))
return (1);
/* Definitely younger. */
if (st->st_mtimespec.tv_nsec > archive_entry_mtime_nsec(entry))
return (0);
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
/* Definitely older. */
if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
return (1);
/* Definitely older. */
if (st->st_mtim.tv_nsec > archive_entry_mtime_nsec(entry))
return (0);
#else
/* This system doesn't have high-res timestamps. */
#endif
/* Same age, so not older. */
return (0);
}

View File

@ -29,22 +29,6 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_ACL_H
#include <sys/acl.h>
#endif
#ifdef HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

View File

@ -35,9 +35,7 @@
struct archive_write {
struct archive archive;
struct archive_entry *entry;
/* Dev/ino of the archive being read/written. */
/* Dev/ino of the archive being written. */
dev_t skip_file_dev;
ino_t skip_file_ino;
@ -45,19 +43,8 @@ struct archive_write {
const unsigned char *nulls;
size_t null_length;
/*
* Used by archive_read_data() to track blocks and copy
* data to client buffers, filling gaps with zero bytes.
*/
const char *read_data_block;
off_t read_data_offset;
off_t read_data_output_offset;
size_t read_data_remaining;
/* Callbacks to open/read/write/close archive stream. */
archive_open_callback *client_opener;
archive_read_callback *client_reader;
archive_skip_callback *client_skipper;
archive_write_callback *client_writer;
archive_close_callback *client_closer;
void *client_data;
@ -86,64 +73,26 @@ struct archive_write {
* On write, the client just invokes an archive_write_set function
* which sets up the data here directly.
*/
void *compression_data; /* Data for (de)compressor. */
int (*compression_init)(struct archive_write *); /* Initialize. */
int (*compression_finish)(struct archive_write *);
int (*compression_write)(struct archive_write *, const void *, size_t);
/*
* Read uses a peek/consume I/O model: the decompression code
* returns a pointer to the requested block and advances the
* file position only when requested by a consume call. This
* reduces copying and also simplifies look-ahead for format
* detection.
*/
ssize_t (*compression_read_ahead)(struct archive *,
const void **, size_t request);
ssize_t (*compression_read_consume)(struct archive *, size_t);
off_t (*compression_skip)(struct archive *, off_t);
struct {
void *data;
void *config;
int (*init)(struct archive_write *);
int (*finish)(struct archive_write *);
int (*write)(struct archive_write *, const void *, size_t);
} compressor;
/*
* Format detection is mostly the same as compression
* detection, with two significant differences: The bidders
* use the read_ahead calls above to examine the stream rather
* than having the supervisor hand them a block of data to
* examine, and the auction is repeated for every header.
* Winning bidders should set the archive_format and
* archive_format_name appropriately. Bid routines should
* check archive_format and decline to bid if the format of
* the last header was incompatible.
*
* Again, write support is considerably simpler because there's
* no need for an auction.
*/
int archive_format;
const char *archive_format_name;
struct archive_format_descriptor {
int (*bid)(struct archive *);
int (*read_header)(struct archive *, struct archive_entry *);
int (*read_data)(struct archive *, const void **, size_t *, off_t *);
int (*read_data_skip)(struct archive *);
int (*cleanup)(struct archive *);
void *format_data; /* Format-specific data for readers. */
} formats[8];
struct archive_format_descriptor *format; /* Active format. */
/*
* Storage for format-specific data. Note that there can be
* multiple format readers active at one time, so we need to
* allow for multiple format readers to have their data
* available. The pformat_data slot here is the solution: on
* read, it is guaranteed to always point to a void* variable
* that the format can use.
*/
void **pformat_data; /* Pointer to current format_data. */
void *format_data; /* Used by writers. */
/*
* Pointers to format-specific functions for writing. They're
* initialized by archive_write_set_format_XXX() calls.
*/
void *format_data;
int (*format_init)(struct archive_write *);
int (*format_finish)(struct archive_write *);
int (*format_destroy)(struct archive_write *);
@ -152,12 +101,6 @@ struct archive_write {
struct archive_entry *);
ssize_t (*format_write_data)(struct archive_write *,
const void *buff, size_t);
/*
* Various information needed by archive_extract.
*/
struct extract *extract;
int (*cleanup_archive_extract)(struct archive *);
};
/*

View File

@ -79,7 +79,7 @@ archive_write_set_compression_bzip2(struct archive *_a)
struct archive_write *a = (struct archive_write *)_a;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
a->compression_init = &archive_compressor_bzip2_init;
a->compressor.init = &archive_compressor_bzip2_init;
return (ARCHIVE_OK);
}
@ -121,13 +121,13 @@ archive_compressor_bzip2_init(struct archive_write *a)
state->stream.next_out = state->compressed;
state->stream.avail_out = state->compressed_buffer_size;
a->compression_write = archive_compressor_bzip2_write;
a->compression_finish = archive_compressor_bzip2_finish;
a->compressor.write = archive_compressor_bzip2_write;
a->compressor.finish = archive_compressor_bzip2_finish;
/* Initialize compression library */
ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30);
if (ret == BZ_OK) {
a->compression_data = state;
a->compressor.data = state;
return (ARCHIVE_OK);
}
@ -171,7 +171,7 @@ archive_compressor_bzip2_write(struct archive_write *a, const void *buff,
{
struct private_data *state;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->compressor.data;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"No write callback is registered? "
@ -205,7 +205,7 @@ archive_compressor_bzip2_finish(struct archive_write *a)
ssize_t bytes_written;
unsigned tocopy;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->compressor.data;
ret = ARCHIVE_OK;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
@ -282,11 +282,6 @@ archive_compressor_bzip2_finish(struct archive_write *a)
free(state->compressed);
free(state);
/* Close the output */
if (a->client_closer != NULL)
(a->client_closer)(&a->archive, a->client_data);
return (ret);
}

View File

@ -81,7 +81,7 @@ archive_write_set_compression_gzip(struct archive *_a)
struct archive_write *a = (struct archive_write *)_a;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
a->compression_init = &archive_compressor_gzip_init;
a->compressor.init = &archive_compressor_gzip_init;
a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
a->archive.compression_name = "gzip";
return (ARCHIVE_OK);
@ -143,8 +143,8 @@ archive_compressor_gzip_init(struct archive_write *a)
state->stream.next_out += 10;
state->stream.avail_out -= 10;
a->compression_write = archive_compressor_gzip_write;
a->compression_finish = archive_compressor_gzip_finish;
a->compressor.write = archive_compressor_gzip_write;
a->compressor.finish = archive_compressor_gzip_finish;
/* Initialize compression library. */
ret = deflateInit2(&(state->stream),
@ -155,7 +155,7 @@ archive_compressor_gzip_init(struct archive_write *a)
Z_DEFAULT_STRATEGY);
if (ret == Z_OK) {
a->compression_data = state;
a->compressor.data = state;
return (0);
}
@ -196,7 +196,7 @@ archive_compressor_gzip_write(struct archive_write *a, const void *buff,
struct private_data *state;
int ret;
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->compressor.data;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"No write callback is registered? "
@ -231,7 +231,7 @@ archive_compressor_gzip_finish(struct archive_write *a)
unsigned tocopy;
unsigned char trailer[8];
state = (struct private_data *)a->compression_data;
state = (struct private_data *)a->compressor.data;
ret = 0;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
@ -340,11 +340,6 @@ archive_compressor_gzip_finish(struct archive_write *a)
}
free(state->compressed);
free(state);
/* Close the output */
if (a->client_closer != NULL)
(a->client_closer)(&a->archive, a->client_data);
return (ret);
}

View File

@ -58,7 +58,7 @@ archive_write_set_compression_none(struct archive *_a)
struct archive_write *a = (struct archive_write *)_a;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_compression_none");
a->compression_init = &archive_compressor_none_init;
a->compressor.init = &archive_compressor_none_init;
return (0);
}
@ -102,9 +102,9 @@ archive_compressor_none_init(struct archive_write *a)
state->next = state->buffer;
state->avail = state->buffer_size;
a->compression_data = state;
a->compression_write = archive_compressor_none_write;
a->compression_finish = archive_compressor_none_finish;
a->compressor.data = state;
a->compressor.write = archive_compressor_none_write;
a->compressor.finish = archive_compressor_none_finish;
return (ARCHIVE_OK);
}
@ -120,7 +120,7 @@ archive_compressor_none_write(struct archive_write *a, const void *vbuff,
ssize_t bytes_written;
struct archive_none *state;
state = (struct archive_none *)a->compression_data;
state = (struct archive_none *)a->compressor.data;
buff = (const char *)vbuff;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
@ -194,7 +194,7 @@ archive_compressor_none_finish(struct archive_write *a)
int ret2;
struct archive_none *state;
state = (struct archive_none *)a->compression_data;
state = (struct archive_none *)a->compressor.data;
ret = ret2 = ARCHIVE_OK;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
@ -233,15 +233,10 @@ archive_compressor_none_finish(struct archive_write *a)
ret = ARCHIVE_OK;
}
}
/* Close the output */
if (a->client_closer != NULL)
ret2 = (a->client_closer)(&a->archive, a->client_data);
if (state->buffer)
free(state->buffer);
free(state);
a->compression_data = NULL;
a->compressor.data = NULL;
return (ret != ARCHIVE_OK ? ret : ret2);
return (ret);
}

View File

@ -0,0 +1,322 @@
/*-
* Copyright (c) 2007 Joerg Sonnenberger
* 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_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#include "archive.h"
#include "archive_private.h"
#include "archive_write_private.h"
#include "filter_fork.h"
struct private_data {
char *description;
pid_t child;
int child_stdin, child_stdout;
char *child_buf;
size_t child_buf_len, child_buf_avail;
};
static int archive_compressor_program_finish(struct archive_write *);
static int archive_compressor_program_init(struct archive_write *);
static int archive_compressor_program_write(struct archive_write *,
const void *, size_t);
/*
* Allocate, initialize and return a archive object.
*/
int
archive_write_set_compression_program(struct archive *_a, const char *cmd)
{
struct archive_write *a = (struct archive_write *)_a;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_compression_program");
a->compressor.init = &archive_compressor_program_init;
a->compressor.config = strdup(cmd);
return (ARCHIVE_OK);
}
/*
* Setup callback.
*/
static int
archive_compressor_program_init(struct archive_write *a)
{
int ret;
struct private_data *state;
static const char *prefix = "Program: ";
char *cmd = a->compressor.config;
if (a->client_opener != NULL) {
ret = (a->client_opener)(&a->archive, a->client_data);
if (ret != ARCHIVE_OK)
return (ret);
}
state = (struct private_data *)malloc(sizeof(*state));
if (state == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate data for compression");
return (ARCHIVE_FATAL);
}
memset(state, 0, sizeof(*state));
a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
strcpy(state->description, prefix);
strcat(state->description, cmd);
a->archive.compression_name = state->description;
state->child_buf_len = a->bytes_per_block;
state->child_buf_avail = 0;
state->child_buf = malloc(state->child_buf_len);
if (state->child_buf == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate data for compression buffer");
free(state);
return (ARCHIVE_FATAL);
}
if ((state->child = __archive_create_child(cmd,
&state->child_stdin, &state->child_stdout)) == -1) {
archive_set_error(&a->archive, EINVAL,
"Can't initialise filter");
free(state->child_buf);
free(state);
return (ARCHIVE_FATAL);
}
a->compressor.write = archive_compressor_program_write;
a->compressor.finish = archive_compressor_program_finish;
a->compressor.data = state;
return (0);
}
static ssize_t
child_write(struct archive_write *a, const char *buf, size_t buf_len)
{
struct private_data *state = a->compressor.data;
ssize_t ret;
if (state->child_stdin == -1)
return (-1);
if (buf_len == 0)
return (-1);
restart_write:
do {
ret = write(state->child_stdin, buf, buf_len);
} while (ret == -1 && errno == EINTR);
if (ret > 0)
return (ret);
if (ret == 0) {
close(state->child_stdin);
state->child_stdin = -1;
fcntl(state->child_stdout, F_SETFL, 0);
return (0);
}
if (ret == -1 && errno != EAGAIN)
return (-1);
do {
ret = read(state->child_stdout,
state->child_buf + state->child_buf_avail,
state->child_buf_len - state->child_buf_avail);
} while (ret == -1 && errno == EINTR);
if (ret == 0 || (ret == -1 && errno == EPIPE)) {
close(state->child_stdout);
state->child_stdout = -1;
fcntl(state->child_stdin, F_SETFL, 0);
goto restart_write;
}
if (ret == -1 && errno == EAGAIN) {
__archive_check_child(state->child_stdin, state->child_stdout);
goto restart_write;
}
if (ret == -1)
return (-1);
state->child_buf_avail += ret;
ret = (a->client_writer)(&a->archive, a->client_data,
state->child_buf, state->child_buf_avail);
if (ret <= 0)
return (-1);
if ((size_t)ret < state->child_buf_avail) {
memmove(state->child_buf, state->child_buf + ret,
state->child_buf_avail - ret);
}
state->child_buf_avail -= ret;
a->archive.raw_position += ret;
goto restart_write;
}
/*
* Write data to the compressed stream.
*/
static int
archive_compressor_program_write(struct archive_write *a, const void *buff,
size_t length)
{
struct private_data *state;
ssize_t ret;
const char *buf;
state = (struct private_data *)a->compressor.data;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"No write callback is registered? "
"This is probably an internal programming error.");
return (ARCHIVE_FATAL);
}
buf = buff;
while (length > 0) {
ret = child_write(a, buf, length);
if (ret == -1 || ret == 0) {
archive_set_error(&a->archive, EIO,
"Can't write to filter");
return (ARCHIVE_FATAL);
}
length -= ret;
buf += ret;
}
a->archive.file_position += length;
return (ARCHIVE_OK);
}
/*
* Finish the compression...
*/
static int
archive_compressor_program_finish(struct archive_write *a)
{
int ret, status;
ssize_t bytes_read, bytes_written;
struct private_data *state;
state = (struct private_data *)a->compressor.data;
ret = 0;
if (a->client_writer == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"No write callback is registered? "
"This is probably an internal programming error.");
ret = ARCHIVE_FATAL;
goto cleanup;
}
/* XXX pad compressed data. */
close(state->child_stdin);
state->child_stdin = -1;
fcntl(state->child_stdout, F_SETFL, 0);
for (;;) {
do {
bytes_read = read(state->child_stdout,
state->child_buf + state->child_buf_avail,
state->child_buf_len - state->child_buf_avail);
} while (bytes_read == -1 && errno == EINTR);
if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
break;
if (bytes_read == -1) {
archive_set_error(&a->archive, errno,
"Read from filter failed unexpectedly.");
ret = ARCHIVE_FATAL;
goto cleanup;
}
state->child_buf_avail += bytes_read;
bytes_written = (a->client_writer)(&a->archive, a->client_data,
state->child_buf, state->child_buf_avail);
if (bytes_written <= 0) {
ret = ARCHIVE_FATAL;
goto cleanup;
}
if ((size_t)bytes_written < state->child_buf_avail) {
memmove(state->child_buf,
state->child_buf + bytes_written,
state->child_buf_avail - bytes_written);
}
state->child_buf_avail -= bytes_written;
a->archive.raw_position += bytes_written;
}
/* XXX pad final compressed block. */
cleanup:
/* Shut down the child. */
if (state->child_stdin != -1)
close(state->child_stdin);
if (state->child_stdout != -1)
close(state->child_stdout);
while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
continue;
if (status != 0) {
archive_set_error(&a->archive, EIO,
"Filter exited with failure.");
ret = ARCHIVE_FATAL;
}
/* Release our configuration data. */
free(a->compressor.config);
a->compressor.config = NULL;
/* Release our private state data. */
free(state->child_buf);
free(state->description);
free(state);
return (ret);
}

View File

@ -28,9 +28,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -72,31 +69,22 @@ struct ar_w {
#define AR_fmag_offset 58
#define AR_fmag_size 2
/*
* "ar" magic numbers.
*/
#define ARMAG "!<arch>\n"
#define SARMAG 8 /* strlen(ARMAG); */
#define AR_EFMT1 "#1/"
#define SAR_EFMT1 3 /* strlen(AR_EFMT1); */
#define ARFMAG "`\n"
#define SARFMAG 2 /* strlen(ARFMAG); */
static int __archive_write_set_format_ar(struct archive_write *);
static int archive_write_ar_header(struct archive_write *,
struct archive_entry *);
static ssize_t archive_write_ar_data(struct archive_write *, const void *buff,
size_t s);
static int archive_write_ar_destroy(struct archive_write *);
static int archive_write_ar_finish_entry(struct archive_write *);
static int format_octal(int64_t v, char *p, int s);
static int format_decimal(int64_t v, char *p, int s);
static int archive_write_set_format_ar(struct archive_write *);
static int archive_write_ar_header(struct archive_write *,
struct archive_entry *);
static ssize_t archive_write_ar_data(struct archive_write *,
const void *buff, size_t s);
static int archive_write_ar_destroy(struct archive_write *);
static int archive_write_ar_finish_entry(struct archive_write *);
static const char *basename(const char *path);
static int format_octal(int64_t v, char *p, int s);
static int format_decimal(int64_t v, char *p, int s);
int
archive_write_set_format_ar_bsd(struct archive *_a)
{
struct archive_write *a = (struct archive_write *)_a;
int r = __archive_write_set_format_ar(a);
int r = archive_write_set_format_ar(a);
if (r == ARCHIVE_OK) {
a->archive_format = ARCHIVE_FORMAT_AR_BSD;
a->archive_format_name = "ar (BSD)";
@ -108,7 +96,7 @@ int
archive_write_set_format_ar_svr4(struct archive *_a)
{
struct archive_write *a = (struct archive_write *)_a;
int r = __archive_write_set_format_ar(a);
int r = archive_write_set_format_ar(a);
if (r == ARCHIVE_OK) {
a->archive_format = ARCHIVE_FORMAT_AR_GNU;
a->archive_format_name = "ar (GNU/SVR4)";
@ -120,7 +108,7 @@ archive_write_set_format_ar_svr4(struct archive *_a)
* Generic initialization.
*/
static int
__archive_write_set_format_ar(struct archive_write *a)
archive_write_set_format_ar(struct archive_write *a)
{
struct ar_w *ar;
@ -151,40 +139,48 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
char buff[60];
char *ss, *se;
struct ar_w *ar;
const char *pp;
const struct stat *st;
const char *pathname;
const char *filename;
ret = 0;
append_fn = 0;
ar = (struct ar_w *)a->format_data;
ar->is_strtab = 0;
filename = NULL;
if (a->archive.file_position == 0) {
/*
* We are now at the beginning of the archive,
* so we need first write the ar global header.
*/
(a->compression_write)(a, ARMAG, SARMAG);
/*
* Reject files with empty name.
*/
pathname = archive_entry_pathname(entry);
if (*pathname == '\0') {
archive_set_error(&a->archive, EINVAL,
"Invalid filename");
return (ARCHIVE_WARN);
}
/*
* If we are now at the beginning of the archive,
* we need first write the ar global header.
*/
if (a->archive.file_position == 0)
(a->compressor.write)(a, "!<arch>\n", 8);
memset(buff, ' ', 60);
strncpy(&buff[AR_fmag_offset], ARFMAG, SARFMAG);
strncpy(&buff[AR_fmag_offset], "`\n", 2);
pp = archive_entry_pathname(entry);
if (strcmp(pp, "/") == 0 ) {
if (strcmp(pathname, "/") == 0 ) {
/* Entry is archive symbol table in GNU format */
buff[AR_name_offset] = '/';
goto stat;
}
if (strcmp(pp, "__.SYMDEF") == 0) {
if (strcmp(pathname, "__.SYMDEF") == 0) {
/* Entry is archive symbol table in BSD format */
strncpy(buff + AR_name_offset, "__.SYMDEF", 9);
goto stat;
}
if (strcmp(pp, "//") == 0) {
if (strcmp(pathname, "//") == 0) {
/*
* Entry is archive string table, inform that we should
* Entry is archive filename table, inform that we should
* collect strtab in next _data call.
*/
ar->is_strtab = 1;
@ -196,7 +192,17 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
goto size;
}
/* Otherwise, entry is a normal archive member. */
/*
* Otherwise, entry is a normal archive member.
* Strip leading paths from filenames, if any.
*/
if ((filename = basename(pathname)) == NULL) {
/* Reject filenames with trailing "/" */
archive_set_error(&a->archive, EINVAL,
"Invalid filename");
return (ARCHIVE_WARN);
}
if (a->archive_format == ARCHIVE_FORMAT_AR_GNU) {
/*
* SVR4/GNU variant use a "/" to mark then end of the filename,
@ -204,9 +210,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* So, the longest filename here (without extension) is
* actually 15 bytes.
*/
if (strlen(pp) <= 15) {
strncpy(&buff[AR_name_offset], pp, strlen(pp));
buff[AR_name_offset + strlen(pp)] = '/';
if (strlen(filename) <= 15) {
strncpy(&buff[AR_name_offset],
filename, strlen(filename));
buff[AR_name_offset + strlen(filename)] = '/';
} else {
/*
* For filename longer than 15 bytes, GNU variant
@ -220,15 +227,15 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_WARN);
}
se = (char *)malloc(strlen(pp) + 3);
se = (char *)malloc(strlen(filename) + 3);
if (se == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate filename buffer");
return (ARCHIVE_FATAL);
}
strncpy(se, pp, strlen(pp));
strcpy(se + strlen(pp), "/\n");
strncpy(se, filename, strlen(filename));
strcpy(se + strlen(filename), "/\n");
ss = strstr(ar->strtab, se);
free(se);
@ -263,47 +270,55 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* The name is then written immediately following the
* archive header.
*/
if (strlen(pp) <= 16 && strchr(pp, ' ') == NULL) {
strncpy(&buff[AR_name_offset], pp, strlen(pp));
buff[AR_name_offset + strlen(pp)] = ' ';
if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
strncpy(&buff[AR_name_offset], filename, strlen(filename));
buff[AR_name_offset + strlen(filename)] = ' ';
}
else {
strncpy(buff + AR_name_offset, AR_EFMT1, SAR_EFMT1);
if (format_decimal(strlen(pp),
buff + AR_name_offset + SAR_EFMT1,
AR_name_size - SAR_EFMT1)) {
strncpy(buff + AR_name_offset, "#1/", 3);
if (format_decimal(strlen(filename),
buff + AR_name_offset + 3,
AR_name_size - 3)) {
archive_set_error(&a->archive, ERANGE,
"File name too long");
return (ARCHIVE_WARN);
}
append_fn = 1;
archive_entry_set_size(entry,
archive_entry_size(entry) + strlen(pp));
archive_entry_size(entry) + strlen(filename));
}
}
stat:
st = archive_entry_stat(entry);
if (format_decimal(st->st_mtime, buff + AR_date_offset, AR_date_size)) {
if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {
archive_set_error(&a->archive, ERANGE,
"File modification time too large");
return (ARCHIVE_WARN);
}
if (format_decimal(st->st_uid, buff + AR_uid_offset, AR_uid_size)) {
if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {
archive_set_error(&a->archive, ERANGE,
"Numeric user ID too large");
return (ARCHIVE_WARN);
}
if (format_decimal(st->st_gid, buff + AR_gid_offset, AR_gid_size)) {
if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {
archive_set_error(&a->archive, ERANGE,
"Numeric group ID too large");
return (ARCHIVE_WARN);
}
if (format_octal(st->st_mode, buff + AR_mode_offset, AR_mode_size)) {
if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {
archive_set_error(&a->archive, ERANGE,
"Numeric mode too large");
return (ARCHIVE_WARN);
}
/*
* Sanity Check: A non-pseudo archive member should always be
* a regular file.
*/
if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {
archive_set_error(&a->archive, EINVAL,
"Regular file required for non-pseudo member");
return (ARCHIVE_WARN);
}
size:
if (format_decimal(archive_entry_size(entry), buff + AR_size_offset,
@ -313,7 +328,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_WARN);
}
ret = (a->compression_write)(a, buff, 60);
ret = (a->compressor.write)(a, buff, 60);
if (ret != ARCHIVE_OK)
return (ret);
@ -321,10 +336,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
ar->entry_padding = ar->entry_bytes_remaining % 2;
if (append_fn > 0) {
ret = (a->compression_write)(a, pp, strlen(pp));
ret = (a->compressor.write)(a, filename, strlen(filename));
if (ret != ARCHIVE_OK)
return (ret);
ar->entry_bytes_remaining -= strlen(pp);
ar->entry_bytes_remaining -= strlen(filename);
}
return (ARCHIVE_OK);
@ -357,7 +372,7 @@ archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
ar->has_strtab = 1;
}
ret = (a->compression_write)(a, buff, s);
ret = (a->compressor.write)(a, buff, s);
if (ret != ARCHIVE_OK)
return (ret);
@ -407,7 +422,7 @@ archive_write_ar_finish_entry(struct archive_write *a)
return (ARCHIVE_WARN);
}
ret = (a->compression_write)(a, "\n", 1);
ret = (a->compressor.write)(a, "\n", 1);
return (ret);
}
@ -490,3 +505,24 @@ format_decimal(int64_t v, char *p, int s)
return (-1);
}
static const char *
basename(const char *path)
{
const char *endp, *startp;
endp = path + strlen(path) - 1;
/*
* For filename with trailing slash(es), we return
* NULL indicating an error.
*/
if (*endp == '/')
return (NULL);
/* Find the start of the base */
startp = endp;
while (startp > path && *(startp - 1) != '/')
startp--;
return (startp);
}

View File

@ -26,9 +26,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -111,7 +108,6 @@ archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
struct cpio *cpio;
const char *p, *path;
int pathlength, ret;
const struct stat *st;
struct cpio_header h;
cpio = (struct cpio *)a->format_data;
@ -119,31 +115,31 @@ archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
path = archive_entry_pathname(entry);
pathlength = strlen(path) + 1; /* Include trailing null. */
st = archive_entry_stat(entry);
memset(&h, 0, sizeof(h));
format_octal(070707, &h.c_magic, sizeof(h.c_magic));
format_octal(st->st_dev, &h.c_dev, sizeof(h.c_dev));
format_octal(archive_entry_dev(entry), &h.c_dev, sizeof(h.c_dev));
/*
* TODO: Generate artificial inode numbers rather than just
* re-using the ones off the disk. That way, the 18-bit c_ino
* field only limits the number of files in the archive.
*/
if (st->st_ino > 0777777) {
if (archive_entry_ino(entry) > 0777777) {
archive_set_error(&a->archive, ERANGE, "large inode number truncated");
ret = ARCHIVE_WARN;
}
format_octal(st->st_ino & 0777777, &h.c_ino, sizeof(h.c_ino));
format_octal(st->st_mode, &h.c_mode, sizeof(h.c_mode));
format_octal(st->st_uid, &h.c_uid, sizeof(h.c_uid));
format_octal(st->st_gid, &h.c_gid, sizeof(h.c_gid));
format_octal(st->st_nlink, &h.c_nlink, sizeof(h.c_nlink));
if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode))
format_octal(st->st_rdev, &h.c_rdev, sizeof(h.c_rdev));
format_octal(archive_entry_ino(entry) & 0777777, &h.c_ino, sizeof(h.c_ino));
format_octal(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
format_octal(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
format_octal(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
format_octal(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
if (archive_entry_filetype(entry) == AE_IFBLK
|| archive_entry_filetype(entry) == AE_IFCHR)
format_octal(archive_entry_dev(entry), &h.c_rdev, sizeof(h.c_rdev));
else
format_octal(0, &h.c_rdev, sizeof(h.c_rdev));
format_octal(st->st_mtime, &h.c_mtime, sizeof(h.c_mtime));
format_octal(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
format_octal(pathlength, &h.c_namesize, sizeof(h.c_namesize));
/* Symlinks get the link written as the body of the entry. */
@ -151,21 +147,21 @@ archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
if (p != NULL && *p != '\0')
format_octal(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
else
format_octal(st->st_size, &h.c_filesize, sizeof(h.c_filesize));
format_octal(archive_entry_size(entry), &h.c_filesize, sizeof(h.c_filesize));
ret = (a->compression_write)(a, &h, sizeof(h));
ret = (a->compressor.write)(a, &h, sizeof(h));
if (ret != ARCHIVE_OK)
return (ARCHIVE_FATAL);
ret = (a->compression_write)(a, path, pathlength);
ret = (a->compressor.write)(a, path, pathlength);
if (ret != ARCHIVE_OK)
return (ARCHIVE_FATAL);
cpio->entry_bytes_remaining = st->st_size;
cpio->entry_bytes_remaining = archive_entry_size(entry);
/* Write the symlink now. */
if (p != NULL && *p != '\0')
ret = (a->compression_write)(a, p, strlen(p));
ret = (a->compressor.write)(a, p, strlen(p));
return (ret);
}
@ -180,7 +176,7 @@ archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s)
if (s > cpio->entry_bytes_remaining)
s = cpio->entry_bytes_remaining;
ret = (a->compression_write)(a, buff, s);
ret = (a->compressor.write)(a, buff, s);
cpio->entry_bytes_remaining -= s;
if (ret >= 0)
return (s);
@ -222,15 +218,12 @@ static int
archive_write_cpio_finish(struct archive_write *a)
{
struct cpio *cpio;
struct stat st;
int er;
struct archive_entry *trailer;
cpio = (struct cpio *)a->format_data;
trailer = archive_entry_new();
memset(&st, 0, sizeof(st));
st.st_nlink = 1;
archive_entry_copy_stat(trailer, &st);
archive_entry_set_nlink(trailer, 1);
archive_entry_set_pathname(trailer, "TRAILER!!!");
er = archive_write_cpio_header(a, trailer);
archive_entry_free(trailer);
@ -259,7 +252,7 @@ archive_write_cpio_finish_entry(struct archive_write *a)
while (cpio->entry_bytes_remaining > 0) {
to_write = cpio->entry_bytes_remaining < a->null_length ?
cpio->entry_bytes_remaining : a->null_length;
ret = (a->compression_write)(a, a->nulls, to_write);
ret = (a->compressor.write)(a, a->nulls, to_write);
if (ret != ARCHIVE_OK)
return (ret);
cpio->entry_bytes_remaining -= to_write;

View File

@ -26,16 +26,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#else
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -45,9 +35,6 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "archive.h"
#include "archive_entry.h"
@ -67,8 +54,8 @@ static void add_pax_attr_int(struct archive_string *,
static void add_pax_attr_time(struct archive_string *,
const char *key, int64_t sec,
unsigned long nanos);
static void add_pax_attr_w(struct archive_string *, const char *,
const wchar_t *, const wchar_t *);
static void add_pax_attr_w(struct archive_string *,
const char *key, const wchar_t *wvalue);
static ssize_t archive_write_pax_data(struct archive_write *,
const void *, size_t);
static int archive_write_pax_finish(struct archive_write *);
@ -205,42 +192,30 @@ add_pax_attr_int(struct archive_string *as, const char *key, int64_t value)
add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value));
}
/*
* UTF-8 encode the concatenation of two strings.
*
* This interface eliminates the need to do some string
* manipulations at higher layers.
*/
static char *
utf8_encode(const wchar_t *wval1, const wchar_t *wval2)
utf8_encode(const wchar_t *wval)
{
int utf8len;
const wchar_t *wp, **wpp;
const wchar_t *wp;
unsigned long wc;
char *utf8_value, *p;
const wchar_t *vals[2];
vals[0] = wval1;
vals[1] = wval2;
utf8len = 0;
for (wpp = vals; wpp < vals + 2 && *wpp; wpp++) {
for (wp = *wpp; *wp != L'\0'; ) {
wc = *wp++;
if (wc <= 0x7f)
utf8len++;
else if (wc <= 0x7ff)
utf8len += 2;
else if (wc <= 0xffff)
utf8len += 3;
else if (wc <= 0x1fffff)
utf8len += 4;
else if (wc <= 0x3ffffff)
utf8len += 5;
else if (wc <= 0x7fffffff)
utf8len += 6;
/* Ignore larger values; UTF-8 can't encode them. */
}
for (wp = wval; *wp != L'\0'; ) {
wc = *wp++;
if (wc <= 0x7f)
utf8len++;
else if (wc <= 0x7ff)
utf8len += 2;
else if (wc <= 0xffff)
utf8len += 3;
else if (wc <= 0x1fffff)
utf8len += 4;
else if (wc <= 0x3ffffff)
utf8len += 5;
else if (wc <= 0x7fffffff)
utf8len += 6;
/* Ignore larger values; UTF-8 can't encode them. */
}
utf8_value = (char *)malloc(utf8len + 1);
@ -249,45 +224,42 @@ utf8_encode(const wchar_t *wval1, const wchar_t *wval2)
return (NULL);
}
p = utf8_value;
for (wpp = vals; wpp < vals + 2 && *wpp; wpp++) {
for (wp = *wpp; *wp != L'\0'; ) {
wc = *wp++;
if (wc <= 0x7f) {
*p++ = (char)wc;
} else if (wc <= 0x7ff) {
p[0] = 0xc0 | ((wc >> 6) & 0x1f);
p[1] = 0x80 | (wc & 0x3f);
p += 2;
} else if (wc <= 0xffff) {
p[0] = 0xe0 | ((wc >> 12) & 0x0f);
p[1] = 0x80 | ((wc >> 6) & 0x3f);
p[2] = 0x80 | (wc & 0x3f);
p += 3;
} else if (wc <= 0x1fffff) {
p[0] = 0xf0 | ((wc >> 18) & 0x07);
p[1] = 0x80 | ((wc >> 12) & 0x3f);
p[2] = 0x80 | ((wc >> 6) & 0x3f);
p[3] = 0x80 | (wc & 0x3f);
p += 4;
} else if (wc <= 0x3ffffff) {
p[0] = 0xf8 | ((wc >> 24) & 0x03);
p[1] = 0x80 | ((wc >> 18) & 0x3f);
p[2] = 0x80 | ((wc >> 12) & 0x3f);
p[3] = 0x80 | ((wc >> 6) & 0x3f);
p[4] = 0x80 | (wc & 0x3f);
p += 5;
} else if (wc <= 0x7fffffff) {
p[0] = 0xfc | ((wc >> 30) & 0x01);
p[1] = 0x80 | ((wc >> 24) & 0x3f);
p[1] = 0x80 | ((wc >> 18) & 0x3f);
p[2] = 0x80 | ((wc >> 12) & 0x3f);
p[3] = 0x80 | ((wc >> 6) & 0x3f);
p[4] = 0x80 | (wc & 0x3f);
p += 6;
}
/* Ignore larger values; UTF-8 can't encode them. */
for (wp = wval, p = utf8_value; *wp != L'\0'; ) {
wc = *wp++;
if (wc <= 0x7f) {
*p++ = (char)wc;
} else if (wc <= 0x7ff) {
p[0] = 0xc0 | ((wc >> 6) & 0x1f);
p[1] = 0x80 | (wc & 0x3f);
p += 2;
} else if (wc <= 0xffff) {
p[0] = 0xe0 | ((wc >> 12) & 0x0f);
p[1] = 0x80 | ((wc >> 6) & 0x3f);
p[2] = 0x80 | (wc & 0x3f);
p += 3;
} else if (wc <= 0x1fffff) {
p[0] = 0xf0 | ((wc >> 18) & 0x07);
p[1] = 0x80 | ((wc >> 12) & 0x3f);
p[2] = 0x80 | ((wc >> 6) & 0x3f);
p[3] = 0x80 | (wc & 0x3f);
p += 4;
} else if (wc <= 0x3ffffff) {
p[0] = 0xf8 | ((wc >> 24) & 0x03);
p[1] = 0x80 | ((wc >> 18) & 0x3f);
p[2] = 0x80 | ((wc >> 12) & 0x3f);
p[3] = 0x80 | ((wc >> 6) & 0x3f);
p[4] = 0x80 | (wc & 0x3f);
p += 5;
} else if (wc <= 0x7fffffff) {
p[0] = 0xfc | ((wc >> 30) & 0x01);
p[1] = 0x80 | ((wc >> 24) & 0x3f);
p[1] = 0x80 | ((wc >> 18) & 0x3f);
p[2] = 0x80 | ((wc >> 12) & 0x3f);
p[3] = 0x80 | ((wc >> 6) & 0x3f);
p[4] = 0x80 | (wc & 0x3f);
p += 6;
}
/* Ignore larger values; UTF-8 can't encode them. */
}
*p = '\0';
@ -295,10 +267,9 @@ utf8_encode(const wchar_t *wval1, const wchar_t *wval2)
}
static void
add_pax_attr_w(struct archive_string *as, const char *key,
const wchar_t *wval1, const wchar_t *wval2)
add_pax_attr_w(struct archive_string *as, const char *key, const wchar_t *wval)
{
char *utf8_value = utf8_encode(wval1, wval2);
char *utf8_value = utf8_encode(wval);
if (utf8_value == NULL)
return;
add_pax_attr(as, key, utf8_value);
@ -383,7 +354,7 @@ archive_write_pax_header_xattrs(struct pax *pax, struct archive_entry *entry)
free(url_encoded_name); /* Done with this. */
}
if (wcs_name != NULL) {
encoded_name = utf8_encode(wcs_name, NULL);
encoded_name = utf8_encode(wcs_name);
free(wcs_name); /* Done with wchar_t name. */
}
@ -413,13 +384,12 @@ archive_write_pax_header(struct archive_write *a,
{
struct archive_entry *entry_main;
const char *linkname, *p;
char *t;
const char *hardlink;
const wchar_t *wp;
const char *suffix_start;
int need_extension, r, ret;
int need_slash = 0;
struct pax *pax;
const struct stat *st_main, *st_original;
char paxbuff[512];
char ustarbuff[512];
@ -429,28 +399,37 @@ archive_write_pax_header(struct archive_write *a,
need_extension = 0;
pax = (struct pax *)a->format_data;
st_original = archive_entry_stat(entry_original);
hardlink = archive_entry_hardlink(entry_original);
/* Make sure this is a type of entry that we can handle here */
if (hardlink == NULL) {
switch (st_original->st_mode & S_IFMT) {
case S_IFREG:
case S_IFLNK:
case S_IFCHR:
case S_IFBLK:
case S_IFDIR:
case S_IFIFO:
switch (archive_entry_filetype(entry_original)) {
case AE_IFBLK:
case AE_IFCHR:
case AE_IFIFO:
case AE_IFLNK:
case AE_IFREG:
break;
case AE_IFDIR:
/*
* Ensure a trailing '/'. Modify the original
* entry so the client sees the change.
*/
p = archive_entry_pathname(entry_original);
if (p[strlen(p) - 1] != '/') {
t = (char *)malloc(strlen(p) + 2);
if (t != NULL) {
strcpy(t, p);
strcat(t, "/");
archive_entry_copy_pathname(entry_original, t);
free(t);
}
}
break;
case S_IFSOCK:
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"tar format cannot archive socket");
return (ARCHIVE_WARN);
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"tar format cannot archive this (mode=0%lo)",
(unsigned long)st_original->st_mode);
"tar format cannot archive this (type=0%lo)",
(unsigned long)archive_entry_filetype(entry_original));
return (ARCHIVE_WARN);
}
}
@ -458,7 +437,6 @@ archive_write_pax_header(struct archive_write *a,
/* Copy entry so we can modify it as needed. */
entry_main = archive_entry_clone(entry_original);
archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
st_main = archive_entry_stat(entry_main);
/*
* Determining whether or not the name is too big is ugly
@ -468,23 +446,18 @@ archive_write_pax_header(struct archive_write *a,
*/
wp = archive_entry_pathname_w(entry_main);
p = archive_entry_pathname(entry_main);
if (S_ISDIR(st_original->st_mode))
if (p[strlen(p) - 1] != '/')
need_slash = 1;
/* Short enough for just 'name' field */
if (strlen(p) + need_slash <= 100)
if (strlen(p) <= 100) /* Short enough for just 'name' field */
suffix_start = p; /* Record a zero-length prefix */
else
/* Find the largest suffix that fits in 'name' field. */
suffix_start = strchr(p + strlen(p) + need_slash - 100 - 1, '/');
suffix_start = strchr(p + strlen(p) - 100 - 1, '/');
/*
* If name is too long, or has non-ASCII characters, add
* 'path' to pax extended attrs.
*/
if (suffix_start == NULL || suffix_start - p > 155 || has_non_ASCII(wp)) {
add_pax_attr_w(&(pax->pax_header), "path", wp,
need_slash ? L"/" : NULL);
add_pax_attr_w(&(pax->pax_header), "path", wp);
archive_entry_set_pathname(entry_main,
build_ustar_entry_name(ustar_entry_name, p, strlen(p), NULL));
need_extension = 1;
@ -506,7 +479,7 @@ archive_write_pax_header(struct archive_write *a,
/* If the link is long or has a non-ASCII character,
* store it as a pax extended attribute. */
if (strlen(linkname) > 100 || has_non_ASCII(wp)) {
add_pax_attr_w(&(pax->pax_header), "linkpath", wp, NULL);
add_pax_attr_w(&(pax->pax_header), "linkpath", wp);
if (hardlink != NULL)
archive_entry_set_hardlink(entry_main,
"././@LongHardLink");
@ -518,14 +491,16 @@ archive_write_pax_header(struct archive_write *a,
}
/* If file size is too large, add 'size' to pax extended attrs. */
if (st_main->st_size >= (((int64_t)1) << 33)) {
add_pax_attr_int(&(pax->pax_header), "size", st_main->st_size);
if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
add_pax_attr_int(&(pax->pax_header), "size",
archive_entry_size(entry_main));
need_extension = 1;
}
/* If numeric GID is too large, add 'gid' to pax extended attrs. */
if (st_main->st_gid >= (1 << 18)) {
add_pax_attr_int(&(pax->pax_header), "gid", st_main->st_gid);
if (archive_entry_gid(entry_main) >= (1 << 18)) {
add_pax_attr_int(&(pax->pax_header), "gid",
archive_entry_gid(entry_main));
need_extension = 1;
}
@ -534,14 +509,15 @@ archive_write_pax_header(struct archive_write *a,
p = archive_entry_gname(entry_main);
wp = archive_entry_gname_w(entry_main);
if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) {
add_pax_attr_w(&(pax->pax_header), "gname", wp, NULL);
add_pax_attr_w(&(pax->pax_header), "gname", wp);
archive_entry_set_gname(entry_main, NULL);
need_extension = 1;
}
/* If numeric UID is too large, add 'uid' to pax extended attrs. */
if (st_main->st_uid >= (1 << 18)) {
add_pax_attr_int(&(pax->pax_header), "uid", st_main->st_uid);
if (archive_entry_uid(entry_main) >= (1 << 18)) {
add_pax_attr_int(&(pax->pax_header), "uid",
archive_entry_uid(entry_main));
need_extension = 1;
}
@ -550,7 +526,7 @@ archive_write_pax_header(struct archive_write *a,
p = archive_entry_uname(entry_main);
wp = archive_entry_uname_w(entry_main);
if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) {
add_pax_attr_w(&(pax->pax_header), "uname", wp, NULL);
add_pax_attr_w(&(pax->pax_header), "uname", wp);
archive_entry_set_uname(entry_main, NULL);
need_extension = 1;
}
@ -566,15 +542,15 @@ archive_write_pax_header(struct archive_write *a,
*
* Of course, this is only needed for block or char device entries.
*/
if (S_ISBLK(st_main->st_mode) ||
S_ISCHR(st_main->st_mode)) {
if (archive_entry_filetype(entry_main) == AE_IFBLK
|| archive_entry_filetype(entry_main) == AE_IFCHR) {
/*
* If rdevmajor is too large, add 'SCHILY.devmajor' to
* extended attributes.
*/
dev_t rdevmajor, rdevminor;
rdevmajor = major(st_main->st_rdev);
rdevminor = minor(st_main->st_rdev);
rdevmajor = archive_entry_rdevmajor(entry_main);
rdevminor = archive_entry_rdevminor(entry_main);
if (rdevmajor >= (1 << 18)) {
add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor",
rdevmajor);
@ -615,7 +591,8 @@ archive_write_pax_header(struct archive_write *a,
* high-resolution timestamp in "restricted pax" mode.
*/
if (!need_extension &&
((st_main->st_mtime < 0) || (st_main->st_mtime >= 0x7fffffff)))
((archive_entry_mtime(entry_main) < 0)
|| (archive_entry_mtime(entry_main) >= 0x7fffffff)))
need_extension = 1;
/* I use a star-compatible file flag attribute. */
@ -647,24 +624,24 @@ archive_write_pax_header(struct archive_write *a,
if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
need_extension) {
if (st_main->st_mtime < 0 ||
st_main->st_mtime >= 0x7fffffff ||
ARCHIVE_STAT_MTIME_NANOS(st_main) != 0)
if (archive_entry_mtime(entry_main) < 0 ||
archive_entry_mtime(entry_main) >= 0x7fffffff ||
archive_entry_mtime_nsec(entry_main) != 0)
add_pax_attr_time(&(pax->pax_header), "mtime",
st_main->st_mtime,
ARCHIVE_STAT_MTIME_NANOS(st_main));
archive_entry_mtime(entry_main),
archive_entry_mtime_nsec(entry_main));
if (st_main->st_ctime != 0 ||
ARCHIVE_STAT_CTIME_NANOS(st_main) != 0)
if (archive_entry_ctime(entry_main) != 0 ||
archive_entry_ctime_nsec(entry_main) != 0)
add_pax_attr_time(&(pax->pax_header), "ctime",
st_main->st_ctime,
ARCHIVE_STAT_CTIME_NANOS(st_main));
archive_entry_ctime(entry_main),
archive_entry_ctime_nsec(entry_main));
if (st_main->st_atime != 0 ||
ARCHIVE_STAT_ATIME_NANOS(st_main) != 0)
if (archive_entry_atime(entry_main) != 0 ||
archive_entry_atime_nsec(entry_main) != 0)
add_pax_attr_time(&(pax->pax_header), "atime",
st_main->st_atime,
ARCHIVE_STAT_ATIME_NANOS(st_main));
archive_entry_atime(entry_main),
archive_entry_atime_nsec(entry_main));
/* I use a star-compatible file flag attribute. */
p = archive_entry_fflags_text(entry_main);
@ -677,30 +654,30 @@ archive_write_pax_header(struct archive_write *a,
ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID);
if (wp != NULL && *wp != L'\0')
add_pax_attr_w(&(pax->pax_header),
"SCHILY.acl.access", wp, NULL);
"SCHILY.acl.access", wp);
wp = archive_entry_acl_text_w(entry_original,
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID);
if (wp != NULL && *wp != L'\0')
add_pax_attr_w(&(pax->pax_header),
"SCHILY.acl.default", wp, NULL);
"SCHILY.acl.default", wp);
/* Include star-compatible metadata info. */
/* Note: "SCHILY.dev{major,minor}" are NOT the
* major/minor portions of "SCHILY.dev". */
add_pax_attr_int(&(pax->pax_header), "SCHILY.dev",
st_main->st_dev);
archive_entry_dev(entry_main));
add_pax_attr_int(&(pax->pax_header), "SCHILY.ino",
st_main->st_ino);
archive_entry_ino(entry_main));
add_pax_attr_int(&(pax->pax_header), "SCHILY.nlink",
st_main->st_nlink);
archive_entry_nlink(entry_main));
/* Store extended attributes */
archive_write_pax_header_xattrs(pax, entry_original);
}
/* Only regular files have data. */
if (!S_ISREG(archive_entry_mode(entry_main)))
if (archive_entry_filetype(entry_main) != AE_IFREG)
archive_entry_set_size(entry_main, 0);
/*
@ -755,36 +732,40 @@ archive_write_pax_header(struct archive_write *a,
/* If we built any extended attributes, write that entry first. */
ret = ARCHIVE_OK;
if (archive_strlen(&(pax->pax_header)) > 0) {
struct stat st;
struct archive_entry *pax_attr_entry;
time_t s;
uid_t uid;
gid_t gid;
mode_t mode;
long ns;
memset(&st, 0, sizeof(st));
pax_attr_entry = archive_entry_new();
p = archive_entry_pathname(entry_main);
archive_entry_set_pathname(pax_attr_entry,
build_pax_attribute_name(pax_entry_name, p));
st.st_size = archive_strlen(&(pax->pax_header));
archive_entry_set_size(pax_attr_entry,
archive_strlen(&(pax->pax_header)));
/* Copy uid/gid (but clip to ustar limits). */
st.st_uid = st_main->st_uid;
if (st.st_uid >= 1 << 18)
st.st_uid = (1 << 18) - 1;
st.st_gid = st_main->st_gid;
if (st.st_gid >= 1 << 18)
st.st_gid = (1 << 18) - 1;
uid = archive_entry_uid(entry_main);
if (uid >= 1 << 18)
uid = (1 << 18) - 1;
archive_entry_set_uid(pax_attr_entry, uid);
gid = archive_entry_gid(entry_main);
if (gid >= 1 << 18)
gid = (1 << 18) - 1;
archive_entry_set_gid(pax_attr_entry, gid);
/* Copy mode over (but not setuid/setgid bits) */
st.st_mode = st_main->st_mode;
mode = archive_entry_mode(entry_main);
#ifdef S_ISUID
st.st_mode &= ~S_ISUID;
mode &= ~S_ISUID;
#endif
#ifdef S_ISGID
st.st_mode &= ~S_ISGID;
mode &= ~S_ISGID;
#endif
#ifdef S_ISVTX
st.st_mode &= ~S_ISVTX;
mode &= ~S_ISVTX;
#endif
archive_entry_copy_stat(pax_attr_entry, &st);
archive_entry_set_mode(pax_attr_entry, mode);
/* Copy uname/gname. */
archive_entry_set_uname(pax_attr_entry,
@ -821,7 +802,7 @@ archive_write_pax_header(struct archive_write *a,
write(2, msg, strlen(msg));
exit(1);
}
r = (a->compression_write)(a, paxbuff, 512);
r = (a->compressor.write)(a, paxbuff, 512);
if (r != ARCHIVE_OK) {
pax->entry_bytes_remaining = 0;
pax->entry_padding = 0;
@ -831,7 +812,7 @@ archive_write_pax_header(struct archive_write *a,
pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));
pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining);
r = (a->compression_write)(a, pax->pax_header.s,
r = (a->compressor.write)(a, pax->pax_header.s,
archive_strlen(&(pax->pax_header)));
if (r != ARCHIVE_OK) {
/* If a write fails, we're pretty much toast. */
@ -847,7 +828,7 @@ archive_write_pax_header(struct archive_write *a,
}
/* Write the header for main entry. */
r = (a->compression_write)(a, ustarbuff, 512);
r = (a->compressor.write)(a, ustarbuff, 512);
if (r != ARCHIVE_OK)
return (r);
@ -1068,7 +1049,7 @@ archive_write_pax_finish(struct archive_write *a)
struct pax *pax;
int r;
if (a->compression_write == NULL)
if (a->compressor.write == NULL)
return (ARCHIVE_OK);
pax = (struct pax *)a->format_data;
@ -1107,7 +1088,7 @@ write_nulls(struct archive_write *a, size_t padding)
while (padding > 0) {
to_write = padding < a->null_length ? padding : a->null_length;
ret = (a->compression_write)(a, a->nulls, to_write);
ret = (a->compressor.write)(a, a->nulls, to_write);
if (ret != ARCHIVE_OK)
return (ret);
padding -= to_write;
@ -1125,7 +1106,7 @@ archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
if (s > pax->entry_bytes_remaining)
s = pax->entry_bytes_remaining;
ret = (a->compression_write)(a, buff, s);
ret = (a->compressor.write)(a, buff, s);
pax->entry_bytes_remaining -= s;
if (ret == ARCHIVE_OK)
return (s);

View File

@ -26,9 +26,6 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -84,7 +81,7 @@ shar_printf(struct archive_write *a, const char *fmt, ...)
va_start(ap, fmt);
archive_string_empty(&(shar->work));
archive_string_vsprintf(&(shar->work), fmt, ap);
ret = ((a->compression_write)(a, shar->work.s, strlen(shar->work.s)));
ret = ((a->compressor.write)(a, shar->work.s, strlen(shar->work.s)));
va_end(ap);
return (ret);
}
@ -149,7 +146,6 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
const char *name;
char *p, *pp;
struct shar *shar;
const struct stat *st;
int ret;
shar = (struct shar *)a->format_data;
@ -168,22 +164,21 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
archive_entry_free(shar->entry);
shar->entry = archive_entry_clone(entry);
name = archive_entry_pathname(entry);
st = archive_entry_stat(entry);
/* Handle some preparatory issues. */
switch(st->st_mode & S_IFMT) {
case S_IFREG:
switch(archive_entry_filetype(entry)) {
case AE_IFREG:
/* Only regular files have non-zero size. */
break;
case S_IFDIR:
case AE_IFDIR:
archive_entry_set_size(entry, 0);
/* Don't bother trying to recreate '.' */
if (strcmp(name, ".") == 0 || strcmp(name, "./") == 0)
return (ARCHIVE_OK);
break;
case S_IFIFO:
case S_IFCHR:
case S_IFBLK:
case AE_IFIFO:
case AE_IFCHR:
case AE_IFBLK:
/* All other file types have zero size in the archive. */
archive_entry_set_size(entry, 0);
break;
@ -202,7 +197,7 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
if (ret != ARCHIVE_OK)
return (ret);
if (!S_ISDIR(st->st_mode)) {
if (archive_entry_filetype(entry) != AE_IFDIR) {
/* Try to create the dir. */
p = strdup(name);
pp = strrchr(p, '/');
@ -255,8 +250,8 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
if (ret != ARCHIVE_OK)
return (ret);
} else {
switch(st->st_mode & S_IFMT) {
case S_IFREG:
switch(archive_entry_filetype(entry)) {
case AE_IFREG:
if (archive_entry_size(entry) == 0) {
/* More portable than "touch." */
ret = shar_printf(a, "test -e \"%s\" || :> \"%s\"\n", name, name);
@ -287,7 +282,7 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
shar->outbytes = 0;
}
break;
case S_IFDIR:
case AE_IFDIR:
ret = shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n",
name);
if (ret != ARCHIVE_OK)
@ -306,19 +301,19 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
* up at end of archive.
*/
break;
case S_IFIFO:
case AE_IFIFO:
ret = shar_printf(a, "mkfifo %s\n", name);
if (ret != ARCHIVE_OK)
return (ret);
break;
case S_IFCHR:
case AE_IFCHR:
ret = shar_printf(a, "mknod %s c %d %d\n", name,
archive_entry_rdevmajor(entry),
archive_entry_rdevminor(entry));
if (ret != ARCHIVE_OK)
return (ret);
break;
case S_IFBLK:
case AE_IFBLK:
ret = shar_printf(a, "mknod %s b %d %d\n", name,
archive_entry_rdevmajor(entry),
archive_entry_rdevminor(entry));
@ -359,7 +354,7 @@ archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
shar->outbuff[shar->outpos++] = *src++;
if (shar->outpos > sizeof(shar->outbuff) - 2) {
ret = (a->compression_write)(a, shar->outbuff,
ret = (a->compressor.write)(a, shar->outbuff,
shar->outpos);
if (ret != ARCHIVE_OK)
return (ret);
@ -368,7 +363,7 @@ archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
}
if (shar->outpos > 0)
ret = (a->compression_write)(a, shar->outbuff, shar->outpos);
ret = (a->compressor.write)(a, shar->outbuff, shar->outpos);
if (ret != ARCHIVE_OK)
return (ret);
return (written);

View File

@ -26,16 +26,7 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#else
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@ -212,13 +203,32 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
/* Only regular files (not hardlinks) have data. */
if (archive_entry_hardlink(entry) != NULL ||
archive_entry_symlink(entry) != NULL ||
!S_ISREG(archive_entry_mode(entry)))
!(archive_entry_filetype(entry) == AE_IFREG))
archive_entry_set_size(entry, 0);
if (AE_IFDIR == archive_entry_mode(entry)) {
const char *p;
char *t;
/*
* Ensure a trailing '/'. Modify the entry so
* the client sees the change.
*/
p = archive_entry_pathname(entry);
if (p[strlen(p) - 1] != '/') {
t = (char *)malloc(strlen(p) + 2);
if (t != NULL) {
strcpy(t, p);
strcat(t, "/");
archive_entry_copy_pathname(entry, t);
free(t);
}
}
}
ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1);
if (ret != ARCHIVE_OK)
return (ret);
ret = (a->compression_write)(a, buff, 512);
ret = (a->compressor.write)(a, buff, 512);
if (ret != ARCHIVE_OK)
return (ret);
@ -243,9 +253,8 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
{
unsigned int checksum;
int i, ret;
size_t copy_length, ps, extra_slash;
size_t copy_length;
const char *p, *pp;
const struct stat *st;
int mytartype;
ret = 0;
@ -256,7 +265,6 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
* elements.
*/
memcpy(h, &template_header, 512);
st = archive_entry_stat(entry);
/*
* Because the block is already null-filled, and strings
@ -265,18 +273,11 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
*/
pp = archive_entry_pathname(entry);
ps = strlen(pp);
if (S_ISDIR(st->st_mode) && pp[ps - 1] != '/')
extra_slash = 1;
else
extra_slash = 0;
if (ps + extra_slash <= USTAR_name_size) {
memcpy(h + USTAR_name_offset, pp, ps);
if (extra_slash)
h[USTAR_name_offset + ps] = '/';
} else {
if (strlen(pp) <= USTAR_name_size)
memcpy(h + USTAR_name_offset, pp, strlen(pp));
else {
/* Store in two pieces, splitting at a '/'. */
p = strchr(pp + ps + extra_slash - USTAR_name_size - 1, '/');
p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/');
/*
* If there is no path separator, or the prefix or
* remaining name are too large, return an error.
@ -292,9 +293,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
} else {
/* Copy prefix and remainder to appropriate places */
memcpy(h + USTAR_prefix_offset, pp, p - pp);
memcpy(h + USTAR_name_offset, p + 1, pp + ps - p - 1);
if (extra_slash)
h[USTAR_name_offset + pp + ps - p - 1] = '/';
memcpy(h + USTAR_name_offset, p + 1, pp + strlen(pp) - p - 1);
}
}
@ -338,41 +337,42 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
memcpy(h + USTAR_gname_offset, p, copy_length);
}
if (format_number(st->st_mode & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
archive_set_error(&a->archive, ERANGE, "Numeric mode too large");
ret = ARCHIVE_WARN;
}
if (format_number(st->st_uid, h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
archive_set_error(&a->archive, ERANGE, "Numeric user ID too large");
ret = ARCHIVE_WARN;
}
if (format_number(st->st_gid, h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
archive_set_error(&a->archive, ERANGE, "Numeric group ID too large");
ret = ARCHIVE_WARN;
}
if (format_number(st->st_size, h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
archive_set_error(&a->archive, ERANGE, "File size out of range");
ret = ARCHIVE_WARN;
}
if (format_number(st->st_mtime, h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
archive_set_error(&a->archive, ERANGE,
"File modification time too large");
ret = ARCHIVE_WARN;
}
if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
if (format_number(major(st->st_rdev), h + USTAR_rdevmajor_offset,
if (archive_entry_filetype(entry) == AE_IFBLK
|| archive_entry_filetype(entry) == AE_IFCHR) {
if (format_number(archive_entry_rdevmajor(entry), h + USTAR_rdevmajor_offset,
USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) {
archive_set_error(&a->archive, ERANGE,
"Major device number too large");
ret = ARCHIVE_WARN;
}
if (format_number(minor(st->st_rdev), h + USTAR_rdevminor_offset,
if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset,
USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) {
archive_set_error(&a->archive, ERANGE,
"Minor device number too large");
@ -385,22 +385,17 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
} else if (mytartype >= 0) {
h[USTAR_typeflag_offset] = mytartype;
} else {
switch (st->st_mode & S_IFMT) {
case S_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
case S_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
case S_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
case S_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
case S_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
case S_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
case S_IFSOCK:
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"tar format cannot archive socket");
ret = ARCHIVE_WARN;
break;
switch (archive_entry_filetype(entry)) {
case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"tar format cannot archive this (mode=0%lo)",
(unsigned long)st->st_mode);
(unsigned long)archive_entry_mode(entry));
ret = ARCHIVE_WARN;
}
}
@ -500,7 +495,7 @@ archive_write_ustar_finish(struct archive_write *a)
{
int r;
if (a->compression_write == NULL)
if (a->compressor.write == NULL)
return (ARCHIVE_OK);
r = write_nulls(a, 512*2);
@ -539,7 +534,7 @@ write_nulls(struct archive_write *a, size_t padding)
while (padding > 0) {
to_write = padding < a->null_length ? padding : a->null_length;
ret = (a->compression_write)(a, a->nulls, to_write);
ret = (a->compressor.write)(a, a->nulls, to_write);
if (ret != ARCHIVE_OK)
return (ret);
padding -= to_write;
@ -556,7 +551,7 @@ archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
ustar = (struct ustar *)a->format_data;
if (s > ustar->entry_bytes_remaining)
s = ustar->entry_bytes_remaining;
ret = (a->compression_write)(a, buff, s);
ret = (a->compressor.write)(a, buff, s);
ustar->entry_bytes_remaining -= s;
if (ret != ARCHIVE_OK)
return (ret);

View File

@ -65,7 +65,10 @@
#define HAVE_MEMSET 1
#define HAVE_MKDIR 1
#define HAVE_MKFIFO 1
#define HAVE_POLL 1
#define HAVE_POLL_H 1
#define HAVE_PWD_H 1
#define HAVE_SELECT 1
#define HAVE_STDINT_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STRCHR 1
@ -77,8 +80,10 @@
#define HAVE_STRRCHR 1
#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
#define HAVE_STRUCT_STAT_ST_RDEV 1
#define HAVE_STRUCT_TM_TM_GMTOFF 1
#define HAVE_SYS_ACL_H 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_SELECT_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_SYS_TIME_H 1
#define HAVE_SYS_TYPES_H 1

View File

@ -0,0 +1,137 @@
/*-
* Copyright (c) 2007 Joerg Sonnenberger
* 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$");
#if defined(HAVE_POLL)
# if defined(HAVE_POLL_H)
# include <poll.h>
# endif
#elif defined(HAVE_SELECT)
# if defined(HAVE_SYS_SELECT_H)
# include <sys/select.h>
# elif defined(HAVE_UNISTD_H)
# include <unistd.h>
# endif
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "filter_fork.h"
pid_t
__archive_create_child(const char *path, int *child_stdin, int *child_stdout)
{
pid_t child;
int stdin_pipe[2], stdout_pipe[2], tmp;
if (pipe(stdin_pipe) == -1)
goto state_allocated;
if (stdin_pipe[0] == STDOUT_FILENO) {
if ((tmp = dup(stdin_pipe[0])) == -1)
goto stdin_opened;
close(stdin_pipe[0]);
stdin_pipe[0] = tmp;
}
if (pipe(stdout_pipe) == -1)
goto stdin_opened;
if (stdout_pipe[1] == STDIN_FILENO) {
if ((tmp = dup(stdout_pipe[1])) == -1)
goto stdout_opened;
close(stdout_pipe[1]);
stdout_pipe[1] = tmp;
}
switch ((child = vfork())) {
case -1:
goto stdout_opened;
case 0:
close(stdin_pipe[1]);
close(stdout_pipe[0]);
if (dup2(stdin_pipe[0], STDIN_FILENO) == -1)
_exit(254);
if (stdin_pipe[0] != STDIN_FILENO)
close(stdin_pipe[0]);
if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1)
_exit(254);
if (stdout_pipe[1] != STDOUT_FILENO)
close(stdout_pipe[1]);
execlp(path, path, (char *)NULL);
_exit(254);
default:
close(stdin_pipe[0]);
close(stdout_pipe[1]);
*child_stdin = stdin_pipe[1];
fcntl(*child_stdin, F_SETFL, O_NONBLOCK);
*child_stdout = stdout_pipe[0];
fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
}
return child;
stdout_opened:
close(stdout_pipe[0]);
close(stdout_pipe[1]);
stdin_opened:
close(stdin_pipe[0]);
close(stdin_pipe[1]);
state_allocated:
return -1;
}
void
__archive_check_child(int in, int out)
{
#if defined(HAVE_POLL)
struct pollfd fds[2];
fds[0].fd = in;
fds[0].events = POLLOUT;
fds[1].fd = out;
fds[1].events = POLLIN;
poll(fds, 2, -1); /* -1 == INFTIM, wait forever */
#elif defined(HAVE_SELECT)
fd_set fds_in, fds_out, fds_error;
FD_ZERO(&fds_in);
FD_SET(out, &fds_in);
FD_ZERO(&fds_out);
FD_SET(in, &fds_out);
FD_ZERO(&fds_error);
FD_SET(in, &fds_error);
FD_SET(out, &fds_error);
select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
#else
sleep(1);
#endif
}

View File

@ -0,0 +1,37 @@
/*-
* Copyright (c) 2007 Joerg Sonnenberger
* 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.
*
* $FreeBSD$
*/
#ifndef FILTER_FORK_H
#define FILTER_FORK_H
pid_t
__archive_create_child(const char *path, int *child_stdin, int *child_stdout);
void
__archive_check_child(int in, int out);
#endif

View File

@ -0,0 +1,376 @@
.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
.\"
.Dd April 16, 2007
.Dt LIBARCHIVE 3
.Os
.Sh NAME
.Nm libarchive_internals
.Nd description of libarchive internal interfaces
.Sh OVERVIEW
The
.Nm libarchive
library provides a flexible interface for reading and writing
streaming archive files such as tar and cpio.
Internally, it follows a modular layered design that should
make it easy to add new archive and compression formats.
.Sh GENERAL ARCHITECTURE
Externally, libarchive exposes most operations through an
opaque, object-style interface.
The
.Xr archive_entry 1
objects store information about a single filesystem object.
The rest of the library provides facilities to write
.Xr archive_entry 1
objects to archive files,
read them from archive files,
and write them to disk.
(There are plans to add a facility to read
.Xr archive_entry 1
objects from disk as well.)
.Pp
The read and write APIs each have four layers: a public API
layer, a format layer that understands the archive file format,
a compression layer, and an I/O layer.
The I/O layer is completely exposed to clients who can replace
it entirely with their own functions.
.Pp
In order to provide as much consistency as possible for clients,
some public functions are virtualized.
Eventually, it should be possible for clients to open
an archive or disk writer, and then use a single set of
code to select and write entries, regardless of the target.
.Sh READ ARCHITECTURE
From the outside, clients use the
.Xr archive_read 3
API to manipulate an
.Nm archive
object to read entries and bodies from an archive stream.
Internally, the
.Nm archive
object is cast to an
.Nm archive_read
object, which holds all read-specific data.
The API has four layers:
The lowest layer is the I/O layer.
This layer can be overridden by clients, but most clients use
the packaged I/O callbacks provided, for example, by
.Xr archive_read_open_memory 3 ,
and
.Xr archive_read_open_fd 3 .
The compression layer calls the I/O layer to
read bytes and decompresses them for the format layer.
The format layer unpacks a stream of uncompressed bytes and
creates
.Nm archive_entry
objects from the incoming data.
The API layer tracks overall state
(for example, it prevents clients from reading data before reading a header)
and invokes the format and compression layer operations
through registered function pointers.
In particular, the API layer drives the format-detection process:
When opening the archive, it reads an initial block of data
and offers it to each registered compression handler.
The one with the highest bid is initialized with the first block.
Similarly, the format handlers are polled to see which handler
is the best for each header request.
(Note that a single file can have entries handled by different
format handlers;
this allows a simple handler for a generic version of a format
with more complex handlers implemented independently for
extended sub-formats.)
.Ss I/O Layer and Client Callbacks
The read API goes to some lengths to be nice to clients.
As a result, there are few restrictions on the behavior of
the client callbacks.
.Pp
The client read callback is expected to provide a block
of data on each call.
A zero-length return does indicate end of file, but otherwise
blocks may be as small as one byte or as large as the entire file.
In particular, blocks may be of different sizes.
.Pp
The client skip callback returns the number of bytes actually
skipped, which may be much smaller than the skip requested.
The only requirement is that the skip not be larger.
The skip callback must never be invoked with a negative value.
.Pp
Keep in mind that not all clients are reading from disk:
clients reading from networks may provide different-sized
blocks on every request and cannot skip at all;
advanced clients may use
.Xr mmap 2
to read the entire file into memory at once and return the
entire file to libarchive as a single block;
other clients may begin asynchronous I/O operations for the
next block on each request.
.Ss Decompresssion Layer
The decompression layer not only handles decompression,
it also buffers data so that the format handlers see a
much nicer I/O model.
The decompression API is a two stage peek/consume model.
A read_ahead request specifies a minimum read amount;
the decompression layer must provide a pointer to at least
that much data.
If more data is immediately available, it should return more:
the format layer handles bulk data reads by asking for a minimum
of one byte and then copying as much data as is available.
.Pp
A subsequent call to the
.Fn consume
function advances the read pointer.
Note that data returned from a
.Fn read_ahead
call is guaranteed to remain in place until
the next call to
.Fn read_ahead .
Intervening calls to
.Fn consume
should not cause the data to move.
.Pp
Skip requests must always be handled exactly.
Decompression handlers that cannot seek forward should
not register a skip handler;
the API layer fills in a generic skip handler that reads and discards data.
.Pp
A decompression handler has a specific lifecycle:
.Bl -tag -compact -width indent
.It Registration/Configuration
When the client invokes the public support function,
the decompression handler invokes the internal
.Fn __archive_read_register_compression
function to provide bid and initialization functions.
This function returns
.Cm NULL
on error or else a pointer to a
.Cm struct decompressor_t .
This structure contains a
.Va void * config
slot that can be used for storing any customization information.
.It Bid
The bid function is invoked with a pointer and size of a block of data.
The decompressor can access its config data
through the
.Va decompressor
element of the
.Cm archive_read
object.
The bid function is otherwise stateless.
In particular, it must not perform any I/O operations.
.Pp
The value returned by the bid function indicates its suitability
for handling this data stream.
A bid of zero will ensure that this decompressor is never invoked.
Return zero if magic number checks fail.
Otherwise, your initial implementation should return the number of bits
actually checked.
For example, if you verify two full bytes and three bits of another
byte, bid 19.
Note that the initial block may be very short;
be careful to only inspect the data you are given.
(The current decompressors require two bytes for correct bidding.)
.It Initialize
The winning bidder will have its init function called.
This function should initialize the remaining slots of the
.Va struct decompressor_t
object pointed to by the
.Va decompressor
element of the
.Va archive_read
object.
In particular, it should allocate any working data it needs
in the
.Va data
slot of that structure.
The init function is called with the block of data that
was used for tasting.
At this point, the decompressor is responsible for all I/O
requests to the client callbacks.
The decompressor is free to read more data as and when
necessary.
.It Satisfy I/O requests
The format handler will invoke the
.Va read_ahead ,
.Va consume ,
and
.Va skip
functions as needed.
.It Finish
The finish method is called only once when the archive is closed.
It should release anything stored in the
.Va data
and
.Va config
slots of the
.Va decompressor
object.
It should not invoke the client close callback.
.El
.Ss Format Layer
The read formats have a similar lifecycle to the decompression handlers:
.Bl -tag -compact -width indent
.It Registration
Allocate your private data and initialize your pointers.
.It Bid
Formats bid by invoking the
.Fn read_ahead
decompression method but not calling the
.Fn consume
method.
This allows each bidder to look ahead in the input stream.
Bidders should not look further ahead than necessary, as long
look aheads put pressure on the compression layer to buffer
lots of data.
Most formats only require a few hundred bytes of look ahead;
look aheads of a few kilobytes are reasonable.
(The ISO9660 reader sometimes looks ahead by 48k, which
should be considered an upper limit.)
Note that the bidder is invoked for every entry.
For many formats, this is inappropriate; if you can only bid at
the beginning of the file, store your bid value and check that
each time your bid function is called.
For example, the ISO9660 reader initializes a
.Va bid
value to -1 at registration time;
each time the bid function is called, the bid value is returned
immediately if it is zero or larger.
.It Read header
The header read is usually the most complex part of any format.
There are a few strategies worth mentioning:
For formats such as tar or cpio, reading and parsing the header is
straightforward since headers alternate with data.
For formats that store all header data at the beginning of the file,
the first header read request may have to read all headers into
memory and store that data, sorted by the location of the file
data.
Subsequent header read requests will skip forward to the
beginning of the file data and return the corresponding header.
.It Read Data
The read data interface supports sparse files; this requires that
each call return a block of data specifying the file offset and
size.
This may require you to carefully track the location so that you
can return accurate file offsets for each read.
Remember that the decompressor will return as much data as it has.
Generally, you will want to request one byte,
examine the return value to see how much data is available, and
possibly trim that to the amount you can use.
You should invoke consume for each block just before you return it.
.It Skip All Data
The skip data call should skip over all file data and trailing padding.
This is called automatically by the API layer just before each
header read.
It is also called in response to the client calling the public
.Fn data_skip
function.
.It Cleanup
On cleanup, the format should release all of its allocated memory.
.El
.Ss API Layer
XXX to do XXX
.Sh WRITE ARCHITECTURE
The write API has a similar set of four layers:
an API layer, a format layer, a compression layer, and an I/O layer.
The registration here is much simpler because only
one format and one compression can be registered at a time.
.Ss I/O Layer and Client Callbacks
XXX To be written XXX
.Ss Compression Layer
XXX To be written XXX
.Ss Format Layer
XXX To be written XXX
.Ss API Layer
XXX To be written XXX
.Sh WRITE_DISK ARCHITECTURE
The write_disk API is intended to look just like the write API
to clients.
Since it does not handle multiple formats or compression, it
is not layered internally.
.Sh GENERAL SERVICES
The
.Nm archive_read ,
.Nm archive_write ,
and
.Nm archive_write_disk
objects all contain an initial
.Nm archive
object which provides common support for a set of standard services.
(Recall that ANSI/ISO C90 guarantees that you can cast freely between
a pointer to a structure and a pointer to the first element of that
structure.)
The
.Nm archive
object has a magic value that indicates which API this object
is associated with,
slots for storing error information,
and function pointers for virtualized API functions.
.Sh MISCELLANEOUS NOTES
Connecting existing archiving libraries into libarchive is generally
quite difficult.
In particular, many existing libraries strongly assume that you
are reading from a file; they seek forwards and backwards as necessary
to locate various pieces of information.
In contrast, libarchive never seeks backwards in its input, which
sometimes requires very different approaches.
.Pp
For example, libarchive's ISO9660 support operates very differently
from most ISO9660 readers.
The libarchive support utilizes a work-queue design that
keeps a list of known entries sorted by their location in the input.
Whenever libarchive's ISO9660 implementation is asked for the next
header, checks this list to find the next item on the disk.
Directories are parsed when they are encountered and new
items are added to the list.
This design relies heavily on the ISO9660 image being optimized so that
directories always occur earlier on the disk than the files they
describe.
.Pp
Depending on the specific format, such approaches may not be possible.
The ZIP format specification, for example, allows archivers to store
key information only at the end of the file.
In theory, it is possible to create ZIP archives that cannot
be read without seeking.
Fortunately, such archives are very rare, and libarchive can read
most ZIP archives, though it cannot always extract as much information
as a dedicated ZIP program.
.Sh SEE ALSO
.Xr archive 3 ,
.Xr archive_entry 3 ,
.Xr archive_read 3 ,
.Xr archive_write 3 ,
.Xr archive_write_disk 3
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle@acm.org .
.Sh BUGS

View File

@ -5,6 +5,8 @@ TESTS= \
test_acl_pax.c \
test_archive_api_feature.c \
test_bad_fd.c \
test_entry.c \
test_read_compress_program.c \
test_read_data_large.c \
test_read_extract.c \
test_read_format_ar.c \
@ -29,6 +31,7 @@ TESTS= \
test_read_position.c \
test_read_truncated.c \
test_tar_filenames.c \
test_write_compress_program.c \
test_write_disk.c \
test_write_disk_perms.c \
test_write_disk_secure.c \
@ -54,6 +57,11 @@ LDADD= -larchive -lz -lbz2
CFLAGS+= -static -g
CFLAGS+= -I${.OBJDIR}
# Uncomment to link against dmalloc
#LDADD+= -L/usr/local/lib -ldmalloc
#CFLAGS+= -I/usr/local/include -DUSE_DMALLOC
#WARNS=6
test: libarchive_test
./libarchive_test

View File

@ -44,3 +44,12 @@ Each test function can rely on the following:
* Tests are encouraged to be economical with their memory and disk usage,
though this is not essential.
* Disable tests on specific platforms as necessary. Please don't
use config.h to adjust feature requirements, as I want the tests
to also serve as a check on the configure process. The following
form is appropriate:
#if !defined(__PLATFORM) && !defined(__Platform2__)
assert(xxxx)
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2006 Tim Kientzle
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,6 +27,7 @@
* Various utility routines useful for test programs.
* Each test program is linked against this file.
*/
#include <errno.h>
#include <stdarg.h>
#include <time.h>
@ -50,6 +51,24 @@ __FBSDID("$FreeBSD$");
*/
static char msg[4096];
/* Common handling of failed tests. */
static void
test_failed(struct archive *a)
{
if (msg[0] != '\0') {
fprintf(stderr, " Description: %s\n", msg);
msg[0] = '\0';
}
if (a != NULL) {
fprintf(stderr, " archive error: %s\n", archive_error_string(a));
}
fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n");
*(char *)(NULL) = 0;
exit(1);
}
/* Set up a message to display only after a test fails. */
void
failure(const char *fmt, ...)
{
@ -59,6 +78,7 @@ failure(const char *fmt, ...)
va_end(ap);
}
/* Generic assert() just displays the failed condition. */
void
test_assert(const char *file, int line, int value, const char *condition, struct archive *a)
{
@ -68,17 +88,10 @@ test_assert(const char *file, int line, int value, const char *condition, struct
}
fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
fprintf(stderr, " Condition: %s\n", condition);
if (msg[0] != '\0') {
fprintf(stderr, " Description: %s\n", msg);
msg[0] = '\0';
}
if (a != NULL) {
fprintf(stderr, " archive error: %s\n", archive_error_string(a));
}
*(char *)(NULL) = 0;
exit(1);
test_failed(a);
}
/* assertEqualInt() displays the values of the two integers. */
void
test_assert_equal_int(const char *file, int line,
int v1, const char *e1, int v2, const char *e2, struct archive *a)
@ -87,19 +100,52 @@ test_assert_equal_int(const char *file, int line,
msg[0] = '\0';
return;
}
fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
fprintf(stderr, " Condition: %s==%s\n", e1, e2);
fprintf(stderr, " %s=%d\n", e1, v1);
fprintf(stderr, " %s=%d\n", e2, v2);
if (msg[0] != '\0') {
fprintf(stderr, " Description: %s\n", msg);
fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
file, line);
fprintf(stderr, " %s=%d\n", e1, v1);
fprintf(stderr, " %s=%d\n", e2, v2);
test_failed(a);
}
/* assertEqualString() displays the values of the two strings. */
void
test_assert_equal_string(const char *file, int line,
const char *v1, const char *e1,
const char *v2, const char *e2,
struct archive *a)
{
if (v1 == NULL || v2 == NULL) {
if (v1 == v2) {
msg[0] = '\0';
return;
}
} else if (strcmp(v1, v2) == 0) {
msg[0] = '\0';
return;
}
if (a != NULL) {
fprintf(stderr, " archive error: %s\n", archive_error_string(a));
fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
file, line);
fprintf(stderr, " %s = \"%s\"\n", e1, v1);
fprintf(stderr, " %s = \"%s\"\n", e2, v2);
test_failed(a);
}
/* assertEqualWString() displays the values of the two strings. */
void
test_assert_equal_wstring(const char *file, int line,
const wchar_t *v1, const char *e1,
const wchar_t *v2, const char *e2,
struct archive *a)
{
if (wcscmp(v1, v2) == 0) {
msg[0] = '\0';
return;
}
*(char *)(NULL) = 0;
exit(1);
fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
file, line);
fwprintf(stderr, L" %s = \"%ls\"\n", e1, v1);
fwprintf(stderr, L" %s = \"%ls\"\n", e2, v2);
test_failed(a);
}
/*
@ -109,19 +155,60 @@ test_assert_equal_int(const char *file, int line,
* We reuse it here to define a list of all tests to run.
*/
#undef DEFINE_TEST
#define DEFINE_TEST(n) n, #n,
struct { void (*func)(void); char *name; } tests[] = {
#define DEFINE_TEST(n) { n, #n },
struct { void (*func)(void); const char *name; } tests[] = {
#include "list.h"
};
static void test_run(int i, const char *tmpdir)
{
printf("%d: %s\n", i, tests[i].name);
/*
* Always explicitly chdir() in case the last test moved us to
* a strange place.
*/
if (chdir(tmpdir)) {
fprintf(stderr,
"ERROR: Couldn't chdir to temp dir %s\n",
tmpdir);
exit(1);
}
/* Create a temp directory for this specific test. */
if (mkdir(tests[i].name, 0755)) {
fprintf(stderr,
"ERROR: Couldn't create temp dir ``%s''\n",
tests[i].name);
exit(1);
}
if (chdir(tests[i].name)) {
fprintf(stderr,
"ERROR: Couldn't chdir to temp dir ``%s''\n",
tests[i].name);
exit(1);
}
(*tests[i].func)();
}
static void usage(void)
{
static const int limit = sizeof(tests) / sizeof(tests[0]);
int i;
printf("Usage: libarchive_test <test> <test> ...\n");
printf("Default is to run all tests.\n");
printf("Otherwise, specify the numbers of the tests you wish to run.\n");
printf("Available tests:\n");
for (i = 0; i < limit; i++)
printf(" %d: %s\n", i, tests[i].name);
exit(1);
}
int main(int argc, char **argv)
{
void (*f)(void);
int limit = sizeof(tests) / sizeof(tests[0]);
int i;
static const int limit = sizeof(tests) / sizeof(tests[0]);
int i, tests_run = 0;
time_t now;
char tmpdir[256];
int tmpdirHandle;
/*
* Create a temp directory for the following tests.
@ -129,10 +216,15 @@ int main(int argc, char **argv)
* to make it easier to track the results of multiple tests.
*/
now = time(NULL);
strftime(tmpdir, sizeof(tmpdir),
"/tmp/libarchive_test.%Y-%m-%dT%H.%M.%S",
localtime(&now));
if (mkdir(tmpdir,0755) != 0) {
for (i = 0; i < 1000; i++) {
strftime(tmpdir, sizeof(tmpdir),
"/tmp/libarchive_test.%Y-%m-%dT%H.%M.%S",
localtime(&now));
sprintf(tmpdir + strlen(tmpdir), "-%03d", i);
if (mkdir(tmpdir,0755) == 0)
break;
if (errno == EEXIST)
continue;
fprintf(stderr, "ERROR: Unable to create temp directory %s\n",
tmpdir);
exit(1);
@ -140,29 +232,25 @@ int main(int argc, char **argv)
printf("Running libarchive tests in: %s\n", tmpdir);
for (i = 0; i < limit; i++) {
printf("%d: %s\n", i, tests[i].name);
if (chdir(tmpdir)) {
fprintf(stderr,
"ERROR: Couldn't chdir to temp dir %s\n",
tmpdir);
exit(1);
if (argc == 1) {
/* Default: Run all tests. */
for (i = 0; i < limit; i++) {
test_run(i, tmpdir);
tests_run++;
}
/* Create a temp directory for this specific test. */
if (mkdir(tests[i].name, 0755)) {
fprintf(stderr,
"ERROR: Couldn't create temp dir ``%s''\n",
tests[i].name);
exit(1);
} else {
while (*(++argv) != NULL) {
i = atoi(*argv);
if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) {
printf("*** INVALID Test %s\n", *argv);
usage();
} else {
test_run(i, tmpdir);
tests_run++;
}
}
if (chdir(tests[i].name)) {
fprintf(stderr,
"ERROR: Couldn't chdir to temp dir ``%s''\n",
tests[i].name);
exit(1);
}
(*tests[i].func)();
}
printf("%d tests succeeded.\n", limit);
printf("%d tests succeeded.\n", tests_run);
return (0);
}

View File

@ -41,6 +41,11 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <wchar.h>
#ifdef USE_DMALLOC
#include <dmalloc.h>
#endif
#if defined(HAVE_CONFIG_H)
/* Most POSIX platforms use the 'configure' script to build config.h */
@ -83,15 +88,26 @@
#define assert(e) test_assert(__FILE__, __LINE__, (e), #e, NULL)
/* As above, but reports any archive_error found in variable 'a' */
#define assertA(e) test_assert(__FILE__, __LINE__, (e), #e, (a))
/* Asserts that two values are the same. Reports value of each one if not. */
/* Asserts that two integers are the same. Reports value of each one if not. */
#define assertEqualIntA(a,v1,v2) \
test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
/* Asserts that two values are the same. Reports value of each one if not. */
#define assertEqualInt(v1,v2) \
test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
/* Asserts that two strings are the same. Reports value of each one if not. */
#define assertEqualStringA(a,v1,v2) \
test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
#define assertEqualString(v1,v2) \
test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
/* As above, but v1 and v2 are wchar_t * */
#define assertEqualWString(v1,v2) \
test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
/* Function declarations. These are defined in test_utility.c. */
void failure(const char *fmt, ...);
void test_assert(const char *, int, int, const char *, struct archive *);
void test_assert_equal_int(const char *, int, int, const char *, int, const char *, struct archive *);
void test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, struct archive *);
void test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, struct archive *);

View File

@ -34,14 +34,12 @@ __FBSDID("$FreeBSD$");
* filesystems support ACLs or not.
*/
static unsigned char buff[16384];
struct acl_t {
int type; /* Type of ACL: "access" or "default" */
int permset; /* Permissions for this class of users. */
int tag; /* Owner, User, Owning group, group, other, etc. */
int qual; /* GID or UID of user/group, depending on tag. */
char *name; /* Name of user/group, depending on tag. */
const char *name; /* Name of user/group, depending on tag. */
};
struct acl_t acls0[] = {
@ -79,7 +77,7 @@ struct acl_t acls2[] = {
ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
};
void
static void
set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
{
int i;
@ -92,7 +90,7 @@ set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
}
}
int
static int
acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const char *name)
{
if (type != acl->type)
@ -120,11 +118,11 @@ acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const cha
return (0 == strcmp(name, acl->name));
}
void
static void
compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
{
int *marker = malloc(sizeof(marker[0]) * n);
int marker_i, i;
int i;
int r;
int type, permset, tag, qual;
int matched;
@ -179,8 +177,6 @@ compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
DEFINE_TEST(test_acl_basic)
{
int i;
struct archive *a;
struct archive_entry *ae;
/* Create a simple archive_entry. */

View File

@ -264,7 +264,7 @@ struct acl_t {
int permset; /* Permissions for this class of users. */
int tag; /* Owner, User, Owning group, group, other, etc. */
int qual; /* GID or UID of user/group, depending on tag. */
char *name; /* Name of user/group, depending on tag. */
const char *name; /* Name of user/group, depending on tag. */
};
static struct acl_t acls0[] = {
@ -347,7 +347,7 @@ static void
compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
{
int *marker = malloc(sizeof(marker[0]) * n);
int marker_i, i;
int i;
int r;
int type, permset, tag, qual;
int matched;
@ -402,7 +402,6 @@ compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
DEFINE_TEST(test_acl_pax)
{
int i;
struct archive *a;
struct archive_entry *ae;
size_t used;
@ -454,7 +453,7 @@ DEFINE_TEST(test_acl_pax)
/* Write out the data we generated to a file for manual inspection. */
assert(-1 < (fd = open("testout", O_WRONLY | O_CREAT | O_TRUNC, 0775)));
assert(used == write(fd, buff, used));
assert(used == (size_t)write(fd, buff, used));
close(fd);
/* Write out the reference data to a file for manual inspection. */

View File

@ -27,7 +27,7 @@ __FBSDID("$FreeBSD$");
DEFINE_TEST(test_archive_api_feature)
{
assert(ARCHIVE_API_FEATURE == archive_api_feature());
assert(ARCHIVE_API_VERSION == archive_api_version());
assert(0 == (strcmp(ARCHIVE_LIBRARY_VERSION, archive_version())));
assertEqualInt(ARCHIVE_API_FEATURE, archive_api_feature());
assertEqualInt(ARCHIVE_API_VERSION, archive_api_version());
assertEqualString(ARCHIVE_LIBRARY_VERSION, archive_version());
}

View File

@ -0,0 +1,576 @@
/*-
* 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 "test.h"
__FBSDID("$FreeBSD$");
/*
* Most of these tests are system-independent, though a few depend on
* features of the local system. Such tests are conditionalized on
* the platform name. On unsupported platforms, only the
* system-independent features will be tested.
*
* No, I don't want to use config.h in the test files because I want
* the tests to also serve as a check on the correctness of config.h.
* A mis-configured library build should cause tests to fail.
*/
DEFINE_TEST(test_entry)
{
char buff[128];
wchar_t wbuff[128];
struct stat st;
struct archive_entry *e, *e2;
const struct stat *pst;
unsigned long set, clear; /* For fflag testing. */
int type, permset, tag, qual; /* For ACL testing. */
const char *name; /* For ACL testing. */
const char *xname; /* For xattr tests. */
const void *xval; /* For xattr tests. */
size_t xsize; /* For xattr tests. */
assert((e = archive_entry_new()) != NULL);
/*
* Basic set/read tests for all fields.
* We should be able to set any field and read
* back the same value.
*
* For methods that "copy" a string, we should be able
* to overwrite the original passed-in string without
* changing the value in the entry.
*
* The following tests are ordered alphabetically by the
* name of the field.
*/
/* atime */
archive_entry_set_atime(e, 13579, 24680);
assertEqualInt(archive_entry_atime(e), 13579);
assertEqualInt(archive_entry_atime_nsec(e), 24680);
/* ctime */
archive_entry_set_ctime(e, 13580, 24681);
assertEqualInt(archive_entry_ctime(e), 13580);
assertEqualInt(archive_entry_ctime_nsec(e), 24681);
/* dev */
archive_entry_set_dev(e, 235);
assertEqualInt(archive_entry_dev(e), 235);
/* devmajor/devminor are tested specially below. */
/* filetype */
archive_entry_set_filetype(e, AE_IFREG);
assertEqualInt(archive_entry_filetype(e), AE_IFREG);
/* fflags are tested specially below */
/* gid */
archive_entry_set_gid(e, 204);
assertEqualInt(archive_entry_gid(e), 204);
/* gname */
archive_entry_set_gname(e, "group");
assertEqualString(archive_entry_gname(e), "group");
wcscpy(wbuff, L"wgroup");
archive_entry_copy_gname_w(e, wbuff);
assertEqualWString(archive_entry_gname_w(e), L"wgroup");
memset(wbuff, 0, sizeof(wbuff));
assertEqualWString(archive_entry_gname_w(e), L"wgroup");
/* hardlink */
archive_entry_set_hardlink(e, "hardlinkname");
assertEqualString(archive_entry_hardlink(e), "hardlinkname");
strcpy(buff, "hardlinkname2");
archive_entry_copy_hardlink(e, buff);
assertEqualString(archive_entry_hardlink(e), "hardlinkname2");
memset(buff, 0, sizeof(buff));
assertEqualString(archive_entry_hardlink(e), "hardlinkname2");
wcscpy(wbuff, L"whardlink");
archive_entry_copy_hardlink_w(e, wbuff);
assertEqualWString(archive_entry_hardlink_w(e), L"whardlink");
memset(wbuff, 0, sizeof(wbuff));
assertEqualWString(archive_entry_hardlink_w(e), L"whardlink");
/* ino */
archive_entry_set_ino(e, 8593);
assertEqualInt(archive_entry_ino(e), 8593);
/* link */
/* TODO: implement these tests. */
/* mode */
archive_entry_set_mode(e, 0123456);
assertEqualInt(archive_entry_mode(e), 0123456);
/* mtime */
archive_entry_set_mtime(e, 13581, 24682);
assertEqualInt(archive_entry_mtime(e), 13581);
assertEqualInt(archive_entry_mtime_nsec(e), 24682);
/* nlink */
archive_entry_set_nlink(e, 736);
assertEqualInt(archive_entry_nlink(e), 736);
/* pathname */
archive_entry_set_pathname(e, "path");
assertEqualString(archive_entry_pathname(e), "path");
archive_entry_set_pathname(e, "path");
assertEqualString(archive_entry_pathname(e), "path");
strcpy(buff, "path2");
archive_entry_copy_pathname(e, buff);
assertEqualString(archive_entry_pathname(e), "path2");
memset(buff, 0, sizeof(buff));
assertEqualString(archive_entry_pathname(e), "path2");
wcscpy(wbuff, L"wpath");
archive_entry_copy_pathname_w(e, wbuff);
assertEqualWString(archive_entry_pathname_w(e), L"wpath");
memset(wbuff, 0, sizeof(wbuff));
assertEqualWString(archive_entry_pathname_w(e), L"wpath");
/* rdev */
archive_entry_set_rdev(e, 532);
assertEqualInt(archive_entry_rdev(e), 532);
/* rdevmajor/rdevminor are tested specially below. */
/* size */
archive_entry_set_size(e, 987654321);
assertEqualInt(archive_entry_size(e), 987654321);
/* symlink */
archive_entry_set_symlink(e, "symlinkname");
assertEqualString(archive_entry_symlink(e), "symlinkname");
strcpy(buff, "symlinkname2");
archive_entry_copy_symlink(e, buff);
assertEqualString(archive_entry_symlink(e), "symlinkname2");
memset(buff, 0, sizeof(buff));
assertEqualString(archive_entry_symlink(e), "symlinkname2");
archive_entry_copy_symlink_w(e, L"wsymlink");
assertEqualWString(archive_entry_symlink_w(e), L"wsymlink");
/* uid */
archive_entry_set_uid(e, 83);
assertEqualInt(archive_entry_uid(e), 83);
/* uname */
archive_entry_set_uname(e, "user");
assertEqualString(archive_entry_uname(e), "user");
wcscpy(wbuff, L"wuser");
archive_entry_copy_gname_w(e, wbuff);
assertEqualWString(archive_entry_gname_w(e), L"wuser");
memset(wbuff, 0, sizeof(wbuff));
assertEqualWString(archive_entry_gname_w(e), L"wuser");
/* Test fflags interface. */
archive_entry_set_fflags(e, 0x55, 0xAA);
archive_entry_fflags(e, &set, &clear);
failure("Testing set/get of fflags data.");
assertEqualInt(set, 0x55);
failure("Testing set/get of fflags data.");
assertEqualInt(clear, 0xAA);
#ifdef __FreeBSD__
/* Converting fflags bitmap to string is currently system-dependent. */
/* TODO: Make this system-independent. */
assertEqualString(archive_entry_fflags_text(e),
"uappnd,nouchg,nodump,noopaque,uunlnk");
/* TODO: Test archive_entry_copy_fflags_text_w() */
#endif
/* See test_acl_basic.c for tests of ACL set/get consistency. */
/* Test xattrs set/get consistency. */
archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue1", 12);
assertEqualInt(1, archive_entry_xattr_reset(e));
assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize));
assertEqualString(xname, "xattr1");
assertEqualString(xval, "xattrvalue1");
assertEqualInt(xsize, 12);
assertEqualInt(1, archive_entry_xattr_count(e));
assertEqualInt(ARCHIVE_WARN,
archive_entry_xattr_next(e, &xname, &xval, &xsize));
archive_entry_xattr_clear(e);
assertEqualInt(0, archive_entry_xattr_reset(e));
assertEqualInt(ARCHIVE_WARN,
archive_entry_xattr_next(e, &xname, &xval, &xsize));
archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue1", 12);
assertEqualInt(1, archive_entry_xattr_reset(e));
archive_entry_xattr_add_entry(e, "xattr2", "xattrvalue2", 12);
assertEqualInt(2, archive_entry_xattr_reset(e));
assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize));
assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize));
assertEqualInt(ARCHIVE_WARN,
archive_entry_xattr_next(e, &xname, &xval, &xsize));
/*
* Test clone() implementation.
*/
/* Set values in 'e' */
archive_entry_clear(e);
archive_entry_set_atime(e, 13579, 24680);
archive_entry_set_ctime(e, 13580, 24681);
archive_entry_set_dev(e, 235);
archive_entry_set_fflags(e, 0x55, 0xAA);
archive_entry_set_gid(e, 204);
archive_entry_set_gname(e, "group");
archive_entry_set_hardlink(e, "hardlinkname");
archive_entry_set_ino(e, 8593);
archive_entry_set_mode(e, 0123456);
archive_entry_set_mtime(e, 13581, 24682);
archive_entry_set_nlink(e, 736);
archive_entry_set_pathname(e, "path");
archive_entry_set_rdev(e, 532);
archive_entry_set_size(e, 987654321);
archive_entry_set_symlink(e, "symlinkname");
archive_entry_set_uid(e, 83);
archive_entry_set_uname(e, "user");
/* Add an ACL entry. */
archive_entry_acl_add_entry(e, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77");
/* Add an extended attribute. */
archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue", 11);
/* Make a clone. */
e2 = archive_entry_clone(e);
/* Clone should have same contents. */
assertEqualInt(archive_entry_atime(e2), 13579);
assertEqualInt(archive_entry_atime_nsec(e2), 24680);
assertEqualInt(archive_entry_ctime(e2), 13580);
assertEqualInt(archive_entry_ctime_nsec(e2), 24681);
assertEqualInt(archive_entry_dev(e2), 235);
archive_entry_fflags(e, &set, &clear);
assertEqualInt(clear, 0xAA);
assertEqualInt(set, 0x55);
assertEqualInt(archive_entry_gid(e2), 204);
assertEqualString(archive_entry_gname(e2), "group");
assertEqualString(archive_entry_hardlink(e2), "hardlinkname");
assertEqualInt(archive_entry_ino(e2), 8593);
assertEqualInt(archive_entry_mode(e2), 0123456);
assertEqualInt(archive_entry_mtime(e2), 13581);
assertEqualInt(archive_entry_mtime_nsec(e2), 24682);
assertEqualInt(archive_entry_nlink(e2), 736);
assertEqualString(archive_entry_pathname(e2), "path");
assertEqualInt(archive_entry_rdev(e2), 532);
assertEqualInt(archive_entry_size(e2), 987654321);
assertEqualString(archive_entry_symlink(e2), "symlinkname");
assertEqualInt(archive_entry_uid(e2), 83);
assertEqualString(archive_entry_uname(e2), "user");
/* Verify ACL was copied. */
assertEqualInt(4, archive_entry_acl_reset(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
/* First three are standard permission bits. */
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, 4);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER_OBJ);
assertEqualInt(qual, -1);
assertEqualString(name, NULL);
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, 5);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_GROUP_OBJ);
assertEqualInt(qual, -1);
assertEqualString(name, NULL);
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, 6);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_OTHER);
assertEqualInt(qual, -1);
assertEqualString(name, NULL);
/* Fourth is custom one. */
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, ARCHIVE_ENTRY_ACL_READ);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER);
assertEqualInt(qual, 77);
assertEqualString(name, "user77");
/* Verify xattr was copied. */
assertEqualInt(1, archive_entry_xattr_reset(e2));
assertEqualInt(0, archive_entry_xattr_next(e2, &xname, &xval, &xsize));
assertEqualString(xname, "xattr1");
assertEqualString(xval, "xattrvalue");
assertEqualInt(xsize, 11);
/* Change the original */
archive_entry_set_atime(e, 13580, 24690);
archive_entry_set_ctime(e, 13590, 24691);
archive_entry_set_dev(e, 245);
archive_entry_set_fflags(e, 0x85, 0xDA);
archive_entry_set_filetype(e, AE_IFLNK);
archive_entry_set_gid(e, 214);
archive_entry_set_gname(e, "grouper");
archive_entry_set_hardlink(e, "hardlinkpath");
archive_entry_set_ino(e, 8763);
archive_entry_set_mode(e, 0123654);
archive_entry_set_mtime(e, 18351, 28642);
archive_entry_set_nlink(e, 73);
archive_entry_set_pathname(e, "pathest");
archive_entry_set_rdev(e, 132);
archive_entry_set_size(e, 987456321);
archive_entry_set_symlink(e, "symlinkpath");
archive_entry_set_uid(e, 93);
archive_entry_set_uname(e, "username");
archive_entry_acl_clear(e);
archive_entry_xattr_clear(e);
/* Clone should still have same contents. */
assertEqualInt(archive_entry_atime(e2), 13579);
assertEqualInt(archive_entry_atime_nsec(e2), 24680);
assertEqualInt(archive_entry_ctime(e2), 13580);
assertEqualInt(archive_entry_ctime_nsec(e2), 24681);
assertEqualInt(archive_entry_dev(e2), 235);
archive_entry_fflags(e2, &set, &clear);
assertEqualInt(clear, 0xAA);
assertEqualInt(set, 0x55);
assertEqualInt(archive_entry_gid(e2), 204);
assertEqualString(archive_entry_gname(e2), "group");
assertEqualString(archive_entry_hardlink(e2), "hardlinkname");
assertEqualInt(archive_entry_ino(e2), 8593);
assertEqualInt(archive_entry_mode(e2), 0123456);
assertEqualInt(archive_entry_mtime(e2), 13581);
assertEqualInt(archive_entry_mtime_nsec(e2), 24682);
assertEqualInt(archive_entry_nlink(e2), 736);
assertEqualString(archive_entry_pathname(e2), "path");
assertEqualInt(archive_entry_rdev(e2), 532);
assertEqualInt(archive_entry_size(e2), 987654321);
assertEqualString(archive_entry_symlink(e2), "symlinkname");
assertEqualInt(archive_entry_uid(e2), 83);
assertEqualString(archive_entry_uname(e2), "user");
/* Verify ACL was unchanged. */
assertEqualInt(4, archive_entry_acl_reset(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
/* First three are standard permission bits. */
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, 4);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER_OBJ);
assertEqualInt(qual, -1);
assertEqualString(name, NULL);
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, 5);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_GROUP_OBJ);
assertEqualInt(qual, -1);
assertEqualString(name, NULL);
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, 6);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_OTHER);
assertEqualInt(qual, -1);
assertEqualString(name, NULL);
/* Fourth is custom one. */
assertEqualInt(0, archive_entry_acl_next(e2,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
assertEqualInt(permset, ARCHIVE_ENTRY_ACL_READ);
assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER);
assertEqualInt(qual, 77);
assertEqualString(name, "user77");
/* Verify xattr was unchanged. */
assertEqualInt(1, archive_entry_xattr_reset(e2));
/* Release clone. */
archive_entry_free(e2);
/*
* Test clear() implementation.
*/
archive_entry_clear(e);
assertEqualInt(archive_entry_atime(e), 0);
assertEqualInt(archive_entry_atime_nsec(e), 0);
assertEqualInt(archive_entry_ctime(e), 0);
assertEqualInt(archive_entry_ctime_nsec(e), 0);
assertEqualInt(archive_entry_dev(e), 0);
archive_entry_fflags(e, &set, &clear);
assertEqualInt(clear, 0);
assertEqualInt(set, 0);
assertEqualInt(archive_entry_filetype(e), 0);
assertEqualInt(archive_entry_gid(e), 0);
assertEqualString(archive_entry_gname(e), NULL);
assertEqualString(archive_entry_hardlink(e), NULL);
assertEqualInt(archive_entry_ino(e), 0);
assertEqualInt(archive_entry_mode(e), 0);
assertEqualInt(archive_entry_mtime(e), 0);
assertEqualInt(archive_entry_mtime_nsec(e), 0);
assertEqualInt(archive_entry_nlink(e), 0);
assertEqualString(archive_entry_pathname(e), NULL);
assertEqualInt(archive_entry_rdev(e), 0);
assertEqualInt(archive_entry_size(e), 0);
assertEqualString(archive_entry_symlink(e), NULL);
assertEqualInt(archive_entry_uid(e), 0);
assertEqualString(archive_entry_uname(e), NULL);
/* ACLs should be cleared. */
assertEqualInt(archive_entry_acl_count(e, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), 0);
assertEqualInt(archive_entry_acl_count(e, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT), 0);
/* Extended attributes should be cleared. */
assertEqualInt(archive_entry_xattr_count(e), 0);
/*
* Test archive_entry_copy_stat().
*/
memset(&st, 0, sizeof(st));
/* Set all of the standard 'struct stat' fields. */
st.st_atime = 456789;
st.st_ctime = 345678;
st.st_dev = 123;
st.st_gid = 34;
st.st_ino = 234;
st.st_mode = 077777;
st.st_mtime = 234567;
st.st_nlink = 345;
st.st_size = 123456789;
st.st_uid = 23;
#ifdef __FreeBSD__
/* On FreeBSD, high-res timestamp data should come through. */
st.st_atimespec.tv_nsec = 6543210;
st.st_ctimespec.tv_nsec = 5432109;
st.st_mtimespec.tv_nsec = 3210987;
#endif
/* Copy them into the entry. */
archive_entry_copy_stat(e, &st);
/* Read each one back separately and compare. */
assertEqualInt(archive_entry_atime(e), 456789);
assertEqualInt(archive_entry_ctime(e), 345678);
assertEqualInt(archive_entry_dev(e), 123);
assertEqualInt(archive_entry_gid(e), 34);
assertEqualInt(archive_entry_ino(e), 234);
assertEqualInt(archive_entry_mode(e), 077777);
assertEqualInt(archive_entry_mtime(e), 234567);
assertEqualInt(archive_entry_nlink(e), 345);
assertEqualInt(archive_entry_size(e), 123456789);
assertEqualInt(archive_entry_uid(e), 23);
#if __FreeBSD__
/* On FreeBSD, high-res timestamp data should come through. */
assertEqualInt(archive_entry_atime_nsec(e), 6543210);
assertEqualInt(archive_entry_ctime_nsec(e), 5432109);
assertEqualInt(archive_entry_mtime_nsec(e), 3210987);
#endif
/*
* Test archive_entry_stat().
*/
/* First, clear out any existing stat data. */
memset(&st, 0, sizeof(st));
archive_entry_copy_stat(e, &st);
/* Set a bunch of fields individually. */
archive_entry_set_atime(e, 456789, 321);
archive_entry_set_ctime(e, 345678, 432);
archive_entry_set_dev(e, 123);
archive_entry_set_gid(e, 34);
archive_entry_set_ino(e, 234);
archive_entry_set_mode(e, 012345);
archive_entry_set_mode(e, 012345);
archive_entry_set_mtime(e, 234567, 543);
archive_entry_set_nlink(e, 345);
archive_entry_set_size(e, 123456789);
archive_entry_set_uid(e, 23);
/* Retrieve a stat structure. */
assert((pst = archive_entry_stat(e)) != NULL);
/* Check that the values match. */
assertEqualInt(pst->st_atime, 456789);
assertEqualInt(pst->st_ctime, 345678);
assertEqualInt(pst->st_dev, 123);
assertEqualInt(pst->st_gid, 34);
assertEqualInt(pst->st_ino, 234);
assertEqualInt(pst->st_mode, 012345);
assertEqualInt(pst->st_mtime, 234567);
assertEqualInt(pst->st_nlink, 345);
assertEqualInt(pst->st_size, 123456789);
assertEqualInt(pst->st_uid, 23);
#ifdef __FreeBSD__
/* On FreeBSD, high-res timestamp data should come through. */
assertEqualInt(pst->st_atimespec.tv_nsec, 321);
assertEqualInt(pst->st_ctimespec.tv_nsec, 432);
assertEqualInt(pst->st_mtimespec.tv_nsec, 543);
#endif
/* Changing any one value should update struct stat. */
archive_entry_set_atime(e, 456788, 0);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_atime, 456788);
archive_entry_set_ctime(e, 345677, 431);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_ctime, 345677);
archive_entry_set_dev(e, 122);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_dev, 122);
archive_entry_set_gid(e, 33);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_gid, 33);
archive_entry_set_ino(e, 233);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_ino, 233);
archive_entry_set_mode(e, 012344);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_mode, 012344);
archive_entry_set_mtime(e, 234566, 542);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_mtime, 234566);
archive_entry_set_nlink(e, 344);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_nlink, 344);
archive_entry_set_size(e, 123456788);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_size, 123456788);
archive_entry_set_uid(e, 22);
assert((pst = archive_entry_stat(e)) != NULL);
assertEqualInt(pst->st_uid, 22);
/* We don't need to check high-res fields here. */
/*
* Test dev/major/minor interfaces. Setting 'dev' or 'rdev'
* should change the corresponding major/minor values, and
* vice versa.
*
* The test here is system-specific because it assumes that
* makedev(), major(), and minor() are defined in sys/stat.h.
* I'm not too worried about it, though, because the code is
* simple. If it works on FreeBSD, it's unlikely to be broken
* anywhere else. Note: The functionality is present on every
* platform even if these tests only run some places;
* libarchive's more extensive configuration logic should find
* the necessary definitions on every platform.
*/
#if __FreeBSD__
archive_entry_set_dev(e, 0x12345678);
assertEqualInt(archive_entry_devmajor(e), major(0x12345678));
assertEqualInt(archive_entry_devminor(e), minor(0x12345678));
assertEqualInt(archive_entry_dev(e), 0x12345678);
archive_entry_set_devmajor(e, 0xfe);
archive_entry_set_devminor(e, 0xdcba98);
assertEqualInt(archive_entry_devmajor(e), 0xfe);
assertEqualInt(archive_entry_devminor(e), 0xdcba98);
assertEqualInt(archive_entry_dev(e), makedev(0xfe, 0xdcba98));
archive_entry_set_rdev(e, 0x12345678);
assertEqualInt(archive_entry_rdevmajor(e), major(0x12345678));
assertEqualInt(archive_entry_rdevminor(e), minor(0x12345678));
assertEqualInt(archive_entry_rdev(e), 0x12345678);
archive_entry_set_rdevmajor(e, 0xfe);
archive_entry_set_rdevminor(e, 0xdcba98);
assertEqualInt(archive_entry_rdevmajor(e), 0xfe);
assertEqualInt(archive_entry_rdevminor(e), 0xdcba98);
assertEqualInt(archive_entry_rdev(e), makedev(0xfe, 0xdcba98));
#endif
/* Release the experimental entry. */
archive_entry_free(e);
}

View File

@ -0,0 +1,55 @@
/*-
* 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 "test.h"
__FBSDID("$FreeBSD$");
static unsigned char archive[] = {
31,139,8,0,222,'C','p','C',0,3,211,'c',160,'=','0','0','0','0','7','5','U',
0,210,134,230,166,6,200,'4',28,'(',24,26,24,27,155,24,152,24,154,27,155,')',
24,24,26,152,154,25,'2','(',152,210,193,'m',12,165,197,'%',137,'E','@',167,
148,'d',230,226,'U','G','H',30,234,15,'8','=',10,'F',193,'(',24,5,131,28,
0,0,29,172,5,240,0,6,0,0};
DEFINE_TEST(test_read_compress_program)
{
struct archive_entry *ae;
struct archive *a;
assert((a = archive_read_new()) != NULL);
assertEqualIntA(a, 0, archive_read_support_compression_none(a));
assertEqualIntA(a, 0, archive_read_support_compression_program(a, "gunzip"));
assert(0 == archive_read_support_format_all(a));
assertEqualIntA(a, 0, archive_read_open_memory(a, archive, sizeof(archive)));
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assert(archive_compression(a) == ARCHIVE_COMPRESSION_PROGRAM);
assert(archive_format(a) == ARCHIVE_FORMAT_TAR_USTAR);
assert(0 == archive_read_close(a));
#if ARCHIVE_API_VERSION > 1
assert(0 == archive_read_finish(a));
#else
archive_read_finish(a);
#endif
}

View File

@ -42,8 +42,8 @@ DEFINE_TEST(test_read_data_large)
struct archive_entry *ae;
struct archive *a;
char tmpfilename[] = "largefile";
int tmpfile;
int i;
int tmpfilefd;
unsigned int i;
size_t used;
/* Create a new archive in memory. */
@ -96,21 +96,21 @@ DEFINE_TEST(test_read_data_large)
assertA(0 == archive_read_support_compression_all(a));
assertA(0 == archive_read_open_memory(a, buff1, sizeof(buff1)));
assertA(0 == archive_read_next_header(a, &ae));
tmpfile = open(tmpfilename, O_WRONLY | O_CREAT, 0777);
assert(tmpfile != 0);
assertEqualIntA(a, 0, archive_read_data_into_fd(a, tmpfile));
tmpfilefd = open(tmpfilename, O_WRONLY | O_CREAT, 0777);
assert(tmpfilefd != 0);
assertEqualIntA(a, 0, archive_read_data_into_fd(a, tmpfilefd));
assert(0 == archive_read_close(a));
#if ARCHIVE_API_VERSION > 1
assert(0 == archive_read_finish(a));
#else
archive_read_finish(a);
#endif
close(tmpfile);
close(tmpfilefd);
tmpfile = open(tmpfilename, O_RDONLY);
assert(tmpfile != 0);
assertEqualIntA(NULL, sizeof(buff3), read(tmpfile, buff3, sizeof(buff3)));
close(tmpfile);
tmpfilefd = open(tmpfilename, O_RDONLY);
assert(tmpfilefd != 0);
assertEqualIntA(NULL, sizeof(buff3), read(tmpfilefd, buff3, sizeof(buff3)));
close(tmpfilefd);
assert(0 == memcmp(buff2, buff3, sizeof(buff3)));
unlink(tmpfilename);

View File

@ -33,7 +33,7 @@ DEFINE_TEST(test_read_extract)
struct archive_entry *ae;
struct archive *a;
struct stat st;
ssize_t used;
size_t used;
int i;
char *buff, *file_buff;
int fd;

View File

@ -66,7 +66,7 @@ DEFINE_TEST(test_read_format_ar)
/* Filename table. */
assertA(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("//", archive_entry_pathname(ae)));
assertEqualString("//", archive_entry_pathname(ae));
assertEqualInt(0, archive_entry_mtime(ae));
assertEqualInt(0, archive_entry_uid(ae));
assertEqualInt(0, archive_entry_gid(ae));
@ -76,7 +76,7 @@ DEFINE_TEST(test_read_format_ar)
/* First Entry */
assertA(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("yyytttsssaaafff.o", archive_entry_pathname(ae)));
assertEqualString("yyytttsssaaafff.o", archive_entry_pathname(ae));
assertEqualInt(1175465652, archive_entry_mtime(ae));
assertEqualInt(1001, archive_entry_uid(ae));
assertEqualInt(0, archive_entry_gid(ae));
@ -86,7 +86,7 @@ DEFINE_TEST(test_read_format_ar)
/* Second Entry */
assertA(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("gghh.o", archive_entry_pathname(ae)));
assertEqualString("gghh.o", archive_entry_pathname(ae));
assertEqualInt(1175465668, archive_entry_mtime(ae));
assertEqualInt(1001, archive_entry_uid(ae));
assertEqualInt(0, archive_entry_gid(ae));
@ -96,7 +96,7 @@ DEFINE_TEST(test_read_format_ar)
/* Third Entry */
assertA(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("hhhhjjjjkkkkllll.o", archive_entry_pathname(ae)));
assertEqualString("hhhhjjjjkkkkllll.o", archive_entry_pathname(ae));
assertEqualInt(1175465713, archive_entry_mtime(ae));
assertEqualInt(1001, archive_entry_uid(ae));
assertEqualInt(0, archive_entry_gid(ae));

View File

@ -102,18 +102,18 @@ DEFINE_TEST(test_read_format_isorr_bz2)
/* First entry is '.' root directory. */
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp(".", archive_entry_pathname(ae)));
assertEqualString(".", archive_entry_pathname(ae));
assert(S_ISDIR(archive_entry_stat(ae)->st_mode));
assert(2048 == archive_entry_size(ae));
assert(1 == archive_entry_mtime(ae));
assert(0 == archive_entry_mtime_nsec(ae));
assert(1 == archive_entry_ctime(ae));
assert(0 == archive_entry_stat(ae)->st_nlink);
assert(0 == archive_entry_uid(ae));
assertEqualInt(2048, archive_entry_size(ae));
assertEqualInt(1, archive_entry_mtime(ae));
assertEqualInt(0, archive_entry_mtime_nsec(ae));
assertEqualInt(1, archive_entry_ctime(ae));
assertEqualInt(0, archive_entry_stat(ae)->st_nlink);
assertEqualInt(0, archive_entry_uid(ae));
/* A directory. */
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("dir", archive_entry_pathname(ae)));
assertEqualString("dir", archive_entry_pathname(ae));
assert(S_ISDIR(archive_entry_stat(ae)->st_mode));
assert(2048 == archive_entry_size(ae));
assert(1 == archive_entry_mtime(ae));
@ -124,7 +124,7 @@ DEFINE_TEST(test_read_format_isorr_bz2)
/* A regular file. */
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("file", archive_entry_pathname(ae)));
assertEqualString("file", archive_entry_pathname(ae));
assert(S_ISREG(archive_entry_stat(ae)->st_mode));
assert(6 == archive_entry_size(ae));
assert(0 == archive_read_data_block(a, &p, &size, &offset));
@ -139,9 +139,9 @@ DEFINE_TEST(test_read_format_isorr_bz2)
/* A hardlink to the regular file. */
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("hardlink", archive_entry_pathname(ae)));
assertEqualString("hardlink", archive_entry_pathname(ae));
assert(S_ISREG(archive_entry_stat(ae)->st_mode));
assert(0 == strcmp("file", archive_entry_hardlink(ae)));
assertEqualString("file", archive_entry_hardlink(ae));
assert(6 == archive_entry_size(ae));
assert(1 == archive_entry_mtime(ae));
assert(1 == archive_entry_atime(ae));
@ -151,9 +151,9 @@ DEFINE_TEST(test_read_format_isorr_bz2)
/* A symlink to the regular file. */
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("symlink", archive_entry_pathname(ae)));
assertEqualString("symlink", archive_entry_pathname(ae));
assert(S_ISLNK(archive_entry_stat(ae)->st_mode));
assert(0 == strcmp("file", archive_entry_symlink(ae)));
assertEqualString("file", archive_entry_symlink(ae));
assert(0 == archive_entry_size(ae));
assert(-2 == archive_entry_mtime(ae));
assert(-2 == archive_entry_atime(ae));

View File

@ -26,21 +26,49 @@
__FBSDID("$FreeBSD$");
static unsigned char archive[] = {
'P','K',3,4,10,0,0,0,0,0,162,186,'g','3',0,0,0,0,0,0,0,0,0,0,0,0,1,0,21,0,
'a','U','T',9,0,3,224,'Q','p','C',224,'Q','p','C','U','x',4,0,232,3,232,3,
'P','K',1,2,23,3,10,0,0,0,0,0,162,186,'g','3',0,0,0,0,0,0,0,0,0,0,0,0,1,0,
13,0,0,0,0,0,0,0,0,0,164,129,0,0,0,0,'a','U','T',5,0,3,224,'Q','p','C','U',
'x',0,0,'P','K',5,6,0,0,0,0,1,0,1,0,'<',0,0,0,'4',0,0,0,0,0};
'P','K',3,4,10,0,0,0,0,0,'Y','f',179,'6',0,0,0,0,0,0,0,0,0,0,0,0,4,0,21,0,
'd','i','r','/','U','T',9,0,3,25,'U','O','F',25,'U','O','F','U','x',4,0,232,
3,232,3,'P','K',3,4,20,0,0,0,8,0,'o','f',179,'6',':','7','f','=',10,0,0,0,
18,0,0,0,5,0,21,0,'f','i','l','e','1','U','T',9,0,3,'A','U','O','F',172,'[',
'O','F','U','x',4,0,232,3,232,3,203,'H',205,201,201,231,202,'@','"',1,'P',
'K',3,4,20,0,0,0,8,0,'Z','j',179,'6',':','7','f','=',10,0,0,0,18,0,0,0,5,
0,21,0,'f','i','l','e','2','U','T',9,0,3,172,'[','O','F',172,'[','O','F',
'U','x',4,0,232,3,232,3,203,'H',205,201,201,231,202,'@','"',1,'P','K',1,2,
23,3,10,0,0,0,0,0,'Y','f',179,'6',0,0,0,0,0,0,0,0,0,0,0,0,4,0,13,0,0,0,0,
0,0,0,16,0,237,'A',0,0,0,0,'d','i','r','/','U','T',5,0,3,25,'U','O','F','U',
'x',0,0,'P','K',1,2,23,3,20,0,0,0,8,0,'o','f',179,'6',':','7','f','=',10,
0,0,0,18,0,0,0,5,0,13,0,0,0,0,0,1,0,0,0,164,129,'7',0,0,0,'f','i','l','e',
'1','U','T',5,0,3,'A','U','O','F','U','x',0,0,'P','K',1,2,23,3,20,0,0,0,8,
0,'Z','j',179,'6',':','7','f','=',10,0,0,0,18,0,0,0,5,0,13,0,0,0,0,0,1,0,
0,0,164,129,'y',0,0,0,'f','i','l','e','2','U','T',5,0,3,172,'[','O','F','U',
'x',0,0,'P','K',5,6,0,0,0,0,3,0,3,0,191,0,0,0,187,0,0,0,0,0};
DEFINE_TEST(test_read_format_zip)
{
struct archive_entry *ae;
struct archive *a;
char *buff[128];
assert((a = archive_read_new()) != NULL);
assertA(0 == archive_read_support_compression_all(a));
assertA(0 == archive_read_support_format_all(a));
assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
assertA(0 == archive_read_next_header(a, &ae));
assertEqualString("dir/", archive_entry_pathname(ae));
assertEqualInt(1179604249, archive_entry_mtime(ae));
assertEqualInt(0, archive_entry_size(ae));
assertA(0 == archive_read_next_header(a, &ae));
assertEqualString("file1", archive_entry_pathname(ae));
assertEqualInt(1179604289, archive_entry_mtime(ae));
assertEqualInt(18, archive_entry_size(ae));
assertEqualInt(18, archive_read_data(a, buff, 18));
assert(0 == memcmp(buff, "hello\nhello\nhello\n", 18));
assertA(0 == archive_read_next_header(a, &ae));
assertEqualString("file2", archive_entry_pathname(ae));
assertEqualInt(1179605932, archive_entry_mtime(ae));
assertEqualInt(18, archive_entry_size(ae));
assertEqualInt(18, archive_read_data(a, buff, 18));
assert(0 == memcmp(buff, "hello\nhello\nhello\n", 18));
assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE);
assertA(archive_format(a) == ARCHIVE_FORMAT_ZIP);
assert(0 == archive_read_close(a));

View File

@ -32,8 +32,8 @@ static unsigned char buff[11 * 1024 * 1024];
/* Check correct behavior on large reads. */
DEFINE_TEST(test_read_large)
{
int i;
int tmpfile;
unsigned int i;
int tmpfilefd;
char tmpfilename[] = "/tmp/test-read_large.XXXXXX";
size_t used;
struct archive *a;
@ -77,17 +77,17 @@ DEFINE_TEST(test_read_large)
assertA(0 == archive_read_support_compression_all(a));
assertA(0 == archive_read_open_memory(a, buff, sizeof(buff)));
assertA(0 == archive_read_next_header(a, &entry));
assert(0 < (tmpfile = mkstemp(tmpfilename)));
assertA(0 == archive_read_data_into_fd(a, tmpfile));
close(tmpfile);
assert(0 < (tmpfilefd = mkstemp(tmpfilename)));
assertA(0 == archive_read_data_into_fd(a, tmpfilefd));
close(tmpfilefd);
#if ARCHIVE_API_VERSION > 1
assertA(0 == archive_read_finish(a));
#else
archive_read_finish(a);
#endif
tmpfile = open(tmpfilename, O_RDONLY);
read(tmpfile, testdatacopy, sizeof(testdatacopy));
close(tmpfile);
tmpfilefd = open(tmpfilename, O_RDONLY);
read(tmpfilefd, testdatacopy, sizeof(testdatacopy));
close(tmpfilefd);
assert(0 == memcmp(testdata, testdatacopy, sizeof(testdata)));
unlink(tmpfilename);

View File

@ -48,7 +48,7 @@ DEFINE_TEST(test_read_position)
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &write_pos));
assertA(0 == archive_write_header(a, ae));
archive_entry_free(ae);
assertA(data_size == archive_write_data(a, nulls, sizeof(nulls)));
assertA(data_size == (size_t)archive_write_data(a, nulls, sizeof(nulls)));
#if ARCHIVE_API_VERSION > 1
assertA(0 == archive_write_finish(a));
#else

View File

@ -32,7 +32,7 @@ DEFINE_TEST(test_read_truncated)
{
struct archive_entry *ae;
struct archive *a;
int i;
unsigned int i;
size_t used;
/* Create a new archive in memory. */

View File

@ -31,7 +31,7 @@ __FBSDID("$FreeBSD$");
* filenames into prefix/suffix.
*/
static
static void
test_filename(int dlen, int flen)
{
char buff[8192];
@ -110,7 +110,7 @@ test_filename(int dlen, int flen)
/* Read the file and check the filename. */
assertA(0 == archive_read_next_header(a, &ae));
failure("Pathname %d/%d: %s", dlen, flen, archive_entry_pathname(ae));
assert(0 == strcmp(filename, archive_entry_pathname(ae)));
assertEqualString(filename, archive_entry_pathname(ae));
assert((S_IFREG | 0755) == archive_entry_mode(ae));
/*
@ -122,12 +122,12 @@ test_filename(int dlen, int flen)
*/
assertA(0 == archive_read_next_header(a, &ae));
failure("Pathname %d/%d: %s", dlen, flen, archive_entry_pathname(ae));
assert(0 == strcmp(dirname, archive_entry_pathname(ae)));
assertEqualString(dirname, archive_entry_pathname(ae));
assert((S_IFDIR | 0755) == archive_entry_mode(ae));
assertA(0 == archive_read_next_header(a, &ae));
failure("Pathname %d/%d: %s", dlen, flen, archive_entry_pathname(ae));
assert(0 == strcmp(dirname, archive_entry_pathname(ae)));
assertEqualString(dirname, archive_entry_pathname(ae));
assert((S_IFDIR | 0755) == archive_entry_mode(ae));
/* Verify the end of the archive. */

View File

@ -0,0 +1,97 @@
/*-
* 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 "test.h"
__FBSDID("$FreeBSD$");
char buff[1000000];
char buff2[64];
DEFINE_TEST(test_write_compress_program)
{
struct archive_entry *ae;
struct archive *a;
size_t used;
int blocksize = 1024;
/* Create a new archive in memory. */
/* Write it through an external "gzip" program. */
assert((a = archive_write_new()) != NULL);
assertA(0 == archive_write_set_format_ustar(a));
assertA(0 == archive_write_set_compression_program(a, "gzip"));
assertA(0 == archive_write_set_bytes_per_block(a, blocksize));
assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize));
assertA(blocksize == archive_write_get_bytes_in_last_block(a));
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
assertA(blocksize == archive_write_get_bytes_in_last_block(a));
/*
* Write a file to it.
*/
assert((ae = archive_entry_new()) != NULL);
archive_entry_set_mtime(ae, 1, 10);
archive_entry_copy_pathname(ae, "file");
archive_entry_set_mode(ae, S_IFREG | 0755);
archive_entry_set_size(ae, 8);
assertA(0 == archive_write_header(a, ae));
archive_entry_free(ae);
assertA(8 == archive_write_data(a, "12345678", 9));
/* Close out the archive. */
assertA(0 == archive_write_close(a));
#if ARCHIVE_API_VERSION > 1
assertA(0 == archive_write_finish(a));
#else
archive_write_finish(a);
#endif
/*
* Now, read the data back through the built-in gzip support.
*/
assert((a = archive_read_new()) != NULL);
assertA(0 == archive_read_support_format_all(a));
assertA(0 == archive_read_support_compression_all(a));
assertA(0 == archive_read_open_memory(a, buff, used));
assertA(0 == archive_read_next_header(a, &ae));
assert(1 == archive_entry_mtime(ae));
assert(0 == archive_entry_atime(ae));
assert(0 == archive_entry_ctime(ae));
assertEqualString("file", archive_entry_pathname(ae));
assert((S_IFREG | 0755) == archive_entry_mode(ae));
assert(8 == archive_entry_size(ae));
assertA(8 == archive_read_data(a, buff2, 10));
assert(0 == memcmp(buff2, "12345678", 8));
/* Verify the end of the archive. */
assert(1 == archive_read_next_header(a, &ae));
assert(0 == archive_read_close(a));
#if ARCHIVE_API_VERSION > 1
assert(0 == archive_read_finish(a));
#else
archive_read_finish(a);
#endif
}

View File

@ -27,17 +27,18 @@ __FBSDID("$FreeBSD$");
#define UMASK 022
static void create(struct archive_entry *ae)
static void create(struct archive_entry *ae, const char *msg)
{
struct archive *ad;
struct stat st;
/* Write the entry to disk. */
assert((ad = archive_write_disk_new()) != NULL);
assert(0 == archive_write_header(ad, ae));
assert(0 == archive_write_finish_entry(ad));
failure("%s", msg);
assertEqualIntA(ad, 0, archive_write_header(ad, ae));
assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
#if ARCHIVE_API_VERSION > 1
assert(0 == archive_write_finish(ad));
assertEqualInt(0, archive_write_finish(ad));
#else
archive_write_finish(ad);
#endif
@ -59,34 +60,34 @@ DEFINE_TEST(test_write_disk)
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "file");
archive_entry_set_mode(ae, S_IFREG | 0755);
create(ae);
create(ae, "Test creating a regular file");
archive_entry_free(ae);
/* A regular file over an existing file */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "file");
archive_entry_set_mode(ae, S_IFREG | 0724);
create(ae);
create(ae, "Test creating a file over an existing file.");
archive_entry_free(ae);
/* A directory. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "dir");
archive_entry_set_mode(ae, S_IFDIR | 0555);
create(ae);
create(ae, "Test creating a regular dir.");
archive_entry_free(ae);
/* A directory over an existing file. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "file");
archive_entry_set_mode(ae, S_IFDIR | 0742);
create(ae);
create(ae, "Test creating a dir over an existing file.");
archive_entry_free(ae);
/* A file over an existing dir. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "file");
archive_entry_set_mode(ae, S_IFREG | 0744);
create(ae);
create(ae, "Test creating a file over an existing dir.");
archive_entry_free(ae);
}

View File

@ -27,9 +27,9 @@ __FBSDID("$FreeBSD$");
#define UMASK 022
static gid_t _default_gid = 0;
static gid_t _invalid_gid = 0;
static gid_t _alt_gid = 0;
static long _default_gid = -1;
static long _invalid_gid = -1;
static long _alt_gid = -1;
/*
* To fully test SGID restores, we need three distinct GIDs to work
@ -42,13 +42,13 @@ static gid_t _alt_gid = 0;
* The second fails if this user doesn't belong to at least two groups;
* the third fails if the current user is root.
*/
static int
static void
searchgid(void)
{
static int _searched = 0;
uid_t uid = getuid();
gid_t gid = 0;
int n;
unsigned int n;
struct stat st;
int fd;
@ -67,7 +67,7 @@ searchgid(void)
_default_gid = st.st_gid;
/* Find a GID for which fchown() fails. This is our "invalid" GID. */
_invalid_gid = 0;
_invalid_gid = -1;
/* This loop stops when we wrap the gid or examine 10,000 gids. */
for (gid = 1, n = 1; gid == n && n < 10000 ; n++, gid++) {
if (fchown(fd, uid, gid) != 0) {
@ -80,10 +80,10 @@ searchgid(void)
* Find a GID for which fchown() succeeds, but which isn't the
* default. This is the "alternate" gid.
*/
_alt_gid = 0;
for (gid = 1, n = 1; gid == n && n < 10000 ; n++, gid++) {
_alt_gid = -1;
for (gid = 0, n = 0; gid == n && n < 10000 ; n++, gid++) {
/* _alt_gid must be different than _default_gid */
if (gid == _default_gid)
if (gid == (gid_t)_default_gid)
continue;
if (fchown(fd, uid, gid) == 0) {
_alt_gid = gid;
@ -126,6 +126,15 @@ DEFINE_TEST(test_write_disk_perms)
struct archive_entry *ae;
struct stat st;
/*
* Set ownership of the current directory to the group of this
* process. Otherwise, the SGID tests below fail if the
* /tmp directory is owned by a group to which we don't belong
* and we're on a system where group ownership is inherited.
* (Because we're not allowed to SGID files with defaultgid().)
*/
assertEqualInt(0, chown(".", getuid(), getgid()));
/* Create an archive_write_disk object. */
assert((a = archive_write_disk_new()) != NULL);
@ -232,7 +241,7 @@ DEFINE_TEST(test_write_disk_perms)
failure("Setting SGID bit should succeed here.");
assertEqualIntA(a, 0, archive_write_finish_entry(a));
if (altgid() == 0) {
if (altgid() == -1) {
/*
* Current user must belong to at least two groups or
* else we can't test setting the GID to another group.
@ -278,7 +287,7 @@ DEFINE_TEST(test_write_disk_perms)
* but wrong GID. POSIX says you shouldn't restore SGID bit
* unless the GID could be restored.
*/
if (invalidgid() == 0) {
if (invalidgid() == -1) {
/* This test always fails for root. */
printf("Running as root: Can't test SGID failures.\n");
} else {
@ -352,7 +361,7 @@ DEFINE_TEST(test_write_disk_perms)
failure("file_perm_sgid: st.st_mode=%o", st.st_mode);
assert((st.st_mode & 07777) == (S_ISGID | 0742));
if (altgid() != 0) {
if (altgid() != -1) {
/* SGID should not be set here. */
assert(0 == stat("file_alt_sgid", &st));
failure("file_alt_sgid: st.st_mode=%o", st.st_mode);
@ -364,7 +373,7 @@ DEFINE_TEST(test_write_disk_perms)
assert((st.st_mode & 07777) == (S_ISGID | 0742));
}
if (invalidgid() != 0) {
if (invalidgid() != -1) {
/* SGID should NOT be set here. */
assert(0 == stat("file_bad_sgid", &st));
failure("file_bad_sgid: st.st_mode=%o", st.st_mode);

View File

@ -30,7 +30,7 @@ __FBSDID("$FreeBSD$");
char buff[4096];
char buff2[64];
static unsigned char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\n\n";
static unsigned char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n";
DEFINE_TEST(test_write_format_ar)
{
@ -43,7 +43,7 @@ DEFINE_TEST(test_write_format_ar)
*/
assert((a = archive_write_new()) != NULL);
assertA(0 == archive_write_set_format_ar_svr4(a));
assertA(0 == archive_write_set_compression_none(a));
assertA(0 == archive_write_set_compression_gzip(a));
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
/* write the filename table */
@ -51,7 +51,7 @@ DEFINE_TEST(test_write_format_ar)
archive_entry_copy_pathname(ae, "//");
archive_entry_set_size(ae, strlen(strtab));
assertA(0 == archive_write_header(a, ae));
assertA(strlen(strtab) == archive_write_data(a, strtab, strlen(strtab)));
assertA(strlen(strtab) == (size_t)archive_write_data(a, strtab, strlen(strtab)));
archive_entry_free(ae);
/* write entries */
@ -68,11 +68,36 @@ DEFINE_TEST(test_write_format_ar)
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "ggghhhjjjrrrttt.o");
archive_entry_set_mode(ae, S_IFREG | 0755);
archive_entry_set_size(ae, 7);
assertA(0 == archive_write_header(a, ae));
assertA(7 == archive_write_data(a, "7777777", 7));
archive_entry_free(ae);
/* test full pathname */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "/usr/home/xx/iiijjjdddsssppp.o");
archive_entry_set_mode(ae, S_IFREG | 0755);
archive_entry_set_size(ae, 8);
assertA(0 == archive_write_header(a, ae));
assertA(8 == archive_write_data(a, "88877766", 8));
archive_entry_free(ae);
/* trailing "/" should be rejected */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "/usr/home/xx/iiijjj/");
archive_entry_set_size(ae, 8);
assertA(0 != archive_write_header(a, ae));
archive_entry_free(ae);
/* Non regular file should be rejected */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "gfgh.o");
archive_entry_set_mode(ae, S_IFDIR | 0755);
archive_entry_set_size(ae, 6);
assertA(0 != archive_write_header(a, ae));
archive_entry_free(ae);
archive_write_close(a);
#if ARCHIVE_API_VERSION > 1
assert(0 == archive_write_finish(a));
@ -90,24 +115,30 @@ DEFINE_TEST(test_write_format_ar)
assertA(0 == archive_read_next_header(a, &ae));
assertEqualInt(0, archive_entry_mtime(ae));
assert(0 == strcmp("//", archive_entry_pathname(ae)));
assertEqualString("//", archive_entry_pathname(ae));
assertEqualInt(strlen(strtab), archive_entry_size(ae));
assertEqualIntA(a, strlen(strtab), archive_read_data(a, buff2, 100));
assert(0 == memcmp(buff2, strtab, strlen(strtab)));
assertA(0 == archive_read_next_header(a, &ae));
assert(1 == archive_entry_mtime(ae));
assert(0 == strcmp("abcdefghijklmn.o", archive_entry_pathname(ae)));
assertEqualString("abcdefghijklmn.o", archive_entry_pathname(ae));
assert(8 == archive_entry_size(ae));
assertA(8 == archive_read_data(a, buff2, 10));
assert(0 == memcmp(buff2, "87654321", 8));
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("ggghhhjjjrrrttt.o", archive_entry_pathname(ae)));
assertEqualString("ggghhhjjjrrrttt.o", archive_entry_pathname(ae));
assert(7 == archive_entry_size(ae));
assertA(7 == archive_read_data(a, buff2, 11));
assert(0 == memcmp(buff2, "7777777", 7));
assert(0 == archive_read_next_header(a, &ae));
assertEqualString("iiijjjdddsssppp.o", archive_entry_pathname(ae));
assert(8 == archive_entry_size(ae));
assertA(8 == archive_read_data(a, buff2, 17));
assert(0 == memcmp(buff2, "88877766", 8));
assert(0 == archive_read_close(a));
#if ARCHIVE_API_VERSION > 1
assert(0 == archive_read_finish(a));
@ -118,14 +149,16 @@ DEFINE_TEST(test_write_format_ar)
/*
* Then, we try to create a BSD format archive.
*/
memset(buff, 0, sizeof(buff));
assert((a = archive_write_new()) != NULL);
assertA(0 == archive_write_set_format_ar_bsd(a));
assertA(0 == archive_write_set_compression_none(a));
assertA(0 == archive_write_set_compression_bzip2(a));
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
/* write a entry need long name extension */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "ttttyyyyuuuuiiii.o");
archive_entry_set_mode(ae, S_IFREG | 0755);
archive_entry_set_size(ae, 5);
assertA(0 == archive_write_header(a, ae));
assertA(5 == archive_write_data(a, "12345", 7));
@ -134,6 +167,7 @@ DEFINE_TEST(test_write_format_ar)
/* write a entry with a short name */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "ttyy.o");
archive_entry_set_mode(ae, S_IFREG | 0755);
archive_entry_set_size(ae, 6);
assertA(0 == archive_write_header(a, ae));
assertA(6 == archive_write_data(a, "555555", 7));
@ -151,20 +185,20 @@ DEFINE_TEST(test_write_format_ar)
assertA(0 == archive_read_support_compression_all(a));
assertA(0 == archive_read_open_memory(a, buff, used));
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("ttttyyyyuuuuiiii.o", archive_entry_pathname(ae)));
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualString("ttttyyyyuuuuiiii.o", archive_entry_pathname(ae));
assertEqualInt(5, archive_entry_size(ae));
assertA(5 == archive_read_data(a, buff2, 10));
assert(0 == memcmp(buff2, "12345", 5));
assert(0 == archive_read_next_header(a, &ae));
assert(0 == strcmp("ttyy.o", archive_entry_pathname(ae)));
assertEqualString("ttyy.o", archive_entry_pathname(ae));
assert(6 == archive_entry_size(ae));
assertA(6 == archive_read_data(a, buff2, 10));
assert(0 == memcmp(buff2, "555555", 6));
/* Test EOF */
assertA(1 == archive_read_next_header(a, &ae));
assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
assert(0 == archive_read_close(a));
#if ARCHIVE_API_VERSION > 1
assert(0 == archive_read_finish(a));

View File

@ -49,7 +49,6 @@ DEFINE_TEST(test_write_format_cpio_empty)
struct archive *a;
char buff[2048];
size_t used;
int i;
/* Create a new archive in memory. */
assert((a = archive_write_new()) != NULL);

View File

@ -34,7 +34,6 @@ DEFINE_TEST(test_write_format_shar_empty)
struct archive *a;
char buff[2048];
size_t used;
int i;
/* Create a new archive in memory. */
assert((a = archive_write_new()) != NULL);

View File

@ -34,7 +34,7 @@ DEFINE_TEST(test_write_format_tar)
struct archive *a;
char *p;
size_t used;
int blocksize;
size_t blocksize;
/* Repeat the following for a variety of odd blocksizes. */
for (blocksize = 1; blocksize < 100000; blocksize += blocksize + 3) {
@ -44,9 +44,9 @@ DEFINE_TEST(test_write_format_tar)
assertA(0 == archive_write_set_compression_none(a));
assertA(0 == archive_write_set_bytes_per_block(a, blocksize));
assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize));
assertA(blocksize == archive_write_get_bytes_in_last_block(a));
assertA(blocksize == (size_t)archive_write_get_bytes_in_last_block(a));
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
assertA(blocksize == archive_write_get_bytes_in_last_block(a));
assertA(blocksize == (size_t)archive_write_get_bytes_in_last_block(a));
/*
* Write a file to it.
@ -54,12 +54,14 @@ DEFINE_TEST(test_write_format_tar)
assert((ae = archive_entry_new()) != NULL);
archive_entry_set_mtime(ae, 1, 10);
assert(1 == archive_entry_mtime(ae));
#if !defined(__INTERIX)
assert(10 == archive_entry_mtime_nsec(ae));
#endif
p = strdup("file");
archive_entry_copy_pathname(ae, p);
strcpy(p, "XXXX");
free(p);
assert(0 == strcmp("file", archive_entry_pathname(ae)));
assertEqualString("file", archive_entry_pathname(ae));
archive_entry_set_mode(ae, S_IFREG | 0755);
assert((S_IFREG | 0755) == archive_entry_mode(ae));
archive_entry_set_size(ae, 8);
@ -90,11 +92,11 @@ DEFINE_TEST(test_write_format_tar)
assertA(0 == archive_read_next_header(a, &ae));
assert(1 == archive_entry_mtime(ae));
/* Not the same as above: ustar doesn't store hi-res timestamps. */
/* Not the same as above: ustar doesn't store hi-res times. */
assert(0 == archive_entry_mtime_nsec(ae));
assert(0 == archive_entry_atime(ae));
assert(0 == archive_entry_ctime(ae));
assert(0 == strcmp("file", archive_entry_pathname(ae)));
assertEqualString("file", archive_entry_pathname(ae));
assert((S_IFREG | 0755) == archive_entry_mode(ae));
assert(8 == archive_entry_size(ae));
assertA(8 == archive_read_data(a, buff2, 10));

View File

@ -34,7 +34,7 @@ DEFINE_TEST(test_write_format_tar_empty)
struct archive *a;
char buff[2048];
size_t used;
int i;
unsigned int i;
/* USTAR format: Create a new archive in memory. */
assert((a = archive_write_new()) != NULL);

View File

@ -30,7 +30,7 @@ static unsigned char buff[16384];
DEFINE_TEST(test_write_open_memory)
{
int i;
unsigned int i;
struct archive *a;
struct archive_entry *ae;
const char *name="/tmp/test";
@ -39,7 +39,7 @@ DEFINE_TEST(test_write_open_memory)
assert((ae = archive_entry_new()) != NULL);
archive_entry_set_pathname(ae, name);
archive_entry_set_mode(ae, S_IFREG);
assert(0 == strcmp(archive_entry_pathname(ae), name));
assertEqualString(archive_entry_pathname(ae), name);
/* Try writing with different buffer sizes. */
/* Make sure that we get failure on too-small buffers, success on