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.
4672 lines
137 KiB
4672 lines
137 KiB
/* |
|
* Copyright (c) 2007 Vreixo Formoso |
|
* Copyright (c) 2007 Mario Danic |
|
* Copyright (c) 2009 - 2019 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" |
|
#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, ©right_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 && t->eff_partition_offset <= 0) { |
|
iso_bb(vol.vol_space_size, t->total_size / 2048, 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; |
|
} |
|
memset(buffer, 0, BLOCK_SIZE); |
|
buf = buffer; |
|
} |
|
/* write the directory entry in any case */ |
|
write_one_dir_record(t, child, -1, buf, fi_len, &info, section); |
|
buf += len; |
|
} |
|
} |
|
|
|
/* write the last block */ |
|
ret = iso_write(t, buffer, BLOCK_SIZE); |
|
if (ret < 0) { |
|
goto ex; |
|
} |
|
|
|
/* write the Continuation Area if needed */ |
|
if (info.ce_len > 0) { |
|
ret = rrip_write_ce_fields(t, &info); |
|
} |
|
|
|
ex:; |
|
LIBISO_FREE_MEM(buffer); |
|
return ret; |
|
} |
|
|
|
static |
|
int write_dirs(Ecma119Image *t, Ecma119Node *root, Ecma119Node *parent) |
|
{ |
|
int ret; |
|
size_t i; |
|
|
|
/* write all directory entries for this dir */ |
|
ret = write_one_dir(t, root, parent); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
/* recurse */ |
|
for (i = 0; i < root->info.dir->nchildren; i++) { |
|
Ecma119Node *child = root->info.dir->children[i]; |
|
if (child->type == ECMA119_DIR) { |
|
ret = write_dirs(t, child, root); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
} |
|
} |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int write_path_table(Ecma119Image *t, Ecma119Node **pathlist, int l_type) |
|
{ |
|
size_t i, len; |
|
uint8_t buf[64]; /* 64 is just a convenient size larger enough */ |
|
struct ecma119_path_table_record *rec; |
|
void (*write_int)(uint8_t*, uint32_t, int); |
|
Ecma119Node *dir; |
|
uint32_t path_table_size; |
|
int parent = 0; |
|
int ret= ISO_SUCCESS; |
|
uint8_t *zeros = NULL; |
|
|
|
path_table_size = 0; |
|
write_int = l_type ? iso_lsb : iso_msb; |
|
|
|
for (i = 0; i < t->ndirs; i++) { |
|
dir = pathlist[i]; |
|
|
|
/* find the index of the parent in the table */ |
|
while ((i) && pathlist[parent] != dir->parent) { |
|
parent++; |
|
} |
|
|
|
/* write the Path Table Record (ECMA-119, 9.4) */ |
|
memset(buf, 0, 64); |
|
rec = (struct ecma119_path_table_record*) buf; |
|
rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1; |
|
rec->len_xa[0] = 0; |
|
write_int(rec->block, dir->info.dir->block - t->eff_partition_offset, |
|
4); |
|
write_int(rec->parent, parent + 1, 2); |
|
if (dir->parent) { |
|
memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]); |
|
} |
|
len = 8 + rec->len_di[0] + (rec->len_di[0] % 2); |
|
ret = iso_write(t, buf, len); |
|
if (ret < 0) { |
|
/* error */ |
|
goto ex; |
|
} |
|
path_table_size += len; |
|
} |
|
|
|
/* we need to fill the last block with zeros */ |
|
path_table_size %= BLOCK_SIZE; |
|
if (path_table_size) { |
|
len = BLOCK_SIZE - path_table_size; |
|
LIBISO_ALLOC_MEM(zeros, uint8_t, len); |
|
ret = iso_write(t, zeros, len); |
|
} |
|
ex:; |
|
LIBISO_FREE_MEM(zeros); |
|
return ret; |
|
} |
|
|
|
static |
|
int write_path_tables(Ecma119Image *t) |
|
{ |
|
int ret; |
|
size_t i, j, cur; |
|
Ecma119Node **pathlist; |
|
|
|
iso_msg_debug(t->image->id, "Writing ISO Path tables"); |
|
|
|
/* allocate temporal pathlist */ |
|
pathlist = malloc(sizeof(void*) * t->ndirs); |
|
if (pathlist == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
if (t->eff_partition_offset > 0) { |
|
pathlist[0] = t->partition_root; |
|
} else { |
|
pathlist[0] = t->root; |
|
} |
|
cur = 1; |
|
|
|
for (i = 0; i < t->ndirs; i++) { |
|
Ecma119Node *dir = pathlist[i]; |
|
for (j = 0; j < dir->info.dir->nchildren; j++) { |
|
Ecma119Node *child = dir->info.dir->children[j]; |
|
if (child->type == ECMA119_DIR) { |
|
pathlist[cur++] = child; |
|
} |
|
} |
|
} |
|
|
|
/* Write L Path Table */ |
|
ret = write_path_table(t, pathlist, 1); |
|
if (ret < 0) { |
|
goto write_path_tables_exit; |
|
} |
|
|
|
/* Write L Path Table */ |
|
ret = write_path_table(t, pathlist, 0); |
|
|
|
write_path_tables_exit: ; |
|
free(pathlist); |
|
return ret; |
|
} |
|
|
|
|
|
/** |
|
* Write the directory structure (ECMA-119, 6.8) and the L and M |
|
* Path Tables (ECMA-119, 6.9). |
|
*/ |
|
static |
|
int ecma119_writer_write_dirs(IsoImageWriter *writer) |
|
{ |
|
int ret, isofs_ca_changed = 0; |
|
Ecma119Image *t; |
|
Ecma119Node *root; |
|
char *value = NULL; |
|
size_t value_length; |
|
|
|
t = writer->target; |
|
|
|
/* first of all, we write the directory structure */ |
|
if (t->eff_partition_offset > 0) { |
|
root = t->partition_root; |
|
|
|
if ((t->opts->md5_file_checksums & 1) || |
|
t->opts->md5_session_checksum) { |
|
/* Take into respect the address offset in "isofs.ca" */ |
|
ret = iso_node_lookup_attr((IsoNode *) t->image->root, "isofs.ca", |
|
&value_length, &value, 0); |
|
if (value != NULL) |
|
free(value); |
|
if (ret == 1 && value_length == 20) { |
|
/* "isofs.ca" does really exist and has the expected length */ |
|
ret = iso_root_set_isofsca((IsoNode *) t->image->root, |
|
t->checksum_range_start - t->eff_partition_offset, |
|
t->checksum_array_pos - t->eff_partition_offset, |
|
t->checksum_idx_counter + 2, 16, "MD5", 0); |
|
if (ret < 0) |
|
return ret; |
|
isofs_ca_changed = 1; |
|
} |
|
} |
|
} else { |
|
root = t->root; |
|
} |
|
ret = write_dirs(t, root, root); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
/* and write the path tables */ |
|
ret = write_path_tables(t); |
|
if (ret < 0) |
|
return ret; |
|
if (t->opts->md5_session_checksum) { |
|
/* Write tree checksum tag */ |
|
if (t->eff_partition_offset > 0) { |
|
/* >>> TWINTREE: >>> For now, tags are only for the |
|
image start and not for the partition */; |
|
} else { |
|
ret = iso_md5_write_tag(t, 3); |
|
} |
|
} |
|
if (isofs_ca_changed) { |
|
/* Restore old addresses offset in "isofs.ca" of root node */ |
|
ret = iso_root_set_isofsca((IsoNode *) t->image->root, |
|
t->checksum_range_start, |
|
t->checksum_array_pos, |
|
t->checksum_idx_counter + 2, 16, "MD5", 0); |
|
if (ret < 0) |
|
return ret; |
|
} |
|
return ret; |
|
} |
|
|
|
/** |
|
* Write directory structure and Path Tables of the ECMA-119 tree. |
|
* This happens eventually a second time for the duplicates which use |
|
* addresses with partition offset. |
|
*/ |
|
static |
|
int ecma119_writer_write_data(IsoImageWriter *writer) |
|
{ |
|
int ret; |
|
Ecma119Image *t; |
|
uint32_t curblock; |
|
char *msg = NULL; |
|
|
|
if (writer == NULL) |
|
{ret = ISO_ASSERT_FAILURE; goto ex;} |
|
|
|
t = writer->target; |
|
|
|
ret = ecma119_writer_write_dirs(writer); |
|
if (ret < 0) |
|
goto ex; |
|
|
|
if (t->opts->partition_offset > 0) { |
|
t->eff_partition_offset = t->opts->partition_offset; |
|
ret = ecma119_writer_write_dirs(writer); |
|
t->eff_partition_offset = 0; |
|
if (ret < 0) |
|
goto ex; |
|
} |
|
|
|
curblock = (t->bytes_written / 2048) + t->opts->ms_block; |
|
if (curblock != t->tree_end_block) { |
|
LIBISO_ALLOC_MEM(msg, char, 100); |
|
sprintf(msg, |
|
"Calculated and written ECMA-119 tree end differ: %lu <> %lu", |
|
(unsigned long) t->tree_end_block, |
|
(unsigned long) curblock); |
|
iso_msgs_submit(0, msg, 0, "WARNING", 0); |
|
|
|
t->tree_end_block = 1;/* Mark for harsher reaction at end of writing */ |
|
} |
|
ret = ISO_SUCCESS; |
|
ex:; |
|
LIBISO_FREE_MEM(msg); |
|
return ret; |
|
} |
|
|
|
static |
|
int ecma119_writer_free_data(IsoImageWriter *writer) |
|
{ |
|
/* nothing to do */ |
|
return ISO_SUCCESS; |
|
} |
|
|
|
int ecma119_writer_create(Ecma119Image *target) |
|
{ |
|
int ret; |
|
IsoImageWriter *writer; |
|
|
|
writer = malloc(sizeof(IsoImageWriter)); |
|
if (writer == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
writer->compute_data_blocks = ecma119_writer_compute_data_blocks; |
|
writer->write_vol_desc = ecma119_writer_write_vol_desc; |
|
writer->write_data = ecma119_writer_write_data; |
|
writer->free_data = ecma119_writer_free_data; |
|
writer->data = NULL; |
|
writer->target = target; |
|
|
|
/* add this writer to image */ |
|
target->writers[target->nwriters++] = writer; |
|
|
|
iso_msg_debug(target->image->id, "Creating low level ECMA-119 tree..."); |
|
ret = ecma119_tree_create(target); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
if (target->image->sparc_core_node != NULL) { |
|
/* Obtain a duplicate of the IsoFile's Ecma119Node->file */ |
|
ret = iso_file_src_create(target, target->image->sparc_core_node, |
|
&target->sparc_core_src); |
|
if (ret < 0) |
|
return ret; |
|
} |
|
|
|
if(target->opts->partition_offset > 0) { |
|
/* Create second tree */ |
|
target->eff_partition_offset = target->opts->partition_offset; |
|
ret = ecma119_tree_create(target); |
|
target->eff_partition_offset = 0; |
|
if (ret < 0) |
|
return ret; |
|
} |
|
|
|
/* we need the volume descriptor */ |
|
target->curblock++; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** compute how many padding bytes are needed */ |
|
static |
|
int mspad_writer_compute_data_blocks(IsoImageWriter *writer) |
|
{ |
|
Ecma119Image *target; |
|
uint32_t min_size; |
|
|
|
if (writer == NULL) { |
|
return ISO_ASSERT_FAILURE; |
|
} |
|
target = writer->target; |
|
min_size = 32 + target->opts->partition_offset; |
|
if (target->curblock < min_size) { |
|
target->mspad_blocks = min_size - target->curblock; |
|
target->curblock = min_size; |
|
} |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int mspad_writer_write_vol_desc(IsoImageWriter *writer) |
|
{ |
|
/* nothing to do */ |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int mspad_writer_write_data(IsoImageWriter *writer) |
|
{ |
|
int ret; |
|
Ecma119Image *t; |
|
uint8_t *pad = NULL; |
|
size_t i; |
|
|
|
if (writer == NULL) { |
|
{ret = ISO_ASSERT_FAILURE; goto ex;} |
|
} |
|
t = writer->target; |
|
|
|
if (t->mspad_blocks == 0) { |
|
{ret = ISO_SUCCESS; goto ex;} |
|
} |
|
|
|
LIBISO_ALLOC_MEM(pad, uint8_t, BLOCK_SIZE); |
|
for (i = 0; i < t->mspad_blocks; ++i) { |
|
ret = iso_write(t, pad, BLOCK_SIZE); |
|
if (ret < 0) { |
|
goto ex; |
|
} |
|
} |
|
|
|
ret = ISO_SUCCESS; |
|
ex:; |
|
LIBISO_FREE_MEM(pad); |
|
return ret; |
|
} |
|
|
|
static |
|
int mspad_writer_free_data(IsoImageWriter *writer) |
|
{ |
|
/* nothing to do */ |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int mspad_writer_create(Ecma119Image *target) |
|
{ |
|
IsoImageWriter *writer; |
|
|
|
writer = malloc(sizeof(IsoImageWriter)); |
|
if (writer == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
writer->compute_data_blocks = mspad_writer_compute_data_blocks; |
|
writer->write_vol_desc = mspad_writer_write_vol_desc; |
|
writer->write_data = mspad_writer_write_data; |
|
writer->free_data = mspad_writer_free_data; |
|
writer->data = NULL; |
|
writer->target = target; |
|
|
|
/* add this writer to image */ |
|
target->writers[target->nwriters++] = writer; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
|
|
/** ----- Zero padding writer ----- */ |
|
|
|
struct iso_zero_writer_data_struct { |
|
uint32_t num_blocks; |
|
}; |
|
|
|
static |
|
int zero_writer_compute_data_blocks(IsoImageWriter *writer) |
|
{ |
|
Ecma119Image *target; |
|
struct iso_zero_writer_data_struct *data; |
|
|
|
if (writer == NULL) |
|
return ISO_ASSERT_FAILURE; |
|
target = writer->target; |
|
data = (struct iso_zero_writer_data_struct *) writer->data; |
|
target->curblock += data->num_blocks; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int zero_writer_write_vol_desc(IsoImageWriter *writer) |
|
{ |
|
/* nothing to do */ |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int zero_writer_write_data(IsoImageWriter *writer) |
|
{ |
|
int ret; |
|
Ecma119Image *t; |
|
struct iso_zero_writer_data_struct *data; |
|
uint8_t *pad = NULL; |
|
size_t i; |
|
|
|
if (writer == NULL) |
|
{ret = ISO_ASSERT_FAILURE; goto ex;} |
|
t = writer->target; |
|
data = (struct iso_zero_writer_data_struct *) writer->data; |
|
|
|
if (data->num_blocks == 0) |
|
{ret = ISO_SUCCESS; goto ex;} |
|
LIBISO_ALLOC_MEM(pad, uint8_t, BLOCK_SIZE); |
|
for (i = 0; i < data->num_blocks; ++i) { |
|
ret = iso_write(t, pad, BLOCK_SIZE); |
|
if (ret < 0) |
|
goto ex; |
|
} |
|
ret = ISO_SUCCESS; |
|
ex:; |
|
LIBISO_FREE_MEM(pad); |
|
return ret; |
|
} |
|
|
|
static |
|
int zero_writer_free_data(IsoImageWriter *writer) |
|
{ |
|
if (writer == NULL) |
|
return ISO_SUCCESS; |
|
if (writer->data == NULL) |
|
return ISO_SUCCESS; |
|
free(writer->data); |
|
writer->data = NULL; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int tail_writer_compute_data_blocks(IsoImageWriter *writer) |
|
{ |
|
int ret; |
|
Ecma119Image *target; |
|
|
|
#ifdef Libisofs_part_align_writeR |
|
|
|
target = writer->target; |
|
|
|
#else |
|
|
|
struct iso_zero_writer_data_struct *data; |
|
char msg[80]; |
|
|
|
target = writer->target; |
|
ret = iso_align_isohybrid(target, 0); |
|
if (ret < 0) |
|
return ret; |
|
data = (struct iso_zero_writer_data_struct *) writer->data; |
|
if (data->num_blocks != target->opts->tail_blocks) { |
|
sprintf(msg, "Aligned image size to cylinder size by %d blocks", |
|
target->opts->tail_blocks - data->num_blocks); |
|
iso_msgs_submit(0, msg, 0, "NOTE", 0); |
|
data->num_blocks = target->opts->tail_blocks; |
|
} |
|
|
|
#endif /* ! Libisofs_part_align_writeR */ |
|
|
|
if (target->opts->tail_blocks <= 0) |
|
return ISO_SUCCESS; |
|
ret = zero_writer_compute_data_blocks(writer); |
|
return ret; |
|
} |
|
|
|
static |
|
int part_align_writer_compute_data_blocks(IsoImageWriter *writer) |
|
{ |
|
int ret; |
|
Ecma119Image *target; |
|
struct iso_zero_writer_data_struct *data; |
|
char msg[80]; |
|
|
|
target = writer->target; |
|
|
|
/* Default setting in case no alignment is needed */ |
|
target->alignment_end_block = target->curblock; |
|
|
|
ret = iso_align_isohybrid(target, 0); |
|
if (ret < 0) |
|
return ret; |
|
data = (struct iso_zero_writer_data_struct *) writer->data; |
|
if (target->part_align_blocks != 0) { |
|
sprintf(msg, "Aligned image size to cylinder size by %d blocks", |
|
target->part_align_blocks); |
|
iso_msgs_submit(0, msg, 0, "NOTE", 0); |
|
data->num_blocks = target->part_align_blocks; |
|
} |
|
if (target->part_align_blocks <= 0) |
|
return ISO_SUCCESS; |
|
ret = zero_writer_compute_data_blocks(writer); |
|
target->alignment_end_block = target->curblock; |
|
return ret; |
|
} |
|
|
|
/* |
|
@param flag bit0-3= compute_data_blocks mode: |
|
0= zero_writer_compute_data_blocks |
|
1= tail_writer_compute_data_blocks |
|
2= part_align_writer_compute_data_blocks |
|
*/ |
|
static |
|
int zero_writer_create(Ecma119Image *target, uint32_t num_blocks, int flag) |
|
{ |
|
IsoImageWriter *writer; |
|
struct iso_zero_writer_data_struct *data; |
|
int mode; |
|
|
|
writer = malloc(sizeof(IsoImageWriter)); |
|
if (writer == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
data = calloc(1, sizeof(struct iso_zero_writer_data_struct)); |
|
if (data == NULL) { |
|
free(writer); |
|
return ISO_OUT_OF_MEM; |
|
} |
|
data->num_blocks = num_blocks; |
|
|
|
mode = (flag & 15); |
|
if (mode == 1) { |
|
writer->compute_data_blocks = tail_writer_compute_data_blocks; |
|
} else if (mode == 2) { |
|
writer->compute_data_blocks = part_align_writer_compute_data_blocks; |
|
} else { |
|
writer->compute_data_blocks = zero_writer_compute_data_blocks; |
|
} |
|
writer->write_vol_desc = zero_writer_write_vol_desc; |
|
writer->write_data = zero_writer_write_data; |
|
writer->free_data = zero_writer_free_data; |
|
writer->data = data; |
|
writer->target = target; |
|
|
|
/* add this writer to image */ |
|
target->writers[target->nwriters++] = writer; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
|
|
/* @param flag bit0= restore preserved cx (else dispose them) |
|
*/ |
|
static |
|
int process_preserved_cx(IsoDir *dir, int flag) |
|
{ |
|
int ret, i; |
|
unsigned int cx_value; |
|
void *xipt; |
|
IsoNode *pos; |
|
|
|
pos = dir->children; |
|
for (pos = dir->children; pos != NULL; pos = pos->next) { |
|
if (pos->type == LIBISO_FILE) { |
|
if (flag & 1) { |
|
/* Restore preserved cx state of nodes */ |
|
ret = iso_node_get_xinfo(pos, checksum_cx_xinfo_func, |
|
&xipt); |
|
if (ret == 1) { |
|
/* xipt is an int disguised as void pointer */ |
|
cx_value = 0; |
|
for (i = 0; i < 4; i++) |
|
cx_value = |
|
(cx_value << 8) | ((unsigned char *) &xipt)[i]; |
|
ret = iso_file_set_isofscx((IsoFile *) pos, cx_value, 0); |
|
if (ret < 0) |
|
return ret; |
|
} else if (ret == 0) { |
|
/* Node had no cx before the write run. Delete cx. */ |
|
iso_file_set_isofscx((IsoFile *) pos, 0, 1); |
|
} |
|
} |
|
iso_node_remove_xinfo(pos, checksum_cx_xinfo_func); |
|
} else if (pos->type == LIBISO_DIR) { |
|
ret = process_preserved_cx((IsoDir *) pos, flag); |
|
if (ret != 0) |
|
return ret; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
static |
|
int transplant_checksum_buffer(Ecma119Image *target, int flag) |
|
{ |
|
/* Transplant checksum buffer from Ecma119Image to IsoImage */ |
|
iso_image_set_checksums(target->image, target->checksum_buffer, |
|
target->checksum_range_start, |
|
target->checksum_array_pos, |
|
target->checksum_idx_counter + 2, 0); |
|
target->checksum_buffer = NULL; |
|
target->checksum_idx_counter = 0; |
|
|
|
/* Delete recorded cx xinfo */ |
|
process_preserved_cx(target->image->root, 0); |
|
|
|
return 1; |
|
} |
|
|
|
static |
|
int write_vol_desc_terminator(Ecma119Image *target) |
|
{ |
|
int ret; |
|
uint8_t *buf = NULL; |
|
struct ecma119_vol_desc_terminator *vol; |
|
|
|
LIBISO_ALLOC_MEM(buf, uint8_t, BLOCK_SIZE); |
|
|
|
vol = (struct ecma119_vol_desc_terminator *) buf; |
|
|
|
vol->vol_desc_type[0] = 255; |
|
memcpy(vol->std_identifier, "CD001", 5); |
|
vol->vol_desc_version[0] = 1; |
|
|
|
ret = iso_write(target, buf, BLOCK_SIZE); |
|
ex: |
|
LIBISO_FREE_MEM(buf); |
|
return ret; |
|
} |
|
|
|
|
|
/* @param flag bit0= initialize system area by target->opts_overwrite |
|
bit1= fifo is not yet draining. Inquire write_count from fifo. |
|
bit2= target->opts->ms_block is not counted in |
|
target->total_size |
|
*/ |
|
static |
|
int write_head_part1(Ecma119Image *target, int *write_count, int flag) |
|
{ |
|
int res, i, ret; |
|
uint8_t *sa, *sa_local = NULL; |
|
IsoImageWriter *writer; |
|
size_t buffer_size = 0, buffer_free = 0, buffer_start_free = 0; |
|
|
|
if (target->sys_area_already_written) { |
|
LIBISO_ALLOC_MEM(sa_local, uint8_t, 16 * BLOCK_SIZE); |
|
sa = sa_local; |
|
} else { |
|
sa = target->sys_area_as_written; |
|
target->sys_area_already_written = 1; |
|
} |
|
iso_ring_buffer_get_buf_status(target->buffer, &buffer_size, |
|
&buffer_start_free); |
|
*write_count = 0; |
|
/* Write System Area (ECMA-119, 6.2.1) */ |
|
if ((flag & 1) && target->opts_overwrite != NULL) |
|
memcpy(sa, target->opts_overwrite, 16 * BLOCK_SIZE); |
|
res = iso_write_system_area(target, sa, (flag & 4) >> 2); |
|
if (res < 0) |
|
goto write_error; |
|
res = iso_write(target, sa, 16 * BLOCK_SIZE); |
|
if (res < 0) |
|
goto write_error; |
|
*write_count = 16; |
|
|
|
/* write volume descriptors, one per writer */ |
|
iso_msg_debug(target->image->id, "Write volume descriptors"); |
|
for (i = 0; i < (int) target->nwriters; ++i) { |
|
writer = target->writers[i]; |
|
res = writer->write_vol_desc(writer); |
|
if (res < 0) |
|
goto write_error; |
|
} |
|
|
|
/* write Volume Descriptor Set Terminator (ECMA-119, 8.3) */ |
|
res = write_vol_desc_terminator(target); |
|
if (res < 0) |
|
goto write_error; |
|
|
|
if(flag & 2) { |
|
iso_ring_buffer_get_buf_status(target->buffer, &buffer_size, |
|
&buffer_free); |
|
*write_count = ( buffer_start_free - buffer_free ) / BLOCK_SIZE; |
|
} else { |
|
*write_count = target->bytes_written / BLOCK_SIZE; |
|
} |
|
|
|
ret = ISO_SUCCESS; |
|
goto ex; |
|
|
|
write_error:; |
|
ret = res; |
|
goto ex; |
|
|
|
ex: |
|
LIBISO_FREE_MEM(sa_local); |
|
return ret; |
|
} |
|
|
|
static |
|
int write_head_part2(Ecma119Image *target, int *write_count, int flag) |
|
{ |
|
int ret, i; |
|
uint8_t *buf = NULL; |
|
IsoImageWriter *writer; |
|
|
|
if (target->opts->partition_offset <= 0) |
|
{ret = ISO_SUCCESS; goto ex;} |
|
|
|
/* Write multi-session padding up to target->opts->partition_offset + 16 */ |
|
LIBISO_ALLOC_MEM(buf, uint8_t, BLOCK_SIZE); |
|
for(; *write_count < (int) target->opts->partition_offset + 16; |
|
(*write_count)++) { |
|
ret = iso_write(target, buf, BLOCK_SIZE); |
|
if (ret < 0) |
|
goto ex; |
|
} |
|
|
|
/* Write volume descriptors subtracting |
|
target->partiton_offset from any LBA pointer. |
|
*/ |
|
target->eff_partition_offset = target->opts->partition_offset; |
|
target->pvd_size_is_total_size = 0; |
|
for (i = 0; i < (int) target->nwriters; ++i) { |
|
writer = target->writers[i]; |
|
/* Not all writers have an entry in the partition volume descriptor set. |
|
It must be guaranteed that they write exactly one block. |
|
*/ |
|
|
|
/* >>> TWINTREE: Enhance ISO1999 writer and add it here */ |
|
|
|
if(writer->write_vol_desc != ecma119_writer_write_vol_desc && |
|
writer->write_vol_desc != joliet_writer_write_vol_desc) |
|
continue; |
|
ret = writer->write_vol_desc(writer); |
|
if (ret < 0) |
|
goto ex; |
|
(*write_count)++; |
|
} |
|
ret = write_vol_desc_terminator(target); |
|
if (ret < 0) |
|
goto ex; |
|
(*write_count)++; |
|
target->eff_partition_offset = 0; |
|
|
|
/* >>> TWINTREE: Postponed for now: |
|
Write second superblock checksum tag */; |
|
|
|
ret = ISO_SUCCESS; |
|
ex:; |
|
if (buf != NULL) |
|
free(buf); |
|
return ret; |
|
} |
|
|
|
static |
|
int write_head_part(Ecma119Image *target, int flag) |
|
{ |
|
int res, write_count = 0; |
|
|
|
/* System area and volume descriptors */ |
|
res = write_head_part1(target, &write_count, 4); |
|
if (res < 0) |
|
return res; |
|
|
|
/* Write superblock checksum tag */ |
|
if (target->opts->md5_session_checksum && target->checksum_ctx != NULL) { |
|
res = iso_md5_write_tag(target, 2); |
|
if (res < 0) |
|
return res; |
|
write_count++; |
|
} |
|
|
|
/* Second set of system area and volume descriptors for partition_offset */ |
|
res = write_head_part2(target, &write_count, 0); |
|
if (res < 0) |
|
return res; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
|
|
/* Eventually end Jigdo Template Extraction */ |
|
static int finish_libjte(Ecma119Image *target) |
|
{ |
|
#ifdef Libisofs_with_libjtE |
|
|
|
int ret; |
|
|
|
if (target->opts->libjte_handle != NULL) { |
|
ret = libjte_write_footer(target->opts->libjte_handle); |
|
if (ret <= 0) { |
|
iso_libjte_forward_msgs(target->opts->libjte_handle, |
|
target->image->id, ISO_LIBJTE_END_FAILED, 0); |
|
return ISO_LIBJTE_END_FAILED; |
|
} |
|
} |
|
|
|
#endif /* Libisofs_with_libjtE */ |
|
|
|
return 1; |
|
} |
|
|
|
|
|
|
|
struct iso_interval_zeroizer { |
|
int z_type; /* 0= $zero_start"-"$zero_end , |
|
1= "zero_mbrpt" , 2= "zero_gpt" , 3= "zero_apm" |
|
*/ |
|
off_t zero_start; |
|
off_t zero_end; |
|
}; |
|
|
|
struct iso_interval_reader { |
|
|
|
/* Setup */ |
|
|
|
IsoImage *image; |
|
|
|
char *path; |
|
|
|
int flags; /* bit0= imported_iso, else local_fs |
|
*/ |
|
|
|
off_t start_byte; |
|
off_t end_byte; |
|
|
|
struct iso_interval_zeroizer *zeroizers; |
|
int num_zeroizers; |
|
|
|
char *source_pt; /* This is a parasite pointer of path. Do not free */ |
|
|
|
/* State information */ |
|
|
|
int initialized; |
|
int is_block_aligned; |
|
off_t cur_block; |
|
int fd; |
|
uint8_t read_buf[BLOCK_SIZE]; |
|
uint8_t *pending_read_pt; |
|
int pending_read_bytes; |
|
off_t read_count; |
|
int eof; |
|
|
|
int src_is_open; |
|
|
|
uint32_t apm_block_size; |
|
|
|
}; |
|
|
|
static |
|
int iso_ivr_next_comp(char *read_pt, char **next_pt, int flag) |
|
{ |
|
*next_pt = NULL; |
|
if (read_pt == NULL) |
|
return 0; |
|
*next_pt = strchr(read_pt, ':'); |
|
if (*next_pt != NULL) |
|
(*next_pt)++; |
|
return 1; |
|
} |
|
|
|
/* @param flag bit1= end number requested, forward to iso_scanf_io_size() |
|
*/ |
|
static |
|
int iso_ivr_read_number(char *start_pt, char *end_pt, off_t *result, int flag) |
|
{ |
|
char txt[20]; |
|
off_t num; |
|
|
|
if (end_pt - start_pt <= 0 || end_pt - start_pt > 16) { |
|
iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, |
|
"Number text too short or too long in interval reader description string"); |
|
return ISO_MALFORMED_READ_INTVL; |
|
} |
|
if (end_pt - start_pt > 0) |
|
strncpy(txt, start_pt, end_pt - start_pt); |
|
txt[end_pt - start_pt] = 0; |
|
|
|
num = iso_scanf_io_size(start_pt, 1 | (flag & 2)); |
|
if (num < 0.0 || num > 281474976710655.0) { |
|
iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, |
|
"Negative or overly large number in interval reader description string"); |
|
return ISO_MALFORMED_READ_INTVL; |
|
} |
|
*result = num; |
|
return 1; |
|
} |
|
|
|
static |
|
int iso_ivr_parse_interval(char *start_pt, char *end_pt, off_t *start_byte, |
|
off_t *end_byte, int flag) |
|
{ |
|
int ret; |
|
char *m_pt; |
|
|
|
m_pt = strchr(start_pt, '-'); |
|
if (m_pt == NULL) { |
|
iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, |
|
"Malformed byte interval in interval reader description string"); |
|
return ISO_MALFORMED_READ_INTVL; |
|
} |
|
ret = iso_ivr_read_number(start_pt, m_pt, start_byte, 0); |
|
if (ret < 0) |
|
return ret; |
|
ret = iso_ivr_read_number(m_pt + 1, end_pt - 1, end_byte, 2); |
|
if (ret < 0) |
|
return ret; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
int iso_ivr_parse_zeroizers(struct iso_interval_reader *ivr, |
|
char *pathpt, char *end_pt, int flag) |
|
{ |
|
int ret, num_zs = 1, idx, i; |
|
char *rpt, *cpt; |
|
|
|
ivr->num_zeroizers = 0; |
|
if (pathpt[0] == 0 || pathpt == end_pt) |
|
return ISO_SUCCESS; |
|
for(cpt = pathpt - 1; cpt != NULL && cpt < end_pt; num_zs++) |
|
cpt = strchr(cpt + 1, ','); |
|
LIBISO_ALLOC_MEM(ivr->zeroizers, struct iso_interval_zeroizer, num_zs); |
|
for (i = 0; i < num_zs; i++) |
|
ivr->zeroizers[i].zero_end = -1; |
|
idx = 0; |
|
for (rpt = pathpt; rpt != NULL && rpt < end_pt; idx++) { |
|
cpt = strchr(rpt, ','); |
|
if (cpt == NULL || cpt > end_pt) |
|
cpt = end_pt; |
|
|
|
if (cpt == rpt) { |
|
continue; |
|
} else if (strncmp(rpt, "zero_mbrpt", cpt - rpt) == 0) { |
|
ivr->zeroizers[idx].z_type = 1; |
|
} else if (strncmp(rpt, "zero_gpt", cpt - rpt) == 0) { |
|
ivr->zeroizers[idx].z_type = 2; |
|
} else if (strncmp(rpt, "zero_apm", cpt - rpt) == 0) { |
|
ivr->zeroizers[idx].z_type = 3; |
|
} else { |
|
ivr->zeroizers[idx].z_type = 0; |
|
ret = iso_ivr_parse_interval(rpt, cpt, |
|
&(ivr->zeroizers[idx].zero_start), |
|
&(ivr->zeroizers[idx].zero_end), 0); |
|
if (ret < 0) |
|
goto ex; |
|
} |
|
rpt = cpt + 1; |
|
ivr->num_zeroizers++; |
|
} |
|
ret = ISO_SUCCESS; |
|
ex:; |
|
return ret; |
|
} |
|
|
|
static |
|
int iso_ivr_parse(struct iso_interval_reader *ivr, char *path, int flag) |
|
{ |
|
int ret; |
|
char *flags_pt, *interval_pt, *zeroize_pt; |
|
|
|
flags_pt = path; |
|
iso_ivr_next_comp(flags_pt, &interval_pt, 0); |
|
iso_ivr_next_comp(interval_pt, &zeroize_pt, 0); |
|
iso_ivr_next_comp(zeroize_pt, &(ivr->source_pt), 0); |
|
if (ivr->source_pt == NULL) { |
|
iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, |
|
"Not enough components in interval reader description string"); |
|
return ISO_MALFORMED_READ_INTVL; |
|
} |
|
|
|
ivr->flags = 0; |
|
if (strncmp(flags_pt, "imported_iso", 12) == 0) { |
|
ivr->flags |= 1; |
|
} else if (strncmp(flags_pt, "local_fs", 8) == 0) { |
|
; |
|
} else { |
|
iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, |
|
"Unknown flag name in first component of interval reader description string"); |
|
return ISO_MALFORMED_READ_INTVL; |
|
} |
|
ret = iso_ivr_parse_interval(interval_pt, zeroize_pt, &(ivr->start_byte), |
|
&(ivr->end_byte), 0); |
|
if (ret < 0) |
|
goto ex; |
|
ret = iso_ivr_parse_zeroizers(ivr, zeroize_pt, ivr->source_pt - 1, 0); |
|
if (ret < 0) |
|
goto ex; |
|
|
|
ret = ISO_SUCCESS; |
|
ex:; |
|
return ret; |
|
} |
|
|
|
int iso_interval_reader_destroy(struct iso_interval_reader **ivr, int flag) |
|
{ |
|
struct iso_interval_reader *o; |
|
|
|
if (*ivr == NULL) |
|
return 0; |
|
o = *ivr; |
|
|
|
LIBISO_FREE_MEM(o->path); |
|
LIBISO_FREE_MEM(o->zeroizers); |
|
|
|
if (o->fd != -1) |
|
close(o->fd); |
|
if (o->src_is_open) |
|
(*o->image->import_src->close)(o->image->import_src); |
|
|
|
LIBISO_FREE_MEM(*ivr); |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/* @param flag bit0= tolerate lack of import_src |
|
*/ |
|
int iso_interval_reader_new(IsoImage *img, char *path, |
|
struct iso_interval_reader **ivr, |
|
off_t *byte_count, int flag) |
|
{ |
|
int ret, no_img = 0; |
|
struct iso_interval_reader *o = NULL; |
|
|
|
*ivr = NULL; |
|
*byte_count = 0; |
|
LIBISO_ALLOC_MEM(o, struct iso_interval_reader, 1); |
|
o->image = img; |
|
o->path = NULL; |
|
o->zeroizers = NULL; |
|
o->num_zeroizers = 0; |
|
o->source_pt = NULL; |
|
o->initialized = 0; |
|
o->is_block_aligned = 0; |
|
o->fd = -1; |
|
o->pending_read_pt = NULL; |
|
o->pending_read_bytes = 0; |
|
o->eof = 0; |
|
o->read_count = 0; |
|
o->src_is_open = 0; |
|
|
|
o->apm_block_size = 0; |
|
|
|
LIBISO_ALLOC_MEM(o->path, char, strlen(path) + 1); |
|
strcpy(o->path, path); |
|
|
|
ret = iso_ivr_parse(o, path, 0); |
|
if (ret < 0) |
|
goto ex; |
|
|
|
if (o->image == NULL) |
|
no_img = 1; |
|
else if (o->image->import_src == NULL) |
|
no_img = 1; |
|
if ((o->flags & 1) && no_img) { |
|
iso_msg_submit(-1, ISO_NO_KEPT_DATA_SRC, 0, |
|
"Interval reader lacks of data source object of imported ISO"); |
|
if (!(flag & 1)) { |
|
ret = ISO_BAD_PARTITION_FILE; |
|
goto ex; |
|
} |
|
o->eof = 1; |
|
} |
|
*byte_count = o->end_byte - o->start_byte + 1; |
|
*ivr = o; |
|
ret = ISO_SUCCESS; |
|
ex:; |
|
if (ret < 0) |
|
iso_interval_reader_destroy(&o, 0); |
|
return ret; |
|
} |
|
|
|
static |
|
int iso_ivr_zeroize(struct iso_interval_reader *ivr, uint8_t *buf, |
|
int buf_fill, int flag) |
|
{ |
|
int i; |
|
off_t low, high, part_start, entry_count, apm_offset = -1, map_entries; |
|
uint8_t *apm_buf; |
|
struct iso_interval_zeroizer *zr; |
|
|
|
for (i = 0; i < ivr->num_zeroizers; i++) { |
|
zr = ivr->zeroizers + i; |
|
if (zr->z_type == 1) { /* zero_mbrpt */ |
|
if (ivr->read_count > 0 || buf_fill < 512) |
|
continue; |
|
if (buf[510] != 0x55 || buf[511] != 0xaa) |
|
continue; |
|
memset(buf + 446, 0, 64); |
|
|
|
} else if (zr->z_type == 2) { /* zero_gpt */ |
|
if (zr->zero_start <= zr->zero_end) |
|
goto process_interval; |
|
|
|
if (ivr->read_count > 0 || buf_fill < 512 + 92) |
|
continue; |
|
if (strncmp((char *) buf + 512, "EFI PART", 8) != 0 || |
|
buf[520] != 0 || buf[521] != 0 || buf[522] != 1 || |
|
buf[523] != 0) |
|
continue; |
|
/* head_size , curr_lba , entry_size */ |
|
if (iso_read_lsb(buf + 524, 4) != 92 || |
|
iso_read_lsb(buf + 536, 4) != 1 || |
|
iso_read_lsb(buf + 596, 4) != 128) |
|
continue; |
|
part_start = iso_read_lsb(buf + 584, 4); |
|
entry_count = iso_read_lsb(buf + 592, 4); |
|
if (part_start < 2 || part_start + (entry_count + 3) / 4 > 64) |
|
continue; |
|
zr->zero_start = part_start * 512; |
|
zr->zero_end = (part_start + (entry_count + 3) / 4) * 512 - 1; |
|
memset(buf + 512, 0, 92); |
|
|
|
} else if (zr->z_type == 3) { /* zero_apm */ |
|
if (zr->zero_start <= zr->zero_end) |
|
goto process_interval; |
|
|
|
if (ivr->read_count == 0) { |
|
if (buf_fill < 512) |
|
continue; |
|
if (buf[0] != 'E' || buf[1] != 'R') |
|
continue; |
|
ivr->apm_block_size = iso_read_msb(buf + 2, 2); |
|
if ((ivr->apm_block_size != 512 && |
|
ivr->apm_block_size != 1024 && |
|
ivr->apm_block_size != 2048) || |
|
((uint32_t) buf_fill) < ivr->apm_block_size) { |
|
ivr->apm_block_size = 0; |
|
continue; |
|
} |
|
if (ivr->read_count + buf_fill >= 2 * ivr->apm_block_size) |
|
apm_offset = ivr->apm_block_size; |
|
} else if (ivr->read_count == 2048 && |
|
ivr->apm_block_size == 2048 && buf_fill == 2048) { |
|
apm_offset = 0; |
|
} |
|
if (apm_offset < 0) |
|
continue; |
|
|
|
/* Check for first APM entry */ |
|
apm_buf = buf + apm_offset; |
|
if(apm_buf[0] != 'P' || apm_buf[1] != 'M') |
|
continue; |
|
if (iso_read_msb(apm_buf + 8, 4) != 1) |
|
continue; |
|
map_entries = iso_read_msb(apm_buf + 4, 4); |
|
if ((1 + map_entries) * ivr->apm_block_size > 16 * 2048) |
|
continue; |
|
zr->zero_start = ivr->apm_block_size; |
|
zr->zero_end = (1 + map_entries) * ivr->apm_block_size; |
|
} |
|
process_interval:; |
|
/* If an interval is defined by now: zeroize its intersection with buf |
|
*/ |
|
if (zr->zero_start <= zr->zero_end) { |
|
low = ivr->read_count >= zr->zero_start ? |
|
ivr->read_count : zr->zero_start; |
|
high = ivr->read_count + buf_fill - 1 <= zr->zero_end ? |
|
ivr->read_count + buf_fill - 1 : zr->zero_end; |
|
if (low <= high) |
|
memset(buf + low - ivr->read_count, 0, high - low + 1); |
|
} |
|
} |
|
|
|
return ISO_SUCCESS; |
|
} |
|
|
|
int iso_interval_reader_read(struct iso_interval_reader *ivr, uint8_t *buf, |
|
int *buf_fill, int flag) |
|
{ |
|
int ret, read_done, to_copy, initializing = 0; |
|
IsoDataSource *src; |
|
uint8_t *read_buf; |
|
off_t to_read; |
|
|
|
*buf_fill = 0; |
|
src = ivr->image->import_src; |
|
|
|
if (ivr->eof) { |
|
eof:; |
|
memset(buf, 0, BLOCK_SIZE); |
|
return 0; |
|
} |
|
|
|
if (ivr->initialized) { |
|
ivr->cur_block++; |
|
} else { |
|
initializing = 1; |
|
ivr->cur_block = ivr->start_byte / BLOCK_SIZE; |
|
ivr->is_block_aligned = !(ivr->start_byte % BLOCK_SIZE); |
|
if (ivr->flags & 1) { |
|
if (src == NULL) |
|
goto eof; |
|
ret = (*src->open)(src); |
|
if (ret < 0) { |
|
ivr->eof = 1; |
|
return ret; |
|
} |
|
ivr->src_is_open = 1; |
|
} else { |
|
ivr->fd = open(ivr->source_pt, O_RDONLY); |
|
if (ivr->fd == -1) { |
|
iso_msg_submit(-1, ISO_BAD_PARTITION_FILE, 0, |
|
"Cannot open local file for interval reading"); |
|
ivr->eof = 1; |
|
return ISO_BAD_PARTITION_FILE; |
|
} |
|
if (ivr->cur_block != 0) { |
|
if (lseek(ivr->fd, ivr->cur_block * BLOCK_SIZE, SEEK_SET) == |
|
-1) { |
|
iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, |
|
"Cannot address interval start in local file"); |
|
ivr->eof = 1; |
|
goto eof; |
|
} |
|
} |
|
} |
|
ivr->initialized = 1; |
|
} |
|
if (ivr->is_block_aligned) { |
|
read_buf = buf; |
|
} else { |
|
process_pending:; |
|
read_buf = ivr->read_buf; |
|
/* Copy pending bytes from previous read */ |
|
if (ivr->pending_read_bytes > 0) { |
|
memcpy(buf, ivr->pending_read_pt, ivr->pending_read_bytes); |
|
*buf_fill = ivr->pending_read_bytes; |
|
ivr->pending_read_bytes = 0; |
|
} |
|
} |
|
|
|
/* Read next block */ |
|
read_done = 0; |
|
if (ivr->cur_block * BLOCK_SIZE <= ivr->end_byte) { |
|
if (ivr->flags & 1) { |
|
ret = (*src->read_block)(src, (uint32_t) ivr->cur_block, read_buf); |
|
if (ret < 0) { |
|
if (iso_error_get_severity(ret) > 0x68000000) /* > FAILURE */ |
|
return ret; |
|
iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, |
|
"Premature EOF while interval reading from imported ISO"); |
|
ivr->eof = 1; |
|
} |
|
read_done = BLOCK_SIZE; |
|
} else { |
|
read_done = 0; |
|
to_read = ivr->end_byte - ivr->start_byte + 1 - ivr->read_count; |
|
if (to_read > BLOCK_SIZE) |
|
to_read = BLOCK_SIZE; |
|
while (read_done < to_read) { |
|
ret = read(ivr->fd, read_buf, to_read - read_done); |
|
if (ret == -1) { |
|
iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, |
|
"Read error while interval reading from local file"); |
|
ivr->eof = 1; |
|
break; |
|
} else if (ret == 0) { |
|
iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, |
|
"Premature EOF while interval reading from local file"); |
|
ivr->eof = 1; |
|
break; |
|
} else |
|
read_done += ret; |
|
} |
|
} |
|
} |
|
if (ivr->is_block_aligned) { |
|
*buf_fill = read_done; |
|
} else if (initializing) { |
|
ivr->pending_read_pt = ivr->read_buf + |
|
(ivr->start_byte - ivr->cur_block * BLOCK_SIZE); |
|
ivr->pending_read_bytes = (((off_t) ivr->cur_block) + 1) * BLOCK_SIZE - |
|
ivr->start_byte; |
|
initializing = 0; |
|
goto process_pending; |
|
|
|
} else if (read_done > 0) { |
|
/* Copy bytes from new read */ |
|
to_copy = read_done > BLOCK_SIZE - *buf_fill ? |
|
BLOCK_SIZE - *buf_fill : read_done; |
|
memcpy(buf + *buf_fill, ivr->read_buf, to_copy); |
|
*buf_fill += to_copy; |
|
ivr->pending_read_pt = ivr->read_buf + to_copy; |
|
ivr->pending_read_bytes = read_done - to_copy; |
|
} |
|
if (ivr->start_byte + ivr->read_count + *buf_fill - 1 > ivr->end_byte) { |
|
*buf_fill = ivr->end_byte - ivr->start_byte + 1 - ivr->read_count; |
|
ivr->eof = 1; |
|
} |
|
|
|
if (*buf_fill < BLOCK_SIZE) |
|
memset(buf + *buf_fill, 0, BLOCK_SIZE - *buf_fill); |
|
|
|
ret = iso_ivr_zeroize(ivr, buf, *buf_fill, 0); |
|
if (ret < 0) |
|
return ret; |
|
|
|
ivr->read_count += *buf_fill; |
|
|
|
return ISO_SUCCESS; |
|
} |
|
|
|
|
|
/* Tells whether ivr is a reader from imported_iso in a multi-session |
|
add-on situation, and thus to be kept in place. |
|
*/ |
|
int iso_interval_reader_keep(Ecma119Image *target, |
|
struct iso_interval_reader *ivr, |
|
int flag) |
|
{ |
|
/* Source must be "imported_iso" */ |
|
if (!(ivr->flags & 1)) |
|
return 0; |
|
|
|
/* It must not be a new ISO */ |
|
if (!target->opts->appendable) |
|
return 0; |
|
|
|
/* --- From here on return either 1 or <0 --- */ |
|
|
|
/* multi-session write offset must be larger than interval end */ |
|
if (target->opts->ms_block <= ivr->end_byte / BLOCK_SIZE) |
|
return ISO_MULTI_OVER_IMPORTED; |
|
|
|
return 1; |
|
} |
|
|
|
|
|
int iso_interval_reader_start_size(Ecma119Image *t, char *path, |
|
off_t *start_byte, off_t *byte_count, |
|
int flag) |
|
{ |
|
struct iso_interval_reader *ivr; |
|
int keep, ret; |
|
|
|
ret = iso_interval_reader_new(t->image, path, &ivr, byte_count, 0); |
|
if (ret < 0) |
|
return ret; |
|
*start_byte = ivr->start_byte; |
|
keep = iso_interval_reader_keep(t, ivr, 0); |
|
if (keep < 0) |
|
return(keep); |
|
iso_interval_reader_destroy(&ivr, 0); |
|
return ISO_SUCCESS + (keep > 0); |
|
} |
|
|
|
|
|
int iso_write_partition_file(Ecma119Image *target, char *path, |
|
uint32_t prepad, uint32_t blocks, int flag) |
|
{ |
|
|
|
struct iso_interval_reader *ivr = NULL; |
|
int buf_fill; |
|
off_t byte_count; |
|
FILE *fp = NULL; |
|
|
|
uint32_t i; |
|
uint8_t *buf = NULL; |
|
int ret; |
|
|
|
LIBISO_ALLOC_MEM(buf, uint8_t, BLOCK_SIZE); |
|
for (i = 0; i < prepad; i++) { |
|
ret = iso_write(target, buf, BLOCK_SIZE); |
|
if (ret < 0) |
|
goto ex; |
|
} |
|
|
|
if (flag & 1) { |
|
ret = iso_interval_reader_new(target->image, path, |
|
&ivr, &byte_count, 0); |
|
if (ret < 0) |
|
goto ex; |
|
ret = iso_interval_reader_keep(target, ivr, 0); |
|
if (ret < 0) |
|
goto ex; |
|
if (ret > 0) { |
|
/* From imported_iso and for add-on session. Leave it in place. */ |
|
ret = ISO_SUCCESS; |
|
goto ex; |
|
} |
|
for (i = 0; i < blocks; i++) { |
|
ret = iso_interval_reader_read(ivr, buf, &buf_fill, 0); |
|
if (ret < 0) |
|
goto ex; |
|
ret = iso_write(target, buf, BLOCK_SIZE); |
|
if (ret < 0) |
|
goto ex; |
|
} |
|
} else { |
|
fp = fopen(path, "rb"); |
|
if (fp == NULL) |
|
{ret = ISO_BAD_PARTITION_FILE; goto ex;} |
|
|
|
for (i = 0; i < blocks; i++) { |
|
memset(buf, 0, BLOCK_SIZE); |
|
if (fp != NULL) { |
|
ret = fread(buf, 1, BLOCK_SIZE, fp); |
|
if (ret != BLOCK_SIZE) { |
|
fclose(fp); |
|
fp = NULL; |
|
} |
|
} |
|
ret = iso_write(target, buf, BLOCK_SIZE); |
|
if (ret < 0) { |
|
if (fp != NULL) |
|
fclose(fp); |
|
goto ex; |
|
} |
|
} |
|
if (fp != NULL) |
|
fclose(fp); |
|
} |
|
ret = ISO_SUCCESS; |
|
ex:; |
|
iso_interval_reader_destroy(&ivr, 0); |
|
LIBISO_FREE_MEM(buf); |
|
return ret; |
|
} |
|
|
|
|
|
void issue_ucs2_warning_summary(size_t failures) |
|
{ |
|
if (failures > ISO_JOLIET_UCS2_WARN_MAX) { |
|
iso_msg_submit(-1, ISO_NAME_NOT_UCS2, 0, |
|
"More filenames found which were not suitable for Joliet character set UCS-2"); |
|
} |
|
if (failures > 0) { |
|
iso_msg_submit(-1, ISO_NAME_NOT_UCS2, 0, |
|
"Sum of filenames not suitable for Joliet character set UCS-2: %.f", |
|
(double) failures); |
|
} |
|
} |
|
|
|
|
|
static |
|
void *write_function(void *arg) |
|
{ |
|
int res, i; |
|
#ifndef Libisofs_appended_partitions_inlinE |
|
int first_partition = 1, last_partition = 0; |
|
#endif |
|
IsoImageWriter *writer; |
|
|
|
Ecma119Image *target = (Ecma119Image*)arg; |
|
iso_msg_debug(target->image->id, "Starting image writing..."); |
|
|
|
target->bytes_written = (off_t) 0; |
|
target->percent_written = 0; |
|
|
|
res = write_head_part(target, 0); |
|
if (res < 0) |
|
goto write_error; |
|
|
|
/* write data for each writer */ |
|
for (i = 0; i < (int) target->nwriters; ++i) { |
|
writer = target->writers[i]; |
|
if (target->gpt_backup_outside && |
|
writer->write_vol_desc == gpt_tail_writer_write_vol_desc) |
|
continue; |
|
res = writer->write_data(writer); |
|
if (res < 0) { |
|
goto write_error; |
|
} |
|
} |
|
|
|
#ifndef Libisofs_appended_partitions_inlinE |
|
|
|
/* Append partition data */ |
|
iso_count_appended_partitions(target, &first_partition, &last_partition); |
|
for (i = first_partition - 1; i <= last_partition - 1; i++) { |
|
if (target->opts->appended_partitions[i] == NULL) |
|
continue; |
|
if (target->opts->appended_partitions[i][0] == 0) |
|
continue; |
|
res = iso_write_partition_file(target, |
|
target->opts->appended_partitions[i], |
|
target->appended_part_prepad[i], |
|
target->appended_part_size[i], |
|
target->opts->appended_part_flags[i] & 1); |
|
if (res < 0) |
|
goto write_error; |
|
} |
|
|
|
#endif /* ! Libisofs_appended_partitions_inlinE */ |
|
|
|
if (target->gpt_backup_outside) { |
|
for (i = 0; i < (int) target->nwriters; ++i) { |
|
writer = target->writers[i]; |
|
if (writer->write_vol_desc != gpt_tail_writer_write_vol_desc) |
|
continue; |
|
res = writer->write_data(writer); |
|
if (res < 0) |
|
goto write_error; |
|
} |
|
} |
|
|
|
/* Transplant checksum buffer from Ecma119Image to IsoImage */ |
|
transplant_checksum_buffer(target, 0); |
|
|
|
iso_ring_buffer_writer_close(target->buffer, 0); |
|
|
|
res = finish_libjte(target); |
|
if (res <= 0) |
|
goto write_error; |
|
|
|
issue_ucs2_warning_summary(target->joliet_ucs2_failures); |
|
|
|
target->image->generator_is_running = 0; |
|
|
|
/* Give up reference claim made in ecma119_image_new(). |
|
Eventually free target */ |
|
ecma119_image_free(target); |
|
|
|
if (target->tree_end_block == 1) { |
|
iso_msgs_submit(0, |
|
"Image is most likely damaged. Calculated/written tree end address mismatch.", |
|
0, "FATAL", 0); |
|
} |
|
if (target->bytes_written != target->total_size) { |
|
iso_msg_debug(target->image->id, |
|
"bytes_written = %.f <-> total_size = %.f", |
|
(double) target->bytes_written, (double) target->total_size); |
|
iso_msgs_submit(0, |
|
"Image is most likely damaged. Calculated/written image end address mismatch.", |
|
0, "FATAL", 0); |
|
} |
|
|
|
#ifdef Libisofs_with_pthread_exiT |
|
pthread_exit(NULL); |
|
#else |
|
return NULL; |
|
#endif |
|
|
|
write_error: ; |
|
if (res != (int) ISO_LIBJTE_END_FAILED) |
|
finish_libjte(target); |
|
target->eff_partition_offset = 0; |
|
if (res == (int) ISO_CANCELED) { |
|
/* canceled */ |
|
if (!target->opts->will_cancel) |
|
iso_msg_submit(target->image->id, ISO_IMAGE_WRITE_CANCELED, |
|
0, NULL); |
|
} else { |
|
/* image write error */ |
|
iso_msg_submit(target->image->id, ISO_WRITE_ERROR, res, |
|
"Image write error"); |
|
} |
|
iso_ring_buffer_writer_close(target->buffer, 1); |
|
|
|
/* Re-activate recorded cx xinfo */ |
|
process_preserved_cx(target->image->root, 1); |
|
|
|
target->image->generator_is_running = 0; |
|
|
|
/* Give up reference claim made in ecma119_image_new(). |
|
Eventually free target */ |
|
ecma119_image_free(target); |
|
|
|
#ifdef Libisofs_with_pthread_exiT |
|
pthread_exit(NULL); |
|
#else |
|
return NULL; |
|
#endif |
|
|
|
} |
|
|
|
|
|
static |
|
int checksum_prepare_image(IsoImage *src, int flag) |
|
{ |
|
int ret; |
|
|
|
/* Set provisory value of isofs.ca with |
|
4 byte LBA, 4 byte count, size 16, name MD5 */ |
|
ret = iso_root_set_isofsca((IsoNode *) src->root, 0, 0, 0, 16, "MD5", 0); |
|
if (ret < 0) |
|
return ret; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
|
|
/* |
|
@flag bit0= recursion |
|
*/ |
|
static |
|
int checksum_prepare_nodes(Ecma119Image *target, IsoNode *node, int flag) |
|
{ |
|
IsoNode *pos; |
|
IsoFile *file; |
|
IsoImage *img; |
|
int ret, i, no_md5 = 0, has_xinfo = 0, has_attr = 0; |
|
size_t old_cx_value_length = 0; |
|
unsigned int idx = 0; |
|
char *old_cx_value= NULL; |
|
void *xipt = NULL; |
|
|
|
img= target->image; |
|
|
|
if (node->type == LIBISO_FILE) { |
|
file = (IsoFile *) node; |
|
if (file->from_old_session) { |
|
/* Record attribute isofs.cx as xinfo before it can get overwritten |
|
for the emerging image. |
|
The recorded index will be used to retrieve the loaded MD5 |
|
and it will be brought back into effect if cancellation of |
|
image production prevents that the old MD5 array gets replaced |
|
by the new one. |
|
*/ |
|
has_attr = iso_node_lookup_attr(node, "isofs.cx", |
|
&old_cx_value_length, &old_cx_value, 0); |
|
if (has_attr == 1 && old_cx_value_length == 4) { |
|
for (i = 0; i < 4; i++) |
|
idx = (idx << 8) | ((unsigned char *) old_cx_value)[i]; |
|
if (idx > 0 && idx < 0x8000000) { |
|
/* xipt is an int disguised as void pointer */ |
|
for (i = 0; i < 4; i++) |
|
((char *) &xipt)[i] = old_cx_value[i]; |
|
ret = iso_node_add_xinfo(node, checksum_cx_xinfo_func, |
|
xipt); |
|
if (ret < 0) |
|
goto ex; |
|
} else |
|
no_md5 = 1; |
|
} |
|
} |
|
if (file->from_old_session && target->opts->appendable) { |
|
/* Save MD5 data of files from old image which will not |
|
be copied and have an MD5 recorded in the old image. */ |
|
has_xinfo = iso_node_get_xinfo(node, checksum_md5_xinfo_func, |
|
&xipt); |
|
|