]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Set UTF-16BE filenames in Joliet extension to archvie_enty directly in a string conve...
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 18 May 2011 15:21:51 +0000 (11:21 -0400)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 18 May 2011 15:21:51 +0000 (11:21 -0400)
report the conversion error if it happened.

SVN-Revision: 3366

libarchive/archive_read_support_format_iso9660.c

index 0ca2c4da97180d5161773545c2ddc23f31c1d662..15402bc0ba25f6e17f440e94ebef27b0a3039bcb 100644 (file)
@@ -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)