Author: benno
Date: Thu Apr  5 19:40:46 2018
New Revision: 332084
URL: https://svnweb.freebsd.org/changeset/base/332084

Log:
  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

Added:
  head/stand/i386/isoboot/
  head/stand/i386/isoboot/Makefile
     - copied, changed from r332081, head/stand/i386/gptboot/Makefile
  head/stand/i386/isoboot/isoboot.8   (contents, props changed)
  head/stand/i386/isoboot/isoboot.c
     - copied, changed from r332083, head/stand/i386/gptboot/gptboot.c
  head/stand/libsa/cd9660read.c   (contents, props changed)
Modified:
  head/stand/i386/Makefile
  head/sys/fs/cd9660/iso.h

Modified: head/stand/i386/Makefile
==============================================================================
--- head/stand/i386/Makefile    Thu Apr  5 19:29:22 2018        (r332083)
+++ head/stand/i386/Makefile    Thu Apr  5 19:40:46 2018        (r332084)
@@ -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
 

Copied and modified: head/stand/i386/isoboot/Makefile (from r332081, 
head/stand/i386/gptboot/Makefile)
==============================================================================
--- head/stand/i386/gptboot/Makefile    Thu Apr  5 18:19:48 2018        
(r332081, copy source)
+++ head/stand/i386/isoboot/Makefile    Thu Apr  5 19:40:46 2018        
(r332084)
@@ -4,10 +4,11 @@ HAVE_GELI=            yes
 
 .include <bsd.init.mk>
 
-.PATH:         ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC}
+.PATH:         ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \
+               ${BOOTSRC}/i386/common ${SASRC}
 
-FILES=         gptboot
-MAN=           gptboot.8
+FILES=         isoboot
+MAN=           isoboot.8
 
 NM?=           nm
 
@@ -19,15 +20,10 @@ REL1=       0x700
 ORG1=  0x7c00
 ORG2=  0x0
 
-# Decide level of UFS support.
-GPTBOOT_UFS?=  UFS1_AND_UFS2
-#GPTBOOT_UFS?= UFS2_ONLY
-#GPTBOOT_UFS?= UFS1_ONLY
+ISOBOOTSIZE?=  30720
 
-CFLAGS+=-DBOOTPROG=\"gptboot\" \
+CFLAGS+=-DBOOTPROG=\"isoboot\" \
        -O1 \
-       -DGPT \
-       -D${GPTBOOT_UFS} \
        -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
        -DSIOFMT=${B2SIOFMT} \
        -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
@@ -40,14 +36,17 @@ CFLAGS+=-DBOOTPROG=\"gptboot\" \
        -Winline -Wno-pointer-sign
 
 CFLAGS.gcc+=   --param max-inline-insns-single=100
+CFLAGS.clang+=  -Oz ${CLANG_OPT_SMALL}
 
 LD_FLAGS+=${LD_FLAGS_BIN}
 
-CLEANFILES+=   gptboot
+CLEANFILES+=   isoboot
 
-gptboot: gptldr.bin gptboot.bin ${BTXKERN}
+isoboot: gptldr.bin isoboot.bin ${BTXKERN}
        btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
-           -o ${.TARGET} gptboot.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
 
@@ -57,16 +56,13 @@ gptldr.bin: gptldr.out
 gptldr.out: gptldr.o
        ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
 
-CLEANFILES+=   gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \
+CLEANFILES+=   isoboot.bin isoboot.out isoboot.o sio.o crc32.o drv.o \
                cons.o ${OPENCRYPTO_XTS}
 
-gptboot.bin: gptboot.out
-       ${OBJCOPY} -S -O binary gptboot.out ${.TARGET}
+isoboot.bin: isoboot.out
+       ${OBJCOPY} -S -O binary isoboot.out ${.TARGET}
 
-gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o ${OPENCRYPTO_XTS}
+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>
-
-# XXX: clang integrated-as doesn't grok .codeNN directives yet
-CFLAGS.gptldr.S=       ${CLANG_NO_IAS}

Added: head/stand/i386/isoboot/isoboot.8
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/stand/i386/isoboot/isoboot.8   Thu Apr  5 19:40:46 2018        
(r332084)
@@ -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 be...@freebsd.org .

Copied and modified: head/stand/i386/isoboot/isoboot.c (from r332083, 
head/stand/i386/gptboot/gptboot.c)
==============================================================================
--- head/stand/i386/gptboot/gptboot.c   Thu Apr  5 19:29:22 2018        
(r332083, copy source)
+++ head/stand/i386/isoboot/isoboot.c   Thu Apr  5 19:40:46 2018        
(r332084)
@@ -58,7 +58,6 @@ __FBSDID("$FreeBSD$");
 
 extern uint32_t _end;
 
-static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
 static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
 static const unsigned char flags[NOPT] = {
        RBX_DUAL,
@@ -85,9 +84,6 @@ static struct dsk dsk;
 static char kname[1024];
 static int comspeed = SIOSPD;
 static struct bootinfo bootinfo;
-#ifdef LOADER_GELI_SUPPORT
-static struct geli_boot_args geliargs;
-#endif
 
 static vm_offset_t     high_heap_base;
 static uint32_t                bios_basemem, bios_extmem, high_heap_size;
@@ -102,27 +98,21 @@ static struct bios_smap smap;
 static char *heap_next;
 static char *heap_end;
 
+int main(void);
+
 static void load(void);
 static int parse_cmds(char *, int *);
-static int dskread(void *, daddr_t, unsigned);
-#ifdef LOADER_GELI_SUPPORT
-static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf,
-       size_t bytes);
-#endif
 
-#include "ufsread.c"
-#include "gpt.c"
-#ifdef LOADER_GELI_SUPPORT
-#include "geliboot.c"
-static char gelipw[GELI_PW_MAXLEN];
-static struct keybuf *gelibuf;
-#endif
+static uint8_t ls, dsk_meta;
+static uint32_t fs_off;
 
+#include "cd9660read.c"
+
 static inline int
-xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
+xfsread(uint64_t inode, void *buf, size_t nbyte)
 {
 
-       if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
+       if ((size_t)cd9660_fsread(inode, buf, nbyte) != nbyte) {
                printf("Invalid %s\n", "format");
                return (-1);
        }
@@ -221,52 +211,22 @@ bios_getmem(void)
        }
 }
 
-static int
-gptinit(void)
-{
-
-       if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) {
-               printf("%s: unable to load GPT\n", BOOTPROG);
-               return (-1);
-       }
-       if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) {
-               printf("%s: no UFS partition was found\n", BOOTPROG);
-               return (-1);
-       }
-#ifdef LOADER_GELI_SUPPORT
-       if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end -
-           gpttable[curent].ent_lba_start)) == 0) {
-               if (geli_havekey(&dsk) != 0 && geli_passphrase(gelipw,
-                   dsk.unit, 'p', curent + 1, &dsk) != 0) {
-                       printf("%s: unable to decrypt GELI key\n", BOOTPROG);
-                       return (-1);
-               }
-       }
-#endif
-
-       dsk_meta = 0;
-       return (0);
-}
-
-int main(void);
-
 int
 main(void)
 {
        char cmd[512], cmdtmp[512];
        ssize_t sz;
        int autoboot, dskupdated;
-       ufs_ino_t ino;
+       uint64_t ino;
 
-       dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
-
        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 *)dmadat + sizeof(*dmadat);
+               heap_next = (char *)
+                   (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
                heap_end = (char *)PTOV(bios_basemem);
        }
        setheap(heap_next, heap_end);
@@ -285,30 +245,20 @@ main(void)
        bootinfo.bi_memsizes_valid++;
        bootinfo.bi_bios_dev = dsk.drive;
 
-#ifdef LOADER_GELI_SUPPORT
-       geli_init();
-#endif
-       /* Process configuration file */
-
-       if (gptinit() != 0)
-               return (-1);
-
        autoboot = 1;
        *cmd = '\0';
 
        for (;;) {
                *kname = '\0';
-               if ((ino = lookup(PATH_CONFIG)) ||
-                   (ino = lookup(PATH_DOTCONFIG))) {
-                       sz = fsread(ino, cmd, sizeof(cmd) - 1);
+               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 (dskupdated && gptinit() != 0)
-                               break;
                        if (!OPT_CHECK(RBX_QUIET))
                                printf("%s: %s", PATH_CONFIG, cmd);
                        *cmd = '\0';
@@ -332,9 +282,6 @@ main(void)
                load();
                memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
                load();
-               gptbootfailed(&dsk);
-               if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1)
-                       break;
                dsk_meta = 0;
        }
 
@@ -359,14 +306,12 @@ main(void)
                        putchar('\a');
                        continue;
                }
-               if (dskupdated && gptinit() != 0)
-                       continue;
                load();
        }
        /* NOTREACHED */
 }
 
-/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+/* Needed so btxld can link us properly; do not remove. */
 void
 exit(int x)
 {
@@ -385,11 +330,11 @@ load(void)
        static Elf32_Phdr ep[2];
        static Elf32_Shdr es[2];
        caddr_t p;
-       ufs_ino_t ino;
+       uint64_t ino;
        uint32_t addr, x;
        int fmt, i, j;
 
-       if (!(ino = lookup(kname))) {
+       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],
@@ -470,23 +415,9 @@ load(void)
        bootinfo.bi_esymtab = VTOP(p);
        bootinfo.bi_kernelname = VTOP(kname);
        bootinfo.bi_bios_dev = dsk.drive;
-#ifdef LOADER_GELI_SUPPORT
-       geliargs.size = sizeof(geliargs);
-       explicit_bzero(gelipw, sizeof(gelipw));
-       gelibuf = malloc(sizeof(struct keybuf) +
-           (GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
-       geli_fill_keybuf(gelibuf);
-       geliargs.notapw = '\0';
-       geliargs.keybuf_sentinel = KEYBUF_SENTINEL;
-       geliargs.keybuf = gelibuf;
-#endif
        __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
-           MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
-           KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo)
-#ifdef LOADER_GELI_SUPPORT
-           , geliargs
-#endif
-           );
+           MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.unit, 0),
+           KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo));
 }
 
 static int
@@ -589,59 +520,3 @@ parse_cmds(char *cmdstr, int *dskupdated)
        }
        return (0);
 }
-
-static int
-dskread(void *buf, daddr_t lba, unsigned nblk)
-{
-       int err;
-
-       err = drvread(&dsk, buf, lba + dsk.start, nblk);
-
-#ifdef LOADER_GELI_SUPPORT
-       if (err == 0 && is_geli(&dsk) == 0) {
-               /* Decrypt */
-               if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE))
-                       return (err);
-       }
-#endif
-
-       return (err);
-}
-
-#ifdef LOADER_GELI_SUPPORT
-/*
- * Read function compartible with the ZFS callback, required to keep the GELI
- * Implementation the same for both UFS and ZFS
- */
-static int
-vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes)
-{
-       char *p;
-       daddr_t lba;
-       unsigned int nb;
-       struct dsk *dskp;
-
-       dskp = (struct dsk *)priv;
-
-       if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
-               return (-1);
-
-       p = buf;
-       lba = off / DEV_BSIZE;
-       lba += dskp->start;
-
-       while (bytes > 0) {
-               nb = bytes / DEV_BSIZE;
-               if (nb > VBLKSIZE / DEV_BSIZE)
-                       nb = VBLKSIZE / DEV_BSIZE;
-               if (drvread(dskp, dmadat->blkbuf, lba, nb))
-                       return (-1);
-               memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE);
-               p += nb * DEV_BSIZE;
-               lba += nb;
-               bytes -= nb * DEV_BSIZE;
-       }
-
-       return (0);
-}
-#endif /* LOADER_GELI_SUPPORT */

Added: head/stand/libsa/cd9660read.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/stand/libsa/cd9660read.c       Thu Apr  5 19:40:46 2018        
(r332084)
@@ -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);
+}

Modified: head/sys/fs/cd9660/iso.h
==============================================================================
--- head/sys/fs/cd9660/iso.h    Thu Apr  5 19:29:22 2018        (r332083)
+++ head/sys/fs/cd9660/iso.h    Thu Apr  5 19:40:46 2018        (r332084)
@@ -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
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to