cd9660.c revision 82208
172909Sjulian/* $FreeBSD: head/lib/libstand/cd9660.c 82208 2001-08-23 17:08:26Z gallatin $ */
272909Sjulian/*	$NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $	*/
379727Sschweikh
472909Sjulian/*
572909Sjulian * Copyright (C) 1996 Wolfgang Solfrank.
672909Sjulian * Copyright (C) 1996 TooLs GmbH.
772909Sjulian * All rights reserved.
872909Sjulian *
972909Sjulian * Redistribution and use in source and binary forms, with or without
1072909Sjulian * modification, are permitted provided that the following conditions
1172909Sjulian * are met:
1272909Sjulian * 1. Redistributions of source code must retain the above copyright
1372909Sjulian *    notice, this list of conditions and the following disclaimer.
1479727Sschweikh * 2. Redistributions in binary form must reproduce the above copyright
1572909Sjulian *    notice, this list of conditions and the following disclaimer in the
1672909Sjulian *    documentation and/or other materials provided with the distribution.
1772909Sjulian * 3. All advertising materials mentioning features or use of this software
1872909Sjulian *    must display the following acknowledgement:
1972909Sjulian *	This product includes software developed by TooLs GmbH.
2072909Sjulian * 4. The name of TooLs GmbH may not be used to endorse or promote products
2172909Sjulian *    derived from this software without specific prior written permission.
2272909Sjulian *
2372909Sjulian * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2472909Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2572909Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2672909Sjulian * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2772909Sjulian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2872909Sjulian * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2972909Sjulian * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3072909Sjulian * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3172909Sjulian * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3279727Sschweikh * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3372909Sjulian */
3472909Sjulian
3572909Sjulian/*
3672909Sjulian * Stand-alone ISO9660 file reading package.
3772909Sjulian *
3872909Sjulian * Note: This doesn't support Rock Ridge extensions, extended attributes,
3979538Sru * blocksizes other than 2048 bytes, multi-extent files, etc.
4072909Sjulian */
4172909Sjulian#include <sys/param.h>
4273753Sru#include <string.h>
4372909Sjulian#include <sys/dirent.h>
4484306Sru#include <isofs/cd9660/iso.h>
4572909Sjulian
4672909Sjulian#include "stand.h"
4772909Sjulian
4879727Sschweikhstatic int	cd9660_open(const char *path, struct open_file *f);
4972909Sjulianstatic int	cd9660_close(struct open_file *f);
5072909Sjulianstatic int	cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid);
5172909Sjulianstatic int	cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid);
52242997Sjoelstatic off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
53242997Sjoelstatic int	cd9660_stat(struct open_file *f, struct stat *sb);
5472909Sjulianstatic int	cd9660_readdir(struct open_file *f, struct dirent *d);
5572909Sjulian
5679727Sschweikhstruct fs_ops cd9660_fsops = {
5772909Sjulian	"cd9660",
58242997Sjoel	cd9660_open,
5979727Sschweikh	cd9660_close,
6072909Sjulian	cd9660_read,
6172909Sjulian	cd9660_write,
62242997Sjoel	cd9660_seek,
6372909Sjulian	cd9660_stat,
6472909Sjulian	cd9660_readdir
6572909Sjulian};
6672909Sjulian
6772909Sjulianstruct file {
6872909Sjulian	int 		f_isdir;	/* nonzero if file is directory */
6972909Sjulian	off_t 		f_off;		/* Current offset within file */
7072909Sjulian	daddr_t 	f_bno;		/* Starting block number */
7172909Sjulian	off_t 		f_size;		/* Size of file */
7272909Sjulian	daddr_t		f_buf_blkno;	/* block number of data block */
7372909Sjulian	char		*f_buf;		/* buffer for data block */
7472909Sjulian};
7572909Sjulian
7672909Sjulianstruct ptable_ent {
7772909Sjulian	char namlen	[ISODCL( 1, 1)];	/* 711 */
7872909Sjulian	char extlen	[ISODCL( 2, 2)];	/* 711 */
7972909Sjulian	char block	[ISODCL( 3, 6)];	/* 732 */
8072909Sjulian	char parent	[ISODCL( 7, 8)];	/* 722 */
8179727Sschweikh	char name	[1];
8289610Smpp};
8373753Sru#define	PTFIXSZ		8
8479727Sschweikh#define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
8572909Sjulian
8672909Sjulian#define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
8772909Sjulian
8872909Sjulian/* 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, blkoff;
266	int rc = 0;
267	size_t read;
268
269	blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
270	blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
271
272	if (blkno != fp->f_buf_blkno) {
273		if (fp->f_buf == (char *)0)
274			fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
275
276		twiddle();
277		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
278		    cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read);
279		if (rc)
280			return (rc);
281		if (read != ISO_DEFAULT_BLOCK_SIZE)
282			return (EIO);
283
284		fp->f_buf_blkno = blkno;
285	}
286
287	*buf_p = fp->f_buf + blkoff;
288	*size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
289
290	if (*size_p > fp->f_size - fp->f_off)
291		*size_p = fp->f_size - fp->f_off;
292	return (rc);
293}
294
295static int
296cd9660_read(f, start, size, resid)
297	struct open_file *f;
298	void *start;
299	size_t size;
300	size_t *resid;
301{
302	struct file *fp = (struct file *)f->f_fsdata;
303	char *buf, *addr;
304	size_t buf_size, csize;
305	int rc = 0;
306
307	addr = start;
308	while (size) {
309		if (fp->f_off < 0 || fp->f_off >= fp->f_size)
310			break;
311
312		rc = buf_read_file(f, &buf, &buf_size);
313		if (rc)
314			break;
315
316		csize = size > buf_size ? buf_size : size;
317		bcopy(buf, addr, csize);
318
319		fp->f_off += csize;
320		addr += csize;
321		size -= csize;
322	}
323	if (resid)
324		*resid = size;
325	return (rc);
326}
327
328static int
329cd9660_readdir(struct open_file *f, struct dirent *d)
330{
331	struct file *fp = (struct file *)f->f_fsdata;
332	struct iso_directory_record *ep;
333	size_t buf_size, reclen, namelen;
334	int error = 0;
335	char *buf;
336
337again:
338	if (fp->f_off >= fp->f_size)
339		return (ENOENT);
340	error = buf_read_file(f, &buf, &buf_size);
341	if (error)
342		return (error);
343	ep = (struct iso_directory_record *)buf;
344
345	if (isonum_711(ep->length) == 0) {
346		daddr_t blkno;
347
348		/* skip to next block, if any */
349		blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
350		fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
351		goto again;
352	}
353
354	namelen = isonum_711(ep->name_len);
355	if (namelen == 1 && ep->name[0] == 1)
356		namelen = 2;
357	reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
358	reclen = (reclen + 3) & ~3;
359
360	d->d_fileno = isonum_733(ep->extent);
361	d->d_reclen = reclen;
362	if (isonum_711(ep->flags) & 2)
363		d->d_type = DT_DIR;
364	else
365		d->d_type = DT_REG;
366	d->d_namlen = namelen;
367
368	if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0)
369		strcpy(d->d_name, ".");
370	else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1)
371		strcpy(d->d_name, "..");
372	else
373		bcopy(ep->name, d->d_name, d->d_namlen);
374	d->d_name[d->d_namlen] = 0;
375
376	fp->f_off += isonum_711(ep->length);
377	return (0);
378}
379
380static int
381cd9660_write(f, start, size, resid)
382	struct open_file *f;
383	void *start;
384	size_t size;
385	size_t *resid;
386{
387	return EROFS;
388}
389
390static off_t
391cd9660_seek(f, offset, where)
392	struct open_file *f;
393	off_t offset;
394	int where;
395{
396	struct file *fp = (struct file *)f->f_fsdata;
397
398	switch (where) {
399	case SEEK_SET:
400		fp->f_off = offset;
401		break;
402	case SEEK_CUR:
403		fp->f_off += offset;
404		break;
405	case SEEK_END:
406		fp->f_off = fp->f_size - offset;
407		break;
408	default:
409		return -1;
410	}
411	return fp->f_off;
412}
413
414static int
415cd9660_stat(f, sb)
416	struct open_file *f;
417	struct stat *sb;
418{
419	struct file *fp = (struct file *)f->f_fsdata;
420
421	/* only important stuff */
422	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
423	if (fp->f_isdir)
424		sb->st_mode |= S_IFDIR;
425	else
426		sb->st_mode |= S_IFREG;
427	sb->st_uid = sb->st_gid = 0;
428	sb->st_size = fp->f_size;
429	return 0;
430}
431