Implemented support for eltorito on MS discs

master
Mario Danic 15 years ago
parent 0746d622e4
commit d60193e2d5
  1. 5
      TODO
  2. 101
      libisofs/ecma119.c
  3. 15
      libisofs/ecma119.h
  4. 171
      libisofs/ecma119_read.c
  5. 3
      libisofs/ecma119_read.h
  6. 31
      libisofs/ecma119_tree.c
  7. 7
      libisofs/ecma119_tree.h
  8. 479
      libisofs/eltorito.c
  9. 81
      libisofs/eltorito.h
  10. 36
      libisofs/joliet.c
  11. 7
      libisofs/joliet.h
  12. 18
      libisofs/libdax_msgs.h
  13. 89
      libisofs/libisofs.h
  14. 7
      libisofs/messages.c
  15. 25
      libisofs/messages.h
  16. 7
      libisofs/tree.c
  17. 15
      libisofs/tree.h
  18. 12
      test/iso.c
  19. 7
      test/iso_grow.c
  20. 23
      test/iso_read.c

@ -10,9 +10,12 @@ FEATURES
[ok] Joliet
Merge RR and Joliet trees
[ok] User options to customize reading
Read El-Torito info
[ok] Read El-Torito info
[ok] Multisession
[ok] DVD+RW image growing
El-Torito images from previous session
Add new el-torito image from prev. session file
Path for isolinux from prev. session
UDF
[ok] ISO relaxed contraints
ISO 9660:1998

@ -184,15 +184,9 @@ calc_dir_size(struct ecma119_write_target *t,
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
//struct iso_tree_node *iso = ch->iso_self;
if (ch->type == ECMA119_DIR) {
calc_dir_size(t, ch);
}
// else if (iso && iso->attrib.st_size
// && iso->loc.type == LIBISO_FILESYS
// && iso->loc.path) {
// t->filelist_len++;
// }
}
}
}
@ -231,12 +225,11 @@ cmp_file(const void *f1, const void *f2)
* Fill out the block field for each file and fill out t->filelist.
*/
static void
calc_file_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
calc_file_pos(struct ecma119_write_target *t)
{
size_t i;
assert(dir->type == ECMA119_DIR);
assert(t);
t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count);
@ -338,7 +331,17 @@ ecma119_target_new(struct iso_volset *volset,
t->catalog = volset->volume[opts->volnum]->bootcat;
t->eltorito = t->catalog ? 1 : 0;
t->write_eltorito = opts->copy_eltorito;
if (t->eltorito && (!t->ms_block || !t->catalog->proc) ) {
/*
* For new and modified images we always need to write el-torito.
* For ms images, if the boot catalog was newly added, we also need
* to write it!
*/
t->write_eltorito = 1;
}
/* create the trees */
t->root = ecma119_tree_create(t, iso_root);
if (t->joliet)
t->joliet_root = joliet_tree_create(t, iso_root);
@ -377,8 +380,8 @@ ecma119_target_new(struct iso_volset *volset,
t->curblock = t->ms_block /* nwa for ms, usually 0 */
+ 16 /* system area */
+ 1 /* volume desc */
+ 1; /* volume desc terminator */
+ 1 /* primary volume desc */
+ 1; /* volume desc terminator */
if (t->eltorito)
t->curblock += 1; /* boot record volume descriptor */
@ -411,17 +414,24 @@ ecma119_target_new(struct iso_volset *volset,
/* 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);
}
if (t->write_eltorito) {
/* add catalog block */
t->catblock = t->curblock;
t->curblock += div_up(2048, t->block_size);
calc_file_pos(t, t->root);
/* add img block */
t->imgblock = t->curblock;
t->curblock += div_up(t->catalog->image->node->node.attrib.st_size,
t->block_size);
} else {
assert(t->ms_block);
assert(t->catalog->proc);
t->catblock = t->catalog->node->loc.block;
t->imgblock = t->catalog->image->node->loc.block;
}
}
if (t->eltorito)
el_torito_patch_image_files(t);
calc_file_pos(t);
if (t->rockridge) {
susp_finalize(t, t->root);
@ -438,9 +448,6 @@ ecma119_target_new(struct iso_volset *volset,
*/
uint8_t *buf;
/* set the 32 block buffer to 0 */
memset(opts->overwrite, 0, 32 * t->block_size);
/* skip the first 16 blocks (system area) */
buf = opts->overwrite + 16 * t->block_size;
@ -494,16 +501,15 @@ is_eltorito_state(enum ecma119_write_state state)
static void
next_state(struct ecma119_write_target *t)
{
char msg[42];
t->state++;
while ( (!t->joliet && is_joliet_state(t->state))
||(!t->eltorito && is_eltorito_state(t->state)) )
||(!t->eltorito && is_eltorito_state(t->state))
||(!t->write_eltorito && t->state == ECMA119_WRITE_ELTORITO_CATALOG) )
t->state++;
{
char msg[42];
sprintf(msg, "Now in state %d, curblock=%d.", t->state, t->curblock);
iso_msg_debug(msg);
}
}
static void
@ -556,13 +562,14 @@ wr_files(struct ecma119_write_target *t, uint8_t *buf)
assert( !t->ms_block && t->src );
/**
* f->block block where file read begins
* f->block block where file write begins
* t->curblock block we're writting now
* f->real_ino initial block for file on source
*/
block = t->curblock - f->block;
if ( t->src->read_block(t->src, f->real_ino + block, buf) ) {
warn("problem reading from previous image");
iso_msg_sorry(LIBISO_CANT_READ_IMG,
"Problem reading file from previous image");
}
if ( f->size <= (off_t) (block+1) * t->block_size ) {
f_st->file++;
@ -581,6 +588,8 @@ wr_files(struct ecma119_write_target *t, uint8_t *buf)
f_st->data_len = f->size;
f_st->fd = fopen(path, "r");
if (!f_st->fd) {
//FIXME instead of exit, just print a msg error and
//skip file (what about reading from /dev/zero? instead)
err(1, "couldn't open %s for reading", path);
}
assert(t->curblock + t->ms_block == f->block);
@ -588,10 +597,15 @@ wr_files(struct ecma119_write_target *t, uint8_t *buf)
nread = fread(buf, 1, t->block_size, f_st->fd);
f_st->pos += t->block_size;
if (nread < 0)
warn("problem reading from %s", path);
else if (nread != t->block_size && f_st->pos < f_st->data_len)
warnx("incomplete read from %s", path);
if (nread < 0) {
char msg[PATH_MAX + 32];
sprintf(msg, "Problem reading from %s", path);
iso_msg_sorry(LIBISO_CANT_READ_FILE, msg);
} else if (nread != t->block_size && f_st->pos < f_st->data_len) {
char msg[PATH_MAX + 32];
sprintf(msg, "Incomplete read from %s", path);
iso_msg_sorry(LIBISO_CANT_READ_FILE, msg);
}
if (f_st->pos >= f_st->data_len) {
fclose(f_st->fd);
f_st->fd = 0;
@ -743,6 +757,16 @@ write_one_dir_record(struct ecma119_write_target *t,
} else if (node->type == ECMA119_FILE) {
len = node->info.file->size;
block = node->info.file->block;
} else if (node->type == ECMA119_BOOT) {
assert(t->eltorito);
if (node->info.boot_img) {
block = t->imgblock;
len = t->catalog->image->node->node.attrib.st_size;
} else {
/* we always assume 2048 as catalog len */
block = t->catblock;
len = 2048;
}
} else {
/* for nodes other than files and dirs, we set both len and block to 0 */
len = 0;
@ -872,11 +896,10 @@ static int
bs_read(struct burn_source *bs, unsigned char *buf, int size)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
if (size != t->block_size) {
warnx("you must read data in block-sized chunks (%d bytes)",
(int)t->block_size);
return 0;
} else if (t->curblock >= t->vol_space_size + t->ms_block) {
assert(size == t->block_size);
if (t->curblock >= t->vol_space_size) {
return 0;
}
if (t->state_data_valid)

@ -65,9 +65,22 @@ struct ecma119_write_target
unsigned int iso_level:2;
unsigned int eltorito:1;
int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */
unsigned int write_eltorito:1;
/**<
* In multisession discs, select whether to copy el-torito catalog
* and boot image. Copy is needed for isolinux images, that need to
* be patched. However, it can lead to problems when the image is
* not present in the iso filesystem, because we can't figure out
* its size. In those cases, we only copy 1 block of data.
* When modifying images, we always need to copy data. Thus, this is
* always 1 for both new and modified images.
*/
struct el_torito_boot_catalog *catalog;
uint32_t catblock; /**< location of the boot catalog in the new image */
uint32_t imgblock; /**< location of the boot image in the new image */
int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */
int replace_mode; /**< Replace ownership and modes of files
*

@ -28,12 +28,128 @@
#include "volume.h"
#include "tree.h"
#include "messages.h"
#include "eltorito.h"
#define BLOCK_SIZE 2048
static int
iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *parent,
uint32_t block);
static struct el_torito_boot_catalog *
read_el_torito_boot_catalog(struct iso_read_info *info, uint32_t block)
{
struct el_torito_validation_entry *ve;
struct el_torito_default_entry *entry;
struct el_torito_boot_catalog *catalog;
struct el_torito_boot_image *image;
unsigned char buffer[BLOCK_SIZE];
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return NULL;
}
ve = (struct el_torito_validation_entry*)buffer;
/* check if it is a valid catalog (TODO: check also the checksum)*/
if ( (ve->header_id[0] != 1) || (ve->key_byte1[0] != 0x55)
|| (ve->key_byte2[0] != 0xAA) ) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG, "Wrong or damaged El-Torito "
"Catalog.\n El-Torito info will be ignored.");
return NULL;
}
/* check for a valid platform */
if (ve->platform_id[0] != 0) {
iso_msg_hint(LIBISO_EL_TORITO_UNHANLED, "Unsupported El-Torito platform.\n"
"Only 80x86 si supported. El-Torito info will be ignored.");
}
/* ok, once we are here we assume it is a valid catalog */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
image = calloc(1, sizeof(struct el_torito_boot_image));
catalog->image = image;
catalog->proc = LIBISO_PREVIMG;
{
/*
* Create the placeholder.
* Note that this could be modified later if we find a directory entry
* for the catalog in the iso tree.
*/
struct iso_tree_node_boot *boot;
boot = calloc(1, sizeof(struct iso_tree_node_boot));
boot->node.refcount = 1;
boot->node.attrib.st_mode = S_IFREG | 0777;
boot->node.attrib.st_atime = boot->node.attrib.st_mtime
= boot->node.attrib.st_ctime = time(NULL);
boot->node.attrib.st_size = 2048;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.procedence = LIBISO_PREVIMG;
boot->node.name = NULL;
boot->loc.block = block;
catalog->node = boot;
}
/* parse the default entry */
entry = (struct el_torito_default_entry *)(buffer + 32);
image->bootable = entry->boot_indicator[0] ? 1 : 0;
//FIXME we need a way to handle patch_isolinux in ms images!!!
image->isolinux = 0;
image->type = entry->boot_media_type[0];
image->partition_type = entry->system_type[0];
image->load_seg = iso_read_lsb(entry->load_seg, 2);
image->load_size = iso_read_lsb(entry->sec_count, 2);
{
/*
* Create the placeholder.
* Note that this could be modified later if we find a directory entry
* for the image in the iso tree.
*/
struct iso_tree_node_boot *boot;
boot = calloc(1, sizeof(struct iso_tree_node_boot));
boot->node.refcount = 1;
boot->node.attrib.st_mode = S_IFREG | 0777;
boot->node.attrib.st_atime = boot->node.attrib.st_mtime
= boot->node.attrib.st_ctime = time(NULL);
boot->node.attrib.st_size = 2048;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.procedence = LIBISO_PREVIMG;
boot->node.name = NULL;
boot->img = 1;
boot->loc.block = iso_read_lsb(entry->block, 4);
image->node = boot;
}
//TODO how can we check if there are more entries?
return catalog;
}
static struct el_torito_boot_catalog *
read_el_torito_vol_desc(struct iso_read_info *info, unsigned char *buf)
{
struct ecma119_boot_rec_vol_desc *vol;
vol = (struct ecma119_boot_rec_vol_desc*)buf;
/* some sanity checks */
if ( strncmp((char*)vol->std_identifier, "CD001", 5)
|| vol->vol_desc_version[0] != 1
|| strncmp((char*)vol->boot_sys_id, "EL TORITO SPECIFICATION", 23)) {
iso_msg_hint(LIBISO_BOOT_VD_UNHANLED, "Unsupported Boot Vol. Desc.\n"
"Only El-Torito Specification, Version 1.0 Volume "
"Descriptors are supported. Ignoring boot info");
return NULL;
}
return read_el_torito_boot_catalog(info, iso_read_lsb(vol->boot_catalog, 4));
}
/**
* This reads the "." directory entry, and set the properties of the
@ -248,12 +364,22 @@ iso_read_single_directory_record(struct iso_read_info *info,
break;
case S_IFREG:
{
node = calloc(1, sizeof(struct iso_tree_node_file));
node->type = LIBISO_NODE_FILE;
/* set block with extend */
((struct iso_tree_node_file*)node)->loc.block =
iso_read_bb(record->block, 4, NULL);
uint32_t block;
block = iso_read_bb(record->block, 4, NULL);
if (info->bootcat && block == info->bootcat->node->loc.block) {
/* it is the boot catalog */
node = (struct iso_tree_node*)info->bootcat->node;
} else if (info->bootcat && block == info->bootcat->image->node->loc.block) {
/* it is the boot image */
node = (struct iso_tree_node*)info->bootcat->image->node;
} else {
/* it is a file */
node = calloc(1, sizeof(struct iso_tree_node_file));
node->type = LIBISO_NODE_FILE;
/* set block with extend */
((struct iso_tree_node_file*)node)->loc.block = block;
}
}
break;
case S_IFLNK:
@ -272,7 +398,7 @@ iso_read_single_directory_record(struct iso_read_info *info,
node->name = name;
node->attrib = atts;
node->refcount = 1;
node->refcount++; /* 1 for news, 2 for boot nodes */
node->procedence = LIBISO_PREVIMG;
iso_tree_add_child(parent, node);
@ -520,7 +646,6 @@ read_root_susp_entries(struct iso_read_info *info,
return 0;
}
static struct iso_volset *
read_pvm(struct iso_read_info *info, uint32_t block)
{
@ -628,6 +753,7 @@ iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts)
info.gid = opts->gid;
info.mode = opts->mode & ~S_IFMT;
info.size = &opts->size;
info.bootcat = NULL;
root_dir_block = 0;
/* read primary volume description */
@ -642,13 +768,16 @@ iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts)
do {
if ( info.src->read_block(info.src, block, buffer) < 0 ) {
info.error = LIBISOFS_READ_FAILURE;
iso_volset_free(volset);
return NULL;
/* cleanup and exit */
goto read_cleanup;
}
switch (buffer[0]) {
case 0:
/* boot record */
//TODO handle el-torito
/*
* This is a boot record
* Here we handle el-torito
*/
info.bootcat = read_el_torito_vol_desc(&info, buffer);
break;
case 2:
/* suplementary volume descritor */
@ -735,11 +864,25 @@ iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts)
if ( iso_read_dir(&info, volset->volume[0]->root, root_dir_block) ) {
/* error, cleanup and return */
iso_volset_free(volset);
return NULL;
goto read_cleanup;
}
// TODO merge tree info
/* Add El-Torito info to the volume */
if (info.bootcat) {
/* ok, add the bootcat to the volume */
iso_msg_debug("Found El-Torito bootable volume");
volset->volume[0]->bootcat = info.bootcat;
}
return volset;
read_cleanup:;
if (info.bootcat) {
el_torito_boot_catalog_free(info.bootcat);
}
iso_volset_free(volset);
return NULL;
}

@ -66,6 +66,9 @@ struct iso_read_info {
unsigned int hasJoliet:1; /*< It will be set to 1 if Joliet ext are present,
to 0 if not. */
uint32_t *size;
/* place for el-torito boot catalog */
struct el_torito_boot_catalog *bootcat;
};

@ -259,37 +259,29 @@ create_symlink(struct ecma119_write_target *t,
}
/**
* Create a new ECMA-119 node representing a boot catalog. This is like a
* regular file, but its contents are taken from a El-Torito boot catalog,
* and not from a file in the local filesystem.
* Create a new ECMA-119 node representing a boot catalog or image.
* This will be treated as a normal file when written the directory record,
* but its contents are written in a different way.
*
* See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for
* more details.
*/
static struct ecma119_tree_node*
create_boot_catalog(struct ecma119_write_target *t,
create_boot(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node_boot_catalog *iso)
struct iso_tree_node_boot *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;
ret->type = ECMA119_BOOT;
file = iso->catalog->file;
file->ino = ++t->ino;
ret->info.boot_img = iso->img;
ret->attrib.st_nlink = file->nlink;
ret->attrib.st_ino = file->ino;
ret->info.file = file;
ret->attrib.st_nlink = 1;
ret->attrib.st_ino = ++t->ino;
return ret;
}
@ -334,9 +326,8 @@ 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);
case LIBISO_NODE_BOOT:
ret = create_boot(t, parent, (struct iso_tree_node_boot*)iso);
break;
default:
/* should never happen */

@ -21,7 +21,8 @@ enum ecma119_node_type {
ECMA119_FILE,
ECMA119_SYMLINK,
ECMA119_DIR,
ECMA119_PLACEHOLDER /**< placeholder for a relocated dir. */
ECMA119_PLACEHOLDER, /**< placeholder for a relocated dir. */
ECMA119_BOOT
};
struct ecma119_dir_info {
@ -72,6 +73,10 @@ struct ecma119_tree_node
struct ecma119_tree_node *real_me; /**< this field points to
* the relocated directory.
*/
unsigned int boot_img:1; /** For boot nodes, it identifies if this
* corresponds to image(1) or catalog(0).
* The block is stored in ecma119_write_target
*/
} info;
};

@ -3,6 +3,7 @@
#include "eltorito.h"
#include "volume.h"
#include "util.h"
#include "messages.h"
#include <assert.h>
#include <malloc.h>
@ -15,46 +16,6 @@
#include <fcntl.h>
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
@ -85,18 +46,67 @@ struct hard_disc_mbr {
uint8_t sign2;
};
static void
el_torito_image_free(struct el_torito_boot_image *image)
{
assert(image);
/* unref image node */
iso_tree_free((struct iso_tree_node*)image->node);
free(image);
}
static struct iso_tree_node_boot*
create_boot_image_node(struct iso_tree_node_file *image)
{
struct iso_tree_node_boot *boot;
struct iso_tree_node_dir *parent;
assert(image);
boot = calloc(1, sizeof(struct iso_tree_node_boot));
boot->node.attrib = image->node.attrib;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.name = strdup(image->node.name);
boot->node.hide_flags = image->node.hide_flags;
boot->node.refcount = 2; /* mine and tree */
boot->loc.path = strdup(image->loc.path);
boot->img = 1; /* it refers to image */
/* replace iso_tree_node_file with the image */
parent = image->node.parent;
iso_tree_node_remove(parent, (struct iso_tree_node*)image);
iso_tree_add_child(parent, (struct iso_tree_node*) boot);
return boot;
}
static struct el_torito_boot_image *
create_image(struct iso_tree_node *image,
create_image(const char *path,
enum eltorito_boot_media_type type)
{
struct stat attrib;
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;
if (stat(path, &attrib)) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't access file.");
libisofs_errno = NO_FILE;
return NULL;
}
if ( !S_ISREG(attrib.st_mode) ) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG,
"We can only create boot images from regular files");
libisofs_errno = ELTORITO_WRONG_IMAGE;
return NULL;
}
switch (type) {
case ELTORITO_FLOPPY_EMUL:
switch (image->attrib.st_size) {
switch (attrib.st_size) {
case 1200 * 1024:
boot_media_type = 1; /* 1.2 meg diskette */
break;
@ -107,10 +117,14 @@ create_image(struct iso_tree_node *image,
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);
{
char msg[72];
sprintf(msg, "Invalid image size %d Kb. Must be one of 1.2, 1.44"
"or 2.88 Mb", (int) attrib.st_size / 1024);
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg);
libisofs_errno = ELTORITO_WRONG_IMAGE_SIZE;
return NULL;
}
break;
}
/* it seems that for floppy emulation we need to load
@ -125,13 +139,16 @@ create_image(struct iso_tree_node *image,
int used_partition;
/* read the MBR on disc and get the type of the partition */
fd = open(((struct iso_tree_node_file*)image)->loc.path, O_RDONLY);
fd = open(path, O_RDONLY);
if ( fd == -1 ) {
fprintf(stderr, "Can't open image file\n");
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't open image file.");
libisofs_errno = NO_READ_ACCESS;
return NULL;
}
if ( read(fd, &mbr, sizeof(mbr)) ) {
fprintf(stderr, "Can't read MBR from image file\n");
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG,
"Can't read MBR from image file.");
libisofs_errno = ELTORITO_WRONG_IMAGE;
close(fd);
return NULL;
}
@ -139,7 +156,9 @@ create_image(struct iso_tree_node *image,
/* check valid MBR signature */
if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) {
fprintf(stderr, "Invalid MBR. Wrong signature.\n");
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG,
"Invalid MBR. Wrong signature.");
libisofs_errno = ELTORITO_WRONG_IMAGE;
return NULL;
}
@ -149,8 +168,11 @@ create_image(struct iso_tree_node *image,
if (mbr.partition[i].type != 0) {
/* it's an used partition */
if (used_partition != -1) {
fprintf(stderr, "Invalid MBR. At least 2 partitions: %d and "
char msg[72];
sprintf(msg, "Invalid MBR. At least 2 partitions: %d and "
"%d, are being used\n", used_partition, i);
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg);
libisofs_errno = ELTORITO_WRONG_IMAGE;
return NULL;
} else
used_partition = i;
@ -170,67 +192,180 @@ create_image(struct iso_tree_node *image,
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*
/* parent and name can be null */
static struct iso_tree_node_boot*
create_boot_catalog_node(struct iso_tree_node_dir *parent,
const char *name)
{
struct iso_tree_node_boot_catalog *boot;
struct iso_tree_node_boot *boot;
assert( parent && name );
assert( (parent && name) || (!parent && !name) );
boot = calloc(1, sizeof(struct iso_tree_node_boot_catalog));
boot = calloc(1, sizeof(struct iso_tree_node_boot));
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);
boot->node.attrib.st_size = 2048;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.name = name ? strdup(name) : NULL;
boot->node.refcount = 1; /* mine */
if (parent) {
boot->node.refcount++; /* add tree ref */
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*
iso_volume_set_boot_image(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 *cat_node;
struct el_torito_boot_catalog *catalog;
struct iso_tree_node_file *imgfile;
assert(volume && !volume->bootcat);
assert(image && ISO_ISREG(image));
assert(dir && name);
imgfile = (struct iso_tree_node_file*)image;
if (image->procedence == LIBISO_PREVIMG) {
/* FIXME mmm, what to do here? */
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't use prev. image files "
"as boot images");
return NULL;
}
boot_image = create_image(imgfile->loc.path, type);
if ( !boot_image )
return NULL;
/* create image node */
boot_image->node = create_boot_image_node(imgfile);
/* creates the catalog with the given default image */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
if (!catalog) {
el_torito_image_free(boot_image);
return NULL;
}
catalog->image = boot_image;
/* add catalog file */
cat_node = create_boot_catalog_node(dir, name);
catalog->node = cat_node;
volume->bootcat = catalog;
return boot_image;
}
struct el_torito_boot_image*
iso_volume_set_boot_image_hidden(struct iso_volume *volume, const char* path,
enum eltorito_boot_media_type type)
{
struct el_torito_boot_image *boot_image;
struct iso_tree_node_boot_catalog *boot_node;
struct iso_tree_node_boot *cat_node;
struct el_torito_boot_catalog *catalog;
assert( volume && !volume->bootcat);
assert( image && ISO_ISREG(image) && dir && name);
assert(volume && !volume->bootcat);
assert(path);
boot_image = create_image(image, type);
boot_image = create_image(path, type);
if ( !boot_image )
return NULL;
/* create image node */
{
struct iso_tree_node_boot *boot;
boot = calloc(1, sizeof(struct iso_tree_node_boot));
/* we assume the stat won't fail, as we already stat the same file above */
stat(path, &boot->node.attrib);
boot->node.type = LIBISO_NODE_BOOT;
boot->node.refcount = 1; /* mine */
boot->loc.path = strdup(path);
boot->img = 1; /* it refers to image */
boot_image->node = boot;
}
/* 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;
if (!catalog) {
el_torito_image_free(boot_image);
return NULL;
}
catalog->image = boot_image;
/* add catalog file */
boot_node = create_boot_catalog_node(dir, name);
boot_node->catalog = catalog;
cat_node = create_boot_catalog_node(NULL, NULL);
catalog->node = cat_node;
volume->bootcat = catalog;
return boot_image;
}
struct el_torito_boot_image*
iso_volume_get_boot_image(struct iso_volume *volume,
struct iso_tree_node **imgnode,
struct iso_tree_node **catnode)
{
struct el_torito_boot_catalog *catalog;
assert(volume);
catalog = volume->bootcat;
if (!catalog)
return NULL;
if (imgnode)
*imgnode = (struct iso_tree_node*)catalog->image->node;
if (catnode)
*catnode = (struct iso_tree_node*)catalog->node;
return catalog->image;
}
void
iso_volume_remove_boot_image(struct iso_volume *volume)
{
struct el_torito_boot_catalog *catalog;
struct iso_tree_node *catnode;
struct iso_tree_node *imgnode;
assert(volume);
catalog = volume->bootcat;
if (!catalog)
return;
/* remove node from tree */
catnode = (struct iso_tree_node*)catalog->node;
if (catnode->parent)
iso_tree_node_remove(catnode->parent, catnode);
/* remove image from tree */
imgnode = (struct iso_tree_node*)catalog->image->node;
if (imgnode->parent)
iso_tree_node_remove(imgnode->parent, imgnode);
/* remove catalog */
el_torito_boot_catalog_free(catalog);
volume->bootcat = NULL;
}
void
el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment)
{
@ -254,43 +389,20 @@ el_torito_set_no_bootable(struct el_torito_boot_image *bootimg)
}
void
el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg)
el_torito_patch_isolinux_image(struct el_torito_boot_image *bootimg)
{
bootimg->patch_isolinux = 1;
bootimg->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);
el_torito_image_free(cat->image);
iso_tree_free((struct iso_tree_node*)cat->node);
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
*/
@ -307,7 +419,7 @@ el_torito_write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
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);
iso_lsb(vol->boot_catalog, t->catblock, 4);
}
static void
@ -333,104 +445,139 @@ write_validation_entry(struct ecma119_write_target *t, uint8_t *buf)
}
static void
patch_boot_file(struct el_torito_boot_image *img)
patch_boot_image(uint8_t *buf, struct ecma119_write_target *t, ssize_t imgsize)
{
struct boot_info_table info;
int fd;
struct boot_info_table *info;
uint32_t checksum;
ssize_t len;
uint8_t buf[4];
ssize_t offset;
memset(&info, 0, sizeof(info));
/* open image */
fd = open(img->image->loc.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->loc.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);
offset = (ssize_t) 64;
//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);
while (offset < imgsize) {
checksum += iso_read_lsb(buf + offset, 4);
offset += 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->loc.path);
close(fd);
return;
if (offset != imgsize) {
/* file length not multiple of 4 */
iso_msg_warn(LIBISO_ISOLINUX_CANT_PATCH,
"Unexpected isolinux image length. Patch might not work.");
}
/* 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);
}
/* patch boot info table */
info = (struct boot_info_table*)(buf + 8);
memset(info, 0, sizeof(struct boot_info_table));
iso_lsb(info->bi_pvd, t->ms_block + 16, 4);
iso_lsb(info->bi_file, t->imgblock, 4);
iso_lsb(info->bi_length, imgsize, 4);
iso_lsb(info->bi_csum, checksum, 4);
}
void
el_torito_patch_image_files(struct ecma119_write_target *t)
static void
write_boot_image(uint8_t *buf, struct ecma119_write_target *t)
{
size_t i;
struct el_torito_boot_catalog *cat = t->catalog;
assert(cat);
ssize_t imgsize;
struct el_torito_boot_image *image;
image = t->catalog->image;
imgsize= image->node->node.attrib.st_size;
/* copy image contents to memory buffer */
if (image->node->node.procedence == LIBISO_PREVIMG) {
int block, nblocks;
uint8_t *memloc;
assert(t->src);
block = 0;
memloc = buf;
nblocks = imgsize ? div_up(imgsize, 2048) : 1;
while (block < nblocks) {
if (t->src->read_block(t->src,
image->node->loc.block + block, memloc)) {
iso_msg_sorry(LIBISO_CANT_READ_IMG,
"Unable to read boot image from previous image.");
break; /* exit loop */
}
memloc += 2048;
++block;
}
} else {
int fd;
ssize_t nread, tread;
uint8_t *memloc;
const char *path = image->node->loc.path;
memloc = buf;
for (i = 0; i < cat->nentries; ++i) {
struct el_torito_boot_image *img = cat->entries[i];
if ( img->patch_isolinux )
patch_boot_file(img);
fd = open(path, O_RDONLY);
if (fd == -1) {
iso_msg_sorry(LIBISO_CANT_READ_FILE,
"Can't open boot image file for reading. Skipping");
return;
}
tread = 0;
while (tread < imgsize) {
nread = read(fd, memloc, t->block_size);
if (nread <= 0) {
iso_msg_sorry(LIBISO_CANT_READ_FILE,
"Problem reading boot image file. Skipping");
break; /* exit loop */
}
tread += nread;
memloc += nread;
}
close(fd);
}
if (image->isolinux) {
/* we need to patch the image */
patch_boot_image(buf, t, imgsize);
}
}
/**
* Write one section entry.
* Currently this is used for both default and other entries since we
* put selection criteria no 0 (no sel. criteria)
* Currently this is used only for default image (the only supported just now)
*/
static void
write_section_entry(uint8_t *buf, struct el_torito_boot_image *img)
write_section_entry(uint8_t *buf, struct ecma119_write_target *t)
{
struct el_torito_boot_image *img;
struct el_torito_section_entry *se =
(struct el_torito_section_entry*)buf;
img = t->catalog->image;
assert(img);
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
se->system_type[0] = img->partition_type;
iso_lsb(se->sec_count, img->load_size, 2);
iso_lsb(se->block, img->file->block, 4);
iso_lsb(se->block, t->imgblock, 4);
}
/**
* Write El-Torito Boot Catalog
* Write El-Torito Boot Catalog plus image
*/
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);
assert(cat->image);
write_validation_entry(t, buf);
/* write default entry */
write_section_entry(buf + 32, cat->entries[0]);
write_section_entry(buf + 32, t);
//TODO write all images
write_boot_image(buf + 2048, t);
}
void
@ -446,9 +593,27 @@ 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)
{
off_t size;
off_t imgsize;
assert(t->catalog);
/*
* Note that when this is called, we need to write el-torito info to
* image. Note, however, that the image could come from a previous session
* and maybe we don't know its size
*/
imgsize = t->catalog->image->node->node.attrib.st_size;
if (imgsize == 0) {
iso_msg_warn(LIBISO_EL_TORITO_BLIND_COPY,
"Can get boot image size, only one block will be copied");
imgsize = 2048;
}
size = 2048 + div_up(imgsize, t->block_size);
ecma119_start_chunking(t,
write_catalog,
2048,
size,
buf);
}

@ -5,51 +5,65 @@
#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 iso_tree_node_boot *node; /* node of the catalog */
struct el_torito_boot_image *image; /* default boot image */
enum tree_node_from proc; /* whether the catalog is new or read from a
* prev session/image */
};
struct el_torito_boot_image {
unsigned char bootable; /**< If the entry is bootable. */
unsigned char patch_isolinux; /**< If the image will be patched */
struct iso_tree_node_boot *node;
unsigned int bootable:1; /**< If the entry is bootable. */
unsigned int isolinux:1; /**< If the image will be patched */
unsigned char type; /**< The type of image */
unsigned char partition_type; /**< type of partition for HD-emul images */