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.
489 lines
13 KiB
489 lines
13 KiB
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ |
|
/* vim: set noet ts=8 sts=8 sw=8 : */ |
|
|
|
#include "joliet.h" |
|
#include "ecma119.h" |
|
#include "ecma119_tree.h" |
|
#include "tree.h" |
|
#include "util.h" |
|
#include "volume.h" |
|
#include "eltorito.h" |
|
#include "messages.h" |
|
|
|
#include <assert.h> |
|
#include <string.h> |
|
|
|
static struct joliet_tree_node* |
|
create_node(struct ecma119_write_target *t, |
|
struct joliet_tree_node *parent, |
|
struct iso_tree_node *iso) |
|
{ |
|
struct joliet_tree_node *ret = |
|
calloc(1, sizeof(struct joliet_tree_node)); |
|
|
|
ret->name = iso_j_id(iso->name, t->input_charset); |
|
ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0); |
|
ret->parent = parent; |
|
ret->target = t; |
|
|
|
if ( ISO_ISDIR(iso) ) { |
|
struct iso_tree_node_dir *dir = (struct iso_tree_node_dir *) iso; |
|
ret->info.dir.children = calloc(sizeof(void*), dir->nchildren); |
|
ret->type = JOLIET_DIR; |
|
} else if (ISO_ISREG(iso)) { |
|
/* it's a file */ |
|
struct iso_tree_node_file *iso_f = (struct iso_tree_node_file *) iso; |
|
struct iso_file *file; |
|
file = iso_file_table_lookup(t->file_table, iso_f); |
|
if ( file == NULL ) { |
|
file = iso_file_new(t, iso_f); |
|
if (!file) { |
|
/* |
|
* That was an error. |
|
* TODO currently this cause the file to be ignored... Maybe |
|
* throw an error is a better alternative |
|
*/ |
|
joliet_tree_free(ret); |
|
return NULL; |
|
} |
|
iso_file_table_add_file(t->file_table, file); |
|
} |
|
ret->info.file = file; |
|
ret->type = JOLIET_FILE; |
|
} else if (ISO_ISBOOT(iso)) { |
|
/* it's boot catalog info */ |
|
struct iso_tree_node_boot *boot = (struct iso_tree_node_boot*) iso; |
|
ret->info.boot_img = boot->img; |
|
ret->type = JOLIET_BOOT; |
|
} else { |
|
/* this should never happen */ |
|
assert(0); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static struct joliet_tree_node* |
|
create_tree(struct ecma119_write_target *t, |
|
struct joliet_tree_node *parent, |
|
struct iso_tree_node *iso) |
|
{ |
|
struct joliet_tree_node *root; |
|
assert(t && iso); |
|
|
|
if ( iso->hide_flags & LIBISO_HIDE_ON_JOLIET ) |
|
return NULL; |
|
|
|
switch (iso->type) { |
|
case LIBISO_NODE_FILE: |
|
case LIBISO_NODE_BOOT: |
|
root = create_node(t, parent, iso); |
|
break; |
|
case LIBISO_NODE_DIR: |
|
{ |
|
size_t i; |
|
struct joliet_tree_node *node; |
|
struct iso_tree_node_dir *dir; |
|
root = create_node(t, parent, iso); |
|
dir = (struct iso_tree_node_dir*)iso; |
|
for (i = 0; i < dir->nchildren; ++i) { |
|
node = create_tree(t, root, dir->children[i]); |
|
if ( node != NULL ) { |
|
root->info.dir.children[root->info.dir.nchildren++] = node; |
|
} |
|
} |
|
} |
|
break; |
|
default: |
|
{ |
|
char msg[512]; |
|
sprintf(msg, "Can't add %s to Joliet tree. This kind of files " |
|
"can only be added to a Rock Ridget tree. Skipping", iso->name); |
|
iso_msg_note(LIBISO_JOLIET_WRONG_FILE_TYPE, msg); |
|
return NULL; |
|
} |
|
break; |
|
} |
|
return root; |
|
} |
|
|
|
static int |
|
cmp_node(const void *f1, const void *f2) |
|
{ |
|
struct joliet_tree_node *f = *((struct joliet_tree_node**)f1); |
|
struct joliet_tree_node *g = *((struct joliet_tree_node**)f2); |
|
return ucscmp(f->name, g->name); |
|
} |
|
|
|
static void |
|
sort_tree(struct joliet_tree_node *root) |
|
{ |
|
size_t i; |
|
|
|
assert(root && (root->type == JOLIET_DIR)); |
|
|
|
qsort(root->info.dir.children, root->info.dir.nchildren, |
|
sizeof(void*), cmp_node); |
|
for (i = 0; i < root->info.dir.nchildren; i++) { |
|
struct joliet_tree_node *child = root->info.dir.children[i]; |
|
if ( child->type == JOLIET_DIR ) |
|
sort_tree(child); |
|
} |
|
} |
|
|
|
void |
|
joliet_prepare_path_tables(struct ecma119_write_target *t) |
|
{ |
|
size_t cur, i, j; |
|
|
|
t->pathlist_joliet[0] = t->joliet_root; |
|
t->path_table_size_joliet = 10; /* root directory record */ |
|
cur = 1; |
|
|
|
for (i = 0; i < t->dirlist_len_joliet; i++) { |
|
struct joliet_tree_node *dir = t->pathlist_joliet[i]; |
|
for (j = 0; j < dir->info.dir.nchildren; j++) { |
|
struct joliet_tree_node *ch = dir->info.dir.children[j]; |
|
if (ch->type == JOLIET_DIR) { |
|
size_t len = 8 + ucslen(ch->name)*2; |
|
t->pathlist_joliet[cur++] = ch; |
|
t->path_table_size_joliet += len; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Calculate the size of each directory. |
|
*/ |
|
void |
|
joliet_calc_dir_size(struct ecma119_write_target *t, |
|
struct joliet_tree_node *root) |
|
{ |
|
size_t i; |
|
size_t newlen; |
|
struct joliet_tree_node *ch; |
|
|
|
assert(root && (root->type == JOLIET_DIR) ); |
|
|
|
t->dirlist_len_joliet++; |
|
root->info.dir.len = 68; /* for "." and ".." entries */ |
|
for (i = 0; i < root->info.dir.nchildren; ++i) { |
|
ch = root->info.dir.children[i]; |
|
newlen = root->info.dir.len + ch->dirent_len; |
|
if ((newlen % 2048) < (root->info.dir.len % 2048)) { |
|
root->info.dir.len = newlen + (2048 - (root->info.dir.len % 2048)); |
|
} else { |
|
root->info.dir.len += ch->dirent_len; |
|
} |
|
if (ch->type == JOLIET_DIR) |
|
joliet_calc_dir_size(t, ch); |
|
} |
|
t->total_dir_size_joliet += round_up (root->info.dir.len, t->block_size); |
|
} |
|
|
|
/** |
|
* Calculate the position of each directory. Also fill out t->dirlist_joliet. |
|
*/ |
|
void |
|
joliet_calc_dir_pos(struct ecma119_write_target *t, |
|
struct joliet_tree_node *root) |
|
{ |
|
size_t i; |
|
struct joliet_tree_node *ch; |
|
|
|
assert(root && (root->type == JOLIET_DIR)); |
|
|
|
root->info.dir.block = t->curblock; |
|
t->curblock += div_up(root->info.dir.len, t->block_size); |
|
|
|
t->dirlist_joliet[t->curfile++] = root; |
|
for (i = 0; i < root->info.dir.nchildren; i++) { |
|
ch = root->info.dir.children[i]; |
|
if (ch->type == JOLIET_DIR) |
|
joliet_calc_dir_pos(t, ch); |
|
} |
|
} |
|
|
|
struct joliet_tree_node* |
|
joliet_tree_create(struct ecma119_write_target *t, |
|
struct iso_tree_node *iso_root) |
|
{ |
|
struct joliet_tree_node *root = create_tree(t, NULL, iso_root); |
|
|
|
sort_tree(root); |
|
return root; |
|
} |
|
|
|
void |
|
joliet_tree_free(struct joliet_tree_node *root) |
|
{ |
|
size_t i; |
|
|
|
if (root->type == JOLIET_DIR) { |
|
for (i=0; i < root->info.dir.nchildren; i++) { |
|
joliet_tree_free(root->info.dir.children[i]); |
|
} |
|
free(root->info.dir.children); |
|
} |
|
free(root->name); |
|
free(root); |
|
} |
|
|
|
/* ugh. this is mostly C&P */ |
|
static void |
|
write_path_table(struct ecma119_write_target *t, |
|
int l_type, |
|
uint8_t *buf) |
|
{ |
|
void (*write_int)(uint8_t*, uint32_t, int) = l_type ? |
|
iso_lsb : iso_msb; |
|
|
|
size_t i; |
|
struct ecma119_path_table_record *rec; |
|
struct joliet_tree_node *dir; |
|
int parent = 0; |
|
|
|
assert (t->joliet); |
|
|
|
for (i = 0; i < t->dirlist_len; i++) { |
|
dir = t->pathlist_joliet[i]; |
|
while ((i) && t->pathlist_joliet[parent] != dir->parent) |
|
parent++; |
|
assert(parent < i || i == 0); |
|
|
|
rec = (struct ecma119_path_table_record*) buf; |
|
rec->len_di[0] = dir->parent ? |
|
(uint8_t) ucslen(dir->name) * 2 : 1; |
|
rec->len_xa[0] = 0; |
|
write_int(rec->block, dir->info.dir.block, 4); |
|
write_int(rec->parent, parent + 1, 2); |
|
if (dir->parent) |
|
memcpy(rec->dir_id, dir->name, rec->len_di[0]); |
|
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2); |
|
} |
|
|
|
} |
|
|
|
/* if file_id is >= 0, we use it instead of the filename. As a magic number, |
|
* file_id == 3 means that we are writing the root directory record (in order |
|
* to distinguish it from the "." entry in the root directory) */ |
|
static void |
|
write_one_dir_record(struct ecma119_write_target *t, |
|
struct joliet_tree_node *node, |
|
int file_id, |
|
uint8_t *buf) |
|
{ |
|
uint32_t len; |
|
uint32_t block; |
|
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; |
|
uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2; |
|
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); |
|
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name; |
|
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; |
|
|
|
if (node->type == JOLIET_DIR) { |
|
len = node->info.dir.len; |
|
block = node->info.dir.block; |
|
} else if (node->type == JOLIET_BOOT) { |
|
assert(t->eltorito); |
|
if (node->info.boot_img) { |
|
block = t->imgblock; |
|
len = t->catalog->image->node->node.attrib.st_size; |
|
} else { |
|
/* we always assume 2048 as catalog len */ |
|
block = t->catblock; |
|
len = 2048; |
|
} |
|
} else { |
|
/* file */ |
|
assert(node->type == JOLIET_FILE); |
|
len = node->info.file->size; |
|
block = node->info.file->block; |
|
} |
|
|
|
if (file_id == 1 && node->parent) |
|
node = node->parent; |
|
|
|
rec->len_dr[0] = len_dr; |
|
iso_bb(rec->block, block, 4); |
|
iso_bb(rec->length, len, 4); |
|
iso_datetime_7(rec->recording_time, t->now); |
|
rec->flags[0] = (node->type == JOLIET_DIR) ? 2 : 0; |
|
iso_bb(rec->vol_seq_number, t->volnum + 1, 2); |
|
rec->len_fi[0] = len_fi; |
|
memcpy(rec->file_id, name, len_fi); |
|
} |
|
|
|
static void |
|
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf) |
|
{ |
|
write_path_table (t, 1, buf); |
|
} |
|
|
|
static void |
|
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf) |
|
{ |
|
write_path_table (t, 0, buf); |
|
} |
|
|
|
void |
|
joliet_write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf) |
|
{ |
|
struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf; |
|
struct iso_volume *volume = t->volset->volume[t->volnum]; |
|
uint16_t *vol_id = str2ucs(volume->volume_id, t->input_charset); |
|
uint16_t *pub_id = str2ucs(volume->publisher_id, t->input_charset); |
|
uint16_t *data_id = str2ucs(volume->data_preparer_id, t->input_charset); |
|
uint16_t *volset_id = str2ucs(t->volset->volset_id, t->input_charset); |
|
int vol_id_len = MIN(32, ucslen(vol_id) * 2); |
|
int pub_id_len = MIN(128, ucslen(pub_id) * 2); |
|
int data_id_len = MIN(128, ucslen(data_id) * 2); |
|
int volset_id_len = MIN(128, ucslen(volset_id) * 2); |
|
|
|
uint16_t *system_id = str2ucs(volume->system_id, t->input_charset); |
|
uint16_t *application_id = str2ucs(volume->application_id, t->input_charset); |
|
uint16_t *copyright_file_id = str2ucs(volume->copyright_file_id, t->input_charset); |
|
uint16_t *abstract_file_id = str2ucs(volume->abstract_file_id, t->input_charset); |
|
uint16_t *biblio_file_id = str2ucs(volume->biblio_file_id, t->input_charset); |
|
int system_id_len = MIN(32, ucslen(system_id) * 2); |
|
int application_id_len = MIN(128, ucslen(application_id) * 2); |
|
int copyright_file_id_len = MIN(37, ucslen(copyright_file_id) * 2); |
|
int abstract_file_id_len = MIN(37, ucslen(abstract_file_id) * 2); |
|
int biblio_file_id_len = MIN(37, ucslen(biblio_file_id) * 2); |
|
|
|
vol->vol_desc_type[0] = 2; |
|
memcpy(vol->std_identifier, "CD001", 5); |
|
vol->vol_desc_version[0] = 1; |
|
if (vol_id) |
|
memcpy(vol->volume_id, vol_id, vol_id_len); |
|
memcpy(vol->esc_sequences, "%/E", 3); |
|
iso_bb(vol->vol_space_size, t->vol_space_size, 4); |
|
iso_bb(vol->vol_set_size, t->volset->volset_size, 2); |
|
iso_bb(vol->vol_seq_number, t->volnum + 1, 2); |
|
iso_bb(vol->block_size, t->block_size, 2); |
|
iso_bb(vol->path_table_size, t->path_table_size_joliet, 4); |
|
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4); |
|
iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4); |
|
|
|
write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record); |
|
|
|
memcpy(vol->vol_set_id, volset_id, volset_id_len); |
|
memcpy(vol->publisher_id, pub_id, pub_id_len); |
|
memcpy(vol->data_prep_id, data_id, data_id_len); |
|
|
|
memcpy(vol->system_id, system_id, system_id_len); |
|
memcpy(vol->application_id, "APPID", application_id_len); |
|
memcpy(vol->copyright_file_id, copyright_file_id, copyright_file_id_len); |
|
memcpy(vol->abstract_file_id, abstract_file_id, abstract_file_id_len); |
|
memcpy(vol->bibliographic_file_id, biblio_file_id, biblio_file_id_len); |
|
|
|
iso_datetime_17(vol->vol_creation_time, t->now); |
|
iso_datetime_17(vol->vol_modification_time, t->now); |
|
iso_datetime_17(vol->vol_effective_time, t->now); |
|
vol->file_structure_version[0] = 1; |
|
|
|
free(vol_id); |
|
free(volset_id); |
|
free(pub_id); |
|
free(data_id); |
|
free(system_id); |
|
free(application_id); |
|
free(copyright_file_id); |
|
free(abstract_file_id); |
|
free(biblio_file_id); |
|
|
|
} |
|
|
|
static void |
|
write_one_dir(struct ecma119_write_target *t, |
|
struct joliet_tree_node *dir, |
|
uint8_t *buf) |
|
{ |
|
size_t i; |
|
int j; |
|
size_t len; |
|
uint8_t *orig_buf = buf; |
|
uint8_t *prior_buf = buf; |
|
|
|
assert(dir->type == JOLIET_DIR); |
|
/* write the "." and ".." entries first */ |
|
write_one_dir_record(t, dir, 0, buf); |
|
buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; |
|
|
|
write_one_dir_record(t, dir, 1, buf); |
|
buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; |
|
|
|
for (i = 0; i < dir->info.dir.nchildren; i++) { |
|
write_one_dir_record(t, dir->info.dir.children[i], -1, buf); |
|
len = ((struct ecma119_dir_record*) buf)->len_dr[0]; |
|
if ((buf + len - prior_buf) >= 2048) { |
|
for (j = len - 1; j >= 0; j--) { |
|
prior_buf[2048 + j] = buf[j]; |
|
buf[j] = 0; |
|
} |
|
prior_buf += 2048; |
|
buf = prior_buf + len; |
|
} |
|
else { |
|
buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; |
|
} |
|
} |
|
|
|
assert (buf - orig_buf == dir->info.dir.len); |
|
} |
|
|
|
static void |
|
write_dirs(struct ecma119_write_target *t, uint8_t *buf) |
|
{ |
|
size_t i; |
|
struct joliet_tree_node *dir; |
|
|
|
assert (t->curblock + t->ms_block == t->dirlist_joliet[0]->info.dir.block); |
|
for (i = 0; i < t->dirlist_len_joliet; i++) { |
|
dir = t->dirlist_joliet[i]; |
|
write_one_dir(t, dir, buf); |
|
buf += round_up(dir->info.dir.len, t->block_size); |
|
} |
|
} |
|
|
|
void |
|
joliet_wr_sup_vol_desc(struct ecma119_write_target *t, |
|
uint8_t *buf) |
|
{ |
|
ecma119_start_chunking(t, |
|
joliet_write_sup_vol_desc, |
|
2048, |
|
buf); |
|
} |
|
|
|
void |
|
joliet_wr_l_path_table(struct ecma119_write_target *t, |
|
uint8_t *buf) |
|
{ |
|
ecma119_start_chunking(t, |
|
write_l_path_table, |
|
t->path_table_size_joliet, |
|
buf); |
|
} |
|
|
|
void |
|
joliet_wr_m_path_table(struct ecma119_write_target *t, |
|
uint8_t *buf) |
|
{ |
|
ecma119_start_chunking(t, |
|
write_m_path_table, |
|
t->path_table_size_joliet, |
|
buf); |
|
} |
|
|
|
void |
|
joliet_wr_dir_records(struct ecma119_write_target *t, |
|
uint8_t *buf) |
|
{ |
|
ecma119_start_chunking(t, |
|
write_dirs, |
|
t->total_dir_size_joliet, |
|
buf); |
|
} |
|
|
|
|