From f13167335a62b317c8a09ebf01e68bbc5bc79bd0 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Tue, 6 Apr 2010 14:41:36 +0200 Subject: [PATCH] New API call iso_write_opts_set_system_area() acts like mkisofs option -G --- libisofs/ecma119.c | 41 +++++++++++++ libisofs/ecma119.h | 21 +++++++ libisofs/eltorito.c | 4 +- libisofs/eltorito.h | 3 +- libisofs/libisofs.h | 52 +++++++++++++--- libisofs/system_area.c | 136 ++++++++++++++++++++++++++++++++--------- 6 files changed, 218 insertions(+), 39 deletions(-) diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 203f036..876a54e 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -73,6 +73,9 @@ void ecma119_image_free(Ecma119Image *t) if (t->output_charset != NULL) free(t->output_charset); + if (t->system_area_data != NULL) + free(t->system_area_data); + #ifdef Libisofs_with_checksumS if (t->checksum_ctx != NULL) { /* dispose checksum context */ char md5[16]; @@ -1127,6 +1130,17 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) target->eltorito = (src->bootcat == NULL ? 0 : 1); target->catalog = src->bootcat; + target->system_area_data = NULL; + if(opts->system_area_data != NULL) { + target->system_area_data = calloc(32768, 1); + if (target->system_area_data == NULL) { + ret = ISO_OUT_OF_MEM; + goto target_cleanup; + } + memcpy(target->system_area_data, opts->system_area_data, 32768); + } + target->system_area_options = opts->system_area_options; + target->input_charset = strdup(iso_get_local_charset(0)); if (target->input_charset == NULL) { ret = ISO_OUT_OF_MEM; @@ -1700,6 +1714,8 @@ void iso_write_opts_free(IsoWriteOpts *opts) } free(opts->output_charset); + if(opts->system_area_data != NULL) + free(opts->system_area_data); free(opts); } @@ -2085,3 +2101,28 @@ int iso_write_opts_get_data_start(IsoWriteOpts *opts, uint32_t *data_start, return ISO_SUCCESS; } +/* + * @param data Either NULL or 32 kB of data. Do not submit less bytes ! + * @param options bit0 = apply GRUB protective msdos label + * @param flag bit0 = invalidate any attached system area data + * same as data == NULL + */ +int iso_write_opts_set_system_area(IsoWriteOpts *opts, char data[32768], + int options, int flag) +{ + if (data == NULL || (flag & 1)) { /* Disable */ + if (opts->system_area_data != NULL) + free(opts->system_area_data); + opts->system_area_data = NULL; + } else { + if (opts->system_area_data == NULL) { + opts->system_area_data = calloc(32768, 1); + if (opts->system_area_data == NULL) + return ISO_OUT_OF_MEM; + } + memcpy(opts->system_area_data, data, 32768); + } + opts->system_area_options = options & 1; + return ISO_SUCCESS; +} + diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index c8a7a00..d91abf5 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -298,6 +298,11 @@ struct iso_write_opts { */ char *scdbackup_tag_written; + /* + * See ecma119_image : System Area related information + */ + char *system_area_data; + int system_area_options; }; typedef struct ecma119_image Ecma119Image; @@ -436,6 +441,22 @@ struct ecma119_image IsoFileSrc *cat; /**< location of the boot catalog in the new image */ IsoFileSrc *bootimg; /**< location of the boot image in the new image */ + /* + * System Area related information + */ + /* Content of an embedded boot image. Valid if not NULL. + * In that case it must point to a memory buffer at least 32 kB. + */ + char *system_area_data; + /* + * bit0= make bytes 446 - 512 of the system area a partition + * table which reserves partition 1 from byte 63*512 to the + * end of the ISO image. Assumed are 63 secs/hed, 255 head/cyl. + * (GRUB protective msdos label.) + * This works with and without system_area_data. + */ + int system_area_options; + /* * Number of pad blocks that we need to write. Padding blocks are blocks * filled by 0s that we put between the directory structures and the file diff --git a/libisofs/eltorito.c b/libisofs/eltorito.c index 6d93c6a..0bc79a5 100644 --- a/libisofs/eltorito.c +++ b/libisofs/eltorito.c @@ -333,9 +333,11 @@ int create_image(IsoImage *image, const char *image_path, boot->image = (IsoFile*)imgfile; iso_node_ref(imgfile); /* get our ref */ boot->bootable = 1; + boot->isolinux_options = 0; boot->type = boot_media_type; - boot->load_size = load_sectors; boot->partition_type = partition_type; + boot->load_seg = 0; + boot->load_size = load_sectors; if (bootimg) { *bootimg = boot; diff --git a/libisofs/eltorito.h b/libisofs/eltorito.h index d467d8a..f49547a 100644 --- a/libisofs/eltorito.h +++ b/libisofs/eltorito.h @@ -39,7 +39,8 @@ struct el_torito_boot_image { /** * isolinux options * bit 0 -> whether to patch image - * bit 1 -> whether to create isolinux image + * bit 1 -> whether to put built-in isolinux 3.72 isohybrid-MBR into image + * System Area (deprecated) */ unsigned int isolinux_options:2; unsigned char type; /**< The type of image */ diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 41cc6b4..6d54f5c 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -1680,6 +1680,32 @@ int iso_write_opts_set_overwrite_buf(IsoWriteOpts *opts, uint8_t *overwrite); */ int iso_write_opts_set_fifo_size(IsoWriteOpts *opts, size_t fifo_size); +/* + * Attach 32 kB of binary data which shall get written to the first 32 kB + * of the ISO image, the ECMA-119 System Area. This space is intended for + * system dependent boot software, e.g. a Master Boot Record which allows to + * boot from USB sticks or hard disks. ECMA-119 makes no own assumptions or + * prescriptions about the byte content. + * + * If system area data are given or options bit0 is set, then bit1 of + * el_torito_set_isolinux_options() is automatically disabled. + * @param data + * Either NULL or 32 kB of data. Do not submit less bytes ! + * @param options + * Can cause manipulations of submitted data before they get written: + * bit0= apply a --protective-msdos-label as of grub-mkisofs. + * This means to patch bytes 446 to 512 of the system area so + * that one partition is defined which begins at the second + * 512-byte block of the image and ends where the image ends. + * @param flag + * bit0 = invalidate any attached system area data. Same as data == NULL + * @return + * ISO_SUCCESS or error + * @since 0.6.30 + */ +int iso_write_opts_set_system_area(IsoWriteOpts *opts, char data[32768], + int options, int flag); + /** * Inquire the start address of the file data blocks after having used * IsoWriteOpts with iso_image_create_burn_source(). @@ -2325,19 +2351,27 @@ void el_torito_set_no_bootable(ElToritoBootImage *bootimg); void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg); /** - * Specifies options for IsoLinux boot images. This should only be used with - * isolinux boot images. + * Specifies options for ISOLINUX or GRUB boot images. This should only be used + * if the type of boot image is known. * * @param options * bitmask style flag. The following values are defined: * - * bit 0 -> 1 to patch the image, 0 to not - * 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 will not be modified. This is - * needed to allow isolinux images to be bootable. - * bit 1 -> 1 to generate an hybrid image with MBR, 0 to not - * An hybrid image is a boot image that boots from either + * 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. + * 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. + * + * bit 1 -> 1 to generate a ISOLINUX isohybrid image with MBR. + * ---------------------------------------------------------- + * @deprecated since 31 Mar 2010: + * The author of syslinux, H. Peter Anvin requested that this + * feature shall not be used any more. He intends to cease + * support for the MBR template that is included in libisofs. + * ---------------------------------------------------------- + * A hybrid image is a boot image that boots from either * CD/DVD media or from disk-like media, e.g. USB stick. * For that you need isolinux.bin from SYSLINUX 3.72 or later. * IMPORTANT: The application has to take care that the image diff --git a/libisofs/system_area.c b/libisofs/system_area.c index b7dd260..79fe939 100644 --- a/libisofs/system_area.c +++ b/libisofs/system_area.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2008 Vreixo Formoso + * Copyright (c) 2010 Thomas Schmitt * * 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 @@ -12,28 +13,104 @@ #include "filesrc.h" #include - +#include /* * Create a MBR for an isohybrid enabled ISOLINUX boot image. - * - * It is assumed that the caller has verified the readiness of the boot image - * by checking for 0xfb 0xc0 0x78 0x70 at bytes 0x40 to 0x43 of isolinux.bin. - * - * @param bin_lba The predicted LBA of isolinux.bin within the emerging ISO. - * @param img_blocks The predicted number of 2048 byte blocks in the ISO. - * It will get rounded up to full MBs and that many blocks - * must really be written as ISO 9660 image. - * @param mbr A buffer of at least 512 bytes to take the result which is - * to be written as the very beginning of the ISO. - * @param flag unused yet, submit 0 - * @return <0 = fatal, 0 = failed , 1 = ok , 2 = ok with size warning + * See libisofs/make_isohybrid_mbr.c + * Deprecated. */ int make_isohybrid_mbr(int bin_lba, int *img_blocks, char *mbr, int flag); + + +/* This is the gesture of grub-mkisofs --protective-msdos-label as explained by + Vladimir Serbinenko , 2 April 2010, on grub-devel@gnu.org + "Currently we use first and not last entry. You need to: + 1) Zero-fill 446-510 + 2) Put 0x55, 0xAA into 510-512 + 3) Put 0x80 (for bootable partition), 0, 2, 0 (C/H/S of the start), 0xcd + (partition type), [3 bytes of C/H/S end], 0x01, 0x00, 0x00, 0x00 (LBA + start in little endian), [LBA end in little endian] at 446-462 + " + + "C/H/S end" means the CHS address of the last block in the partition. + It seems that not "[LBA end in little endian]" but "number of blocks" + should go into bytes 458-461. But with a start lba of 1, this is the + same number. + See also http://en.wikipedia.org/wiki/Master_boot_record +*/ +int make_grub_msdos_label(int img_blocks, uint8_t *buf, int flag) +{ + uint8_t *wpt; + unsigned long end_lba, secs, end_sec, end_head, end_cyl; + int sph = 63, hpc = 255, i; + + /* Partition table unit is 512 bytes per sector, ECMA-119 unit is 2048 */ + if (img_blocks >= 0x40000000) + img_blocks = 0x40000000 - 1; /* truncate rather than roll over */ + secs = end_lba = img_blocks * 4 - 1; /* last valid 512-lba */ + end_cyl = secs / (sph * hpc); + secs -= end_cyl * sph * hpc; + end_head = secs / sph; + end_sec = secs - end_head * sph + 1; /* Sector count starts by 1 */ + if (end_cyl >= 1024) { + end_cyl = 1023; + end_head = hpc - 1; + end_sec = sph; + } + + /* 1) Zero-fill 446-510 */ + wpt = buf + 446; + memset(wpt, 0, 64); + + /* 2) Put 0x55, 0xAA into 510-512 (actually 510-511) */ + buf[510] = 0x55; + buf[511] = 0xAA; + + /* 3) Put 0x80 (for bootable partition), */ + *(wpt++) = 0x80; + + /* 0, 2, 0 (C/H/S of the start), */ + *(wpt++) = 0; + *(wpt++) = 2; + *(wpt++) = 0; + + /* 0xcd (partition type) */ + *(wpt++) = 0xcd; + + /* [3 bytes of C/H/S end], */ + *(wpt++) = end_head; + *(wpt++) = end_sec | ((end_cyl & 0x300) >> 2); + *(wpt++) = end_cyl & 0xff; + + + /* 0x01, 0x00, 0x00, 0x00 (LBA start in little endian), */ + *(wpt++) = 0x01; + *(wpt++) = 0x00; + *(wpt++) = 0x00; + *(wpt++) = 0x00; + + /* [LBA end in little endian] */ + for (i = 0; i < 4; i++) + *(wpt++) = (end_lba >> (8 * i)) & 0xff; + + /* at 446-462 */ + if (wpt - buf != 462) { + fprintf(stderr, + "libisofs: program error in make_grub_msdos_label: \"assert 462\"\n"); + return ISO_ASSERT_FAILURE; + } + return ISO_SUCCESS; +} + + int iso_write_system_area(Ecma119Image *t, uint8_t *buf) { + int ret; + int img_blocks; + if ((t == NULL) || (buf == NULL)) { return ISO_NULL_POINTER; } @@ -41,26 +118,29 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) /* set buf to 0s */ memset(buf, 0, 16 * BLOCK_SIZE); - if (t->catalog != NULL && t->catalog->image->isolinux_options & 0x02) { - /* We need to write a MBR for an hybrid image */ - int ret; - int img_blocks; - - img_blocks = t->curblock; - ret = make_isohybrid_mbr(t->bootimg->sections[0].block, &img_blocks, (char*)buf, 0); - -/* - API description of el_torito_set_isolinux_options() prescribes - to pad to full MB. - So this is not urgent any more : - - // FIXME the new img_blocks size should be taken into account -*/ + img_blocks = t->curblock; + if (t->system_area_data != NULL) { + /* Write more or less opaque boot image */ + memcpy(buf, t->system_area_data, 16 * BLOCK_SIZE); + } else if (t->catalog != NULL && + (t->catalog->image->isolinux_options & 0x0a) == 0x02) { + /* Check for isolinux image with magic number of 3.72 and produce + an MBR from our built-in template. (Deprecated since 31 Mar 2010) + */ + ret = make_isohybrid_mbr(t->bootimg->sections[0].block, + &img_blocks, (char*)buf, 0); if (ret != 1) { /* error, it should never happen */ return ISO_ASSERT_FAILURE; } + return ISO_SUCCESS; + } + if (t->system_area_options & 1) { + /* Write GRUB protective msdos label, i.e. a isimple partition table */ + ret = make_grub_msdos_label(img_blocks, buf, 0); + if (ret != 1) /* error should never happen */ + return ISO_ASSERT_FAILURE; } return ISO_SUCCESS; }