diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 5b915e0..6120a78 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -141,7 +141,7 @@ static int show_chunk_to_jte(Ecma119Image *target, char *buf, int count) static int need_version_number(Ecma119Image *t, Ecma119Node *n) { - if (t->omit_version_numbers & 1) { + if ((t->omit_version_numbers & 1) || t->untranslated_name_len > 0) { return 0; } if (n->type == ECMA119_DIR || n->type == ECMA119_PLACEHOLDER) { @@ -188,9 +188,9 @@ size_t calc_dir_size(Ecma119Image *t, Ecma119Node *dir, size_t *ce) /* size of "." and ".." entries */ len = 34 + 34; if (t->rockridge) { - len += rrip_calc_len(t, dir, 1, 255 - 34, &ce_len); + len += rrip_calc_len(t, dir, 1, 34, &ce_len); *ce += ce_len; - len += rrip_calc_len(t, dir, 2, 255 - 34, &ce_len); + len += rrip_calc_len(t, dir, 2, 34, &ce_len); *ce += ce_len; } @@ -203,7 +203,7 @@ size_t calc_dir_size(Ecma119Image *t, Ecma119Node *dir, size_t *ce) for (section = 0; section < nsections; ++section) { size_t dirent_len = calc_dirent_len(t, child); if (t->rockridge) { - dirent_len += rrip_calc_len(t, child, 0, 255 - dirent_len, &ce_len); + dirent_len += rrip_calc_len(t, child, 0, dirent_len, &ce_len); *ce += ce_len; } remaining = BLOCK_SIZE - (len % BLOCK_SIZE); @@ -594,7 +594,7 @@ int write_one_dir(Ecma119Image *t, Ecma119Node *dir, Ecma119Node *parent) /* write the "." and ".." entries first */ if (t->rockridge) { - ret = rrip_get_susp_fields(t, dir, 1, 255 - 32, &info); + ret = rrip_get_susp_fields(t, dir, 1, 34, &info); if (ret < 0) { return ret; } @@ -604,7 +604,7 @@ int write_one_dir(Ecma119Image *t, Ecma119Node *dir, Ecma119Node *parent) buf += len; if (t->rockridge) { - ret = rrip_get_susp_fields(t, dir, 2, 255 - 32, &info); + ret = rrip_get_susp_fields(t, dir, 2, 34, &info); if (ret < 0) { return ret; } @@ -630,7 +630,7 @@ int write_one_dir(Ecma119Image *t, Ecma119Node *dir, Ecma119Node *parent) /* get the SUSP fields if rockridge is enabled */ if (t->rockridge) { - ret = rrip_get_susp_fields(t, child, 0, 255 - len, &info); + ret = rrip_get_susp_fields(t, child, 0, len, &info); if (ret < 0) { return ret; } @@ -1575,6 +1575,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img) target->hardlinks = opts->hardlinks; target->aaip = opts->aaip; target->always_gmt = opts->always_gmt; + target->untranslated_name_len = opts->untranslated_name_len; target->omit_version_numbers = opts->omit_version_numbers | opts->max_37_char_filenames; target->allow_deep_paths = opts->allow_deep_paths; @@ -2363,6 +2364,7 @@ int iso_write_opts_new(IsoWriteOpts **opts, int profile) wopts->appended_partitions[i] = NULL; wopts->ascii_disc_label[0] = 0; wopts->will_cancel = 0; + wopts->untranslated_name_len = 0; *opts = wopts; return ISO_SUCCESS; @@ -2451,6 +2453,22 @@ int iso_write_opts_set_aaip(IsoWriteOpts *opts, int enable) return ISO_SUCCESS; } +int iso_write_opts_set_untranslated_name_len(IsoWriteOpts *opts, int len) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + if (len == -1) + opts->untranslated_name_len = ISO_UNTRANSLATED_NAMES_MAX; + else if(len == 0) + opts->untranslated_name_len = 0; + else if(len > ISO_UNTRANSLATED_NAMES_MAX || len < 0) + return ISO_WRONG_ARG_VALUE; + else + opts->untranslated_name_len = len; + return opts->untranslated_name_len; +} + int iso_write_opts_set_omit_version_numbers(IsoWriteOpts *opts, int omit) { if (opts == NULL) { diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index 46dfe96..2df584e 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -52,6 +52,17 @@ #define ISO_DISC_LABEL_SIZE 129 +/* The maximum lenght of an specs violating ECMA-119 file identifier. + The theoretical limit is 254 - 34 - 28 (len of SUSP CE entry) = 192 + Currently the practical limit is 254 - 34 - 96 (non-CE RR entries) - 28 (CE) +*/ +#ifdef Libisofs_with_rrip_rR +#define ISO_UNTRANSLATED_NAMES_MAX 92 +#else +#define ISO_UNTRANSLATED_NAMES_MAX 96 +#endif + + /** * Holds the options for the image generation. */ @@ -213,6 +224,21 @@ struct iso_write_opts { unsigned int replace_uid :2; unsigned int replace_gid :2; + /** + * Extra Caution: This option breaks any assumptions about names that + * are supported by ECMA-119 specifications. + * Omit any translation which would make a file name compliant to the + * ECMA-119 rules. This includes and exceeds omit_version_numbers, + * max_37_char_filenames, no_force_dots bit0, allow_lowercase. + * The maximum name length is given by this variable. + * There is a length limit of ISO_UNTRANSLATED_NAMES_MAX characters, + * because ECMA-119 allows 254 byte in a directory record, some + * of them are occupied by ECMA-119, some more are needed for SUSP CE, + * and some are fixely occupied by libisofs Rock Ridge code. + * The default value 0 disables this feature. + */ + unsigned int untranslated_name_len; + mode_t dir_mode; /** Mode to use on dirs when replace_dir_mode == 2. */ mode_t file_mode; /** Mode to use on files when replace_file_mode == 2. */ uid_t uid; /** uid to use when replace_uid == 2. */ @@ -437,6 +463,8 @@ struct ecma119_image unsigned int replace_dir_mode :1; unsigned int replace_timestamps :1; + unsigned int untranslated_name_len; + uid_t uid; gid_t gid; mode_t file_mode; diff --git a/libisofs/ecma119_tree.c b/libisofs/ecma119_tree.c index 619d96a..7d3cca5 100644 --- a/libisofs/ecma119_tree.c +++ b/libisofs/ecma119_tree.c @@ -12,6 +12,9 @@ #include "../config.h" #endif +/* Must be before ecma119.h because of eventual Libisofs_with_rrip_rR */ +#include "libisofs.h" + #include "ecma119_tree.h" #include "ecma119.h" #include "node.h" @@ -29,7 +32,7 @@ static int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) { - int ret, relaxed; + int ret, relaxed, free_ascii_name= 0; char *ascii_name; char *isoname= NULL; @@ -38,9 +41,15 @@ int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) return ISO_SUCCESS; } - ret = str2ascii(img->input_charset, iso->name, &ascii_name); + if (img->untranslated_name_len > 0) { + ascii_name = iso->name; + } else { + ret = str2ascii(img->input_charset, iso->name, &ascii_name); + free_ascii_name = 1; + } if (ret < 0) { - iso_msg_submit(img->image->id, ret, 0, "Can't convert %s", iso->name); + iso_msg_submit(img->image->id, ret, 0, + "Cannot convert name '%s' to ASCII", iso->name); return ret; } @@ -50,7 +59,17 @@ int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) relaxed = (int)img->allow_lowercase; } if (iso->type == LIBISO_DIR) { - if (img->max_37_char_filenames) { + if (img->untranslated_name_len > 0) { + if (strlen(ascii_name) > img->untranslated_name_len) { +needs_transl:; + iso_msg_submit(img->image->id, ISO_NAME_NEEDS_TRANSL, 0, + "File name too long (%d > %d) for untranslated recording: '%s'", + strlen(ascii_name), img->untranslated_name_len, + ascii_name); + return ISO_NAME_NEEDS_TRANSL; + } + isoname = strdup(ascii_name); + } else if (img->max_37_char_filenames) { isoname = iso_r_dirid(ascii_name, 37, relaxed); } else if (img->iso_level == 1) { if (relaxed) { @@ -66,7 +85,11 @@ int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) } } } else { - if (img->max_37_char_filenames) { + if (img->untranslated_name_len > 0) { + if (strlen(ascii_name) > img->untranslated_name_len) + goto needs_transl; + isoname = strdup(ascii_name); + } else if (img->max_37_char_filenames) { isoname = iso_r_fileid(ascii_name, 36, relaxed, (img->no_force_dots & 1) ? 0 : 1); } else if (img->iso_level == 1) { @@ -85,7 +108,8 @@ int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) } } } - free(ascii_name); + if (free_ascii_name) + free(ascii_name); if (isoname != NULL) { *name = isoname; return ISO_SUCCESS; @@ -416,8 +440,6 @@ int create_tree(Ecma119Image *image, IsoNode *iso, Ecma119Node **tree, !!hidden); if (cret < 0) { /* error */ - if (!hidden) - ecma119_node_free(node); ret = cret; break; } else if (cret == ISO_SUCCESS && !hidden) { @@ -537,6 +559,18 @@ int mangle_single_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len, continue; } + if (img->untranslated_name_len) { + /* This should not happen because no two IsoNode names should be + identical and only unaltered IsoNode names should be seen here. + Thus the Ema119Node names should be unique. + */ + iso_msg_submit(img->image->id, ISO_NAME_NEEDS_TRANSL, 0, + "ECMA-119 file name collision: '%s'", + children[i]->iso_name); + ret = ISO_NAME_NEEDS_TRANSL; + goto mangle_cleanup; + } + /* * A max of 7 characters is good enought, it allows handling up to * 9,999,999 files with same name. We can increment this to @@ -716,7 +750,9 @@ int mangle_tree(Ecma119Image *img, int recurse) int max_file, max_dir; Ecma119Node *root; - if (img->max_37_char_filenames) { + if (img->untranslated_name_len > 0) { + max_file = max_dir = img->untranslated_name_len; + } else if (img->max_37_char_filenames) { max_file = max_dir = 37; } else if (img->iso_level == 1) { max_file = 12; /* 8 + 3 + 1 */ diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index e34dfd2..b7558d9 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -1344,6 +1344,34 @@ int iso_write_opts_set_hardlinks(IsoWriteOpts *opts, int enable); */ int iso_write_opts_set_aaip(IsoWriteOpts *opts, int enable); +/** + * Caution: This option breaks any assumptions about names that + * are supported by ECMA-119 specifications. + * Try to omit any translation which would make a file name compliant to the + * ECMA-119 rules. This includes and exceeds omit_version_numbers, + * max_37_char_filenames, no_force_dots bit0, allow_full_ascii. Further it + * prevents the conversion from local character set to ASCII. + * The maximum name length is given by this call. If a filename exceeds + * this length or cannot be recorded untranslated for other reasons, then + * image production is aborted with ISO_NAME_NEEDS_TRANSL. + * Currently the length limit is 96 characters, because an ECMA-119 directory + * record may at most have 254 bytes and up to 158 other bytes must fit into + * the record. Probably 96 more bytes can be made free for the name in future. + * @param opts + * The option set to be manipulated. + * @param len + * 0 = disable this feature and perform name translation according to + * other settings. + * >0 = Omit any translation. Eventually abort image production + * if a name is longer than the given value. + * -1 = Like >0. Allow maximum possible length (currently 96) + * @return >=0 success, <0 failure + * In case of >=0 the return value tells the effectively set len. + * E.g. 96 after using len == -1. + * @since 0.6.42 + */ +int iso_write_opts_set_untranslated_name_len(IsoWriteOpts *opts, int len); + /** * Omit the version number (";1") at the end of the ISO-9660 identifiers. * This breaks ECMA-119 specification, but version numbers are usually not @@ -6422,6 +6450,9 @@ int iso_md5_match(char first_md5[16], char second_md5[16]); /** Displacement offset leads outside 32 bit range (FAILURE, HIGH, -372) */ #define ISO_DISPLACE_ROLLOVER 0xE830FE8C +/** File name cannot be written into ECMA-119 untranslated + (FAILURE, HIGH, -373) */ +#define ISO_NAME_NEEDS_TRANSL 0xE830FE8B /* Internal developer note: diff --git a/libisofs/libisofs.ver b/libisofs/libisofs.ver index ceb7b8f..85ed462 100644 --- a/libisofs/libisofs.ver +++ b/libisofs/libisofs.ver @@ -293,6 +293,7 @@ iso_write_opts_set_scdbackup_tag; iso_write_opts_set_sort_files; iso_write_opts_set_system_area; iso_write_opts_set_tail_blocks; +iso_write_opts_set_untranslated_name_len; iso_write_opts_set_will_cancel; iso_zisofs_get_params; iso_zisofs_get_refcounts; diff --git a/libisofs/messages.c b/libisofs/messages.c index e3fadf0..aa5c5af 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -361,6 +361,8 @@ const char *iso_error_to_msg(int errcode) return "May not combine appended partition with non-MBR system area"; case ISO_DISPLACE_ROLLOVER: return "Displacement offset leads outside 32 bit range"; + case ISO_NAME_NEEDS_TRANSL: + return "File name cannot be written into ECMA-119 untranslated"; default: return "Unknown error"; } diff --git a/libisofs/rockridge.c b/libisofs/rockridge.c index e11ca9a..74118f3 100644 --- a/libisofs/rockridge.c +++ b/libisofs/rockridge.c @@ -13,6 +13,9 @@ #include "../config.h" #endif +#include +#include + #include "rockridge.h" #include "node.h" #include "ecma119_tree.h" @@ -22,7 +25,12 @@ #include "aaip_0_2.h" #include "libisofs.h" -#include + +#ifdef Libisofs_with_rrip_rR +#define ISO_ROCKRIDGE_IN_DIR_REC 128 +#else +#define ISO_ROCKRIDGE_IN_DIR_REC 124 +#endif static @@ -1104,27 +1112,30 @@ unannounced_ca:; * @param type * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." * for that node (i.e., it will refer to the parent) - * @param space - * Available space in the System Use Area for the directory record. + * @param used_up + * Already occupied space in the directory record. * @param ce * Will be filled with the space needed in a CE * @return * The size needed for the RR entries in the System Use Area */ -size_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t space, +size_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t used_up, size_t *ce) { - size_t su_size; + size_t su_size, space; int ret; - /* space min is 255 - 33 - 37 = 185 - * At the same time, it is always an odd number, but we need to pad it - * propertly to ensure the length of a directory record is a even number - * (ECMA-119, 9.1.13). Thus, in fact the real space is always space - 1 - */ - space--; - *ce = 0; + /* Directory record length must be even (ECMA-119, 9.1.13). Maximum is 254. + */ + space = 254 - used_up - (used_up % 2); + if (type < 0 || type > 2 || space < ISO_ROCKRIDGE_IN_DIR_REC) { + iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0, + "Unknown node type %d or short RR space %d < %d in directory record", + type, (int) space, ISO_ROCKRIDGE_IN_DIR_REC); + return ISO_ASSERT_FAILURE; + } + *ce = 0; su_size = 0; /* If AAIP enabled and announced by ER : account for 5 bytes of ES */; @@ -1268,8 +1279,8 @@ int add_aa_string(Ecma119Image *t, Ecma119Node *n, struct susp_info *info, * @param type * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." * for that node (i.e., it will refer to the parent) - * @param space - * Available space in the System Use Area for the directory record. + * @param used_up + * Already occupied space in the directory record. * @param info * Pointer to the struct susp_info where the entries will be stored. * If some entries need to go to a Continuation Area, they will be added @@ -1279,7 +1290,7 @@ int add_aa_string(Ecma119Image *t, Ecma119Node *n, struct susp_info *info, * 1 success, < 0 error */ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, - size_t space, struct susp_info *info) + size_t used_up, struct susp_info *info) { int ret; size_t i; @@ -1291,13 +1302,20 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, size_t su_size_pd, ce_len_pd; /* predicted sizes of SUA and CA */ int ce_is_predicted = 0; size_t aaip_sua_free= 0, aaip_len= 0; + size_t space; if (t == NULL || n == NULL || info == NULL) { return ISO_NULL_POINTER; } - if (type < 0 || type > 2 || space < 185) { - /* space min is 255 - 33 - 37 = 185 */ - return ISO_WRONG_ARG_VALUE; + + /* Directory record length must be even (ECMA-119, 9.1.13). Maximum is 254. + */ + space = 254 - used_up - (used_up % 2); + if (type < 0 || type > 2 || space < ISO_ROCKRIDGE_IN_DIR_REC) { + iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0, + "Unknown node type %d or short RR space %d < %d in directory record", + type, (int) space, ISO_ROCKRIDGE_IN_DIR_REC); + return ISO_ASSERT_FAILURE; } if (type == 2 && n->parent != NULL) { @@ -1306,13 +1324,6 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, node = n; } - /* space min is 255 - 33 - 37 = 185 - * At the same time, it is always an odd number, but we need to pad it - * propertly to ensure the length of a directory record is a even number - * (ECMA-119, 9.1.13). Thus, in fact the real space is always space - 1 - */ - space--; - /* * SP must be the first entry for the "." record of the root directory * (SUSP, 5.3) @@ -1384,6 +1395,16 @@ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, } } + + /* <<< ts B01222 : For testing only */ + if (info->suf_len + 28 > space) { + fprintf(stderr, + "libisofs_debug: Directory Record overflow. name='%s' , info->suf_len=%d > space=%d - 28\n", + node->iso_name, (int) info->suf_len, (int) space); + return ISO_ASSERT_FAILURE; + } + + if (type == 0) { size_t sua_free; /* free space in the SUA */ int nm_type = 0; /* 0 whole entry in SUA, 1 part in CE */ diff --git a/libisofs/util.c b/libisofs/util.c index 3a297d4..906cb8c 100644 --- a/libisofs/util.c +++ b/libisofs/util.c @@ -773,6 +773,8 @@ char *iso_r_dirid(const char *src, int size, int relaxed) len = size; } dest = malloc(len + 1); + if (dest == NULL) + return NULL; for (i = 0; i < len; i++) { char c= src[i]; if (relaxed == 2) { @@ -1036,6 +1038,8 @@ uint16_t *ucsdup(const uint16_t *str) size_t len = ucslen(str); ret = malloc(2 * (len + 1)); + if (ret == NULL) + return NULL; if (ret != NULL) { memcpy(ret, str, 2 * (len + 1)); } @@ -1460,6 +1464,8 @@ char *ucs2str(const char *buf, size_t len) /* ensure enought space */ out = calloc(outbytes, 1); + if (out == NULL) + return NULL; /* convert to local charset */ conv_ret = iso_iconv_open(&conv, iso_get_local_charset(0), "UCS-2BE", 0);