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