xref: /freebsd/stand/libsa/cd9660.c (revision b3e16b02)
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 int	cd9660_write(struct open_file *f, void *buf, size_t size,
70ca987d46SWarner Losh 		    size_t *resid);
71ca987d46SWarner Losh static off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
72ca987d46SWarner Losh static int	cd9660_stat(struct open_file *f, struct stat *sb);
73ca987d46SWarner Losh static int	cd9660_readdir(struct open_file *f, struct dirent *d);
74ca987d46SWarner Losh static int	dirmatch(struct open_file *f, const char *path,
75ca987d46SWarner Losh 		    struct iso_directory_record *dp, int use_rrip, int lenskip);
76ca987d46SWarner Losh static int	rrip_check(struct open_file *f, struct iso_directory_record *dp,
77ca987d46SWarner Losh 		    int *lenskip);
78ca987d46SWarner Losh static char	*rrip_lookup_name(struct open_file *f,
79ca987d46SWarner Losh 		    struct iso_directory_record *dp, int lenskip, size_t *len);
80ca987d46SWarner Losh static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
81ca987d46SWarner Losh 		    const char *identifier, struct iso_directory_record *dp,
82ca987d46SWarner Losh 		    int lenskip);
83ca987d46SWarner Losh 
84ca987d46SWarner Losh struct fs_ops cd9660_fsops = {
85ca987d46SWarner Losh 	"cd9660",
86ca987d46SWarner Losh 	cd9660_open,
87ca987d46SWarner Losh 	cd9660_close,
88ca987d46SWarner Losh 	cd9660_read,
89ca987d46SWarner Losh 	cd9660_write,
90ca987d46SWarner Losh 	cd9660_seek,
91ca987d46SWarner Losh 	cd9660_stat,
92ca987d46SWarner Losh 	cd9660_readdir
93ca987d46SWarner Losh };
94ca987d46SWarner Losh 
95ca987d46SWarner Losh #define	F_ISDIR		0x0001		/* Directory */
96ca987d46SWarner Losh #define	F_ROOTDIR	0x0002		/* Root directory */
97ca987d46SWarner Losh #define	F_RR		0x0004		/* Rock Ridge on this volume */
98ca987d46SWarner Losh 
99ca987d46SWarner Losh struct file {
100ca987d46SWarner Losh 	int 		f_flags;	/* file flags */
101ca987d46SWarner Losh 	off_t 		f_off;		/* Current offset within file */
102ca987d46SWarner Losh 	daddr_t 	f_bno;		/* Starting block number */
103ca987d46SWarner Losh 	off_t 		f_size;		/* Size of file */
104ca987d46SWarner Losh 	daddr_t		f_buf_blkno;	/* block number of data block */
105ca987d46SWarner Losh 	char		*f_buf;		/* buffer for data block */
106ca987d46SWarner Losh 	int		f_susp_skip;	/* len_skip for SUSP records */
107ca987d46SWarner Losh };
108ca987d46SWarner Losh 
109ca987d46SWarner Losh struct ptable_ent {
110ca987d46SWarner Losh 	char namlen	[ISODCL( 1, 1)];	/* 711 */
111ca987d46SWarner Losh 	char extlen	[ISODCL( 2, 2)];	/* 711 */
112ca987d46SWarner Losh 	char block	[ISODCL( 3, 6)];	/* 732 */
113ca987d46SWarner Losh 	char parent	[ISODCL( 7, 8)];	/* 722 */
114ca987d46SWarner Losh 	char name	[1];
115ca987d46SWarner Losh };
116ca987d46SWarner Losh #define	PTFIXSZ		8
117ca987d46SWarner Losh #define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
118ca987d46SWarner Losh 
119ca987d46SWarner Losh #define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
120ca987d46SWarner Losh 
121ca987d46SWarner Losh static ISO_SUSP_HEADER *
122ca987d46SWarner Losh susp_lookup_record(struct open_file *f, const char *identifier,
123ca987d46SWarner Losh     struct iso_directory_record *dp, int lenskip)
124ca987d46SWarner Losh {
125ca987d46SWarner Losh 	static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
126ca987d46SWarner Losh 	ISO_SUSP_HEADER *sh;
127ca987d46SWarner Losh 	ISO_RRIP_CONT *shc;
128ca987d46SWarner Losh 	char *p, *end;
129ca987d46SWarner Losh 	int error;
130ca987d46SWarner Losh 	size_t read;
131ca987d46SWarner Losh 
132ca987d46SWarner Losh 	p = dp->name + isonum_711(dp->name_len) + lenskip;
133ca987d46SWarner Losh 	/* Names of even length have a padding byte after the name. */
134ca987d46SWarner Losh 	if ((isonum_711(dp->name_len) & 1) == 0)
135ca987d46SWarner Losh 		p++;
136ca987d46SWarner Losh 	end = (char *)dp + isonum_711(dp->length);
137ca987d46SWarner Losh 	while (p + 3 < end) {
138ca987d46SWarner Losh 		sh = (ISO_SUSP_HEADER *)p;
139ca987d46SWarner Losh 		if (bcmp(sh->type, identifier, 2) == 0)
140ca987d46SWarner Losh 			return (sh);
141ca987d46SWarner Losh 		if (bcmp(sh->type, SUSP_STOP, 2) == 0)
142ca987d46SWarner Losh 			return (NULL);
143ca987d46SWarner Losh 		if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
144ca987d46SWarner Losh 			shc = (ISO_RRIP_CONT *)sh;
145ca987d46SWarner Losh 			error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
146ca987d46SWarner Losh 			    cdb2devb(isonum_733(shc->location)),
147ca987d46SWarner Losh 			    ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
148ca987d46SWarner Losh 
149ca987d46SWarner Losh 			/* Bail if it fails. */
150ca987d46SWarner Losh 			if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
151ca987d46SWarner Losh 				return (NULL);
152ca987d46SWarner Losh 			p = susp_buffer + isonum_733(shc->offset);
153ca987d46SWarner Losh 			end = p + isonum_733(shc->length);
154ca987d46SWarner Losh 		} else {
155ca987d46SWarner Losh 			/* Ignore this record and skip to the next. */
156ca987d46SWarner Losh 			p += isonum_711(sh->length);
157ca987d46SWarner Losh 
158ca987d46SWarner Losh 			/* Avoid infinite loops with corrupted file systems */
159ca987d46SWarner Losh 			if (isonum_711(sh->length) == 0)
160ca987d46SWarner Losh 				return (NULL);
161ca987d46SWarner Losh 		}
162ca987d46SWarner Losh 	}
163ca987d46SWarner Losh 	return (NULL);
164ca987d46SWarner Losh }
165ca987d46SWarner Losh 
166ca987d46SWarner Losh static char *
167ca987d46SWarner Losh rrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
168ca987d46SWarner Losh     int lenskip, size_t *len)
169ca987d46SWarner Losh {
170ca987d46SWarner Losh 	ISO_RRIP_ALTNAME *p;
171ca987d46SWarner Losh 
172ca987d46SWarner Losh 	if (len == NULL)
173ca987d46SWarner Losh 		return (NULL);
174ca987d46SWarner Losh 
175ca987d46SWarner Losh 	p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
176ca987d46SWarner Losh 	if (p == NULL)
177ca987d46SWarner Losh 		return (NULL);
178ca987d46SWarner Losh 	switch (*p->flags) {
179ca987d46SWarner Losh 	case ISO_SUSP_CFLAG_CURRENT:
180ca987d46SWarner Losh 		*len = 1;
181ca987d46SWarner Losh 		return (".");
182ca987d46SWarner Losh 	case ISO_SUSP_CFLAG_PARENT:
183ca987d46SWarner Losh 		*len = 2;
184ca987d46SWarner Losh 		return ("..");
185ca987d46SWarner Losh 	case 0:
186ca987d46SWarner Losh 		*len = isonum_711(p->h.length) - 5;
187ca987d46SWarner Losh 		return ((char *)p + 5);
188ca987d46SWarner Losh 	default:
189ca987d46SWarner Losh 		/*
190ca987d46SWarner Losh 		 * We don't handle hostnames or continued names as they are
191ca987d46SWarner Losh 		 * too hard, so just bail and use the default name.
192ca987d46SWarner Losh 		 */
193ca987d46SWarner Losh 		return (NULL);
194ca987d46SWarner Losh 	}
195ca987d46SWarner Losh }
196ca987d46SWarner Losh 
197ca987d46SWarner Losh static int
198ca987d46SWarner Losh rrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
199ca987d46SWarner Losh {
200ca987d46SWarner Losh 	ISO_SUSP_PRESENT *sp;
201ca987d46SWarner Losh 	ISO_RRIP_EXTREF *er;
202ca987d46SWarner Losh 	char *p;
203ca987d46SWarner Losh 
204ca987d46SWarner Losh 	/* First, see if we can find a SP field. */
205ca987d46SWarner Losh 	p = dp->name + isonum_711(dp->name_len);
206ca987d46SWarner Losh 	if (p > (char *)dp + isonum_711(dp->length))
207ca987d46SWarner Losh 		return (0);
208ca987d46SWarner Losh 	sp = (ISO_SUSP_PRESENT *)p;
209ca987d46SWarner Losh 	if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
210ca987d46SWarner Losh 		return (0);
211ca987d46SWarner Losh 	if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT))
212ca987d46SWarner Losh 		return (0);
213ca987d46SWarner Losh 	if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
214ca987d46SWarner Losh 		return (0);
215ca987d46SWarner Losh 	*lenskip = isonum_711(sp->len_skp);
216ca987d46SWarner Losh 
217ca987d46SWarner Losh 	/*
218ca987d46SWarner Losh 	 * Now look for an ER field.  If RRIP is present, then there must
219ca987d46SWarner Losh 	 * be at least one of these.  It would be more pedantic to walk
220ca987d46SWarner Losh 	 * through the list of fields looking for a Rock Ridge ER field.
221ca987d46SWarner Losh 	 */
222ca987d46SWarner Losh 	er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
223ca987d46SWarner Losh 	if (er == NULL)
224ca987d46SWarner Losh 		return (0);
225ca987d46SWarner Losh 	return (1);
226ca987d46SWarner Losh }
227ca987d46SWarner Losh 
228ca987d46SWarner Losh static int
229ca987d46SWarner Losh dirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
230ca987d46SWarner Losh     int use_rrip, int lenskip)
231ca987d46SWarner Losh {
232ca987d46SWarner Losh 	size_t len;
233ca987d46SWarner Losh 	char *cp;
234ca987d46SWarner Losh 	int i, icase;
235ca987d46SWarner Losh 
236ca987d46SWarner Losh 	if (use_rrip)
237ca987d46SWarner Losh 		cp = rrip_lookup_name(f, dp, lenskip, &len);
238ca987d46SWarner Losh 	else
239ca987d46SWarner Losh 		cp = NULL;
240ca987d46SWarner Losh 	if (cp == NULL) {
241ca987d46SWarner Losh 		len = isonum_711(dp->name_len);
242ca987d46SWarner Losh 		cp = dp->name;
243ca987d46SWarner Losh 		icase = 1;
244ca987d46SWarner Losh 	} else
245ca987d46SWarner Losh 		icase = 0;
246ca987d46SWarner Losh 	for (i = len; --i >= 0; path++, cp++) {
247ca987d46SWarner Losh 		if (!*path || *path == '/')
248ca987d46SWarner Losh 			break;
249ca987d46SWarner Losh 		if (*path == *cp)
250ca987d46SWarner Losh 			continue;
251ca987d46SWarner Losh 		if (!icase && toupper(*path) == *cp)
252ca987d46SWarner Losh 			continue;
253ca987d46SWarner Losh 		return 0;
254ca987d46SWarner Losh 	}
255ca987d46SWarner Losh 	if (*path && *path != '/')
256ca987d46SWarner Losh 		return 0;
257ca987d46SWarner Losh 	/*
258ca987d46SWarner Losh 	 * Allow stripping of trailing dots and the version number.
259ca987d46SWarner Losh 	 * Note that this will find the first instead of the last version
260ca987d46SWarner Losh 	 * of a file.
261ca987d46SWarner Losh 	 */
262ca987d46SWarner Losh 	if (i >= 0 && (*cp == ';' || *cp == '.')) {
263ca987d46SWarner Losh 		/* This is to prevent matching of numeric extensions */
264ca987d46SWarner Losh 		if (*cp == '.' && cp[1] != ';')
265ca987d46SWarner Losh 			return 0;
266ca987d46SWarner Losh 		while (--i >= 0)
267ca987d46SWarner Losh 			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
268ca987d46SWarner Losh 				return 0;
269ca987d46SWarner Losh 	}
270ca987d46SWarner Losh 	return 1;
271ca987d46SWarner Losh }
272ca987d46SWarner Losh 
273ca987d46SWarner Losh static int
274ca987d46SWarner Losh cd9660_open(const char *path, struct open_file *f)
275ca987d46SWarner Losh {
276ca987d46SWarner Losh 	struct file *fp = NULL;
277ca987d46SWarner Losh 	void *buf;
278ca987d46SWarner Losh 	struct iso_primary_descriptor *vd;
279ca987d46SWarner Losh 	size_t buf_size, read, dsize, off;
280ca987d46SWarner Losh 	daddr_t bno, boff;
281ca987d46SWarner Losh 	struct iso_directory_record rec;
282ca987d46SWarner Losh 	struct iso_directory_record *dp = NULL;
283ca987d46SWarner Losh 	int rc, first, use_rrip, lenskip;
284ca987d46SWarner Losh 
285ca987d46SWarner Losh 	/* First find the volume descriptor */
286ca987d46SWarner Losh 	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
287ca987d46SWarner Losh 	vd = buf;
288ca987d46SWarner Losh 	for (bno = 16;; bno++) {
289ca987d46SWarner Losh 		twiddle(1);
290ca987d46SWarner Losh 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
291ca987d46SWarner Losh 					ISO_DEFAULT_BLOCK_SIZE, buf, &read);
292ca987d46SWarner Losh 		if (rc)
293ca987d46SWarner Losh 			goto out;
294ca987d46SWarner Losh 		if (read != ISO_DEFAULT_BLOCK_SIZE) {
295ca987d46SWarner Losh 			rc = EIO;
296ca987d46SWarner Losh 			goto out;
297ca987d46SWarner Losh 		}
298ca987d46SWarner Losh 		rc = EINVAL;
299ca987d46SWarner Losh 		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
300ca987d46SWarner Losh 			goto out;
301ca987d46SWarner Losh 		if (isonum_711(vd->type) == ISO_VD_END)
302ca987d46SWarner Losh 			goto out;
303ca987d46SWarner Losh 		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
304ca987d46SWarner Losh 			break;
305ca987d46SWarner Losh 	}
306ca987d46SWarner Losh 	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
307ca987d46SWarner Losh 		goto out;
308ca987d46SWarner Losh 
309ca987d46SWarner Losh 	rec = *(struct iso_directory_record *) vd->root_directory_record;
310ca987d46SWarner Losh 	if (*path == '/') path++; /* eat leading '/' */
311ca987d46SWarner Losh 
312ca987d46SWarner Losh 	first = 1;
313ca987d46SWarner Losh 	use_rrip = 0;
314ca987d46SWarner Losh 	while (*path) {
315ca987d46SWarner Losh 		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
316ca987d46SWarner Losh 		dsize = isonum_733(rec.size);
317ca987d46SWarner Losh 		off = 0;
318ca987d46SWarner Losh 		boff = 0;
319ca987d46SWarner Losh 
320ca987d46SWarner Losh 		while (off < dsize) {
321ca987d46SWarner Losh 			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
322ca987d46SWarner Losh 				twiddle(1);
323ca987d46SWarner Losh 				rc = f->f_dev->dv_strategy
324ca987d46SWarner Losh 					(f->f_devdata, F_READ,
325ca987d46SWarner Losh 					 cdb2devb(bno + boff),
326ca987d46SWarner Losh 					 ISO_DEFAULT_BLOCK_SIZE,
327ca987d46SWarner Losh 					 buf, &read);
328ca987d46SWarner Losh 				if (rc)
329ca987d46SWarner Losh 					goto out;
330ca987d46SWarner Losh 				if (read != ISO_DEFAULT_BLOCK_SIZE) {
331ca987d46SWarner Losh 					rc = EIO;
332ca987d46SWarner Losh 					goto out;
333ca987d46SWarner Losh 				}
334ca987d46SWarner Losh 				boff++;
335ca987d46SWarner Losh 				dp = (struct iso_directory_record *) buf;
336ca987d46SWarner Losh 			}
337ca987d46SWarner Losh 			if (isonum_711(dp->length) == 0) {
338ca987d46SWarner Losh 			    /* skip to next block, if any */
339ca987d46SWarner Losh 			    off = boff * ISO_DEFAULT_BLOCK_SIZE;
340ca987d46SWarner Losh 			    continue;
341ca987d46SWarner Losh 			}
342ca987d46SWarner Losh 
343ca987d46SWarner Losh 			/* See if RRIP is in use. */
344ca987d46SWarner Losh 			if (first)
345ca987d46SWarner Losh 				use_rrip = rrip_check(f, dp, &lenskip);
346ca987d46SWarner Losh 
347ca987d46SWarner Losh 			if (dirmatch(f, path, dp, use_rrip,
348ca987d46SWarner Losh 				first ? 0 : lenskip)) {
349ca987d46SWarner Losh 				first = 0;
350ca987d46SWarner Losh 				break;
351ca987d46SWarner Losh 			} else
352ca987d46SWarner Losh 				first = 0;
353ca987d46SWarner Losh 
354ca987d46SWarner Losh 			dp = (struct iso_directory_record *)
355ca987d46SWarner Losh 				((char *) dp + isonum_711(dp->length));
356ca987d46SWarner Losh 			/* If the new block has zero length, it is padding. */
357ca987d46SWarner Losh 			if (isonum_711(dp->length) == 0) {
358ca987d46SWarner Losh 				/* Skip to next block, if any. */
359ca987d46SWarner Losh 				off = boff * ISO_DEFAULT_BLOCK_SIZE;
360ca987d46SWarner Losh 				continue;
361ca987d46SWarner Losh 			}
362ca987d46SWarner Losh 			off += isonum_711(dp->length);
363ca987d46SWarner Losh 		}
364ca987d46SWarner Losh 		if (off >= dsize) {
365ca987d46SWarner Losh 			rc = ENOENT;
366ca987d46SWarner Losh 			goto out;
367ca987d46SWarner Losh 		}
368ca987d46SWarner Losh 
369ca987d46SWarner Losh 		rec = *dp;
370ca987d46SWarner Losh 		while (*path && *path != '/') /* look for next component */
371ca987d46SWarner Losh 			path++;
372ca987d46SWarner Losh 		if (*path) path++; /* skip '/' */
373ca987d46SWarner Losh 	}
374ca987d46SWarner Losh 
375ca987d46SWarner Losh 	/* allocate file system specific data structure */
376ca987d46SWarner Losh 	fp = malloc(sizeof(struct file));
377ca987d46SWarner Losh 	bzero(fp, sizeof(struct file));
378ca987d46SWarner Losh 	f->f_fsdata = (void *)fp;
379ca987d46SWarner Losh 
380ca987d46SWarner Losh 	if ((isonum_711(rec.flags) & 2) != 0) {
381ca987d46SWarner Losh 		fp->f_flags = F_ISDIR;
382ca987d46SWarner Losh 	}
383ca987d46SWarner Losh 	if (first) {
384ca987d46SWarner Losh 		fp->f_flags |= F_ROOTDIR;
385ca987d46SWarner Losh 
386ca987d46SWarner Losh 		/* Check for Rock Ridge since we didn't in the loop above. */
387ca987d46SWarner Losh 		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
388ca987d46SWarner Losh 		twiddle(1);
389ca987d46SWarner Losh 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
390ca987d46SWarner Losh 		    ISO_DEFAULT_BLOCK_SIZE, buf, &read);
391ca987d46SWarner Losh 		if (rc)
392ca987d46SWarner Losh 			goto out;
393ca987d46SWarner Losh 		if (read != ISO_DEFAULT_BLOCK_SIZE) {
394ca987d46SWarner Losh 			rc = EIO;
395ca987d46SWarner Losh 			goto out;
396ca987d46SWarner Losh 		}
397ca987d46SWarner Losh 		dp = (struct iso_directory_record *)buf;
398ca987d46SWarner Losh 		use_rrip = rrip_check(f, dp, &lenskip);
399ca987d46SWarner Losh 	}
400ca987d46SWarner Losh 	if (use_rrip) {
401ca987d46SWarner Losh 		fp->f_flags |= F_RR;
402ca987d46SWarner Losh 		fp->f_susp_skip = lenskip;
403ca987d46SWarner Losh 	}
404ca987d46SWarner Losh 	fp->f_off = 0;
405ca987d46SWarner Losh 	fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
406ca987d46SWarner Losh 	fp->f_size = isonum_733(rec.size);
407ca987d46SWarner Losh 	free(buf);
408ca987d46SWarner Losh 
409ca987d46SWarner Losh 	return 0;
410ca987d46SWarner Losh 
411ca987d46SWarner Losh out:
412ca987d46SWarner Losh 	if (fp)
413ca987d46SWarner Losh 		free(fp);
414ca987d46SWarner Losh 	free(buf);
415ca987d46SWarner Losh 
416ca987d46SWarner Losh 	return rc;
417ca987d46SWarner Losh }
418ca987d46SWarner Losh 
419ca987d46SWarner Losh static int
420ca987d46SWarner Losh cd9660_close(struct open_file *f)
421ca987d46SWarner Losh {
422ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
423ca987d46SWarner Losh 
424ca987d46SWarner Losh 	f->f_fsdata = NULL;
425ca987d46SWarner Losh 	free(fp);
426ca987d46SWarner Losh 
427ca987d46SWarner Losh 	return 0;
428ca987d46SWarner Losh }
429ca987d46SWarner Losh 
430ca987d46SWarner Losh static int
431ca987d46SWarner Losh buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
432ca987d46SWarner Losh {
433ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
434ca987d46SWarner Losh 	daddr_t blkno, blkoff;
435ca987d46SWarner Losh 	int rc = 0;
436ca987d46SWarner Losh 	size_t read;
437ca987d46SWarner Losh 
438ca987d46SWarner Losh 	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
439ca987d46SWarner Losh 	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
440ca987d46SWarner Losh 
441ca987d46SWarner Losh 	if (blkno != fp->f_buf_blkno) {
442ca987d46SWarner Losh 		if (fp->f_buf == (char *)0)
443ca987d46SWarner Losh 			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
444ca987d46SWarner Losh 
445ca987d46SWarner Losh 		twiddle(16);
446ca987d46SWarner Losh 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
447ca987d46SWarner Losh 		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE,
448ca987d46SWarner Losh 		    fp->f_buf, &read);
449ca987d46SWarner Losh 		if (rc)
450ca987d46SWarner Losh 			return (rc);
451ca987d46SWarner Losh 		if (read != ISO_DEFAULT_BLOCK_SIZE)
452ca987d46SWarner Losh 			return (EIO);
453ca987d46SWarner Losh 
454ca987d46SWarner Losh 		fp->f_buf_blkno = blkno;
455ca987d46SWarner Losh 	}
456ca987d46SWarner Losh 
457ca987d46SWarner Losh 	*buf_p = fp->f_buf + blkoff;
458ca987d46SWarner Losh 	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
459ca987d46SWarner Losh 
460ca987d46SWarner Losh 	if (*size_p > fp->f_size - fp->f_off)
461ca987d46SWarner Losh 		*size_p = fp->f_size - fp->f_off;
462ca987d46SWarner Losh 	return (rc);
463ca987d46SWarner Losh }
464ca987d46SWarner Losh 
465ca987d46SWarner Losh static int
466ca987d46SWarner Losh cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
467ca987d46SWarner Losh {
468ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
469ca987d46SWarner Losh 	char *buf, *addr;
470ca987d46SWarner Losh 	size_t buf_size, csize;
471ca987d46SWarner Losh 	int rc = 0;
472ca987d46SWarner Losh 
473ca987d46SWarner Losh 	addr = start;
474ca987d46SWarner Losh 	while (size) {
475ca987d46SWarner Losh 		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
476ca987d46SWarner Losh 			break;
477ca987d46SWarner Losh 
478ca987d46SWarner Losh 		rc = buf_read_file(f, &buf, &buf_size);
479ca987d46SWarner Losh 		if (rc)
480ca987d46SWarner Losh 			break;
481ca987d46SWarner Losh 
482ca987d46SWarner Losh 		csize = size > buf_size ? buf_size : size;
483ca987d46SWarner Losh 		bcopy(buf, addr, csize);
484ca987d46SWarner Losh 
485ca987d46SWarner Losh 		fp->f_off += csize;
486ca987d46SWarner Losh 		addr += csize;
487ca987d46SWarner Losh 		size -= csize;
488ca987d46SWarner Losh 	}
489ca987d46SWarner Losh 	if (resid)
490ca987d46SWarner Losh 		*resid = size;
491ca987d46SWarner Losh 	return (rc);
492ca987d46SWarner Losh }
493ca987d46SWarner Losh 
494ca987d46SWarner Losh static int
495ca987d46SWarner Losh cd9660_readdir(struct open_file *f, struct dirent *d)
496ca987d46SWarner Losh {
497ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
498ca987d46SWarner Losh 	struct iso_directory_record *ep;
499ca987d46SWarner Losh 	size_t buf_size, reclen, namelen;
500ca987d46SWarner Losh 	int error = 0;
501ca987d46SWarner Losh 	int lenskip;
502ca987d46SWarner Losh 	char *buf, *name;
503ca987d46SWarner Losh 
504ca987d46SWarner Losh again:
505ca987d46SWarner Losh 	if (fp->f_off >= fp->f_size)
506ca987d46SWarner Losh 		return (ENOENT);
507ca987d46SWarner Losh 	error = buf_read_file(f, &buf, &buf_size);
508ca987d46SWarner Losh 	if (error)
509ca987d46SWarner Losh 		return (error);
510ca987d46SWarner Losh 	ep = (struct iso_directory_record *)buf;
511ca987d46SWarner Losh 
512ca987d46SWarner Losh 	if (isonum_711(ep->length) == 0) {
513ca987d46SWarner Losh 		daddr_t blkno;
514ca987d46SWarner Losh 
515ca987d46SWarner Losh 		/* skip to next block, if any */
516ca987d46SWarner Losh 		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
517ca987d46SWarner Losh 		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
518ca987d46SWarner Losh 		goto again;
519ca987d46SWarner Losh 	}
520ca987d46SWarner Losh 
521ca987d46SWarner Losh 	if (fp->f_flags & F_RR) {
522ca987d46SWarner Losh 		if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
523ca987d46SWarner Losh 			lenskip = 0;
524ca987d46SWarner Losh 		else
525ca987d46SWarner Losh 			lenskip = fp->f_susp_skip;
526ca987d46SWarner Losh 		name = rrip_lookup_name(f, ep, lenskip, &namelen);
527ca987d46SWarner Losh 	} else
528ca987d46SWarner Losh 		name = NULL;
529ca987d46SWarner Losh 	if (name == NULL) {
530ca987d46SWarner Losh 		namelen = isonum_711(ep->name_len);
531ca987d46SWarner Losh 		name = ep->name;
532ca987d46SWarner Losh 		if (namelen == 1) {
533ca987d46SWarner Losh 			if (ep->name[0] == 0)
534ca987d46SWarner Losh 				name = ".";
535ca987d46SWarner Losh 			else if (ep->name[0] == 1) {
536ca987d46SWarner Losh 				namelen = 2;
537ca987d46SWarner Losh 				name = "..";
538ca987d46SWarner Losh 			}
539ca987d46SWarner Losh 		}
540ca987d46SWarner Losh 	}
541ca987d46SWarner Losh 	reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
542ca987d46SWarner Losh 	reclen = (reclen + 3) & ~3;
543ca987d46SWarner Losh 
544ca987d46SWarner Losh 	d->d_fileno = isonum_733(ep->extent);
545ca987d46SWarner Losh 	d->d_reclen = reclen;
546ca987d46SWarner Losh 	if (isonum_711(ep->flags) & 2)
547ca987d46SWarner Losh 		d->d_type = DT_DIR;
548ca987d46SWarner Losh 	else
549ca987d46SWarner Losh 		d->d_type = DT_REG;
550ca987d46SWarner Losh 	d->d_namlen = namelen;
551ca987d46SWarner Losh 
552ca987d46SWarner Losh 	bcopy(name, d->d_name, d->d_namlen);
553ca987d46SWarner Losh 	d->d_name[d->d_namlen] = 0;
554ca987d46SWarner Losh 
555ca987d46SWarner Losh 	fp->f_off += isonum_711(ep->length);
556ca987d46SWarner Losh 	return (0);
557ca987d46SWarner Losh }
558ca987d46SWarner Losh 
559ca987d46SWarner Losh static int
560ca987d46SWarner Losh cd9660_write(struct open_file *f __unused, void *start __unused, size_t size __unused, size_t *resid __unused)
561ca987d46SWarner Losh {
562ca987d46SWarner Losh 	return EROFS;
563ca987d46SWarner Losh }
564ca987d46SWarner Losh 
565ca987d46SWarner Losh static off_t
566ca987d46SWarner Losh cd9660_seek(struct open_file *f, off_t offset, int where)
567ca987d46SWarner Losh {
568ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
569ca987d46SWarner Losh 
570ca987d46SWarner Losh 	switch (where) {
571ca987d46SWarner Losh 	case SEEK_SET:
572ca987d46SWarner Losh 		fp->f_off = offset;
573ca987d46SWarner Losh 		break;
574ca987d46SWarner Losh 	case SEEK_CUR:
575ca987d46SWarner Losh 		fp->f_off += offset;
576ca987d46SWarner Losh 		break;
577ca987d46SWarner Losh 	case SEEK_END:
578ca987d46SWarner Losh 		fp->f_off = fp->f_size - offset;
579ca987d46SWarner Losh 		break;
580ca987d46SWarner Losh 	default:
581ca987d46SWarner Losh 		return -1;
582ca987d46SWarner Losh 	}
583ca987d46SWarner Losh 	return fp->f_off;
584ca987d46SWarner Losh }
585ca987d46SWarner Losh 
586ca987d46SWarner Losh static int
587ca987d46SWarner Losh cd9660_stat(struct open_file *f, struct stat *sb)
588ca987d46SWarner Losh {
589ca987d46SWarner Losh 	struct file *fp = (struct file *)f->f_fsdata;
590ca987d46SWarner Losh 
591ca987d46SWarner Losh 	/* only important stuff */
592ca987d46SWarner Losh 	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
593ca987d46SWarner Losh 	if (fp->f_flags & F_ISDIR)
594ca987d46SWarner Losh 		sb->st_mode |= S_IFDIR;
595ca987d46SWarner Losh 	else
596ca987d46SWarner Losh 		sb->st_mode |= S_IFREG;
597ca987d46SWarner Losh 	sb->st_uid = sb->st_gid = 0;
598ca987d46SWarner Losh 	sb->st_size = fp->f_size;
599ca987d46SWarner Losh 	return 0;
600ca987d46SWarner Losh }
601