diff --git a/libisofs/eltorito.c b/libisofs/eltorito.c index 5176d5c..95eb722 100644 --- a/libisofs/eltorito.c +++ b/libisofs/eltorito.c @@ -153,8 +153,14 @@ int el_torito_get_selection_crit(ElToritoBootImage *bootimg, uint8_t crit[20]) return 1; } +/* API */ +int el_torito_seems_boot_info_table(ElToritoBootImage *bootimg, int flag) +{ + return bootimg->seems_boot_info_table; +} + /** - * Specifies that this image needs to be patched. This involves the writting + * Specifies that this image needs to be patched. This involves the writing * of a 56 bytes boot information table at offset 8 of the boot image file. * The original boot image file won't be modified. * This is needed for isolinux boot images. @@ -173,7 +179,7 @@ void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg) * bitmask style flag. The following values are defined: * * bit 0 -> 1 to path the image, 0 to not - * Patching the image involves the writting of a 56 bytes + * Patching the image involves the writing of a 56 bytes * boot information table at offset 8 of the boot image file. * The original boot image file won't be modified. This is needed * to allow isolinux images to be bootable. @@ -405,6 +411,7 @@ int create_image(IsoImage *image, const char *image_path, boot->image = (IsoFile*)imgfile; iso_node_ref(imgfile); /* get our ref */ boot->bootable = 1; + boot->seems_boot_info_table = 0; boot->isolinux_options = 0; boot->type = boot_media_type; boot->partition_type = partition_type; @@ -677,10 +684,12 @@ void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat) image = cat->bootimages[i]; if (image == NULL) continue; - iso_node_unref((IsoNode*)image->image); + if ((IsoNode*)image->image != NULL) + iso_node_unref((IsoNode*)image->image); free(image); } - iso_node_unref((IsoNode*)cat->node); + if ((IsoNode*)cat->node != NULL) + iso_node_unref((IsoNode*)cat->node); free(cat); } @@ -981,6 +990,50 @@ int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src) /******************* EL-TORITO WRITER *******************************/ +/** + * Insert boot info table content into buf. + * + * @return + * 1 on success, 0 error (but continue), < 0 error + */ +int make_boot_info_table(uint8_t *buf, uint32_t pvd_lba, + uint32_t boot_lba, uint32_t imgsize) +{ + struct boot_info_table *info; + uint32_t checksum; + uint32_t offset; + + info = (struct boot_info_table *) (buf + 8); + if (imgsize < 64) + return ISO_ISOLINUX_CANT_PATCH; + + /* compute checksum, as the the sum of all 32 bit words in boot image + * from offset 64 */ + checksum = 0; + offset = 64; + + while (offset <= imgsize - 4) { + checksum += iso_read_lsb(buf + offset, 4); + offset += 4; + } + if (offset != imgsize) { + /* + * file length not multiple of 4 + * empty space in isofs is padded with zero; + * assume same for last dword + */ + checksum += iso_read_lsb(buf + offset, imgsize - offset); + } + + /*memset(info, 0, sizeof(struct boot_info_table));*/ + iso_lsb(info->bi_pvd, pvd_lba, 4); + iso_lsb(info->bi_file, boot_lba, 4); + iso_lsb(info->bi_length, imgsize, 4); + iso_lsb(info->bi_csum, checksum, 4); + memset(buf + 24, 0, 40); + return ISO_SUCCESS; +} + /** * Patch an isolinux boot image. * @@ -990,6 +1043,24 @@ int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src) static int patch_boot_image(uint8_t *buf, Ecma119Image *t, size_t imgsize, int idx) { + +/* >>> ts B00428 BOOT : make this the default case */ +#define Libisofs_new_patch_boot_imagE 1 +#ifdef Libisofs_new_patch_boot_imagE + + int ret; + + if (imgsize < 64) { + return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, + "Isolinux image too small. We won't patch it."); + } + ret = make_boot_info_table(buf, t->ms_block + (uint32_t) 16, + t->bootsrc[idx]->sections[0].block, + (uint32_t) imgsize); + return ret; + +#else /* Libisofs_new_patch_boot_imagE */ + struct boot_info_table *info; uint32_t checksum; size_t offset; @@ -1024,7 +1095,11 @@ int patch_boot_image(uint8_t *buf, Ecma119Image *t, size_t imgsize, int idx) iso_lsb(info->bi_file, t->bootsrc[idx]->sections[0].block, 4); iso_lsb(info->bi_length, imgsize, 4); iso_lsb(info->bi_csum, checksum, 4); + memset(buf + 24, 0, 40); return ISO_SUCCESS; + +#endif /* ! Libisofs_new_patch_boot_imagE */ + } static @@ -1054,6 +1129,10 @@ int eltorito_writer_compute_data_blocks(IsoImageWriter *writer) continue; original = t->bootsrc[idx]->stream; size = (size_t) iso_stream_get_size(original); + + /* >>> BOOT ts B00428 : + check whether size is not too large for buffering */; + buf = calloc(1, size); if (buf == NULL) { return ISO_OUT_OF_MEM; diff --git a/libisofs/eltorito.h b/libisofs/eltorito.h index 65bc58c..40e4ec9 100644 --- a/libisofs/eltorito.h +++ b/libisofs/eltorito.h @@ -48,6 +48,10 @@ struct el_torito_boot_image { IsoFile *image; unsigned int bootable:1; /**< If the entry is bootable. */ + /** + * Whether the boot image seems to contain a boot_info_table + */ + unsigned int seems_boot_info_table:1; /** * isolinux options * bit 0 -> whether to patch image @@ -126,4 +130,14 @@ int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src); */ int eltorito_writer_create(Ecma119Image *target); +/** + * Insert boot info table content into buf. + * + * @return + * 1 on success, 0 error (but continue), < 0 error + */ +int make_boot_info_table(uint8_t *buf, uint32_t pvd_lba, + uint32_t boot_lba, uint32_t imgsize); + + #endif /* LIBISO_ELTORITO_H */ diff --git a/libisofs/fs_image.c b/libisofs/fs_image.c index 267bb58..b38ace5 100644 --- a/libisofs/fs_image.c +++ b/libisofs/fs_image.c @@ -2121,19 +2121,15 @@ int read_root_susp_entries(_ImageFsData *data, uint32_t block) } static -int read_pvm(_ImageFsData *data, uint32_t block) +int read_pvd_block(IsoDataSource *src, uint32_t block, uint8_t *buffer, + uint32_t *image_size) { int ret; struct ecma119_pri_vol_desc *pvm; - struct ecma119_dir_record *rootdr; - uint8_t buffer[BLOCK_SIZE]; - /* read PVM */ - ret = data->src->read_block(data->src, block, buffer); - if (ret < 0) { + ret = src->read_block(src, block, buffer); + if (ret < 0) return ret; - } - pvm = (struct ecma119_pri_vol_desc *)buffer; /* sanity checks */ @@ -2143,8 +2139,24 @@ int read_pvm(_ImageFsData *data, uint32_t block) return ISO_WRONG_PVD; } + if (image_size != NULL) + *image_size = iso_read_bb(pvm->vol_space_size, 4, NULL); + 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; + uint8_t buffer[BLOCK_SIZE]; + + ret = read_pvd_block(data->src, block, buffer, NULL); + if (ret < 0) + return ret; /* ok, it is a valid PVD */ + pvm = (struct ecma119_pri_vol_desc *)buffer; /* fill volume attributes */ /* TODO take care of input charset */ @@ -2206,6 +2218,7 @@ int read_el_torito_boot_catalog(_ImageFsData *data, uint32_t block) struct el_torito_section_entry *entry; /* also usable as default_entry */ unsigned char buffer[BLOCK_SIZE]; + data->num_bootimgs = 0; ret = data->src->read_block(data->src, block, buffer); if (ret < 0) { return ret; @@ -2281,7 +2294,6 @@ int read_el_torito_boot_catalog(_ImageFsData *data, uint32_t block) } } after_bootblocks:; - return ISO_SUCCESS; } @@ -3005,6 +3017,105 @@ boot_fs_cleanup: ; return ret; } +/** ??? ts B00428 : should the max size become public ? */ +#define Libisofs_boot_image_max_sizE (4096*1024) + +/** ts B00428 BOOT : perform boot-info-table detection +*/ +static +int iso_image_eval_boot_info_table(IsoImage *image, struct iso_read_opts *opts, + IsoDataSource *src, uint32_t iso_image_size, int flag) +{ + int i, ret, section_count, todo, chunk; + uint32_t img_lba, img_size, boot_pvd_found, image_pvd, alleged_size; + struct iso_file_section *sections = NULL; + struct el_torito_boot_image *boot; + uint8_t *boot_image_buf = NULL, boot_info_found[16], buf[BLOCK_SIZE]; + IsoStream *stream = NULL; + IsoFile *boot_file; + + if (image->bootcat == NULL) + return ISO_SUCCESS; + for (i = 0; i < image->bootcat->num_bootimages; i++) { + boot = image->bootcat->bootimages[i]; + boot_file = boot->image; + boot->seems_boot_info_table = 0; + img_size = iso_file_get_size(boot_file); + if (img_size > Libisofs_boot_image_max_sizE || img_size < 64) + continue; + img_lba = 0; + sections = NULL; + ret = iso_file_get_old_image_sections(boot_file, + §ion_count, §ions, 0); + if (ret == 1 && section_count > 0) + img_lba = sections[0].block; + if (sections != NULL) { + free(sections); + sections = NULL; + } + if(img_lba == 0) + continue; + + boot_image_buf = calloc(1, img_size); + if (boot_image_buf == NULL) { + ret = ISO_OUT_OF_MEM; + goto ex; + } + stream = iso_file_get_stream(boot_file); + ret = iso_stream_open(stream); + if (ret < 0) { + stream = NULL; + goto ex; + } + for (todo = img_size; todo > 0; ) { + if (todo > BLOCK_SIZE) + chunk = BLOCK_SIZE; + else + chunk = todo; + ret = iso_stream_read(stream, boot_image_buf + (img_size - todo), + chunk); + if (ret != chunk) { + ret = (ret < 0) ? ret : ISO_FILE_READ_ERROR; + goto ex; + } + todo -= chunk; + } + iso_stream_close(stream); + stream = NULL; + + memcpy(boot_info_found, boot_image_buf + 8, 16); + boot_pvd_found = iso_read_lsb(boot_info_found, 4); + image_pvd = (uint32_t) (opts->block + 16); + + /* Accomodate to eventually relocated superblock */ + if (image_pvd != boot_pvd_found && + image_pvd == 16 && boot_pvd_found < iso_image_size) { + /* Check whether there is a PVD at boot_pvd_found + and whether it bears the same image size + */ + ret = read_pvd_block(src, boot_pvd_found, buf, &alleged_size); + if (ret == 1 && + alleged_size + boot_pvd_found == iso_image_size + image_pvd) + image_pvd = boot_pvd_found; + } + + ret = make_boot_info_table(boot_image_buf, image_pvd, + img_lba, img_size); + if (ret < 0) + goto ex; + if (memcmp(boot_image_buf + 8, boot_info_found, 16) == 0) + boot->seems_boot_info_table = 1; + free(boot_image_buf); + boot_image_buf = NULL; + } + ret = 1; +ex:; + if (boot_image_buf != NULL) + free(boot_image_buf); + if (stream != NULL) + iso_stream_close(stream); + return ret; +} int iso_image_import(IsoImage *image, IsoDataSource *src, struct iso_read_opts *opts, @@ -3140,6 +3251,7 @@ int iso_image_import(IsoImage *image, IsoDataSource *src, ret = ISO_OUT_OF_MEM; goto import_revert; } + boot_image->image = NULL; boot_image->bootable = data->boot_flags[idx] & 1; boot_image->type = data->media_types[idx]; boot_image->partition_type = data->partition_types[idx]; @@ -3314,6 +3426,11 @@ int iso_image_import(IsoImage *image, IsoDataSource *src, #endif /* Libisofs_with_checksumS */ + /* ts B00428 */ + ret = iso_image_eval_boot_info_table(image, opts, src, data->nblocks, 0); + if (ret < 0) + goto import_revert; + ret = ISO_SUCCESS; goto import_cleanup; diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 53a155a..25ea5ca 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -2631,35 +2631,41 @@ int el_torito_set_selection_crit(ElToritoBootImage *bootimg, uint8_t crit[20]); */ int el_torito_get_selection_crit(ElToritoBootImage *bootimg, uint8_t crit[20]); + /** - * Specifies that this image needs to be patched. This involves the writing - * of a 56 bytes boot information table at offset 8 of the boot image file. - * The original boot image file won't be modified. - * This is needed for isolinux boot images. + * Makes a guess whether the boot image was patched by a boot information + * table. It is advisable to patch such boot images if their content gets + * copied to a new location. See el_torito_set_isolinux_options(). + * Note: The reply can be positive only if the boot image was imported + * from an existing ISO image. * - * @since 0.6.2 - * @deprecated Use el_torito_set_isolinux_options() instead + * @param bootimg + * The image to inquire + * @return + * 1 = seems to contain oot info table , 0 = quite surely not + * @since 0.6.32 */ -void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg); +int el_torito_seems_boot_info_table(ElToritoBootImage *bootimg, int flag); /** * Specifies options for ISOLINUX or GRUB boot images. This should only be used * if the type of boot image is known. * - * Regrettably there is no unambigous way to detect the presence of a boot - * info table in a boot image or the relation of a boot image to the System - * Area and its eventual MBR. - * * @param options * bitmask style flag. The following values are defined: * * bit 0 -> 1 to patch the boot info table of the boot image. * 1 does the same as mkisofs option -boot-info-table. - * Needed for ISOLINUX and for GRUB rescue boot images. + * Needed for ISOLINUX or GRUB boot images with platform ID 0. * The table is located at byte 8 of the boot image file. * Its size is 56 bytes. * The original boot image file on disk will not be modified. * + * One may use el_torito_seems_boot_info_table() for a + * qualified guess whether a boot info table is present in + * the boot image. If the result is 1 then it should get bit0 + * set if its content gets copied to a new LBA. + * * bit 1 -> 1 to generate a ISOLINUX isohybrid image with MBR. * ---------------------------------------------------------- * @deprecated since 31 Mar 2010: @@ -2695,6 +2701,17 @@ int el_torito_set_isolinux_options(ElToritoBootImage *bootimg, */ int el_torito_get_isolinux_options(ElToritoBootImage *bootimg, int flag); +/** Deprecated: + * Specifies that this image needs to be patched. This involves the writing + * of a 16 bytes boot information table at offset 8 of the boot image file. + * The original boot image file won't be modified. + * This is needed for isolinux boot images. + * + * @since 0.6.2 + * @deprecated Use el_torito_set_isolinux_options() instead + */ +void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg); + /** * Obtain a copy of the eventually loaded first 32768 bytes of the imported * session, the System Area.