diff --git a/Makefile.am b/Makefile.am index 37d2e72..ef951d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,7 +31,9 @@ libisofs_libisofs_la_SOURCES = \ libisofs/hash.h \ libisofs/hash.c \ libisofs/file.h \ - libisofs/file.c + libisofs/file.c \ + libisofs/eltorito.h \ + libisofs/eltorito.c libinclude_HEADERS = \ libisofs/libisofs.h diff --git a/TODO b/TODO new file mode 100644 index 0000000..6b00299 --- /dev/null +++ b/TODO @@ -0,0 +1,29 @@ +FEATURES +======== + + El-Torito + Support for multiple images + HFS/HFS+ + CD reading + Multisession + UDF + ISO relaxed contraints + ISO 9660:1998 + + Support for special files (only dirs, reg. files and symlinks are supported). + +TESTS +===== + + For all + +IMPLEMENTATION +============== + + a way to return NULL sources meaning a failure!! + Error message queue + Public API for all things already implemented + Better charset support + default input charset to locale one, no always UTF-8 + + \ No newline at end of file diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 07154ce..7e2b5e0 100755 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -19,6 +19,7 @@ #include "file.h" #include "libisofs.h" #include "libburn/libburn.h" +#include "eltorito.h" /* burn-source compatible stuff */ static int @@ -78,6 +79,7 @@ static const write_fn writers[] = NULL, wr_system_area, wr_pri_vol_desc, + el_torito_wr_boot_vol_desc, joliet_wr_sup_vol_desc, wr_vol_desc_term, wr_l_path_table, @@ -86,6 +88,7 @@ static const write_fn writers[] = joliet_wr_m_path_table, wr_dir_records, joliet_wr_dir_records, + el_torito_wr_catalog, wr_files }; @@ -297,6 +300,9 @@ ecma119_target_new(struct iso_volset *volset, t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1 : 0; t->joliet = (flags & ECMA119_JOLIET) ? 1 : 0; + t->catalog = volset->volume[volnum]->bootcat; + t->eltorito = t->catalog ? 1 : 0; + t->root = ecma119_tree_create(t, iso_root); if (t->joliet) t->joliet_root = joliet_tree_create(t, iso_root); @@ -337,6 +343,8 @@ ecma119_target_new(struct iso_volset *volset, + 1 /* volume desc */ + 1; /* volume desc terminator */ + if (t->eltorito) + t->curblock += 1; /* boot record volume descriptor */ if (t->joliet) /* supplementary vol desc */ t->curblock += div_up (2048, t->block_size); @@ -363,7 +371,20 @@ ecma119_target_new(struct iso_volset *volset, /* reset curfile when we're finished */ t->curfile = 0; } + + /* 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); + } + calc_file_pos(t, t->root); + + if (t->eltorito) + el_torito_patch_image_files(t); if (t->rockridge) { susp_finalize(t, t->root); @@ -389,11 +410,19 @@ is_joliet_state(enum ecma119_write_state state) || state == ECMA119_WRITE_DIR_RECORDS_JOLIET; } +static int +is_eltorito_state(enum ecma119_write_state state) +{ + return state == ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC + || state == ECMA119_WRITE_ELTORITO_CATALOG; +} + static void next_state(struct ecma119_write_target *t) { t->state++; - while (!t->joliet && is_joliet_state(t->state)) + while ( (!t->joliet && is_joliet_state(t->state)) + ||(!t->eltorito && is_eltorito_state(t->state)) ) t->state++; printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock); diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index 800b919..2b893cd 100755 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -29,6 +29,7 @@ enum ecma119_write_state ECMA119_WRITE_SYSTEM_AREA, ECMA119_WRITE_PRI_VOL_DESC, + ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC, ECMA119_WRITE_SUP_VOL_DESC_JOLIET, ECMA119_WRITE_VOL_DESC_TERMINATOR, ECMA119_WRITE_L_PATH_TABLE, @@ -37,6 +38,7 @@ enum ecma119_write_state ECMA119_WRITE_M_PATH_TABLE_JOLIET, ECMA119_WRITE_DIR_RECORDS, ECMA119_WRITE_DIR_RECORDS_JOLIET, + ECMA119_WRITE_ELTORITO_CATALOG, ECMA119_WRITE_FILES, ECMA119_WRITE_DONE @@ -61,6 +63,9 @@ struct ecma119_write_target unsigned int rockridge:1; unsigned int joliet:1; unsigned int iso_level:2; + unsigned int eltorito:1; + + struct el_torito_boot_catalog *catalog; int replace_mode; /**< Replace ownership and modes of files * @@ -252,6 +257,17 @@ struct ecma119_sup_vol_desc uint8_t reserved2 BP(1396, 2048); }; +struct ecma119_boot_rec_vol_desc +{ + uint8_t vol_desc_type BP(1, 1); + uint8_t std_identifier BP(2, 6); + uint8_t vol_desc_version BP(7, 7); + uint8_t boot_sys_id BP(8, 39); + uint8_t boot_id BP(40, 71); + uint8_t boot_catalog BP(72, 75); + uint8_t unused BP(76, 2048); +}; + struct ecma119_vol_desc_terminator { uint8_t vol_desc_type BP(1, 1); diff --git a/libisofs/ecma119_tree.c b/libisofs/ecma119_tree.c index 88359f0..db9f926 100644 --- a/libisofs/ecma119_tree.c +++ b/libisofs/ecma119_tree.c @@ -9,6 +9,7 @@ #include "ecma119_tree.h" #include "tree.h" #include "util.h" +#include "eltorito.h" static size_t calc_dirent_len(struct ecma119_tree_node *n) { @@ -200,6 +201,33 @@ create_symlink(struct ecma119_write_target *t, return ret; } +static struct ecma119_tree_node* +create_boot_catalog(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node_boot_catalog *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; + + file = iso->catalog->file; + file->ino = ++t->ino; + + ret->attrib.st_nlink = file->nlink; + ret->attrib.st_ino = file->ino; + ret->info.file = file; + return ret; +} + static struct ecma119_tree_node* create_tree(struct ecma119_write_target *t, struct ecma119_tree_node *parent, @@ -212,18 +240,18 @@ create_tree(struct ecma119_write_target *t, if ( iso->hide_flags & LIBISO_HIDE_ON_RR ) return NULL; - switch (iso->attrib.st_mode & S_IFMT) { - case S_IFREG: + switch ( iso->type ) { + case LIBISO_NODE_FILE: ret = create_file(t, parent, (struct iso_tree_node_file*)iso); break; - case S_IFLNK: + case LIBISO_NODE_SYMLINK: if ( !t->rockridge ) printf("Can't add symlinks to a non ISO tree. Skipping %s \n", iso->name); else ret = create_symlink(t, parent, (struct iso_tree_node_symlink*)iso); break; - case S_IFDIR: + case LIBISO_NODE_DIR: { size_t i; struct iso_tree_node_dir *dir = (struct iso_tree_node_dir*)iso; @@ -236,6 +264,10 @@ 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); + break; default: /* should never happen */ assert( 0 ); diff --git a/libisofs/ecma119_tree.h b/libisofs/ecma119_tree.h index a140924..e215acd 100644 --- a/libisofs/ecma119_tree.h +++ b/libisofs/ecma119_tree.h @@ -17,7 +17,7 @@ struct ecma119_write_target; struct iso_tree_node; -enum { +enum ecma119_node_type { ECMA119_FILE, ECMA119_SYMLINK, ECMA119_DIR, @@ -58,14 +58,13 @@ struct ecma119_tree_node * not including SU. */ struct ecma119_tree_node *parent; - /*struct iso_tree_node *iso_self;*/ struct ecma119_write_target *target; struct stat attrib; struct susp_info susp; - int type; /**< file, symlink, directory or placeholder */ + enum ecma119_node_type type; /**< file, symlink, directory or placeholder */ union { struct iso_file *file; char *dest; diff --git a/libisofs/eltorito.c b/libisofs/eltorito.c new file mode 100644 index 0000000..f066db2 --- /dev/null +++ b/libisofs/eltorito.c @@ -0,0 +1,454 @@ +#include "libisofs.h" + +#include "eltorito.h" +#include "volume.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#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 + */ +struct boot_info_table { + uint8_t bi_pvd BP(1, 4); /* LBA of primary volume descriptor */ + uint8_t bi_file BP(5, 8); /* LBA of boot file */ + uint8_t bi_length BP(9, 12); /* Length of boot file */ + uint8_t bi_csum BP(13, 16); /* Checksum of boot file */ + uint8_t bi_reserved BP(17, 56); /* Reserved */ +}; + +struct partition_desc { + uint8_t boot_ind; + uint8_t begin_chs[3]; + uint8_t type; + uint8_t end_chs[3]; + uint8_t start[4]; + uint8_t size[4]; +}; + +struct hard_disc_mbr { + uint8_t code_area[440]; + uint8_t opt_disk_sg[4]; + uint8_t pad[2]; + struct partition_desc partition[4]; + uint8_t sign1; + uint8_t sign2; +}; + +static struct el_torito_boot_image * +create_image(struct iso_tree_node *image, + enum eltorito_boot_media_type type) +{ + 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; + + switch (type) { + case ELTORITO_FLOPPY_EMUL: + switch (image->attrib.st_size) { + case 1200 * 1024: + boot_media_type = 1; /* 1.2 meg diskette */ + break; + case 1440 * 1024: + boot_media_type = 2; /* 1.44 meg diskette */ + break; + case 2880 * 1024: + 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); + libisofs_errno = ELTORITO_WRONG_IMAGE_SIZE; + return NULL; + break; + } + /* it seems that for floppy emulation we need to load + * a single sector (512b) */ + load_sectors = 1; + break; + case ELTORITO_HARD_DISC_EMUL: + { + size_t i; + int fd; + struct hard_disc_mbr mbr; + int used_partition; + + /* read the MBR on disc and get the type of the partition */ + fd = open(((struct iso_tree_node_file*)image)->path, O_RDONLY); + if ( fd == -1 ) { + fprintf(stderr, "Can't open image file\n"); + return NULL; + } + if ( read(fd, &mbr, sizeof(mbr)) ) { + fprintf(stderr, "Can't read MBR from image file\n"); + close(fd); + return NULL; + } + close(fd); + + /* check valid MBR signature */ + if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) { + fprintf(stderr, "Invalid MBR. Wrong signature.\n"); + return NULL; + } + + /* ensure single partition */ + used_partition = -1; + for (i = 0; i < 4; ++i) { + if (mbr.partition[i].type != 0) { + /* it's an used partition */ + if (used_partition != -1) { + fprintf(stderr, "Invalid MBR. At least 2 paritions: %d and " + "%d, are being used\n", used_partition, i); + return NULL; + } else + used_partition = i; + } + } + partition_type = mbr.partition[used_partition].type; + } + boot_media_type = 4; + + /* only load the MBR */ + load_sectors = 1; + break; + case ELTORITO_NO_EMUL: + boot_media_type = 0; + break; + } + + 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* +create_boot_catalog_node(struct iso_tree_node_dir *parent, + const char *name) +{ + struct iso_tree_node_boot_catalog *boot; + + assert( parent && name ); + + boot = calloc(1, sizeof(struct iso_tree_node_boot_catalog)); + 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); + 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 *boot_image; + struct iso_tree_node_boot_catalog *boot_node; + struct el_torito_boot_catalog *catalog; + + assert( volume && !volume->bootcat); + assert( image && ISO_ISREG(image) && dir && name); + + boot_image = create_image(image, type); + if ( !boot_image ) + return NULL; + + /* 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; + + /* add catalog file */ + boot_node = create_boot_catalog_node(dir, name); + boot_node->catalog = catalog; + + volume->bootcat = catalog; + + return boot_image; +} + +void +el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment) +{ + if (bootimg->type != ELTORITO_NO_EMUL) + return; + bootimg->load_seg = segment; +} + +void +el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors) +{ + if (bootimg->type != ELTORITO_NO_EMUL) + return; + bootimg->load_size = sectors; +} + +void +el_torito_set_no_bootable(struct el_torito_boot_image *bootimg) +{ + bootimg->bootable = 0; +} + +void +el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg) +{ + bootimg->patch_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); + 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 + */ +static void +write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + struct el_torito_boot_catalog *cat = t->catalog; + struct ecma119_boot_rec_vol_desc *vol = + (struct ecma119_boot_rec_vol_desc*)buf; + + assert(cat); + + vol->vol_desc_type[0] = 0; + 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); +} + +static void +write_validation_entry(struct ecma119_write_target *t, uint8_t *buf) +{ + size_t i; + int checksum; + + struct el_torito_validation_entry *ve = + (struct el_torito_validation_entry*)buf; + ve->header_id[0] = 1; + ve->platform_id[0] = 0; /* 0: 80x86, 1: PowerPC, 2: Mac */ + ve->key_byte1[0] = 0x55; + ve->key_byte2[0] = 0xAA; + + /* calculate the checksum, to ensure sum of all words is 0 */ + checksum = 0; + for (i = 0; i < sizeof(struct el_torito_validation_entry); i += 2) { + checksum -= buf[i]; + checksum -= (buf[i] << 8); + } + iso_lsb(ve->checksum, checksum, 2); +} + +static void +patch_boot_file(struct el_torito_boot_image *img) +{ + struct boot_info_table info; + int fd; + uint32_t checksum; + ssize_t len; + uint8_t buf[4]; + + memset(&info, 0, sizeof(info)); + + /* open image */ + fd = open(img->image->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->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); + + //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); + } + 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->path); + close(fd); + return; + } + + /* 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); +} + +void +el_torito_patch_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 el_torito_boot_image *img = cat->entries[i]; + if ( img->patch_isolinux ) + patch_boot_file(img); + } +} + +/** + * Write one section entry. + * Currently this is used for both default and other entries since we + * put selection criteria no 0 (no sel. criteria) + */ +static void +write_section_entry(uint8_t *buf, struct el_torito_boot_image *img) +{ + struct el_torito_section_entry *se = + (struct el_torito_section_entry*)buf; + + 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 + iso_lsb(se->sec_count, img->load_size, 2); + iso_lsb(se->block, img->file->block, 4); +} + +/** + * Write El-Torito Boot Catalog + */ +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); + + write_validation_entry(t, buf); + + /* write default entry */ + write_section_entry(buf + 32, cat->entries[0]); + + //TODO write all images +} + +void +el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + assert(t->catalog); + ecma119_start_chunking(t, + write_boot_vol_desc, + 2048, + buf); +} + +void +el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf) +{ + assert(t->catalog); + ecma119_start_chunking(t, + write_catalog, + 2048, + buf); +} diff --git a/libisofs/eltorito.h b/libisofs/eltorito.h new file mode 100644 index 0000000..2e27fac --- /dev/null +++ b/libisofs/eltorito.h @@ -0,0 +1,60 @@ +#ifndef ELTORITO_H_ +#define ELTORITO_H_ + +#include "tree.h" +#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 el_torito_boot_image { + unsigned char bootable; /**< If the entry is bootable. */ + unsigned char patch_isolinux; /**< 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); +*/ + +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); + +void +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); + +#endif /*ELTORITO_H_*/ diff --git a/libisofs/joliet.c b/libisofs/joliet.c index 10b4347..0266f8d 100644 --- a/libisofs/joliet.c +++ b/libisofs/joliet.c @@ -7,6 +7,7 @@ #include "tree.h" #include "util.h" #include "volume.h" +#include "eltorito.h" #include #include @@ -28,7 +29,7 @@ create_node(struct ecma119_write_target *t, struct iso_tree_node_dir *dir = (struct iso_tree_node_dir *) iso; ret->info.dir.children = calloc(sizeof(void*), dir->nchildren); ret->type = JOLIET_DIR; - } else { + } else if (ISO_ISREG(iso)) { /* it's a file */ struct iso_tree_node_file *iso_f = (struct iso_tree_node_file *) iso; struct iso_file *file; @@ -39,6 +40,14 @@ create_node(struct ecma119_write_target *t, } ret->info.file = file; ret->type = JOLIET_FILE; + } else { + /* 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; } return ret; @@ -55,11 +64,12 @@ create_tree(struct ecma119_write_target *t, if ( iso->hide_flags & LIBISO_HIDE_ON_JOLIET ) return NULL; - switch (iso->attrib.st_mode & S_IFMT) { - case S_IFREG: + switch (iso->type) { + case LIBISO_NODE_FILE: + case LIBISO_NODE_BOOTCATALOG: root = create_node(t, parent, iso); break; - case S_IFDIR: + case LIBISO_NODE_DIR: { size_t i; struct joliet_tree_node *node; @@ -320,7 +330,6 @@ write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf) vol->vol_desc_type[0] = 2; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; - memcpy(vol->system_id, "SYSID", 5); if (vol_id) memcpy(vol->volume_id, vol_id, vol_id_len); memcpy(vol->esc_sequences, "%/E", 3); diff --git a/libisofs/joliet.h b/libisofs/joliet.h index a54866e..07f9a3e 100644 --- a/libisofs/joliet.h +++ b/libisofs/joliet.h @@ -17,7 +17,7 @@ struct ecma119_write_target; struct iso_tree_node; -enum { +enum joliet_node_type { JOLIET_FILE, JOLIET_DIR }; @@ -37,7 +37,7 @@ struct joliet_tree_node struct joliet_tree_node *parent; struct ecma119_write_target *target; - int type; + enum joliet_node_type type; union { struct iso_file *file; struct joliet_dir_info dir; diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 8d62366..5bfcae7 100755 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -35,6 +35,12 @@ struct iso_volset; */ struct iso_tree_node; +/** + * El-Torito boot image + * \see eltorito.h + */ +struct el_torito_boot_image; + /** * A directory in the filesystem tree. * The first member of this is an iso_tree_node. @@ -52,6 +58,12 @@ enum hide_node_flag { LIBISO_HIDE_ON_JOLIET = 1 << 1 }; +enum eltorito_boot_media_type { + ELTORITO_FLOPPY_EMUL, + ELTORITO_HARD_DISC_EMUL, + ELTORITO_NO_EMUL +}; + /** * This will hold the error code for some functions, if them fail. */ @@ -65,6 +77,8 @@ int libisofs_errno; #define NO_READ_ACCESS 2 /* unexpected file type, eg., passing a dir instead of a regular file */ #define UNEXPECTED_FILE_TYPE 3 +/* invalid boot image size */ +#define ELTORITO_WRONG_IMAGE_SIZE 4 /** * Controls the bahavior of iso_tree_radd_dir function @@ -157,6 +171,71 @@ void iso_volume_set_abstract_file_id(struct iso_volume *volume, void iso_volume_set_biblio_file_id(struct iso_volume *volume, const char *biblio_file_id); +/** + * Create a bootable volume by adding a El-Torito boot 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: + * - Floppy emulation: Boot image files must be exactly + * 1200 kB, 1440 kB or 2880 kB. + * - Hard disc emulation: The image must begin with a master + * boot record with a single image. + * - No emulation. You should specify load segment and load size + * of image. + * \param dir The directory node where the boot catalog will be located + * in image. Usually both boot catalog and boot image will be + * located in the same dir, maybe /boot. + * \param name The name of the boot catalog. + * + * \return The default El-Torito bootable image. If specified image file + * seems to be not correct, this returns NULL and libisofs_errno + * is set propertly. + * + * \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 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); + +/** + * Sets the load segment for the initial boot image. This is only for + * no emulation boot images, and is a NOP for other image types. + */ +void +el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment); + +/** + * Sets the number of sectors (512b) to be load at load segment during + * the initial boot procedure. This is only for + * no emulation boot images, and is a NOP for other image types. + */ +void +el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors); + +/** + * Marks the specified boot image as not bootable + */ +void +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. + * This is needed for isolinux boot images. + */ +void +el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg); + /** * Locate a node by its path on disc. * @@ -165,8 +244,10 @@ void iso_volume_set_biblio_file_id(struct iso_volume *volume, * * \return The node found or NULL. * + * TODO we need a way to allow developers know which kind of node is. + * Think about this when designing the read api */ -//struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path); +struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path); /** * Add a file or a directory (recursively) to a volume by specifying its path on the volume. @@ -284,10 +365,9 @@ struct iso_tree_node *iso_tree_add_node(struct iso_tree_node_dir *parent, * \pre \p parent is non-NULL. * \pre \p path is non-NULL and is a valid path to a directory on the local * filesystem. - * \return a pointer to the newly created directory. */ -struct iso_tree_node_dir *iso_tree_radd_dir(struct iso_tree_node_dir *parent, - const char *path, struct iso_tree_radd_dir_behavior *behavior); +void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, + struct iso_tree_radd_dir_behavior *behavior); /** * Set the name of a tree node (using the current locale). @@ -353,6 +433,16 @@ void iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w); */ void iso_tree_print(const struct iso_tree_node *root, int spaces); + +/** + * Holds the options for the image generation. + */ +//struct ecma119_source_opts { +// int volnum; /**< The volume in the set which you want to write (usually 0) */ +// int level; /**< ISO level to write at. */ +// int flags; /**< Which extensions to support. */ +//}; + /** Create a burn_source which can be used as a data source for a track * * The volume set used to create the libburn_source can _not_ be modified diff --git a/libisofs/tree.c b/libisofs/tree.c index dfab662..abd69fb 100755 --- a/libisofs/tree.c +++ b/libisofs/tree.c @@ -32,9 +32,9 @@ set_default_stat(struct stat *s) s->st_atime = s->st_mtime = s->st_ctime = now; } -static void -append_node(struct iso_tree_node_dir *parent, - struct iso_tree_node *child) +void +iso_tree_add_child(struct iso_tree_node_dir *parent, + struct iso_tree_node *child) { assert( parent && child); @@ -54,6 +54,7 @@ iso_tree_new_root() set_default_stat(&root->node.attrib); root->node.attrib.st_mode = S_IFDIR | 0777; + root->node.type = LIBISO_NODE_DIR; return root; } @@ -86,13 +87,14 @@ iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path) /* fill fields */ f->node.attrib = st; f->path = strdup(path); + f->node.type = LIBISO_NODE_FILE; p = strdup(path); /* because basename() might modify its arg */ f->node.name = strdup( basename(p) ); free(p); /* add to parent (this also sets f->node->parent) */ - append_node(parent, (struct iso_tree_node*) f); + iso_tree_add_child(parent, (struct iso_tree_node*) f); return (struct iso_tree_node*) f; } @@ -109,12 +111,13 @@ iso_tree_add_symlink(struct iso_tree_node_dir *parent, /* fill fields */ set_default_stat(&link->node.attrib); - link->node.attrib.st_mode |= S_IFLNK; + link->node.attrib.st_mode |= S_IFLNK;//TODO Not needed link->node.name = strdup(name); + link->node.type = LIBISO_NODE_SYMLINK; link->dest = strdup(dest); /* add to parent (this also sets link->node->parent) */ - append_node(parent, (struct iso_tree_node*) link); + iso_tree_add_child(parent, (struct iso_tree_node*) link); return (struct iso_tree_node*) link; } @@ -130,9 +133,10 @@ iso_tree_add_dir(struct iso_tree_node_dir *parent, dir = calloc(1, sizeof(struct iso_tree_node_dir)); dir->node.attrib = parent->node.attrib; + dir->node.type = LIBISO_NODE_DIR; dir->node.name = strdup(name); - append_node(parent, (struct iso_tree_node*) dir); + iso_tree_add_child(parent, (struct iso_tree_node*) dir); return dir; } @@ -276,7 +280,7 @@ iso_tree_free(struct iso_tree_node *root) free(root); } -static struct iso_tree_node* +static void iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path, struct iso_tree_radd_dir_behavior *behavior, struct iso_hash_table *excludes) @@ -285,20 +289,18 @@ iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path, DIR *dir; struct dirent *ent; - new = iso_tree_add_node(parent, path); - if (!new || !ISO_ISDIR(new)) { - return new; - } - dir = opendir(path); if (!dir) { warn("couldn't open directory %s: %s\n", path, strerror(errno)); - return new; + return; } while ((ent = readdir(dir))) { char child[strlen(ent->d_name) + strlen(path) + 2]; + if (behavior->stop_on_error & behavior->error) + break; + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; @@ -310,20 +312,23 @@ iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path, if (iso_exclude_lookup(excludes, child)) continue; - if ( !iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child, - behavior, excludes) ) { - /* error */ - behavior->error = 1; - if (behavior->stop_on_error) - break; + new = iso_tree_add_node(parent, child); + if (!new || !ISO_ISDIR(new)) { + if (!new) + behavior->error = 1; + continue; } + + iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child, + behavior, excludes); + } closedir(dir); - return new; + return; } -struct iso_tree_node_dir* +void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, struct iso_tree_radd_dir_behavior *behavior) { @@ -344,8 +349,7 @@ iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, } /* recurse into dir */ - dir = (struct iso_tree_node_dir*) iso_tree_radd_dir_aux( - parent, path, behavior, &table); + iso_tree_radd_dir_aux(parent, path, behavior, &table); /* clear hashtable */ iso_exclude_empty(&table); @@ -382,7 +386,7 @@ iso_tree_print_verbose(const struct iso_tree_node *root, int spaces) { - (S_ISDIR(root->attrib.st_mode) ? dir : file) + (ISO_ISDIR(root) ? dir : file) (root, callback_data, spaces); if ( ISO_ISDIR(root) ) { diff --git a/libisofs/tree.h b/libisofs/tree.h index a40a220..3f89570 100755 --- a/libisofs/tree.h +++ b/libisofs/tree.h @@ -44,6 +44,13 @@ // /* };*/ //}; +enum iso_tree_node_type { + LIBISO_NODE_DIR, + LIBISO_NODE_FILE, + LIBISO_NODE_SYMLINK, + LIBISO_NODE_BOOTCATALOG +}; + /** * A node in the filesystem tree. */ @@ -56,6 +63,7 @@ struct iso_tree_node int hide_flags; /**< If the node is to be hidden in RR/ISO or * Joilet tree */ + enum iso_tree_node_type type; }; /** @@ -112,6 +120,12 @@ struct iso_tree_node_dir */ void iso_tree_free(struct iso_tree_node *root); +/** + * Adds a child to a directory + */ +void iso_tree_add_child(struct iso_tree_node_dir *parent, + struct iso_tree_node *child); + /** * A function that prints verbose information about a directory. * @@ -156,8 +170,8 @@ void iso_tree_print_verbose(const struct iso_tree_node *root, void *callback_data, int spaces); -#define ISO_ISDIR(n) S_ISDIR(n->attrib.st_mode) -#define ISO_ISREG(n) S_ISREG(n->attrib.st_mode) -#define ISO_ISLNK(n) S_ISLNK(n->attrib.st_mode) +#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) #endif /* LIBISO_TREE_H */ diff --git a/libisofs/volume.c b/libisofs/volume.c index a06e6ee..8f59628 100755 --- a/libisofs/volume.c +++ b/libisofs/volume.c @@ -9,6 +9,7 @@ #include "tree.h" #include "util.h" #include "volume.h" +#include "eltorito.h" struct iso_volset* iso_volset_new(struct iso_volume *vol, const char *id) @@ -80,7 +81,13 @@ iso_volume_free(struct iso_volume *volume) free(volume->volume_id); free(volume->publisher_id); free(volume->data_preparer_id); - + free(volume->system_id); + free(volume->application_id); + free(volume->copyright_file_id); + free(volume->abstract_file_id); + free(volume->biblio_file_id); + if (volume->bootcat) + el_torito_boot_catalog_free(volume->bootcat); free(volume); } } @@ -139,52 +146,59 @@ void iso_volume_set_biblio_file_id(struct iso_volume *volume, volume->biblio_file_id = strdup(biblio_file_id); } -//struct iso_tree_node * -//iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path) -//{ -// struct iso_tree_node *node; -// char *ptr, *brk_info, *component; -// -// /* get the first child at the root of the volume -// * that is "/" */ -// node=iso_volume_get_root(volume); -// if (!strcmp (path, "/")) -// return node; -// -// if (!node->nchildren) -// return NULL; -// -// /* the name of the nodes is in wide characters so first convert path -// * into wide characters. */ -// ptr = strdup(path); -// -// /* get the first component of the path */ -// component=strtok_r(ptr, "/", &brk_info); -// while (component) { -// size_t max; -// size_t i; -// -// /* search among all the children of this directory if this path component exists */ -// max=node->nchildren; -// for (i=0; i < max; i++) { -// if (!strcmp(component, node->children[i]->name)) { -// node=node->children[i]; -// break; -// } -// } -// -// /* see if a node could be found */ -// if (i==max) { -// node=NULL; -// break; -// } -// -// component=strtok_r(NULL, "/", &brk_info); -// } -// -// free(ptr); -// return node; -//} +struct iso_tree_node * +iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path) +{ + struct iso_tree_node *node; + struct iso_tree_node_dir *dir; + char *ptr, *brk_info, *component; + + /* get the first child at the root of the volume + * that is "/" */ + dir = iso_volume_get_root(volume); + node = (struct iso_tree_node *)dir; + if (!strcmp(path, "/")) + return node; + + if (!dir->nchildren) + return NULL; + + ptr = strdup(path); + + /* get the first component of the path */ + component=strtok_r(ptr, "/", &brk_info); + while (component) { + size_t max; + size_t i; + + if ( !ISO_ISDIR(node) ) { + node=NULL; + break; + } + dir = (struct iso_tree_node_dir *)node; + + /* search among all the children of this directory if this path component exists */ + max=dir->nchildren; + for (i=0; i < max; i++) { + if (!strcmp(component, dir->children[i]->name)) { + node=dir->children[i]; + break; + } + } + + /* see if a node could be found */ + if (i==max) { + node=NULL; + break; + } + + component=strtok_r(NULL, "/", &brk_info); + } + + free(ptr); + return node; +} + // //struct iso_tree_node * //iso_tree_volume_add_path(struct iso_volume *volume, diff --git a/libisofs/volume.h b/libisofs/volume.h index 64f835b..7603c0d 100755 --- a/libisofs/volume.h +++ b/libisofs/volume.h @@ -30,6 +30,8 @@ struct iso_volume char *copyright_file_id; char *abstract_file_id; char *biblio_file_id; + + struct el_torito_boot_catalog *bootcat; /**< El-Torito boot catalog */ }; /** diff --git a/test/iso.c b/test/iso.c index 34018d4..ef83dda 100644 --- a/test/iso.c +++ b/test/iso.c @@ -20,7 +20,7 @@ #define SECSIZE 2048 -const char * const optstring = "JRL:h"; +const char * const optstring = "JRL:b:h"; extern char *optarg; extern int optind; @@ -36,6 +36,7 @@ void help() " -J Add Joliet support\n" " -R Add Rock Ridge support\n" " -L Set the ISO level (1 or 2)\n" +" -b file Specifies a boot image to add to image\n" " -h Print this message\n" ); } @@ -51,6 +52,7 @@ int main(int argc, char **argv) int c; struct iso_tree_radd_dir_behavior behav = {0,0,0}; int level=1, flags=0; + char *boot_img = NULL; while ((c = getopt(argc, argv, optstring)) != -1) { switch(c) { @@ -68,6 +70,9 @@ int main(int argc, char **argv) case 'L': level = atoi(optarg); break; + case 'b': + boot_img = optarg; + break; case '?': usage(); exit(1); @@ -96,9 +101,25 @@ int main(int argc, char **argv) err(1, "error opening input directory"); } volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root ); + + if ( boot_img ) { + /* adds El-Torito boot info. Tunned for isolinux */ + struct iso_tree_node_dir *boot = (struct iso_tree_node_dir *) + iso_tree_volume_path_to_node(volume, "isolinux"); + struct iso_tree_node *img = iso_tree_volume_path_to_node(volume, boot_img); + if (!img) { + err(1, "boot image patch is not valid"); + } + struct el_torito_boot_image *bootimg = + iso_volume_create_boot_catalog(volume, img, ELTORITO_NO_EMUL, + boot, "boot.cat"); + el_torito_set_load_size(bootimg, 4); + el_torito_set_write_boot_info(bootimg); + } + volset = iso_volset_new( volume, "VOLSETID" ); - //some tests + /* some tests */ iso_volume_set_application_id(volume, "Libburnia"); iso_volume_set_copyright_file_id(volume, "LICENSE");