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