From: Michihiro NAKAJIMA Date: Thu, 24 Mar 2011 05:01:05 +0000 (-0400) Subject: Add support for charset option to ustar writer. X-Git-Tag: v3.0.0a~613 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9bb47894fb71967a9887c47bcb59aab9ea14eb9b;p=thirdparty%2Flibarchive.git Add support for charset option to ustar writer. SVN-Revision: 3065 --- diff --git a/libarchive/archive_write_private.h b/libarchive/archive_write_private.h index 0fb447cb6..9c00ae97c 100644 --- a/libarchive/archive_write_private.h +++ b/libarchive/archive_write_private.h @@ -129,6 +129,6 @@ struct archive_write { */ int __archive_write_format_header_ustar(struct archive_write *, char buff[512], - struct archive_entry *, int tartype, int strict); + struct archive_entry *, int tartype, int strict, const char *); #endif diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c index 2a04fe41a..8507f6a83 100644 --- a/libarchive/archive_write_set_format_pax.c +++ b/libarchive/archive_write_set_format_pax.c @@ -176,12 +176,7 @@ archive_write_pax_options(struct archive_write *a, const char *key, &a->archive, val) == 0) { pax->opt_binary = 0; free(pax->opt_charset); - if (strcmp(val, "UTF-8") == 0) { - /* This is default charset, so we should - * not specify the charset. */ - pax->opt_charset = NULL; - } else - pax->opt_charset = strdup(val); + pax->opt_charset = strdup(val); ret = ARCHIVE_OK; } else ret = ARCHIVE_FATAL; @@ -401,7 +396,8 @@ archive_write_pax_header(struct archive_write *a, */ if ((binary = pax->opt_binary) != 0) charset = NULL; - else if (pax->opt_charset != NULL) + else if (pax->opt_charset != NULL && + strcmp(pax->opt_charset, "UTF-8") != 0) charset = pax->opt_charset; else charset = "UTF-8";/* Default */ @@ -1052,7 +1048,8 @@ archive_write_pax_header(struct archive_write *a, * The non-strict formatter uses similar logic for other * numeric fields, though they're less critical. */ - __archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0); + __archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0, + pax->opt_charset); /* If we built any extended attributes, write that entry first. */ if (archive_strlen(&(pax->pax_header)) > 0) { @@ -1108,18 +1105,19 @@ archive_write_pax_header(struct archive_write *a, archive_entry_set_ctime(pax_attr_entry, 0, 0); r = __archive_write_format_header_ustar(a, paxbuff, - pax_attr_entry, 'x', 1); + pax_attr_entry, 'x', 1, pax->opt_charset); archive_entry_free(pax_attr_entry); /* Note that the 'x' header shouldn't ever fail to format */ - if (r != 0) { + if (r < ARCHIVE_WARN) { const char *msg = "archive_write_pax_header: " "'x' header failed?! This can't happen.\n"; size_t u = write(2, msg, strlen(msg)); (void)u; /* UNUSED */ exit(1); - } + } else if (r < ret) + ret = r; r = __archive_write_output(a, paxbuff, 512); if (r != ARCHIVE_OK) { sparse_list_clear(pax); diff --git a/libarchive/archive_write_set_format_ustar.c b/libarchive/archive_write_set_format_ustar.c index 58e68fa74..df8d3a198 100644 --- a/libarchive/archive_write_set_format_ustar.c +++ b/libarchive/archive_write_set_format_ustar.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2011 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,6 +47,8 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ustar.c 191579 struct ustar { uint64_t entry_bytes_remaining; uint64_t entry_padding; + + char *opt_charset; }; /* @@ -148,6 +151,8 @@ static int archive_write_ustar_close(struct archive_write *); static int archive_write_ustar_finish_entry(struct archive_write *); static int archive_write_ustar_header(struct archive_write *, struct archive_entry *entry); +static int archive_write_ustar_options(struct archive_write *, + const char *, const char *); static int format_256(int64_t, char *, int); static int format_number(int64_t, char *, int size, int max, int strict); static int format_octal(int64_t, char *, int); @@ -170,18 +175,22 @@ archive_write_set_format_ustar(struct archive *_a) /* Basic internal sanity test. */ if (sizeof(template_header) != 512) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %zu should be 512", sizeof(template_header)); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal: template_header wrong size: %zu should be 512", + sizeof(template_header)); return (ARCHIVE_FATAL); } ustar = (struct ustar *)malloc(sizeof(*ustar)); if (ustar == NULL) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate ustar data"); return (ARCHIVE_FATAL); } memset(ustar, 0, sizeof(*ustar)); a->format_data = ustar; a->format_name = "ustar"; + a->format_options = archive_write_ustar_options; a->format_write_header = archive_write_ustar_header; a->format_write_data = archive_write_ustar_data; a->format_close = archive_write_ustar_close; @@ -192,6 +201,32 @@ archive_write_set_format_ustar(struct archive *_a) return (ARCHIVE_OK); } +static int +archive_write_ustar_options(struct archive_write *a, const char *key, + const char *val) +{ + struct ustar *ustar = (struct ustar *)a->format_data; + int ret = ARCHIVE_FAILED; + + if (strcmp(key, "charset") == 0) { + if (val == NULL || val[0] == 0) + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "%s: charset option needs a character-set name", + a->format_name); + else if (archive_string_conversion_to_charset( + &a->archive, val) == 0) { + free(ustar->opt_charset); + ustar->opt_charset = strdup(val); + ret = ARCHIVE_OK; + } else + ret = ARCHIVE_FATAL; + } else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "%s: unknown keyword ``%s''", a->format_name, key); + + return (ret); +} + static int archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) { @@ -201,6 +236,13 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) ustar = (struct ustar *)a->format_data; + /* Sanity check. */ + if (archive_entry_pathname(entry) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Can't record entry in tar file without pathname"); + return (ARCHIVE_FAILED); + } + /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || @@ -229,7 +271,8 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) } } - ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1); + ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, + ustar->opt_charset); if (ret < ARCHIVE_WARN) return (ret); ret2 = __archive_write_output(a, buff, 512); @@ -255,16 +298,18 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) */ int __archive_write_format_header_ustar(struct archive_write *a, char h[512], - struct archive_entry *entry, int tartype, int strict) + struct archive_entry *entry, int tartype, int strict, const char *charset) { unsigned int checksum; - int i, ret; + int i, r, ret; size_t copy_length; const char *p, *pp; int mytartype; + struct archive_string l_name; ret = 0; mytartype = -1; + archive_string_init(&l_name); /* * The "template header" already includes the "ustar" * signature, various end-of-field markers and other required @@ -279,13 +324,25 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], */ pp = archive_entry_pathname(entry); - if (pp == NULL) { - /* Yuck. */ - } else if (strlen(pp) <= USTAR_name_size) - memcpy(h + USTAR_name_offset, pp, strlen(pp)); + /* Check if a charset conversion is needed or not, + * to avoid extra memory copy. */ + if (charset != NULL) { + r = archive_strcpy_to_locale(&(a->archive), &l_name, pp, charset); + if (r != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname '%s' to %s", + pp, charset); + ret = ARCHIVE_WARN; + } + pp = l_name.s; + copy_length = archive_strlen(&l_name); + } else + copy_length = strlen(pp); + if (copy_length <= USTAR_name_size) + memcpy(h + USTAR_name_offset, pp, copy_length); else { /* Store in two pieces, splitting at a '/'. */ - p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/'); + p = strchr(pp + copy_length - USTAR_name_size - 1, '/'); /* * Look for the next '/' if we chose the first character * as the separator. (ustar format doesn't permit @@ -317,7 +374,8 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], } else { /* Copy prefix and remainder to appropriate places */ memcpy(h + USTAR_prefix_offset, pp, p - pp); - memcpy(h + USTAR_name_offset, p + 1, pp + strlen(pp) - p - 1); + memcpy(h + USTAR_name_offset, p + 1, + pp + copy_length - p - 1); } } @@ -327,7 +385,23 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], else p = archive_entry_symlink(entry); if (p != NULL && p[0] != '\0') { - copy_length = strlen(p); + if (charset != NULL) { + r = archive_strcpy_to_locale(&(a->archive), &l_name, p, + charset); + if (r != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate linkname '%s' to %s", + p, charset); + ret = ARCHIVE_WARN; + } + p = l_name.s; + copy_length = archive_strlen(&l_name); + } else + copy_length = strlen(p); + } else + copy_length = 0; + if (copy_length > 0) { if (copy_length > USTAR_linkname_size) { archive_set_error(&a->archive, ENAMETOOLONG, "Link contents too long"); @@ -339,7 +413,23 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], p = archive_entry_uname(entry); if (p != NULL && p[0] != '\0') { - copy_length = strlen(p); + if (charset != NULL) { + r = archive_strcpy_to_locale(&(a->archive), &l_name, + p, charset); + if (r != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate uname '%s' to %s", + p, charset); + ret = ARCHIVE_WARN; + } + p = l_name.s; + copy_length = archive_strlen(&l_name); + } else + copy_length = strlen(p); + } else + copy_length = 0; + if (copy_length > 0) { if (copy_length > USTAR_uname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Username too long"); @@ -351,7 +441,23 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { - copy_length = strlen(p); + if (charset != NULL) { + r = archive_strcpy_to_locale(&(a->archive), &l_name, + p, charset); + if (r != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate gname '%s' to %s", + p, charset); + ret = ARCHIVE_WARN; + } + p = l_name.s; + copy_length = archive_strlen(&l_name); + } else + copy_length = strlen(p); + } else + copy_length = 0; + if (copy_length > 0) { if (strlen(p) > USTAR_gname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Group name too long"); @@ -361,27 +467,36 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], memcpy(h + USTAR_gname_offset, p, copy_length); } - if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) { - archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); + if (format_number(archive_entry_mode(entry) & 07777, + h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, + "Numeric mode too large"); ret = ARCHIVE_FAILED; } - if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) { - archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); + if (format_number(archive_entry_uid(entry), + h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, + "Numeric user ID too large"); ret = ARCHIVE_FAILED; } - if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) { - archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); + if (format_number(archive_entry_gid(entry), + h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, + "Numeric group ID too large"); ret = ARCHIVE_FAILED; } - if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) { - archive_set_error(&a->archive, ERANGE, "File size out of range"); + if (format_number(archive_entry_size(entry), + h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, + "File size out of range"); ret = ARCHIVE_FAILED; } - if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) { + if (format_number(archive_entry_mtime(entry), + h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); ret = ARCHIVE_FAILED; @@ -389,21 +504,25 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { - if (format_number(archive_entry_rdevmajor(entry), h + USTAR_rdevmajor_offset, - USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) { + if (format_number(archive_entry_rdevmajor(entry), + h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size, + USTAR_rdevmajor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); ret = ARCHIVE_FAILED; } - if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset, - USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) { + if (format_number(archive_entry_rdevminor(entry), + h + USTAR_rdevminor_offset, USTAR_rdevminor_size, + USTAR_rdevminor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); ret = ARCHIVE_FAILED; } } + archive_string_free(&l_name); + if (tartype >= 0) { h[USTAR_typeflag_offset] = tartype; } else if (mytartype >= 0) { @@ -532,6 +651,7 @@ archive_write_ustar_free(struct archive_write *a) struct ustar *ustar; ustar = (struct ustar *)a->format_data; + free(ustar->opt_charset); free(ustar); a->format_data = NULL; return (ARCHIVE_OK);