2 * Copyright (C) 1996 Wolfgang Solfrank.
3 * Copyright (C) 1996 TooLs GmbH.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by TooLs GmbH.
17 * 4. The name of TooLs GmbH may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $
32 * $FreeBSD: src/lib/libstand/cd9660.c,v 1.4.2.4 2001/12/21 22:17:44 jhb Exp $
33 * $DragonFly: src/lib/libstand/cd9660.c,v 1.6 2005/12/11 02:27:26 swildner Exp $
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 int cd9660_write(struct open_file
*f
, void *buf
, size_t size
,
70 static off_t
cd9660_seek(struct open_file
*f
, off_t offset
, int where
);
71 static int cd9660_stat(struct open_file
*f
, struct stat
*sb
);
72 static int cd9660_readdir(struct open_file
*f
, struct dirent
*d
);
73 static int dirmatch(struct open_file
*f
, const char *path
,
74 struct iso_directory_record
*dp
, int use_rrip
, int lenskip
);
75 static int rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
,
77 static char *rrip_lookup_name(struct open_file
*f
,
78 struct iso_directory_record
*dp
, int lenskip
, size_t *len
);
79 static ISO_SUSP_HEADER
*susp_lookup_record(struct open_file
*f
,
80 const char *identifier
, struct iso_directory_record
*dp
,
83 struct fs_ops cd9660_fsops
= {
94 #define F_ISDIR 0x0001 /* Directory */
95 #define F_ROOTDIR 0x0002 /* Root directory */
96 #define F_RR 0x0004 /* Rock Ridge on this volume */
99 int f_flags
; /* file flags */
100 off_t f_off
; /* Current offset within file */
101 daddr_t f_bno
; /* Starting block number */
102 off_t f_size
; /* Size of file */
103 daddr_t f_buf_blkno
; /* block number of data block */
104 char *f_buf
; /* buffer for data block */
105 int f_susp_skip
; /* len_skip for SUSP records */
109 char namlen
[ISODCL( 1, 1)]; /* 711 */
110 char extlen
[ISODCL( 2, 2)]; /* 711 */
111 char block
[ISODCL( 3, 6)]; /* 732 */
112 char parent
[ISODCL( 7, 8)]; /* 722 */
116 #define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
118 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
120 /* XXX these should be in the system headers */
122 isonum_722(u_char
*p
)
124 return (*p
<< 8)|p
[1];
128 isonum_732(u_char
*p
)
130 return (*p
<< 24)|(p
[1] << 16)|(p
[2] << 8)|p
[3];
133 static ISO_SUSP_HEADER
*
134 susp_lookup_record(struct open_file
*f
, const char *identifier
,
135 struct iso_directory_record
*dp
, int lenskip
)
137 static char susp_buffer
[ISO_DEFAULT_BLOCK_SIZE
];
144 p
= dp
->name
+ isonum_711(dp
->name_len
) + lenskip
;
145 /* Names of even length have a padding byte after the name. */
146 if ((isonum_711(dp
->name_len
) & 1) == 0)
148 end
= (char *)dp
+ isonum_711(dp
->length
);
149 while (p
+ 3 < end
) {
150 sh
= (ISO_SUSP_HEADER
*)p
;
151 if (bcmp(sh
->type
, identifier
, 2) == 0)
153 if (bcmp(sh
->type
, SUSP_STOP
, 2) == 0)
155 if (bcmp(sh
->type
, SUSP_CONTINUATION
, 2) == 0) {
156 shc
= (ISO_RRIP_CONT
*)sh
;
157 error
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
158 cdb2devb(isonum_733(shc
->location
)),
159 ISO_DEFAULT_BLOCK_SIZE
, susp_buffer
, &read
);
161 /* Bail if it fails. */
162 if (error
!= 0 || read
!= ISO_DEFAULT_BLOCK_SIZE
)
164 p
= susp_buffer
+ isonum_733(shc
->offset
);
165 end
= p
+ isonum_733(shc
->length
);
167 /* Ignore this record and skip to the next. */
168 p
+= isonum_711(sh
->length
);
174 rrip_lookup_name(struct open_file
*f
, struct iso_directory_record
*dp
,
175 int lenskip
, size_t *len
)
182 p
= (ISO_RRIP_ALTNAME
*)susp_lookup_record(f
, RRIP_NAME
, dp
, lenskip
);
186 case ISO_SUSP_CFLAG_CURRENT
:
189 case ISO_SUSP_CFLAG_PARENT
:
193 *len
= isonum_711(p
->h
.length
) - 5;
194 return ((char *)p
+ 5);
197 * We don't handle hostnames or continued names as they are
198 * too hard, so just bail and use the default name.
205 rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
, int *lenskip
)
207 ISO_SUSP_PRESENT
*sp
;
211 /* First, see if we can find a SP field. */
212 p
= dp
->name
+ isonum_711(dp
->name_len
);
213 if (p
> (char *)dp
+ isonum_711(dp
->length
))
215 sp
= (ISO_SUSP_PRESENT
*)p
;
216 if (bcmp(sp
->h
.type
, SUSP_PRESENT
, 2) != 0)
218 if (isonum_711(sp
->h
.length
) != sizeof(ISO_SUSP_PRESENT
))
220 if (sp
->signature
[0] != 0xbe || sp
->signature
[1] != 0xef)
222 *lenskip
= isonum_711(sp
->len_skp
);
225 * Now look for an ER field. If RRIP is present, then there must
226 * be at least one of these. It would be more pedantic to walk
227 * through the list of fields looking for a Rock Ridge ER field.
229 er
= (ISO_RRIP_EXTREF
*)susp_lookup_record(f
, SUSP_EXTREF
, dp
, 0);
236 dirmatch(struct open_file
*f
, const char *path
, struct iso_directory_record
*dp
,
237 int use_rrip
, int lenskip
)
244 cp
= rrip_lookup_name(f
, dp
, lenskip
, &len
);
248 len
= isonum_711(dp
->name_len
);
253 for (i
= len
; --i
>= 0; path
++, cp
++) {
254 if (!*path
|| *path
== '/')
258 if (!icase
&& toupper(*path
) == *cp
)
262 if (*path
&& *path
!= '/')
265 * Allow stripping of trailing dots and the version number.
266 * Note that this will find the first instead of the last version
269 if (i
>= 0 && (*cp
== ';' || *cp
== '.')) {
270 /* This is to prevent matching of numeric extensions */
271 if (*cp
== '.' && cp
[1] != ';')
274 if (*++cp
!= ';' && (*cp
< '0' || *cp
> '9'))
281 cd9660_open(const char *path
, struct open_file
*f
)
285 struct iso_primary_descriptor
*vd
;
286 size_t buf_size
, read
, dsize
, off
;
288 struct iso_directory_record rec
;
289 struct iso_directory_record
*dp
= 0;
290 int rc
, first
, use_rrip
, lenskip
;
292 /* First find the volume descriptor */
293 buf
= malloc(buf_size
= ISO_DEFAULT_BLOCK_SIZE
);
295 for (bno
= 16;; bno
++) {
297 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
, cdb2devb(bno
),
298 ISO_DEFAULT_BLOCK_SIZE
, buf
, &read
);
301 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
306 if (bcmp(vd
->id
, ISO_STANDARD_ID
, sizeof vd
->id
) != 0)
308 if (isonum_711(vd
->type
) == ISO_VD_END
)
310 if (isonum_711(vd
->type
) == ISO_VD_PRIMARY
)
313 if (isonum_723(vd
->logical_block_size
) != ISO_DEFAULT_BLOCK_SIZE
)
316 rec
= *(struct iso_directory_record
*) vd
->root_directory_record
;
317 if (*path
== '/') path
++; /* eat leading '/' */
322 bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
323 dsize
= isonum_733(rec
.size
);
327 while (off
< dsize
) {
328 if ((off
% ISO_DEFAULT_BLOCK_SIZE
) == 0) {
330 rc
= f
->f_dev
->dv_strategy
331 (f
->f_devdata
, F_READ
,
332 cdb2devb(bno
+ boff
),
333 ISO_DEFAULT_BLOCK_SIZE
,
337 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
342 dp
= (struct iso_directory_record
*) buf
;
344 if (isonum_711(dp
->length
) == 0) {
345 /* skip to next block, if any */
346 off
= boff
* ISO_DEFAULT_BLOCK_SIZE
;
350 /* See if RRIP is in use. */
352 use_rrip
= rrip_check(f
, dp
, &lenskip
);
354 if (dirmatch(f
, path
, dp
, use_rrip
,
355 first
? 0 : lenskip
)) {
361 dp
= (struct iso_directory_record
*)
362 ((char *) dp
+ isonum_711(dp
->length
));
363 off
+= isonum_711(dp
->length
);
371 while (*path
&& *path
!= '/') /* look for next component */
373 if (*path
== '/') { /* skip /, make sure is dir */
375 if (*path
&& (isonum_711(dp
->flags
) & 2) == 0) {
376 rc
= ENOENT
; /* not directory */
382 /* allocate file system specific data structure */
383 fp
= malloc(sizeof(struct file
));
384 bzero(fp
, sizeof(struct file
));
385 f
->f_fsdata
= (void *)fp
;
387 if ((isonum_711(rec
.flags
) & 2) != 0) {
388 fp
->f_flags
= F_ISDIR
;
391 fp
->f_flags
|= F_ROOTDIR
;
393 /* Check for Rock Ridge since we didn't in the loop above. */
394 bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
396 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
, cdb2devb(bno
),
397 ISO_DEFAULT_BLOCK_SIZE
, buf
, &read
);
400 if (read
!= ISO_DEFAULT_BLOCK_SIZE
) {
404 dp
= (struct iso_directory_record
*)buf
;
405 use_rrip
= rrip_check(f
, dp
, &lenskip
);
409 fp
->f_susp_skip
= lenskip
;
412 fp
->f_bno
= isonum_733(rec
.extent
) + isonum_711(rec
.ext_attr_length
);
413 fp
->f_size
= isonum_733(rec
.size
);
427 cd9660_close(struct open_file
*f
)
429 struct file
*fp
= (struct file
*)f
->f_fsdata
;
438 buf_read_file(struct open_file
*f
, char **buf_p
, size_t *size_p
)
440 struct file
*fp
= (struct file
*)f
->f_fsdata
;
441 daddr_t blkno
, blkoff
;
445 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
+ fp
->f_bno
;
446 blkoff
= fp
->f_off
% ISO_DEFAULT_BLOCK_SIZE
;
448 if (blkno
!= fp
->f_buf_blkno
) {
449 if (fp
->f_buf
== (char *)0)
450 fp
->f_buf
= malloc(ISO_DEFAULT_BLOCK_SIZE
);
453 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
454 cdb2devb(blkno
), ISO_DEFAULT_BLOCK_SIZE
, fp
->f_buf
, &read
);
457 if (read
!= ISO_DEFAULT_BLOCK_SIZE
)
460 fp
->f_buf_blkno
= blkno
;
463 *buf_p
= fp
->f_buf
+ blkoff
;
464 *size_p
= ISO_DEFAULT_BLOCK_SIZE
- blkoff
;
466 if (*size_p
> fp
->f_size
- fp
->f_off
)
467 *size_p
= fp
->f_size
- fp
->f_off
;
472 cd9660_read(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
474 struct file
*fp
= (struct file
*)f
->f_fsdata
;
476 size_t buf_size
, csize
;
481 if (fp
->f_off
< 0 || fp
->f_off
>= fp
->f_size
)
484 rc
= buf_read_file(f
, &buf
, &buf_size
);
488 csize
= size
> buf_size
? buf_size
: size
;
489 bcopy(buf
, addr
, csize
);
501 cd9660_readdir(struct open_file
*f
, struct dirent
*d
)
503 struct file
*fp
= (struct file
*)f
->f_fsdata
;
504 struct iso_directory_record
*ep
;
505 size_t buf_size
, reclen
, namelen
;
511 if (fp
->f_off
>= fp
->f_size
)
513 error
= buf_read_file(f
, &buf
, &buf_size
);
516 ep
= (struct iso_directory_record
*)buf
;
518 if (isonum_711(ep
->length
) == 0) {
521 /* skip to next block, if any */
522 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
;
523 fp
->f_off
= (blkno
+ 1) * ISO_DEFAULT_BLOCK_SIZE
;
527 if (fp
->f_flags
& F_RR
) {
528 if (fp
->f_flags
& F_ROOTDIR
&& fp
->f_off
== 0)
531 lenskip
= fp
->f_susp_skip
;
532 name
= rrip_lookup_name(f
, ep
, lenskip
, &namelen
);
536 namelen
= isonum_711(ep
->name_len
);
539 if (ep
->name
[0] == 0)
541 else if (ep
->name
[0] == 1) {
547 reclen
= _DIRENT_RECLEN(namelen
);
549 d
->d_ino
= isonum_733(ep
->extent
);
550 if (isonum_711(ep
->flags
) & 2)
554 d
->d_namlen
= namelen
;
556 bcopy(name
, d
->d_name
, d
->d_namlen
);
557 d
->d_name
[d
->d_namlen
] = 0;
559 fp
->f_off
+= isonum_711(ep
->length
);
564 cd9660_write(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
570 cd9660_seek(struct open_file
*f
, off_t offset
, int where
)
572 struct file
*fp
= (struct file
*)f
->f_fsdata
;
582 fp
->f_off
= fp
->f_size
- offset
;
591 cd9660_stat(struct open_file
*f
, struct stat
*sb
)
593 struct file
*fp
= (struct file
*)f
->f_fsdata
;
595 /* only important stuff */
596 sb
->st_mode
= S_IRUSR
| S_IRGRP
| S_IROTH
;
597 if (fp
->f_flags
& F_ISDIR
)
598 sb
->st_mode
|= S_IFDIR
;
600 sb
->st_mode
|= S_IFREG
;
601 sb
->st_uid
= sb
->st_gid
= 0;
602 sb
->st_size
= fp
->f_size
;