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