cd9660.c revision 86137
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 86137 2001-11-06 17:13:05Z jhb $");
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	buf_read_file(struct open_file *f, char **buf_p,
51		    size_t *size_p);
52static int	cd9660_open(const char *path, struct open_file *f);
53static int	cd9660_close(struct open_file *f);
54static int	cd9660_read(struct open_file *f, void *buf, size_t size,
55		    size_t *resid);
56static int	cd9660_write(struct open_file *f, void *buf, size_t size,
57		    size_t *resid);
58static off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
59static int	cd9660_stat(struct open_file *f, struct stat *sb);
60static int	cd9660_readdir(struct open_file *f, struct dirent *d);
61static int	dirmatch(const char *path, struct iso_directory_record *dp);
62
63struct fs_ops cd9660_fsops = {
64	"cd9660",
65	cd9660_open,
66	cd9660_close,
67	cd9660_read,
68	cd9660_write,
69	cd9660_seek,
70	cd9660_stat,
71	cd9660_readdir
72};
73
74struct file {
75	int 		f_isdir;	/* nonzero if file is directory */
76	off_t 		f_off;		/* Current offset within file */
77	daddr_t 	f_bno;		/* Starting block number */
78	off_t 		f_size;		/* Size of file */
79	daddr_t		f_buf_blkno;	/* block number of data block */
80	char		*f_buf;		/* buffer for data block */
81};
82
83struct ptable_ent {
84	char namlen	[ISODCL( 1, 1)];	/* 711 */
85	char extlen	[ISODCL( 2, 2)];	/* 711 */
86	char block	[ISODCL( 3, 6)];	/* 732 */
87	char parent	[ISODCL( 7, 8)];	/* 722 */
88	char name	[1];
89};
90#define	PTFIXSZ		8
91#define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
92
93#define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
94
95/* XXX these should be in the system headers */
96static __inline int
97isonum_722(p)
98	u_char *p;
99{
100	return (*p << 8)|p[1];
101}
102
103static __inline int
104isonum_732(p)
105	u_char *p;
106{
107	return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
108}
109
110static int
111dirmatch(const char *path, struct iso_directory_record *dp)
112{
113	char *cp;
114	int i;
115
116	cp = dp->name;
117	for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) {
118		if (!*path || *path == '/')
119			break;
120		if (toupper(*path) == *cp)
121			continue;
122		return 0;
123	}
124	if (*path && *path != '/')
125		return 0;
126	/*
127	 * Allow stripping of trailing dots and the version number.
128	 * Note that this will find the first instead of the last version
129	 * of a file.
130	 */
131	if (i >= 0 && (*cp == ';' || *cp == '.')) {
132		/* This is to prevent matching of numeric extensions */
133		if (*cp == '.' && cp[1] != ';')
134			return 0;
135		while (--i >= 0)
136			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
137				return 0;
138	}
139	return 1;
140}
141
142static int
143cd9660_open(const char *path, struct open_file *f)
144{
145	struct file *fp = 0;
146	void *buf;
147	struct iso_primary_descriptor *vd;
148	size_t buf_size, read, dsize, off;
149	daddr_t bno, boff;
150	struct iso_directory_record rec;
151	struct iso_directory_record *dp = 0;
152	int rc;
153
154	/* First find the volume descriptor */
155	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
156	vd = buf;
157	for (bno = 16;; bno++) {
158		twiddle();
159		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
160					   ISO_DEFAULT_BLOCK_SIZE, buf, &read);
161		if (rc)
162			goto out;
163		if (read != ISO_DEFAULT_BLOCK_SIZE) {
164			rc = EIO;
165			goto out;
166		}
167		rc = EINVAL;
168		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
169			goto out;
170		if (isonum_711(vd->type) == ISO_VD_END)
171			goto out;
172		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
173			break;
174	}
175	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
176		goto out;
177
178	rec = *(struct iso_directory_record *) vd->root_directory_record;
179	if (*path == '/') path++; /* eat leading '/' */
180
181	while (*path) {
182		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
183		dsize = isonum_733(rec.size);
184		off = 0;
185		boff = 0;
186
187		while (off < dsize) {
188			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
189				twiddle();
190				rc = f->f_dev->dv_strategy
191					(f->f_devdata, F_READ,
192					 cdb2devb(bno + boff),
193					 ISO_DEFAULT_BLOCK_SIZE,
194					 buf, &read);
195				if (rc)
196					goto out;
197				if (read != ISO_DEFAULT_BLOCK_SIZE) {
198					rc = EIO;
199					goto out;
200				}
201				boff++;
202				dp = (struct iso_directory_record *) buf;
203			}
204			if (isonum_711(dp->length) == 0) {
205			    /* skip to next block, if any */
206			    off = boff * ISO_DEFAULT_BLOCK_SIZE;
207			    continue;
208			}
209
210			if (dirmatch(path, dp))
211				break;
212
213			dp = (struct iso_directory_record *)
214				((char *) dp + isonum_711(dp->length));
215			off += isonum_711(dp->length);
216		}
217		if (off >= dsize) {
218			rc = ENOENT;
219			goto out;
220		}
221
222		rec = *dp;
223		while (*path && *path != '/') /* look for next component */
224			path++;
225		if (*path) path++; /* skip '/' */
226	}
227
228	/* allocate file system specific data structure */
229	fp = malloc(sizeof(struct file));
230	bzero(fp, sizeof(struct file));
231	f->f_fsdata = (void *)fp;
232
233	fp->f_isdir = (isonum_711(rec.flags) & 2) != 0;
234	fp->f_off = 0;
235	fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
236	fp->f_size = isonum_733(rec.size);
237	free(buf);
238
239	return 0;
240
241out:
242	if (fp)
243		free(fp);
244	free(buf);
245
246	return rc;
247}
248
249static int
250cd9660_close(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(struct open_file *f, char **buf_p, size_t *size_p)
262{
263	struct file *fp = (struct file *)f->f_fsdata;
264	daddr_t blkno, blkoff;
265	int rc = 0;
266	size_t read;
267
268	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
269	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
270
271	if (blkno != fp->f_buf_blkno) {
272		if (fp->f_buf == (char *)0)
273			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
274
275		twiddle();
276		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
277		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read);
278		if (rc)
279			return (rc);
280		if (read != ISO_DEFAULT_BLOCK_SIZE)
281			return (EIO);
282
283		fp->f_buf_blkno = blkno;
284	}
285
286	*buf_p = fp->f_buf + blkoff;
287	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
288
289	if (*size_p > fp->f_size - fp->f_off)
290		*size_p = fp->f_size - fp->f_off;
291	return (rc);
292}
293
294static int
295cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
296{
297	struct file *fp = (struct file *)f->f_fsdata;
298	char *buf, *addr;
299	size_t buf_size, csize;
300	int rc = 0;
301
302	addr = start;
303	while (size) {
304		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
305			break;
306
307		rc = buf_read_file(f, &buf, &buf_size);
308		if (rc)
309			break;
310
311		csize = size > buf_size ? buf_size : size;
312		bcopy(buf, addr, csize);
313
314		fp->f_off += csize;
315		addr += csize;
316		size -= csize;
317	}
318	if (resid)
319		*resid = size;
320	return (rc);
321}
322
323static int
324cd9660_readdir(struct open_file *f, struct dirent *d)
325{
326	struct file *fp = (struct file *)f->f_fsdata;
327	struct iso_directory_record *ep;
328	size_t buf_size, reclen, namelen;
329	int error = 0;
330	char *buf;
331
332again:
333	if (fp->f_off >= fp->f_size)
334		return (ENOENT);
335	error = buf_read_file(f, &buf, &buf_size);
336	if (error)
337		return (error);
338	ep = (struct iso_directory_record *)buf;
339
340	if (isonum_711(ep->length) == 0) {
341		daddr_t blkno;
342
343		/* skip to next block, if any */
344		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
345		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
346		goto again;
347	}
348
349	namelen = isonum_711(ep->name_len);
350	if (namelen == 1 && ep->name[0] == 1)
351		namelen = 2;
352	reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
353	reclen = (reclen + 3) & ~3;
354
355	d->d_fileno = isonum_733(ep->extent);
356	d->d_reclen = reclen;
357	if (isonum_711(ep->flags) & 2)
358		d->d_type = DT_DIR;
359	else
360		d->d_type = DT_REG;
361	d->d_namlen = namelen;
362
363	if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0)
364		strcpy(d->d_name, ".");
365	else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1)
366		strcpy(d->d_name, "..");
367	else
368		bcopy(ep->name, d->d_name, d->d_namlen);
369	d->d_name[d->d_namlen] = 0;
370
371	fp->f_off += isonum_711(ep->length);
372	return (0);
373}
374
375static int
376cd9660_write(struct open_file *f, void *start, size_t size, size_t *resid)
377{
378	return EROFS;
379}
380
381static off_t
382cd9660_seek(struct open_file *f, off_t offset, int where)
383{
384	struct file *fp = (struct file *)f->f_fsdata;
385
386	switch (where) {
387	case SEEK_SET:
388		fp->f_off = offset;
389		break;
390	case SEEK_CUR:
391		fp->f_off += offset;
392		break;
393	case SEEK_END:
394		fp->f_off = fp->f_size - offset;
395		break;
396	default:
397		return -1;
398	}
399	return fp->f_off;
400}
401
402static int
403cd9660_stat(struct open_file *f, struct stat *sb)
404{
405	struct file *fp = (struct file *)f->f_fsdata;
406
407	/* only important stuff */
408	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
409	if (fp->f_isdir)
410		sb->st_mode |= S_IFDIR;
411	else
412		sb->st_mode |= S_IFREG;
413	sb->st_uid = sb->st_gid = 0;
414	sb->st_size = fp->f_size;
415	return 0;
416}
417