Improved handling of unconvertable file names and name collsions during iso_image_import()

This commit is contained in:
Thomas Schmitt 2014-01-03 18:29:29 +01:00
parent 60eb7e883c
commit 44f475a4ef
8 changed files with 248 additions and 50 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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;
};

View File

@ -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.

View File

@ -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";
}

View File

@ -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 <stdlib.h>
#include <string.h>
@ -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)