2 * Copyright (c) 2003-2007 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 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.22 2007/04/02 00:29:52 kientzle Exp $");
32 /* #include <stdint.h> */ /* See archive_platform.h */
43 #include "archive_entry.h"
44 #include "archive_private.h"
45 #include "archive_read_private.h"
46 #include "archive_string.h"
49 * An overview of ISO 9660 format:
51 * Each disk is laid out as follows:
52 * * 32k reserved for private use
53 * * Volume descriptor table. Each volume descriptor
54 * is 2k and specifies basic format information.
55 * The "Primary Volume Descriptor" (PVD) is defined by the
56 * standard and should always be present; other volume
57 * descriptors include various vendor-specific extensions.
58 * * Files and directories. Each file/dir is specified by
59 * an "extent" (starting sector and length in bytes).
60 * Dirs are just files with directory records packed one
61 * after another. The PVD contains a single dir entry
62 * specifying the location of the root directory. Everything
63 * else follows from there.
65 * This module works by first reading the volume descriptors, then
66 * building a list of directory entries, sorted by starting
67 * sector. At each step, I look for the earliest dir entry that
68 * hasn't yet been read, seek forward to that location and read
69 * that entry. If it's a dir, I slurp in the new dir entries and
70 * add them to the heap; if it's a regular file, I return the
71 * corresponding archive_entry and wait for the client to request
72 * the file body. This strategy allows us to read most compliant
73 * CDs with a single pass through the data, as required by libarchive.
76 /* Structure of on-disk primary volume descriptor. */
77 #define PVD_type_offset 0
78 #define PVD_type_size 1
79 #define PVD_id_offset (PVD_type_offset + PVD_type_size)
81 #define PVD_version_offset (PVD_id_offset + PVD_id_size)
82 #define PVD_version_size 1
83 #define PVD_reserved1_offset (PVD_version_offset + PVD_version_size)
84 #define PVD_reserved1_size 1
85 #define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size)
86 #define PVD_system_id_size 32
87 #define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size)
88 #define PVD_volume_id_size 32
89 #define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size)
90 #define PVD_reserved2_size 8
91 #define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size)
92 #define PVD_volume_space_size_size 8
93 #define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size)
94 #define PVD_reserved3_size 32
95 #define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size)
96 #define PVD_volume_set_size_size 4
97 #define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size)
98 #define PVD_volume_sequence_number_size 4
99 #define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size)
100 #define PVD_logical_block_size_size 4
101 #define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size)
102 #define PVD_path_table_size_size 8
103 #define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size)
104 #define PVD_type_1_path_table_size 4
105 #define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size)
106 #define PVD_opt_type_1_path_table_size 4
107 #define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size)
108 #define PVD_type_m_path_table_size 4
109 #define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size)
110 #define PVD_opt_type_m_path_table_size 4
111 #define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size)
112 #define PVD_root_directory_record_size 34
113 #define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size)
114 #define PVD_volume_set_id_size 128
115 #define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size)
116 #define PVD_publisher_id_size 128
117 #define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size)
118 #define PVD_preparer_id_size 128
119 #define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size)
120 #define PVD_application_id_size 128
121 #define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size)
122 #define PVD_copyright_file_id_size 37
123 #define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size)
124 #define PVD_abstract_file_id_size 37
125 #define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size)
126 #define PVD_bibliographic_file_id_size 37
127 #define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size)
128 #define PVD_creation_date_size 17
129 #define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size)
130 #define PVD_modification_date_size 17
131 #define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size)
132 #define PVD_expiration_date_size 17
133 #define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size)
134 #define PVD_effective_date_size 17
135 #define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size)
136 #define PVD_file_structure_version_size 1
137 #define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size)
138 #define PVD_reserved4_size 1
139 #define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size)
140 #define PVD_application_data_size 512
142 /* Structure of an on-disk directory record. */
143 /* Note: ISO9660 stores each multi-byte integer twice, once in
144 * each byte order. The sizes here are the size of just one
145 * of the two integers. (This is why the offset of a field isn't
146 * the same as the offset+size of the previous field.) */
147 #define DR_length_offset 0
148 #define DR_length_size 1
149 #define DR_ext_attr_length_offset 1
150 #define DR_ext_attr_length_size 1
151 #define DR_extent_offset 2
152 #define DR_extent_size 4
153 #define DR_size_offset 10
154 #define DR_size_size 4
155 #define DR_date_offset 18
156 #define DR_date_size 7
157 #define DR_flags_offset 25
158 #define DR_flags_size 1
159 #define DR_file_unit_size_offset 26
160 #define DR_file_unit_size_size 1
161 #define DR_interleave_offset 27
162 #define DR_interleave_size 1
163 #define DR_volume_sequence_number_offset 28
164 #define DR_volume_sequence_number_size 2
165 #define DR_name_len_offset 32
166 #define DR_name_len_size 1
167 #define DR_name_offset 33
173 /* In-memory storage for a directory record. */
175 struct file_info
*parent
;
177 uint64_t offset
; /* Offset on disk. */
178 uint64_t size
; /* File size in bytes. */
179 uint64_t ce_offset
; /* Offset of CE */
180 uint64_t ce_size
; /* Size of CE */
181 time_t mtime
; /* File last modified time. */
182 time_t atime
; /* File last accessed time. */
183 time_t ctime
; /* File creation time. */
189 char *name
; /* Null-terminated filename. */
190 struct archive_string symlink
;
196 #define ISO9660_MAGIC 0x96609660
197 int bid
; /* If non-zero, return this as our bid. */
198 struct archive_string pathname
;
199 char seenRockridge
; /* Set true if RR extensions are used. */
200 unsigned char suspOffset
;
202 uint64_t previous_offset
;
203 uint64_t previous_size
;
204 struct archive_string previous_pathname
;
206 /* TODO: Make this a heap for fast inserts and deletions. */
207 struct file_info
**pending_files
;
208 int pending_files_allocated
;
209 int pending_files_used
;
211 uint64_t current_position
;
212 ssize_t logical_block_size
;
214 off_t entry_sparse_offset
;
215 int64_t entry_bytes_remaining
;
218 static void add_entry(struct iso9660
*iso9660
, struct file_info
*file
);
219 static int archive_read_format_iso9660_bid(struct archive_read
*);
220 static int archive_read_format_iso9660_cleanup(struct archive_read
*);
221 static int archive_read_format_iso9660_read_data(struct archive_read
*,
222 const void **, size_t *, off_t
*);
223 static int archive_read_format_iso9660_read_data_skip(struct archive_read
*);
224 static int archive_read_format_iso9660_read_header(struct archive_read
*,
225 struct archive_entry
*);
226 static const char *build_pathname(struct archive_string
*, struct file_info
*);
227 static void dump_isodirrec(FILE *, const unsigned char *isodirrec
);
228 static time_t time_from_tm(struct tm
*);
229 static time_t isodate17(const unsigned char *);
230 static time_t isodate7(const unsigned char *);
231 static int isPVD(struct iso9660
*, const unsigned char *);
232 static struct file_info
*next_entry(struct iso9660
*);
233 static int next_entry_seek(struct archive_read
*a
, struct iso9660
*iso9660
,
234 struct file_info
**pfile
);
235 static struct file_info
*
236 parse_file_info(struct iso9660
*iso9660
,
237 struct file_info
*parent
, const unsigned char *isodirrec
);
238 static void parse_rockridge(struct iso9660
*iso9660
,
239 struct file_info
*file
, const unsigned char *start
,
240 const unsigned char *end
);
241 static void release_file(struct iso9660
*, struct file_info
*);
242 static unsigned toi(const void *p
, int n
);
245 archive_read_support_format_iso9660(struct archive
*_a
)
247 struct archive_read
*a
= (struct archive_read
*)_a
;
248 struct iso9660
*iso9660
;
251 iso9660
= (struct iso9660
*)malloc(sizeof(*iso9660
));
252 if (iso9660
== NULL
) {
253 archive_set_error(&a
->archive
, ENOMEM
, "Can't allocate iso9660 data");
254 return (ARCHIVE_FATAL
);
256 memset(iso9660
, 0, sizeof(*iso9660
));
257 iso9660
->magic
= ISO9660_MAGIC
;
258 iso9660
->bid
= -1; /* We haven't yet bid. */
260 r
= __archive_read_register_format(a
,
262 archive_read_format_iso9660_bid
,
263 archive_read_format_iso9660_read_header
,
264 archive_read_format_iso9660_read_data
,
265 archive_read_format_iso9660_read_data_skip
,
266 archive_read_format_iso9660_cleanup
);
268 if (r
!= ARCHIVE_OK
) {
277 archive_read_format_iso9660_bid(struct archive_read
*a
)
279 struct iso9660
*iso9660
;
282 const unsigned char *p
;
284 iso9660
= (struct iso9660
*)(a
->format
->data
);
286 if (iso9660
->bid
>= 0)
287 return (iso9660
->bid
);
290 * Skip the first 32k (reserved area) and get the first
291 * 8 sectors of the volume descriptor table. Of course,
292 * if the I/O layer gives us more, we'll take it.
294 bytes_read
= (a
->decompressor
->read_ahead
)(a
, &h
, 32768 + 8*2048);
295 if (bytes_read
< 32768 + 8*2048)
296 return (iso9660
->bid
= -1);
297 p
= (const unsigned char *)h
;
299 /* Skip the reserved area. */
303 /* Check each volume descriptor to locate the PVD. */
304 for (; bytes_read
> 2048; bytes_read
-= 2048, p
+= 2048) {
305 iso9660
->bid
= isPVD(iso9660
, p
);
306 if (iso9660
->bid
> 0)
307 return (iso9660
->bid
);
308 if (*p
== '\177') /* End-of-volume-descriptor marker. */
312 /* We didn't find a valid PVD; return a bid of zero. */
314 return (iso9660
->bid
);
318 isPVD(struct iso9660
*iso9660
, const unsigned char *h
)
320 struct file_info
*file
;
324 if (memcmp(h
+1, "CD001", 5) != 0)
327 iso9660
->logical_block_size
= toi(h
+ PVD_logical_block_size_offset
, 2);
329 /* Store the root directory in the pending list. */
330 file
= parse_file_info(iso9660
, NULL
, h
+ PVD_root_directory_record_offset
);
331 add_entry(iso9660
, file
);
336 archive_read_format_iso9660_read_header(struct archive_read
*a
,
337 struct archive_entry
*entry
)
339 struct iso9660
*iso9660
;
340 struct file_info
*file
;
344 iso9660
= (struct iso9660
*)(a
->format
->data
);
346 if (!a
->archive
.archive_format
) {
347 a
->archive
.archive_format
= ARCHIVE_FORMAT_ISO9660
;
348 a
->archive
.archive_format_name
= "ISO9660";
351 /* Get the next entry that appears after the current offset. */
352 r
= next_entry_seek(a
, iso9660
, &file
);
356 iso9660
->entry_bytes_remaining
= file
->size
;
357 iso9660
->entry_sparse_offset
= 0; /* Offset for sparse-file-aware clients. */
359 /* Set up the entry structure with information about this entry. */
360 archive_entry_set_mode(entry
, file
->mode
);
361 archive_entry_set_uid(entry
, file
->uid
);
362 archive_entry_set_gid(entry
, file
->gid
);
363 archive_entry_set_nlink(entry
, file
->nlinks
);
364 archive_entry_set_ino(entry
, file
->inode
);
365 archive_entry_set_mtime(entry
, file
->mtime
, 0);
366 archive_entry_set_ctime(entry
, file
->ctime
, 0);
367 archive_entry_set_atime(entry
, file
->atime
, 0);
368 archive_entry_set_size(entry
, iso9660
->entry_bytes_remaining
);
369 archive_string_empty(&iso9660
->pathname
);
370 archive_entry_set_pathname(entry
,
371 build_pathname(&iso9660
->pathname
, file
));
372 if (file
->symlink
.s
!= NULL
)
373 archive_entry_copy_symlink(entry
, file
->symlink
.s
);
375 /* If this entry points to the same data as the previous
376 * entry, convert this into a hardlink to that entry.
377 * But don't bother for zero-length files. */
378 if (file
->offset
== iso9660
->previous_offset
379 && file
->size
== iso9660
->previous_size
381 archive_entry_set_hardlink(entry
,
382 iso9660
->previous_pathname
.s
);
383 iso9660
->entry_bytes_remaining
= 0;
384 iso9660
->entry_sparse_offset
= 0;
385 release_file(iso9660
, file
);
389 /* If the offset is before our current position, we can't
390 * seek backwards to extract it, so issue a warning. */
391 if (file
->offset
< iso9660
->current_position
) {
392 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
393 "Ignoring out-of-order file");
394 iso9660
->entry_bytes_remaining
= 0;
395 iso9660
->entry_sparse_offset
= 0;
396 release_file(iso9660
, file
);
397 return (ARCHIVE_WARN
);
400 iso9660
->previous_size
= file
->size
;
401 iso9660
->previous_offset
= file
->offset
;
402 archive_strcpy(&iso9660
->previous_pathname
, iso9660
->pathname
.s
);
404 /* If this is a directory, read in all of the entries right now. */
405 if (archive_entry_filetype(entry
) == AE_IFDIR
) {
406 while (iso9660
->entry_bytes_remaining
> 0) {
408 const unsigned char *p
;
409 ssize_t step
= iso9660
->logical_block_size
;
410 if (step
> iso9660
->entry_bytes_remaining
)
411 step
= iso9660
->entry_bytes_remaining
;
412 bytes_read
= (a
->decompressor
->read_ahead
)(a
, &block
, step
);
413 if (bytes_read
< step
) {
414 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
415 "Failed to read full block when scanning ISO9660 directory list");
416 release_file(iso9660
, file
);
417 return (ARCHIVE_FATAL
);
419 if (bytes_read
> step
)
421 (a
->decompressor
->consume
)(a
, bytes_read
);
422 iso9660
->current_position
+= bytes_read
;
423 iso9660
->entry_bytes_remaining
-= bytes_read
;
424 for (p
= (const unsigned char *)block
;
425 *p
!= 0 && p
< (const unsigned char *)block
+ bytes_read
;
427 struct file_info
*child
;
429 /* Skip '.' entry. */
430 if (*(p
+ DR_name_len_offset
) == 1
431 && *(p
+ DR_name_offset
) == '\0')
433 /* Skip '..' entry. */
434 if (*(p
+ DR_name_len_offset
) == 1
435 && *(p
+ DR_name_offset
) == '\001')
437 child
= parse_file_info(iso9660
, file
, p
);
438 add_entry(iso9660
, child
);
439 if (iso9660
->seenRockridge
) {
440 a
->archive
.archive_format
=
441 ARCHIVE_FORMAT_ISO9660_ROCKRIDGE
;
442 a
->archive
.archive_format_name
=
443 "ISO9660 with Rockridge extensions";
449 release_file(iso9660
, file
);
454 archive_read_format_iso9660_read_data_skip(struct archive_read
*a
)
456 /* Because read_next_header always does an explicit skip
457 * to the next entry, we don't need to do anything here. */
458 (void)a
; /* UNUSED */
463 archive_read_format_iso9660_read_data(struct archive_read
*a
,
464 const void **buff
, size_t *size
, off_t
*offset
)
467 struct iso9660
*iso9660
;
469 iso9660
= (struct iso9660
*)(a
->format
->data
);
470 if (iso9660
->entry_bytes_remaining
<= 0) {
473 *offset
= iso9660
->entry_sparse_offset
;
474 return (ARCHIVE_EOF
);
477 bytes_read
= (a
->decompressor
->read_ahead
)(a
, buff
, 1);
479 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
480 "Truncated input file");
482 return (ARCHIVE_FATAL
);
483 if (bytes_read
> iso9660
->entry_bytes_remaining
)
484 bytes_read
= iso9660
->entry_bytes_remaining
;
486 *offset
= iso9660
->entry_sparse_offset
;
487 iso9660
->entry_sparse_offset
+= bytes_read
;
488 iso9660
->entry_bytes_remaining
-= bytes_read
;
489 iso9660
->current_position
+= bytes_read
;
490 (a
->decompressor
->consume
)(a
, bytes_read
);
495 archive_read_format_iso9660_cleanup(struct archive_read
*a
)
497 struct iso9660
*iso9660
;
498 struct file_info
*file
;
500 iso9660
= (struct iso9660
*)(a
->format
->data
);
501 while ((file
= next_entry(iso9660
)) != NULL
)
502 release_file(iso9660
, file
);
503 archive_string_free(&iso9660
->pathname
);
504 archive_string_free(&iso9660
->previous_pathname
);
505 if (iso9660
->pending_files
)
506 free(iso9660
->pending_files
);
508 (a
->format
->data
) = NULL
;
513 * This routine parses a single ISO directory record, makes sense
514 * of any extensions, and stores the result in memory.
516 static struct file_info
*
517 parse_file_info(struct iso9660
*iso9660
, struct file_info
*parent
,
518 const unsigned char *isodirrec
)
520 struct file_info
*file
;
524 /* TODO: Sanity check that name_len doesn't exceed length, etc. */
526 /* Create a new file entry and copy data from the ISO dir record. */
527 file
= (struct file_info
*)malloc(sizeof(*file
));
530 memset(file
, 0, sizeof(*file
));
531 file
->parent
= parent
;
534 file
->offset
= toi(isodirrec
+ DR_extent_offset
, DR_extent_size
)
535 * iso9660
->logical_block_size
;
536 file
->size
= toi(isodirrec
+ DR_size_offset
, DR_size_size
);
537 file
->mtime
= isodate7(isodirrec
+ DR_date_offset
);
538 file
->ctime
= file
->atime
= file
->mtime
;
539 name_len
= (size_t)*(const unsigned char *)(isodirrec
+ DR_name_len_offset
);
540 file
->name
= (char *)malloc(name_len
+ 1);
541 if (file
->name
== NULL
) {
545 memcpy(file
->name
, isodirrec
+ DR_name_offset
, name_len
);
546 file
->name
[name_len
] = '\0';
547 flags
= *(isodirrec
+ DR_flags_offset
);
549 file
->mode
= AE_IFDIR
| 0700;
551 file
->mode
= AE_IFREG
| 0400;
553 /* Rockridge extensions overwrite information from above. */
555 const unsigned char *rr_start
, *rr_end
;
556 rr_end
= (const unsigned char *)isodirrec
557 + *(isodirrec
+ DR_length_offset
);
558 rr_start
= (const unsigned char *)(isodirrec
+ DR_name_offset
560 if ((name_len
& 1) == 0)
562 rr_start
+= iso9660
->suspOffset
;
563 parse_rockridge(iso9660
, file
, rr_start
, rr_end
);
566 /* DEBUGGING: Warn about attributes I don't yet fully support. */
567 if ((flags
& ~0x02) != 0) {
568 fprintf(stderr
, "\n ** Unrecognized flag: ");
569 dump_isodirrec(stderr
, isodirrec
);
570 fprintf(stderr
, "\n");
571 } else if (toi(isodirrec
+ DR_volume_sequence_number_offset
, 2) != 1) {
572 fprintf(stderr
, "\n ** Unrecognized sequence number: ");
573 dump_isodirrec(stderr
, isodirrec
);
574 fprintf(stderr
, "\n");
575 } else if (*(isodirrec
+ DR_file_unit_size_offset
) != 0) {
576 fprintf(stderr
, "\n ** Unexpected file unit size: ");
577 dump_isodirrec(stderr
, isodirrec
);
578 fprintf(stderr
, "\n");
579 } else if (*(isodirrec
+ DR_interleave_offset
) != 0) {
580 fprintf(stderr
, "\n ** Unexpected interleave: ");
581 dump_isodirrec(stderr
, isodirrec
);
582 fprintf(stderr
, "\n");
583 } else if (*(isodirrec
+ DR_ext_attr_length_offset
) != 0) {
584 fprintf(stderr
, "\n ** Unexpected extended attribute length: ");
585 dump_isodirrec(stderr
, isodirrec
);
586 fprintf(stderr
, "\n");
593 add_entry(struct iso9660
*iso9660
, struct file_info
*file
)
595 /* Expand our pending files list as necessary. */
596 if (iso9660
->pending_files_used
>= iso9660
->pending_files_allocated
) {
597 struct file_info
**new_pending_files
;
598 int new_size
= iso9660
->pending_files_allocated
* 2;
602 new_pending_files
= (struct file_info
**)malloc(new_size
* sizeof(new_pending_files
[0]));
603 if (new_pending_files
== NULL
)
604 __archive_errx(1, "Out of memory");
605 memcpy(new_pending_files
, iso9660
->pending_files
,
606 iso9660
->pending_files_allocated
* sizeof(new_pending_files
[0]));
607 if (iso9660
->pending_files
!= NULL
)
608 free(iso9660
->pending_files
);
609 iso9660
->pending_files
= new_pending_files
;
610 iso9660
->pending_files_allocated
= new_size
;
613 iso9660
->pending_files
[iso9660
->pending_files_used
++] = file
;
617 parse_rockridge(struct iso9660
*iso9660
, struct file_info
*file
,
618 const unsigned char *p
, const unsigned char *end
)
620 (void)iso9660
; /* UNUSED */
622 while (p
+ 4 < end
/* Enough space for another entry. */
623 && p
[0] >= 'A' && p
[0] <= 'Z' /* Sanity-check 1st char of name. */
624 && p
[1] >= 'A' && p
[1] <= 'Z' /* Sanity-check 2nd char of name. */
625 && p
+ p
[2] <= end
) { /* Sanity-check length. */
626 const unsigned char *data
= p
+ 4;
627 int data_length
= p
[2] - 4;
631 * Yes, each 'if' here does test p[0] again.
632 * Otherwise, the fall-through handling to catch
633 * unsupported extensions doesn't work.
637 if (p
[0] == 'C' && p
[1] == 'E' && version
== 1) {
639 * CE extension comprises:
640 * 8 byte sector containing extension
641 * 8 byte offset w/in above sector
642 * 8 byte length of continuation
644 file
->ce_offset
= toi(data
, 4)
645 * iso9660
->logical_block_size
647 file
->ce_size
= toi(data
+ 16, 4);
652 if (p
[0] == 'N' && p
[1] == 'M' && version
== 1
654 /* NM extension with flag byte == 0 */
656 * NM extension comprises:
660 /* TODO: Obey flags. */
661 char *old_name
= file
->name
;
663 data
++; /* Skip flag byte. */
665 file
->name
= (char *)malloc(data_length
+ 1);
666 if (file
->name
!= NULL
) {
668 memcpy(file
->name
, data
, data_length
);
669 file
->name
[data_length
] = '\0';
671 file
->name
= old_name
;
676 if (p
[0] == 'P' && p
[1] == 'D' && version
== 1) {
678 * PD extension is padding;
679 * contents are always ignored.
683 if (p
[0] == 'P' && p
[1] == 'X' && version
== 1) {
685 * PX extension comprises:
687 * 8 bytes for nlinks,
692 if (data_length
== 32) {
693 file
->mode
= toi(data
, 4);
694 file
->nlinks
= toi(data
+ 8, 4);
695 file
->uid
= toi(data
+ 16, 4);
696 file
->gid
= toi(data
+ 24, 4);
697 file
->inode
= toi(data
+ 32, 4);
703 if (p
[0] == 'R' && p
[1] == 'R' && version
== 1) {
704 iso9660
->seenRockridge
= 1;
706 * RR extension comprises:
707 * one byte flag value
709 /* TODO: Handle RR extension. */
714 if (p
[0] == 'S' && p
[1] == 'L' && version
== 1
717 /* SL extension with flags == 0 */
718 /* TODO: handle non-zero flag values. */
719 data
++; /* Skip flag byte. */
721 while (data_length
> 0) {
722 unsigned char flag
= *data
++;
723 unsigned char nlen
= *data
++;
727 archive_strcat(&file
->symlink
, "/");
731 case 0x01: /* Continue */
732 archive_strncat(&file
->symlink
,
733 (const char *)data
, nlen
);
736 case 0x02: /* Current */
737 archive_strcat(&file
->symlink
, ".");
739 case 0x04: /* Parent */
740 archive_strcat(&file
->symlink
, "..");
742 case 0x08: /* Root */
743 case 0x10: /* Volume root */
744 archive_string_empty(&file
->symlink
);
746 case 0x20: /* Hostname */
747 archive_strcat(&file
->symlink
, "hostname");
750 archive_strncat(&file
->symlink
,
751 (const char *)data
, nlen
);
754 /* TODO: issue a warning ? */
762 if (p
[0] == 'S' && p
[1] == 'P'
763 && version
== 1 && data_length
== 7
764 && data
[0] == (unsigned char)'\xbe'
765 && data
[1] == (unsigned char)'\xef') {
767 * SP extension stores the suspOffset
768 * (Number of bytes to skip between
769 * filename and SUSP records.)
770 * It is mandatory by the SUSP standard
773 * It allows SUSP to coexist with
774 * non-SUSP uses of the System
775 * Use Area by placing non-SUSP data
778 * TODO: Add a check for 'SP' in
779 * first directory entry, disable all SUSP
780 * processing if not found.
782 iso9660
->suspOffset
= data
[2];
785 if (p
[0] == 'S' && p
[1] == 'T'
786 && data_length
== 0 && version
== 1) {
788 * ST extension marks end of this
789 * block of SUSP entries.
791 * It allows SUSP to coexist with
792 * non-SUSP uses of the System
793 * Use Area by placing non-SUSP data
799 if (p
[0] == 'T' && p
[1] == 'F' && version
== 1) {
802 * TF extension comprises:
804 * create time (optional)
805 * modify time (optional)
806 * access time (optional)
807 * attribute time (optional)
808 * Time format and presence of fields
809 * is controlled by flag bits.
813 /* Use 17-byte time format. */
814 if (flag
& 1) /* Create time. */
816 if (flag
& 2) { /* Modify time. */
817 file
->mtime
= isodate17(data
);
820 if (flag
& 4) { /* Access time. */
821 file
->atime
= isodate17(data
);
824 if (flag
& 8) { /* Attribute time. */
825 file
->ctime
= isodate17(data
);
829 /* Use 7-byte time format. */
830 if (flag
& 1) /* Create time. */
832 if (flag
& 2) { /* Modify time. */
833 file
->mtime
= isodate7(data
);
836 if (flag
& 4) { /* Access time. */
837 file
->atime
= isodate7(data
);
840 if (flag
& 8) { /* Attribute time. */
841 file
->ctime
= isodate7(data
);
849 /* The FALLTHROUGHs above leave us here for
850 * any unsupported extension. */
852 const unsigned char *t
;
853 fprintf(stderr
, "\nUnsupported RRIP extension for %s\n", file
->name
);
854 fprintf(stderr
, " %c%c(%d):", p
[0], p
[1], data_length
);
855 for (t
= data
; t
< data
+ data_length
&& t
< data
+ 16; t
++)
856 fprintf(stderr
, " %02x", *t
);
857 fprintf(stderr
, "\n");
868 release_file(struct iso9660
*iso9660
, struct file_info
*file
)
870 struct file_info
*parent
;
872 if (file
->refcount
== 0) {
873 parent
= file
->parent
;
876 archive_string_free(&file
->symlink
);
878 if (parent
!= NULL
) {
880 release_file(iso9660
, parent
);
886 next_entry_seek(struct archive_read
*a
, struct iso9660
*iso9660
,
887 struct file_info
**pfile
)
889 struct file_info
*file
;
894 *pfile
= file
= next_entry(iso9660
);
896 return (ARCHIVE_EOF
);
898 /* CE area precedes actual file data? Ignore it. */
899 if (file
->ce_offset
> file
->offset
) {
900 fprintf(stderr
, " *** Discarding CE data.\n");
905 /* If CE exists, find and read it now. */
906 if (file
->ce_offset
> 0)
907 offset
= file
->ce_offset
;
909 offset
= file
->offset
;
911 /* Seek forward to the start of the entry. */
912 if (iso9660
->current_position
< offset
) {
913 off_t step
= offset
- iso9660
->current_position
;
915 bytes_read
= (a
->decompressor
->skip
)(a
, step
);
918 iso9660
->current_position
= offset
;
921 /* We found body of file; handle it now. */
922 if (offset
== file
->offset
)
925 /* Found CE? Process it and push the file back onto list. */
926 if (offset
== file
->ce_offset
) {
928 ssize_t size
= file
->ce_size
;
930 const unsigned char *rr_start
;
934 bytes_read
= (a
->decompressor
->read_ahead
)(a
, &p
, size
);
935 if (bytes_read
> size
)
937 rr_start
= (const unsigned char *)p
;
938 parse_rockridge(iso9660
, file
, rr_start
,
939 rr_start
+ bytes_read
);
940 (a
->decompressor
->consume
)(a
, bytes_read
);
941 iso9660
->current_position
+= bytes_read
;
942 add_entry(iso9660
, file
);
947 static struct file_info
*
948 next_entry(struct iso9660
*iso9660
)
951 uint64_t least_end_offset
;
955 if (iso9660
->pending_files_used
< 1)
958 /* Assume the first file in the list is the earliest on disk. */
960 least_end_offset
= iso9660
->pending_files
[0]->offset
961 + iso9660
->pending_files
[0]->size
;
963 /* Now, try to find an earlier one. */
964 for (i
= 0; i
< iso9660
->pending_files_used
; i
++) {
965 /* Use the position of the file *end* as our comparison. */
966 uint64_t end_offset
= iso9660
->pending_files
[i
]->offset
967 + iso9660
->pending_files
[i
]->size
;
968 if (iso9660
->pending_files
[i
]->ce_offset
> 0
969 && iso9660
->pending_files
[i
]->ce_offset
< iso9660
->pending_files
[i
]->offset
)
970 end_offset
= iso9660
->pending_files
[i
]->ce_offset
971 + iso9660
->pending_files
[i
]->ce_size
;
972 if (least_end_offset
> end_offset
) {
974 least_end_offset
= end_offset
;
977 r
= iso9660
->pending_files
[least_index
];
978 iso9660
->pending_files
[least_index
]
979 = iso9660
->pending_files
[--iso9660
->pending_files_used
];
984 toi(const void *p
, int n
)
986 const unsigned char *v
= (const unsigned char *)p
;
988 return v
[0] + 256 * toi(v
+ 1, n
- 1);
995 isodate7(const unsigned char *v
)
999 memset(&tm
, 0, sizeof(tm
));
1001 tm
.tm_mon
= v
[1] - 1;
1006 /* v[6] is the signed timezone offset, in 1/4-hour increments. */
1007 offset
= ((const signed char *)v
)[6];
1008 if (offset
> -48 && offset
< 52) {
1009 tm
.tm_hour
-= offset
/ 4;
1010 tm
.tm_min
-= (offset
% 4) * 15;
1012 return (time_from_tm(&tm
));
1016 isodate17(const unsigned char *v
)
1020 memset(&tm
, 0, sizeof(tm
));
1021 tm
.tm_year
= (v
[0] - '0') * 1000 + (v
[1] - '0') * 100
1022 + (v
[2] - '0') * 10 + (v
[3] - '0')
1024 tm
.tm_mon
= (v
[4] - '0') * 10 + (v
[5] - '0');
1025 tm
.tm_mday
= (v
[6] - '0') * 10 + (v
[7] - '0');
1026 tm
.tm_hour
= (v
[8] - '0') * 10 + (v
[9] - '0');
1027 tm
.tm_min
= (v
[10] - '0') * 10 + (v
[11] - '0');
1028 tm
.tm_sec
= (v
[12] - '0') * 10 + (v
[13] - '0');
1029 /* v[16] is the signed timezone offset, in 1/4-hour increments. */
1030 offset
= ((const signed char *)v
)[16];
1031 if (offset
> -48 && offset
< 52) {
1032 tm
.tm_hour
-= offset
/ 4;
1033 tm
.tm_min
-= (offset
% 4) * 15;
1035 return (time_from_tm(&tm
));
1039 * timegm() converts a struct tm to a time_t, except it isn't standard,
1040 * so I provide my own function here that (ideally) is just a wrapper
1044 time_from_tm(struct tm
*t
)
1048 #elif HAVE_STRUCT_TM_TM_GMTOFF
1050 * Unfortunately, timegm() isn't standard. The standard
1051 * mktime() function is a close match, except that it uses
1052 * local timezone instead of GMT. You can compensate for
1053 * this by adding the timezone and DST offsets back in, at
1054 * the cost of two calls to mktime().
1056 mktime(t
); /* Normalize the time and get the TZ offset. */
1057 t
->tm_sec
+= t
->tm_gmtoff
; /* Try to adjust for the timezone and DST.*/
1060 return (mktime(t
)); /* Re-convert. */
1063 * If you don't have tm_gmtoff, let's try resetting the timezone
1070 setenv("TZ", "UTC 0", 1);
1074 setenv("TZ", tz
, 1);
1083 build_pathname(struct archive_string
*as
, struct file_info
*file
)
1085 if (file
->parent
!= NULL
&& file
->parent
->name
[0] != '\0') {
1086 build_pathname(as
, file
->parent
);
1087 archive_strcat(as
, "/");
1089 if (file
->name
[0] == '\0')
1090 archive_strcat(as
, ".");
1092 archive_strcat(as
, file
->name
);
1097 dump_isodirrec(FILE *out
, const unsigned char *isodirrec
)
1099 fprintf(out
, " l %d,",
1100 toi(isodirrec
+ DR_length_offset
, DR_length_size
));
1101 fprintf(out
, " a %d,",
1102 toi(isodirrec
+ DR_ext_attr_length_offset
, DR_ext_attr_length_size
));
1103 fprintf(out
, " ext 0x%x,",
1104 toi(isodirrec
+ DR_extent_offset
, DR_extent_size
));
1105 fprintf(out
, " s %d,",
1106 toi(isodirrec
+ DR_size_offset
, DR_extent_size
));
1107 fprintf(out
, " f 0x%02x,",
1108 toi(isodirrec
+ DR_flags_offset
, DR_flags_size
));
1109 fprintf(out
, " u %d,",
1110 toi(isodirrec
+ DR_file_unit_size_offset
, DR_file_unit_size_size
));
1111 fprintf(out
, " ilv %d,",
1112 toi(isodirrec
+ DR_interleave_offset
, DR_interleave_size
));
1113 fprintf(out
, " seq %d,",
1114 toi(isodirrec
+ DR_volume_sequence_number_offset
, DR_volume_sequence_number_size
));
1115 fprintf(out
, " nl %d:",
1116 toi(isodirrec
+ DR_name_len_offset
, DR_name_len_size
));
1117 fprintf(out
, " `%.*s'",
1118 toi(isodirrec
+ DR_name_len_offset
, DR_name_len_size
), isodirrec
+ DR_name_offset
);