diff options
Diffstat (limited to 'genisoimage/eltorito.c')
-rw-r--r-- | genisoimage/eltorito.c | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/genisoimage/eltorito.c b/genisoimage/eltorito.c new file mode 100644 index 0000000..d52e17e --- /dev/null +++ b/genisoimage/eltorito.c @@ -0,0 +1,716 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)eltorito.c 1.33 05/02/27 joerg */ +/* + * Program eltorito.c - Handle El Torito specific extensions to iso9660. + * + * + * Written by Michael Fulbright <msf@redhat.com> (1996). + * + * Copyright 1996 RedHat Software, Incorporated + * Copyright (c) 1999-2004 J. Schilling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <mconfig.h> +#include "genisoimage.h" +#include <fctldefs.h> +#include <utypes.h> +#include <intcvt.h> +#include "match.h" +#include "diskmbr.h" +#include "bootinfo.h" +#include <schily.h> + +#undef MIN +#define MIN(a, b) (((a) < (b))? (a): (b)) + +static struct eltorito_validation_entry valid_desc; +static struct eltorito_boot_descriptor gboot_desc; +static struct disk_master_boot_record disk_mbr; +static unsigned int bcat_de_flags; + +void init_boot_catalog(const char *path); +void insert_boot_cat(void); +static void get_torito_desc(struct eltorito_boot_descriptor *boot_desc); +static void fill_boot_desc(struct eltorito_defaultboot_entry *boot_desc_entry, + struct eltorito_boot_entry_info *boot_entry); +void get_boot_entry(void); +void new_boot_entry(void); +static int tvd_write(FILE *outfile); + + +static char *bootcat_path; /* filename of boot catalog */ +/* + * Make sure any existing boot catalog is excluded + */ +void +init_boot_catalog(const char *path) +{ +#ifdef SORTING + struct eltorito_boot_entry_info * cbe; + + for (cbe = first_boot_entry; + cbe != NULL; + cbe = cbe->next) { + char *p; + + if (cbe->boot_image == NULL) + comerrno(EX_BAD, "Missing boot image name, use -eltorito-boot option.\n"); + p = (char *) e_malloc(strlen(cbe->boot_image) + strlen(path) + 2); + strcpy(p, path); + if (p[strlen(p) - 1] != '/') { + strcat(p, "/"); + } + strcat(p, cbe->boot_image); + add_sort_match(p, sort_matches(p, 1)); + free(p); + } +#endif + bootcat_path = (char *) e_malloc(strlen(boot_catalog) + strlen(path) + 2); + strcpy(bootcat_path, path); + if (bootcat_path[strlen(bootcat_path) - 1] != '/') { + strcat(bootcat_path, "/"); + } + strcat(bootcat_path, boot_catalog); + + /* + * we are going to create a virtual catalog file + * - so make sure any existing is excluded + */ + add_match(bootcat_path); + + /* flag the file as a memory file */ + bcat_de_flags = MEMORY_FILE; + + /* find out if we want to "hide" this file */ + if (i_matches(boot_catalog) || i_matches(bootcat_path)) + bcat_de_flags |= INHIBIT_ISO9660_ENTRY; + + if (j_matches(boot_catalog) || j_matches(bootcat_path)) + bcat_de_flags |= INHIBIT_JOLIET_ENTRY; + +}/* init_boot_catalog(... */ + +/* + * Create a boot catalog file in memory - genisoimage already uses this type of + * file for the TRANS.TBL files. Therefore the boot catalog is set up in + * similar way + */ +void +insert_boot_cat() +{ + struct directory_entry *de; + struct directory_entry *s_entry; + char *p1; + char *p2; + char *p3; + struct directory *this_dir; + struct directory *dir; + char *buffer; + + init_fstatbuf(); + + buffer = (char *) e_malloc(SECTOR_SIZE); + memset(buffer, 0, SECTOR_SIZE); + + /* + * try to find the directory that will contain the boot.cat file + * - not very neat, but I can't think of a better way + */ + p1 = strdup(boot_catalog); + + /* get dirname (p1) and basename (p2) of boot.cat */ + if ((p2 = strrchr(p1, '/')) != NULL) { + *p2 = '\0'; + p2++; + + /* find the dirname directory entry */ + de = search_tree_file(root, p1); + if (!de) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Uh oh, I cant find the boot catalog directory '%s'!\n", + p1); +#else + fprintf(stderr, + "Uh oh, I cant find the boot catalog directory '%s'!\n", + p1); + exit(1); +#endif + } + this_dir = 0; + + /* get the basename (p3) of the directory */ + if ((p3 = strrchr(p1, '/')) != NULL) + p3++; + else + p3 = p1; + + /* find the correct sub-directory entry */ + for (dir = de->filedir->subdir; dir; dir = dir->next) + if (!(strcmp(dir->de_name, p3))) + this_dir = dir; + + if (this_dir == 0) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Uh oh, I cant find the boot catalog directory '%s'!\n", + p3); +#else + fprintf(stderr, + "Uh oh, I cant find the boot catalog directory '%s'!\n", + p3); + exit(1); +#endif + } + } else { + /* boot.cat is in the root directory */ + this_dir = root; + p2 = p1; + } + + /* + * make a directory entry in memory (using the same set up as for table + * entries + */ + s_entry = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + memset(s_entry, 0, sizeof (struct directory_entry)); + s_entry->next = this_dir->contents; + this_dir->contents = s_entry; + +#ifdef SORTING + /* inherit any sort weight from parent directory */ + s_entry->sort = this_dir->sort; + s_entry->sort += 2; + + /* see if this entry should have a new weighting */ + if (do_sort) { + s_entry->sort = sort_matches(bootcat_path, s_entry->sort); + } +#endif /* SORTING */ + + s_entry->isorec.flags[0] = ISO_FILE; + s_entry->priority = 32768; + iso9660_date(s_entry->isorec.date, fstatbuf.st_mtime); + s_entry->inode = TABLE_INODE; + s_entry->dev = (dev_t) UNCACHED_DEVICE; + set_723(s_entry->isorec.volume_sequence_number, + volume_sequence_number); + set_733((char *) s_entry->isorec.size, SECTOR_SIZE); + s_entry->size = SECTOR_SIZE; + s_entry->filedir = this_dir; + s_entry->name = strdup(p2); + iso9660_file_length(p2, s_entry, 0); + + /* flag file as necessary */ + + /* + * If the current directory is hidden, then hide this entry + */ + if (this_dir->dir_flags & INHIBIT_ISO9660_ENTRY) + bcat_de_flags |= INHIBIT_ISO9660_ENTRY; + if (this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) + bcat_de_flags |= INHIBIT_JOLIET_ENTRY; + + s_entry->de_flags = bcat_de_flags; + + if ((use_XA || use_RockRidge) && + !(bcat_de_flags & INHIBIT_ISO9660_ENTRY)) { + fstatbuf.st_mode = 0444 | S_IFREG; + fstatbuf.st_nlink = 1; + generate_xa_rr_attributes("", + p2, s_entry, + &fstatbuf, &fstatbuf, 0); + } + /* + * memory files are stored at s_entry->table + * - but this is also used for each s_entry to generate + * TRANS.TBL entries. So if we are generating tables, + * store the TRANS.TBL data here for the moment + */ + if (generate_tables && !(bcat_de_flags & INHIBIT_ISO9660_ENTRY)) { + sprintf(buffer, "F\t%s\n", s_entry->name); + + /* copy the TRANS.TBL entry info and clear the buffer */ + s_entry->table = strdup(buffer); + memset(buffer, 0, SECTOR_SIZE); + + /* + * store the (empty) file data in the + * unused s_entry->whole_name element for the time being + * - this will be transferred to s_entry->table after any + * TRANS.TBL processing later + */ + s_entry->whole_name = buffer; + } else { + /* store the (empty) file data in the s_entry->table element */ + s_entry->table = buffer; + s_entry->whole_name = NULL; + } +} + +static void +get_torito_desc(struct eltorito_boot_descriptor *boot_desc) +{ + int checksum; + unsigned char *checksum_ptr; + struct directory_entry *de2; /* Boot catalog */ + int i; + int offset; + struct eltorito_defaultboot_entry boot_desc_record; + + memset(boot_desc, 0, sizeof (*boot_desc)); + boot_desc->type[0] = 0; + memcpy(boot_desc->id, ISO_STANDARD_ID, sizeof (ISO_STANDARD_ID)); + boot_desc->version[0] = 1; + + memcpy(boot_desc->system_id, EL_TORITO_ID, sizeof (EL_TORITO_ID)); + + /* + * search from root of iso fs to find boot catalog + * - we already know where the boot catalog is + * - we created it above - but lets search for it anyway + * - good sanity check! + */ + de2 = search_tree_file(root, boot_catalog); + if (!de2 || !(de2->de_flags & MEMORY_FILE)) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Uh oh, I cant find the boot catalog '%s'!\n", + boot_catalog); +#else + fprintf(stderr, "Uh oh, I cant find the boot catalog '%s'!\n", + boot_catalog); + exit(1); +#endif + } + set_731(boot_desc->bootcat_ptr, + (unsigned int) get_733(de2->isorec.extent)); + + /* + * we have the boot image, so write boot catalog information + * Next we write out the primary descriptor for the disc + */ + memset(&valid_desc, 0, sizeof (valid_desc)); + valid_desc.headerid[0] = 1; + valid_desc.arch[0] = EL_TORITO_ARCH_x86; + + /* + * we'll shove start of publisher id into id field, + * may get truncated but who really reads this stuff! + */ + if (publisher) + memcpy_max(valid_desc.id, publisher, + MIN(23, strlen(publisher))); + + valid_desc.key1[0] = (char) 0x55; + valid_desc.key2[0] = (char) 0xAA; + + /* compute the checksum */ + checksum = 0; + checksum_ptr = (unsigned char *) &valid_desc; + /* Set checksum to 0 before computing checksum */ + set_721(valid_desc.cksum, 0); + for (i = 0; i < (int)sizeof (valid_desc); i += 2) { + checksum += (unsigned int) checksum_ptr[i]; + checksum += ((unsigned int) checksum_ptr[i + 1]) * 256; + } + + /* now find out the real checksum */ + checksum = -checksum; + set_721(valid_desc.cksum, (unsigned int) checksum); + + /* now write it to the virtual boot catalog */ + memcpy(de2->table, &valid_desc, 32); + + for (current_boot_entry = first_boot_entry, offset = sizeof (valid_desc); + current_boot_entry != NULL; + current_boot_entry = current_boot_entry->next, + offset += sizeof (boot_desc_record)) { + + if (offset >= SECTOR_SIZE) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Too many El Torito boot entries\n"); +#else + fprintf(stderr, + "Too many El Torito boot entries\n"); + exit(1); +#endif + } + fill_boot_desc(&boot_desc_record, current_boot_entry); + memcpy(de2->table + offset, &boot_desc_record, + sizeof (boot_desc_record)); + } +}/* get_torito_desc(... */ + +static void +fill_boot_desc(struct eltorito_defaultboot_entry *boot_desc_entry, + struct eltorito_boot_entry_info *boot_entry) +{ + struct directory_entry *de; /* Boot file */ + int bootmbr; + int i; + int nsectors; + int geosec; + + if (!boot_desc_entry || !boot_entry) + return; + + /* now adjust boot catalog lets find boot image first */ + de = search_tree_file(root, boot_entry->boot_image); + if (!de) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Uh oh, I cant find the boot image '%s' !\n", + boot_entry->boot_image); +#else + fprintf(stderr, "Uh oh, I cant find the boot image '%s' !\n", + boot_entry->boot_image); + exit(1); +#endif + } + /* now make the initial/default entry for boot catalog */ + memset(boot_desc_entry, 0, sizeof (*boot_desc_entry)); + boot_desc_entry->boot_id[0] = (char) boot_entry->not_bootable ? + EL_TORITO_NOT_BOOTABLE : EL_TORITO_BOOTABLE; + + /* use default BIOS loadpnt */ + set_721(boot_desc_entry->loadseg, boot_entry->load_addr); + + /* + * figure out size of boot image in 512-byte sectors. + * However, round up to the nearest integral CD (2048-byte) sector. + * This is only used for no-emulation booting. + */ + nsectors = boot_entry->load_size ? boot_entry->load_size : + ISO_BLOCKS(de->size) * (SECTOR_SIZE/512); + + if (verbose > 0) { + fprintf(stderr, + "Size of boot image is %d sectors -> ", nsectors); + } + + if (boot_entry->hard_disk_boot) { + /* sanity test hard disk boot image */ + boot_desc_entry->boot_media[0] = EL_TORITO_MEDIA_HD; + if (verbose > 0) + fprintf(stderr, "Emulating a hard disk\n"); + + /* read MBR */ + bootmbr = open(de->whole_name, O_RDONLY | O_BINARY); + if (bootmbr == -1) { +#ifdef USE_LIBSCHILY + comerr("Error opening boot image '%s' for read.\n", + de->whole_name); +#else + fprintf(stderr, + "Error opening boot image '%s' for read.\n", + de->whole_name); + perror(""); + exit(1); +#endif + } + if (read(bootmbr, &disk_mbr, sizeof (disk_mbr)) != + sizeof (disk_mbr)) { +#ifdef USE_LIBSCHILY + comerr("Error reading MBR from boot image '%s'.\n", + de->whole_name); +#else + fprintf(stderr, + "Error reading MBR from boot image '%s'.\n", + de->whole_name); + exit(1); +#endif + } + close(bootmbr); + if (la_to_u_2_byte(disk_mbr.magic) != MBR_MAGIC) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, + "Warning: boot image '%s' MBR is not a boot sector.\n", + de->whole_name); +#else + fprintf(stderr, + "Warning: boot image '%s' MBR is not a boot sector.\n", + de->whole_name); +#endif + } + /* find partition type */ + boot_desc_entry->sys_type[0] = PARTITION_UNUSED; + for (i = 0; i < PARTITION_COUNT; ++i) { + int s_cyl_sec; + int e_cyl_sec; + + s_cyl_sec = + la_to_u_2_byte(disk_mbr.partition[i].s_cyl_sec); + e_cyl_sec = + la_to_u_2_byte(disk_mbr.partition[i].e_cyl_sec); + + if (disk_mbr.partition[i].type != PARTITION_UNUSED) { + if (boot_desc_entry->sys_type[0] != + PARTITION_UNUSED) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Boot image '%s' has multiple partitions.\n", + de->whole_name); +#else + fprintf(stderr, + "Boot image '%s' has multiple partitions.\n", + de->whole_name); + exit(1); +#endif + } + boot_desc_entry->sys_type[0] = + disk_mbr.partition[i].type; + + /* a few simple sanity warnings */ + if (!boot_entry->not_bootable && + disk_mbr.partition[i].status != + PARTITION_ACTIVE) { + fprintf(stderr, + "Warning: partition not marked active.\n"); + } + if (MBR_CYLINDER(s_cyl_sec) != 0 || + disk_mbr.partition[i].s_head != 1 || + MBR_SECTOR(s_cyl_sec != 1)) { + fprintf(stderr, + "Warning: partition does not start at 0/1/1.\n"); + } + geosec = (MBR_CYLINDER(e_cyl_sec) + 1) * + (disk_mbr.partition[i].e_head + 1) * + MBR_SECTOR(e_cyl_sec); + if (geosec != nsectors) { + fprintf(stderr, + "Warning: image size does not match geometry (%d)\n", + geosec); + } +#ifdef DEBUG_TORITO + fprintf(stderr, "Partition start %u/%u/%u\n", + MBR_CYLINDER(s_cyl_sec), + disk_mbr.partition[i].s_head, + MBR_SECTOR(s_cyl_sec)); + fprintf(stderr, "Partition end %u/%u/%u\n", + MBR_CYLINDER(e_cyl_sec), + disk_mbr.partition[i].e_head, + MBR_SECTOR(e_cyl_sec)); +#endif + } + } + if (boot_desc_entry->sys_type[0] == PARTITION_UNUSED) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Boot image '%s' has no partitions.\n", + de->whole_name); +#else + fprintf(stderr, + "Boot image '%s' has no partitions.\n", + de->whole_name); + exit(1); +#endif + } +#ifdef DEBUG_TORITO + fprintf(stderr, "Partition type %u\n", + boot_desc_entry->sys_type[0]); +#endif + /* load single boot sector, in this case the MBR */ + nsectors = 1; + + } else if (boot_entry->no_emul_boot) { + /* + * no emulation is a simple image boot of all the sectors + * in the boot image + */ + boot_desc_entry->boot_media[0] = EL_TORITO_MEDIA_NOEMUL; + if (verbose > 0) + fprintf(stderr, "No emulation\n"); + + } else { + /* choose size of emulated floppy based on boot image size */ + if (nsectors == 2880) { + boot_desc_entry->boot_media[0] = EL_TORITO_MEDIA_144FLOP; + if (verbose > 0) + fprintf(stderr, "Emulating a 1440 kB floppy\n"); + + } else if (nsectors == 5760) { + boot_desc_entry->boot_media[0] = EL_TORITO_MEDIA_288FLOP; + if (verbose > 0) + fprintf(stderr, "Emulating a 2880 kB floppy\n"); + + } else if (nsectors == 2400) { + boot_desc_entry->boot_media[0] = EL_TORITO_MEDIA_12FLOP; + if (verbose > 0) + fprintf(stderr, "Emulating a 1200 kB floppy\n"); + + } else { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Error - boot image '%s' has not an allowable size.\n", + de->whole_name); +#else + fprintf(stderr, + "Error - boot image '%s' has not an allowable size.\n", + de->whole_name); + exit(1); +#endif + } + + /* load single boot sector for floppies */ + nsectors = 1; + } + + /* fill in boot image details */ +#ifdef DEBUG_TORITO + fprintf(stderr, "Boot %u sectors\n", nsectors); + fprintf(stderr, "Extent of boot images is %d\n", + get_733(de->isorec.extent)); +#endif + set_721(boot_desc_entry->nsect, (unsigned int) nsectors); + set_731(boot_desc_entry->bootoff, + (unsigned int) get_733(de->isorec.extent)); + + + /* If the user has asked for it, patch the boot image */ + if (boot_entry->boot_info_table) { + int bootimage; + unsigned int bi_checksum; + unsigned int total_len; + static char csum_buffer[SECTOR_SIZE]; + int len; + struct genisoimage_boot_info bi_table; + bootimage = open(de->whole_name, O_RDWR | O_BINARY); + if (bootimage == -1) { +#ifdef USE_LIBSCHILY + comerr( + "Error opening boot image file '%s' for update.\n", + de->whole_name); +#else + fprintf(stderr, + "Error opening boot image file '%s' for update.\n", + de->whole_name); + perror(""); + exit(1); +#endif + } + /* Compute checksum of boot image, sans 64 bytes */ + total_len = 0; + bi_checksum = 0; + while ((len = read(bootimage, csum_buffer, SECTOR_SIZE)) > 0) { + if (total_len & 3) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Odd alignment at non-end-of-file in boot image '%s'.\n", + de->whole_name); +#else + fprintf(stderr, + "Odd alignment at non-end-of-file in boot image '%s'.\n", + de->whole_name); + exit(1); +#endif + } + if (total_len < 64) + memset(csum_buffer, 0, 64 - total_len); + if (len < SECTOR_SIZE) + memset(csum_buffer + len, 0, SECTOR_SIZE-len); + for (i = 0; i < SECTOR_SIZE; i += 4) + bi_checksum += get_731(&csum_buffer[i]); + total_len += len; + } + + if (total_len != de->size) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Boot image file '%s' changed underneath us!\n", + de->whole_name); +#else + fprintf(stderr, + "Boot image file '%s' changed underneath us!\n", + de->whole_name); + exit(1); +#endif + } + /* End of file, set position to byte 8 */ + lseek(bootimage, (off_t)8, SEEK_SET); + memset(&bi_table, 0, sizeof (bi_table)); + /* Is it always safe to assume PVD is at session_start+16? */ + set_731(bi_table.bi_pvd, session_start + 16); + set_731(bi_table.bi_file, de->starting_block); + set_731(bi_table.bi_length, de->size); + set_731(bi_table.bi_csum, bi_checksum); + + write(bootimage, &bi_table, sizeof (bi_table)); /* FIXME: check return value */ + close(bootimage); + } +}/* fill_boot_desc(... */ + +void +get_boot_entry() +{ + if (current_boot_entry) + return; + + current_boot_entry = (struct eltorito_boot_entry_info *) + e_malloc(sizeof (struct eltorito_boot_entry_info)); + memset(current_boot_entry, 0, sizeof (*current_boot_entry)); + + if (!first_boot_entry) { + first_boot_entry = current_boot_entry; + last_boot_entry = current_boot_entry; + } else { + last_boot_entry->next = current_boot_entry; + last_boot_entry = current_boot_entry; + } +} + +void +new_boot_entry() +{ + current_boot_entry = NULL; +} + +/* + * Function to write the EVD for the disc. + */ +static int +tvd_write(FILE *outfile) +{ + /* check the boot image is not NULL */ + if (!boot_image) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "No boot image specified.\n"); +#else + fprintf(stderr, "No boot image specified.\n"); + exit(1); +#endif + } + /* Next we write out the boot volume descriptor for the disc */ + get_torito_desc(&gboot_desc); + jtwrite(&gboot_desc, SECTOR_SIZE, 1, 0, FALSE); + xfwrite(&gboot_desc, SECTOR_SIZE, 1, outfile, 0, FALSE); + last_extent_written++; + return (0); +} + +struct output_fragment torito_desc = {NULL, oneblock_size, NULL, tvd_write, "Eltorito Volume Descriptor"}; |