Home | History | Annotate | Line # | Download | only in cd9660
cd9660_eltorito.c revision 1.19.2.1
      1 /*	$NetBSD: cd9660_eltorito.c,v 1.19.2.1 2013/02/25 00:30:44 tls Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
      5  * Perez-Rathke and Ram Vedam.  All rights reserved.
      6  *
      7  * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
      8  * Alan Perez-Rathke and Ram Vedam.
      9  *
     10  * Redistribution and use in source and binary forms, with or
     11  * without modification, are permitted provided that the following
     12  * conditions are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above
     16  *    copyright notice, this list of conditions and the following
     17  *    disclaimer in the documentation and/or other materials provided
     18  *    with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
     21  * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     24  * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
     25  * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
     28  * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
     32  * OF SUCH DAMAGE.
     33  */
     34 
     35 
     36 #include "cd9660.h"
     37 #include "cd9660_eltorito.h"
     38 #include <sys/bootblock.h>
     39 #include <util.h>
     40 
     41 #include <sys/cdefs.h>
     42 #if defined(__RCSID) && !defined(__lint)
     43 __RCSID("$NetBSD: cd9660_eltorito.c,v 1.19.2.1 2013/02/25 00:30:44 tls Exp $");
     44 #endif  /* !__lint */
     45 
     46 #ifdef DEBUG
     47 #define	ELTORITO_DPRINTF(__x)	printf __x
     48 #else
     49 #define	ELTORITO_DPRINTF(__x)
     50 #endif
     51 
     52 #include <util.h>
     53 
     54 static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void);
     55 static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
     56 static struct boot_catalog_entry *cd9660_boot_setup_default_entry(
     57     struct cd9660_boot_image *);
     58 static struct boot_catalog_entry *cd9660_boot_setup_section_head(char);
     59 static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
     60 #if 0
     61 static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *);
     62 #endif
     63 
     64 int
     65 cd9660_add_boot_disk(iso9660_disk *diskStructure, const char *boot_info)
     66 {
     67 	struct stat stbuf;
     68 	const char *mode_msg;
     69 	char *temp;
     70 	char *sysname;
     71 	char *filename;
     72 	struct cd9660_boot_image *new_image, *tmp_image;
     73 
     74 	assert(boot_info != NULL);
     75 
     76 	if (*boot_info == '\0') {
     77 		warnx("Error: Boot disk information must be in the "
     78 		      "format 'system;filename'");
     79 		return 0;
     80 	}
     81 
     82 	/* First decode the boot information */
     83 	temp = estrdup(boot_info);
     84 
     85 	sysname = temp;
     86 	filename = strchr(sysname, ';');
     87 	if (filename == NULL) {
     88 		warnx("supply boot disk information in the format "
     89 		    "'system;filename'");
     90 		free(temp);
     91 		return 0;
     92 	}
     93 
     94 	*filename++ = '\0';
     95 
     96 	if (diskStructure->verbose_level > 0) {
     97 		printf("Found bootdisk with system %s, and filename %s\n",
     98 		    sysname, filename);
     99 	}
    100 	new_image = ecalloc(1, sizeof(*new_image));
    101 	new_image->loadSegment = 0;	/* default for now */
    102 
    103 	/* Decode System */
    104 	if (strcmp(sysname, "i386") == 0)
    105 		new_image->system = ET_SYS_X86;
    106 	else if (strcmp(sysname, "powerpc") == 0)
    107 		new_image->system = ET_SYS_PPC;
    108 	else if (strcmp(sysname, "macppc") == 0 ||
    109 	         strcmp(sysname, "mac68k") == 0)
    110 		new_image->system = ET_SYS_MAC;
    111 	else {
    112 		warnx("boot disk system must be "
    113 		      "i386, powerpc, macppc, or mac68k");
    114 		free(temp);
    115 		free(new_image);
    116 		return 0;
    117 	}
    118 
    119 
    120 	new_image->filename = estrdup(filename);
    121 
    122 	free(temp);
    123 
    124 	/* Get information about the file */
    125 	if (lstat(new_image->filename, &stbuf) == -1)
    126 		err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
    127 		    new_image->filename);
    128 
    129 	switch (stbuf.st_size) {
    130 	case 1440 * 1024:
    131 		new_image->targetMode = ET_MEDIA_144FDD;
    132 		mode_msg = "Assigned boot image to 1.44 emulation mode";
    133 		break;
    134 	case 1200 * 1024:
    135 		new_image->targetMode = ET_MEDIA_12FDD;
    136 		mode_msg = "Assigned boot image to 1.2 emulation mode";
    137 		break;
    138 	case 2880 * 1024:
    139 		new_image->targetMode = ET_MEDIA_288FDD;
    140 		mode_msg = "Assigned boot image to 2.88 emulation mode";
    141 		break;
    142 	default:
    143 		new_image->targetMode = ET_MEDIA_NOEM;
    144 		mode_msg = "Assigned boot image to no emulation mode";
    145 		break;
    146 	}
    147 
    148 	if (diskStructure->verbose_level > 0)
    149 		printf("%s\n", mode_msg);
    150 
    151 	new_image->size = stbuf.st_size;
    152 	new_image->num_sectors =
    153 	    howmany(new_image->size, diskStructure->sectorSize) *
    154 	    howmany(diskStructure->sectorSize, 512);
    155 	if (diskStructure->verbose_level > 0) {
    156 		printf("New image has size %d, uses %d 512-byte sectors\n",
    157 		    new_image->size, new_image->num_sectors);
    158 	}
    159 	new_image->sector = -1;
    160 	/* Bootable by default */
    161 	new_image->bootable = ET_BOOTABLE;
    162 	/* Add boot disk */
    163 
    164 	/* Group images for the same platform together. */
    165 	TAILQ_FOREACH(tmp_image, &diskStructure->boot_images, image_list) {
    166 		if (tmp_image->system != new_image->system)
    167 			break;
    168 	}
    169 
    170 	if (tmp_image == NULL) {
    171 		TAILQ_INSERT_HEAD(&diskStructure->boot_images, new_image,
    172 		    image_list);
    173 	} else
    174 		TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list);
    175 
    176 	new_image->serialno = diskStructure->image_serialno++;
    177 
    178 	/* TODO : Need to do anything about the boot image in the tree? */
    179 	diskStructure->is_bootable = 1;
    180 
    181 	return 1;
    182 }
    183 
    184 int
    185 cd9660_eltorito_add_boot_option(iso9660_disk *diskStructure,
    186     const char *option_string, const char *value)
    187 {
    188 	char *eptr;
    189 	struct cd9660_boot_image *image;
    190 
    191 	assert(option_string != NULL);
    192 
    193 	/* Find the last image added */
    194 	TAILQ_FOREACH(image, &diskStructure->boot_images, image_list) {
    195 		if (image->serialno + 1 == diskStructure->image_serialno)
    196 			break;
    197 	}
    198 	if (image == NULL)
    199 		errx(EXIT_FAILURE, "Attempted to add boot option, "
    200 		    "but no boot images have been specified");
    201 
    202 	if (strcmp(option_string, "no-emul-boot") == 0) {
    203 		image->targetMode = ET_MEDIA_NOEM;
    204 	} else if (strcmp(option_string, "no-boot") == 0) {
    205 		image->bootable = ET_NOT_BOOTABLE;
    206 	} else if (strcmp(option_string, "hard-disk-boot") == 0) {
    207 		image->targetMode = ET_MEDIA_HDD;
    208 	} else if (strcmp(option_string, "boot-load-segment") == 0) {
    209 		image->loadSegment = strtoul(value, &eptr, 16);
    210 		if (eptr == value || *eptr != '\0' || errno != ERANGE) {
    211 			warn("%s: strtoul", __func__);
    212 			return 0;
    213 		}
    214 	} else {
    215 		return 0;
    216 	}
    217 	return 1;
    218 }
    219 
    220 static struct boot_catalog_entry *
    221 cd9660_init_boot_catalog_entry(void)
    222 {
    223 	return ecalloc(1, sizeof(struct boot_catalog_entry));
    224 }
    225 
    226 static struct boot_catalog_entry *
    227 cd9660_boot_setup_validation_entry(char sys)
    228 {
    229 	struct boot_catalog_entry *entry;
    230 	boot_catalog_validation_entry *ve;
    231 	int16_t checksum;
    232 	unsigned char *csptr;
    233 	size_t i;
    234 	entry = cd9660_init_boot_catalog_entry();
    235 
    236 	ve = &entry->entry_data.VE;
    237 
    238 	ve->header_id[0] = 1;
    239 	ve->platform_id[0] = sys;
    240 	ve->key[0] = 0x55;
    241 	ve->key[1] = 0xAA;
    242 
    243 	/* Calculate checksum */
    244 	checksum = 0;
    245 	cd9660_721(0, ve->checksum);
    246 	csptr = (unsigned char*)ve;
    247 	for (i = 0; i < sizeof(*ve); i += 2) {
    248 		checksum += (int16_t)csptr[i];
    249 		checksum += 256 * (int16_t)csptr[i + 1];
    250 	}
    251 	checksum = -checksum;
    252 	cd9660_721(checksum, ve->checksum);
    253 
    254         ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
    255 	    "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0],
    256 	    ve->key[0], ve->key[1], checksum));
    257 	return entry;
    258 }
    259 
    260 static struct boot_catalog_entry *
    261 cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
    262 {
    263 	struct boot_catalog_entry *default_entry;
    264 	boot_catalog_initial_entry *ie;
    265 
    266 	default_entry = cd9660_init_boot_catalog_entry();
    267 	if (default_entry == NULL)
    268 		return NULL;
    269 
    270 	ie = &default_entry->entry_data.IE;
    271 
    272 	ie->boot_indicator[0] = disk->bootable;
    273 	ie->media_type[0] = disk->targetMode;
    274 	cd9660_721(disk->loadSegment, ie->load_segment);
    275 	ie->system_type[0] = disk->system;
    276 	cd9660_721(disk->num_sectors, ie->sector_count);
    277 	cd9660_731(disk->sector, ie->load_rba);
    278 
    279 	ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
    280 	    "load segment %04x, system type %d, sector count %d, "
    281 	    "load rba %d\n", __func__, ie->boot_indicator[0],
    282 	    ie->media_type[0], disk->loadSegment, ie->system_type[0],
    283 	    disk->num_sectors, disk->sector));
    284 	return default_entry;
    285 }
    286 
    287 static struct boot_catalog_entry *
    288 cd9660_boot_setup_section_head(char platform)
    289 {
    290 	struct boot_catalog_entry *entry;
    291 	boot_catalog_section_header *sh;
    292 
    293 	entry = cd9660_init_boot_catalog_entry();
    294 	if (entry == NULL)
    295 		return NULL;
    296 
    297 	sh = &entry->entry_data.SH;
    298 	/* More by default. The last one will manually be set to 0x91 */
    299 	sh->header_indicator[0] = ET_SECTION_HEADER_MORE;
    300 	sh->platform_id[0] = platform;
    301 	sh->num_section_entries[0] = 0;
    302 	return entry;
    303 }
    304 
    305 static struct boot_catalog_entry *
    306 cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
    307 {
    308 	struct boot_catalog_entry *entry;
    309 	boot_catalog_section_entry *se;
    310 	if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
    311 		return NULL;
    312 
    313 	se = &entry->entry_data.SE;
    314 
    315 	se->boot_indicator[0] = ET_BOOTABLE;
    316 	se->media_type[0] = disk->targetMode;
    317 	cd9660_721(disk->loadSegment, se->load_segment);
    318 	cd9660_721(disk->num_sectors, se->sector_count);
    319 	cd9660_731(disk->sector, se->load_rba);
    320 	return entry;
    321 }
    322 
    323 #if 0
    324 static u_char
    325 cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
    326 {
    327 	/*
    328 		For hard drive booting, we need to examine the MBR to figure
    329 		out what the partition type is
    330 	*/
    331 	return 0;
    332 }
    333 #endif
    334 
    335 /*
    336  * Set up the BVD, Boot catalog, and the boot entries, but do no writing
    337  */
    338 int
    339 cd9660_setup_boot(iso9660_disk *diskStructure, int first_sector)
    340 {
    341 	int sector;
    342 	int used_sectors;
    343 	int num_entries = 0;
    344 	int catalog_sectors;
    345 	struct boot_catalog_entry *x86_head, *mac_head, *ppc_head,
    346 		*valid_entry, *default_entry, *temp, *head, **headp, *next;
    347 	struct cd9660_boot_image *tmp_disk;
    348 
    349 	headp = NULL;
    350 	x86_head = mac_head = ppc_head = NULL;
    351 
    352 	/* If there are no boot disks, don't bother building boot information */
    353 	if (TAILQ_EMPTY(&diskStructure->boot_images))
    354 		return 0;
    355 
    356 	/* Point to catalog: For now assume it consumes one sector */
    357 	ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector));
    358 	diskStructure->boot_catalog_sector = first_sector;
    359 	cd9660_bothendian_dword(first_sector,
    360 		diskStructure->boot_descriptor->boot_catalog_pointer);
    361 
    362 	/* Step 1: Generate boot catalog */
    363 	/* Step 1a: Validation entry */
    364 	valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86);
    365 	if (valid_entry == NULL)
    366 		return -1;
    367 
    368 	/*
    369 	 * Count how many boot images there are,
    370 	 * and how many sectors they consume.
    371 	 */
    372 	num_entries = 1;
    373 	used_sectors = 0;
    374 
    375 	TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
    376 		used_sectors += tmp_disk->num_sectors;
    377 
    378 		/* One default entry per image */
    379 		num_entries++;
    380 	}
    381 	catalog_sectors = howmany(num_entries * 0x20, diskStructure->sectorSize);
    382 	used_sectors += catalog_sectors;
    383 
    384 	if (diskStructure->verbose_level > 0) {
    385 		printf("%s: there will be %i entries consuming %i sectors. "
    386 		       "Catalog is %i sectors\n", __func__, num_entries,
    387 		       used_sectors, catalog_sectors);
    388 	}
    389 
    390 	/* Populate sector numbers */
    391 	sector = first_sector + catalog_sectors;
    392 	TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
    393 		tmp_disk->sector = sector;
    394 		sector += tmp_disk->num_sectors;
    395 	}
    396 
    397 	LIST_INSERT_HEAD(&diskStructure->boot_entries, valid_entry, ll_struct);
    398 
    399 	/* Step 1b: Initial/default entry */
    400 	/* TODO : PARAM */
    401 	tmp_disk = TAILQ_FIRST(&diskStructure->boot_images);
    402 	default_entry = cd9660_boot_setup_default_entry(tmp_disk);
    403 	if (default_entry == NULL) {
    404 		warnx("Error: memory allocation failed in cd9660_setup_boot");
    405 		return -1;
    406 	}
    407 
    408 	LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
    409 
    410 	/* Todo: multiple default entries? */
    411 
    412 	tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
    413 
    414 	temp = default_entry;
    415 
    416 	/* If multiple boot images are given : */
    417 	while (tmp_disk != NULL) {
    418 		/* Step 2: Section header */
    419 		switch (tmp_disk->system) {
    420 		case ET_SYS_X86:
    421 			headp = &x86_head;
    422 			break;
    423 		case ET_SYS_PPC:
    424 			headp = &ppc_head;
    425 			break;
    426 		case ET_SYS_MAC:
    427 			headp = &mac_head;
    428 			break;
    429 		default:
    430 			warnx("%s: internal error: unknown system type",
    431 			    __func__);
    432 			return -1;
    433 		}
    434 
    435 		if (*headp == NULL) {
    436 			head =
    437 			    cd9660_boot_setup_section_head(tmp_disk->system);
    438 			if (head == NULL) {
    439 				warnx("Error: memory allocation failed in "
    440 				      "cd9660_setup_boot");
    441 				return -1;
    442 			}
    443 			LIST_INSERT_AFTER(default_entry, head, ll_struct);
    444 			*headp = head;
    445 		} else
    446 			head = *headp;
    447 
    448 		head->entry_data.SH.num_section_entries[0]++;
    449 
    450 		/* Step 2a: Section entry and extensions */
    451 		temp = cd9660_boot_setup_section_entry(tmp_disk);
    452 		if (temp == NULL) {
    453 			warn("%s: cd9660_boot_setup_section_entry", __func__);
    454 			return -1;
    455 		}
    456 
    457 		while ((next = LIST_NEXT(head, ll_struct)) != NULL &&
    458 		       next->entry_type == ET_ENTRY_SE)
    459 			head = next;
    460 
    461 		LIST_INSERT_AFTER(head, temp, ll_struct);
    462 		tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
    463 	}
    464 
    465 	/* TODO: Remaining boot disks when implemented */
    466 
    467 	return first_sector + used_sectors;
    468 }
    469 
    470 int
    471 cd9660_setup_boot_volume_descriptor(iso9660_disk *diskStructure,
    472     volume_descriptor *bvd)
    473 {
    474 	boot_volume_descriptor *bvdData =
    475 	    (boot_volume_descriptor*)bvd->volumeDescriptorData;
    476 
    477 	bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
    478 	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
    479 	bvdData->version[0] = 1;
    480 	memcpy(bvdData->boot_system_identifier, ET_ID, 23);
    481 	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
    482 	diskStructure->boot_descriptor =
    483 	    (boot_volume_descriptor*) bvd->volumeDescriptorData;
    484 	return 1;
    485 }
    486 
    487 static int
    488 cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start,
    489     off_t nsectors, int type)
    490 {
    491 	uint8_t val;
    492 	uint32_t lba;
    493 
    494 	if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1)
    495 		err(1, "fseeko");
    496 
    497 	val = 0x80; /* Bootable */
    498 	fwrite(&val, sizeof(val), 1, fd);
    499 
    500 	val = 0xff; /* CHS begin */
    501 	fwrite(&val, sizeof(val), 1, fd);
    502 	fwrite(&val, sizeof(val), 1, fd);
    503 	fwrite(&val, sizeof(val), 1, fd);
    504 
    505 	val = type; /* Part type */
    506 	fwrite(&val, sizeof(val), 1, fd);
    507 
    508 	val = 0xff; /* CHS end */
    509 	fwrite(&val, sizeof(val), 1, fd);
    510 	fwrite(&val, sizeof(val), 1, fd);
    511 	fwrite(&val, sizeof(val), 1, fd);
    512 
    513 	/* LBA extent */
    514 	lba = htole32(sector_start);
    515 	fwrite(&lba, sizeof(lba), 1, fd);
    516 	lba = htole32(nsectors);
    517 	fwrite(&lba, sizeof(lba), 1, fd);
    518 
    519 	return 0;
    520 }
    521 
    522 static int
    523 cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions,
    524     off_t sector_start, off_t nsectors, off_t sector_size,
    525     const char *part_name, const char *part_type)
    526 {
    527 	uint32_t apm32, part_status;
    528 	uint16_t apm16;
    529 
    530 	/* See Apple Tech Note 1189 for the details about the pmPartStatus
    531 	 * flags.
    532 	 * Below the flags which are default:
    533 	 * - IsValid     0x01
    534 	 * - IsAllocated 0x02
    535 	 * - IsReadable  0x10
    536 	 * - IsWritable  0x20
    537 	 */
    538 	part_status = APPLE_PS_VALID | APPLE_PS_ALLOCATED | APPLE_PS_READABLE |
    539 	    APPLE_PS_WRITABLE;
    540 
    541 	if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1)
    542 		err(1, "fseeko");
    543 
    544 	/* Signature */
    545 	apm16 = htobe16(0x504d);
    546 	fwrite(&apm16, sizeof(apm16), 1, fd);
    547 	apm16 = 0;
    548 	fwrite(&apm16, sizeof(apm16), 1, fd);
    549 
    550 	/* Total number of partitions */
    551 	apm32 = htobe32(total_partitions);
    552 	fwrite(&apm32, sizeof(apm32), 1, fd);
    553 	/* Bounds */
    554 	apm32 = htobe32(sector_start);
    555 	fwrite(&apm32, sizeof(apm32), 1, fd);
    556 	apm32 = htobe32(nsectors);
    557 	fwrite(&apm32, sizeof(apm32), 1, fd);
    558 
    559 	fwrite(part_name, strlen(part_name) + 1, 1, fd);
    560 	fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR);
    561 	fwrite(part_type, strlen(part_type) + 1, 1, fd);
    562 	fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR);
    563 
    564 	apm32 = 0;
    565 	/* pmLgDataStart */
    566 	fwrite(&apm32, sizeof(apm32), 1, fd);
    567 	/* pmDataCnt */
    568 	apm32 = htobe32(nsectors);
    569 	fwrite(&apm32, sizeof(apm32), 1, fd);
    570 	/* pmPartStatus */
    571 	apm32 = htobe32(part_status);
    572 	fwrite(&apm32, sizeof(apm32), 1, fd);
    573 
    574 	return 0;
    575 }
    576 
    577 int
    578 cd9660_write_boot(iso9660_disk *diskStructure, FILE *fd)
    579 {
    580 	struct boot_catalog_entry *e;
    581 	struct cd9660_boot_image *t;
    582 	int apm_partitions = 0;
    583 	int mbr_partitions = 0;
    584 
    585 	/* write boot catalog */
    586 	if (fseeko(fd, (off_t)diskStructure->boot_catalog_sector *
    587 	    diskStructure->sectorSize, SEEK_SET) == -1)
    588 		err(1, "fseeko");
    589 
    590 	if (diskStructure->verbose_level > 0) {
    591 		printf("Writing boot catalog to sector %" PRId64 "\n",
    592 		    diskStructure->boot_catalog_sector);
    593 	}
    594 	LIST_FOREACH(e, &diskStructure->boot_entries, ll_struct) {
    595 		if (diskStructure->verbose_level > 0) {
    596 			printf("Writing catalog entry of type %d\n",
    597 			    e->entry_type);
    598 		}
    599 		/*
    600 		 * It doesnt matter which one gets written
    601 		 * since they are the same size
    602 		 */
    603 		fwrite(&(e->entry_data.VE), 1, 32, fd);
    604 	}
    605 	if (diskStructure->verbose_level > 0)
    606 		printf("Finished writing boot catalog\n");
    607 
    608 	/* copy boot images */
    609 	TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
    610 		if (diskStructure->verbose_level > 0) {
    611 			printf("Writing boot image from %s to sectors %d\n",
    612 			    t->filename, t->sector);
    613 		}
    614 		cd9660_copy_file(diskStructure, fd, t->sector, t->filename);
    615 
    616 		if (t->system == ET_SYS_MAC)
    617 			apm_partitions++;
    618 		if (t->system == ET_SYS_PPC)
    619 			mbr_partitions++;
    620 	}
    621 
    622 	/* some systems need partition tables as well */
    623 	if (mbr_partitions > 0 || diskStructure->chrp_boot) {
    624 		uint16_t sig;
    625 
    626 		fseek(fd, 0x1fe, SEEK_SET);
    627 		sig = htole16(0xaa55);
    628 		fwrite(&sig, sizeof(sig), 1, fd);
    629 
    630 		mbr_partitions = 0;
    631 
    632 		/* Write ISO9660 descriptor, enclosing the whole disk */
    633 		if (diskStructure->chrp_boot)
    634 			cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
    635 			    0, diskStructure->totalSectors *
    636 			    (diskStructure->sectorSize / 512), 0x96);
    637 
    638 		/* Write all partition entries */
    639 		TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
    640 			if (t->system != ET_SYS_PPC)
    641 				continue;
    642 			cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
    643 			    t->sector * (diskStructure->sectorSize / 512),
    644 			    t->num_sectors * (diskStructure->sectorSize / 512),
    645 			    0x41 /* PReP Boot */);
    646 		}
    647 	}
    648 
    649 	if (apm_partitions > 0) {
    650 		/* Write DDR and global APM info */
    651 		uint32_t apm32;
    652 		uint16_t apm16;
    653 		int total_parts;
    654 
    655 		fseek(fd, 0, SEEK_SET);
    656 		apm16 = htobe16(0x4552);
    657 		fwrite(&apm16, sizeof(apm16), 1, fd);
    658 		/* Device block size */
    659 		apm16 = htobe16(512);
    660 		fwrite(&apm16, sizeof(apm16), 1, fd);
    661 		/* Device block count */
    662 		apm32 = htobe32(diskStructure->totalSectors *
    663 		    (diskStructure->sectorSize / 512));
    664 		fwrite(&apm32, sizeof(apm32), 1, fd);
    665 		/* Device type/id */
    666 		apm16 = htobe16(1);
    667 		fwrite(&apm16, sizeof(apm16), 1, fd);
    668 		fwrite(&apm16, sizeof(apm16), 1, fd);
    669 
    670 		/* Count total needed entries */
    671 		total_parts = 2 + apm_partitions; /* Self + ISO9660 */
    672 
    673 		/* Write self-descriptor */
    674 		cd9660_write_apm_partition_entry(fd, 0, total_parts, 1,
    675 		    total_parts, 512, "Apple", "Apple_partition_map");
    676 
    677 		/* Write all partition entries */
    678 		apm_partitions = 0;
    679 		TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
    680 			if (t->system != ET_SYS_MAC)
    681 				continue;
    682 
    683 			cd9660_write_apm_partition_entry(fd,
    684 			    1 + apm_partitions++, total_parts,
    685 			    t->sector * (diskStructure->sectorSize / 512),
    686 			    t->num_sectors * (diskStructure->sectorSize / 512),
    687 			    512, "CD Boot", "Apple_Bootstrap");
    688 		}
    689 
    690 		/* Write ISO9660 descriptor, enclosing the whole disk */
    691 		cd9660_write_apm_partition_entry(fd, 2 + apm_partitions,
    692 		    total_parts, 0, diskStructure->totalSectors *
    693 		    (diskStructure->sectorSize / 512), 512, "ISO9660",
    694 		    "CD_ROM_Mode_1");
    695 	}
    696 
    697 	return 0;
    698 }
    699