/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ #include #include #include #include #include #include #include #include #include #include "ecma119.h" #include "ecma119_tree.h" #include "susp.h" #include "rockridge.h" #include "joliet.h" #include "volume.h" #include "tree.h" #include "util.h" #include "file.h" #include "file_src.h" #include "libisofs.h" #include "libburn/libburn.h" #include "eltorito.h" #include "messages.h" /* burn-source compatible stuff */ static int bs_read(struct burn_source *bs, unsigned char *buf, int size); static off_t bs_get_size(struct burn_source *bs); static void bs_free_data(struct burn_source *bs); typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*); /* return true if the given state is only required for Joliet volumes */ static int is_joliet_state(enum ecma119_write_state); static void next_state(struct ecma119_write_target *t); /* write t->state_data to the buf, one block at a time */ static void write_data_chunk(struct ecma119_write_target *t, uint8_t *buf); /* writing functions. All these functions assume the buf is large enough */ static void write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf); static void write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf); static void write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf); static void write_l_path_table(struct ecma119_write_target *t, uint8_t *buf); static void write_m_path_table(struct ecma119_write_target *t, uint8_t *buf); static void write_one_dir_record(struct ecma119_write_target *t, struct ecma119_tree_node *dir, int file_id, uint8_t *buf); static void write_one_dir(struct ecma119_write_target *t, struct ecma119_tree_node *dir, uint8_t *buf); static void write_dirs(struct ecma119_write_target *t, uint8_t *buf); /* wrapper functions for writing */ static void wr_system_area(struct ecma119_write_target*, uint8_t*); static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*); static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*); static void wr_l_path_table(struct ecma119_write_target*, uint8_t*); static void wr_m_path_table(struct ecma119_write_target*, uint8_t*); static void wr_dir_records(struct ecma119_write_target*, uint8_t*); static void wr_files(struct ecma119_write_target*, uint8_t*); static const write_fn writers[] = { NULL, wr_system_area, wr_pri_vol_desc, el_torito_wr_boot_vol_desc, joliet_wr_sup_vol_desc, wr_vol_desc_term, wr_l_path_table, wr_m_path_table, joliet_wr_l_path_table, joliet_wr_m_path_table, wr_dir_records, joliet_wr_dir_records, el_torito_wr_catalog, wr_files }; /* When a writer is created, we * 1) create an ecma119 tree * 2) add SUSP fields (if necessary) * 3) calculate the size and position of all nodes in the tree * 4) finalize SUSP fields (if necessary) */ static void add_susp_fields_rec(struct ecma119_write_target *t, struct ecma119_tree_node *node) { size_t i; rrip_add_PX(t, node); rrip_add_NM(t, node); rrip_add_TF(t, node); switch (node->type) { case ECMA119_FILE: break; case ECMA119_SYMLINK: rrip_add_SL(t, node); break; case ECMA119_DIR: if (node->info.dir.real_parent != node->parent) { rrip_add_RE(t, node); rrip_add_PL(t, node); } for (i = 0; i < node->info.dir.nchildren; i++) { add_susp_fields_rec(t, node->info.dir.children[i]); } break; case ECMA119_PLACEHOLDER: rrip_add_CL(t, node); break; default: // FIXME support for device blocks by uncommenting this //if (node->iso_self->attrib.st_rdev) // rrip_add_PN(t, node); break; } susp_add_CE(t, node); } static void add_susp_fields(struct ecma119_write_target *t) { susp_add_SP(t, t->root); rrip_add_ER(t, t->root); add_susp_fields_rec(t, t->root); } /** * Fill out the dir.len and dir.CE_len fields for each * ecma119_tree_node that is a directory. Also calculate the total number of * directories and the number of files for which we need to write out data. * (dirlist_len and filelist_len) */ static void calc_dir_size(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { size_t i; size_t newlen; assert(dir->type == ECMA119_DIR); t->dirlist_len++; dir->info.dir.len = 34 + dir->info.dir.self_susp.non_CE_len + 34 + dir->info.dir.parent_susp.non_CE_len; dir->info.dir.CE_len = dir->info.dir.self_susp.CE_len + dir->info.dir.parent_susp.CE_len; for (i = 0; i < dir->info.dir.nchildren; ++i) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; newlen = dir->info.dir.len + ch->dirent_len + ch->susp.non_CE_len; if ((newlen % 2048) < (dir->info.dir.len % 2048)) { dir->info.dir.len = newlen + (2048 - (dir->info.dir.len % 2048)); } else { dir->info.dir.len += ch->dirent_len + ch->susp.non_CE_len; } dir->info.dir.CE_len += ch->susp.CE_len; } t->total_dir_size += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); for (i = 0; i < dir->info.dir.nchildren; i++) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; if (ch->type == ECMA119_DIR) { calc_dir_size(t, ch); } } } /** * Fill out the block field in each ecma119_tree_node that is a directory and * fill out t->dirlist. */ static void calc_dir_pos(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { size_t i; assert(dir->type == ECMA119_DIR); dir->info.dir.block = t->curblock; t->curblock += div_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); t->dirlist[t->curfile++] = dir; for (i = 0; i < dir->info.dir.nchildren; i++) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; if (ch->type == ECMA119_DIR) calc_dir_pos(t, ch); } } static int cmp_file(const void *f1, const void *f2) { struct iso_file *f = *((struct iso_file**)f1); struct iso_file *g = *((struct iso_file**)f2); /* higher weighted first */ return g->sort_weight - f->sort_weight; } /** * Fill out the block field for each file and fill out t->filelist. */ static void calc_file_pos(struct ecma119_write_target *t) { size_t i; assert(t); t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count); for (i = 0; i < FILE_HASH_NODES; ++i) { struct iso_file_hash_node *node; node = t->file_table->table[i]; if (!node) continue; do { struct iso_file *file = node->file; /* * We only need to write the file when. * a) The size is greater than 0. * b) The file is new (not from a previous image) or we * are writting an image not for ms (i.e., we are modifying an * image). */ if ( file->size && (!file->prev_img || !t->ms_block) ) t->filelist[t->curfile++] = file; node = node->next; } while (node); } t->filelist_len = t->curfile; /* sort */ if ( t->sort_files ) qsort(t->filelist, t->filelist_len, sizeof(void*), cmp_file); /* fill block value */ for ( i = 0; i < t->filelist_len; ++i) { struct iso_file *file = t->filelist[i]; file->block = t->curblock; t->curblock += div_up(file->size, t->block_size); } /* reset curfile when we're finished */ t->curfile = 0; } /** * Create a new ecma119_write_target from the given volume number of the * given volume set. * * \pre \p volnum is less than \p volset-\>volset_size. * \post For each node in the tree, writer_data has been allocated. * \post The directory heirarchy has been reorganised to be ecma119-compatible. */ static struct ecma119_write_target* ecma119_target_new(struct iso_volset *volset, struct ecma119_source_opts *opts) { struct ecma119_write_target *t = calloc(1, sizeof(struct ecma119_write_target)); size_t i, j, cur; struct iso_tree_node *iso_root = (struct iso_tree_node*) volset->volume[opts->volnum]->root; t->cache_inodes = opts->no_cache_inodes ? 0 : 1; t->replace_mode = opts->default_mode ? 0 : 1; if ( opts->replace_dir_mode ) t->replace_mode |= 0x02; if ( opts->replace_file_mode ) t->replace_mode |= 0x04; if ( opts->replace_gid ) t->replace_mode |= 0x08; if ( opts->replace_uid ) t->replace_mode |= 0x10; t->dir_mode = opts->dir_mode; t->file_mode = opts->file_mode; t->gid = opts->gid; t->uid = opts->uid; if (opts->input_charset) { t->input_charset = opts->input_charset; } else { /* default to locale charset */ setlocale(LC_CTYPE, ""); t->input_charset = nl_langinfo(CODESET); } t->ouput_charset = opts->ouput_charset ? opts->ouput_charset : "UTF-8"; t->sort_files = opts->sort_files; t->ms_block = opts->ms_block; t->src = opts->src; t->file_table = iso_file_table_new(t->cache_inodes); volset->refcount++; t->iso_level = opts->level; t->block_size = 2048; t->relaxed_constraints = opts->relaxed_constraints; t->rockridge = (opts->flags & ECMA119_ROCKRIDGE) ? 1 : 0; t->joliet = (opts->flags & ECMA119_JOLIET) ? 1 : 0; t->catalog = volset->volume[opts->volnum]->bootcat; t->eltorito = t->catalog ? 1 : 0; t->write_eltorito = opts->copy_eltorito; if (t->eltorito && (!t->ms_block || !t->catalog->proc) ) { /* * For new and modified images we always need to write el-torito. * For ms images, if the boot catalog was newly added, we also need * to write it! */ t->write_eltorito = 1; } /* create the trees */ t->root = ecma119_tree_create(t, iso_root); if (t->joliet) t->joliet_root = joliet_tree_create(t, iso_root); t->volset = volset; t->volnum = opts->volnum; t->now = time(NULL); if (t->rockridge) add_susp_fields(t); calc_dir_size(t, t->root); if (t->joliet) { joliet_calc_dir_size(t, t->joliet_root); t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet); t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet); } t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len); t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len); /* fill out the pathlist */ t->pathlist[0] = t->root; t->path_table_size = 10; /* root directory record */ cur = 1; for (i = 0; i < t->dirlist_len; i++) { struct ecma119_tree_node *dir = t->pathlist[i]; for (j = 0; j < dir->info.dir.nchildren; j++) { struct ecma119_tree_node *ch = dir->info.dir.children[j]; if (ch->type == ECMA119_DIR) { size_t len = 8 + strlen(ch->iso_name); t->pathlist[cur++] = ch; t->path_table_size += len + len % 2; } } } t->curblock = t->ms_block /* nwa for ms, usually 0 */ + 16 /* system area */ + 1 /* primary volume desc */ + 1; /* volume desc terminator */ if (t->eltorito) t->curblock += 1; /* boot record volume descriptor */ if (t->joliet) /* supplementary vol desc */ t->curblock += div_up (2048, t->block_size); t->l_path_table_pos = t->curblock; t->curblock += div_up(t->path_table_size, t->block_size); t->m_path_table_pos = t->curblock; t->curblock += div_up(t->path_table_size, t->block_size); if (t->joliet) { joliet_prepare_path_tables(t); t->l_path_table_pos_joliet = t->curblock; t->curblock += div_up(t->path_table_size_joliet, t->block_size); t->m_path_table_pos_joliet = t->curblock; t->curblock += div_up(t->path_table_size_joliet, t->block_size); } calc_dir_pos(t, t->root); /* reset curfile when we're finished */ t->curfile = 0; if (t->joliet) { joliet_calc_dir_pos(t, t->joliet_root); /* reset curfile when we're finished */ t->curfile = 0; } /* el-torito? */ if (t->eltorito) { if (t->write_eltorito) { /* add catalog block */ t->catblock = t->curblock; t->curblock += div_up(2048, t->block_size); /* add img block */ t->imgblock = t->curblock; t->curblock += div_up(t->catalog->image->node->node.attrib.st_size, t->block_size); } else { assert(t->ms_block); assert(t->catalog->proc); t->catblock = t->catalog->node->loc.block; t->imgblock = t->catalog->image->node->loc.block; } } calc_file_pos(t); if (t->rockridge) { susp_finalize(t, t->root); rrip_finalize(t, t->root); } t->total_size = (t->curblock - t->ms_block) * t->block_size; if (opts->overwrite) { /* * Get a copy of the volume descriptors to be written in a DVD+RW * disc */ uint8_t *buf; /* skip the first 16 blocks (system area) */ buf = opts->overwrite + 16 * t->block_size; /* * In the PVM to be written in the 16th sector of the disc, we * need to specify the full size. */ t->vol_space_size = t->curblock; write_pri_vol_desc(t, buf); buf += t->block_size; if (t->joliet) { joliet_write_sup_vol_desc(t, buf); buf += t->block_size; } if (t->eltorito) { el_torito_write_boot_vol_desc(t, buf); buf += t->block_size; } write_vol_desc_terminator(t, buf); } /* * The volume space size is just the size of the last session, in * case of ms images. */ t->vol_space_size = t->curblock - t->ms_block; /* prepare for writing */ t->curblock = 0; t->state = ECMA119_WRITE_SYSTEM_AREA; t->bytes_read = 2048; return t; } static int is_joliet_state(enum ecma119_write_state state) { return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET || state == ECMA119_WRITE_L_PATH_TABLE_JOLIET || state == ECMA119_WRITE_M_PATH_TABLE_JOLIET || state == ECMA119_WRITE_DIR_RECORDS_JOLIET; } static int is_eltorito_state(enum ecma119_write_state state) { return state == ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC || state == ECMA119_WRITE_ELTORITO_CATALOG; } static void next_state(struct ecma119_write_target *t) { char msg[42]; t->state++; while ( (!t->joliet && is_joliet_state(t->state)) ||(!t->eltorito && is_eltorito_state(t->state)) ||(!t->write_eltorito && t->state == ECMA119_WRITE_ELTORITO_CATALOG) ) t->state++; sprintf(msg, "Now in state %d, curblock=%d.", t->state, t->curblock); iso_msg_debug(msg); } static void wr_system_area(struct ecma119_write_target *t, uint8_t *buf) { memset(buf, 0, t->block_size); if (t->curblock == 15) { next_state(t); } } static void wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf); } static void wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf); } static void wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf); } static void wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf); } static void wr_dir_records(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf); } static void wr_files(struct ecma119_write_target *t, uint8_t *buf) { int res; struct state_files *f_st = &t->state_files; struct iso_file *f = t->filelist[f_st->file]; if (!f_st->src) { if (!f->prev_img) { char msg[PATH_MAX + 14]; sprintf(msg, "Writing file %s", f->path); iso_msg_debug(msg); } f_st->src = f->src; if ( f->src->open(f->src) <= 0) { //FIXME instead of exit, just print a msg error and //skip file (what about reading from /dev/zero? instead) /* can only happen with new files */ err(1, "couldn't open %s for reading", f->path); } assert(t->curblock + t->ms_block == f->block); } res = f_st->src->read_block(f_st->src, buf); if (res == 0) { /* we have read the expected bytes from file */ f_st->src->close(f_st->src); f_st->src = NULL; f_st->file++; if (f_st->file >= t->filelist_len) next_state(t); } } static void write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf; struct iso_volume *volume = t->volset->volume[t->volnum]; char *vol_id = str2d_char(volume->volume_id, t->input_charset); char *pub_id = str2a_char(volume->publisher_id, t->input_charset); char *data_id = str2a_char(volume->data_preparer_id, t->input_charset); char *volset_id = str2d_char(t->volset->volset_id, t->input_charset); char *system_id = str2a_char(volume->system_id, t->input_charset); char *application_id = str2a_char(volume->application_id, t->input_charset); char *copyright_file_id = str2d_char(volume->copyright_file_id, t->input_charset); char *abstract_file_id = str2d_char(volume->abstract_file_id, t->input_charset); char *biblio_file_id = str2d_char(volume->biblio_file_id, t->input_charset); vol->vol_desc_type[0] = 1; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; if (system_id) strncpy((char*)vol->system_id, system_id, 32); else /* put linux by default? */ memcpy(vol->system_id, "LINUX", 5); if (vol_id) strncpy((char*)vol->volume_id, vol_id, 32); iso_bb(vol->vol_space_size, t->vol_space_size, 4); iso_bb(vol->vol_set_size, t->volset->volset_size, 2); iso_bb(vol->vol_seq_number, t->volnum + 1, 2); iso_bb(vol->block_size, t->block_size, 2); iso_bb(vol->path_table_size, t->path_table_size, 4); iso_lsb(vol->l_path_table_pos, t->l_path_table_pos, 4); iso_msb(vol->m_path_table_pos, t->m_path_table_pos, 4); write_one_dir_record(t, t->root, 3, vol->root_dir_record); /* mmm, why not check for null? */ strncpy((char*)vol->vol_set_id, volset_id, 128); strncpy((char*)vol->publisher_id, pub_id, 128); strncpy((char*)vol->data_prep_id, data_id, 128); if (application_id) strncpy((char*)vol->application_id, application_id, 128); if (copyright_file_id) strncpy((char*)vol->copyright_file_id, copyright_file_id, 37); if (abstract_file_id) strncpy((char*)vol->abstract_file_id, abstract_file_id, 37); if (biblio_file_id) strncpy((char*)vol->bibliographic_file_id, biblio_file_id, 37); iso_datetime_17(vol->vol_creation_time, t->now); iso_datetime_17(vol->vol_modification_time, t->now); iso_datetime_17(vol->vol_effective_time, t->now); vol->file_structure_version[0] = 1; free(vol_id); free(volset_id); free(pub_id); free(data_id); free(system_id); free(application_id); free(copyright_file_id); free(abstract_file_id); free(biblio_file_id); } static void write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf) { struct ecma119_vol_desc_terminator *vol = (struct ecma119_vol_desc_terminator*) buf; vol->vol_desc_type[0] = 255; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; } static void write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf) { void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb : iso_msb; size_t i; struct ecma119_path_table_record *rec; struct ecma119_tree_node *dir; int parent = 0; for (i = 0; i < t->dirlist_len; i++) { dir = t->pathlist[i]; assert(dir->type == ECMA119_DIR); while ((i) && t->pathlist[parent] != dir->parent) parent++; assert(parent < i || i == 0); rec = (struct ecma119_path_table_record*) buf; rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1; rec->len_xa[0] = 0; write_int(rec->block, dir->info.dir.block, 4); write_int(rec->parent, parent + 1, 2); if (dir->parent) memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]); buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2); } } static void write_l_path_table(struct ecma119_write_target *t, uint8_t *buf) { write_path_table(t, 1, buf); } static void write_m_path_table(struct ecma119_write_target *t, uint8_t *buf) { write_path_table(t, 0, buf); } /* if file_id is >= 0, we use it instead of the filename. As a magic number, * file_id == 3 means that we are writing the root directory record (in order * to distinguish it from the "." entry in the root directory) */ static void write_one_dir_record(struct ecma119_write_target *t, struct ecma119_tree_node *node, int file_id, uint8_t *buf) { uint32_t len; uint32_t block; uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->iso_name); uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->iso_name; struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; if (node->type == ECMA119_DIR) { len = node->info.dir.len; block = node->info.dir.block; } else if (node->type == ECMA119_FILE) { len = node->info.file->size; block = node->info.file->block; } else if (node->type == ECMA119_BOOT) { assert(t->eltorito); if (node->info.boot_img) { block = t->imgblock; len = t->catalog->image->node->node.attrib.st_size; } else { /* we always assume 2048 as catalog len */ block = t->catblock; len = 2048; } } else { /* for nodes other than files and dirs, we set both len and block to 0 */ len = 0; block = 0; } /* we don't write out susp fields for the root node */ if (t->rockridge) { if (file_id == 0) { assert(node->type == ECMA119_DIR); susp_write(t, &node->info.dir.self_susp, &buf[len_dr]); len_dr += node->info.dir.self_susp.non_CE_len; } else if (file_id == 1) { assert(node->type == ECMA119_DIR); susp_write(t, &node->info.dir.parent_susp, &buf[len_dr]); len_dr += node->info.dir.parent_susp.non_CE_len; } else if (file_id < 0) { susp_write(t, &node->susp, &buf[len_dr]); len_dr += node->susp.non_CE_len; } } if (file_id == 1 && node->parent) node = node->parent; rec->len_dr[0] = len_dr; iso_bb(rec->block, block, 4); iso_bb(rec->length, len, 4); iso_datetime_7(rec->recording_time, t->now); rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0; iso_bb(rec->vol_seq_number, t->volnum + 1, 2); rec->len_fi[0] = len_fi; memcpy(rec->file_id, name, len_fi); } static void write_one_dir(struct ecma119_write_target *t, struct ecma119_tree_node *dir, uint8_t *buf) { size_t i; int j; size_t len; uint8_t *orig_buf = buf; uint8_t *prior_buf = buf; assert(dir->type == ECMA119_DIR); /* write the "." and ".." entries first */ write_one_dir_record(t, dir, 0, buf); buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; write_one_dir_record(t, dir, 1, buf); buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; for (i = 0; i < dir->info.dir.nchildren; i++) { write_one_dir_record(t, dir->info.dir.children[i], -1, buf); len = ((struct ecma119_dir_record*) buf)->len_dr[0]; if ((buf + len - prior_buf) >= 2048) { for (j = len - 1; j >= 0; j--) { prior_buf[2048 + j] = buf[j]; buf[j] = 0; } prior_buf += 2048; buf = prior_buf + len; } else { buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; } } /* write the susp continuation areas */ if (t->rockridge) { susp_write_CE(t, &dir->info.dir.self_susp, buf); buf += dir->info.dir.self_susp.CE_len; susp_write_CE(t, &dir->info.dir.parent_susp, buf); buf += dir->info.dir.parent_susp.CE_len; for (i = 0; i < dir->info.dir.nchildren; i++) { susp_write_CE(t, &dir->info.dir.children[i]->susp, buf); buf += dir->info.dir.children[i]->susp.CE_len; } } assert (buf - orig_buf == dir->info.dir.len + dir->info.dir.CE_len); } static void write_dirs(struct ecma119_write_target *t, uint8_t *buf) { size_t i; struct ecma119_tree_node *dir; for (i = 0; i < t->dirlist_len; i++) { dir = t->dirlist[i]; write_one_dir(t, dir, buf); buf += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); } } void ecma119_start_chunking(struct ecma119_write_target *t, write_fn writer, off_t data_size, uint8_t *buf) { if (data_size != t->state_data_size) { data_size = round_up(data_size, t->block_size); t->state_data = realloc(t->state_data, data_size); t->state_data_size = data_size; } memset(t->state_data, 0, t->state_data_size); t->state_data_off = 0; t->state_data_valid = 1; writer(t, t->state_data); write_data_chunk(t, buf); } static void write_data_chunk(struct ecma119_write_target *t, uint8_t *buf) { memcpy(buf, t->state_data + t->state_data_off, t->block_size); t->state_data_off += t->block_size; if (t->state_data_off >= t->state_data_size) { assert (t->state_data_off <= t->state_data_size); t->state_data_valid = 0; next_state(t); } } static int bs_read_block(struct burn_source *bs) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; if (t->curblock >= t->vol_space_size) { /* total_size could be setted by libburn */ if ( t->curblock < (t->total_size / (off_t) t->block_size) ) { /* we pad the image */ memset(t->buffer, 0, t->block_size); t->curblock++; return t->block_size; } return 0; } if (t->state_data_valid) write_data_chunk(t, t->buffer); else writers[t->state](t, t->buffer); t->curblock++; return t->block_size; } static int bs_read(struct burn_source *bs, unsigned char *buf, int size) { int ret = 0,summed_ret = 0; struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; /* make safe against partial buffer returns */ while (1) { if (t->bytes_read == 2048) { /* we need to read next block */ t->bytes_read = 0; ret = bs_read_block(bs); if (ret <= 0) return ret; } int bytes_to_read = 2048 - t->bytes_read; if (summed_ret + bytes_to_read > size) { bytes_to_read = size - summed_ret; } memcpy(buf + summed_ret, t->buffer, bytes_to_read); t->bytes_read += bytes_to_read; summed_ret += bytes_to_read; if (summed_ret >= size) break; } return summed_ret; } static off_t bs_get_size(struct burn_source *bs) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; return t->total_size; } static void bs_free_data(struct burn_source *bs) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; ecma119_tree_free(t->root); iso_file_table_clear(t->file_table); free(t->dirlist); free(t->pathlist); free(t->dirlist_joliet); free(t->pathlist_joliet); free(t->filelist); free(t->state_data); if (t->joliet) joliet_tree_free(t->joliet_root); // TODO is this needed? if (t->state_files.src) t->state_files.src->close(t->state_files.src); } int bs_set_size(struct burn_source *bs, off_t size) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; t->total_size = size; return 1; } struct burn_source *iso_source_new_ecma119(struct iso_volset *volset, struct ecma119_source_opts *opts) { struct burn_source *ret = calloc(1, sizeof(struct burn_source)); ret->refcount = 1; ret->read = bs_read; ret->get_size = bs_get_size; ret->set_size = bs_set_size; ret->free_data = bs_free_data; ret->data = ecma119_target_new(volset, opts); return ret; }