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:
parent
8d573cc158
commit
b48b40f1f8
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
59
lib/libarchive/archive_entry_copy_stat.c
Normal file
59
lib/libarchive/archive_entry_copy_stat.c
Normal 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);
|
||||
}
|
155
lib/libarchive/archive_entry_private.h
Normal file
155
lib/libarchive/archive_entry_private.h
Normal 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 */
|
100
lib/libarchive/archive_entry_stat.c
Normal file
100
lib/libarchive/archive_entry_stat.c
Normal 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);
|
||||
}
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
@ -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. */
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
312
lib/libarchive/archive_read_support_compression_program.c
Normal file
312
lib/libarchive/archive_read_support_compression_program.c
Normal 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);
|
||||
}
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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 )
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 @@ success:
|
||||
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, ×) != 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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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 @@ cleanup:
|
||||
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
|
||||
/* Close the output */
|
||||
if (a->client_closer != NULL)
|
||||
(a->client_closer)(&a->archive, a->client_data);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
@ -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 @@ cleanup:
|
||||
}
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
|
||||
/* Close the output */
|
||||
if (a->client_closer != NULL)
|
||||
(a->client_closer)(&a->archive, a->client_data);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
322
lib/libarchive/archive_write_set_compression_program.c
Normal file
322
lib/libarchive/archive_write_set_compression_program.c
Normal 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);
|
||||
}
|
@ -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 @@ size:
|
||||
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 @@ size:
|
||||
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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
137
lib/libarchive/filter_fork.c
Normal file
137
lib/libarchive/filter_fork.c
Normal 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
|
||||
}
|
37
lib/libarchive/filter_fork.h
Normal file
37
lib/libarchive/filter_fork.h
Normal 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
|
376
lib/libarchive/libarchive_internals.3
Normal file
376
lib/libarchive/libarchive_internals.3
Normal 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
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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. */
|
||||
|
@ -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());
|
||||
}
|
||||
|
576
lib/libarchive/test/test_entry.c
Normal file
576
lib/libarchive/test/test_entry.c
Normal 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);
|
||||
}
|
55
lib/libarchive/test/test_read_compress_program.c
Normal file
55
lib/libarchive/test/test_read_compress_program.c
Normal 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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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. */
|
||||
|
97
lib/libarchive/test/test_write_compress_program.c
Normal file
97
lib/libarchive/test/test_write_compress_program.c
Normal 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
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user