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