diff --git a/doc/boot_sectors.txt b/doc/boot_sectors.txt index e4b327f..91df213 100644 --- a/doc/boot_sectors.txt +++ b/doc/boot_sectors.txt @@ -266,9 +266,8 @@ Cylinder/Head/Sector (C/H/S) and Logical Block Address (LBA). Both are based on units of 512 bytes. So MBR_LBA = ISO_LBA * 4. For C/H/S, the sector address is broken up into whole cylinders, remaining -heads, and remaining sectors. The nomenclature seems to stem from antique +heads, and remaining sectors + 1. The nomenclature seems to stem from antique drum storage. -C/H/S counting starts with 0/0/1, not 0/0/0. There are two parameters, sectors_per_head and heads_per_cylinder which are not stored in the MBR. So it is more or less arbitray how to convert a LBA into a C/H/S address and vice versa. For maximum range of C/H/S addresses one @@ -306,6 +305,7 @@ Byte Range | Value | Meaning 451 - 453 | ========== | C/H/S address of last absolute sector in partition 451 - 451 | end_head | Heads part of end address. 452 - 452 | end_c_s | Bits 0 to 5 : Sectors part of end address. + | Values: 1 to 63, not 0. | | Bits 6 to 7 : Bits 8 to 9 of cylinders part. 453 - 453 | end_cyl | Lower 8 bits of cylinders part of end address | | diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index ca51f42..4a5a05d 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -107,7 +107,10 @@ void ecma119_image_free(Ecma119Image *t) free(t->writers); if (t->partition_root != NULL) ecma119_node_free(t->partition_root); - t->partition_root = NULL; + for (i = 0; i < 4; i++) + if (t->appended_partitions[i] != NULL) + free(t->appended_partitions[i]); + free(t); } @@ -1295,6 +1298,39 @@ static int finish_libjte(Ecma119Image *target) } +static int write_mbr_partition_file(Ecma119Image *target, char *path, + uint32_t blocks, int flag) +{ + FILE *fp = NULL; + uint32_t i; + uint8_t buf[BLOCK_SIZE]; + int ret; + + fp = fopen(path, "rb"); + if (fp == NULL) + return ISO_BAD_PARTITION_FILE; + + for (i = 0; i < blocks; i++) { + memset(buf, 0, BLOCK_SIZE); + if (fp != NULL) { + ret = fread(buf, 1, BLOCK_SIZE, fp); + if (ret != BLOCK_SIZE) { + fclose(fp); + fp = NULL; + } + } + ret = iso_write(target, buf, BLOCK_SIZE); + if (ret < 0) { + fclose(fp); + return ret; + } + } + + fclose(fp); + return ISO_SUCCESS; +} + + static void *write_function(void *arg) { @@ -1321,6 +1357,17 @@ void *write_function(void *arg) } } + /* Append partition data */ + for (i = 0; i < 4; i++) { + if (target->appended_partitions[i] == NULL) + continue; + res = write_mbr_partition_file(target, target->appended_partitions[i], + target->appended_part_size[i], 0); + if (res < 0) + goto write_error; + } + + /* Transplant checksum buffer from Ecma119Image to IsoImage */ transplant_checksum_buffer(target, 0); @@ -1469,7 +1516,6 @@ int checksum_prepare_nodes(Ecma119Image *target, IsoNode *node, int flag) return ISO_SUCCESS; } - static int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) { @@ -1589,6 +1635,10 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) target->partition_offset = opts->partition_offset; target->partition_secs_per_head = opts->partition_secs_per_head; target->partition_heads_per_cyl = opts->partition_heads_per_cyl; + if (target->partition_secs_per_head == 0) + target->partition_secs_per_head = 63; + if (target->partition_heads_per_cyl == 0) + target->partition_heads_per_cyl = 255; target->eff_partition_offset = 0; target->partition_root = NULL; target->partition_l_table_pos = 0; @@ -1646,12 +1696,24 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) #endif /* Libisofs_with_libjtE */ + target->tail_blocks = opts->tail_blocks; + target->mipsel_e_entry = 0; target->mipsel_p_offset = 0; target->mipsel_p_vaddr = 0; target->mipsel_p_filesz = 0; - target->tail_blocks = opts->tail_blocks; + for (i = 0; i < 4; i++) { + if (opts->appended_partitions[i] != NULL) { + target->appended_partitions[i] = + strdup(opts->appended_partitions[i]); + if (target->appended_partitions[i] == NULL) + return ISO_OUT_OF_MEM; + target->appended_part_types[i] = opts->appended_part_types[i]; + } + target->appended_part_start[i] = target->appended_part_size[i] = 0; + } + /* * 2. Based on those options, create needed writers: iso, joliet... @@ -1843,6 +1905,18 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) goto target_cleanup; } + /* + * The volume space size is just the size of the last session, in + * case of ms images. + */ + target->vol_space_size = target->curblock - target->ms_block; + target->total_size = (off_t) target->vol_space_size * BLOCK_SIZE; + + /* Add sizes of eventually appended partitions */ + ret = iso_compute_append_partitions(target, 0); + if (ret < 0) + goto target_cleanup; + /* create the ring buffer */ if (opts->overwrite != NULL && opts->fifo_size / 2048 < 32 + target->partition_offset) { @@ -1936,13 +2010,6 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) } } - /* - * The volume space size is just the size of the last session, in - * case of ms images. - */ - target->vol_space_size = target->curblock - target->ms_block; - target->total_size = (off_t) target->vol_space_size * BLOCK_SIZE; - /* 4. Create and start writing thread */ if (target->md5_session_checksum) { @@ -2172,6 +2239,7 @@ int iso_write(Ecma119Image *target, void *buf, size_t count) int iso_write_opts_new(IsoWriteOpts **opts, int profile) { + int i; IsoWriteOpts *wopts; if (opts == NULL) { @@ -2231,6 +2299,8 @@ int iso_write_opts_new(IsoWriteOpts **opts, int profile) #endif /* Libisofs_with_libjtE */ wopts->tail_blocks = 0; + for (i = 0; i < 4; i++) + wopts->appended_partitions[i] = NULL; *opts = wopts; return ISO_SUCCESS; @@ -2238,13 +2308,17 @@ int iso_write_opts_new(IsoWriteOpts **opts, int profile) void iso_write_opts_free(IsoWriteOpts *opts) { + int i; + if (opts == NULL) { return; } - free(opts->output_charset); if (opts->system_area_data != NULL) free(opts->system_area_data); + for (i = 0; i < 4; i++) + if (opts->appended_partitions[i] != NULL) + free(opts->appended_partitions[i]); free(opts); } @@ -2718,7 +2792,23 @@ int iso_write_opts_detach_jte(IsoWriteOpts *opts, void **libjte_handle) int iso_write_opts_set_tail_blocks(IsoWriteOpts *opts, uint32_t num_blocks) { - opts->tail_blocks = num_blocks; - return ISO_SUCCESS; + opts->tail_blocks = num_blocks; + return ISO_SUCCESS; +} + +int iso_write_opts_set_partition_img(IsoWriteOpts *opts, int partition_number, + uint8_t partition_type, char *image_path, int flag) +{ + if (partition_number < 1 || partition_number > 4) + return ISO_BAD_PARTITION_NO; + + if (opts->appended_partitions[partition_number - 1] != NULL) + free(opts->appended_partitions[partition_number - 1]); + opts->appended_partitions[partition_number - 1] = strdup(image_path); + if (opts->appended_partitions[partition_number - 1] == NULL) + return ISO_OUT_OF_MEM; + opts->appended_part_types[partition_number - 1] = partition_type; + + return ISO_SUCCESS; } diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index 9cc44bc..1ed525a 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -334,6 +334,11 @@ struct iso_write_opts { */ uint32_t tail_blocks; + /* Eventual disk file paths of prepared images which shall be appended + after the ISO image and described by partiton table entries in a MBR + */ + char *appended_partitions[4]; + uint8_t appended_part_types[4]; }; typedef struct ecma119_image Ecma119Image; @@ -588,13 +593,20 @@ struct ecma119_image struct libjte_env *libjte_handle; #endif /* Libisofs_with_libjtE */ + uint32_t tail_blocks; + /* Memorized ELF parameters from MIPS Little Endian boot file */ uint32_t mipsel_e_entry; uint32_t mipsel_p_offset; uint32_t mipsel_p_vaddr; uint32_t mipsel_p_filesz; - uint32_t tail_blocks; + char *appended_partitions[4]; + uint8_t appended_part_types[4]; + /* Counted in blocks of 2048 */ + uint32_t appended_part_start[4]; + uint32_t appended_part_size[4]; + }; #define BP(a,b) [(b) - (a) + 1] diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 12d7f1f..81a2c5a 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -1936,6 +1936,32 @@ int iso_write_opts_detach_jte(IsoWriteOpts *opts, void **libjte_handle); */ int iso_write_opts_set_tail_blocks(IsoWriteOpts *opts, uint32_t num_blocks); +/** + * Cause an arbitrary data file to be appended to the ISO image and to be + * described by a partition table entry in an MBR at the start of the + * ISO image. + * The partition entry will bear the size of the image file rounded up to + * the next multiple of 2048 bytes. + * @param opts + * The option set to be manipulated. + * @param partition_number + * Depicts the partition table entry which shall describe the + * appended image. Range 1 to 4. + * 1 will cause the whole ISO image to be unclaimable space before + * partition 1. + * @param image_path + * File address in the local file system. + * @param image_type + * The partition type. E.g. FAT12 = 0x01 , FAT16 = 0x06, + * Linux Native Partition = 0x83. See fdisk command L. + * @return + * ISO_SUCCESS or error + * + * @since 0.6.38 + */ +int iso_write_opts_set_partition_img(IsoWriteOpts *opts, int partition_number, + uint8_t partition_type, char *image_path, int flag); + /** * Inquire the start address of the file data blocks after having used @@ -6321,12 +6347,17 @@ int iso_md5_match(char first_md5[16], char second_md5[16]); (MISHAP, HIGH, -366) */ #define ISO_LIBJTE_FILE_FAILED 0xE430FE92 -/** Too many MIPS Big Endian boot files given (max. 15) (FAILURE, HIGH, -365)*/ +/** Too many MIPS Big Endian boot files given (max. 15) (FAILURE, HIGH, -367)*/ #define ISO_BOOT_TOO_MANY_MIPS 0xE830FE91 -/** Boot file missing in image (MISHAP, HIGH, -364) */ +/** Boot file missing in image (MISHAP, HIGH, -368) */ #define ISO_BOOT_FILE_MISSING 0xE430FE90 +/** Partition number out of range (FAILURE, HIGH, -369) */ +#define ISO_BAD_PARTITION_NO 0xE830FE8F + +/** Cannot open data file for appended partition (FAILURE, HIGH, -370) */ +#define ISO_BAD_PARTITION_FILE 0xE830FE8E /* Internal developer note: diff --git a/libisofs/libisofs.ver b/libisofs/libisofs.ver index d1df4fc..8de9482 100644 --- a/libisofs/libisofs.ver +++ b/libisofs/libisofs.ver @@ -279,6 +279,7 @@ iso_write_opts_set_omit_version_numbers; iso_write_opts_set_output_charset; iso_write_opts_set_overwrite_buf; iso_write_opts_set_part_offset; +iso_write_opts_set_partition_img; iso_write_opts_set_pvd_times; iso_write_opts_set_record_md5; iso_write_opts_set_relaxed_vol_atts; diff --git a/libisofs/messages.c b/libisofs/messages.c index 54fa682..92feb5e 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -353,6 +353,10 @@ const char *iso_error_to_msg(int errcode) return "Too many MIPS Big Endian boot files given (max. 15)"; case ISO_BOOT_FILE_MISSING: return "Boot file missing in image"; + case ISO_BAD_PARTITION_NO: + return "Partition number out of range"; + case ISO_BAD_PARTITION_FILE: + return "Cannot open data file for appended partition"; default: return "Unknown error"; } diff --git a/libisofs/system_area.c b/libisofs/system_area.c index c21182b..691e2d4 100644 --- a/libisofs/system_area.c +++ b/libisofs/system_area.c @@ -22,6 +22,9 @@ #include #include +#include +#include +#include /* @@ -72,6 +75,85 @@ void iso_compute_cyl_head_sec(uint32_t *img_blocks, int hpc, int sph, } +/* Compute size and position of appended partitions. +*/ +int iso_compute_append_partitions(Ecma119Image *t, int flag) +{ + int ret, i; + uint32_t pos, size; + struct stat stbuf; + + pos = (t->vol_space_size + t->ms_block); + for (i = 0; i < 4; i++) { + if (t->appended_partitions[i] == NULL) + continue; + ret = stat(t->appended_partitions[i], &stbuf); + if (ret == -1) + return ISO_BAD_PARTITION_FILE; + if (! S_ISREG(stbuf.st_mode)) + return ISO_BAD_PARTITION_FILE; + size = ((stbuf.st_size + 2047) / 2048); + t->appended_part_start[i] = pos; + t->appended_part_size[i] = size; + pos += size; + t->total_size += size * 2048; + } + return ISO_SUCCESS; +} + + +/* Note: partition_offset and partition_size are counted in 2048 blocks + */ +static int write_mbr_partition_entry(int partition_number, int partition_type, + uint32_t partition_offset, uint32_t partition_size, + int sph, int hpc, uint8_t *buf, int flag) +{ + uint8_t *wpt; + uint32_t end_lba, end_sec, end_head, end_cyl; + uint32_t start_lba, start_sec, start_head, start_cyl; + uint32_t after_end; + int i; + + after_end = partition_offset + partition_size; + iso_compute_cyl_head_sec(&partition_offset, hpc, sph, + &start_lba, &start_sec, &start_head, &start_cyl, 1); + iso_compute_cyl_head_sec(&after_end, hpc, sph, + &end_lba, &end_sec, &end_head, &end_cyl, 0); + wpt = buf + 446 + (partition_number - 1) * 16; + + /* Not bootable */ + *(wpt++) = 0x00; + + /* C/H/S of the start */ + *(wpt++) = start_head; + *(wpt++) = start_sec | ((start_cyl & 0x300) >> 2); + *(wpt++) = start_cyl & 0xff; + + /* (partition type) */ + *(wpt++) = partition_type; + + /* 3 bytes of C/H/S end */ + *(wpt++) = end_head; + *(wpt++) = end_sec | ((end_cyl & 0x300) >> 2); + *(wpt++) = end_cyl & 0xff; + + /* LBA start in little endian */ + for (i = 0; i < 4; i++) + *(wpt++) = (start_lba >> (8 * i)) & 0xff; + + /* Number of sectors in partition, little endian */ + end_lba = end_lba - start_lba + 1; + for (i = 0; i < 4; i++) + *(wpt++) = (end_lba >> (8 * i)) & 0xff; + + /* Afaik, partition tables are recognize donly with MBR signature */ + buf[510] = 0x55; + buf[511] = 0xAA; + + return ISO_SUCCESS; +} + + /* 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: @@ -92,11 +174,12 @@ void iso_compute_cyl_head_sec(uint32_t *img_blocks, int hpc, int sph, bit1= do not mark partition as bootable */ static -int make_grub_msdos_label(uint32_t img_blocks, uint8_t *buf, int flag) +int make_grub_msdos_label(uint32_t img_blocks, int sph, int hpc, + uint8_t *buf, int flag) { uint8_t *wpt; uint32_t end_lba, end_sec, end_head, end_cyl; - int sph = 63, hpc = 255, i; + int i; iso_compute_cyl_head_sec(&img_blocks, hpc, sph, &end_lba, &end_sec, &end_head, &end_cyl, 0); @@ -155,17 +238,13 @@ int make_grub_msdos_label(uint32_t img_blocks, uint8_t *buf, int flag) */ static int iso_offset_partition_start(uint32_t img_blocks, uint32_t partition_offset, - int sph_in, int hpc_in, uint8_t *buf, int flag) + int sph, int hpc, uint8_t *buf, int flag) { uint8_t *wpt; uint32_t end_lba, end_sec, end_head, end_cyl; uint32_t start_lba, start_sec, start_head, start_cyl; - int sph = 63, hpc = 255, i; + int i; - if (sph_in > 0) - sph = sph_in; - if (hpc_in > 0) - hpc = hpc_in; iso_compute_cyl_head_sec(&partition_offset, hpc, sph, &start_lba, &start_sec, &start_head, &start_cyl, 1); iso_compute_cyl_head_sec(&img_blocks, hpc, sph, @@ -180,7 +259,7 @@ int iso_offset_partition_start(uint32_t img_blocks, uint32_t partition_offset, /* C/H/S of the start */ *(wpt++) = start_head; *(wpt++) = start_sec | ((start_cyl & 0x300) >> 2); - *(wpt++) = end_cyl & 0xff; + *(wpt++) = start_cyl & 0xff; /* (partition type) */ wpt++; @@ -302,9 +381,6 @@ static int make_mips_volume_header(Ecma119Image *t, uint8_t *buf, int flag) /* 88 - 311 | 0 | Volume Directory Entries 2 to 15 */ for (idx = 0; idx < t->image->num_mips_boot_files; idx++) { - -#ifndef NIX - ret = boot_nodes_from_iso_path(t, t->image->mips_boot_file_paths[idx], &node, &ecma_node, "MIPS boot file", 0); if (ret < 0) @@ -314,45 +390,6 @@ static int make_mips_volume_header(Ecma119Image *t, uint8_t *buf, int flag) name_field = (char *) (buf + (72 + 16 * idx)); strncpy(name_field, namept, 8); -#else /* ! NIX */ - - ret = iso_tree_path_to_node(t->image, - t->image->mips_boot_file_paths[idx], &node); - if (ret < 0) { - iso_msg_submit(t->image->id, ISO_BOOT_FILE_MISSING, 0, - "Cannot find MIPS boot file '%s'", - t->image->mips_boot_file_paths[idx]); - return ISO_BOOT_FILE_MISSING; - } - if (node->type != LIBISO_FILE) { - iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, - "Designated MIPS boot file is not a data file: '%s'", - t->image->mips_boot_file_paths[idx]); - return ISO_BOOT_IMAGE_NOT_VALID; - } - - namept = (char *) iso_node_get_name(node); - name_field = (char *) (buf + (72 + 16 * idx)); - strncpy(name_field, namept, 8); - - ecma_node= ecma119_search_iso_node(t, node); - if (ecma_node != NULL) { - if (ecma_node->type != ECMA119_FILE) { - iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, - "Program error: Ecma119Node of IsoFile is no ECMA119_FILE: '%s'", - t->image->mips_boot_file_paths[idx]); - return ISO_ASSERT_FAILURE; - } - } else { - iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, - "Program error: IsoFile has no Ecma119Node: '%s'", - t->image->mips_boot_file_paths[idx]); - return ISO_ASSERT_FAILURE; - } - -#endif /* NIX */ - - file_lba = ecma_node->info.file->sections[0].block; iso_msb(buf + (72 + 16 * idx) + 8, file_lba * 4, 4); @@ -529,7 +566,7 @@ static int make_mipsel_boot_block(Ecma119Image *t, uint8_t *buf, int flag) int iso_write_system_area(Ecma119Image *t, uint8_t *buf) { - int ret, int_img_blocks, sa_type; + int ret, int_img_blocks, sa_type, i; uint32_t img_blocks; if ((t == NULL) || (buf == NULL)) { @@ -565,7 +602,8 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) } if (sa_type == 0 && (t->system_area_options & 1)) { /* Write GRUB protective msdos label, i.e. a simple partition table */ - ret = make_grub_msdos_label(img_blocks, buf, 0); + ret = make_grub_msdos_label(img_blocks, t->partition_secs_per_head, + t->partition_heads_per_cyl, buf, 0); if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; } else if(sa_type == 0 && (t->system_area_options & 2)) { @@ -577,7 +615,8 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) return ISO_ISOLINUX_CANT_PATCH; } ret = make_isolinux_mbr(&img_blocks, t->bootsrc[0]->sections[0].block, - (uint32_t) 0, 64, 32, 0, 1, 0x17, buf, 1); + (uint32_t) 0, t->partition_heads_per_cyl, + t->partition_secs_per_head, 0, 1, 0x17, buf, 1); if (ret != 1) return ret; } else if(sa_type == 1) { @@ -590,7 +629,8 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) return ret; } else if(t->partition_offset > 0 && sa_type == 0) { /* Write a simple partition table. */ - ret = make_grub_msdos_label(img_blocks, buf, 2); + ret = make_grub_msdos_label(img_blocks, t->partition_secs_per_head, + t->partition_heads_per_cyl, buf, 2); if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; } @@ -605,5 +645,17 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) return ISO_ASSERT_FAILURE; } + /* This eventually overwrites the partition table entries made so far */ + for (i = 0; i < 4; i++) { + if (t->appended_partitions[i] == NULL) + continue; + ret = write_mbr_partition_entry(i + 1, t->appended_part_types[i], + t->appended_part_start[i], t->appended_part_size[i], + t->partition_secs_per_head, t->partition_heads_per_cyl, + buf, 0); + if (ret < 0) + return ret; + } + return ISO_SUCCESS; } diff --git a/libisofs/system_area.h b/libisofs/system_area.h index 47b68a2..cdf1a2e 100644 --- a/libisofs/system_area.h +++ b/libisofs/system_area.h @@ -53,4 +53,9 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf); */ int iso_read_mipsel_elf(Ecma119Image *t, int flag); + +/* Compute size and position of appended partitions. +*/ +int iso_compute_append_partitions(Ecma119Image *t, int flag); + #endif /* SYSTEM_AREA_H_ */