cd9660.c revision 56222
12258Scsgr/* $FreeBSD: head/lib/libstand/cd9660.c 56222 2000-01-18 07:37:10Z obrien $ */
22258Scsgr/*	$NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $	*/
32258Scsgr
42258Scsgr/*
52258Scsgr * Copyright (C) 1996 Wolfgang Solfrank.
62258Scsgr * Copyright (C) 1996 TooLs GmbH.
72258Scsgr * All rights reserved.
82258Scsgr *
92258Scsgr * Redistribution and use in source and binary forms, with or without
102258Scsgr * modification, are permitted provided that the following conditions
112258Scsgr * are met:
122258Scsgr * 1. Redistributions of source code must retain the above copyright
132258Scsgr *    notice, this list of conditions and the following disclaimer.
142258Scsgr * 2. Redistributions in binary form must reproduce the above copyright
152258Scsgr *    notice, this list of conditions and the following disclaimer in the
162258Scsgr *    documentation and/or other materials provided with the distribution.
172258Scsgr * 3. All advertising materials mentioning features or use of this software
182258Scsgr *    must display the following acknowledgement:
192258Scsgr *	This product includes software developed by TooLs GmbH.
202258Scsgr * 4. The name of TooLs GmbH may not be used to endorse or promote products
212258Scsgr *    derived from this software without specific prior written permission.
222258Scsgr *
232258Scsgr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
242258Scsgr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
252258Scsgr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
262258Scsgr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
272258Scsgr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
282258Scsgr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
292258Scsgr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
302258Scsgr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
312258Scsgr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
322258Scsgr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
332258Scsgr */
342258Scsgr
352258Scsgr/*
362258Scsgr * Stand-alone ISO9660 file reading package.
372258Scsgr *
382258Scsgr * Note: This doesn't support Rock Ridge extensions, extended attributes,
392258Scsgr * blocksizes other than 2048 bytes, multi-extent files, etc.
402258Scsgr */
412258Scsgr#include <sys/param.h>
422258Scsgr#include <string.h>
432258Scsgr#include <sys/dirent.h>
442258Scsgr#include <isofs/cd9660/iso.h>
452258Scsgr
462258Scsgr#include "stand.h"
472258Scsgr
482258Scsgrstatic int	cd9660_open(const char *path, struct open_file *f);
492258Scsgrstatic int	cd9660_close(struct open_file *f);
502258Scsgrstatic int	cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid);
512258Scsgrstatic int	cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid);
522258Scsgrstatic off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
532258Scsgrstatic int	cd9660_stat(struct open_file *f, struct stat *sb);
542258Scsgr
552258Scsgrstruct fs_ops cd9660_fsops = {
562258Scsgr	"cd9660", cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek, cd9660_stat
572258Scsgr};
582258Scsgr
592258Scsgrstruct file {
602258Scsgr	int isdir;			/* nonzero if file is directory */
612258Scsgr	off_t off;			/* Current offset within file */
622258Scsgr	daddr_t bno;			/* Starting block number  */
632258Scsgr	off_t size;			/* Size of file */
642258Scsgr};
652258Scsgr
662258Scsgrstruct ptable_ent {
672258Scsgr	char namlen	[ISODCL( 1, 1)];	/* 711 */
682258Scsgr	char extlen	[ISODCL( 2, 2)];	/* 711 */
692258Scsgr	char block	[ISODCL( 3, 6)];	/* 732 */
702258Scsgr	char parent	[ISODCL( 7, 8)];	/* 722 */
712258Scsgr	char name	[1];
722258Scsgr};
732258Scsgr#define	PTFIXSZ		8
742258Scsgr#define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
752258Scsgr
762258Scsgr#define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
772258Scsgr
782258Scsgr/* XXX these should be in the system headers */
792258Scsgrstatic __inline int
802258Scsgrisonum_722(p)
812258Scsgr	u_char *p;
822258Scsgr{
832258Scsgr	return (*p << 8)|p[1];
842258Scsgr}
852258Scsgr
862258Scsgrstatic __inline int
872258Scsgrisonum_732(p)
882258Scsgr	u_char *p;
892258Scsgr{
902258Scsgr	return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
912258Scsgr}
922258Scsgr
932258Scsgr
942258Scsgr
952258Scsgrstatic int
962258Scsgrdirmatch(path, dp)
972258Scsgr	const char *path;
982258Scsgr	struct iso_directory_record *dp;
992258Scsgr{
1002258Scsgr	char *cp;
1012258Scsgr	int i;
1022258Scsgr
1032258Scsgr	cp = dp->name;
1042258Scsgr	for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) {
1052258Scsgr		if (!*path || *path == '/')
1062258Scsgr			break;
1072258Scsgr		if (toupper(*path) == *cp)
1082258Scsgr			continue;
1092258Scsgr		return 0;
1102258Scsgr	}
1112258Scsgr	if (*path && *path != '/')
1122258Scsgr		return 0;
1132258Scsgr	/*
1142258Scsgr	 * Allow stripping of trailing dots and the version number.
1152258Scsgr	 * Note that this will find the first instead of the last version
1162258Scsgr	 * of a file.
1172258Scsgr	 */
1182258Scsgr	if (i >= 0 && (*cp == ';' || *cp == '.')) {
1192258Scsgr		/* This is to prevent matching of numeric extensions */
1202258Scsgr		if (*cp == '.' && cp[1] != ';')
1212258Scsgr			return 0;
1222258Scsgr		while (--i >= 0)
1232258Scsgr			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
1242258Scsgr				return 0;
1252258Scsgr	}
1262258Scsgr	return 1;
1272258Scsgr}
1282258Scsgr
1292258Scsgrstatic int
1302258Scsgrcd9660_open(path, f)
1312258Scsgr	const char *path;
1322258Scsgr	struct open_file *f;
1332258Scsgr{
1342258Scsgr	struct file *fp = 0;
1352258Scsgr	void *buf;
1362258Scsgr	struct iso_primary_descriptor *vd;
1372258Scsgr	size_t buf_size, read, dsize, off;
1382258Scsgr	daddr_t bno, boff;
1392258Scsgr	struct iso_directory_record rec;
1402258Scsgr	struct iso_directory_record *dp = 0;
1412258Scsgr	int rc;
1422258Scsgr
1432258Scsgr	/* First find the volume descriptor */
1442258Scsgr	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
1452258Scsgr	vd = buf;
1462258Scsgr	for (bno = 16;; bno++) {
1472258Scsgr		twiddle();
1482258Scsgr		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
1492258Scsgr					   ISO_DEFAULT_BLOCK_SIZE, buf, &read);
1502258Scsgr		if (rc)
1512258Scsgr			goto out;
1522258Scsgr		if (read != ISO_DEFAULT_BLOCK_SIZE) {
1532258Scsgr			rc = EIO;
1542258Scsgr			goto out;
1552258Scsgr		}
1562258Scsgr		rc = EINVAL;
1572258Scsgr		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
1582258Scsgr			goto out;
1592258Scsgr		if (isonum_711(vd->type) == ISO_VD_END)
1602258Scsgr			goto out;
1612258Scsgr		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
1622258Scsgr			break;
1632258Scsgr	}
1642258Scsgr	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
1652258Scsgr		goto out;
1662258Scsgr
1672258Scsgr	rec = *(struct iso_directory_record *) vd->root_directory_record;
1682258Scsgr	if (*path == '/') path++; /* eat leading '/' */
1692258Scsgr
1702258Scsgr	while (*path) {
1712258Scsgr		bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
1722258Scsgr		dsize = isonum_733(rec.size);
1732258Scsgr		off = 0;
1742258Scsgr		boff = 0;
1752258Scsgr
1762258Scsgr		while (off < dsize) {
1772258Scsgr			if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
1782258Scsgr				twiddle();
1792258Scsgr				rc = f->f_dev->dv_strategy
1802258Scsgr					(f->f_devdata, F_READ,
1812258Scsgr					 cdb2devb(bno + boff),
1822258Scsgr					 ISO_DEFAULT_BLOCK_SIZE,
1832258Scsgr					 buf, &read);
1842258Scsgr				if (rc)
1852258Scsgr					goto out;
1862258Scsgr				if (read != ISO_DEFAULT_BLOCK_SIZE) {
1872258Scsgr					rc = EIO;
1882258Scsgr					goto out;
1892258Scsgr				}
1902258Scsgr				boff++;
1912258Scsgr				dp = (struct iso_directory_record *) buf;
1922258Scsgr			}
1932258Scsgr			if (isonum_711(dp->length) == 0) {
1942258Scsgr			    /* skip to next block, if any */
1952258Scsgr			    off = boff * ISO_DEFAULT_BLOCK_SIZE;
1962258Scsgr			    continue;
1972258Scsgr			}
1982258Scsgr
1992258Scsgr			if (dirmatch(path, dp))
2002258Scsgr				break;
2012258Scsgr
2022258Scsgr			dp = (struct iso_directory_record *)
2032258Scsgr				((char *) dp + isonum_711(dp->length));
2042258Scsgr			off += isonum_711(dp->length);
2052258Scsgr		}
2062258Scsgr		if (off == dsize) {
2072258Scsgr			rc = ENOENT;
2082258Scsgr			goto out;
2092258Scsgr		}
2102258Scsgr
2112258Scsgr		rec = *dp;
2122258Scsgr		while (*path && *path != '/') /* look for next component */
2132258Scsgr			path++;
2142258Scsgr		if (*path) path++; /* skip '/' */
2152258Scsgr	}
2162258Scsgr
2172258Scsgr	/* allocate file system specific data structure */
2182258Scsgr	fp = malloc(sizeof(struct file));
2192258Scsgr	bzero(fp, sizeof(struct file));
2202258Scsgr	f->f_fsdata = (void *)fp;
2212258Scsgr
2222258Scsgr	fp->isdir = (isonum_711(rec.flags) & 2) != 0;
2232258Scsgr	fp->off = 0;
2242258Scsgr	fp->bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
2252258Scsgr	fp->size = isonum_733(rec.size);
226	free(buf);
227
228	return 0;
229
230out:
231	if (fp)
232		free(fp);
233	free(buf);
234
235	return rc;
236}
237
238static int
239cd9660_close(f)
240	struct open_file *f;
241{
242	struct file *fp = (struct file *)f->f_fsdata;
243
244	f->f_fsdata = 0;
245	free(fp);
246
247	return 0;
248}
249
250static int
251cd9660_readfile(f, start, size, resid)
252	struct open_file *f;
253	void *start;
254	size_t size;
255	size_t *resid;
256{
257	struct file *fp = (struct file *)f->f_fsdata;
258	int rc = 0;
259	daddr_t bno;
260	char buf[ISO_DEFAULT_BLOCK_SIZE];
261	char *dp;
262	size_t read, off;
263
264	while (size) {
265		if (fp->off < 0 || fp->off >= fp->size)
266			break;
267		bno = fp->off / ISO_DEFAULT_BLOCK_SIZE + fp->bno;
268		if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1)
269		    || size < ISO_DEFAULT_BLOCK_SIZE)
270			dp = buf;
271		else
272			dp = start;
273		twiddle();
274		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
275					   ISO_DEFAULT_BLOCK_SIZE, dp, &read);
276		if (rc)
277			return rc;
278		if (read != ISO_DEFAULT_BLOCK_SIZE)
279			return EIO;
280		if (dp == buf) {
281			off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1);
282			if (read > off + size)
283				read = off + size;
284			read -= off;
285			bcopy(buf + off, start, read);
286			start += read;
287			fp->off += read;
288			size -= read;
289		} else {
290			start += ISO_DEFAULT_BLOCK_SIZE;
291			fp->off += ISO_DEFAULT_BLOCK_SIZE;
292			size -= ISO_DEFAULT_BLOCK_SIZE;
293		}
294	}
295	if (resid)
296		*resid = size;
297	return rc;
298}
299
300static int
301cd9660_readdir(f, start, size, resid)
302	struct open_file *f;
303	void *start;
304	size_t size;
305	size_t *resid;
306{
307	struct file *fp = (struct file *)f->f_fsdata;
308	int rc = 0;
309	daddr_t bno, boff;
310	char buf[ISO_DEFAULT_BLOCK_SIZE];
311	struct dirent *dp;
312	struct dirent *lastdp;
313	struct iso_directory_record *ep = 0;
314	size_t read, off, reclen, namelen;
315
316	if (fp->off < 0 || fp->off >= fp->size)
317		return 0;
318	boff = fp->off / ISO_DEFAULT_BLOCK_SIZE;
319	bno = fp->bno;
320	off = fp->off;
321
322	if (off % ISO_DEFAULT_BLOCK_SIZE) {
323		twiddle();
324		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
325					   ISO_DEFAULT_BLOCK_SIZE, buf, &read);
326		if (rc)
327			return rc;
328		if (read != ISO_DEFAULT_BLOCK_SIZE)
329			return EIO;
330		boff++;
331		ep = (struct iso_directory_record *)
332			(buf + (off % ISO_DEFAULT_BLOCK_SIZE));
333	}
334
335	lastdp = dp = (struct dirent *) start;
336	dp->d_fileno = 0;
337	dp->d_type = DT_UNKNOWN;
338	dp->d_namlen = 0;
339	while (size && off < fp->size) {
340		if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
341			twiddle();
342			rc = f->f_dev->dv_strategy
343				(f->f_devdata, F_READ,
344				 cdb2devb(bno + boff),
345				 ISO_DEFAULT_BLOCK_SIZE,
346				 buf, &read);
347			if (rc)
348				break;
349			if (read != ISO_DEFAULT_BLOCK_SIZE) {
350				rc = EIO;
351				break;
352			}
353			boff++;
354			ep = (struct iso_directory_record *) buf;
355		}
356
357		if (isonum_711(ep->length) == 0) {
358			/* skip to next block, if any */
359			off = boff * ISO_DEFAULT_BLOCK_SIZE;
360			continue;
361		}
362
363		namelen = isonum_711(ep->name_len);
364		if (namelen == 1 && ep->name[0] == 1)
365			namelen = 2;
366		reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
367		reclen = (reclen + 3) & ~3;
368		if (reclen > size)
369			break;
370
371		dp->d_fileno = isonum_733(ep->extent);
372		dp->d_reclen = reclen;
373		if (isonum_711(ep->flags) & 2)
374			dp->d_type = DT_DIR;
375		else
376			dp->d_type = DT_REG;
377		dp->d_namlen = namelen;
378		if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0)
379			strcpy(dp->d_name, ".");
380		else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1)
381			strcpy(dp->d_name, "..");
382		else
383			bcopy(ep->name, dp->d_name, dp->d_namlen);
384		dp->d_name[dp->d_namlen] = 0;
385
386		lastdp = dp;
387		dp = (struct dirent *) ((char *) dp + dp->d_reclen);
388		size -= reclen;
389		ep = (struct iso_directory_record *)
390		    ((char *) ep + isonum_711(ep->length));
391		off += isonum_711(ep->length);
392	}
393
394	fp->off = off;
395	lastdp->d_reclen += size;
396	if (resid)
397		*resid = 0;
398	return rc;
399}
400
401static int
402cd9660_read(f, start, size, resid)
403	struct open_file *f;
404	void *start;
405	size_t size;
406	size_t *resid;
407{
408	struct file *fp = (struct file *)f->f_fsdata;
409
410	if (fp->isdir)
411		return cd9660_readdir(f, start, size, resid);
412	else
413		return cd9660_readfile(f, start, size, resid);
414}
415
416static int
417cd9660_write(f, start, size, resid)
418	struct open_file *f;
419	void *start;
420	size_t size;
421	size_t *resid;
422{
423	return EROFS;
424}
425
426static off_t
427cd9660_seek(f, offset, where)
428	struct open_file *f;
429	off_t offset;
430	int where;
431{
432	struct file *fp = (struct file *)f->f_fsdata;
433
434	switch (where) {
435	case SEEK_SET:
436		fp->off = offset;
437		break;
438	case SEEK_CUR:
439		fp->off += offset;
440		break;
441	case SEEK_END:
442		fp->off = fp->size - offset;
443		break;
444	default:
445		return -1;
446	}
447	return fp->off;
448}
449
450static int
451cd9660_stat(f, sb)
452	struct open_file *f;
453	struct stat *sb;
454{
455	struct file *fp = (struct file *)f->f_fsdata;
456
457	/* only important stuff */
458	sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
459	if (fp->isdir)
460		sb->st_mode |= S_IFDIR;
461	else
462		sb->st_mode |= S_IFREG;
463	sb->st_uid = sb->st_gid = 0;
464	sb->st_size = fp->size;
465	return 0;
466}
467