cd9660.c revision 1.9 1 /* $NetBSD: cd9660.c,v 1.9 1999/11/11 20:23:16 thorpej Exp $ */
2
3 /*
4 * Copyright (C) 1996 Wolfgang Solfrank.
5 * Copyright (C) 1996 TooLs GmbH.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by TooLs GmbH.
19 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * Stand-alone ISO9660 file reading package.
36 *
37 * Note: This doesn't support Rock Ridge extensions, extended attributes,
38 * blocksizes other than 2048 bytes, multi-extent files, etc.
39 */
40 #include <sys/param.h>
41 #include <isofs/cd9660/iso.h>
42
43 #include "stand.h"
44 #include "cd9660.h"
45
46 /*
47 * XXX Does not currently implement:
48 * XXX
49 * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
50 * XXX LIBSA_FS_SINGLECOMPONENT
51 */
52
53 struct file {
54 off_t off; /* Current offset within file */
55 daddr_t bno; /* Starting block number */
56 off_t size; /* Size of file */
57 };
58
59 struct ptable_ent {
60 char namlen [ISODCL( 1, 1)]; /* 711 */
61 char extlen [ISODCL( 2, 2)]; /* 711 */
62 char block [ISODCL( 3, 6)]; /* 732 */
63 char parent [ISODCL( 7, 8)]; /* 722 */
64 char name [1];
65 };
66 #define PTFIXSZ 8
67 #define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
68
69 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
70
71 static int pnmatch __P((char *, struct ptable_ent *));
72 static int dirmatch __P((char *, struct iso_directory_record *));
73
74 static int
75 pnmatch(path, pp)
76 char *path;
77 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
94 dirmatch(path, dp)
95 char *path;
96 struct iso_directory_record *dp;
97 {
98 char *cp;
99 int i;
100
101 /* This needs to be a regular file */
102 if (dp->flags[0] & 6)
103 return 0;
104
105 cp = dp->name;
106 for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) {
107 if (!*path)
108 break;
109 if (toupper(*path) == *cp)
110 continue;
111 return 0;
112 }
113 if (*path)
114 return 0;
115 /*
116 * Allow stripping of trailing dots and the version number.
117 * Note that this will find the first instead of the last version
118 * of a file.
119 */
120 if (i >= 0 && (*cp == ';' || *cp == '.')) {
121 /* This is to prevent matching of numeric extensions */
122 if (*cp == '.' && cp[1] != ';')
123 return 0;
124 while (--i >= 0)
125 if (*++cp != ';' && (*cp < '0' || *cp > '9'))
126 return 0;
127 }
128 return 1;
129 }
130
131 int
132 cd9660_open(path, f)
133 char *path;
134 struct open_file *f;
135 {
136 struct file *fp = 0;
137 void *buf;
138 struct iso_primary_descriptor *vd;
139 size_t buf_size, read, psize, dsize;
140 daddr_t bno;
141 int parent, ent;
142 struct ptable_ent *pp;
143 struct iso_directory_record *dp = 0;
144 int rc;
145
146 /* First find the volume descriptor */
147 buf = alloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
148 vd = buf;
149 for (bno = 16;; bno++) {
150 #if !defined(LIBSA_NO_TWIDDLE)
151 twiddle();
152 #endif
153 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno),
154 ISO_DEFAULT_BLOCK_SIZE, buf, &read);
155 if (rc)
156 goto out;
157 if (read != ISO_DEFAULT_BLOCK_SIZE) {
158 rc = EIO;
159 goto out;
160 }
161 rc = EINVAL;
162 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
163 goto out;
164 if (isonum_711(vd->type) == ISO_VD_END)
165 goto out;
166 if (isonum_711(vd->type) == ISO_VD_PRIMARY)
167 break;
168 }
169 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
170 goto out;
171
172 /* Now get the path table and lookup the directory of the file */
173 bno = isonum_732(vd->type_m_path_table);
174 psize = isonum_733(vd->path_table_size);
175
176 if (psize > ISO_DEFAULT_BLOCK_SIZE) {
177 free(buf, ISO_DEFAULT_BLOCK_SIZE);
178 buf = alloc(buf_size = roundup(psize, ISO_DEFAULT_BLOCK_SIZE));
179 }
180
181 #if !defined(LIBSA_NO_TWIDDLE)
182 twiddle();
183 #endif
184 rc = DEV_STRATEGY(f->f_dev)(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 ((caddr_t)pp >= (caddr_t)buf + psize)
201 break;
202 if (isonum_722(pp->parent) != parent)
203 break;
204 if (!pnmatch(path, pp)) {
205 pp = (struct ptable_ent *)((caddr_t)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 ((caddr_t)pp < (caddr_t)buf + psize) {
213 if (isonum_722(pp->parent) == parent)
214 break;
215 pp = (struct ptable_ent *)((caddr_t)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 #if !defined(LIBSA_NO_TWIDDLE)
227 twiddle();
228 #endif
229 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
230 cdb2devb(bno),
231 ISO_DEFAULT_BLOCK_SIZE,
232 buf, &read);
233 if (rc)
234 goto out;
235 if (read != ISO_DEFAULT_BLOCK_SIZE) {
236 rc = EIO;
237 goto out;
238 }
239 dp = (struct iso_directory_record *)buf;
240 }
241 if (!isonum_711(dp->length)) {
242 if ((void *)dp == buf)
243 psize += ISO_DEFAULT_BLOCK_SIZE;
244 else
245 psize = roundup(psize, ISO_DEFAULT_BLOCK_SIZE);
246 continue;
247 }
248 if (dsize == 1)
249 dsize = isonum_733(dp->size);
250 if (dirmatch(path, dp))
251 break;
252 psize += isonum_711(dp->length);
253 dp = (struct iso_directory_record *)((caddr_t)dp + isonum_711(dp->length));
254 }
255
256 if (psize >= dsize) {
257 rc = ENOENT;
258 goto out;
259 }
260
261 /* allocate file system specific data structure */
262 fp = alloc(sizeof(struct file));
263 bzero(fp, sizeof(struct file));
264 f->f_fsdata = (void *)fp;
265
266 fp->off = 0;
267 fp->bno = isonum_733(dp->extent);
268 fp->size = isonum_733(dp->size);
269 free(buf, buf_size);
270
271 return 0;
272
273 out:
274 if (fp)
275 free(fp, sizeof(struct file));
276 free(buf, buf_size);
277
278 return rc;
279 }
280
281 #if !defined(LIBSA_NO_FS_CLOSE)
282 int
283 cd9660_close(f)
284 struct open_file *f;
285 {
286 struct file *fp = (struct file *)f->f_fsdata;
287
288 f->f_fsdata = 0;
289 free(fp, sizeof *fp);
290
291 return 0;
292 }
293 #endif /* !defined(LIBSA_NO_FS_CLOSE) */
294
295 int
296 cd9660_read(f, start, size, resid)
297 struct open_file *f;
298 void *start;
299 size_t size;
300 size_t *resid;
301 {
302 struct file *fp = (struct file *)f->f_fsdata;
303 int rc = 0;
304 daddr_t bno;
305 char buf[ISO_DEFAULT_BLOCK_SIZE];
306 char *dp;
307 size_t read, off;
308
309 while (size) {
310 if (fp->off < 0 || fp->off >= fp->size)
311 break;
312 bno = fp->off / ISO_DEFAULT_BLOCK_SIZE + fp->bno;
313 if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1)
314 || size < ISO_DEFAULT_BLOCK_SIZE)
315 dp = buf;
316 else
317 dp = start;
318 #if !defined(LIBSA_NO_TWIDDLE)
319 twiddle();
320 #endif
321 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno),
322 ISO_DEFAULT_BLOCK_SIZE, dp, &read);
323 if (rc)
324 return rc;
325 if (read != ISO_DEFAULT_BLOCK_SIZE)
326 return EIO;
327 if (dp == buf) {
328 off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1);
329 if (read > off + size)
330 read = off + size;
331 read -= off;
332 bcopy(buf + off, start, read);
333 start = (caddr_t)start + read;
334 fp->off += read;
335 size -= read;
336 } else {
337 start = (caddr_t)start + ISO_DEFAULT_BLOCK_SIZE;
338 fp->off += ISO_DEFAULT_BLOCK_SIZE;
339 size -= ISO_DEFAULT_BLOCK_SIZE;
340 }
341 }
342 if (resid)
343 *resid = size;
344 return rc;
345 }
346
347 #if !defined(LIBSA_NO_FS_WRITE)
348 int
349 cd9660_write(f, start, size, resid)
350 struct open_file *f;
351 void *start;
352 size_t size;
353 size_t *resid;
354 {
355 return EROFS;
356 }
357 #endif /* !defined(LIBSA_NO_FS_WRITE) */
358
359 #if !defined(LIBSA_NO_FS_SEEK)
360 off_t
361 cd9660_seek(f, offset, where)
362 struct open_file *f;
363 off_t offset;
364 int where;
365 {
366 struct file *fp = (struct file *)f->f_fsdata;
367
368 switch (where) {
369 case SEEK_SET:
370 fp->off = offset;
371 break;
372 case SEEK_CUR:
373 fp->off += offset;
374 break;
375 case SEEK_END:
376 fp->off = fp->size - offset;
377 break;
378 default:
379 return -1;
380 }
381 return fp->off;
382 }
383 #endif /* !defined(LIBSA_NO_FS_SEEK) */
384
385 int
386 cd9660_stat(f, sb)
387 struct open_file *f;
388 struct stat *sb;
389 {
390 struct file *fp = (struct file *)f->f_fsdata;
391
392 /* only importatn stuff */
393 sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
394 sb->st_uid = sb->st_gid = 0;
395 sb->st_size = fp->size;
396 return 0;
397 }
398