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