/* 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 #include #include #include #include #include #include #include #include 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" /* 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" /* 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 ("%0d", 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; inon_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; idirent_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; infiles; i++) { ret[j++] = ISO_NODE(dir->files[i]); } for (i=0; inchildren; i++) { ret[j++] = ISO_NODE(dir->children[i]); } qsort(ret, retsize, sizeof(void*), iso_node_cmp_iso); for (i=0; i= 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; infiles; i++) { ret[j++] = ISO_NODE(dir->files[i]); } for (i=0; ireal_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; inchildren; i++) { ecma119_alloc_writer_data(target, dir->children[i]); } for (i=0; infiles; 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; ichildren[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; infiles; 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; infiles; 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; inchildren; 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; infiles; 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; ireal_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; inchildren; 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; ireal_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; infiles; 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; inchildren; 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; infiles; i++) { struct file_write_info *finf = GET_FILE_INF(dir->files[i]); susp_free_fields(&finf->susp); } for (i=0; inchildren; 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; idirlist_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; parentstate_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; iattrib.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; isusp, 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; idirlist_len; i++) { struct iso_tree_dir *dir = t->pathlist[i]; children = ecma119_mangle_names(dir); for (j=0; jnchildren + 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; idirlist_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; jreal_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; } } } }