cd9660.c revision 84221
1/*	$NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $	*/
2
3/*
4 * Copyright (C) 1996 Wolfgang Solfrank.
5 * Copyright (C) 1996 TooLs GmbH.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by TooLs GmbH.
19 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/lib/libstand/cd9660.c 84221 2001-09-30 22:28:01Z dillon $");
36
37/*
38 * Stand-alone ISO9660 file reading package.
39 *
40 * Note: This doesn't support Rock Ridge extensions, extended attributes,
41 * blocksizes other than 2048 bytes, multi-extent files, etc.
42 */
43#include <sys/param.h>
44#include <string.h>
45#include <sys/dirent.h>
46#include <isofs/cd9660/iso.h>
47
48#include "stand.h"
49
50static int	cd9660_open(const char *path, struct open_file *f);
51static int	cd9660_close(struct open_file *f);
52static int	cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid);
53static int	cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid);
54static off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
55static int	cd9660_stat(struct open_file *f, struct stat *sb);
56static int	cd9660_readdir(struct open_file *f, struct dirent *d);
57
58struct fs_ops cd9660_fsops = {
59	"cd9660",
60	cd9660_open,
61	cd9660_close,
62	cd9660_read,
63	cd9660_write,
64	cd9660_seek,
65	cd9660_stat,
66	cd9660_readdir
67};
68
69struct file {
70	int 		f_isdir;	/* nonzero if file is directory */
71	off_t 		f_off;		/* Current offset within file */
72	daddr_t 	f_bno;		/* Starting block number */
73	off_t 		f_size;		/* Size of file */
74	daddr_t		f_buf_blkno;	/* block number of data block */
75	char		*f_buf;		/* buffer for data block */
76};
77
78struct ptable_ent {
79	char namlen	[ISODCL( 1, 1)];	/* 711 */
80	char extlen	[ISODCL( 2, 2)];	/* 711 */
81	char block	[ISODCL( 3, 6)];	/* 732 */
82	char parent	[ISODCL( 7, 8)];	/* 722 */
83	char name	[1];
84};
85#define	PTFIXSZ		8
86#define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
87
88#define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
89
90/* XXX these should be in the system headers */
91static __inline int
92isonum_722(p)
93	u_char *p;
94{
95	return (*p << 8)|p[1];
96}
97
98static __inline int
99isonum_732(p)
100	u_char *p;
101{
102	return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
103}
104
105static int
106dirmatch(path, dp)
107	const char *path;
108	struct iso_directory_record *dp;
109{
110	char *cp;
111	int i;
112
113	cp = dp->name;
114	for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) {
115		if (!*path || *path == '/')
116			break;
117		if (toupper(*path) == *cp)
118			continue;
119		return 0;
120	}
121	if (*path && *path != '/')
122		return 0;
123	/*
124	 * Allow stripping of trailing dots and the version number.
125	 * Note that this will find the first instead of the last version
126	 * of a file.
127	 */
128	if (i >= 0 && (*cp == ';' || *cp == '.')) {
129		/* This is to prevent matching of numeric extensions */
130		if (*cp == '.' && cp[1] != ';')
131			return 0;
132		while (--i >= 0)
133			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
134				return 0;
135	}
136	return 1;
137}
138
139static int
140cd9660_open(path, f)
141	const char *path;
142	struct open_file *f;
143{
144	struct file *fp = 0;
145	void *buf;
146	struct iso_primary_descriptor *vd;
147	size_t buf_size, read, dsize, off;
148	daddr_t bno, boff;
149	struct iso_directory_record rec;
150	struct iso_directory_record *dp = 0;
151	int rc;
152
153	/* First find the volume descriptor */
154	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
155	vd = buf;
156	for (bno = 16;; bno++) {
157		twiddle();
158		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
159					   ISO_DEFAULT_BLOCK_SIZE, buf, &read);
160		if (rc)
161			goto out;
162		if (read != ISO_DEFAULT_BLOCK_SIZE) {
163			rc = EIO;
164			goto out;
165		}
166		rc = EINVAL;
167		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
168			goto out;
169		if (isonum_711(vd->type) == ISO_VD_END)
170			goto out;
171		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
172			break;
173	}
174	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
175		goto out;
176
177	rec = *(struct iso_directory_record *) vd->root_directory_record;
178	if (*path == '/') path++; /* eat leading '/' */
179
180	while (*path) {
181		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
182		dsize = isonum_733(rec.size);
183		off = 0;
184		boff = 0;
185
186		while (off < dsize) {
187			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
188				twiddle();
189				rc = f->f_dev->dv_strategy
190					(f->f_devdata, F_READ,
191					 cdb2devb(bno + boff),
192					 ISO_DEFAULT_BLOCK_SIZE,
193					 buf, &read);
194				if (rc)
195					goto out;
196				if (read != ISO_DEFAULT_BLOCK_SIZE) {
197					rc = EIO;
198					goto out;
199				}
200				boff++;
201				dp = (struct iso_directory_record *) buf;
202			}
203			if (isonum_711(dp->length) == 0) {
204			    /* skip to next block, if any */
205			    off = boff * ISO_DEFAULT_BLOCK_SIZE;
206			    continue;
207			}
208
209			if (dirmatch(path, dp))
210				break;
211
212			dp = (struct iso_directory_record *)
213				((char *) dp + isonum_711(dp->length));
214			off += isonum_711(dp->length);
215		}
216		if (off >= dsize) {
217			rc = ENOENT;
218			goto out;
219		}
220
221		rec = *dp;
222		while (*path && *path != '/') /* look for next component */
223			path++;
224		if (*path) path++; /* skip '/' */
225	}
226
227	/* allocate file system specific data structure */
228	fp = malloc(sizeof(struct file));
229	bzero(fp, sizeof(struct file));
230	f->f_fsdata = (void *)fp;
231
232	fp->f_isdir = (isonum_711(rec.flags) & 2) != 0;
233	fp->f_off = 0;
234	fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
235	fp->f_size = isonum_733(rec.size);
236	free(buf);
237
238	return 0;
239
240out:
241	if (fp)
242		free(fp);
243	free(buf);
244
245	return rc;
246}
247
248static int
249cd9660_close(f)
250	struct open_file *f;
251{
252	struct file *fp = (struct file *)f->f_fsdata;
253
254	f->f_fsdata = 0;
255	free(fp);
256
257	return 0;
258}
259
260static int
261buf_read_file(f, buf_p, size_p)
262	struct open_file *f;
263	char **buf_p;
264	size_t *size_p;
265{
266	struct file *fp = (struct file *)f->f_fsdata;
267	daddr_t blkno, blkoff;
268	int rc = 0;
269	size_t read;
270
271	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
272	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
273
274	if (blkno != fp->f_buf_blkno) {
275		if (fp->f_buf == (char *)0)
276			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
277
278		twiddle();
279		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
280		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read);
281		if (rc)
282			return (rc);
283		if (read != ISO_DEFAULT_BLOCK_SIZE)
284			return (EIO);
285
286		fp->f_buf_blkno = blkno;
287	}
288
289	*buf_p = fp->f_buf + blkoff;
290	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
291
292	if (*size_p > fp->f_size - fp->f_off)
293		*size_p = fp->f_size - fp->f_off;
294	return (rc);
295}
296
297static int
298cd9660_read(f, start, size, resid)
299	struct open_file *f;
300	void *start;
301	size_t size;
302	size_t *resid;
303{
304	struct file *fp = (struct file *)f->f_fsdata;
305	char *buf, *addr;
306	size_t buf_size, csize;
307	int rc = 0;
308
309	addr = start;
310	while (size) {
311		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
312			break;
313
314		rc = buf_read_file(f, &buf, &buf_size);
315		if (rc)
316			break;
317
318		csize = size > buf_size ? buf_size : size;
319		bcopy(buf, addr, csize);
320
321		fp->f_off += csize;
322		addr += csize;
323		size -= csize;
324	}
325	if (resid)
326		*resid = size;
327	return (rc);
328}
329
330static int
331cd9660_readdir(struct open_file *f, struct dirent *d)
332{
333	struct file *fp = (struct file *)f->f_fsdata;
334	struct iso_directory_record *ep;
335	size_t buf_size, reclen, namelen;
336	int error = 0;
337	char *buf;
338
339again:
340	if (fp->f_off >= fp->f_size)
341		return (ENOENT);
342	error = buf_read_file(f, &buf, &buf_size);
343	if (error)
344		return (error);
345	ep = (struct iso_directory_record *)buf;
346
347	if (isonum_711(ep->length) == 0) {
348		daddr_t blkno;
349
350		/* skip to next block, if any */
351		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
352		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
353		goto again;
354	}
355
356	namelen = isonum_711(ep->name_len);
357	if (namelen == 1 && ep->name[0] == 1)
358		namelen = 2;
359	reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
360	reclen = (reclen + 3) & ~3;
361
362	d->d_fileno = isonum_733(ep->extent);
363	d->d_reclen = reclen;
364	if (isonum_711(ep->flags) & 2)
365		d->d_type = DT_DIR;
366	else
367		d->d_type = DT_REG;
368	d->d_namlen = namelen;
369
370	if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0)
371		strcpy(d->d_name, ".");
372	else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1)
373		strcpy(d->d_name, "..");
374	else
375		bcopy(ep->name, d->d_name, d->d_namlen);
376	d->d_name[d->d_namlen] = 0;
377
378	fp->f_off += isonum_711(ep->length);
379	return (0);
380}
381
382static int
383cd9660_write(f, start, size, resid)
384	struct open_file *f;
385	void *start;
386	size_t size;
387	size_t *resid;
388{
389	return EROFS;
390}
391
392static off_t
393cd9660_seek(f, offset, where)
394	struct open_file *f;
395	off_t offset;
396	int where;
397{
398	struct file *fp = (struct file *)f->f_fsdata;
399
400	switch (where) {
401	case SEEK_SET:
402		fp->f_off = offset;
403		break;
404	case SEEK_CUR:
405		fp->f_off += offset;
406		break;
407	case SEEK_END:
408		fp->f_off = fp->f_size - offset;
409		break;
410	default:
411		return -1;
412	}
413	return fp->f_off;
414}
415
416static int
417cd9660_stat(f, sb)
418	struct open_file *f;
419	struct stat *sb;
420{
421	struct file *fp = (struct file *)f->f_fsdata;
422
423	/* only important stuff */
424	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
425	if (fp->f_isdir)
426		sb->st_mode |= S_IFDIR;
427	else
428		sb->st_mode |= S_IFREG;
429	sb->st_uid = sb->st_gid = 0;
430	sb->st_size = fp->f_size;
431	return 0;
432}
433