1507 lines
39 KiB
C
1507 lines
39 KiB
C
|
/* 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|