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