From 5b2c5d03e874e4861b2e75221e1ee37492682e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 13 Nov 2007 14:55:11 +0000 Subject: [PATCH] * Fixed a crashing bug in the identification code: it copied a C++ object on the stack to an allocated one - on destruction of the latter, the resources were already freed. * Made the identify code more negligent against bad CDs - ie. it will identify even broken CDs if they can be mounted. * Made identification endian aware (it should now also work on big endian systems). * Renamed many structures, methods, and fields to be less verbose, and follow our style guide. * Renamed iso9660.cpp|h to iso9660_identify.cpp|h. * Renamed iso.c to iso9660.c, rock.h to rock_ridge.h. * Removed unnecessary cruft from the Jamfile. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22920 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/file_systems/iso9660/Jamfile | 26 +- .../file_systems/iso9660/{iso.c => iso9660.c} | 7 +- .../kernel/file_systems/iso9660/iso9660.cpp | 564 ------------------ .../file_systems/iso9660/iso9660_identify.cpp | 501 ++++++++++++++++ .../iso9660/{iso9660.h => iso9660_identify.h} | 30 +- .../file_systems/iso9660/kernel_interface.cpp | 110 +--- .../iso9660/{rock.h => rock_ridge.h} | 11 +- 7 files changed, 563 insertions(+), 686 deletions(-) rename src/add-ons/kernel/file_systems/iso9660/{iso.c => iso9660.c} (99%) delete mode 100644 src/add-ons/kernel/file_systems/iso9660/iso9660.cpp create mode 100644 src/add-ons/kernel/file_systems/iso9660/iso9660_identify.cpp rename src/add-ons/kernel/file_systems/iso9660/{iso9660.h => iso9660_identify.h} (53%) rename src/add-ons/kernel/file_systems/iso9660/{rock.h => rock_ridge.h} (93%) diff --git a/src/add-ons/kernel/file_systems/iso9660/Jamfile b/src/add-ons/kernel/file_systems/iso9660/Jamfile index 63d8fdd7e5..80c68f0013 100644 --- a/src/add-ons/kernel/file_systems/iso9660/Jamfile +++ b/src/add-ons/kernel/file_systems/iso9660/Jamfile @@ -1,27 +1,13 @@ SubDir HAIKU_TOP src add-ons kernel file_systems iso9660 ; -# save original optimization level -oldOPTIM = $(OPTIM) ; - -# set some additional defines -{ - SubDirCcFlags -Wall -Wno-multichar ; - SubDirC++Flags -Wall -Wno-multichar -fno-rtti ; -} - UsePrivateHeaders kernel ; +# set some additional defines +SubDirCcFlags -Wall -Wno-multichar ; +SubDirC++Flags -Wall -Wno-multichar -fno-rtti ; + KernelAddon iso9660 : - iso.c - iso9660.cpp + iso9660.c + iso9660_identify.cpp kernel_interface.cpp - kernel_cpp.cpp ; - -SEARCH on [ FGristFiles - kernel_cpp.cpp - ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; - -# restore original optimization level -OPTIM = $(oldOPTIM) ; - diff --git a/src/add-ons/kernel/file_systems/iso9660/iso.c b/src/add-ons/kernel/file_systems/iso9660/iso9660.c similarity index 99% rename from src/add-ons/kernel/file_systems/iso9660/iso.c rename to src/add-ons/kernel/file_systems/iso9660/iso9660.c index f8c94944b7..dba53af24f 100644 --- a/src/add-ons/kernel/file_systems/iso9660/iso.c +++ b/src/add-ons/kernel/file_systems/iso9660/iso9660.c @@ -6,7 +6,6 @@ */ -#include "rock.h" #include "iso.h" #include @@ -24,6 +23,8 @@ #include #include +#include "rock_ridge.h" + //#define TRACE_ISO9660 1 #if TRACE_ISO9660 # define TRACE(x) dprintf x @@ -139,7 +140,7 @@ unicode_to_utf8(const char *src, int32 *srcLen, char *dst, int32 *dstLen) int32 dstLimit = *dstLen; int32 srcCount = 0; int32 dstCount = 0; - + for (srcCount = 0; srcCount < srcLimit; srcCount += 2) { uint16 *UNICODE = (uint16 *)&src[srcCount]; uchar utf8[4]; @@ -161,7 +162,7 @@ unicode_to_utf8(const char *src, int32 *srcLen, char *dst, int32 *dstLen) *srcLen = srcCount; *dstLen = dstCount; - return ((dstCount > 0) ? B_NO_ERROR : B_ERROR); + return dstCount > 0 ? B_NO_ERROR : B_ERROR; } diff --git a/src/add-ons/kernel/file_systems/iso9660/iso9660.cpp b/src/add-ons/kernel/file_systems/iso9660/iso9660.cpp deleted file mode 100644 index e2b3b8637c..0000000000 --- a/src/add-ons/kernel/file_systems/iso9660/iso9660.cpp +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright 2002, Tyler Dauwalder. - * This file may be used under the terms of the MIT License. - */ -/*! - \file iso9660.cpp - \brief disk_scanner filesystem module for iso9660 CD-ROM filesystems - -
iso9660
- The standard to which this module is written is ECMA-119 second - edition, a freely available iso9660 equivalent. - -
Joliet
- Joliet support comes courtesy of the following document: - - http://www-plateau.cs.berkeley.edu/people/chaffee/jolspec.htm - - As specified there, the existence of any of the following escape - sequences in a supplementary volume descriptor's "escape sequences" - field denotes a Joliet volume descriptor using unicode ucs-2 - character encoding (2-byte characters, big-endian): - - - UCS-2 Level 1: 0x252F40 == "%/@" - - UCS-2 Level 2: 0x252F43 == "%/C" - - UCS-2 Level 3: 0x252F45 == "%/E" - - The following UCS-2 characters are considered illegal (we allow them, - printing out a warning if encountered): - - - All values between 0x0000 and 0x001f inclusive == control chars - - 0x002A == '*' - - 0x002F == '/' - - 0x003A == ':' - - 0x003B == ';' - - 0x003F == '?' - - 0x005C == '\' -*/ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "iso.h" -#include "iso9660.h" - -//#define TRACE(x) ; -#define TRACE(x) dprintf x - -// misc constants -static const char *kISO9660Signature = "CD001"; -static const uint32 kVolumeDescriptorLength = 2048; -#define ISO9660_VOLUME_IDENTIFIER_LENGTH 32 -#define ISO9660_ESCAPE_SEQUENCE_LENGTH 32 - -//! Volume descriptor types -typedef enum { - ISO9660VD_BOOT, - ISO9660VD_PRIMARY, - ISO9660VD_SUPPLEMENTARY, - ISO9660VD_PARTITION, - ISO9660VD_TERMINATOR = 255 -} iso9660_volume_descriptor_type; - -/*! \brief The portion of the volume descriptor common to all - descriptor types. -*/ -typedef struct iso9660_common_volume_descriptor { - uchar volume_descriptor_type; - char standard_identifier[5]; // should be 'CD001' - uchar volume_descriptor_version; - // Remaining bytes are unused -} __attribute__((packed)) iso9660_common_volume_descriptor; - -/*! \brief Primary volume descriptor -*/ -typedef struct iso9660_primary_volume_descriptor { - iso9660_common_volume_descriptor info; - uchar volume_flags; - char system_identifier[32]; - char volume_identifier[ISO9660_VOLUME_IDENTIFIER_LENGTH]; - uchar unused01[8]; - uint32 volume_space_size_little_endian; - uint32 volume_space_size_big_endian; - uchar unused02[ISO9660_ESCAPE_SEQUENCE_LENGTH]; - uint16 volume_set_size_little_endian; - uint16 volume_set_size_big_endian; - uint16 volume_sequence_number_little_endian; - uint16 volume_sequence_number_big_endian; - uint16 logical_block_size_little_endian; - uint16 logical_block_size_big_endian; - uint32 path_table_size_little_endian; - uint32 path_table_size_big_endian; - uint32 ignored02[4]; - uchar root_directory_record[34]; - char volume_set_identifier[28]; - // Remaining bytes are disinteresting to us -} __attribute__((packed)) iso9660_primary_volume_descriptor; - -typedef struct iso9660_supplementary_volume_descriptor { - iso9660_common_volume_descriptor info; - uchar volume_flags; - char system_identifier[32]; - char volume_identifier[ISO9660_VOLUME_IDENTIFIER_LENGTH]; - uchar unused01[8]; - uint32 volume_space_size_little_endian; - uint32 volume_space_size_big_endian; - char escape_sequences[ISO9660_ESCAPE_SEQUENCE_LENGTH]; - uint16 volume_set_size_little_endian; - uint16 volume_set_size_big_endian; - uint16 volume_sequence_number_little_endian; - uint16 volume_sequence_number_big_endian; - uint16 logical_block_size_little_endian; - uint16 logical_block_size_big_endian; - uint32 path_table_size_little_endian; - uint32 path_table_size_big_endian; - uint32 ignored02[4]; - uchar root_directory_record[34]; - char volume_set_identifier[28]; - // Remaining bytes are disinteresting to us -} __attribute__((packed)) iso9660_supplementary_volume_descriptor; - -typedef struct iso9660_directory_record { - uint8 length; - uint8 extended_attribute_record_length; - uint32 location_le; - uint32 location_be; - uint32 data_length; - uchar ignored[14]; - uint16 volume_space_le; -} __attribute__((packed)) iso9660_directory_record; - - -static void dump_directory_record(iso9660_directory_record *record, const char *indent); - - -//---------------------------------------------------------------------------- -// iso9660_info -//---------------------------------------------------------------------------- - -/*! \brief Creates a new iso9660_info struct with empty volume names. - - \note Use the applicable set_XYZ_volume_name() functions rather than - messing with the volume name data members directly. -*/ -iso9660_info::iso9660_info() - : iso9660_volume_name(NULL) - , joliet_volume_name(NULL) -{ -} - -/*! \brief Destroys the struct, freeing the volume name strings. -*/ -iso9660_info::~iso9660_info() -{ - if (iso9660_volume_name) { - free(iso9660_volume_name); - iso9660_volume_name = NULL; - } - if (joliet_volume_name) { - free(joliet_volume_name); - joliet_volume_name = NULL; - } -} - -/*! \brief Returns true if a valid volume name exists. -*/ -bool -iso9660_info::is_valid() -{ - return iso9660_volume_name || joliet_volume_name; -} - -/*! \brief Sets the iso9660 volume name. - - \param name UTF-8 string containing the name. - \param length The length (in bytes) of the string. -*/ -void -iso9660_info::set_iso9660_volume_name(const char *name, uint32 length) -{ - set_string(&iso9660_volume_name, name, length); -} - -/*! \brief Sets the Joliet volume name. - - \param name UTF-8 string containing the name. - \param length The length (in bytes) of the string. -*/ -void -iso9660_info::set_joliet_volume_name(const char *name, uint32 length) -{ - set_string(&joliet_volume_name, name, length); -} - -/*! \brief Returns the volume name of highest precedence. - - Currently, the ordering is (decreasingly): - - Joliet - - iso9660 -*/ -const char* -iso9660_info::get_preferred_volume_name() -{ - if (joliet_volume_name) - return joliet_volume_name; - else - return iso9660_volume_name; -} - -/*! \brief Copies the given string into the old string, managing memory - deallocation and allocation as necessary. -*/ -void -iso9660_info::set_string(char **string, const char *new_string, uint32 new_length) -{ - TRACE(("iso9660_info::set_string(%p (`%s'), `%s', %ld)\n", string, *string, new_string, new_length)); - if (string) { - char *&old_string = *string; - if (old_string) - free(old_string); - if (new_string) { - old_string = (char*)malloc(new_length+1); - if (old_string) { - strncpy(old_string, new_string, new_length); - old_string[new_length] = 0; - } - } else { - old_string = NULL; - } - } -} - - -// #pragma mark - C functions - - -/*! \brief Converts the given unicode character to utf8. - - Courtesy Mr. Axel Dörfler. - - \todo Once OpenTracker's locale kit is done, perhaps that functionality - should be used rather than outright stealing the code. -*/ -static void -unicode_to_utf8(uint32 c, char **out) -{ - char *s = *out; - - if (c < 0x80) - *(s++) = c; - else if (c < 0x800) { - *(s++) = 0xc0 | (c>>6); - *(s++) = 0x80 | (c & 0x3f); - } else if (c < 0x10000) { - *(s++) = 0xe0 | (c>>12); - *(s++) = 0x80 | ((c>>6) & 0x3f); - *(s++) = 0x80 | (c & 0x3f); - } else if (c <= 0x10ffff) { - *(s++) = 0xf0 | (c>>18); - *(s++) = 0x80 | ((c>>12) & 0x3f); - *(s++) = 0x80 | ((c>>6) & 0x3f); - *(s++) = 0x80 | (c & 0x3f); - } - *out = s; -} - - -static const char* -volume_descriptor_type_to_string(iso9660_volume_descriptor_type type) -{ - switch (type) { - case ISO9660VD_BOOT: return "boot"; - case ISO9660VD_PRIMARY: return "primary"; - case ISO9660VD_SUPPLEMENTARY: return "supplementary"; - case ISO9660VD_PARTITION: return "partiton"; - case ISO9660VD_TERMINATOR: return "terminator"; - default: return "invalid"; - } -} - - -static void -dump_common_volume_descriptor(iso9660_common_volume_descriptor *common, - const char *indent, bool print_header) -{ - if (print_header) - TRACE(("%siso9660_common_volume_descriptor:\n", indent)); - - TRACE(("%s volume descriptor type == %d (%s)\n", indent, - common->volume_descriptor_type, - volume_descriptor_type_to_string((iso9660_volume_descriptor_type)common->volume_descriptor_type))); - TRACE(("%s standard identifier == %.5s (%s)\n", indent, common->standard_identifier, - strncmp(common->standard_identifier, kISO9660Signature, 5) == 0 ? "valid" : "INVALID")); - TRACE(("%s volume descriptor version == %d\n", indent, common->volume_descriptor_version)); -} - - -static void -dump_primary_volume_descriptor(iso9660_primary_volume_descriptor *primary, - const char *indent, bool print_header) -{ - if (print_header) - TRACE(("%siso9660_primary_volume_descriptor:\n", indent)); - - dump_common_volume_descriptor(&(primary->info), indent, false); - TRACE(("%s volume identifier == `%.32s'\n", indent, - primary->volume_identifier)); - TRACE(("%s volume space size == %ld\n", indent, - primary->volume_space_size_little_endian)); - TRACE(("%s volume set size == %d\n", indent, - primary->volume_set_size_little_endian)); - TRACE(("%s volume sequence number == %d\n", indent, - primary->volume_sequence_number_little_endian)); - TRACE(("%s logical block size == %d\n", indent, - primary->logical_block_size_little_endian)); - TRACE(("%s path table size == %ld\n", indent, - primary->path_table_size_little_endian)); - TRACE(("%s volume set identifier == %.28s\n", indent, - primary->volume_set_identifier)); - dump_directory_record((iso9660_directory_record*)primary->root_directory_record, indent); -} - - -static void -dump_supplementary_volume_descriptor(iso9660_supplementary_volume_descriptor *supplementary, - const char *indent, bool print_header) -{ - if (print_header) - TRACE(("%siso9660_supplementary_volume_descriptor:\n", indent)); - - dump_primary_volume_descriptor((iso9660_primary_volume_descriptor*)supplementary, - indent, false); - TRACE(("%s escape sequences ==", indent)); - for (int i = 0; i < ISO9660_ESCAPE_SEQUENCE_LENGTH; i++) { - TRACE((" %2x", supplementary->escape_sequences[i])); - if (i == ISO9660_ESCAPE_SEQUENCE_LENGTH/2-1) - TRACE(("\n ")); - } - TRACE(("\n")); -} - - -static void -dump_directory_record(iso9660_directory_record *record, const char *indent) -{ - TRACE(("%s root directory record:\n", indent)); - TRACE(("%s length == %d\n", indent, record->length)); - TRACE(("%s location == %ld\n", indent, record->location_le)); - TRACE(("%s data length == %ld\n", indent, record->data_length)); - TRACE(("%s volume sequence number == %d\n", indent, record->volume_space_le)); -} - - -static status_t -check_common_volume_descriptor(iso9660_common_volume_descriptor *common) -{ - status_t error = common ? B_OK : B_BAD_VALUE; - if (!error) { - error = strncmp(common->standard_identifier, kISO9660Signature, 5) == 0 - ? B_OK : B_BAD_DATA; - } - return error; -} - - -// #pragma mark - Public functions - - -// iso9660_fs_identify -/*! \brief Returns true if the given partition is a valid iso9660 partition. - - See fs_identify_hook() for more information. - - \todo Fill in partitionInfo->mounted_at with something useful. -*/ -status_t -iso9660_fs_identify(int deviceFD, iso9660_info *info) -{ - char buffer[ISO_PVD_SIZE]; - bool exit = false; - status_t error = B_OK; - nspace *vol; - - TRACE(("identify(%d, %p)\n", deviceFD, info)); - off_t offset = 0x8000; - - vol = (nspace *)calloc(sizeof(nspace), 1); - if (vol == NULL) - return ENOMEM; - - vol->fd = deviceFD; - - // Read through the volume descriptors looking for a primary descriptor. - // If for some reason there are more than one primary descriptor, the - // volume name from the last encountered descriptor will be used. - while (!error && !exit) {// && count++ < 10) { - iso9660_common_volume_descriptor *common = NULL; - - // Read the block containing the current descriptor - error = read_pos (vol->fd, offset, (void *)&buffer, ISO_PVD_SIZE); - offset += ISO_PVD_SIZE; - if (error < ISO_PVD_SIZE) - break; - - common = (iso9660_common_volume_descriptor*)buffer; - error = check_common_volume_descriptor(common); -// dump_common_volume_descriptor(common, "", true); - - // Handle each type of descriptor appropriately - if (!error) { - TRACE(("found %s descriptor\n", volume_descriptor_type_to_string((iso9660_volume_descriptor_type)common->volume_descriptor_type))); - - switch (common->volume_descriptor_type) { - case ISO9660VD_BOOT: - break; - - case ISO9660VD_PRIMARY: - { - int i; - iso9660_primary_volume_descriptor *primary = (iso9660_primary_volume_descriptor*)buffer; - - dump_primary_volume_descriptor(primary, " ", true); - - // Cut off any trailing spaces from the volume id. Note - // that this allows for spaces INSIDE the volume id, even - // though that's not technically allowed by the standard; - // this was necessary to support certain RedHat 6.2 CD-ROMs - // from a certain Linux company who shall remain unnamed. ;-) - for (i = ISO9660_VOLUME_IDENTIFIER_LENGTH-1; i >= 0; i--) { - if (primary->volume_identifier[i] != 0x20) - break; - } - - // Give a holler if the iso9660 name is already set - if (info->iso9660_volume_name) { - char str[ISO9660_VOLUME_IDENTIFIER_LENGTH+1]; - strncpy(str, primary->volume_identifier, i+1); - str[i+1] = 0; - TRACE(("duplicate iso9660 volume name found, using latter (`%s') " - "instead of former (`%s')\n", str, - info->iso9660_volume_name)); - } - - info->set_iso9660_volume_name(primary->volume_identifier, i+1); - info->maxBlocks = primary->volume_set_size_little_endian; - break; - } - - case ISO9660VD_SUPPLEMENTARY: - { - iso9660_supplementary_volume_descriptor *supplementary = (iso9660_supplementary_volume_descriptor*)buffer; - dump_supplementary_volume_descriptor((iso9660_supplementary_volume_descriptor*)supplementary, " ", true); - - // Copy and null terminate the escape sequences - char escapes[ISO9660_ESCAPE_SEQUENCE_LENGTH+1]; - strncpy(escapes, supplementary->escape_sequences, ISO9660_ESCAPE_SEQUENCE_LENGTH); - escapes[ISO9660_ESCAPE_SEQUENCE_LENGTH] = 0; - - // Check for a Joliet VD - if (strstr(escapes, "%/@") || strstr(escapes, "%/C") || strstr(escapes, "%/E")) { - char str[(ISO9660_VOLUME_IDENTIFIER_LENGTH*3/2)+1]; - // Since we're dealing with 16-bit Unicode, each UTF-8 sequence - // will be at most 3 bytes long. So we need 3/2 as many chars as - // we start out with. - char *str_iterator = str; - uint16 ch; - - // Walk thru the unicode volume name, converting to utf8 as we go. - for (int i = 0; - (ch = B_BENDIAN_TO_HOST_INT16(((uint16*)supplementary->volume_identifier)[i])) - && i < ISO9660_VOLUME_IDENTIFIER_LENGTH; - i++) { - // Give a warning if the character is technically illegal - if ( ch <= 0x001F - || ch == 0x002A - || ch == 0x002F - || ch == 0x003A - || ch == 0x003B - || ch == 0x003F - || ch == 0x005C) - { - TRACE(("warning: illegal Joliet character found: 0%4x\n", ch)); - } - - // Convert to utf-8 - unicode_to_utf8(ch, &str_iterator); - } - *str_iterator = 0; - - // Give a holler if the joliet name is already set - if (info->joliet_volume_name) { - TRACE(("duplicate joliet volume name found, using latter (`%s') " - "instead of former (`%s')\n", str, - info->joliet_volume_name)); - } - - info->set_joliet_volume_name(str, strlen(str)); - } // end "if Joliet VD" - break; - } - - case ISO9660VD_PARTITION: - break; - - case ISO9660VD_TERMINATOR: - exit = true; - break; - - default: - break; - } - } - } - - free(vol); - - return error; -/* - switch (error) { - case B_OK: - if (info.is_valid()) { - result = true; - if (partitionInfo->file_system_short_name) - strcpy(partitionInfo->file_system_short_name, "iso9660"); - if (partitionInfo->file_system_long_name) - strcpy(partitionInfo->file_system_long_name, "iso9660 CD-ROM File System"); - partitionInfo->file_system_flags = B_FS_IS_PERSISTENT; - if (priority) - *priority = 0; - // Copy the volume name of highest precedence - if (partitionInfo->volume_name) { - TRACE(("%s: iso9660 name: `%s'\n", kModuleDebugName, info.iso9660_volume_name)); - TRACE(("%s: joliet name: `%s'\n", kModuleDebugName, info.joliet_volume_name)); - const char *name = info.get_preferred_volume_name(); - int length = strlen(name); - if (length > B_FILE_NAME_LENGTH-1) - length = B_FILE_NAME_LENGTH-1; - strncpy(partitionInfo->volume_name, name, length); - partitionInfo->volume_name[length] = 0; - } - return 0.8; - } - break; - - case B_BAD_DATA: - TRACE(("%s: identify: bad signature\n", kModuleDebugName)); - break; - - default: - TRACE(("%s: identify error: 0x%lx\n", kModuleDebugName, - error)); - break; - } - - return 0.0; -*/ -} - diff --git a/src/add-ons/kernel/file_systems/iso9660/iso9660_identify.cpp b/src/add-ons/kernel/file_systems/iso9660/iso9660_identify.cpp new file mode 100644 index 0000000000..2087febe70 --- /dev/null +++ b/src/add-ons/kernel/file_systems/iso9660/iso9660_identify.cpp @@ -0,0 +1,501 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2002, Tyler Dauwalder. + * + * This file may be used under the terms of the MIT License. + */ + +/*! +
iso9660
+ The standard to which this module is written is ECMA-119 second + edition, a freely available iso9660 equivalent. + +
Joliet
+ Joliet support comes courtesy of the following document: + + http://www-plateau.cs.berkeley.edu/people/chaffee/jolspec.htm + + As specified there, the existence of any of the following escape + sequences in a supplementary volume descriptor's "escape sequences" + field denotes a Joliet volume descriptor using unicode ucs-2 + character encoding (2-byte characters, big-endian): + + - UCS-2 Level 1: 0x252F40 == "%/@" + - UCS-2 Level 2: 0x252F43 == "%/C" + - UCS-2 Level 3: 0x252F45 == "%/E" + + The following UCS-2 characters are considered illegal (we allow them, + printing out a warning if encountered): + + - All values between 0x0000 and 0x001f inclusive == control chars + - 0x002A == '*' + - 0x002F == '/' + - 0x003A == ':' + - 0x003B == ';' + - 0x003F == '?' + - 0x005C == '\' +*/ + +#include "iso9660_identify.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iso.h" + +//#define TRACE(x) ; +#define TRACE(x) dprintf x + +// misc constants +static const char *kISO9660Signature = "CD001"; +static const uint32 kVolumeDescriptorLength = 2048; +#define ISO9660_VOLUME_IDENTIFIER_LENGTH 32 +#define ISO9660_ESCAPE_SEQUENCE_LENGTH 32 + +//! Volume descriptor types +typedef enum { + ISO9660VD_BOOT, + ISO9660VD_PRIMARY, + ISO9660VD_SUPPLEMENTARY, + ISO9660VD_PARTITION, + ISO9660VD_TERMINATOR = 255 +} iso9660_descriptor_type; + +/*! \brief The portion of the volume descriptor common to all + descriptor types. +*/ +typedef struct iso9660_common_descriptor { + uchar type; + char standard_identifier[5]; // should be 'CD001' + uchar version; + // Remaining bytes are unused +} __attribute__((packed)) iso9660_common_volume_descriptor; + +typedef struct iso9660_volume_descriptor { + iso9660_common_descriptor common; + uchar flags; + char system_identifier[32]; + char identifier[ISO9660_VOLUME_IDENTIFIER_LENGTH]; + uchar _reserved0[8]; + uint32 size; + uint32 size_big_endian; + char escape_sequences[ISO9660_ESCAPE_SEQUENCE_LENGTH]; + // unused on primary descriptor + uint16 set_size; + uint16 set_size_big_endian; + uint16 sequence_number; + uint16 sequence_number_big_endian; + uint16 logical_block_size; + uint16 logical_block_size_big_endian; + uint32 path_table_size; + uint32 path_table_size_big_endian; + uint32 _reserved1[4]; + uchar root_directory_record[34]; + char set_identifier[28]; + // Remaining bytes are disinteresting to us +} __attribute__((packed)) iso9660_volume_descriptor; + +typedef struct iso9660_directory_record { + uint8 length; + uint8 extended_attribute_record_length; + uint32 location; + uint32 location_big_endian; + uint32 data_length; + uchar _reserved[14]; + uint16 volume_space; +} __attribute__((packed)) iso9660_directory_record; + + +static void dump_directory_record(iso9660_directory_record *record, + const char *indent); + + +// #pragma mark - + + +/*! \brief Creates a new iso9660_info struct with empty volume names. + + \note Use the applicable set_XYZ_volume_name() functions rather than + messing with the volume name data members directly. +*/ +iso9660_info::iso9660_info() + : + iso9660_name(NULL), + joliet_name(NULL) +{ +} + + +iso9660_info::~iso9660_info() +{ + free(iso9660_name); + free(joliet_name); +} + + +/*! \brief Returns true if a valid volume name exists. +*/ +bool +iso9660_info::IsValid() +{ + return iso9660_name != NULL || joliet_name != NULL; +} + + +/*! \brief Sets the iso9660 volume name. + + \param name UTF-8 string containing the name. + \param length The length (in bytes) of the string. +*/ +void +iso9660_info::SetISO9660Name(const char *name, uint32 length) +{ + _SetString(&iso9660_name, name, length); +} + + +/*! \brief Sets the Joliet volume name. + + \param name UTF-8 string containing the name. + \param length The length (in bytes) of the string. +*/ +void +iso9660_info::SetJolietName(const char *name, uint32 length) +{ + _SetString(&joliet_name, name, length); +} + + +/*! \brief Returns the volume name of highest precedence. + + Currently, the ordering is (decreasingly): + - Joliet + - iso9660 +*/ +const char* +iso9660_info::PreferredName() +{ + if (joliet_name) + return joliet_name; + + return iso9660_name; +} + + +/*! \brief Copies the given string into the old string, managing memory + deallocation and allocation as necessary. +*/ +void +iso9660_info::_SetString(char **string, const char *newString, + uint32 newLength) +{ + TRACE(("iso9660_info::set_string(%p ('%s'), '%s', %ld)\n", string, + *string, newString, newLength)); + if (string == NULL) + return; + + char *&oldString = *string; + free(oldString); + + if (newString) { + oldString = (char*)malloc(newLength + 1); + if (oldString != NULL) { + memcpy(oldString, newString, newLength); + oldString[newLength] = '\0'; + } + } else + oldString = NULL; +} + + +// #pragma mark - C functions + + +/*! \brief Converts the given unicode character to utf8. +*/ +static void +unicode_to_utf8(uint32 c, char **out) +{ + char *s = *out; + + if (c < 0x80) + *(s++) = c; + else if (c < 0x800) { + *(s++) = 0xc0 | (c >> 6); + *(s++) = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + *(s++) = 0xe0 | (c >> 12); + *(s++) = 0x80 | ((c >> 6) & 0x3f); + *(s++) = 0x80 | (c & 0x3f); + } else if (c <= 0x10ffff) { + *(s++) = 0xf0 | (c >> 18); + *(s++) = 0x80 | ((c >> 12) & 0x3f); + *(s++) = 0x80 | ((c >> 6) & 0x3f); + *(s++) = 0x80 | (c & 0x3f); + } + *out = s; +} + + +static const char* +descriptor_type_to_string(iso9660_descriptor_type type) +{ + switch (type) { + case ISO9660VD_BOOT: + return "boot"; + case ISO9660VD_PRIMARY: + return "primary"; + case ISO9660VD_SUPPLEMENTARY: + return "supplementary"; + case ISO9660VD_PARTITION: + return "partiton"; + case ISO9660VD_TERMINATOR: + return "terminator"; + default: + return "invalid"; + } +} + + +static void +dump_common_descriptor(iso9660_common_descriptor *common, + const char *indent, bool printHeader) +{ + if (printHeader) + TRACE(("%siso9660_common_descriptor:\n", indent)); + + TRACE(("%s volume descriptor type: %d (%s)\n", indent, + common->type, descriptor_type_to_string( + (iso9660_descriptor_type)common->type))); + TRACE(("%s standard identifier: %.5s (%s)\n", indent, + common->standard_identifier, + strncmp(common->standard_identifier, kISO9660Signature, 5) == 0 + ? "valid" : "INVALID")); + TRACE(("%s version: %d\n", indent, common->version)); +} + + +static void +dump_primary_descriptor(iso9660_volume_descriptor *primary, + const char *indent, bool printHeader) +{ + if (printHeader) + TRACE(("%siso9660_primary_descriptor:\n", indent)); + + dump_common_descriptor(&primary->common, indent, false); + TRACE(("%s identifier: '%.32s'\n", indent, + primary->identifier)); + TRACE(("%s size: %ld\n", indent, + B_LENDIAN_TO_HOST_INT32(primary->size))); + TRACE(("%s set size: %ld\n", indent, + B_LENDIAN_TO_HOST_INT32(primary->set_size))); + TRACE(("%s sequence number: %ld\n", indent, + B_LENDIAN_TO_HOST_INT32(primary->sequence_number))); + TRACE(("%s logical block size: %ld\n", indent, + B_LENDIAN_TO_HOST_INT32(primary->logical_block_size))); + TRACE(("%s path table size: %ld\n", indent, + B_LENDIAN_TO_HOST_INT32(primary->path_table_size))); + TRACE(("%s set identifier: %.28s\n", indent, + primary->set_identifier)); + dump_directory_record((iso9660_directory_record*) + primary->root_directory_record, indent); +} + + +static void +dump_supplementary_descriptor(iso9660_volume_descriptor *supplementary, + const char *indent, bool printHeader) +{ + if (printHeader) + TRACE(("%siso9660_supplementary_descriptor:\n", indent)); + + dump_primary_descriptor(supplementary, indent, false); + TRACE(("%s escape sequences: ", indent)); + for (int i = 0; i < ISO9660_ESCAPE_SEQUENCE_LENGTH; i++) { + TRACE((" %2x", supplementary->escape_sequences[i])); + if (i == ISO9660_ESCAPE_SEQUENCE_LENGTH / 2 - 1) + TRACE(("\n ")); + } + TRACE(("\n")); +} + + +static void +dump_directory_record(iso9660_directory_record *record, const char *indent) +{ + TRACE(("%s root directory record:\n", indent)); + TRACE(("%s length: %d\n", indent, record->length)); + TRACE(("%s location: %ld\n", indent, + B_LENDIAN_TO_HOST_INT32(record->location))); + TRACE(("%s data length: %ld\n", indent, + B_LENDIAN_TO_HOST_INT32(record->data_length))); + TRACE(("%s volume space: %d\n", indent, + B_LENDIAN_TO_HOST_INT16(record->volume_space))); +} + + +static status_t +check_common_descriptor(iso9660_common_descriptor *common) +{ + if (common == NULL) + return B_BAD_VALUE; + + return strncmp(common->standard_identifier, kISO9660Signature, 5) == 0 + ? B_OK : B_BAD_DATA; +} + + +// #pragma mark - Public functions + + +// iso9660_fs_identify +/*! \brief Returns true if the given partition is a valid iso9660 partition. + + See fs_identify_hook() for more information. + + \todo Fill in partitionInfo->mounted_at with something useful. +*/ +status_t +iso9660_fs_identify(int deviceFD, iso9660_info *info) +{ + char buffer[ISO_PVD_SIZE]; + bool exit = false; + bool found = false; + status_t error = B_OK; + + TRACE(("identify(%d, %p)\n", deviceFD, info)); + off_t offset = 0x8000; + + // Read through the volume descriptors looking for a primary descriptor. + // If for some reason there are more than one primary descriptor, the + // volume name from the last encountered descriptor will be used. + while (!error && !exit) {// && count++ < 10) { + iso9660_common_descriptor *common = NULL; + + // Read the block containing the current descriptor + error = read_pos(deviceFD, offset, (void *)&buffer, ISO_PVD_SIZE); + offset += ISO_PVD_SIZE; + if (error < ISO_PVD_SIZE) + break; + + common = (iso9660_common_descriptor*)buffer; + error = check_common_descriptor(common); + if (error < B_OK) + break; + +// dump_common_descriptor(common, "", true); + + // Handle each type of descriptor appropriately + TRACE(("found %s descriptor\n", descriptor_type_to_string( + (iso9660_descriptor_type)common->type))); + found = true; + + switch (common->type) { + case ISO9660VD_BOOT: + break; + + case ISO9660VD_PRIMARY: + { + iso9660_volume_descriptor *primary + = (iso9660_volume_descriptor*)buffer; + int i; + + dump_primary_descriptor(primary, " ", true); + + // Cut off any trailing spaces from the volume id. Note + // that this allows for spaces INSIDE the volume id, even + // though that's not technically allowed by the standard; + // this was necessary to support certain RedHat 6.2 CD-ROMs + // from a certain Linux company who shall remain unnamed. ;-) + for (i = ISO9660_VOLUME_IDENTIFIER_LENGTH - 1; i >= 0; + i--) { + if (primary->identifier[i] != 0x20) + break; + } + + // Give a holler if the iso9660 name is already set + if (info->iso9660_name) { + char name[ISO9660_VOLUME_IDENTIFIER_LENGTH + 1]; + strlcpy(name, primary->identifier, i + 1); + TRACE(("duplicate iso9660 volume name found, using " + "latter (`%s') instead of former (`%s')\n", name, + info->iso9660_name)); + } + + info->SetISO9660Name(primary->identifier, i + 1); + info->max_blocks = B_LENDIAN_TO_HOST_INT32(primary->set_size); + break; + } + + case ISO9660VD_SUPPLEMENTARY: + { + iso9660_volume_descriptor *supplementary + = (iso9660_volume_descriptor*)buffer; + dump_supplementary_descriptor(supplementary, " ", true); + + // Copy and null terminate the escape sequences + char escapes[ISO9660_ESCAPE_SEQUENCE_LENGTH + 1]; + strlcpy(escapes, supplementary->escape_sequences, + ISO9660_ESCAPE_SEQUENCE_LENGTH + 1); + + // Check for a Joliet VD + if (strstr(escapes, "%/@") || strstr(escapes, "%/C") + || strstr(escapes, "%/E")) { + char name[(ISO9660_VOLUME_IDENTIFIER_LENGTH * 3 / 2) + 1]; + // Since we're dealing with 16-bit Unicode, each + // UTF-8 sequence will be at most 3 bytes long. + char *pos = name; + uint16 ch; + + // Walk thru the unicode volume name, converting to utf8 as we go. + for (int i = 0; (ch = B_BENDIAN_TO_HOST_INT16( + ((uint16*)supplementary->identifier)[i])) + && i < ISO9660_VOLUME_IDENTIFIER_LENGTH; i++) { + // Give a warning if the character is technically + // illegal + if (ch <= 0x001F || ch == '*' || ch == '/' + || ch == ':' || ch == ';' + || ch == '?' || ch == '\\') { + TRACE(("warning: illegal Joliet character " + "found: 0%4x\n", ch)); + } + + // Convert to utf-8 + unicode_to_utf8(ch, &pos); + } + pos[0] = '\0'; + + // Give a holler if the joliet name is already set + if (info->joliet_name) { + TRACE(("duplicate joliet volume name found, using " + "latter (`%s') instead of former (`%s')\n", + name, info->joliet_name)); + } + + info->SetJolietName(name, pos - name); + } + break; + } + + case ISO9660VD_PARTITION: + break; + + case ISO9660VD_TERMINATOR: + exit = true; + break; + + default: + break; + } + } + + return found ? B_OK : error; +} + diff --git a/src/add-ons/kernel/file_systems/iso9660/iso9660.h b/src/add-ons/kernel/file_systems/iso9660/iso9660_identify.h similarity index 53% rename from src/add-ons/kernel/file_systems/iso9660/iso9660.h rename to src/add-ons/kernel/file_systems/iso9660/iso9660_identify.h index acd9e09c3f..733b6f8c7b 100644 --- a/src/add-ons/kernel/file_systems/iso9660/iso9660.h +++ b/src/add-ons/kernel/file_systems/iso9660/iso9660_identify.h @@ -1,10 +1,15 @@ /* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. * Copyright 2002, Tyler Dauwalder. + * * This file may be used under the terms of the MIT License. */ +#ifndef ISO9660_IDENTIFY_H +#define ISO9660_IDENTIFY_H + + +#include -#ifndef _ISO9660_H -#define _ISO9660_H /*! \brief Contains all the info of interest pertaining to an iso9660 volume. @@ -18,22 +23,23 @@ struct iso9660_info { iso9660_info(); ~iso9660_info(); - bool is_valid(); + bool IsValid(); - void set_iso9660_volume_name(const char *name, uint32 length); - void set_joliet_volume_name(const char *name, uint32 length); + void SetISO9660Name(const char *name, uint32 length); + void SetJolietName(const char *name, uint32 length); - const char* get_preferred_volume_name(); + const char* PreferredName(); - char *iso9660_volume_name; - char *joliet_volume_name; - - off_t maxBlocks; + char *iso9660_name; + char *joliet_name; - void set_string(char **string, const char *new_string, uint32 new_length); + off_t max_blocks; + +private: + void _SetString(char **string, const char *newString, uint32 newLength); }; status_t iso9660_fs_identify(int deviceFD, iso9660_info *info); -#endif +#endif // ISO9660_IDENTIFY_H diff --git a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp index 2f2da505b8..52ca369792 100644 --- a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp @@ -8,20 +8,16 @@ */ -#include "iso.h" -#include "iso9660.h" - -#include -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include #include #include -#include +#include #include #include @@ -36,95 +32,53 @@ #include -//#define TRACE_ISO9660 1 -#if TRACE_ISO9660 +#include "iso.h" +#include "iso9660_identify.h" + + +//#define TRACE_ISO9660 +#ifdef TRACE_ISO9660 # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif -/* Start of fundamental (read-only) required functions */ -static status_t fs_mount(dev_t mountID, const char *device, uint32 flags, - const char *args, void **_data, ino_t *_rootID); -static status_t fs_unmount(void *ns); - -static status_t fs_get_vnode_name(void *_ns, void *_node, - char *buffer, size_t bufferSize); -static status_t fs_walk(void *_ns, void *_base, const char *file, - ino_t *_vnodeID, int *_type); - -static status_t fs_read_vnode(void *_ns, ino_t vnid, void **node, bool reenter); -static status_t fs_release_vnode(void *_ns, void *_node, bool reenter); -static status_t fs_get_file_map(fs_volume _fs, fs_vnode _node, off_t offset, size_t size, - struct file_io_vec *vecs, size_t *_count); -static status_t fs_read_stat(void *_ns, void *_node, struct stat *st); -static status_t fs_open(void *_ns, void *_node, int omode, void **cookie); -static status_t fs_read(void *_ns, void *_node, void *cookie, off_t pos, - void *buf, size_t *len); -/// fs_free_cookie - free cookie for file created in open. -static status_t fs_free_cookie(void *ns, void *node, void *cookie); -static status_t fs_close(void *ns, void *node, void *cookie); - -// fs_access - checks permissions for access. -static status_t fs_access(void *_ns, void *_node, int mode); - -// fs_opendir - creates fs-specific "cookie" struct that can tell where -// we are at in the directory list. -static status_t fs_open_dir(void* _ns, void* _node, void** cookie); -// fs_readdir - read 1 or more dirents, keep state in cookie, return -// 0 when no more entries. -static status_t fs_read_dir(void *_ns, void *_node, void *cookie, struct dirent *buf, - size_t bufsize, uint32 *_num); -// fs_rewinddir - set cookie to represent beginning of directory, so -// later fs_readdir calls start at beginning. -static status_t fs_rewind_dir(void *_ns, void *_node, void *cookie); -// fs_closedir - Do whatever you need to to close a directory (sometimes -// nothing), but DON'T free the cookie! -static status_t fs_close_dir(void *_ns, void *_node, void *cookie); -// fs_free_dircookie - Free the fs-specific cookie struct -static status_t fs_free_dir_cookie(void *_ns, void *_node, void *cookie); - -// fs_rfsstat - Fill in fs_info struct for device. -static status_t fs_read_fs_stat(void *_ns, struct fs_info *); - -// fs_readlink - Read in the name of a symbolic link. -static status_t fs_read_link(void *_ns, void *_node, char *buf, size_t *bufsize); - - -// #pragma mark - Scanning - struct identify_cookie { iso9660_info info; }; +// #pragma mark - Scanning + + static float fs_identify_partition(int fd, partition_data *partition, void **_cookie) { - iso9660_info info; - status_t status = iso9660_fs_identify(fd, &info); - if (status != B_OK) - return status; + iso9660_info *info = new iso9660_info; - identify_cookie *cookie = new identify_cookie; - memcpy(&cookie->info, &info, sizeof(info)); + status_t status = iso9660_fs_identify(fd, info); + if (status != B_OK) { + delete info; + return -1; + } - *_cookie = cookie; - return 0.8f; + *_cookie = info; + return 0.6f; } static status_t fs_scan_partition(int fd, partition_data *partition, void *_cookie) { - identify_cookie *cookie = (identify_cookie *)_cookie; + iso9660_info *info = (iso9660_info *)_cookie; partition->status = B_PARTITION_VALID; partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ; partition->block_size = ISO_PVD_SIZE; - partition->content_size = ISO_PVD_SIZE * cookie->info.maxBlocks; - partition->content_name = strdup(cookie->info.get_preferred_volume_name()); + partition->content_size = ISO_PVD_SIZE * info->max_blocks; + partition->content_name = strdup(info->PreferredName()); + if (partition->content_name == NULL) return B_NO_MEMORY; @@ -135,9 +89,7 @@ fs_scan_partition(int fd, partition_data *partition, void *_cookie) static void fs_free_identify_partition_cookie(partition_data *partition, void *_cookie) { - identify_cookie *cookie = (identify_cookie *)_cookie; - - delete cookie; + delete (iso9660_info *)_cookie; } @@ -158,7 +110,7 @@ fs_mount(dev_t mountID, const char *device, uint32 flags, */ status_t result = EINVAL; // return EINVAL if it's not a device compatible with the driver. - bool allow_joliet = true; + bool allowJoliet = true; nspace *vol; (void)flags; @@ -181,13 +133,13 @@ fs_mount(dev_t mountID, const char *device, uint32 flags, // look for nojoliet spot = strstr(buf, "nojoliet"); if (spot != NULL) - allow_joliet = false; + allowJoliet = false; free(buf); } // Try and mount volume as an ISO volume. - result = ISOMount(device, O_RDONLY, &vol, allow_joliet); + result = ISOMount(device, O_RDONLY, &vol, allowJoliet); // If it is ISO … if (result == B_NO_ERROR) { diff --git a/src/add-ons/kernel/file_systems/iso9660/rock.h b/src/add-ons/kernel/file_systems/iso9660/rock_ridge.h similarity index 93% rename from src/add-ons/kernel/file_systems/iso9660/rock.h rename to src/add-ons/kernel/file_systems/iso9660/rock_ridge.h index 53736d90df..fecc81360c 100644 --- a/src/add-ons/kernel/file_systems/iso9660/rock.h +++ b/src/add-ons/kernel/file_systems/iso9660/rock_ridge.h @@ -1,13 +1,10 @@ -// rock.h - /* Copyright 1999, Be Incorporated. All Rights Reserved. This file may be used under the terms of the Be Sample Code License. */ // Altername name field flags. -enum -{ +enum { NM_CONTINUE = 1, NM_CURRENT = 2, NM_PARENT = 4, @@ -15,14 +12,12 @@ enum } NMFLAGS; // Symbolic link field flags -enum -{ +enum { SL_CONTINUE = 1 } SLFLAGS; // Symbolic link field component flags -enum -{ +enum { SLCP_CONTINUE = 1, SLCP_CURRENT = 2, SLCP_PARENT = 4,