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>