Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 30 Dec 2009 05:30:35 +0000 (UTC)
From:      Tim Kientzle <kientzle@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r201246 - head/lib/libarchive
Message-ID:  <200912300530.nBU5UZiS047852@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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@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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912300530.nBU5UZiS047852>