From 44f475a4ef73b704d90e79d75b64f83d341aee40 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Fri, 3 Jan 2014 18:29:29 +0100 Subject: [PATCH] Improved handling of unconvertable file names and name collsions during iso_image_import() --- libisofs/builder.c | 14 +++- libisofs/builder.h | 4 +- libisofs/fs_image.c | 96 +++++++++++++++++++++++--- libisofs/image.c | 3 +- libisofs/image.h | 8 ++- libisofs/libisofs.h | 7 +- libisofs/messages.c | 4 +- libisofs/tree.c | 162 +++++++++++++++++++++++++++++++++++--------- 8 files changed, 248 insertions(+), 50 deletions(-) diff --git a/libisofs/builder.c b/libisofs/builder.c index 66da392..cb9b4d5 100644 --- a/libisofs/builder.c +++ b/libisofs/builder.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2011 Thomas Schmitt + * Copyright (c) 2009 - 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -96,7 +96,7 @@ int default_create_file(IsoNodeBuilder *builder, IsoImage *image, static int default_create_node(IsoNodeBuilder *builder, IsoImage *image, - IsoFileSource *src, IsoNode **node) + IsoFileSource *src, char *in_name, IsoNode **node) { int ret; struct stat info; @@ -122,7 +122,15 @@ int default_create_node(IsoNodeBuilder *builder, IsoImage *image, goto ex; } - name = iso_file_source_get_name(src); + if (in_name == NULL) { + name = iso_file_source_get_name(src); + } else { + name = strdup(in_name); + if (name == NULL) { + ret = ISO_OUT_OF_MEM; goto ex; + } + } + if (strlen(name) > LIBISOFS_NODE_NAME_MAX) name[LIBISOFS_NODE_NAME_MAX] = 0; fs = iso_file_source_get_filesystem(src); diff --git a/libisofs/builder.h b/libisofs/builder.h index 7a8a4ca..9bd95e1 100644 --- a/libisofs/builder.h +++ b/libisofs/builder.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso + * Copyright (c) 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -47,6 +48,7 @@ struct Iso_Node_Builder * Create a new IsoNode from a IsoFileSource. The type of the node to be * created is determined from the type of the file source. Name, * permissions and other attributes are taken from source file. + * But name may be overridden by parameter name if it is not NULL. * * Note that the src is never unref, so you need to free it. * @@ -54,7 +56,7 @@ struct Iso_Node_Builder * 1 on success, < 0 on error */ int (*create_node)(IsoNodeBuilder *builder, IsoImage *image, - IsoFileSource *src, IsoNode **node); + IsoFileSource *src, char *name, IsoNode **node); /** * Free implementation specific data. Should never be called by user. diff --git a/libisofs/fs_image.c b/libisofs/fs_image.c index e28ecd2..eedbff5 100644 --- a/libisofs/fs_image.c +++ b/libisofs/fs_image.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2011 Thomas Schmitt + * Copyright (c) 2009 - 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -1237,15 +1237,67 @@ int iso_ifs_source_get_zf(IsoFileSource *src, int *header_size_div4, } +static +int make_hopefully_unique_name(_ImageFsData *fsdata, + char *str, size_t len, char **name) +{ + int ret, name_len, i; + char c, *smashed = NULL, md5[16]; + void *md5_context = NULL; + + /* Shorten so that 32 characters of MD5 fit. + If shorter than 8, pad up to 8 by '_'. + Smash characters to [0-9A-Za-z_.]. + Append MD5 of original str as hex digits. + */ + name_len = len > 223 ? 223 : len; + LIBISO_ALLOC_MEM(smashed, char, (name_len >= 8 ? name_len : 8) + 32 + 1); + memcpy(smashed, str, name_len); + for (; name_len < 8; name_len++) + smashed[name_len] = '_'; + smashed[name_len] = 0; + for (i = 0; i < name_len; i++) { + c = smashed[i]; + if (c == '.' || (c >= '0' && c <= '9') || + c == '_' || (c >= 'a' && c <= 'z')) + continue; + smashed[i] = '_'; + } + ret = iso_md5_start(&md5_context); + if (ret != 1) + goto ex; + ret = iso_md5_compute(md5_context, str, len); + if (ret != 1) + goto ex; + ret = iso_md5_end(&md5_context, md5); + if (ret != 1) + goto ex; + for (i = 0; i < 16; i++) + sprintf(smashed + i * 2 + name_len, "%2.2x", + ((unsigned char *) md5)[i]); + name_len += 32; + smashed[name_len] = 0; + *name = smashed; smashed = NULL; + + ret = ISO_SUCCESS; +ex: + LIBISO_FREE_MEM(smashed); + if (md5_context != NULL) + iso_md5_end(&md5_context, md5); + return ret; +} + + /** * Read a file name from a directory record, doing the needed charset * conversion */ static -char *get_name(_ImageFsData *fsdata, const char *str, size_t len) +char *get_name(_ImageFsData *fsdata, char *str, size_t len) { int ret; char *name = NULL, *from_ucs = NULL; + if (strcmp(fsdata->local_charset, fsdata->input_charset)) { /* charset conversion needed */ ret = strnconv(str, fsdata->input_charset, fsdata->local_charset, len, @@ -1278,13 +1330,14 @@ char *get_name(_ImageFsData *fsdata, const char *str, size_t len) return NULL; /* aborted */ } /* fallback */ - - /* >>> create a hopefully unique name */; - + ret = make_hopefully_unique_name(fsdata, str, len, &name); + if (ret == ISO_SUCCESS) + return name; + return NULL; } } - /* we reach here when the charset conversion is not needed or has failed */ + /* we reach here when the charset conversion is not needed */ name = malloc(len + 1); if (name == NULL) { @@ -3029,7 +3082,8 @@ int src_aa_to_node(IsoFileSource *src, IsoNode *node, int flag) static int image_builder_create_node(IsoNodeBuilder *builder, IsoImage *image, - IsoFileSource *src, IsoNode **node) + IsoFileSource *src, char *in_name, + IsoNode **node) { int ret, idx, to_copy; struct stat info; @@ -3055,7 +3109,14 @@ int image_builder_create_node(IsoNodeBuilder *builder, IsoImage *image, data = (ImageFileSourceData*)src->data; fsdata = data->fs->data; - name = iso_file_source_get_name(src); + if (in_name == NULL) { + name = iso_file_source_get_name(src); + } else { + name = strdup(in_name); + if (name == NULL) { + ret = ISO_OUT_OF_MEM; goto ex; + } + } /* get info about source */ ret = iso_file_source_lstat(src, &info); @@ -3518,6 +3579,22 @@ ex:; return ret; } + +static +void issue_collision_warning_summary(size_t failures) +{ + if (failures > ISO_IMPORT_COLL_WARN_MAX) { + iso_msg_submit(-1, ISO_IMPORT_COLLISION, 0, + "More file name collisions had to be resolved"); + } + if (failures > 0) { + iso_msg_submit(-1, ISO_IMPORT_COLLISION, 0, + "Sum of resolved file name collisions: %.f", + (double) failures); + } +} + + int iso_image_import(IsoImage *image, IsoDataSource *src, struct iso_read_opts *opts, IsoReadImageFeatures **features) @@ -3667,6 +3744,7 @@ int iso_image_import(IsoImage *image, IsoDataSource *src, goto import_revert; } issue_ucs2_warning_summary(data->joliet_ucs2_failures); + issue_collision_warning_summary(image->collision_warnings); /* Take over inode management from IsoImageFilesystem. data->inode_counter is supposed to hold the maximum PX inode number. @@ -3700,7 +3778,7 @@ int iso_image_import(IsoImage *image, IsoDataSource *src, goto import_revert; } ret = image_builder_create_node(image->builder, image, boot_src, - &node); + NULL, &node); if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; diff --git a/libisofs/image.c b/libisofs/image.c index d830136..34c3ef6 100644 --- a/libisofs/image.c +++ b/libisofs/image.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2013 Thomas Schmitt + * Copyright (c) 2009 - 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -97,6 +97,7 @@ int iso_image_new(const char *name, IsoImage **image) img->generator_is_running = 0; for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) img->hfsplus_blessed[i] = NULL; + img->collision_warnings = 0; *image = img; return ISO_SUCCESS; diff --git a/libisofs/image.h b/libisofs/image.h index 018a101..eb92db4 100644 --- a/libisofs/image.h +++ b/libisofs/image.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2012 Thomas Schmitt + * Copyright (c) 2009 - 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -22,6 +22,9 @@ */ #define ISO_USED_INODE_RANGE (1 << 18) +/* How many warnings to issue about name collisions during iso_image_import() +*/ +#define ISO_IMPORT_COLL_WARN_MAX 10 /* * Image is a context for image manipulation. @@ -196,6 +199,9 @@ struct Iso_Image */ IsoNode *hfsplus_blessed[ISO_HFSPLUS_BLESS_MAX]; + /* Counts the name collisions while iso_image_import() */ + size_t collision_warnings; + }; diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 3be3571..abbde54 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -4,7 +4,7 @@ /* * Copyright (c) 2007-2008 Vreixo Formoso, Mario Danic - * Copyright (c) 2009-2013 Thomas Schmitt + * Copyright (c) 2009-2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -328,7 +328,7 @@ enum eltorito_boot_media_type { }; /** - * Replace mode used when addding a node to a file. + * Replace mode used when addding a node to a directory. * This controls how libisofs will act when you tried to add to a dir a file * with the same name that an existing file. * @@ -7605,6 +7605,9 @@ int iso_conv_name_chars(IsoWriteOpts *opts, char *name, size_t name_len, /** Filename not suitable for character set UCS-2 (WARNING, HIGH, -397) */ #define ISO_NAME_NOT_UCS2 0xD030FE73 +/** File name collision during ISO image import (WARNING, HIGH, -398) */ +#define ISO_IMPORT_COLLISION 0xD030FE72 + /* Internal developer note: Place new error codes directly above this comment. diff --git a/libisofs/messages.c b/libisofs/messages.c index b04e655..06ceeed 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2011 Thomas Schmitt + * Copyright (c) 2009 - 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -505,6 +505,8 @@ const char *iso_error_to_msg(int errcode) return "Unrecognized file type in ISO image"; case ISO_NAME_NOT_UCS2: return "Filename not suitable for character set UCS-2"; + case ISO_IMPORT_COLLISION: + return "File name collision during ISO image import"; default: return "Unknown error"; } diff --git a/libisofs/tree.c b/libisofs/tree.c index f595a09..5798850 100644 --- a/libisofs/tree.c +++ b/libisofs/tree.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2011 Thomas Schmitt + * Copyright (c) 2011 - 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -23,6 +23,7 @@ #include "builder.h" #include "messages.h" #include "tree.h" +#include "util.h" #include #include @@ -521,7 +522,7 @@ int iso_tree_add_node_builder(IsoImage *image, IsoDir *parent, return ISO_NODE_NAME_NOT_UNIQUE; } - result = builder->create_node(builder, image, src, &new); + result = builder->create_node(builder, image, src, name, &new); if (result < 0) { return result; } @@ -587,7 +588,8 @@ int iso_tree_add_new_node(IsoImage *image, IsoDir *parent, const char *name, return result; } - result = image->builder->create_node(image->builder, image, file, &new); + result = image->builder->create_node(image->builder, image, file, + (char *) name, &new); /* free the file */ iso_file_source_unref(file); @@ -596,12 +598,6 @@ int iso_tree_add_new_node(IsoImage *image, IsoDir *parent, const char *name, return result; } - result = iso_node_set_name(new, name); - if (result < 0) { - iso_node_unref(new); - return result; - } - if (node) { *node = new; } @@ -742,6 +738,97 @@ int check_special(IsoImage *image, mode_t mode) return 0; } + +static +void ascii_increment(char *name, int pos) +{ + int c, len; + + len = strlen(name); + if (pos < 0 || pos >= len) + pos = len - 1; + c = name[pos]; + if (c >= '0' && c < '9') { + c++; + } else if (c == '9') { + c = 'A'; + } else if (c >= 'A' && c < 'Z') { + c++; + } else if (c == 'Z') { + c = '_'; + } else if (c == '_') { + c = 'a'; + } else if (c >= 'a' && c < 'z') { + c++; + } else if (c == 'z') { + c = '0'; + name[pos] = c; + ascii_increment(name, pos - 1); + return; + } else { + if (pos == len - 1 || name[pos + 1] == '.') + c = '_'; /* Make first change less riddling */ + else + c = '0'; /* But else use the full range of valid characters */ + } + name[pos] = c; +} + + +static +int make_really_unique_name(IsoDir *parent, char **name, char **unique_name, + IsoNode ***pos, int flag) +{ + int len, ret, pre_check = 0, ascii_idx = -1, first; + char *dpt; + + len = strlen(*name); + if (len < 8) { + /* Insert underscores before last dot */ + LIBISO_ALLOC_MEM(*unique_name, char, 8 + 1); + first = len; + dpt = strrchr(*name, '.'); + if (dpt != NULL) + first = (dpt - *name); + if (first > 0) + memcpy(*unique_name, *name, first); + memset(*unique_name + first, '_', 8 - len); + if (len > 0) + memcpy(*unique_name + (8 - (len - first)), *name + first, + len - first); + len = 8; + pre_check = 1; /* It might now already be unique */ + } else { + LIBISO_ALLOC_MEM(*unique_name, char, len + 1); + memcpy(*unique_name, *name, len); + } + (*unique_name)[len] = 0; + + dpt = strrchr(*unique_name, '.'); + if (dpt != NULL) + ascii_idx = (dpt - *unique_name) - 1; /* Begin before last dot */ + while (1) { + if (!pre_check) + ascii_increment(*unique_name, ascii_idx); + else + pre_check = 0; + ret = iso_dir_exists(parent, *unique_name, pos); + if (ret < 0) + goto ex; + if (ret == 0) + break; + } + *name = *unique_name; + ret = ISO_SUCCESS; +ex:; + if (ret < 0) { + LIBISO_FREE_MEM(*unique_name); + *unique_name = NULL; + } + return ret; +} + + /** * Recursively add a given directory to the image tree. * @@ -750,12 +837,12 @@ int check_special(IsoImage *image, mode_t mode) */ int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir) { - int ret; + int ret, dir_is_open = 0; IsoNodeBuilder *builder; IsoFileSource *file; IsoNode **pos; struct stat info; - char *name, *path; + char *name, *path, *allocated_name = NULL; IsoNode *new; enum iso_replace_mode replace; @@ -771,8 +858,9 @@ int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir) ret = iso_msg_submit(image->id, ISO_NULL_POINTER, ret, "Can't open dir. NULL pointer caught as dir name"); } - return ret; + goto ex; } + dir_is_open = 1; builder = image->builder; @@ -785,15 +873,16 @@ int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir) if (ret < 0) { /* error reading dir */ ret = iso_msg_submit(image->id, ret, ret, "Error reading dir"); + goto ex; } - break; + break; /* End of directory */ } path = iso_file_source_get_path(file); if (path == NULL) { ret = iso_msg_submit(image->id, ISO_NULL_POINTER, ret, "NULL pointer caught as file path"); - return ret; + goto ex; } name = strrchr(path, '/') + 1; @@ -827,19 +916,25 @@ int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir) /* find place where to insert */ ret = iso_dir_exists(parent, name, &pos); - /* TODO - * if (ret && replace == ISO_REPLACE_ASK) { - * replace = /.... - * } - */ - - /* chek if we must insert or not */ - /* TODO check for other replace behavior */ - if (ret && (replace == ISO_REPLACE_NEVER)) { - /* skip file */ - goto dir_rec_continue; + if (ret) { + /* Resolve name collision + e.g. caused by fs_image.c:make_hopefully_unique_name() + */ + LIBISO_FREE_MEM(allocated_name); allocated_name = NULL; + ret = make_really_unique_name(parent, &name, &allocated_name, &pos, + 0); + if (ret < 0) + goto ex; + image->collision_warnings++; + if (image->collision_warnings < ISO_IMPORT_COLL_WARN_MAX) { + ret = iso_msg_submit(image->id, ISO_IMPORT_COLLISION, 0, + "File name collision resolved with %s . Now: %s", + path, name); + if (ret < 0) + goto ex; + } } - + /* if we are here we must insert. Give user a chance for cancel */ if (image->report) { int r = image->report(image, file); @@ -848,7 +943,7 @@ int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir) goto dir_rec_continue; } } - ret = builder->create_node(builder, image, file, &new); + ret = builder->create_node(builder, image, file, name, &new); if (ret < 0) { ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret, "Error when adding file %s", path); @@ -884,14 +979,17 @@ dir_rec_continue:; /* check for error severity to decide what to do */ if (ret < 0) { ret = iso_msg_submit(image->id, ret, 0, NULL); - if (ret < 0) { - break; - } + if (ret < 0) + goto ex; } } /* while */ - iso_file_source_close(dir); - return ret < 0 ? ret : ISO_SUCCESS; + ret = ISO_SUCCESS; +ex:; + if (dir_is_open) + iso_file_source_close(dir); + LIBISO_FREE_MEM(allocated_name); + return ret; } int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir)