diff options
| author | Tim Kientzle <kientzle@FreeBSD.org> | 2008-05-21 04:12:29 +0000 |
|---|---|---|
| committer | Tim Kientzle <kientzle@FreeBSD.org> | 2008-05-21 04:12:29 +0000 |
| commit | a61833cfec298de0eb805d57aedab17d5e3aeba1 (patch) | |
| tree | f76dbee78173a8e25258bff8d8366a1b69c260ec /lib/libarchive | |
| parent | 09193177fbd709ff32636fc0e02f1fbe337c6cf0 (diff) | |
Notes
Diffstat (limited to 'lib/libarchive')
| -rw-r--r-- | lib/libarchive/archive_entry.c | 42 | ||||
| -rw-r--r-- | lib/libarchive/archive_entry.h | 68 | ||||
| -rw-r--r-- | lib/libarchive/archive_platform.h | 5 | ||||
| -rw-r--r-- | lib/libarchive/archive_read_support_format_mtree.c | 280 | ||||
| -rw-r--r-- | lib/libarchive/archive_read_support_format_tar.c | 451 | ||||
| -rw-r--r-- | lib/libarchive/archive_util.c | 46 | ||||
| -rw-r--r-- | lib/libarchive/archive_write_private.h | 7 | ||||
| -rw-r--r-- | lib/libarchive/archive_write_set_format_pax.c | 231 |
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); } |
