diff --git a/.bzrignore b/.bzrignore index e09f2f5..97762d1 100644 --- a/.bzrignore +++ b/.bzrignore @@ -30,3 +30,4 @@ demo/tree demo/ecma119tree demo/iso demo/catbuffer +demo/isoread diff --git a/Makefile.am b/Makefile.am index a141c36..04b4809 100644 --- a/Makefile.am +++ b/Makefile.am @@ -42,6 +42,7 @@ src_libisofs_la_SOURCES = \ src/buffer.c \ src/rockridge.h \ src/rockridge.c \ + src/rockridge_read.c \ src/data_source.c libinclude_HEADERS = \ src/libisofs.h @@ -55,7 +56,8 @@ noinst_PROGRAMS = \ demo/catbuffer \ demo/tree \ demo/ecma119tree \ - demo/iso + demo/iso \ + demo/isoread demo_lsl_CPPFLAGS = -Isrc demo_lsl_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) @@ -81,6 +83,10 @@ demo_iso_CPPFLAGS = -Isrc demo_iso_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) demo_iso_SOURCES = demo/iso.c +demo_isoread_CPPFLAGS = -Isrc +demo_isoread_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isoread_SOURCES = demo/iso_read.c + ## Build unit test diff --git a/demo/iso_read.c b/demo/iso_read.c new file mode 100644 index 0000000..cbf87a8 --- /dev/null +++ b/demo/iso_read.c @@ -0,0 +1,61 @@ +/* + * Little program to output the contents of an iso image. + * Note that this is not an API example, but a little program for test + * purposes. + */ + + +#include +#include +#include + +#include "messages.h" +#include "libisofs.h" +#include "fs_image.h" + +int main(int argc, char **argv) +{ + int result; + IsoImageFilesystem *fs; + IsoDataSource *src; + struct iso_read_opts opts = { + 0, /* block */ + 0, /* norock */ + 0, /* nojoliet */ + 0, /* preferjoliet */ + 0, /* uid; */ + 0, /* gid; */ + 0, /* mode */ + NULL, /* messenger */ + "UTF-8" /* input_charset */ + }; + + if (argc != 2) { + printf ("You need to specify a valid path\n"); + return 1; + } + + result = libiso_msgs_new(&opts.messenger, 0); + if (result <= 0) { + printf ("Can't create messenger\n"); + return 1; + } + libiso_msgs_set_severities(opts.messenger, LIBISO_MSGS_SEV_NEVER, + LIBISO_MSGS_SEV_ALL, "", 0); + + result = iso_data_source_new_from_file(argv[1], &src); + if (result < 0) { + printf ("Error creating data source\n"); + return 1; + } + + result = iso_image_filesystem_new(src, &opts, &fs); + if (result < 0) { + printf ("Error creating filesystem\n"); + return 1; + } + + iso_filesystem_unref((IsoFilesystem*)fs); + iso_data_source_unref(src); + return 0; +} diff --git a/src/error.h b/src/error.h index 70dfa6e..8c0fda3 100644 --- a/src/error.h +++ b/src/error.h @@ -47,6 +47,7 @@ /* image read errors */ #define ISO_WRONG_PVD -300 +#define ISO_WRONG_RR -301 #endif /*LIBISO_ERROR_H_*/ diff --git a/src/fs_image.c b/src/fs_image.c index 86819dc..0b75275 100644 --- a/src/fs_image.c +++ b/src/fs_image.c @@ -14,6 +14,8 @@ #include "fs_image.h" #include "error.h" #include "ecma119.h" +#include "messages.h" +#include "rockridge.h" #include #include @@ -34,14 +36,14 @@ typedef struct { /** DataSource from where data will be read */ IsoDataSource *src; - + /** * Counter of the times the filesystem has been openned still pending of * close. It is used to keep track of when we need to actually open or * close the IsoDataSource. */ unsigned int open_count; - + uid_t uid; /**< Default uid when no RR */ gid_t gid; /**< Default uid when no RR */ mode_t mode; /**< Default mode when no RR (only permissions) */ @@ -49,37 +51,37 @@ typedef struct struct libiso_msgs *messenger; char *input_charset; /**< Input charset for RR names */ - + /** * Will be filled with the block lba of the extend for the root directory, * as read from the PVM */ - uint32_t iso_root_block; + uint32_t iso_root_block; /** * If we need to read RR extensions. i.e., if the image contains RR * extensions, and the user wants to read them. */ - enum read_rr_ext rr; - + enum read_rr_ext rr; + /** * The function used to read the name from a directoy record. For ISO, * the name is in US-ASCII. For Joliet, in UCS-2BE. Thus, we need * different functions for both. */ char *(*get_name)(const char *, size_t); - + /** * Joliet and RR version 1.10 does not have file serial numbers, * we need to generate it. TODO what is this for?!?!?! */ //ino_t ino; - + /** * Bytes skipped within the System Use field of a directory record, before * the beginning of the SUSP system user entries. See IEEE 1281, SUSP. 5.3. */ - uint8_t len_skp; + uint8_t len_skp; /* Volume attributes */ char *volset_id; @@ -98,28 +100,31 @@ typedef struct * RR version being used in image. * 0 no RR extension, 1 RRIP 1.10, 2 RRIP 1.12 */ - unsigned int rr_version : 2; + enum read_rr_ext rr_version; + /** If Joliet extensions are available on image */ + unsigned int joliet : 1; + /** * Number of blocks of the volume, as reported in the PVM. */ uint32_t nblocks; - + //TODO el-torito information - + } _ImageFsData; static int ifs_fs_open(IsoImageFilesystem *fs) { _ImageFsData *data; - + if (fs == NULL || fs->fs.data == NULL) { return ISO_NULL_POINTER; } - + data = (_ImageFsData*)fs->fs.data; - + if (data->open_count == 0) { /* we need to actually open the data source */ int res = data->src->open(data->src); @@ -135,13 +140,13 @@ static int ifs_fs_close(IsoImageFilesystem *fs) { _ImageFsData *data; - + if (fs == NULL || fs->fs.data == NULL) { return ISO_NULL_POINTER; } - + data = (_ImageFsData*)fs->fs.data; - + if (--data->open_count == 0) { /* we need to actually close the data source */ return data->src->close(data->src); @@ -154,18 +159,18 @@ void ifs_fs_free(IsoFilesystem *fs) { IsoImageFilesystem *ifs; _ImageFsData *data; - + ifs = (IsoImageFilesystem*)fs; data = (_ImageFsData*) fs->data; - + /* close data source if already openned */ if (data->open_count > 0) { data->src->close(data->src); } - + /* free our ref to datasource */ iso_data_source_unref(data->src); - + /* free volume atts */ free(data->volset_id); free(data->volume_id); @@ -176,37 +181,165 @@ void ifs_fs_free(IsoFilesystem *fs) free(data->copyright_file_id); free(data->abstract_file_id); free(data->biblio_file_id); - + free(data->input_charset); free(data); } +/** + * Read the SUSP system user entries of the "." entry of the root directory, + * indentifying when Rock Ridge extensions are being used. + */ +static +int read_root_susp_entries(_ImageFsData *data, uint32_t block) +{ + int ret; + unsigned char buffer[2048]; + struct ecma119_dir_record *record; + struct susp_sys_user_entry *sue; + SuspIterator *iter; + + ret = data->src->read_block(data->src, block, buffer); + if (ret < 0) { + return ret; + } + + /* record will be the "." directory entry for the root record */ + record = (struct ecma119_dir_record *)buffer; + + /* + * TODO + * SUSP specification claims that for CD-ROM XA the SP entry + * is not at position BP 1, but at BP 15. Is that used? + * In that case, we need to set info->len_skp to 15!! + */ + + iter = susp_iter_new(data->src, record, data->len_skp, data->messenger); + if (iter == NULL) { + return ISO_MEM_ERROR; + } + + /* first entry must be an SP system use entry */ + ret = susp_iter_next(iter, &sue); + if (ret < 0) { + /* error */ + susp_iter_free(iter); + return ret; + } else if (ret == 0 || !SUSP_SIG(sue, 'S', 'P') ) { + iso_msg_debug(data->messenger, "SUSP/RR is not being used."); + susp_iter_free(iter); + return ISO_SUCCESS; + } + + /* it is a SP system use entry */ + if (sue->version[0] != 1 || sue->data.SP.be[0] != 0xBE + || sue->data.SP.ef[0] != 0xEF) { + + iso_msg_sorry(data->messenger, LIBISO_SUSP_WRONG, "SUSP SP system use " + "entry seems to be wrong. Ignoring Rock Ridge Extensions."); + susp_iter_free(iter); + return ISO_SUCCESS; + } + + iso_msg_debug(data->messenger, "SUSP/RR is being used."); + + /* + * The LEN_SKP field, defined in IEEE 1281, SUSP. 5.3, specifies the + * number of bytes to be skipped within each System Use field. + * I think this will be always 0, but given that support this standard + * feature is easy... + */ + data->len_skp = sue->data.SP.len_skp[0]; + + /* + * Ok, now search for ER entry. + * Just notice that the attributes for root dir are read elsewhere. + * + * TODO if several ER are present, we need to identify the position of + * what refers to RR, and then look for corresponding ES entry in + * each directory record. I have not implemented this (it's not used, + * no?), but if we finally need it, it can be easily implemented in + * the iterator, transparently for the rest of the code. + */ + while ((ret = susp_iter_next(iter, &sue)) > 0) { + + /* ignore entries from different version */ + if (sue->version[0] != 1) + continue; + + if (SUSP_SIG(sue, 'E', 'R')) { + + if (data->rr_version) { + iso_msg_warn(data->messenger, LIBISO_SUSP_MULTIPLE_ER, + "More than one ER has found. This is not supported. " + "It will be ignored, but can cause problems. " + "Please notify us about this."); + } + + /* + * it seems that Rock Ridge can be identified with any + * of the following + */ + if ( sue->data.ER.len_id[0] == 10 && + !strncmp((char*)sue->data.ER.ext_id, "RRIP_1991A", 10) ) { + + iso_msg_debug(data->messenger, + "Suitable Rock Ridge ER found. Version 1.10."); + data->rr_version = RR_EXT_110; + + } else if ( (sue->data.ER.len_id[0] == 10 && + !strncmp((char*)sue->data.ER.ext_id, "IEEE_P1282", 10)) + || (sue->data.ER.len_id[0] == 9 && + !strncmp((char*)sue->data.ER.ext_id, "IEEE_1282", 9)) ) { + + iso_msg_debug(data->messenger, + "Suitable Rock Ridge ER found. Version 1.12."); + data->rr_version = RR_EXT_112; + //TODO check also version? + } else { + iso_msg_warn(data->messenger, LIBISO_SUSP_MULTIPLE_ER, + "Not Rock Ridge ER found.\n" + "That will be ignored, but can cause problems in " + "image reading. Please notify us about this"); + } + } + } + + susp_iter_free(iter); + + if (ret < 0) { + return ret; + } + + return ISO_SUCCESS; +} + static int read_pvm(_ImageFsData *data, uint32_t block) { int ret; struct ecma119_pri_vol_desc *pvm; struct ecma119_dir_record *rootdr; - unsigned char buffer[BLOCK_SIZE]; - + uint8_t buffer[BLOCK_SIZE]; + /* read PVM */ ret = data->src->read_block(data->src, block, buffer); if (ret < 0) { return ret; } - - pvm = (struct ecma119_pri_vol_desc *)buffer; - - /* sanity checks */ - if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1 - || strncmp((char*)pvm->std_identifier, "CD001", 5) - || pvm->file_structure_version[0] != 1 ) { - return ISO_WRONG_PVD; + pvm = (struct ecma119_pri_vol_desc *)buffer; + + /* sanity checks */ + if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1 + || strncmp((char*)pvm->std_identifier, "CD001", 5) + || pvm->file_structure_version[0] != 1) { + + return ISO_WRONG_PVD; } - + /* ok, it is a valid PVD */ - + /* fill volume attributes */ data->volset_id = strcopy((char*)pvm->vol_set_id, 128); data->volume_id = strcopy((char*)pvm->volume_id, 32); @@ -217,12 +350,12 @@ int read_pvm(_ImageFsData *data, uint32_t block) data->copyright_file_id = strcopy((char*)pvm->copyright_file_id, 37); data->abstract_file_id = strcopy((char*)pvm->abstract_file_id, 37); data->biblio_file_id = strcopy((char*)pvm->bibliographic_file_id, 37); - + data->nblocks = iso_read_bb(pvm->vol_space_size, 4, NULL); - + rootdr = (struct ecma119_dir_record*) pvm->root_dir_record; data->iso_root_block = iso_read_bb(rootdr->block, 4, NULL); - + /* * TODO * PVM has other things that could be interesting, but that don't have a @@ -230,7 +363,7 @@ int read_pvm(_ImageFsData *data, uint32_t block) * could keep the creation date and update the modification date, for * example. */ - + return ISO_SUCCESS; } @@ -238,44 +371,46 @@ int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts, IsoImageFilesystem **fs) { int ret; + uint32_t block; IsoImageFilesystem *ifs; _ImageFsData *data; - + uint8_t buffer[BLOCK_SIZE]; + if (src == NULL || opts == NULL || fs == NULL) { return ISO_NULL_POINTER; } - + data = calloc(1, sizeof(_ImageFsData)); if (data == NULL) { return ISO_MEM_ERROR; } - + ifs = calloc(1, sizeof(IsoImageFilesystem)); if (ifs == NULL) { free(data); return ISO_MEM_ERROR; } - + /* get our ref to IsoDataSource */ data->src = src; iso_data_source_ref(src); data->open_count = 0; //TODO - + /* fill data from opts */ data->gid = opts->gid; data->uid = opts->uid; data->mode = opts->mode & ~S_IFMT; - data->input_charset = strdup("UTF-8"); //TODO strdup(opts->input_charset); + data->input_charset = strdup(opts->input_charset); data->messenger = opts->messenger; - + ifs->open = ifs_fs_open; ifs->close = ifs_fs_close; - + ifs->fs.data = data; ifs->fs.free = ifs_fs_free; - + /* read Volume Descriptors and ensure it is a valid image */ - + /* 1. first, open the filesystem */ ifs_fs_open(ifs); @@ -284,26 +419,102 @@ int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts, if (ret < 0) { goto fs_cleanup; } - + /* 3. read next volume descriptors */ - // TODO - + block = opts->block + 17; + do { + ret = src->read_block(src, block, buffer); + if (ret < 0) { + /* cleanup and exit */ + goto fs_cleanup; + } + switch (buffer[0]) { + case 0: + /* + * This is a boot record + * Here we handle el-torito + */ + //TODO add support for El-Torito + iso_msg_hint(data->messenger, LIBISO_UNSUPPORTED_VD, + "El-Torito extensions not supported yet"); + break; + case 2: + /* suplementary volume descritor */ + // TODO support Joliet + iso_msg_hint(data->messenger, LIBISO_UNSUPPORTED_VD, + "Joliet extensions not supported yet"); + break; + case 255: + /* + * volume set terminator + * ignore, as it's checked in loop end condition + */ + break; + default: + { + iso_msg_hint(data->messenger, LIBISO_UNSUPPORTED_VD, + "Ignoring Volume descriptor %x.", buffer[0]); + } + break; + } + block++; + } while (buffer[0] != 255); + /* 4. check if RR extensions are being used */ - //TODO - //ret = read_root_susp_entries(info, volume->root, data->iso_root_block); + ret = read_root_susp_entries(data, data->iso_root_block); if (ret < 0) { return ret; } + + /* user doesn't want to read RR extensions */ + if (opts->norock) { + data->rr = RR_EXT_NO; + } else { + data->rr = data->rr_version; + } /* select what tree to read */ - //TODO - + if (data->rr) { + /* RR extensions are available */ + if (opts->preferjoliet && data->joliet) { + /* if user prefers joliet, that is used */ + iso_msg_debug(data->messenger, "Reading Joliet extensions."); + //data->get_name = ucs2str; + data->rr = RR_EXT_NO; + //data->root_dir_block = joliet root + /* root_dir_block already contains root for joliet */ + //TODO add joliet support + goto fs_cleanup; + } else { + /* RR will be used */ + iso_msg_debug(data->messenger, "Reading Rock Ridge extensions."); + //data->root_dir_block = info.iso_root_block; + data->get_name = strcopy; + } + } else { + /* RR extensions are not available */ + if (opts->preferjoliet && data->joliet) { + /* joliet will be used */ + iso_msg_debug(data->messenger, "Reading Joliet extensions."); + //data->get_name = ucs2str; + //data->root_dir_block = joliet root + /* root_dir_block already contains root for joliet */ + //TODO add joliet support + goto fs_cleanup; + } else { + /* default to plain iso */ + iso_msg_debug(data->messenger, "Reading plain ISO-9660 tree."); + //data->root_dir_block = info.iso_root_block; + data->get_name = strcopy; + } + } + /* and finally return. Note that we keep the DataSource opened */ - + *fs = ifs; return ISO_SUCCESS; - -fs_cleanup: ; + + fs_cleanup: ; ifs_fs_free((IsoFilesystem*)ifs); free(ifs); return ret; diff --git a/src/fs_image.h b/src/fs_image.h index 2b4bbe5..9c7d8ae 100644 --- a/src/fs_image.h +++ b/src/fs_image.h @@ -42,12 +42,15 @@ struct iso_read_opts * different on a multisession disc. */ - unsigned int norock :1; /*< Do not read Rock Ridge extensions */ - // unsigned int nojoliet:1; /*< Do not read Joliet extensions */ - // unsigned int preferjoliet:1; - /*< When both Joliet and RR extensions are present, the RR - * tree is used. If you prefer using Joliet, set this to 1. */ + unsigned int norock : 1; /*< Do not read Rock Ridge extensions */ + unsigned int nojoliet : 1; /*< Do not read Joliet extensions */ + /** + * When both Joliet and RR extensions are present, the RR tree is used. + * If you prefer using Joliet, set this to 1. + */ + unsigned int preferjoliet : 1; + uid_t uid; /**< Default uid when no RR */ gid_t gid; /**< Default uid when no RR */ mode_t mode; /**< Default mode when no RR (only permissions) */ diff --git a/src/rockridge.h b/src/rockridge.h index b6c347c..4270ff3 100644 --- a/src/rockridge.h +++ b/src/rockridge.h @@ -27,6 +27,9 @@ #define LIBISO_ROCKRIDGE_H #include "ecma119.h" +#include "messages.h" + +#define SUSP_SIG(entry, a, b) ((entry->sig[0] == a) && (entry->sig[1] == b)) /** * This contains the information about the System Use Fields (SUSP, 4.1), @@ -51,6 +54,89 @@ struct susp_info uint8_t **ce_susp_fields; }; +/* SUSP 5.1 */ +struct susp_CE { + uint8_t block[8]; + uint8_t offset[8]; + uint8_t len[8]; +}; + +/* SUSP 5.3 */ +struct susp_SP { + uint8_t be[1]; + uint8_t ef[1]; + uint8_t len_skp[1]; +}; + +/* SUSP 5.5 */ +struct susp_ER { + uint8_t len_id[1]; + uint8_t len_des[1]; + uint8_t len_src[1]; + uint8_t ext_ver[1]; + uint8_t ext_id[1]; /*< up to len_id bytes */ + /* ext_des, ext_src */ +}; + +/** POSIX file attributes (RRIP, 4.1.1) */ +struct rr_PX { + uint8_t mode[8]; + uint8_t links[8]; + uint8_t uid[8]; + uint8_t gid[8]; + uint8_t serial[8]; +}; + +/** Time stamps for a file (RRIP, 4.1.6) */ +struct rr_TF { + uint8_t flags[1]; + uint8_t t_stamps[1]; +}; + +/** Info for character and block device (RRIP, 4.1.2) */ +struct rr_PN { + uint8_t high[8]; + uint8_t low[8]; +}; + +/** Alternate name (RRIP, 4.1.4) */ +struct rr_NM { + uint8_t flags[1]; + uint8_t name[1]; +}; + +/** Link for a relocated directory (RRIP, 4.1.5.1) */ +struct rr_CL { + uint8_t child_loc[8]; +}; + +/** Sim link (RRIP, 4.1.3) */ +struct rr_SL { + uint8_t flags[1]; + uint8_t comps[1]; +}; + +/** + * Struct for a SUSP System User Entry (SUSP, 4.1) + */ +struct susp_sys_user_entry +{ + uint8_t sig[2]; + uint8_t len_sue[1]; + uint8_t version[1]; + union { + struct susp_CE CE; + struct susp_SP SP; + struct susp_ER ER; + struct rr_PX PX; + struct rr_TF TF; + struct rr_PN PN; + struct rr_NM NM; + struct rr_CL CL; + struct rr_SL SL; + } data; /* 5 to 4+len_sue */ +}; + /** * Compute the length needed for write all RR and SUSP entries for a given * node. @@ -105,4 +191,34 @@ void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info, */ int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info); +/** + * The SUSP iterator is used to iterate over the System User Entries + * of a ECMA-168 directory record. + * It takes care about Continuation Areas, handles the end of the different + * system user entries and skip padding areas. Thus, using an iteration + * we are accessing just to the meaning entries. + */ +typedef struct susp_iterator SuspIterator; + +SuspIterator * +susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record, + uint8_t len_skp, IsoMessenger *msgr); + +/** + * Get the next SUSP System User Entry using given iterator. + * + * @param sue + * Pointer to the next susp entry. It refers to an internal buffer and + * it's not guaranteed to be allocated after calling susp_iter_next() + * again. Thus, if you need to keep some entry you have to do a copy. + * @return + * 1 on success, 0 if no more entries, < 0 error + */ +int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue); + +/** + * Free a given susp iterator. + */ +void susp_iter_free(SuspIterator *iter); + #endif /* LIBISO_ROCKRIDGE_H */ diff --git a/src/rockridge_read.c b/src/rockridge_read.c new file mode 100644 index 0000000..c0c6d70 --- /dev/null +++ b/src/rockridge_read.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +/* + * This file contains functions related to the reading of SUSP and + * Rock Ridge extensions on an ECMA-119 image. + */ + +#include "libisofs.h" +#include "ecma119.h" +#include "util.h" +#include "rockridge.h" +#include "error.h" + +#include +#include + +struct susp_iterator +{ + uint8_t* base; + int pos; + int size; + IsoDataSource *src; + IsoMessenger *msgr; + + /* block and offset for next continuation area */ + uint32_t ce_block; + uint32_t ce_off; + + /** Length of the next continuation area, 0 if no more CA are specified */ + uint32_t ce_len; + + uint8_t *buffer; /*< If there are continuation areas */ +}; + +SuspIterator* +susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record, + uint8_t len_skp, IsoMessenger *msgr) +{ + int pad = (record->len_fi[0] + 1) % 2; + struct susp_iterator *iter = malloc(sizeof(struct susp_iterator)); + if (iter == NULL) { + return NULL; + } + + iter->base = record->file_id + record->len_fi[0] + pad; + iter->pos = len_skp; /* 0 in most cases */ + iter->size = record->len_dr[0] - record->len_fi[0] - 33 - pad; + iter->src = src; + iter->msgr = msgr; + + iter->ce_len = 0; + iter->buffer = NULL; + + return iter; +} + +int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue) +{ + struct susp_sys_user_entry *entry; + + entry = (struct susp_sys_user_entry*)(iter->base + iter->pos); + + if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T'))) { + + /* + * End of the System Use Area or Continuation Area. + * Note that ST is not needed when the space left is less than 4. + * (IEEE 1281, SUSP. section 4) + */ + if (iter->ce_len) { + uint32_t block; + int nblocks; + + /* A CE has found, there is another continuation area */ + nblocks = div_up(iter->ce_off + iter->ce_len, BLOCK_SIZE); + iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE); + + /* read all blocks needed to cache the full CE */ + for (block = 0; block < nblocks; ++block) { + int ret; + ret = iter->src->read_block(iter->src, iter->ce_block + block, + iter->buffer + block * BLOCK_SIZE); + if (ret < 0) { + return ret; + } + } + iter->base = iter->buffer + iter->ce_off; + iter->pos = 0; + iter->size = iter->ce_len; + iter->ce_len = 0; + entry = (struct susp_sys_user_entry*)iter->base; + } else { + return 0; + } + } + + if (entry->len_sue[0] == 0) { + /* a wrong image with this lead us to a infinity loop */ + iso_msg_sorry(iter->msgr, LIBISO_RR_ERROR, + "Damaged RR/SUSP information."); + return ISO_WRONG_RR; + } + + iter->pos += entry->len_sue[0]; + + if (SUSP_SIG(entry, 'C', 'E')) { + /* Continuation entry */ + if (iter->ce_len) { + iso_msg_sorry(iter->msgr, LIBISO_RR_ERROR, "More than one CE " + "System user entry has found in a single System Use field or " + "continuation area. This breaks SUSP standard and it's not " + "supported. Ignoring last CE. Maybe the image is damaged."); + } else { + iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL); + iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL); + iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL); + } + + /* we don't want to return CE entry to the user */ + return susp_iter_next(iter, sue); + } else if (SUSP_SIG(entry, 'P', 'D')) { + /* skip padding */ + return susp_iter_next(iter, sue); + } + + *sue = entry; + return ISO_SUCCESS; +} + +void susp_iter_free(SuspIterator *iter) +{ + free(iter->buffer); + free(iter); +}