diff --git a/TODO b/TODO index 36b1e44..d6032e0 100644 --- a/TODO +++ b/TODO @@ -10,9 +10,12 @@ FEATURES [ok] Joliet Merge RR and Joliet trees [ok] User options to customize reading - Read El-Torito info + [ok] Read El-Torito info [ok] Multisession [ok] DVD+RW image growing + El-Torito images from previous session + Add new el-torito image from prev. session file + Path for isolinux from prev. session UDF [ok] ISO relaxed contraints ISO 9660:1998 diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 0af05fd..13956cc 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -184,15 +184,9 @@ calc_dir_size(struct ecma119_write_target *t, for (i = 0; i < dir->info.dir.nchildren; i++) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; - //struct iso_tree_node *iso = ch->iso_self; if (ch->type == ECMA119_DIR) { calc_dir_size(t, ch); - } -// else if (iso && iso->attrib.st_size -// && iso->loc.type == LIBISO_FILESYS -// && iso->loc.path) { -// t->filelist_len++; -// } + } } } @@ -231,12 +225,11 @@ cmp_file(const void *f1, const void *f2) * Fill out the block field for each file and fill out t->filelist. */ static void -calc_file_pos(struct ecma119_write_target *t, - struct ecma119_tree_node *dir) +calc_file_pos(struct ecma119_write_target *t) { size_t i; - assert(dir->type == ECMA119_DIR); + assert(t); t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count); @@ -338,7 +331,17 @@ ecma119_target_new(struct iso_volset *volset, t->catalog = volset->volume[opts->volnum]->bootcat; t->eltorito = t->catalog ? 1 : 0; + t->write_eltorito = opts->copy_eltorito; + if (t->eltorito && (!t->ms_block || !t->catalog->proc) ) { + /* + * For new and modified images we always need to write el-torito. + * For ms images, if the boot catalog was newly added, we also need + * to write it! + */ + t->write_eltorito = 1; + } + /* create the trees */ t->root = ecma119_tree_create(t, iso_root); if (t->joliet) t->joliet_root = joliet_tree_create(t, iso_root); @@ -377,8 +380,8 @@ ecma119_target_new(struct iso_volset *volset, t->curblock = t->ms_block /* nwa for ms, usually 0 */ + 16 /* system area */ - + 1 /* volume desc */ - + 1; /* volume desc terminator */ + + 1 /* primary volume desc */ + + 1; /* volume desc terminator */ if (t->eltorito) t->curblock += 1; /* boot record volume descriptor */ @@ -411,17 +414,24 @@ ecma119_target_new(struct iso_volset *volset, /* el-torito? */ if (t->eltorito) { - - /* add catalog block */ - t->catalog->file->block = t->curblock; - t->curblock += div_up(2048, t->block_size); - el_torito_get_image_files(t); + if (t->write_eltorito) { + /* add catalog block */ + t->catblock = t->curblock; + t->curblock += div_up(2048, t->block_size); + + /* add img block */ + t->imgblock = t->curblock; + t->curblock += div_up(t->catalog->image->node->node.attrib.st_size, + t->block_size); + } else { + assert(t->ms_block); + assert(t->catalog->proc); + t->catblock = t->catalog->node->loc.block; + t->imgblock = t->catalog->image->node->loc.block; + } } - calc_file_pos(t, t->root); - - if (t->eltorito) - el_torito_patch_image_files(t); + calc_file_pos(t); if (t->rockridge) { susp_finalize(t, t->root); @@ -438,9 +448,6 @@ ecma119_target_new(struct iso_volset *volset, */ uint8_t *buf; - /* set the 32 block buffer to 0 */ - memset(opts->overwrite, 0, 32 * t->block_size); - /* skip the first 16 blocks (system area) */ buf = opts->overwrite + 16 * t->block_size; @@ -494,16 +501,15 @@ is_eltorito_state(enum ecma119_write_state state) static void next_state(struct ecma119_write_target *t) { + char msg[42]; t->state++; while ( (!t->joliet && is_joliet_state(t->state)) - ||(!t->eltorito && is_eltorito_state(t->state)) ) + ||(!t->eltorito && is_eltorito_state(t->state)) + ||(!t->write_eltorito && t->state == ECMA119_WRITE_ELTORITO_CATALOG) ) t->state++; - { - char msg[42]; sprintf(msg, "Now in state %d, curblock=%d.", t->state, t->curblock); iso_msg_debug(msg); - } } static void @@ -556,13 +562,14 @@ wr_files(struct ecma119_write_target *t, uint8_t *buf) assert( !t->ms_block && t->src ); /** - * f->block block where file read begins + * f->block block where file write begins * t->curblock block we're writting now * f->real_ino initial block for file on source */ block = t->curblock - f->block; if ( t->src->read_block(t->src, f->real_ino + block, buf) ) { - warn("problem reading from previous image"); + iso_msg_sorry(LIBISO_CANT_READ_IMG, + "Problem reading file from previous image"); } if ( f->size <= (off_t) (block+1) * t->block_size ) { f_st->file++; @@ -581,6 +588,8 @@ wr_files(struct ecma119_write_target *t, uint8_t *buf) f_st->data_len = f->size; f_st->fd = fopen(path, "r"); if (!f_st->fd) { + //FIXME instead of exit, just print a msg error and + //skip file (what about reading from /dev/zero? instead) err(1, "couldn't open %s for reading", path); } assert(t->curblock + t->ms_block == f->block); @@ -588,10 +597,15 @@ wr_files(struct ecma119_write_target *t, uint8_t *buf) nread = fread(buf, 1, t->block_size, f_st->fd); f_st->pos += t->block_size; - if (nread < 0) - warn("problem reading from %s", path); - else if (nread != t->block_size && f_st->pos < f_st->data_len) - warnx("incomplete read from %s", path); + if (nread < 0) { + char msg[PATH_MAX + 32]; + sprintf(msg, "Problem reading from %s", path); + iso_msg_sorry(LIBISO_CANT_READ_FILE, msg); + } else if (nread != t->block_size && f_st->pos < f_st->data_len) { + char msg[PATH_MAX + 32]; + sprintf(msg, "Incomplete read from %s", path); + iso_msg_sorry(LIBISO_CANT_READ_FILE, msg); + } if (f_st->pos >= f_st->data_len) { fclose(f_st->fd); f_st->fd = 0; @@ -743,6 +757,16 @@ write_one_dir_record(struct ecma119_write_target *t, } else if (node->type == ECMA119_FILE) { len = node->info.file->size; block = node->info.file->block; + } else if (node->type == ECMA119_BOOT) { + assert(t->eltorito); + if (node->info.boot_img) { + block = t->imgblock; + len = t->catalog->image->node->node.attrib.st_size; + } else { + /* we always assume 2048 as catalog len */ + block = t->catblock; + len = 2048; + } } else { /* for nodes other than files and dirs, we set both len and block to 0 */ len = 0; @@ -872,11 +896,10 @@ static int bs_read(struct burn_source *bs, unsigned char *buf, int size) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; - if (size != t->block_size) { - warnx("you must read data in block-sized chunks (%d bytes)", - (int)t->block_size); - return 0; - } else if (t->curblock >= t->vol_space_size + t->ms_block) { + + assert(size == t->block_size); + + if (t->curblock >= t->vol_space_size) { return 0; } if (t->state_data_valid) diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index 99f6208..4d93b87 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -65,9 +65,22 @@ struct ecma119_write_target unsigned int iso_level:2; unsigned int eltorito:1; - int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */ + unsigned int write_eltorito:1; + /**< + * In multisession discs, select whether to copy el-torito catalog + * and boot image. Copy is needed for isolinux images, that need to + * be patched. However, it can lead to problems when the image is + * not present in the iso filesystem, because we can't figure out + * its size. In those cases, we only copy 1 block of data. + * When modifying images, we always need to copy data. Thus, this is + * always 1 for both new and modified images. + */ struct el_torito_boot_catalog *catalog; + uint32_t catblock; /**< location of the boot catalog in the new image */ + uint32_t imgblock; /**< location of the boot image in the new image */ + + int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */ int replace_mode; /**< Replace ownership and modes of files * diff --git a/libisofs/ecma119_read.c b/libisofs/ecma119_read.c index 9dc6375..6379619 100644 --- a/libisofs/ecma119_read.c +++ b/libisofs/ecma119_read.c @@ -28,12 +28,128 @@ #include "volume.h" #include "tree.h" #include "messages.h" +#include "eltorito.h" #define BLOCK_SIZE 2048 static int iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *parent, uint32_t block); + +static struct el_torito_boot_catalog * +read_el_torito_boot_catalog(struct iso_read_info *info, uint32_t block) +{ + struct el_torito_validation_entry *ve; + struct el_torito_default_entry *entry; + struct el_torito_boot_catalog *catalog; + struct el_torito_boot_image *image; + unsigned char buffer[BLOCK_SIZE]; + + if ( info->src->read_block(info->src, block, buffer) < 0 ) { + info->error = LIBISOFS_READ_FAILURE; + return NULL; + } + + ve = (struct el_torito_validation_entry*)buffer; + + /* check if it is a valid catalog (TODO: check also the checksum)*/ + if ( (ve->header_id[0] != 1) || (ve->key_byte1[0] != 0x55) + || (ve->key_byte2[0] != 0xAA) ) { + + iso_msg_sorry(LIBISO_EL_TORITO_WRONG, "Wrong or damaged El-Torito " + "Catalog.\n El-Torito info will be ignored."); + return NULL; + } + + /* check for a valid platform */ + if (ve->platform_id[0] != 0) { + iso_msg_hint(LIBISO_EL_TORITO_UNHANLED, "Unsupported El-Torito platform.\n" + "Only 80x86 si supported. El-Torito info will be ignored."); + } + + /* ok, once we are here we assume it is a valid catalog */ + catalog = malloc(sizeof(struct el_torito_boot_catalog)); + image = calloc(1, sizeof(struct el_torito_boot_image)); + catalog->image = image; + catalog->proc = LIBISO_PREVIMG; + + { + /* + * Create the placeholder. + * Note that this could be modified later if we find a directory entry + * for the catalog in the iso tree. + */ + struct iso_tree_node_boot *boot; + boot = calloc(1, sizeof(struct iso_tree_node_boot)); + boot->node.refcount = 1; + boot->node.attrib.st_mode = S_IFREG | 0777; + boot->node.attrib.st_atime = boot->node.attrib.st_mtime + = boot->node.attrib.st_ctime = time(NULL); + boot->node.attrib.st_size = 2048; + boot->node.type = LIBISO_NODE_BOOT; + boot->node.procedence = LIBISO_PREVIMG; + boot->node.name = NULL; + boot->loc.block = block; + catalog->node = boot; + } + + /* parse the default entry */ + entry = (struct el_torito_default_entry *)(buffer + 32); + + image->bootable = entry->boot_indicator[0] ? 1 : 0; + //FIXME we need a way to handle patch_isolinux in ms images!!! + image->isolinux = 0; + image->type = entry->boot_media_type[0]; + image->partition_type = entry->system_type[0]; + image->load_seg = iso_read_lsb(entry->load_seg, 2); + image->load_size = iso_read_lsb(entry->sec_count, 2); + + { + /* + * Create the placeholder. + * Note that this could be modified later if we find a directory entry + * for the image in the iso tree. + */ + struct iso_tree_node_boot *boot; + boot = calloc(1, sizeof(struct iso_tree_node_boot)); + boot->node.refcount = 1; + boot->node.attrib.st_mode = S_IFREG | 0777; + boot->node.attrib.st_atime = boot->node.attrib.st_mtime + = boot->node.attrib.st_ctime = time(NULL); + boot->node.attrib.st_size = 2048; + boot->node.type = LIBISO_NODE_BOOT; + boot->node.procedence = LIBISO_PREVIMG; + boot->node.name = NULL; + boot->img = 1; + boot->loc.block = iso_read_lsb(entry->block, 4); + image->node = boot; + } + + //TODO how can we check if there are more entries? + + return catalog; +} + +static struct el_torito_boot_catalog * +read_el_torito_vol_desc(struct iso_read_info *info, unsigned char *buf) +{ + struct ecma119_boot_rec_vol_desc *vol; + + vol = (struct ecma119_boot_rec_vol_desc*)buf; + + /* some sanity checks */ + if ( strncmp((char*)vol->std_identifier, "CD001", 5) + || vol->vol_desc_version[0] != 1 + || strncmp((char*)vol->boot_sys_id, "EL TORITO SPECIFICATION", 23)) { + + iso_msg_hint(LIBISO_BOOT_VD_UNHANLED, "Unsupported Boot Vol. Desc.\n" + "Only El-Torito Specification, Version 1.0 Volume " + "Descriptors are supported. Ignoring boot info"); + return NULL; + } + + return read_el_torito_boot_catalog(info, iso_read_lsb(vol->boot_catalog, 4)); +} /** * This reads the "." directory entry, and set the properties of the @@ -248,12 +364,22 @@ iso_read_single_directory_record(struct iso_read_info *info, break; case S_IFREG: { - node = calloc(1, sizeof(struct iso_tree_node_file)); - node->type = LIBISO_NODE_FILE; - - /* set block with extend */ - ((struct iso_tree_node_file*)node)->loc.block = - iso_read_bb(record->block, 4, NULL); + uint32_t block; + block = iso_read_bb(record->block, 4, NULL); + + if (info->bootcat && block == info->bootcat->node->loc.block) { + /* it is the boot catalog */ + node = (struct iso_tree_node*)info->bootcat->node; + } else if (info->bootcat && block == info->bootcat->image->node->loc.block) { + /* it is the boot image */ + node = (struct iso_tree_node*)info->bootcat->image->node; + } else { + /* it is a file */ + node = calloc(1, sizeof(struct iso_tree_node_file)); + node->type = LIBISO_NODE_FILE; + /* set block with extend */ + ((struct iso_tree_node_file*)node)->loc.block = block; + } } break; case S_IFLNK: @@ -272,7 +398,7 @@ iso_read_single_directory_record(struct iso_read_info *info, node->name = name; node->attrib = atts; - node->refcount = 1; + node->refcount++; /* 1 for news, 2 for boot nodes */ node->procedence = LIBISO_PREVIMG; iso_tree_add_child(parent, node); @@ -520,7 +646,6 @@ read_root_susp_entries(struct iso_read_info *info, return 0; } - static struct iso_volset * read_pvm(struct iso_read_info *info, uint32_t block) { @@ -628,6 +753,7 @@ iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts) info.gid = opts->gid; info.mode = opts->mode & ~S_IFMT; info.size = &opts->size; + info.bootcat = NULL; root_dir_block = 0; /* read primary volume description */ @@ -642,13 +768,16 @@ iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts) do { if ( info.src->read_block(info.src, block, buffer) < 0 ) { info.error = LIBISOFS_READ_FAILURE; - iso_volset_free(volset); - return NULL; + /* cleanup and exit */ + goto read_cleanup; } switch (buffer[0]) { case 0: - /* boot record */ - //TODO handle el-torito + /* + * This is a boot record + * Here we handle el-torito + */ + info.bootcat = read_el_torito_vol_desc(&info, buffer); break; case 2: /* suplementary volume descritor */ @@ -735,11 +864,25 @@ iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts) if ( iso_read_dir(&info, volset->volume[0]->root, root_dir_block) ) { /* error, cleanup and return */ - iso_volset_free(volset); - return NULL; + goto read_cleanup; } // TODO merge tree info + /* Add El-Torito info to the volume */ + if (info.bootcat) { + /* ok, add the bootcat to the volume */ + iso_msg_debug("Found El-Torito bootable volume"); + volset->volume[0]->bootcat = info.bootcat; + } + return volset; + +read_cleanup:; + if (info.bootcat) { + el_torito_boot_catalog_free(info.bootcat); + } + iso_volset_free(volset); + + return NULL; } diff --git a/libisofs/ecma119_read.h b/libisofs/ecma119_read.h index ead19e0..48b416f 100644 --- a/libisofs/ecma119_read.h +++ b/libisofs/ecma119_read.h @@ -66,6 +66,9 @@ struct iso_read_info { unsigned int hasJoliet:1; /*< It will be set to 1 if Joliet ext are present, to 0 if not. */ uint32_t *size; + + /* place for el-torito boot catalog */ + struct el_torito_boot_catalog *bootcat; }; diff --git a/libisofs/ecma119_tree.c b/libisofs/ecma119_tree.c index e6f890b..e49f6d9 100644 --- a/libisofs/ecma119_tree.c +++ b/libisofs/ecma119_tree.c @@ -259,37 +259,29 @@ create_symlink(struct ecma119_write_target *t, } /** - * Create a new ECMA-119 node representing a boot catalog. This is like a - * regular file, but its contents are taken from a El-Torito boot catalog, - * and not from a file in the local filesystem. + * Create a new ECMA-119 node representing a boot catalog or image. + * This will be treated as a normal file when written the directory record, + * but its contents are written in a different way. * * See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for * more details. */ static struct ecma119_tree_node* -create_boot_catalog(struct ecma119_write_target *t, +create_boot(struct ecma119_write_target *t, struct ecma119_tree_node *parent, - struct iso_tree_node_boot_catalog *iso) + struct iso_tree_node_boot *iso) { struct ecma119_tree_node *ret; - struct iso_file *file; assert(t && iso && parent && parent->type == ECMA119_DIR); - /* - * This will simply create a ECMA119 file, with the only difference - * that the iso_file is not taken from table, but from boot catalog - */ - ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); - ret->type = ECMA119_FILE; + ret->type = ECMA119_BOOT; - file = iso->catalog->file; - file->ino = ++t->ino; + ret->info.boot_img = iso->img; - ret->attrib.st_nlink = file->nlink; - ret->attrib.st_ino = file->ino; - ret->info.file = file; + ret->attrib.st_nlink = 1; + ret->attrib.st_ino = ++t->ino; return ret; } @@ -334,9 +326,8 @@ create_tree(struct ecma119_write_target *t, } } break; - case LIBISO_NODE_BOOTCATALOG: - ret = create_boot_catalog(t, parent, - (struct iso_tree_node_boot_catalog*)iso); + case LIBISO_NODE_BOOT: + ret = create_boot(t, parent, (struct iso_tree_node_boot*)iso); break; default: /* should never happen */ diff --git a/libisofs/ecma119_tree.h b/libisofs/ecma119_tree.h index 77ad042..f122310 100644 --- a/libisofs/ecma119_tree.h +++ b/libisofs/ecma119_tree.h @@ -21,7 +21,8 @@ enum ecma119_node_type { ECMA119_FILE, ECMA119_SYMLINK, ECMA119_DIR, - ECMA119_PLACEHOLDER /**< placeholder for a relocated dir. */ + ECMA119_PLACEHOLDER, /**< placeholder for a relocated dir. */ + ECMA119_BOOT }; struct ecma119_dir_info { @@ -72,6 +73,10 @@ struct ecma119_tree_node struct ecma119_tree_node *real_me; /**< this field points to * the relocated directory. */ + unsigned int boot_img:1; /** For boot nodes, it identifies if this + * corresponds to image(1) or catalog(0). + * The block is stored in ecma119_write_target + */ } info; }; diff --git a/libisofs/eltorito.c b/libisofs/eltorito.c index 8e461f7..9f3f9bd 100644 --- a/libisofs/eltorito.c +++ b/libisofs/eltorito.c @@ -3,6 +3,7 @@ #include "eltorito.h" #include "volume.h" #include "util.h" +#include "messages.h" #include #include @@ -15,46 +16,6 @@ #include -struct el_torito_validation_entry { - uint8_t header_id BP(1, 1); - uint8_t platform_id BP(2, 2); - uint8_t reserved BP(3, 4); - uint8_t id_string BP(5, 28); - uint8_t checksum BP(29, 30); - uint8_t key_byte1 BP(31, 31); - uint8_t key_byte2 BP(32, 32); -}; - -struct el_torito_default_entry { - uint8_t boot_indicator BP(1, 1); - uint8_t boot_media_type BP(2, 2); - uint8_t load_seg BP(3, 4); - uint8_t system_type BP(5, 5); - uint8_t unused1 BP(6, 6); - uint8_t sec_count BP(7, 8); - uint8_t block BP(9, 12); - uint8_t unused2 BP(13, 32); -}; - -struct el_torito_section_header_entry { - uint8_t header_indicator BP(1, 1); - uint8_t platform_id BP(2, 2); - uint8_t number BP(3, 4); - uint8_t character BP(5, 32); -}; - -struct el_torito_section_entry { - uint8_t boot_indicator BP(1, 1); - uint8_t boot_media_type BP(2, 2); - uint8_t load_seg BP(3, 4); - uint8_t system_type BP(5, 5); - uint8_t unused1 BP(6, 6); - uint8_t sec_count BP(7, 8); - uint8_t block BP(9, 12); - uint8_t selec_criteria BP(13, 13); - uint8_t vendor_sc BP(14, 32); -}; - /** * This table should be written with accuracy values at offset * 8 of boot image, when used ISOLINUX boot loader @@ -85,18 +46,67 @@ struct hard_disc_mbr { uint8_t sign2; }; +static void +el_torito_image_free(struct el_torito_boot_image *image) +{ + assert(image); + + /* unref image node */ + iso_tree_free((struct iso_tree_node*)image->node); + free(image); +} + +static struct iso_tree_node_boot* +create_boot_image_node(struct iso_tree_node_file *image) +{ + struct iso_tree_node_boot *boot; + struct iso_tree_node_dir *parent; + + assert(image); + + boot = calloc(1, sizeof(struct iso_tree_node_boot)); + boot->node.attrib = image->node.attrib; + boot->node.type = LIBISO_NODE_BOOT; + boot->node.name = strdup(image->node.name); + boot->node.hide_flags = image->node.hide_flags; + boot->node.refcount = 2; /* mine and tree */ + boot->loc.path = strdup(image->loc.path); + boot->img = 1; /* it refers to image */ + + /* replace iso_tree_node_file with the image */ + parent = image->node.parent; + iso_tree_node_remove(parent, (struct iso_tree_node*)image); + iso_tree_add_child(parent, (struct iso_tree_node*) boot); + + return boot; +} + static struct el_torito_boot_image * -create_image(struct iso_tree_node *image, +create_image(const char *path, enum eltorito_boot_media_type type) { + struct stat attrib; struct el_torito_boot_image *boot; int boot_media_type = 0; int load_sectors = 0; /* number of sector to load */ unsigned char partition_type = 0; + if (stat(path, &attrib)) { + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't access file."); + libisofs_errno = NO_FILE; + return NULL; + } + + if ( !S_ISREG(attrib.st_mode) ) { + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, + "We can only create boot images from regular files"); + libisofs_errno = ELTORITO_WRONG_IMAGE; + return NULL; + } + switch (type) { case ELTORITO_FLOPPY_EMUL: - switch (image->attrib.st_size) { + switch (attrib.st_size) { case 1200 * 1024: boot_media_type = 1; /* 1.2 meg diskette */ break; @@ -107,10 +117,14 @@ create_image(struct iso_tree_node *image, boot_media_type = 3; /* 2.88 meg diskette */ break; default: - fprintf(stderr, "Invalid image size %d Kb. Must be one of 1.2, 1.44" - "or 2.88 Mb", (int) image->attrib.st_size / 1024); + { + char msg[72]; + sprintf(msg, "Invalid image size %d Kb. Must be one of 1.2, 1.44" + "or 2.88 Mb", (int) attrib.st_size / 1024); + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg); libisofs_errno = ELTORITO_WRONG_IMAGE_SIZE; return NULL; + } break; } /* it seems that for floppy emulation we need to load @@ -125,13 +139,16 @@ create_image(struct iso_tree_node *image, int used_partition; /* read the MBR on disc and get the type of the partition */ - fd = open(((struct iso_tree_node_file*)image)->loc.path, O_RDONLY); + fd = open(path, O_RDONLY); if ( fd == -1 ) { - fprintf(stderr, "Can't open image file\n"); + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't open image file."); + libisofs_errno = NO_READ_ACCESS; return NULL; } if ( read(fd, &mbr, sizeof(mbr)) ) { - fprintf(stderr, "Can't read MBR from image file\n"); + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, + "Can't read MBR from image file."); + libisofs_errno = ELTORITO_WRONG_IMAGE; close(fd); return NULL; } @@ -139,7 +156,9 @@ create_image(struct iso_tree_node *image, /* check valid MBR signature */ if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) { - fprintf(stderr, "Invalid MBR. Wrong signature.\n"); + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, + "Invalid MBR. Wrong signature."); + libisofs_errno = ELTORITO_WRONG_IMAGE; return NULL; } @@ -149,8 +168,11 @@ create_image(struct iso_tree_node *image, if (mbr.partition[i].type != 0) { /* it's an used partition */ if (used_partition != -1) { - fprintf(stderr, "Invalid MBR. At least 2 partitions: %d and " + char msg[72]; + sprintf(msg, "Invalid MBR. At least 2 partitions: %d and " "%d, are being used\n", used_partition, i); + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg); + libisofs_errno = ELTORITO_WRONG_IMAGE; return NULL; } else used_partition = i; @@ -170,67 +192,180 @@ create_image(struct iso_tree_node *image, boot = calloc(1, sizeof(struct el_torito_boot_image)); boot->bootable = 1; - boot->image = (struct iso_tree_node_file *) image; boot->type = boot_media_type; boot->load_size = load_sectors; boot->partition_type = partition_type; return boot; } -static struct iso_tree_node_boot_catalog* +/* parent and name can be null */ +static struct iso_tree_node_boot* create_boot_catalog_node(struct iso_tree_node_dir *parent, const char *name) { - struct iso_tree_node_boot_catalog *boot; + struct iso_tree_node_boot *boot; - assert( parent && name ); + assert( (parent && name) || (!parent && !name) ); - boot = calloc(1, sizeof(struct iso_tree_node_boot_catalog)); + boot = calloc(1, sizeof(struct iso_tree_node_boot)); boot->node.attrib.st_mode = S_IFREG | 0444; boot->node.attrib.st_atime = boot->node.attrib.st_mtime = boot->node.attrib.st_ctime = time(NULL); - boot->node.type = LIBISO_NODE_BOOTCATALOG; - boot->node.name = strdup(name); - iso_tree_add_child(parent, (struct iso_tree_node*) boot); + boot->node.attrib.st_size = 2048; + boot->node.type = LIBISO_NODE_BOOT; + boot->node.name = name ? strdup(name) : NULL; + boot->node.refcount = 1; /* mine */ + if (parent) { + boot->node.refcount++; /* add tree ref */ + iso_tree_add_child(parent, (struct iso_tree_node*) boot); + } return boot; } -struct el_torito_boot_image * -iso_volume_create_boot_catalog(struct iso_volume *volume, - struct iso_tree_node *image, - enum eltorito_boot_media_type type, - struct iso_tree_node_dir *dir, - char *name) +struct el_torito_boot_image* +iso_volume_set_boot_image(struct iso_volume *volume, + struct iso_tree_node *image, + enum eltorito_boot_media_type type, + struct iso_tree_node_dir *dir, + char *name) { struct el_torito_boot_image *boot_image; - struct iso_tree_node_boot_catalog *boot_node; + struct iso_tree_node_boot *cat_node; struct el_torito_boot_catalog *catalog; + struct iso_tree_node_file *imgfile; - assert( volume && !volume->bootcat); - assert( image && ISO_ISREG(image) && dir && name); + assert(volume && !volume->bootcat); + assert(image && ISO_ISREG(image)); + assert(dir && name); - boot_image = create_image(image, type); + imgfile = (struct iso_tree_node_file*)image; + + if (image->procedence == LIBISO_PREVIMG) { + /* FIXME mmm, what to do here? */ + iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't use prev. image files " + "as boot images"); + return NULL; + } + + boot_image = create_image(imgfile->loc.path, type); if ( !boot_image ) return NULL; + + /* create image node */ + boot_image->node = create_boot_image_node(imgfile); /* creates the catalog with the given default image */ catalog = malloc(sizeof(struct el_torito_boot_catalog)); - catalog->nentries = 1; - catalog->entries = malloc(sizeof(struct el_torito_boot_image *)); - catalog->entries[0] = boot_image; - catalog->file = malloc(sizeof(struct iso_file)); - catalog->file->size = 2048; + if (!catalog) { + el_torito_image_free(boot_image); + return NULL; + } + catalog->image = boot_image; /* add catalog file */ - boot_node = create_boot_catalog_node(dir, name); - boot_node->catalog = catalog; + cat_node = create_boot_catalog_node(dir, name); + catalog->node = cat_node; volume->bootcat = catalog; return boot_image; } +struct el_torito_boot_image* +iso_volume_set_boot_image_hidden(struct iso_volume *volume, const char* path, + enum eltorito_boot_media_type type) +{ + struct el_torito_boot_image *boot_image; + struct iso_tree_node_boot *cat_node; + struct el_torito_boot_catalog *catalog; + + assert(volume && !volume->bootcat); + assert(path); + + boot_image = create_image(path, type); + if ( !boot_image ) + return NULL; + + /* create image node */ + { + struct iso_tree_node_boot *boot; + boot = calloc(1, sizeof(struct iso_tree_node_boot)); + /* we assume the stat won't fail, as we already stat the same file above */ + stat(path, &boot->node.attrib); + boot->node.type = LIBISO_NODE_BOOT; + boot->node.refcount = 1; /* mine */ + boot->loc.path = strdup(path); + boot->img = 1; /* it refers to image */ + boot_image->node = boot; + } + + /* creates the catalog with the given default image */ + catalog = malloc(sizeof(struct el_torito_boot_catalog)); + if (!catalog) { + el_torito_image_free(boot_image); + return NULL; + } + catalog->image = boot_image; + + /* add catalog file */ + cat_node = create_boot_catalog_node(NULL, NULL); + catalog->node = cat_node; + + volume->bootcat = catalog; + + return boot_image; +} + +struct el_torito_boot_image* +iso_volume_get_boot_image(struct iso_volume *volume, + struct iso_tree_node **imgnode, + struct iso_tree_node **catnode) +{ + struct el_torito_boot_catalog *catalog; + + assert(volume); + + catalog = volume->bootcat; + if (!catalog) + return NULL; + + if (imgnode) + *imgnode = (struct iso_tree_node*)catalog->image->node; + + if (catnode) + *catnode = (struct iso_tree_node*)catalog->node; + + return catalog->image; +} + +void +iso_volume_remove_boot_image(struct iso_volume *volume) +{ + struct el_torito_boot_catalog *catalog; + struct iso_tree_node *catnode; + struct iso_tree_node *imgnode; + assert(volume); + + catalog = volume->bootcat; + if (!catalog) + return; + + /* remove node from tree */ + catnode = (struct iso_tree_node*)catalog->node; + if (catnode->parent) + iso_tree_node_remove(catnode->parent, catnode); + + /* remove image from tree */ + imgnode = (struct iso_tree_node*)catalog->image->node; + if (imgnode->parent) + iso_tree_node_remove(imgnode->parent, imgnode); + + /* remove catalog */ + el_torito_boot_catalog_free(catalog); + volume->bootcat = NULL; +} + void el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment) { @@ -254,43 +389,20 @@ el_torito_set_no_bootable(struct el_torito_boot_image *bootimg) } void -el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg) +el_torito_patch_isolinux_image(struct el_torito_boot_image *bootimg) { - bootimg->patch_isolinux = 1; + bootimg->isolinux = 1; } void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat) { - size_t i; - assert(cat); - for(i = 0; i < cat->nentries; ++i) { - free(cat->entries[i]); - } - free(cat->entries); - free(cat->file); + el_torito_image_free(cat->image); + iso_tree_free((struct iso_tree_node*)cat->node); free(cat); } -void el_torito_get_image_files(struct ecma119_write_target *t) -{ - size_t i; - struct el_torito_boot_catalog *cat = t->catalog; - assert(cat); - - for(i = 0; i < cat->nentries; ++i) { - - struct iso_tree_node_file *image = cat->entries[i]->image; - struct iso_file *file = iso_file_table_lookup(t->file_table, image); - if ( file == NULL ) { - file = iso_file_new(image); - iso_file_table_add_file(t->file_table, file); - } - cat->entries[i]->file = file; - } -} - /** * Write the Boot Record Volume Descriptor */ @@ -307,7 +419,7 @@ el_torito_write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; memcpy(vol->boot_sys_id, "EL TORITO SPECIFICATION", 23); - iso_lsb(vol->boot_catalog, cat->file->block, 4); + iso_lsb(vol->boot_catalog, t->catblock, 4); } static void @@ -333,104 +445,139 @@ write_validation_entry(struct ecma119_write_target *t, uint8_t *buf) } static void -patch_boot_file(struct el_torito_boot_image *img) +patch_boot_image(uint8_t *buf, struct ecma119_write_target *t, ssize_t imgsize) { - struct boot_info_table info; - int fd; + struct boot_info_table *info; uint32_t checksum; - ssize_t len; - uint8_t buf[4]; + ssize_t offset; memset(&info, 0, sizeof(info)); - /* open image */ - fd = open(img->image->loc.path, O_RDWR); - if ( fd == -1 ) { - //TODO what do do? exit or just continue? - fprintf(stderr, "Can't patch boot image %s\n", img->image->loc.path); - close(fd); - return; - } - /* compute checksum, as the the sum of all 32 bit words in boot image * from offset 64 */ checksum = 0; - lseek(fd, (off_t) 64, SEEK_SET); + offset = (ssize_t) 64; - //TODO this can (must) be optimizied by reading to a longer buffer - while ( (len = read(fd, buf, 4) ) == 4 ) { - checksum += iso_read_lsb(buf, 4); + while (offset < imgsize) { + checksum += iso_read_lsb(buf + offset, 4); + offset += 4; } - if ( len != 0 ) { - /* error reading file, or file length not multiple of 4 */ - //TODO what do do? exit or just continue? - fprintf(stderr, "Can't patch boot image %s\n", img->image->loc.path); - close(fd); - return; + if (offset != imgsize) { + /* file length not multiple of 4 */ + iso_msg_warn(LIBISO_ISOLINUX_CANT_PATCH, + "Unexpected isolinux image length. Patch might not work."); } - /* fill boot info table */ - iso_lsb(info.bi_pvd, 16, 4); //FIXME this should be changed when we implement ms - iso_lsb(info.bi_file, img->file->block, 4); - iso_lsb(info.bi_length, img->image->node.attrib.st_size, 4); - iso_lsb(info.bi_csum, checksum, 4); - - /* patch file */ - lseek(fd, (off_t) 8, SEEK_SET); - write(fd, &info, sizeof(info)); - - close(fd); -} + /* patch boot info table */ + info = (struct boot_info_table*)(buf + 8); + memset(info, 0, sizeof(struct boot_info_table)); + iso_lsb(info->bi_pvd, t->ms_block + 16, 4); + iso_lsb(info->bi_file, t->imgblock, 4); + iso_lsb(info->bi_length, imgsize, 4); + iso_lsb(info->bi_csum, checksum, 4); +} -void -el_torito_patch_image_files(struct ecma119_write_target *t) +static void +write_boot_image(uint8_t *buf, struct ecma119_write_target *t) { - size_t i; - struct el_torito_boot_catalog *cat = t->catalog; - assert(cat); + ssize_t imgsize; + struct el_torito_boot_image *image; - for (i = 0; i < cat->nentries; ++i) { - struct el_torito_boot_image *img = cat->entries[i]; - if ( img->patch_isolinux ) - patch_boot_file(img); + image = t->catalog->image; + imgsize= image->node->node.attrib.st_size; + + /* copy image contents to memory buffer */ + if (image->node->node.procedence == LIBISO_PREVIMG) { + int block, nblocks; + uint8_t *memloc; + assert(t->src); + + block = 0; + memloc = buf; + nblocks = imgsize ? div_up(imgsize, 2048) : 1; + while (block < nblocks) { + if (t->src->read_block(t->src, + image->node->loc.block + block, memloc)) { + iso_msg_sorry(LIBISO_CANT_READ_IMG, + "Unable to read boot image from previous image."); + break; /* exit loop */ + } + memloc += 2048; + ++block; + } + } else { + int fd; + ssize_t nread, tread; + uint8_t *memloc; + const char *path = image->node->loc.path; + memloc = buf; + + fd = open(path, O_RDONLY); + if (fd == -1) { + iso_msg_sorry(LIBISO_CANT_READ_FILE, + "Can't open boot image file for reading. Skipping"); + return; + } + + tread = 0; + while (tread < imgsize) { + nread = read(fd, memloc, t->block_size); + if (nread <= 0) { + iso_msg_sorry(LIBISO_CANT_READ_FILE, + "Problem reading boot image file. Skipping"); + break; /* exit loop */ + } + tread += nread; + memloc += nread; + } + close(fd); + } + + if (image->isolinux) { + /* we need to patch the image */ + patch_boot_image(buf, t, imgsize); } } /** * Write one section entry. - * Currently this is used for both default and other entries since we - * put selection criteria no 0 (no sel. criteria) + * Currently this is used only for default image (the only supported just now) */ static void -write_section_entry(uint8_t *buf, struct el_torito_boot_image *img) +write_section_entry(uint8_t *buf, struct ecma119_write_target *t) { + struct el_torito_boot_image *img; struct el_torito_section_entry *se = (struct el_torito_section_entry*)buf; + + img = t->catalog->image; + + assert(img); se->boot_indicator[0] = img->bootable ? 0x88 : 0x00; se->boot_media_type[0] = img->type; iso_lsb(se->load_seg, img->load_seg, 2); - se->system_type[0] = 0; //TODO need to get the partition type + se->system_type[0] = img->partition_type; iso_lsb(se->sec_count, img->load_size, 2); - iso_lsb(se->block, img->file->block, 4); + iso_lsb(se->block, t->imgblock, 4); } /** - * Write El-Torito Boot Catalog + * Write El-Torito Boot Catalog plus image */ static void write_catalog(struct ecma119_write_target *t, uint8_t *buf) { struct el_torito_boot_catalog *cat = t->catalog; assert(cat); - assert(cat->nentries >= 1 && cat->nentries < 63); + assert(cat->image); write_validation_entry(t, buf); /* write default entry */ - write_section_entry(buf + 32, cat->entries[0]); + write_section_entry(buf + 32, t); - //TODO write all images + write_boot_image(buf + 2048, t); } void @@ -446,9 +593,27 @@ el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) void el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf) { + off_t size; + off_t imgsize; assert(t->catalog); + + /* + * Note that when this is called, we need to write el-torito info to + * image. Note, however, that the image could come from a previous session + * and maybe we don't know its size + */ + + imgsize = t->catalog->image->node->node.attrib.st_size; + if (imgsize == 0) { + iso_msg_warn(LIBISO_EL_TORITO_BLIND_COPY, + "Can get boot image size, only one block will be copied"); + imgsize = 2048; + } + + size = 2048 + div_up(imgsize, t->block_size); + ecma119_start_chunking(t, write_catalog, - 2048, + size, buf); } diff --git a/libisofs/eltorito.h b/libisofs/eltorito.h index 0b9c91b..54c7adc 100644 --- a/libisofs/eltorito.h +++ b/libisofs/eltorito.h @@ -5,52 +5,66 @@ #include "file.h" #include "ecma119.h" -/** - * Location of the boot catalog - */ -struct iso_tree_node_boot_catalog -{ - struct iso_tree_node node; - struct el_torito_boot_catalog *catalog; -}; - struct el_torito_boot_catalog { - int nentries; - struct el_torito_boot_image **entries; - struct iso_file *file; /**< The catalog file */ + struct iso_tree_node_boot *node; /* node of the catalog */ + struct el_torito_boot_image *image; /* default boot image */ + enum tree_node_from proc; /* whether the catalog is new or read from a + * prev session/image */ }; struct el_torito_boot_image { - unsigned char bootable; /**< If the entry is bootable. */ - unsigned char patch_isolinux; /**< If the image will be patched */ + struct iso_tree_node_boot *node; + + unsigned int bootable:1; /**< If the entry is bootable. */ + unsigned int isolinux:1; /**< If the image will be patched */ unsigned char type; /**< The type of image */ unsigned char partition_type; /**< type of partition for HD-emul images */ short load_seg; /**< Load segment for the initial boot image. */ short load_size; /**< Number of sector to load. */ - struct iso_tree_node_file *image; - struct iso_file *file; }; -/*struct el_torito_boot_entry * -el_torito_add_boot_entry(struct el_torito_boot_catalog *cat, - struct iso_tree_node_file *image); -*/ +struct el_torito_validation_entry { + uint8_t header_id BP(1, 1); + uint8_t platform_id BP(2, 2); + uint8_t reserved BP(3, 4); + uint8_t id_string BP(5, 28); + uint8_t checksum BP(29, 30); + uint8_t key_byte1 BP(31, 31); + uint8_t key_byte2 BP(32, 32); +}; + +struct el_torito_default_entry { + uint8_t boot_indicator BP(1, 1); + uint8_t boot_media_type BP(2, 2); + uint8_t load_seg BP(3, 4); + uint8_t system_type BP(5, 5); + uint8_t unused1 BP(6, 6); + uint8_t sec_count BP(7, 8); + uint8_t block BP(9, 12); + uint8_t unused2 BP(13, 32); +}; + +struct el_torito_section_header_entry { + uint8_t header_indicator BP(1, 1); + uint8_t platform_id BP(2, 2); + uint8_t number BP(3, 4); + uint8_t character BP(5, 32); +}; + +struct el_torito_section_entry { + uint8_t boot_indicator BP(1, 1); + uint8_t boot_media_type BP(2, 2); + uint8_t load_seg BP(3, 4); + uint8_t system_type BP(5, 5); + uint8_t unused1 BP(6, 6); + uint8_t sec_count BP(7, 8); + uint8_t block BP(9, 12); + uint8_t selec_criteria BP(13, 13); + uint8_t vendor_sc BP(14, 32); +}; void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat); -/** - * For each boot image file, this gets the related iso_file object. - * In most cases, the file is already in the hash table. However, if the - * boot record is hidden in both ISO/RR and joliet trees, this ensures - * that boot images will be written to image. - */ -void el_torito_get_image_files(struct ecma119_write_target *t); - -/** - * Patch image files if selected. This is needed for isolinux boot images - */ -void el_torito_patch_image_files(struct ecma119_write_target *t); - /** * Write the Boot Record Volume Descriptor */ @@ -60,6 +74,9 @@ el_torito_write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf); void el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf); +/** + * Write catalog + image + */ void el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf); diff --git a/libisofs/joliet.c b/libisofs/joliet.c index ad8583b..dda2579 100644 --- a/libisofs/joliet.c +++ b/libisofs/joliet.c @@ -8,6 +8,7 @@ #include "util.h" #include "volume.h" #include "eltorito.h" +#include "messages.h" #include #include @@ -40,14 +41,14 @@ create_node(struct ecma119_write_target *t, } ret->info.file = file; ret->type = JOLIET_FILE; - } else { + } else if (ISO_ISBOOT(iso)) { /* it's boot catalog info */ - struct iso_tree_node_boot_catalog *iso_b = - (struct iso_tree_node_boot_catalog *) iso; - struct iso_file *file; - file = iso_b->catalog->file; - ret->info.file = file; - ret->type = JOLIET_FILE; + struct iso_tree_node_boot *boot = (struct iso_tree_node_boot*) iso; + ret->info.boot_img = boot->img; + ret->type = JOLIET_BOOT; + } else { + /* this should never happen */ + assert(0); } return ret; @@ -66,7 +67,7 @@ create_tree(struct ecma119_write_target *t, switch (iso->type) { case LIBISO_NODE_FILE: - case LIBISO_NODE_BOOTCATALOG: + case LIBISO_NODE_BOOT: root = create_node(t, parent, iso); break; case LIBISO_NODE_DIR: @@ -85,9 +86,13 @@ create_tree(struct ecma119_write_target *t, } break; default: - //TODO replace this printf - printf("Can't add this kind of node to a Joliet tree"); + { + char msg[512]; + sprintf(msg, "Can't add %s to Joliet tree. This kind of files " + "can only be added to a Rock Ridget tree. Skipping", iso->name); + iso_msg_note(LIBISO_JOLIET_WRONG_FILE_TYPE, msg); return NULL; + } break; } return root; @@ -271,8 +276,19 @@ write_one_dir_record(struct ecma119_write_target *t, if (node->type == JOLIET_DIR) { len = node->info.dir.len; block = node->info.dir.block; + } else if (node->type == JOLIET_BOOT) { + assert(t->eltorito); + if (node->info.boot_img) { + block = t->imgblock; + len = t->catalog->image->node->node.attrib.st_size; + } else { + /* we always assume 2048 as catalog len */ + block = t->catblock; + len = 2048; + } } else { /* file */ + assert(node->type == JOLIET_FILE); len = node->info.file->size; block = node->info.file->block; } diff --git a/libisofs/joliet.h b/libisofs/joliet.h index 1b4adc4..0930ac2 100644 --- a/libisofs/joliet.h +++ b/libisofs/joliet.h @@ -19,7 +19,8 @@ struct iso_tree_node; enum joliet_node_type { JOLIET_FILE, - JOLIET_DIR + JOLIET_DIR, + JOLIET_BOOT }; struct joliet_dir_info { @@ -41,6 +42,10 @@ struct joliet_tree_node union { struct iso_file *file; struct joliet_dir_info dir; + unsigned int boot_img:1; /** For boot nodes, it identifies if this + * corresponds to image(1) or catalog(0). + * The block is stored in ecma119_write_target + */ } info; }; diff --git a/libisofs/libdax_msgs.h b/libisofs/libdax_msgs.h index 0d532bb..77d0d9f 100644 --- a/libisofs/libdax_msgs.h +++ b/libisofs/libdax_msgs.h @@ -382,17 +382,35 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff ------------------------------------------------------------------------------ Range "vreixo" : 0x00030000 to 0x0003ffff + General: + 0x00031001 (SORRY,HIGH) = Can't read file (ignored) + 0x00031002 (FATAL,HIGH) = Can't read file (operation canceled) + Image reading: 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image + 0x00031003 (SORRY,HIGH) = Can't read previous image file + Rock-Ridge: 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found 0x00030111 (SORRY,HIGH) = Unsupported RR feature 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry + El-Torito: + 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored + 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog + 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature + 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image + 0x00030205 (WARNING,MEDIUM)= Can't properly patch isolinux image + 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without + enought info about it + + Joliet: + 0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree + ------------------------------------------------------------------------------ diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 84cc67b..85a64a1 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -64,7 +64,7 @@ enum iso_tree_node_type { LIBISO_NODE_DIR, LIBISO_NODE_FILE, LIBISO_NODE_SYMLINK, - LIBISO_NODE_BOOTCATALOG + LIBISO_NODE_BOOT }; #define LIBISO_ISDIR(n) (iso_tree_node_get_type(n) == LIBISO_NODE_DIR) @@ -89,13 +89,12 @@ struct iso_tree_node_file; struct iso_tree_node_symlink; /** - * A node that represents a boot catalog - * FIXME I'm not very sure how this should be treated. + * A node that represents an El-Torito file. */ -struct iso_tree_node_boot_catalog; +struct iso_tree_node_boot; /** - * El-Torito boot image + * Information about El-Torito boot image. * \see eltorito.h */ struct el_torito_boot_image; @@ -213,6 +212,16 @@ struct ecma119_source_opts { int level; /**< ISO level to write at. */ int flags; /**< Which extensions to support. */ int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */ + + unsigned int copy_eltorito:1; + /**< + * In multisession discs, select whether to copy el-torito catalog + * and boot image. Copy is needed for isolinux images, that need to + * be patched. However, it can lead to problems when the image is + * not present in the iso filesystem, because we can't figure out + * its size. In those cases, we only copy 1 block of data. + */ + unsigned int no_cache_inodes:1; /**< If use inode caching or not. Set it to 1 to prevent * inode caching. @@ -283,6 +292,9 @@ struct ecma119_source_opts { * 64KiB, where libisofs will write the contents that should * be written at the beginning of a overwriteable media, to * grow the image. + * You shoudl initialize the buffer either with 0s, or with + * the contents of the first blocks of the image you're + * growing. In most cases, 0 is good enought. */ }; @@ -386,6 +398,8 @@ int libisofs_errno; #define UNEXPECTED_FILE_TYPE 3 /* invalid boot image size */ #define ELTORITO_WRONG_IMAGE_SIZE 4 +/* invalid image */ +#define ELTORITO_WRONG_IMAGE 5 /** * Controls the bahavior of iso_tree_radd_dir function @@ -553,6 +567,10 @@ const char *iso_volume_get_biblio_file_id(struct iso_volume *volume); /** * Create a bootable volume by adding a El-Torito boot image. * + * This also add a catalog tree node to the image filesystem tree. The tree + * node for the image will be replaced with a iso_tree_node_boot node, that + * acts as a placeholder for the real image. + * * \param volume The volume to make bootable. * \param image The tree node with the file to use as default boot image. * \param type The boot media type. This can be one of 3 types: @@ -573,16 +591,60 @@ const char *iso_volume_get_biblio_file_id(struct iso_volume *volume); * * \pre \p volume is a volume without any boot catalog yet * \pre \p image is a file tree node already inserted in the volume tree. + * \pre \p image is a file tree node that refers to a newly added file, not + * one from a previous session. FIXME allow prev session files too * \pre \p dir is a directory node already inserted in the volume tree. * \pre \p name There isn't any dir child with the same name. * */ -struct el_torito_boot_image * -iso_volume_create_boot_catalog(struct iso_volume *volume, - struct iso_tree_node *image, - enum eltorito_boot_media_type type, - struct iso_tree_node_dir *dir, - char *name); +struct el_torito_boot_image* +iso_volume_set_boot_image(struct iso_volume *volume, + struct iso_tree_node *image, + enum eltorito_boot_media_type type, + struct iso_tree_node_dir *dir, + char *name); + +struct el_torito_boot_image* +iso_volume_set_boot_image_hidden(struct iso_volume *volume, + const char* path, + enum eltorito_boot_media_type type); + +/** + * Get El-Torito boot image of the volume, if any. + * + * This can be useful, for example, to check if a volume read from a previous + * session or an existing image is bootable. It can also be useful to get + * the image and catalog tree nodes. An application would want those, for + * example, to prevent the user removing it. + * + * Both tree nodes are owned by libisofs and should not be freed. You can check + * if the node is already on the tree by getting its parent (note that when + * reading El-Torito info from a previous image, the nodes might not be on + * the tree even if you haven't removed them). Remember that you'll need to + * get a new ref (with iso_tree_node_ref()) before inserting them again to the + * tree, and probably you will also need to set the name or permissions. + * + * \param imgnode When not NULL, it will be filled with the image tree node, if + * any. + * \param catnode When not NULL, it will be filled with the catalog tree node, + * if any. + * + * \return The default El-Torito bootable image, or NULL is the volume is not + * bootable. + */ +struct el_torito_boot_image* +iso_volume_get_boot_image(struct iso_volume *volume, + struct iso_tree_node **imgnode, + struct iso_tree_node **catnode); + +/** + * Removes the El-Torito bootable image. Both the catalog and image tree nodes + * are also removed from the image filesystem tree, if there. + * If the volume is not bootable (don't have el-torito image) this function is + * a nop. + */ +void +iso_volume_remove_boot_image(struct iso_volume *volume); /** * Sets the load segment for the initial boot image. This is only for @@ -608,12 +670,11 @@ el_torito_set_no_bootable(struct el_torito_boot_image *bootimg); /** * Specifies that this image needs to be patched. This involves the writting * of a 56 bytes boot information table at offset 8 of the boot image file. - * Be aware that libisofs will modify original boot image file, so do a backup - * if needed. + * The original boot image file won't be modified. * This is needed for isolinux boot images. */ void -el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg); +el_torito_patch_isolinux_image(struct el_torito_boot_image *bootimg); /** * Locate a node by its path on disc. diff --git a/libisofs/messages.c b/libisofs/messages.c index 03ecf2d..93b0514 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -29,6 +29,13 @@ void iso_msg_debug(char *msg_text) msg_text, 0, 0); } +void iso_msg_note(int error_code, char *msg_text) +{ + libdax_msgs_submit(libdax_messenger, -1, error_code, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_MEDIUM, + msg_text, 0, 0); +} + void iso_msg_hint(int error_code, char *msg_text) { libdax_msgs_submit(libdax_messenger, -1, error_code, diff --git a/libisofs/messages.h b/libisofs/messages.h index 53dfebe..1359b08 100644 --- a/libisofs/messages.h +++ b/libisofs/messages.h @@ -7,12 +7,19 @@ #include "libdax_msgs.h" +/** Can't read file (ignored) */ +#define LIBISO_CANT_READ_FILE 0x00031001 +/** Can't read file (operation canceled) */ +#define LIBISO_FILE_READ_ERROR 0x00031002 + /** Unsupported image feature */ #define LIBISO_IMG_UNSUPPORTED 0x00031000 /** Unsupported Vol Desc that will be ignored */ #define LIBISO_UNSUPPORTED_VD 0x00031001 /** damaged image */ #define LIBISO_WRONG_IMG 0x00031002 +/** Can't read previous image file */ +#define LIBISO_CANT_READ_IMG 0x00031003 /* Unsupported SUSP entry */ #define LIBISO_SUSP_UNHANLED 0x00030101 @@ -25,8 +32,26 @@ /** Error in a Rock Ridge entry. */ #define LIBISO_RR_ERROR 0x00030112 +/** Unsupported boot vol desc. */ +#define LIBISO_BOOT_VD_UNHANLED 0x00030201 +/** Wrong or damaged el-torito catalog */ +#define LIBISO_EL_TORITO_WRONG 0x00030202 +/** Unsupproted el-torito feature */ +#define LIBISO_EL_TORITO_UNHANLED 0x00030203 +/** Trying to add an invalid file as a El-Torito image */ +#define LIBISO_EL_TORITO_WRONG_IMG 0x00030204 +/** Can't properly patch isolinux image */ +#define LIBISO_ISOLINUX_CANT_PATCH 0x00030205 +/** Copying El-Torito from a previous image without enought info about it */ +#define LIBISO_EL_TORITO_BLIND_COPY 0x00030206 + +/** Unsupported file type for Joliet tree */ +#define LIBISO_JOLIET_WRONG_FILE_TYPE 0x00030301 + void iso_msg_debug(char *msg_text); +void iso_msg_note(int error_code, char *msg_text); + void iso_msg_hint(int error_code, char *msg_text); void iso_msg_warn(int error_code, char *msg_text); diff --git a/libisofs/tree.c b/libisofs/tree.c index 044961e..7159dbf 100644 --- a/libisofs/tree.c +++ b/libisofs/tree.c @@ -469,6 +469,8 @@ iso_tree_node_ref(struct iso_tree_node *node) void iso_tree_free(struct iso_tree_node *root) { + if (!root) + return; if (--root->refcount < 1) { if ( ISO_ISDIR(root) ) { size_t i; @@ -487,6 +489,11 @@ iso_tree_free(struct iso_tree_node *root) file = (struct iso_tree_node_file *) root; if (root->procedence == LIBISO_NEW) free(file->loc.path); + } else if ( ISO_ISBOOT(root) ) { + struct iso_tree_node_boot *boot; + boot = (struct iso_tree_node_boot *) root; + if (root->procedence == LIBISO_NEW && boot->img) + free(boot->loc.path); } free(root->name); free(root); diff --git a/libisofs/tree.h b/libisofs/tree.h index 7816726..ae42664 100644 --- a/libisofs/tree.h +++ b/libisofs/tree.h @@ -95,6 +95,20 @@ struct iso_tree_node_dir struct iso_tree_node **children; }; +/** + * Tree node that corresponds to some El-Torito artifact. + * This can be either the boot catalog or a bootable image. + */ +struct iso_tree_node_boot +{ + struct iso_tree_node node; + unsigned int img:1; /*< 1 if img, 0 if catalog */ + union { + char *path; /**< the path of the file on local filesystem */ + uint32_t block; /**< If the file is from a previous session.*/ + } loc; +}; + /** * An iterator for directory children. */ @@ -151,5 +165,6 @@ void iso_tree_print_verbose(const struct iso_tree_node *root, #define ISO_ISDIR(n) (n->type == LIBISO_NODE_DIR) #define ISO_ISREG(n) (n->type == LIBISO_NODE_FILE) #define ISO_ISLNK(n) (n->type == LIBISO_NODE_SYMLINK) +#define ISO_ISBOOT(n) (n->type == LIBISO_NODE_BOOT) #endif /* LIBISO_TREE_H */ diff --git a/test/iso.c b/test/iso.c index 08c0589..fba9d6b 100644 --- a/test/iso.c +++ b/test/iso.c @@ -119,10 +119,14 @@ int main(int argc, char **argv) if (!img) { err(1, "boot image patch is not valid"); } - bootimg = iso_volume_create_boot_catalog(volume, img, ELTORITO_NO_EMUL, + bootimg = iso_volume_set_boot_image(volume, img, ELTORITO_NO_EMUL, boot, "boot.cat"); el_torito_set_load_size(bootimg, 4); - el_torito_set_write_boot_info(bootimg); + el_torito_patch_isolinux_image(bootimg); + + /* Or just this for hidden img + * iso_volume_set_boot_image_hidden(volume, boot_img, ELTORITO_NO_EMUL); + */ } volset = iso_volset_new( volume, "VOLSETID" ); @@ -138,8 +142,8 @@ int main(int argc, char **argv) memset(&opts, 0, sizeof(struct ecma119_source_opts)); opts.level = level; opts.flags = flags; - opts.relaxed_constraints = 0;//constraints; - opts.input_charset = NULL;//"UTF-8"; + opts.relaxed_constraints = 0; + opts.input_charset = NULL; opts.ouput_charset = "UTF-8"; src = iso_source_new_ecma119(volset, &opts); diff --git a/test/iso_grow.c b/test/iso_grow.c index b90f26d..aae5eab 100644 --- a/test/iso_grow.c +++ b/test/iso_grow.c @@ -165,8 +165,9 @@ int main(int argc, char **argv) wopts.relaxed_constraints = 0; wopts.input_charset = "UTF-8"; wopts.ouput_charset = "UTF-8"; - wopts.ms_block = ropts.size; - wopts.overwrite = malloc(32*2048); + /* round up to 32kb aligment = 16 block*/ + wopts.ms_block = ((ropts.size + 15) / 16 ) * 16; + wopts.overwrite = calloc(32, 2048); wsrc = iso_source_new_ecma119(volset, &wopts); @@ -193,7 +194,7 @@ int main(int argc, char **argv) burn_write_opts_set_underrun_proof(burn_options, 1); //mmm, check for 32K alignment? - burn_write_opts_set_start_byte(burn_options, ropts.size * 2048); + burn_write_opts_set_start_byte(burn_options, wopts.ms_block * 2048); if (burn_write_opts_auto_write_type(burn_options, target_disc, reasons, 0) == BURN_WRITE_NONE) { diff --git a/test/iso_read.c b/test/iso_read.c index 9dbb05b..e7438a5 100644 --- a/test/iso_read.c +++ b/test/iso_read.c @@ -64,11 +64,30 @@ print_dir(struct iso_tree_node_dir *dir, int level) print_permissions(iso_tree_node_get_permissions(node)); printf(" %s -> %s \n", iso_tree_node_get_name(node), iso_tree_node_symlink_get_dest((struct iso_tree_node_symlink*)node) ); - } + } else { + /* boot catalog */ + printf("%s-[C] ", sp); + print_permissions(iso_tree_node_get_permissions(node)); + printf(" %s\n", iso_tree_node_get_name(node) ); + } } iso_tree_iter_free(iter); } +static void +check_el_torito(struct iso_volume *volume) +{ + struct iso_tree_node *cat, *img; + + if (iso_volume_get_boot_image(volume, &img, &cat)) { + + printf("\nEL-TORITO INFORMATION\n"); + printf("=====================\n\n"); + printf("Catalog: %s\n", iso_tree_node_get_name(cat) ); + printf("Image: %s\n", iso_tree_node_get_name(img) ); + } +} + int main(int argc, char **argv) { struct ecma119_read_opts opts; @@ -131,6 +150,8 @@ int main(int argc, char **argv) print_dir(iso_volume_get_root(volume), 0); + check_el_torito(volume); + printf("\n\n"); data_source_free(src);