138451Smsmith/*	$NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $	*/
238451Smsmith
338451Smsmith/*
438451Smsmith * Copyright (C) 1996 Wolfgang Solfrank.
538451Smsmith * Copyright (C) 1996 TooLs GmbH.
638451Smsmith * All rights reserved.
738451Smsmith *
838451Smsmith * Redistribution and use in source and binary forms, with or without
938451Smsmith * modification, are permitted provided that the following conditions
1038451Smsmith * are met:
1138451Smsmith * 1. Redistributions of source code must retain the above copyright
1238451Smsmith *    notice, this list of conditions and the following disclaimer.
1338451Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1438451Smsmith *    notice, this list of conditions and the following disclaimer in the
1538451Smsmith *    documentation and/or other materials provided with the distribution.
1638451Smsmith * 3. All advertising materials mentioning features or use of this software
1738451Smsmith *    must display the following acknowledgement:
1838451Smsmith *	This product includes software developed by TooLs GmbH.
1938451Smsmith * 4. The name of TooLs GmbH may not be used to endorse or promote products
2038451Smsmith *    derived from this software without specific prior written permission.
2138451Smsmith *
2238451Smsmith * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2338451Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2438451Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2538451Smsmith * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2638451Smsmith * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2738451Smsmith * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2838451Smsmith * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2938451Smsmith * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3038451Smsmith * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3138451Smsmith * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3238451Smsmith */
3338451Smsmith
3484221Sdillon#include <sys/cdefs.h>
3584221Sdillon__FBSDID("$FreeBSD: stable/11/stand/libsa/cd9660.c 346477 2019-04-21 03:43:27Z kevans $");
3684221Sdillon
3738451Smsmith/*
3838451Smsmith * Stand-alone ISO9660 file reading package.
3938451Smsmith *
4038451Smsmith * Note: This doesn't support Rock Ridge extensions, extended attributes,
4138451Smsmith * blocksizes other than 2048 bytes, multi-extent files, etc.
4238451Smsmith */
4338451Smsmith#include <sys/param.h>
4438451Smsmith#include <string.h>
45346477Skevans#include <stdbool.h>
4656222Sobrien#include <sys/dirent.h>
47329175Skevans#include <fs/cd9660/iso.h>
48329175Skevans#include <fs/cd9660/cd9660_rrip.h>
4938451Smsmith
5038451Smsmith#include "stand.h"
5138451Smsmith
5286142Sjhb#define	SUSP_CONTINUATION	"CE"
5386142Sjhb#define	SUSP_PRESENT		"SP"
5486142Sjhb#define	SUSP_STOP		"ST"
5586142Sjhb#define	SUSP_EXTREF		"ER"
5686142Sjhb#define	RRIP_NAME		"NM"
5786142Sjhb
5886142Sjhbtypedef struct {
5986142Sjhb	ISO_SUSP_HEADER		h;
6086142Sjhb	u_char signature	[ISODCL (  5,    6)];
6186142Sjhb	u_char len_skp		[ISODCL (  7,    7)]; /* 711 */
6286142Sjhb} ISO_SUSP_PRESENT;
6386142Sjhb
6486137Sjhbstatic int	buf_read_file(struct open_file *f, char **buf_p,
6586137Sjhb		    size_t *size_p);
6639468Smsmithstatic int	cd9660_open(const char *path, struct open_file *f);
6738451Smsmithstatic int	cd9660_close(struct open_file *f);
6886137Sjhbstatic int	cd9660_read(struct open_file *f, void *buf, size_t size,
6986137Sjhb		    size_t *resid);
7038451Smsmithstatic off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
7138451Smsmithstatic int	cd9660_stat(struct open_file *f, struct stat *sb);
7259766Sjlemonstatic int	cd9660_readdir(struct open_file *f, struct dirent *d);
7386142Sjhbstatic int	dirmatch(struct open_file *f, const char *path,
7486142Sjhb		    struct iso_directory_record *dp, int use_rrip, int lenskip);
7586142Sjhbstatic int	rrip_check(struct open_file *f, struct iso_directory_record *dp,
7686142Sjhb		    int *lenskip);
7786142Sjhbstatic char	*rrip_lookup_name(struct open_file *f,
7886142Sjhb		    struct iso_directory_record *dp, int lenskip, size_t *len);
7986142Sjhbstatic ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
8086142Sjhb		    const char *identifier, struct iso_directory_record *dp,
8186142Sjhb		    int lenskip);
8238451Smsmith
8338451Smsmithstruct fs_ops cd9660_fsops = {
8459766Sjlemon	"cd9660",
8559766Sjlemon	cd9660_open,
8659766Sjlemon	cd9660_close,
8759766Sjlemon	cd9660_read,
88332141Skevans	null_write,
8959766Sjlemon	cd9660_seek,
9059766Sjlemon	cd9660_stat,
9159766Sjlemon	cd9660_readdir
9238451Smsmith};
9338451Smsmith
9486158Sjhb#define	F_ISDIR		0x0001		/* Directory */
9586158Sjhb#define	F_ROOTDIR	0x0002		/* Root directory */
9686158Sjhb#define	F_RR		0x0004		/* Rock Ridge on this volume */
9786158Sjhb
9838451Smsmithstruct file {
9986158Sjhb	int 		f_flags;	/* file flags */
10059766Sjlemon	off_t 		f_off;		/* Current offset within file */
10159766Sjlemon	daddr_t 	f_bno;		/* Starting block number */
10259766Sjlemon	off_t 		f_size;		/* Size of file */
10359766Sjlemon	daddr_t		f_buf_blkno;	/* block number of data block */
10459766Sjlemon	char		*f_buf;		/* buffer for data block */
10586158Sjhb	int		f_susp_skip;	/* len_skip for SUSP records */
10638451Smsmith};
10738451Smsmith
10838451Smsmithstruct ptable_ent {
10938451Smsmith	char namlen	[ISODCL( 1, 1)];	/* 711 */
11038451Smsmith	char extlen	[ISODCL( 2, 2)];	/* 711 */
11138451Smsmith	char block	[ISODCL( 3, 6)];	/* 732 */
11238451Smsmith	char parent	[ISODCL( 7, 8)];	/* 722 */
11338451Smsmith	char name	[1];
11438451Smsmith};
11538451Smsmith#define	PTFIXSZ		8
11638451Smsmith#define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
11738451Smsmith
11838451Smsmith#define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
11938451Smsmith
12086142Sjhbstatic ISO_SUSP_HEADER *
12186142Sjhbsusp_lookup_record(struct open_file *f, const char *identifier,
12286142Sjhb    struct iso_directory_record *dp, int lenskip)
12386142Sjhb{
12486142Sjhb	static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
12586142Sjhb	ISO_SUSP_HEADER *sh;
12686142Sjhb	ISO_RRIP_CONT *shc;
12786142Sjhb	char *p, *end;
12886142Sjhb	int error;
12986142Sjhb	size_t read;
13086142Sjhb
13186142Sjhb	p = dp->name + isonum_711(dp->name_len) + lenskip;
13286142Sjhb	/* Names of even length have a padding byte after the name. */
13386142Sjhb	if ((isonum_711(dp->name_len) & 1) == 0)
13486142Sjhb		p++;
13586142Sjhb	end = (char *)dp + isonum_711(dp->length);
13686142Sjhb	while (p + 3 < end) {
13786142Sjhb		sh = (ISO_SUSP_HEADER *)p;
13886142Sjhb		if (bcmp(sh->type, identifier, 2) == 0)
13986142Sjhb			return (sh);
14086142Sjhb		if (bcmp(sh->type, SUSP_STOP, 2) == 0)
14186142Sjhb			return (NULL);
14286142Sjhb		if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
14386142Sjhb			shc = (ISO_RRIP_CONT *)sh;
14486142Sjhb			error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
145313355Stsoome			    cdb2devb(isonum_733(shc->location)),
14686142Sjhb			    ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
14786142Sjhb
14886142Sjhb			/* Bail if it fails. */
14986142Sjhb			if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
15086142Sjhb				return (NULL);
15186142Sjhb			p = susp_buffer + isonum_733(shc->offset);
15286142Sjhb			end = p + isonum_733(shc->length);
153276412Snwhitehorn		} else {
15486142Sjhb			/* Ignore this record and skip to the next. */
15586142Sjhb			p += isonum_711(sh->length);
156276412Snwhitehorn
157276412Snwhitehorn			/* Avoid infinite loops with corrupted file systems */
158276412Snwhitehorn			if (isonum_711(sh->length) == 0)
159276412Snwhitehorn				return (NULL);
160276412Snwhitehorn		}
16186142Sjhb	}
16286142Sjhb	return (NULL);
16386142Sjhb}
16486142Sjhb
16586142Sjhbstatic char *
16686142Sjhbrrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
16786142Sjhb    int lenskip, size_t *len)
16886142Sjhb{
16986142Sjhb	ISO_RRIP_ALTNAME *p;
17086142Sjhb
17186142Sjhb	if (len == NULL)
17286142Sjhb		return (NULL);
17386142Sjhb
17486142Sjhb	p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
17586142Sjhb	if (p == NULL)
17686142Sjhb		return (NULL);
17786142Sjhb	switch (*p->flags) {
17886142Sjhb	case ISO_SUSP_CFLAG_CURRENT:
17986142Sjhb		*len = 1;
18086142Sjhb		return (".");
18186142Sjhb	case ISO_SUSP_CFLAG_PARENT:
18286142Sjhb		*len = 2;
18386142Sjhb		return ("..");
18486142Sjhb	case 0:
18586142Sjhb		*len = isonum_711(p->h.length) - 5;
18686142Sjhb		return ((char *)p + 5);
18786142Sjhb	default:
18886142Sjhb		/*
18986142Sjhb		 * We don't handle hostnames or continued names as they are
19086142Sjhb		 * too hard, so just bail and use the default name.
19186142Sjhb		 */
19286142Sjhb		return (NULL);
19386142Sjhb	}
19486142Sjhb}
19586142Sjhb
19638451Smsmithstatic int
19786142Sjhbrrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
19838451Smsmith{
19986142Sjhb	ISO_SUSP_PRESENT *sp;
20086142Sjhb	ISO_RRIP_EXTREF *er;
20186142Sjhb	char *p;
20286142Sjhb
20386142Sjhb	/* First, see if we can find a SP field. */
20486142Sjhb	p = dp->name + isonum_711(dp->name_len);
20586142Sjhb	if (p > (char *)dp + isonum_711(dp->length))
20686142Sjhb		return (0);
20786142Sjhb	sp = (ISO_SUSP_PRESENT *)p;
20886142Sjhb	if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
20986142Sjhb		return (0);
21086142Sjhb	if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT))
21186142Sjhb		return (0);
21286142Sjhb	if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
21386142Sjhb		return (0);
21486142Sjhb	*lenskip = isonum_711(sp->len_skp);
21586142Sjhb
21686142Sjhb	/*
21786142Sjhb	 * Now look for an ER field.  If RRIP is present, then there must
21886142Sjhb	 * be at least one of these.  It would be more pedantic to walk
21986142Sjhb	 * through the list of fields looking for a Rock Ridge ER field.
22086142Sjhb	 */
22186142Sjhb	er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
22286142Sjhb	if (er == NULL)
22386142Sjhb		return (0);
22486142Sjhb	return (1);
22586142Sjhb}
22686142Sjhb
22786142Sjhbstatic int
22886142Sjhbdirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
22986142Sjhb    int use_rrip, int lenskip)
23086142Sjhb{
231346477Skevans	size_t len, plen;
232346477Skevans	char *cp, *sep;
23386142Sjhb	int i, icase;
23438451Smsmith
23586142Sjhb	if (use_rrip)
23686142Sjhb		cp = rrip_lookup_name(f, dp, lenskip, &len);
23786142Sjhb	else
23886142Sjhb		cp = NULL;
23986142Sjhb	if (cp == NULL) {
24086142Sjhb		len = isonum_711(dp->name_len);
24186142Sjhb		cp = dp->name;
24286142Sjhb		icase = 1;
24386142Sjhb	} else
24486142Sjhb		icase = 0;
245346477Skevans
246346477Skevans	sep = strchr(path, '/');
247346477Skevans	if (sep != NULL) {
248346477Skevans		plen = sep - path;
249346477Skevans	} else {
250346477Skevans		plen = strlen(path);
251346477Skevans	}
252346477Skevans
253346477Skevans	if (plen != len)
254346477Skevans		return (0);
255346477Skevans
25686142Sjhb	for (i = len; --i >= 0; path++, cp++) {
25756222Sobrien		if (!*path || *path == '/')
25838451Smsmith			break;
25986142Sjhb		if (*path == *cp)
26038451Smsmith			continue;
26186142Sjhb		if (!icase && toupper(*path) == *cp)
26286142Sjhb			continue;
26338451Smsmith		return 0;
26438451Smsmith	}
26556222Sobrien	if (*path && *path != '/')
26638451Smsmith		return 0;
26738451Smsmith	/*
26838451Smsmith	 * Allow stripping of trailing dots and the version number.
26938451Smsmith	 * Note that this will find the first instead of the last version
27038451Smsmith	 * of a file.
27138451Smsmith	 */
27238451Smsmith	if (i >= 0 && (*cp == ';' || *cp == '.')) {
27338451Smsmith		/* This is to prevent matching of numeric extensions */
27438451Smsmith		if (*cp == '.' && cp[1] != ';')
27538451Smsmith			return 0;
27638451Smsmith		while (--i >= 0)
27738451Smsmith			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
27838451Smsmith				return 0;
27938451Smsmith	}
28038451Smsmith	return 1;
28138451Smsmith}
28238451Smsmith
28338451Smsmithstatic int
28486137Sjhbcd9660_open(const char *path, struct open_file *f)
28538451Smsmith{
286298210Spfg	struct file *fp = NULL;
28738451Smsmith	void *buf;
28838451Smsmith	struct iso_primary_descriptor *vd;
28956222Sobrien	size_t buf_size, read, dsize, off;
29056222Sobrien	daddr_t bno, boff;
29156222Sobrien	struct iso_directory_record rec;
292298210Spfg	struct iso_directory_record *dp = NULL;
29386142Sjhb	int rc, first, use_rrip, lenskip;
294346477Skevans	bool isdir = false;
29556223Sobrien
29638451Smsmith	/* First find the volume descriptor */
29738451Smsmith	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
29838451Smsmith	vd = buf;
29938451Smsmith	for (bno = 16;; bno++) {
300276079Sian		twiddle(1);
30138451Smsmith		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
302313355Stsoome					ISO_DEFAULT_BLOCK_SIZE, buf, &read);
30338451Smsmith		if (rc)
30438451Smsmith			goto out;
30538451Smsmith		if (read != ISO_DEFAULT_BLOCK_SIZE) {
30638451Smsmith			rc = EIO;
30738451Smsmith			goto out;
30838451Smsmith		}
30938451Smsmith		rc = EINVAL;
31038451Smsmith		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
31138451Smsmith			goto out;
31238451Smsmith		if (isonum_711(vd->type) == ISO_VD_END)
31338451Smsmith			goto out;
31438451Smsmith		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
31538451Smsmith			break;
31638451Smsmith	}
31738451Smsmith	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
31838451Smsmith		goto out;
31956223Sobrien
320344408Skevans	bcopy(vd->root_directory_record, &rec, sizeof(rec));
32156222Sobrien	if (*path == '/') path++; /* eat leading '/' */
32238451Smsmith
32386142Sjhb	first = 1;
32486142Sjhb	use_rrip = 0;
325344266Skevans	lenskip = 0;
32638451Smsmith	while (*path) {
32756222Sobrien		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
32856222Sobrien		dsize = isonum_733(rec.size);
32956222Sobrien		off = 0;
33056222Sobrien		boff = 0;
33156222Sobrien
33256222Sobrien		while (off < dsize) {
33356222Sobrien			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
334276079Sian				twiddle(1);
33556222Sobrien				rc = f->f_dev->dv_strategy
33656222Sobrien					(f->f_devdata, F_READ,
337313355Stsoome					 cdb2devb(bno + boff),
33856222Sobrien					 ISO_DEFAULT_BLOCK_SIZE,
33956222Sobrien					 buf, &read);
34056222Sobrien				if (rc)
34156222Sobrien					goto out;
34256222Sobrien				if (read != ISO_DEFAULT_BLOCK_SIZE) {
34356222Sobrien					rc = EIO;
34456222Sobrien					goto out;
34556222Sobrien				}
34656222Sobrien				boff++;
34756222Sobrien				dp = (struct iso_directory_record *) buf;
34856222Sobrien			}
34956222Sobrien			if (isonum_711(dp->length) == 0) {
35056222Sobrien			    /* skip to next block, if any */
35156222Sobrien			    off = boff * ISO_DEFAULT_BLOCK_SIZE;
35256222Sobrien			    continue;
35356222Sobrien			}
35456222Sobrien
35586142Sjhb			/* See if RRIP is in use. */
35686142Sjhb			if (first)
35786142Sjhb				use_rrip = rrip_check(f, dp, &lenskip);
35886142Sjhb
35986142Sjhb			if (dirmatch(f, path, dp, use_rrip,
360344266Skevans			    first ? 0 : lenskip)) {
36186142Sjhb				first = 0;
36238451Smsmith				break;
36386142Sjhb			} else
36486142Sjhb				first = 0;
36538451Smsmith
36656222Sobrien			dp = (struct iso_directory_record *)
36756222Sobrien				((char *) dp + isonum_711(dp->length));
368329098Skevans			/* If the new block has zero length, it is padding. */
369329098Skevans			if (isonum_711(dp->length) == 0) {
370329098Skevans				/* Skip to next block, if any. */
371329098Skevans				off = boff * ISO_DEFAULT_BLOCK_SIZE;
372329098Skevans				continue;
373329098Skevans			}
37456222Sobrien			off += isonum_711(dp->length);
37538451Smsmith		}
37682208Sgallatin		if (off >= dsize) {
37756222Sobrien			rc = ENOENT;
37856222Sobrien			goto out;
37938451Smsmith		}
38038451Smsmith
38156222Sobrien		rec = *dp;
38256222Sobrien		while (*path && *path != '/') /* look for next component */
38356222Sobrien			path++;
384346477Skevans
385346477Skevans		if (*path)	/* this component was directory */
386346477Skevans			isdir = true;
387346477Skevans
388346477Skevans		while (*path == '/')
389346477Skevans			path++;	/* skip '/' */
390346477Skevans
391346477Skevans		if (*path)	/* We do have next component. */
392346477Skevans			isdir = false;
39338451Smsmith	}
39456223Sobrien
395346477Skevans	/*
396346477Skevans	 * if the path had trailing / but the path does point to file,
397346477Skevans	 * report the error ENOTDIR.
398346477Skevans	 */
399346477Skevans	if (isdir == true && (isonum_711(rec.flags) & 2) == 0) {
400346477Skevans		rc = ENOTDIR;
401346477Skevans		goto out;
402346477Skevans	}
403346477Skevans
40438451Smsmith	/* allocate file system specific data structure */
40538451Smsmith	fp = malloc(sizeof(struct file));
40638451Smsmith	bzero(fp, sizeof(struct file));
40738451Smsmith	f->f_fsdata = (void *)fp;
40838451Smsmith
40986158Sjhb	if ((isonum_711(rec.flags) & 2) != 0) {
41086158Sjhb		fp->f_flags = F_ISDIR;
41186158Sjhb	}
41286158Sjhb	if (first) {
41386158Sjhb		fp->f_flags |= F_ROOTDIR;
41486158Sjhb
41586158Sjhb		/* Check for Rock Ridge since we didn't in the loop above. */
41686158Sjhb		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
417276079Sian		twiddle(1);
41886158Sjhb		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
419313355Stsoome		    ISO_DEFAULT_BLOCK_SIZE, buf, &read);
42086158Sjhb		if (rc)
42186158Sjhb			goto out;
42286158Sjhb		if (read != ISO_DEFAULT_BLOCK_SIZE) {
42386158Sjhb			rc = EIO;
42486158Sjhb			goto out;
42586158Sjhb		}
42686158Sjhb		dp = (struct iso_directory_record *)buf;
42786158Sjhb		use_rrip = rrip_check(f, dp, &lenskip);
42886158Sjhb	}
42986158Sjhb	if (use_rrip) {
43086158Sjhb		fp->f_flags |= F_RR;
43186158Sjhb		fp->f_susp_skip = lenskip;
43286158Sjhb	}
43359766Sjlemon	fp->f_off = 0;
43459766Sjlemon	fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
43559766Sjlemon	fp->f_size = isonum_733(rec.size);
43638451Smsmith	free(buf);
43756223Sobrien
43838451Smsmith	return 0;
43956223Sobrien
44038451Smsmithout:
44138451Smsmith	if (fp)
44238451Smsmith		free(fp);
44338451Smsmith	free(buf);
44456223Sobrien
44538451Smsmith	return rc;
44638451Smsmith}
44738451Smsmith
44838451Smsmithstatic int
44986137Sjhbcd9660_close(struct open_file *f)
45038451Smsmith{
45138451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
45256223Sobrien
453298210Spfg	f->f_fsdata = NULL;
45438451Smsmith	free(fp);
45556223Sobrien
45638451Smsmith	return 0;
45738451Smsmith}
45838451Smsmith
45938451Smsmithstatic int
46086137Sjhbbuf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
46138451Smsmith{
46238451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
46375298Sgallatin	daddr_t blkno, blkoff;
46438451Smsmith	int rc = 0;
46559766Sjlemon	size_t read;
46656223Sobrien
46759766Sjlemon	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
46875298Sgallatin	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
46959766Sjlemon
47059766Sjlemon	if (blkno != fp->f_buf_blkno) {
47159766Sjlemon		if (fp->f_buf == (char *)0)
47259766Sjlemon			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
47359766Sjlemon
474276079Sian		twiddle(16);
47559766Sjlemon		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
476313355Stsoome		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE,
477298230Sallanjude		    fp->f_buf, &read);
47838451Smsmith		if (rc)
47959766Sjlemon			return (rc);
48038451Smsmith		if (read != ISO_DEFAULT_BLOCK_SIZE)
48159766Sjlemon			return (EIO);
48259766Sjlemon
48359766Sjlemon		fp->f_buf_blkno = blkno;
48438451Smsmith	}
48559766Sjlemon
48675298Sgallatin	*buf_p = fp->f_buf + blkoff;
48775298Sgallatin	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
48859766Sjlemon
48959766Sjlemon	if (*size_p > fp->f_size - fp->f_off)
49059766Sjlemon		*size_p = fp->f_size - fp->f_off;
49159766Sjlemon	return (rc);
49238451Smsmith}
49338451Smsmith
49438451Smsmithstatic int
49586137Sjhbcd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
49656222Sobrien{
49756222Sobrien	struct file *fp = (struct file *)f->f_fsdata;
49859766Sjlemon	char *buf, *addr;
49959766Sjlemon	size_t buf_size, csize;
50056222Sobrien	int rc = 0;
50156223Sobrien
50259766Sjlemon	addr = start;
50359766Sjlemon	while (size) {
50459766Sjlemon		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
50559766Sjlemon			break;
50656222Sobrien
50759766Sjlemon		rc = buf_read_file(f, &buf, &buf_size);
50856222Sobrien		if (rc)
50956222Sobrien			break;
51056222Sobrien
51159766Sjlemon		csize = size > buf_size ? buf_size : size;
51259766Sjlemon		bcopy(buf, addr, csize);
51356222Sobrien
51459766Sjlemon		fp->f_off += csize;
51559766Sjlemon		addr += csize;
51659766Sjlemon		size -= csize;
51756222Sobrien	}
51856222Sobrien	if (resid)
51959766Sjlemon		*resid = size;
52059766Sjlemon	return (rc);
52156222Sobrien}
52256222Sobrien
52356222Sobrienstatic int
52459766Sjlemoncd9660_readdir(struct open_file *f, struct dirent *d)
52556222Sobrien{
52656222Sobrien	struct file *fp = (struct file *)f->f_fsdata;
52759766Sjlemon	struct iso_directory_record *ep;
52859766Sjlemon	size_t buf_size, reclen, namelen;
52959766Sjlemon	int error = 0;
53086158Sjhb	int lenskip;
53186158Sjhb	char *buf, *name;
53256222Sobrien
53359766Sjlemonagain:
53459766Sjlemon	if (fp->f_off >= fp->f_size)
53559766Sjlemon		return (ENOENT);
53659766Sjlemon	error = buf_read_file(f, &buf, &buf_size);
53759766Sjlemon	if (error)
53859766Sjlemon		return (error);
53959766Sjlemon	ep = (struct iso_directory_record *)buf;
54059766Sjlemon
54159766Sjlemon	if (isonum_711(ep->length) == 0) {
54259766Sjlemon		daddr_t blkno;
54359766Sjlemon
54459766Sjlemon		/* skip to next block, if any */
54559766Sjlemon		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
54659766Sjlemon		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
54759766Sjlemon		goto again;
54859766Sjlemon	}
54959766Sjlemon
55086158Sjhb	if (fp->f_flags & F_RR) {
55186158Sjhb		if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
55286158Sjhb			lenskip = 0;
55386158Sjhb		else
55486158Sjhb			lenskip = fp->f_susp_skip;
55586158Sjhb		name = rrip_lookup_name(f, ep, lenskip, &namelen);
55686158Sjhb	} else
55786158Sjhb		name = NULL;
55886158Sjhb	if (name == NULL) {
55986158Sjhb		namelen = isonum_711(ep->name_len);
56086158Sjhb		name = ep->name;
56186158Sjhb		if (namelen == 1) {
56286158Sjhb			if (ep->name[0] == 0)
56386158Sjhb				name = ".";
56486158Sjhb			else if (ep->name[0] == 1) {
56586158Sjhb				namelen = 2;
56686158Sjhb				name = "..";
56786158Sjhb			}
56886158Sjhb		}
56986158Sjhb	}
57059766Sjlemon	reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
57159766Sjlemon	reclen = (reclen + 3) & ~3;
57259766Sjlemon
57359766Sjlemon	d->d_fileno = isonum_733(ep->extent);
57459766Sjlemon	d->d_reclen = reclen;
57559766Sjlemon	if (isonum_711(ep->flags) & 2)
57659766Sjlemon		d->d_type = DT_DIR;
57756222Sobrien	else
57859766Sjlemon		d->d_type = DT_REG;
57959766Sjlemon	d->d_namlen = namelen;
58059766Sjlemon
58186158Sjhb	bcopy(name, d->d_name, d->d_namlen);
58259766Sjlemon	d->d_name[d->d_namlen] = 0;
58359766Sjlemon
58459766Sjlemon	fp->f_off += isonum_711(ep->length);
58559766Sjlemon	return (0);
58656222Sobrien}
58756222Sobrien
58838451Smsmithstatic off_t
58986137Sjhbcd9660_seek(struct open_file *f, off_t offset, int where)
59038451Smsmith{
59138451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
59256223Sobrien
59338451Smsmith	switch (where) {
59438451Smsmith	case SEEK_SET:
59559766Sjlemon		fp->f_off = offset;
59638451Smsmith		break;
59738451Smsmith	case SEEK_CUR:
59859766Sjlemon		fp->f_off += offset;
59938451Smsmith		break;
60038451Smsmith	case SEEK_END:
60159766Sjlemon		fp->f_off = fp->f_size - offset;
60238451Smsmith		break;
60338451Smsmith	default:
60438451Smsmith		return -1;
60538451Smsmith	}
60659766Sjlemon	return fp->f_off;
60738451Smsmith}
60838451Smsmith
60938451Smsmithstatic int
61086137Sjhbcd9660_stat(struct open_file *f, struct stat *sb)
61138451Smsmith{
61238451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
61356223Sobrien
61456222Sobrien	/* only important stuff */
61556222Sobrien	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
61686158Sjhb	if (fp->f_flags & F_ISDIR)
61756222Sobrien		sb->st_mode |= S_IFDIR;
61856222Sobrien	else
61956222Sobrien		sb->st_mode |= S_IFREG;
62038451Smsmith	sb->st_uid = sb->st_gid = 0;
62159766Sjlemon	sb->st_size = fp->f_size;
62238451Smsmith	return 0;
62338451Smsmith}
624