xref: /openbsd-src/sys/lib/libsa/cd9660.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: cd9660.c,v 1.7 1998/05/30 02:29:56 mickey 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 	while (*path) {
200 		if ((void *)pp >= buf + psize)
201 			break;
202 		if (isonum_722(pp->parent) != parent)
203 			break;
204 		if (!pnmatch(path, pp)) {
205 			pp = (struct ptable_ent *)((void *)pp + PTSIZE(pp));
206 			ent++;
207 			continue;
208 		}
209 		path += isonum_711(pp->namlen) + 1;
210 		parent = ent;
211 		bno = isonum_732(pp->block) + isonum_711(pp->extlen);
212 		while ((void *)pp < buf + psize) {
213 			if (isonum_722(pp->parent) == parent)
214 				break;
215 			pp = (struct ptable_ent *)((void *)pp + PTSIZE(pp));
216 			ent++;
217 		}
218 	}
219 
220 	/* Now bno has the start of the directory that supposedly contains the file */
221 	bno--;
222 	dsize = 1;		/* Something stupid, but > 0	XXX */
223 	for (psize = 0; psize < dsize;) {
224 		if (!(psize % ISO_DEFAULT_BLOCK_SIZE)) {
225 			bno++;
226 			twiddle();
227 			rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
228 						   cdb2devb(bno),
229 						   ISO_DEFAULT_BLOCK_SIZE,
230 						   buf, &read);
231 			if (rc)
232 				goto out;
233 			if (read != ISO_DEFAULT_BLOCK_SIZE) {
234 				rc = EIO;
235 				goto out;
236 			}
237 			dp = (struct iso_directory_record *)buf;
238 		}
239 		if (!isonum_711(dp->length)) {
240 			if ((void *)dp == buf)
241 				psize += ISO_DEFAULT_BLOCK_SIZE;
242 			else
243 				psize = roundup(psize, ISO_DEFAULT_BLOCK_SIZE);
244 			continue;
245 		}
246 		if (dsize == 1)
247 			dsize = isonum_733(dp->size);
248 		if (dirmatch(path, dp))
249 			break;
250 		psize += isonum_711(dp->length);
251 		dp = (struct iso_directory_record *)((void *)dp + isonum_711(dp->length));
252 	}
253 
254 	if (psize >= dsize) {
255 		rc = ENOENT;
256 		goto out;
257 	}
258 
259 	/* allocate file system specific data structure */
260 	fp = alloc(sizeof(struct file));
261 	bzero(fp, sizeof(struct file));
262 	f->f_fsdata = (void *)fp;
263 
264 	fp->off = 0;
265 	fp->bno = isonum_733(dp->extent);
266 	fp->size = isonum_733(dp->size);
267 	free(buf, buf_size);
268 
269 	return 0;
270 
271 out:
272 	if (fp)
273 		free(fp, sizeof(struct file));
274 	free(buf, buf_size);
275 
276 	return rc;
277 }
278 
279 int
280 cd9660_close(f)
281 	struct open_file *f;
282 {
283 	struct file *fp = (struct file *)f->f_fsdata;
284 
285 	f->f_fsdata = 0;
286 	free(fp, sizeof *fp);
287 
288 	return 0;
289 }
290 
291 int
292 cd9660_read(f, start, size, resid)
293 	struct open_file *f;
294 	void *start;
295 	size_t size;
296 	size_t *resid;
297 {
298 	struct file *fp = (struct file *)f->f_fsdata;
299 	int rc = 0;
300 	daddr_t bno;
301 	char buf[ISO_DEFAULT_BLOCK_SIZE];
302 	char *dp;
303 	size_t read, off;
304 
305 	while (size) {
306 		if (fp->off < 0 || fp->off >= fp->size)
307 			break;
308 		bno = (fp->off >> ISO_DEFAULT_BLOCK_SHIFT) + fp->bno;
309 		if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1)
310 		    || size < ISO_DEFAULT_BLOCK_SIZE)
311 			dp = buf;
312 		else
313 			dp = start;
314 		twiddle();
315 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
316 					   ISO_DEFAULT_BLOCK_SIZE, dp, &read);
317 		if (rc)
318 			return rc;
319 		if (read != ISO_DEFAULT_BLOCK_SIZE)
320 			return EIO;
321 		if (dp == buf) {
322 			off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1);
323 			if (read > off + size)
324 				read = off + size;
325 			read -= off;
326 			bcopy(buf + off, start, read);
327 			start += read;
328 			fp->off += read;
329 			size -= read;
330 		} else {
331 			start += ISO_DEFAULT_BLOCK_SIZE;
332 			fp->off += ISO_DEFAULT_BLOCK_SIZE;
333 			size -= ISO_DEFAULT_BLOCK_SIZE;
334 		}
335 	}
336 	if (resid)
337 		*resid = size;
338 	return rc;
339 }
340 
341 int
342 cd9660_write(f, start, size, resid)
343 	struct open_file *f;
344 	void *start;
345 	size_t size;
346 	size_t *resid;
347 {
348 	return EROFS;
349 }
350 
351 off_t
352 cd9660_seek(f, offset, where)
353 	struct open_file *f;
354 	off_t offset;
355 	int where;
356 {
357 	struct file *fp = (struct file *)f->f_fsdata;
358 
359 	switch (where) {
360 	case SEEK_SET:
361 		fp->off = offset;
362 		break;
363 	case SEEK_CUR:
364 		fp->off += offset;
365 		break;
366 	case SEEK_END:
367 		fp->off = fp->size - offset;
368 		break;
369 	default:
370 		return -1;
371 	}
372 	return fp->off;
373 }
374 
375 int
376 cd9660_stat(f, sb)
377 	struct open_file *f;
378 	struct stat *sb;
379 {
380 	struct file *fp = (struct file *)f->f_fsdata;
381 
382 	/* only importatn stuff */
383 	sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
384 	sb->st_uid = sb->st_gid = 0;
385 	sb->st_size = fp->size;
386 	return 0;
387 }
388 
389 /*
390  * Not implemented.
391  */
392 #ifndef NO_READDIR
393 int
394 cd9660_readdir(f, name)
395 	struct open_file *f;
396 	char *name;
397 {
398 	return (EROFS);
399 }
400 #endif
401 
402