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 $
36 * Stand-alone ISO9660 file reading package.
38 * Note: This doesn't support Rock Ridge extensions, extended attributes,
39 * blocksizes other than 2048 bytes, multi-extent files, etc.
41 #include <sys/param.h>
43 #include <sys/dirent.h>
44 #include <isofs/cd9660/iso.h>
45 #include <isofs/cd9660/cd9660_rrip.h>
49 #define SUSP_CONTINUATION "CE"
50 #define SUSP_PRESENT "SP"
51 #define SUSP_STOP "ST"
52 #define SUSP_EXTREF "ER"
53 #define RRIP_NAME "NM"
57 u_char signature
[ISODCL ( 5, 6)];
58 u_char len_skp
[ISODCL ( 7, 7)]; /* 711 */
61 static int buf_read_file(struct open_file
*f
, char **buf_p
,
63 static int cd9660_open(const char *path
, struct open_file
*f
);
64 static int cd9660_close(struct open_file
*f
);
65 static int cd9660_read(struct open_file
*f
, void *buf
, size_t size
,
67 static int cd9660_write(struct open_file
*f
, void *buf
, size_t size
,
69 static off_t
cd9660_seek(struct open_file
*f
, off_t offset
, int where
);
70 static int cd9660_stat(struct open_file
*f
, struct stat
*sb
);
71 static int cd9660_readdir(struct open_file
*f
, struct dirent
*d
);
72 static int dirmatch(struct open_file
*f
, const char *path
,
73 struct iso_directory_record
*dp
, int use_rrip
, int lenskip
);
74 static int rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
,
76 static char *rrip_lookup_name(struct open_file
*f
,
77 struct iso_directory_record
*dp
, int lenskip
, size_t *len
);
78 static ISO_SUSP_HEADER
*susp_lookup_record(struct open_file
*f
,
79 const char *identifier
, struct iso_directory_record
*dp
,
82 struct fs_ops cd9660_fsops
= {
93 #define F_ISDIR 0x0001 /* Directory */
94 #define F_ROOTDIR 0x0002 /* Root directory */
95 #define F_RR 0x0004 /* Rock Ridge on this volume */
98 int f_flags
; /* file flags */
99 off_t f_off
; /* Current offset within file */
100 u_daddr_t f_bno
; /* Starting block number */
101 off_t f_size
; /* Size of file */
102 u_daddr_t f_buf_blkno
; /* block number of data block */
103 char *f_buf
; /* buffer for data block */
104 int f_susp_skip
; /* len_skip for SUSP records */
108 char namlen
[ISODCL( 1, 1)]; /* 711 */
109 char extlen
[ISODCL( 2, 2)]; /* 711 */
110 char block
[ISODCL( 3, 6)]; /* 732 */
111 char parent
[ISODCL( 7, 8)]; /* 722 */
115 #define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
117 #define cdb2devb(bno) ((bno) * (ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE))
119 static ISO_SUSP_HEADER
*
120 susp_lookup_record(struct open_file
*f
, const char *identifier
,
121 struct iso_directory_record
*dp
, int lenskip
)
123 static char susp_buffer
[ISO_DEFAULT_BLOCK_SIZE
];
130 p
= dp
->name
+ isonum_711(dp
->name_len
) + lenskip
;
131 /* Names of even length have a padding byte after the name. */
132 if ((isonum_711(dp
->name_len
) & 1) == 0)
134 end
= (char *)dp
+ isonum_711(dp
->length
);
135 while (p
+ 3 < end
) {
136 sh
= (ISO_SUSP_HEADER
*)p
;
137 if (bcmp(sh
->type
, identifier
, 2) == 0)
139 if (bcmp(sh
->type
, SUSP_STOP
, 2) == 0)
141 if (bcmp(sh
->type
, SUSP_CONTINUATION
, 2) == 0) {
142 shc
= (ISO_RRIP_CONT
*)sh
;
143 error
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
144 cdb2devb(isonum_733(shc
->location
)),
145 ISO_DEFAULT_BLOCK_SIZE
, susp_buffer
, &read
);
147 /* Bail if it fails. */
148 if (error
!= 0 || read
!= ISO_DEFAULT_BLOCK_SIZE
)
150 p
= susp_buffer
+ isonum_733(shc
->offset
);
151 end
= p
+ isonum_733(shc
->length
);
153 /* Ignore this record and skip to the next. */
154 p
+= isonum_711(sh
->length
);
160 rrip_lookup_name(struct open_file
*f
, struct iso_directory_record
*dp
,
161 int lenskip
, size_t *len
)
168 p
= (ISO_RRIP_ALTNAME
*)susp_lookup_record(f
, RRIP_NAME
, dp
, lenskip
);
172 case ISO_SUSP_CFLAG_CURRENT
:
175 case ISO_SUSP_CFLAG_PARENT
:
179 *len
= isonum_711(p
->h
.length
) - 5;
180 return ((char *)p
+ 5);
183 * We don't handle hostnames or continued names as they are
184 * too hard, so just bail and use the default name.
191 rrip_check(struct open_file
*f
, struct iso_directory_record
*dp
, int *lenskip
)
193 ISO_SUSP_PRESENT
*sp
;
197 /* First, see if we can find a SP field. */
198 p
= dp
->name
+ isonum_711(dp
->name_len
);
199 if (p
> (char *)dp
+ isonum_711(dp
->length
))
201 sp
= (ISO_SUSP_PRESENT
*)p
;
202 if (bcmp(sp
->h
.type
, SUSP_PRESENT
, 2) != 0)
204 if (isonum_711(sp
->h
.length
) != sizeof(ISO_SUSP_PRESENT
))
206 if (sp
->signature
[0] != 0xbe || sp
->signature
[1] != 0xef)
208 *lenskip
= isonum_711(sp
->len_skp
);
211 * Now look for an ER field. If RRIP is present, then there must
212 * be at least one of these. It would be more pedantic to walk
213 * through the list of fields looking for a Rock Ridge ER field.
215 er
= (ISO_RRIP_EXTREF
*)susp_lookup_record(f
, SUSP_EXTREF
, dp
, 0);
222 dirmatch(struct open_file
*f
, const char *path
, struct iso_directory_record
*dp
,
223 int use_rrip
, int lenskip
)
230 cp
= rrip_lookup_name(f
, dp
, lenskip
, &len
);
234 len
= isonum_711(dp
->name_len
);
239 for (i
= len
; --i
>= 0; path
++, cp
++) {
240 if (!*path
|| *path
== '/')
244 if (!icase
&& toupper(*path
) == *cp
)
248 if (*path
&& *path
!= '/')
252 * Allow stripping of trailing dots and the version number.
254 * Note that this will find the first instead of the last version
255 * of a file unless explicitly specified.
257 * For any inexact matches we ignore everything after and including
258 * the semicolon in the directory entry name, assuming it is a
259 * version number whether it is or not.
263 return 1; /* ignore version */
264 if (cp
[0] == '.' && i
== 0)
265 return 1; /* ignore trailing dot */
266 if (cp
[0] == '.' && i
> 0 && cp
[1] == ';')
267 return 1; /* ignore trailing dot w/ version */
268 return 0; /* else mismatch */
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 off
+= isonum_711(dp
->length
);
364 while (*path
&& *path
!= '/') /* look for next component */
366 if (*path
== '/') { /* skip /, make sure is dir */
368 if (*path
&& (isonum_711(dp
->flags
) & 2) == 0) {
369 rc
= ENOENT
; /* not directory */
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
);
421 cd9660_close(struct open_file
*f
)
423 struct file
*fp
= (struct file
*)f
->f_fsdata
;
433 buf_read_file(struct open_file
*f
, char **buf_p
, size_t *size_p
)
435 struct file
*fp
= (struct file
*)f
->f_fsdata
;
436 u_daddr_t blkno
, blkoff
;
440 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
+ fp
->f_bno
;
441 blkoff
= fp
->f_off
% ISO_DEFAULT_BLOCK_SIZE
;
443 if (blkno
!= fp
->f_buf_blkno
) {
444 if (fp
->f_buf
== NULL
)
445 fp
->f_buf
= malloc(ISO_DEFAULT_BLOCK_SIZE
);
448 rc
= f
->f_dev
->dv_strategy(f
->f_devdata
, F_READ
,
449 cdb2devb(blkno
), ISO_DEFAULT_BLOCK_SIZE
, fp
->f_buf
, &read
);
452 if (read
!= ISO_DEFAULT_BLOCK_SIZE
)
455 fp
->f_buf_blkno
= blkno
;
458 *buf_p
= fp
->f_buf
+ blkoff
;
459 *size_p
= ISO_DEFAULT_BLOCK_SIZE
- blkoff
;
461 if (*size_p
> fp
->f_size
- fp
->f_off
)
462 *size_p
= fp
->f_size
- fp
->f_off
;
467 cd9660_read(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
469 struct file
*fp
= (struct file
*)f
->f_fsdata
;
471 size_t buf_size
, csize
;
476 if (fp
->f_off
< 0 || fp
->f_off
>= fp
->f_size
)
479 rc
= buf_read_file(f
, &buf
, &buf_size
);
483 csize
= size
> buf_size
? buf_size
: size
;
484 bcopy(buf
, addr
, csize
);
496 cd9660_readdir(struct open_file
*f
, struct dirent
*d
)
498 struct file
*fp
= (struct file
*)f
->f_fsdata
;
499 struct iso_directory_record
*ep
;
500 size_t buf_size
, namelen
;
506 if (fp
->f_off
>= fp
->f_size
)
508 error
= buf_read_file(f
, &buf
, &buf_size
);
511 ep
= (struct iso_directory_record
*)buf
;
513 if (isonum_711(ep
->length
) == 0) {
516 /* skip to next block, if any */
517 blkno
= fp
->f_off
/ ISO_DEFAULT_BLOCK_SIZE
;
518 fp
->f_off
= (blkno
+ 1) * ISO_DEFAULT_BLOCK_SIZE
;
522 if (fp
->f_flags
& F_RR
) {
523 if (fp
->f_flags
& F_ROOTDIR
&& fp
->f_off
== 0)
526 lenskip
= fp
->f_susp_skip
;
527 name
= rrip_lookup_name(f
, ep
, lenskip
, &namelen
);
531 namelen
= isonum_711(ep
->name_len
);
534 if (ep
->name
[0] == 0)
536 else if (ep
->name
[0] == 1) {
543 d
->d_ino
= isonum_733(ep
->extent
);
544 if (isonum_711(ep
->flags
) & 2)
548 d
->d_namlen
= namelen
;
550 bcopy(name
, d
->d_name
, d
->d_namlen
);
551 d
->d_name
[d
->d_namlen
] = 0;
553 fp
->f_off
+= isonum_711(ep
->length
);
558 cd9660_write(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
564 cd9660_seek(struct open_file
*f
, off_t offset
, int where
)
566 struct file
*fp
= (struct file
*)f
->f_fsdata
;
576 fp
->f_off
= fp
->f_size
- offset
;
585 cd9660_stat(struct open_file
*f
, struct stat
*sb
)
587 struct file
*fp
= (struct file
*)f
->f_fsdata
;
589 /* only important stuff */
590 sb
->st_mode
= S_IRUSR
| S_IRGRP
| S_IROTH
;
591 if (fp
->f_flags
& F_ISDIR
)
592 sb
->st_mode
|= S_IFDIR
;
594 sb
->st_mode
|= S_IFREG
;
595 sb
->st_uid
= sb
->st_gid
= 0;
596 sb
->st_size
= fp
->f_size
;