libisofs-legacy/libisofs/ecma119.c

841 lines
23 KiB
C
Raw Permalink Normal View History

2006-08-24 19:23:37 +00:00
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
2006-08-15 20:37:04 +00:00
/* vim: set noet ts=8 sts=8 sw=8 : */
2006-08-24 19:23:37 +00:00
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <err.h>
2006-08-15 20:37:04 +00:00
#include "ecma119.h"
2006-08-24 19:23:37 +00:00
#include "ecma119_tree.h"
#include "susp.h"
#include "rockridge.h"
#include "joliet.h"
2006-08-15 20:37:04 +00:00
#include "volume.h"
2006-08-24 19:23:37 +00:00
#include "tree.h"
2006-08-15 20:37:04 +00:00
#include "util.h"
#include "file.h"
2006-08-15 20:37:04 +00:00
#include "libisofs.h"
2006-08-24 20:46:12 +00:00
#include "libburn/libburn.h"
2007-06-05 22:01:26 +00:00
#include "eltorito.h"
2006-08-24 19:23:37 +00:00
/* burn-source compatible stuff */
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size);
static off_t
bs_get_size(struct burn_source *bs);
static void
bs_free_data(struct burn_source *bs);
typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*);
/* return true if the given state is only required for Joliet volumes */
static int
is_joliet_state(enum ecma119_write_state);
static void
next_state(struct ecma119_write_target *t);
/* write t->state_data to the buf, one block at a time */
static void
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf);
/* writing functions. All these functions assume the buf is large enough */
static void
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
static void
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf);
static void
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf);
static void
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
static void
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
static void
write_one_dir_record(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
int file_id,
uint8_t *buf);
static void
write_one_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
uint8_t *buf);
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf);
/* wrapper functions for writing */
static void wr_system_area(struct ecma119_write_target*, uint8_t*);
static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*);
static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*);
static void wr_l_path_table(struct ecma119_write_target*, uint8_t*);
static void wr_m_path_table(struct ecma119_write_target*, uint8_t*);
static void wr_dir_records(struct ecma119_write_target*, uint8_t*);
static void wr_files(struct ecma119_write_target*, uint8_t*);
static const write_fn writers[] =
{
NULL,
wr_system_area,
wr_pri_vol_desc,
2007-06-05 22:01:26 +00:00
el_torito_wr_boot_vol_desc,
2006-08-24 19:23:37 +00:00
joliet_wr_sup_vol_desc,
wr_vol_desc_term,
wr_l_path_table,
wr_m_path_table,
joliet_wr_l_path_table,
joliet_wr_m_path_table,
wr_dir_records,
joliet_wr_dir_records,
2007-06-05 22:01:26 +00:00
el_torito_wr_catalog,
2006-08-24 19:23:37 +00:00
wr_files
2006-08-15 20:37:04 +00:00
};
2006-08-24 19:23:37 +00:00
/* When a writer is created, we
* 1) create an ecma119 tree
* 2) add SUSP fields (if necessary)
* 3) calculate the size and position of all nodes in the tree
* 4) finalize SUSP fields (if necessary)
2006-08-15 20:37:04 +00:00
*/
2006-08-24 19:23:37 +00:00
static void
add_susp_fields_rec(struct ecma119_write_target *t,
struct ecma119_tree_node *node)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
size_t i;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
rrip_add_PX(t, node);
rrip_add_NM(t, node);
rrip_add_TF(t, node);
switch (node->type) {
case ECMA119_FILE:
break;
case ECMA119_SYMLINK:
rrip_add_SL(t, node);
break;
case ECMA119_DIR:
if (node->info.dir.real_parent != node->parent) {
rrip_add_RE(t, node);
rrip_add_PL(t, node);
}
for (i = 0; i < node->info.dir.nchildren; i++) {
add_susp_fields_rec(t, node->info.dir.children[i]);
}
break;
case ECMA119_PLACEHOLDER:
rrip_add_CL(t, node);
break;
default:
// FIXME support for device blocks by uncommenting this
//if (node->iso_self->attrib.st_rdev)
// rrip_add_PN(t, node);
break;
2006-08-24 19:23:37 +00:00
}
susp_add_CE(t, node);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
add_susp_fields(struct ecma119_write_target *t)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
susp_add_SP(t, t->root);
rrip_add_ER(t, t->root);
add_susp_fields_rec(t, t->root);
2006-08-15 20:37:04 +00:00
}
/**
2006-08-24 19:23:37 +00:00
* Fill out the dir.len and dir.CE_len fields for each
* ecma119_tree_node that is a directory. Also calculate the total number of
* directories and the number of files for which we need to write out data.
* (dirlist_len and filelist_len)
2006-08-15 20:37:04 +00:00
*/
2006-08-24 19:23:37 +00:00
static void
calc_dir_size(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
size_t i;
2007-03-20 09:41:52 +00:00
size_t newlen;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
assert(dir->type == ECMA119_DIR);
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
t->dirlist_len++;
dir->info.dir.len = 34 + dir->info.dir.self_susp.non_CE_len
+ 34 + dir->info.dir.parent_susp.non_CE_len;
dir->info.dir.CE_len = dir->info.dir.self_susp.CE_len
+ dir->info.dir.parent_susp.CE_len;
for (i = 0; i < dir->info.dir.nchildren; ++i) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
newlen = dir->info.dir.len + ch->dirent_len + ch->susp.non_CE_len;
if ((newlen % 2048) < (dir->info.dir.len % 2048)) {
dir->info.dir.len = newlen + (2048 - (dir->info.dir.len % 2048));
} else {
dir->info.dir.len += ch->dirent_len + ch->susp.non_CE_len;
2007-03-20 09:41:52 +00:00
}
dir->info.dir.CE_len += ch->susp.CE_len;
2006-08-24 19:23:37 +00:00
}
t->total_dir_size += round_up(dir->info.dir.len + dir->info.dir.CE_len,
2006-08-24 19:23:37 +00:00
t->block_size);
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
//struct iso_tree_node *iso = ch->iso_self;
2006-08-24 19:23:37 +00:00
if (ch->type == ECMA119_DIR) {
calc_dir_size(t, ch);
}
// else if (iso && iso->attrib.st_size
// && iso->loc.type == LIBISO_FILESYS
// && iso->loc.path) {
// t->filelist_len++;
// }
2006-08-15 20:37:04 +00:00
}
}
/**
2006-08-24 19:23:37 +00:00
* Fill out the block field in each ecma119_tree_node that is a directory and
* fill out t->dirlist.
2006-08-15 20:37:04 +00:00
*/
2006-08-24 19:23:37 +00:00
static void
calc_dir_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
size_t i;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
assert(dir->type == ECMA119_DIR);
2006-08-15 20:37:04 +00:00
dir->info.dir.block = t->curblock;
t->curblock += div_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size);
2006-08-24 19:23:37 +00:00
t->dirlist[t->curfile++] = dir;
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
2006-08-24 19:23:37 +00:00
if (ch->type == ECMA119_DIR)
calc_dir_pos(t, ch);
2006-08-15 20:37:04 +00:00
}
}
2006-08-15 20:37:04 +00:00
static int
cmp_file(const void *f1, const void *f2)
{
struct iso_file *f = *((struct iso_file**)f1);
struct iso_file *g = *((struct iso_file**)f2);
/* higher weighted first */
return g->sort_weight - f->sort_weight;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
/**
* Fill out the block field for each file and fill out t->filelist.
2006-08-15 20:37:04 +00:00
*/
2006-08-24 19:23:37 +00:00
static void
calc_file_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
size_t i;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
assert(dir->type == ECMA119_DIR);
t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count);
for (i = 0; i < FILE_HASH_NODES; ++i) {
struct iso_file_hash_node *node;
node = t->file_table->table[i];
if (!node)
continue;
do {
struct iso_file *file = node->file;
if (file->size)
t->filelist[t->curfile++] = file;
node = node->next;
} while (node);
2006-08-15 20:37:04 +00:00
}
t->filelist_len = t->curfile;
/* sort */
if ( t->sort_files )
qsort(t->filelist, t->filelist_len, sizeof(void*), cmp_file);
/* fill block value */
for ( i = 0; i < t->filelist_len; ++i) {
struct iso_file *file = t->filelist[i];
file->block = t->curblock;
t->curblock += div_up(file->size, t->block_size);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
/* reset curfile when we're finished */
t->curfile = 0;
2006-08-15 20:37:04 +00:00
}
/**
* Create a new ecma119_write_target from the given volume number of the
* given volume set.
*
* \pre \p volnum is less than \p volset-\>volset_size.
* \post For each node in the tree, writer_data has been allocated.
* \post The directory heirarchy has been reorganised to be ecma119-compatible.
*/
static struct ecma119_write_target*
2006-08-24 19:23:37 +00:00
ecma119_target_new(struct iso_volset *volset,
const struct ecma119_source_opts *opts)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
struct ecma119_write_target *t =
calloc(1, sizeof(struct ecma119_write_target));
size_t i, j, cur;
struct iso_tree_node *iso_root =
(struct iso_tree_node*) volset->volume[opts->volnum]->root;
t->cache_inodes = opts->no_cache_inodes ? 0 : 1;
t->replace_mode = opts->default_mode ? 0 : 1;
if ( opts->replace_dir_mode )
t->replace_mode |= 0x02;
if ( opts->replace_file_mode )
t->replace_mode |= 0x04;
if ( opts->replace_gid )
t->replace_mode |= 0x08;
if ( opts->replace_uid )
t->replace_mode |= 0x10;
t->dir_mode = opts->dir_mode;
t->file_mode = opts->file_mode;
t->gid = opts->gid;
t->uid = opts->uid;
//TODO get defailt values for current locale, no UTF-8
t->input_charset = opts->input_charset ? opts->input_charset : "UTF-8";
t->ouput_charset = opts->ouput_charset ? opts->ouput_charset : "UTF-8";
t->sort_files = opts->sort_files;
t->file_table = iso_file_table_new(t->cache_inodes);
2006-08-24 19:23:37 +00:00
volset->refcount++;
t->iso_level = opts->level;
t->block_size = 2048;
t->relaxed_constraints = opts->relaxed_constraints;
t->rockridge = (opts->flags & ECMA119_ROCKRIDGE) ? 1 : 0;
t->joliet = (opts->flags & ECMA119_JOLIET) ? 1 : 0;
t->catalog = volset->volume[opts->volnum]->bootcat;
2007-06-05 22:01:26 +00:00
t->eltorito = t->catalog ? 1 : 0;
t->root = ecma119_tree_create(t, iso_root);
2006-08-24 19:23:37 +00:00
if (t->joliet)
t->joliet_root = joliet_tree_create(t, iso_root);
t->volset = volset;
t->volnum = opts->volnum;
2006-08-24 19:23:37 +00:00
t->now = time(NULL);
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
if (t->rockridge)
add_susp_fields(t);
2006-08-24 19:23:37 +00:00
calc_dir_size(t, t->root);
if (t->joliet) {
joliet_calc_dir_size(t, t->joliet_root);
t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet);
t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len);
t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len);
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
/* fill out the pathlist */
t->pathlist[0] = t->root;
t->path_table_size = 10; /* root directory record */
cur = 1;
for (i = 0; i < t->dirlist_len; i++) {
struct ecma119_tree_node *dir = t->pathlist[i];
for (j = 0; j < dir->info.dir.nchildren; j++) {
struct ecma119_tree_node *ch = dir->info.dir.children[j];
2006-08-24 19:23:37 +00:00
if (ch->type == ECMA119_DIR) {
size_t len = 8 + strlen(ch->iso_name);
2006-08-24 19:23:37 +00:00
t->pathlist[cur++] = ch;
t->path_table_size += len + len % 2;
}
2006-08-15 20:37:04 +00:00
}
}
2006-08-24 19:23:37 +00:00
t->curblock = 16 /* system area */
+ 1 /* volume desc */
+ 1; /* volume desc terminator */
2007-06-05 22:01:26 +00:00
if (t->eltorito)
t->curblock += 1; /* boot record volume descriptor */
2006-08-24 19:23:37 +00:00
if (t->joliet) /* supplementary vol desc */
t->curblock += div_up (2048, t->block_size);
2006-08-15 20:37:04 +00:00
t->l_path_table_pos = t->curblock;
2006-08-24 19:23:37 +00:00
t->curblock += div_up(t->path_table_size, t->block_size);
2006-08-15 20:37:04 +00:00
t->m_path_table_pos = t->curblock;
2006-08-24 19:23:37 +00:00
t->curblock += div_up(t->path_table_size, t->block_size);
2006-08-15 20:37:04 +00:00
if (t->joliet) {
2006-08-24 19:23:37 +00:00
joliet_prepare_path_tables(t);
2006-08-15 20:37:04 +00:00
t->l_path_table_pos_joliet = t->curblock;
2006-08-24 19:23:37 +00:00
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
2006-08-15 20:37:04 +00:00
t->m_path_table_pos_joliet = t->curblock;
2006-08-24 19:23:37 +00:00
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
calc_dir_pos(t, t->root);
/* reset curfile when we're finished */
t->curfile = 0;
if (t->joliet) {
2006-08-24 19:23:37 +00:00
joliet_calc_dir_pos(t, t->joliet_root);
/* reset curfile when we're finished */
t->curfile = 0;
}
2007-06-05 22:01:26 +00:00
/* el-torito? */
if (t->eltorito) {
/* add catalog block */
t->catalog->file->block = t->curblock;
t->curblock += div_up(2048, t->block_size);
el_torito_get_image_files(t);
}
2006-08-24 19:23:37 +00:00
calc_file_pos(t, t->root);
2007-06-05 22:01:26 +00:00
if (t->eltorito)
el_torito_patch_image_files(t);
2006-08-15 20:37:04 +00:00
if (t->rockridge) {
2006-08-24 19:23:37 +00:00
susp_finalize(t, t->root);
rrip_finalize(t, t->root);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
t->total_size = t->curblock * t->block_size;
t->vol_space_size = t->curblock;
2006-08-15 20:37:04 +00:00
/* prepare for writing */
2006-08-24 19:23:37 +00:00
t->curblock = 0;
2006-08-15 20:37:04 +00:00
t->state = ECMA119_WRITE_SYSTEM_AREA;
2006-08-24 19:23:37 +00:00
return t;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static int
is_joliet_state(enum ecma119_write_state state)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET
|| state == ECMA119_WRITE_L_PATH_TABLE_JOLIET
|| state == ECMA119_WRITE_M_PATH_TABLE_JOLIET
|| state == ECMA119_WRITE_DIR_RECORDS_JOLIET;
2006-08-15 20:37:04 +00:00
}
2007-06-05 22:01:26 +00:00
static int
is_eltorito_state(enum ecma119_write_state state)
{
return state == ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC
|| state == ECMA119_WRITE_ELTORITO_CATALOG;
}
2006-08-24 19:23:37 +00:00
static void
next_state(struct ecma119_write_target *t)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
t->state++;
2007-06-05 22:01:26 +00:00
while ( (!t->joliet && is_joliet_state(t->state))
||(!t->eltorito && is_eltorito_state(t->state)) )
2006-08-24 19:23:37 +00:00
t->state++;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
wr_system_area(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
memset(buf, 0, t->block_size);
if (t->curblock == 15) {
next_state(t);
2006-08-15 20:37:04 +00:00
}
}
2006-08-24 19:23:37 +00:00
static void
wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
wr_dir_records(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
wr_files(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
struct state_files *f_st = &t->state_files;
size_t nread;
struct iso_file *f = t->filelist[f_st->file];
const char *path = f->path;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
if (!f_st->fd) {
printf("Writing file %s\n", path);
f_st->data_len = f->size;
2006-08-24 19:23:37 +00:00
f_st->fd = fopen(path, "r");
if (!f_st->fd)
err(1, "couldn't open %s for reading", path);
assert(t->curblock == f->block);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
nread = fread(buf, 1, t->block_size, f_st->fd);
f_st->pos += t->block_size;
if (nread < 0)
warn("problem reading from %s", path);
else if (nread != t->block_size && f_st->pos < f_st->data_len)
warnx("incomplete read from %s", path);
if (f_st->pos >= f_st->data_len) {
fclose(f_st->fd);
f_st->fd = 0;
f_st->pos = 0;
f_st->file++;
if (f_st->file >= t->filelist_len)
next_state(t);
2006-08-15 20:37:04 +00:00
}
}
2006-08-24 19:23:37 +00:00
static void
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf;
struct iso_volume *volume = t->volset->volume[t->volnum];
char *vol_id = str2d_char(volume->volume_id, t->input_charset);
char *pub_id = str2a_char(volume->publisher_id, t->input_charset);
char *data_id = str2a_char(volume->data_preparer_id, t->input_charset);
char *volset_id = str2d_char(t->volset->volset_id, t->input_charset);
char *system_id = str2a_char(volume->system_id, t->input_charset);
char *application_id = str2a_char(volume->application_id, t->input_charset);
char *copyright_file_id = str2d_char(volume->copyright_file_id, t->input_charset);
char *abstract_file_id = str2d_char(volume->abstract_file_id, t->input_charset);
char *biblio_file_id = str2d_char(volume->biblio_file_id, t->input_charset);
2006-08-24 19:23:37 +00:00
vol->vol_desc_type[0] = 1;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
if (system_id)
strncpy((char*)vol->system_id, system_id, 32);
else
/* put linux by default? */
memcpy(vol->system_id, "LINUX", 5);
2006-08-24 19:23:37 +00:00
if (vol_id)
strncpy((char*)vol->volume_id, vol_id, 32);
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, 4);
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, 3, vol->root_dir_record);
/* mmm, why not check for null? */
2006-08-24 19:23:37 +00:00
strncpy((char*)vol->vol_set_id, volset_id, 128);
strncpy((char*)vol->publisher_id, pub_id, 128);
strncpy((char*)vol->data_prep_id, data_id, 128);
if (application_id)
strncpy((char*)vol->application_id, application_id, 128);
if (copyright_file_id)
strncpy((char*)vol->copyright_file_id, copyright_file_id, 37);
if (abstract_file_id)
strncpy((char*)vol->abstract_file_id, abstract_file_id, 37);
if (biblio_file_id)
strncpy((char*)vol->bibliographic_file_id, biblio_file_id, 37);
2006-08-24 19:23:37 +00:00
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);
2006-08-24 19:23:37 +00:00
}
static void
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf)
{
struct ecma119_vol_desc_terminator *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;
}
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 ecma119_tree_node *dir;
int parent = 0;
for (i = 0; i < t->dirlist_len; i++) {
dir = t->pathlist[i];
assert(dir->type == ECMA119_DIR);
2006-08-24 19:23:37 +00:00
while ((i) && t->pathlist[parent] != dir->parent)
parent++;
assert(parent < i || i == 0);
rec = (struct ecma119_path_table_record*) buf;
rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1;
2006-08-24 19:23:37 +00:00
rec->len_xa[0] = 0;
write_int(rec->block, dir->info.dir.block, 4);
2006-08-24 19:23:37 +00:00
write_int(rec->parent, parent + 1, 2);
if (dir->parent)
memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]);
2006-08-24 19:23:37 +00:00
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
}
}
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);
}
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
/* 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 ecma119_tree_node *node,
int file_id,
uint8_t *buf)
{
uint32_t len;
uint32_t block;
2006-08-24 19:23:37 +00:00
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->iso_name);
2006-08-24 19:23:37 +00:00
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->iso_name;
2006-08-24 19:23:37 +00:00
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
if (node->type == ECMA119_DIR) {
len = node->info.dir.len;
block = node->info.dir.block;
} else if (node->type == ECMA119_FILE) {
len = node->info.file->size;
block = node->info.file->block;
} else {
/* for nodes other than files and dirs, we set both len and block to 0 */
len = 0;
block = 0;
}
2006-08-24 19:23:37 +00:00
/* we don't write out susp fields for the root node */
if (t->rockridge) {
if (file_id == 0) {
assert(node->type == ECMA119_DIR);
susp_write(t, &node->info.dir.self_susp, &buf[len_dr]);
len_dr += node->info.dir.self_susp.non_CE_len;
2006-08-24 19:23:37 +00:00
} else if (file_id == 1) {
assert(node->type == ECMA119_DIR);
susp_write(t, &node->info.dir.parent_susp, &buf[len_dr]);
len_dr += node->info.dir.parent_susp.non_CE_len;
2006-08-24 19:23:37 +00:00
} else if (file_id < 0) {
susp_write(t, &node->susp, &buf[len_dr]);
len_dr += node->susp.non_CE_len;
2006-08-15 20:37:04 +00:00
}
}
2006-08-24 19:23:37 +00:00
if (file_id == 1 && node->parent)
node = node->parent;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
rec->len_dr[0] = len_dr;
iso_bb(rec->block, block, 4);
2006-08-24 19:23:37 +00:00
iso_bb(rec->length, len, 4);
iso_datetime_7(rec->recording_time, t->now);
rec->flags[0] = (node->type == ECMA119_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);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
write_one_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
size_t i;
2007-03-20 09:41:52 +00:00
int j;
size_t len;
2006-08-24 19:23:37 +00:00
uint8_t *orig_buf = buf;
2007-03-20 09:41:52 +00:00
uint8_t *prior_buf = buf;
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
assert(dir->type == ECMA119_DIR);
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
write_one_dir_record(t, dir, 1, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
2006-08-15 20:37:04 +00:00
for (i = 0; i < dir->info.dir.nchildren; i++) {
write_one_dir_record(t, dir->info.dir.children[i], -1, buf);
2007-03-20 09:41:52 +00:00
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];
}
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
/* write the susp continuation areas */
if (t->rockridge) {
susp_write_CE(t, &dir->info.dir.self_susp, buf);
buf += dir->info.dir.self_susp.CE_len;
susp_write_CE(t, &dir->info.dir.parent_susp, buf);
buf += dir->info.dir.parent_susp.CE_len;
for (i = 0; i < dir->info.dir.nchildren; i++) {
susp_write_CE(t, &dir->info.dir.children[i]->susp, buf);
buf += dir->info.dir.children[i]->susp.CE_len;
2006-08-15 20:37:04 +00:00
}
}
assert (buf - orig_buf == dir->info.dir.len + dir->info.dir.CE_len);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
size_t i;
struct ecma119_tree_node *dir;
for (i = 0; i < t->dirlist_len; i++) {
dir = t->dirlist[i];
write_one_dir(t, dir, buf);
buf += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size);
2006-08-15 20:37:04 +00:00
}
}
2006-08-24 19:23:37 +00:00
void
ecma119_start_chunking(struct ecma119_write_target *t,
write_fn writer,
off_t data_size,
uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
if (data_size != t->state_data_size) {
2006-08-24 19:23:37 +00:00
data_size = round_up(data_size, t->block_size);
t->state_data = realloc(t->state_data, data_size);
t->state_data_size = data_size;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
memset(t->state_data, 0, t->state_data_size);
t->state_data_off = 0;
t->state_data_valid = 1;
writer(t, t->state_data);
write_data_chunk(t, buf);
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
memcpy(buf, t->state_data + t->state_data_off, t->block_size);
t->state_data_off += t->block_size;
if (t->state_data_off >= t->state_data_size) {
assert (t->state_data_off <= t->state_data_size);
t->state_data_valid = 0;
next_state(t);
2006-08-15 20:37:04 +00:00
}
}
2006-08-24 19:23:37 +00:00
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
if (size != t->block_size) {
warnx("you must read data in block-sized chunks (%d bytes)",
(int)t->block_size);
return 0;
} else if (t->curblock >= t->vol_space_size) {
return 0;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
if (t->state_data_valid)
write_data_chunk(t, buf);
else
writers[t->state](t, buf);
t->curblock++;
return size;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static off_t
bs_get_size(struct burn_source *bs)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
return t->total_size;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
bs_free_data(struct burn_source *bs)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
ecma119_tree_free(t->root);
iso_file_table_clear(t->file_table);
2006-08-24 19:23:37 +00:00
free(t->dirlist);
free(t->pathlist);
free(t->dirlist_joliet);
free(t->pathlist_joliet);
free(t->filelist);
free(t->state_data);
if (t->joliet)
joliet_tree_free(t->joliet_root);
2006-08-24 19:23:37 +00:00
if (t->state_files.fd)
fclose(t->state_files.fd);
}
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
struct ecma119_source_opts *opts)
2006-08-24 19:23:37 +00:00
{
struct burn_source *ret = calloc(1, sizeof(struct burn_source));
ret->refcount = 1;
ret->read = bs_read;
ret->get_size = bs_get_size;
ret->free_data = bs_free_data;
ret->data = ecma119_target_new(volset, opts);
2006-08-24 19:23:37 +00:00
return ret;
2006-08-15 20:37:04 +00:00
}