cd9660.c revision 38451
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/*
35 * Stand-alone ISO9660 file reading package.
36 *
37 * Note: This doesn't support Rock Ridge extensions, extended attributes,
38 * blocksizes other than 2048 bytes, multi-extent files, etc.
39 */
40#include <sys/param.h>
41#include <string.h>
42#include <isofs/cd9660/iso.h>
43
44#include "stand.h"
45
46static int	cd9660_open(char *path, struct open_file *f);
47static int	cd9660_close(struct open_file *f);
48static int	cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid);
49static int	cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid);
50static off_t	cd9660_seek(struct open_file *f, off_t offset, int where);
51static int	cd9660_stat(struct open_file *f, struct stat *sb);
52
53struct fs_ops cd9660_fsops = {
54	"cd9660", cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek, cd9660_stat
55};
56
57struct file {
58	off_t off;			/* Current offset within file */
59	daddr_t bno;			/* Starting block number  */
60	off_t size;			/* Size of file */
61};
62
63struct ptable_ent {
64	char namlen	[ISODCL( 1, 1)];	/* 711 */
65	char extlen	[ISODCL( 2, 2)];	/* 711 */
66	char block	[ISODCL( 3, 6)];	/* 732 */
67	char parent	[ISODCL( 7, 8)];	/* 722 */
68	char name	[1];
69};
70#define	PTFIXSZ		8
71#define	PTSIZE(pp)	roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
72
73#define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
74
75/* XXX these should be in the system headers */
76static __inline int
77isonum_722(p)
78	u_char *p;
79{
80	return (*p << 8)|p[1];
81}
82
83static __inline int
84isonum_732(p)
85	u_char *p;
86{
87	return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
88}
89
90
91
92static int
93pnmatch(path, pp)
94	char *path;
95	struct ptable_ent *pp;
96{
97	char *cp;
98	int i;
99
100	cp = pp->name;
101	for (i = isonum_711(pp->namlen); --i >= 0; path++, cp++) {
102		if (toupper(*path) == *cp)
103			continue;
104		return 0;
105	}
106	if (*path != '/')
107		return 0;
108	return 1;
109}
110
111static int
112dirmatch(path, dp)
113	char *path;
114	struct iso_directory_record *dp;
115{
116	char *cp;
117	int i;
118
119	/* This needs to be a regular file */
120	if (dp->flags[0] & 6)
121		return 0;
122
123	cp = dp->name;
124	for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) {
125		if (!*path)
126			break;
127		if (toupper(*path) == *cp)
128			continue;
129		return 0;
130	}
131	if (*path)
132		return 0;
133	/*
134	 * Allow stripping of trailing dots and the version number.
135	 * Note that this will find the first instead of the last version
136	 * of a file.
137	 */
138	if (i >= 0 && (*cp == ';' || *cp == '.')) {
139		/* This is to prevent matching of numeric extensions */
140		if (*cp == '.' && cp[1] != ';')
141			return 0;
142		while (--i >= 0)
143			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
144				return 0;
145	}
146	return 1;
147}
148
149static int
150cd9660_open(path, f)
151	char *path;
152	struct open_file *f;
153{
154	struct file *fp = 0;
155	void *buf;
156	struct iso_primary_descriptor *vd;
157	size_t buf_size, read, psize, dsize;
158	daddr_t bno;
159	int parent, ent;
160	struct ptable_ent *pp;
161	struct iso_directory_record *dp = 0;
162	int rc;
163
164	/* First find the volume descriptor */
165	buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
166	vd = buf;
167	for (bno = 16;; bno++) {
168		twiddle();
169		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
170					   ISO_DEFAULT_BLOCK_SIZE, buf, &read);
171		if (rc)
172			goto out;
173		if (read != ISO_DEFAULT_BLOCK_SIZE) {
174			rc = EIO;
175			goto out;
176		}
177		rc = EINVAL;
178		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
179			goto out;
180		if (isonum_711(vd->type) == ISO_VD_END)
181			goto out;
182		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
183			break;
184	}
185	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
186		goto out;
187
188	/* Now get the path table and lookup the directory of the file */
189	bno = isonum_732(vd->type_m_path_table);
190	psize = isonum_733(vd->path_table_size);
191
192	if (psize > ISO_DEFAULT_BLOCK_SIZE) {
193		free(buf);
194		buf = malloc(buf_size = roundup(psize, ISO_DEFAULT_BLOCK_SIZE));
195	}
196
197	twiddle();
198	rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
199				   buf_size, buf, &read);
200	if (rc)
201		goto out;
202	if (read != buf_size) {
203		rc = EIO;
204		goto out;
205	}
206
207	parent = 1;
208	pp = (struct ptable_ent *)buf;
209	ent = 1;
210	bno = isonum_732(pp->block) + isonum_711(pp->extlen);
211
212	rc = ENOENT;
213	while (*path) {
214		if ((void *)pp >= buf + psize)
215			break;
216		if (isonum_722(pp->parent) != parent)
217			break;
218		if (!pnmatch(path, pp)) {
219			pp = (struct ptable_ent *)((void *)pp + PTSIZE(pp));
220			ent++;
221			continue;
222		}
223		path += isonum_711(pp->namlen) + 1;
224		parent = ent;
225		bno = isonum_732(pp->block) + isonum_711(pp->extlen);
226		while ((void *)pp < buf + psize) {
227			if (isonum_722(pp->parent) == parent)
228				break;
229			pp = (struct ptable_ent *)((void *)pp + PTSIZE(pp));
230			ent++;
231		}
232	}
233
234	/* Now bno has the start of the directory that supposedly contains the file */
235	bno--;
236	dsize = 1;		/* Something stupid, but > 0			XXX */
237	for (psize = 0; psize < dsize;) {
238		if (!(psize % ISO_DEFAULT_BLOCK_SIZE)) {
239			bno++;
240			twiddle();
241			rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
242						   cdb2devb(bno),
243						   ISO_DEFAULT_BLOCK_SIZE,
244						   buf, &read);
245			if (rc)
246				goto out;
247			if (read != ISO_DEFAULT_BLOCK_SIZE) {
248				rc = EIO;
249				goto out;
250			}
251			dp = (struct iso_directory_record *)buf;
252		}
253		if (!isonum_711(dp->length)) {
254			if ((void *)dp == buf)
255				psize += ISO_DEFAULT_BLOCK_SIZE;
256			else
257				psize = roundup(psize, ISO_DEFAULT_BLOCK_SIZE);
258			continue;
259		}
260		if (dsize == 1)
261			dsize = isonum_733(dp->size);
262		if (dirmatch(path, dp))
263			break;
264		psize += isonum_711(dp->length);
265		dp = (struct iso_directory_record *)((void *)dp + isonum_711(dp->length));
266	}
267
268	if (psize >= dsize) {
269		rc = ENOENT;
270		goto out;
271	}
272
273	/* allocate file system specific data structure */
274	fp = malloc(sizeof(struct file));
275	bzero(fp, sizeof(struct file));
276	f->f_fsdata = (void *)fp;
277
278	fp->off = 0;
279	fp->bno = isonum_733(dp->extent);
280	fp->size = isonum_733(dp->size);
281	free(buf);
282
283	return 0;
284
285out:
286	if (fp)
287		free(fp);
288	free(buf);
289
290	return rc;
291}
292
293static int
294cd9660_close(f)
295	struct open_file *f;
296{
297	struct file *fp = (struct file *)f->f_fsdata;
298
299	f->f_fsdata = 0;
300	free(fp);
301
302	return 0;
303}
304
305static int
306cd9660_read(f, start, size, resid)
307	struct open_file *f;
308	void *start;
309	size_t size;
310	size_t *resid;
311{
312	struct file *fp = (struct file *)f->f_fsdata;
313	int rc = 0;
314	daddr_t bno;
315	char buf[ISO_DEFAULT_BLOCK_SIZE];
316	char *dp;
317	size_t read, off;
318
319	while (size) {
320		if (fp->off < 0 || fp->off >= fp->size)
321			break;
322		bno = fp->off / ISO_DEFAULT_BLOCK_SIZE + fp->bno;
323		if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1)
324		    || size < ISO_DEFAULT_BLOCK_SIZE)
325			dp = buf;
326		else
327			dp = start;
328		twiddle();
329		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
330					   ISO_DEFAULT_BLOCK_SIZE, dp, &read);
331		if (rc)
332			return rc;
333		if (read != ISO_DEFAULT_BLOCK_SIZE)
334			return EIO;
335		if (dp == buf) {
336			off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1);
337			if (read > off + size)
338				read = off + size;
339			read -= off;
340			bcopy(buf + off, start, read);
341			start += read;
342			fp->off += read;
343			size -= read;
344		} else {
345			start += ISO_DEFAULT_BLOCK_SIZE;
346			fp->off += ISO_DEFAULT_BLOCK_SIZE;
347			size -= ISO_DEFAULT_BLOCK_SIZE;
348		}
349	}
350	if (resid)
351		*resid = size;
352	return rc;
353}
354
355static int
356cd9660_write(f, start, size, resid)
357	struct open_file *f;
358	void *start;
359	size_t size;
360	size_t *resid;
361{
362	return EROFS;
363}
364
365static off_t
366cd9660_seek(f, offset, where)
367	struct open_file *f;
368	off_t offset;
369	int where;
370{
371	struct file *fp = (struct file *)f->f_fsdata;
372
373	switch (where) {
374	case SEEK_SET:
375		fp->off = offset;
376		break;
377	case SEEK_CUR:
378		fp->off += offset;
379		break;
380	case SEEK_END:
381		fp->off = fp->size - offset;
382		break;
383	default:
384		return -1;
385	}
386	return fp->off;
387}
388
389static int
390cd9660_stat(f, sb)
391	struct open_file *f;
392	struct stat *sb;
393{
394	struct file *fp = (struct file *)f->f_fsdata;
395
396	/* only importatn stuff */
397	sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
398	sb->st_uid = sb->st_gid = 0;
399	sb->st_size = fp->size;
400	return 0;
401}
402