Index: release/amd64/mkisoimages.sh =================================================================== --- release/amd64/mkisoimages.sh +++ release/amd64/mkisoimages.sh @@ -58,3 +58,8 @@ makefs -t cd9660 $bootable -o rockridge -o label="$LABEL" -o publisher="$publisher" "$NAME" "$@" rm -f "$1/etc/fstab" rm -f efiboot.img + +# Drop our hybrid boot magic into the 32K of blank space at the front of the image. +mkimg -s mbr -b $4/boot/mbr -p freebsd:-"mkimg -s bsd -b $4/boot/isoboot -p-" -o hybrid.img +dd if=hybrid.img of=$NAME conv=notrunc +rm -f hybrid.img Index: stand/common/disk.c =================================================================== --- stand/common/disk.c +++ stand/common/disk.c @@ -270,6 +270,9 @@ dev->d_offset = part.start; od->entrysize = part.end - part.start + 1; } + } else if (ptable_gettype(od->table) == PTABLE_ISO9660) { + dev->d_offset = 0; + od->entrysize = mediasize; } else if (slice >= 0) { /* Try to get information about partition */ if (slice == 0) Index: stand/common/part.h =================================================================== --- stand/common/part.h +++ stand/common/part.h @@ -36,7 +36,8 @@ PTABLE_BSD, PTABLE_MBR, PTABLE_GPT, - PTABLE_VTOC8 + PTABLE_VTOC8, + PTABLE_ISO9660 }; enum partition_type { @@ -52,6 +53,7 @@ PART_LINUX, PART_LINUX_SWAP, PART_DOS, + PART_ISO9660 }; struct ptable_entry { Index: stand/common/part.c =================================================================== --- stand/common/part.c +++ stand/common/part.c @@ -37,6 +37,8 @@ #include #include +#include + #include #include #include @@ -97,6 +99,7 @@ { PART_LINUX, "Linux" }, { PART_LINUX_SWAP, "Linux swap" }, { PART_DOS, "DOS/Windows" }, + { PART_ISO9660, "ISO9660" }, }; const char * @@ -603,6 +606,45 @@ } #endif /* LOADER_VTOC8_SUPPORT */ +#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize) + +static struct ptable * +ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread) +{ + uint8_t *buf; + struct iso_primary_descriptor *vd; + struct pentry *entry; + + buf = malloc(table->sectorsize); + if (buf == NULL) + return (table); + + if (dread(dev, buf, 1, cdb2devb(16)) != 0) { + DEBUG("read failed"); + ptable_close(table); + table = NULL; + goto out; + } + vd = (struct iso_primary_descriptor *)buf; + if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) + goto out; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + goto out; + entry->part.start = 0; + entry->part.end = table->sectors; + entry->part.type = PART_ISO9660; + entry->part.index = 0; + STAILQ_INSERT_TAIL(&table->entries, entry, entry); + + table->type = PTABLE_ISO9660; + +out: + free(buf); + return (table); +} + struct ptable * ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, diskread_t *dread) @@ -634,6 +676,11 @@ table->type = PTABLE_NONE; STAILQ_INIT(&table->entries); + if (ptable_iso9660read(table, dev, dread) != NULL) { + if (table->type == PTABLE_ISO9660) + goto out; + } + #ifdef LOADER_VTOC8_SUPPORT if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) { if (ptable_vtoc8read(table, dev, dread) == NULL) { Index: stand/i386/boot2/Makefile =================================================================== --- stand/i386/boot2/Makefile +++ stand/i386/boot2/Makefile @@ -2,7 +2,7 @@ .include -FILES= boot boot1 boot2 +FILES= boot boot1 boot2 isoboot isoboot2 NM?= nm @@ -45,13 +45,18 @@ CFLAGS.clang+= -Oz ${CLANG_OPT_SMALL} +CFLAGS.isoboot2.c+= -I${BOOTSRC}/i386/common + LD_FLAGS+=${LD_FLAGS_BIN} -CLEANFILES+= boot +CLEANFILES+= boot isoboot boot: boot1 boot2 cat boot1 boot2 > boot +isoboot: boot1 isoboot2 + cat boot1 isoboot2 > isoboot + CLEANFILES+= boot1 boot1.out boot1.o boot1: boot1.out @@ -62,6 +67,8 @@ CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \ boot2.h sio.o +CLEANFILES+= isoboot2 isoboot2.ld isoboot2.ldr isoboot2.bin isoboot2.out \ + isoboot2.o BOOT2SIZE= 7680 @@ -83,8 +90,6 @@ boot2.out: ${BTXCRT} boot2.o sio.o ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} -SRCS= boot2.c boot2.h - boot2.h: boot1.out ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \ { x = $$1 - ORG1; \ @@ -92,6 +97,26 @@ ORG1=`printf "%d" ${ORG1}` \ REL1=`printf "%d" ${REL1}` > ${.TARGET} +isoboot2: isoboot2.ld + @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \ + echo "$$x bytes available"; test $$x -ge 0 + ${DD} if=${.ALLSRC} of=${.TARGET} obs=${BOOT2SIZE} conv=osync + +isoboot2.ld: isoboot2.ldr isoboot2.bin ${BTXKERN} + btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l isoboot2.ldr \ + -o ${.TARGET} -P 1 isoboot2.bin + +isoboot2.ldr: + ${DD} if=/dev/zero of=${.TARGET} bs=512 count=1 + +isoboot2.bin: isoboot2.out + ${OBJCOPY} -S -O binary isoboot2.out ${.TARGET} + +isoboot2.out: ${BTXCRT} isoboot2.o sio.o + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} + +SRCS= boot2.c isoboot2.c boot2.h + .include # XXX: clang integrated-as doesn't grok .codeNN directives yet Index: stand/i386/boot2/isoboot2.c =================================================================== --- /dev/null +++ stand/i386/boot2/isoboot2.c @@ -0,0 +1,901 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +#include "boot2.h" +#include "lib.h" +#include "paths.h" +#include "rbx.h" + +#include "bootargs.h" + +/* Define to 0 to omit serial support */ +#ifndef SERIAL +#define SERIAL 1 +#endif + +#define IO_KEYBOARD 1 +#define IO_SERIAL 2 + +#if SERIAL +#define DO_KBD (ioctrl & IO_KEYBOARD) +#define DO_SIO (ioctrl & IO_SERIAL) +#else +#define DO_KBD (1) +#define DO_SIO (0) +#endif + +#define SECOND 18 /* Circa that many ticks in a second. */ + +#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 +}; + +static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; +static const unsigned char dev_maj[NDEV] = {30, 4, 2}; + +static struct dsk { + unsigned drive; + unsigned type; + unsigned unit; + uint8_t slice; + uint8_t part; + unsigned start; + int init; +} dsk; +static char cmd[512], cmddup[512], knamebuf[1024]; +static const char *kname; +uint32_t opts; +static struct bootinfo bootinfo; +static off_t fs_off = 0; +static bool ls; +#if SERIAL +static int comspeed = SIOSPD; +static uint8_t ioctrl = IO_KEYBOARD; +#endif + +#define VBLKSHIFT 12 +#define VBLKSIZE (1 << VBLKSHIFT) +#define VBLKMASK (VBLKSIZE - 1) + +struct dmadat { + char blkbuf[VBLKSIZE]; /* filesystem blocks */ + char indbuf[VBLKSIZE]; /* indir blocks */ +}; +static struct dmadat *dmadat; + +int main(void); +void exit(int); +static void load(void); +static int parse(void); +static void printf(const char *,...); +static void putchar(int); +static int drvread(void *, unsigned, unsigned); +static int keyhit(unsigned); +static int xputc(int); +static int xgetc(int); +static inline int getc(int); + +static int lookup(const char *, struct iso_directory_record *); +static ssize_t fsread(struct iso_directory_record *, void *, size_t); + +static void memcpy(void *, const void *, int); +static void +memcpy(void *dst, const void *src, int len) +{ + const char *s; + char *d; + + s = src; + d = dst; + + while (len--) + *d++ = *s++; +} + +static inline int +strcmp(const char *s1, const char *s2) +{ + + for (; *s1 == *s2 && *s1; s1++, s2++); + return ((unsigned char)*s1 - (unsigned char)*s2); +} + +static inline int +bcmp(const uint8_t *s1, const uint8_t *s2, size_t nbytes) +{ + + for (; nbytes > 1 && *s1 == *s2; s1++, s2++, nbytes--); + return (*s1 - *s2); +} + +static inline int +toupper(int c) +{ + + return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; +} + +static int +xfsread(struct iso_directory_record *rec, void *buf, size_t nbyte) +{ + + if ((size_t)fsread(rec, buf, nbyte) != nbyte) { + printf("Invalid %s\n", "format"); + return (-1); + } + return (0); +} + +static inline void +getstr(void) +{ + char *s; + int c; + + s = cmd; + for (;;) { + switch (c = xgetc(0)) { + case 0: + break; + case '\177': + case '\b': + if (s > cmd) { + s--; + printf("\b \b"); + } + break; + case '\n': + case '\r': + *s = 0; + return; + default: + if (s - cmd < sizeof(cmd) - 1) + *s++ = c; + putchar(c); + } + } +} + +static inline void +putc(int c) +{ + + v86.addr = 0x10; + v86.eax = 0xe00 | (c & 0xff); + v86.ebx = 0x7; + v86int(); +} + +int +main(void) +{ + uint8_t autoboot; + size_t nbyte; + struct iso_directory_record rec; + + dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + 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.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; + bootinfo.bi_version = BOOTINFO_VERSION; + bootinfo.bi_size = sizeof(bootinfo); + + /* Process configuration file */ + + autoboot = 1; + + if (lookup(PATH_CONFIG, &rec) == 0 || + lookup(PATH_DOTCONFIG, &rec) == 0) { + nbyte = fsread(&rec, cmd, sizeof(cmd) - 1); + cmd[nbyte] = '\0'; + } + + if (*cmd) { + memcpy(cmddup, cmd, sizeof(cmd)); + if (parse()) + autoboot = 0; + if (!OPT_CHECK(RBX_QUIET)) + printf("%s: %s", PATH_CONFIG, cmddup); + /* Do not process this command twice */ + *cmd = 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) { + kname = PATH_LOADER; + if (autoboot && !keyhit(3*SECOND)) { + load(); + kname = PATH_KERNEL; + } + } + + /* Present the user with the boot2 prompt. */ + + for (;;) { + if (!autoboot || !OPT_CHECK(RBX_QUIET)) + printf("\nFreeBSD/x86 boot\n" + "Default: %u:%s(%u,%c)%s\n" + "boot: ", + dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, + 'a' + dsk.part, kname); + if (DO_SIO) + sio_flush(); + if (!autoboot || keyhit(3*SECOND)) + getstr(); + else if (!autoboot || !OPT_CHECK(RBX_QUIET)) + putchar('\n'); + autoboot = 0; + if (parse()) + putchar('\a'); + else + load(); + } +} + +/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ +void +exit(int x) +{ + +} + +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; + uint32_t addr; + int k; + uint8_t i, j; + struct iso_directory_record rec; + + if (lookup(kname, &rec)) { + if (!ls) + printf("No %s\n", kname); + return; + } + if (xfsread(&rec, &hdr, sizeof(hdr))) + return; + + if (N_GETMAGIC(hdr.ex) == ZMAGIC) { + addr = hdr.ex.a_entry & 0xffffff; + p = PTOV(addr); + fs_off = PAGE_SIZE; + if (xfsread(&rec, p, hdr.ex.a_text)) + return; + p += roundup2(hdr.ex.a_text, PAGE_SIZE); + if (xfsread(&rec, p, hdr.ex.a_data)) + return; + } else if (IS_ELF(hdr.eh)) { + fs_off = hdr.eh.e_phoff; + for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) { + if (xfsread(&rec, 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(&rec, 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(&rec, &es, sizeof(es))) + return; + for (i = 0; i < 2; i++) { + *(Elf32_Word *)p = es[i].sh_size; + p += sizeof(es[i].sh_size); + fs_off = es[i].sh_offset; + if (xfsread(&rec, p, es[i].sh_size)) + return; + p += es[i].sh_size; + } + } + addr = hdr.eh.e_entry & 0xffffff; + bootinfo.bi_esymtab = VTOP(p); + } else { + printf("Invalid %s\n", "format"); + return; + } + + 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], dsk.slice, dsk.unit, dsk.part), + 0, 0, 0, VTOP(&bootinfo)); +} + +static int +parse() +{ + char *arg, *ep, *p; + const char *cp; + int c, i, j; + size_t k; + + arg = cmd; + + 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; +#if SERIAL + } else if (c == 'S') { + j = 0; + while ((u_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[]). + */ +#endif + } + for (i = 0; c != optstr[i]; i++) + if (i == NOPT - 1) + return (-1); + opts ^= OPT_SET(flags[i]); + } +#if SERIAL + ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : + OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; + if (DO_SIO) { + if (sio_init(115200 / comspeed) != 0) + ioctrl &= ~IO_SERIAL; + } +#endif + } else { + k = ep - arg; + if (k > 0) { + if (k >= sizeof(knamebuf)) + return (-1); + memcpy(knamebuf, arg, k + 1); + kname = knamebuf; + } + } + arg = p; + } + return (0); +} + +static void +printf(const char *fmt,...) +{ + va_list ap; + static char buf[10]; + char *s; + unsigned u; + int c; + + va_start(ap, fmt); + while ((c = *fmt++)) { + if (c == '%') { + c = *fmt++; + switch (c) { + case 'c': + putchar(va_arg(ap, int)); + continue; + case 's': + for (s = va_arg(ap, char *); *s; s++) + putchar(*s); + continue; + case 'u': + u = va_arg(ap, unsigned); + s = buf; + do + *s++ = '0' + u % 10U; + while (u /= 10U); + while (--s >= buf) + putchar(*s); + continue; + } + } + putchar(c); + } + va_end(ap); + return; +} + +static void +putchar(int c) +{ + + if (c == '\n') + xputc('\r'); + xputc(c); +} + +static int +drvread(void *buf, unsigned lba, unsigned nblk) +{ + static unsigned c = 0x2d5c7c2f; + + if (!OPT_CHECK(RBX_QUIET)) { + xputc(c = c << 8 | c >> 24); + xputc('\b'); + } + v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; + v86.addr = XREADORG; /* call to xread in boot1 */ + v86.es = VTOPSEG(buf); + v86.eax = lba; + v86.ebx = VTOPOFF(buf); + v86.ecx = lba >> 16; + v86.edx = nblk << 8 | dsk.drive; + v86int(); + v86.ctl = V86_FLAGS; + if (V86_CY(v86.efl)) { + printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); + return (-1); + } + return (0); +} + +static int +keyhit(unsigned ticks) +{ + uint32_t t0, t1; + + if (OPT_CHECK(RBX_NOINTR)) + return (0); + t0 = 0; + for (;;) { + if (xgetc(1)) + return (1); + t1 = *(uint32_t *)PTOV(0x46c); + if (!t0) + t0 = t1; + if ((uint32_t)(t1 - t0) >= ticks) + return (0); + } +} + +static int +xputc(int c) +{ + + if (DO_KBD) + putc(c); + if (DO_SIO) + sio_putc(c); + return (c); +} + +static int +getc(int fn) +{ + + v86.addr = 0x16; + v86.eax = fn << 8; + v86int(); + return (fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl)); +} + +static int +xgetc(int fn) +{ + + if (OPT_CHECK(RBX_NOINTR)) + return (0); + for (;;) { + if (DO_KBD && getc(1)) + return (fn ? 1 : getc(0)); + if (DO_SIO && sio_ischar()) + return (fn ? 1 : sio_getc()); + if (fn) + return (0); + } +} + +#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(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 int +lookup(const char *path, struct iso_directory_record *orec) +{ + 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; + + 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 (-1); + if (isonum_711(vd->type) == ISO_VD_END) + return (-1); + 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 (-1); + } + 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 (-1); + } + + rec = *dp; + while (*path && *path != '/') /* look for next component */ + path++; + if (*path) path++; /* skip '/' */ + } + + if ((isonum_711(rec.flags) & 2) != 0) { + return (-1); + } + + *orec = rec; + + return (0); +} + +static ssize_t +fsread(struct iso_directory_record *rec, 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; + + blk = isonum_733(rec->extent) + isonum_711(rec->ext_attr_length); + size = isonum_733(rec->size); + + 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); +} Index: sys/fs/cd9660/iso.h =================================================================== --- sys/fs/cd9660/iso.h +++ sys/fs/cd9660/iso.h @@ -95,7 +95,8 @@ 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