svn commit: r201246 - head/lib/libarchive
Tim Kientzle
kientzle at FreeBSD.org
Wed Dec 30 05:30:36 UTC 2009
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 <andreas at 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 ***
More information about the svn-src-head
mailing list