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$");
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>
4556222Sobrien#include <sys/dirent.h>
4638451Smsmith#include <isofs/cd9660/iso.h>
4786142Sjhb#include <isofs/cd9660/cd9660_rrip.h>
4838451Smsmith
4938451Smsmith#include "stand.h"
5038451Smsmith
5186142Sjhb#define	SUSP_CONTINUATION	"CE"
5286142Sjhb#define	SUSP_PRESENT		"SP"
5386142Sjhb#define	SUSP_STOP		"ST"
5486142Sjhb#define	SUSP_EXTREF		"ER"
5586142Sjhb#define	RRIP_NAME		"NM"
5686142Sjhb
5786142Sjhbtypedef struct {
5886142Sjhb	ISO_SUSP_HEADER		h;
5986142Sjhb	u_char signature	[ISODCL (  5,    6)];
6086142Sjhb	u_char len_skp		[ISODCL (  7,    7)]; /* 711 */
6186142Sjhb} ISO_SUSP_PRESENT;
6286142Sjhb
6386137Sjhbstatic int	buf_read_file(struct open_file *f, char **buf_p,
6486137Sjhb		    size_t *size_p);
6539468Smsmithstatic int	cd9660_open(const char *path, struct open_file *f);
6638451Smsmithstatic int	cd9660_close(struct open_file *f);
6786137Sjhbstatic int	cd9660_read(struct open_file *f, void *buf, size_t size,
6886137Sjhb		    size_t *resid);
6986137Sjhbstatic int	cd9660_write(struct open_file *f, void *buf, size_t size,
7086137Sjhb		    size_t *resid);
7138451Smsmithstatic off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
7238451Smsmithstatic int	cd9660_stat(struct open_file *f, struct stat *sb);
7359766Sjlemonstatic int	cd9660_readdir(struct open_file *f, struct dirent *d);
7486142Sjhbstatic int	dirmatch(struct open_file *f, const char *path,
7586142Sjhb		    struct iso_directory_record *dp, int use_rrip, int lenskip);
7686142Sjhbstatic int	rrip_check(struct open_file *f, struct iso_directory_record *dp,
7786142Sjhb		    int *lenskip);
7886142Sjhbstatic char	*rrip_lookup_name(struct open_file *f,
7986142Sjhb		    struct iso_directory_record *dp, int lenskip, size_t *len);
8086142Sjhbstatic ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
8186142Sjhb		    const char *identifier, struct iso_directory_record *dp,
8286142Sjhb		    int lenskip);
8338451Smsmith
8438451Smsmithstruct fs_ops cd9660_fsops = {
8559766Sjlemon	"cd9660",
8659766Sjlemon	cd9660_open,
8759766Sjlemon	cd9660_close,
8859766Sjlemon	cd9660_read,
8959766Sjlemon	cd9660_write,
9059766Sjlemon	cd9660_seek,
9159766Sjlemon	cd9660_stat,
9259766Sjlemon	cd9660_readdir
9338451Smsmith};
9438451Smsmith
9586158Sjhb#define	F_ISDIR		0x0001		/* Directory */
9686158Sjhb#define	F_ROOTDIR	0x0002		/* Root directory */
9786158Sjhb#define	F_RR		0x0004		/* Rock Ridge on this volume */
9886158Sjhb
9938451Smsmithstruct file {
10086158Sjhb	int 		f_flags;	/* file flags */
10159766Sjlemon	off_t 		f_off;		/* Current offset within file */
10259766Sjlemon	daddr_t 	f_bno;		/* Starting block number */
10359766Sjlemon	off_t 		f_size;		/* Size of file */
10459766Sjlemon	daddr_t		f_buf_blkno;	/* block number of data block */
10559766Sjlemon	char		*f_buf;		/* buffer for data block */
10686158Sjhb	int		f_susp_skip;	/* len_skip for SUSP records */
10738451Smsmith};
10838451Smsmith
10938451Smsmithstruct ptable_ent {
11038451Smsmith	char namlen	[ISODCL( 1, 1)];	/* 711 */
11138451Smsmith	char extlen	[ISODCL( 2, 2)];	/* 711 */
11238451Smsmith	char block	[ISODCL( 3, 6)];	/* 732 */
11338451Smsmith	char parent	[ISODCL( 7, 8)];	/* 722 */
11438451Smsmith	char name	[1];
11538451Smsmith};
11638451Smsmith#define	PTFIXSZ		8
11738451Smsmith#define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
11838451Smsmith
11938451Smsmith#define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
12038451Smsmith
12186142Sjhbstatic ISO_SUSP_HEADER *
12286142Sjhbsusp_lookup_record(struct open_file *f, const char *identifier,
12386142Sjhb    struct iso_directory_record *dp, int lenskip)
12486142Sjhb{
12586142Sjhb	static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
12686142Sjhb	ISO_SUSP_HEADER *sh;
12786142Sjhb	ISO_RRIP_CONT *shc;
12886142Sjhb	char *p, *end;
12986142Sjhb	int error;
13086142Sjhb	size_t read;
13186142Sjhb
13286142Sjhb	p = dp->name + isonum_711(dp->name_len) + lenskip;
13386142Sjhb	/* Names of even length have a padding byte after the name. */
13486142Sjhb	if ((isonum_711(dp->name_len) & 1) == 0)
13586142Sjhb		p++;
13686142Sjhb	end = (char *)dp + isonum_711(dp->length);
13786142Sjhb	while (p + 3 < end) {
13886142Sjhb		sh = (ISO_SUSP_HEADER *)p;
13986142Sjhb		if (bcmp(sh->type, identifier, 2) == 0)
14086142Sjhb			return (sh);
14186142Sjhb		if (bcmp(sh->type, SUSP_STOP, 2) == 0)
14286142Sjhb			return (NULL);
14386142Sjhb		if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
14486142Sjhb			shc = (ISO_RRIP_CONT *)sh;
14586142Sjhb			error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
14686142Sjhb			    cdb2devb(isonum_733(shc->location)),
14786142Sjhb			    ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
14886142Sjhb
14986142Sjhb			/* Bail if it fails. */
15086142Sjhb			if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
15186142Sjhb				return (NULL);
15286142Sjhb			p = susp_buffer + isonum_733(shc->offset);
15386142Sjhb			end = p + isonum_733(shc->length);
15486142Sjhb		} else
15586142Sjhb			/* Ignore this record and skip to the next. */
15686142Sjhb			p += isonum_711(sh->length);
15786142Sjhb	}
15886142Sjhb	return (NULL);
15986142Sjhb}
16086142Sjhb
16186142Sjhbstatic char *
16286142Sjhbrrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
16386142Sjhb    int lenskip, size_t *len)
16486142Sjhb{
16586142Sjhb	ISO_RRIP_ALTNAME *p;
16686142Sjhb
16786142Sjhb	if (len == NULL)
16886142Sjhb		return (NULL);
16986142Sjhb
17086142Sjhb	p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
17186142Sjhb	if (p == NULL)
17286142Sjhb		return (NULL);
17386142Sjhb	switch (*p->flags) {
17486142Sjhb	case ISO_SUSP_CFLAG_CURRENT:
17586142Sjhb		*len = 1;
17686142Sjhb		return (".");
17786142Sjhb	case ISO_SUSP_CFLAG_PARENT:
17886142Sjhb		*len = 2;
17986142Sjhb		return ("..");
18086142Sjhb	case 0:
18186142Sjhb		*len = isonum_711(p->h.length) - 5;
18286142Sjhb		return ((char *)p + 5);
18386142Sjhb	default:
18486142Sjhb		/*
18586142Sjhb		 * We don't handle hostnames or continued names as they are
18686142Sjhb		 * too hard, so just bail and use the default name.
18786142Sjhb		 */
18886142Sjhb		return (NULL);
18986142Sjhb	}
19086142Sjhb}
19186142Sjhb
19238451Smsmithstatic int
19386142Sjhbrrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
19438451Smsmith{
19586142Sjhb	ISO_SUSP_PRESENT *sp;
19686142Sjhb	ISO_RRIP_EXTREF *er;
19786142Sjhb	char *p;
19886142Sjhb
19986142Sjhb	/* First, see if we can find a SP field. */
20086142Sjhb	p = dp->name + isonum_711(dp->name_len);
20186142Sjhb	if (p > (char *)dp + isonum_711(dp->length))
20286142Sjhb		return (0);
20386142Sjhb	sp = (ISO_SUSP_PRESENT *)p;
20486142Sjhb	if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
20586142Sjhb		return (0);
20686142Sjhb	if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT))
20786142Sjhb		return (0);
20886142Sjhb	if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
20986142Sjhb		return (0);
21086142Sjhb	*lenskip = isonum_711(sp->len_skp);
21186142Sjhb
21286142Sjhb	/*
21386142Sjhb	 * Now look for an ER field.  If RRIP is present, then there must
21486142Sjhb	 * be at least one of these.  It would be more pedantic to walk
21586142Sjhb	 * through the list of fields looking for a Rock Ridge ER field.
21686142Sjhb	 */
21786142Sjhb	er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
21886142Sjhb	if (er == NULL)
21986142Sjhb		return (0);
22086142Sjhb	return (1);
22186142Sjhb}
22286142Sjhb
22386142Sjhbstatic int
22486142Sjhbdirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
22586142Sjhb    int use_rrip, int lenskip)
22686142Sjhb{
22786142Sjhb	size_t len;
22838451Smsmith	char *cp;
22986142Sjhb	int i, icase;
23038451Smsmith
23186142Sjhb	if (use_rrip)
23286142Sjhb		cp = rrip_lookup_name(f, dp, lenskip, &len);
23386142Sjhb	else
23486142Sjhb		cp = NULL;
23586142Sjhb	if (cp == NULL) {
23686142Sjhb		len = isonum_711(dp->name_len);
23786142Sjhb		cp = dp->name;
23886142Sjhb		icase = 1;
23986142Sjhb	} else
24086142Sjhb		icase = 0;
24186142Sjhb	for (i = len; --i >= 0; path++, cp++) {
24256222Sobrien		if (!*path || *path == '/')
24338451Smsmith			break;
24486142Sjhb		if (*path == *cp)
24538451Smsmith			continue;
24686142Sjhb		if (!icase && toupper(*path) == *cp)
24786142Sjhb			continue;
24838451Smsmith		return 0;
24938451Smsmith	}
25056222Sobrien	if (*path && *path != '/')
25138451Smsmith		return 0;
25238451Smsmith	/*
25338451Smsmith	 * Allow stripping of trailing dots and the version number.
25438451Smsmith	 * Note that this will find the first instead of the last version
25538451Smsmith	 * of a file.
25638451Smsmith	 */
25738451Smsmith	if (i >= 0 && (*cp == ';' || *cp == '.')) {
25838451Smsmith		/* This is to prevent matching of numeric extensions */
25938451Smsmith		if (*cp == '.' && cp[1] != ';')
26038451Smsmith			return 0;
26138451Smsmith		while (--i >= 0)
26238451Smsmith			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
26338451Smsmith				return 0;
26438451Smsmith	}
26538451Smsmith	return 1;
26638451Smsmith}
26738451Smsmith
26838451Smsmithstatic int
26986137Sjhbcd9660_open(const char *path, struct open_file *f)
27038451Smsmith{
27138451Smsmith	struct file *fp = 0;
27238451Smsmith	void *buf;
27338451Smsmith	struct iso_primary_descriptor *vd;
27456222Sobrien	size_t buf_size, read, dsize, off;
27556222Sobrien	daddr_t bno, boff;
27656222Sobrien	struct iso_directory_record rec;
27738451Smsmith	struct iso_directory_record *dp = 0;
27886142Sjhb	int rc, first, use_rrip, lenskip;
27956223Sobrien
28038451Smsmith	/* First find the volume descriptor */
28138451Smsmith	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
28238451Smsmith	vd = buf;
28338451Smsmith	for (bno = 16;; bno++) {
28438451Smsmith		twiddle();
28538451Smsmith		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
28638451Smsmith					   ISO_DEFAULT_BLOCK_SIZE, buf, &read);
28738451Smsmith		if (rc)
28838451Smsmith			goto out;
28938451Smsmith		if (read != ISO_DEFAULT_BLOCK_SIZE) {
29038451Smsmith			rc = EIO;
29138451Smsmith			goto out;
29238451Smsmith		}
29338451Smsmith		rc = EINVAL;
29438451Smsmith		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
29538451Smsmith			goto out;
29638451Smsmith		if (isonum_711(vd->type) == ISO_VD_END)
29738451Smsmith			goto out;
29838451Smsmith		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
29938451Smsmith			break;
30038451Smsmith	}
30138451Smsmith	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
30238451Smsmith		goto out;
30356223Sobrien
30456222Sobrien	rec = *(struct iso_directory_record *) vd->root_directory_record;
30556222Sobrien	if (*path == '/') path++; /* eat leading '/' */
30638451Smsmith
30786142Sjhb	first = 1;
30886142Sjhb	use_rrip = 0;
30938451Smsmith	while (*path) {
31056222Sobrien		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
31156222Sobrien		dsize = isonum_733(rec.size);
31256222Sobrien		off = 0;
31356222Sobrien		boff = 0;
31456222Sobrien
31556222Sobrien		while (off < dsize) {
31656222Sobrien			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
31756222Sobrien				twiddle();
31856222Sobrien				rc = f->f_dev->dv_strategy
31956222Sobrien					(f->f_devdata, F_READ,
32056222Sobrien					 cdb2devb(bno + boff),
32156222Sobrien					 ISO_DEFAULT_BLOCK_SIZE,
32256222Sobrien					 buf, &read);
32356222Sobrien				if (rc)
32456222Sobrien					goto out;
32556222Sobrien				if (read != ISO_DEFAULT_BLOCK_SIZE) {
32656222Sobrien					rc = EIO;
32756222Sobrien					goto out;
32856222Sobrien				}
32956222Sobrien				boff++;
33056222Sobrien				dp = (struct iso_directory_record *) buf;
33156222Sobrien			}
33256222Sobrien			if (isonum_711(dp->length) == 0) {
33356222Sobrien			    /* skip to next block, if any */
33456222Sobrien			    off = boff * ISO_DEFAULT_BLOCK_SIZE;
33556222Sobrien			    continue;
33656222Sobrien			}
33756222Sobrien
33886142Sjhb			/* See if RRIP is in use. */
33986142Sjhb			if (first)
34086142Sjhb				use_rrip = rrip_check(f, dp, &lenskip);
34186142Sjhb
34286142Sjhb			if (dirmatch(f, path, dp, use_rrip,
34386142Sjhb				first ? 0 : lenskip)) {
34486142Sjhb				first = 0;
34538451Smsmith				break;
34686142Sjhb			} else
34786142Sjhb				first = 0;
34838451Smsmith
34956222Sobrien			dp = (struct iso_directory_record *)
35056222Sobrien				((char *) dp + isonum_711(dp->length));
35156222Sobrien			off += isonum_711(dp->length);
35238451Smsmith		}
35382208Sgallatin		if (off >= dsize) {
35456222Sobrien			rc = ENOENT;
35556222Sobrien			goto out;
35638451Smsmith		}
35738451Smsmith
35856222Sobrien		rec = *dp;
35956222Sobrien		while (*path && *path != '/') /* look for next component */
36056222Sobrien			path++;
36156222Sobrien		if (*path) path++; /* skip '/' */
36238451Smsmith	}
36356223Sobrien
36438451Smsmith	/* allocate file system specific data structure */
36538451Smsmith	fp = malloc(sizeof(struct file));
36638451Smsmith	bzero(fp, sizeof(struct file));
36738451Smsmith	f->f_fsdata = (void *)fp;
36838451Smsmith
36986158Sjhb	if ((isonum_711(rec.flags) & 2) != 0) {
37086158Sjhb		fp->f_flags = F_ISDIR;
37186158Sjhb	}
37286158Sjhb	if (first) {
37386158Sjhb		fp->f_flags |= F_ROOTDIR;
37486158Sjhb
37586158Sjhb		/* Check for Rock Ridge since we didn't in the loop above. */
37686158Sjhb		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
37786158Sjhb		twiddle();
37886158Sjhb		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
37986158Sjhb		    ISO_DEFAULT_BLOCK_SIZE, buf, &read);
38086158Sjhb		if (rc)
38186158Sjhb			goto out;
38286158Sjhb		if (read != ISO_DEFAULT_BLOCK_SIZE) {
38386158Sjhb			rc = EIO;
38486158Sjhb			goto out;
38586158Sjhb		}
38686158Sjhb		dp = (struct iso_directory_record *)buf;
38786158Sjhb		use_rrip = rrip_check(f, dp, &lenskip);
38886158Sjhb	}
38986158Sjhb	if (use_rrip) {
39086158Sjhb		fp->f_flags |= F_RR;
39186158Sjhb		fp->f_susp_skip = lenskip;
39286158Sjhb	}
39359766Sjlemon	fp->f_off = 0;
39459766Sjlemon	fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
39559766Sjlemon	fp->f_size = isonum_733(rec.size);
39638451Smsmith	free(buf);
39756223Sobrien
39838451Smsmith	return 0;
39956223Sobrien
40038451Smsmithout:
40138451Smsmith	if (fp)
40238451Smsmith		free(fp);
40338451Smsmith	free(buf);
40456223Sobrien
40538451Smsmith	return rc;
40638451Smsmith}
40738451Smsmith
40838451Smsmithstatic int
40986137Sjhbcd9660_close(struct open_file *f)
41038451Smsmith{
41138451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
41256223Sobrien
41338451Smsmith	f->f_fsdata = 0;
41438451Smsmith	free(fp);
41556223Sobrien
41638451Smsmith	return 0;
41738451Smsmith}
41838451Smsmith
41938451Smsmithstatic int
42086137Sjhbbuf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
42138451Smsmith{
42238451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
42375298Sgallatin	daddr_t blkno, blkoff;
42438451Smsmith	int rc = 0;
42559766Sjlemon	size_t read;
42656223Sobrien
42759766Sjlemon	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
42875298Sgallatin	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
42959766Sjlemon
43059766Sjlemon	if (blkno != fp->f_buf_blkno) {
43159766Sjlemon		if (fp->f_buf == (char *)0)
43259766Sjlemon			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
43359766Sjlemon
43456223Sobrien		twiddle();
43559766Sjlemon		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
43659766Sjlemon		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read);
43738451Smsmith		if (rc)
43859766Sjlemon			return (rc);
43938451Smsmith		if (read != ISO_DEFAULT_BLOCK_SIZE)
44059766Sjlemon			return (EIO);
44159766Sjlemon
44259766Sjlemon		fp->f_buf_blkno = blkno;
44338451Smsmith	}
44459766Sjlemon
44575298Sgallatin	*buf_p = fp->f_buf + blkoff;
44675298Sgallatin	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
44759766Sjlemon
44859766Sjlemon	if (*size_p > fp->f_size - fp->f_off)
44959766Sjlemon		*size_p = fp->f_size - fp->f_off;
45059766Sjlemon	return (rc);
45138451Smsmith}
45238451Smsmith
45338451Smsmithstatic int
45486137Sjhbcd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
45556222Sobrien{
45656222Sobrien	struct file *fp = (struct file *)f->f_fsdata;
45759766Sjlemon	char *buf, *addr;
45859766Sjlemon	size_t buf_size, csize;
45956222Sobrien	int rc = 0;
46056223Sobrien
46159766Sjlemon	addr = start;
46259766Sjlemon	while (size) {
46359766Sjlemon		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
46459766Sjlemon			break;
46556222Sobrien
46659766Sjlemon		rc = buf_read_file(f, &buf, &buf_size);
46756222Sobrien		if (rc)
46856222Sobrien			break;
46956222Sobrien
47059766Sjlemon		csize = size > buf_size ? buf_size : size;
47159766Sjlemon		bcopy(buf, addr, csize);
47256222Sobrien
47359766Sjlemon		fp->f_off += csize;
47459766Sjlemon		addr += csize;
47559766Sjlemon		size -= csize;
47656222Sobrien	}
47756222Sobrien	if (resid)
47859766Sjlemon		*resid = size;
47959766Sjlemon	return (rc);
48056222Sobrien}
48156222Sobrien
48256222Sobrienstatic int
48359766Sjlemoncd9660_readdir(struct open_file *f, struct dirent *d)
48456222Sobrien{
48556222Sobrien	struct file *fp = (struct file *)f->f_fsdata;
48659766Sjlemon	struct iso_directory_record *ep;
48759766Sjlemon	size_t buf_size, reclen, namelen;
48859766Sjlemon	int error = 0;
48986158Sjhb	int lenskip;
49086158Sjhb	char *buf, *name;
49156222Sobrien
49259766Sjlemonagain:
49359766Sjlemon	if (fp->f_off >= fp->f_size)
49459766Sjlemon		return (ENOENT);
49559766Sjlemon	error = buf_read_file(f, &buf, &buf_size);
49659766Sjlemon	if (error)
49759766Sjlemon		return (error);
49859766Sjlemon	ep = (struct iso_directory_record *)buf;
49959766Sjlemon
50059766Sjlemon	if (isonum_711(ep->length) == 0) {
50159766Sjlemon		daddr_t blkno;
50259766Sjlemon
50359766Sjlemon		/* skip to next block, if any */
50459766Sjlemon		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
50559766Sjlemon		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
50659766Sjlemon		goto again;
50759766Sjlemon	}
50859766Sjlemon
50986158Sjhb	if (fp->f_flags & F_RR) {
51086158Sjhb		if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
51186158Sjhb			lenskip = 0;
51286158Sjhb		else
51386158Sjhb			lenskip = fp->f_susp_skip;
51486158Sjhb		name = rrip_lookup_name(f, ep, lenskip, &namelen);
51586158Sjhb	} else
51686158Sjhb		name = NULL;
51786158Sjhb	if (name == NULL) {
51886158Sjhb		namelen = isonum_711(ep->name_len);
51986158Sjhb		name = ep->name;
52086158Sjhb		if (namelen == 1) {
52186158Sjhb			if (ep->name[0] == 0)
52286158Sjhb				name = ".";
52386158Sjhb			else if (ep->name[0] == 1) {
52486158Sjhb				namelen = 2;
52586158Sjhb				name = "..";
52686158Sjhb			}
52786158Sjhb		}
52886158Sjhb	}
52959766Sjlemon	reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
53059766Sjlemon	reclen = (reclen + 3) & ~3;
53159766Sjlemon
53259766Sjlemon	d->d_fileno = isonum_733(ep->extent);
53359766Sjlemon	d->d_reclen = reclen;
53459766Sjlemon	if (isonum_711(ep->flags) & 2)
53559766Sjlemon		d->d_type = DT_DIR;
53656222Sobrien	else
53759766Sjlemon		d->d_type = DT_REG;
53859766Sjlemon	d->d_namlen = namelen;
53959766Sjlemon
54086158Sjhb	bcopy(name, d->d_name, d->d_namlen);
54159766Sjlemon	d->d_name[d->d_namlen] = 0;
54259766Sjlemon
54359766Sjlemon	fp->f_off += isonum_711(ep->length);
54459766Sjlemon	return (0);
54556222Sobrien}
54656222Sobrien
54756222Sobrienstatic int
548221358Srodrigccd9660_write(struct open_file *f __unused, void *start __unused, size_t size __unused, size_t *resid __unused)
54938451Smsmith{
55038451Smsmith	return EROFS;
55138451Smsmith}
55238451Smsmith
55338451Smsmithstatic off_t
55486137Sjhbcd9660_seek(struct open_file *f, off_t offset, int where)
55538451Smsmith{
55638451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
55756223Sobrien
55838451Smsmith	switch (where) {
55938451Smsmith	case SEEK_SET:
56059766Sjlemon		fp->f_off = offset;
56138451Smsmith		break;
56238451Smsmith	case SEEK_CUR:
56359766Sjlemon		fp->f_off += offset;
56438451Smsmith		break;
56538451Smsmith	case SEEK_END:
56659766Sjlemon		fp->f_off = fp->f_size - offset;
56738451Smsmith		break;
56838451Smsmith	default:
56938451Smsmith		return -1;
57038451Smsmith	}
57159766Sjlemon	return fp->f_off;
57238451Smsmith}
57338451Smsmith
57438451Smsmithstatic int
57586137Sjhbcd9660_stat(struct open_file *f, struct stat *sb)
57638451Smsmith{
57738451Smsmith	struct file *fp = (struct file *)f->f_fsdata;
57856223Sobrien
57956222Sobrien	/* only important stuff */
58056222Sobrien	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
58186158Sjhb	if (fp->f_flags & F_ISDIR)
58256222Sobrien		sb->st_mode |= S_IFDIR;
58356222Sobrien	else
58456222Sobrien		sb->st_mode |= S_IFREG;
58538451Smsmith	sb->st_uid = sb->st_gid = 0;
58659766Sjlemon	sb->st_size = fp->f_size;
58738451Smsmith	return 0;
58838451Smsmith}
589