diff options
Diffstat (limited to 'usr.sbin/makefs/cd9660')
| -rw-r--r-- | usr.sbin/makefs/cd9660/Makefile.inc | 6 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/cd9660_conversion.c | 200 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/cd9660_debug.c | 454 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/cd9660_eltorito.c | 751 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/cd9660_eltorito.h | 166 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/cd9660_strings.c | 120 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/cd9660_write.c | 510 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/iso9660_rrip.c | 892 | ||||
| -rw-r--r-- | usr.sbin/makefs/cd9660/iso9660_rrip.h | 292 |
9 files changed, 3391 insertions, 0 deletions
diff --git a/usr.sbin/makefs/cd9660/Makefile.inc b/usr.sbin/makefs/cd9660/Makefile.inc new file mode 100644 index 000000000000..ec949f1413dc --- /dev/null +++ b/usr.sbin/makefs/cd9660/Makefile.inc @@ -0,0 +1,6 @@ +.PATH: ${SRCDIR}/cd9660 + +CFLAGS+=-I${SRCTOP}/sys/fs/cd9660/ + +SRCS+= cd9660_strings.c cd9660_debug.c cd9660_eltorito.c \ + cd9660_write.c cd9660_conversion.c iso9660_rrip.c diff --git a/usr.sbin/makefs/cd9660/cd9660_conversion.c b/usr.sbin/makefs/cd9660/cd9660_conversion.c new file mode 100644 index 000000000000..4ccc87e22661 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_conversion.c @@ -0,0 +1,200 @@ +/* $NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#include "cd9660.h" + +#include <sys/cdefs.h> +static char cd9660_compute_gm_offset(time_t); + +#if 0 +static inline int +cd9660_pad_even(length) +int length; +{ + return length + (length & 0x01); +} +#endif + +/* +* These can probably be implemented using a macro +*/ + +/* Little endian */ +void +cd9660_721(uint16_t w, unsigned char *twochar) +{ +#if BYTE_ORDER == BIG_ENDIAN + w = bswap16(w); +#endif + memcpy(twochar,&w,2); +} + +void +cd9660_731(uint32_t w, unsigned char *fourchar) +{ +#if BYTE_ORDER == BIG_ENDIAN + w = bswap32(w); +#endif + memcpy(fourchar,&w,4); +} + +/* Big endian */ +void +cd9660_722(uint16_t w, unsigned char *twochar) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + w = bswap16(w); +#endif + memcpy(twochar,&w,2); +} + +void +cd9660_732(uint32_t w, unsigned char *fourchar) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + w = bswap32(w); +#endif + memcpy(fourchar,&w,4); +} + +/** +* Convert a dword into a double endian string of eight characters +* @param int The double word to convert +* @param char* The string to write the both endian double word to - It is assumed this is allocated and at least +* eight characters long +*/ +void +cd9660_bothendian_dword(uint32_t dw, unsigned char *eightchar) +{ + uint32_t le, be; +#if BYTE_ORDER == LITTLE_ENDIAN + le = dw; + be = bswap32(dw); +#endif +#if BYTE_ORDER == BIG_ENDIAN + be = dw; + le = bswap32(dw); +#endif + memcpy(eightchar, &le, 4); + memcpy((eightchar+4), &be, 4); +} + +/** +* Convert a word into a double endian string of four characters +* @param int The word to convert +* @param char* The string to write the both endian word to - It is assumed this is allocated and at least +* four characters long +*/ +void +cd9660_bothendian_word(uint16_t dw, unsigned char *fourchar) +{ + uint16_t le, be; +#if BYTE_ORDER == LITTLE_ENDIAN + le = dw; + be = bswap16(dw); +#endif +#if BYTE_ORDER == BIG_ENDIAN + be = dw; + le = bswap16(dw); +#endif + memcpy(fourchar, &le, 2); + memcpy((fourchar+2), &be, 2); +} + +void +cd9660_pad_string_spaces(char *str, int len) +{ + int i; + + for (i = 0; i < len; i ++) { + if (str[i] == '\0') + str[i] = 0x20; + } +} + +static char +cd9660_compute_gm_offset(time_t tim) +{ + struct tm t, gm; + + (void)localtime_r(&tim, &t); + (void)gmtime_r(&tim, &gm); + gm.tm_year -= t.tm_year; + gm.tm_yday -= t.tm_yday; + gm.tm_hour -= t.tm_hour; + gm.tm_min -= t.tm_min; + if (gm.tm_year < 0) + gm.tm_yday = -1; + else if (gm.tm_year > 0) + gm.tm_yday = 1; + + return (char)(-(gm.tm_min + 60* (24 * gm.tm_yday + gm.tm_hour)) / 15); +} + +/* Long dates: 17 characters */ +void +cd9660_time_8426(unsigned char *buf, time_t tim) +{ + struct tm t; + char temp[18]; + + (void)localtime_r(&tim, &t); + (void)snprintf(temp, sizeof(temp), "%04i%02i%02i%02i%02i%02i%02i", + 1900+(int)t.tm_year, + (int)t.tm_mon+1, + (int)t.tm_mday, + (int)t.tm_hour, + (int)t.tm_min, + (int)t.tm_sec, + 0); + (void)memcpy(buf, temp, 16); + buf[16] = cd9660_compute_gm_offset(tim); +} + +/* Short dates: 7 characters */ +void +cd9660_time_915(unsigned char *buf, time_t tim) +{ + struct tm t; + + (void)localtime_r(&tim, &t); + buf[0] = t.tm_year; + buf[1] = t.tm_mon+1; + buf[2] = t.tm_mday; + buf[3] = t.tm_hour; + buf[4] = t.tm_min; + buf[5] = t.tm_sec; + buf[6] = cd9660_compute_gm_offset(tim); +} diff --git a/usr.sbin/makefs/cd9660/cd9660_debug.c b/usr.sbin/makefs/cd9660/cd9660_debug.c new file mode 100644 index 000000000000..f49c5108f4c2 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_debug.c @@ -0,0 +1,454 @@ +/* $NetBSD: cd9660_debug.c,v 1.11 2010/10/27 18:51:35 christos Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include <sys/param.h> + +#include <sys/mount.h> + +#include "makefs.h" +#include "cd9660.h" +#include "iso9660_rrip.h" + +static void debug_print_susp_attrs(cd9660node *, int); +static void debug_dump_to_xml_padded_hex_output(const char *, const char *, + int); + +static inline void +print_n_tabs(int n) +{ + int i; + + for (i = 1; i <= n; i ++) + printf("\t"); +} + +#if 0 +void +debug_print_rrip_info(cd9660node *n) +{ + struct ISO_SUSP_ATTRIBUTES *t; + TAILQ_FOREACH(t, &node->head, rr_ll) { + + } +} +#endif + +static void +debug_print_susp_attrs(cd9660node *n, int indent) +{ + struct ISO_SUSP_ATTRIBUTES *t; + + TAILQ_FOREACH(t, &n->head, rr_ll) { + print_n_tabs(indent); + printf("-"); + printf("%c%c: L:%i",t->attr.su_entry.SP.h.type[0], + t->attr.su_entry.SP.h.type[1], + (int)t->attr.su_entry.SP.h.length[0]); + printf("\n"); + } +} + +void +debug_print_tree(iso9660_disk *diskStructure, cd9660node *node, int level) +{ +#if !HAVE_NBTOOL_CONFIG_H + cd9660node *cn; + + print_n_tabs(level); + if (node->type & CD9660_TYPE_DOT) { + printf(". (%i)\n", + isonum_733(node->isoDirRecord->extent)); + } else if (node->type & CD9660_TYPE_DOTDOT) { + printf("..(%i)\n", + isonum_733(node->isoDirRecord->extent)); + } else if (node->isoDirRecord->name[0]=='\0') { + printf("(ROOT) (%" PRIu32 " to %" PRId64 ")\n", + node->fileDataSector, + node->fileDataSector + + node->fileSectorsUsed - 1); + } else { + printf("%s (%s) (%" PRIu32 " to %" PRId64 ")\n", + node->isoDirRecord->name, + (node->isoDirRecord->flags[0] + & ISO_FLAG_DIRECTORY) ? "DIR" : "FILE", + node->fileDataSector, + (node->fileSectorsUsed == 0) ? + node->fileDataSector : + node->fileDataSector + + node->fileSectorsUsed - 1); + } + if (diskStructure->rock_ridge_enabled) + debug_print_susp_attrs(node, level + 1); + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) + debug_print_tree(diskStructure, cn, level + 1); +#else + printf("Sorry, debugging is not supported in host-tools mode.\n"); +#endif +} + +void +debug_print_path_tree(cd9660node *n) +{ + cd9660node *iterator = n; + + /* Only display this message when called with the root node */ + if (n->parent == NULL) + printf("debug_print_path_table: Dumping path table contents\n"); + + while (iterator != NULL) { + if (iterator->isoDirRecord->name[0] == '\0') + printf("0) (ROOT)\n"); + else + printf("%i) %s\n", iterator->level, + iterator->isoDirRecord->name); + + iterator = iterator->ptnext; + } +} + +void +debug_print_volume_descriptor_information(iso9660_disk *diskStructure) +{ + volume_descriptor *tmp = diskStructure->firstVolumeDescriptor; + char temp[CD9660_SECTOR_SIZE]; + + printf("==Listing Volume Descriptors==\n"); + + while (tmp != NULL) { + memset(temp, 0, CD9660_SECTOR_SIZE); + memcpy(temp, tmp->volumeDescriptorData + 1, 5); + printf("Volume descriptor in sector %" PRId64 + ": type %i, ID %s\n", + tmp->sector, tmp->volumeDescriptorData[0], temp); + switch(tmp->volumeDescriptorData[0]) { + case 0:/*boot record*/ + break; + + case 1: /* PVD */ + break; + + case 2: /* SVD */ + break; + + case 3: /* Volume Partition Descriptor */ + break; + + case 255: /* terminator */ + break; + } + tmp = tmp->next; + } + + printf("==Done Listing Volume Descriptors==\n"); +} + +void +debug_dump_to_xml_ptentry(path_table_entry *pttemp, int num, int mode) +{ + printf("<ptentry num=\"%i\">\n" ,num); + printf("<length>%i</length>\n", pttemp->length[0]); + printf("<extended_attribute_length>%i</extended_attribute_length>\n", + pttemp->extended_attribute_length[0]); + printf("<parent_number>%i</parent_number>\n", + debug_get_encoded_number(pttemp->parent_number,mode)); + debug_dump_to_xml_padded_hex_output("name", + pttemp->name, pttemp->length[0]); + printf("</ptentry>\n"); +} + +void +debug_dump_to_xml_path_table(FILE *fd, off_t sector, int size, int mode) +{ + path_table_entry pttemp; + int t = 0; + int n = 0; + + if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1) + err(1, "fseeko"); + + while (t < size) { + /* Read fixed data first */ + fread(&pttemp, 1, 8, fd); + t += 8; + /* Read variable */ + fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd); + t += pttemp.length[0]; + debug_dump_to_xml_ptentry(&pttemp, n, mode); + n++; + } + +} + +/* + * XML Debug output functions + * Dump hierarchy of CD, as well as volume info, to XML + * Can be used later to diff against a standard, + * or just provide easy to read detailed debug output + */ +void +debug_dump_to_xml(FILE *fd) +{ + unsigned char buf[CD9660_SECTOR_SIZE]; + off_t sector; + int t, t2; + struct iso_primary_descriptor primaryVD; + struct _boot_volume_descriptor bootVD; + + printf("<cd9660dump>\n"); + + /* Display Volume Descriptors */ + sector = 16; + do { + if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1) + err(1, "fseeko"); + fread(buf, 1, CD9660_SECTOR_SIZE, fd); + t = (int)((unsigned char)buf[0]); + switch (t) { + case 0: + memcpy(&bootVD, buf, CD9660_SECTOR_SIZE); + break; + case 1: + memcpy(&primaryVD, buf, CD9660_SECTOR_SIZE); + break; + } + debug_dump_to_xml_volume_descriptor(buf, sector); + sector++; + } while (t != 255); + + t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table, + 731); + t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733); + printf("Path table 1 located at sector %i and is %i bytes long\n", + t,t2); + debug_dump_to_xml_path_table(fd, t, t2, 721); + + t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table, + 731); + debug_dump_to_xml_path_table(fd, t, t2, 722); + + printf("</cd9660dump>\n"); +} + +static void +debug_dump_to_xml_padded_hex_output(const char *element, const char *buf, + int len) +{ + int i; + int t; + + printf("<%s>",element); + for (i = 0; i < len; i++) { + t = (unsigned char)buf[i]; + if (t >= 32 && t < 127) + printf("%c",t); + } + printf("</%s>\n",element); + + printf("<%s:hex>",element); + for (i = 0; i < len; i++) { + t = (unsigned char)buf[i]; + printf(" %x",t); + } + printf("</%s:hex>\n",element); +} + +int +debug_get_encoded_number(const unsigned char* buf, int mode) +{ +#if !HAVE_NBTOOL_CONFIG_H + switch (mode) { + /* 711: Single bite */ + case 711: + return isonum_711(buf); + + /* 712: Single signed byte */ + case 712: + return isonum_712(buf); + + /* 721: 16 bit LE */ + case 721: + return isonum_721(buf); + + /* 731: 32 bit LE */ + case 731: + return isonum_731(buf); + + /* 722: 16 bit BE */ + case 722: + return isonum_722(buf); + + /* 732: 32 bit BE */ + case 732: + return isonum_732(buf); + + /* 723: 16 bit bothE */ + case 723: + return isonum_723(buf); + + /* 733: 32 bit bothE */ + case 733: + return isonum_733(buf); + } +#endif + return 0; +} + +void +debug_dump_integer(const char *element, const unsigned char* buf, int mode) +{ + printf("<%s>%i</%s>\n", element, debug_get_encoded_number(buf, mode), + element); +} + +void +debug_dump_string(const char *element __unused, const unsigned char *buf __unused, int len __unused) +{ + +} + +void +debug_dump_directory_record_9_1(unsigned char* buf) +{ + struct iso_directory_record *rec = (struct iso_directory_record *)buf; + printf("<directoryrecord>\n"); + debug_dump_integer("length", rec->length, 711); + debug_dump_integer("ext_attr_length", rec->ext_attr_length, 711); + debug_dump_integer("extent", rec->extent, 733); + debug_dump_integer("size", rec->size, 733); + debug_dump_integer("flags", rec->flags, 711); + debug_dump_integer("file_unit_size", rec->file_unit_size, 711); + debug_dump_integer("interleave", rec->interleave, 711); + debug_dump_integer("volume_sequence_number", + rec->volume_sequence_number, 723); + debug_dump_integer("name_len", rec->name_len, 711); + debug_dump_to_xml_padded_hex_output("name", rec->name, + debug_get_encoded_number(rec->length, 711)); + printf("</directoryrecord>\n"); +} + + +void +debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector) +{ + struct iso_primary_descriptor *desc = + (struct iso_primary_descriptor *)buf; + + printf("<volumedescriptor sector=\"%i\">\n", sector); + printf("<vdtype>"); + switch(buf[0]) { + case 0: + printf("boot"); + break; + + case 1: + printf("primary"); + break; + + case 2: + printf("supplementary"); + break; + + case 3: + printf("volume partition descriptor"); + break; + + case 255: + printf("terminator"); + break; + } + + printf("</vdtype>\n"); + switch(buf[0]) { + case 1: + debug_dump_integer("type", desc->type, 711); + debug_dump_to_xml_padded_hex_output("id", desc->id, + ISODCL(2, 6)); + debug_dump_integer("version", (u_char *)desc->version, 711); + debug_dump_to_xml_padded_hex_output("system_id", + desc->system_id, ISODCL(9, 40)); + debug_dump_to_xml_padded_hex_output("volume_id", + desc->volume_id, ISODCL(41, 72)); + debug_dump_integer("volume_space_size", + (u_char *)desc->volume_space_size, 733); + debug_dump_integer("volume_set_size", + (u_char *)desc->volume_set_size, 733); + debug_dump_integer("volume_sequence_number", + (u_char *)desc->volume_sequence_number, 723); + debug_dump_integer("logical_block_size", + (u_char *)desc->logical_block_size, 723); + debug_dump_integer("path_table_size", + (u_char *)desc->path_table_size, 733); + debug_dump_integer("type_l_path_table", + (u_char *)desc->type_l_path_table, 731); + debug_dump_integer("opt_type_l_path_table", + (u_char *)desc->opt_type_l_path_table, 731); + debug_dump_integer("type_m_path_table", + (u_char *)desc->type_m_path_table, 732); + debug_dump_integer("opt_type_m_path_table", + (u_char *)desc->opt_type_m_path_table, 732); + debug_dump_directory_record_9_1( + (u_char *)desc->root_directory_record); + debug_dump_to_xml_padded_hex_output("volume_set_id", + desc->volume_set_id, ISODCL(191, 318)); + debug_dump_to_xml_padded_hex_output("publisher_id", + desc->publisher_id, ISODCL(319, 446)); + debug_dump_to_xml_padded_hex_output("preparer_id", + desc->preparer_id, ISODCL(447, 574)); + debug_dump_to_xml_padded_hex_output("application_id", + desc->application_id, ISODCL(575, 702)); + debug_dump_to_xml_padded_hex_output("copyright_file_id", + desc->copyright_file_id, ISODCL(703, 739)); + debug_dump_to_xml_padded_hex_output("abstract_file_id", + desc->abstract_file_id, ISODCL(740, 776)); + debug_dump_to_xml_padded_hex_output("bibliographic_file_id", + desc->bibliographic_file_id, ISODCL(777, 813)); + + debug_dump_to_xml_padded_hex_output("creation_date", + desc->creation_date, ISODCL(814, 830)); + debug_dump_to_xml_padded_hex_output("modification_date", + desc->modification_date, ISODCL(831, 847)); + debug_dump_to_xml_padded_hex_output("expiration_date", + desc->expiration_date, ISODCL(848, 864)); + debug_dump_to_xml_padded_hex_output("effective_date", + desc->effective_date, ISODCL(865, 881)); + + debug_dump_to_xml_padded_hex_output("file_structure_version", + desc->file_structure_version, ISODCL(882, 882)); + break; + } + printf("</volumedescriptor>\n"); +} + diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.c b/usr.sbin/makefs/cd9660/cd9660_eltorito.c new file mode 100644 index 000000000000..dd5bf67b2b09 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.c @@ -0,0 +1,751 @@ +/* $NetBSD: cd9660_eltorito.c,v 1.23 2018/03/28 06:48:55 nonaka Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "cd9660.h" +#include "cd9660_eltorito.h" +#include <util.h> + +#include <sys/cdefs.h> +/* + * Partition Status Information from Apple Tech Note 1189 + */ +#define APPLE_PS_VALID 0x00000001 /* Entry is valid */ +#define APPLE_PS_ALLOCATED 0x00000002 /* Entry is allocated */ +#define APPLE_PS_READABLE 0x00000010 /* Entry is readable */ +#define APPLE_PS_WRITABLE 0x00000020 /* Entry is writable */ + +#ifdef DEBUG +#define ELTORITO_DPRINTF(__x) printf __x +#else +#define ELTORITO_DPRINTF(__x) +#endif + +#include <util.h> + +static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void); +static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char); +static struct boot_catalog_entry *cd9660_boot_setup_default_entry( + struct cd9660_boot_image *); +static struct boot_catalog_entry *cd9660_boot_setup_section_head(char); +#if 0 +static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *); +#endif + +static struct cd9660_boot_image *default_boot_image; + +int +cd9660_add_boot_disk(iso9660_disk *diskStructure, const char *boot_info) +{ + struct stat stbuf; + const char *mode_msg; + char *temp; + char *sysname; + char *filename; + struct cd9660_boot_image *new_image, *tmp_image; + + assert(boot_info != NULL); + + if (*boot_info == '\0') { + warnx("Error: Boot disk information must be in the " + "format 'system;filename'"); + return 0; + } + + /* First decode the boot information */ + temp = estrdup(boot_info); + + sysname = temp; + filename = strchr(sysname, ';'); + if (filename == NULL) { + warnx("supply boot disk information in the format " + "'system;filename'"); + free(temp); + return 0; + } + + *filename++ = '\0'; + + if (diskStructure->verbose_level > 0) { + printf("Found bootdisk with system %s, and filename %s\n", + sysname, filename); + } + new_image = ecalloc(1, sizeof(*new_image)); + new_image->loadSegment = 0; /* default for now */ + + /* Decode System */ + if (strcmp(sysname, "i386") == 0) + new_image->system = ET_SYS_X86; + else if (strcmp(sysname, "powerpc") == 0) + new_image->system = ET_SYS_PPC; + else if (strcmp(sysname, "macppc") == 0 || + strcmp(sysname, "mac68k") == 0) + new_image->system = ET_SYS_MAC; + else if (strcmp(sysname, "efi") == 0) + new_image->system = ET_SYS_EFI; + else { + warnx("boot disk system must be " + "i386, powerpc, macppc, mac68k, or efi"); + free(temp); + free(new_image); + return 0; + } + + + new_image->filename = estrdup(filename); + + free(temp); + + /* Get information about the file */ + if (lstat(new_image->filename, &stbuf) == -1) + err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__, + new_image->filename); + + switch (stbuf.st_size) { + case 1440 * 1024: + new_image->targetMode = ET_MEDIA_144FDD; + mode_msg = "Assigned boot image to 1.44 emulation mode"; + break; + case 1200 * 1024: + new_image->targetMode = ET_MEDIA_12FDD; + mode_msg = "Assigned boot image to 1.2 emulation mode"; + break; + case 2880 * 1024: + new_image->targetMode = ET_MEDIA_288FDD; + mode_msg = "Assigned boot image to 2.88 emulation mode"; + break; + default: + new_image->targetMode = ET_MEDIA_NOEM; + mode_msg = "Assigned boot image to no emulation mode"; + break; + } + + if (diskStructure->verbose_level > 0) + printf("%s\n", mode_msg); + + new_image->size = stbuf.st_size; + new_image->num_sectors = + howmany(new_image->size, diskStructure->sectorSize) * + howmany(diskStructure->sectorSize, 512); + if (diskStructure->verbose_level > 0) { + printf("New image has size %d, uses %d 512-byte sectors\n", + new_image->size, new_image->num_sectors); + } + new_image->sector = -1; + /* Bootable by default */ + new_image->bootable = ET_BOOTABLE; + /* Add boot disk */ + + /* Group images for the same platform together. */ + TAILQ_FOREACH(tmp_image, &diskStructure->boot_images, image_list) { + if (tmp_image->system != new_image->system) + break; + } + + if (tmp_image == NULL) { + TAILQ_INSERT_HEAD(&diskStructure->boot_images, new_image, + image_list); + } else + TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list); + + new_image->serialno = diskStructure->image_serialno++; + + new_image->platform_id = new_image->system; + + /* TODO : Need to do anything about the boot image in the tree? */ + diskStructure->is_bootable = 1; + + /* First boot image is initial/default entry. */ + if (default_boot_image == NULL) + default_boot_image = new_image; + + return 1; +} + +int +cd9660_eltorito_add_boot_option(iso9660_disk *diskStructure, + const char *option_string, const char *value) +{ + char *eptr; + struct cd9660_boot_image *image; + + assert(option_string != NULL); + + /* Find the last image added */ + TAILQ_FOREACH(image, &diskStructure->boot_images, image_list) { + if (image->serialno + 1 == diskStructure->image_serialno) + break; + } + if (image == NULL) + errx(EXIT_FAILURE, "Attempted to add boot option, " + "but no boot images have been specified"); + + if (strcmp(option_string, "no-emul-boot") == 0) { + image->targetMode = ET_MEDIA_NOEM; + } else if (strcmp(option_string, "no-boot") == 0) { + image->bootable = ET_NOT_BOOTABLE; + } else if (strcmp(option_string, "hard-disk-boot") == 0) { + image->targetMode = ET_MEDIA_HDD; + } else if (strcmp(option_string, "boot-load-segment") == 0) { + image->loadSegment = strtoul(value, &eptr, 16); + if (eptr == value || *eptr != '\0' || errno != ERANGE) { + warn("%s: strtoul", __func__); + return 0; + } + } else if (strcmp(option_string, "platformid") == 0) { + if (strcmp(value, "efi") == 0) + image->platform_id = ET_SYS_EFI; + else { + warn("%s: unknown platform: %s", __func__, value); + return 0; + } + } else { + return 0; + } + return 1; +} + +static struct boot_catalog_entry * +cd9660_init_boot_catalog_entry(void) +{ + return ecalloc(1, sizeof(struct boot_catalog_entry)); +} + +static struct boot_catalog_entry * +cd9660_boot_setup_validation_entry(char sys) +{ + struct boot_catalog_entry *entry; + boot_catalog_validation_entry *ve; + int16_t checksum; + unsigned char *csptr; + size_t i; + entry = cd9660_init_boot_catalog_entry(); + + entry->entry_type = ET_ENTRY_VE; + ve = &entry->entry_data.VE; + + ve->header_id[0] = 1; + ve->platform_id[0] = sys; + ve->key[0] = 0x55; + ve->key[1] = 0xAA; + + /* Calculate checksum */ + checksum = 0; + cd9660_721(0, ve->checksum); + csptr = (unsigned char*)ve; + for (i = 0; i < sizeof(*ve); i += 2) { + checksum += (int16_t)csptr[i]; + checksum += 256 * (int16_t)csptr[i + 1]; + } + checksum = -checksum; + cd9660_721(checksum, ve->checksum); + + ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, " + "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0], + ve->key[0], ve->key[1], checksum)); + return entry; +} + +static struct boot_catalog_entry * +cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk) +{ + struct boot_catalog_entry *default_entry; + boot_catalog_initial_entry *ie; + + default_entry = cd9660_init_boot_catalog_entry(); + if (default_entry == NULL) + return NULL; + + default_entry->entry_type = ET_ENTRY_IE; + ie = &default_entry->entry_data.IE; + + ie->boot_indicator[0] = disk->bootable; + ie->media_type[0] = disk->targetMode; + cd9660_721(disk->loadSegment, ie->load_segment); + ie->system_type[0] = disk->system; + cd9660_721(disk->num_sectors, ie->sector_count); + cd9660_731(disk->sector, ie->load_rba); + + ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, " + "load segment %04x, system type %d, sector count %d, " + "load rba %d\n", __func__, ie->boot_indicator[0], + ie->media_type[0], disk->loadSegment, ie->system_type[0], + disk->num_sectors, disk->sector)); + return default_entry; +} + +static struct boot_catalog_entry * +cd9660_boot_setup_section_head(char platform) +{ + struct boot_catalog_entry *entry; + boot_catalog_section_header *sh; + + entry = cd9660_init_boot_catalog_entry(); + if (entry == NULL) + return NULL; + + entry->entry_type = ET_ENTRY_SH; + sh = &entry->entry_data.SH; + /* + * More by default. + * The last one will manually be set to ET_SECTION_HEADER_LAST + */ + sh->header_indicator[0] = ET_SECTION_HEADER_MORE; + sh->platform_id[0] = platform; + sh->num_section_entries[0] = 0; + return entry; +} + +static struct boot_catalog_entry * +cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk) +{ + struct boot_catalog_entry *entry; + boot_catalog_section_entry *se; + if ((entry = cd9660_init_boot_catalog_entry()) == NULL) + return NULL; + + entry->entry_type = ET_ENTRY_SE; + se = &entry->entry_data.SE; + + se->boot_indicator[0] = ET_BOOTABLE; + se->media_type[0] = disk->targetMode; + cd9660_721(disk->loadSegment, se->load_segment); + cd9660_721(disk->num_sectors, se->sector_count); + cd9660_731(disk->sector, se->load_rba); + return entry; +} + +#if 0 +static u_char +cd9660_boot_get_system_type(struct cd9660_boot_image *disk) +{ + /* + For hard drive booting, we need to examine the MBR to figure + out what the partition type is + */ + return 0; +} +#endif + +/* + * Set up the BVD, Boot catalog, and the boot entries, but do no writing + */ +int +cd9660_setup_boot(iso9660_disk *diskStructure, int first_sector) +{ + int sector; + int used_sectors; + int num_entries = 0; + int catalog_sectors; + struct boot_catalog_entry *x86_head, *mac_head, *ppc_head, *efi_head, + *valid_entry, *default_entry, *temp, *head, **headp, *next; + struct cd9660_boot_image *tmp_disk; + uint8_t system; + + headp = NULL; + x86_head = mac_head = ppc_head = efi_head = NULL; + + /* If there are no boot disks, don't bother building boot information */ + if (TAILQ_EMPTY(&diskStructure->boot_images)) + return 0; + + /* Point to catalog: For now assume it consumes one sector */ + ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector)); + diskStructure->boot_catalog_sector = first_sector; + cd9660_731(first_sector, + diskStructure->boot_descriptor->boot_catalog_pointer); + + /* + * Use system type of default image for validation entry. Fallback to + * X86 system type if not found. + */ + system = default_boot_image != NULL ? default_boot_image->system : + ET_SYS_X86; + + /* Step 1: Generate boot catalog */ + /* Step 1a: Validation entry */ + valid_entry = cd9660_boot_setup_validation_entry(system); + if (valid_entry == NULL) + return -1; + + /* + * Count how many boot images there are, + * and how many sectors they consume. + */ + num_entries = 1; + used_sectors = 0; + + TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) { + used_sectors += tmp_disk->num_sectors; + + /* One default entry per image */ + num_entries++; + } + catalog_sectors = howmany(num_entries * 0x20, diskStructure->sectorSize); + used_sectors += catalog_sectors; + + if (diskStructure->verbose_level > 0) { + printf("%s: there will be %i entries consuming %i sectors. " + "Catalog is %i sectors\n", __func__, num_entries, + used_sectors, catalog_sectors); + } + + /* Populate sector numbers */ + sector = first_sector + catalog_sectors; + TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) { + tmp_disk->sector = sector; + sector += tmp_disk->num_sectors / + (diskStructure->sectorSize / 512); + } + + LIST_INSERT_HEAD(&diskStructure->boot_entries, valid_entry, ll_struct); + + /* Step 1b: Initial/default entry */ + /* TODO : PARAM */ + if (default_boot_image != NULL) { + struct cd9660_boot_image *tcbi; + TAILQ_FOREACH(tcbi, &diskStructure->boot_images, image_list) { + if (tcbi == default_boot_image) { + tmp_disk = tcbi; + break; + } + } + } + if (tmp_disk == NULL) + tmp_disk = TAILQ_FIRST(&diskStructure->boot_images); + default_entry = cd9660_boot_setup_default_entry(tmp_disk); + if (default_entry == NULL) { + warnx("Error: memory allocation failed in cd9660_setup_boot"); + return -1; + } + + LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct); + + /* Todo: multiple default entries? */ + + tmp_disk = TAILQ_FIRST(&diskStructure->boot_images); + + head = NULL; + temp = default_entry; + + /* If multiple boot images are given : */ + for (; tmp_disk != NULL; tmp_disk = TAILQ_NEXT(tmp_disk, image_list)) { + if (tmp_disk == default_boot_image) + continue; + + /* Step 2: Section header */ + switch (tmp_disk->platform_id) { + case ET_SYS_X86: + headp = &x86_head; + break; + case ET_SYS_PPC: + headp = &ppc_head; + break; + case ET_SYS_MAC: + headp = &mac_head; + break; + case ET_SYS_EFI: + headp = &efi_head; + break; + default: + warnx("%s: internal error: unknown system type", + __func__); + return -1; + } + + if (*headp == NULL) { + head = + cd9660_boot_setup_section_head(tmp_disk->platform_id); + if (head == NULL) { + warnx("Error: memory allocation failed in " + "cd9660_setup_boot"); + return -1; + } + LIST_INSERT_AFTER(default_entry, head, ll_struct); + *headp = head; + } else + head = *headp; + + head->entry_data.SH.num_section_entries[0]++; + + /* Step 2a: Section entry and extensions */ + temp = cd9660_boot_setup_section_entry(tmp_disk); + if (temp == NULL) { + warn("%s: cd9660_boot_setup_section_entry", __func__); + return -1; + } + + while ((next = LIST_NEXT(head, ll_struct)) != NULL && + next->entry_type == ET_ENTRY_SE) + head = next; + + LIST_INSERT_AFTER(head, temp, ll_struct); + } + + /* Find the last Section Header entry and mark it as the last. */ + head = NULL; + LIST_FOREACH(next, &diskStructure->boot_entries, ll_struct) { + if (next->entry_type == ET_ENTRY_SH) + head = next; + } + if (head != NULL) + head->entry_data.SH.header_indicator[0] = ET_SECTION_HEADER_LAST; + + /* TODO: Remaining boot disks when implemented */ + + return first_sector + used_sectors; +} + +int +cd9660_setup_boot_volume_descriptor(iso9660_disk *diskStructure, + volume_descriptor *bvd) +{ + boot_volume_descriptor *bvdData = + (boot_volume_descriptor*)bvd->volumeDescriptorData; + + bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT; + memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); + bvdData->version[0] = 1; + memcpy(bvdData->boot_system_identifier, ET_ID, 23); + memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); + diskStructure->boot_descriptor = + (boot_volume_descriptor*) bvd->volumeDescriptorData; + return 1; +} + +static int +cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start, + off_t nsectors, int type) +{ + uint8_t val; + uint32_t lba; + + if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1) + err(1, "fseeko"); + + val = 0x80; /* Bootable */ + fwrite(&val, sizeof(val), 1, fd); + + val = 0xff; /* CHS begin */ + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + + val = type; /* Part type */ + fwrite(&val, sizeof(val), 1, fd); + + val = 0xff; /* CHS end */ + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + + /* LBA extent */ + lba = htole32(sector_start); + fwrite(&lba, sizeof(lba), 1, fd); + lba = htole32(nsectors); + fwrite(&lba, sizeof(lba), 1, fd); + + return 0; +} + +static int +cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions, + off_t sector_start, off_t nsectors, off_t sector_size, + const char *part_name, const char *part_type) +{ + uint32_t apm32, part_status; + uint16_t apm16; + + part_status = APPLE_PS_VALID | APPLE_PS_ALLOCATED | APPLE_PS_READABLE | + APPLE_PS_WRITABLE; + + if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1) + err(1, "fseeko"); + + /* Signature */ + apm16 = htobe16(0x504d); + fwrite(&apm16, sizeof(apm16), 1, fd); + apm16 = 0; + fwrite(&apm16, sizeof(apm16), 1, fd); + + /* Total number of partitions */ + apm32 = htobe32(total_partitions); + fwrite(&apm32, sizeof(apm32), 1, fd); + /* Bounds */ + apm32 = htobe32(sector_start); + fwrite(&apm32, sizeof(apm32), 1, fd); + apm32 = htobe32(nsectors); + fwrite(&apm32, sizeof(apm32), 1, fd); + + fwrite(part_name, strlen(part_name) + 1, 1, fd); + fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR); + fwrite(part_type, strlen(part_type) + 1, 1, fd); + fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR); + + apm32 = 0; + /* pmLgDataStart */ + fwrite(&apm32, sizeof(apm32), 1, fd); + /* pmDataCnt */ + apm32 = htobe32(nsectors); + fwrite(&apm32, sizeof(apm32), 1, fd); + /* pmPartStatus */ + apm32 = htobe32(part_status); + fwrite(&apm32, sizeof(apm32), 1, fd); + + return 0; +} + +int +cd9660_write_boot(iso9660_disk *diskStructure, FILE *fd) +{ + struct boot_catalog_entry *e; + struct cd9660_boot_image *t; + int apm_partitions = 0; + int mbr_partitions = 0; + + /* write boot catalog */ + if (fseeko(fd, (off_t)diskStructure->boot_catalog_sector * + diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + + if (diskStructure->verbose_level > 0) { + printf("Writing boot catalog to sector %" PRId64 "\n", + diskStructure->boot_catalog_sector); + } + LIST_FOREACH(e, &diskStructure->boot_entries, ll_struct) { + if (diskStructure->verbose_level > 0) { + printf("Writing catalog entry of type %d\n", + e->entry_type); + } + /* + * It doesn't matter which one gets written + * since they are the same size + */ + fwrite(&(e->entry_data.VE), 1, 32, fd); + } + if (diskStructure->verbose_level > 0) + printf("Finished writing boot catalog\n"); + + /* copy boot images */ + TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) { + if (diskStructure->verbose_level > 0) { + printf("Writing boot image from %s to sectors %d\n", + t->filename, t->sector); + } + cd9660_copy_file(diskStructure, fd, t->sector, t->filename); + + if (t->system == ET_SYS_MAC) + apm_partitions++; + if (t->system == ET_SYS_PPC) + mbr_partitions++; + } + + /* some systems need partition tables as well */ + if (mbr_partitions > 0 || diskStructure->chrp_boot) { + uint16_t sig; + + fseek(fd, 0x1fe, SEEK_SET); + sig = htole16(0xaa55); + fwrite(&sig, sizeof(sig), 1, fd); + + mbr_partitions = 0; + + /* Write ISO9660 descriptor, enclosing the whole disk */ + if (diskStructure->chrp_boot) + cd9660_write_mbr_partition_entry(fd, mbr_partitions++, + 0, diskStructure->totalSectors * + (diskStructure->sectorSize / 512), 0x96); + + /* Write all partition entries */ + TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) { + if (t->system != ET_SYS_PPC) + continue; + cd9660_write_mbr_partition_entry(fd, mbr_partitions++, + t->sector * (diskStructure->sectorSize / 512), + t->num_sectors * (diskStructure->sectorSize / 512), + 0x41 /* PReP Boot */); + } + } + + if (apm_partitions > 0) { + /* Write DDR and global APM info */ + uint32_t apm32; + uint16_t apm16; + int total_parts; + + fseek(fd, 0, SEEK_SET); + apm16 = htobe16(0x4552); + fwrite(&apm16, sizeof(apm16), 1, fd); + /* Device block size */ + apm16 = htobe16(512); + fwrite(&apm16, sizeof(apm16), 1, fd); + /* Device block count */ + apm32 = htobe32(diskStructure->totalSectors * + (diskStructure->sectorSize / 512)); + fwrite(&apm32, sizeof(apm32), 1, fd); + /* Device type/id */ + apm16 = htobe16(1); + fwrite(&apm16, sizeof(apm16), 1, fd); + fwrite(&apm16, sizeof(apm16), 1, fd); + + /* Count total needed entries */ + total_parts = 2 + apm_partitions; /* Self + ISO9660 */ + + /* Write self-descriptor */ + cd9660_write_apm_partition_entry(fd, 0, total_parts, 1, + total_parts, 512, "Apple", "Apple_partition_map"); + + /* Write all partition entries */ + apm_partitions = 0; + TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) { + if (t->system != ET_SYS_MAC) + continue; + + cd9660_write_apm_partition_entry(fd, + 1 + apm_partitions++, total_parts, + t->sector * (diskStructure->sectorSize / 512), + t->num_sectors * (diskStructure->sectorSize / 512), + 512, "CD Boot", "Apple_Bootstrap"); + } + + /* Write ISO9660 descriptor, enclosing the whole disk */ + cd9660_write_apm_partition_entry(fd, 2 + apm_partitions, + total_parts, 0, diskStructure->totalSectors * + (diskStructure->sectorSize / 512), 512, "ISO9660", + "CD_ROM_Mode_1"); + } + + return 0; +} diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.h b/usr.sbin/makefs/cd9660/cd9660_eltorito.h new file mode 100644 index 000000000000..a9ad0901e1a7 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.h @@ -0,0 +1,166 @@ +/* $NetBSD: cd9660_eltorito.h,v 1.6 2017/01/24 11:22:43 nonaka Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef _CD9660_ELTORITO_H_ +#define _CD9660_ELTORITO_H_ + +/* Boot defines */ +#define ET_ID "EL TORITO SPECIFICATION" +#define ET_SYS_X86 0 +#define ET_SYS_PPC 1 +#define ET_SYS_MAC 2 +#define ET_SYS_EFI 0xef /* Platform ID at section header entry */ + +#define ET_BOOT_ENTRY_SIZE 0x20 + +#define ET_BOOTABLE 0x88 +#define ET_NOT_BOOTABLE 0 + +#define ET_MEDIA_NOEM 0 +#define ET_MEDIA_12FDD 1 +#define ET_MEDIA_144FDD 2 +#define ET_MEDIA_288FDD 3 +#define ET_MEDIA_HDD 4 + +#define ET_INDICATOR_HEADERMORE 0x90 +#define ET_INDICATOR_HEADERLAST 0x91 +#define ET_INDICATOR_EXTENSION 0x44 + +/*** Boot Structures ***/ + +typedef struct _boot_volume_descriptor { + u_char boot_record_indicator [ISODCL(0x00,0x00)]; + u_char identifier [ISODCL(0x01,0x05)]; + u_char version [ISODCL(0x06,0x06)]; + u_char boot_system_identifier [ISODCL(0x07,0x26)]; + u_char unused1 [ISODCL(0x27,0x46)]; + u_char boot_catalog_pointer [ISODCL(0x47,0x4A)]; + u_char unused2 [ISODCL(0x4B,0x7FF)]; +} boot_volume_descriptor; + +typedef struct _boot_catalog_validation_entry { + u_char header_id [ISODCL(0x00,0x00)]; + u_char platform_id [ISODCL(0x01,0x01)]; + u_char reserved1 [ISODCL(0x02,0x03)]; + u_char manufacturer [ISODCL(0x04,0x1B)]; + u_char checksum [ISODCL(0x1C,0x1D)]; + u_char key [ISODCL(0x1E,0x1F)]; +} boot_catalog_validation_entry; + +typedef struct _boot_catalog_initial_entry { + u_char boot_indicator [ISODCL(0x00,0x00)]; + u_char media_type [ISODCL(0x01,0x01)]; + u_char load_segment [ISODCL(0x02,0x03)]; + u_char system_type [ISODCL(0x04,0x04)]; + u_char unused_1 [ISODCL(0x05,0x05)]; + u_char sector_count [ISODCL(0x06,0x07)]; + u_char load_rba [ISODCL(0x08,0x0B)]; + u_char unused_2 [ISODCL(0x0C,0x1F)]; +} boot_catalog_initial_entry; + +#define ET_SECTION_HEADER_MORE 0x90 +#define ET_SECTION_HEADER_LAST 0x91 + +typedef struct _boot_catalog_section_header { + u_char header_indicator [ISODCL(0x00,0x00)]; + u_char platform_id [ISODCL(0x01,0x01)]; + u_char num_section_entries [ISODCL(0x02,0x03)]; + u_char id_string [ISODCL(0x04,0x1F)]; +} boot_catalog_section_header; + +typedef struct _boot_catalog_section_entry { + u_char boot_indicator [ISODCL(0x00,0x00)]; + u_char media_type [ISODCL(0x01,0x01)]; + u_char load_segment [ISODCL(0x02,0x03)]; + u_char system_type [ISODCL(0x04,0x04)]; + u_char unused_1 [ISODCL(0x05,0x05)]; + u_char sector_count [ISODCL(0x06,0x07)]; + u_char load_rba [ISODCL(0x08,0x0B)]; + u_char selection_criteria [ISODCL(0x0C,0x0C)]; + u_char vendor_criteria [ISODCL(0x0D,0x1F)]; +} boot_catalog_section_entry; + +typedef struct _boot_catalog_section_entry_extension { + u_char extension_indicator [ISODCL(0x00,0x00)]; + u_char flags [ISODCL(0x01,0x01)]; + u_char vendor_criteria [ISODCL(0x02,0x1F)]; +} boot_catalog_section_entry_extension; + +#define ET_ENTRY_VE 1 +#define ET_ENTRY_IE 2 +#define ET_ENTRY_SH 3 +#define ET_ENTRY_SE 4 +#define ET_ENTRY_EX 5 + +struct boot_catalog_entry { + char entry_type; + union { + boot_catalog_validation_entry VE; + boot_catalog_initial_entry IE; + boot_catalog_section_header SH; + boot_catalog_section_entry SE; + boot_catalog_section_entry_extension EX; + } entry_data; + + LIST_ENTRY(boot_catalog_entry) ll_struct; +}; + +/* Temporary structure */ +struct cd9660_boot_image { + char *filename; + int size; + int sector; /* copied to LoadRBA */ + int num_sectors; + unsigned int loadSegment; + u_char targetMode; + u_char system; + u_char bootable; + u_char platform_id; /* for section header entry */ + /* + * If the boot image exists in the filesystem + * already, this is a pointer to that node. For the sake + * of simplicity in future versions, this pointer is only + * to the node in the primary volume. This SHOULD be done + * via a hashtable lookup. + */ + struct _cd9660node *boot_image_node; + TAILQ_ENTRY(cd9660_boot_image) image_list; + int serialno; +}; + + +#endif /* _CD9660_ELTORITO_H_ */ + diff --git a/usr.sbin/makefs/cd9660/cd9660_strings.c b/usr.sbin/makefs/cd9660/cd9660_strings.c new file mode 100644 index 000000000000..b3111fca6cd1 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_strings.c @@ -0,0 +1,120 @@ +/* $NetBSD: cd9660_strings.c,v 1.4 2007/01/16 17:32:05 hubertf Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include <sys/mount.h> + +#include <sys/param.h> +#include <ctype.h> + +#include "makefs.h" +#include "cd9660.h" + + +void +cd9660_uppercase_characters(char *str, size_t len) +{ + size_t p; + + for (p = 0; p < len; p++) { + if (islower((unsigned char)str[p]) ) + str[p] -= 32; + } +} + +static inline int +cd9660_is_d_char(char c) +{ + return (isupper((unsigned char)c) + || c == '_' + || (c >= '0' && c <= '9')); +} + +static inline int +cd9660_is_a_char(char c) +{ + return (isupper((unsigned char)c) + || c == '_' + || (c >= '%' && c <= '?') + || (c >= ' ' && c <= '\"')); +} + +/* + * Test a string to see if it is composed of valid a characters + * @param const char* The string to test + * @returns int 1 if valid, 2 if valid if characters are converted to + * upper case, 0 otherwise + */ +int +cd9660_valid_a_chars(const char *str) +{ + const char *c = str; + int upperFound = 0; + + while ((*c) != '\0') { + if (!(cd9660_is_a_char(*c))) { + if (islower((unsigned char)*c) ) + upperFound = 1; + else + return 0; + } + c++; + } + return upperFound + 1; +} + +/* + * Test a string to see if it is composed of valid d characters + * @param const char* The string to test + * @returns int 1 if valid, 2 if valid if characters are converted to + * upper case, 0 otherwise + */ +int +cd9660_valid_d_chars(const char *str) +{ + const char *c=str; + int upperFound = 0; + + while ((*c) != '\0') { + if (!(cd9660_is_d_char(*c))) { + if (islower((unsigned char)*c) ) + upperFound = 1; + else + return 0; + } + c++; + } + return upperFound + 1; +} diff --git a/usr.sbin/makefs/cd9660/cd9660_write.c b/usr.sbin/makefs/cd9660/cd9660_write.c new file mode 100644 index 000000000000..828af11669c1 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_write.c @@ -0,0 +1,510 @@ +/* $NetBSD: cd9660_write.c,v 1.14 2011/01/04 09:48:21 wiz Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "cd9660.h" +#include "iso9660_rrip.h" + +#include <sys/cdefs.h> +#include <util.h> + +static int cd9660_write_volume_descriptors(iso9660_disk *, FILE *); +static int cd9660_write_path_table(iso9660_disk *, FILE *, off_t, int); +static int cd9660_write_path_tables(iso9660_disk *, FILE *); +static int cd9660_write_file(iso9660_disk *, FILE *, cd9660node *); +static int cd9660_write_filedata(iso9660_disk *, FILE *, off_t, + const unsigned char *, int); +#if 0 +static int cd9660_write_buffered(FILE *, off_t, int, const unsigned char *); +#endif +static void cd9660_write_rr(iso9660_disk *, FILE *, cd9660node *, off_t, off_t); + +/* + * Write the image + * Writes the entire image + * @param const char* The filename for the image + * @returns int 1 on success, 0 on failure + */ +int +cd9660_write_image(iso9660_disk *diskStructure, const char* image) +{ + FILE *fd; + int status; + unsigned char buf[CD9660_SECTOR_SIZE]; + + if ((fd = fopen(image, "w+")) == NULL) { + err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__, + image); + } + + if (diskStructure->verbose_level > 0) + printf("Writing image\n"); + + if (diskStructure->has_generic_bootimage) { + status = cd9660_copy_file(diskStructure, fd, 0, + diskStructure->generic_bootimage); + if (status == 0) { + warnx("%s: Error writing generic boot image", + __func__); + goto cleanup_bad_image; + } + } + + /* Write the volume descriptors */ + status = cd9660_write_volume_descriptors(diskStructure, fd); + if (status == 0) { + warnx("%s: Error writing volume descriptors to image", + __func__); + goto cleanup_bad_image; + } + + if (diskStructure->verbose_level > 0) + printf("Volume descriptors written\n"); + + /* + * Write the path tables: there are actually four, but right + * now we are only concerned with two. + */ + status = cd9660_write_path_tables(diskStructure, fd); + if (status == 0) { + warnx("%s: Error writing path tables to image", __func__); + goto cleanup_bad_image; + } + + if (diskStructure->verbose_level > 0) + printf("Path tables written\n"); + + /* Write the directories and files */ + status = cd9660_write_file(diskStructure, fd, diskStructure->rootNode); + if (status == 0) { + warnx("%s: Error writing files to image", __func__); + goto cleanup_bad_image; + } + + if (diskStructure->is_bootable) { + cd9660_write_boot(diskStructure, fd); + } + + /* Write padding bits. This is temporary */ + memset(buf, 0, CD9660_SECTOR_SIZE); + cd9660_write_filedata(diskStructure, fd, + diskStructure->totalSectors - 1, buf, 1); + + if (diskStructure->verbose_level > 0) + printf("Files written\n"); + fclose(fd); + + if (diskStructure->verbose_level > 0) + printf("Image closed\n"); + return 1; + +cleanup_bad_image: + fclose(fd); + if (!diskStructure->keep_bad_images) + unlink(image); + if (diskStructure->verbose_level > 0) + printf("Bad image cleaned up\n"); + return 0; +} + +static int +cd9660_write_volume_descriptors(iso9660_disk *diskStructure, FILE *fd) +{ + volume_descriptor *vd_temp = diskStructure->firstVolumeDescriptor; + + while (vd_temp != NULL) { + cd9660_write_filedata(diskStructure, fd, vd_temp->sector, + vd_temp->volumeDescriptorData, 1); + vd_temp = vd_temp->next; + } + return 1; +} + +/* + * Write out an individual path table + * Used just to keep redundant code to a minimum + * @param FILE *fd Valid file pointer + * @param int Sector to start writing path table to + * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN + * @returns int 1 on success, 0 on failure + */ +static int +cd9660_write_path_table(iso9660_disk *diskStructure, FILE *fd, off_t sector, + int mode) +{ + int path_table_sectors = CD9660_BLOCKS(diskStructure->sectorSize, + diskStructure->pathTableLength); + unsigned char *buffer; + unsigned char *buffer_head; + int len, ret; + path_table_entry temp_entry; + cd9660node *ptcur; + + buffer = ecalloc(path_table_sectors, diskStructure->sectorSize); + buffer_head = buffer; + + ptcur = diskStructure->rootNode; + + while (ptcur != NULL) { + memset(&temp_entry, 0, sizeof(path_table_entry)); + temp_entry.length[0] = ptcur->isoDirRecord->name_len[0]; + temp_entry.extended_attribute_length[0] = + ptcur->isoDirRecord->ext_attr_length[0]; + memcpy(temp_entry.name, ptcur->isoDirRecord->name, + temp_entry.length[0] + 1); + + /* round up */ + len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01); + + /* todo: function pointers instead */ + if (mode == LITTLE_ENDIAN) { + cd9660_731(ptcur->fileDataSector, + temp_entry.first_sector); + cd9660_721((ptcur->parent == NULL ? + 1 : ptcur->parent->ptnumber), + temp_entry.parent_number); + } else { + cd9660_732(ptcur->fileDataSector, + temp_entry.first_sector); + cd9660_722((ptcur->parent == NULL ? + 1 : ptcur->parent->ptnumber), + temp_entry.parent_number); + } + + + memcpy(buffer, &temp_entry, len); + buffer += len; + + ptcur = ptcur->ptnext; + } + + ret = cd9660_write_filedata(diskStructure, fd, sector, buffer_head, + path_table_sectors); + free(buffer_head); + return ret; +} + + +/* + * Write out the path tables to disk + * Each file descriptor should be pointed to by the PVD, so we know which + * sector to copy them to. One thing to watch out for: the only path tables + * stored are in the endian mode that the application is compiled for. So, + * the first thing to do is write out that path table, then to write the one + * in the other endian mode requires to convert the endianness of each entry + * in the table. The best way to do this would be to create a temporary + * path_table_entry structure, then for each path table entry, copy it to + * the temporary entry, translate, then copy that to disk. + * + * @param FILE* Valid file descriptor + * @returns int 0 on failure, 1 on success + */ +static int +cd9660_write_path_tables(iso9660_disk *diskStructure, FILE *fd) +{ + if (cd9660_write_path_table(diskStructure, fd, + diskStructure->primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0) + return 0; + + if (cd9660_write_path_table(diskStructure, fd, + diskStructure->primaryBigEndianTableSector, BIG_ENDIAN) == 0) + return 0; + + /* @TODO: handle remaining two path tables */ + return 1; +} + +/* + * Write a file to disk + * Writes a file, its directory record, and its data to disk + * This file is designed to be called RECURSIVELY, so initially call it + * with the root node. All of the records should store what sector the + * file goes in, so no computation should be necessary. + * + * @param int fd Valid file descriptor + * @param struct cd9660node* writenode Pointer to the file to be written + * @returns int 0 on failure, 1 on success + */ +static int +cd9660_write_file(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode) +{ + char *buf; + char *temp_file_name; + int ret; + off_t working_sector; + int cur_sector_offset; + iso_directory_record_cd9660 temp_record; + cd9660node *temp; + int rv = 0; + + /* Todo : clean up variables */ + + temp_file_name = ecalloc(PATH_MAX, 1); + buf = emalloc(diskStructure->sectorSize); + if ((writenode->level != 0) && + !(writenode->node->type & S_IFDIR)) { + fsinode *inode = writenode->node->inode; + /* Only attempt to write unwritten files that have length. */ + if ((inode->flags & FI_WRITTEN) != 0) { + INODE_WARNX(("%s: skipping written inode %d", __func__, + (int)inode->st.st_ino)); + } else if (writenode->fileDataLength > 0) { + INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32, + __func__, (int)inode->st.st_ino, inode->ino)); + inode->flags |= FI_WRITTEN; + if (writenode->node->contents == NULL) + cd9660_compute_full_filename(writenode, + temp_file_name); + ret = cd9660_copy_file(diskStructure, fd, + writenode->fileDataSector, + (writenode->node->contents != NULL) ? + writenode->node->contents : temp_file_name); + if (ret == 0) + goto out; + } + } else { + /* + * Here is a new revelation that ECMA didn't explain + * (at least not well). + * ALL . and .. records store the name "\0" and "\1" + * respectively. So, for each directory, we have to + * make a new node. + * + * This is where it gets kinda messy, since we have to + * be careful of sector boundaries + */ + cur_sector_offset = 0; + working_sector = writenode->fileDataSector; + if (fseeko(fd, working_sector * diskStructure->sectorSize, + SEEK_SET) == -1) + err(1, "fseeko"); + + /* + * Now loop over children, writing out their directory + * records - beware of sector boundaries + */ + TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) { + /* + * Copy the temporary record and adjust its size + * if necessary + */ + memcpy(&temp_record, temp->isoDirRecord, + sizeof(iso_directory_record_cd9660)); + + temp_record.length[0] = + cd9660_compute_record_size(diskStructure, temp); + + if (temp_record.length[0] + cur_sector_offset >= + diskStructure->sectorSize) { + cur_sector_offset = 0; + working_sector++; + + /* Seek to the next sector. */ + if (fseeko(fd, working_sector * + diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + } + /* Write out the basic ISO directory record */ + (void)fwrite(&temp_record, 1, + temp->isoDirRecord->length[0], fd); + if (diskStructure->rock_ridge_enabled) { + cd9660_write_rr(diskStructure, fd, temp, + cur_sector_offset, working_sector); + } + if (fseeko(fd, working_sector * + diskStructure->sectorSize + cur_sector_offset + + temp_record.length[0] - temp->su_tail_size, + SEEK_SET) == -1) + err(1, "fseeko"); + if (temp->su_tail_size > 0) + fwrite(temp->su_tail_data, 1, + temp->su_tail_size, fd); + if (ferror(fd)) { + warnx("%s: write error", __func__); + goto out; + } + cur_sector_offset += temp_record.length[0]; + + } + + /* + * Recurse on children. + */ + TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) { + if ((ret = cd9660_write_file(diskStructure, fd, temp)) == 0) + goto out; + } + } + rv = 1; +out: + free(temp_file_name); + free(buf); + return rv; +} + +/* + * Wrapper function to write a buffer (one sector) to disk. + * Seeks and writes the buffer. + * NOTE: You dont NEED to use this function, but it might make your + * life easier if you have to write things that align to a sector + * (such as volume descriptors). + * + * @param int fd Valid file descriptor + * @param int sector Sector number to write to + * @param const unsigned char* Buffer to write. This should be the + * size of a sector, and if only a portion + * is written, the rest should be set to 0. + */ +static int +cd9660_write_filedata(iso9660_disk *diskStructure, FILE *fd, off_t sector, + const unsigned char *buf, int numsecs) +{ + off_t curpos; + size_t success; + + curpos = ftello(fd); + + if (fseeko(fd, sector * diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + + success = fwrite(buf, diskStructure->sectorSize * numsecs, 1, fd); + + if (fseeko(fd, curpos, SEEK_SET) == -1) + err(1, "fseeko"); + + if (success == 1) + success = diskStructure->sectorSize * numsecs; + return success; +} + +#if 0 +static int +cd9660_write_buffered(FILE *fd, off_t offset, int buff_len, + const unsigned char* buffer) +{ + static int working_sector = -1; + static char buf[CD9660_SECTOR_SIZE]; + + return 0; +} +#endif + +int +cd9660_copy_file(iso9660_disk *diskStructure, FILE *fd, off_t start_sector, + const char *filename) +{ + FILE *rf; + int bytes_read; + int buf_size = diskStructure->sectorSize; + char *buf; + + buf = emalloc(buf_size); + if ((rf = fopen(filename, "rb")) == NULL) { + warn("%s: cannot open %s", __func__, filename); + free(buf); + return 0; + } + + if (diskStructure->verbose_level > 1) + printf("Writing file: %s\n",filename); + + if (fseeko(fd, start_sector * diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + + while (!feof(rf)) { + bytes_read = fread(buf,1,buf_size,rf); + if (ferror(rf)) { + warn("%s: fread", __func__); + free(buf); + (void)fclose(rf); + return 0; + } + + fwrite(buf,1,bytes_read,fd); + if (ferror(fd)) { + warn("%s: fwrite", __func__); + free(buf); + (void)fclose(rf); + return 0; + } + } + + fclose(rf); + free(buf); + return 1; +} + +static void +cd9660_write_rr(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode, + off_t offset, off_t sector) +{ + int in_ca = 0; + struct ISO_SUSP_ATTRIBUTES *myattr; + + offset += writenode->isoDirRecord->length[0]; + if (fseeko(fd, sector * diskStructure->sectorSize + offset, SEEK_SET) == + -1) + err(1, "fseeko"); + /* Offset now points at the end of the record */ + TAILQ_FOREACH(myattr, &writenode->head, rr_ll) { + fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd); + + if (!in_ca) { + offset += CD9660_SUSP_ENTRY_SIZE(myattr); + if (myattr->last_in_suf) { + /* + * Point the offset to the start of this + * record's CE area + */ + if (fseeko(fd, ((off_t)diskStructure-> + susp_continuation_area_start_sector * + diskStructure->sectorSize) + + writenode->susp_entry_ce_start, + SEEK_SET) == -1) + err(1, "fseeko"); + in_ca = 1; + } + } + } + + /* + * If we had to go to the continuation area, head back to + * where we should be. + */ + if (in_ca) + if (fseeko(fd, sector * diskStructure->sectorSize + offset, + SEEK_SET) == -1) + err(1, "fseeko"); +} diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.c b/usr.sbin/makefs/cd9660/iso9660_rrip.c new file mode 100644 index 000000000000..31c6e38a96fe --- /dev/null +++ b/usr.sbin/makefs/cd9660/iso9660_rrip.c @@ -0,0 +1,892 @@ +/* $NetBSD: iso9660_rrip.c,v 1.14 2014/05/30 13:14:47 martin Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/* This will hold all the function definitions + * defined in iso9660_rrip.h + */ + +#include <sys/cdefs.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <stdio.h> + +#include "makefs.h" +#include "cd9660.h" +#include "iso9660_rrip.h" +#include <util.h> + +static void cd9660_rrip_initialize_inode(iso9660_disk *, cd9660node *); +static int cd9660_susp_handle_continuation(iso9660_disk *, cd9660node *); +static int cd9660_susp_handle_continuation_common(iso9660_disk *, cd9660node *, + int); + +int +cd9660_susp_initialize(iso9660_disk *diskStructure, cd9660node *node, + cd9660node *parent, cd9660node *grandparent) +{ + cd9660node *cn; + int r; + + /* Make sure the node is not NULL. If it is, there are major problems */ + assert(node != NULL); + + if (!(node->type & CD9660_TYPE_DOT) && + !(node->type & CD9660_TYPE_DOTDOT)) + TAILQ_INIT(&(node->head)); + if (node->dot_record != 0) + TAILQ_INIT(&(node->dot_record->head)); + if (node->dot_dot_record != 0) + TAILQ_INIT(&(node->dot_dot_record->head)); + + if (diskStructure->rr_inode_next == 0) { + RB_INIT(&diskStructure->rr_inode_map); + diskStructure->rr_inode_next = 1; + } + + /* SUSP specific entries here */ + if ((r = cd9660_susp_initialize_node(diskStructure, node)) < 0) + return r; + + /* currently called cd9660node_rrip_init_links */ + r = cd9660_rrip_initialize_node(diskStructure, node, parent, grandparent); + if (r < 0) + return r; + + /* + * See if we need a CE record, and set all of the + * associated counters. + * + * This should be called after all extensions. After + * this is called, no new records should be added. + */ + if ((r = cd9660_susp_handle_continuation(diskStructure, node)) < 0) + return r; + + /* Recurse on children. */ + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { + if ((r = cd9660_susp_initialize(diskStructure, cn, node, parent)) < 0) + return 0; + } + return 1; +} + +int +cd9660_susp_finalize(iso9660_disk *diskStructure, cd9660node *node) +{ + cd9660node *temp; + struct inode_map_node *mapnode, *mapnodetmp; + int r; + + assert(node != NULL); + + if (node == diskStructure->rootNode) + diskStructure->susp_continuation_area_current_free = 0; + + if ((r = cd9660_susp_finalize_node(diskStructure, node)) < 0) + return r; + if ((r = cd9660_rrip_finalize_node(node)) < 0) + return r; + + TAILQ_FOREACH(temp, &node->cn_children, cn_next_child) { + if ((r = cd9660_susp_finalize(diskStructure, temp)) < 0) + return r; + } + + if (diskStructure->rr_inode_next != 0) { + RB_FOREACH_SAFE(mapnode, inode_map_tree, + &(diskStructure->rr_inode_map), mapnodetmp) { + RB_REMOVE(inode_map_tree, + &(diskStructure->rr_inode_map), mapnode); + free(mapnode); + } + diskStructure->rr_inode_next = 0; + } + return 1; +} + +/* + * If we really wanted to speed things up, we could have some sort of + * lookup table on the SUSP entry type that calls a functor. Or, we could + * combine the functions. These functions are kept separate to allow + * easier addition of other extensions. + + * For the sake of simplicity and clarity, we won't be doing that for now. + */ + +/* + * SUSP needs to update the following types: + * CE (continuation area) + */ +int +cd9660_susp_finalize_node(iso9660_disk *diskStructure, cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *t; + + /* Handle CE counters */ + if (node->susp_entry_ce_length > 0) { + node->susp_entry_ce_start = + diskStructure->susp_continuation_area_current_free; + diskStructure->susp_continuation_area_current_free += + node->susp_entry_ce_length; + } + + TAILQ_FOREACH(t, &node->head, rr_ll) { + if (t->susp_type != SUSP_TYPE_SUSP || + t->entry_type != SUSP_ENTRY_SUSP_CE) + continue; + cd9660_bothendian_dword( + diskStructure-> + susp_continuation_area_start_sector, + t->attr.su_entry.CE.ca_sector); + + cd9660_bothendian_dword( + diskStructure-> + susp_continuation_area_start_sector, + t->attr.su_entry.CE.ca_sector); + cd9660_bothendian_dword(node->susp_entry_ce_start, + t->attr.su_entry.CE.offset); + cd9660_bothendian_dword(node->susp_entry_ce_length, + t->attr.su_entry.CE.length); + } + return 0; +} + +int +cd9660_rrip_finalize_node(cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *t; + + TAILQ_FOREACH(t, &node->head, rr_ll) { + if (t->susp_type != SUSP_TYPE_RRIP) + continue; + switch (t->entry_type) { + case SUSP_ENTRY_RRIP_CL: + /* Look at rr_relocated*/ + if (node->rr_relocated == NULL) + return -1; + cd9660_bothendian_dword( + node->rr_relocated->fileDataSector, + (unsigned char *) + t->attr.rr_entry.CL.dir_loc); + break; + case SUSP_ENTRY_RRIP_PL: + /* Look at rr_real_parent */ + if (node->parent == NULL || + node->parent->rr_real_parent == NULL) + return -1; + cd9660_bothendian_dword( + node->parent->rr_real_parent->fileDataSector, + (unsigned char *) + t->attr.rr_entry.PL.dir_loc); + break; + } + } + return 0; +} + +static int +cd9660_susp_handle_continuation_common(iso9660_disk *diskStructure, + cd9660node *node, int space) +{ + int ca_used, susp_used, susp_used_pre_ce, working; + struct ISO_SUSP_ATTRIBUTES *temp, *pre_ce, *last, *CE, *ST; + + pre_ce = last = NULL; + working = 254 - space; + if (node->su_tail_size > 0) + /* Allow 4 bytes for "ST" record. */ + working -= node->su_tail_size + 4; + /* printf("There are %i bytes to work with\n",working); */ + + susp_used_pre_ce = susp_used = 0; + ca_used = 0; + TAILQ_FOREACH(temp, &node->head, rr_ll) { + if (working < 0) + break; + /* + * printf("SUSP Entry found, length is %i\n", + * CD9660_SUSP_ENTRY_SIZE(temp)); + */ + working -= CD9660_SUSP_ENTRY_SIZE(temp); + if (working >= 0) { + last = temp; + susp_used += CD9660_SUSP_ENTRY_SIZE(temp); + } + if (working >= 28) { + /* + * Remember the last entry after which we + * could insert a "CE" entry. + */ + pre_ce = last; + susp_used_pre_ce = susp_used; + } + } + + /* A CE entry is needed */ + if (working <= 0) { + CE = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_CE, "CE", SUSP_LOC_ENTRY); + cd9660_susp_ce(CE, node); + /* This will automatically insert at the appropriate location */ + if (pre_ce != NULL) + TAILQ_INSERT_AFTER(&node->head, pre_ce, CE, rr_ll); + else + TAILQ_INSERT_HEAD(&node->head, CE, rr_ll); + last = CE; + susp_used = susp_used_pre_ce + 28; + /* Count how much CA data is necessary */ + for (temp = TAILQ_NEXT(last, rr_ll); temp != NULL; + temp = TAILQ_NEXT(temp, rr_ll)) { + ca_used += CD9660_SUSP_ENTRY_SIZE(temp); + } + } + + /* An ST entry is needed */ + if (node->su_tail_size > 0) { + ST = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_ST, "ST", SUSP_LOC_ENTRY); + cd9660_susp_st(ST, node); + if (last != NULL) + TAILQ_INSERT_AFTER(&node->head, last, ST, rr_ll); + else + TAILQ_INSERT_HEAD(&node->head, ST, rr_ll); + last = ST; + susp_used += 4; + } + if (last != NULL) + last->last_in_suf = 1; + + node->susp_entry_size = susp_used; + node->susp_entry_ce_length = ca_used; + + diskStructure->susp_continuation_area_size += ca_used; + return 1; +} + +/* See if a continuation entry is needed for each of the different types */ +static int +cd9660_susp_handle_continuation(iso9660_disk *diskStructure, cd9660node *node) +{ + assert (node != NULL); + + /* Entry */ + if (cd9660_susp_handle_continuation_common(diskStructure, + node,(int)(node->isoDirRecord->length[0])) < 0) + return 0; + + return 1; +} + +int +cd9660_susp_initialize_node(iso9660_disk *diskStructure, cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *temp; + + /* + * Requirements/notes: + * CE: is added for us where needed + * ST: not sure if it is even required, but if so, should be + * handled by the CE code + * PD: isn't needed (though might be added for testing) + * SP: is stored ONLY on the . record of the root directory + * ES: not sure + */ + + /* Check for root directory, add SP and ER if needed. */ + if (node->type & CD9660_TYPE_DOT) { + if (node->parent == diskStructure->rootNode) { + temp = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_SP, "SP", SUSP_LOC_DOT); + cd9660_susp_sp(temp, node); + + /* Should be first entry. */ + TAILQ_INSERT_HEAD(&node->head, temp, rr_ll); + } + } + return 1; +} + +static void +cd9660_rrip_initialize_inode(iso9660_disk *diskStructure, cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *attr; + + /* + * Inode dependent values - this may change, + * but for now virtual files and directories do + * not have an inode structure + */ + + if ((node->node != NULL) && (node->node->inode != NULL)) { + /* PX - POSIX attributes */ + attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); + cd9660node_rrip_px(diskStructure, attr, node->node); + + TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); + + /* TF - timestamp */ + attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY); + cd9660node_rrip_tf(attr, node->node); + TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); + + /* SL - Symbolic link */ + /* ?????????? Dan - why is this here? */ + if (TAILQ_EMPTY(&node->cn_children) && + node->node->inode != NULL && + S_ISLNK(node->node->inode->st.st_mode)) + cd9660_createSL(node); + + /* PN - device number */ + if (node->node->inode != NULL && + ((S_ISCHR(node->node->inode->st.st_mode) || + S_ISBLK(node->node->inode->st.st_mode)))) { + attr = + cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PN, "PN", + SUSP_LOC_ENTRY); + cd9660node_rrip_pn(attr, node->node); + TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); + } + } +} + +int +cd9660_rrip_initialize_node(iso9660_disk *diskStructure, cd9660node *node, + cd9660node *parent, cd9660node *grandparent) +{ + struct ISO_SUSP_ATTRIBUTES *current = NULL; + + assert(node != NULL); + + if (node->type & CD9660_TYPE_DOT) { + /* + * Handle ER - should be the only entry to appear on + * a "." record + */ + if (node->parent == diskStructure->rootNode) { + cd9660_susp_ER(node, 1, SUSP_RRIP_ER_EXT_ID, + SUSP_RRIP_ER_EXT_DES, SUSP_RRIP_ER_EXT_SRC); + } + if (parent != NULL && parent->node != NULL && + parent->node->inode != NULL) { + /* PX - POSIX attributes */ + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); + cd9660node_rrip_px(diskStructure, current, + parent->node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + + /* TF - timestamp */ + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY); + cd9660node_rrip_tf(current, parent->node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + } else if (node->type & CD9660_TYPE_DOTDOT) { + if (grandparent != NULL && grandparent->node != NULL && + grandparent->node->inode != NULL) { + /* PX - POSIX attributes */ + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); + cd9660node_rrip_px(diskStructure, current, + grandparent->node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + + /* TF - timestamp */ + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY); + cd9660node_rrip_tf(current, grandparent->node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + /* Handle PL */ + if (parent != NULL && parent->rr_real_parent != NULL) { + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PL, "PL", SUSP_LOC_DOTDOT); + cd9660_rrip_PL(current,node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + } else { + cd9660_rrip_initialize_inode(diskStructure, node); + + if (node == diskStructure->rr_moved_dir) { + cd9660_rrip_add_NM(node, RRIP_DEFAULT_MOVE_DIR_NAME); + } else if (node->node != NULL) { + cd9660_rrip_NM(node); + } + + /* Rock ridge directory relocation code here. */ + + /* First handle the CL for the placeholder file. */ + if (node->rr_relocated != NULL) { + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_CL, "CL", SUSP_LOC_ENTRY); + cd9660_rrip_CL(current, node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + + /* Handle RE*/ + if (node->rr_real_parent != NULL) { + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_RE, "RE", SUSP_LOC_ENTRY); + cd9660_rrip_RE(current,node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + } + return 1; +} + +struct ISO_SUSP_ATTRIBUTES* +cd9660node_susp_create_node(int susp_type, int entry_type, const char *type_id, + int write_loc) +{ + struct ISO_SUSP_ATTRIBUTES* temp; + + temp = emalloc(sizeof(*temp)); + temp->susp_type = susp_type; + temp->entry_type = entry_type; + temp->last_in_suf = 0; + /* Phase this out */ + temp->type_of[0] = type_id[0]; + temp->type_of[1] = type_id[1]; + temp->write_location = write_loc; + + /* + * Since the first four bytes is common, lets go ahead and + * set the type identifier, since we are passing that to this + * function anyhow. + */ + temp->attr.su_entry.SP.h.type[0] = type_id[0]; + temp->attr.su_entry.SP.h.type[1] = type_id[1]; + return temp; +} + +int +cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES* p, cd9660node *node __unused) +{ + p->attr.rr_entry.PL.h.length[0] = 12; + p->attr.rr_entry.PL.h.version[0] = 1; + return 1; +} + +int +cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused) +{ + p->attr.rr_entry.CL.h.length[0] = 12; + p->attr.rr_entry.CL.h.version[0] = 1; + return 1; +} + +int +cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused) +{ + p->attr.rr_entry.RE.h.length[0] = 4; + p->attr.rr_entry.RE.h.version[0] = 1; + return 1; +} + +void +cd9660_createSL(cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES* current; + int path_count, dir_count, done, i, j, dir_copied; + char temp_cr[255]; + char temp_sl[255]; /* used in copying continuation entry*/ + char* sl_ptr; + + sl_ptr = node->node->symlink; + + done = 0; + path_count = 0; + dir_count = 0; + dir_copied = 0; + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY); + + current->attr.rr_entry.SL.h.version[0] = 1; + current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE; + + if (*sl_ptr == '/') { + temp_cr[0] = SL_FLAGS_ROOT; + temp_cr[1] = 0; + memcpy(current->attr.rr_entry.SL.component + path_count, + temp_cr, 2); + path_count += 2; + sl_ptr++; + } + + for (i = 0; i < (dir_count + 2); i++) + temp_cr[i] = '\0'; + + while (!done) { + while ((*sl_ptr != '/') && (*sl_ptr != '\0')) { + dir_copied = 1; + if (*sl_ptr == '.') { + if ((*(sl_ptr + 1) == '/') || (*(sl_ptr + 1) + == '\0')) { + temp_cr[0] = SL_FLAGS_CURRENT; + sl_ptr++; + } else if(*(sl_ptr + 1) == '.') { + if ((*(sl_ptr + 2) == '/') || + (*(sl_ptr + 2) == '\0')) { + temp_cr[0] = SL_FLAGS_PARENT; + sl_ptr += 2; + } + } else { + temp_cr[dir_count+2] = *sl_ptr; + sl_ptr++; + dir_count++; + } + } else { + temp_cr[dir_count + 2] = *sl_ptr; + sl_ptr++; + dir_count++; + } + } + + if ((path_count + dir_count) >= 249) { + current->attr.rr_entry.SL.flags[0] |= SL_FLAGS_CONTINUE; + + j = 0; + + if (path_count <= 249) { + while(j != (249 - path_count)) { + temp_sl[j] = temp_cr[j]; + j++; + } + temp_sl[0] = SL_FLAGS_CONTINUE; + temp_sl[1] = j - 2; + memcpy( + current->attr.rr_entry.SL.component + + path_count, + temp_sl, j); + } + + path_count += j; + current->attr.rr_entry.SL.h.length[0] = path_count + 5; + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + current= cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY); + current->attr.rr_entry.SL.h.version[0] = 1; + current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE; + + path_count = 0; + + if (dir_count > 2) { + while (j != dir_count + 2) { + current->attr.rr_entry.SL.component[ + path_count + 2] = temp_cr[j]; + j++; + path_count++; + } + current->attr.rr_entry.SL.component[1] + = path_count; + path_count+= 2; + } else { + while(j != dir_count) { + current->attr.rr_entry.SL.component[ + path_count+2] = temp_cr[j]; + j++; + path_count++; + } + } + } else { + if (dir_copied == 1) { + temp_cr[1] = dir_count; + memcpy(current->attr.rr_entry.SL.component + + path_count, + temp_cr, dir_count + 2); + path_count += dir_count + 2; + } + } + + if (*sl_ptr == '\0') { + done = 1; + current->attr.rr_entry.SL.h.length[0] = path_count + 5; + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } else { + sl_ptr++; + dir_count = 0; + dir_copied = 0; + for(i = 0; i < 255; i++) { + temp_cr[i] = '\0'; + } + } + } +} + +static int +inode_map_node_cmp(struct inode_map_node *a, struct inode_map_node *b) +{ + if (a->key < b->key) + return (-1); + if (a->key > b->key) + return (1); + return (0); +} + +RB_GENERATE(inode_map_tree, inode_map_node, entry, inode_map_node_cmp); + +static uint64_t +inode_map(iso9660_disk *diskStructure, uint64_t in) +{ + struct inode_map_node lookup = { .key = in }; + struct inode_map_node *node; + + /* + * Always assign an inode number if src inode unset. mtree mode leaves + * src inode unset for files with st_nlink == 1. + */ + if (in != 0) { + node = RB_FIND(inode_map_tree, &(diskStructure->rr_inode_map), + &lookup); + if (node != NULL) + return (node->value); + } + + node = emalloc(sizeof(struct inode_map_node)); + node->key = in; + node->value = diskStructure->rr_inode_next++; + RB_INSERT(inode_map_tree, &(diskStructure->rr_inode_map), node); + return (node->value); +} + +int +cd9660node_rrip_px(iso9660_disk *diskStructure, struct ISO_SUSP_ATTRIBUTES *v, + fsnode *pxinfo) +{ + v->attr.rr_entry.PX.h.length[0] = 44; + v->attr.rr_entry.PX.h.version[0] = 1; + cd9660_bothendian_dword(pxinfo->inode->st.st_mode, + v->attr.rr_entry.PX.mode); + cd9660_bothendian_dword(pxinfo->inode->st.st_nlink, + v->attr.rr_entry.PX.links); + cd9660_bothendian_dword(pxinfo->inode->st.st_uid, + v->attr.rr_entry.PX.uid); + cd9660_bothendian_dword(pxinfo->inode->st.st_gid, + v->attr.rr_entry.PX.gid); + cd9660_bothendian_dword(inode_map(diskStructure, + pxinfo->inode->st.st_ino), v->attr.rr_entry.PX.serial); + + return 1; +} + +int +cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *pn_field, fsnode *fnode) +{ + pn_field->attr.rr_entry.PN.h.length[0] = 20; + pn_field->attr.rr_entry.PN.h.version[0] = 1; + + if (sizeof (fnode->inode->st.st_rdev) > 4) + cd9660_bothendian_dword( + (uint64_t)fnode->inode->st.st_rdev >> 32, + pn_field->attr.rr_entry.PN.high); + else + cd9660_bothendian_dword(0, pn_field->attr.rr_entry.PN.high); + + cd9660_bothendian_dword(fnode->inode->st.st_rdev & 0xffffffff, + pn_field->attr.rr_entry.PN.low); + return 1; +} + +#if 0 +int +cd9660node_rrip_nm(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *file_node) +{ + int nm_length = strlen(file_node->isoDirRecord->name) + 5; + p->attr.rr_entry.NM.h.type[0] = 'N'; + p->attr.rr_entry.NM.h.type[1] = 'M'; + sprintf(p->attr.rr_entry.NM.altname, "%s", file_node->isoDirRecord->name); + p->attr.rr_entry.NM.h.length[0] = (unsigned char)nm_length; + p->attr.rr_entry.NM.h.version[0] = (unsigned char)1; + p->attr.rr_entry.NM.flags[0] = (unsigned char) NM_PARENT; + return 1; +} +#endif + +int +cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *p, fsnode *_node) +{ + p->attr.rr_entry.TF.flags[0] = TF_MODIFY | TF_ACCESS | TF_ATTRIBUTES; + p->attr.rr_entry.TF.h.length[0] = 5; + p->attr.rr_entry.TF.h.version[0] = 1; + + /* + * Need to add creation time, backup time, + * expiration time, and effective time. + */ + + cd9660_time_915(p->attr.rr_entry.TF.timestamp, + _node->inode->st.st_mtime); + p->attr.rr_entry.TF.h.length[0] += 7; + + cd9660_time_915(p->attr.rr_entry.TF.timestamp + 7, + _node->inode->st.st_atime); + p->attr.rr_entry.TF.h.length[0] += 7; + + cd9660_time_915(p->attr.rr_entry.TF.timestamp + 14, + _node->inode->st.st_ctime); + p->attr.rr_entry.TF.h.length[0] += 7; + return 1; +} + +int +cd9660_susp_sp(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused) +{ + p->attr.su_entry.SP.h.length[0] = 7; + p->attr.su_entry.SP.h.version[0] = 1; + p->attr.su_entry.SP.check[0] = 0xBE; + p->attr.su_entry.SP.check[1] = 0xEF; + p->attr.su_entry.SP.len_skp[0] = 0; + return 1; +} + +int +cd9660_susp_st(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *stinfo __unused) +{ + p->attr.su_entry.ST.h.type[0] = 'S'; + p->attr.su_entry.ST.h.type[1] = 'T'; + p->attr.su_entry.ST.h.length[0] = 4; + p->attr.su_entry.ST.h.version[0] = 1; + return 1; +} + +int +cd9660_susp_ce(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused) +{ + p->attr.su_entry.CE.h.length[0] = 28; + p->attr.su_entry.CE.h.version[0] = 1; + /* Other attributes dont matter right now, will be updated later */ + return 1; +} + +int +cd9660_susp_pd(struct ISO_SUSP_ATTRIBUTES *p __unused, int length __unused) +{ + return 1; +} + +void +cd9660_rrip_add_NM(cd9660node *node, const char *name) +{ + int working,len; + const char *p; + struct ISO_SUSP_ATTRIBUTES *r; + + /* + * Each NM record has 254 bytes to work with. This means that + * the name data itself only has 249 bytes to work with. So, a + * name with 251 characters would require two nm records. + */ + p = name; + working = 1; + while (working) { + r = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_NM, "NM", SUSP_LOC_ENTRY); + r->attr.rr_entry.NM.h.version[0] = 1; + r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_NONE; + len = strlen(p); + + if (len > 249) { + len = 249; + r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_CONTINUE; + } else { + working = 0; + } + memcpy(r->attr.rr_entry.NM.altname, p, len); + r->attr.rr_entry.NM.h.length[0] = 5 + len; + + TAILQ_INSERT_TAIL(&node->head, r, rr_ll); + + p += len; + } +} + +void +cd9660_rrip_NM(cd9660node *node) +{ + cd9660_rrip_add_NM(node, node->node->name); +} + +struct ISO_SUSP_ATTRIBUTES* +cd9660_susp_ER(cd9660node *node, + u_char ext_version, const char* ext_id, const char* ext_des, + const char* ext_src) +{ + int l; + struct ISO_SUSP_ATTRIBUTES *r; + + r = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_ER, "ER", SUSP_LOC_DOT); + + /* Fixed data is 8 bytes */ + r->attr.su_entry.ER.h.length[0] = 8; + r->attr.su_entry.ER.h.version[0] = 1; + + r->attr.su_entry.ER.len_id[0] = (u_char)strlen(ext_id); + r->attr.su_entry.ER.len_des[0] = (u_char)strlen(ext_des); + r->attr.su_entry.ER.len_src[0] = (u_char)strlen(ext_src); + + l = r->attr.su_entry.ER.len_id[0] + + r->attr.su_entry.ER.len_src[0] + + r->attr.su_entry.ER.len_des[0]; + + /* Everything must fit. */ + assert(l + r->attr.su_entry.ER.h.length[0] <= 254); + + r->attr.su_entry.ER.h.length[0] += (u_char)l; + + + r->attr.su_entry.ER.ext_ver[0] = ext_version; + memcpy(r->attr.su_entry.ER.ext_data, ext_id, + (int)r->attr.su_entry.ER.len_id[0]); + l = (int) r->attr.su_entry.ER.len_id[0]; + memcpy(r->attr.su_entry.ER.ext_data + l,ext_des, + (int)r->attr.su_entry.ER.len_des[0]); + + l += (int)r->attr.su_entry.ER.len_des[0]; + memcpy(r->attr.su_entry.ER.ext_data + l,ext_src, + (int)r->attr.su_entry.ER.len_src[0]); + + TAILQ_INSERT_TAIL(&node->head, r, rr_ll); + return r; +} + +struct ISO_SUSP_ATTRIBUTES* +cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES *last __unused, cd9660node *node __unused) +{ + return NULL; +} diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.h b/usr.sbin/makefs/cd9660/iso9660_rrip.h new file mode 100644 index 000000000000..4c738d27ba45 --- /dev/null +++ b/usr.sbin/makefs/cd9660/iso9660_rrip.h @@ -0,0 +1,292 @@ +/* $NetBSD: iso9660_rrip.h,v 1.5 2009/01/10 22:06:29 bjh21 Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#ifndef __ISO9660_RRIP_H__ +#define __ISO9660_RRIP_H__ + +/* + * This will hold all the functions needed to + * write an ISO 9660 image with Rock Ridge Extensions + */ + +/* For writing must use ISO_RRIP_EXTREF structure */ + +#include "makefs.h" +#include <cd9660_rrip.h> +#include "cd9660.h" +#include <sys/queue.h> + +#define PX_LENGTH 0x2C +#define PN_LENGTH 0x14 +#define TF_CREATION 0x01 +#define TF_MODIFY 0x02 +#define TF_ACCESS 0x04 +#define TF_ATTRIBUTES 0x08 +#define TF_BACKUP 0x10 +#define TF_EXPIRATION 0x20 +#define TF_EFFECTIVE 0x40 +#define TF_LONG_FORM 0x80 +#define NM_CONTINUE 0x01 +#define NM_CURRENT 0x02 +#define NM_PARENT 0x04 + + +#define SUSP_LOC_ENTRY 0x01 +#define SUSP_LOC_DOT 0x02 +#define SUSP_LOC_DOTDOT 0x04 + +#define SUSP_TYPE_SUSP 1 +#define SUSP_TYPE_RRIP 2 + +#define SUSP_ENTRY_SUSP_CE 1 +#define SUSP_ENTRY_SUSP_PD 2 +#define SUSP_ENTRY_SUSP_SP 3 +#define SUSP_ENTRY_SUSP_ST 4 +#define SUSP_ENTRY_SUSP_ER 5 +#define SUSP_ENTRY_SUSP_ES 6 + +#define SUSP_ENTRY_RRIP_PX 1 +#define SUSP_ENTRY_RRIP_PN 2 +#define SUSP_ENTRY_RRIP_SL 3 +#define SUSP_ENTRY_RRIP_NM 4 +#define SUSP_ENTRY_RRIP_CL 5 +#define SUSP_ENTRY_RRIP_PL 6 +#define SUSP_ENTRY_RRIP_RE 7 +#define SUSP_ENTRY_RRIP_TF 8 +#define SUSP_ENTRY_RRIP_SF 9 + +#define SUSP_RRIP_ER_EXT_ID "IEEE_P1282" +#define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS." +#define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION." + +#define SL_FLAGS_NONE 0 +#define SL_FLAGS_CONTINUE 1 +#define SL_FLAGS_CURRENT 2 +#define SL_FLAGS_PARENT 4 +#define SL_FLAGS_ROOT 8 + +typedef struct { + ISO_SUSP_HEADER h; + u_char mode [ISODCL(5,12)]; + u_char links [ISODCL(13,20)]; + u_char uid [ISODCL(21,28)]; + u_char gid [ISODCL(29,36)]; + u_char serial [ISODCL(37,44)]; +} ISO_RRIP_PX; + +typedef struct { + ISO_SUSP_HEADER h; + u_char high [ISODCL(5,12)]; + u_char low [ISODCL(13,20)]; +} ISO_RRIP_PN; + +typedef struct { + ISO_SUSP_HEADER h; + u_char flags [ISODCL ( 4, 4)]; + u_char component [ISODCL ( 4, 256)]; + u_int nBytes; +} ISO_RRIP_SL; + +typedef struct { + ISO_SUSP_HEADER h; + u_char flags [ISODCL ( 4, 4)]; + u_char timestamp [ISODCL ( 5, 256)]; +} ISO_RRIP_TF; + +#define RRIP_NM_FLAGS_NONE 0x00 +#define RRIP_NM_FLAGS_CONTINUE 0x01 +#define RRIP_NM_FLAGS_CURRENT 0x02 +#define RRIP_NM_FLAGS_PARENT 0x04 + +typedef struct { + ISO_SUSP_HEADER h; + u_char flags [ISODCL ( 4, 4)]; + u_char altname [ISODCL ( 4, 256)]; +} ISO_RRIP_NM; + +/* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */ +typedef struct { + ISO_SUSP_HEADER h; + u_char ca_sector [ISODCL ( 5, 12)]; + u_char offset [ISODCL ( 13, 20)]; + u_char length [ISODCL ( 21, 28)]; +} ISO_SUSP_CE; + +typedef struct { + ISO_SUSP_HEADER h; + u_char padding_area [ISODCL ( 4, 256)]; +} ISO_SUSP_PD; + +typedef struct { + ISO_SUSP_HEADER h; + u_char check [ISODCL ( 4, 5)]; + u_char len_skp [ISODCL ( 6, 6)]; +} ISO_SUSP_SP; + +typedef struct { + ISO_SUSP_HEADER h; +} ISO_SUSP_ST; + +typedef struct { + ISO_SUSP_HEADER h; + u_char len_id [ISODCL ( 4, 4)]; + u_char len_des [ISODCL ( 5, 5)]; + u_char len_src [ISODCL ( 6, 6)]; + u_char ext_ver [ISODCL ( 7, 7)]; + u_char ext_data [ISODCL (8,256)]; +/* u_char ext_id [ISODCL ( 8, 256)]; + u_char ext_des [ISODCL ( 257, 513)]; + u_char ext_src [ISODCL ( 514, 770)];*/ +} ISO_SUSP_ER; + +typedef struct { + ISO_SUSP_HEADER h; + u_char ext_seq [ISODCL ( 4, 4)]; +} ISO_SUSP_ES; + +typedef union { + ISO_RRIP_PX PX; + ISO_RRIP_PN PN; + ISO_RRIP_SL SL; + ISO_RRIP_NM NM; + ISO_RRIP_CLINK CL; + ISO_RRIP_PLINK PL; + ISO_RRIP_RELDIR RE; + ISO_RRIP_TF TF; +} rrip_entry; + +typedef union { + ISO_SUSP_CE CE; + ISO_SUSP_PD PD; + ISO_SUSP_SP SP; + ISO_SUSP_ST ST; + ISO_SUSP_ER ER; + ISO_SUSP_ES ES; +} susp_entry; + +typedef union { + susp_entry su_entry; + rrip_entry rr_entry; +} SUSP_ENTRIES; + +struct ISO_SUSP_ATTRIBUTES { + SUSP_ENTRIES attr; + int type; + char type_of[2]; + char last_in_suf; /* last entry in the System Use Field? */ + /* Dan's addons - will merge later. This allows use of a switch */ + char susp_type; /* SUSP or RRIP */ + char entry_type; /* Record type */ + char write_location; + TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll; +}; + +#define CD9660_SUSP_ENTRY_SIZE(entry)\ + ((int) ((entry)->attr.su_entry.SP.h.length[0])) + +/* Recursive function - move later to func pointer code*/ +int cd9660_susp_finalize(iso9660_disk *, cd9660node *); + +/* These two operate on single nodes */ +int cd9660_susp_finalize_node(iso9660_disk *, cd9660node *); +int cd9660_rrip_finalize_node(cd9660node *); + +/* POSIX File attribute */ +int cd9660node_rrip_px(iso9660_disk *, struct ISO_SUSP_ATTRIBUTES *, fsnode *); + +/* Device number */ +int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *); + +/* Symbolic link */ +int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *); + +/* Alternate Name function */ +void cd9660_rrip_NM(cd9660node *); +void cd9660_rrip_add_NM(cd9660node *,const char *); + +/* Parent and child link function */ +int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); + +int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *); + + + +/* + * Relocation directory function. I'm not quite sure what + * sort of parameters are needed, but personally I don't think + * any parameters are needed except for the memory address where + * the information needs to be put in + */ +int cd9660node_rrip_re(void *, fsnode *); + +/* + * Don't know if this function is needed because it apparently is an + * optional feature that does not really need to be implemented but I + * thought I should add it anyway. + */ +int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int); +int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); + +struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *, + const char *, const char *); +struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*, + cd9660node *); + + +/* Helper functions */ + +/* Common SUSP/RRIP functions */ +int cd9660_susp_initialize(iso9660_disk *, cd9660node *, cd9660node *, + cd9660node *); +int cd9660_susp_initialize_node(iso9660_disk *, cd9660node *); +struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *, + int); +struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *, + struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int); + +/* RRIP specific functions */ +int cd9660_rrip_initialize_node(iso9660_disk *, cd9660node *, cd9660node *, + cd9660node *); +void cd9660_createSL(cd9660node *); + +/* Functions that probably can be removed */ +/* int cd9660node_initialize_node(int, char *); */ + + +#endif |
