1 /* $NetBSD: cd9660.c,v 1.25 2009/01/10 22:06:29 bjh21 Exp $ */
4 * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
5 * Perez-Rathke and Ram Vedam. All rights reserved.
7 * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
8 * Alan Perez-Rathke and Ram Vedam.
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
21 * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
25 * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
35 * Copyright (c) 2001 Wasabi Systems, Inc.
36 * All rights reserved.
38 * Written by Luke Mewburn for Wasabi Systems, Inc.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. All advertising materials mentioning features or use of this software
49 * must display the following acknowledgement:
50 * This product includes software developed for the NetBSD Project by
51 * Wasabi Systems, Inc.
52 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
53 * or promote products derived from this software without specific prior
56 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
58 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
60 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
61 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
62 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
63 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
64 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
65 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
66 * POSSIBILITY OF SUCH DAMAGE.
69 * Copyright (c) 1982, 1986, 1989, 1993
70 * The Regents of the University of California. All rights reserved.
72 * Redistribution and use in source and binary forms, with or without
73 * modification, are permitted provided that the following conditions
75 * 1. Redistributions of source code must retain the above copyright
76 * notice, this list of conditions and the following disclaimer.
77 * 2. Redistributions in binary form must reproduce the above copyright
78 * notice, this list of conditions and the following disclaimer in the
79 * documentation and/or other materials provided with the distribution.
80 * 3. Neither the name of the University nor the names of its contributors
81 * may be used to endorse or promote products derived from this software
82 * without specific prior written permission.
84 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
85 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
86 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
87 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
88 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
89 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
90 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
91 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
92 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
93 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
98 #if HAVE_NBTOOL_CONFIG_H
99 #include "nbtool_config.h"
101 #include <sys/mount.h>
104 #include <sys/cdefs.h>
105 #if defined(__RCSID) && !defined(__lint)
106 __RCSID("$NetBSD: cd9660.c,v 1.25 2009/01/10 22:06:29 bjh21 Exp $");
111 #include <sys/param.h>
112 #include <sys/queue.h>
116 #include "cd9660/iso9660_rrip.h"
117 #include "cd9660/cd9660_archimedes.h"
122 iso9660_disk diskStructure
;
124 static void cd9660_finalize_PVD(void);
125 static cd9660node
*cd9660_allocate_cd9660node(void);
126 static void cd9660_set_defaults(void);
127 static int cd9660_arguments_set_string(const char *, const char *, int,
129 static void cd9660_populate_iso_dir_record(
130 struct _iso_directory_record_cd9660
*, u_char
, u_char
, u_char
,
132 static void cd9660_setup_root_node(void);
133 static int cd9660_setup_volume_descriptors(void);
135 static int cd9660_fill_extended_attribute_record(cd9660node
*);
137 static void cd9660_sort_nodes(cd9660node
*);
138 static int cd9960_translate_node_common(cd9660node
*);
139 static int cd9660_translate_node(fsnode
*, cd9660node
*);
140 static int cd9660_compare_filename(const char *, const char *);
141 static void cd9660_sorted_child_insert(cd9660node
*, cd9660node
*);
142 static int cd9660_handle_collisions(cd9660node
*, int);
143 static cd9660node
*cd9660_rename_filename(cd9660node
*, int, int);
144 static void cd9660_copy_filenames(cd9660node
*);
145 static void cd9660_sorting_nodes(cd9660node
*);
146 static int cd9660_count_collisions(cd9660node
*);
147 static cd9660node
*cd9660_rrip_move_directory(cd9660node
*);
148 static int cd9660_add_dot_records(cd9660node
*);
150 static void cd9660_convert_structure(fsnode
*, cd9660node
*, int,
152 static void cd9660_free_structure(cd9660node
*);
153 static int cd9660_generate_path_table(void);
154 static int cd9660_level1_convert_filename(const char *, char *, int);
155 static int cd9660_level2_convert_filename(const char *, char *, int);
157 static int cd9660_joliet_convert_filename(const char *, char *, int);
159 static int cd9660_convert_filename(const char *, char *, int);
160 static void cd9660_populate_dot_records(cd9660node
*);
161 static int cd9660_compute_offsets(cd9660node
*, int);
163 static int cd9660_copy_stat_info(cd9660node
*, cd9660node
*, int);
165 static cd9660node
*cd9660_create_virtual_entry(const char *, cd9660node
*, int,
167 static cd9660node
*cd9660_create_file(const char *, cd9660node
*, cd9660node
*);
168 static cd9660node
*cd9660_create_directory(const char *, cd9660node
*,
170 static cd9660node
*cd9660_create_special_directory(u_char
, cd9660node
*);
174 * Allocate and initalize a cd9660node
175 * @returns struct cd9660node * Pointer to new node, or NULL on error
178 cd9660_allocate_cd9660node(void)
182 if ((temp
= calloc(1, sizeof(cd9660node
))) == NULL
)
183 err(EXIT_FAILURE
, "%s: calloc", __func__
);
184 TAILQ_INIT(&temp
->cn_children
);
185 temp
->parent
= temp
->dot_record
= temp
->dot_dot_record
= NULL
;
186 temp
->ptnext
= temp
->ptprev
= temp
->ptlast
= NULL
;
188 temp
->isoDirRecord
= NULL
;
189 temp
->isoExtAttributes
= NULL
;
190 temp
->rr_real_parent
= temp
->rr_relocated
= NULL
;
191 temp
->su_tail_data
= NULL
;
195 int cd9660_defaults_set
= 0;
198 * Set default values for cd9660 extension to makefs
201 cd9660_set_defaults(void)
203 /*Fix the sector size for now, though the spec allows for other sizes*/
204 diskStructure
.sectorSize
= 2048;
206 /* Set up defaults in our own structure */
207 diskStructure
.verbose_level
= 0;
208 diskStructure
.keep_bad_images
= 0;
209 diskStructure
.follow_sym_links
= 0;
210 diskStructure
.isoLevel
= 2;
212 diskStructure
.rock_ridge_enabled
= 0;
213 diskStructure
.rock_ridge_renamed_dir_name
= 0;
214 diskStructure
.rock_ridge_move_count
= 0;
215 diskStructure
.rr_moved_dir
= 0;
217 diskStructure
.archimedes_enabled
= 0;
219 diskStructure
.include_padding_areas
= 1;
221 /* Spec breaking functionality */
222 diskStructure
.allow_deep_trees
=
223 diskStructure
.allow_start_dot
=
224 diskStructure
.allow_max_name
=
225 diskStructure
.allow_illegal_chars
=
226 diskStructure
.allow_lowercase
=
227 diskStructure
.allow_multidot
=
228 diskStructure
.omit_trailing_period
= 0;
230 /* Make sure the PVD is clear */
231 memset(&diskStructure
.primaryDescriptor
, 0, 2048);
233 memset(diskStructure
.primaryDescriptor
.volume_set_id
, 0x20,32);
234 memset(diskStructure
.primaryDescriptor
.publisher_id
, 0x20,128);
235 memset(diskStructure
.primaryDescriptor
.preparer_id
, 0x20,128);
236 memset(diskStructure
.primaryDescriptor
.application_id
, 0x20,128);
237 memset(diskStructure
.primaryDescriptor
.copyright_file_id
, 0x20,128);
238 memset(diskStructure
.primaryDescriptor
.abstract_file_id
, 0x20,128);
239 memset(diskStructure
.primaryDescriptor
.bibliographic_file_id
, 0x20,128);
241 strcpy(diskStructure
.primaryDescriptor
.system_id
,"NetBSD");
243 cd9660_defaults_set
= 1;
245 /* Boot support: Initially disabled */
246 diskStructure
.has_generic_bootimage
= 0;
247 diskStructure
.generic_bootimage
= NULL
;
249 diskStructure
.boot_image_directory
= 0;
250 /*memset(diskStructure.boot_descriptor, 0, 2048);*/
252 diskStructure
.is_bootable
= 0;
253 TAILQ_INIT(&diskStructure
.boot_images
);
254 LIST_INIT(&diskStructure
.boot_entries
);
258 cd9660_prep_opts(fsinfo_t
*fsopts __unused
)
260 cd9660_set_defaults();
264 cd9660_cleanup_opts(fsinfo_t
*fsopts __unused
)
270 cd9660_arguments_set_string(const char *val
, const char *fieldtitle
, int length
,
271 char testmode
, char * dest
)
276 warnx("error: The %s requires a string argument", fieldtitle
);
277 else if ((len
= strlen(val
)) <= length
) {
279 test
= cd9660_valid_d_chars(val
);
281 test
= cd9660_valid_a_chars(val
);
283 memcpy(dest
, val
, len
);
285 cd9660_uppercase_characters(dest
, len
);
288 warnx("error: The %s must be composed of "
289 "%c-characters", fieldtitle
, testmode
);
291 warnx("error: The %s must be at most 32 characters long",
297 * Command-line parsing function
301 cd9660_parse_opts(const char *option
, fsinfo_t
*fsopts
)
305 /* Set up allowed options - integer options ONLY */
306 option_t cd9660_options
[] = {
307 { "l", &diskStructure
.isoLevel
, 1, 3, "ISO Level" },
308 { "isolevel", &diskStructure
.isoLevel
, 1, 3, "ISO Level" },
309 { "verbose", &diskStructure
.verbose_level
, 0, 2,
310 "Turns on verbose output" },
311 { "v", &diskStructure
.verbose_level
, 0 , 2,
312 "Turns on verbose output"},
316 if (cd9660_defaults_set
== 0)
317 cd9660_set_defaults();
320 * Todo : finish implementing this, and make a function that
324 string_option_t cd9660_string_options[] = {
325 { "L", "Label", &diskStructure.primaryDescriptor.volume_id, 1, 32, "Disk Label", ISO_STRING_FILTER_DCHARS },
330 assert(option
!= NULL
);
332 if (debug
& DEBUG_FS_PARSE_OPTS
)
333 printf("cd9660_parse_opts: got `%s'\n", option
);
335 if ((var
= strdup(option
)) == NULL
)
336 err(1, "allocating memory for copy of option string");
339 val
= strchr(var
, '=');
343 /* First handle options with no parameters */
344 if (strcmp(var
, "h") == 0) {
345 diskStructure
.displayHelp
= 1;
347 } else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "S", "follow-symlinks")) {
348 /* this is not handled yet */
349 diskStructure
.follow_sym_links
= 1;
351 } else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "L", "label")) {
352 rv
= cd9660_arguments_set_string(val
, "Disk Label", 32, 'd',
353 diskStructure
.primaryDescriptor
.volume_id
);
354 } else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "A", "applicationid")) {
355 rv
= cd9660_arguments_set_string(val
, "Application Identifier", 128, 'a',
356 diskStructure
.primaryDescriptor
.application_id
);
357 } else if(CD9660_IS_COMMAND_ARG_DUAL(var
, "P", "publisher")) {
358 rv
= cd9660_arguments_set_string(val
, "Publisher Identifier",
359 128, 'a', diskStructure
.primaryDescriptor
.publisher_id
);
360 } else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "p", "preparer")) {
361 rv
= cd9660_arguments_set_string(val
, "Preparer Identifier",
362 128, 'a', diskStructure
.primaryDescriptor
.preparer_id
);
363 } else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "V", "volumeid")) {
364 rv
= cd9660_arguments_set_string(val
, "Volume Set Identifier",
365 128, 'a', diskStructure
.primaryDescriptor
.volume_set_id
);
367 } else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "B", "bootimage")) {
369 warnx("error: The Boot Image parameter requires a valid boot information string");
371 rv
= cd9660_add_boot_disk(val
);
372 } else if (CD9660_IS_COMMAND_ARG(var
, "bootimagedir")) {
374 * XXXfvdl this is unused.
377 errx(1, "error: The Boot Image Directory parameter"
378 " requires a directory name\n");
380 if ((diskStructure
.boot_image_directory
=
381 malloc(strlen(val
) + 1)) == NULL
) {
382 CD9660_MEM_ALLOC_ERROR("cd9660_parse_opts");
386 /* BIG TODO: Add the max length function here */
387 cd9660_arguments_set_string(val
, "Boot Image Directory",
388 12 , 'd', diskStructure
.boot_image_directory
);
390 } else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "G", "generic-bootimage")) {
392 warnx("error: The Boot Image parameter requires a valid boot information string");
394 rv
= cd9660_add_generic_bootimage(val
);
395 } else if (CD9660_IS_COMMAND_ARG(var
, "no-trailing-padding"))
396 diskStructure
.include_padding_areas
= 0;
398 else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "R", "rockridge"))
399 diskStructure
.rock_ridge_enabled
= 1;
400 else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "A", "archimedes"))
401 diskStructure
.archimedes_enabled
= 1;
402 else if (CD9660_IS_COMMAND_ARG_DUAL(var
, "K", "keep-bad-images"))
403 diskStructure
.keep_bad_images
= 1;
404 else if (CD9660_IS_COMMAND_ARG(var
, "allow-deep-trees"))
405 diskStructure
.allow_deep_trees
= 1;
406 else if (CD9660_IS_COMMAND_ARG(var
, "allow-max-name"))
407 diskStructure
.allow_max_name
= 1;
408 else if (CD9660_IS_COMMAND_ARG(var
, "allow-illegal-chars"))
409 diskStructure
.allow_illegal_chars
= 1;
410 else if (CD9660_IS_COMMAND_ARG(var
, "allow-lowercase"))
411 diskStructure
.allow_lowercase
= 1;
412 else if (CD9660_IS_COMMAND_ARG(var
,"allow-multidot"))
413 diskStructure
.allow_multidot
= 1;
414 else if (CD9660_IS_COMMAND_ARG(var
, "omit-trailing-period"))
415 diskStructure
.omit_trailing_period
= 1;
416 else if (CD9660_IS_COMMAND_ARG(var
, "no-emul-boot") ||
417 CD9660_IS_COMMAND_ARG(var
, "no-boot") ||
418 CD9660_IS_COMMAND_ARG(var
, "hard-disk-boot")) {
419 cd9660_eltorito_add_boot_option(var
, 0);
421 /* End of flag variables */
422 } else if (CD9660_IS_COMMAND_ARG(var
, "boot-load-segment")) {
424 warnx("Option `%s' doesn't contain a value", var
);
427 cd9660_eltorito_add_boot_option(var
, val
);
431 warnx("Option `%s' doesn't contain a value", var
);
434 rv
= set_option(cd9660_options
, var
, val
);
443 * Main function for cd9660_makefs
444 * Builds the ISO image file
445 * @param const char *image The image filename to create
446 * @param const char *dir The directory that is being read
447 * @param struct fsnode *root The root node of the filesystem tree
448 * @param struct fsinfo_t *fsopts Any options
451 cd9660_makefs(const char *image
, const char *dir
, fsnode
*root
,
456 int pathTableSectors
;
457 int firstAvailableSector
;
460 cd9660node
*real_root
;
462 if (diskStructure
.verbose_level
> 0)
463 printf("cd9660_makefs: ISO level is %i\n",
464 diskStructure
.isoLevel
);
465 if (diskStructure
.isoLevel
< 2 &&
466 diskStructure
.allow_multidot
)
467 errx(1, "allow-multidot requires iso level of 2\n");
469 assert(image
!= NULL
);
471 assert(root
!= NULL
);
473 if (diskStructure
.displayHelp
) {
475 * Display help here - probably want to put it in
476 * a separate function
481 diskStructure
.rootFilesystemPath
= dir
;
483 if (diskStructure
.verbose_level
> 0)
484 printf("cd9660_makefs: image %s directory %s root %p\n",
487 /* Set up some constants. Later, these will be defined with options */
489 /* Counter needed for path tables */
492 /* Convert tree to our own format */
493 /* Actually, we now need to add the REAL root node, at level 0 */
495 real_root
= cd9660_allocate_cd9660node();
496 if ((real_root
->isoDirRecord
=
497 malloc( sizeof(iso_directory_record_cd9660
) )) == NULL
) {
498 CD9660_MEM_ALLOC_ERROR("cd9660_makefs");
502 /* Leave filename blank for root */
503 memset(real_root
->isoDirRecord
->name
, 0,
504 ISO_FILENAME_MAXLENGTH_WITH_PADDING
);
506 real_root
->level
= 0;
507 diskStructure
.rootNode
= real_root
;
508 real_root
->type
= CD9660_TYPE_DIR
;
510 real_root
->node
= root
;
511 cd9660_convert_structure(root
, real_root
, 1, &numDirectories
, &error
);
513 if (TAILQ_EMPTY(&real_root
->cn_children
)) {
514 errx(1, "cd9660_makefs: converted directory is empty. "
515 "Tree conversion failed\n");
516 } else if (error
!= 0) {
517 errx(1, "cd9660_makefs: tree conversion failed\n");
519 if (diskStructure
.verbose_level
> 0)
520 printf("cd9660_makefs: tree converted\n");
523 /* Add the dot and dot dot records */
524 cd9660_add_dot_records(real_root
);
526 cd9660_setup_root_node();
528 if (diskStructure
.verbose_level
> 0)
529 printf("cd9660_makefs: done converting tree\n");
531 /* non-SUSP extensions */
532 if (diskStructure
.archimedes_enabled
)
533 archimedes_convert_tree(diskStructure
.rootNode
);
535 /* Rock ridge / SUSP init pass */
536 if (diskStructure
.rock_ridge_enabled
) {
537 cd9660_susp_initialize(diskStructure
.rootNode
,
538 diskStructure
.rootNode
, NULL
);
541 /* Build path table structure */
542 diskStructure
.pathTableLength
= cd9660_generate_path_table();
544 pathTableSectors
= CD9660_BLOCKS(diskStructure
.sectorSize
,
545 diskStructure
.pathTableLength
);
547 firstAvailableSector
= cd9660_setup_volume_descriptors();
548 if (diskStructure
.is_bootable
) {
549 firstAvailableSector
= cd9660_setup_boot(firstAvailableSector
);
550 if (firstAvailableSector
< 0)
551 errx(1, "setup_boot failed");
553 /* LE first, then BE */
554 diskStructure
.primaryLittleEndianTableSector
= firstAvailableSector
;
555 diskStructure
.primaryBigEndianTableSector
=
556 diskStructure
.primaryLittleEndianTableSector
+ pathTableSectors
;
558 /* Set the secondary ones to -1, not going to use them for now */
559 diskStructure
.secondaryBigEndianTableSector
= -1;
560 diskStructure
.secondaryLittleEndianTableSector
= -1;
562 diskStructure
.dataFirstSector
=
563 diskStructure
.primaryBigEndianTableSector
+ pathTableSectors
;
564 if (diskStructure
.verbose_level
> 0)
565 printf("cd9660_makefs: Path table conversion complete. "
566 "Each table is %i bytes, or %i sectors.\n",
567 diskStructure
.pathTableLength
, pathTableSectors
);
569 startoffset
= diskStructure
.sectorSize
*diskStructure
.dataFirstSector
;
571 totalSpace
= cd9660_compute_offsets(real_root
, startoffset
);
573 diskStructure
.totalSectors
= diskStructure
.dataFirstSector
+
574 CD9660_BLOCKS(diskStructure
.sectorSize
, totalSpace
);
576 /* Disabled until pass 1 is done */
577 if (diskStructure
.rock_ridge_enabled
) {
578 diskStructure
.susp_continuation_area_start_sector
=
579 diskStructure
.totalSectors
;
580 diskStructure
.totalSectors
+=
581 CD9660_BLOCKS(diskStructure
.sectorSize
,
582 diskStructure
.susp_continuation_area_size
);
583 cd9660_susp_finalize(diskStructure
.rootNode
);
587 cd9660_finalize_PVD();
589 /* Add padding sectors, just for testing purposes right now */
590 /* diskStructure.totalSectors+=150; */
592 /* Debugging output */
593 if (diskStructure
.verbose_level
> 0) {
594 printf("cd9660_makefs: Sectors 0-15 reserved\n");
595 printf("cd9660_makefs: Primary path tables starts in sector %i\n",
596 diskStructure
.primaryLittleEndianTableSector
);
597 printf("cd9660_makefs: File data starts in sector %i\n",
598 diskStructure
.dataFirstSector
);
599 printf("cd9660_makefs: Total sectors: %i\n",diskStructure
.totalSectors
);
603 * Add padding sectors at the end
604 * TODO: Clean this up and separate padding
606 if (diskStructure
.include_padding_areas
)
607 diskStructure
.totalSectors
+= 150;
609 cd9660_write_image(image
);
611 if (diskStructure
.verbose_level
> 1) {
612 debug_print_volume_descriptor_information();
613 debug_print_tree(real_root
,0);
614 debug_print_path_tree(real_root
);
617 /* Clean up data structures */
618 cd9660_free_structure(real_root
);
620 if (diskStructure
.verbose_level
> 0)
621 printf("cd9660_makefs: done\n");
624 /* Generic function pointer - implement later */
625 typedef int (*cd9660node_func
)(cd9660node
*);
628 cd9660_finalize_PVD(void)
633 /* Copy the root directory record */
634 temp
= (unsigned char *) &diskStructure
.primaryDescriptor
;
636 /* root should be a fixed size of 34 bytes since it has no name */
637 memcpy(diskStructure
.primaryDescriptor
.root_directory_record
,
638 diskStructure
.rootNode
->dot_record
->isoDirRecord
, 34);
640 /* In RRIP, this might be longer than 34 */
641 diskStructure
.primaryDescriptor
.root_directory_record
[0] = 34;
643 /* Set up all the important numbers in the PVD */
644 cd9660_bothendian_dword(diskStructure
.totalSectors
,
645 (unsigned char *)diskStructure
.primaryDescriptor
.volume_space_size
);
646 cd9660_bothendian_word(1,
647 (unsigned char *)diskStructure
.primaryDescriptor
.volume_set_size
);
648 cd9660_bothendian_word(1,
650 diskStructure
.primaryDescriptor
.volume_sequence_number
);
651 cd9660_bothendian_word(diskStructure
.sectorSize
,
653 diskStructure
.primaryDescriptor
.logical_block_size
);
654 cd9660_bothendian_dword(diskStructure
.pathTableLength
,
655 (unsigned char *)diskStructure
.primaryDescriptor
.path_table_size
);
657 cd9660_731(diskStructure
.primaryLittleEndianTableSector
,
658 (u_char
*)diskStructure
.primaryDescriptor
.type_l_path_table
);
659 cd9660_732(diskStructure
.primaryBigEndianTableSector
,
660 (u_char
*)diskStructure
.primaryDescriptor
.type_m_path_table
);
662 diskStructure
.primaryDescriptor
.file_structure_version
[0] = 1;
664 /* Pad all strings with spaces instead of nulls */
665 cd9660_pad_string_spaces(diskStructure
.primaryDescriptor
.volume_id
, 32);
666 cd9660_pad_string_spaces(diskStructure
.primaryDescriptor
.system_id
, 32);
667 cd9660_pad_string_spaces(diskStructure
.primaryDescriptor
.volume_set_id
,
669 cd9660_pad_string_spaces(diskStructure
.primaryDescriptor
.publisher_id
,
671 cd9660_pad_string_spaces(diskStructure
.primaryDescriptor
.preparer_id
,
673 cd9660_pad_string_spaces(diskStructure
.primaryDescriptor
.application_id
,
675 cd9660_pad_string_spaces(
676 diskStructure
.primaryDescriptor
.copyright_file_id
, 128);
677 cd9660_pad_string_spaces(
678 diskStructure
.primaryDescriptor
.abstract_file_id
, 128);
679 cd9660_pad_string_spaces(
680 diskStructure
.primaryDescriptor
.bibliographic_file_id
, 128);
685 (unsigned char *)diskStructure
.primaryDescriptor
.creation_date
,
688 (unsigned char *)diskStructure
.primaryDescriptor
.modification_date
,
692 cd9660_set_date(diskStructure.primaryDescriptor.expiration_date, now);
695 memset(diskStructure
.primaryDescriptor
.expiration_date
, '0' ,17);
697 (unsigned char *)diskStructure
.primaryDescriptor
.effective_date
,
702 cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660
*record
,
703 u_char ext_attr_length
, u_char flags
,
704 u_char name_len
, const char * name
)
706 record
->ext_attr_length
[0] = ext_attr_length
;
707 record
->flags
[0] = ISO_FLAG_CLEAR
| flags
;
708 record
->file_unit_size
[0] = 0;
709 record
->interleave
[0] = 0;
710 cd9660_bothendian_word(1, record
->volume_sequence_number
);
711 record
->name_len
[0] = name_len
;
712 memset(record
->name
, '\0', sizeof (record
->name
));
713 memcpy(record
->name
, name
, name_len
);
714 record
->length
[0] = 33 + name_len
;
716 /* Todo : better rounding */
717 record
->length
[0] += (record
->length
[0] & 1) ? 1 : 0;
721 cd9660_setup_root_node(void)
723 cd9660_populate_iso_dir_record(diskStructure
.rootNode
->isoDirRecord
,
724 0, ISO_FLAG_DIRECTORY
, 1, "\0");
728 /*********** SUPPORT FUNCTIONS ***********/
730 cd9660_setup_volume_descriptors(void)
732 /* Boot volume descriptor should come second */
734 /* For now, a fixed 2 : PVD and terminator */
735 volume_descriptor
*temp
, *t
;
738 if ((temp
= malloc(sizeof(volume_descriptor
))) == NULL
) {
739 CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors");
743 temp
->volumeDescriptorData
=
744 (unsigned char *)&diskStructure
.primaryDescriptor
;
745 temp
->volumeDescriptorData
[0] = ISO_VOLUME_DESCRIPTOR_PVD
;
746 temp
->volumeDescriptorData
[6] = 1;
747 temp
->sector
= sector
;
748 memcpy(temp
->volumeDescriptorData
+ 1,
749 ISO_VOLUME_DESCRIPTOR_STANDARD_ID
, 5);
750 diskStructure
.firstVolumeDescriptor
= temp
;
753 /* Set up boot support if enabled. BVD must reside in sector 17 */
754 if (diskStructure
.is_bootable
) {
755 if ((t
= malloc(sizeof(volume_descriptor
))) == NULL
) {
756 CD9660_MEM_ALLOC_ERROR(
757 "cd9660_setup_volume_descriptors");
760 if ((t
->volumeDescriptorData
= malloc(2048)) == NULL
) {
761 CD9660_MEM_ALLOC_ERROR(
762 "cd9660_setup_volume_descriptors");
767 memset(t
->volumeDescriptorData
, 0, 2048);
769 if (diskStructure
.verbose_level
> 0)
770 printf("Setting up boot volume descriptor\n");
771 cd9660_setup_boot_volume_descriptor(t
);
775 /* Set up the terminator */
776 if ((t
= malloc(sizeof(volume_descriptor
))) == NULL
) {
777 CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors");
780 if ((t
->volumeDescriptorData
= malloc(2048)) == NULL
) {
781 CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors");
786 memset(t
->volumeDescriptorData
, 0, 2048);
787 t
->volumeDescriptorData
[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR
;
789 t
->volumeDescriptorData
[6] = 1;
791 memcpy(t
->volumeDescriptorData
+ 1,
792 ISO_VOLUME_DESCRIPTOR_STANDARD_ID
, 5);
800 * Populate EAR at some point. Not required, but is used by NetBSD's
804 cd9660_fill_extended_attribute_record(cd9660node
*node
)
806 if ((node
->isoExtAttributes
=
807 malloc(sizeof(struct iso_extended_attributes
))) == NULL
) {
808 CD9660_MEM_ALLOC_ERROR("cd9660_fill_extended_attribute_record");
817 cd9960_translate_node_common(cd9660node
*newnode
)
822 char temp
[ISO_FILENAME_MAXLENGTH_WITH_PADDING
];
824 /* Now populate the isoDirRecord structure */
825 memset(temp
, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING
);
827 test
= cd9660_convert_filename(newnode
->node
->name
,
828 temp
, !(S_ISDIR(newnode
->node
->type
)));
830 flag
= ISO_FLAG_CLEAR
;
831 if (S_ISDIR(newnode
->node
->type
))
832 flag
|= ISO_FLAG_DIRECTORY
;
834 cd9660_populate_iso_dir_record(newnode
->isoDirRecord
, 0,
835 flag
, strlen(temp
), temp
);
837 /* Set the various dates */
839 /* If we want to use the current date and time */
842 cd9660_time_915(newnode
->isoDirRecord
->date
, tim
);
844 cd9660_bothendian_dword(newnode
->fileDataLength
,
845 newnode
->isoDirRecord
->size
);
846 /* If the file is a link, we want to set the size to 0 */
847 if (S_ISLNK(newnode
->node
->type
))
848 newnode
->fileDataLength
= 0;
854 * Translate fsnode to cd9960node
855 * Translate filenames and other metadata, including dates, sizes,
857 * @param struct fsnode * The node generated by makefs
858 * @param struct cd9660node * The intermediate node to be written to
859 * @returns int 0 on failure, 1 on success
862 cd9660_translate_node(fsnode
*node
, cd9660node
*newnode
)
865 if (diskStructure
.verbose_level
> 0)
866 printf("cd9660_translate_node: NULL node passed, "
870 if ((newnode
->isoDirRecord
=
871 malloc(sizeof(iso_directory_record_cd9660
))) == NULL
) {
872 CD9660_MEM_ALLOC_ERROR("cd9660_translate_node");
876 /* Set the node pointer */
877 newnode
->node
= node
;
880 if (!(S_ISDIR(node
->type
)))
881 newnode
->fileDataLength
= node
->inode
->st
.st_size
;
883 if (cd9960_translate_node_common(newnode
) == 0)
886 /* Finally, overwrite some of the values that are set by default */
887 cd9660_time_915(newnode
->isoDirRecord
->date
, node
->inode
->st
.st_mtime
);
893 * Compares two ISO filenames
894 * @param const char * The first file name
895 * @param const char * The second file name
896 * @returns : -1 if first is less than second, 0 if they are the same, 1 if
897 * the second is greater than the first
900 cd9660_compare_filename(const char *first
, const char *second
)
903 * This can be made more optimal once it has been tested
904 * (the extra character, for example, is for testing)
910 /* First, on the filename */
912 while (p1
< ISO_FILENAME_MAXLENGTH_BEFORE_VERSION
-1
913 && p2
< ISO_FILENAME_MAXLENGTH_BEFORE_VERSION
-1) {
916 if (c1
== '.' && c2
=='.')
918 else if (c1
== '.') {
921 } else if (c2
== '.') {
936 if (first
[p1
] == '.' && second
[p2
] == '.') {
939 while (p1
< ISO_FILENAME_MAXLENGTH_BEFORE_VERSION
- 1
940 && p2
< ISO_FILENAME_MAXLENGTH_BEFORE_VERSION
- 1) {
943 if (c1
== ';' && c2
== ';')
945 else if (c1
== ';') {
948 } else if (c2
== ';') {
966 * Insert a node into list with ISO sorting rules
967 * @param cd9660node * The head node of the list
968 * @param cd9660node * The node to be inserted
971 cd9660_sorted_child_insert(cd9660node
*parent
, cd9660node
*cn_new
)
975 struct cd9660_children_head
*head
= &parent
->cn_children
;
977 /* TODO: Optimize? */
978 cn_new
->parent
= parent
;
981 * first will either be 0, the . or the ..
982 * if . or .., this means no other entry may be written before first
983 * if 0, the new node may be inserted at the head
986 TAILQ_FOREACH(cn
, head
, cn_next_child
) {
988 * Dont insert a node twice -
989 * that would cause an infinite loop
994 compare
= cd9660_compare_filename(cn_new
->isoDirRecord
->name
,
995 cn
->isoDirRecord
->name
);
998 compare
= cd9660_compare_filename(cn_new
->node
->name
,
1005 TAILQ_INSERT_TAIL(head
, cn_new
, cn_next_child
);
1007 TAILQ_INSERT_BEFORE(cn
, cn_new
, cn_next_child
);
1011 * Called After cd9660_sorted_child_insert
1012 * handles file collisions by suffixing each filname with ~n
1013 * where n represents the files respective place in the ordering
1016 cd9660_handle_collisions(cd9660node
*colliding
, int past
)
1018 cd9660node
*iter
, *next
, *prev
;
1020 int delete_chars
= 0;
1021 int temp_past
= past
;
1024 cd9660node
*end_of_range
;
1026 for (iter
= TAILQ_FIRST(&colliding
->cn_children
);
1027 iter
!= NULL
&& (next
= TAILQ_NEXT(iter
, cn_next_child
)) != NULL
;) {
1028 if (strcmp(iter
->isoDirRecord
->name
,
1029 next
->isoDirRecord
->name
) != 0) {
1030 iter
= TAILQ_NEXT(iter
, cn_next_child
);
1034 temp_skip
= skip
= cd9660_count_collisions(iter
);
1035 end_of_range
= iter
;
1036 while (temp_skip
> 0) {
1038 end_of_range
= TAILQ_NEXT(end_of_range
, cn_next_child
);
1041 while (temp_past
> 0) {
1042 if ((next
= TAILQ_NEXT(end_of_range
, cn_next_child
)) != NULL
)
1043 end_of_range
= next
;
1044 else if ((prev
= TAILQ_PREV(iter
, cd9660_children_head
, cn_next_child
)) != NULL
)
1051 iter
= cd9660_rename_filename(iter
, skip
, delete_chars
);
1058 cd9660_rename_filename(cd9660node
*iter
, int num
, int delete_chars
)
1061 int numbts
, dot
, semi
, digit
, digits
, temp
, powers
, multiplier
, count
;
1066 if (diskStructure
.verbose_level
> 0)
1067 printf("Rename_filename called\n");
1069 /* TODO : A LOT of chanes regarding 8.3 filenames */
1070 if (diskStructure
.isoLevel
== 1)
1072 else if (diskStructure
.isoLevel
== 2)
1075 maxlength
= ISO_FILENAME_MAXLENGTH_BEFORE_VERSION
;
1077 tmp
= malloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING
);
1084 while (((int)(i
/ powers
) ) >= 10) {
1086 powers
= powers
* 10;
1089 naming
= iter
->o_name
;
1092 while ((*naming != '.') && (*naming != ';')) {
1100 while (count
< maxlength
) {
1103 else if (*naming
== ';') {
1111 if ((count
+ digits
) < maxlength
)
1114 numbts
= maxlength
- (digits
);
1115 numbts
-= delete_chars
;
1117 /* 8.3 rules - keep the extension, add before the dot */
1120 * This code makes a bunch of assumptions.
1121 * See if you can spot them all :)
1125 if (diskStructure.isoLevel == 1) {
1126 numbts = 8 - digits - delete_chars;
1131 memmove(&tmp[numbts],&tmp[dot],4);
1137 /* (copying just the filename before the '.' */
1138 memcpy(tmp
, (iter
->o_name
), numbts
);
1140 /* adding the appropriate number following the name */
1142 while (digits
> 0) {
1143 digit
= (int)(temp
/ powers
);
1144 temp
= temp
- digit
* powers
;
1145 sprintf(&tmp
[numbts
] , "%d", digit
);
1148 powers
= powers
/ 10;
1151 while ((*naming
!= ';') && (numbts
< maxlength
)) {
1152 tmp
[numbts
] = (*naming
);
1158 tmp
[numbts
+1] = '1';
1159 tmp
[numbts
+2] = '\0';
1162 * now tmp has exactly the identifier
1163 * we want so we'll copy it back to record
1165 memcpy((iter
->isoDirRecord
->name
), tmp
, numbts
+ 3);
1167 iter
= TAILQ_NEXT(iter
, cn_next_child
);
1175 /* Todo: Figure out why these functions are nec. */
1177 cd9660_copy_filenames(cd9660node
*node
)
1181 if (TAILQ_EMPTY(&node
->cn_children
))
1184 if (TAILQ_FIRST(&node
->cn_children
)->isoDirRecord
== NULL
) {
1185 debug_print_tree(diskStructure
.rootNode
, 0);
1189 TAILQ_FOREACH(cn
, &node
->cn_children
, cn_next_child
) {
1190 cd9660_copy_filenames(cn
);
1191 memcpy(cn
->o_name
, cn
->isoDirRecord
->name
,
1192 ISO_FILENAME_MAXLENGTH_WITH_PADDING
);
1197 cd9660_sorting_nodes(cd9660node
*node
)
1201 TAILQ_FOREACH(cn
, &node
->cn_children
, cn_next_child
)
1202 cd9660_sorting_nodes(cn
);
1203 cd9660_sort_nodes(node
);
1206 /* XXX Bubble sort. */
1208 cd9660_sort_nodes(cd9660node
*node
)
1210 cd9660node
*cn
, *next
;
1213 TAILQ_FOREACH(cn
, &node
->cn_children
, cn_next_child
) {
1214 if ((next
= TAILQ_NEXT(cn
, cn_next_child
)) == NULL
)
1216 else if (strcmp(next
->isoDirRecord
->name
,
1217 cn
->isoDirRecord
->name
) >= 0)
1219 TAILQ_REMOVE(&node
->cn_children
, next
, cn_next_child
);
1220 TAILQ_INSERT_BEFORE(cn
, next
, cn_next_child
);
1223 } while (cn
!= NULL
);
1227 cd9660_count_collisions(cd9660node
*copy
)
1230 cd9660node
*iter
, *next
;
1233 (next
= TAILQ_NEXT(iter
, cn_next_child
)) != NULL
;
1235 if (cd9660_compare_filename(iter
->isoDirRecord
->name
,
1236 next
->isoDirRecord
->name
) == 0)
1242 if ((next
= TAILQ_NEXT(iter
, cn_next_child
)) != NULL
) {
1243 printf("cd9660_recurse_on_collision: count is %i \n", count
);
1244 compare
= cd9660_compare_filename(iter
->isoDirRecord
->name
,
1245 next
->isoDirRecord
->name
);
1248 return cd9660_recurse_on_collision(next
, count
);
1257 cd9660_rrip_move_directory(cd9660node
*dir
)
1263 * This function needs to:
1264 * 1) Create an empty virtual file in place of the old directory
1265 * 2) Point the virtual file to the new directory
1266 * 3) Point the relocated directory to its old parent
1267 * 4) Move the directory specified by dir into rr_moved_dir,
1268 * and rename it to "diskStructure.rock_ridge_move_count" (as a string)
1271 /* First see if the moved directory even exists */
1272 if (diskStructure
.rr_moved_dir
== NULL
) {
1273 diskStructure
.rr_moved_dir
=
1274 cd9660_create_directory(ISO_RRIP_DEFAULT_MOVE_DIR_NAME
,
1275 diskStructure
.rootNode
, dir
);
1276 if (diskStructure
.rr_moved_dir
== NULL
)
1280 /* Create a file with the same ORIGINAL name */
1281 tfile
= cd9660_create_file(dir
->node
->name
, dir
->parent
, dir
);
1285 diskStructure
.rock_ridge_move_count
++;
1286 snprintf(newname
, sizeof(newname
), "%08i",
1287 diskStructure
.rock_ridge_move_count
);
1289 /* Point to old parent */
1290 dir
->rr_real_parent
= dir
->parent
;
1292 /* Place the placeholder file */
1293 if (TAILQ_EMPTY(&dir
->rr_real_parent
->cn_children
)) {
1294 TAILQ_INSERT_HEAD(&dir
->rr_real_parent
->cn_children
, tfile
,
1297 cd9660_sorted_child_insert(dir
->rr_real_parent
, tfile
);
1300 /* Point to new parent */
1301 dir
->parent
= diskStructure
.rr_moved_dir
;
1303 /* Point the file to the moved directory */
1304 tfile
->rr_relocated
= dir
;
1306 /* Actually move the directory */
1307 cd9660_sorted_child_insert(diskStructure
.rr_moved_dir
, dir
);
1309 /* TODO: Inherit permissions / ownership (basically the entire inode) */
1311 /* Set the new name */
1312 memset(dir
->isoDirRecord
->name
, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING
);
1313 strncpy(dir
->isoDirRecord
->name
, newname
, 8);
1319 cd9660_add_dot_records(cd9660node
*root
)
1321 struct cd9660_children_head
*head
= &root
->cn_children
;
1324 TAILQ_FOREACH(cn
, head
, cn_next_child
) {
1325 if ((cn
->type
& CD9660_TYPE_DIR
) == 0)
1327 /* Recursion first */
1328 cd9660_add_dot_records(cn
);
1330 cd9660_create_special_directory(CD9660_TYPE_DOT
, root
);
1331 cd9660_create_special_directory(CD9660_TYPE_DOTDOT
, root
);
1336 * Convert node to cd9660 structure
1337 * This function is designed to be called recursively on the root node of
1339 * Lots of recursion going on here, want to make sure it is efficient
1340 * @param struct fsnode * The root node to be converted
1341 * @param struct cd9660* The parent node (should not be NULL)
1342 * @param int Current directory depth
1343 * @param int* Running count of the number of directories that are being created
1346 cd9660_convert_structure(fsnode
*root
, cd9660node
*parent_node
, int level
,
1347 int *numDirectories
, int *error
)
1349 fsnode
*iterator
= root
;
1350 cd9660node
*this_node
;
1357 * Newer, more efficient method, reduces recursion depth
1360 warnx("%s: root is null\n", __func__
);
1364 /* Test for an empty directory - makefs still gives us the . record */
1365 if ((S_ISDIR(root
->type
)) && (root
->name
[0] == '.')
1366 && (root
->name
[1] == '\0')) {
1371 if ((this_node
= cd9660_allocate_cd9660node()) == NULL
) {
1372 CD9660_MEM_ALLOC_ERROR(__func__
);
1376 * To reduce the number of recursive calls, we will iterate over
1377 * the next pointers to the right.
1379 while (iterator
!= NULL
) {
1382 * Increment the directory count if this is a directory
1383 * Ignore "." entries. We will generate them later
1385 if (!S_ISDIR(iterator
->type
) ||
1386 strcmp(iterator
->name
, ".") != 0) {
1388 /* Translate the node, including its filename */
1389 this_node
->parent
= parent_node
;
1390 cd9660_translate_node(iterator
, this_node
);
1391 this_node
->level
= level
;
1393 if (S_ISDIR(iterator
->type
)) {
1394 (*numDirectories
)++;
1395 this_node
->type
= CD9660_TYPE_DIR
;
1396 working_level
= level
+ 1;
1399 * If at level 8, directory would be at 8
1400 * and have children at 9 which is not
1401 * allowed as per ISO spec
1404 if ((!diskStructure
.allow_deep_trees
) &&
1405 (!diskStructure
.rock_ridge_enabled
)) {
1406 warnx("error: found entry "
1407 "with depth greater "
1411 } else if (diskStructure
.
1412 rock_ridge_enabled
) {
1415 * Moved directory is actually
1420 if (cd9660_rrip_move_directory(
1433 /* Do the recursive call on the children */
1434 if (iterator
->child
!= 0) {
1435 cd9660_convert_structure(
1436 iterator
->child
, this_node
,
1438 numDirectories
, error
);
1440 if ((*error
) == 1) {
1441 warnx("%s: Error on recursive "
1448 /* Only directories should have children */
1449 assert(iterator
->child
== NULL
);
1451 this_node
->type
= CD9660_TYPE_FILE
;
1455 * Finally, do a sorted insert
1458 cd9660_sorted_child_insert(
1459 parent_node
, this_node
);
1462 /*Allocate new temp_node */
1463 if (iterator
->next
!= 0) {
1464 this_node
= cd9660_allocate_cd9660node();
1465 if (this_node
== NULL
)
1466 CD9660_MEM_ALLOC_ERROR(__func__
);
1469 iterator
= iterator
->next
;
1472 /* cd9660_handle_collisions(first_node); */
1474 /* TODO: need cleanup */
1475 cd9660_copy_filenames(parent_node
);
1478 flag
= cd9660_handle_collisions(parent_node
, counter
);
1480 cd9660_sorting_nodes(parent_node
);
1481 } while ((flag
== 1) && (counter
< 100));
1485 * Clean up the cd9660node tree
1486 * This is designed to be called recursively on the root node
1487 * @param struct cd9660node *root The node to free
1491 cd9660_free_structure(cd9660node
*root
)
1495 while ((cn
= TAILQ_FIRST(&root
->cn_children
)) != NULL
) {
1496 TAILQ_REMOVE(&root
->cn_children
, cn
, cn_next_child
);
1497 cd9660_free_structure(cn
);
1503 * Be a little more memory conservative:
1504 * instead of having the TAILQ_ENTRY as part of the cd9660node,
1505 * just create a temporary structure
1509 TAILQ_ENTRY(ptq_entry
) ptq
;
1513 #define PTQUEUE_NEW(n,s,r,t){\
1514 n = malloc(sizeof(struct s)); \
1521 * Generate the path tables
1522 * The specific implementation of this function is left as an exercise to the
1523 * programmer. It could be done recursively. Make sure you read how the path
1524 * table has to be laid out, it has levels.
1525 * @param struct iso9660_disk *disk The disk image
1526 * @returns int The number of built path tables (between 1 and 4), 0 on failure
1529 cd9660_generate_path_table(void)
1531 cd9660node
*cn
, *dirNode
= diskStructure
.rootNode
;
1532 cd9660node
*last
= dirNode
;
1533 int pathTableSize
= 0; /* computed as we go */
1534 int counter
= 1; /* root gets a count of 0 */
1535 int parentRecNum
= 0; /* root's parent is '0' */
1537 TAILQ_HEAD(cd9660_pt_head
, ptq_entry
) pt_head
;
1538 TAILQ_INIT(&pt_head
);
1540 PTQUEUE_NEW(n
, ptq_entry
, -1, diskStructure
.rootNode
);
1542 /* Push the root node */
1543 TAILQ_INSERT_HEAD(&pt_head
, n
, ptq
);
1545 /* Breadth-first traversal of file structure */
1546 while (pt_head
.tqh_first
!= 0) {
1547 n
= pt_head
.tqh_first
;
1549 TAILQ_REMOVE(&pt_head
, pt_head
.tqh_first
, ptq
);
1552 /* Update the size */
1553 pathTableSize
+= ISO_PATHTABLE_ENTRY_BASESIZE
1554 + dirNode
->isoDirRecord
->name_len
[0]+
1555 (dirNode
->isoDirRecord
->name_len
[0] % 2 == 0 ? 0 : 1);
1556 /* includes the padding bit */
1558 dirNode
->ptnumber
=counter
;
1559 if (dirNode
!= last
) {
1560 last
->ptnext
= dirNode
;
1561 dirNode
->ptprev
= last
;
1566 if (dirNode
->parent
!= 0)
1567 parentRecNum
= dirNode
->parent
->ptnumber
;
1569 /* Push children onto queue */
1570 TAILQ_FOREACH(cn
, &dirNode
->cn_children
, cn_next_child
) {
1572 * Dont add the DOT and DOTDOT types to the path
1575 if ((cn
->type
!= CD9660_TYPE_DOT
)
1576 && (cn
->type
!= CD9660_TYPE_DOTDOT
)) {
1578 if (S_ISDIR(cn
->node
->type
)) {
1579 PTQUEUE_NEW(n
, ptq_entry
, -1, cn
);
1580 TAILQ_INSERT_TAIL(&pt_head
, n
, ptq
);
1586 return pathTableSize
;
1590 cd9660_compute_full_filename(cd9660node
*node
, char *buf
, int level
)
1594 parent
= (node
->rr_real_parent
== NULL
?
1595 node
->parent
: node
->rr_real_parent
);
1596 if (parent
!= NULL
) {
1597 cd9660_compute_full_filename(parent
, buf
, level
+ 1);
1598 strcat(buf
, node
->node
->name
);
1600 /* We are at the root */
1601 strcat(buf
, diskStructure
.rootFilesystemPath
);
1602 if (buf
[strlen(buf
) - 1] == '/')
1603 buf
[strlen(buf
) - 1] = '\0';
1610 /* NEW filename conversion method */
1611 typedef int(*cd9660_filename_conversion_functor
)(const char *, char *, int);
1615 * TODO: These two functions are almost identical.
1616 * Some code cleanup is possible here
1618 * XXX bounds checking!
1621 cd9660_level1_convert_filename(const char *oldname
, char *newname
, int is_file
)
1625 * File Name shall not contain more than 8 d or d1 characters
1626 * File Name Extension shall not contain more than 3 d or d1 characters
1627 * Directory Identifier shall not contain more than 8 d or d1 characters
1633 while (*oldname
!= '\0') {
1634 /* Handle period first, as it is special */
1635 if (*oldname
== '.') {
1645 /* cut RISC OS file type off ISO name */
1646 if (diskStructure
.archimedes_enabled
&&
1647 *oldname
== ',' && strlen(oldname
) == 4)
1649 /* Enforce 12.3 / 8 */
1650 if (((namelen
== 8) && !found_ext
) ||
1651 (found_ext
&& extlen
== 3)) {
1655 if (islower((unsigned char)*oldname
))
1656 *newname
++ = toupper((unsigned char)*oldname
);
1657 else if (isupper((unsigned char)*oldname
)
1658 || isdigit((unsigned char)*oldname
))
1659 *newname
++ = *oldname
;
1671 if (!found_ext
&& !diskStructure
.omit_trailing_period
)
1674 sprintf(newname
, ";%i", 1);
1676 return namelen
+ extlen
+ found_ext
;
1679 /* XXX bounds checking! */
1681 cd9660_level2_convert_filename(const char *oldname
, char *newname
, int is_file
)
1685 * File name : 0+ d or d1 characters
1687 * File name extension : 0+ d or d1 characters
1689 * File version number (5 characters, 1-32767)
1690 * 1 <= Sum of File name and File name extension <= 30
1696 while (*oldname
!= '\0') {
1697 /* Handle period first, as it is special */
1698 if (*oldname
== '.') {
1700 if (diskStructure
.allow_multidot
) {
1712 /* cut RISC OS file type off ISO name */
1713 if (diskStructure
.archimedes_enabled
&&
1714 *oldname
== ',' && strlen(oldname
) == 4)
1716 if ((namelen
+ extlen
) == 30)
1719 if (islower((unsigned char)*oldname
))
1720 *newname
++ = toupper((unsigned char)*oldname
);
1721 else if (isupper((unsigned char)*oldname
) ||
1722 isdigit((unsigned char)*oldname
))
1723 *newname
++ = *oldname
;
1724 else if (diskStructure
.allow_multidot
&&
1739 if (!found_ext
&& !diskStructure
.omit_trailing_period
)
1742 sprintf(newname
, ";%i", 1);
1744 return namelen
+ extlen
+ found_ext
;
1749 cd9660_joliet_convert_filename(const char *oldname
, char *newname
, int is_file
)
1751 /* TODO: implement later, move to cd9660_joliet.c ?? */
1757 * Convert a file name to ISO compliant file name
1758 * @param char * oldname The original filename
1759 * @param char ** newname The new file name, in the appropriate character
1760 * set and of appropriate length
1761 * @param int 1 if file, 0 if directory
1762 * @returns int The length of the new string
1765 cd9660_convert_filename(const char *oldname
, char *newname
, int is_file
)
1768 cd9660_filename_conversion_functor conversion_function
= 0;
1769 if (diskStructure
.isoLevel
== 1)
1770 conversion_function
= &cd9660_level1_convert_filename
;
1771 else if (diskStructure
.isoLevel
== 2)
1772 conversion_function
= &cd9660_level2_convert_filename
;
1773 return (*conversion_function
)(oldname
, newname
, is_file
);
1777 cd9660_compute_record_size(cd9660node
*node
)
1779 int size
= node
->isoDirRecord
->length
[0];
1781 if (diskStructure
.rock_ridge_enabled
)
1782 size
+= node
->susp_entry_size
;
1783 size
+= node
->su_tail_size
;
1784 size
+= size
& 1; /* Ensure length of record is even. */
1785 assert(size
<= 254);
1790 cd9660_populate_dot_records(cd9660node
*node
)
1792 node
->dot_record
->fileDataSector
= node
->fileDataSector
;
1793 memcpy(node
->dot_record
->isoDirRecord
,node
->isoDirRecord
, 34);
1794 node
->dot_record
->isoDirRecord
->name_len
[0] = 1;
1795 node
->dot_record
->isoDirRecord
->name
[0] = 0;
1796 node
->dot_record
->isoDirRecord
->name
[1] = 0;
1797 node
->dot_record
->isoDirRecord
->length
[0] = 34;
1798 node
->dot_record
->fileRecordSize
=
1799 cd9660_compute_record_size(node
->dot_record
);
1801 if (node
== diskStructure
.rootNode
) {
1802 node
->dot_dot_record
->fileDataSector
= node
->fileDataSector
;
1803 memcpy(node
->dot_dot_record
->isoDirRecord
,node
->isoDirRecord
,
1806 node
->dot_dot_record
->fileDataSector
=
1807 node
->parent
->fileDataSector
;
1808 memcpy(node
->dot_dot_record
->isoDirRecord
,
1809 node
->parent
->isoDirRecord
,34);
1811 node
->dot_dot_record
->isoDirRecord
->name_len
[0] = 1;
1812 node
->dot_dot_record
->isoDirRecord
->name
[0] = 1;
1813 node
->dot_dot_record
->isoDirRecord
->name
[1] = 0;
1814 node
->dot_dot_record
->isoDirRecord
->length
[0] = 34;
1815 node
->dot_dot_record
->fileRecordSize
=
1816 cd9660_compute_record_size(node
->dot_dot_record
);
1820 * @param struct cd9660node *node The node
1821 * @param int The offset (in bytes) - SHOULD align to the beginning of a sector
1822 * @returns int The total size of files and directory entries (should be
1823 * a multiple of sector size)
1826 cd9660_compute_offsets(cd9660node
*node
, int startOffset
)
1829 * This function needs to compute the size of directory records and
1830 * runs, file lengths, and set the appropriate variables both in
1831 * cd9660node and isoDirEntry
1834 int current_sector_usage
= 0;
1839 assert(node
!= NULL
);
1843 * NOTE : There needs to be some special case detection for
1844 * the "real root" node, since for it, node->node is undefined
1847 node
->fileDataSector
= -1;
1849 if (node
->type
& CD9660_TYPE_DIR
) {
1850 node
->fileRecordSize
= cd9660_compute_record_size(node
);
1851 /*Set what sector this directory starts in*/
1852 node
->fileDataSector
=
1853 CD9660_BLOCKS(diskStructure
.sectorSize
,startOffset
);
1855 cd9660_bothendian_dword(node
->fileDataSector
,
1856 node
->isoDirRecord
->extent
);
1859 * First loop over children, need to know the size of
1860 * their directory records
1862 node
->fileSectorsUsed
= 1;
1863 TAILQ_FOREACH(child
, &node
->cn_children
, cn_next_child
) {
1864 node
->fileDataLength
+=
1865 cd9660_compute_record_size(child
);
1866 if ((cd9660_compute_record_size(child
) +
1867 current_sector_usage
) >=
1868 diskStructure
.sectorSize
) {
1869 current_sector_usage
= 0;
1870 node
->fileSectorsUsed
++;
1873 current_sector_usage
+=
1874 cd9660_compute_record_size(child
);
1877 cd9660_bothendian_dword(node
->fileSectorsUsed
*
1878 diskStructure
.sectorSize
,node
->isoDirRecord
->size
);
1881 * This should point to the sector after the directory
1882 * record (or, the first byte in that sector)
1884 used_bytes
+= node
->fileSectorsUsed
* diskStructure
.sectorSize
;
1886 for (child
= TAILQ_NEXT(node
->dot_dot_record
, cn_next_child
);
1887 child
!= NULL
; child
= TAILQ_NEXT(child
, cn_next_child
)) {
1888 /* Directories need recursive call */
1889 if (S_ISDIR(child
->node
->type
)) {
1890 r
= cd9660_compute_offsets(child
,
1891 used_bytes
+ startOffset
);
1900 /* Explicitly set the . and .. records */
1901 cd9660_populate_dot_records(node
);
1903 /* Finally, do another iteration to write the file data*/
1904 for (child
= TAILQ_NEXT(node
->dot_dot_record
, cn_next_child
);
1906 child
= TAILQ_NEXT(child
, cn_next_child
)) {
1907 /* Files need extent set */
1908 if (S_ISDIR(child
->node
->type
))
1910 child
->fileRecordSize
=
1911 cd9660_compute_record_size(child
);
1913 child
->fileSectorsUsed
=
1914 CD9660_BLOCKS(diskStructure
.sectorSize
,
1915 child
->fileDataLength
);
1917 inode
= child
->node
->inode
;
1918 if ((inode
->flags
& FI_ALLOCATED
) == 0) {
1920 CD9660_BLOCKS(diskStructure
.sectorSize
,
1921 used_bytes
+ startOffset
);
1922 inode
->flags
|= FI_ALLOCATED
;
1923 used_bytes
+= child
->fileSectorsUsed
*
1924 diskStructure
.sectorSize
;
1926 INODE_WARNX(("%s: already allocated inode %d "
1927 "data sectors at %" PRIu32
, __func__
,
1928 (int)inode
->st
.st_ino
, inode
->ino
));
1930 child
->fileDataSector
= inode
->ino
;
1931 cd9660_bothendian_dword(child
->fileDataSector
,
1932 child
->isoDirRecord
->extent
);
1940 /* Might get rid of this func */
1942 cd9660_copy_stat_info(cd9660node
*from
, cd9660node
*to
, int file
)
1944 to
->node
->inode
->st
.st_dev
= 0;
1945 to
->node
->inode
->st
.st_ino
= 0;
1946 to
->node
->inode
->st
.st_size
= 0;
1947 to
->node
->inode
->st
.st_blksize
= from
->node
->inode
->st
.st_blksize
;
1948 to
->node
->inode
->st
.st_atime
= from
->node
->inode
->st
.st_atime
;
1949 to
->node
->inode
->st
.st_mtime
= from
->node
->inode
->st
.st_mtime
;
1950 to
->node
->inode
->st
.st_ctime
= from
->node
->inode
->st
.st_ctime
;
1951 to
->node
->inode
->st
.st_uid
= from
->node
->inode
->st
.st_uid
;
1952 to
->node
->inode
->st
.st_gid
= from
->node
->inode
->st
.st_gid
;
1953 to
->node
->inode
->st
.st_mode
= from
->node
->inode
->st
.st_mode
;
1954 /* Clear out type */
1955 to
->node
->inode
->st
.st_mode
= to
->node
->inode
->st
.st_mode
& ~(S_IFMT
);
1957 to
->node
->inode
->st
.st_mode
|= S_IFREG
;
1959 to
->node
->inode
->st
.st_mode
|= S_IFDIR
;
1965 cd9660_create_virtual_entry(const char *name
, cd9660node
*parent
, int file
,
1971 assert(parent
!= NULL
);
1973 temp
= cd9660_allocate_cd9660node();
1977 if ((tfsnode
= malloc(sizeof(fsnode
))) == NULL
) {
1978 CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry");
1982 /* Assume for now name is a valid length */
1983 if ((tfsnode
->name
= malloc(strlen(name
) + 1)) == NULL
) {
1984 CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry");
1988 if ((temp
->isoDirRecord
=
1989 malloc(sizeof(iso_directory_record_cd9660
))) == NULL
) {
1990 CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry");
1994 strcpy(tfsnode
->name
, name
);
1996 cd9660_convert_filename(tfsnode
->name
, temp
->isoDirRecord
->name
, file
);
1998 temp
->node
= tfsnode
;
1999 temp
->parent
= parent
;
2002 if (temp
->parent
!= NULL
) {
2003 temp
->level
= temp
->parent
->level
+ 1;
2004 if (!TAILQ_EMPTY(&temp
->parent
->cn_children
))
2005 cd9660_sorted_child_insert(temp
->parent
, temp
);
2007 TAILQ_INSERT_HEAD(&temp
->parent
->cn_children
,
2008 temp
, cn_next_child
);
2012 if (parent
->node
!= NULL
) {
2013 tfsnode
->type
= parent
->node
->type
;
2016 /* Clear out file type bits */
2017 tfsnode
->type
&= ~(S_IFMT
);
2019 tfsnode
->type
|= S_IFREG
;
2021 tfsnode
->type
|= S_IFDIR
;
2023 /* Indicate that there is no spec entry (inode) */
2024 tfsnode
->flags
&= ~(FSNODE_F_HASSPEC
);
2026 cd9660_copy_stat_info(parent
, temp
, file
);
2032 cd9660_create_file(const char * name
, cd9660node
*parent
, cd9660node
*me
)
2036 temp
= cd9660_create_virtual_entry(name
,parent
,1,1);
2040 temp
->fileDataLength
= 0;
2042 temp
->type
= CD9660_TYPE_FILE
| CD9660_TYPE_VIRTUAL
;
2044 if ((temp
->node
->inode
= calloc(1, sizeof(fsinode
))) == NULL
)
2046 *temp
->node
->inode
= *me
->node
->inode
;
2048 if (cd9960_translate_node_common(temp
) == 0)
2054 * Create a new directory which does not exist on disk
2055 * @param const char * name The name to assign to the directory
2056 * @param const char * parent Pointer to the parent directory
2057 * @returns cd9660node * Pointer to the new directory
2060 cd9660_create_directory(const char *name
, cd9660node
*parent
, cd9660node
*me
)
2064 temp
= cd9660_create_virtual_entry(name
,parent
,0,1);
2067 temp
->node
->type
|= S_IFDIR
;
2069 temp
->type
= CD9660_TYPE_DIR
| CD9660_TYPE_VIRTUAL
;
2071 if ((temp
->node
->inode
= calloc(1, sizeof(fsinode
))) == NULL
)
2073 *temp
->node
->inode
= *me
->node
->inode
;
2075 if (cd9960_translate_node_common(temp
) == 0)
2081 cd9660_create_special_directory(u_char type
, cd9660node
*parent
)
2083 cd9660node
*temp
, *first
;
2086 assert(parent
!= NULL
);
2088 if (type
== CD9660_TYPE_DOT
)
2090 else if (type
== CD9660_TYPE_DOTDOT
)
2096 if ((temp
= cd9660_create_virtual_entry(na
, parent
, 0, 0)) == NULL
)
2099 temp
->parent
= parent
;
2101 temp
->isoDirRecord
->length
[0] = 34;
2102 /* Dot record is always first */
2103 if (type
== CD9660_TYPE_DOT
) {
2104 parent
->dot_record
= temp
;
2105 TAILQ_INSERT_HEAD(&parent
->cn_children
, temp
, cn_next_child
);
2106 /* DotDot should be second */
2107 } else if (type
== CD9660_TYPE_DOTDOT
) {
2108 parent
->dot_dot_record
= temp
;
2110 * If the first child is the dot record, insert
2111 * this second. Otherwise, insert it at the head.
2113 if ((first
= TAILQ_FIRST(&parent
->cn_children
)) == NULL
||
2114 (first
->type
& CD9660_TYPE_DOT
) == 0) {
2115 TAILQ_INSERT_HEAD(&parent
->cn_children
, temp
,
2118 TAILQ_INSERT_AFTER(&parent
->cn_children
, first
, temp
,
2127 cd9660_add_generic_bootimage(const char *bootimage
)
2131 assert(bootimage
!= NULL
);
2133 if (*bootimage
== '\0') {
2134 warnx("Error: Boot image must be a filename");
2138 if ((diskStructure
.generic_bootimage
= strdup(bootimage
)) == NULL
) {
2139 warn("%s: strdup", __func__
);
2143 /* Get information about the file */
2144 if (lstat(diskStructure
.generic_bootimage
, &stbuf
) == -1)
2145 err(EXIT_FAILURE
, "%s: lstat(\"%s\")", __func__
,
2146 diskStructure
.generic_bootimage
);
2148 if (stbuf
.st_size
> 32768) {
2149 warnx("Error: Boot image must be no greater than 32768 bytes");
2153 if (diskStructure
.verbose_level
> 0) {
2154 printf("Generic boot image image has size %lld\n",
2155 (long long)stbuf
.st_size
);
2158 diskStructure
.has_generic_bootimage
= 1;