2 * Copyright (c) 2003-2004 Tim Kientzle
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
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.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.8 2005/04/06 04:19:30 kientzle Exp $");
33 /* #include <stdint.h> */ /* See archive_platform.h */
41 #include "archive_entry.h"
42 #include "archive_private.h"
43 #include "archive_string.h"
46 * An overview of ISO 9660 format:
48 * Each disk is laid out as follows:
49 * * 32k reserved for private use
50 * * Volume descriptor table. Each volume descriptor
51 * is 2k and specifies basic format information.
52 * The "Primary Volume Descriptor" (PVD) is defined by the
53 * standard and should always be present; other volume
54 * descriptors include various vendor-specific extensions.
55 * * Files and directories. Each file/dir is specified by
56 * an "extent" (starting sector and length in bytes).
57 * Dirs are just files with directory records packed one
58 * after another. The PVD contains a single dir entry
59 * specifying the location of the root directory. Everything
60 * else follows from there.
62 * This module works by first reading the volume descriptors, then
63 * building a list of directory entries, sorted by starting
64 * sector. At each step, I look for the earliest dir entry that
65 * hasn't yet been read, seek forward to that location and read
66 * that entry. If it's a dir, I slurp in the new dir entries and
67 * add them to the heap; if it's a regular file, I return the
68 * corresponding archive_entry and wait for the client to request
69 * the file body. This strategy allows us to read most compliant
70 * CDs with a single pass through the data, as required by libarchive.
73 /* Structure of on-disk PVD. */
74 struct iso9660_primary_volume_descriptor
{
75 unsigned char type
[1];
77 unsigned char version
[1];
82 char volume_space_size
[8];
84 char volume_set_size
[4];
85 char volume_sequence_number
[4];
86 char logical_block_size
[4];
87 char path_table_size
[8];
88 char type_1_path_table
[4];
89 char opt_type_1_path_table
[4];
90 char type_m_path_table
[4];
91 char opt_type_m_path_table
[4];
92 char root_directory_record
[34];
93 char volume_set_id
[128];
94 char publisher_id
[128];
95 char preparer_id
[128];
96 char application_id
[128];
97 char copyright_file_id
[37];
98 char abstract_file_id
[37];
99 char bibliographic_file_id
[37];
100 char creation_date
[17];
101 char modification_date
[17];
102 char expiration_date
[17];
103 char effective_date
[17];
104 char file_structure_version
[1];
106 char application_data
[512];
109 /* Structure of an on-disk directory record. */
110 struct iso9660_directory_record
{
111 unsigned char length
[1];
112 unsigned char ext_attr_length
[1];
113 unsigned char extent
[8];
114 unsigned char size
[8];
116 unsigned char flags
[1];
117 unsigned char file_unit_size
[1];
118 unsigned char interleave
[1];
119 unsigned char volume_sequence_number
[4];
120 unsigned char name_len
[1];
129 /* In-memory storage for a directory record. */
131 struct file_info
*parent
;
133 uint64_t offset
; /* Offset on disk. */
134 uint64_t size
; /* File size in bytes. */
135 uint64_t ce_offset
; /* Offset of CE */
136 uint64_t ce_size
; /* Size of CE */
137 time_t mtime
; /* File last modified time. */
138 time_t atime
; /* File last accessed time. */
139 time_t ctime
; /* File creation time. */
145 char *name
; /* Null-terminated filename. */
146 struct archive_string symlink
;
152 #define ISO9660_MAGIC 0x96609660
153 int bid
; /* If non-zero, return this as our bid. */
154 struct archive_string pathname
;
155 char seenRockridge
; /* Set true if RR extensions are used. */
156 unsigned char suspOffset
;
158 uint64_t previous_offset
;
159 uint64_t previous_size
;
160 struct archive_string previous_pathname
;
162 /* TODO: Make this a heap for fast inserts and deletions. */
163 struct file_info
**pending_files
;
164 int pending_files_allocated
;
165 int pending_files_used
;
167 uint64_t current_position
;
168 ssize_t logical_block_size
;
170 off_t entry_sparse_offset
;
171 ssize_t entry_bytes_remaining
;
174 static void add_entry(struct iso9660
*iso9660
, struct file_info
*file
);
175 static int archive_read_format_iso9660_bid(struct archive
*);
176 static int archive_read_format_iso9660_cleanup(struct archive
*);
177 static int archive_read_format_iso9660_read_data(struct archive
*,
178 const void **, size_t *, off_t
*);
179 static int archive_read_format_iso9660_read_header(struct archive
*,
180 struct archive_entry
*);
181 static const char *build_pathname(struct archive_string
*, struct file_info
*);
182 static void dump_isodirrec(FILE *, const struct iso9660_directory_record
*);
183 static time_t isodate17(const void *);
184 static time_t isodate7(const void *);
185 static int isPVD(struct iso9660
*, const char *);
186 static struct file_info
*next_entry(struct iso9660
*);
187 static int next_entry_seek(struct archive
*a
, struct iso9660
*iso9660
,
188 struct file_info
**pfile
);
189 static struct file_info
*
190 parse_file_info(struct iso9660
*iso9660
,
191 struct file_info
*parent
,
192 const struct iso9660_directory_record
*isodirrec
);
193 static void parse_rockridge(struct iso9660
*iso9660
,
194 struct file_info
*file
, const unsigned char *start
,
195 const unsigned char *end
);
196 static void release_file(struct iso9660
*, struct file_info
*);
197 static int toi(const void *p
, int n
);
200 archive_read_support_format_iso9660(struct archive
*a
)
202 struct iso9660
*iso9660
;
205 iso9660
= malloc(sizeof(*iso9660
));
206 memset(iso9660
, 0, sizeof(*iso9660
));
207 iso9660
->magic
= ISO9660_MAGIC
;
208 iso9660
->bid
= -1; /* We haven't yet bid. */
210 r
= __archive_read_register_format(a
,
212 archive_read_format_iso9660_bid
,
213 archive_read_format_iso9660_read_header
,
214 archive_read_format_iso9660_read_data
,
216 archive_read_format_iso9660_cleanup
);
218 if (r
!= ARCHIVE_OK
) {
227 archive_read_format_iso9660_bid(struct archive
*a
)
229 struct iso9660
*iso9660
;
234 iso9660
= *(a
->pformat_data
);
236 if (iso9660
->bid
>= 0)
237 return (iso9660
->bid
);
240 * Skip the first 32k (reserved area) and get the first
241 * 8 sectors of the volume descriptor table. Of course,
242 * if the I/O layer gives us more, we'll take it.
244 bytes_read
= (a
->compression_read_ahead
)(a
, &h
, 32768 + 8*2048);
245 if (bytes_read
< 32768 + 8*2048)
246 return (iso9660
->bid
= -1);
249 /* Skip the reserved area. */
253 /* Check each volume descriptor to locate the PVD. */
254 for (; bytes_read
> 2048; bytes_read
-= 2048, p
+= 2048) {
255 iso9660
->bid
= isPVD(iso9660
, p
);
256 if (iso9660
->bid
> 0)
257 return (iso9660
->bid
);
258 if (*p
== '\xff') /* End-of-volume-descriptor marker. */
262 /* We didn't find a valid PVD; return a bid of zero. */
264 return (iso9660
->bid
);
268 isPVD(struct iso9660
*iso9660
, const char *h
)
270 const struct iso9660_primary_volume_descriptor
*voldesc
;
271 struct file_info
*file
;
275 if (memcmp(h
+1, "CD001", 5) != 0)
279 voldesc
= (const struct iso9660_primary_volume_descriptor
*)h
;
280 iso9660
->logical_block_size
= toi(&voldesc
->logical_block_size
, 2);
282 /* Store the root directory in the pending list. */
283 file
= parse_file_info(iso9660
, NULL
,
284 (struct iso9660_directory_record
*)&voldesc
->root_directory_record
);
285 add_entry(iso9660
, file
);
290 archive_read_format_iso9660_read_header(struct archive
*a
,
291 struct archive_entry
*entry
)
294 struct iso9660
*iso9660
;
295 struct file_info
*file
;
299 iso9660
= *(a
->pformat_data
);
301 if (iso9660
->seenRockridge
) {
302 a
->archive_format
= ARCHIVE_FORMAT_ISO9660_ROCKRIDGE
;
303 a
->archive_format_name
= "ISO9660 with Rockridge extensions";
305 a
->archive_format
= ARCHIVE_FORMAT_ISO9660
;
306 a
->archive_format_name
= "ISO9660";
309 /* Get the next entry that appears after the current offset. */
310 r
= next_entry_seek(a
, iso9660
, &file
);
314 iso9660
->entry_bytes_remaining
= file
->size
;
315 iso9660
->entry_sparse_offset
= 0; /* Offset for sparse-file-aware clients. */
317 /* Set up the entry structure with information about this entry. */
318 memset(&st
, 0, sizeof(st
));
319 st
.st_mode
= file
->mode
;
320 st
.st_uid
= file
->uid
;
321 st
.st_gid
= file
->gid
;
322 st
.st_nlink
= file
->nlinks
;
323 st
.st_ino
= file
->inode
;
324 st
.st_mtime
= file
->mtime
;
325 st
.st_ctime
= file
->ctime
;
326 st
.st_atime
= file
->atime
;
327 st
.st_size
= iso9660
->entry_bytes_remaining
;
328 archive_entry_copy_stat(entry
, &st
);
329 archive_string_empty(&iso9660
->pathname
);
330 archive_entry_set_pathname(entry
,
331 build_pathname(&iso9660
->pathname
, file
));
332 if (file
->symlink
.s
!= NULL
)
333 archive_entry_set_symlink(entry
, file
->symlink
.s
);
335 /* If this entry points to the same data as the previous
336 * entry, convert this into a hardlink to that entry.
337 * But don't bother for zero-length files. */
338 if (file
->offset
== iso9660
->previous_offset
339 && file
->size
== iso9660
->previous_size
341 archive_entry_set_hardlink(entry
,
342 iso9660
->previous_pathname
.s
);
343 iso9660
->entry_bytes_remaining
= 0;
344 iso9660
->entry_sparse_offset
= 0;
345 release_file(iso9660
, file
);
349 /* If the offset is before our current position, we can't
350 * seek backwards to extract it, so issue a warning. */
351 if (file
->offset
< iso9660
->current_position
) {
352 archive_set_error(a
, ARCHIVE_ERRNO_MISC
,
353 "Ignoring out-of-order file");
354 iso9660
->entry_bytes_remaining
= 0;
355 iso9660
->entry_sparse_offset
= 0;
356 release_file(iso9660
, file
);
357 return (ARCHIVE_WARN
);
360 iso9660
->previous_size
= file
->size
;
361 iso9660
->previous_offset
= file
->offset
;
362 archive_strcpy(&iso9660
->previous_pathname
, iso9660
->pathname
.s
);
364 /* If this is a directory, read in all of the entries right now. */
365 if (S_ISDIR(st
.st_mode
)) {
366 while(iso9660
->entry_bytes_remaining
> 0) {
368 const unsigned char *p
;
369 ssize_t step
= iso9660
->logical_block_size
;
370 if (step
> iso9660
->entry_bytes_remaining
)
371 step
= iso9660
->entry_bytes_remaining
;
372 bytes_read
= (a
->compression_read_ahead
)(a
, &block
, step
);
373 if (bytes_read
< step
) {
374 archive_set_error(a
, ARCHIVE_ERRNO_MISC
,
375 "Failed to read full block when scanning ISO9660 directory list");
376 release_file(iso9660
, file
);
377 return (ARCHIVE_FATAL
);
379 if (bytes_read
> step
)
381 (a
->compression_read_consume
)(a
, bytes_read
);
382 iso9660
->current_position
+= bytes_read
;
383 iso9660
->entry_bytes_remaining
-= bytes_read
;
385 *p
!= 0 && p
< (const unsigned char *)block
+ bytes_read
;
387 const struct iso9660_directory_record
*dr
388 = (const struct iso9660_directory_record
*)p
;
389 struct file_info
*child
;
391 /* Skip '.' entry. */
392 if (dr
->name_len
[0] == 1
393 && dr
->name
[0] == '\0')
395 /* Skip '..' entry. */
396 if (dr
->name_len
[0] == 1
397 && dr
->name
[0] == '\001')
399 child
= parse_file_info(iso9660
, file
, dr
);
400 add_entry(iso9660
, child
);
405 release_file(iso9660
, file
);
410 archive_read_format_iso9660_read_data(struct archive
*a
,
411 const void **buff
, size_t *size
, off_t
*offset
)
414 struct iso9660
*iso9660
;
416 iso9660
= *(a
->pformat_data
);
417 if (iso9660
->entry_bytes_remaining
<= 0) {
420 *offset
= iso9660
->entry_sparse_offset
;
421 return (ARCHIVE_EOF
);
424 bytes_read
= (a
->compression_read_ahead
)(a
, buff
, 1);
426 return (ARCHIVE_FATAL
);
427 if (bytes_read
> iso9660
->entry_bytes_remaining
)
428 bytes_read
= iso9660
->entry_bytes_remaining
;
430 *offset
= iso9660
->entry_sparse_offset
;
431 iso9660
->entry_sparse_offset
+= bytes_read
;
432 iso9660
->entry_bytes_remaining
-= bytes_read
;
433 iso9660
->current_position
+= bytes_read
;
434 (a
->compression_read_consume
)(a
, bytes_read
);
439 archive_read_format_iso9660_cleanup(struct archive
*a
)
441 struct iso9660
*iso9660
;
442 struct file_info
*file
;
444 iso9660
= *(a
->pformat_data
);
445 while ((file
= next_entry(iso9660
)) != NULL
)
446 release_file(iso9660
, file
);
447 archive_string_free(&iso9660
->pathname
);
448 archive_string_free(&iso9660
->previous_pathname
);
450 *(a
->pformat_data
) = NULL
;
455 * This routine parses a single ISO directory record, makes sense
456 * of any extensions, and stores the result in memory.
458 static struct file_info
*
459 parse_file_info(struct iso9660
*iso9660
, struct file_info
*parent
,
460 const struct iso9660_directory_record
*isodirrec
)
462 struct file_info
*file
;
464 /* TODO: Sanity check that name_len doesn't exceed length, etc. */
466 /* Create a new file entry and copy data from the ISO dir record. */
467 file
= malloc(sizeof(*file
));
468 memset(file
, 0, sizeof(*file
));
469 file
->parent
= parent
;
472 file
->offset
= toi(isodirrec
->extent
, 4)
473 * iso9660
->logical_block_size
;
474 file
->size
= toi(isodirrec
->size
, 4);
475 file
->mtime
= isodate7(isodirrec
->date
);
476 file
->ctime
= file
->atime
= file
->mtime
;
477 file
->name
= malloc(isodirrec
->name_len
[0] + 1);
478 memcpy(file
->name
, isodirrec
->name
, isodirrec
->name_len
[0]);
479 file
->name
[(int)isodirrec
->name_len
[0]] = '\0';
480 if (isodirrec
->flags
[0] & 0x02)
481 file
->mode
= S_IFDIR
| 0700;
483 file
->mode
= S_IFREG
| 0400;
485 /* Rockridge extensions overwrite information from above. */
487 const unsigned char *rr_start
, *rr_end
;
488 rr_end
= (const unsigned char *)isodirrec
489 + isodirrec
->length
[0];
490 rr_start
= isodirrec
->name
+ isodirrec
->name_len
[0];
491 if ((isodirrec
->name_len
[0] & 1) == 0)
493 rr_start
+= iso9660
->suspOffset
;
494 parse_rockridge(iso9660
, file
, rr_start
, rr_end
);
497 /* DEBUGGING: Warn about attributes I don't yet fully support. */
498 if ((isodirrec
->flags
[0] & ~0x02) != 0) {
499 fprintf(stderr
, "\n ** Unrecognized flag: ");
500 dump_isodirrec(stderr
, isodirrec
);
501 fprintf(stderr
, "\n");
502 } else if (toi(isodirrec
->volume_sequence_number
, 2) != 1) {
503 fprintf(stderr
, "\n ** Unrecognized sequence number: ");
504 dump_isodirrec(stderr
, isodirrec
);
505 fprintf(stderr
, "\n");
506 } else if (isodirrec
->file_unit_size
[0] != 0) {
507 fprintf(stderr
, "\n ** Unexpected file unit size: ");
508 dump_isodirrec(stderr
, isodirrec
);
509 fprintf(stderr
, "\n");
510 } else if (isodirrec
->interleave
[0] != 0) {
511 fprintf(stderr
, "\n ** Unexpected interleave: ");
512 dump_isodirrec(stderr
, isodirrec
);
513 fprintf(stderr
, "\n");
514 } else if (isodirrec
->ext_attr_length
[0] != 0) {
515 fprintf(stderr
, "\n ** Unexpected extended attribute length: ");
516 dump_isodirrec(stderr
, isodirrec
);
517 fprintf(stderr
, "\n");
524 add_entry(struct iso9660
*iso9660
, struct file_info
*file
)
526 /* Expand our pending files list as necessary. */
527 if (iso9660
->pending_files_used
>= iso9660
->pending_files_allocated
) {
528 struct file_info
**new_pending_files
;
529 int new_size
= iso9660
->pending_files_allocated
* 2;
533 new_pending_files
= malloc(new_size
* sizeof(new_pending_files
[0]));
534 memcpy(new_pending_files
, iso9660
->pending_files
,
535 iso9660
->pending_files_allocated
* sizeof(new_pending_files
[0]));
536 if (iso9660
->pending_files
!= NULL
)
537 free(iso9660
->pending_files
);
538 iso9660
->pending_files
= new_pending_files
;
539 iso9660
->pending_files_allocated
= new_size
;
542 iso9660
->pending_files
[iso9660
->pending_files_used
++] = file
;
546 parse_rockridge(struct iso9660
*iso9660
, struct file_info
*file
,
547 const unsigned char *p
, const unsigned char *end
)
549 (void)iso9660
; /* UNUSED */
551 while (p
+ 4 < end
/* Enough space for another entry. */
552 && p
[0] >= 'A' && p
[0] <= 'Z' /* Sanity-check 1st char of name. */
553 && p
[1] >= 'A' && p
[1] <= 'Z' /* Sanity-check 2nd char of name. */
554 && p
+ p
[2] <= end
) { /* Sanity-check length. */
555 const unsigned char *data
= p
+ 4;
556 int data_length
= p
[2] - 4;
560 * Yes, each 'if' here does test p[0] again.
561 * Otherwise, the fall-through handling to catch
562 * unsupported extensions doesn't work.
566 if (p
[0] == 'C' && p
[1] == 'E' && version
== 1) {
568 * CE extension comprises:
569 * 8 byte sector containing extension
570 * 8 byte offset w/in above sector
571 * 8 byte length of continuation
573 file
->ce_offset
= toi(data
, 4)
574 * iso9660
->logical_block_size
576 file
->ce_size
= toi(data
+ 16, 4);
581 if (p
[0] == 'N' && p
[1] == 'M' && version
== 1
583 /* NM extension with flag byte == 0 */
585 * NM extension comprises:
589 /* TODO: Obey flags. */
590 char *old_name
= file
->name
;
592 data
++; /* Skip flag byte. */
594 file
->name
= malloc(data_length
+ 1);
595 if (file
->name
!= NULL
) {
597 memcpy(file
->name
, data
, data_length
);
598 file
->name
[data_length
] = '\0';
600 file
->name
= old_name
;
605 if (p
[0] == 'P' && p
[1] == 'D' && version
== 1) {
607 * PD extension is padding;
608 * contents are always ignored.
612 if (p
[0] == 'P' && p
[1] == 'X' && version
== 1) {
614 * PX extension comprises:
616 * 8 bytes for nlinks,
621 if (data_length
== 32) {
622 file
->mode
= toi(data
, 4);
623 file
->nlinks
= toi(data
+ 8, 4);
624 file
->uid
= toi(data
+ 16, 4);
625 file
->gid
= toi(data
+ 24, 4);
626 file
->inode
= toi(data
+ 32, 4);
632 if (p
[0] == 'R' && p
[1] == 'R' && version
== 1) {
633 iso9660
->seenRockridge
= 1;
635 * RR extension comprises:
636 * one byte flag value
638 /* TODO: Handle RR extension. */
643 if (p
[0] == 'S' && p
[1] == 'L' && version
== 1
646 /* SL extension with flags == 0 */
647 /* TODO: handle non-zero flag values. */
648 data
++; /* Skip flag byte. */
650 while (data_length
> 0) {
651 unsigned char flag
= *data
++;
652 unsigned char nlen
= *data
++;
656 archive_strcat(&file
->symlink
, "/");
660 case 0x01: /* Continue */
661 archive_strncat(&file
->symlink
, data
, nlen
);
664 case 0x02: /* Current */
665 archive_strcat(&file
->symlink
, ".");
667 case 0x04: /* Parent */
668 archive_strcat(&file
->symlink
, "..");
670 case 0x08: /* Root */
671 case 0x10: /* Volume root */
672 archive_string_empty(&file
->symlink
);
674 case 0x20: /* Hostname */
675 archive_strcat(&file
->symlink
, "hostname");
678 archive_strncat(&file
->symlink
, data
, nlen
);
681 /* TODO: issue a warning ? */
689 if (p
[0] == 'S' && p
[1] == 'P'
690 && version
== 1 && data_length
== 7
691 && data
[0] == (unsigned char)'\xbe'
692 && data
[1] == (unsigned char)'\xef') {
694 * SP extension stores the suspOffset
695 * (Number of bytes to skip between
696 * filename and SUSP records.)
697 * It is mandatory by the SUSP standard
700 * It allows SUSP to coexist with
701 * non-SUSP uses of the System
702 * Use Area by placing non-SUSP data
705 * TODO: Add a check for 'SP' in
706 * first directory entry, disable all SUSP
707 * processing if not found.
709 iso9660
->suspOffset
= data
[2];
712 if (p
[0] == 'S' && p
[1] == 'T'
713 && data_length
== 0 && version
== 1) {
715 * ST extension marks end of this
716 * block of SUSP entries.
718 * It allows SUSP to coexist with
719 * non-SUSP uses of the System
720 * Use Area by placing non-SUSP data
726 if (p
[0] == 'T' && p
[1] == 'F' && version
== 1) {
729 * TF extension comprises:
731 * create time (optional)
732 * modify time (optional)
733 * access time (optional)
734 * attribute time (optional)
735 * Time format and presence of fields
736 * is controlled by flag bits.
740 /* Use 17-byte time format. */
741 if (flag
& 1) /* Create time. */
743 if (flag
& 2) { /* Modify time. */
744 file
->mtime
= isodate17(data
);
747 if (flag
& 4) { /* Access time. */
748 file
->atime
= isodate17(data
);
751 if (flag
& 8) { /* Attribute time. */
752 file
->ctime
= isodate17(data
);
756 /* Use 7-byte time format. */
757 if (flag
& 1) /* Create time. */
759 if (flag
& 2) { /* Modify time. */
760 file
->mtime
= isodate7(data
);
763 if (flag
& 4) { /* Access time. */
764 file
->atime
= isodate7(data
);
767 if (flag
& 8) { /* Attribute time. */
768 file
->ctime
= isodate7(data
);
776 /* The FALLTHROUGHs above leave us here for
777 * any unsupported extension. */
779 const unsigned char *t
;
780 fprintf(stderr
, "\nUnsupported RRIP extension for %s\n", file
->name
);
781 fprintf(stderr
, " %c%c(%d):", p
[0], p
[1], data_length
);
782 for (t
= data
; t
< data
+ data_length
&& t
< data
+ 16; t
++)
783 fprintf(stderr
, " %02x", *t
);
784 fprintf(stderr
, "\n");
795 release_file(struct iso9660
*iso9660
, struct file_info
*file
)
797 struct file_info
*parent
;
799 if (file
->refcount
== 0) {
800 parent
= file
->parent
;
803 archive_string_free(&file
->symlink
);
805 if (parent
!= NULL
) {
807 release_file(iso9660
, parent
);
813 next_entry_seek(struct archive
*a
, struct iso9660
*iso9660
,
814 struct file_info
**pfile
)
816 struct file_info
*file
;
821 *pfile
= file
= next_entry(iso9660
);
823 return (ARCHIVE_EOF
);
825 /* CE area precedes actual file data? Ignore it. */
826 if (file
->ce_offset
> file
->offset
) {
827 fprintf(stderr
, " *** Discarding CE data.\n");
832 /* If CE exists, find and read it now. */
833 if (file
->ce_offset
> 0)
834 offset
= file
->ce_offset
;
836 offset
= file
->offset
;
838 /* Seek forward to the start of the entry. */
839 while (iso9660
->current_position
< offset
) {
840 ssize_t step
= offset
- iso9660
->current_position
;
844 if (step
> iso9660
->logical_block_size
)
845 step
= iso9660
->logical_block_size
;
846 bytes_read
= (a
->compression_read_ahead
)(a
, &buff
, step
);
847 if (bytes_read
<= 0) {
848 release_file(iso9660
, file
);
849 return (ARCHIVE_FATAL
);
851 if (bytes_read
> step
)
853 iso9660
->current_position
+= bytes_read
;
854 (a
->compression_read_consume
)(a
, bytes_read
);
857 /* We found body of file; handle it now. */
858 if (offset
== file
->offset
)
861 /* Found CE? Process it and push the file back onto list. */
862 if (offset
== file
->ce_offset
) {
864 ssize_t size
= file
->ce_size
;
866 const unsigned char *rr_start
;
870 bytes_read
= (a
->compression_read_ahead
)(a
, &p
, size
);
871 if (bytes_read
> size
)
873 rr_start
= (const unsigned char *)p
;
874 parse_rockridge(iso9660
, file
, rr_start
,
875 rr_start
+ bytes_read
);
876 (a
->compression_read_consume
)(a
, bytes_read
);
877 iso9660
->current_position
+= bytes_read
;
878 add_entry(iso9660
, file
);
883 static struct file_info
*
884 next_entry(struct iso9660
*iso9660
)
887 uint64_t least_end_offset
;
891 if (iso9660
->pending_files_used
< 1)
894 /* Assume the first file in the list is the earliest on disk. */
896 least_end_offset
= iso9660
->pending_files
[0]->offset
897 + iso9660
->pending_files
[0]->size
;
899 /* Now, try to find an earlier one. */
900 for(i
= 0; i
< iso9660
->pending_files_used
; i
++) {
901 /* Use the position of the file *end* as our comparison. */
902 uint64_t end_offset
= iso9660
->pending_files
[i
]->offset
903 + iso9660
->pending_files
[i
]->size
;
904 if (iso9660
->pending_files
[i
]->ce_offset
> 0
905 && iso9660
->pending_files
[i
]->ce_offset
< iso9660
->pending_files
[i
]->offset
)
906 end_offset
= iso9660
->pending_files
[i
]->ce_offset
907 + iso9660
->pending_files
[i
]->ce_size
;
908 if (least_end_offset
> end_offset
) {
910 least_end_offset
= end_offset
;
913 r
= iso9660
->pending_files
[least_index
];
914 iso9660
->pending_files
[least_index
]
915 = iso9660
->pending_files
[--iso9660
->pending_files_used
];
920 toi(const void *p
, int n
)
922 const unsigned char *v
= (const unsigned char *)p
;
924 return v
[0] + 256 * toi(v
+ 1, n
- 1);
931 isodate7(const void *p
)
934 const unsigned char *v
= (const unsigned char *)p
;
937 tm
.tm_mon
= v
[1] - 1;
942 /* v[6] is the timezone offset, in 1/4-hour increments. */
943 offset
= ((const signed char *)p
)[6];
944 if (offset
> -48 && offset
< 52) {
945 tm
.tm_hour
-= offset
/ 4;
946 tm
.tm_min
-= (offset
% 4) * 15;
948 return (timegm(&tm
));
952 isodate17(const void *p
)
955 const unsigned char *v
= (const unsigned char *)p
;
957 tm
.tm_year
= (v
[0] - '0') * 1000 + (v
[1] - '0') * 100
958 + (v
[2] - '0') * 10 + (v
[3] - '0')
960 tm
.tm_mon
= (v
[4] - '0') * 10 + (v
[5] - '0');
961 tm
.tm_mday
= (v
[6] - '0') * 10 + (v
[7] - '0');
962 tm
.tm_hour
= (v
[8] - '0') * 10 + (v
[9] - '0');
963 tm
.tm_min
= (v
[10] - '0') * 10 + (v
[11] - '0');
964 tm
.tm_sec
= (v
[12] - '0') * 10 + (v
[13] - '0');
965 /* v[16] is the timezone offset, in 1/4-hour increments. */
966 offset
= ((const signed char *)p
)[16];
967 if (offset
> -48 && offset
< 52) {
968 tm
.tm_hour
-= offset
/ 4;
969 tm
.tm_min
-= (offset
% 4) * 15;
971 return (timegm(&tm
));
975 build_pathname(struct archive_string
*as
, struct file_info
*file
)
977 if (file
->parent
!= NULL
&& file
->parent
->name
[0] != '\0') {
978 build_pathname(as
, file
->parent
);
979 archive_strcat(as
, "/");
981 if (file
->name
[0] == '\0')
982 archive_strcat(as
, ".");
984 archive_strcat(as
, file
->name
);
989 dump_isodirrec(FILE *out
, const struct iso9660_directory_record
*isodirrec
)
991 fprintf(out
, " l %d,", isodirrec
->length
[0]);
992 fprintf(out
, " a %d,", isodirrec
->ext_attr_length
[0]);
993 fprintf(out
, " ext 0x%x,", toi(isodirrec
->extent
, 4));
994 fprintf(out
, " s %d,", toi(isodirrec
->size
, 4));
995 fprintf(out
, " f 0x%02x,", isodirrec
->flags
[0]);
996 fprintf(out
, " u %d,", isodirrec
->file_unit_size
[0]);
997 fprintf(out
, " ilv %d,", isodirrec
->interleave
[0]);
998 fprintf(out
, " seq %d,", toi(isodirrec
->volume_sequence_number
,2));
999 fprintf(out
, " nl %d:", isodirrec
->name_len
[0]);
1000 fprintf(out
, " `%.*s'", isodirrec
->name_len
[0], isodirrec
->name
);