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