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