diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index ff1b3d2..30f5212 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -122,6 +122,9 @@ void ecma119_image_free(Ecma119Image *t) for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) if (t->hfsplus_blessed[i] != NULL) iso_node_unref(t->hfsplus_blessed[i]); + for (i = 0; (int) i < t->apm_req_count; i++) + if (t->apm_req[i] != NULL) + free(t->apm_req[i]); free(t); } @@ -1876,6 +1879,13 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) if (target->hfsplus_blessed[i] != NULL) iso_node_ref(target->hfsplus_blessed[i]); } + target->apm_block_size = 512; + for (i = 0; i < ISO_APM_ENTRIES_MAX; i++) + target->apm_req[i] = NULL; + /* Set apm_block_size to 2048, if desired, before pthread_create() + at the end of this function. + Register any Apple Partition Map entries before pthread_create(). + */ /* * 2. Based on those options, create needed writers: iso, joliet... diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index 29b5f68..04426fb 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -70,6 +70,11 @@ #endif +/* The maximum number of Apple Partition Map entries. +*/ +#define ISO_APM_ENTRIES_MAX 63 + + /** * Holds the options for the image generation. */ @@ -598,6 +603,7 @@ struct ecma119_image /* * HFS+ related information + * (by Vladimir Serbinenko, see libisofs/hfsplus.c) */ HFSPlusNode *hfsp_leafs; struct hfsplus_btree_level *hfsp_levels; @@ -776,7 +782,17 @@ struct ecma119_image char ascii_disc_label[ISO_DISC_LABEL_SIZE]; + /* See IsoImage and libisofs.h */ IsoNode *hfsplus_blessed[ISO_HFSPLUS_BLESS_MAX]; + + /* Apple Partition Map description. To be composed during IsoImageWriter + method ->compute_data_blocks() by calling iso_register_apm_entry(). + */ + struct iso_apm_partition_request *apm_req[ISO_APM_ENTRIES_MAX]; + int apm_req_count; + /* 512 by default. May be changed to 2048 before writer thread starts. */ + int apm_block_size; + }; #define BP(a,b) [(b) - (a) + 1] diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index bd50cf7..cbf56fe 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -7154,6 +7154,9 @@ int iso_image_hfsplus_bless(IsoImage *img, enum IsoHfsplusBlessings blessing, (FAILURE, HIGH, -378) */ #define ISO_SECT_SCATTERED 0xE830FE82 +/** Too many Apple Partition Map entries requested (FAILURE, HIGH, -377)*/ +#define ISO_BOOT_TOO_MANY_APM 0xE830FE81 + /* Internal developer note: diff --git a/libisofs/messages.c b/libisofs/messages.c index f3e3dff..d2587eb 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -474,6 +474,8 @@ const char *iso_error_to_msg(int errcode) return "ACL text contains multiple entries of user::, group::, other::"; case ISO_SECT_SCATTERED: return "File sections do not form consecutive array of blocks"; + case ISO_BOOT_TOO_MANY_APM: + return "Too many Apple Partition Map entries requested"; default: return "Unknown error"; } diff --git a/libisofs/system_area.c b/libisofs/system_area.c index 89d5d9a..858ee88 100644 --- a/libisofs/system_area.c +++ b/libisofs/system_area.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2008 Vreixo Formoso - * Copyright (c) 2010 - 2011 Thomas Schmitt + * Copyright (c) 2010 - 2012 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 @@ -688,6 +688,189 @@ static int make_sun_disk_label(Ecma119Image *t, uint8_t *buf, int flag) } +/* Convenience frontend for iso_register_apm_entry(). + name and type are 0-terminated strings. +*/ +int iso_quick_apm_entry(Ecma119Image *t, + uint32_t start_block, uint32_t block_count, char *name, char *type) +{ + int ret; + struct iso_apm_partition_request *entry; + + entry = calloc(1, sizeof(struct iso_apm_partition_request)); + if (entry == NULL) + return ISO_OUT_OF_MEM; + entry->start_block = start_block; + entry->block_count = block_count; + strncpy((char *) entry->name, name, 32); + strncpy((char *) entry->type, type, 32); + ret = iso_register_apm_entry(t, entry, 0); + free(entry); + return ret; +} + + +/** + * Compare the start_sectors of two iso_apm_partition_request + */ +static +int cmp_apm_partition_request(const void *f1, const void *f2) +{ + struct iso_apm_partition_request *r1, *r2; + + r1 = *((struct iso_apm_partition_request **) f1); + r2 = *((struct iso_apm_partition_request **) f2); + if (r1->start_block < r2->start_block) + return -1; + if (r1->start_block > r2->start_block) + return 1; + return 0; +} + +static int iso_write_apm_entry(Ecma119Image *t, int apm_block_size, + struct iso_apm_partition_request *req, + uint8_t *buf, int map_entries, int flag) +{ + uint8_t *wpt; + int block_fac; + + block_fac = apm_block_size / 512; + + memset(buf, apm_block_size, 0); + wpt = buf; + + /* Signature */ + wpt[0] = 'P'; wpt[1] = 'M'; + wpt+= 2; + /* reserved */ + wpt += 2; + /* Number of partition entries */ + iso_msb(wpt, (uint32_t) map_entries, 4); + wpt += 4; + /* Physical block start of partition */ + iso_msb(wpt, req->start_block * block_fac, 4); + wpt += 4; + /* Physical block count of partition */ + iso_msb(wpt, req->block_count * block_fac, 4); + wpt += 4; + /* Partition name */ + memcpy(wpt, req->name, 32); + wpt += 32; + /* Type string */ + memcpy(wpt, req->type, 32); + wpt += 32; + /* Logical block start */ + iso_msb(wpt, 0, 4); + wpt += 4; + /* Logical block count */ + iso_msb(wpt, req->block_count * block_fac, 4); + wpt += 4; + /* Status flags : bit0= entry is valid , bit1= entry is allocated */ + iso_msb(wpt, 3, 4); + wpt += 4; + + /* boot_block , boot_bytes , processor , reserved : are all 0 */ + + return ISO_SUCCESS; +} + +static int iso_write_apm(Ecma119Image *t, uint32_t img_blocks, uint8_t *buf) +{ + int i, ret, gap_counter = 0, up_to; + uint32_t part_end, goal; + char gap_name[33]; + +#ifdef NIX + /* Disabled */ + + /* <<< ts B20526 : Dummy mock-up */ + if (t->apm_req_count <= 0) { + /* + ret = iso_quick_apm_entry(t, 16, 20, "Test1_name_16_20", "Test1_type"); + */ + ret = iso_quick_apm_entry(t, 30, 20, "Test1_name_30_20", "Test1_type"); + if (ret < 0) + return ret; + ret = iso_quick_apm_entry(t, 100, 400, "Test2_name_100_400", + "Test2_type"); + if (ret < 0) + return ret; + } +#endif /* NIX */ + + if (t->apm_req_count <= 0) + return 2; + + /* Find out whether an entry with start_block == 1 is requested */ + for (i = 0; i < t->apm_req_count; i++) { + if (t->apm_req[i]->start_block <= 1) + break; + } + if (i >= t->apm_req_count) { + ret = iso_quick_apm_entry(t, 1, 0, "Apple", "Apple_partition_map"); + if (ret < 0) + return ret; + } + + /* Sort and fill gaps */ + qsort(t->apm_req, t->apm_req_count, + sizeof(struct iso_apm_partition_request *), cmp_apm_partition_request); + /* t->apm_req_count will grow during the loop */ + up_to = t->apm_req_count + 1; + for (i = 1; i < up_to; i++) { + if (i < up_to - 1) + goal = t->apm_req[i]->start_block; + else + goal = img_blocks; + if (i == 1) { + /* Description of APM itself */ + /* Actual APM size is not yet known. Protection begins at PVD */ + part_end = 16; + if (goal < 16 && goal> 1) + part_end = goal; + } else { + part_end = t->apm_req[i - 1]->start_block + + t->apm_req[i - 1]->block_count; + } + if (part_end > goal) { + + /* >>> Overlapping partition. ??? Warn ??? Bail out ??? */; + + } + if (part_end < goal) { + sprintf(gap_name, "Gap%d", gap_counter); + gap_counter++; + ret = iso_quick_apm_entry(t, part_end, + goal - part_end, + gap_name, "ISO9660_data"); + if (ret < 0) + return ret; + } + } + + /* Merge list of gap partitions with list of already sorted entries */ + qsort(t->apm_req, t->apm_req_count, + sizeof(struct iso_apm_partition_request *), cmp_apm_partition_request); + + /* If block size is larger than 512, then not all 63 entries will fit */ + if ((t->apm_req_count + 1) * t->apm_block_size > 32768) + return ISO_BOOT_TOO_MANY_APM; + + t->apm_req[0]->start_block = 1; + + /* >>> ts B20526 : ??? isohybrid has 16. Logical block count is 10. Why ?*/ + t->apm_req[0]->block_count = t->apm_req_count; + + for (i = 0; i < t->apm_req_count; i++) { + ret = iso_write_apm_entry(t, t->apm_block_size, t->apm_req[i], + buf + (i + 1) * t->apm_block_size, t->apm_req_count, 0); + if (ret < 0) + return ret; + } + return ISO_SUCCESS; +} + + int iso_write_system_area(Ecma119Image *t, uint8_t *buf) { int ret, int_img_blocks, sa_type, i, will_append = 0; @@ -735,6 +918,32 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) } return ISO_SUCCESS; } + + /* If APM entries were submitted to iso_register_apm_entry(), then they + get sprinkled over the system area before any other data get inserted. + Note that Block0 of the APM is not written but is in the responsibility + of the MBR template. Its block size MUST match t->apm_block_size. + + >>> ts B20526 + >>> ??? Shall i check t->system_area_data whether there are the first + >>> ??? four bytes of a Block0 and what block size they announce ? + >>> pro: That would prevent cruel mishaps + >>> con: t->system_area_data is totally opaque up to now. + >>> I cannot easily predict whether the first bytes get altered + >>> in the course of processing. + + >>> ts B20526 + >>> This does not care for eventual image enlargements in last minute. + >>> A sa_type, that does this, will have to adjust the last APM entry + >>> if exactness matters. + */ + ret = iso_write_apm(t, img_blocks, buf); + if (ret < 0) { + iso_msg_submit(t->image->id, ret, 0, + "Cannot set up Apple Partition Map"); + return ret; + } + 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, t->partition_secs_per_head, @@ -942,3 +1151,22 @@ ex:; LIBISO_FREE_MEM(msg); return ret; } + + +int iso_register_apm_entry(Ecma119Image *t, + struct iso_apm_partition_request *req, int flag) +{ + struct iso_apm_partition_request *entry; + + if (t->apm_req_count >= ISO_APM_ENTRIES_MAX) + return ISO_BOOT_TOO_MANY_APM; + entry = calloc(1, sizeof(struct iso_apm_partition_request)); + if (entry == NULL) + return ISO_OUT_OF_MEM; + + memcpy(entry, req, sizeof(struct iso_apm_partition_request)); + t->apm_req[t->apm_req_count] = entry; + t->apm_req_count++; + return ISO_SUCCESS; +} + diff --git a/libisofs/system_area.h b/libisofs/system_area.h index 444f8b7..524b9e5 100644 --- a/libisofs/system_area.h +++ b/libisofs/system_area.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2008 Vreixo Formoso + * Copyright (c) 2012 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 @@ -63,4 +64,43 @@ int iso_read_mipsel_elf(Ecma119Image *t, int flag); */ int iso_compute_append_partitions(Ecma119Image *t, int flag); + +/* The parameter struct for production of a single Apple Partition Map entry. + See also the partial APM description in doc/boot_sectors.txt. + The list of entries is stored in Ecma119Image.apm_req. + The size of a block can be chosen by setting Ecma119Image.apm_block_size. + If an entry has start_block <=1, then its block_count will be adjusted + to the final size of the partition map. + If no such entry is requested, then it will be prepended automatically + with name "Apple" and type "Apple_partition_map". +*/ +struct iso_apm_partition_request { + + /* Always given in blocks of 2 KiB. + Written to the ISO image according to Ecma119Image.apm_block_size. + */ + uint32_t start_block; + uint32_t block_count; + + /* All 32 bytes get copied to the system area. + Take care to pad up short strings by 0. + */ + uint8_t name[32]; + uint8_t type[32]; +}; + +/* Copies the content of req and registers it in t.apm_req[]. + I.e. after the call the submitted storage of req can be disposed or re-used. + Submit 0 as value flag. +*/ +int iso_register_apm_entry(Ecma119Image *t, + struct iso_apm_partition_request *req, int flag); + +/* Convenience frontend for iso_register_apm_entry(). + name and type are 0-terminated strings, which may get silently truncated. +*/ +int iso_quick_apm_entry(Ecma119Image *t, + uint32_t start_block, uint32_t block_count, char *name, char *type); + + #endif /* SYSTEM_AREA_H_ */