Author: kientzle
Date: Wed Dec 30 05:30:35 2009
New Revision: 201246
URL: http://svn.freebsd.org/changeset/base/201246

Log:
  Merge Michihiro NAKAJIMA's significant work on the ISO9660 reader
  from googlecode:
   * Support for zisofs compressed entries
   * Support for relocated deep directories
   * Direct calculation of link counts for accurate nlink values
     even on images that lack Rockridge extensions
   * Faster handling of the internal file lists.
   * Better detection of ISO variants

Modified:
  head/lib/libarchive/archive_read_support_format_iso9660.c

Modified: head/lib/libarchive/archive_read_support_format_iso9660.c
==============================================================================
--- head/lib/libarchive/archive_read_support_format_iso9660.c   Wed Dec 30 
03:59:45 2009        (r201245)
+++ head/lib/libarchive/archive_read_support_format_iso9660.c   Wed Dec 30 
05:30:35 2009        (r201246)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2009 Andreas Henriksson <andr...@fatal.se>
+ * Copyright (c) 2009 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -39,8 +40,12 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #endif
 #include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
 
 #include "archive.h"
+#include "archive_endian.h"
 #include "archive_entry.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
@@ -73,6 +78,8 @@ __FBSDID("$FreeBSD$");
  * the file body.  This strategy allows us to read most compliant
  * CDs with a single pass through the data, as required by libarchive.
  */
+#define        LOGICAL_BLOCK_SIZE      2048
+#define        SYSTEM_AREA_BLOCK       16
 
 /* Structure of on-disk primary volume descriptor. */
 #define PVD_type_offset 0
@@ -158,6 +165,8 @@ __FBSDID("$FreeBSD$");
 #define SVD_version_offset (SVD_id_offset + SVD_id_size)
 #define SVD_version_size 1
 /* ... */
+#define SVD_reserved1_offset   72
+#define SVD_reserved1_size     8
 #define SVD_volume_space_size_offset 80
 #define SVD_volume_space_size_size 8
 #define SVD_escape_sequences_offset (SVD_volume_space_size_offset + 
SVD_volume_space_size_size)
@@ -165,9 +174,16 @@ __FBSDID("$FreeBSD$");
 /* ... */
 #define SVD_logical_block_size_offset 128
 #define SVD_logical_block_size_size 4
+#define SVD_type_L_path_table_offset 140
+#define SVD_type_M_path_table_offset 148
 /* ... */
 #define SVD_root_directory_record_offset 156
 #define SVD_root_directory_record_size 34
+#define SVD_file_structure_version_offset 881
+#define SVD_reserved2_offset   882
+#define SVD_reserved2_size     1
+#define SVD_reserved3_offset   1395
+#define SVD_reserved3_size     653
 /* ... */
 /* FIXME: validate correctness of last SVD entry offset. */
 
@@ -198,60 +214,145 @@ __FBSDID("$FreeBSD$");
 #define DR_name_len_size 1
 #define DR_name_offset 33
 
+#ifdef HAVE_ZLIB_H
+static const unsigned char zisofs_magic[8] = {
+       0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
+};
+
+struct zisofs {
+       /* Set 1 if this file compressed by paged zlib */
+       int              pz;
+       int              pz_log2_bs; /* Log2 of block size */
+       uint64_t         pz_uncompressed_size;
+
+       int              initialized;
+       unsigned char   *uncompressed_buffer;
+       size_t           uncompressed_buffer_size;
+
+       uint32_t         pz_offset;
+       unsigned char    header[16];
+       size_t           header_avail;
+       int              header_passed;
+       unsigned char   *block_pointers;
+       size_t           block_pointers_alloc;
+       size_t           block_pointers_size;
+       size_t           block_pointers_avail;
+       size_t           block_off;
+       uint32_t         block_avail;
+
+       z_stream         stream;
+       int              stream_valid;
+};
+#else
+struct zisofs {
+       /* Set 1 if this file compressed by paged zlib */
+       int              pz;
+};
+#endif
+
+struct content {
+       uint64_t         offset;/* Offset on disk.              */
+       uint64_t         size;  /* File size in bytes.          */
+       struct content  *next;
+};
+
 /* In-memory storage for a directory record. */
 struct file_info {
        struct file_info        *parent;
+       struct file_info        *next;
        int              refcount;
-       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 */
-       time_t           birthtime; /* File created time. */
-       time_t           mtime; /* File last modified time. */
-       time_t           atime; /* File last accessed time. */
-       time_t           ctime; /* File attribute change time. */
-       uint64_t         rdev; /* Device number */
+       int              subdirs;
+       uint64_t         key;           /* Heap Key.                    */
+       uint64_t         offset;        /* Offset on disk.              */
+       uint64_t         size;          /* File size in bytes.          */
+       uint32_t         ce_offset;     /* Offset of CE.                */
+       uint32_t         ce_size;       /* Size of CE.                  */
+       char             re;            /* Having RRIP "RE" extension.  */
+       uint64_t         cl_offset;     /* Having RRIP "CL" extension.  */
+       int              birthtime_is_set;
+       time_t           birthtime;     /* File created time.           */
+       time_t           mtime;         /* File last modified time.     */
+       time_t           atime;         /* File last accessed time.     */
+       time_t           ctime;         /* File attribute change time.  */
+       uint64_t         rdev;          /* Device number.               */
        mode_t           mode;
        uid_t            uid;
        gid_t            gid;
-       ino_t            inode;
+       int64_t          number;
        int              nlinks;
        struct archive_string name; /* Pathname */
        char             name_continues; /* Non-zero if name continues */
        struct archive_string symlink;
        char             symlink_continues; /* Non-zero if link continues */
+       /* Set 1 if this file compressed by paged zlib(zisofs) */
+       int              pz;
+       int              pz_log2_bs; /* Log2 of block size */
+       uint64_t         pz_uncompressed_size;
+       /* Set 1 if this file is multi extent. */
+       int              multi_extent;
+       struct {
+               struct content  *first;
+               struct content  **last;
+       } contents;
+       char             exposed;
 };
 
+struct heap_queue {
+       struct file_info **files;
+       int              allocated;
+       int              used;
+};
 
 struct iso9660 {
        int     magic;
 #define ISO9660_MAGIC   0x96609660
 
-       int option_ignore_joliet;
+       int opt_support_joliet;
+       int opt_support_rockridge;
 
        struct archive_string pathname;
-       char    seenRockridge; /* Set true if RR extensions are used. */
-       unsigned char   suspOffset;
+       char    seenRockridge;  /* Set true if RR extensions are used. */
+       char    seenSUSP;       /* Set true if SUSP is beging used. */
        char    seenJoliet;
 
-       uint64_t        previous_offset;
-       uint64_t        previous_size;
+       unsigned char   suspOffset;
+       struct file_info *rr_moved;
+       struct heap_queue                re_dirs;
+       struct heap_queue                cl_files;
+       struct read_ce_queue {
+               struct read_ce_req {
+                       uint64_t         offset;/* Offset of CE on disk. */
+                       struct file_info *file;
+               }               *reqs;
+               int              cnt;
+               int              allocated;
+       }       read_ce_req;
+
+       int64_t         previous_number;
        struct archive_string previous_pathname;
 
-       /* TODO: Make this a heap for fast inserts and deletions. */
-       struct file_info **pending_files;
-       int     pending_files_allocated;
-       int     pending_files_used;
+       struct heap_queue                pending_files;
+       struct {
+               struct file_info        *first;
+               struct file_info        **last;
+       }       cache_files;
 
        uint64_t current_position;
        ssize_t logical_block_size;
        uint64_t volume_size; /* Total size of volume in bytes. */
+       int32_t  volume_block;/* Total size of volume in logical blocks. */
+
+       struct vd {
+               int             location;       /* Location of Extent.  */
+               uint32_t        size;
+       } primary, joliet;
 
        off_t   entry_sparse_offset;
        int64_t entry_bytes_remaining;
+       struct zisofs    entry_zisofs;
+       struct content  *entry_content;
 };
 
-static void    add_entry(struct iso9660 *iso9660, struct file_info *file);
 static int     archive_read_format_iso9660_bid(struct archive_read *);
 static int     archive_read_format_iso9660_options(struct archive_read *,
                    const char *, const char *);
@@ -268,25 +369,48 @@ static void       dump_isodirrec(FILE *, const
 static time_t  time_from_tm(struct tm *);
 static time_t  isodate17(const unsigned char *);
 static time_t  isodate7(const unsigned char *);
+static int     isBootRecord(struct iso9660 *, const unsigned char *);
+static int     isVolumePartition(struct iso9660 *, const unsigned char *);
+static int     isVDSetTerminator(struct iso9660 *, const unsigned char *);
 static int     isJolietSVD(struct iso9660 *, const unsigned char *);
+static int     isSVD(struct iso9660 *, const unsigned char *);
+static int     isEVD(struct iso9660 *, const unsigned char *);
 static int     isPVD(struct iso9660 *, const unsigned char *);
-static struct file_info *next_entry(struct iso9660 *);
+static struct file_info *next_cache_entry(struct iso9660 *iso9660);
 static int     next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
                    struct file_info **pfile);
 static struct file_info *
-               parse_file_info(struct iso9660 *iso9660,
+               parse_file_info(struct archive_read *a,
                    struct file_info *parent, const unsigned char *isodirrec);
-static void    parse_rockridge(struct iso9660 *iso9660,
+static int     parse_rockridge(struct archive_read *a,
                    struct file_info *file, const unsigned char *start,
                    const unsigned char *end);
+static int     register_CE(struct archive_read *a, int32_t location,
+                   struct file_info *file);
+static int     read_CE(struct archive_read *a, struct iso9660 *iso9660);
 static void    parse_rockridge_NM1(struct file_info *,
                    const unsigned char *, int);
 static void    parse_rockridge_SL1(struct file_info *,
                    const unsigned char *, int);
 static void    parse_rockridge_TF1(struct file_info *,
                    const unsigned char *, int);
+static void    parse_rockridge_ZF1(struct file_info *,
+                   const unsigned char *, int);
 static void    release_file(struct iso9660 *, struct file_info *);
 static unsigned        toi(const void *p, int n);
+static inline void cache_add_entry(struct iso9660 *iso9660,
+                   struct file_info *file);
+static inline void cache_add_to_next_of_parent(struct iso9660 *iso9660,
+                   struct file_info *file);
+static inline struct file_info *cache_get_entry(struct iso9660 *iso9660);
+static void    heap_add_entry(struct heap_queue *heap,
+                   struct file_info *file, uint64_t key);
+static struct file_info *heap_get_entry(struct heap_queue *heap);
+
+#define add_entry(iso9660, file)       \
+       heap_add_entry(&((iso9660)->pending_files), file, file->offset)
+#define next_entry(iso9660)            \
+       heap_get_entry(&((iso9660)->pending_files))
 
 int
 archive_read_support_format_iso9660(struct archive *_a)
@@ -302,6 +426,12 @@ archive_read_support_format_iso9660(stru
        }
        memset(iso9660, 0, sizeof(*iso9660));
        iso9660->magic = ISO9660_MAGIC;
+       iso9660->cache_files.first = NULL;
+       iso9660->cache_files.last = &(iso9660->cache_files.first);
+       /* Enable to support Joliet extensions by default.      */
+       iso9660->opt_support_joliet = 1;
+       /* Enable to support Rock Ridge extensions by default.  */
+       iso9660->opt_support_rockridge = 1;
 
        r = __archive_read_register_format(a,
            iso9660,
@@ -325,10 +455,10 @@ static int
 archive_read_format_iso9660_bid(struct archive_read *a)
 {
        struct iso9660 *iso9660;
-       ssize_t bytes_read, brsvd;
+       ssize_t bytes_read;
        const void *h;
-       const unsigned char *p, *psvd;
-       int bid;
+       const unsigned char *p;
+       int seenTerminator;
 
        iso9660 = (struct iso9660 *)(a->format->data);
 
@@ -337,34 +467,56 @@ archive_read_format_iso9660_bid(struct a
         * 8 sectors of the volume descriptor table.  Of course,
         * if the I/O layer gives us more, we'll take it.
         */
-       h = __archive_read_ahead(a, 32768 + 8*2048, &bytes_read);
+#define RESERVED_AREA  (SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE)
+       h = __archive_read_ahead(a,
+           RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE,
+           &bytes_read);
        if (h == NULL)
            return (-1);
        p = (const unsigned char *)h;
 
        /* Skip the reserved area. */
-       bytes_read -= 32768;
-       p += 32768;
-
-       /* Check each volume descriptor to locate possible SVD with Joliet. */
-       for (brsvd = bytes_read, psvd = p;
-                       !iso9660->option_ignore_joliet && brsvd > 2048;
-                       brsvd -= 2048, psvd += 2048) {
-               bid = isJolietSVD(iso9660, psvd);
-               if (bid > 0)
-                       return (bid);
-               if (*p == '\177') /* End-of-volume-descriptor marker. */
-                       break;
-       }
+       bytes_read -= RESERVED_AREA;
+       p += RESERVED_AREA;
 
-       /* Check each volume descriptor to locate the PVD. */
-       for (; bytes_read > 2048; bytes_read -= 2048, p += 2048) {
-               bid = isPVD(iso9660, p);
-               if (bid > 0)
-                       return (bid);
-               if (*p == '\177') /* End-of-volume-descriptor marker. */
+       /* Check each volume descriptor. */
+       seenTerminator = 0;
+       for (; bytes_read > LOGICAL_BLOCK_SIZE;
+           bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) {
+               /* Do not handle undefined Volume Descriptor Type. */
+               if (p[0] >= 4 && p[0] <= 254)
+                       return (0);
+               /* Standard Identifier must be "CD001" */
+               if (memcmp(p + 1, "CD001", 5) != 0)
+                       return (0);
+               if (!iso9660->primary.location) {
+                       if (isPVD(iso9660, p))
+                               continue;
+               }
+               if (!iso9660->joliet.location) {
+                       if (isJolietSVD(iso9660, p))
+                               continue;
+               }
+               if (isBootRecord(iso9660, p))
+                       continue;
+               if (isEVD(iso9660, p))
+                       continue;
+               if (isSVD(iso9660, p))
+                       continue;
+               if (isVolumePartition(iso9660, p))
+                       continue;
+               if (isVDSetTerminator(iso9660, p)) {
+                       seenTerminator = 1;
                        break;
+               }
+               return (0);
        }
+       /*
+        * ISO 9660 format must have Primary Volume Descriptor and
+        * Volume Descriptor Set Terminator.
+        */
+       if (seenTerminator && iso9660->primary.location > 16)
+               return (48);
 
        /* We didn't find a valid PVD; return a bid of zero. */
        return (0);
@@ -383,9 +535,14 @@ archive_read_format_iso9660_options(stru
                                strcmp(val, "ignore") == 0 ||
                                strcmp(val, "disable") == 0 ||
                                strcmp(val, "0") == 0)
-                       iso9660->option_ignore_joliet = 1;
+                       iso9660->opt_support_joliet = 0;
                else
-                       iso9660->option_ignore_joliet = 0;
+                       iso9660->opt_support_joliet = 1;
+               return (ARCHIVE_OK);
+       }
+       if (strcmp(key, "rockridge") == 0 ||
+           strcmp(key, "Rockridge") == 0) {
+               iso9660->opt_support_rockridge = val != NULL;
                return (ARCHIVE_OK);
        }
 
@@ -396,17 +553,80 @@ archive_read_format_iso9660_options(stru
 }
 
 static int
-isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
+isBootRecord(struct iso9660 *iso9660, const unsigned char *h)
 {
-       struct file_info *file;
-       const unsigned char *p;
+       (void)iso9660; /* UNUSED */
 
-       /* Type 2 means it's a SVD. */
-       if (h[SVD_type_offset] != 2)
+       /* Type of the Volume Descriptor Boot Record must be 0. */
+       if (h[0] != 0)
+               return (0);
+
+       /* Volume Descriptor Version must be 1. */
+       if (h[6] != 1)
+               return (0);
+
+       return (1);
+}
+
+static int
+isVolumePartition(struct iso9660 *iso9660, const unsigned char *h)
+{
+       int32_t location;
+
+       /* Type of the Volume Partition Descriptor must be 3. */
+       if (h[0] != 3)
+               return (0);
+
+       /* Volume Descriptor Version must be 1. */
+       if (h[6] != 1)
+               return (0);
+       /* Unused Field */
+       if (h[7] != 0)
+               return (0);
+
+       location = archive_le32dec(h + 72);
+       if (location <= SYSTEM_AREA_BLOCK ||
+           location >= iso9660->volume_block)
+               return (0);
+       if ((uint32_t)location != archive_be32dec(h + 76))
+               return (0);
+
+       return (1);
+}
+
+static int
+isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
+{
+       int i;
+
+       (void)iso9660; /* UNUSED */
+
+       /* Type of the Volume Descriptor Set Terminator must be 255. */
+       if (h[0] != 255)
+               return (0);
+
+       /* Volume Descriptor Version must be 1. */
+       if (h[6] != 1)
                return (0);
 
-       /* ID must be "CD001" */
-       if (memcmp(h + SVD_id_offset, "CD001", 5) != 0)
+       /* Reserved field must be 0. */
+       for (i = 7; i < 2048; ++i)
+               if (h[i] != 0)
+                       return (0);
+
+       return (1);
+}
+
+static int
+isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+       const unsigned char *p;
+       ssize_t logical_block_size;
+       int32_t volume_block;
+
+       /* Check if current sector is a kind of Supplementary Volume
+        * Descriptor. */
+       if (!isSVD(iso9660, h))
                return (0);
 
        /* FIXME: do more validations according to joliet spec. */
@@ -431,23 +651,160 @@ isJolietSVD(struct iso9660 *iso9660, con
        } else /* not joliet */
                return (0);
 
-       iso9660->logical_block_size = toi(h + SVD_logical_block_size_offset, 2);
-       if (iso9660->logical_block_size <= 0)
+       logical_block_size =
+           archive_le16dec(h + SVD_logical_block_size_offset);
+       volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+
+       iso9660->logical_block_size = logical_block_size;
+       iso9660->volume_block = volume_block;
+       iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
+       /* Read Root Directory Record in Volume Descriptor. */
+       p = h + SVD_root_directory_record_offset;
+       iso9660->joliet.location = archive_le32dec(p + DR_extent_offset);
+       iso9660->joliet.size = archive_le32dec(p + DR_size_offset);
+
+       return (48);
+}
+
+static int
+isSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+       const unsigned char *p;
+       ssize_t logical_block_size;
+       int32_t volume_block;
+       int32_t location;
+       int i;
+
+       (void)iso9660; /* UNUSED */
+
+       /* Type 2 means it's a SVD. */
+       if (h[SVD_type_offset] != 2)
                return (0);
 
-       iso9660->volume_size = iso9660->logical_block_size
-           * (uint64_t)toi(h + SVD_volume_space_size_offset, 4);
+       /* Reserved field must be 0. */
+       for (i = 0; i < SVD_reserved1_size; ++i)
+               if (h[SVD_reserved1_offset + i] != 0)
+                       return (0);
+       for (i = 0; i < SVD_reserved2_size; ++i)
+               if (h[SVD_reserved2_offset + i] != 0)
+                       return (0);
+       for (i = 0; i < SVD_reserved3_size; ++i)
+               if (h[SVD_reserved3_offset + i] != 0)
+                       return (0);
 
-#if DEBUG
-       fprintf(stderr, "Joliet UCS-2 level %d with "
-                       "logical block size:%d, volume size:%d\n",
-                       iso9660->seenJoliet,
-                       iso9660->logical_block_size, iso9660->volume_size);
-#endif
+       /* File structure version must be 1 for ISO9660/ECMA119. */
+       if (h[SVD_file_structure_version_offset] != 1)
+               return (0);
+
+       logical_block_size =
+           archive_le16dec(h + SVD_logical_block_size_offset);
+       if (logical_block_size <= 0)
+               return (0);
+
+       volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+       if (volume_block <= SYSTEM_AREA_BLOCK+4)
+               return (0);
+
+       /* Location of Occurrence of Type L Path Table must be
+        * available location,
+        * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+       location = archive_le32dec(h+SVD_type_L_path_table_offset);
+       if (location <= SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+               return (0);
+
+       /* Location of Occurrence of Type M Path Table must be
+        * available location,
+        * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+       location = archive_be32dec(h+SVD_type_M_path_table_offset);
+       if (location <= SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+               return (0);
+
+       /* Read Root Directory Record in Volume Descriptor. */
+       p = h + SVD_root_directory_record_offset;
+       if (p[DR_length_offset] != 34)
+               return (0);
+
+       return (48);
+}
+
+static int
+isEVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+       const unsigned char *p;
+       ssize_t logical_block_size;
+       int32_t volume_block;
+       int32_t location;
+       int i;
+
+       (void)iso9660; /* UNUSED */
+
+       /* Type of the Enhanced Volume Descriptor must be 2. */
+       if (h[PVD_type_offset] != 2)
+               return (0);
+
+       /* EVD version must be 2. */
+       if (h[PVD_version_offset] != 2)
+               return (0);
+
+       /* Reserved field must be 0. */
+       if (h[PVD_reserved1_offset] != 0)
+               return (0);
+
+       /* Reserved field must be 0. */
+       for (i = 0; i < PVD_reserved2_size; ++i)
+               if (h[PVD_reserved2_offset + i] != 0)
+                       return (0);
+
+       /* Reserved field must be 0. */
+       for (i = 0; i < PVD_reserved3_size; ++i)
+               if (h[PVD_reserved3_offset + i] != 0)
+                       return (0);
+
+       /* Logical block size must be > 0. */
+       /* I've looked at Ecma 119 and can't find any stronger
+        * restriction on this field. */
+       logical_block_size =
+           archive_le16dec(h + PVD_logical_block_size_offset);
+       if (logical_block_size <= 0)
+               return (0);
+
+       volume_block =
+           archive_le32dec(h + PVD_volume_space_size_offset);
+       if (volume_block <= SYSTEM_AREA_BLOCK+4)
+               return (0);
+
+       /* File structure version must be 2 for ISO9660:1999. */
+       if (h[PVD_file_structure_version_offset] != 2)
+               return (0);
+
+       /* Location of Occurrence of Type L Path Table must be
+        * available location,
+        * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+       location = archive_le32dec(h+PVD_type_1_path_table_offset);
+       if (location <= SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+               return (0);
+
+       /* Location of Occurrence of Type M Path Table must be
+        * available location,
+        * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+       location = archive_be32dec(h+PVD_type_m_path_table_offset);
+       if (location <= SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+               return (0);
+
+       /* Reserved field must be 0. */
+       for (i = 0; i < PVD_reserved4_size; ++i)
+               if (h[PVD_reserved4_offset + i] != 0)
+                       return (0);
 
-       /* Store the root directory in the pending list. */
-       file = parse_file_info(iso9660, NULL, h + 
SVD_root_directory_record_offset);
-       add_entry(iso9660, file);
+       /* Reserved field must be 0. */
+       for (i = 0; i < PVD_reserved5_size; ++i)
+               if (h[PVD_reserved5_offset + i] != 0)
+                       return (0);
+
+       /* Read Root Directory Record in Volume Descriptor. */
+       p = h + PVD_root_directory_record_offset;
+       if (p[DR_length_offset] != 34)
+               return (0);
 
        return (48);
 }
@@ -455,17 +812,16 @@ isJolietSVD(struct iso9660 *iso9660, con
 static int
 isPVD(struct iso9660 *iso9660, const unsigned char *h)
 {
-       struct file_info *file;
+       const unsigned char *p;
+       ssize_t logical_block_size;
+       int32_t volume_block;
+       int32_t location;
        int i;
 
        /* Type of the Primary Volume Descriptor must be 1. */
        if (h[PVD_type_offset] != 1)
                return (0);
 
-       /* ID must be "CD001" */
-       if (memcmp(h + PVD_id_offset, "CD001", 5) != 0)
-               return (0);
-
        /* PVD version must be 1. */
        if (h[PVD_version_offset] != 1)
                return (0);
@@ -487,17 +843,32 @@ isPVD(struct iso9660 *iso9660, const uns
        /* Logical block size must be > 0. */
        /* I've looked at Ecma 119 and can't find any stronger
         * restriction on this field. */
-       iso9660->logical_block_size = toi(h + PVD_logical_block_size_offset, 2);
-       if (iso9660->logical_block_size <= 0)
+       logical_block_size =
+           archive_le16dec(h + PVD_logical_block_size_offset);
+       if (logical_block_size <= 0)
                return (0);
 
-       iso9660->volume_size = iso9660->logical_block_size
-           * (uint64_t)toi(h + PVD_volume_space_size_offset, 4);
+       volume_block = archive_le32dec(h + PVD_volume_space_size_offset);
+       if (volume_block <= SYSTEM_AREA_BLOCK+4)
+               return (0);
 
        /* File structure version must be 1 for ISO9660/ECMA119. */
        if (h[PVD_file_structure_version_offset] != 1)
                return (0);
 
+       /* Location of Occurrence of Type L Path Table must be
+        * available location,
+        * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+       location = archive_le32dec(h+PVD_type_1_path_table_offset);
+       if (location <= SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+               return (0);
+
+       /* Location of Occurrence of Type M Path Table must be
+        * available location,
+        * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+       location = archive_be32dec(h+PVD_type_m_path_table_offset);
+       if (location <= SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+               return (0);
 
        /* Reserved field must be 0. */
        for (i = 0; i < PVD_reserved4_size; ++i)
@@ -512,19 +883,239 @@ isPVD(struct iso9660 *iso9660, const uns
        /* XXX TODO: Check other values for sanity; reject more
         * malformed PVDs. XXX */
 
-       /* Store the root directory in the pending list. */
-       file = parse_file_info(iso9660, NULL, h + 
PVD_root_directory_record_offset);
-       add_entry(iso9660, file);
+       /* Read Root Directory Record in Volume Descriptor. */
+       p = h + PVD_root_directory_record_offset;
+       if (p[DR_length_offset] != 34)
+               return (0);
+
+       iso9660->logical_block_size = logical_block_size;
+       iso9660->volume_block = volume_block;
+       iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
+       iso9660->primary.location = archive_le32dec(p + DR_extent_offset);
+       iso9660->primary.size = archive_le32dec(p + DR_size_offset);
+
        return (48);
 }
 
 static int
+read_children(struct archive_read *a, struct file_info *parent)
+{
+       struct iso9660 *iso9660;
+       const unsigned char *b, *p;
+       struct file_info *multi;
+       size_t step;
+
+       iso9660 = (struct iso9660 *)(a->format->data);
+       if (iso9660->current_position > parent->offset) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Ignoring out-of-order directory (%s) %jd > %jd",
+                   parent->name.s,
+                   iso9660->current_position,
+                   parent->offset);
+               return (ARCHIVE_WARN);
+       }
+       if (parent->offset + parent->size > iso9660->volume_size) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Directory is beyond end-of-media: %s",
+                   parent->name);
+               return (ARCHIVE_WARN);
+       }
+       if (iso9660->current_position < parent->offset) {
+               int64_t skipsize;
+
+               skipsize = parent->offset - iso9660->current_position;
+               skipsize = __archive_read_skip(a, skipsize);
+               if (skipsize < 0)
+                       return ((int)skipsize);
+               iso9660->current_position = parent->offset;
+       }
+
+       step = ((parent->size + iso9660->logical_block_size -1) /
+           iso9660->logical_block_size) * 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);
+       }
+       __archive_read_consume(a, step);
+       iso9660->current_position += step;
+       multi = NULL;
+       while (step) {
+               p = b;
+               b += iso9660->logical_block_size;
+               step -= iso9660->logical_block_size;
+               for (; *p != 0 && p < b && p + *p <= b; p += *p) {
+                       struct file_info *child;
+
+                       /* N.B.: these special directory identifiers
+                        * are 8 bit "values" even on a
+                        * Joliet CD with UCS-2 (16bit) encoding.
+                        */
+
+                       /* Skip '.' entry. */
+                       if (*(p + DR_name_len_offset) == 1
+                           && *(p + DR_name_offset) == '\0')
+                               continue;
+                       /* Skip '..' entry. */
+                       if (*(p + DR_name_len_offset) == 1
+                           && *(p + DR_name_offset) == '\001')
+                               continue;
+                       child = parse_file_info(a, parent, p);
+                       if (child == NULL)
+                               return (ARCHIVE_FATAL);
+                       if (child->cl_offset)
+                               heap_add_entry(&(iso9660->cl_files),
+                                   child, child->cl_offset);
+                       else {
+                               if (child->multi_extent || multi != NULL) {
+                                       struct content *con;
+
+                                       if (multi == NULL) {
+                                               multi = child;
+                                               multi->contents.first = NULL;
+                                               multi->contents.last =
+                                                   &(multi->contents.first);
+                                       }
+                                       con = malloc(sizeof(struct content));
+                                       if (con == NULL) {
+                                               release_file(iso9660, child);
+                                               archive_set_error(
+                                                   &a->archive, ENOMEM,
+                                                   "No memory for "
+                                                   "multi extent");
+                                               return (ARCHIVE_FATAL);
+                                       }
+                                       con->offset = child->offset;
+                                       con->size = child->size;
+                                       con->next = NULL;
+                                       *multi->contents.last = con;
+                                       multi->contents.last = &(con->next);
+                                       if (multi == child)
+                                               add_entry(iso9660, child);
+                                       else {
+                                               multi->size += child->size;
+                                               if (!child->multi_extent)
+                                                       multi = NULL;
+                                               release_file(iso9660, child);
+                                       }
+                               } else
+                                       add_entry(iso9660, child);
+                       }
+               }
+       }
+
+       /* Read data which recorded by RRIP "CE" extension. */
+       if (read_CE(a, iso9660) != ARCHIVE_OK)
+               return (ARCHIVE_FATAL);
+
+       return (ARCHIVE_OK);
+}
+
+static int
+relocate_dir(struct iso9660 *iso9660, struct file_info *file)
+{
+       struct file_info *re;
+
+       re = heap_get_entry(&(iso9660->re_dirs));
+       while (re != NULL && re->offset < file->cl_offset) {
+               /* This case is wrong pattern.
+                * But dont't reject this directory entry to be robust. */
+               cache_add_entry(iso9660, re);
+               re = heap_get_entry(&(iso9660->re_dirs));
+       }
+       if (re == NULL)
+               /* This case is wrong pattern. */
+               return (0);
+       if (re->offset == file->cl_offset) {
+               re->parent->refcount--;
+               re->parent->subdirs--;
+               re->parent = file->parent;
+               re->parent->refcount++;
+               re->parent->subdirs++;
+               cache_add_to_next_of_parent(iso9660, re);
+               return (1);
+       } else
+               /* This case is wrong pattern. */
+               heap_add_entry(&(iso9660->re_dirs), re, re->offset);
+       return (0);
+}
+
+static int
+read_entries(struct archive_read *a)
+{
+       struct iso9660 *iso9660;
+       struct file_info *file;
+       int r;
+
+       iso9660 = (struct iso9660 *)(a->format->data);
+
+       while ((file = next_entry(iso9660)) != NULL &&
+           (file->mode & AE_IFMT) == AE_IFDIR) {
+               r = read_children(a, file);
+               if (r != ARCHIVE_OK)
+                       return (r);
+
+               if (iso9660->seenRockridge &&
+                   file->parent != NULL &&
+                   file->parent->parent == NULL &&
+                   iso9660->rr_moved == NULL &&
+                   (strcmp(file->name.s, "rr_moved") == 0 ||
+                    strcmp(file->name.s, ".rr_moved") == 0)) {
+                       iso9660->rr_moved = file;
+               } else if (file->re)
+                       heap_add_entry(&(iso9660->re_dirs), file,
+                           file->offset);
+               else
+                       cache_add_entry(iso9660, file);
+       }
+       if (file != NULL)
+               add_entry(iso9660, file);
+
+       if (iso9660->rr_moved != NULL) {
+               /*
+                * Relocate directory which rr_moved has.
+                */
+               while ((file = heap_get_entry(&(iso9660->cl_files))) != NULL) {
+                       relocate_dir(iso9660, file);
+                       release_file(iso9660, file);
+               }
+
+               /* If rr_moved directory still has children,
+                * Add rr_moved into pending_files to show
+                */
+               if (iso9660->rr_moved->subdirs) {
+                       cache_add_entry(iso9660, iso9660->rr_moved);
+                       /* If entries which have "RE" extension are still
+                        * remaining(this case is unlikely except ISO image
+                        * is broken), the entries won't be exposed. */
+                       while ((file = heap_get_entry(&(iso9660->re_dirs))) != 
NULL)
+                               cache_add_entry(iso9660, file);
+               } else {
+                       iso9660->rr_moved->parent->subdirs--;
+                       release_file(iso9660, iso9660->rr_moved);
+               }
+       } else {
+               /*
+                * In case ISO image is broken. If the name of rr_moved
+                * directory has been changed by damage, subdirectories
+                * of rr_moved entry won't be exposed.
+                */
+               while ((file = heap_get_entry(&(iso9660->re_dirs))) != NULL)
+                       cache_add_entry(iso9660, file);
+       }
+
+       return (ARCHIVE_OK);
+}
+
+static int
 archive_read_format_iso9660_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
        struct iso9660 *iso9660;
        struct file_info *file;
-       int r;
+       int r, rd_r;
 
        iso9660 = (struct iso9660 *)(a->format->data);
 
@@ -533,6 +1124,94 @@ archive_read_format_iso9660_read_header(
                a->archive.archive_format_name = "ISO9660";
        }
 
+       if (iso9660->current_position == 0) {
+               int64_t skipsize;
+               struct vd *vd;
+               const void *block;
+               char seenJoliet;
+
+               vd = &(iso9660->primary);
+               if (!iso9660->opt_support_joliet)
+                       iso9660->seenJoliet = 0;
+               if (iso9660->seenJoliet &&
+                       vd->location > iso9660->joliet.location)
+                       /* This condition is unlikely; by way of caution. */
+                       vd = &(iso9660->joliet);
+
+               skipsize = LOGICAL_BLOCK_SIZE * vd->location;
+               skipsize = __archive_read_skip(a, skipsize);
+               if (skipsize < 0)
+                       return ((int)skipsize);
+               iso9660->current_position = skipsize;
+
+               block = __archive_read_ahead(a, vd->size, NULL);
+               if (block == NULL) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "Failed to read full block when scanning "
+                           "ISO9660 directory list");
+                       return (ARCHIVE_FATAL);
+               }
+
+               /*
+                * While reading Root Directory, flag seenJoliet
+                * must be zero to avoid converting special name
+                * 0x00(Current Directory) and next byte to UCS2.
+                */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to