xref: /illumos-gate/usr/src/boot/libsa/cd9660.c (revision 22028508)
1199767f8SToomas Soome /*	$NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $	*/
2199767f8SToomas Soome 
3199767f8SToomas Soome /*
4199767f8SToomas Soome  * Copyright (C) 1996 Wolfgang Solfrank.
5199767f8SToomas Soome  * Copyright (C) 1996 TooLs GmbH.
6199767f8SToomas Soome  * All rights reserved.
7199767f8SToomas Soome  *
8199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
9199767f8SToomas Soome  * modification, are permitted provided that the following conditions
10199767f8SToomas Soome  * are met:
11199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
12199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
13199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
14199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
15199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
16199767f8SToomas Soome  * 3. All advertising materials mentioning features or use of this software
17199767f8SToomas Soome  *    must display the following acknowledgement:
18199767f8SToomas Soome  *	This product includes software developed by TooLs GmbH.
19199767f8SToomas Soome  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20199767f8SToomas Soome  *    derived from this software without specific prior written permission.
21199767f8SToomas Soome  *
32199767f8SToomas Soome  */
33199767f8SToomas Soome 
34199767f8SToomas Soome #include <sys/cdefs.h>
35199767f8SToomas Soome 
36199767f8SToomas Soome /*
37199767f8SToomas Soome  * Stand-alone ISO9660 file reading package.
38199767f8SToomas Soome  *
39199767f8SToomas Soome  * Note: This doesn't support Rock Ridge extensions, extended attributes,
40199767f8SToomas Soome  * blocksizes other than 2048 bytes, multi-extent files, etc.
41199767f8SToomas Soome  */
42199767f8SToomas Soome #include <sys/param.h>
43199767f8SToomas Soome #include <string.h>
444bf63a95SToomas Soome #include <stdbool.h>
45199767f8SToomas Soome #include <sys/dirent.h>
462a6e99a0SToomas Soome #include <fs/cd9660/iso.h>
472a6e99a0SToomas Soome #include <fs/cd9660/cd9660_rrip.h>
48199767f8SToomas Soome 
49199767f8SToomas Soome #include "stand.h"
50199767f8SToomas Soome 
51199767f8SToomas Soome #define	SUSP_CONTINUATION	"CE"
52199767f8SToomas Soome #define	SUSP_PRESENT		"SP"
53199767f8SToomas Soome #define	SUSP_STOP		"ST"
54199767f8SToomas Soome #define	SUSP_EXTREF		"ER"
55199767f8SToomas Soome #define	RRIP_NAME		"NM"
56199767f8SToomas Soome 
57199767f8SToomas Soome typedef struct {
58199767f8SToomas Soome 	ISO_SUSP_HEADER		h;
59734b3a42SToomas Soome 	uchar_t signature	[ISODCL(5,    6)];
60734b3a42SToomas Soome 	uchar_t len_skp		[ISODCL(7,    7)]; /* 711 */
61199767f8SToomas Soome } ISO_SUSP_PRESENT;
62734b3a42SToomas Soome 
63199767f8SToomas Soome static int	buf_read_file(struct open_file *f, char **buf_p,
64199767f8SToomas Soome 		    size_t *size_p);
65199767f8SToomas Soome static int	cd9660_open(const char *path, struct open_file *f);
66199767f8SToomas Soome static int	cd9660_close(struct open_file *f);
67199767f8SToomas Soome static int	cd9660_read(struct open_file *f, void *buf, size_t size,
68199767f8SToomas Soome 		    size_t *resid);
69199767f8SToomas Soome static off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
70199767f8SToomas Soome static int	cd9660_stat(struct open_file *f, struct stat *sb);
71199767f8SToomas Soome static int	cd9660_readdir(struct open_file *f, struct dirent *d);
72199767f8SToomas Soome static int	dirmatch(struct open_file *f, const char *path,
73199767f8SToomas Soome 		    struct iso_directory_record *dp, int use_rrip, int lenskip);
74199767f8SToomas Soome static int	rrip_check(struct open_file *f, struct iso_directory_record *dp,
75199767f8SToomas Soome 		    int *lenskip);
76199767f8SToomas Soome static char	*rrip_lookup_name(struct open_file *f,
77199767f8SToomas Soome 		    struct iso_directory_record *dp, int lenskip, size_t *len);
78199767f8SToomas Soome static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
79199767f8SToomas Soome 		    const char *identifier, struct iso_directory_record *dp,
80199767f8SToomas Soome 		    int lenskip);
81199767f8SToomas Soome 
82199767f8SToomas Soome struct fs_ops cd9660_fsops = {
83734b3a42SToomas Soome 	.fs_name = "cd9660",
84734b3a42SToomas Soome 	.fo_open = cd9660_open,
85734b3a42SToomas Soome 	.fo_close = cd9660_close,
86734b3a42SToomas Soome 	.fo_read = cd9660_read,
87734b3a42SToomas Soome 	.fo_write = null_write,
88734b3a42SToomas Soome 	.fo_seek = cd9660_seek,
89734b3a42SToomas Soome 	.fo_stat = cd9660_stat,
90734b3a42SToomas Soome 	.fo_readdir = cd9660_readdir
91199767f8SToomas Soome };
92199767f8SToomas Soome 
93199767f8SToomas Soome #define	F_ISDIR		0x0001		/* Directory */
94199767f8SToomas Soome #define	F_ROOTDIR	0x0002		/* Root directory */
95199767f8SToomas Soome #define	F_RR		0x0004		/* Rock Ridge on this volume */
96199767f8SToomas Soome 
97199767f8SToomas Soome struct file {
98734b3a42SToomas Soome 	int		f_flags;	/* file flags */
99734b3a42SToomas Soome 	off_t		f_off;		/* Current offset within file */
100734b3a42SToomas Soome 	daddr_t		f_bno;		/* Starting block number */
101734b3a42SToomas Soome 	off_t		f_size;		/* Size of file */
102734b3a42SToomas Soome 	daddr_t		f_buf_blkno;	/* block number of data block */
103199767f8SToomas Soome 	char		*f_buf;		/* buffer for data block */
104199767f8SToomas Soome 	int		f_susp_skip;	/* len_skip for SUSP records */
105199767f8SToomas Soome };
106199767f8SToomas Soome 
107199767f8SToomas Soome struct ptable_ent {
108734b3a42SToomas Soome 	char namlen	[ISODCL(1, 1)];	/* 711 */
109734b3a42SToomas Soome 	char extlen	[ISODCL(2, 2)];	/* 711 */
110734b3a42SToomas Soome 	char block	[ISODCL(3, 6)];	/* 732 */
111734b3a42SToomas Soome 	char parent	[ISODCL(7, 8)];	/* 722 */
112199767f8SToomas Soome 	char name	[1];
113199767f8SToomas Soome };
114199767f8SToomas Soome #define	PTFIXSZ		8
115199767f8SToomas Soome #define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
116199767f8SToomas Soome 
117199767f8SToomas Soome #define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
118199767f8SToomas Soome 
119199767f8SToomas Soome static ISO_SUSP_HEADER *
susp_lookup_record(struct open_file * f,const char * identifier,struct iso_directory_record * dp,int lenskip)120199767f8SToomas Soome susp_lookup_record(struct open_file *f, const char *identifier,
121199767f8SToomas Soome     struct iso_directory_record *dp, int lenskip)
122199767f8SToomas Soome {
123199767f8SToomas Soome 	static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
124199767f8SToomas Soome 	ISO_SUSP_HEADER *sh;
125199767f8SToomas Soome 	ISO_RRIP_CONT *shc;
126199767f8SToomas Soome 	char *p, *end;
127199767f8SToomas Soome 	int error;
128199767f8SToomas Soome 	size_t read;
129199767f8SToomas Soome 
130199767f8SToomas Soome 	p = dp->name + isonum_711(dp->name_len) + lenskip;
131199767f8SToomas Soome 	/* Names of even length have a padding byte after the name. */
132199767f8SToomas Soome 	if ((isonum_711(dp->name_len) & 1) == 0)
133199767f8SToomas Soome 		p++;
134199767f8SToomas Soome 	end = (char *)dp + isonum_711(dp->length);
135199767f8SToomas Soome 	while (p + 3 < end) {
136199767f8SToomas Soome 		sh = (ISO_SUSP_HEADER *)p;
137199767f8SToomas Soome 		if (bcmp(sh->type, identifier, 2) == 0)
138199767f8SToomas Soome 			return (sh);
139199767f8SToomas Soome 		if (bcmp(sh->type, SUSP_STOP, 2) == 0)
140199767f8SToomas Soome 			return (NULL);
141199767f8SToomas Soome 		if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
142199767f8SToomas Soome 			shc = (ISO_RRIP_CONT *)sh;
143199767f8SToomas Soome 			error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
14438dea910SToomas Soome 			    cdb2devb(isonum_733(shc->location)),
145199767f8SToomas Soome 			    ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
146199767f8SToomas Soome 
147199767f8SToomas Soome 			/* Bail if it fails. */
148199767f8SToomas Soome 			if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
149199767f8SToomas Soome 				return (NULL);
150199767f8SToomas Soome 			p = susp_buffer + isonum_733(shc->offset);
151199767f8SToomas Soome 			end = p + isonum_733(shc->length);
152199767f8SToomas Soome 		} else {
153199767f8SToomas Soome 			/* Ignore this record and skip to the next. */
154199767f8SToomas Soome 			p += isonum_711(sh->length);
155199767f8SToomas Soome 
156199767f8SToomas Soome 			/* Avoid infinite loops with corrupted file systems */
157199767f8SToomas Soome 			if (isonum_711(sh->length) == 0)
158199767f8SToomas Soome 				return (NULL);
159199767f8SToomas Soome 		}
160199767f8SToomas Soome 	}
161199767f8SToomas Soome 	return (NULL);
162199767f8SToomas Soome }
163199767f8SToomas Soome 
164199767f8SToomas Soome static char *
rrip_lookup_name(struct open_file * f,struct iso_directory_record * dp,int lenskip,size_t * len)165199767f8SToomas Soome rrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
166199767f8SToomas Soome     int lenskip, size_t *len)
167199767f8SToomas Soome {
168199767f8SToomas Soome 	ISO_RRIP_ALTNAME *p;
169199767f8SToomas Soome 
170199767f8SToomas Soome 	if (len == NULL)
171199767f8SToomas Soome 		return (NULL);
172199767f8SToomas Soome 
173199767f8SToomas Soome 	p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
174199767f8SToomas Soome 	if (p == NULL)
175199767f8SToomas Soome 		return (NULL);
176199767f8SToomas Soome 	switch (*p->flags) {
177199767f8SToomas Soome 	case ISO_SUSP_CFLAG_CURRENT:
178199767f8SToomas Soome 		*len = 1;
179199767f8SToomas Soome 		return (".");
180199767f8SToomas Soome 	case ISO_SUSP_CFLAG_PARENT:
181199767f8SToomas Soome 		*len = 2;
182199767f8SToomas Soome 		return ("..");
183199767f8SToomas Soome 	case 0:
184199767f8SToomas Soome 		*len = isonum_711(p->h.length) - 5;
185199767f8SToomas Soome 		return ((char *)p + 5);
186199767f8SToomas Soome 	default:
187199767f8SToomas Soome 		/*
188199767f8SToomas Soome 		 * We don't handle hostnames or continued names as they are
189199767f8SToomas Soome 		 * too hard, so just bail and use the default name.
190199767f8SToomas Soome 		 */
191199767f8SToomas Soome 		return (NULL);
192199767f8SToomas Soome 	}
193199767f8SToomas Soome }
194199767f8SToomas Soome 
195199767f8SToomas Soome static int
rrip_check(struct open_file * f,struct iso_directory_record * dp,int * lenskip)196199767f8SToomas Soome rrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
197199767f8SToomas Soome {
198199767f8SToomas Soome 	ISO_SUSP_PRESENT *sp;
199199767f8SToomas Soome 	ISO_RRIP_EXTREF *er;
200199767f8SToomas Soome 	char *p;
201199767f8SToomas Soome 
202199767f8SToomas Soome 	/* First, see if we can find a SP field. */
203199767f8SToomas Soome 	p = dp->name + isonum_711(dp->name_len);
204199767f8SToomas Soome 	if (p > (char *)dp + isonum_711(dp->length))
205199767f8SToomas Soome 		return (0);
206199767f8SToomas Soome 	sp = (ISO_SUSP_PRESENT *)p;
207199767f8SToomas Soome 	if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
208199767f8SToomas Soome 		return (0);
209734b3a42SToomas Soome 	if (isonum_711(sp->h.length) != sizeof (ISO_SUSP_PRESENT))
210199767f8SToomas Soome 		return (0);
211199767f8SToomas Soome 	if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
212199767f8SToomas Soome 		return (0);
213199767f8SToomas Soome 	*lenskip = isonum_711(sp->len_skp);
214199767f8SToomas Soome 
215199767f8SToomas Soome 	/*
216199767f8SToomas Soome 	 * Now look for an ER field.  If RRIP is present, then there must
217199767f8SToomas Soome 	 * be at least one of these.  It would be more pedantic to walk
218199767f8SToomas Soome 	 * through the list of fields looking for a Rock Ridge ER field.
219199767f8SToomas Soome 	 */
220199767f8SToomas Soome 	er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
221199767f8SToomas Soome 	if (er == NULL)
222199767f8SToomas Soome 		return (0);
223199767f8SToomas Soome 	return (1);
224199767f8SToomas Soome }
225199767f8SToomas Soome 
226199767f8SToomas Soome static int
dirmatch(struct open_file * f,const char * path,struct iso_directory_record * dp,int use_rrip,int lenskip)227199767f8SToomas Soome dirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
228199767f8SToomas Soome     int use_rrip, int lenskip)
229199767f8SToomas Soome {
2304bf63a95SToomas Soome 	size_t len, plen;
2314bf63a95SToomas Soome 	char *cp, *sep;
232199767f8SToomas Soome 	int i, icase;
233199767f8SToomas Soome 
234199767f8SToomas Soome 	if (use_rrip)
235199767f8SToomas Soome 		cp = rrip_lookup_name(f, dp, lenskip, &len);
236199767f8SToomas Soome 	else
237199767f8SToomas Soome 		cp = NULL;
238199767f8SToomas Soome 	if (cp == NULL) {
239199767f8SToomas Soome 		len = isonum_711(dp->name_len);
240199767f8SToomas Soome 		cp = dp->name;
241199767f8SToomas Soome 		icase = 1;
242199767f8SToomas Soome 	} else
243199767f8SToomas Soome 		icase = 0;
24408b0760eSToomas Soome 
2454bf63a95SToomas Soome 	sep = strchr(path, '/');
2464bf63a95SToomas Soome 	if (sep != NULL) {
2474bf63a95SToomas Soome 		plen = sep - path;
2484bf63a95SToomas Soome 	} else {
2494bf63a95SToomas Soome 		plen = strlen(path);
2504bf63a95SToomas Soome 	}
2514bf63a95SToomas Soome 
2524bf63a95SToomas Soome 	if (plen != len)
25308b0760eSToomas Soome 		return (0);
25408b0760eSToomas Soome 
255199767f8SToomas Soome 	for (i = len; --i >= 0; path++, cp++) {
256199767f8SToomas Soome 		if (!*path || *path == '/')
257199767f8SToomas Soome 			break;
258199767f8SToomas Soome 		if (*path == *cp)
259199767f8SToomas Soome 			continue;
260199767f8SToomas Soome 		if (!icase && toupper(*path) == *cp)
261199767f8SToomas Soome 			continue;
262734b3a42SToomas Soome 		return (0);
263199767f8SToomas Soome 	}
264199767f8SToomas Soome 	if (*path && *path != '/')
265734b3a42SToomas Soome 		return (0);
26608b0760eSToomas Soome 
267199767f8SToomas Soome 	/*
268199767f8SToomas Soome 	 * Allow stripping of trailing dots and the version number.
269199767f8SToomas Soome 	 * Note that this will find the first instead of the last version
270199767f8SToomas Soome 	 * of a file.
271199767f8SToomas Soome 	 */
272199767f8SToomas Soome 	if (i >= 0 && (*cp == ';' || *cp == '.')) {
273199767f8SToomas Soome 		/* This is to prevent matching of numeric extensions */
274199767f8SToomas Soome 		if (*cp == '.' && cp[1] != ';')
275734b3a42SToomas Soome 			return (0);
276199767f8SToomas Soome 		while (--i >= 0)
277199767f8SToomas Soome 			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
278734b3a42SToomas Soome 				return (0);
279199767f8SToomas Soome 	}
280734b3a42SToomas Soome 	return (1);
281199767f8SToomas Soome }
282199767f8SToomas Soome 
283199767f8SToomas Soome static int
cd9660_open(const char * path,struct open_file * f)284199767f8SToomas Soome cd9660_open(const char *path, struct open_file *f)
285199767f8SToomas Soome {
28600b63190SToomas Soome 	struct file *fp = NULL;
287199767f8SToomas Soome 	void *buf;
288199767f8SToomas Soome 	struct iso_primary_descriptor *vd;
289e58ba7f2SToomas Soome 	size_t read, dsize, off;
290199767f8SToomas Soome 	daddr_t bno, boff;
291199767f8SToomas Soome 	struct iso_directory_record rec;
29200b63190SToomas Soome 	struct iso_directory_record *dp = NULL;
293199767f8SToomas Soome 	int rc, first, use_rrip, lenskip;
2944bf63a95SToomas Soome 	bool isdir = false;
295199767f8SToomas Soome 
296199767f8SToomas Soome 	/* First find the volume descriptor */
297e58ba7f2SToomas Soome 	buf = malloc(MAX(ISO_DEFAULT_BLOCK_SIZE,
298e58ba7f2SToomas Soome 	    sizeof (struct iso_primary_descriptor)));
299199767f8SToomas Soome 	vd = buf;
300734b3a42SToomas Soome 	for (bno = 16; ; bno++) {
301199767f8SToomas Soome 		twiddle(1);
302199767f8SToomas Soome 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
30338dea910SToomas Soome 		    ISO_DEFAULT_BLOCK_SIZE, buf, &read);
304199767f8SToomas Soome 		if (rc)
305199767f8SToomas Soome 			goto out;
306199767f8SToomas Soome 		if (read != ISO_DEFAULT_BLOCK_SIZE) {
307199767f8SToomas Soome 			rc = EIO;
308199767f8SToomas Soome 			goto out;
309199767f8SToomas Soome 		}
310199767f8SToomas Soome 		rc = EINVAL;
311734b3a42SToomas Soome 		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof (vd->id)) != 0)
312199767f8SToomas Soome 			goto out;
313199767f8SToomas Soome 		if (isonum_711(vd->type) == ISO_VD_END)
314199767f8SToomas Soome 			goto out;
315199767f8SToomas Soome 		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
316199767f8SToomas Soome 			break;
317199767f8SToomas Soome 	}
318199767f8SToomas Soome 	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
319199767f8SToomas Soome 		goto out;
320199767f8SToomas Soome 
321567af71dSToomas Soome 	bcopy(vd->root_directory_record, &rec, sizeof (rec));
322199767f8SToomas Soome 	if (*path == '/') path++; /* eat leading '/' */
323199767f8SToomas Soome 
324199767f8SToomas Soome 	first = 1;
325199767f8SToomas Soome 	use_rrip = 0;
32664f9afd1SToomas Soome 	lenskip = 0;
327199767f8SToomas Soome 	while (*path) {
328199767f8SToomas Soome 		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
329199767f8SToomas Soome 		dsize = isonum_733(rec.size);
330199767f8SToomas Soome 		off = 0;
331199767f8SToomas Soome 		boff = 0;
332199767f8SToomas Soome 
333199767f8SToomas Soome 		while (off < dsize) {
334199767f8SToomas Soome 			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
335199767f8SToomas Soome 				twiddle(1);
336734b3a42SToomas Soome 				rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
337734b3a42SToomas Soome 				    cdb2devb(bno + boff),
338734b3a42SToomas Soome 				    ISO_DEFAULT_BLOCK_SIZE,
339734b3a42SToomas Soome 				    buf, &read);
340199767f8SToomas Soome 				if (rc)
341199767f8SToomas Soome 					goto out;
342199767f8SToomas Soome 				if (read != ISO_DEFAULT_BLOCK_SIZE) {
343199767f8SToomas Soome 					rc = EIO;
344199767f8SToomas Soome 					goto out;
345199767f8SToomas Soome 				}
346199767f8SToomas Soome 				boff++;
347734b3a42SToomas Soome 				dp = (struct iso_directory_record *)buf;
348199767f8SToomas Soome 			}
349199767f8SToomas Soome 			if (isonum_711(dp->length) == 0) {
350734b3a42SToomas Soome 				/* skip to next block, if any */
351734b3a42SToomas Soome 				off = boff * ISO_DEFAULT_BLOCK_SIZE;
352734b3a42SToomas Soome 				continue;
353199767f8SToomas Soome 			}
354199767f8SToomas Soome 
355199767f8SToomas Soome 			/* See if RRIP is in use. */
356199767f8SToomas Soome 			if (first)
357199767f8SToomas Soome 				use_rrip = rrip_check(f, dp, &lenskip);
358199767f8SToomas Soome 
359199767f8SToomas Soome 			if (dirmatch(f, path, dp, use_rrip,
360734b3a42SToomas Soome 			    first ? 0 : lenskip)) {
361199767f8SToomas Soome 				first = 0;
362199767f8SToomas Soome 				break;
36308b0760eSToomas Soome 			} else {
364199767f8SToomas Soome 				first = 0;
36508b0760eSToomas Soome 			}
366199767f8SToomas Soome 
367199767f8SToomas Soome 			dp = (struct iso_directory_record *)
368734b3a42SToomas Soome 			    ((char *)dp + isonum_711(dp->length));
369472d5772SToomas Soome 			/* If the new block has zero length, it is padding. */
370472d5772SToomas Soome 			if (isonum_711(dp->length) == 0) {
371472d5772SToomas Soome 				/* Skip to next block, if any. */
372472d5772SToomas Soome 				off = boff * ISO_DEFAULT_BLOCK_SIZE;
373472d5772SToomas Soome 				continue;
374472d5772SToomas Soome 			}
375199767f8SToomas Soome 			off += isonum_711(dp->length);
376199767f8SToomas Soome 		}
377199767f8SToomas Soome 		if (off >= dsize) {
378199767f8SToomas Soome 			rc = ENOENT;
379199767f8SToomas Soome 			goto out;
380199767f8SToomas Soome 		}
381199767f8SToomas Soome 
382199767f8SToomas Soome 		rec = *dp;
383199767f8SToomas Soome 		while (*path && *path != '/') /* look for next component */
384199767f8SToomas Soome 			path++;
3854bf63a95SToomas Soome 
3864bf63a95SToomas Soome 		if (*path)	/* this component was directory */
3874bf63a95SToomas Soome 			isdir = true;
3884bf63a95SToomas Soome 
3894bf63a95SToomas Soome 		while (*path == '/')
3904bf63a95SToomas Soome 			path++; /* skip '/' */
3914bf63a95SToomas Soome 
3924bf63a95SToomas Soome 		if (*path)	/* We do have next component. */
3934bf63a95SToomas Soome 			isdir = false;
3944bf63a95SToomas Soome 	}
3954bf63a95SToomas Soome 
3964bf63a95SToomas Soome 	/*
3974bf63a95SToomas Soome 	 * if the path had trailing / but the path does point to file,
3984bf63a95SToomas Soome 	 * report the error ENOTDIR.
3994bf63a95SToomas Soome 	 */
4004bf63a95SToomas Soome 	if (isdir == true && (isonum_711(rec.flags) & 2) == 0) {
4014bf63a95SToomas Soome 		rc = ENOTDIR;
4024bf63a95SToomas Soome 		goto out;
403199767f8SToomas Soome 	}
404199767f8SToomas Soome 
405199767f8SToomas Soome 	/* allocate file system specific data structure */
406734b3a42SToomas Soome 	fp = malloc(sizeof (struct file));
407734b3a42SToomas Soome 	bzero(fp, sizeof (struct file));
408199767f8SToomas Soome 	f->f_fsdata = (void *)fp;
409199767f8SToomas Soome 
410199767f8SToomas Soome 	if ((isonum_711(rec.flags) & 2) != 0) {
411199767f8SToomas Soome 		fp->f_flags = F_ISDIR;
412199767f8SToomas Soome 	}
413199767f8SToomas Soome 	if (first) {
414199767f8SToomas Soome 		fp->f_flags |= F_ROOTDIR;
415199767f8SToomas Soome 
416199767f8SToomas Soome 		/* Check for Rock Ridge since we didn't in the loop above. */
417199767f8SToomas Soome 		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
418199767f8SToomas Soome 		twiddle(1);
419199767f8SToomas Soome 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
42038dea910SToomas Soome 		    ISO_DEFAULT_BLOCK_SIZE, buf, &read);
421199767f8SToomas Soome 		if (rc)
422199767f8SToomas Soome 			goto out;
423199767f8SToomas Soome 		if (read != ISO_DEFAULT_BLOCK_SIZE) {
424199767f8SToomas Soome 			rc = EIO;
425199767f8SToomas Soome 			goto out;
426199767f8SToomas Soome 		}
427199767f8SToomas Soome 		dp = (struct iso_directory_record *)buf;
428199767f8SToomas Soome 		use_rrip = rrip_check(f, dp, &lenskip);
429199767f8SToomas Soome 	}
430199767f8SToomas Soome 	if (use_rrip) {
431199767f8SToomas Soome 		fp->f_flags |= F_RR;
432199767f8SToomas Soome 		fp->f_susp_skip = lenskip;
433199767f8SToomas Soome 	}
434199767f8SToomas Soome 	fp->f_off = 0;
435199767f8SToomas Soome 	fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
436199767f8SToomas Soome 	fp->f_size = isonum_733(rec.size);
437199767f8SToomas Soome 	free(buf);
438199767f8SToomas Soome 
439734b3a42SToomas Soome 	return (0);
440199767f8SToomas Soome 
441199767f8SToomas Soome out:
442e58ba7f2SToomas Soome 	free(fp);
443199767f8SToomas Soome 	free(buf);
444199767f8SToomas Soome 
445734b3a42SToomas Soome 	return (rc);
446199767f8SToomas Soome }
447199767f8SToomas Soome 
448199767f8SToomas Soome static int
cd9660_close(struct open_file * f)449199767f8SToomas Soome cd9660_close(struct open_file *f)
450199767f8SToomas Soome {
451199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
452199767f8SToomas Soome 
45300b63190SToomas Soome 	f->f_fsdata = NULL;
454199767f8SToomas Soome 	free(fp);
455199767f8SToomas Soome 
456734b3a42SToomas Soome 	return (0);
457199767f8SToomas Soome }
458199767f8SToomas Soome 
459199767f8SToomas Soome static int
buf_read_file(struct open_file * f,char ** buf_p,size_t * size_p)460199767f8SToomas Soome buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
461199767f8SToomas Soome {
462199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
463199767f8SToomas Soome 	daddr_t blkno, blkoff;
464199767f8SToomas Soome 	int rc = 0;
465199767f8SToomas Soome 	size_t read;
466199767f8SToomas Soome 
467199767f8SToomas Soome 	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
468199767f8SToomas Soome 	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
469199767f8SToomas Soome 
470199767f8SToomas Soome 	if (blkno != fp->f_buf_blkno) {
471199767f8SToomas Soome 		if (fp->f_buf == (char *)0)
472199767f8SToomas Soome 			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
473199767f8SToomas Soome 
474199767f8SToomas Soome 		twiddle(16);
475199767f8SToomas Soome 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
47638dea910SToomas Soome 		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE,
477199767f8SToomas Soome 		    fp->f_buf, &read);
478199767f8SToomas Soome 		if (rc)
479199767f8SToomas Soome 			return (rc);
480199767f8SToomas Soome 		if (read != ISO_DEFAULT_BLOCK_SIZE)
481199767f8SToomas Soome 			return (EIO);
482199767f8SToomas Soome 
483199767f8SToomas Soome 		fp->f_buf_blkno = blkno;
484199767f8SToomas Soome 	}
485199767f8SToomas Soome 
486199767f8SToomas Soome 	*buf_p = fp->f_buf + blkoff;
487199767f8SToomas Soome 	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
488199767f8SToomas Soome 
489199767f8SToomas Soome 	if (*size_p > fp->f_size - fp->f_off)
490199767f8SToomas Soome 		*size_p = fp->f_size - fp->f_off;
491199767f8SToomas Soome 	return (rc);
492199767f8SToomas Soome }
493199767f8SToomas Soome 
494199767f8SToomas Soome static int
cd9660_read(struct open_file * f,void * start,size_t size,size_t * resid)495199767f8SToomas Soome cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
496199767f8SToomas Soome {
497199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
498199767f8SToomas Soome 	char *buf, *addr;
499199767f8SToomas Soome 	size_t buf_size, csize;
500199767f8SToomas Soome 	int rc = 0;
501199767f8SToomas Soome 
502199767f8SToomas Soome 	addr = start;
503199767f8SToomas Soome 	while (size) {
504199767f8SToomas Soome 		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
505199767f8SToomas Soome 			break;
506199767f8SToomas Soome 
507199767f8SToomas Soome 		rc = buf_read_file(f, &buf, &buf_size);
508199767f8SToomas Soome 		if (rc)
509199767f8SToomas Soome 			break;
510199767f8SToomas Soome 
511199767f8SToomas Soome 		csize = size > buf_size ? buf_size : size;
512199767f8SToomas Soome 		bcopy(buf, addr, csize);
513199767f8SToomas Soome 
514199767f8SToomas Soome 		fp->f_off += csize;
515199767f8SToomas Soome 		addr += csize;
516199767f8SToomas Soome 		size -= csize;
517199767f8SToomas Soome 	}
518199767f8SToomas Soome 	if (resid)
519199767f8SToomas Soome 		*resid = size;
520199767f8SToomas Soome 	return (rc);
521199767f8SToomas Soome }
522199767f8SToomas Soome 
523199767f8SToomas Soome static int
cd9660_readdir(struct open_file * f,struct dirent * d)524199767f8SToomas Soome cd9660_readdir(struct open_file *f, struct dirent *d)
525199767f8SToomas Soome {
526199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
527199767f8SToomas Soome 	struct iso_directory_record *ep;
528199767f8SToomas Soome 	size_t buf_size, reclen, namelen;
529199767f8SToomas Soome 	int error = 0;
530199767f8SToomas Soome 	int lenskip;
531199767f8SToomas Soome 	char *buf, *name;
532199767f8SToomas Soome 
533199767f8SToomas Soome again:
534199767f8SToomas Soome 	if (fp->f_off >= fp->f_size)
535199767f8SToomas Soome 		return (ENOENT);
536199767f8SToomas Soome 	error = buf_read_file(f, &buf, &buf_size);
537199767f8SToomas Soome 	if (error)
538199767f8SToomas Soome 		return (error);
539199767f8SToomas Soome 	ep = (struct iso_directory_record *)buf;
540199767f8SToomas Soome 
541199767f8SToomas Soome 	if (isonum_711(ep->length) == 0) {
542199767f8SToomas Soome 		daddr_t blkno;
543734b3a42SToomas Soome 
544199767f8SToomas Soome 		/* skip to next block, if any */
545199767f8SToomas Soome 		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
546199767f8SToomas Soome 		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
547199767f8SToomas Soome 		goto again;
548199767f8SToomas Soome 	}
549199767f8SToomas Soome 
550199767f8SToomas Soome 	if (fp->f_flags & F_RR) {
551199767f8SToomas Soome 		if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
552199767f8SToomas Soome 			lenskip = 0;
553199767f8SToomas Soome 		else
554199767f8SToomas Soome 			lenskip = fp->f_susp_skip;
555199767f8SToomas Soome 		name = rrip_lookup_name(f, ep, lenskip, &namelen);
556199767f8SToomas Soome 	} else
557199767f8SToomas Soome 		name = NULL;
558199767f8SToomas Soome 	if (name == NULL) {
559199767f8SToomas Soome 		namelen = isonum_711(ep->name_len);
560199767f8SToomas Soome 		name = ep->name;
561199767f8SToomas Soome 		if (namelen == 1) {
562199767f8SToomas Soome 			if (ep->name[0] == 0)
563199767f8SToomas Soome 				name = ".";
564199767f8SToomas Soome 			else if (ep->name[0] == 1) {
565199767f8SToomas Soome 				namelen = 2;
566199767f8SToomas Soome 				name = "..";
567199767f8SToomas Soome 			}
568199767f8SToomas Soome 		}
569199767f8SToomas Soome 	}
570734b3a42SToomas Soome 	reclen = sizeof (struct dirent) - (MAXNAMLEN+1) + namelen + 1;
571199767f8SToomas Soome 	reclen = (reclen + 3) & ~3;
572199767f8SToomas Soome 
573199767f8SToomas Soome 	d->d_fileno = isonum_733(ep->extent);
574199767f8SToomas Soome 	d->d_reclen = reclen;
575199767f8SToomas Soome 	if (isonum_711(ep->flags) & 2)
576199767f8SToomas Soome 		d->d_type = DT_DIR;
577199767f8SToomas Soome 	else
578199767f8SToomas Soome 		d->d_type = DT_REG;
579199767f8SToomas Soome 	d->d_namlen = namelen;
580199767f8SToomas Soome 
581199767f8SToomas Soome 	bcopy(name, d->d_name, d->d_namlen);
582199767f8SToomas Soome 	d->d_name[d->d_namlen] = 0;
583199767f8SToomas Soome 
584199767f8SToomas Soome 	fp->f_off += isonum_711(ep->length);
585199767f8SToomas Soome 	return (0);
586199767f8SToomas Soome }
587199767f8SToomas Soome 
588199767f8SToomas Soome static off_t
cd9660_seek(struct open_file * f,off_t offset,int where)589199767f8SToomas Soome cd9660_seek(struct open_file *f, off_t offset, int where)
590199767f8SToomas Soome {
591199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
592199767f8SToomas Soome 
593199767f8SToomas Soome 	switch (where) {
594199767f8SToomas Soome 	case SEEK_SET:
595199767f8SToomas Soome 		fp->f_off = offset;
596199767f8SToomas Soome 		break;
597199767f8SToomas Soome 	case SEEK_CUR:
598199767f8SToomas Soome 		fp->f_off += offset;
599199767f8SToomas Soome 		break;
600199767f8SToomas Soome 	case SEEK_END:
601199767f8SToomas Soome 		fp->f_off = fp->f_size - offset;
602199767f8SToomas Soome 		break;
603199767f8SToomas Soome 	default:
604734b3a42SToomas Soome 		return (-1);
605199767f8SToomas Soome 	}
606734b3a42SToomas Soome 	return (fp->f_off);
607199767f8SToomas Soome }
608199767f8SToomas Soome 
609199767f8SToomas Soome static int
cd9660_stat(struct open_file * f,struct stat * sb)610199767f8SToomas Soome cd9660_stat(struct open_file *f, struct stat *sb)
611199767f8SToomas Soome {
612199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
613199767f8SToomas Soome 
614199767f8SToomas Soome 	/* only important stuff */
615199767f8SToomas Soome 	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
616199767f8SToomas Soome 	if (fp->f_flags & F_ISDIR)
617199767f8SToomas Soome 		sb->st_mode |= S_IFDIR;
618199767f8SToomas Soome 	else
619199767f8SToomas Soome 		sb->st_mode |= S_IFREG;
620199767f8SToomas Soome 	sb->st_uid = sb->st_gid = 0;
621199767f8SToomas Soome 	sb->st_size = fp->f_size;
622734b3a42SToomas Soome 	return (0);
623199767f8SToomas Soome }