]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix a bug; reading extensions recorded by SUSP CE extension
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Tue, 13 Oct 2009 17:50:32 +0000 (13:50 -0400)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Tue, 13 Oct 2009 17:50:32 +0000 (13:50 -0400)
did not work and sometimes stole file contents.
Add test for this case.

libarchive 2.7.* have had these following bugs.
1. When length of symlink name is longer than about 70,
   it cannot get modify/access times recorded by "TF" extension
   and maybe next file is stolen a part of its contents by
   that process.
2. When length of file name is longer than about 80,
   it cannot get modify/access times recorded by "TF" extension
   and maybe it's stolen a part of its contents by that process.
3. When length of file name is longer than about 100,
   it cannot get file mode, user id and group id recorded by "PX"
   extension and as above.
4. When length of file name is longer than about 142,
   it gets wrong file name(shorter name) and as above.

Condition of stealing file contents:
  if file->offset - iso9660->current_position < file->ce_size
  in next_entry_seek function, that process consumed file contents
  of which were data of extensions.

That process read wrong data whether that process consumed file
contents or not.

SVN-Revision: 1513

Makefile.am
libarchive/archive_read_support_format_iso9660.c
libarchive/test/CMakeLists.txt
libarchive/test/Makefile
libarchive/test/test_read_format_isorr_ce.c [new file with mode: 0644]
libarchive/test/test_read_format_isorr_ce.iso.bz2.uu [new file with mode: 0644]

index ab6a78cfbd1d7097d042d29df8cdb4a7f96428fd..2fcf62aa11be0b295cb059764a3a1f9863f0299b 100644 (file)
@@ -261,6 +261,7 @@ libarchive_test_SOURCES=                                    \
        libarchive/test/test_read_format_isojoliet_long.c       \
        libarchive/test/test_read_format_isojoliet_rr.c         \
        libarchive/test/test_read_format_isorr_bz2.c            \
+       libarchive/test/test_read_format_isorr_ce.c             \
        libarchive/test/test_read_format_isorr_new_bz2.c        \
        libarchive/test/test_read_format_isozisofs_bz2.c        \
        libarchive/test/test_read_format_mtree.c                \
@@ -344,6 +345,7 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_read_format_isojoliet_long.iso.bz2.uu      \
        libarchive/test/test_read_format_isojoliet_rr.iso.bz2.uu        \
        libarchive/test/test_read_format_isorr_bz2.iso.bz2.uu           \
+       libarchive/test/test_read_format_isorr_ce.iso.bz2.uu            \
        libarchive/test/test_read_format_isorr_new_bz2.iso.bz2.uu       \
        libarchive/test/test_read_format_isozisofs_bz2.iso.bz2.uu       \
        libarchive/test/test_read_format_raw.data.Z.uu                  \
index 29f3fedb1b9450ebfe550bdeb674def132d9bdd6..4bab50b94e64c2788d458746f50deaa3b416de94 100644 (file)
@@ -254,8 +254,8 @@ struct file_info {
        int              subdirs;
        uint64_t         offset;  /* Offset on disk. */
        uint64_t         size;  /* File size in bytes. */
-       uint64_t         ce_offset; /* Offset of CE */
-       uint64_t         ce_size; /* Size of CE */
+       uint32_t         ce_offset; /* Offset of CE */
+       uint32_t         ce_size; /* Size of CE */
        time_t           birthtime; /* File created time. */
        time_t           mtime; /* File last modified time. */
        time_t           atime; /* File last accessed time. */
@@ -287,9 +287,17 @@ struct iso9660 {
        struct archive_string pathname;
        char    seenRockridge;  /* Set true if RR extensions are used. */
        char    seenSUSP;       /* Set true if SUSP is beging used. */
-       unsigned char   suspOffset;
        char    seenJoliet;
 
+       unsigned char   suspOffset;
+       struct {
+               struct read_ce_req {
+                       int64_t          location;/* Location of CE */
+                       struct file_info *file;
+               }               *reqs;
+               int              cnt;
+               int              allocated;
+       }       read_ce_req;
        int64_t         previous_number;
        uint64_t        previous_size;
        struct archive_string previous_pathname;
@@ -355,6 +363,8 @@ static struct file_info *
 static void    parse_rockridge(struct iso9660 *iso9660,
                    struct file_info *file, const unsigned char *start,
                    const unsigned char *end);
+static int     register_CE(struct iso9660 *iso9660, int32_t location,
+                   struct file_info *file);
 static void    parse_rockridge_NM1(struct file_info *,
                    const unsigned char *, int);
 static void    parse_rockridge_SL1(struct file_info *,
@@ -1247,6 +1257,7 @@ archive_read_format_iso9660_cleanup(struct archive_read *a)
        free(iso9660->cache_files.files);
        while ((file = next_entry(iso9660)) != NULL)
                release_file(iso9660, file);
+       free(iso9660->read_ce_req.reqs);
        archive_string_free(&iso9660->pathname);
        archive_string_free(&iso9660->previous_pathname);
        if (iso9660->pending_files)
@@ -1442,6 +1453,8 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
                        }
                }
                if (iso9660->seenSUSP) {
+                       file->name_continues = 0;
+                       file->symlink_continues = 0;
                        rr_start += iso9660->suspOffset;
                        parse_rockridge(iso9660, file, rr_start, rr_end);
                } else
@@ -1530,8 +1543,6 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file,
     const unsigned char *p, const unsigned char *end)
 {
        (void)iso9660; /* UNUSED */
-       file->name_continues = 0;
-       file->symlink_continues = 0;
 
        while (p + 4 < end  /* Enough space for another entry. */
            && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
@@ -1557,17 +1568,13 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file,
                                         *   8 byte offset w/in above sector
                                         *   8 byte length of continuation
                                         */
-                                       file->ce_offset = (uint64_t)toi(data, 4)
-                                           * iso9660->logical_block_size
-                                           + toi(data + 8, 4);
-                                       file->ce_size = toi(data + 16, 4);
-                                       /* If the result is rediculous,
-                                        * ignore it. */
-                                       if (file->ce_offset + file->ce_size
-                                           > iso9660->volume_size) {
-                                               file->ce_offset = 0;
-                                               file->ce_size = 0;
-                                       }
+                                       int32_t location =
+                                           archive_le32dec(data);
+                                       file->ce_offset =
+                                           archive_le32dec(data+8);
+                                       file->ce_size =
+                                           archive_le32dec(data+16);
+                                       register_CE(iso9660, location, file);
                                }
                                break;
                        }
@@ -1704,6 +1711,58 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file,
        }
 }
 
+static int
+register_CE(struct iso9660 *iso9660, int32_t location,
+    struct file_info *file)
+{
+       struct read_ce_req *p;
+       int64_t offset;
+       int i;
+
+       offset = location * iso9660->logical_block_size;
+       if (((file->mode & AE_IFMT) == AE_IFREG &&
+           offset >= file->offset) ||
+           offset < iso9660->current_position) {
+               /*archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Invalid location in RRIP \"CE\"");*/
+               return (ARCHIVE_FATAL);
+       }
+       if (iso9660->read_ce_req.cnt + 1 > iso9660->read_ce_req.allocated) {
+               int alloc = iso9660->read_ce_req.allocated;
+
+               if (alloc == 0)
+                       alloc = 8;
+               else
+                       alloc *= 2;
+               p = malloc(alloc * sizeof(*p));
+               if (p == NULL)
+                       __archive_errx(1, "Out of memory");
+               iso9660->read_ce_req.allocated = alloc;
+               if (iso9660->read_ce_req.reqs != NULL) {
+                       memcpy(p, iso9660->read_ce_req.reqs,
+                           iso9660->read_ce_req.cnt * sizeof(*p));
+                       free(iso9660->read_ce_req.reqs);
+               }
+               iso9660->read_ce_req.reqs = p;
+       }
+       for (i = 0; i < iso9660->read_ce_req.cnt; i++) {
+               if (iso9660->read_ce_req.reqs[i].location > location) {
+                       p = &iso9660->read_ce_req.reqs[i];
+                       memmove(p+1, p,
+                           (iso9660->read_ce_req.cnt -i) * sizeof(*p));
+                       p->location = offset;
+                       p->file = file;
+                       iso9660->read_ce_req.cnt++;
+                       return (ARCHIVE_OK);
+               }
+       }
+       p = &iso9660->read_ce_req.reqs[iso9660->read_ce_req.cnt];
+       p->location = offset;
+       p->file = file;
+       iso9660->read_ce_req.cnt++;
+       return (ARCHIVE_OK);
+}
+
 static void
 parse_rockridge_NM1(struct file_info *file,
                    const unsigned char *data, int data_length)
@@ -1830,7 +1889,8 @@ parse_rockridge_SL1(struct file_info *file, const unsigned char *data,
 
        if (!file->symlink_continues || file->symlink.length < 1)
                archive_string_empty(&file->symlink);
-       else if (file->symlink.s[file->symlink.length - 1] != '/')
+       else if (!file->symlink_continues &&
+           file->symlink.s[file->symlink.length - 1] != '/')
                separator = "/";
        file->symlink_continues = 0;
 
@@ -1952,63 +2012,59 @@ next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
        struct file_info *file;
        uint64_t offset;
 
-       *pfile = NULL;
-       for (;;) {
-               *pfile = file = next_cache_entry(iso9660);
-               if (file == NULL)
-                       return (ARCHIVE_EOF);
-
-               /* CE area precedes actual file data? Ignore it. */
-               if (file->ce_offset > file->offset) {
-                       /* fprintf(stderr, " *** Discarding CE data.\n"); */
-                       file->ce_offset = 0;
-                       file->ce_size = 0;
-               }
-
-               /* Don't waste time seeking for zero-length bodies. */
-               if (file->size == 0) {
-                       file->offset = iso9660->current_position;
-               }
+       *pfile = file = next_cache_entry(iso9660);
+       if (file == NULL)
+               return (ARCHIVE_EOF);
 
-               /* If CE exists, find and read it now. */
-               if (file->ce_offset > 0)
-                       offset = file->ce_offset;
-               else
-                       offset = file->offset;
-
-               /* Seek forward to the start of the entry. */
-               if (iso9660->current_position < offset) {
-                       off_t step = offset - iso9660->current_position;
-                       off_t bytes_read;
-                       bytes_read = __archive_read_skip(a, step);
-                       if (bytes_read < 0)
-                               return (bytes_read);
-                       iso9660->current_position = offset;
+       /* Read RRIP "CE" System Use Entry. */
+       while (iso9660->read_ce_req.cnt &&
+           iso9660->read_ce_req.reqs[0].location ==
+               iso9660->current_position) {
+               struct read_ce_req *ce;
+               const unsigned char *b, *p, *end;
+               size_t step;
+
+               step = iso9660->logical_block_size;
+               b = __archive_read_ahead(a, step, NULL);
+               if (b == NULL) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "Failed to read full block when scanning "
+                           "ISO9660 directory list");
+                       return (ARCHIVE_FATAL);
                }
+               ce = iso9660->read_ce_req.reqs;
+               do {
+                       p = b + ce->file->ce_offset;
+                       end = p + ce->file->ce_size;
+                       parse_rockridge(iso9660, ce->file, p, end);
+                       memmove(ce, ce+1, sizeof(*ce) *
+                           (iso9660->read_ce_req.cnt -1));
+                       iso9660->read_ce_req.cnt--;
+               } while (iso9660->read_ce_req.cnt &&
+                   ce->location == iso9660->current_position);
+               __archive_read_consume(a, step);
+               iso9660->current_position += step;
+       }
 
-               /* We found body of file; handle it now. */
-               if (offset == file->offset)
-                       return (ARCHIVE_OK);
-
-               /* Found CE?  Process it and push the file back onto list. */
-               if (offset == file->ce_offset) {
-                       const void *p;
-                       ssize_t size = file->ce_size;
-                       const unsigned char *rr_start;
-
-                       file->ce_offset = 0;
-                       file->ce_size = 0;
-                       p = __archive_read_ahead(a, size, NULL);
-                       if (p == NULL)
-                               return (ARCHIVE_FATAL);
-                       rr_start = (const unsigned char *)p;
-                       parse_rockridge(iso9660, file, rr_start,
-                           rr_start + size);
-                       __archive_read_consume(a, size);
-                       iso9660->current_position += size;
-                       add_entry(iso9660, file);
-               }
+       /* Don't waste time seeking for zero-length bodies. */
+       if (file->size == 0)
+               file->offset = iso9660->current_position;
+
+       offset = file->offset;
+
+       /* Seek forward to the start of the entry. */
+       if (iso9660->current_position < offset) {
+               off_t step = offset - iso9660->current_position;
+               off_t bytes_read;
+               bytes_read = __archive_read_skip(a, step);
+               if (bytes_read < 0)
+                       return (bytes_read);
+               iso9660->current_position = offset;
        }
+
+       /* We found body of file; handle it now. */
+       return (ARCHIVE_OK);
 }
 
 static struct file_info *
index 47a74921b13e5bcfd51c9a7d0f07aa8ae6a9fc38..66b98bd37bdd6bbeb86d30b038b5d781899f43ab 100644 (file)
@@ -60,6 +60,7 @@ IF(ENABLE_TEST)
     test_read_format_isojoliet_long.c
     test_read_format_isojoliet_rr.c
     test_read_format_isorr_bz2.c
+    test_read_format_isorr_ce.c
     test_read_format_isorr_new_bz2.c
     test_read_format_isozisofs_bz2.c
     test_read_format_mtree.c
index 31d75fba2e71364ee5658dcef5716483ef1a5fbe..4f70859b83dfe2fc46610e1fb3be22d947952308 100644 (file)
@@ -55,6 +55,7 @@ TESTS= \
        test_read_format_isojoliet_long.c       \
        test_read_format_isojoliet_rr.c         \
        test_read_format_isorr_bz2.c            \
+       test_read_format_isorr_ce.c             \
        test_read_format_isorr_new_bz2.c        \
        test_read_format_isozisofs_bz2.c        \
        test_read_format_mtree.c                \
diff --git a/libarchive/test/test_read_format_isorr_ce.c b/libarchive/test/test_read_format_isorr_ce.c
new file mode 100644 (file)
index 0000000..81c8c18
--- /dev/null
@@ -0,0 +1,229 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+/*
+Execute the following command to rebuild the data for this program:
+   tail -n +32 test_read_format_isorr_ce.c | /bin/sh
+
+dirname=/tmp/iso
+#
+rm -rf $dirname
+mkdir $dirname
+#
+num=0
+file=""
+while [ $num -lt 150 ]
+do
+  num=$((num+1))
+  file="a$file"
+done
+#
+num=0
+while [ $num -lt 3 ]
+do
+  num=$((num+1))
+  file="a$file"
+  echo "hello $((num+150))" > $dirname/$file
+  dd if=/dev/zero count=1 bs=4080 >> $dirname/$file
+  (cd $dirname; ln -s $file sym$num)
+done
+#
+mkdir $dirname/dir
+#
+time1="197001020000.01"
+time2="197001030000.02"
+TZ=utc touch -afhm -t $time1 $dirname/dir $dirname/aaaa*
+TZ=utc touch -afhm -t $time2 $dirname/sym*
+TZ=utc touch -afhm -t $time1 $dirname
+#
+F=test_read_format_isorr_ce.iso.bz2
+mkisofs -R -uid 1 -gid 2 $dirname | bzip2 > $F
+uuencode $F $F > $F.uu
+rm -rf $dirname
+exit 1
+ */
+
+/*
+ * Test reading SUSP "CE" extension is works fine.
+ */
+
+static void
+mkpath(char *p, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++)
+               p[i] = 'a';
+       p[len] = '\0';
+}
+
+DEFINE_TEST(test_read_format_isorr_ce)
+{
+       const char *refname = "test_read_format_isorr_ce.iso.bz2";
+       char path1[160];
+       char path2[160];
+       char path3[160];
+       struct archive_entry *ae;
+       struct archive *a;
+       const void *p;
+       size_t size;
+       off_t offset;
+       int i;
+       int r;
+
+       mkpath(path1, 151);
+       mkpath(path2, 152);
+       mkpath(path3, 153);
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_compression_bzip2(a);
+       if (r == ARCHIVE_WARN) {
+               skipping("bzip2 reading not fully supported on this platform");
+               assertEqualInt(0, archive_read_finish(a));
+               return;
+       }
+       assertEqualInt(0, r);
+       assertEqualInt(0, archive_read_support_format_all(a));
+       assertEqualInt(ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Retrieve each of the 8 files on the ISO image and
+        * verify that each one is what we expect. */
+       for (i = 0; i < 8; ++i) {
+               assertEqualInt(0, archive_read_next_header(a, &ae));
+
+               if (strcmp(".", archive_entry_pathname(ae)) == 0) {
+                       /* '.' root directory. */
+                       assertEqualInt(AE_IFDIR, archive_entry_filetype(ae));
+                       assertEqualInt(2048, archive_entry_size(ae));
+                       /* Now, we read timestamp recorded by RRIP "TF". */
+                       assertEqualInt(86401, archive_entry_mtime(ae));
+                       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+                       /* Now, we read links recorded by RRIP "PX". */
+                       assertEqualInt(3, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualIntA(a, ARCHIVE_EOF,
+                           archive_read_data_block(a, &p, &size, &offset));
+                       assertEqualInt((int)size, 0);
+               } else if (strcmp("dir", archive_entry_pathname(ae)) == 0) {
+                       /* A directory. */
+                       assertEqualString("dir", archive_entry_pathname(ae));
+                       assertEqualInt(AE_IFDIR, archive_entry_filetype(ae));
+                       assertEqualInt(2048, archive_entry_size(ae));
+                       assertEqualInt(86401, archive_entry_mtime(ae));
+                       assertEqualInt(86401, archive_entry_atime(ae));
+                       assertEqualInt(2, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualInt(2, archive_entry_gid(ae));
+               } else if (strcmp(path1, archive_entry_pathname(ae)) == 0) {
+                       /* A regular file. */
+                       assertEqualString(path1, archive_entry_pathname(ae));
+                       assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
+                       assertEqualInt(4090, archive_entry_size(ae));
+                       assertEqualInt(0,
+                           archive_read_data_block(a, &p, &size, &offset));
+                       assertEqualInt(0, offset);
+                       assertEqualMem(p, "hello 151\n", 10);
+                       assertEqualInt(86401, archive_entry_mtime(ae));
+                       assertEqualInt(86401, archive_entry_atime(ae));
+                       assertEqualInt(1, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualInt(2, archive_entry_gid(ae));
+               } else if (strcmp(path2, archive_entry_pathname(ae)) == 0) {
+                       /* A regular file. */
+                       assertEqualString(path2, archive_entry_pathname(ae));
+                       assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
+                       assertEqualInt(4090, archive_entry_size(ae));
+                       assertEqualInt(0,
+                           archive_read_data_block(a, &p, &size, &offset));
+                       assertEqualInt(0, offset);
+                       assertEqualMem(p, "hello 152\n", 10);
+                       assertEqualInt(86401, archive_entry_mtime(ae));
+                       assertEqualInt(86401, archive_entry_atime(ae));
+                       assertEqualInt(1, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualInt(2, archive_entry_gid(ae));
+               } else if (strcmp(path3, archive_entry_pathname(ae)) == 0) {
+                       /* A regular file. */
+                       assertEqualString(path3, archive_entry_pathname(ae));
+                       assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
+                       assertEqualInt(4090, archive_entry_size(ae));
+                       assertEqualInt(0,
+                           archive_read_data_block(a, &p, &size, &offset));
+                       assertEqualInt(0, offset);
+                       assertEqualMem(p, "hello 153\n", 10);
+                       assertEqualInt(86401, archive_entry_mtime(ae));
+                       assertEqualInt(86401, archive_entry_atime(ae));
+                       assertEqualInt(1, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualInt(2, archive_entry_gid(ae));
+               } else if (strcmp("sym1", archive_entry_pathname(ae)) == 0) {
+                       /* A symlink to the regular file. */
+                       assertEqualInt(AE_IFLNK, archive_entry_filetype(ae));
+                       assertEqualString(path1, archive_entry_symlink(ae));
+                       assertEqualInt(0, archive_entry_size(ae));
+                       assertEqualInt(172802, archive_entry_mtime(ae));
+                       assertEqualInt(1, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualInt(2, archive_entry_gid(ae));
+               } else if (strcmp("sym2", archive_entry_pathname(ae)) == 0) {
+                       /* A symlink to the regular file. */
+                       assertEqualInt(AE_IFLNK, archive_entry_filetype(ae));
+                       assertEqualString(path2, archive_entry_symlink(ae));
+                       assertEqualInt(0, archive_entry_size(ae));
+                       assertEqualInt(172802, archive_entry_mtime(ae));
+                       assertEqualInt(1, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualInt(2, archive_entry_gid(ae));
+               } else if (strcmp("sym3", archive_entry_pathname(ae)) == 0) {
+                       /* A symlink to the regular file. */
+                       assertEqualInt(AE_IFLNK, archive_entry_filetype(ae));
+                       assertEqualString(path3, archive_entry_symlink(ae));
+                       assertEqualInt(0, archive_entry_size(ae));
+                       assertEqualInt(172802, archive_entry_mtime(ae));
+                       assertEqualInt(1, archive_entry_stat(ae)->st_nlink);
+                       assertEqualInt(1, archive_entry_uid(ae));
+                       assertEqualInt(2, archive_entry_gid(ae));
+               } else {
+                       failure("Saw a file that shouldn't have been there");
+                       assertEqualString(archive_entry_pathname(ae), "");
+               }
+       }
+
+       /* End of archive. */
+       assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_BZIP2);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660_ROCKRIDGE);
+
+       /* Close the archive. */
+       assertEqualInt(0, archive_read_close(a));
+       assertEqualInt(0, archive_read_finish(a));
+}
+
+
diff --git a/libarchive/test/test_read_format_isorr_ce.iso.bz2.uu b/libarchive/test/test_read_format_isorr_ce.iso.bz2.uu
new file mode 100644 (file)
index 0000000..eeb23b0
--- /dev/null
@@ -0,0 +1,27 @@
+begin 644 test_read_format_isorr_ce.iso.bz2
+M0EIH.3%!629368!]L?(``/)__?_]B_[59___/___Z[[GGF8@JT"`)`!!@2``
+M1`/5&-`#WJ%!#5H88,4(%,TF3U,FB;331&FU&FAH:&FC-$-`80!Z0TP3TFU'
+MJ-!IIY)H-$`@GHC(-"@&FTU`]08-0T`&(P1D``!IH]!#"&@@P```````````
+M`````````"#`````````````````````"))32>DIZGBCR:GM1J'ZD:,FU-'H
+MU`T#:AY0VIIA-!D`8-31Z@8@VD>I"\<UJ[/UT[#NMYA0,2.4Z/`72'Q@T\9P
+MC4-G:]\:FKK?X6N#AN6[=N2]DJX!`EH2DP(A(I%$"-`'GH`#D1'7JCLP`=$X
+MY0`+_4!`0*R4N;E#X^8@A=$!%3(,&I$8@A(`Q@H"I=%=4!NH%:8*6M&4."/%
+MFMWMPNP5N^;;>D+BCE1"L0S.SJU*I)&X2C>:$0@D7O%QXIQ3,-`70.=$SL,(
+M.GJ:K<=^:V%HI9E"AA&8#"#V&].^X%.FHW!H1\Y306@'U3NUVLL_FU#[:FTV
+M]R@/"+E-(<TPH!8!E,1A$D)"1,4!WRT)"4864W!=F8!R*3G"'+PMATH/VS$`
+MY&SSOC\BW[B'3R[1Z%<,0R"&!3??#=G;Y=L^)&J*7",&B$(%3AJ2T)Q5JHR%
+MST4!5CUW!\/$9B(P!%/GMH?3],S3K!WIW)L[S1RFL^4S?L/N13S:^WGOV'['
+M:985&:_!*>LOD<JH*@*(QF`H,8`B$@D$%48D4\7HQ(2&ST(Y3*8I4XU73;_>
+M$,D%U$PBDD4)W4F!DH!P?4LLVG%P.S-8*.06M+Q8!IH.R=L%^%Z*]"FB6ZTF
+M@C.%%2.!5X$`P2%*A[C+&@!H`0'FLE-"O`;L#+(RR!H&J*.]>8*4NHUMT1\Q
+M:G5<?;&0[-Q&;-P?5&.+<$,G0#@@TQ6`]+<$Y7VL-QSRY2FC5:@B.*O"'T+8
+M[8MG5,6H34+S/P8\!?K/,=`=P-,(MZF(S4ULS'?8C.FZ054Y'57%9G4Q?$2'
+MK:)S8$BD`H*AC1'%0)5BF=M%@X<`0QA3$2+(OD:Y9ZV]3@)D!&&/_7\*"UCJ
+M)P<VE(^PBK:4T*4%VC,(.L&3L1-X$\HPQ4YE$8XW3X^[]0,KGWH&R'[KJ@A1
+M$*]&`P@C")4IKW*DR7+4M8CX4P-L@)+[<)I;'9"?HLQ8)@>$6'&J^YNOB;#`
+MI*89JF8QSZQAAA;^U9L[K4*UK/OH@A?+"TD,#IO7:J*2L;^$D3X#A(5#L8CR
+MV5!$>6@`:9^WWX/P99?($AO.$1=_*X3DS1K_W^5ZT1XCE`@6S'J<FQ<@.E)V
+M5";F->X<1>=H'!`:=%@#$:1M1F<7CG:)P82U.K.Y'._SOT:N]?X&WII>F7&&
+E5#?E1DF9X+6.9L;'_.!4$4QOU#OP'FFV;9_XNY(IPH2$`^V/D```
+`
+end