aboutsummaryrefslogtreecommitdiff
path: root/lib/libarchive
diff options
context:
space:
mode:
authorTim Kientzle <kientzle@FreeBSD.org>2008-05-21 04:12:29 +0000
committerTim Kientzle <kientzle@FreeBSD.org>2008-05-21 04:12:29 +0000
commita61833cfec298de0eb805d57aedab17d5e3aeba1 (patch)
treef76dbee78173a8e25258bff8d8366a1b69c260ec /lib/libarchive
parent09193177fbd709ff32636fc0e02f1fbe337c6cf0 (diff)
Notes
Diffstat (limited to 'lib/libarchive')
-rw-r--r--lib/libarchive/archive_entry.c42
-rw-r--r--lib/libarchive/archive_entry.h68
-rw-r--r--lib/libarchive/archive_platform.h5
-rw-r--r--lib/libarchive/archive_read_support_format_mtree.c280
-rw-r--r--lib/libarchive/archive_read_support_format_tar.c451
-rw-r--r--lib/libarchive/archive_util.c46
-rw-r--r--lib/libarchive/archive_write_private.h7
-rw-r--r--lib/libarchive/archive_write_set_format_pax.c231
8 files changed, 716 insertions, 414 deletions
diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c
index 34c91162fc20..8da0215197c1 100644
--- a/lib/libarchive/archive_entry.c
+++ b/lib/libarchive/archive_entry.c
@@ -207,6 +207,8 @@ aes_get_mbs(struct aes *aes)
static const wchar_t *
aes_get_wcs(struct aes *aes)
{
+ int r;
+
if (aes->aes_wcs == NULL && aes->aes_mbs == NULL)
return NULL;
if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) {
@@ -221,8 +223,13 @@ aes_get_wcs(struct aes *aes)
aes->aes_wcs = aes->aes_wcs_alloc;
if (aes->aes_wcs == NULL)
__archive_errx(1, "No memory for aes_get_wcs()");
- mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
+ r = mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
aes->aes_wcs_alloc[wcs_length] = 0;
+ if (r == -1) {
+ /* Conversion failed, don't lie to our clients. */
+ free(aes->aes_wcs_alloc);
+ aes->aes_wcs = aes->aes_wcs_alloc = NULL;
+ }
}
return (aes->aes_wcs);
}
@@ -307,6 +314,8 @@ aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
struct archive_entry *
archive_entry_clear(struct archive_entry *entry)
{
+ if (entry == NULL)
+ return (NULL);
aes_clean(&entry->ae_fflags_text);
aes_clean(&entry->ae_gname);
aes_clean(&entry->ae_hardlink);
@@ -752,6 +761,28 @@ archive_entry_set_link(struct archive_entry *entry, const char *target)
aes_set_mbs(&entry->ae_hardlink, target);
}
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link(struct archive_entry *entry, const char *target)
+{
+ if (entry->ae_symlink.aes_mbs != NULL ||
+ entry->ae_symlink.aes_wcs != NULL)
+ aes_copy_mbs(&entry->ae_symlink, target);
+ else
+ aes_copy_mbs(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
+{
+ if (entry->ae_symlink.aes_mbs != NULL ||
+ entry->ae_symlink.aes_wcs != NULL)
+ aes_copy_wcs(&entry->ae_symlink, target);
+ else
+ aes_copy_wcs(&entry->ae_hardlink, target);
+}
+
void
archive_entry_set_mode(struct archive_entry *entry, mode_t m)
{
@@ -1124,6 +1155,11 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
entry->acl_p = entry->acl_p->next;
if (entry->acl_p == NULL) {
entry->acl_state = 0;
+ *type = 0;
+ *permset = 0;
+ *tag = 0;
+ *id = -1;
+ *name = NULL;
return (ARCHIVE_EOF); /* End of ACL entries. */
}
*type = entry->acl_p->type;
@@ -1143,7 +1179,7 @@ const wchar_t *
archive_entry_acl_text_w(struct archive_entry *entry, int flags)
{
int count;
- int length;
+ size_t length;
const wchar_t *wname;
const wchar_t *prefix;
wchar_t separator;
@@ -1505,7 +1541,7 @@ archive_entry_xattr_next(struct archive_entry * entry,
return (ARCHIVE_OK);
} else {
*name = NULL;
- *name = NULL;
+ *value = NULL;
*size = (size_t)0;
return (ARCHIVE_WARN);
}
diff --git a/lib/libarchive/archive_entry.h b/lib/libarchive/archive_entry.h
index 173fbc30474e..8c61b9553b63 100644
--- a/lib/libarchive/archive_entry.h
+++ b/lib/libarchive/archive_entry.h
@@ -57,7 +57,25 @@ extern "C" {
struct archive_entry;
/*
- * File-type constants. These are returned from archive_entry_filetype().
+ * File-type constants. These are returned from archive_entry_filetype()
+ * and passed to archive_entry_set_filetype().
+ *
+ * These values match S_XXX defines on every platform I've checked,
+ * including Windows, AIX, Linux, Solaris, and BSD. They're
+ * (re)defined here because platforms generally don't define the ones
+ * they don't support. For example, Windows doesn't define S_IFLNK or
+ * S_IFBLK. Instead of having a mass of conditional logic and system
+ * checks to define any S_XXX values that aren't supported locally,
+ * I've just defined a new set of such constants so that
+ * libarchive-based applications can manipulate and identify archive
+ * entries properly even if the hosting platform can't store them on
+ * disk.
+ *
+ * These values are also used directly within some portable formats,
+ * such as cpio. If you find a platform that varies from these, the
+ * correct solution is to leave these alone and translate from these
+ * portable values to platform-native values when entries are read from
+ * or written to disk.
*/
#define AE_IFMT 0170000
#define AE_IFREG 0100000
@@ -91,7 +109,8 @@ 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);
+ unsigned long * /* set */,
+ unsigned long * /* clear */);
const char *archive_entry_fflags_text(struct archive_entry *);
gid_t archive_entry_gid(struct archive_entry *);
const char *archive_entry_gname(struct archive_entry *);
@@ -130,7 +149,7 @@ 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);
+ unsigned long /* set */, unsigned long /* clear */);
/* Returns pointer to start of first invalid token, or NULL if none. */
/* Note that all recognized tokens are processed, regardless. */
const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
@@ -144,6 +163,8 @@ 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_copy_link(struct archive_entry *, const char *);
+void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
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);
@@ -182,6 +203,13 @@ void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
* = there are many different ACL text formats
* = would like to be able to read/convert archives containing ACLs
* on platforms that lack ACL libraries
+ *
+ * This last point, in particular, forces me to implement a reasonably
+ * complete set of ACL support routines.
+ *
+ * TODO: Extend this to support NFSv4/NTFS permissions. That should
+ * allow full ACL support on Mac OS, in particular, which uses
+ * POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions.
*/
/*
@@ -216,21 +244,24 @@ void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
*/
void archive_entry_acl_clear(struct archive_entry *);
void archive_entry_acl_add_entry(struct archive_entry *,
- int type, int permset, int tag, int qual, const char *name);
+ int /* type */, int /* permset */, int /* tag */,
+ int /* qual */, const char * /* name */);
void archive_entry_acl_add_entry_w(struct archive_entry *,
- int type, int permset, int tag, int qual, const wchar_t *name);
+ int /* type */, int /* permset */, int /* tag */,
+ int /* qual */, const wchar_t * /* name */);
/*
* To retrieve the ACL, first "reset", then repeatedly ask for the
* "next" entry. The want_type parameter allows you to request only
* access entries or only default entries.
*/
-int archive_entry_acl_reset(struct archive_entry *, int want_type);
-int archive_entry_acl_next(struct archive_entry *, int want_type,
- int *type, int *permset, int *tag, int *qual, const char **name);
-int archive_entry_acl_next_w(struct archive_entry *, int want_type,
- int *type, int *permset, int *tag, int *qual,
- const wchar_t **name);
+int archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
+int archive_entry_acl_next(struct archive_entry *, int /* want_type */,
+ int * /* type */, int * /* permset */, int * /* tag */,
+ int * /* qual */, const char ** /* name */);
+int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
+ int * /* type */, int * /* permset */, int * /* tag */,
+ int * /* qual */, const wchar_t ** /* name */);
/*
* Construct a text-format ACL. The flags argument is a bitmask that
@@ -245,10 +276,11 @@ int archive_entry_acl_next_w(struct archive_entry *, int want_type,
*/
#define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024
#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048
-const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int flags);
+const wchar_t *archive_entry_acl_text_w(struct archive_entry *,
+ int /* flags */);
/* Return a count of entries matching 'want_type' */
-int archive_entry_acl_count(struct archive_entry *, int want_type);
+int archive_entry_acl_count(struct archive_entry *, int /* want_type */);
/*
* Private ACL parser. This is private because it handles some
@@ -259,9 +291,12 @@ int archive_entry_acl_count(struct archive_entry *, int want_type);
* this interface are likely to be surprised when it changes.
*
* You were warned!
+ *
+ * TODO: Move this declaration out of the public header and into
+ * a private header. Warnings above are silly.
*/
int __archive_entry_acl_parse_w(struct archive_entry *,
- const wchar_t *, int type);
+ const wchar_t *, int /* type */);
/*
* extended attributes
@@ -269,7 +304,8 @@ int __archive_entry_acl_parse_w(struct archive_entry *,
void archive_entry_xattr_clear(struct archive_entry *);
void archive_entry_xattr_add_entry(struct archive_entry *,
- const char *name, const void *value, size_t size);
+ const char * /* name */, const void * /* value */,
+ size_t /* size */);
/*
* To retrieve the xattr list, first "reset", then repeatedly ask for the
@@ -279,7 +315,7 @@ void archive_entry_xattr_add_entry(struct archive_entry *,
int archive_entry_xattr_count(struct archive_entry *);
int archive_entry_xattr_reset(struct archive_entry *);
int archive_entry_xattr_next(struct archive_entry *,
- const char **name, const void **value, size_t *);
+ const char ** /* name */, const void ** /* value */, size_t *);
/*
* Utility to detect hardlinks.
diff --git a/lib/libarchive/archive_platform.h b/lib/libarchive/archive_platform.h
index 3f232803a6f1..035d27ccd30e 100644
--- a/lib/libarchive/archive_platform.h
+++ b/lib/libarchive/archive_platform.h
@@ -36,7 +36,10 @@
#ifndef ARCHIVE_PLATFORM_H_INCLUDED
#define ARCHIVE_PLATFORM_H_INCLUDED
-#if defined(PLATFORM_CONFIG_H)
+#ifdef _WIN32
+#include "config_windows.h"
+#include "archive_windows.h"
+#elif defined(PLATFORM_CONFIG_H)
/* Use hand-built config.h in environments that need it. */
#include PLATFORM_CONFIG_H
#elif defined(HAVE_CONFIG_H)
diff --git a/lib/libarchive/archive_read_support_format_mtree.c b/lib/libarchive/archive_read_support_format_mtree.c
index d7afb8c13c4a..fc806a51fc4b 100644
--- a/lib/libarchive/archive_read_support_format_mtree.c
+++ b/lib/libarchive/archive_read_support_format_mtree.c
@@ -76,12 +76,18 @@ struct mtree {
struct mtree_entry *this_entry;
struct archive_string current_dir;
struct archive_string contents_name;
+
+ off_t cur_size, cur_offset;
};
static int cleanup(struct archive_read *);
static int mtree_bid(struct archive_read *);
+static int parse_file(struct archive_read *, struct archive_entry *,
+ struct mtree *, struct mtree_entry *);
static void parse_escapes(char *, struct mtree_entry *);
-static int parse_setting(struct archive_read *, struct mtree *,
+static int parse_line(struct archive_read *, struct archive_entry *,
+ struct mtree *, struct mtree_entry *);
+static int parse_keyword(struct archive_read *, struct mtree *,
struct archive_entry *, char *, char *);
static int read_data(struct archive_read *a,
const void **buff, size_t *size, off_t *offset);
@@ -252,14 +258,16 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
}
}
+/*
+ * Read in the entire mtree file into memory on the first request.
+ * Then use the next unused file to satisfy each header request.
+ */
static int
read_header(struct archive_read *a, struct archive_entry *entry)
{
- struct stat st;
struct mtree *mtree;
- struct mtree_entry *mentry, *mentry2;
- char *p, *q;
- int r = ARCHIVE_OK, r1;
+ char *p;
+ int r;
mtree = (struct mtree *)(a->format->data);
@@ -278,16 +286,10 @@ read_header(struct archive_read *a, struct archive_entry *entry)
a->archive.archive_format_name = mtree->archive_format_name;
for (;;) {
- mentry = mtree->this_entry;
- if (mentry == NULL) {
- mtree->this_entry = NULL;
+ if (mtree->this_entry == NULL)
return (ARCHIVE_EOF);
- }
- mtree->this_entry = mentry->next;
- if (mentry->used)
- continue;
- mentry->used = 1;
- if (strcmp(mentry->name, "..") == 0) {
+ if (strcmp(mtree->this_entry->name, "..") == 0) {
+ mtree->this_entry->used = 1;
if (archive_strlen(&mtree->current_dir) > 0) {
/* Roll back current path. */
p = mtree->current_dir.s
@@ -299,117 +301,165 @@ read_header(struct archive_read *a, struct archive_entry *entry)
mtree->current_dir.length
= p - mtree->current_dir.s + 1;
}
- continue;
}
+ if (!mtree->this_entry->used) {
+ r = parse_file(a, entry, mtree, mtree->this_entry);
+ return (r);
+ }
+ mtree->this_entry = mtree->this_entry->next;
+ }
+}
+
+/*
+ * A single file can have multiple lines contribute specifications.
+ * Parse as many lines as necessary, then pull additional information
+ * from a backing file on disk as necessary.
+ */
+static int
+parse_file(struct archive_read *a, struct archive_entry *entry,
+ struct mtree *mtree, struct mtree_entry *mentry)
+{
+ struct stat st;
+ struct mtree_entry *mp;
+ int r = ARCHIVE_OK, r1;
- mtree->filetype = AE_IFREG;
+ mentry->used = 1;
- /* Parse options. */
- p = mentry->option_start;
- while (p < mentry->option_end) {
- q = p + strlen(p);
- r1 = parse_setting(a, mtree, entry, p, q);
- if (r1 != ARCHIVE_OK)
- r = r1;
- p = q + 1;
- }
+ /* Initialize reasonable defaults. */
+ mtree->filetype = AE_IFREG;
+ archive_entry_set_size(entry, 0);
- if (mentry->full) {
- archive_entry_copy_pathname(entry, mentry->name);
- /*
- * "Full" entries are allowed to have multiple
- * lines and those lines aren't required to be
- * adjacent. We don't support multiple lines
- * for "relative" entries nor do we make any
- * attempt to merge data from separate
- * "relative" and "full" entries. (Merging
- * "relative" and "full" entries would require
- * dealing with pathname canonicalization,
- * which is a very tricky subject.)
- */
- mentry2 = mentry->next;
- while (mentry2 != NULL) {
- if (mentry2->full
- && !mentry2->used
- && strcmp(mentry->name, mentry2->name) == 0) {
- /*
- * Add those options as well;
- * later lines override
- * earlier ones.
- */
- p = mentry2->option_start;
- while (p < mentry2->option_end) {
- q = p + strlen(p);
- r1 = parse_setting(a, mtree, entry, p, q);
- if (r1 != ARCHIVE_OK)
- r = r1;
- p = q + 1;
- }
- mentry2->used = 1;
- }
- mentry2 = mentry2->next;
- }
- } else {
- /*
- * Relative entries require us to construct
- * the full path and possibly update the
- * current directory.
- */
- size_t n = archive_strlen(&mtree->current_dir);
- if (n > 0)
- archive_strcat(&mtree->current_dir, "/");
- archive_strcat(&mtree->current_dir, mentry->name);
- archive_entry_copy_pathname(entry, mtree->current_dir.s);
- if (archive_entry_filetype(entry) != AE_IFDIR)
- mtree->current_dir.length = n;
- }
+ /* Parse options from this line. */
+ r = parse_line(a, entry, mtree, mentry);
+ if (mentry->full) {
+ archive_entry_copy_pathname(entry, mentry->name);
/*
- * Try to open and stat the file to get the real size.
- * It would be nice to avoid this here so that getting
- * a listing of an mtree wouldn't require opening
- * every referenced contents file. But then we
- * wouldn't know the actual contents size, so I don't
- * see a really viable way around this. (Also, we may
- * want to someday pull other unspecified info from
- * the contents file on disk.)
+ * "Full" entries are allowed to have multiple lines
+ * and those lines aren't required to be adjacent. We
+ * don't support multiple lines for "relative" entries
+ * nor do we make any attempt to merge data from
+ * separate "relative" and "full" entries. (Merging
+ * "relative" and "full" entries would require dealing
+ * with pathname canonicalization, which is a very
+ * tricky subject.)
*/
- if (archive_strlen(&mtree->contents_name) > 0) {
- mtree->fd = open(mtree->contents_name.s,
- O_RDONLY | O_BINARY);
- if (mtree->fd < 0) {
- archive_set_error(&a->archive, errno,
- "Can't open content=\"%s\"",
- mtree->contents_name.s);
- r = ARCHIVE_WARN;
+ for (mp = mentry->next; mp != NULL; mp = mp->next) {
+ if (mp->full && !mp->used
+ && strcmp(mentry->name, mp->name) == 0) {
+ /* Later lines override earlier ones. */
+ mp->used = 1;
+ r1 = parse_line(a, entry, mtree, mp);
+ if (r1 < r)
+ r = r1;
}
- } else {
- /* If the specified path opens, use it. */
- mtree->fd = open(mtree->current_dir.s,
- O_RDONLY | O_BINARY);
- /* But don't fail if it's not there. */
}
-
+ } else {
/*
- * If there is a contents file on disk, use that size;
- * otherwise leave it as-is (it might have been set from
- * the mtree size= keyword).
+ * Relative entries require us to construct
+ * the full path and possibly update the
+ * current directory.
*/
- if (mtree->fd >= 0) {
- fstat(mtree->fd, &st);
+ size_t n = archive_strlen(&mtree->current_dir);
+ if (n > 0)
+ archive_strcat(&mtree->current_dir, "/");
+ archive_strcat(&mtree->current_dir, mentry->name);
+ archive_entry_copy_pathname(entry, mtree->current_dir.s);
+ if (archive_entry_filetype(entry) != AE_IFDIR)
+ mtree->current_dir.length = n;
+ }
+
+ /*
+ * Try to open and stat the file to get the real size
+ * and other file info. It would be nice to avoid
+ * this here so that getting a listing of an mtree
+ * wouldn't require opening every referenced contents
+ * file. But then we wouldn't know the actual
+ * contents size, so I don't see a really viable way
+ * around this. (Also, we may want to someday pull
+ * other unspecified info from the contents file on
+ * disk.)
+ */
+ mtree->fd = -1;
+ if (archive_strlen(&mtree->contents_name) > 0) {
+ mtree->fd = open(mtree->contents_name.s,
+ O_RDONLY | O_BINARY);
+ if (mtree->fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't open content=\"%s\"",
+ mtree->contents_name.s);
+ r = ARCHIVE_WARN;
+ }
+ } else if (archive_entry_filetype(entry) == AE_IFREG) {
+ mtree->fd = open(archive_entry_pathname(entry),
+ O_RDONLY | O_BINARY);
+ }
+
+ /*
+ * If there is a contents file on disk, use that size;
+ * otherwise leave it as-is (it might have been set from
+ * the mtree size= keyword).
+ */
+ if (mtree->fd >= 0) {
+ if (fstat(mtree->fd, &st) != 0) {
+ archive_set_error(&a->archive, errno,
+ "could not stat %s",
+ archive_entry_pathname(entry));
+ r = ARCHIVE_WARN;
+ /* If we can't stat it, don't keep it open. */
+ close(mtree->fd);
+ mtree->fd = -1;
+ } else if ((st.st_mode & S_IFMT) != S_IFREG) {
+ archive_set_error(&a->archive, errno,
+ "%s is not a regular file",
+ archive_entry_pathname(entry));
+ r = ARCHIVE_WARN;
+ /* Don't hold a non-regular file open. */
+ close(mtree->fd);
+ mtree->fd = -1;
+ } else {
archive_entry_set_size(entry, st.st_size);
+ archive_entry_set_ino(entry, st.st_ino);
+ archive_entry_set_dev(entry, st.st_dev);
+ archive_entry_set_nlink(entry, st.st_nlink);
}
+ }
+ mtree->cur_size = archive_entry_size(entry);
+ mtree->offset = 0;
- return r;
+ return r;
+}
+
+/*
+ * Each line contains a sequence of keywords.
+ */
+static int
+parse_line(struct archive_read *a, struct archive_entry *entry,
+ struct mtree *mtree, struct mtree_entry *mp)
+{
+ char *p, *q;
+ int r = ARCHIVE_OK, r1;
+
+ p = mp->option_start;
+ while (p < mp->option_end) {
+ q = p + strlen(p);
+ r1 = parse_keyword(a, mtree, entry, p, q);
+ if (r1 < r)
+ r = r1;
+ p = q + 1;
}
+ return (r);
}
+/*
+ * Parse a single keyword and its value.
+ */
static int
-parse_setting(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, char *key, char *end)
+parse_keyword(struct archive_read *a, struct mtree *mtree,
+ struct archive_entry *entry, char *key, char *end)
{
char *val;
-
if (end == key)
return (ARCHIVE_OK);
if (*key == '\0')
@@ -427,7 +477,8 @@ parse_setting(struct archive_read *a, struct mtree *mtree, struct archive_entry
switch (key[0]) {
case 'c':
- if (strcmp(key, "content") == 0) {
+ if (strcmp(key, "content") == 0
+ || strcmp(key, "contents") == 0) {
parse_escapes(val, NULL);
archive_strcpy(&mtree->contents_name, val);
break;
@@ -441,6 +492,11 @@ parse_setting(struct archive_read *a, struct mtree *mtree, struct archive_entry
archive_entry_copy_gname(entry, val);
break;
}
+ case 'l':
+ if (strcmp(key, "link") == 0) {
+ archive_entry_set_link(entry, val);
+ break;
+ }
case 'm':
if (strcmp(key, "mode") == 0) {
if (val[0] == '0') {
@@ -452,6 +508,11 @@ parse_setting(struct archive_read *a, struct mtree *mtree, struct archive_entry
"Symbolic mode \"%s\" unsupported", val);
break;
}
+ case 's':
+ if (strcmp(key, "size") == 0) {
+ archive_entry_set_size(entry, mtree_atol10(&val));
+ break;
+ }
case 't':
if (strcmp(key, "type") == 0) {
switch (val[0]) {
@@ -517,6 +578,7 @@ parse_setting(struct archive_read *a, struct mtree *mtree, struct archive_entry
static int
read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset)
{
+ size_t bytes_to_read;
ssize_t bytes_read;
struct mtree *mtree;
@@ -538,7 +600,11 @@ read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset
*buff = mtree->buff;
*offset = mtree->offset;
- bytes_read = read(mtree->fd, mtree->buff, mtree->buffsize);
+ if ((off_t)mtree->buffsize > mtree->cur_size - mtree->offset)
+ bytes_to_read = mtree->cur_size - mtree->offset;
+ else
+ bytes_to_read = mtree->buffsize;
+ bytes_read = read(mtree->fd, mtree->buff, bytes_to_read);
if (bytes_read < 0) {
archive_set_error(&a->archive, errno, "Can't read");
return (ARCHIVE_WARN);
@@ -548,7 +614,7 @@ read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset
return (ARCHIVE_EOF);
}
mtree->offset += bytes_read;
- *size = (size_t)bytes_read;
+ *size = bytes_read;
return (ARCHIVE_OK);
}
diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c
index 9cfe0e762aa4..a0c434244830 100644
--- a/lib/libarchive/archive_read_support_format_tar.c
+++ b/lib/libarchive/archive_read_support_format_tar.c
@@ -144,8 +144,8 @@ struct sparse_block {
struct tar {
struct archive_string acl_text;
- struct archive_string entry_name;
- struct archive_string entry_linkname;
+ struct archive_string entry_pathname;
+ struct archive_string entry_linkpath;
struct archive_string entry_uname;
struct archive_string entry_gname;
struct archive_string longlink;
@@ -153,6 +153,7 @@ struct tar {
struct archive_string pax_header;
struct archive_string pax_global;
struct archive_string line;
+ int pax_hdrcharset_binary;
wchar_t *pax_entry;
size_t pax_entry_length;
int header_recursion_depth;
@@ -169,9 +170,9 @@ struct tar {
char sparse_gnu_pending;
};
-static size_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
+static ssize_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
static int archive_block_is_null(const unsigned char *p);
-static char *base64_decode(const wchar_t *, size_t, size_t *);
+static char *base64_decode(const char *, size_t, size_t *);
static void gnu_add_sparse_entry(struct tar *,
off_t offset, off_t remaining);
static void gnu_clear_sparse_list(struct tar *);
@@ -179,7 +180,7 @@ static int gnu_sparse_old_read(struct archive_read *, struct tar *,
const struct archive_entry_header_gnutar *header);
static void gnu_sparse_old_parse(struct tar *,
const struct gnu_sparse *sparse, int length);
-static int gnu_sparse_01_parse(struct tar *, const wchar_t *);
+static int gnu_sparse_01_parse(struct tar *, const char *);
static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *);
static int header_Solaris_ACL(struct archive_read *, struct tar *,
struct archive_entry *, const void *);
@@ -210,24 +211,23 @@ 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 tar *, struct archive_entry *,
- wchar_t *key, wchar_t *value);
+ char *key, char *value);
static int pax_header(struct archive_read *, struct tar *,
struct archive_entry *, char *attr);
-static void pax_time(const wchar_t *, int64_t *sec, long *nanos);
+static void pax_time(const char *, int64_t *sec, long *nanos);
static ssize_t readline(struct archive_read *, struct tar *, const char **,
ssize_t limit);
static int read_body_to_string(struct archive_read *, struct tar *,
struct archive_string *, const void *h);
static int64_t tar_atol(const char *, unsigned);
-static int64_t tar_atol10(const wchar_t *, unsigned);
+static int64_t tar_atol10(const char *, 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 *);
static int tohex(int c);
static char *url_decode(const char *);
-static int utf8_decode(wchar_t *, const char *, size_t length);
-static char *wide_to_narrow(const wchar_t *wval);
+static wchar_t *utf8_decode(struct tar *, const char *, size_t length);
int
archive_read_support_format_gnutar(struct archive *a)
@@ -271,8 +271,8 @@ archive_read_format_tar_cleanup(struct archive_read *a)
tar = (struct tar *)(a->format->data);
gnu_clear_sparse_list(tar);
archive_string_free(&tar->acl_text);
- archive_string_free(&tar->entry_name);
- archive_string_free(&tar->entry_linkname);
+ archive_string_free(&tar->entry_pathname);
+ archive_string_free(&tar->entry_linkpath);
archive_string_free(&tar->entry_uname);
archive_string_free(&tar->entry_gname);
archive_string_free(&tar->line);
@@ -714,7 +714,7 @@ archive_block_is_null(const unsigned char *p)
{
unsigned i;
- for (i = 0; i < ARCHIVE_BYTES_PER_RECORD / sizeof(*p); i++)
+ for (i = 0; i < 512; i++)
if (*p++)
return (0);
return (1);
@@ -766,16 +766,9 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar,
while (*p != '\0' && p < acl + size)
p++;
- wp = (wchar_t *)malloc((p - acl + 1) * sizeof(wchar_t));
- if (wp == NULL) {
- archive_set_error(&a->archive, ENOMEM,
- "Can't allocate work buffer for ACL parsing");
- return (ARCHIVE_FATAL);
- }
- utf8_decode(wp, acl, p - acl);
+ wp = utf8_decode(tar, acl, p - acl);
err = __archive_entry_acl_parse_w(entry, wp,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
- free(wp);
return (err);
}
@@ -795,7 +788,7 @@ header_longlink(struct archive_read *a, struct tar *tar,
if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
return (err);
/* Set symlink if symlink already set, else hardlink. */
- archive_entry_set_link(entry, tar->longlink.s);
+ archive_entry_copy_link(entry, tar->longlink.s);
return (ARCHIVE_OK);
}
@@ -815,7 +808,7 @@ header_longname(struct archive_read *a, struct tar *tar,
err = tar_read_header(a, tar, entry);
if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
return (err);
- archive_entry_set_pathname(entry, tar->longname.s);
+ archive_entry_copy_pathname(entry, tar->longname.s);
return (ARCHIVE_OK);
}
@@ -907,10 +900,10 @@ header_common(struct archive_read *a, struct tar *tar,
header = (const struct archive_entry_header_ustar *)h;
if (header->linkname[0])
- archive_strncpy(&(tar->entry_linkname), header->linkname,
+ archive_strncpy(&(tar->entry_linkpath), header->linkname,
sizeof(header->linkname));
else
- archive_string_empty(&(tar->entry_linkname));
+ archive_string_empty(&(tar->entry_linkpath));
/* Parse out the numeric fields (all are octal) */
archive_entry_set_mode(entry, tar_atol(header->mode, sizeof(header->mode)));
@@ -926,7 +919,7 @@ header_common(struct archive_read *a, struct tar *tar,
switch (tartype) {
case '1': /* Hard link */
- archive_entry_set_hardlink(entry, tar->entry_linkname.s);
+ archive_entry_copy_hardlink(entry, tar->entry_linkpath.s);
/*
* The following may seem odd, but: Technically, tar
* does not store the file type for a "hard link"
@@ -988,7 +981,7 @@ header_common(struct archive_read *a, struct tar *tar,
archive_entry_set_filetype(entry, AE_IFLNK);
archive_entry_set_size(entry, 0);
tar->entry_bytes_remaining = 0;
- archive_entry_set_symlink(entry, tar->entry_linkname.s);
+ archive_entry_copy_symlink(entry, tar->entry_linkpath.s);
break;
case '3': /* Character device */
archive_entry_set_filetype(entry, AE_IFCHR);
@@ -1060,8 +1053,8 @@ header_old_tar(struct archive_read *a, struct tar *tar,
/* Copy filename over (to ensure null termination). */
header = (const struct archive_entry_header_ustar *)h;
- archive_strncpy(&(tar->entry_name), header->name, sizeof(header->name));
- archive_entry_set_pathname(entry, tar->entry_name.s);
+ archive_strncpy(&(tar->entry_pathname), header->name, sizeof(header->name));
+ archive_entry_copy_pathname(entry, tar->entry_pathname.s);
/* Grab rest of common fields */
header_common(a, tar, entry, h);
@@ -1132,7 +1125,7 @@ header_ustar(struct archive_read *a, struct tar *tar,
header = (const struct archive_entry_header_ustar *)h;
/* Copy name into an internal buffer to ensure null-termination. */
- as = &(tar->entry_name);
+ as = &(tar->entry_pathname);
if (header->prefix[0]) {
archive_strncpy(as, header->prefix, sizeof(header->prefix));
if (as->s[archive_strlen(as) - 1] != '/')
@@ -1141,7 +1134,7 @@ header_ustar(struct archive_read *a, struct tar *tar,
} else
archive_strncpy(as, header->name, sizeof(header->name));
- archive_entry_set_pathname(entry, as->s);
+ archive_entry_copy_pathname(entry, as->s);
/* Handle rest of common fields. */
header_common(a, tar, entry, h);
@@ -1149,11 +1142,11 @@ header_ustar(struct archive_read *a, struct tar *tar,
/* Handle POSIX ustar fields. */
archive_strncpy(&(tar->entry_uname), header->uname,
sizeof(header->uname));
- archive_entry_set_uname(entry, tar->entry_uname.s);
+ archive_entry_copy_uname(entry, tar->entry_uname.s);
archive_strncpy(&(tar->entry_gname), header->gname,
sizeof(header->gname));
- archive_entry_set_gname(entry, tar->entry_gname.s);
+ archive_entry_copy_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') {
@@ -1180,10 +1173,16 @@ pax_header(struct archive_read *a, struct tar *tar,
{
size_t attr_length, l, line_length;
char *line, *p;
- wchar_t *key, *wp, *value;
+ char *key, *value;
+ wchar_t *wp;
int err, err2;
attr_length = strlen(attr);
+ tar->pax_hdrcharset_binary = 0;
+ archive_string_empty(&(tar->entry_gname));
+ archive_string_empty(&(tar->entry_linkpath));
+ archive_string_empty(&(tar->entry_pathname));
+ archive_string_empty(&(tar->entry_uname));
err = ARCHIVE_OK;
while (attr_length > 0) {
/* Parse decimal length field at start of line. */
@@ -1226,49 +1225,24 @@ pax_header(struct archive_read *a, struct tar *tar,
return (ARCHIVE_WARN);
}
- /* Ensure pax_entry buffer is big enough. */
- if (tar->pax_entry_length <= line_length) {
- wchar_t *old_entry = tar->pax_entry;
-
- if (tar->pax_entry_length <= 0)
- tar->pax_entry_length = 1024;
- while (tar->pax_entry_length <= line_length + 1)
- tar->pax_entry_length *= 2;
-
- old_entry = tar->pax_entry;
- tar->pax_entry = (wchar_t *)realloc(tar->pax_entry,
- tar->pax_entry_length * sizeof(wchar_t));
- if (tar->pax_entry == NULL) {
- free(old_entry);
- archive_set_error(&a->archive, ENOMEM,
- "No memory");
- return (ARCHIVE_FATAL);
- }
- }
-
- /* Decode UTF-8 to wchar_t, null-terminate result. */
- if (utf8_decode(tar->pax_entry, p,
- line_length - (p - attr) - 1)) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Invalid UTF8 character in pax extended attribute");
- err = err_combine(err, ARCHIVE_WARN);
- }
+ /* Null-terminate the line. */
+ attr[line_length - 1] = '\0';
- /* Null-terminate 'key' value. */
- wp = key = tar->pax_entry;
- if (key[0] == L'=')
+ /* Find end of key and null terminate it. */
+ key = p;
+ if (key[0] == '=')
return (-1);
- while (*wp && *wp != L'=')
- ++wp;
- if (*wp == L'\0') {
+ while (*p && *p != '=')
+ ++p;
+ if (*p == '\0') {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Invalid pax extended attributes");
return (ARCHIVE_WARN);
}
- *wp = 0;
+ *p = '\0';
/* Identify null-terminated 'value' portion. */
- value = wp + 1;
+ value = p + 1;
/* Identify this attribute and set it in the entry. */
err2 = pax_attribute(tar, entry, key, value);
@@ -1278,33 +1252,85 @@ pax_header(struct archive_read *a, struct tar *tar,
attr += line_length;
attr_length -= line_length;
}
+ if (archive_strlen(&(tar->entry_gname)) > 0) {
+ value = tar->entry_gname.s;
+ if (tar->pax_hdrcharset_binary)
+ archive_entry_copy_gname(entry, value);
+ else {
+ wp = utf8_decode(tar, value, strlen(value));
+ if (wp == NULL) {
+ archive_entry_copy_gname(entry, value);
+ if (err > ARCHIVE_WARN)
+ err = ARCHIVE_WARN;
+ } else
+ archive_entry_copy_gname_w(entry, wp);
+ }
+ }
+ if (archive_strlen(&(tar->entry_linkpath)) > 0) {
+ value = tar->entry_linkpath.s;
+ if (tar->pax_hdrcharset_binary)
+ archive_entry_copy_link(entry, value);
+ else {
+ wp = utf8_decode(tar, value, strlen(value));
+ if (wp == NULL) {
+ archive_entry_copy_link(entry, value);
+ if (err > ARCHIVE_WARN)
+ err = ARCHIVE_WARN;
+ } else
+ archive_entry_copy_link_w(entry, wp);
+ }
+ }
+ if (archive_strlen(&(tar->entry_pathname)) > 0) {
+ value = tar->entry_pathname.s;
+ if (tar->pax_hdrcharset_binary)
+ archive_entry_copy_pathname(entry, value);
+ else {
+ wp = utf8_decode(tar, value, strlen(value));
+ if (wp == NULL) {
+ archive_entry_copy_pathname(entry, value);
+ if (err > ARCHIVE_WARN)
+ err = ARCHIVE_WARN;
+ } else
+ archive_entry_copy_pathname_w(entry, wp);
+ }
+ }
+ if (archive_strlen(&(tar->entry_uname)) > 0) {
+ value = tar->entry_uname.s;
+ if (tar->pax_hdrcharset_binary)
+ archive_entry_copy_uname(entry, value);
+ else {
+ wp = utf8_decode(tar, value, strlen(value));
+ if (wp == NULL) {
+ archive_entry_copy_uname(entry, value);
+ if (err > ARCHIVE_WARN)
+ err = ARCHIVE_WARN;
+ } else
+ archive_entry_copy_uname_w(entry, wp);
+ }
+ }
return (err);
}
static int
pax_attribute_xattr(struct archive_entry *entry,
- wchar_t *name, wchar_t *value)
+ char *name, char *value)
{
- char *name_decoded, *name_narrow;
+ char *name_decoded;
void *value_decoded;
size_t value_len;
- if (wcslen(name) < 18 || (wcsncmp(name, L"LIBARCHIVE.xattr.", 17)) != 0)
+ if (strlen(name) < 18 || (strncmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
return 3;
name += 17;
/* URL-decode name */
- name_narrow = wide_to_narrow(name);
- if (name_narrow == NULL)
- return 2;
- name_decoded = url_decode(name_narrow);
- free(name_narrow);
+ name_decoded = url_decode(name);
if (name_decoded == NULL)
return 2;
/* Base-64 decode value */
- value_decoded = base64_decode(value, wcslen(value), &value_len);
+ value_decoded = base64_decode(value, strlen(value), &value_len);
if (value_decoded == NULL) {
free(name_decoded);
return 1;
@@ -1333,22 +1359,23 @@ pax_attribute_xattr(struct archive_entry *entry,
*/
static int
pax_attribute(struct tar *tar, struct archive_entry *entry,
- wchar_t *key, wchar_t *value)
+ char *key, char *value)
{
int64_t s;
long n;
+ wchar_t *wp;
switch (key[0]) {
case 'G':
/* GNU "0.0" sparse pax format. */
- if (wcscmp(key, L"GNU.sparse.numblocks") == 0) {
+ if (strcmp(key, "GNU.sparse.numblocks") == 0) {
tar->sparse_offset = -1;
tar->sparse_numbytes = -1;
tar->sparse_gnu_major = 0;
tar->sparse_gnu_minor = 0;
}
- if (wcscmp(key, L"GNU.sparse.offset") == 0) {
- tar->sparse_offset = tar_atol10(value, wcslen(value));
+ if (strcmp(key, "GNU.sparse.offset") == 0) {
+ tar->sparse_offset = tar_atol10(value, strlen(value));
if (tar->sparse_numbytes != -1) {
gnu_add_sparse_entry(tar,
tar->sparse_offset, tar->sparse_numbytes);
@@ -1356,8 +1383,8 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
tar->sparse_numbytes = -1;
}
}
- if (wcscmp(key, L"GNU.sparse.numbytes") == 0) {
- tar->sparse_numbytes = tar_atol10(value, wcslen(value));
+ if (strcmp(key, "GNU.sparse.numbytes") == 0) {
+ tar->sparse_numbytes = tar_atol10(value, strlen(value));
if (tar->sparse_numbytes != -1) {
gnu_add_sparse_entry(tar,
tar->sparse_offset, tar->sparse_numbytes);
@@ -1365,13 +1392,13 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
tar->sparse_numbytes = -1;
}
}
- if (wcscmp(key, L"GNU.sparse.size") == 0) {
- tar->realsize = tar_atol10(value, wcslen(value));
+ if (strcmp(key, "GNU.sparse.size") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
archive_entry_set_size(entry, tar->realsize);
}
/* GNU "0.1" sparse pax format. */
- if (wcscmp(key, L"GNU.sparse.map") == 0) {
+ if (strcmp(key, "GNU.sparse.map") == 0) {
tar->sparse_gnu_major = 0;
tar->sparse_gnu_minor = 1;
if (gnu_sparse_01_parse(tar, value) != ARCHIVE_OK)
@@ -1379,18 +1406,23 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
}
/* GNU "1.0" sparse pax format */
- if (wcscmp(key, L"GNU.sparse.major") == 0) {
- tar->sparse_gnu_major = tar_atol10(value, wcslen(value));
+ if (strcmp(key, "GNU.sparse.major") == 0) {
+ tar->sparse_gnu_major = tar_atol10(value, strlen(value));
tar->sparse_gnu_pending = 1;
}
- if (wcscmp(key, L"GNU.sparse.minor") == 0) {
- tar->sparse_gnu_minor = tar_atol10(value, wcslen(value));
+ if (strcmp(key, "GNU.sparse.minor") == 0) {
+ tar->sparse_gnu_minor = tar_atol10(value, strlen(value));
tar->sparse_gnu_pending = 1;
}
- if (wcscmp(key, L"GNU.sparse.name") == 0)
- archive_entry_copy_pathname_w(entry, value);
- if (wcscmp(key, L"GNU.sparse.realsize") == 0) {
- tar->realsize = tar_atol10(value, wcslen(value));
+ if (strcmp(key, "GNU.sparse.name") == 0) {
+ wp = utf8_decode(tar, value, strlen(value));
+ if (wp != NULL)
+ archive_entry_copy_pathname_w(entry, wp);
+ else
+ archive_entry_copy_pathname(entry, value);
+ }
+ if (strcmp(key, "GNU.sparse.realsize") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
archive_entry_set_size(entry, tar->realsize);
}
break;
@@ -1401,85 +1433,107 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
if (strcmp(key, "LIBARCHIVE.xxxxxxx")==0)
archive_entry_set_xxxxxx(entry, value);
*/
- if (wcsncmp(key, L"LIBARCHIVE.xattr.", 17)==0)
+ if (strncmp(key, "LIBARCHIVE.xattr.", 17)==0)
pax_attribute_xattr(entry, key, value);
break;
case 'S':
/* We support some keys used by the "star" archiver */
- if (wcscmp(key, L"SCHILY.acl.access")==0)
- __archive_entry_acl_parse_w(entry, value,
+ if (strcmp(key, "SCHILY.acl.access")==0) {
+ wp = utf8_decode(tar, value, strlen(value));
+ /* TODO: if (wp == NULL) */
+ __archive_entry_acl_parse_w(entry, wp,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
- else if (wcscmp(key, L"SCHILY.acl.default")==0)
- __archive_entry_acl_parse_w(entry, value,
+ } else if (strcmp(key, "SCHILY.acl.default")==0) {
+ wp = utf8_decode(tar, value, strlen(value));
+ /* TODO: if (wp == NULL) */
+ __archive_entry_acl_parse_w(entry, wp,
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
- else if (wcscmp(key, L"SCHILY.devmajor")==0)
- archive_entry_set_rdevmajor(entry, tar_atol10(value, wcslen(value)));
- else if (wcscmp(key, L"SCHILY.devminor")==0)
- 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)
- archive_entry_set_dev(entry, tar_atol10(value, wcslen(value)));
- else if (wcscmp(key, L"SCHILY.ino")==0)
- archive_entry_set_ino(entry, tar_atol10(value, wcslen(value)));
- else if (wcscmp(key, L"SCHILY.nlink")==0)
- archive_entry_set_nlink(entry, tar_atol10(value, wcslen(value)));
- else if (wcscmp(key, L"SCHILY.realsize")==0) {
- tar->realsize = tar_atol10(value, wcslen(value));
+ } else if (strcmp(key, "SCHILY.devmajor")==0) {
+ archive_entry_set_rdevmajor(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.devminor")==0) {
+ archive_entry_set_rdevminor(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.fflags")==0) {
+ wp = utf8_decode(tar, value, strlen(value));
+ /* TODO: if (wp == NULL) */
+ archive_entry_copy_fflags_text_w(entry, wp);
+ } else if (strcmp(key, "SCHILY.dev")==0) {
+ archive_entry_set_dev(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.ino")==0) {
+ archive_entry_set_ino(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.nlink")==0) {
+ archive_entry_set_nlink(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.realsize")==0) {
+ tar->realsize = tar_atol10(value, strlen(value));
archive_entry_set_size(entry, tar->realsize);
}
break;
case 'a':
- if (wcscmp(key, L"atime")==0) {
+ if (strcmp(key, "atime")==0) {
pax_time(value, &s, &n);
archive_entry_set_atime(entry, s, n);
}
break;
case 'c':
- if (wcscmp(key, L"ctime")==0) {
+ if (strcmp(key, "ctime")==0) {
pax_time(value, &s, &n);
archive_entry_set_ctime(entry, s, n);
- } else if (wcscmp(key, L"charset")==0) {
+ } else if (strcmp(key, "charset")==0) {
/* TODO: Publish charset information in entry. */
- } else if (wcscmp(key, L"comment")==0) {
+ } else if (strcmp(key, "comment")==0) {
/* TODO: Publish comment in entry. */
}
break;
case 'g':
- if (wcscmp(key, L"gid")==0)
- archive_entry_set_gid(entry, tar_atol10(value, wcslen(value)));
- else if (wcscmp(key, L"gname")==0)
- archive_entry_copy_gname_w(entry, value);
+ if (strcmp(key, "gid")==0) {
+ archive_entry_set_gid(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "gname")==0) {
+ archive_strcpy(&(tar->entry_gname), value);
+ }
+ break;
+ case 'h':
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (strcmp(value, "BINARY") == 0)
+ tar->pax_hdrcharset_binary = 1;
+ else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)
+ tar->pax_hdrcharset_binary = 0;
+ else {
+ /* TODO: Warn about unsupported hdrcharset */
+ }
+ }
break;
case 'l':
/* pax interchange doesn't distinguish hardlink vs. symlink. */
- if (wcscmp(key, L"linkpath")==0) {
- if (archive_entry_hardlink(entry))
- archive_entry_copy_hardlink_w(entry, value);
- else
- archive_entry_copy_symlink_w(entry, value);
+ if (strcmp(key, "linkpath")==0) {
+ archive_strcpy(&(tar->entry_linkpath), value);
}
break;
case 'm':
- if (wcscmp(key, L"mtime")==0) {
+ if (strcmp(key, "mtime")==0) {
pax_time(value, &s, &n);
archive_entry_set_mtime(entry, s, n);
}
break;
case 'p':
- if (wcscmp(key, L"path")==0)
- archive_entry_copy_pathname_w(entry, value);
+ if (strcmp(key, "path")==0) {
+ archive_strcpy(&(tar->entry_pathname), value);
+ }
break;
case 'r':
/* POSIX has reserved 'realtime.*' */
break;
case 's':
/* POSIX has reserved 'security.*' */
- /* Someday: if (wcscmp(key, L"security.acl")==0) { ... } */
- if (wcscmp(key, L"size")==0) {
+ /* Someday: if (strcmp(key, "security.acl")==0) { ... } */
+ if (strcmp(key, "size")==0) {
/* "size" is the size of the data in the entry. */
tar->entry_bytes_remaining
- = tar_atol10(value, wcslen(value));
+ = tar_atol10(value, strlen(value));
/*
* But, "size" is not necessarily the size of
* the file on disk; if this is a sparse file,
@@ -1497,10 +1551,12 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
}
break;
case 'u':
- if (wcscmp(key, L"uid")==0)
- archive_entry_set_uid(entry, tar_atol10(value, wcslen(value)));
- else if (wcscmp(key, L"uname")==0)
- archive_entry_copy_uname_w(entry, value);
+ if (strcmp(key, "uid")==0) {
+ archive_entry_set_uid(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "uname")==0) {
+ archive_strcpy(&(tar->entry_uname), value);
+ }
break;
}
return (0);
@@ -1512,7 +1568,7 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
* parse a decimal time value, which may include a fractional portion
*/
static void
-pax_time(const wchar_t *p, int64_t *ps, long *pn)
+pax_time(const char *p, int64_t *ps, long *pn)
{
char digit;
int64_t s;
@@ -1580,9 +1636,9 @@ header_gnutar(struct archive_read *a, struct tar *tar,
/* Copy filename over (to ensure null termination). */
header = (const struct archive_entry_header_gnutar *)h;
- archive_strncpy(&(tar->entry_name), header->name,
+ archive_strncpy(&(tar->entry_pathname), header->name,
sizeof(header->name));
- archive_entry_set_pathname(entry, tar->entry_name.s);
+ archive_entry_copy_pathname(entry, tar->entry_pathname.s);
/* Fields common to ustar and GNU */
/* XXX Can the following be factored out since it's common
@@ -1590,11 +1646,11 @@ header_gnutar(struct archive_read *a, struct tar *tar,
* header_common, perhaps? */
archive_strncpy(&(tar->entry_uname),
header->uname, sizeof(header->uname));
- archive_entry_set_uname(entry, tar->entry_uname.s);
+ archive_entry_copy_uname(entry, tar->entry_uname.s);
archive_strncpy(&(tar->entry_gname),
header->gname, sizeof(header->gname));
- archive_entry_set_gname(entry, tar->entry_gname.s);
+ archive_entry_copy_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') {
@@ -1748,9 +1804,9 @@ gnu_sparse_old_parse(struct tar *tar,
*/
static int
-gnu_sparse_01_parse(struct tar *tar, const wchar_t *p)
+gnu_sparse_01_parse(struct tar *tar, const char *p)
{
- const wchar_t *e;
+ const char *e;
off_t offset = -1, size = -1;
for (;;) {
@@ -1785,12 +1841,11 @@ gnu_sparse_01_parse(struct tar *tar, const wchar_t *p)
* don't support this format will extract the block map along with the
* data and a separate post-process can restore the sparseness.
*
- * Unfortunately, GNU tar 1.16 adds bogus padding to the end of the
- * entry that depends on the size of the map; this means we have to
- * parse the sparse map when we read the header (otherwise, entry_skip
- * will fail). This is why sparse_10_read is called from read_header
- * above, instead of at the beginning of read_data, where it "should"
- * go.
+ * Unfortunately, GNU tar 1.16 had a bug that added unnecessary
+ * padding to the body of the file when using this format. GNU tar
+ * 1.17 corrected this bug without bumping the version number, so
+ * it's not possible to support both variants. This code supports
+ * the later variant at the expense of not supporting the former.
*
* This variant also replaced GNU.sparse.size with GNU.sparse.realsize
* and introduced the GNU.sparse.major/GNU.sparse.minor attributes.
@@ -1954,7 +2009,7 @@ tar_atol8(const char *p, unsigned char_cnt)
* it does obey locale.
*/
static int64_t
-tar_atol10(const wchar_t *p, unsigned char_cnt)
+tar_atol10(const char *p, unsigned char_cnt)
{
int64_t l, limit, last_digit_limit;
int base, digit, sign;
@@ -1987,10 +2042,7 @@ tar_atol10(const wchar_t *p, unsigned char_cnt)
/*
* Parse a base-256 integer. This is just a straight signed binary
* value in big-endian order, except that the high-order bit is
- * ignored. Remember that "int64_t" may or may not be exactly 64
- * bits; the implementation here tries to avoid making any assumptions
- * about the actual size of an int64_t. It does assume we're using
- * twos-complement arithmetic, though.
+ * ignored.
*/
static int64_t
tar_atol256(const char *_p, unsigned char_cnt)
@@ -2088,15 +2140,38 @@ readline(struct archive_read *a, struct tar *tar, const char **start,
}
}
-static int
-utf8_decode(wchar_t *dest, const char *src, size_t length)
+static wchar_t *
+utf8_decode(struct tar *tar, const char *src, size_t length)
{
- size_t n;
+ wchar_t *dest;
+ ssize_t n;
int err;
+ /* Ensure pax_entry buffer is big enough. */
+ if (tar->pax_entry_length <= length) {
+ wchar_t *old_entry = tar->pax_entry;
+
+ if (tar->pax_entry_length <= 0)
+ tar->pax_entry_length = 1024;
+ while (tar->pax_entry_length <= length + 1)
+ tar->pax_entry_length *= 2;
+
+ old_entry = tar->pax_entry;
+ tar->pax_entry = (wchar_t *)realloc(tar->pax_entry,
+ tar->pax_entry_length * sizeof(wchar_t));
+ if (tar->pax_entry == NULL) {
+ free(old_entry);
+ /* TODO: Handle this error. */
+ return (NULL);
+ }
+ }
+
+ dest = tar->pax_entry;
err = 0;
while (length > 0) {
n = UTF8_mbrtowc(dest, src, length);
+ if (n < 0)
+ return (NULL);
if (n == 0)
break;
dest++;
@@ -2104,13 +2179,13 @@ utf8_decode(wchar_t *dest, const char *src, size_t length)
length -= n;
}
*dest++ = L'\0';
- return (err);
+ return (tar->pax_entry);
}
/*
* Copied and simplified from FreeBSD libc/locale.
*/
-static size_t
+static ssize_t
UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
{
int ch, i, len, mask;
@@ -2137,22 +2212,14 @@ UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
} else if ((ch & 0xf8) == 0xf0) {
mask = 0x07;
len = 4;
- } else if ((ch & 0xfc) == 0xf8) {
- mask = 0x03;
- len = 5;
- } else if ((ch & 0xfe) == 0xfc) {
- mask = 0x01;
- len = 6;
} else {
- /* Invalid first byte; convert to '?' */
- *pwc = '?';
- return (1);
+ /* Invalid first byte. */
+ return (-1);
}
if (n < (size_t)len) {
- /* Invalid first byte; convert to '?' */
- *pwc = '?';
- return (1);
+ /* Valid first byte but truncated. */
+ return (-2);
}
/*
@@ -2198,7 +2265,7 @@ UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
* omits line breaks; RFC1341 used for MIME requires both.)
*/
static char *
-base64_decode(const wchar_t *src, size_t len, size_t *out_len)
+base64_decode(const char *s, size_t len, size_t *out_len)
{
static const unsigned char digits[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
@@ -2208,6 +2275,7 @@ base64_decode(const wchar_t *src, size_t len, size_t *out_len)
'4','5','6','7','8','9','+','/' };
static unsigned char decode_table[128];
char *out, *d;
+ const unsigned char *src = (const unsigned char *)s;
/* If the decode table is not yet initialized, prepare it. */
if (decode_table[digits[1]] != 1) {
@@ -2268,43 +2336,6 @@ base64_decode(const wchar_t *src, size_t len, size_t *out_len)
return (out);
}
-/*
- * This is a little tricky because the C99 standard wcstombs()
- * function returns the number of bytes that were converted,
- * not the number that should be converted. As a result,
- * we can never accurately size the output buffer (without
- * doing a tedious output size calculation in advance).
- * This approach (try a conversion, then try again if it fails)
- * will almost always succeed on the first try, and is thus
- * much faster, at the cost of sometimes requiring multiple
- * passes while we expand the buffer.
- */
-static char *
-wide_to_narrow(const wchar_t *wval)
-{
- int converted_length;
- /* Guess an output buffer size and try the conversion. */
- int alloc_length = wcslen(wval) * 3;
- char *mbs_val = (char *)malloc(alloc_length + 1);
- if (mbs_val == NULL)
- return (NULL);
- converted_length = wcstombs(mbs_val, wval, alloc_length);
-
- /* If we exhausted the buffer, resize and try again. */
- while (converted_length >= alloc_length) {
- free(mbs_val);
- alloc_length *= 2;
- mbs_val = (char *)malloc(alloc_length + 1);
- if (mbs_val == NULL)
- return (NULL);
- converted_length = wcstombs(mbs_val, wval, alloc_length);
- }
-
- /* Ensure a trailing null and return the final string. */
- mbs_val[alloc_length] = '\0';
- return (mbs_val);
-}
-
static char *
url_decode(const char *in)
{
diff --git a/lib/libarchive/archive_util.c b/lib/libarchive/archive_util.c
index e80cabb642db..cad14f4b810b 100644
--- a/lib/libarchive/archive_util.c
+++ b/lib/libarchive/archive_util.c
@@ -38,29 +38,71 @@ __FBSDID("$FreeBSD$");
#include "archive.h"
#include "archive_private.h"
+#include "archive_string.h"
+#if ARCHIVE_VERSION_NUMBER < 3000000
+/* These disappear in libarchive 3.0 */
+/* Deprecated. */
int
archive_api_feature(void)
{
return (ARCHIVE_API_FEATURE);
}
+/* Deprecated. */
int
archive_api_version(void)
{
return (ARCHIVE_API_VERSION);
}
+/* Deprecated synonym for archive_version_number() */
int
archive_version_stamp(void)
{
- return (ARCHIVE_VERSION_STAMP);
+ return (archive_version_number());
}
+/* Deprecated synonym for archive_version_string() */
const char *
archive_version(void)
{
- return (ARCHIVE_LIBRARY_VERSION);
+ return (archive_version_string());
+}
+#endif
+
+int
+archive_version_number(void)
+{
+ return (ARCHIVE_VERSION_NUMBER);
+}
+
+/*
+ * Format a version string of the form "libarchive x.y.z", where x, y,
+ * z are the correct parts of the version ID from
+ * archive_version_number().
+ *
+ * I used to do all of this at build time in shell scripts but that
+ * proved to be a portability headache.
+ */
+
+const char *
+archive_version_string(void)
+{
+ static char buff[128];
+ struct archive_string as;
+ int n;
+
+ if (buff[0] == '\0') {
+ n = archive_version_number();
+ memset(&as, 0, sizeof(as));
+ archive_string_sprintf(&as, "libarchive %d.%d.%d",
+ n / 1000000, (n / 1000) % 1000, n % 1000);
+ strncpy(buff, as.s, sizeof(buff));
+ buff[sizeof(buff) - 1] = '\0';
+ archive_string_free(&as);
+ }
+ return (buff);
}
int
diff --git a/lib/libarchive/archive_write_private.h b/lib/libarchive/archive_write_private.h
index 7764fdd9b4d9..8deabbf0e0e3 100644
--- a/lib/libarchive/archive_write_private.h
+++ b/lib/libarchive/archive_write_private.h
@@ -82,13 +82,6 @@ struct archive_write {
} compressor;
/*
- * Again, write support is considerably simpler because there's
- * no need for an auction.
- */
- int archive_format;
- const char *archive_format_name;
-
- /*
* Pointers to format-specific functions for writing. They're
* initialized by archive_write_set_format_XXX() calls.
*/
diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c
index fbec7eb316c8..6f05a99292ad 100644
--- a/lib/libarchive/archive_write_set_format_pax.c
+++ b/lib/libarchive/archive_write_set_format_pax.c
@@ -85,8 +85,8 @@ archive_write_set_format_pax_restricted(struct archive *_a)
struct archive_write *a = (struct archive_write *)_a;
int r;
r = archive_write_set_format_pax(&a->archive);
- a->archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
- a->archive_format_name = "restricted POSIX pax interchange";
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
+ a->archive.archive_format_name = "restricted POSIX pax interchange";
return (r);
}
@@ -116,8 +116,8 @@ archive_write_set_format_pax(struct archive *_a)
a->format_finish = archive_write_pax_finish;
a->format_destroy = archive_write_pax_destroy;
a->format_finish_entry = archive_write_pax_finish_entry;
- a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
- a->archive_format_name = "POSIX pax interchange";
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "POSIX pax interchange";
return (ARCHIVE_OK);
}
@@ -383,19 +383,25 @@ archive_write_pax_header(struct archive_write *a,
struct archive_entry *entry_original)
{
struct archive_entry *entry_main;
- const char *linkname, *p;
+ const char *p;
char *t;
- const char *hardlink;
const wchar_t *wp;
const char *suffix_start;
int need_extension, r, ret;
struct pax *pax;
+ const char *hdrcharset = NULL;
+ const char *hardlink;
+ const char *path = NULL, *linkpath = NULL;
+ const char *uname = NULL, *gname = NULL;
+ const wchar_t *path_w = NULL, *linkpath_w = NULL;
+ const wchar_t *uname_w = NULL, *gname_w = NULL;
char paxbuff[512];
char ustarbuff[512];
char ustar_entry_name[256];
char pax_entry_name[256];
+ ret = ARCHIVE_OK;
need_extension = 0;
pax = (struct pax *)a->format_data;
@@ -442,53 +448,109 @@ archive_write_pax_header(struct archive_write *a,
archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
/*
+ * First, check the name fields and see if any of them
+ * require binary coding. If any of them does, then all of
+ * them do.
+ */
+ hdrcharset = NULL;
+ path = archive_entry_pathname(entry_main);
+ path_w = archive_entry_pathname_w(entry_main);
+ if (path != NULL && path_w == NULL) {
+ archive_set_error(&a->archive, EILSEQ,
+ "Can't translate pathname '%s' to UTF-8", path);
+ ret = ARCHIVE_WARN;
+ hdrcharset = "BINARY";
+ }
+ uname = archive_entry_uname(entry_main);
+ uname_w = archive_entry_uname_w(entry_main);
+ if (uname != NULL && uname_w == NULL) {
+ archive_set_error(&a->archive, EILSEQ,
+ "Can't translate uname '%s' to UTF-8", uname);
+ ret = ARCHIVE_WARN;
+ hdrcharset = "BINARY";
+ }
+ gname = archive_entry_gname(entry_main);
+ gname_w = archive_entry_gname_w(entry_main);
+ if (gname != NULL && gname_w == NULL) {
+ archive_set_error(&a->archive, EILSEQ,
+ "Can't translate gname '%s' to UTF-8", gname);
+ ret = ARCHIVE_WARN;
+ hdrcharset = "BINARY";
+ }
+ linkpath = hardlink;
+ if (linkpath != NULL) {
+ linkpath_w = archive_entry_hardlink_w(entry_main);
+ } else {
+ linkpath = archive_entry_symlink(entry_main);
+ if (linkpath != NULL)
+ linkpath_w = archive_entry_symlink_w(entry_main);
+ }
+ if (linkpath != NULL && linkpath_w == NULL) {
+ archive_set_error(&a->archive, EILSEQ,
+ "Can't translate linkpath '%s' to UTF-8", linkpath);
+ ret = ARCHIVE_WARN;
+ hdrcharset = "BINARY";
+ }
+
+ /* Store the header encoding first, to be nice to readers. */
+ if (hdrcharset != NULL)
+ add_pax_attr(&(pax->pax_header), "hdrcharset", hdrcharset);
+
+ /*
* Determining whether or not the name is too big is ugly
* because of the rules for dividing names between 'name' and
* 'prefix' fields. Here, I pick out the longest possible
* suffix, then test whether the remaining prefix is too long.
*/
- wp = archive_entry_pathname_w(entry_main);
- p = archive_entry_pathname(entry_main);
- if (strlen(p) <= 100) /* Short enough for just 'name' field */
- suffix_start = p; /* Record a zero-length prefix */
+ if (strlen(path) <= 100) /* Short enough for just 'name' field */
+ suffix_start = path; /* Record a zero-length prefix */
else
/* Find the largest suffix that fits in 'name' field. */
- suffix_start = strchr(p + strlen(p) - 100 - 1, '/');
+ suffix_start = strchr(path + strlen(path) - 100 - 1, '/');
/*
* If name is too long, or has non-ASCII characters, add
- * 'path' to pax extended attrs.
+ * 'path' to pax extended attrs. (Note that an unconvertible
+ * name must have non-ASCII characters.)
*/
- if (suffix_start == NULL || suffix_start - p > 155 || has_non_ASCII(wp)) {
- add_pax_attr_w(&(pax->pax_header), "path", wp);
+ if (suffix_start == NULL || suffix_start - path > 155
+ || path_w == NULL || has_non_ASCII(path_w)) {
+ if (path_w == NULL || hdrcharset != NULL)
+ /* Can't do UTF-8, so store it raw. */
+ add_pax_attr(&(pax->pax_header), "path", path);
+ else
+ add_pax_attr_w(&(pax->pax_header), "path", path_w);
archive_entry_set_pathname(entry_main,
- build_ustar_entry_name(ustar_entry_name, p, strlen(p), NULL));
+ build_ustar_entry_name(ustar_entry_name,
+ path, strlen(path), NULL));
need_extension = 1;
}
- /* If link name is too long or has non-ASCII characters, add
- * 'linkpath' to pax extended attrs. */
- linkname = hardlink;
- if (linkname == NULL)
- linkname = archive_entry_symlink(entry_main);
-
- if (linkname != NULL) {
- /* There is a link name, get the wide version as well. */
- if (hardlink != NULL)
- wp = archive_entry_hardlink_w(entry_main);
- else
- wp = archive_entry_symlink_w(entry_main);
-
- /* 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);
- if (hardlink != NULL)
- archive_entry_set_hardlink(entry_main,
- "././@LongHardLink");
+ if (linkpath != NULL) {
+ /* If link name is too long or has non-ASCII characters, add
+ * 'linkpath' to pax extended attrs. */
+ if (strlen(linkpath) > 100 || linkpath_w == NULL
+ || linkpath_w == NULL || has_non_ASCII(linkpath_w)) {
+ if (linkpath_w == NULL || hdrcharset != NULL)
+ /* If the linkpath is not convertible
+ * to wide, or we're encoding in
+ * binary anyway, store it raw. */
+ add_pax_attr(&(pax->pax_header),
+ "linkpath", linkpath);
else
- archive_entry_set_symlink(entry_main,
- "././@LongSymLink");
+ /* If the link is long or has a
+ * non-ASCII character, store it as a
+ * pax extended attribute. */
+ add_pax_attr_w(&(pax->pax_header),
+ "linkpath", linkpath_w);
+ if (strlen(linkpath) > 100) {
+ if (hardlink != NULL)
+ archive_entry_set_hardlink(entry_main,
+ "././@LongHardLink");
+ else
+ archive_entry_set_symlink(entry_main,
+ "././@LongSymLink");
+ }
need_extension = 1;
}
}
@@ -509,12 +571,20 @@ archive_write_pax_header(struct archive_write *a,
/* If group name is too large or has non-ASCII characters, add
* 'gname' to pax extended attrs. */
- 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);
- archive_entry_set_gname(entry_main, NULL);
- need_extension = 1;
+ if (gname != NULL) {
+ if (strlen(gname) > 31
+ || gname_w == NULL
+ || has_non_ASCII(gname_w))
+ {
+ if (gname_w == NULL || hdrcharset != NULL) {
+ add_pax_attr(&(pax->pax_header),
+ "gname", gname);
+ } else {
+ add_pax_attr_w(&(pax->pax_header),
+ "gname", gname_w);
+ }
+ need_extension = 1;
+ }
}
/* If numeric UID is too large, add 'uid' to pax extended attrs. */
@@ -524,14 +594,21 @@ archive_write_pax_header(struct archive_write *a,
need_extension = 1;
}
- /* If user name is too large, add 'uname' to pax extended attrs. */
- /* TODO: If uname has non-ASCII characters, use pax attribute. */
- 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);
- archive_entry_set_uname(entry_main, NULL);
- need_extension = 1;
+ /* Add 'uname' to pax extended attrs if necessary. */
+ if (uname != NULL) {
+ if (strlen(uname) > 31
+ || uname_w == NULL
+ || has_non_ASCII(uname_w))
+ {
+ if (uname_w == NULL || hdrcharset != NULL) {
+ add_pax_attr(&(pax->pax_header),
+ "uname", uname);
+ } else {
+ add_pax_attr_w(&(pax->pax_header),
+ "uname", uname_w);
+ }
+ need_extension = 1;
+ }
}
/*
@@ -624,7 +701,7 @@ archive_write_pax_header(struct archive_write *a,
* already set (we're already generating an extended header, so
* may as well include these).
*/
- if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
need_extension) {
if (archive_entry_mtime(entry_main) < 0 ||
@@ -687,7 +764,7 @@ archive_write_pax_header(struct archive_write *a,
* Pax-restricted does not store data for hardlinks, in order
* to improve compatibility with ustar.
*/
- if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
hardlink != NULL)
archive_entry_set_size(entry_main, 0);
@@ -733,7 +810,6 @@ archive_write_pax_header(struct archive_write *a,
__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0);
/* If we built any extended attributes, write that entry first. */
- ret = ARCHIVE_OK;
if (archive_strlen(&(pax->pax_header)) > 0) {
struct archive_entry *pax_attr_entry;
time_t s;
@@ -793,13 +869,13 @@ archive_write_pax_header(struct archive_write *a,
/* Standard ustar doesn't support ctime. */
archive_entry_set_ctime(pax_attr_entry, 0, 0);
- ret = __archive_write_format_header_ustar(a, paxbuff,
+ r = __archive_write_format_header_ustar(a, paxbuff,
pax_attr_entry, 'x', 1);
archive_entry_free(pax_attr_entry);
/* Note that the 'x' header shouldn't ever fail to format */
- if (ret != 0) {
+ if (r != 0) {
const char *msg = "archive_write_pax_header: "
"'x' header failed?! This can't happen.\n";
write(2, msg, strlen(msg));
@@ -984,23 +1060,31 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length,
/*
* The ustar header for the pax extended attributes must have a
- * reasonable name: SUSv3 suggests 'dirname'/PaxHeader/'filename'
+ * reasonable name: SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename'
+ * where 'pid' is the PID of the archiving process. Unfortunately,
+ * that makes testing a pain since the output varies for each run,
+ * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename'
+ * for now. (Someday, I'll make this settable. Then I can use the
+ * SUS recommendation as default and test harnesses can override it
+ * to get predictable results.)
*
- * Joerg Schiling has argued that this is unnecessary because, in practice,
- * if the pax extended attributes get extracted as regular files, noone is
- * going to bother reading those attributes to manually restore them.
- * Based on this, 'star' uses /tmp/PaxHeader/'basename' as the ustar header
- * name. This is a tempting argument, but I'm not entirely convinced.
- * I'm also uncomfortable with the fact that "/tmp" is a Unix-ism.
+ * Joerg Schilling has argued that this is unnecessary because, in
+ * practice, if the pax extended attributes get extracted as regular
+ * files, noone is going to bother reading those attributes to
+ * manually restore them. Based on this, 'star' uses
+ * /tmp/PaxHeader/'basename' as the ustar header name. This is a
+ * tempting argument, in part because it's simpler than the SUSv3
+ * recommendation, but I'm not entirely convinced. I'm also
+ * uncomfortable with the fact that "/tmp" is a Unix-ism.
*
- * The following routine implements the SUSv3 recommendation, and is
- * much simpler because build_ustar_entry_name() above already does
- * most of the work (we just need to give it an extra path element to
- * insert and handle a few pathological cases).
+ * The following routine leverages build_ustar_entry_name() above and
+ * so is simpler than you might think. It just needs to provide the
+ * additional path element and handle a few pathological cases).
*/
static char *
build_pax_attribute_name(char *dest, const char *src)
{
+ char buff[64];
const char *p;
/* Handle the null filename case. */
@@ -1039,8 +1123,19 @@ build_pax_attribute_name(char *dest, const char *src)
return (dest);
}
+ /*
+ * TODO: Push this string into the 'pax' structure to avoid
+ * recomputing it every time. That will also open the door
+ * to having clients override it.
+ */
+#if HAVE_GETPID && 0 /* Disable this for now; see above comment. */
+ sprintf(buff, "PaxHeader.%d", getpid());
+#else
+ /* If the platform can't fetch the pid, don't include it. */
+ strcpy(buff, "PaxHeader");
+#endif
/* General case: build a ustar-compatible name adding "/PaxHeader/". */
- build_ustar_entry_name(dest, src, p - src, "PaxHeader");
+ build_ustar_entry_name(dest, src, p - src, buff);
return (dest);
}