]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libstand/cd9660.c
mdoc(7) police: s/BSD/.Bx/ where appropriate.
[FreeBSD/FreeBSD.git] / lib / libstand / cd9660.c
1 /* $FreeBSD$ */
2 /*      $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner 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 <string.h>
43 #include <sys/dirent.h>
44 #include <isofs/cd9660/iso.h>
45
46 #include "stand.h"
47
48 static int      cd9660_open(const char *path, struct open_file *f);
49 static int      cd9660_close(struct open_file *f);
50 static int      cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid);
51 static int      cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid);
52 static off_t    cd9660_seek(struct open_file *f, off_t offset, int where);
53 static int      cd9660_stat(struct open_file *f, struct stat *sb);
54 static int      cd9660_readdir(struct open_file *f, struct dirent *d);
55
56 struct fs_ops cd9660_fsops = {
57         "cd9660",
58         cd9660_open,
59         cd9660_close,
60         cd9660_read,
61         cd9660_write,
62         cd9660_seek,
63         cd9660_stat,
64         cd9660_readdir
65 };
66
67 struct file {
68         int             f_isdir;        /* nonzero if file is directory */
69         off_t           f_off;          /* Current offset within file */
70         daddr_t         f_bno;          /* Starting block number */
71         off_t           f_size;         /* Size of file */
72         daddr_t         f_buf_blkno;    /* block number of data block */        
73         char            *f_buf;         /* buffer for data block */
74 };
75
76 struct ptable_ent {
77         char namlen     [ISODCL( 1, 1)];        /* 711 */
78         char extlen     [ISODCL( 2, 2)];        /* 711 */
79         char block      [ISODCL( 3, 6)];        /* 732 */
80         char parent     [ISODCL( 7, 8)];        /* 722 */
81         char name       [1];
82 };
83 #define PTFIXSZ         8
84 #define PTSIZE(pp)      roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
85
86 #define cdb2devb(bno)   ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
87
88 /* XXX these should be in the system headers */
89 static __inline int
90 isonum_722(p)
91         u_char *p;
92 {
93         return (*p << 8)|p[1];
94 }
95
96 static __inline int
97 isonum_732(p)
98         u_char *p;
99 {
100         return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
101 }
102
103 static int
104 dirmatch(path, dp)
105         const char *path;
106         struct iso_directory_record *dp;
107 {
108         char *cp;
109         int i;
110
111         cp = dp->name;
112         for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) {
113                 if (!*path || *path == '/')
114                         break;
115                 if (toupper(*path) == *cp)
116                         continue;
117                 return 0;
118         }
119         if (*path && *path != '/')
120                 return 0;
121         /*
122          * Allow stripping of trailing dots and the version number.
123          * Note that this will find the first instead of the last version
124          * of a file.
125          */
126         if (i >= 0 && (*cp == ';' || *cp == '.')) {
127                 /* This is to prevent matching of numeric extensions */
128                 if (*cp == '.' && cp[1] != ';')
129                         return 0;
130                 while (--i >= 0)
131                         if (*++cp != ';' && (*cp < '0' || *cp > '9'))
132                                 return 0;
133         }
134         return 1;
135 }
136
137 static int
138 cd9660_open(path, f)
139         const char *path;
140         struct open_file *f;
141 {
142         struct file *fp = 0;
143         void *buf;
144         struct iso_primary_descriptor *vd;
145         size_t buf_size, read, dsize, off;
146         daddr_t bno, boff;
147         struct iso_directory_record rec;
148         struct iso_directory_record *dp = 0;
149         int rc;
150
151         /* First find the volume descriptor */
152         buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
153         vd = buf;
154         for (bno = 16;; bno++) {
155                 twiddle();
156                 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
157                                            ISO_DEFAULT_BLOCK_SIZE, buf, &read);
158                 if (rc)
159                         goto out;
160                 if (read != ISO_DEFAULT_BLOCK_SIZE) {
161                         rc = EIO;
162                         goto out;
163                 }
164                 rc = EINVAL;
165                 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
166                         goto out;
167                 if (isonum_711(vd->type) == ISO_VD_END)
168                         goto out;
169                 if (isonum_711(vd->type) == ISO_VD_PRIMARY)
170                         break;
171         }
172         if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
173                 goto out;
174
175         rec = *(struct iso_directory_record *) vd->root_directory_record;
176         if (*path == '/') path++; /* eat leading '/' */
177
178         while (*path) {
179                 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
180                 dsize = isonum_733(rec.size);
181                 off = 0;
182                 boff = 0;
183
184                 while (off < dsize) {
185                         if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
186                                 twiddle();
187                                 rc = f->f_dev->dv_strategy
188                                         (f->f_devdata, F_READ,
189                                          cdb2devb(bno + boff),
190                                          ISO_DEFAULT_BLOCK_SIZE,
191                                          buf, &read);
192                                 if (rc)
193                                         goto out;
194                                 if (read != ISO_DEFAULT_BLOCK_SIZE) {
195                                         rc = EIO;
196                                         goto out;
197                                 }
198                                 boff++;
199                                 dp = (struct iso_directory_record *) buf;
200                         }
201                         if (isonum_711(dp->length) == 0) {
202                             /* skip to next block, if any */
203                             off = boff * ISO_DEFAULT_BLOCK_SIZE;
204                             continue;
205                         }
206
207                         if (dirmatch(path, dp))
208                                 break;
209
210                         dp = (struct iso_directory_record *)
211                                 ((char *) dp + isonum_711(dp->length));
212                         off += isonum_711(dp->length);
213                 }
214                 if (off == dsize) {
215                         rc = ENOENT;
216                         goto out;
217                 }
218
219                 rec = *dp;
220                 while (*path && *path != '/') /* look for next component */
221                         path++;
222                 if (*path) path++; /* skip '/' */
223         }
224
225         /* allocate file system specific data structure */
226         fp = malloc(sizeof(struct file));
227         bzero(fp, sizeof(struct file));
228         f->f_fsdata = (void *)fp;
229
230         fp->f_isdir = (isonum_711(rec.flags) & 2) != 0;
231         fp->f_off = 0;
232         fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
233         fp->f_size = isonum_733(rec.size);
234         free(buf);
235
236         return 0;
237
238 out:
239         if (fp)
240                 free(fp);
241         free(buf);
242
243         return rc;
244 }
245
246 static int
247 cd9660_close(f)
248         struct open_file *f;
249 {
250         struct file *fp = (struct file *)f->f_fsdata;
251
252         f->f_fsdata = 0;
253         free(fp);
254
255         return 0;
256 }
257
258 static int
259 buf_read_file(f, buf_p, size_p)
260         struct open_file *f;
261         char **buf_p;
262         size_t *size_p;
263 {
264         struct file *fp = (struct file *)f->f_fsdata;
265         daddr_t blkno, blkoff;
266         int rc = 0;
267         size_t read;
268
269         blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
270         blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
271
272         if (blkno != fp->f_buf_blkno) {
273                 if (fp->f_buf == (char *)0)
274                         fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
275
276                 twiddle();
277                 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
278                     cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read);
279                 if (rc)
280                         return (rc);
281                 if (read != ISO_DEFAULT_BLOCK_SIZE)
282                         return (EIO);
283
284                 fp->f_buf_blkno = blkno;
285         }
286
287         *buf_p = fp->f_buf + blkoff;
288         *size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
289
290         if (*size_p > fp->f_size - fp->f_off)
291                 *size_p = fp->f_size - fp->f_off;
292         return (rc);
293 }
294
295 static 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         char *buf, *addr;
304         size_t buf_size, csize;
305         int rc = 0;
306
307         addr = start;
308         while (size) {
309                 if (fp->f_off < 0 || fp->f_off >= fp->f_size)
310                         break;
311
312                 rc = buf_read_file(f, &buf, &buf_size);
313                 if (rc)
314                         break;
315
316                 csize = size > buf_size ? buf_size : size;
317                 bcopy(buf, addr, csize);
318
319                 fp->f_off += csize;
320                 addr += csize;
321                 size -= csize;
322         }
323         if (resid)
324                 *resid = size;
325         return (rc);
326 }
327
328 static int
329 cd9660_readdir(struct open_file *f, struct dirent *d)
330 {
331         struct file *fp = (struct file *)f->f_fsdata;
332         struct iso_directory_record *ep;
333         size_t buf_size, reclen, namelen;
334         int error = 0;
335         char *buf;
336
337 again:
338         if (fp->f_off >= fp->f_size)
339                 return (ENOENT);
340         error = buf_read_file(f, &buf, &buf_size);
341         if (error)
342                 return (error);
343         ep = (struct iso_directory_record *)buf;
344
345         if (isonum_711(ep->length) == 0) {
346                 daddr_t blkno;
347                 
348                 /* skip to next block, if any */
349                 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
350                 fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
351                 goto again;
352         }
353
354         namelen = isonum_711(ep->name_len);
355         if (namelen == 1 && ep->name[0] == 1)
356                 namelen = 2;
357         reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
358         reclen = (reclen + 3) & ~3;
359
360         d->d_fileno = isonum_733(ep->extent);
361         d->d_reclen = reclen;
362         if (isonum_711(ep->flags) & 2)
363                 d->d_type = DT_DIR;
364         else
365                 d->d_type = DT_REG;
366         d->d_namlen = namelen;
367
368         if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0)
369                 strcpy(d->d_name, ".");
370         else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1)
371                 strcpy(d->d_name, "..");
372         else
373                 bcopy(ep->name, d->d_name, d->d_namlen);
374         d->d_name[d->d_namlen] = 0;
375
376         fp->f_off += isonum_711(ep->length);
377         return (0);
378 }
379
380 static int
381 cd9660_write(f, start, size, resid)
382         struct open_file *f;
383         void *start;
384         size_t size;
385         size_t *resid;
386 {
387         return EROFS;
388 }
389
390 static off_t
391 cd9660_seek(f, offset, where)
392         struct open_file *f;
393         off_t offset;
394         int where;
395 {
396         struct file *fp = (struct file *)f->f_fsdata;
397
398         switch (where) {
399         case SEEK_SET:
400                 fp->f_off = offset;
401                 break;
402         case SEEK_CUR:
403                 fp->f_off += offset;
404                 break;
405         case SEEK_END:
406                 fp->f_off = fp->f_size - offset;
407                 break;
408         default:
409                 return -1;
410         }
411         return fp->f_off;
412 }
413
414 static int
415 cd9660_stat(f, sb)
416         struct open_file *f;
417         struct stat *sb;
418 {
419         struct file *fp = (struct file *)f->f_fsdata;
420
421         /* only important stuff */
422         sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
423         if (fp->f_isdir)
424                 sb->st_mode |= S_IFDIR;
425         else
426                 sb->st_mode |= S_IFREG;
427         sb->st_uid = sb->st_gid = 0;
428         sb->st_size = fp->f_size;
429         return 0;
430 }