From: Michihiro NAKAJIMA Date: Wed, 18 May 2011 15:21:51 +0000 (-0400) Subject: Set UTF-16BE filenames in Joliet extension to archvie_enty directly in a string conve... X-Git-Tag: v3.0.0a~321 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=507d6f8ea3f2439904ba087cf615f4033bf6a031;p=thirdparty%2Flibarchive.git Set UTF-16BE filenames in Joliet extension to archvie_enty directly in a string conversion, and report the conversion error if it happened. SVN-Revision: 3366 --- diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c index 0ca2c4da9..15402bc0b 100644 --- a/libarchive/archive_read_support_format_iso9660.c +++ b/libarchive/archive_read_support_format_iso9660.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_iso9660.c 20 #include "archive.h" #include "archive_endian.h" #include "archive_entry.h" +#include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_string.h" @@ -281,6 +282,8 @@ struct file_info { int64_t number; int nlinks; struct archive_string name; /* Pathname */ + unsigned char *utf16be_name; + size_t utf16be_bytes; char name_continues; /* Non-zero if name continues */ struct archive_string symlink; char symlink_continues; /* Non-zero if link continues */ @@ -355,6 +358,10 @@ struct iso9660 { struct zisofs entry_zisofs; struct content *entry_content; struct archive_string_conv *sconv_utf16be; + unsigned char utf16be_path[240]; + size_t utf16be_path_len; + unsigned char utf16be_previous_path[240]; + size_t utf16be_previous_path_len; }; static int archive_read_format_iso9660_bid(struct archive_read *); @@ -367,6 +374,8 @@ static int archive_read_format_iso9660_read_data_skip(struct archive_read *); static int archive_read_format_iso9660_read_header(struct archive_read *, struct archive_entry *); static const char *build_pathname(struct archive_string *, struct file_info *); +static int build_pathname_utf16be(unsigned char *, size_t, size_t *, + struct file_info *); #if DEBUG static void dump_isodirrec(FILE *, const unsigned char *isodirrec); #endif @@ -1241,12 +1250,60 @@ archive_read_format_iso9660_read_header(struct archive_read *a, if (r != ARCHIVE_OK) return (r); + if (iso9660->seenJoliet) { + /* + * Convert UTF-16BE of a filename to local locale MBS + * and store the result into a filename field. + */ + if (iso9660->sconv_utf16be == NULL) { + iso9660->sconv_utf16be = + archive_string_conversion_from_charset( + &(a->archive), "UTF-16BE", 1); + if (iso9660->sconv_utf16be == NULL) + /* Coundn't allocate memory */ + return (ARCHIVE_FATAL); + } + + iso9660->utf16be_path_len = 0; + if (build_pathname_utf16be(iso9660->utf16be_path, + sizeof(iso9660->utf16be_path), + &(iso9660->utf16be_path_len), file) != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname is too long(over 240 bytes)"); + } + + r = archive_entry_copy_pathname_l(entry, + iso9660->utf16be_path, iso9660->utf16be_path_len, + iso9660->sconv_utf16be); + if (r != 0) { + if (errno == ENOMEM) { + archive_set_error(&a->archive, ENOMEM, + "No memory for Pathname"); + return (ARCHIVE_FATAL); + } + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname cannot be converted " + "from %s to current locale.", + archive_string_conversion_charset_name( + iso9660->sconv_utf16be)); + + rd_r = ARCHIVE_WARN; + } + } else { + archive_string_empty(&iso9660->pathname); + archive_entry_set_pathname(entry, + build_pathname(&iso9660->pathname, file)); + } + iso9660->entry_bytes_remaining = file->size; iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */ if (file->offset + file->size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "File is beyond end-of-media: %s", file->name.s); + "File is beyond end-of-media: %s", + archive_entry_pathname(entry)); iso9660->entry_bytes_remaining = 0; iso9660->entry_sparse_offset = 0; return (ARCHIVE_WARN); @@ -1267,9 +1324,6 @@ archive_read_format_iso9660_read_header(struct archive_read *a, /* N.B.: Rock Ridge supports 64-bit device numbers. */ archive_entry_set_rdev(entry, (dev_t)file->rdev); archive_entry_set_size(entry, iso9660->entry_bytes_remaining); - archive_string_empty(&iso9660->pathname); - archive_entry_set_pathname(entry, - build_pathname(&iso9660->pathname, file)); if (file->symlink.s != NULL) archive_entry_copy_symlink(entry, file->symlink.s); @@ -1279,8 +1333,28 @@ archive_read_format_iso9660_read_header(struct archive_read *a, * original entry. */ if (file->number != -1 && file->number == iso9660->previous_number) { - archive_entry_set_hardlink(entry, - iso9660->previous_pathname.s); + if (iso9660->seenJoliet) { + r = archive_entry_copy_hardlink_l(entry, + iso9660->utf16be_previous_path, + iso9660->utf16be_previous_path_len, + iso9660->sconv_utf16be); + if (r != 0) { + if (errno == ENOMEM) { + archive_set_error(&a->archive, ENOMEM, + "No memory for Linkname"); + return (ARCHIVE_FATAL); + } + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Linkname cannot be converted " + "from %s to current locale.", + archive_string_conversion_charset_name( + iso9660->sconv_utf16be)); + rd_r = ARCHIVE_WARN; + } + } else + archive_entry_set_hardlink(entry, + iso9660->previous_pathname.s); archive_entry_unset_size(entry); iso9660->entry_bytes_remaining = 0; iso9660->entry_sparse_offset = 0; @@ -1334,7 +1408,13 @@ archive_read_format_iso9660_read_header(struct archive_read *a, } iso9660->previous_number = file->number; - archive_strcpy(&iso9660->previous_pathname, iso9660->pathname.s); + if (iso9660->seenJoliet) { + memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path, + iso9660->utf16be_path_len); + iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len; + } else + archive_strcpy( + &iso9660->previous_pathname, iso9660->pathname.s); /* Reset entry_bytes_remaining if the file is multi extent. */ iso9660->entry_content = file->contents.first; @@ -1752,13 +1832,12 @@ parse_file_info(struct archive_read *a, struct file_info *parent, } /* Create a new file entry and copy data from the ISO dir record. */ - file = (struct file_info *)malloc(sizeof(*file)); + file = (struct file_info *)calloc(1, sizeof(*file)); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for file entry"); return (NULL); } - memset(file, 0, sizeof(*file)); file->parent = parent; file->offset = iso9660->logical_block_size * (uint64_t)location; file->size = fsize; @@ -1800,23 +1879,13 @@ parse_file_info(struct archive_read *a, struct file_info *parent, if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.') name_len -= 2; #endif - - /* Convert UTF-16BE of a filename to local locale MBS and store - * the result into a filename field. */ - if (iso9660->sconv_utf16be == NULL) { - iso9660->sconv_utf16be = - archive_string_conversion_from_charset( - &(a->archive), "UTF-16BE", 1); - if (iso9660->sconv_utf16be == NULL) - return (NULL);/* Coundn't allocate memory */ - } - if (archive_strncpy_in_locale(&file->name, - (const char *)p, name_len, iso9660->sconv_utf16be) != 0 - && errno == ENOMEM) { + if ((file->utf16be_name = malloc(name_len)) == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for file name"); return (NULL); } + memcpy(file->utf16be_name, p, name_len); + file->utf16be_bytes = name_len; } else { /* Chop off trailing ';1' from files. */ if (name_len > 2 && p[name_len - 2] == ';' && @@ -2514,6 +2583,7 @@ release_files(struct iso9660 *iso9660) archive_string_free(&file->name); archive_string_free(&file->symlink); + free(file->utf16be_name); con = file->contents.first; while (con != NULL) { connext = con->next; @@ -2899,6 +2969,32 @@ build_pathname(struct archive_string *as, struct file_info *file) return (as->s); } +static int +build_pathname_utf16be(unsigned char *p, size_t max, size_t *len, + struct file_info *file) +{ + if (file->parent != NULL && file->parent->utf16be_bytes > 0) { + if (build_pathname_utf16be(p, max, len, file->parent) != 0) + return (-1); + p[*len] = 0; + p[*len + 1] = '/'; + *len += 2; + } + if (file->utf16be_bytes == 0) { + if (*len + 2 > max) + return (-1);/* Path is too long! */ + p[*len] = 0; + p[*len + 1] = '.'; + *len += 2; + } else { + if (*len + file->utf16be_bytes > max) + return (-1);/* Path is too long! */ + memcpy(p + *len, file->utf16be_name, file->utf16be_bytes); + *len += file->utf16be_bytes; + } + return (0); +} + #if DEBUG static void dump_isodirrec(FILE *out, const unsigned char *isodirrec)