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