xref: /freebsd/stand/libsa/cd9660.c (revision ca987d4641cdcd7f27e153db17c5bf064934faf5)
1*ca987d46SWarner Losh /*	$NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $	*/
2*ca987d46SWarner Losh 
3*ca987d46SWarner Losh /*
4*ca987d46SWarner Losh  * Copyright (C) 1996 Wolfgang Solfrank.
5*ca987d46SWarner Losh  * Copyright (C) 1996 TooLs GmbH.
6*ca987d46SWarner Losh  * All rights reserved.
7*ca987d46SWarner Losh  *
8*ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
9*ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
10*ca987d46SWarner Losh  * are met:
11*ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
12*ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
13*ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
14*ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
15*ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
16*ca987d46SWarner Losh  * 3. All advertising materials mentioning features or use of this software
17*ca987d46SWarner Losh  *    must display the following acknowledgement:
18*ca987d46SWarner Losh  *	This product includes software developed by TooLs GmbH.
19*ca987d46SWarner Losh  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20*ca987d46SWarner Losh  *    derived from this software without specific prior written permission.
21*ca987d46SWarner Losh  *
22*ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23*ca987d46SWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24*ca987d46SWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25*ca987d46SWarner Losh  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26*ca987d46SWarner Losh  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27*ca987d46SWarner Losh  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28*ca987d46SWarner Losh  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29*ca987d46SWarner Losh  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30*ca987d46SWarner Losh  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31*ca987d46SWarner Losh  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*ca987d46SWarner Losh  */
33*ca987d46SWarner Losh 
34*ca987d46SWarner Losh #include <sys/cdefs.h>
35*ca987d46SWarner Losh __FBSDID("$FreeBSD$");
36*ca987d46SWarner Losh 
37*ca987d46SWarner Losh /*
38*ca987d46SWarner Losh  * Stand-alone ISO9660 file reading package.
39*ca987d46SWarner Losh  *
40*ca987d46SWarner Losh  * Note: This doesn't support Rock Ridge extensions, extended attributes,
41*ca987d46SWarner Losh  * blocksizes other than 2048 bytes, multi-extent files, etc.
42*ca987d46SWarner Losh  */
43*ca987d46SWarner Losh #include <sys/param.h>
44*ca987d46SWarner Losh #include <string.h>
45*ca987d46SWarner Losh #include <sys/dirent.h>
46*ca987d46SWarner Losh #include <isofs/cd9660/iso.h>
47*ca987d46SWarner Losh #include <isofs/cd9660/cd9660_rrip.h>
48*ca987d46SWarner Losh 
49*ca987d46SWarner Losh #include "stand.h"
50*ca987d46SWarner Losh 
51*ca987d46SWarner Losh #define	SUSP_CONTINUATION	"CE"
52*ca987d46SWarner Losh #define	SUSP_PRESENT		"SP"
53*ca987d46SWarner Losh #define	SUSP_STOP		"ST"
54*ca987d46SWarner Losh #define	SUSP_EXTREF		"ER"
55*ca987d46SWarner Losh #define	RRIP_NAME		"NM"
56*ca987d46SWarner Losh 
57*ca987d46SWarner Losh typedef struct {
58*ca987d46SWarner Losh 	ISO_SUSP_HEADER		h;
59*ca987d46SWarner Losh 	u_char signature	[ISODCL (  5,    6)];
60*ca987d46SWarner Losh 	u_char len_skp		[ISODCL (  7,    7)]; /* 711 */
61*ca987d46SWarner Losh } ISO_SUSP_PRESENT;
62*ca987d46SWarner Losh 
63*ca987d46SWarner Losh static int	buf_read_file(struct open_file *f, char **buf_p,
64*ca987d46SWarner Losh 		    size_t *size_p);
65*ca987d46SWarner Losh static int	cd9660_open(const char *path, struct open_file *f);
66*ca987d46SWarner Losh static int	cd9660_close(struct open_file *f);
67*ca987d46SWarner Losh static int	cd9660_read(struct open_file *f, void *buf, size_t size,
68*ca987d46SWarner Losh 		    size_t *resid);
69*ca987d46SWarner Losh static int	cd9660_write(struct open_file *f, void *buf, size_t size,
70*ca987d46SWarner Losh 		    size_t *resid);
71*ca987d46SWarner Losh static off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
72*ca987d46SWarner Losh static int	cd9660_stat(struct open_file *f, struct stat *sb);
73*ca987d46SWarner Losh static int	cd9660_readdir(struct open_file *f, struct dirent *d);
74*ca987d46SWarner Losh static int	dirmatch(struct open_file *f, const char *path,
75*ca987d46SWarner Losh 		    struct iso_directory_record *dp, int use_rrip, int lenskip);
76*ca987d46SWarner Losh static int	rrip_check(struct open_file *f, struct iso_directory_record *dp,
77*ca987d46SWarner Losh 		    int *lenskip);
78*ca987d46SWarner Losh static char	*rrip_lookup_name(struct open_file *f,
79*ca987d46SWarner Losh 		    struct iso_directory_record *dp, int lenskip, size_t *len);
80*ca987d46SWarner Losh static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
81*ca987d46SWarner Losh 		    const char *identifier, struct iso_directory_record *dp,
82*ca987d46SWarner Losh 		    int lenskip);
83*ca987d46SWarner Losh 
84*ca987d46SWarner Losh struct fs_ops cd9660_fsops = {
85*ca987d46SWarner Losh 	"cd9660",
86*ca987d46SWarner Losh 	cd9660_open,
87*ca987d46SWarner Losh 	cd9660_close,
88*ca987d46SWarner Losh 	cd9660_read,
89*ca987d46SWarner Losh 	cd9660_write,
90*ca987d46SWarner Losh 	cd9660_seek,
91*ca987d46SWarner Losh 	cd9660_stat,
92*ca987d46SWarner Losh 	cd9660_readdir
93*ca987d46SWarner Losh };
94*ca987d46SWarner Losh 
95*ca987d46SWarner Losh #define	F_ISDIR		0x0001		/* Directory */
96*ca987d46SWarner Losh #define	F_ROOTDIR	0x0002		/* Root directory */
97*ca987d46SWarner Losh #define	F_RR		0x0004		/* Rock Ridge on this volume */
98*ca987d46SWarner Losh 
99*ca987d46SWarner Losh struct file {
100*ca987d46SWarner Losh 	int 		f_flags;	/* file flags */
101*ca987d46SWarner Losh 	off_t 		f_off;		/* Current offset within file */
102*ca987d46SWarner Losh 	daddr_t 	f_bno;		/* Starting block number */
103*ca987d46SWarner Losh 	off_t 		f_size;		/* Size of file */
104*ca987d46SWarner Losh 	daddr_t		f_buf_blkno;	/* block number of data block */
105*ca987d46SWarner Losh 	char		*f_buf;		/* buffer for data block */
106*ca987d46SWarner Losh 	int		f_susp_skip;	/* len_skip for SUSP records */
107*ca987d46SWarner Losh };
108*ca987d46SWarner Losh 
109*ca987d46SWarner Losh struct ptable_ent {
110*ca987d46SWarner Losh 	char namlen	[ISODCL( 1, 1)];	/* 711 */
111*ca987d46SWarner Losh 	char extlen	[ISODCL( 2, 2)];	/* 711 */
112*ca987d46SWarner Losh 	char block	[ISODCL( 3, 6)];	/* 732 */
113*ca987d46SWarner Losh 	char parent	[ISODCL( 7, 8)];	/* 722 */
114*ca987d46SWarner Losh 	char name	[1];
115*ca987d46SWarner Losh };
116*ca987d46SWarner Losh #define	PTFIXSZ		8
117*ca987d46SWarner Losh #define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
118*ca987d46SWarner Losh 
119*ca987d46SWarner Losh #define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
120*ca987d46SWarner Losh 
121*ca987d46SWarner Losh static ISO_SUSP_HEADER *
122*ca987d46SWarner Losh susp_lookup_record(struct open_file *f, const char *identifier,
123*ca987d46SWarner Losh     struct iso_directory_record *dp, int lenskip)
124*ca987d46SWarner Losh {
125*ca987d46SWarner Losh 	static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
126*ca987d46SWarner Losh 	ISO_SUSP_HEADER *sh;
127*ca987d46SWarner Losh 	ISO_RRIP_CONT *shc;
128*ca987d46SWarner Losh 	char *p, *end;
129*ca987d46SWarner Losh 	int error;
130*ca987d46SWarner Losh 	size_t read;
131*ca987d46SWarner Losh 
132*ca987d46SWarner Losh 	p = dp->name + isonum_711(dp->name_len) + lenskip;
133*ca987d46SWarner Losh 	/* Names of even length have a padding byte after the name. */
134*ca987d46SWarner Losh 	if ((isonum_711(dp->name_len) & 1) == 0)
135*ca987d46SWarner Losh 		p++;
136*ca987d46SWarner Losh 	end = (char *)dp + isonum_711(dp->length);
137*ca987d46SWarner Losh 	while (p + 3 < end) {
138*ca987d46SWarner Losh 		sh = (ISO_SUSP_HEADER *)p;
139*ca987d46SWarner Losh 		if (bcmp(sh->type, identifier, 2) == 0)
140*ca987d46SWarner Losh 			return (sh);
141*ca987d46SWarner Losh 		if (bcmp(sh->type, SUSP_STOP, 2) == 0)
142*ca987d46SWarner Losh 			return (NULL);
143*ca987d46SWarner Losh 		if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
144*ca987d46SWarner Losh 			shc = (ISO_RRIP_CONT *)sh;
145*ca987d46SWarner Losh 			error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
146*ca987d46SWarner Losh 			    cdb2devb(isonum_733(shc->location)),
147*ca987d46SWarner Losh 			    ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
148*ca987d46SWarner Losh 
149*ca987d46SWarner Losh 			/* Bail if it fails. */
150*ca987d46SWarner Losh 			if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
151*ca987d46SWarner Losh 				return (NULL);
152*ca987d46SWarner Losh 			p = susp_buffer + isonum_733(shc->offset);
153*ca987d46SWarner Losh 			end = p + isonum_733(shc->length);
154*ca987d46SWarner Losh 		} else {
155*ca987d46SWarner Losh 			/* Ignore this record and skip to the next. */
156*ca987d46SWarner Losh 			p += isonum_711(sh->length);
157*ca987d46SWarner Losh 
158*ca987d46SWarner Losh 			/* Avoid infinite loops with corrupted file systems */
159*ca987d46SWarner Losh 			if (isonum_711(sh->length) == 0)
160*ca987d46SWarner Losh 				return (NULL);
161*ca987d46SWarner Losh 		}
162*ca987d46SWarner Losh 	}
163*ca987d46SWarner Losh 	return (NULL);
164*ca987d46SWarner Losh }
165*ca987d46SWarner Losh 
166*ca987d46SWarner Losh static char *
167*ca987d46SWarner Losh rrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
168*ca987d46SWarner Losh     int lenskip, size_t *len)
169*ca987d46SWarner Losh {
170*ca987d46SWarner Losh 	ISO_RRIP_ALTNAME *p;
171*ca987d46SWarner Losh 
172*ca987d46SWarner Losh 	if (len == NULL)
173*ca987d46SWarner Losh 		return (NULL);
174*ca987d46SWarner Losh 
175*ca987d46SWarner Losh 	p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
176*ca987d46SWarner Losh 	if (p == NULL)
177*ca987d46SWarner Losh 		return (NULL);
178*ca987d46SWarner Losh 	switch (*p->flags) {
179*ca987d46SWarner Losh 	case ISO_SUSP_CFLAG_CURRENT:
180*ca987d46SWarner Losh 		*len = 1;
181*ca987d46SWarner Losh 		return (".");
182*ca987d46SWarner Losh 	case ISO_SUSP_CFLAG_PARENT:
183*ca987d46SWarner Losh 		*len = 2;
184*ca987d46SWarner Losh 		return ("..");
185*ca987d46SWarner Losh 	case 0:
186*ca987d46SWarner Losh 		*len = isonum_711(p->h.length) - 5;
187*ca987d46SWarner Losh 		return ((char *)p + 5);
188*ca987d46SWarner Losh 	default:
189*ca987d46SWarner Losh 		/*
190*ca987d46SWarner Losh 		 * We don't handle hostnames or continued names as they are
191*ca987d46SWarner Losh 		 * too hard, so just bail and use the default name.
192*ca987d46SWarner Losh 		 */
193*ca987d46SWarner Losh 		return (NULL);
194*ca987d46SWarner Losh 	}
195*ca987d46SWarner Losh }
196*ca987d46SWarner Losh 
197*ca987d46SWarner Losh static int
198*ca987d46SWarner Losh rrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
199*ca987d46SWarner Losh {
200*ca987d46SWarner Losh 	ISO_SUSP_PRESENT *sp;
201*ca987d46SWarner Losh 	ISO_RRIP_EXTREF *er;
202*ca987d46SWarner Losh 	char *p;
203*ca987d46SWarner Losh 
204*ca987d46SWarner Losh 	/* First, see if we can find a SP field. */
205*ca987d46SWarner Losh 	p = dp->name + isonum_711(dp->name_len);
206*ca987d46SWarner Losh 	if (p > (char *)dp + isonum_711(dp->length))
207*ca987d46SWarner Losh 		return (0);
208*ca987d46SWarner Losh 	sp = (ISO_SUSP_PRESENT *)p;
209*ca987d46SWarner Losh 	if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
210*ca987d46SWarner Losh 		return (0);
211*ca987d46SWarner Losh 	if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT))
212*ca987d46SWarner Losh 		return (0);
213*ca987d46SWarner Losh 	if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
214*ca987d46SWarner Losh 		return (0);
215*ca987d46SWarner Losh 	*lenskip = isonum_711(sp->len_skp);
216*ca987d46SWarner Losh 
217*ca987d46SWarner Losh 	/*
218*ca987d46SWarner Losh 	 * Now look for an ER field.  If RRIP is present, then there must
219*ca987d46SWarner Losh 	 * be at least one of these.  It would be more pedantic to walk
220*ca987d46SWarner Losh 	 * through the list of fields looking for a Rock Ridge ER field.
221*ca987d46SWarner Losh 	 */
222*ca987d46SWarner Losh 	er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
223*ca987d46SWarner Losh 	if (er == NULL)
224*ca987d46SWarner Losh 		return (0);
225*ca987d46SWarner Losh 	return (1);
226*ca987d46SWarner Losh }
227*ca987d46SWarner Losh 
228*ca987d46SWarner Losh static int
229*ca987d46SWarner Losh dirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
230*ca987d46SWarner Losh     int use_rrip, int lenskip)
231*ca987d46SWarner Losh {
232*ca987d46SWarner Losh 	size_t len;
233*ca987d46SWarner Losh 	char *cp;
234*ca987d46SWarner Losh 	int i, icase;
235*ca987d46SWarner Losh 
236*ca987d46SWarner Losh 	if (use_rrip)
237*ca987d46SWarner Losh 		cp = rrip_lookup_name(f, dp, lenskip, &len);
238*ca987d46SWarner Losh 	else
239*ca987d46SWarner Losh 		cp = NULL;
240*ca987d46SWarner Losh 	if (cp == NULL) {
241*ca987d46SWarner Losh 		len = isonum_711(dp->name_len);
242*ca987d46SWarner Losh 		cp = dp->name;
243*ca987d46SWarner Losh 		icase = 1;
244*ca987d46SWarner Losh 	} else
245*ca987d46SWarner Losh 		icase = 0;
246*ca987d46SWarner Losh 	for (i = len; --i >= 0; path++, cp++) {
247*ca987d46SWarner Losh 		if (!*path || *path == '/')
248*ca987d46SWarner Losh 			break;
249*ca987d46SWarner Losh 		if (*path == *cp)
250*ca987d46SWarner Losh 			continue;
251*ca987d46SWarner Losh 		if (!icase && toupper(*path) == *cp)
252*ca987d46SWarner Losh 			continue;
253*ca987d46SWarner Losh 		return 0;
254*ca987d46SWarner Losh 	}
255*ca987d46SWarner Losh 	if (*path && *path != '/')
256*ca987d46SWarner Losh 		return 0;
257*ca987d46SWarner Losh 	/*
258*ca987d46SWarner Losh 	 * Allow stripping of trailing dots and the version number.
259*ca987d46SWarner Losh 	 * Note that this will find the first instead of the last version
260*ca987d46SWarner Losh 	 * of a file.
261*ca987d46SWarner Losh 	 */
262*ca987d46SWarner Losh 	if (i >= 0 && (*cp == ';' || *cp == '.')) {
263*ca987d46SWarner Losh 		/* This is to prevent matching of numeric extensions */
264*ca987d46SWarner Losh 		if (*cp == '.' && cp[1] != ';')
265*ca987d46SWarner Losh 			return 0;
266*ca987d46SWarner Losh 		while (--i >= 0)
267*ca987d46SWarner Losh 			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
268*ca987d46SWarner Losh 				return 0;
269*ca987d46SWarner Losh 	}
270*ca987d46SWarner Losh 	return 1;
271*ca987d46SWarner Losh }
272*ca987d46SWarner Losh 
273*ca987d46SWarner Losh static int
274*ca987d46SWarner Losh cd9660_open(const char *path, struct open_file *f)
275*ca987d46SWarner Losh {
276*ca987d46SWarner Losh 	struct file *fp = NULL;
277*ca987d46SWarner Losh 	void *buf;
278*ca987d46SWarner Losh 	struct iso_primary_descriptor *vd;
279*ca987d46SWarner Losh 	size_t buf_size, read, dsize, off;
280*ca987d46SWarner Losh 	daddr_t bno, boff;
281*ca987d46SWarner Losh 	struct iso_directory_record rec;
282*ca987d46SWarner Losh 	struct iso_directory_record *dp = NULL;
283*ca987d46SWarner Losh 	int rc, first, use_rrip, lenskip;
284*ca987d46SWarner Losh 
285*ca987d46SWarner Losh 	/* First find the volume descriptor */
286*ca987d46SWarner Losh 	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
287*ca987d46SWarner Losh 	vd = buf;
288*ca987d46SWarner Losh 	for (bno = 16;; bno++) {
289*ca987d46SWarner Losh 		twiddle(1);
290*ca987d46SWarner Losh 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
291*ca987d46SWarner Losh 					ISO_DEFAULT_BLOCK_SIZE, buf, &read);
292*ca987d46SWarner Losh 		if (rc)
293*ca987d46SWarner Losh 			goto out;
294*ca987d46SWarner Losh 		if (read != ISO_DEFAULT_BLOCK_SIZE) {
295*ca987d46SWarner Losh 			rc = EIO;
296*ca987d46SWarner Losh 			goto out;
297*ca987d46SWarner Losh 		}
298*ca987d46SWarner Losh 		rc = EINVAL;
299*ca987d46SWarner Losh 		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
300*ca987d46SWarner Losh 			goto out;
301*ca987d46SWarner Losh 		if (isonum_711(vd->type) == ISO_VD_END)
302*ca987d46SWarner Losh 			goto out;
303*ca987d46SWarner Losh 		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
304*ca987d46SWarner Losh 			break;
305*ca987d46SWarner Losh 	}
306*ca987d46SWarner Losh 	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
307*ca987d46SWarner Losh 		goto out;
308*ca987d46SWarner Losh 
309*ca987d46SWarner Losh 	rec = *(struct iso_directory_record *) vd->root_directory_record;
310*ca987d46SWarner Losh 	if (*path == '/') path++; /* eat leading '/' */
311*ca987d46SWarner Losh 
312*ca987d46SWarner Losh 	first = 1;
313*ca987d46SWarner Losh 	use_rrip = 0;
314*ca987d46SWarner Losh 	while (*path) {
315*ca987d46SWarner Losh 		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
316*ca987d46SWarner Losh 		dsize = isonum_733(rec.size);
317*ca987d46SWarner Losh 		off = 0;
318*ca987d46SWarner Losh 		boff = 0;
319*ca987d46SWarner Losh 
320*ca987d46SWarner Losh 		while (off < dsize) {
321*ca987d46SWarner Losh 			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
322*ca987d46SWarner Losh 				twiddle(1);
323*ca987d46SWarner Losh 				rc = f->f_dev->dv_strategy
324*ca987d46SWarner Losh 					(f->f_devdata, F_READ,
325*ca987d46SWarner Losh 					 cdb2devb(bno + boff),
326*ca987d46SWarner Losh 					 ISO_DEFAULT_BLOCK_SIZE,
327*ca987d46SWarner Losh 					 buf, &read);
328*ca987d46SWarner Losh 				if (rc)
329*ca987d46SWarner Losh 					goto out;
330*ca987d46SWarner Losh 				if (read != ISO_DEFAULT_BLOCK_SIZE) {
331*ca987d46SWarner Losh 					rc = EIO;
332*ca987d46SWarner Losh 					goto out;
333*ca987d46SWarner Losh 				}
334*ca987d46SWarner Losh 				boff++;
335*ca987d46SWarner Losh 				dp = (struct iso_directory_record *) buf;
336*ca987d46SWarner Losh 			}
337*ca987d46SWarner Losh 			if (isonum_711(dp->length) == 0) {
338*ca987d46SWarner Losh 			    /* skip to next block, if any */
339*ca987d46SWarner Losh 			    off = boff * ISO_DEFAULT_BLOCK_SIZE;
340*ca987d46SWarner Losh 			    continue;
341*ca987d46SWarner Losh 			}
342*ca987d46SWarner Losh 
343*ca987d46SWarner Losh 			/* See if RRIP is in use. */
344*ca987d46SWarner Losh 			if (first)
345*ca987d46SWarner Losh 				use_rrip = rrip_check(f, dp, &lenskip);
346*ca987d46SWarner Losh 
347*ca987d46SWarner Losh 			if (dirmatch(f, path, dp, use_rrip,
348*ca987d46SWarner Losh 				first ? 0 : lenskip)) {
349*ca987d46SWarner Losh 				first = 0;
350*ca987d46SWarner Losh 				break;
351*ca987d46SWarner Losh 			} else
352*ca987d46SWarner Losh 				first = 0;
353*ca987d46SWarner Losh 
354*ca987d46SWarner Losh 			dp = (struct iso_directory_record *)
355*ca987d46SWarner Losh 				((char *) dp + isonum_711(dp->length));
356*ca987d46SWarner Losh 			/* If the new block has zero length, it is padding. */
357*ca987d46SWarner Losh 			if (isonum_711(dp->length) == 0) {
358*ca987d46SWarner Losh 				/* Skip to next block, if any. */
359*ca987d46SWarner Losh 				off = boff * ISO_DEFAULT_BLOCK_SIZE;
360*ca987d46SWarner Losh 				continue;
361*ca987d46SWarner Losh 			}
362*ca987d46SWarner Losh 			off += isonum_711(dp->length);
363*ca987d46SWarner Losh 		}
364*ca987d46SWarner Losh 		if (off >= dsize) {
365*ca987d46SWarner Losh 			rc = ENOENT;
366*ca987d46SWarner Losh 			goto out;
367*ca987d46SWarner Losh 		}
368*ca987d46SWarner Losh 
369*ca987d46SWarner Losh 		rec = *dp;
370*ca987d46SWarner Losh 		while (*path && *path != '/') /* look for next component */
371*ca987d46SWarner Losh 			path++;
372*ca987d46SWarner Losh 		if (*path) path++; /* skip '/' */
373*ca987d46SWarner Losh 	}
374*ca987d46SWarner Losh 
375*ca987d46SWarner Losh 	/* allocate file system specific data structure */
376*ca987d46SWarner Losh 	fp = malloc(sizeof(struct file));
377*ca987d46SWarner Losh 	bzero(fp, sizeof(struct file));
378*ca987d46SWarner Losh 	f->f_fsdata = (void *)fp;
379*ca987d46SWarner Losh 
380*ca987d46SWarner Losh 	if ((isonum_711(rec.flags) & 2) != 0) {
381*ca987d46SWarner Losh 		fp->f_flags = F_ISDIR;
382*ca987d46SWarner Losh 	}
383*ca987d46SWarner Losh 	if (first) {
384*ca987d46SWarner Losh 		fp->f_flags |= F_ROOTDIR;
385*ca987d46SWarner Losh 
386*ca987d46SWarner Losh 		/* Check for Rock Ridge since we didn't in the loop above. */
387*ca987d46SWarner Losh 		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
388*ca987d46SWarner Losh 		twiddle(1);
389*ca987d46SWarner Losh 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
390*ca987d46SWarner Losh 		    ISO_DEFAULT_BLOCK_SIZE, buf, &read);
391*ca987d46SWarner Losh 		if (rc)
392*ca987d46SWarner Losh 			goto out;
393*ca987d46SWarner Losh 		if (read != ISO_DEFAULT_BLOCK_SIZE) {
394*ca987d46SWarner Losh 			rc = EIO;
395*ca987d46SWarner Losh 			goto out;
396*ca987d46SWarner Losh 		}
397*ca987d46SWarner Losh 		dp = (struct iso_directory_record *)buf;
398*ca987d46SWarner Losh 		use_rrip = rrip_check(f, dp, &lenskip);
399*ca987d46SWarner Losh 	}
400*ca987d46SWarner Losh 	if (use_rrip) {
401*ca987d46SWarner Losh 		fp->f_flags |= F_RR;
402*ca987d46SWarner Losh 		fp->f_susp_skip = lenskip;
403*ca987d46SWarner Losh 	}
404*ca987d46SWarner Losh 	fp->f_off = 0;
405*ca987d46SWarner Losh 	fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
406*ca987d46SWarner Losh 	fp->f_size = isonum_733(rec.size);
407*ca987d46SWarner Losh 	free(buf);
408*ca987d46SWarner Losh 
409*ca987d46SWarner Losh 	return 0;
410*ca987d46SWarner Losh 
411*ca987d46SWarner Losh out:
412*ca987d46SWarner Losh 	if (fp)
413*ca987d46SWarner Losh 		free(fp);
414*ca987d46SWarner Losh 	free(buf);
415*ca987d46SWarner Losh 
416*ca987d46SWarner Losh 	return rc;
417*ca987d46SWarner Losh }
418*ca987d46SWarner Losh 
419*ca987d46SWarner Losh static int
420*ca987d46SWarner Losh cd9660_close(struct open_file *f)
421*ca987d46SWarner Losh {
422*ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
423*ca987d46SWarner Losh 
424*ca987d46SWarner Losh 	f->f_fsdata = NULL;
425*ca987d46SWarner Losh 	free(fp);
426*ca987d46SWarner Losh 
427*ca987d46SWarner Losh 	return 0;
428*ca987d46SWarner Losh }
429*ca987d46SWarner Losh 
430*ca987d46SWarner Losh static int
431*ca987d46SWarner Losh buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
432*ca987d46SWarner Losh {
433*ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
434*ca987d46SWarner Losh 	daddr_t blkno, blkoff;
435*ca987d46SWarner Losh 	int rc = 0;
436*ca987d46SWarner Losh 	size_t read;
437*ca987d46SWarner Losh 
438*ca987d46SWarner Losh 	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
439*ca987d46SWarner Losh 	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
440*ca987d46SWarner Losh 
441*ca987d46SWarner Losh 	if (blkno != fp->f_buf_blkno) {
442*ca987d46SWarner Losh 		if (fp->f_buf == (char *)0)
443*ca987d46SWarner Losh 			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
444*ca987d46SWarner Losh 
445*ca987d46SWarner Losh 		twiddle(16);
446*ca987d46SWarner Losh 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
447*ca987d46SWarner Losh 		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE,
448*ca987d46SWarner Losh 		    fp->f_buf, &read);
449*ca987d46SWarner Losh 		if (rc)
450*ca987d46SWarner Losh 			return (rc);
451*ca987d46SWarner Losh 		if (read != ISO_DEFAULT_BLOCK_SIZE)
452*ca987d46SWarner Losh 			return (EIO);
453*ca987d46SWarner Losh 
454*ca987d46SWarner Losh 		fp->f_buf_blkno = blkno;
455*ca987d46SWarner Losh 	}
456*ca987d46SWarner Losh 
457*ca987d46SWarner Losh 	*buf_p = fp->f_buf + blkoff;
458*ca987d46SWarner Losh 	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
459*ca987d46SWarner Losh 
460*ca987d46SWarner Losh 	if (*size_p > fp->f_size - fp->f_off)
461*ca987d46SWarner Losh 		*size_p = fp->f_size - fp->f_off;
462*ca987d46SWarner Losh 	return (rc);
463*ca987d46SWarner Losh }
464*ca987d46SWarner Losh 
465*ca987d46SWarner Losh static int
466*ca987d46SWarner Losh cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
467*ca987d46SWarner Losh {
468*ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
469*ca987d46SWarner Losh 	char *buf, *addr;
470*ca987d46SWarner Losh 	size_t buf_size, csize;
471*ca987d46SWarner Losh 	int rc = 0;
472*ca987d46SWarner Losh 
473*ca987d46SWarner Losh 	addr = start;
474*ca987d46SWarner Losh 	while (size) {
475*ca987d46SWarner Losh 		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
476*ca987d46SWarner Losh 			break;
477*ca987d46SWarner Losh 
478*ca987d46SWarner Losh 		rc = buf_read_file(f, &buf, &buf_size);
479*ca987d46SWarner Losh 		if (rc)
480*ca987d46SWarner Losh 			break;
481*ca987d46SWarner Losh 
482*ca987d46SWarner Losh 		csize = size > buf_size ? buf_size : size;
483*ca987d46SWarner Losh 		bcopy(buf, addr, csize);
484*ca987d46SWarner Losh 
485*ca987d46SWarner Losh 		fp->f_off += csize;
486*ca987d46SWarner Losh 		addr += csize;
487*ca987d46SWarner Losh 		size -= csize;
488*ca987d46SWarner Losh 	}
489*ca987d46SWarner Losh 	if (resid)
490*ca987d46SWarner Losh 		*resid = size;
491*ca987d46SWarner Losh 	return (rc);
492*ca987d46SWarner Losh }
493*ca987d46SWarner Losh 
494*ca987d46SWarner Losh static int
495*ca987d46SWarner Losh cd9660_readdir(struct open_file *f, struct dirent *d)
496*ca987d46SWarner Losh {
497*ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
498*ca987d46SWarner Losh 	struct iso_directory_record *ep;
499*ca987d46SWarner Losh 	size_t buf_size, reclen, namelen;
500*ca987d46SWarner Losh 	int error = 0;
501*ca987d46SWarner Losh 	int lenskip;
502*ca987d46SWarner Losh 	char *buf, *name;
503*ca987d46SWarner Losh 
504*ca987d46SWarner Losh again:
505*ca987d46SWarner Losh 	if (fp->f_off >= fp->f_size)
506*ca987d46SWarner Losh 		return (ENOENT);
507*ca987d46SWarner Losh 	error = buf_read_file(f, &buf, &buf_size);
508*ca987d46SWarner Losh 	if (error)
509*ca987d46SWarner Losh 		return (error);
510*ca987d46SWarner Losh 	ep = (struct iso_directory_record *)buf;
511*ca987d46SWarner Losh 
512*ca987d46SWarner Losh 	if (isonum_711(ep->length) == 0) {
513*ca987d46SWarner Losh 		daddr_t blkno;
514*ca987d46SWarner Losh 
515*ca987d46SWarner Losh 		/* skip to next block, if any */
516*ca987d46SWarner Losh 		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
517*ca987d46SWarner Losh 		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
518*ca987d46SWarner Losh 		goto again;
519*ca987d46SWarner Losh 	}
520*ca987d46SWarner Losh 
521*ca987d46SWarner Losh 	if (fp->f_flags & F_RR) {
522*ca987d46SWarner Losh 		if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
523*ca987d46SWarner Losh 			lenskip = 0;
524*ca987d46SWarner Losh 		else
525*ca987d46SWarner Losh 			lenskip = fp->f_susp_skip;
526*ca987d46SWarner Losh 		name = rrip_lookup_name(f, ep, lenskip, &namelen);
527*ca987d46SWarner Losh 	} else
528*ca987d46SWarner Losh 		name = NULL;
529*ca987d46SWarner Losh 	if (name == NULL) {
530*ca987d46SWarner Losh 		namelen = isonum_711(ep->name_len);
531*ca987d46SWarner Losh 		name = ep->name;
532*ca987d46SWarner Losh 		if (namelen == 1) {
533*ca987d46SWarner Losh 			if (ep->name[0] == 0)
534*ca987d46SWarner Losh 				name = ".";
535*ca987d46SWarner Losh 			else if (ep->name[0] == 1) {
536*ca987d46SWarner Losh 				namelen = 2;
537*ca987d46SWarner Losh 				name = "..";
538*ca987d46SWarner Losh 			}
539*ca987d46SWarner Losh 		}
540*ca987d46SWarner Losh 	}
541*ca987d46SWarner Losh 	reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
542*ca987d46SWarner Losh 	reclen = (reclen + 3) & ~3;
543*ca987d46SWarner Losh 
544*ca987d46SWarner Losh 	d->d_fileno = isonum_733(ep->extent);
545*ca987d46SWarner Losh 	d->d_reclen = reclen;
546*ca987d46SWarner Losh 	if (isonum_711(ep->flags) & 2)
547*ca987d46SWarner Losh 		d->d_type = DT_DIR;
548*ca987d46SWarner Losh 	else
549*ca987d46SWarner Losh 		d->d_type = DT_REG;
550*ca987d46SWarner Losh 	d->d_namlen = namelen;
551*ca987d46SWarner Losh 
552*ca987d46SWarner Losh 	bcopy(name, d->d_name, d->d_namlen);
553*ca987d46SWarner Losh 	d->d_name[d->d_namlen] = 0;
554*ca987d46SWarner Losh 
555*ca987d46SWarner Losh 	fp->f_off += isonum_711(ep->length);
556*ca987d46SWarner Losh 	return (0);
557*ca987d46SWarner Losh }
558*ca987d46SWarner Losh 
559*ca987d46SWarner Losh static int
560*ca987d46SWarner Losh cd9660_write(struct open_file *f __unused, void *start __unused, size_t size __unused, size_t *resid __unused)
561*ca987d46SWarner Losh {
562*ca987d46SWarner Losh 	return EROFS;
563*ca987d46SWarner Losh }
564*ca987d46SWarner Losh 
565*ca987d46SWarner Losh static off_t
566*ca987d46SWarner Losh cd9660_seek(struct open_file *f, off_t offset, int where)
567*ca987d46SWarner Losh {
568*ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
569*ca987d46SWarner Losh 
570*ca987d46SWarner Losh 	switch (where) {
571*ca987d46SWarner Losh 	case SEEK_SET:
572*ca987d46SWarner Losh 		fp->f_off = offset;
573*ca987d46SWarner Losh 		break;
574*ca987d46SWarner Losh 	case SEEK_CUR:
575*ca987d46SWarner Losh 		fp->f_off += offset;
576*ca987d46SWarner Losh 		break;
577*ca987d46SWarner Losh 	case SEEK_END:
578*ca987d46SWarner Losh 		fp->f_off = fp->f_size - offset;
579*ca987d46SWarner Losh 		break;
580*ca987d46SWarner Losh 	default:
581*ca987d46SWarner Losh 		return -1;
582*ca987d46SWarner Losh 	}
583*ca987d46SWarner Losh 	return fp->f_off;
584*ca987d46SWarner Losh }
585*ca987d46SWarner Losh 
586*ca987d46SWarner Losh static int
587*ca987d46SWarner Losh cd9660_stat(struct open_file *f, struct stat *sb)
588*ca987d46SWarner Losh {
589*ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
590*ca987d46SWarner Losh 
591*ca987d46SWarner Losh 	/* only important stuff */
592*ca987d46SWarner Losh 	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
593*ca987d46SWarner Losh 	if (fp->f_flags & F_ISDIR)
594*ca987d46SWarner Losh 		sb->st_mode |= S_IFDIR;
595*ca987d46SWarner Losh 	else
596*ca987d46SWarner Losh 		sb->st_mode |= S_IFREG;
597*ca987d46SWarner Losh 	sb->st_uid = sb->st_gid = 0;
598*ca987d46SWarner Losh 	sb->st_size = fp->f_size;
599*ca987d46SWarner Losh 	return 0;
600*ca987d46SWarner Losh }
601