Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

eltorito.c

/*
 * 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"};

Generated by  Doxygen 1.6.0   Back to index