1 /* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */
4 * Copyright (C) 1996 Wolfgang Solfrank.
5 * Copyright (C) 1996 TooLs GmbH.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
34 #include <sys/cdefs.h>
37 * Stand-alone ISO9660 file reading package.
39 * Note: This doesn't support Rock Ridge extensions, extended attributes,
40 * blocksizes other than 2048 bytes, multi-extent files, etc.
42 #include <sys/param.h>
44 #include <sys/dirent.h>
45 #include <isofs/cd9660/iso.h>
46 #include <isofs/cd9660/cd9660_rrip.h>
50 #define SUSP_CONTINUATION "CE"
51 #define SUSP_PRESENT "SP"
52 #define SUSP_STOP "ST"
53 #define SUSP_EXTREF "ER"
54 #define RRIP_NAME "NM"
58 u_char signature
[ISODCL ( 5, 6)];
59 u_char len_skp
[ISODCL ( 7, 7)]; /* 711 */
62 static int buf_read_file(struct open_file
*f
, char **buf_p
,
64 static int cd9660_open(const char *path
, struct open_file
*f
);
65 static int cd9660_close(struct open_file
*f
);
66 static int cd9660_read(struct open_file
*f
, void *buf
, size_t size
,
68 static off_t
cd9660_seek(struct open_file
*f
, off_t offset
, int where
);
69 static int cd9660_stat(struct open_file
*f
, struct stat
*sb
);
70 static int cd9660_readdir(struct open_file
*f
, struct dirent
*d
);
71 static int dirmatch(struct open_file
*f
, const char *path
,
72 struct iso_directory_record
*dp
, int use_rrip
, int lenskip
);
73 static int rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
,
75 static char *rrip_lookup_name(struct open_file
*f
,
76 struct iso_directory_record
*dp
, int lenskip
, size_t *len
);
77 static ISO_SUSP_HEADER
*susp_lookup_record(struct open_file
*f
,
78 const char *identifier
, struct iso_directory_record
*dp
,
81 struct fs_ops cd9660_fsops
= {
92 #define F_ISDIR 0x0001 /* Directory */
93 #define F_ROOTDIR 0x0002 /* Root directory */
94 #define F_RR 0x0004 /* Rock Ridge on this volume */
97 int f_flags
; /* file flags */
98 off_t f_off
; /* Current offset within file */
99 daddr_t f_bno
; /* Starting block number */
100 off_t f_size
; /* Size of file */
101 daddr_t f_buf_blkno
; /* block number of data block */
102 char *f_buf
; /* buffer for data block */
103 int f_susp_skip
; /* len_skip for SUSP records */
107 char namlen
[ISODCL( 1, 1)]; /* 711 */
108 char extlen
[ISODCL( 2, 2)]; /* 711 */
109 char block
[ISODCL( 3, 6)]; /* 732 */
110 char parent
[ISODCL( 7, 8)]; /* 722 */
114 #define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
116 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
118 static ISO_SUSP_HEADER
*
119 susp_lookup_record(struct open_file
*f
, const char *identifier
,
120 struct iso_directory_record
*dp
, int lenskip
)
122 static char susp_buffer
[ISO_DEFAULT_BLOCK_SIZE
];
129 p
= dp
->name
+ isonum_711(dp
->name_len
) + lenskip
;
130 /* Names of even length have a padding byte after the name. */
131 if ((isonum_711(dp
->name_len
) & 1) == 0)
133 end
= (char *)dp
+ isonum_711(dp
->length
);
134 while (p
+ 3 < end
) {
135 sh
= (ISO_SUSP_HEADER
*)p
;
136 if (bcmp(sh
->type
, identifier
, 2) == 0)
138 if (bcmp(sh
->type
, SUSP_STOP
, 2) == 0)
140 if (bcmp(sh
->type
, SUSP_CONTINUATION
, 2) == 0) {
141 shc
= (ISO_RRIP_CONT
*)sh
;
142 error
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
143 cdb2devb(isonum_733(shc
->location
)),
144 ISO_DEFAULT_BLOCK_SIZE
, susp_buffer
, &read
);
146 /* Bail if it fails. */
147 if (error
!= 0 || read
!= ISO_DEFAULT_BLOCK_SIZE
)
149 p
= susp_buffer
+ isonum_733(shc
->offset
);
150 end
= p
+ isonum_733(shc
->length
);
152 /* Ignore this record and skip to the next. */
153 p
+= isonum_711(sh
->length
);
155 /* Avoid infinite loops with corrupted file systems */
156 if (isonum_711(sh
->length
) == 0)
164 rrip_lookup_name(struct open_file
*f
, struct iso_directory_record
*dp
,
165 int lenskip
, size_t *len
)
172 p
= (ISO_RRIP_ALTNAME
*)susp_lookup_record(f
, RRIP_NAME
, dp
, lenskip
);
176 case ISO_SUSP_CFLAG_CURRENT
:
179 case ISO_SUSP_CFLAG_PARENT
:
183 *len
= isonum_711(p
->h
.length
) - 5;
184 return ((char *)p
+ 5);
187 * We don't handle hostnames or continued names as they are
188 * too hard, so just bail and use the default name.
195 rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
, int *lenskip
)
197 ISO_SUSP_PRESENT
*sp
;
201 /* First, see if we can find a SP field. */
202 p
= dp
->name
+ isonum_711(dp
->name_len
);
203 if (p
> (char *)dp
+ isonum_711(dp
->length
))
205 sp
= (ISO_SUSP_PRESENT
*)p
;
206 if (bcmp(sp
->h
.type
, SUSP_PRESENT
, 2) != 0)
208 if (isonum_711(sp
->h
.length
) != sizeof(ISO_SUSP_PRESENT
))
210 if (sp
->signature
[0] != 0xbe || sp
->signature
[1] != 0xef)
212 *lenskip
= isonum_711(sp
->len_skp
);
215 * Now look for an ER field. If RRIP is present, then there must
216 * be at least one of these. It would be more pedantic to walk
217 * through the list of fields looking for a Rock Ridge ER field.
219 er
= (ISO_RRIP_EXTREF
*)susp_lookup_record(f
, SUSP_EXTREF
, dp
, 0);
226 dirmatch(struct open_file
*f
, const char *path
, struct iso_directory_record
*dp
,
227 int use_rrip
, int lenskip
)
234 cp
= rrip_lookup_name(f
, dp
, lenskip
, &len
);
238 len
= isonum_711(dp
->name_len
);
243 for (i
= len
; --i
>= 0; path
++, cp
++) {
244 if (!*path
|| *path
== '/')
248 if (!icase
&& toupper(*path
) == *cp
)
252 if (*path
&& *path
!= '/')
255 * Allow stripping of trailing dots and the version number.
256 * Note that this will find the first instead of the last version
259 if (i
>= 0 && (*cp
== ';' || *cp
== '.')) {
260 /* This is to prevent matching of numeric extensions */
261 if (*cp
== '.' && cp
[1] != ';')
264 if (*++cp
!= ';' && (*cp
< '0' || *cp
> '9'))
271 cd9660_open(const char *path
, struct open_file
*f
)
273 struct file
*fp
= NULL
;
275 struct iso_primary_descriptor
*vd
;
276 size_t buf_size
, read
, dsize
, off
;
278 struct iso_directory_record rec
;
279 struct iso_directory_record
*dp
= NULL
;
280 int rc
, first
, use_rrip
, lenskip
;
282 /* First find the volume descriptor */
283 buf
= malloc(buf_size
= ISO_DEFAULT_BLOCK_SIZE
);
285 for (bno
= 16;; bno
++) {
287 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
, cdb2devb(bno
),
288 ISO_DEFAULT_BLOCK_SIZE
, buf
, &read
);
291 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
296 if (bcmp(vd
->id
, ISO_STANDARD_ID
, sizeof vd
->id
) != 0)
298 if (isonum_711(vd
->type
) == ISO_VD_END
)
300 if (isonum_711(vd
->type
) == ISO_VD_PRIMARY
)
303 if (isonum_723(vd
->logical_block_size
) != ISO_DEFAULT_BLOCK_SIZE
)
306 bcopy(vd
->root_directory_record
, &rec
, sizeof (rec
));
307 if (*path
== '/') path
++; /* eat leading '/' */
312 bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
313 dsize
= isonum_733(rec
.size
);
317 while (off
< dsize
) {
318 if ((off
% ISO_DEFAULT_BLOCK_SIZE
) == 0) {
320 rc
= f
->f_dev
->dv_strategy
321 (f
->f_devdata
, F_READ
,
322 cdb2devb(bno
+ boff
),
323 ISO_DEFAULT_BLOCK_SIZE
,
327 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
332 dp
= (struct iso_directory_record
*) buf
;
334 if (isonum_711(dp
->length
) == 0) {
335 /* skip to next block, if any */
336 off
= boff
* ISO_DEFAULT_BLOCK_SIZE
;
340 /* See if RRIP is in use. */
342 use_rrip
= rrip_check(f
, dp
, &lenskip
);
344 if (dirmatch(f
, path
, dp
, use_rrip
,
345 first
? 0 : lenskip
)) {
351 dp
= (struct iso_directory_record
*)
352 ((char *) dp
+ isonum_711(dp
->length
));
353 /* If the new block has zero length, it is padding. */
354 if (isonum_711(dp
->length
) == 0) {
355 /* Skip to next block, if any. */
356 off
= boff
* ISO_DEFAULT_BLOCK_SIZE
;
359 off
+= isonum_711(dp
->length
);
367 while (*path
&& *path
!= '/') /* look for next component */
369 if (*path
) path
++; /* skip '/' */
372 /* allocate file system specific data structure */
373 fp
= malloc(sizeof(struct file
));
374 bzero(fp
, sizeof(struct file
));
375 f
->f_fsdata
= (void *)fp
;
377 if ((isonum_711(rec
.flags
) & 2) != 0) {
378 fp
->f_flags
= F_ISDIR
;
381 fp
->f_flags
|= F_ROOTDIR
;
383 /* Check for Rock Ridge since we didn't in the loop above. */
384 bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
386 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
, cdb2devb(bno
),
387 ISO_DEFAULT_BLOCK_SIZE
, buf
, &read
);
390 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
394 dp
= (struct iso_directory_record
*)buf
;
395 use_rrip
= rrip_check(f
, dp
, &lenskip
);
399 fp
->f_susp_skip
= lenskip
;
402 fp
->f_bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
403 fp
->f_size
= isonum_733(rec
.size
);
417 cd9660_close(struct open_file
*f
)
419 struct file
*fp
= (struct file
*)f
->f_fsdata
;
428 buf_read_file(struct open_file
*f
, char **buf_p
, size_t *size_p
)
430 struct file
*fp
= (struct file
*)f
->f_fsdata
;
431 daddr_t blkno
, blkoff
;
435 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
+ fp
->f_bno
;
436 blkoff
= fp
->f_off
% ISO_DEFAULT_BLOCK_SIZE
;
438 if (blkno
!= fp
->f_buf_blkno
) {
439 if (fp
->f_buf
== NULL
)
440 fp
->f_buf
= malloc(ISO_DEFAULT_BLOCK_SIZE
);
443 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
444 cdb2devb(blkno
), ISO_DEFAULT_BLOCK_SIZE
,
448 if (read
!= ISO_DEFAULT_BLOCK_SIZE
)
451 fp
->f_buf_blkno
= blkno
;
454 *buf_p
= fp
->f_buf
+ blkoff
;
455 *size_p
= ISO_DEFAULT_BLOCK_SIZE
- blkoff
;
457 if (*size_p
> fp
->f_size
- fp
->f_off
)
458 *size_p
= fp
->f_size
- fp
->f_off
;
463 cd9660_read(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
465 struct file
*fp
= (struct file
*)f
->f_fsdata
;
467 size_t buf_size
, csize
;
472 if (fp
->f_off
< 0 || fp
->f_off
>= fp
->f_size
)
475 rc
= buf_read_file(f
, &buf
, &buf_size
);
479 csize
= size
> buf_size
? buf_size
: size
;
480 bcopy(buf
, addr
, csize
);
492 cd9660_readdir(struct open_file
*f
, struct dirent
*d
)
494 struct file
*fp
= (struct file
*)f
->f_fsdata
;
495 struct iso_directory_record
*ep
;
496 size_t buf_size
, reclen
, namelen
;
502 if (fp
->f_off
>= fp
->f_size
)
504 error
= buf_read_file(f
, &buf
, &buf_size
);
507 ep
= (struct iso_directory_record
*)buf
;
509 if (isonum_711(ep
->length
) == 0) {
512 /* skip to next block, if any */
513 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
;
514 fp
->f_off
= (blkno
+ 1) * ISO_DEFAULT_BLOCK_SIZE
;
518 if (fp
->f_flags
& F_RR
) {
519 if (fp
->f_flags
& F_ROOTDIR
&& fp
->f_off
== 0)
522 lenskip
= fp
->f_susp_skip
;
523 name
= rrip_lookup_name(f
, ep
, lenskip
, &namelen
);
527 namelen
= isonum_711(ep
->name_len
);
530 if (ep
->name
[0] == 0)
532 else if (ep
->name
[0] == 1) {
538 reclen
= sizeof(struct dirent
) - (MAXNAMLEN
+1) + namelen
+ 1;
539 reclen
= (reclen
+ 3) & ~3;
541 d
->d_fileno
= isonum_733(ep
->extent
);
542 d
->d_reclen
= reclen
;
543 if (isonum_711(ep
->flags
) & 2)
547 d
->d_namlen
= namelen
;
549 bcopy(name
, d
->d_name
, d
->d_namlen
);
550 d
->d_name
[d
->d_namlen
] = 0;
552 fp
->f_off
+= isonum_711(ep
->length
);
557 cd9660_seek(struct open_file
*f
, off_t offset
, int where
)
559 struct file
*fp
= (struct file
*)f
->f_fsdata
;
569 fp
->f_off
= fp
->f_size
- offset
;
578 cd9660_stat(struct open_file
*f
, struct stat
*sb
)
580 struct file
*fp
= (struct file
*)f
->f_fsdata
;
582 /* only important stuff */
583 sb
->st_mode
= S_IRUSR
| S_IRGRP
| S_IROTH
;
584 if (fp
->f_flags
& F_ISDIR
)
585 sb
->st_mode
|= S_IFDIR
;
587 sb
->st_mode
|= S_IFREG
;
588 sb
->st_uid
= sb
->st_gid
= 0;
589 sb
->st_size
= fp
->f_size
;