Add isoboot(8) for booting BIOS systems from HDDs containing ISO images.

This is part of a project for adding the ability to create hybrid CD/USB boot
images. In the BIOS case when booting from something that isn't a CD we need
some extra boot code to actually find our next stage (loader) within an
ISO9660 filesystem. This code will reside in a GPT partition (similar to
gptboot(8) from which it is derived) and looks for /boot/loader in an
ISO9660 filesystem on the image.

Reviewed by:	imp
Sponsored by:	iXsystems, Inc.
Differential Revision:	https://reviews.freebsd.org/D14914
This commit is contained in:
Benno Rice 2018-04-05 19:40:46 +00:00
parent 288013f54c
commit 7acb51f681
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=332084
6 changed files with 1026 additions and 2 deletions

View file

@ -5,7 +5,7 @@ NO_OBJ=t
.include <bsd.init.mk>
SUBDIR.yes= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot \
libi386
isoboot libi386
SUBDIR.${MK_LOADER_FIREWIRE}+= libfirewire

View file

@ -0,0 +1,68 @@
# $FreeBSD$
HAVE_GELI= yes
.include <bsd.init.mk>
.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \
${BOOTSRC}/i386/common ${SASRC}
FILES= isoboot
MAN= isoboot.8
NM?= nm
BOOT_COMCONSOLE_PORT?= 0x3f8
BOOT_COMCONSOLE_SPEED?= 9600
B2SIOFMT?= 0x3
REL1= 0x700
ORG1= 0x7c00
ORG2= 0x0
ISOBOOTSIZE?= 30720
CFLAGS+=-DBOOTPROG=\"isoboot\" \
-O1 \
-DSIOPRT=${BOOT_COMCONSOLE_PORT} \
-DSIOFMT=${B2SIOFMT} \
-DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
-I${LDRSRC} \
-I${BOOTSRC}/i386/common \
-I${BOOTSRC}/i386/boot2 \
-Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
-Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
-Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
-Winline -Wno-pointer-sign
CFLAGS.gcc+= --param max-inline-insns-single=100
CFLAGS.clang+= -Oz ${CLANG_OPT_SMALL}
LD_FLAGS+=${LD_FLAGS_BIN}
CLEANFILES+= isoboot
isoboot: gptldr.bin isoboot.bin ${BTXKERN}
btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
-o ${.TARGET} isoboot.bin
@set -- `ls -l ${.TARGET}`; x=$$((${ISOBOOTSIZE}-$$5)); \
echo "$$x bytes available"; test $$x -ge 0
CLEANFILES+= gptldr.bin gptldr.out gptldr.o
gptldr.bin: gptldr.out
${OBJCOPY} -S -O binary gptldr.out ${.TARGET}
gptldr.out: gptldr.o
${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
CLEANFILES+= isoboot.bin isoboot.out isoboot.o sio.o crc32.o drv.o \
cons.o ${OPENCRYPTO_XTS}
isoboot.bin: isoboot.out
${OBJCOPY} -S -O binary isoboot.out ${.TARGET}
isoboot.out: ${BTXCRT} isoboot.o sio.o crc32.o drv.o cons.o ${OPENCRYPTO_XTS}
${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBGELIBOOT} ${LIBSA32}
.include <bsd.prog.mk>

View file

@ -0,0 +1,69 @@
.\" Copyright (c) 2018 iXsystems, Inc.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd March 30, 2018
.Dt ISOBOOT 8
.Os
.Sh NAME
.Nm isoboot
.Nd Boot code for hybrid ISO/USB images on BIOS-based computers
.Sh DESCRIPTION
.Nm
is used on BIOS-based computers to boot from an ISO image that has
been written to a USB flash drive or other HDD-like device.
.Nm
is installed in a
.Cm freebsd-boot
partition with
.Xr mkimg 1 .
.Sh IMPLEMENTATION NOTES
The El Torito standard for bootable CDs provides a 32KB "System Area"
at the beginning of an image.
To create an image that is able to be booted by the BIOS as either a
CD-ROM ("ISO") and as a more HDD-like image (e.g. on a USB flash drive)
it is necessary to have both a standard El Torito boot catalog
containing a HDD-style partition table and boot code.
.Nm
is intended to be placed in a GPT partition to allow the system to find
the standard
.Fx
.Xr loader 8
in the ISO filesystem later in the image.
.Sh BOOTING
.Nm
looks for an ISO filesystem image on the device it was booted from and
seeks to read either the primary
.Fx
.Xr loader 8
or kernel from there.
.Sh SEE ALSO
.Xr mkimg 1
.Sh HISTORY
.Nm
appeared in FreeBSD 12.0.
.Sh AUTHORS
This manual page written by
.An Benno Rice Aq benno@FreeBSD.org .

View file

@ -0,0 +1,522 @@
/*-
* Copyright (c) 1998 Robert Nordier
* All rights reserved.
*
* Redistribution and use in source and binary forms are freely
* permitted provided that the above copyright notice and this
* paragraph and the following disclaimer are duplicated in all
* such forms.
*
* This software is provided "AS IS" and without any express or
* implied warranties, including, without limitation, the implied
* warranties of merchantability and fitness for a particular
* purpose.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/gpt.h>
#include <sys/dirent.h>
#include <sys/reboot.h>
#include <machine/bootinfo.h>
#include <machine/elf.h>
#include <machine/pc/bios.h>
#include <machine/psl.h>
#include <stdarg.h>
#include <a.out.h>
#include <btxv86.h>
#include "stand.h"
#include "bootargs.h"
#include "lib.h"
#include "rbx.h"
#include "drv.h"
#include "cons.h"
#include "gpt.h"
#include "paths.h"
#define ARGS 0x900
#define NOPT 14
#define NDEV 3
#define MEM_BASE 0x12
#define MEM_EXT 0x15
#define DRV_HARD 0x80
#define DRV_MASK 0x7f
#define TYPE_AD 0
#define TYPE_DA 1
#define TYPE_MAXHARD TYPE_DA
#define TYPE_FD 2
extern uint32_t _end;
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
static const unsigned char flags[NOPT] = {
RBX_DUAL,
RBX_SERIAL,
RBX_ASKNAME,
RBX_CDROM,
RBX_CONFIG,
RBX_KDB,
RBX_GDB,
RBX_MUTE,
RBX_NOINTR,
RBX_PAUSE,
RBX_QUIET,
RBX_DFLTROOT,
RBX_SINGLE,
RBX_VERBOSE
};
uint32_t opts;
static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
static const unsigned char dev_maj[NDEV] = {30, 4, 2};
static struct dsk dsk;
static char kname[1024];
static int comspeed = SIOSPD;
static struct bootinfo bootinfo;
static vm_offset_t high_heap_base;
static uint32_t bios_basemem, bios_extmem, high_heap_size;
static struct bios_smap smap;
/*
* The minimum amount of memory to reserve in bios_extmem for the heap.
*/
#define HEAP_MIN (3 * 1024 * 1024)
static char *heap_next;
static char *heap_end;
int main(void);
static void load(void);
static int parse_cmds(char *, int *);
static uint8_t ls, dsk_meta;
static uint32_t fs_off;
#include "cd9660read.c"
static inline int
xfsread(uint64_t inode, void *buf, size_t nbyte)
{
if ((size_t)cd9660_fsread(inode, buf, nbyte) != nbyte) {
printf("Invalid %s\n", "format");
return (-1);
}
return (0);
}
static void
bios_getmem(void)
{
uint64_t size;
/* Parse system memory map */
v86.ebx = 0;
do {
v86.ctl = V86_FLAGS;
v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/
v86.eax = 0xe820;
v86.ecx = sizeof(struct bios_smap);
v86.edx = SMAP_SIG;
v86.es = VTOPSEG(&smap);
v86.edi = VTOPOFF(&smap);
v86int();
if ((v86.efl & 1) || (v86.eax != SMAP_SIG))
break;
/* look for a low-memory segment that's large enough */
if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
(smap.length >= (512 * 1024)))
bios_basemem = smap.length;
/* look for the first segment in 'extended' memory */
if ((smap.type == SMAP_TYPE_MEMORY) &&
(smap.base == 0x100000)) {
bios_extmem = smap.length;
}
/*
* Look for the largest segment in 'extended' memory beyond
* 1MB but below 4GB.
*/
if ((smap.type == SMAP_TYPE_MEMORY) &&
(smap.base > 0x100000) && (smap.base < 0x100000000ull)) {
size = smap.length;
/*
* If this segment crosses the 4GB boundary,
* truncate it.
*/
if (smap.base + size > 0x100000000ull)
size = 0x100000000ull - smap.base;
if (size > high_heap_size) {
high_heap_size = size;
high_heap_base = smap.base;
}
}
} while (v86.ebx != 0);
/* Fall back to the old compatibility function for base memory */
if (bios_basemem == 0) {
v86.ctl = 0;
v86.addr = 0x12; /* int 0x12 */
v86int();
bios_basemem = (v86.eax & 0xffff) * 1024;
}
/*
* Fall back through several compatibility functions for extended
* memory
*/
if (bios_extmem == 0) {
v86.ctl = V86_FLAGS;
v86.addr = 0x15; /* int 0x15 function 0xe801*/
v86.eax = 0xe801;
v86int();
if (!(v86.efl & 1)) {
bios_extmem = ((v86.ecx & 0xffff) +
((v86.edx & 0xffff) * 64)) * 1024;
}
}
if (bios_extmem == 0) {
v86.ctl = 0;
v86.addr = 0x15; /* int 0x15 function 0x88*/
v86.eax = 0x8800;
v86int();
bios_extmem = (v86.eax & 0xffff) * 1024;
}
/*
* If we have extended memory and did not find a suitable heap
* region in the SMAP, use the last 3MB of 'extended' memory as a
* high heap candidate.
*/
if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
high_heap_size = HEAP_MIN;
high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
}
}
int
main(void)
{
char cmd[512], cmdtmp[512];
ssize_t sz;
int autoboot, dskupdated;
uint64_t ino;
bios_getmem();
if (high_heap_size > 0) {
heap_end = PTOV(high_heap_base + high_heap_size);
heap_next = PTOV(high_heap_base);
} else {
heap_next = (char *)
(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
heap_end = (char *)PTOV(bios_basemem);
}
setheap(heap_next, heap_end);
v86.ctl = V86_FLAGS;
v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
dsk.drive = *(uint8_t *)PTOV(ARGS);
dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
dsk.unit = dsk.drive & DRV_MASK;
dsk.part = -1;
dsk.start = 0;
bootinfo.bi_version = BOOTINFO_VERSION;
bootinfo.bi_size = sizeof(bootinfo);
bootinfo.bi_basemem = bios_basemem / 1024;
bootinfo.bi_extmem = bios_extmem / 1024;
bootinfo.bi_memsizes_valid++;
bootinfo.bi_bios_dev = dsk.drive;
autoboot = 1;
*cmd = '\0';
for (;;) {
*kname = '\0';
if ((ino = cd9660_lookup(PATH_CONFIG)) ||
(ino = cd9660_lookup(PATH_DOTCONFIG))) {
sz = cd9660_fsread(ino, cmd, sizeof(cmd) - 1);
cmd[(sz < 0) ? 0 : sz] = '\0';
}
if (*cmd != '\0') {
memcpy(cmdtmp, cmd, sizeof(cmdtmp));
if (parse_cmds(cmdtmp, &dskupdated))
break;
if (!OPT_CHECK(RBX_QUIET))
printf("%s: %s", PATH_CONFIG, cmd);
*cmd = '\0';
}
if (autoboot && keyhit(3)) {
if (*kname == '\0')
memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
break;
}
autoboot = 0;
/*
* Try to exec stage 3 boot loader. If interrupted by a
* keypress, or in case of failure, try to load a kernel
* directly instead.
*/
if (*kname != '\0')
load();
memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
load();
memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
load();
dsk_meta = 0;
}
/* Present the user with the boot2 prompt. */
for (;;) {
if (!OPT_CHECK(RBX_QUIET)) {
printf("\nFreeBSD/x86 boot\n"
"Default: %u:%s(%up%u)%s\n"
"boot: ",
dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
dsk.part, kname);
}
if (ioctrl & IO_SERIAL)
sio_flush();
*cmd = '\0';
if (keyhit(0))
getstr(cmd, sizeof(cmd));
else if (!OPT_CHECK(RBX_QUIET))
putchar('\n');
if (parse_cmds(cmd, &dskupdated)) {
putchar('\a');
continue;
}
load();
}
/* NOTREACHED */
}
/* Needed so btxld can link us properly; do not remove. */
void
exit(int x)
{
while (1);
__unreachable();
}
static void
load(void)
{
union {
struct exec ex;
Elf32_Ehdr eh;
} hdr;
static Elf32_Phdr ep[2];
static Elf32_Shdr es[2];
caddr_t p;
uint64_t ino;
uint32_t addr, x;
int fmt, i, j;
if (!(ino = cd9660_lookup(kname))) {
if (!ls) {
printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
kname, dsk.drive & DRV_MASK, dev_nm[dsk.type],
dsk.unit,
dsk.part);
}
return;
}
if (xfsread(ino, &hdr, sizeof(hdr)))
return;
if (N_GETMAGIC(hdr.ex) == ZMAGIC)
fmt = 0;
else if (IS_ELF(hdr.eh))
fmt = 1;
else {
printf("Invalid %s\n", "format");
return;
}
if (fmt == 0) {
addr = hdr.ex.a_entry & 0xffffff;
p = PTOV(addr);
fs_off = PAGE_SIZE;
if (xfsread(ino, p, hdr.ex.a_text))
return;
p += roundup2(hdr.ex.a_text, PAGE_SIZE);
if (xfsread(ino, p, hdr.ex.a_data))
return;
p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
bootinfo.bi_symtab = VTOP(p);
memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
p += sizeof(hdr.ex.a_syms);
if (hdr.ex.a_syms) {
if (xfsread(ino, p, hdr.ex.a_syms))
return;
p += hdr.ex.a_syms;
if (xfsread(ino, p, sizeof(int)))
return;
x = *(uint32_t *)p;
p += sizeof(int);
x -= sizeof(int);
if (xfsread(ino, p, x))
return;
p += x;
}
} else {
fs_off = hdr.eh.e_phoff;
for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
if (xfsread(ino, ep + j, sizeof(ep[0])))
return;
if (ep[j].p_type == PT_LOAD)
j++;
}
for (i = 0; i < 2; i++) {
p = PTOV(ep[i].p_paddr & 0xffffff);
fs_off = ep[i].p_offset;
if (xfsread(ino, p, ep[i].p_filesz))
return;
}
p += roundup2(ep[1].p_memsz, PAGE_SIZE);
bootinfo.bi_symtab = VTOP(p);
if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
(hdr.eh.e_shstrndx + 1);
if (xfsread(ino, &es, sizeof(es)))
return;
for (i = 0; i < 2; i++) {
memcpy(p, &es[i].sh_size,
sizeof(es[i].sh_size));
p += sizeof(es[i].sh_size);
fs_off = es[i].sh_offset;
if (xfsread(ino, p, es[i].sh_size))
return;
p += es[i].sh_size;
}
}
addr = hdr.eh.e_entry & 0xffffff;
}
bootinfo.bi_esymtab = VTOP(p);
bootinfo.bi_kernelname = VTOP(kname);
bootinfo.bi_bios_dev = dsk.drive;
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.unit, 0),
KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo));
}
static int
parse_cmds(char *cmdstr, int *dskupdated)
{
char *arg;
char *ep, *p, *q;
const char *cp;
unsigned int drv;
int c, i, j;
arg = cmdstr;
*dskupdated = 0;
while ((c = *arg++)) {
if (c == ' ' || c == '\t' || c == '\n')
continue;
for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
ep = p;
if (*p)
*p++ = 0;
if (c == '-') {
while ((c = *arg++)) {
if (c == 'P') {
if (*(uint8_t *)PTOV(0x496) & 0x10) {
cp = "yes";
} else {
opts |= OPT_SET(RBX_DUAL) |
OPT_SET(RBX_SERIAL);
cp = "no";
}
printf("Keyboard: %s\n", cp);
continue;
} else if (c == 'S') {
j = 0;
while ((unsigned int)(i = *arg++ - '0')
<= 9)
j = j * 10 + i;
if (j > 0 && i == -'0') {
comspeed = j;
break;
}
/*
* Fall through to error below
* ('S' not in optstr[]).
*/
}
for (i = 0; c != optstr[i]; i++)
if (i == NOPT - 1)
return (-1);
opts ^= OPT_SET(flags[i]);
}
ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
if (ioctrl & IO_SERIAL) {
if (sio_init(115200 / comspeed) != 0)
ioctrl &= ~IO_SERIAL;
}
} else {
for (q = arg--; *q && *q != '('; q++);
if (*q) {
drv = -1;
if (arg[1] == ':') {
drv = *arg - '0';
if (drv > 9)
return (-1);
arg += 2;
}
if (q - arg != 2)
return (-1);
for (i = 0; arg[0] != dev_nm[i][0] ||
arg[1] != dev_nm[i][1]; i++)
if (i == NDEV - 1)
return (-1);
dsk.type = i;
arg += 3;
dsk.unit = *arg - '0';
if (arg[1] != 'p' || dsk.unit > 9)
return (-1);
arg += 2;
dsk.part = *arg - '0';
if (dsk.part < 1 || dsk.part > 9)
return (-1);
arg++;
if (arg[0] != ')')
return (-1);
arg++;
if (drv == -1)
drv = dsk.unit;
dsk.drive = (dsk.type <= TYPE_MAXHARD
? DRV_HARD : 0) + drv;
*dskupdated = 1;
}
if ((i = ep - arg)) {
if ((size_t)i >= sizeof(kname))
return (-1);
memcpy(kname, arg, i + 1);
}
}
arg = p;
}
return (0);
}

364
stand/libsa/cd9660read.c Normal file
View file

@ -0,0 +1,364 @@
/*
* Copyright (C) 1996 Wolfgang Solfrank.
* Copyright (C) 1996 TooLs GmbH.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Originally derived from libsa/cd9660.c: */
/* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <fs/cd9660/iso.h>
#include <fs/cd9660/cd9660_rrip.h>
static uint64_t cd9660_lookup(const char *);
static ssize_t cd9660_fsread(uint64_t, void *, size_t);
#define SUSP_CONTINUATION "CE"
#define SUSP_PRESENT "SP"
#define SUSP_STOP "ST"
#define SUSP_EXTREF "ER"
#define RRIP_NAME "NM"
typedef struct {
ISO_SUSP_HEADER h;
u_char signature [ISODCL ( 5, 6)];
u_char len_skp [ISODCL ( 7, 7)]; /* 711 */
} ISO_SUSP_PRESENT;
static int
read_iso_block(void *buffer, daddr_t blkno)
{
return (drvread(&dsk, buffer, blkno * 4, 4));
}
static ISO_SUSP_HEADER *
susp_lookup_record(const char *identifier, struct iso_directory_record *dp,
int lenskip)
{
static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
ISO_SUSP_HEADER *sh;
ISO_RRIP_CONT *shc;
char *p, *end;
int error;
p = dp->name + isonum_711(dp->name_len) + lenskip;
/* Names of even length have a padding byte after the name. */
if ((isonum_711(dp->name_len) & 1) == 0)
p++;
end = (char *)dp + isonum_711(dp->length);
while (p + 3 < end) {
sh = (ISO_SUSP_HEADER *)p;
if (bcmp(sh->type, identifier, 2) == 0)
return (sh);
if (bcmp(sh->type, SUSP_STOP, 2) == 0)
return (NULL);
if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
shc = (ISO_RRIP_CONT *)sh;
error = read_iso_block(susp_buffer,
isonum_733(shc->location));
/* Bail if it fails. */
if (error != 0)
return (NULL);
p = susp_buffer + isonum_733(shc->offset);
end = p + isonum_733(shc->length);
} else {
/* Ignore this record and skip to the next. */
p += isonum_711(sh->length);
/* Avoid infinite loops with corrupted file systems */
if (isonum_711(sh->length) == 0)
return (NULL);
}
}
return (NULL);
}
static const char *
rrip_lookup_name(struct iso_directory_record *dp, int lenskip, size_t *len)
{
ISO_RRIP_ALTNAME *p;
if (len == NULL)
return (NULL);
p = (ISO_RRIP_ALTNAME *)susp_lookup_record(RRIP_NAME, dp, lenskip);
if (p == NULL)
return (NULL);
switch (*p->flags) {
case ISO_SUSP_CFLAG_CURRENT:
*len = 1;
return (".");
case ISO_SUSP_CFLAG_PARENT:
*len = 2;
return ("..");
case 0:
*len = isonum_711(p->h.length) - 5;
return ((char *)p + 5);
default:
/*
* We don't handle hostnames or continued names as they are
* too hard, so just bail and use the default name.
*/
return (NULL);
}
}
static int
rrip_check(struct iso_directory_record *dp, int *lenskip)
{
ISO_SUSP_PRESENT *sp;
ISO_RRIP_EXTREF *er;
char *p;
/* First, see if we can find a SP field. */
p = dp->name + isonum_711(dp->name_len);
if (p > (char *)dp + isonum_711(dp->length)) {
return (0);
}
sp = (ISO_SUSP_PRESENT *)p;
if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0) {
return (0);
}
if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT)) {
return (0);
}
if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef) {
return (0);
}
*lenskip = isonum_711(sp->len_skp);
/*
* Now look for an ER field. If RRIP is present, then there must
* be at least one of these. It would be more pedantic to walk
* through the list of fields looking for a Rock Ridge ER field.
*/
er = (ISO_RRIP_EXTREF *)susp_lookup_record(SUSP_EXTREF, dp, 0);
if (er == NULL) {
return (0);
}
return (1);
}
static int
dirmatch(const char *path, struct iso_directory_record *dp, int use_rrip,
int lenskip)
{
size_t len;
const char *cp = NULL, *name = NULL;
int i, icase;
if (use_rrip)
cp = rrip_lookup_name(dp, lenskip, &len);
else
cp = NULL;
if (cp == NULL) {
len = isonum_711(dp->name_len);
cp = dp->name;
icase = 1;
} else
icase = 0;
name = cp;
for (i = len; --i >= 0; path++, cp++) {
if (!*path || *path == '/')
break;
if (*path == *cp)
continue;
if (!icase && toupper(*path) == *cp)
continue;
return 0;
}
if (*path && *path != '/') {
return 0;
}
/*
* Allow stripping of trailing dots and the version number.
* Note that this will find the first instead of the last version
* of a file.
*/
if (i >= 0 && (*cp == ';' || *cp == '.')) {
/* This is to prevent matching of numeric extensions */
if (*cp == '.' && cp[1] != ';') {
return 0;
}
while (--i >= 0)
if (*++cp != ';' && (*cp < '0' || *cp > '9')) {
return 0;
}
}
return 1;
}
static uint64_t
cd9660_lookup(const char *path)
{
static char blkbuf[ISO_DEFAULT_BLOCK_SIZE];
struct iso_primary_descriptor *vd;
struct iso_directory_record rec;
struct iso_directory_record *dp = NULL;
size_t dsize, off;
daddr_t bno, boff;
int rc, first, use_rrip, lenskip;
uint64_t cookie;
for (bno = 16;; bno++) {
rc = read_iso_block(blkbuf, bno);
vd = (struct iso_primary_descriptor *)blkbuf;
if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
return (0);
if (isonum_711(vd->type) == ISO_VD_END)
return (0);
if (isonum_711(vd->type) == ISO_VD_PRIMARY)
break;
}
rec = *(struct iso_directory_record *) vd->root_directory_record;
if (*path == '/') path++; /* eat leading '/' */
first = 1;
use_rrip = 0;
while (*path) {
bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
dsize = isonum_733(rec.size);
off = 0;
boff = 0;
while (off < dsize) {
if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
rc = read_iso_block(blkbuf, bno + boff);
if (rc) {
return (0);
}
boff++;
dp = (struct iso_directory_record *) blkbuf;
}
if (isonum_711(dp->length) == 0) {
/* skip to next block, if any */
off = boff * ISO_DEFAULT_BLOCK_SIZE;
continue;
}
/* See if RRIP is in use. */
if (first)
use_rrip = rrip_check(dp, &lenskip);
if (dirmatch(path, dp, use_rrip,
first ? 0 : lenskip)) {
first = 0;
break;
} else
first = 0;
dp = (struct iso_directory_record *)
((char *) dp + isonum_711(dp->length));
/* If the new block has zero length, it is padding. */
if (isonum_711(dp->length) == 0) {
/* Skip to next block, if any. */
off = boff * ISO_DEFAULT_BLOCK_SIZE;
continue;
}
off += isonum_711(dp->length);
}
if (off >= dsize) {
return (0);
}
rec = *dp;
while (*path && *path != '/') /* look for next component */
path++;
if (*path) path++; /* skip '/' */
}
if ((isonum_711(rec.flags) & 2) != 0) {
return (0);
}
cookie = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
cookie = (cookie << 32) | isonum_733(rec.size);
return (cookie);
}
static ssize_t
cd9660_fsread(uint64_t cookie, void *buf, size_t nbytes)
{
static char blkbuf[ISO_DEFAULT_BLOCK_SIZE];
static daddr_t curstart = 0, curblk = 0;
daddr_t blk, blk_off;
off_t byte_off;
size_t size, remaining, n;
char *s;
size = cookie & 0xffffffff;
blk = (cookie >> 32) & 0xffffffff;
/* Make sure we're looking at the right file. */
if (((blk << 32) | size) != cookie) {
return (-1);
}
if (blk != curstart) {
curstart = blk;
fs_off = 0;
}
size -= fs_off;
if (size < nbytes) {
nbytes = size;
}
remaining = nbytes;
s = buf;
while (remaining > 0) {
blk_off = fs_off >> ISO_DEFAULT_BLOCK_SHIFT;
byte_off = fs_off & (ISO_DEFAULT_BLOCK_SIZE - 1);
if (curblk != curstart + blk_off) {
curblk = curstart + blk_off;
read_iso_block(blkbuf, curblk);
}
if (remaining < ISO_DEFAULT_BLOCK_SIZE - byte_off) {
n = remaining;
} else {
n = ISO_DEFAULT_BLOCK_SIZE - byte_off;
}
memcpy(s, blkbuf + byte_off, n);
remaining -= n;
s += n;
fs_off += n;
}
return (nbytes);
}

View file

@ -95,7 +95,8 @@ struct iso_primary_descriptor {
char application_data [ISODCL (884, 1395)];
char unused5 [ISODCL (1396, 2048)];
};
#define ISO_DEFAULT_BLOCK_SIZE 2048
#define ISO_DEFAULT_BLOCK_SHIFT 11
#define ISO_DEFAULT_BLOCK_SIZE (1 << ISO_DEFAULT_BLOCK_SHIFT)
/*
* Used by Microsoft Joliet extension to ISO9660. Almost the same