libburn/libisofs/ecma119.c

1507 lines
39 KiB
C
Executable File

/* vim: set noet ts=8 sts=8 sw=8 : */
#include "tree.h"
#include "ecma119.h"
#include "volume.h"
#include "util.h"
#include "struct.h"
#include "rockridge.h"
#include "libisofs.h"
#include "libburn/libburn.h"
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <wchar.h>
const char* const ecma119_standard_id = "CD001";
/* Format definitions */
const char* const ecma119_vol_fmt = "B5bB"; /* common between all vol descs */
const char* const ecma119_privol_fmt =
"B" /* volume desc type */
"5b" /* standard id */
"B" /* volume desc version */
"x"
"32b" /* system id */
"32b" /* volume id */
"8x"
"=L" /* volume space size */
"32x"
"=H" /* volume set size */
"=H" /* volume sequence number */
"=H" /* block size */
"=L" /* path table size */
"<L" /* l path table 1 */
"<L" /* l path table 2 */
">L" /* m path table 1 */
">L" /* m path table 2 */
"34B" /* root directory record */
"128b" /* volume id */
"128b" /* publisher id */
"128b" /* data preparer id */
"128b" /* application id */
"37b" /* copyright file id */
"37b" /* abstract file id */
"37b" /* bibliographic file id */
"T" /* creation timestamp */
"T" /* modification timestamp */
"T" /* expiration timestamp */
"T" /* effective timestamp */
"B" /* file structure version */
"x"
"512x" /* Application Use */
"653x"; /* reserved */
const char* const ecma119_supvol_joliet_fmt =
"B" /* volume desc type */
"5b" /* standard id */
"B" /* volume desc version */
"B" /* volume flags */
"16h" /* system id */
"16h" /* volume id */
"8x"
"=L" /* volume space size */
"32b" /* escape sequences */
"=H" /* volume set size */
"=H" /* volume sequence number */
"=H" /* block size */
"=L" /* path table size */
"<L" /* l path table 1 */
"<L" /* l path table 2 */
">L" /* m path table 1 */
">L" /* m path table 2 */
"34B" /* root directory record */
"64h" /* volume id */
"64h" /* publisher id */
"64h" /* data preparer id */
"64h" /* application id */
"18hx" /* copyright file id */
"18hx" /* abstract file id */
"18hx" /* bibliographic file id */
"T" /* creation timestamp */
"T" /* modification timestamp */
"T" /* expiration timestamp */
"T" /* effective timestamp */
"B" /* file structure version */
"x"
"512x" /* Application Use */
"653x"; /* reserved */
const char* const ecma119_dir_record_fmt =
"B" /* dir record length */
"B" /* extended attribute length */
"=L" /* block */
"=L" /* size */
"S"
"B" /* file flags */
"B" /* file unit size */
"B" /* interleave gap size */
"=H" /* volume sequence number */
"B"; /* name length */
/* file id ansa SU field have variable length, so they don't get defined
* yet. */
/* abstract string functions away from char* to make Joliet stuff easier */
typedef void (*copy_string)(unsigned char *dest, void *str, int maxlen);
typedef int (*node_namelen)(struct iso_tree_node *node);
typedef const void* (*node_getname)(struct iso_tree_node *node);
/* because we don't write SUSP fields in the Joliet Directory Records, it helps
* to abstract away the task of getting the non_CE_len of a susp_info.
*/
typedef int (*susp_len)(struct susp_info*);
typedef int (*total_dirent_len)(struct iso_tree_node*);
const char application_id[] = "LIBBURN SUITE (C) 2002 D.FOREMAN/B.JANSENS";
const char system_id[] = "LINUX";
const uint16_t* application_id_joliet = (uint16_t*) "\0j\0a\0p\0p\0i\0d\0\0";
const uint16_t* system_id_joliet = (uint16_t*) "\0j\0s\0y\0s\0i\0d\0\0";
/* since we have to pass things by address to iso_struct_pack, this makes
* it easier. */
const uint8_t zero = 0;
const uint8_t one = 1;
enum RecordType {
RECORD_TYPE_SELF,
RECORD_TYPE_PARENT,
RECORD_TYPE_NORMAL,
RECORD_TYPE_ROOT
};
/* layout functions */
static void ecma119_reorganize_heirarchy(struct ecma119_write_target *target,
struct iso_tree_dir *dir);
static void ecma119_alloc_writer_data(struct ecma119_write_target *target,
struct iso_tree_dir *dir);
static void ecma119_setup_path_tables_iso(struct ecma119_write_target*);
static void ecma119_setup_path_tables_joliet(struct ecma119_write_target*);
/* burn_source functions */
static int ecma119_read(struct burn_source*, unsigned char*, int);
static int ecma119_get_size(struct burn_source*);
static void ecma119_free_data(struct burn_source*);
/* Writers for the different write_states. */
static void ecma119_write_system_area(struct ecma119_write_target*,
unsigned char *buf);
static void ecma119_write_privol_desc(struct ecma119_write_target*,
unsigned char *buf);
static void ecma119_write_supvol_desc_joliet(struct ecma119_write_target*,
unsigned char *buf);
static void ecma119_write_vol_desc_terminator(struct ecma119_write_target*,
unsigned char *buf);
static void ecma119_write_path_table(struct ecma119_write_target*,
unsigned char *buf,
int flags,
int m_type);
static void ecma119_write_dir_records(struct ecma119_write_target*,
unsigned char *buf,
int flags);
static void ecma119_write_files(struct ecma119_write_target*,
unsigned char *buf);
/* helpers for the writers */
static void ecma119_write_path_table_full(struct ecma119_write_target*,
unsigned char *buf,
int flags,
int mtype);
static unsigned char *ecma119_write_dir(struct ecma119_write_target*,
int flags,
struct iso_tree_dir*);
static void ecma119_write_dir_record(struct ecma119_write_target*,
unsigned char *,
int flags,
struct iso_tree_node*,
enum RecordType);
static void ecma119_write_dir_record_iso(struct ecma119_write_target *t,
uint8_t *buf,
struct iso_tree_node *node,
enum RecordType type);
static void ecma119_write_dir_record_joliet(struct ecma119_write_target *t,
uint8_t *buf,
struct iso_tree_node *node,
enum RecordType type);
static void ecma119_write_dir_record_noname(struct ecma119_write_target *t,
uint8_t *buf,
struct iso_tree_node *node,
enum RecordType type,
uint32_t block,
uint32_t size,
int joliet);
/* name-mangling routines */
/* return a newly allocated array of pointers to all the children, in
* in alphabetical order and with mangled names. */
static struct iso_tree_node **ecma119_mangle_names(struct iso_tree_dir *dir);
/* mangle a single filename according to where the last '.' is:
* \param name the name to mangle
* \param num_change the number of characters to mangle
* \param seq_num the string ("%0<num_change>d", seq_num) to which it should
* be mangled
*/
static void ecma119_mangle_name(char *name,
int num_change,
int seq_num);
/* mangle a single filename based on the explicit positions passed. That is,
* the difference between this and ecma119_mangle_name_iso is that this one
* doesn't look for the '.' character; it changes the character at the
* beginning of name. */
static void ecma119_mangle_name_priv(char *name,
int num_change,
int seq_num);
/* return a newly allocated array of pointers to all the children, in
* alphabetical order. The difference between this and ecma119_mangle_names
* is that this function sorts the files according to joliet names and it
* doesn't mangle the names. */
static struct iso_tree_node **ecma119_sort_joliet(struct iso_tree_dir *dir);
/* string abstraction functions */
static int node_namelen_iso(struct iso_tree_node *node)
{
return strlen(iso_tree_node_get_name(node, ISO_NAME_ISO));
}
static int node_namelen_joliet(struct iso_tree_node *node)
{
const char *name = iso_tree_node_get_name(node, ISO_NAME_JOLIET);
/* return length in bytes, not length in characters */
return ucslen((uint16_t*)name) * 2;
}
static const void *node_getname_iso(struct iso_tree_node *node)
{
return iso_tree_node_get_name(node, ISO_NAME_ISO);
}
static const void *node_getname_joliet(struct iso_tree_node *node)
{
return iso_tree_node_get_name(node, ISO_NAME_JOLIET);
}
static int susp_len_iso(struct susp_info *s)
{
return s->non_CE_len;
}
static int susp_len_joliet(struct susp_info *s)
{
return 0;
}
static int dirent_len_iso(struct iso_tree_node *n)
{
return n->dirent_len + GET_NODE_INF(n)->susp.non_CE_len;
}
static int dirent_len_joliet(struct iso_tree_node *n)
{
return 34 + node_namelen_joliet(n);
}
static void print_dir_info(struct iso_tree_dir *dir,
struct ecma119_write_target *t,
int spaces)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
int i;
for (i=0; i<spaces; i++) {
putchar(' ');
}
printf("non_CE_len=%d, CE_len=%d, self->non_CE_len=%d, "
"parent->non_CE_len=%d, len=%d, blk=%d, jblk=%d\n",
inf->susp.non_CE_len,
inf->susp.CE_len,
inf->self_susp.non_CE_len,
inf->parent_susp.non_CE_len,
inf->len,
(int)dir->block,
(int)inf->joliet_block);
}
static void print_file_info(struct iso_tree_file *file,
struct ecma119_write_target *t,
int spaces)
{
struct file_write_info *inf = GET_FILE_INF(file);
int i;
for (i=0; i<spaces; i++) {
putchar(' ');
}
printf("dirent size=%d, non_CE_len=%d, CE_len=%d, len=%d, blk=%d\n",
(int)file->dirent_len,
inf->susp.non_CE_len,
inf->susp.CE_len,
(int)file->attrib.st_size,
(int)file->block);
}
static struct iso_tree_node **ecma119_mangle_names(struct iso_tree_dir *dir)
{
size_t retsize = dir->nfiles + dir->nchildren;
struct iso_tree_node **ret = calloc(1, sizeof(void*) * retsize);
int i, j, k;
j = 0;
for (i=0; i<dir->nfiles; i++) {
ret[j++] = ISO_NODE(dir->files[i]);
}
for (i=0; i<dir->nchildren; i++) {
ret[j++] = ISO_NODE(dir->children[i]);
}
qsort(ret, retsize, sizeof(void*), iso_node_cmp_iso);
for (i=0; i<retsize; i++) {
int n_change;
char *name;
/* find the number of consecutive equal names */
j = 1;
while (i+j < retsize && !iso_node_cmp_iso(&ret[i], &ret[i+j]))
j++;
if (j == 1) continue;
/* mangle the names */
n_change = j / 10 + 1;
for (k=0; k<j; k++) {
name = iso_tree_node_get_name_nconst(ret[i+k],
ISO_NAME_ISO);
ecma119_mangle_name(name, n_change, k);
}
/* skip ahead by the number of mangled names */
i += j - 1;
}
return ret;
}
static void ecma119_mangle_name(char *name,
int num_change,
int seq_num)
{
char *dot = strrchr(name, '.');
int dot_pos;
int len = strlen(name);
int ext_len;
if (!dot) dot = name + len;
dot_pos = dot - name;
ext_len = len - dot_pos;
if (dot_pos < num_change) {
/* we need to shift part of the mangling into the extension */
int digits=0, i;
int n_ext;
int nleft = num_change - ext_len;
char *pos = name + len - num_change;
pos = MAX(pos, dot+1);
n_ext = name + len - pos;
/* digits = 10^ext_len */
for (i=0; i<ext_len; i++) {
digits *= 10;
}
ecma119_mangle_name_priv(pos, n_ext, seq_num % digits);
ecma119_mangle_name_priv(dot-nleft, nleft, seq_num / digits);
} else {
ecma119_mangle_name_priv(dot-num_change, num_change, seq_num);
}
}
static void ecma119_mangle_name_priv(char *name,
int num_change,
int seq_num)
{
char fmt[5];
char overwritten;
if (num_change <= 0 || num_change >= 10) {
return;
}
overwritten = name[num_change];
sprintf(fmt, "%%0%1dd", num_change);
sprintf(name, fmt, seq_num);
name[num_change] = overwritten;
}
static struct iso_tree_node **ecma119_sort_joliet(struct iso_tree_dir *dir)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
size_t retsize = dir->nfiles + inf->real_nchildren;
struct iso_tree_node **ret = malloc(retsize * sizeof(void*));
int i, j;
j = 0;
for (i=0; i<dir->nfiles; i++) {
ret[j++] = ISO_NODE(dir->files[i]);
}
for (i=0; i<inf->real_nchildren; i++) {
ret[j++] = ISO_NODE(inf->real_children[i]);
}
qsort(ret, retsize, sizeof(void*), iso_node_cmp_joliet);
return ret;
}
struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset,
int volnum)
{
struct ecma119_write_target *t;
assert(volnum < volset->volset_size);
t = calloc(1, sizeof(struct ecma119_write_target));
t->volset = volset;
t->volnum = volnum;
t->now = time(NULL);
t->block_size = 2048;
iso_tree_sort(TARGET_ROOT(t));
ecma119_alloc_writer_data(t, TARGET_ROOT(t));
ecma119_reorganize_heirarchy(t, TARGET_ROOT(t));
return t;
}
/**
* Write create all necessary SUSP fields for the given directory and
* initialise them. Any SUSP fields that require an offset won't be completed
* yet. Ensure that the size fields in the susp_info structs are correct.
*/
static void ecma119_susp_dir_layout(struct ecma119_write_target *target,
struct iso_tree_dir *dir,
int flags)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
struct susp_info *susp = &inf->susp;
susp->n_susp_fields = 0;
susp->susp_fields = NULL;
if (!target->rockridge) {
/* since Rock Ridge is the only SUSP extension supported,
* no need to continue. */
return;
}
if (dir->depth == 1) {
susp_add_SP(target, dir);
susp_add_ER(target, dir);
} else {
rrip_add_NM(target, ISO_NODE(dir));
rrip_add_TF(target, ISO_NODE(dir));
}
rrip_add_PX_dir(target, dir);
if (inf->real_parent != dir->parent) {
rrip_add_RE(target, ISO_NODE(dir));
rrip_add_PL(target, dir);
}
susp_add_CE(target, ISO_NODE(dir));
}
/**
* Write create all necessary SUSP fields for the given file and
* initialise them. Any SUSP fields that require an offset won't be completed
* yet. Ensure that the size fields in the susp_info structs are correct.
*/
static void ecma119_susp_file_layout(struct ecma119_write_target *target,
struct iso_tree_file *file)
{
struct file_write_info *inf = GET_FILE_INF(file);
if (!target->rockridge) {
return;
}
rrip_add_PX(target, ISO_NODE(file));
rrip_add_NM(target, ISO_NODE(file));
rrip_add_TF(target, ISO_NODE(file));
if (inf->real_me) {
rrip_add_CL(target, ISO_NODE(file));
}
if (S_ISLNK(file->attrib.st_mode)) {
rrip_add_SL(target, ISO_NODE(file));
}
if (S_ISCHR(file->attrib.st_mode) || S_ISBLK(file->attrib.st_mode)) {
rrip_add_PN(target, ISO_NODE(file));
}
susp_add_CE(target, ISO_NODE(file));
}
/**
* Recursively allocate the writer_data pointer for each node in the tree.
* Also, save the current state of the tree in the inf->real_XXX pointers.
*/
static void ecma119_alloc_writer_data(struct ecma119_write_target *target,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf;
int i;
inf = calloc(1, sizeof(struct dir_write_info));
inf->real_parent = dir->parent;
inf->real_nchildren = dir->nchildren;
inf->real_children = malloc(sizeof(void*) * dir->nchildren);
memcpy(inf->real_children, dir->children, dir->nchildren*sizeof(void*));
inf->real_depth = dir->depth;
dir->writer_data = inf;
for (i=0; i<dir->nchildren; i++) {
ecma119_alloc_writer_data(target, dir->children[i]);
}
for (i=0; i<dir->nfiles; i++) {
dir->files[i]->writer_data =
calloc(1, sizeof(struct file_write_info));
}
}
/* ensure that the maximum height of the directory tree is 8, repositioning
* directories as necessary */
static void ecma119_reorganize_heirarchy(struct ecma119_write_target *target,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
struct iso_tree_dir *root = TARGET_ROOT(target);
struct iso_tree_file *file;
struct file_write_info *finf;
/* save this now in case a recursive call modifies this value */
int nchildren = dir->nchildren;
int i;
if (dir == root) {
dir->depth = 1;
} else {
dir->depth = dir->parent->depth + 1;
assert(dir->depth <= 9);
if (dir->depth == 9) {
dir->depth = 2;
dir->parent = root;
root->nchildren++;
root->children = realloc(root->children,
root->nchildren * sizeof(void*));
root->children[root->nchildren-1] = dir;
/* no need to reshuffle the siblings since we know that
* _every_ sibling will also be relocated. */
inf->real_parent->nchildren--;
if (!inf->real_parent->nchildren) {
free(inf->real_parent->children);
inf->real_parent->children = NULL;
}
/* insert a placeholder file for the CL field */
file = iso_tree_add_new_file(inf->real_parent,
dir->name.full);
finf = calloc(1, sizeof(struct file_write_info));
finf->real_me = dir;
file->writer_data = finf;
}
}
for (i=0; i<nchildren; i++) {
ecma119_reorganize_heirarchy(target, dir->children[i]);
}
}
/* set directory sizes recursively. Also fill out the dirlist_len and
* filelist_len fields in the ecma119 writer.
*/
static void ecma119_target_rsize(struct ecma119_write_target *t,
struct iso_tree_dir *dir)
{
int i;
struct dir_write_info *inf = GET_DIR_INF(dir);
struct node_write_info *cinf;
t->dirlist_len++;
/* work out the size of the dirents */
if (dir->depth == 1) {
dir->dirent_len = 34;
} else {
dir->dirent_len = 33 + node_namelen_iso(ISO_NODE(dir));
}
dir->dirent_len += dir->dirent_len % 2;
for (i=0; i<dir->nfiles; i++) {
dir->files[i]->dirent_len = 33 + node_namelen_iso(
ISO_NODE(dir->files[i]));
dir->files[i]->dirent_len += dir->files[i]->dirent_len % 2;
if (dir->files[i]->path && dir->files[i]->attrib.st_size) {
t->filelist_len++;
}
}
/* layout all the susp entries and calculate the total size */
ecma119_susp_dir_layout(t, dir, 0);
inf->len = 34 + inf->self_susp.non_CE_len /* for "." and ".." */
+ 34 + inf->parent_susp.non_CE_len;
inf->susp_len = inf->susp.CE_len
+ inf->self_susp.CE_len
+ inf->parent_susp.CE_len;
for (i=0; i<dir->nfiles; i++) {
ecma119_susp_file_layout(t, dir->files[i]);
cinf = GET_NODE_INF(dir->files[i]);
inf->len += dir->files[i]->dirent_len + cinf->susp.non_CE_len;
inf->susp_len += cinf->susp.CE_len;
}
for (i=0; i<dir->nchildren; i++) {
ecma119_target_rsize(t, dir->children[i]);
cinf = GET_NODE_INF(dir->children[i]);
inf->len += dir->children[i]->dirent_len +cinf->susp.non_CE_len;
inf->susp_len += cinf->susp.CE_len;
}
dir->attrib.st_size = inf->len; /* the actual size of the data is
* inf->len + inf->susp_len because we
* append the CE data to the end of the
* directory. But the ISO volume
* doesn't need to know.
*/
}
static void ecma119_target_rsize_joliet(struct ecma119_write_target *t,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
int i;
inf->joliet_len = 34 + 34; /* for "." and ".." */
for (i=0; i<dir->nfiles; i++) {
/* don't count files that are placeholders for Rock Ridge
* relocated directories */
if (!GET_FILE_INF(dir->files[i])->real_me) {
inf->joliet_len +=
dirent_len_joliet(ISO_NODE(dir->files[i]));
}
}
for (i=0; i<inf->real_nchildren; i++) {
struct iso_tree_node *ch = ISO_NODE(inf->real_children[i]);
inf->joliet_len += dirent_len_joliet(ch);
ecma119_target_rsize_joliet(t, inf->real_children[i]);
}
}
/* set directory positions recursively. Also fill out the dirlist in the
* ecma119_write_target */
static void ecma119_target_dir_layout(struct ecma119_write_target *t,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
int i;
t->dirlist[t->curfile++] = dir;
dir->block = t->curblock;
t->curblock += DIV_UP(inf->len + inf->susp_len, 2048);
for (i=0; i<dir->nchildren; i++) {
ecma119_target_dir_layout(t, dir->children[i]);
}
}
/* same as ecma119_target_dir_layout, but for Joliet. */
static void ecma119_target_dir_layout_joliet(struct ecma119_write_target *t,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
int i;
t->dirlist_joliet[t->curfile++] = dir;
inf->joliet_block = t->curblock;
t->curblock += DIV_UP(inf->joliet_len, 2048);
for (i=0; i<inf->real_nchildren; i++) {
ecma119_target_dir_layout_joliet(t, inf->real_children[i]);
}
}
/* set file positions recursively. Also fill in the filelist in the
* ecma119_write_target */
static void ecma119_target_file_layout(struct ecma119_write_target *t,
struct iso_tree_dir *dir)
{
int i;
for (i=0; i<dir->nfiles; i++) {
if (dir->files[i]->path && dir->files[i]->attrib.st_size) {
t->filelist[t->curfile++] = dir->files[i];
}
dir->files[i]->block = t->curblock;
t->curblock += DIV_UP(dir->files[i]->attrib.st_size, 2048);
}
for (i=0; i<dir->nchildren; i++) {
ecma119_target_file_layout(t, dir->children[i]);
}
}
void ecma119_target_layout(struct ecma119_write_target *t)
{
ecma119_target_rsize(t, TARGET_ROOT(t));
if (t->joliet) {
ecma119_target_rsize_joliet(t, TARGET_ROOT(t));
}
ecma119_setup_path_tables_iso(t);
t->curblock = 16 /* for the system area */
+ 1 /* volume desc */
+ 1; /* volume desc terminator */
if (t->joliet) {
t->curblock++; /* joliet supplementary volume desc */
}
t->l_path_table_pos = t->curblock;
t->curblock += DIV_UP(t->path_table_size, 2048);
t->m_path_table_pos = t->curblock;
t->curblock += DIV_UP(t->path_table_size, 2048);
if (t->joliet) {
ecma119_setup_path_tables_joliet(t);
t->l_path_table_pos_joliet = t->curblock;
t->curblock += DIV_UP(t->path_table_size_joliet, 2048);
t->m_path_table_pos_joliet = t->curblock;
t->curblock += DIV_UP(t->path_table_size_joliet, 2048);
}
t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len);
t->filelist = calloc(1, sizeof(void*) * t->filelist_len);
t->curfile = 0;
ecma119_target_dir_layout(t, TARGET_ROOT(t));
if (t->joliet) {
t->curfile = 0;
t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
ecma119_target_dir_layout_joliet(t, TARGET_ROOT(t));
}
t->curfile = 0;
ecma119_target_file_layout(t, TARGET_ROOT(t));
t->total_size = t->curblock * 2048;
t->vol_space_size = t->curblock;
if (t->rockridge) {
susp_finalize(t, TARGET_ROOT(t));
rrip_finalize(t, TARGET_ROOT(t));
}
iso_tree_print_verbose(TARGET_ROOT(t),
(print_dir_callback)print_dir_info,
(print_file_callback)print_file_info,
t, 0);
/* prepare for writing */
t->curblock = -1;
t->state = ECMA119_WRITE_SYSTEM_AREA;
}
struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
int volnum,
int level,
int flags)
{
struct burn_source *src = calloc(1, sizeof(struct burn_source));
struct ecma119_write_target *t = ecma119_target_new(volset, volnum);
struct iso_volume *vol = volset->volume[volnum];
t->iso_level = level;
t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1:0;
t->joliet = (flags & ECMA119_JOLIET) ? 1:0;
vol->iso_level = t->iso_level;
vol->rockridge = t->rockridge;
vol->joliet = t->joliet;
ecma119_target_layout(t);
src->read = ecma119_read;
src->read_sub = NULL;
src->get_size = ecma119_get_size;
src->free_data = ecma119_free_data;
src->data = t;
return src;
}
static int ecma119_read(struct burn_source *src,
unsigned char *data,
int size)
{
struct ecma119_write_target *t = src->data;
assert( src->read == ecma119_read && src->get_size == ecma119_get_size
&& src->free_data == ecma119_free_data
&& size == 2048);
t->curblock++;
memset(data, 0, size);
switch(t->state) {
case ECMA119_WRITE_SYSTEM_AREA:
ecma119_write_system_area(t, data);
break;
case ECMA119_WRITE_PRI_VOL_DESC:
ecma119_write_privol_desc(t, data);
break;
case ECMA119_WRITE_SUP_VOL_DESC_JOLIET:
ecma119_write_supvol_desc_joliet(t, data);
break;
case ECMA119_WRITE_VOL_DESC_TERMINATOR:
ecma119_write_vol_desc_terminator(t, data);
break;
case ECMA119_WRITE_L_PATH_TABLE:
ecma119_write_path_table(t, data, 0, 0);
break;
case ECMA119_WRITE_M_PATH_TABLE:
ecma119_write_path_table(t, data, 0, 1);
break;
case ECMA119_WRITE_L_PATH_TABLE_JOLIET:
ecma119_write_path_table(t, data, ECMA119_JOLIET, 0);
break;
case ECMA119_WRITE_M_PATH_TABLE_JOLIET:
ecma119_write_path_table(t, data, ECMA119_JOLIET, 1);
break;
case ECMA119_WRITE_DIR_RECORDS:
ecma119_write_dir_records(t, data, 0);
break;
case ECMA119_WRITE_DIR_RECORDS_JOLIET:
ecma119_write_dir_records(t, data, ECMA119_JOLIET);
break;
case ECMA119_WRITE_FILES:
ecma119_write_files(t, data);
break;
case ECMA119_WRITE_DONE:
return 0;
default:
assert(0);
}
return 2048;
}
static int ecma119_get_size(struct burn_source *src)
{
struct ecma119_write_target *t = src->data;
assert( src->read == ecma119_read && src->get_size == ecma119_get_size
&& src->free_data == ecma119_free_data);
return t->total_size;
}
/* free writer_data fields recursively */
static void ecma119_free_writer_data(struct ecma119_write_target *t,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
int i;
susp_free_fields(&inf->susp);
susp_free_fields(&inf->self_susp);
susp_free_fields(&inf->parent_susp);
if (inf->real_children) {
free(inf->real_children);
}
for (i=0; i<dir->nfiles; i++) {
struct file_write_info *finf = GET_FILE_INF(dir->files[i]);
susp_free_fields(&finf->susp);
}
for (i=0; i<dir->nchildren; i++) {
ecma119_free_writer_data(t, dir->children[i]);
}
}
static void ecma119_free_data(struct burn_source *src)
{
struct ecma119_write_target *t = src->data;
assert( src->read == ecma119_read && src->get_size == ecma119_get_size
&& src->free_data == ecma119_free_data);
if (t->filelist) free(t->filelist);
if (t->dirlist) free(t->dirlist);
if (t->pathlist) free(t->pathlist);
if (t->dirlist_joliet) free(t->dirlist_joliet);
if (t->pathlist_joliet) free(t->pathlist_joliet);
ecma119_free_writer_data(t, TARGET_ROOT(t));
free(t);
src->data = NULL;
src->read = NULL;
src->get_size = NULL;
src->free_data = NULL;
}
/*============================================================================*/
/* Writing functions */
/*============================================================================*/
static void ecma119_write_system_area(struct ecma119_write_target *t,
unsigned char *buf)
{
if (t->curblock == 15) {
t->state = ECMA119_WRITE_PRI_VOL_DESC;
}
}
static void ecma119_write_privol_desc(struct ecma119_write_target *t,
unsigned char *buf)
{
struct iso_tree_node *root = ISO_NODE(TARGET_ROOT(t));
struct iso_volume *vol = t->volset->volume[t->volnum];
uint8_t one = 1; /* so that we can take &one */
uint32_t zero = 0;
time_t never = -1;
uint8_t dir_record[34];
ecma119_write_dir_record(t, dir_record, 0, root, RECORD_TYPE_ROOT);
iso_struct_pack(ecma119_privol_fmt, buf,
&one,
"CD001",
&one,
system_id,
vol->volume_id.cstr,
&t->vol_space_size,
&t->volset->volset_size,
&t->volnum,
&t->block_size,
&t->path_table_size,
&t->l_path_table_pos,
&zero,
&t->m_path_table_pos,
&zero,
dir_record,
t->volset->volset_id.cstr,
vol->publisher_id.cstr,
vol->data_preparer_id.cstr,
application_id,
"",
"",
"",
&t->now,
&t->now,
&never,
&t->now,
&one);
t->state = (t->joliet) ? ECMA119_WRITE_SUP_VOL_DESC_JOLIET :
ECMA119_WRITE_VOL_DESC_TERMINATOR;
}
static void ecma119_write_supvol_desc_joliet(struct ecma119_write_target *t,
unsigned char *buf)
{ struct iso_tree_node *root = ISO_NODE(TARGET_ROOT(t));
struct iso_volume *vol = t->volset->volume[t->volnum];
uint8_t one = 1; /* so that we can take &one */
uint8_t two = 2;
uint32_t zero = 0;
time_t never = -1;
uint8_t dir_record[34];
ecma119_write_dir_record(t, dir_record, ECMA119_JOLIET,
root, RECORD_TYPE_ROOT);
iso_struct_pack(ecma119_supvol_joliet_fmt, buf,
&two,
"CD001",
&one,
&zero,
system_id_joliet,
vol->volume_id.jstr,
&t->vol_space_size,
"%/E",
&t->volset->volset_size,
&t->volnum,
&t->block_size,
&t->path_table_size_joliet,
&t->l_path_table_pos_joliet,
&zero,
&t->m_path_table_pos_joliet,
&zero,
dir_record,
t->volset->volset_id.jstr,
vol->publisher_id.jstr,
vol->data_preparer_id.jstr,
application_id_joliet,
&zero,
&zero,
&zero,
&t->now,
&t->now,
&never,
&t->now,
&one);
t->state = ECMA119_WRITE_VOL_DESC_TERMINATOR;
}
static void ecma119_write_vol_desc_terminator(struct ecma119_write_target *t,
unsigned char *buf)
{
buf[0] = 255;
strcpy((char*)&buf[1], "CD001");
buf[6] = 1;
t->state = ECMA119_WRITE_L_PATH_TABLE;
}
/**
* Write a full path table to the buffer (it is assumed to be large enough).
* The path table will be broken into 2048-byte chunks later is necessary.
*/
static void ecma119_write_path_table_full(struct ecma119_write_target *t,
unsigned char *buf,
int flags,
int m_type)
{
void (*write_int)(uint8_t*, uint32_t, int);
struct iso_tree_dir *dir;
const char *name;
int len, parent, i;
size_t off;
node_namelen namelen;
node_getname getname;
off_t root_block;
struct iso_tree_dir **pathlist;
if (flags & ECMA119_JOLIET) {
namelen = node_namelen_joliet;
getname = node_getname_joliet;
pathlist = t->pathlist_joliet;
root_block = GET_DIR_INF(pathlist[0])->joliet_block;
} else {
namelen = node_namelen_iso;
getname = node_getname_iso;
pathlist = t->pathlist;
root_block = pathlist[0]->block;
}
write_int = m_type ? iso_msb : iso_lsb;
/* write the root directory */
buf[0] = 1;
buf[1] = 0;
write_int(&buf[2], root_block, 4);
write_int(&buf[6], 1, 2);
/* write the rest */
off = 10;
for (i=1; i<t->dirlist_len; i++) {
struct iso_tree_dir *dirparent;
off_t block;
dir = pathlist[i];
name = getname(ISO_NODE(dir));
len = namelen(ISO_NODE(dir));
if (flags & ECMA119_JOLIET) {
dirparent = GET_DIR_INF(dir)->real_parent;
block = GET_DIR_INF(dir)->joliet_block;
} else {
dirparent = dir->parent;
block = dir->block;
}
for (parent=0; parent<i; parent++) {
if (pathlist[parent] == dirparent) {
break;
}
}
assert(parent<i);
buf[off + 0] = len;
buf[off + 1] = 0; /* extended attribute length */
write_int(&buf[off + 2], block, 4);
write_int(&buf[off + 6], parent + 1, 2);
memcpy(&buf[off + 8], name, len);
off += 8 + len + len % 2;
}
}
#define SDATA t->state_data.path_table
static void ecma119_write_path_table(struct ecma119_write_target *t,
unsigned char *buf,
int flags,
int m_type)
{
int path_table_size;
if (flags & ECMA119_JOLIET) {
path_table_size = t->path_table_size_joliet;
} else {
path_table_size = t->path_table_size;
}
if (!SDATA.data) {
SDATA.data = calloc(1, ROUND_UP(path_table_size, 2048));
ecma119_write_path_table_full(t, SDATA.data, flags, m_type);
}
memcpy(buf, SDATA.data + SDATA.blocks*2048, 2048);
SDATA.blocks++;
if (SDATA.blocks*2048 >= path_table_size) {
free(SDATA.data);
SDATA.data = NULL;
SDATA.blocks = 0;
if (!t->joliet && m_type) {
t->state = ECMA119_WRITE_DIR_RECORDS;
} else {
t->state++;
}
}
}
#undef SDATA
#define SDATA t->state_data.dir_records
static void ecma119_write_dir_records(struct ecma119_write_target *t,
unsigned char *buf,
int flags)
{
struct iso_tree_dir **dirlist;
if (flags & ECMA119_JOLIET) {
dirlist = t->dirlist_joliet;
} else {
dirlist = t->dirlist;
}
if (!SDATA.data) {
struct iso_tree_dir *dir = dirlist[SDATA.dir++];
struct dir_write_info *inf = GET_DIR_INF(dir);
SDATA.data = ecma119_write_dir(t, flags, dir);
SDATA.pos = 0;
if (flags & ECMA119_JOLIET) {
SDATA.data_len = inf->joliet_len;
} else {
SDATA.data_len = inf->len + inf->susp_len;
}
}
memcpy(buf, SDATA.data + SDATA.pos, 2048);
SDATA.pos += 2048;
if (SDATA.pos >= SDATA.data_len) {
free(SDATA.data);
SDATA.data = 0;
SDATA.pos = 0;
SDATA.data_len = 0;
if (SDATA.dir == t->dirlist_len) {
SDATA.dir = 0;
if (!t->joliet || (flags & ECMA119_JOLIET)) {
t->state = t->filelist_len ? ECMA119_WRITE_FILES
: ECMA119_WRITE_DONE;
} else {
t->state = ECMA119_WRITE_DIR_RECORDS_JOLIET;
}
}
}
}
#undef SDATA
#define SDATA t->state_data.files
static void ecma119_write_files(struct ecma119_write_target *t,
unsigned char *buf)
{
int numread;
if (!SDATA.fd) {
struct iso_tree_file *f = t->filelist[SDATA.file++];
assert(t->curblock == f->block);
SDATA.fd = fopen(f->path, "r");
SDATA.data_len = f->attrib.st_size;
if (!SDATA.fd) {
fprintf(stderr, "Error: couldn't open file %s: %s\n",
f->path, strerror(errno));
}
}
if (SDATA.fd) {
numread = fread(buf, 1, 2048, SDATA.fd);
} else {
numread = t->block_size;
}
if (numread == -1) {
fprintf(stderr, "Error reading file: %s\n", strerror(errno));
return;
}
SDATA.pos += numread;
if (!SDATA.pos || SDATA.pos >= SDATA.data_len) {
fclose(SDATA.fd);
SDATA.data_len = 0;
SDATA.fd = NULL;
SDATA.pos = 0;
if (SDATA.file == t->filelist_len) {
SDATA.file = 0;
t->state = ECMA119_WRITE_DONE;
}
}
}
#undef SDATA
static unsigned char *ecma119_write_dir(struct ecma119_write_target *t,
int flags,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf = GET_DIR_INF(dir);
int len = ROUND_UP(inf->susp_len + inf->len, 2048);
unsigned char *buf;
int i, pos;
struct iso_tree_node *ch;
susp_len slen;
total_dirent_len dlen;
struct iso_tree_node **children;
int nchildren;
assert ( ((flags & ECMA119_JOLIET) && t->curblock == inf->joliet_block)
|| t->curblock == dir->block );
if (flags & ECMA119_JOLIET) {
children = ecma119_sort_joliet(dir);
nchildren = inf->real_nchildren + dir->nfiles;
len = ROUND_UP(inf->joliet_len, 2048);
slen = susp_len_joliet;
dlen = dirent_len_joliet;
} else {
children = ecma119_mangle_names(dir);
nchildren = dir->nchildren + dir->nfiles;
len = ROUND_UP(inf->susp_len + inf->len, 2048);
slen = susp_len_iso;
dlen = dirent_len_iso;
}
buf = calloc(1, len);
pos = 0;
/* write all the dir records */
ecma119_write_dir_record(t, buf+pos, flags, ISO_NODE(dir),
RECORD_TYPE_SELF);
pos += 34 + slen(&inf->self_susp);
ecma119_write_dir_record(t, buf+pos, flags, ISO_NODE(dir),
RECORD_TYPE_PARENT);
pos += 34 + slen(&inf->parent_susp);
for (i=0; i<nchildren; i++) {
ch = children[i];
/* Joliet directories shouldn't list RR directory placeholders.
*/
if (flags & ECMA119_JOLIET
&& S_ISREG( ch->attrib.st_mode )
&& GET_FILE_INF(ch)->real_me) {
continue;
}
ecma119_write_dir_record(t, buf+pos, flags, ch,
RECORD_TYPE_NORMAL);
pos += dlen(ch);
}
if (flags & ECMA119_JOLIET) {
free(children);
return buf;
}
/* write all the SUSP continuation areas */
susp_write_CE(t, &inf->self_susp, buf+pos);
pos += inf->self_susp.CE_len;
susp_write_CE(t, &inf->parent_susp, buf+pos);
pos += inf->parent_susp.CE_len;
for (i=0; i<nchildren; i++) {
struct node_write_info *ninf = GET_NODE_INF(children[i]);
susp_write_CE(t, &ninf->susp, buf+pos);
pos += ninf->susp.CE_len;
}
free(children);
return buf;
}
static void ecma119_write_dir_record(struct ecma119_write_target *t,
unsigned char *buf,
int flags,
struct iso_tree_node *node,
enum RecordType type)
{
if (flags & ECMA119_JOLIET) {
ecma119_write_dir_record_joliet(t, buf, node, type);
} else {
ecma119_write_dir_record_iso(t, buf, node, type);
}
}
static void ecma119_write_dir_record_iso(struct ecma119_write_target *t,
uint8_t *buf,
struct iso_tree_node *node,
enum RecordType type)
{
struct node_write_info *inf = GET_NODE_INF(node);
uint8_t len_dr, len_fi, flags;
uint8_t vol_seq_num = t->volnum;
if (type == RECORD_TYPE_NORMAL) {
len_fi = node_namelen_iso(node);
len_dr = 33 + len_fi + 1 - len_fi%2 + inf->susp.non_CE_len;
flags = (S_ISDIR(node->attrib.st_mode)) ? 2 : 0;
iso_struct_pack(ecma119_dir_record_fmt, buf,
&len_dr,
&zero,
&node->block,
&node->attrib.st_size,
&t->now,
&flags,
&zero,
&zero,
&vol_seq_num,
&len_fi);
iso_struct_pack_long(&buf[33], len_fi, '<', 'B', 0, 0, 0,
node_getname_iso(node));
susp_write(t, &inf->susp, &buf[len_dr - inf->susp.non_CE_len]);
} else if (type == RECORD_TYPE_PARENT && node->parent) {
ecma119_write_dir_record_noname(t, buf, node, type,
node->parent->block,
node->parent->attrib.st_size, 0);
susp_write(t, &GET_DIR_INF(node)->parent_susp, &buf[34]);
} else {
ecma119_write_dir_record_noname(t, buf, node, type,
node->block,
node->attrib.st_size, 0);
if (type != RECORD_TYPE_ROOT) {
susp_write(t, &GET_DIR_INF(node)->self_susp, &buf[34]);
}
}
}
static void ecma119_write_dir_record_joliet(struct ecma119_write_target *t,
uint8_t *buf,
struct iso_tree_node *node,
enum RecordType type)
{
uint8_t len_dr, len_fi, flags;
uint8_t vol_seq_num = t->volnum;
uint32_t block, size;
if (type == RECORD_TYPE_NORMAL) {
if (S_ISDIR(node->attrib.st_mode)) {
block = GET_DIR_INF(node)->joliet_block;
size = GET_DIR_INF(node)->joliet_len;
flags = 2;
} else {
block = node->block;
size = node->attrib.st_size;
flags = 0;
}
len_fi = node_namelen_joliet(node);
len_dr = 34 + len_fi;
iso_struct_pack(ecma119_dir_record_fmt, buf,
&len_dr,
&zero,
&block,
&size,
&t->now,
&flags,
&zero,
&zero,
&vol_seq_num,
&len_fi);
iso_struct_pack_long(&buf[33], len_fi, '<', 'B', 0, 0, 0,
node->name.joliet);
} else if (type == RECORD_TYPE_PARENT && node->parent) {
struct iso_tree_dir *p = GET_DIR_INF(node)->real_parent;
struct dir_write_info *inf = GET_DIR_INF(p);
ecma119_write_dir_record_noname(t, buf, node, type,
inf->joliet_block,
inf->joliet_len,
1);
} else {
struct dir_write_info *inf = GET_DIR_INF(node);
ecma119_write_dir_record_noname(t, buf, node, type,
inf->joliet_block,
inf->joliet_len,
1);
}
}
/* this writes a directory record for a file whose RecordType is not
* RECORD_TYPE_NORMAL. Since this implies that we don't need to write a file
* id, the only difference between Joliet and non-Joliet records is whetheror
* not we write the SUSP fields. */
static void ecma119_write_dir_record_noname(struct ecma119_write_target *t,
uint8_t *buf,
struct iso_tree_node *node,
enum RecordType type,
uint32_t block,
uint32_t size,
int joliet)
{
int file_id;
uint8_t len_dr;
uint8_t flags = 2;
uint8_t len_fi = 1;
uint8_t zero = 0;
struct dir_write_info *inf = GET_DIR_INF(node);
assert( type != RECORD_TYPE_NORMAL );
switch(type) {
case RECORD_TYPE_ROOT:
file_id = 0;
len_dr = 34;
break;
case RECORD_TYPE_SELF:
file_id = 0;
len_dr = 34 + (joliet ? 0 : inf->self_susp.non_CE_len);
break;
case RECORD_TYPE_PARENT:
file_id = 1;
len_dr = 34 + (joliet ? 0 : inf->parent_susp.non_CE_len);
break;
case RECORD_TYPE_NORMAL: /* shut up warning */
assert(0);
}
assert(iso_struct_calcsize(ecma119_dir_record_fmt) == 33);
iso_struct_pack(ecma119_dir_record_fmt, buf,
&len_dr,
&zero,
&block,
&size,
&t->now,
&flags, /* file flags */
&zero,
&zero,
&len_fi, /* vol seq number */
&len_fi); /* len_fi */
buf[33] = file_id;
}
static void ecma119_setup_path_tables_iso(struct ecma119_write_target *t)
{
int i, j, cur;
struct iso_tree_node **children;
t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len);
t->pathlist[0] = TARGET_ROOT(t);
t->path_table_size = 10; /* root directory record */
cur = 1;
for (i=0; i<t->dirlist_len; i++) {
struct iso_tree_dir *dir = t->pathlist[i];
children = ecma119_mangle_names(dir);
for (j=0; j<dir->nchildren + dir->nfiles; j++) {
if (S_ISDIR(children[j]->attrib.st_mode)) {
int len = 8 + node_namelen_iso(children[j]);
t->pathlist[cur++] = ISO_DIR(children[j]);
t->path_table_size += len + len % 2;
}
}
free(children);
}
}
static void ecma119_setup_path_tables_joliet(struct ecma119_write_target *t)
{
int i, j, cur;
struct iso_tree_node **children;
t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
t->pathlist_joliet[0] = TARGET_ROOT(t);
t->path_table_size_joliet = 10; /* root directory record */
cur = 1;
for (i=0; i<t->dirlist_len; i++) {
struct iso_tree_dir *dir = t->pathlist_joliet[i];
struct dir_write_info *inf = GET_DIR_INF(dir);
children = ecma119_sort_joliet(dir);
for (j=0; j<inf->real_nchildren + dir->nfiles; j++) {
if (S_ISDIR(children[j]->attrib.st_mode)) {
int len = 8 + node_namelen_joliet(children[j]);
t->pathlist_joliet[cur++] =
ISO_DIR(children[j]);
t->path_table_size_joliet += len + len % 2;
}
}
}
}