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.

4543 lines
133 KiB

/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic
* Copyright (c) 2009 - 2017 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
* or later as published by the Free Software Foundation.
* See COPYING file for details.
*/
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
/*
Use the copy of the struct burn_source definition in libisofs.h
*/
#define LIBISOFS_WITHOUT_LIBBURN yes
#include "libisofs.h"
#include "ecma119.h"
14 years ago
#include "joliet.h"
#include "hfsplus.h"
#include "iso1999.h"
#include "eltorito.h"
#include "ecma119_tree.h"
#include "filesrc.h"
#include "image.h"
#include "writer.h"
#include "messages.h"
#include "rockridge.h"
#include "util.h"
#include "system_area.h"
#include "md5.h"
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <locale.h>
#include <langinfo.h>
#include <stdio.h>
#ifdef Xorriso_standalonE
#ifdef Xorriso_with_libjtE
#include "../libjte/libjte.h"
#endif
#else
#ifdef Libisofs_with_libjtE
#include <libjte/libjte.h>
#endif
#endif /* ! Xorriso_standalonE */
int iso_write_opts_clone(IsoWriteOpts *in, IsoWriteOpts **out, int flag);
/*
* TODO #00011 : guard against bad path table usage with more than 65535 dirs
* image with more than 65535 directories have path_table related problems
* due to 16 bits parent id. Note that this problem only affects to folders
* that are parent of another folder.
*/
static
void ecma119_image_free(Ecma119Image *t)
{
size_t i;
if (t == NULL)
return;
if (t->refcount > 1) {
t->refcount--;
return;
}
if (t->root != NULL)
ecma119_node_free(t->root);
if (t->opts != NULL)
iso_write_opts_free(t->opts);
if (t->image != NULL)
iso_image_unref(t->image);
if (t->files != NULL)
iso_rbtree_destroy(t->files, iso_file_src_free);
if (t->ecma119_hidden_list != NULL)
iso_filesrc_list_destroy(&(t->ecma119_hidden_list));
if (t->buffer != NULL)
iso_ring_buffer_free(t->buffer);
for (i = 0; i < t->nwriters; ++i) {
IsoImageWriter *writer = t->writers[i];
writer->free_data(writer);
free(writer);
}
if (t->input_charset != NULL)
free(t->input_charset);
if (t->output_charset != NULL)
free(t->output_charset);
if (t->bootsrc != NULL)
free(t->bootsrc);
if (t->boot_appended_idx != NULL)
free(t->boot_appended_idx);
if (t->boot_intvl_start != NULL)
free(t->boot_intvl_start);
if (t->boot_intvl_size != NULL)
free(t->boot_intvl_size);
if (t->system_area_data != NULL)
free(t->system_area_data);
if (t->checksum_ctx != NULL) { /* dispose checksum context */
char md5[16];
iso_md5_end(&(t->checksum_ctx), md5);
}
if (t->checksum_buffer != NULL)
free(t->checksum_buffer);
if (t->writers != NULL)
free(t->writers);
if (t->partition_root != NULL)
ecma119_node_free(t->partition_root);
for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++)
if (t->hfsplus_blessed[i] != NULL)
iso_node_unref(t->hfsplus_blessed[i]);
for (i = 0; (int) i < t->apm_req_count; i++)
if (t->apm_req[i] != NULL)
free(t->apm_req[i]);
for (i = 0; (int) i < t->mbr_req_count; i++)
if (t->mbr_req[i] != NULL)
free(t->mbr_req[i]);
for (i = 0; (int) i < t->gpt_req_count; i++)
if (t->gpt_req[i] != NULL)
free(t->gpt_req[i]);
free(t);
}
static int show_chunk_to_jte(Ecma119Image *target, char *buf, int count)
{
#ifdef Libisofs_with_libjtE
int ret;
if (target->opts->libjte_handle == NULL)
return ISO_SUCCESS;
ret = libjte_show_data_chunk(target->opts->libjte_handle, buf, count, 1);
if (ret <= 0) {
iso_libjte_forward_msgs(target->opts->libjte_handle,
target->image->id, ISO_LIBJTE_FILE_FAILED, 0);
return ISO_LIBJTE_FILE_FAILED;
}
#endif /* Libisofs_with_libjtE */
return ISO_SUCCESS;
}
/**
* Check if we should add version number ";" to the given node name.
*/
static
int need_version_number(IsoWriteOpts *opts, enum ecma119_node_type node_type)
{
if ((opts->omit_version_numbers & 1) ||
opts->max_37_char_filenames || opts->untranslated_name_len > 0) {
return 0;
}
if (node_type == ECMA119_DIR || node_type == ECMA119_PLACEHOLDER) {
return 0;
} else {
return 1;
}
}
/**
* Compute the size of a directory entry for a single node
*/
static
size_t calc_dirent_len(Ecma119Image *t, Ecma119Node *n)
{
int ret = n->iso_name ? strlen(n->iso_name) + 33 : 34;
if (need_version_number(t->opts, n->type)) {
ret += 2; /* take into account version numbers */
}
if (ret % 2)
ret++;
return ret;
}
/**
* Computes the total size of all directory entries of a single dir,
* according to ECMA-119 6.8.1.1
*
* This also take into account the size needed for RR entries and
* SUSP continuation areas (SUSP, 5.1).
*
* @param ce
* Will be filled with the size needed for Continuation Areas
* @return
* The size needed for all dir entries of the given dir, without
* taking into account the continuation areas.
*/
static
size_t calc_dir_size(Ecma119Image *t, Ecma119Node *dir, size_t *ce)
{
size_t i, len;
size_t ce_len = 0;
/* size of "." and ".." entries */
len = 34 + 34;
if (t->opts->rockridge) {
len += rrip_calc_len(t, dir, 1, 34, &ce_len, *ce);
*ce += ce_len;
len += rrip_calc_len(t, dir, 2, 34, &ce_len, *ce);
*ce += ce_len;
}
for (i = 0; i < dir->info.dir->nchildren; ++i) {
size_t remaining;
int section, nsections;
Ecma119Node *child = dir->info.dir->children[i];
nsections = (child->type == ECMA119_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
size_t dirent_len = calc_dirent_len(t, child);
if (t->opts->rockridge) {
dirent_len += rrip_calc_len(t, child, 0, dirent_len, &ce_len,
*ce);
*ce += ce_len;
}
remaining = BLOCK_SIZE - (len % BLOCK_SIZE);
if (dirent_len > remaining) {
/* child directory entry doesn't fit on block */
len += remaining + dirent_len;
} else {
len += dirent_len;
}
}
}
/*
* The size of a dir is always a multiple of block size, as we must add
* the size of the unused space after the last directory record
* (ECMA-119, 6.8.1.3)
*/
len = ROUND_UP(len, BLOCK_SIZE);
/* cache the len */
dir->info.dir->len = len;
return len;
}
static
void calc_dir_pos(Ecma119Image *t, Ecma119Node *dir)
{
size_t i, len;
size_t ce_len = 0;
t->ndirs++;
dir->info.dir->block = t->curblock;
len = calc_dir_size(t, dir, &ce_len);
t->curblock += DIV_UP(len, BLOCK_SIZE);
if (t->opts->rockridge) {
t->curblock += DIV_UP(ce_len, BLOCK_SIZE);
}
for (i = 0; i < dir->info.dir->nchildren; i++) {
Ecma119Node *child = dir->info.dir->children[i];
if (child->type == ECMA119_DIR) {
calc_dir_pos(t, child);
}
}
}
/**
* Compute the length of the path table, in bytes.
*/
static
uint32_t calc_path_table_size(Ecma119Node *dir)
{
uint32_t size;
size_t i;
/* size of path table for this entry */
size = 8;
size += dir->iso_name ? strlen(dir->iso_name) : 1;
size += (size % 2);
/* and recurse */
for (i = 0; i < dir->info.dir->nchildren; i++) {
Ecma119Node *child = dir->info.dir->children[i];
if (child->type == ECMA119_DIR) {
size += calc_path_table_size(child);
}
}
return size;
}
static
int ecma119_writer_compute_data_blocks(IsoImageWriter *writer)
{
Ecma119Image *target;
uint32_t path_table_size;
size_t ndirs;
if (writer == NULL) {
return ISO_ASSERT_FAILURE;
}
target = writer->target;
/* compute position of directories */
iso_msg_debug(target->image->id, "Computing position of dir structure");
target->ndirs = 0;
calc_dir_pos(target, target->root);
/* compute length of pathlist */
iso_msg_debug(target->image->id, "Computing length of pathlist");
path_table_size = calc_path_table_size(target->root);
/* compute location for path tables */
target->l_path_table_pos = target->curblock;
target->curblock += DIV_UP(path_table_size, BLOCK_SIZE);
target->m_path_table_pos = target->curblock;
target->curblock += DIV_UP(path_table_size, BLOCK_SIZE);
target->path_table_size = path_table_size;
if (target->opts->md5_session_checksum) {
/* Account for first tree checksum tag */
target->checksum_tree_tag_pos = target->curblock;
target->curblock++;
}
if (target->opts->partition_offset > 0) {
/* Take into respect the second directory tree */
ndirs = target->ndirs;
target->ndirs = 0;
calc_dir_pos(target, target->partition_root);
if (target->ndirs != ndirs) {
iso_msg_submit(target->image->id, ISO_ASSERT_FAILURE, 0,
"Number of directories differs in ECMA-119 partiton_tree");
return ISO_ASSERT_FAILURE;
}
/* Take into respect the second set of path tables */
path_table_size = calc_path_table_size(target->partition_root);
target->partition_l_table_pos = target->curblock;
target->curblock += DIV_UP(path_table_size, BLOCK_SIZE);
target->partition_m_table_pos = target->curblock;
target->curblock += DIV_UP(path_table_size, BLOCK_SIZE);
/* >>> TWINTREE: >>> For now, checksum tags are only for the
image start and not for the partition */;
}
target->tree_end_block = target->curblock;
return ISO_SUCCESS;
}
/**
* Write a single directory record (ECMA-119, 9.1)
*
* @param file_id
* if >= 0, we use it instead of the filename (for "." and ".." entries).
* @param len_fi
* Computed length of the file identifier. Total size of the directory
* entry will be len + 33 + padding if needed (ECMA-119, 9.1.12)
* @param info
* SUSP entries for the given directory record. It will be NULL for the
* root directory record in the PVD (ECMA-119, 8.4.18) (in order to
* distinguish it from the "." entry in the root directory)
*/
static
void write_one_dir_record(Ecma119Image *t, Ecma119Node *node, int file_id,
uint8_t *buf, size_t len_fi, struct susp_info *info,
int extent)
{
uint32_t len;
uint32_t block;
uint8_t len_dr; /*< size of dir entry without SUSP fields */
int multi_extend = 0;
uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id
: (uint8_t*)node->iso_name;
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
IsoNode *iso;
len_dr = 33 + len_fi + ((len_fi % 2) ? 0 : 1);
memcpy(rec->file_id, name, len_fi);
if (need_version_number(t->opts, node->type)) {
len_dr += 2;
rec->file_id[len_fi++] = ';';
rec->file_id[len_fi++] = '1';
}
if (node->type == ECMA119_DIR) {
/* use the cached length */
len = node->info.dir->len;
block = node->info.dir->block;
} else if (node->type == ECMA119_FILE) {
block = node->info.file->sections[extent].block;
len = node->info.file->sections[extent].size;
multi_extend = (node->info.file->nsections - 1 == extent) ? 0 : 1;
} else {
/*
* for nodes other than files and dirs, we set len to 0, and
* the content block address to a dummy value.
*/
len = 0;
if (! t->opts->old_empty)
block = t->empty_file_block;
else
block = 0;
}
/*
* For ".." entry we need to write the parent info!
*/
if (file_id == 1 && node->parent)
node = node->parent;
rec->len_dr[0] = len_dr + (info != NULL ? info->suf_len : 0);
iso_bb(rec->block, block - t->eff_partition_offset, 4);
iso_bb(rec->length, len, 4);
if (t->opts->dir_rec_mtime & 1) {
iso= node->node;
iso_datetime_7(rec->recording_time,
t->replace_timestamps ? t->timestamp : iso->mtime,
t->opts->always_gmt);
} else {
iso_datetime_7(rec->recording_time, t->now, t->opts->always_gmt);
}
rec->flags[0] = ((node->type == ECMA119_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0);
iso_bb(rec->vol_seq_number, (uint32_t) 1, 2);
rec->len_fi[0] = len_fi;
/*
* and finally write the SUSP fields.
*/
if (info != NULL) {
rrip_write_susp_fields(t, info, buf + len_dr);
}
}
static
char *get_relaxed_vol_id(Ecma119Image *t, const char *name)
{
int ret;
if (name == NULL) {
return NULL;
}
if (strcmp(t->input_charset, t->output_charset)) {
/* charset conversion needed */
char *str;
ret = strconv(name, t->input_charset, t->output_charset, &str);
if (ret == ISO_SUCCESS) {
return str;
}
iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret,
"Charset conversion error. Cannot convert from %s to %s",
t->input_charset, t->output_charset);
}
return strdup(name);
}
/**
* Set the timestamps of Primary, Supplementary, or Enhanced Volume Descriptor.
*/
void ecma119_set_voldescr_times(IsoImageWriter *writer,
struct ecma119_pri_vol_desc *vol)
{
Ecma119Image *t = writer->target;
IsoWriteOpts *o;
int i;
o = t->opts;
if (o->vol_uuid[0]) {
for(i = 0; i < 16; i++)
if(o->vol_uuid[i] < '0' || o->vol_uuid[i] > '9')
break;
else
vol->vol_creation_time[i] = o->vol_uuid[i];
for(; i < 16; i++)
vol->vol_creation_time[i] = '1';
vol->vol_creation_time[16] = 0;
} else if (o->vol_creation_time > 0)
iso_datetime_17(vol->vol_creation_time, o->vol_creation_time,
o->always_gmt);
else
iso_datetime_17(vol->vol_creation_time, t->now, o->always_gmt);
if (o->vol_uuid[0]) {
for(i = 0; i < 16; i++)
if(o->vol_uuid[i] < '0' || o->vol_uuid[i] > '9')
break;
else
vol->vol_modification_time[i] = o->vol_uuid[i];
for(; i < 16; i++)
vol->vol_modification_time[i] = '1';
vol->vol_modification_time[16] = 0;
} else if (o->vol_modification_time > 0)
iso_datetime_17(vol->vol_modification_time, o->vol_modification_time,
o->always_gmt);
else
iso_datetime_17(vol->vol_modification_time, t->now, o->always_gmt);
if (o->vol_expiration_time > 0) {
iso_datetime_17(vol->vol_expiration_time, o->vol_expiration_time,
o->always_gmt);
} else {
for(i = 0; i < 16; i++)
vol->vol_expiration_time[i] = '0';
vol->vol_expiration_time[16] = 0;
}
if (o->vol_effective_time > 0) {
iso_datetime_17(vol->vol_effective_time, o->vol_effective_time,
o->always_gmt);
} else {
for(i = 0; i < 16; i++)
vol->vol_effective_time[i] = '0';
vol->vol_effective_time[16] = 0;
}
}
/**
* Write the Primary Volume Descriptor (ECMA-119, 8.4)
*/
static
int ecma119_writer_write_vol_desc(IsoImageWriter *writer)
{
IsoImage *image;
Ecma119Image *t;
struct ecma119_pri_vol_desc vol;
char *vol_id, *pub_id, *data_id, *volset_id;
char *system_id, *application_id, *copyright_file_id;
char *abstract_file_id, *biblio_file_id;
if (writer == NULL) {
return ISO_ASSERT_FAILURE;
}
t = writer->target;
image = t->image;
iso_msg_debug(image->id, "Write Primary Volume Descriptor");
memset(&vol, 0, sizeof(struct ecma119_pri_vol_desc));
if (t->opts->relaxed_vol_atts) {
vol_id = get_relaxed_vol_id(t, image->volume_id);
volset_id = get_relaxed_vol_id(t, image->volset_id);
} else {
str2d_char(t->input_charset, image->volume_id, &vol_id);
str2d_char(t->input_charset, image->volset_id, &volset_id);
}
str2a_char(t->input_charset, image->publisher_id, &pub_id);
str2a_char(t->input_charset, image->data_preparer_id, &data_id);
str2a_char(t->input_charset, image->system_id, &system_id);
str2a_char(t->input_charset, image->application_id, &application_id);
str2d_char(t->input_charset, image->copyright_file_id, &copyright_file_id);
str2d_char(t->input_charset, image->abstract_file_id, &abstract_file_id);
str2d_char(t->input_charset, image->biblio_file_id, &biblio_file_id);
vol.vol_desc_type[0] = 1;
memcpy(vol.std_identifier, "CD001", 5);
vol.vol_desc_version[0] = 1;
strncpy_pad((char*)vol.system_id, system_id, 32);
strncpy_pad((char*)vol.volume_id, vol_id, 32);
if (t->pvd_size_is_total_size) {
iso_bb(vol.vol_space_size,
t->total_size / 2048 + t->opts->ms_block - t->eff_partition_offset,
4);
} else {
iso_bb(vol.vol_space_size,
t->vol_space_size - t->eff_partition_offset, 4);
}
iso_bb(vol.vol_set_size, (uint32_t) 1, 2);
iso_bb(vol.vol_seq_number, (uint32_t) 1, 2);
iso_bb(vol.block_size, (uint32_t) BLOCK_SIZE, 2);
iso_bb(vol.path_table_size, t->path_table_size, 4);
if (t->eff_partition_offset > 0) {
/* Point to second tables and second root */
iso_lsb(vol.l_path_table_pos,
t->partition_l_table_pos - t->eff_partition_offset, 4);
iso_msb(vol.m_path_table_pos,
t->partition_m_table_pos - t->eff_partition_offset, 4);
write_one_dir_record(t, t->partition_root, 0,
vol.root_dir_record, 1, NULL, 0);
} else {
iso_lsb(vol.l_path_table_pos, t->l_path_table_pos, 4);
iso_msb(vol.m_path_table_pos, t->m_path_table_pos, 4);
write_one_dir_record(t, t->root, 0, vol.root_dir_record, 1, NULL, 0);
}
strncpy_pad((char*)vol.vol_set_id, volset_id, 128);
strncpy_pad((char*)vol.publisher_id, pub_id, 128);
strncpy_pad((char*)vol.data_prep_id, data_id, 128);
strncpy_pad((char*)vol.application_id, application_id, 128);
strncpy_pad((char*)vol.copyright_file_id, copyright_file_id, 37);
strncpy_pad((char*)vol.abstract_file_id, abstract_file_id, 37);
strncpy_pad((char*)vol.bibliographic_file_id, biblio_file_id, 37);
ecma119_set_voldescr_times(writer, &vol);
vol.file_structure_version[0] = 1;
memcpy(vol.app_use, image->application_use, 512);
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);
/* Finally write the Volume Descriptor */
return iso_write(t, &vol, sizeof(struct ecma119_pri_vol_desc));
}
static
int write_one_dir(Ecma119Image *t, Ecma119Node *dir, Ecma119Node *parent)
{
int ret;
uint8_t *buffer = NULL;
size_t i;
size_t fi_len, len;
struct susp_info info;
/* buf will point to current write position on buffer */
uint8_t *buf;
LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE);
buf = buffer;
/*
* set susp_info to 0's, this way code for both plain ECMA-119 and
* RR is very similar
*/
memset(&info, 0, sizeof(struct susp_info));
if (t->opts->rockridge) {
/* initialize the ce_block, it might be needed */
info.ce_block = dir->info.dir->block + DIV_UP(dir->info.dir->len,
BLOCK_SIZE);
info.ce_susp_fields = NULL;
}
/* write the "." and ".." entries first */
if (t->opts->rockridge) {
ret = rrip_get_susp_fields(t, dir, 1, 34, &info);
if (ret < 0) {
goto ex;
}
}
len = 34 + info.suf_len;
write_one_dir_record(t, dir, 0, buf, 1, &info, 0);
buf += len;
if (t->opts->rockridge) {
ret = rrip_get_susp_fields(t, dir, 2, 34, &info);
if (ret < 0) {
goto ex;
}
}
len = 34 + info.suf_len;
write_one_dir_record(t, parent, 1, buf, 1, &info, 0);
buf += len;
for (i = 0; i < dir->info.dir->nchildren; i++) {
int section, nsections;
Ecma119Node *child = dir->info.dir->children[i];
fi_len = strlen(child->iso_name);
nsections = (child->type == ECMA119_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
/* compute len of directory entry */
len = fi_len + 33 + ((fi_len % 2) ? 0 : 1);
if (need_version_number(t->opts, child->type)) {
len += 2;
}
/* get the SUSP fields if rockridge is enabled */
if (t->opts->rockridge) {
ret = rrip_get_susp_fields(t, child, 0, len, &info);
if (ret < 0) {
goto ex;
}
len += info.suf_len;
}
if ( (buf + len - buffer) > BLOCK_SIZE) {
/* dir doesn't fit in current block */
ret = iso_write(t, buffer, BLOCK_SIZE);
if (ret < 0) {
goto ex;