xref: /openbsd-src/sys/lib/libsa/cd9660.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: cd9660.c,v 1.12 2004/07/09 19:20:17 drahn 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(char *path, struct ptable_ent *pp)
79 {
80 	char *cp;
81 	int i;
82 
83 	cp = pp->name;
84 	for (i = isonum_711(pp->namlen); --i >= 0; path++, cp++) {
85 		if (toupper(*path) == *cp)
86 			continue;
87 		return 0;
88 	}
89 	if (*path != '/')
90 		return 0;
91 	return 1;
92 }
93 
94 static int
95 dirmatch(char *path, struct iso_directory_record *dp)
96 {
97 	char *cp;
98 	int i;
99 
100 	/* This needs to be a regular file */
101 	if (dp->flags[0] & 6)
102 		return 0;
103 
104 	cp = dp->name;
105 	for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) {
106 		if (!*path)
107 			break;
108 		if (toupper(*path) == *cp)
109 			continue;
110 		return 0;
111 	}
112 	if (*path)
113 		return 0;
114 	/*
115 	 * Allow stripping of trailing dots and the version number.
116 	 * Note that this will find the first instead of the last version
117 	 * of a file.
118 	 */
119 	if (i >= 0 && (*cp == ';' || *cp == '.')) {
120 		/* This is to prevent matching of numeric extensions */
121 		if (*cp == '.' && cp[1] != ';')
122 			return 0;
123 		while (--i >= 0)
124 			if (*++cp != ';' && (*cp < '0' || *cp > '9'))
125 				return 0;
126 	}
127 	return 1;
128 }
129 
130 int
131 cd9660_open(char *path, struct open_file *f)
132 {
133 	struct file *fp = 0;
134 	void *buf;
135 	struct iso_primary_descriptor *vd;
136 	size_t buf_size, nread, psize, dsize;
137 	daddr_t bno;
138 	int parent, ent;
139 	struct ptable_ent *pp;
140 	struct iso_directory_record *dp;
141 	int rc;
142 
143 	/* First find the volume descriptor */
144 	buf = alloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
145 	dp = (struct iso_directory_record *)buf;
146 	vd = buf;
147 	for (bno = 16;; bno++) {
148 		twiddle();
149 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
150 					   ISO_DEFAULT_BLOCK_SIZE, buf, &nread);
151 		if (rc)
152 			goto out;
153 		if (nread != ISO_DEFAULT_BLOCK_SIZE) {
154 			rc = EIO;
155 			goto out;
156 		}
157 		rc = EINVAL;
158 		if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
159 			goto out;
160 		if (isonum_711(vd->type) == ISO_VD_END)
161 			goto out;
162 		if (isonum_711(vd->type) == ISO_VD_PRIMARY)
163 			break;
164 	}
165 	if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
166 		goto out;
167 
168 	/* Now get the path table and lookup the directory of the file */
169 	bno = isonum_732(vd->type_m_path_table);
170 	psize = isonum_733(vd->path_table_size);
171 
172 	if (psize > ISO_DEFAULT_BLOCK_SIZE) {
173 		free(buf, ISO_DEFAULT_BLOCK_SIZE);
174 		buf = alloc(buf_size = roundup(psize, ISO_DEFAULT_BLOCK_SIZE));
175 	}
176 
177 	twiddle();
178 	rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
179 				   buf_size, buf, &nread);
180 	if (rc)
181 		goto out;
182 	if (nread != buf_size) {
183 		rc = EIO;
184 		goto out;
185 	}
186 
187 	parent = 1;
188 	pp = (struct ptable_ent *)buf;
189 	ent = 1;
190 	bno = isonum_732(pp->block) + isonum_711(pp->extlen);
191 
192 	rc = ENOENT;
193 	/*
194 	 * Remove extra separators
195 	 */
196 	while (*path == '/')
197 		path++;
198 
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, &nread);
231 			if (rc)
232 				goto out;
233 			if (nread != 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 +
252 		    isonum_711(dp->length));
253 	}
254 
255 	if (psize >= dsize) {
256 		rc = ENOENT;
257 		goto out;
258 	}
259 
260 	/* allocate file system specific data structure */
261 	fp = alloc(sizeof(struct file));
262 	bzero(fp, sizeof(struct file));
263 	f->f_fsdata = (void *)fp;
264 
265 	fp->off = 0;
266 	fp->bno = isonum_733(dp->extent);
267 	fp->size = isonum_733(dp->size);
268 	free(buf, buf_size);
269 
270 	return 0;
271 
272 out:
273 	if (fp)
274 		free(fp, sizeof(struct file));
275 	free(buf, buf_size);
276 
277 	return rc;
278 }
279 
280 int
281 cd9660_close(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(struct open_file *f, void *start, size_t size, size_t *resid)
293 {
294 	struct file *fp = (struct file *)f->f_fsdata;
295 	int rc = 0;
296 	daddr_t bno;
297 	char buf[ISO_DEFAULT_BLOCK_SIZE];
298 	char *dp;
299 	size_t nread, off;
300 
301 	while (size) {
302 		if (fp->off < 0 || fp->off >= fp->size)
303 			break;
304 		bno = (fp->off >> ISO_DEFAULT_BLOCK_SHIFT) + fp->bno;
305 		if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1)
306 		    || size < ISO_DEFAULT_BLOCK_SIZE)
307 			dp = buf;
308 		else
309 			dp = start;
310 		twiddle();
311 		rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
312 					   ISO_DEFAULT_BLOCK_SIZE, dp, &nread);
313 		if (rc)
314 			return rc;
315 		if (nread != ISO_DEFAULT_BLOCK_SIZE)
316 			return EIO;
317 
318 		/*
319 		 * off is either 0 in the dp == start case or
320 		 * the offset to the interesting data into the buffer of 'buf'
321 		 */
322 		off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1);
323 		nread -= off;
324 		if (nread > size)
325 			nread = size;
326 
327 		if (nread > (fp->size - fp->off))
328 			nread = (fp->size - fp->off);
329 
330 		if (dp == buf)
331 			bcopy(buf + off, start, nread);
332 
333 		start += nread;
334 		fp->off += nread;
335 		size -= nread;
336 	}
337 	if (resid)
338 		*resid = size;
339 	return rc;
340 }
341 
342 int
343 cd9660_write(struct open_file *f, void *start, size_t size, size_t *resid)
344 {
345 	return EROFS;
346 }
347 
348 off_t
349 cd9660_seek(struct open_file *f, off_t offset, int where)
350 {
351 	struct file *fp = (struct file *)f->f_fsdata;
352 
353 	switch (where) {
354 	case SEEK_SET:
355 		fp->off = offset;
356 		break;
357 	case SEEK_CUR:
358 		fp->off += offset;
359 		break;
360 	case SEEK_END:
361 		fp->off = fp->size - offset;
362 		break;
363 	default:
364 		return -1;
365 	}
366 	return fp->off;
367 }
368 
369 int
370 cd9660_stat(struct open_file *f, struct stat *sb)
371 {
372 	struct file *fp = (struct file *)f->f_fsdata;
373 
374 	/* only important stuff */
375 	sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
376 	sb->st_uid = sb->st_gid = 0;
377 	sb->st_size = fp->size;
378 	return 0;
379 }
380 
381 /*
382  * Not implemented.
383  */
384 #ifndef NO_READDIR
385 int
386 cd9660_readdir(struct open_file *f, char *name)
387 {
388 	return (EROFS);
389 }
390 #endif
391