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