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