cdrtools/mkisofs/boot.c
2025-06-15 04:19:58 +08:00

543 lines
14 KiB
C

/* @(#)boot.c 1.28 16/10/23 Copyright 1999-2016 J. Schilling */
#include <schily/mconfig.h>
#ifndef lint
static UConst char sccsid[] =
"@(#)boot.c 1.28 16/10/23 Copyright 1999-2016 J. Schilling";
#endif
/*
* Support for generic boot (sector 0..16)
* and to boot Sun sparc and Sun x86 systems.
*
* Copyright (c) 1999-2016 J. Schilling
*/
/*@@C@@*/
#include "mkisofs.h"
#include <schily/fcntl.h>
#include <schily/utypes.h>
#include <schily/intcvt.h>
#include <schily/schily.h>
#include "sunlabel.h"
extern int use_sunx86boot;
LOCAL struct sun_label cd_label;
LOCAL struct x86_label sx86_label;
LOCAL struct pc_part fdisk_part;
LOCAL char *boot_files[NDKMAP]; /* Change this for > 8 x86 parts */
LOCAL void init_sparc_label __PR((void));
LOCAL void init_sunx86_label __PR((void));
EXPORT int sparc_boot_label __PR((char *label));
EXPORT int sunx86_boot_label __PR((char *label));
EXPORT int scan_sparc_boot __PR((char *files));
EXPORT int scan_sunx86_boot __PR((char *files));
EXPORT int make_sun_label __PR((void));
EXPORT int make_sunx86_label __PR((void));
LOCAL void dup_sun_label __PR((int part));
LOCAL int sunboot_write __PR((FILE *outfile));
LOCAL int sunlabel_size __PR((UInt32_t starting_extent));
LOCAL int sunlabel_write __PR((FILE *outfile));
LOCAL int genboot_size __PR((UInt32_t starting_extent));
LOCAL int genboot_write __PR((FILE *outfile));
/*
* Set the virtual geometry in the disk label.
* If we like to make the geometry variable, we may change
* dkl_ncyl and dkl_pcyl later.
*/
LOCAL void
init_sparc_label()
{
i_to_4_byte(cd_label.dkl_vtoc.v_version, V_VERSION);
i_to_2_byte(cd_label.dkl_vtoc.v_nparts, NDKMAP);
i_to_4_byte(cd_label.dkl_vtoc.v_sanity, VTOC_SANE);
i_to_2_byte(cd_label.dkl_rpm, CD_RPM);
i_to_2_byte(cd_label.dkl_pcyl, CD_PCYL);
i_to_2_byte(cd_label.dkl_apc, CD_APC);
i_to_2_byte(cd_label.dkl_intrlv, CD_INTRLV);
i_to_2_byte(cd_label.dkl_ncyl, CD_NCYL);
i_to_2_byte(cd_label.dkl_acyl, CD_ACYL);
i_to_2_byte(cd_label.dkl_nhead, CD_NHEAD);
i_to_2_byte(cd_label.dkl_nsect, CD_NSECT);
cd_label.dkl_magic[0] = DKL_MAGIC_0;
cd_label.dkl_magic[1] = DKL_MAGIC_1;
}
LOCAL void
init_sunx86_label()
{
li_to_4_byte(sx86_label.dkl_vtoc.v_sanity, VTOC_SANE);
li_to_4_byte(sx86_label.dkl_vtoc.v_version, V_VERSION);
li_to_2_byte(sx86_label.dkl_vtoc.v_sectorsz, 512);
li_to_2_byte(sx86_label.dkl_vtoc.v_nparts, NX86MAP);
li_to_4_byte(sx86_label.dkl_pcyl, CD_PCYL);
li_to_4_byte(sx86_label.dkl_ncyl, CD_NCYL);
li_to_2_byte(sx86_label.dkl_acyl, CD_ACYL);
li_to_2_byte(sx86_label.dkl_bcyl, 0);
li_to_4_byte(sx86_label.dkl_nhead, CD_NHEAD);
li_to_4_byte(sx86_label.dkl_nsect, CD_NSECT);
li_to_2_byte(sx86_label.dkl_intrlv, CD_INTRLV);
li_to_2_byte(sx86_label.dkl_skew, 0);
li_to_2_byte(sx86_label.dkl_apc, CD_APC);
li_to_2_byte(sx86_label.dkl_rpm, CD_RPM);
li_to_2_byte(sx86_label.dkl_write_reinstruct, 0);
li_to_2_byte(sx86_label.dkl_read_reinstruct, 0);
li_to_2_byte(sx86_label.dkl_magic, DKL_MAGIC);
}
/*
* For command line parser: set ASCII label.
*/
EXPORT int
sparc_boot_label(label)
char *label;
{
strncpy(cd_label.dkl_ascilabel, label, 127);
cd_label.dkl_ascilabel[127] = '\0';
return (1);
}
EXPORT int
sunx86_boot_label(label)
char *label;
{
strncpy(sx86_label.dkl_vtoc.v_asciilabel, label, 127);
sx86_label.dkl_vtoc.v_asciilabel[127] = '\0';
return (1);
}
/*
* Parse the command line argument for boot images.
*/
EXPORT int
scan_sparc_boot(files)
char *files;
{
char *p;
int i = 1;
struct stat statbuf;
int status;
extern int use_sparcboot;
extern int use_sunx86boot;
if (use_sunx86boot)
comerrno(EX_BAD,
_("-sparc-boot and -sunx86-boot are mutual exclusive.\n"));
use_sparcboot++;
init_sparc_label();
do {
if (i >= NDKMAP)
comerrno(EX_BAD, _("Too many boot partitions.\n"));
boot_files[i++] = files;
if ((p = strchr(files, ',')) != NULL)
*p++ = '\0';
files = p;
} while (p);
i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_tag, V_USR);
i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_flag, V_RONLY);
for (i = 0; i < NDKMAP; i++) {
p = boot_files[i];
if (p == NULL || *p == '\0')
continue;
if (strcmp(p, "...") == '\0')
break;
status = stat_filter(p, &statbuf);
if (status < 0 || access(p, R_OK) < 0)
comerr(_("Cannot access '%s'.\n"), p);
i_to_4_byte(cd_label.dkl_map[i].dkl_nblk,
roundup(statbuf.st_size, CD_CYLSIZE)/512);
i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag, V_ROOT);
i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
}
return (1);
}
EXPORT int
scan_sunx86_boot(files)
char *files;
{
char *p;
int i = 0;
struct stat statbuf;
int status;
extern int use_sparcboot;
extern int use_sunx86boot;
if (use_sparcboot)
comerrno(EX_BAD,
_("-sparc-boot and -sunx86-boot are mutual exclusive.\n"));
use_sunx86boot++;
init_sunx86_label();
do {
if (i >= NDKMAP)
comerrno(EX_BAD, _("Too many boot partitions.\n"));
boot_files[i++] = files;
if ((p = strchr(files, ',')) != NULL)
*p++ = '\0';
files = p;
} while (p);
li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_tag, V_ROOT); /* UFS */
li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_flag, V_RONLY);
li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_tag, V_USR); /* ISO */
li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_flag, V_RONLY);
li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_tag, 0); /* ALL */
li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_flag, 0);
for (i = 0; i < NDKMAP; i++) {
p = boot_files[i];
if (p == NULL || *p == '\0')
continue;
if (i == 1 || i == 2) {
comerrno(EX_BAD,
_("Partition %d may not have a filename.\n"), i);
}
status = stat_filter(p, &statbuf);
if (status < 0 || access(p, R_OK) < 0)
comerr(_("Cannot access '%s'.\n"), p);
li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size,
roundup(statbuf.st_size, CD_CYLSIZE)/512);
if (i > 2) {
li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_tag, V_USR);
li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
}
}
return (1);
}
/*
* Finish the Sun disk label and compute the size of the additional data.
*/
EXPORT int
make_sun_label()
{
int last;
int cyl = 0;
int nblk;
int bsize;
int i;
char *p;
/*
* Compute the size of the padding for the iso9660 image
* to allow the next partition to start on a cylinder boundary.
*/
last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE));
i_to_4_byte(cd_label.dkl_map[0].dkl_nblk, last*4);
bsize = 0;
for (i = 0; i < NDKMAP; i++) {
p = boot_files[i];
if (p != NULL && strcmp(p, "...") == '\0') {
dup_sun_label(i);
break;
}
if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0)
continue;
i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl);
cyl += nblk / (CD_CYLSIZE/512);
if (i > 0)
bsize += nblk;
}
bsize /= 4;
return (last-last_extent+bsize);
}
/*
* A typical Solaris boot/install CD from a Sun CD set looks
* this way:
*
* UFS Part 0 tag 2 flag 10 start 3839 size 1314560
* ISO Part 1 tag 4 flag 10 start 0 size 3839
* ALL Part 2 tag 0 flag 0 start 0 size 1318400
*/
EXPORT int
make_sunx86_label()
{
int last;
int cyl = 0;
int nblk;
int bsize;
int i;
int partoff = 1; /* The offset of the Solaris 0x82 partition */
/*
* Compute the size of the padding for the iso9660 image
* to allow the next partition to start on a cylinder boundary.
*/
last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE));
li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4);
/*
* Note that the Solaris fdisk partition with fdisk signature 0x82
* is created at fixed offset 1 sector == 512 Bytes by this
* implementation.
* We need subtract this partition offset from all absolute
* partition offsets in order to get offsets relative to the
* Solaris primary partition.
*/
bsize = 0;
for (i = 0; i < NDKMAP; i++) {
if (i == 2) /* Never include the whole disk in */
continue; /* size/offset computations */
if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0)
continue;
li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_start,
cyl*(CD_CYLSIZE/512)-partoff);
cyl += nblk / (CD_CYLSIZE/512);
if (i == 0 || i > 2)
bsize += nblk;
}
li_to_4_byte(sx86_label.dkl_vtoc.v_part[0].p_start, last*4-partoff);
li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_start, 0);
li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4-partoff);
li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_start, 0);
li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_size, last*4+bsize);
fdisk_part.part[0].pr_status = STATUS_ACTIVE;
fdisk_part.part[0].pr_type = TYPE_SOLARIS;
li_to_4_byte(fdisk_part.part[0].pr_partoff, partoff);
li_to_4_byte(fdisk_part.part[0].pr_nsect, last*4+bsize-partoff);
fdisk_part.magic[0] = 0x55;
fdisk_part.magic[1] = 0xAA;
bsize /= 4;
return (last-last_extent+bsize);
}
/*
* Duplicate a partition of the Sun disk label until all partitions are filled up.
*/
LOCAL void
dup_sun_label(part)
int part;
{
int cyl;
int nblk;
int i;
if (part < 1 || part >= NDKMAP)
part = 1;
cyl = a_to_4_byte(cd_label.dkl_map[part-1].dkl_cylno);
nblk = a_to_4_byte(cd_label.dkl_map[part-1].dkl_nblk);
for (i = part; i < NDKMAP; i++) {
i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl);
i_to_4_byte(cd_label.dkl_map[i].dkl_nblk, nblk);
i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag, V_ROOT);
i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
}
}
/*
* Write out Sun boot partitions.
*/
LOCAL int
sunboot_write(outfile)
FILE *outfile;
{
char buffer[SECTOR_SIZE];
int i;
int n;
int nblk;
int amt;
int f;
char *p;
memset(buffer, 0, sizeof (buffer));
/*
* Write padding to the iso9660 image to allow the
* boot partitions to start on a cylinder boundary.
*/
amt = roundup(last_extent_written, (CD_CYLSIZE/SECTOR_SIZE)) - last_extent_written;
for (n = 0; n < amt; n++) {
xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
last_extent_written++;
}
if (use_sunx86boot)
i = 0;
else
i = 1;
for (; i < NDKMAP; i++) {
if (use_sunx86boot && (i == 1 || i == 2))
continue;
p = boot_files[i];
if (p == NULL || *p == '\0')
continue;
if (p != NULL && strcmp(p, "...") == '\0')
break;
if (use_sunx86boot) {
if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0)
continue;
} else {
if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0)
continue;
}
if ((f = open(boot_files[i], O_RDONLY| O_BINARY)) < 0)
comerr(_("Cannot open '%s'.\n"), boot_files[i]);
amt = nblk / 4;
for (n = 0; n < amt; n++) {
memset(buffer, 0, sizeof (buffer));
if (read(f, buffer, SECTOR_SIZE) < 0)
comerr(_("Read error on '%s'.\n"), boot_files[i]);
xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
last_extent_written++;
}
close(f);
}
fprintf(stderr, _("Total extents including %s boot = %u\n"),
use_sunx86boot ? "Solaris x86":"sparc",
last_extent_written - session_start);
return (0);
}
/*
* Do size management for the Sun disk label that is located in the first
* sector of a disk.
*/
LOCAL int
sunlabel_size(starting_extent)
UInt32_t starting_extent;
{
if (last_extent != session_start)
comerrno(EX_BAD, _("Cannot create sparc boot on offset != 0.\n"));
last_extent++;
return (0);
}
/*
* Compute the checksum and write a Sun disk label to the first sector
* of the disk.
* If the -generic-boot option has been specified too, overlay the
* Sun disk label on the first 512 bytes of the generic boot code.
*/
LOCAL int
sunlabel_write(outfile)
FILE *outfile;
{
char buffer[SECTOR_SIZE];
register char *p;
register short count = (512/2) - 1;
int f;
memset(buffer, 0, sizeof (buffer));
if (genboot_image) {
if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0)
comerr(_("Cannot open '%s'.\n"), genboot_image);
if (read(f, buffer, SECTOR_SIZE) < 0)
comerr(_("Read error on '%s'.\n"), genboot_image);
close(f);
}
if (use_sunx86boot) {
if (sx86_label.dkl_vtoc.v_asciilabel[0] == '\0')
strcpy(sx86_label.dkl_vtoc.v_asciilabel, CD_X86LABEL);
p = (char *)&sx86_label;
sx86_label.dkl_cksum[0] = 0;
sx86_label.dkl_cksum[1] = 0;
while (count-- > 0) {
sx86_label.dkl_cksum[0] ^= *p++;
sx86_label.dkl_cksum[1] ^= *p++;
}
memcpy(&buffer[0x1BE], fdisk_part.part, sizeof (fdisk_part.part));
p = &buffer[510];
*p++ = 0x55;
*p = 0xAA;
memcpy(&buffer[1024], &sx86_label, 512);
} else {
/*
* If we don't already have a Sun disk label text
* set up the default.
*/
if (cd_label.dkl_ascilabel[0] == '\0')
strcpy(cd_label.dkl_ascilabel, CD_DEFLABEL);
p = (char *)&cd_label;
cd_label.dkl_cksum[0] = 0;
cd_label.dkl_cksum[1] = 0;
while (count--) {
cd_label.dkl_cksum[0] ^= *p++;
cd_label.dkl_cksum[1] ^= *p++;
}
memcpy(buffer, &cd_label, 512);
}
xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
last_extent_written++;
return (0);
}
/*
* Do size management for the generic boot code on sectors 0..16.
*/
LOCAL int
genboot_size(starting_extent)
UInt32_t starting_extent;
{
if (last_extent > (session_start + 1))
comerrno(EX_BAD, _("Cannot create generic boot on offset != 0.\n"));
last_extent = session_start + 16;
return (0);
}
/*
* Write the generic boot code to sectors 0..16.
* If there is a Sun disk label, start writing at sector 1.
*/
LOCAL int
genboot_write(outfile)
FILE *outfile;
{
char buffer[SECTOR_SIZE];
int i;
int f;
if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0)
comerr(_("Cannot open '%s'.\n"), genboot_image);
for (i = 0; i < 16; i++) {
memset(buffer, 0, sizeof (buffer));
if (read(f, buffer, SECTOR_SIZE) < 0)
comerr(_("Read error on '%s'.\n"), genboot_image);
if (i != 0 || last_extent_written == session_start) {
xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
last_extent_written++;
}
}
close(f);
return (0);
}
struct output_fragment sunboot_desc = {NULL, NULL, NULL, sunboot_write, "Sun Boot" };
struct output_fragment sunlabel_desc = {NULL, sunlabel_size, NULL, sunlabel_write, "Sun Disk Label" };
struct output_fragment genboot_desc = {NULL, genboot_size, NULL, genboot_write, "Generic Boot" };