aboutsummaryrefslogtreecommitdiff
path: root/stand/libsa/cd9660read.c
diff options
context:
space:
mode:
authorBenno Rice <benno@FreeBSD.org>2018-04-05 19:40:46 +0000
committerBenno Rice <benno@FreeBSD.org>2018-04-05 19:40:46 +0000
commit7acb51f681a6960ce8dd029ad78fd354bd05b4bd (patch)
treede9ad7b25961af9af0fb54dd0c9a64a506864afd /stand/libsa/cd9660read.c
parent288013f54c32d42cf0437f16de308a1dd088eefe (diff)
Notes
Diffstat (limited to 'stand/libsa/cd9660read.c')
-rw-r--r--stand/libsa/cd9660read.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/stand/libsa/cd9660read.c b/stand/libsa/cd9660read.c
new file mode 100644
index 000000000000..03d29870d2d7
--- /dev/null
+++ b/stand/libsa/cd9660read.c
@@ -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);
+}