You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
846 lines
24 KiB
846 lines
24 KiB
/* |
|
* Copyright (c) 2007 Vreixo Formoso |
|
* |
|
* 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 as |
|
* published by the Free Software Foundation. See COPYING file for details. |
|
*/ |
|
|
|
#include "ecma119_tree.h" |
|
#include "ecma119.h" |
|
#include "node.h" |
|
#include "util.h" |
|
#include "filesrc.h" |
|
#include "messages.h" |
|
#include "image.h" |
|
#include "stream.h" |
|
#include "eltorito.h" |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdio.h> |
|
|
|
static |
|
int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) |
|
{ |
|
int ret, relaxed; |
|
char *ascii_name; |
|
char *isoname= NULL; |
|
|
|
if (iso->name == NULL) { |
|
/* it is not necessarily an error, it can be the root */ |
|
return ISO_SUCCESS; |
|
} |
|
|
|
ret = str2ascii(img->input_charset, iso->name, &ascii_name); |
|
if (ret < 0) { |
|
iso_msg_submit(img->image->id, ret, 0, "Can't convert %s", iso->name); |
|
return ret; |
|
} |
|
|
|
if (img->allow_full_ascii) { |
|
relaxed = 2; |
|
} else { |
|
relaxed = (int)img->allow_lowercase; |
|
} |
|
if (iso->type == LIBISO_DIR) { |
|
if (img->max_37_char_filenames) { |
|
isoname = iso_r_dirid(ascii_name, 37, relaxed); |
|
} else if (img->iso_level == 1) { |
|
if (relaxed) { |
|
isoname = iso_r_dirid(ascii_name, 8, relaxed); |
|
} else { |
|
isoname = iso_1_dirid(ascii_name); |
|
} |
|
} else { |
|
if (relaxed) { |
|
isoname = iso_r_dirid(ascii_name, 8, relaxed); |
|
} else { |
|
isoname = iso_2_dirid(ascii_name); |
|
} |
|
} |
|
} else { |
|
if (img->max_37_char_filenames) { |
|
isoname = iso_r_fileid(ascii_name, 36, relaxed, |
|
img->no_force_dots ? 0 : 1); |
|
} else if (img->iso_level == 1) { |
|
if (relaxed) { |
|
isoname = iso_r_fileid(ascii_name, 11, relaxed, |
|
img->no_force_dots ? 0 : 1); |
|
} else { |
|
isoname = iso_1_fileid(ascii_name); |
|
} |
|
} else { |
|
if (relaxed) { |
|
isoname = iso_r_fileid(ascii_name, 30, relaxed, |
|
img->no_force_dots ? 0 : 1); |
|
} else { |
|
isoname = iso_2_fileid(ascii_name); |
|
} |
|
} |
|
} |
|
free(ascii_name); |
|
if (isoname != NULL) { |
|
*name = isoname; |
|
return ISO_SUCCESS; |
|
} else { |
|
/* |
|
* only possible if mem error, as check for empty names is done |
|
* in public tree |
|
*/ |
|
return ISO_OUT_OF_MEM; |
|
} |
|
} |
|
|
|
static |
|
int create_ecma119_node(Ecma119Image *img, IsoNode *iso, Ecma119Node **node) |
|
{ |
|
Ecma119Node *ecma; |
|
|
|
ecma = calloc(1, sizeof(Ecma119Node)); |
|
if (ecma == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
/* take a ref to the IsoNode */ |
|
ecma->node = iso; |
|
iso_node_ref(iso); |
|
|
|
/* TODO #00009 : add true support for harlinks and inode numbers */ |
|
ecma->nlink = 1; |
|
ecma->ino = ++img->ino; |
|
|
|
*node = ecma; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** |
|
* Create a new ECMA-119 node representing a directory from a iso directory |
|
* node. |
|
*/ |
|
static |
|
int create_dir(Ecma119Image *img, IsoDir *iso, Ecma119Node **node) |
|
{ |
|
int ret; |
|
Ecma119Node **children; |
|
struct ecma119_dir_info *dir_info; |
|
|
|
children = calloc(1, sizeof(void*) * iso->nchildren); |
|
if (children == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
dir_info = calloc(1, sizeof(struct ecma119_dir_info)); |
|
if (dir_info == NULL) { |
|
free(children); |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
ret = create_ecma119_node(img, (IsoNode*)iso, node); |
|
if (ret < 0) { |
|
free(children); |
|
free(dir_info); |
|
return ret; |
|
} |
|
(*node)->type = ECMA119_DIR; |
|
(*node)->info.dir = dir_info; |
|
(*node)->info.dir->nchildren = 0; |
|
(*node)->info.dir->children = children; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** |
|
* Create a new ECMA-119 node representing a regular file from a iso file |
|
* node. |
|
*/ |
|
static |
|
int create_file(Ecma119Image *img, IsoFile *iso, Ecma119Node **node) |
|
{ |
|
int ret; |
|
IsoFileSrc *src; |
|
off_t size; |
|
|
|
size = iso_stream_get_size(iso->stream); |
|
if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && img->iso_level != 3) { |
|
return iso_msg_submit(img->image->id, ISO_FILE_TOO_BIG, 0, |
|
"File \"%s\" can't be added to image because " |
|
"is greater than 4GB", iso->node.name); |
|
} |
|
|
|
ret = iso_file_src_create(img, iso, &src); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
ret = create_ecma119_node(img, (IsoNode*)iso, node); |
|
if (ret < 0) { |
|
/* |
|
* the src doesn't need to be freed, it is free together with |
|
* the Ecma119Image |
|
*/ |
|
return ret; |
|
} |
|
(*node)->type = ECMA119_FILE; |
|
(*node)->info.file = src; |
|
|
|
return ret; |
|
} |
|
|
|
/** |
|
* Create a new ECMA-119 node representing a regular file from an El-Torito |
|
* boot catalog |
|
*/ |
|
static |
|
int create_boot_cat(Ecma119Image *img, IsoBoot *iso, Ecma119Node **node) |
|
{ |
|
int ret; |
|
IsoFileSrc *src; |
|
|
|
ret = el_torito_catalog_file_src_create(img, &src); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
ret = create_ecma119_node(img, (IsoNode*)iso, node); |
|
if (ret < 0) { |
|
/* |
|
* the src doesn't need to be freed, it is free together with |
|
* the Ecma119Image |
|
*/ |
|
return ret; |
|
} |
|
(*node)->type = ECMA119_FILE; |
|
(*node)->info.file = src; |
|
|
|
return ret; |
|
} |
|
|
|
/** |
|
* Create a new ECMA-119 node representing a symbolic link from a iso symlink |
|
* node. |
|
*/ |
|
static |
|
int create_symlink(Ecma119Image *img, IsoSymlink *iso, Ecma119Node **node) |
|
{ |
|
int ret; |
|
|
|
ret = create_ecma119_node(img, (IsoNode*)iso, node); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
(*node)->type = ECMA119_SYMLINK; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** |
|
* Create a new ECMA-119 node representing a special file. |
|
*/ |
|
static |
|
int create_special(Ecma119Image *img, IsoSpecial *iso, Ecma119Node **node) |
|
{ |
|
int ret; |
|
|
|
ret = create_ecma119_node(img, (IsoNode*)iso, node); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
(*node)->type = ECMA119_SPECIAL; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
void ecma119_node_free(Ecma119Node *node) |
|
{ |
|
if (node == NULL) { |
|
return; |
|
} |
|
if (node->type == ECMA119_DIR) { |
|
int i; |
|
for (i = 0; i < node->info.dir->nchildren; i++) { |
|
ecma119_node_free(node->info.dir->children[i]); |
|
} |
|
free(node->info.dir->children); |
|
free(node->info.dir); |
|
} |
|
free(node->iso_name); |
|
iso_node_unref(node->node); |
|
free(node); |
|
} |
|
|
|
/** |
|
* |
|
* @return |
|
* 1 success, 0 node ignored, < 0 error |
|
* |
|
*/ |
|
static |
|
int create_tree(Ecma119Image *image, IsoNode *iso, Ecma119Node **tree, |
|
int depth, int pathlen) |
|
{ |
|
int ret; |
|
Ecma119Node *node; |
|
int max_path; |
|
char *iso_name= NULL; |
|
|
|
if (image == NULL || iso == NULL || tree == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
if (iso->hidden & LIBISO_HIDE_ON_RR) { |
|
/* file will be ignored */ |
|
return 0; |
|
} |
|
ret = get_iso_name(image, iso, &iso_name); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
max_path = pathlen + 1 + (iso_name ? strlen(iso_name) : 0); |
|
if (!image->rockridge) { |
|
if ((iso->type == LIBISO_DIR && depth > 8) && !image->allow_deep_paths) { |
|
free(iso_name); |
|
return iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0, |
|
"File \"%s\" can't be added, because directory depth " |
|
"is greater than 8.", iso->name); |
|
} else if (max_path > 255 && !image->allow_longer_paths) { |
|
free(iso_name); |
|
return iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0, |
|
"File \"%s\" can't be added, because path length " |
|
"is greater than 255 characters", iso->name); |
|
} |
|
} |
|
|
|
switch (iso->type) { |
|
case LIBISO_FILE: |
|
ret = create_file(image, (IsoFile*)iso, &node); |
|
break; |
|
case LIBISO_SYMLINK: |
|
if (image->rockridge) { |
|
ret = create_symlink(image, (IsoSymlink*)iso, &node); |
|
} else { |
|
/* symlinks are only supported when RR is enabled */ |
|
ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, |
|
"File \"%s\" ignored. Symlinks need RockRidge extensions.", |
|
iso->name); |
|
} |
|
break; |
|
case LIBISO_SPECIAL: |
|
if (image->rockridge) { |
|
ret = create_special(image, (IsoSpecial*)iso, &node); |
|
} else { |
|
/* symlinks are only supported when RR is enabled */ |
|
ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, |
|
"File \"%s\" ignored. Special files need RockRidge extensions.", |
|
iso->name); |
|
} |
|
break; |
|
case LIBISO_BOOT: |
|
if (image->eltorito) { |
|
ret = create_boot_cat(image, (IsoBoot*)iso, &node); |
|
} else { |
|
/* log and ignore */ |
|
ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, |
|
"El-Torito catalog found on a image without El-Torito.", |
|
iso->name); |
|
} |
|
break; |
|
case LIBISO_DIR: |
|
{ |
|
IsoNode *pos; |
|
IsoDir *dir = (IsoDir*)iso; |
|
ret = create_dir(image, dir, &node); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
pos = dir->children; |
|
while (pos) { |
|
int cret; |
|
Ecma119Node *child; |
|
cret = create_tree(image, pos, &child, depth + 1, max_path); |
|
if (cret < 0) { |
|
/* error */ |
|
ecma119_node_free(node); |
|
ret = cret; |
|
break; |
|
} else if (cret == ISO_SUCCESS) { |
|
/* add child to this node */ |
|
int nchildren = node->info.dir->nchildren++; |
|
node->info.dir->children[nchildren] = child; |
|
child->parent = node; |
|
} |
|
pos = pos->next; |
|
} |
|
} |
|
break; |
|
default: |
|
/* should never happen */ |
|
return ISO_ASSERT_FAILURE; |
|
} |
|
if (ret <= 0) { |
|
free(iso_name); |
|
return ret; |
|
} |
|
node->iso_name = iso_name; |
|
*tree = node; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** |
|
* Compare the iso name of two ECMA-119 nodes |
|
*/ |
|
static |
|
int cmp_node_name(const void *f1, const void *f2) |
|
{ |
|
Ecma119Node *f = *((Ecma119Node**)f1); |
|
Ecma119Node *g = *((Ecma119Node**)f2); |
|
return strcmp(f->iso_name, g->iso_name); |
|
} |
|
|
|
/** |
|
* Sorts a the children of each directory in the ECMA-119 tree represented |
|
* by \p root, acording to the order specified in ECMA-119, section 9.3. |
|
*/ |
|
static |
|
void sort_tree(Ecma119Node *root) |
|
{ |
|
size_t i; |
|
|
|
qsort(root->info.dir->children, root->info.dir->nchildren, sizeof(void*), |
|
cmp_node_name); |
|
for (i = 0; i < root->info.dir->nchildren; i++) { |
|
if (root->info.dir->children[i]->type == ECMA119_DIR) |
|
sort_tree(root->info.dir->children[i]); |
|
} |
|
} |
|
|
|
/** |
|
* Ensures that the ISO name of each children of the given dir is unique, |
|
* changing some of them if needed. |
|
* It also ensures that resulting filename is always <= than given |
|
* max_name_len, including extension. If needed, the extension will be reduced, |
|
* but never under 3 characters. |
|
*/ |
|
static |
|
int mangle_single_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len, |
|
int max_dir_len) |
|
{ |
|
int ret; |
|
int i, nchildren; |
|
Ecma119Node **children; |
|
IsoHTable *table; |
|
int need_sort = 0; |
|
|
|
nchildren = dir->info.dir->nchildren; |
|
children = dir->info.dir->children; |
|
|
|
/* a hash table will temporary hold the names, for fast searching */ |
|
ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash, |
|
(compare_function_t)strcmp, &table); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
for (i = 0; i < nchildren; ++i) { |
|
char *name = children[i]->iso_name; |
|
ret = iso_htable_add(table, name, name); |
|
if (ret < 0) { |
|
goto mangle_cleanup; |
|
} |
|
} |
|
|
|
for (i = 0; i < nchildren; ++i) { |
|
char *name, *ext; |
|
char full_name[40]; |
|
int max; /* computed max len for name, without extension */ |
|
int j = i; |
|
int digits = 1; /* characters to change per name */ |
|
|
|
/* first, find all child with same name */ |
|
while (j + 1 < nchildren && !cmp_node_name(children + i, children + j |
|
+ 1)) { |
|
++j; |
|
} |
|
if (j == i) { |
|
/* name is unique */ |
|
continue; |
|
} |
|
|
|
/* |
|
* 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 |
|
* max_name_len, but the int_pow() function must then be modified |
|
* to return a bigger integer. |
|
*/ |
|
while (digits < 8) { |
|
int ok, k; |
|
char *dot; |
|
int change = 0; /* number to be written */ |
|
|
|
/* copy name to buffer */ |
|
strcpy(full_name, children[i]->iso_name); |
|
|
|
/* compute name and extension */ |
|
dot = strrchr(full_name, '.'); |
|
if (dot != NULL && children[i]->type != ECMA119_DIR) { |
|
|
|
/* |
|
* File (not dir) with extension |
|
* Note that we don't need to check for placeholders, as |
|
* tree reparent happens later, so no placeholders can be |
|
* here at this time. |
|
*/ |
|
int extlen; |
|
full_name[dot - full_name] = '\0'; |
|
name = full_name; |
|
ext = dot + 1; |
|
|
|
/* |
|
* For iso level 1 we force ext len to be 3, as name |
|
* can't grow on the extension space |
|
*/ |
|
extlen = (max_file_len == 12) ? 3 : strlen(ext); |
|
max = max_file_len - extlen - 1 - digits; |
|
if (max <= 0) { |
|
/* this can happen if extension is too long */ |
|
if (extlen + max > 3) { |
|
/* |
|
* reduce extension len, to give name an extra char |
|
* note that max is negative or 0 |
|
*/ |
|
extlen = extlen + max - 1; |
|
ext[extlen] = '\0'; |
|
max = max_file_len - extlen - 1 - digits; |
|
} else { |
|
/* |
|
* error, we don't support extensions < 3 |
|
* This can't happen with current limit of digits. |
|
*/ |
|
ret = ISO_ERROR; |
|
goto mangle_cleanup; |
|
} |
|
} |
|
/* ok, reduce name by digits */ |
|
if (name + max < dot) { |
|
name[max] = '\0'; |
|
} |
|
} else { |
|
/* Directory, or file without extension */ |
|
if (children[i]->type == ECMA119_DIR) { |
|
max = max_dir_len - digits; |
|
dot = NULL; /* dots have no meaning in dirs */ |
|
} else { |
|
max = max_file_len - digits; |
|
} |
|
name = full_name; |
|
if (max < strlen(name)) { |
|
name[max] = '\0'; |
|
} |
|
/* let ext be an empty string */ |
|
ext = name + strlen(name); |
|
} |
|
|
|
ok = 1; |
|
/* change name of each file */ |
|
for (k = i; k <= j; ++k) { |
|
char tmp[40]; |
|
char fmt[16]; |
|
if (dot != NULL) { |
|
sprintf(fmt, "%%s%%0%dd.%%s", digits); |
|
} else { |
|
sprintf(fmt, "%%s%%0%dd%%s", digits); |
|
} |
|
while (1) { |
|
sprintf(tmp, fmt, name, change, ext); |
|
++change; |
|
if (change > int_pow(10, digits)) { |
|
ok = 0; |
|
break; |
|
} |
|
if (!iso_htable_get(table, tmp, NULL)) { |
|
/* the name is unique, so it can be used */ |
|
break; |
|
} |
|
} |
|
if (ok) { |
|
char *new = strdup(tmp); |
|
if (new == NULL) { |
|
ret = ISO_OUT_OF_MEM; |
|
goto mangle_cleanup; |
|
} |
|
iso_msg_debug(img->image->id, "\"%s\" renamed to \"%s\"", |
|
children[k]->iso_name, new); |
|
|
|
iso_htable_remove_ptr(table, children[k]->iso_name, NULL); |
|
free(children[k]->iso_name); |
|
children[k]->iso_name = new; |
|
iso_htable_add(table, new, new); |
|
|
|
/* |
|
* if we change a name we need to sort again children |
|
* at the end |
|
*/ |
|
need_sort = 1; |
|
} else { |
|
/* we need to increment digits */ |
|
break; |
|
} |
|
} |
|
if (ok) { |
|
break; |
|
} else { |
|
++digits; |
|
} |
|
} |
|
if (digits == 8) { |
|
ret = ISO_MANGLE_TOO_MUCH_FILES; |
|
goto mangle_cleanup; |
|
} |
|
i = j; |
|
} |
|
|
|
/* |
|
* If needed, sort again the files inside dir |
|
*/ |
|
if (need_sort) { |
|
qsort(children, nchildren, sizeof(void*), cmp_node_name); |
|
} |
|
|
|
ret = ISO_SUCCESS; |
|
|
|
mangle_cleanup : ; |
|
iso_htable_destroy(table, NULL); |
|
return ret; |
|
} |
|
|
|
static |
|
int mangle_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len, |
|
int max_dir_len) |
|
{ |
|
int ret; |
|
size_t i; |
|
|
|
ret = mangle_single_dir(img, dir, max_file_len, max_dir_len); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
/* recurse */ |
|
for (i = 0; i < dir->info.dir->nchildren; ++i) { |
|
if (dir->info.dir->children[i]->type == ECMA119_DIR) { |
|
ret = mangle_dir(img, dir->info.dir->children[i], max_file_len, |
|
max_dir_len); |
|
if (ret < 0) { |
|
/* error */ |
|
return ret; |
|
} |
|
} |
|
} |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int mangle_tree(Ecma119Image *img, int recurse) |
|
{ |
|
int max_file, max_dir; |
|
|
|
if (img->max_37_char_filenames) { |
|
max_file = max_dir = 37; |
|
} else if (img->iso_level == 1) { |
|
max_file = 12; /* 8 + 3 + 1 */ |
|
max_dir = 8; |
|
} else { |
|
max_file = max_dir = 31; |
|
} |
|
if (recurse) { |
|
return mangle_dir(img, img->root, max_file, max_dir); |
|
} else { |
|
return mangle_single_dir(img, img->root, max_file, max_dir); |
|
} |
|
} |
|
|
|
/** |
|
* Create a new ECMA-119 node representing a placeholder for a relocated |
|
* dir. |
|
* |
|
* See IEEE P1282, section 4.1.5 for details |
|
*/ |
|
static |
|
int create_placeholder(Ecma119Node *parent, Ecma119Node *real, |
|
Ecma119Node **node) |
|
{ |
|
Ecma119Node *ret; |
|
|
|
ret = calloc(1, sizeof(Ecma119Node)); |
|
if (ret == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
/* |
|
* TODO |
|
* If real is a dir, while placeholder is a file, ISO name restricctions |
|
* are different, what to do? |
|
*/ |
|
ret->iso_name = strdup(real->iso_name); |
|
if (ret->iso_name == NULL) { |
|
free(ret); |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
/* take a ref to the IsoNode */ |
|
ret->node = real->node; |
|
iso_node_ref(real->node); |
|
ret->parent = parent; |
|
ret->type = ECMA119_PLACEHOLDER; |
|
ret->info.real_me = real; |
|
ret->ino = real->ino; |
|
ret->nlink = real->nlink; |
|
|
|
*node = ret; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
size_t max_child_name_len(Ecma119Node *dir) |
|
{ |
|
size_t ret = 0, i; |
|
for (i = 0; i < dir->info.dir->nchildren; i++) { |
|
size_t len = strlen(dir->info.dir->children[i]->iso_name); |
|
ret = MAX(ret, len); |
|
} |
|
return ret; |
|
} |
|
|
|
/** |
|
* Relocates a directory, as specified in Rock Ridge Specification |
|
* (see IEEE P1282, section 4.1.5). This is needed when the number of levels |
|
* on a directory hierarchy exceeds 8, or the length of a path is higher |
|
* than 255 characters, as specified in ECMA-119, section 6.8.2.1 |
|
*/ |
|
static |
|
int reparent(Ecma119Node *child, Ecma119Node *parent) |
|
{ |
|
int ret; |
|
size_t i; |
|
Ecma119Node *placeholder; |
|
|
|
/* replace the child in the original parent with a placeholder */ |
|
for (i = 0; i < child->parent->info.dir->nchildren; i++) { |
|
if (child->parent->info.dir->children[i] == child) { |
|
ret = create_placeholder(child->parent, child, &placeholder); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
child->parent->info.dir->children[i] = placeholder; |
|
break; |
|
} |
|
} |
|
|
|
/* just for debug, this should never happen... */ |
|
if (i == child->parent->info.dir->nchildren) { |
|
return ISO_ASSERT_FAILURE; |
|
} |
|
|
|
/* keep track of the real parent */ |
|
child->info.dir->real_parent = child->parent; |
|
|
|
/* add the child to its new parent */ |
|
child->parent = parent; |
|
parent->info.dir->nchildren++; |
|
parent->info.dir->children = realloc(parent->info.dir->children, |
|
sizeof(void*) * parent->info.dir->nchildren); |
|
parent->info.dir->children[parent->info.dir->nchildren - 1] = child; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** |
|
* Reorder the tree, if necessary, to ensure that |
|
* - the depth is at most 8 |
|
* - each path length is at most 255 characters |
|
* This restriction is imposed by ECMA-119 specification (ECMA-119, 6.8.2.1). |
|
* |
|
* @param dir |
|
* Dir we are currently processing |
|
* @param level |
|
* Level of the directory in the hierarchy |
|
* @param pathlen |
|
* Length of the path until dir, including it |
|
* @return |
|
* 1 success, < 0 error |
|
*/ |
|
static |
|
int reorder_tree(Ecma119Image *img, Ecma119Node *dir, int level, int pathlen) |
|
{ |
|
int ret; |
|
size_t max_path; |
|
|
|
max_path = pathlen + 1 + max_child_name_len(dir); |
|
|
|
if (level > 8 || max_path > 255) { |
|
ret = reparent(dir, img->root); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
/* |
|
* we are appended to the root's children now, so there is no |
|
* need to recurse (the root will hit us again) |
|
*/ |
|
} else { |
|
size_t i; |
|
|
|
for (i = 0; i < dir->info.dir->nchildren; i++) { |
|
Ecma119Node *child = dir->info.dir->children[i]; |
|
if (child->type == ECMA119_DIR) { |
|
int newpathlen = pathlen + 1 + strlen(child->iso_name); |
|
ret = reorder_tree(img, child, level + 1, newpathlen); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
} |
|
} |
|
} |
|
return ISO_SUCCESS; |
|
} |
|
|
|
int ecma119_tree_create(Ecma119Image *img) |
|
{ |
|
int ret; |
|
Ecma119Node *root; |
|
|
|
ret = create_tree(img, (IsoNode*)img->image->root, &root, 1, 0); |
|
if (ret <= 0) { |
|
if (ret == 0) { |
|
/* unexpected error, root ignored!! This can't happen */ |
|
ret = ISO_ASSERT_FAILURE; |
|
} |
|
return ret; |
|
} |
|
img->root = root; |
|
|
|
iso_msg_debug(img->image->id, "Sorting the low level tree..."); |
|
sort_tree(root); |
|
|
|
iso_msg_debug(img->image->id, "Mangling names..."); |
|
ret = mangle_tree(img, 1); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
if (img->rockridge && !img->allow_deep_paths) { |
|
|
|
/* reorder the tree, acording to RRIP, 4.1.5 */ |
|
ret = reorder_tree(img, img->root, 1, 0); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
/* |
|
* and we need to remangle the root directory, as the function |
|
* above could insert new directories into the root. |
|
* Note that recurse = 0, as we don't need to recurse. |
|
*/ |
|
ret = mangle_tree(img, 0); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
} |
|
|
|
return ISO_SUCCESS; |
|
}
|
|
|