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>
35 __FBSDID("$FreeBSD$");
38 * Stand-alone ISO9660 file reading package.
40 * Note: This doesn't support Rock Ridge extensions, extended attributes,
41 * blocksizes other than 2048 bytes, multi-extent files, etc.
43 #include <sys/param.h>
45 #include <sys/dirent.h>
46 #include <isofs/cd9660/iso.h>
47 #include <isofs/cd9660/cd9660_rrip.h>
51 #define SUSP_CONTINUATION "CE"
52 #define SUSP_PRESENT "SP"
53 #define SUSP_STOP "ST"
54 #define SUSP_EXTREF "ER"
55 #define RRIP_NAME "NM"
59 u_char signature
[ISODCL ( 5, 6)];
60 u_char len_skp
[ISODCL ( 7, 7)]; /* 711 */
63 static int buf_read_file(struct open_file
*f
, char **buf_p
,
65 static int cd9660_open(const char *path
, struct open_file
*f
);
66 static int cd9660_close(struct open_file
*f
);
67 static int cd9660_read(struct open_file
*f
, void *buf
, size_t size
,
69 static int cd9660_write(struct open_file
*f
, void *buf
, size_t size
,
71 static off_t
cd9660_seek(struct open_file
*f
, off_t offset
, int where
);
72 static int cd9660_stat(struct open_file
*f
, struct stat
*sb
);
73 static int cd9660_readdir(struct open_file
*f
, struct dirent
*d
);
74 static int dirmatch(struct open_file
*f
, const char *path
,
75 struct iso_directory_record
*dp
, int use_rrip
, int lenskip
);
76 static int rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
,
78 static char *rrip_lookup_name(struct open_file
*f
,
79 struct iso_directory_record
*dp
, int lenskip
, size_t *len
);
80 static ISO_SUSP_HEADER
*susp_lookup_record(struct open_file
*f
,
81 const char *identifier
, struct iso_directory_record
*dp
,
84 struct fs_ops cd9660_fsops
= {
95 #define F_ISDIR 0x0001 /* Directory */
96 #define F_ROOTDIR 0x0002 /* Root directory */
97 #define F_RR 0x0004 /* Rock Ridge on this volume */
100 int f_flags
; /* file flags */
101 off_t f_off
; /* Current offset within file */
102 daddr_t f_bno
; /* Starting block number */
103 off_t f_size
; /* Size of file */
104 daddr_t f_buf_blkno
; /* block number of data block */
105 char *f_buf
; /* buffer for data block */
106 int f_susp_skip
; /* len_skip for SUSP records */
110 char namlen
[ISODCL( 1, 1)]; /* 711 */
111 char extlen
[ISODCL( 2, 2)]; /* 711 */
112 char block
[ISODCL( 3, 6)]; /* 732 */
113 char parent
[ISODCL( 7, 8)]; /* 722 */
117 #define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
119 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
121 static ISO_SUSP_HEADER
*
122 susp_lookup_record(struct open_file
*f
, const char *identifier
,
123 struct iso_directory_record
*dp
, int lenskip
)
125 static char susp_buffer
[ISO_DEFAULT_BLOCK_SIZE
];
132 p
= dp
->name
+ isonum_711(dp
->name_len
) + lenskip
;
133 /* Names of even length have a padding byte after the name. */
134 if ((isonum_711(dp
->name_len
) & 1) == 0)
136 end
= (char *)dp
+ isonum_711(dp
->length
);
137 while (p
+ 3 < end
) {
138 sh
= (ISO_SUSP_HEADER
*)p
;
139 if (bcmp(sh
->type
, identifier
, 2) == 0)
141 if (bcmp(sh
->type
, SUSP_STOP
, 2) == 0)
143 if (bcmp(sh
->type
, SUSP_CONTINUATION
, 2) == 0) {
144 shc
= (ISO_RRIP_CONT
*)sh
;
145 error
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
146 cdb2devb(isonum_733(shc
->location
)),
147 ISO_DEFAULT_BLOCK_SIZE
, susp_buffer
, &read
);
149 /* Bail if it fails. */
150 if (error
!= 0 || read
!= ISO_DEFAULT_BLOCK_SIZE
)
152 p
= susp_buffer
+ isonum_733(shc
->offset
);
153 end
= p
+ isonum_733(shc
->length
);
155 /* Ignore this record and skip to the next. */
156 p
+= isonum_711(sh
->length
);
158 /* Avoid infinite loops with corrupted file systems */
159 if (isonum_711(sh
->length
) == 0)
167 rrip_lookup_name(struct open_file
*f
, struct iso_directory_record
*dp
,
168 int lenskip
, size_t *len
)
175 p
= (ISO_RRIP_ALTNAME
*)susp_lookup_record(f
, RRIP_NAME
, dp
, lenskip
);
179 case ISO_SUSP_CFLAG_CURRENT
:
182 case ISO_SUSP_CFLAG_PARENT
:
186 *len
= isonum_711(p
->h
.length
) - 5;
187 return ((char *)p
+ 5);
190 * We don't handle hostnames or continued names as they are
191 * too hard, so just bail and use the default name.
198 rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
, int *lenskip
)
200 ISO_SUSP_PRESENT
*sp
;
204 /* First, see if we can find a SP field. */
205 p
= dp
->name
+ isonum_711(dp
->name_len
);
206 if (p
> (char *)dp
+ isonum_711(dp
->length
))
208 sp
= (ISO_SUSP_PRESENT
*)p
;
209 if (bcmp(sp
->h
.type
, SUSP_PRESENT
, 2) != 0)
211 if (isonum_711(sp
->h
.length
) != sizeof(ISO_SUSP_PRESENT
))
213 if (sp
->signature
[0] != 0xbe || sp
->signature
[1] != 0xef)
215 *lenskip
= isonum_711(sp
->len_skp
);
218 * Now look for an ER field. If RRIP is present, then there must
219 * be at least one of these. It would be more pedantic to walk
220 * through the list of fields looking for a Rock Ridge ER field.
222 er
= (ISO_RRIP_EXTREF
*)susp_lookup_record(f
, SUSP_EXTREF
, dp
, 0);
229 dirmatch(struct open_file
*f
, const char *path
, struct iso_directory_record
*dp
,
230 int use_rrip
, int lenskip
)
237 cp
= rrip_lookup_name(f
, dp
, lenskip
, &len
);
241 len
= isonum_711(dp
->name_len
);
246 for (i
= len
; --i
>= 0; path
++, cp
++) {
247 if (!*path
|| *path
== '/')
251 if (!icase
&& toupper(*path
) == *cp
)
255 if (*path
&& *path
!= '/')
258 * Allow stripping of trailing dots and the version number.
259 * Note that this will find the first instead of the last version
262 if (i
>= 0 && (*cp
== ';' || *cp
== '.')) {
263 /* This is to prevent matching of numeric extensions */
264 if (*cp
== '.' && cp
[1] != ';')
267 if (*++cp
!= ';' && (*cp
< '0' || *cp
> '9'))
274 cd9660_open(const char *path
, struct open_file
*f
)
276 struct file
*fp
= NULL
;
278 struct iso_primary_descriptor
*vd
;
279 size_t buf_size
, read
, dsize
, off
;
281 struct iso_directory_record rec
;
282 struct iso_directory_record
*dp
= NULL
;
283 int rc
, first
, use_rrip
, lenskip
;
285 /* First find the volume descriptor */
286 buf
= malloc(buf_size
= ISO_DEFAULT_BLOCK_SIZE
);
288 for (bno
= 16;; bno
++) {
290 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
, cdb2devb(bno
),
291 ISO_DEFAULT_BLOCK_SIZE
, buf
, &read
);
294 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
299 if (bcmp(vd
->id
, ISO_STANDARD_ID
, sizeof vd
->id
) != 0)
301 if (isonum_711(vd
->type
) == ISO_VD_END
)
303 if (isonum_711(vd
->type
) == ISO_VD_PRIMARY
)
306 if (isonum_723(vd
->logical_block_size
) != ISO_DEFAULT_BLOCK_SIZE
)
309 rec
= *(struct iso_directory_record
*) vd
->root_directory_record
;
310 if (*path
== '/') path
++; /* eat leading '/' */
315 bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
316 dsize
= isonum_733(rec
.size
);
320 while (off
< dsize
) {
321 if ((off
% ISO_DEFAULT_BLOCK_SIZE
) == 0) {
323 rc
= f
->f_dev
->dv_strategy
324 (f
->f_devdata
, F_READ
,
325 cdb2devb(bno
+ boff
),
326 ISO_DEFAULT_BLOCK_SIZE
,
330 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
335 dp
= (struct iso_directory_record
*) buf
;
337 if (isonum_711(dp
->length
) == 0) {
338 /* skip to next block, if any */
339 off
= boff
* ISO_DEFAULT_BLOCK_SIZE
;
343 /* See if RRIP is in use. */
345 use_rrip
= rrip_check(f
, dp
, &lenskip
);
347 if (dirmatch(f
, path
, dp
, use_rrip
,
348 first
? 0 : lenskip
)) {
354 dp
= (struct iso_directory_record
*)
355 ((char *) dp
+ isonum_711(dp
->length
));
356 /* If the new block has zero length, it is padding. */
357 if (isonum_711(dp
->length
) == 0) {
358 /* Skip to next block, if any. */
359 off
= boff
* ISO_DEFAULT_BLOCK_SIZE
;
362 off
+= isonum_711(dp
->length
);
370 while (*path
&& *path
!= '/') /* look for next component */
372 if (*path
) path
++; /* skip '/' */
375 /* allocate file system specific data structure */
376 fp
= malloc(sizeof(struct file
));
377 bzero(fp
, sizeof(struct file
));
378 f
->f_fsdata
= (void *)fp
;
380 if ((isonum_711(rec
.flags
) & 2) != 0) {
381 fp
->f_flags
= F_ISDIR
;
384 fp
->f_flags
|= F_ROOTDIR
;
386 /* Check for Rock Ridge since we didn't in the loop above. */
387 bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
389 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
, cdb2devb(bno
),
390 ISO_DEFAULT_BLOCK_SIZE
, buf
, &read
);
393 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
397 dp
= (struct iso_directory_record
*)buf
;
398 use_rrip
= rrip_check(f
, dp
, &lenskip
);
402 fp
->f_susp_skip
= lenskip
;
405 fp
->f_bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
406 fp
->f_size
= isonum_733(rec
.size
);
420 cd9660_close(struct open_file
*f
)
422 struct file
*fp
= (struct file
*)f
->f_fsdata
;
431 buf_read_file(struct open_file
*f
, char **buf_p
, size_t *size_p
)
433 struct file
*fp
= (struct file
*)f
->f_fsdata
;
434 daddr_t blkno
, blkoff
;
438 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
+ fp
->f_bno
;
439 blkoff
= fp
->f_off
% ISO_DEFAULT_BLOCK_SIZE
;
441 if (blkno
!= fp
->f_buf_blkno
) {
442 if (fp
->f_buf
== NULL
)
443 fp
->f_buf
= malloc(ISO_DEFAULT_BLOCK_SIZE
);
446 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
447 cdb2devb(blkno
), ISO_DEFAULT_BLOCK_SIZE
,
451 if (read
!= ISO_DEFAULT_BLOCK_SIZE
)
454 fp
->f_buf_blkno
= blkno
;
457 *buf_p
= fp
->f_buf
+ blkoff
;
458 *size_p
= ISO_DEFAULT_BLOCK_SIZE
- blkoff
;
460 if (*size_p
> fp
->f_size
- fp
->f_off
)
461 *size_p
= fp
->f_size
- fp
->f_off
;
466 cd9660_read(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
468 struct file
*fp
= (struct file
*)f
->f_fsdata
;
470 size_t buf_size
, csize
;
475 if (fp
->f_off
< 0 || fp
->f_off
>= fp
->f_size
)
478 rc
= buf_read_file(f
, &buf
, &buf_size
);
482 csize
= size
> buf_size
? buf_size
: size
;
483 bcopy(buf
, addr
, csize
);
495 cd9660_readdir(struct open_file
*f
, struct dirent
*d
)
497 struct file
*fp
= (struct file
*)f
->f_fsdata
;
498 struct iso_directory_record
*ep
;
499 size_t buf_size
, reclen
, namelen
;
505 if (fp
->f_off
>= fp
->f_size
)
507 error
= buf_read_file(f
, &buf
, &buf_size
);
510 ep
= (struct iso_directory_record
*)buf
;
512 if (isonum_711(ep
->length
) == 0) {
515 /* skip to next block, if any */
516 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
;
517 fp
->f_off
= (blkno
+ 1) * ISO_DEFAULT_BLOCK_SIZE
;
521 if (fp
->f_flags
& F_RR
) {
522 if (fp
->f_flags
& F_ROOTDIR
&& fp
->f_off
== 0)
525 lenskip
= fp
->f_susp_skip
;
526 name
= rrip_lookup_name(f
, ep
, lenskip
, &namelen
);
530 namelen
= isonum_711(ep
->name_len
);
533 if (ep
->name
[0] == 0)
535 else if (ep
->name
[0] == 1) {
541 reclen
= sizeof(struct dirent
) - (MAXNAMLEN
+1) + namelen
+ 1;
542 reclen
= (reclen
+ 3) & ~3;
544 d
->d_fileno
= isonum_733(ep
->extent
);
545 d
->d_reclen
= reclen
;
546 if (isonum_711(ep
->flags
) & 2)
550 d
->d_namlen
= namelen
;
552 bcopy(name
, d
->d_name
, d
->d_namlen
);
553 d
->d_name
[d
->d_namlen
] = 0;
555 fp
->f_off
+= isonum_711(ep
->length
);
560 cd9660_write(struct open_file
*f __unused
, void *start __unused
, size_t size __unused
, size_t *resid __unused
)
566 cd9660_seek(struct open_file
*f
, off_t offset
, int where
)
568 struct file
*fp
= (struct file
*)f
->f_fsdata
;
578 fp
->f_off
= fp
->f_size
- offset
;
587 cd9660_stat(struct open_file
*f
, struct stat
*sb
)
589 struct file
*fp
= (struct file
*)f
->f_fsdata
;
591 /* only important stuff */
592 sb
->st_mode
= S_IRUSR
| S_IRGRP
| S_IROTH
;
593 if (fp
->f_flags
& F_ISDIR
)
594 sb
->st_mode
|= S_IFDIR
;
596 sb
->st_mode
|= S_IFREG
;
597 sb
->st_uid
= sb
->st_gid
= 0;
598 sb
->st_size
= fp
->f_size
;