/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ #include "writer.h" #include "util.h" #include "volume.h" #include #include #include #include #include #define APPLICATION_ID "LIBBURN SUITE (C) 2006 M.DANIC/L.BIDDELL/A.NARAYANAN" enum DirType { DIR_TYPE_SELF, DIR_TYPE_PARENT, DIR_TYPE_NORMAL, DIR_TYPE_ROOT /* the dir record in the volume descriptor */ }; enum SUSPType { SUSP_TYPE_SP = 1 << 0, SUSP_TYPE_CE = 1 << 1, SUSP_TYPE_NM = 1 << 2 }; static int get_path_table_size(struct iso_tree_dir **ddir); static void iso_next_state(struct iso_write_target *target); static int iso_source_get_size(struct burn_source *src); static void iso_source_free(struct burn_source *src); static void iso_target_layout(struct iso_write_target *target); static void iso_dir_layout(struct iso_write_target *target, struct iso_tree_dir **dir); static void iso_file_layout(struct iso_write_target *target, struct iso_tree_dir **dir); static int iso_write_system_area(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err); static int iso_write_pri_volume(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err); static int iso_write_volume_terminator(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err); static int write_path_table_record(unsigned char *buffer, struct iso_tree_dir **ddir, int *position, int lsb); static int write_path_table_records(unsigned char *buffer, struct iso_tree_dir **ddir, int level, int *position, int lsb); static int iso_write_path_table(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err, int lsb); static int iso_write_dir_record(struct iso_write_target *target, unsigned char *buffer, struct iso_tree_dir **dir, enum DirType type); static int iso_write_file_record(struct iso_write_target *t, unsigned char *buffer, struct iso_tree_file **ffile, enum DirType type); static void iso_write_file_id(unsigned char *buf, int size, struct iso_tree_file **f); static struct iso_tree_dir **find_dir_at_block(struct iso_tree_dir **ddir, int block); static struct iso_tree_file **find_file_at_block(struct iso_tree_dir **ddir, int block); static void write_child_records(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err, struct iso_tree_dir *dir); static int iso_write_dir_records(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err); static int get_directory_record_length(const char *isoname); static int iso_write_er_area(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err); static int copy_file_to_buffer(FILE * fd, unsigned char *buffer, int length); static int iso_write_files(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err); int get_path_table_size(struct iso_tree_dir **ddir) { const char *isoname; int i, size = 0; struct iso_tree_dir *dir = *ddir; assert(dir); isoname = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); if (isoname) { /* a path table record is 8 bytes + the length of the directory identifier */ size += (8 + strlen(isoname)); } else { /* if iso_tree_dir_get_name is NULL, this is the root dir and will have a path record of 10 bytes */ size += 10; } /* pad the field if the directory identifier is an odd number of bytes */ if (size % 2) ++size; for (i = 0; i < dir->nchildren; i++) size += get_path_table_size(dir->children[i].me); return size; } struct burn_source *iso_source_new(struct iso_volumeset *volumeset, int volume) { struct burn_source *src; struct iso_write_target *t; if (!volumeset) return NULL; if (volume >= volumeset->volumeset_size) return NULL; t = malloc(sizeof(struct iso_write_target)); iso_tree_sort(volumeset->root); t->volset = volumeset; t->volset->refcount++; t->volume = volume; t->now = time(NULL); t->phys_sector_size = 2048; iso_target_layout(t); t->state = ISO_WRITE_BEFORE; iso_next_state(t); /* prepare for the first state */ src = malloc(sizeof(struct burn_source)); src->refcount = 1; src->free_data = iso_source_free; src->read = iso_source_generate; src->read_sub = NULL; src->get_size = iso_source_get_size; src->data = t; return src; } void iso_source_free(struct burn_source *src) { struct iso_write_target *target = src->data; assert(src->read == iso_source_generate && src->read_sub == NULL && src->get_size == iso_source_get_size); iso_volumeset_free(target->volset); free(target); return; } static int get_susp_length(struct RRInfo *info, enum SUSPType fields) { int len = 0; if (fields & SUSP_TYPE_SP) len += 7; if (fields & SUSP_TYPE_CE) len += 28; if (info->px[0] != 0) len += info->px[2]; if (info->tf) len += info->tf[2]; if (info->nm && (fields & SUSP_TYPE_NM)) len += info->nm[2]; return len; } int get_directory_record_length(const char *isoname) { int size; /* size = the length of the filename + 33 bytes (9.1) */ size = 33 + strlen(isoname); /* if size is odd, pad it with a single byte (9.1.12) */ if (size % 2) ++size; return size; } /* fill in the 'logical_block' and 'size' values for each directory */ void iso_dir_layout(struct iso_write_target *target, struct iso_tree_dir **ddir) { int i, length, sectors, rr; const char *name; struct iso_tree_dir *dir = *ddir; rr = iso_volumeset_get_rr(target->volset); /* Each directory record starts with a record pointing to itself and another pointing to its parent; combined, they are 68 bytes. If RR is in use, calculate the susp length as well. */ length = 68; if (rr) { /* if this is the root dir recored, we need to include the length of the CE and SP fields */ if (ddir == target->volset->root) { length += get_susp_length(&dir->rr, SUSP_TYPE_SP | SUSP_TYPE_CE); length += get_susp_length(&dir->rr, 0); } else { length += get_susp_length(&dir->rr, 0); length += get_susp_length(&dir->parent->rr, 0); } } /* calculate the length of this directory */ name = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); if (name) length += get_directory_record_length(name); else length += get_directory_record_length(""); if (rr) length += get_susp_length(&dir->rr, SUSP_TYPE_NM); for (i = 0; i < dir->nfiles; ++i) { name = iso_tree_file_get_name(dir->files[i].me, ISO_FILE_NAME_ISO); length += get_directory_record_length(name); if (rr) length += get_susp_length(&dir->files[i].rr, SUSP_TYPE_NM); } for (i = 0; i < dir->nchildren; ++i) { name = iso_tree_dir_get_name(dir->children[i].me, ISO_FILE_NAME_ISO); length += get_directory_record_length(name); if (rr) length += get_susp_length(&dir->children[i].rr, SUSP_TYPE_NM); } /* dir->size is the number of bytes needed to hold the directory record for each file and subdirectory of 'dir', padded to use up all of the bytes of a physical sector. */ sectors = length / target->phys_sector_size; if (length % target->phys_sector_size) sectors++; dir->size = sectors * target->phys_sector_size; dir->logical_block = target->logical_block; target->logical_block += sectors; /* now calculate the lengths of its children */ for (i = 0; i < dir->nchildren; ++i) { iso_dir_layout(target, dir->children[i].me); } return; } /* fill in the 'logical_block' and 'size' values for each file */ void iso_file_layout(struct iso_write_target *target, struct iso_tree_dir **ddir) { int i, sectors; struct iso_tree_dir *dir = *ddir; for (i = 0; i < dir->nfiles; ++i) { dir->files[i].logical_block = target->logical_block; sectors = dir->files[i].size / target->phys_sector_size; if (dir->files[i].size % target->phys_sector_size) sectors++; /* ensure we move past this logical block if the file was empty */ else if (!dir->files[i].size) sectors++; target->logical_block += sectors; } for (i = 0; i < dir->nchildren; ++i) iso_file_layout(target, dir->children[i].me); return; } void iso_target_layout(struct iso_write_target *target) { /* logical block numbering starts at 1, not 0 */ target->logical_block = 1; /* system area */ target->logical_block += 16; /* primary volume descriptor */ target->logical_block++; /* volume descriptor terminator */ target->logical_block++; target->logical_block_size = 2048; target->path_table_size = get_path_table_size(target->volset->root); /* put a 1-block buffer before each path table */ target->l_path_table_pos = 19; target->logical_block += 2; target->total_size += 2 * target->phys_sector_size; /* put a 1-block buffer before each path table */ target->m_path_table_pos = 21; target->logical_block += 2; iso_dir_layout(target, target->volset->root); /* iso_write_dir_records() puts a 1-block buffer after the directory section, so increment this accordingly */ target->logical_block++; /* the ce area only takes up one block */ target->susp_er_pos = target->logical_block++; iso_file_layout(target, target->volset->root); target->volume_space_size = target->logical_block; target->total_size = target->volume_space_size * target->phys_sector_size; return; } int iso_source_get_size(struct burn_source *src) { struct iso_write_target *target = src->data; assert(src->read == iso_source_generate && src->read_sub == NULL && src->get_size == iso_source_get_size); return target->total_size; } void iso_next_state(struct iso_write_target *target) { switch (++target->state) { case ISO_WRITE_BEFORE: /* this should never occur */ assert(0); break; case ISO_WRITE_SYSTEM_AREA: target->state_data.system_area.sectors = 0; break; case ISO_WRITE_PRI_VOL_DESC: target->total_size = target->logical_block - 1; break; case ISO_WRITE_VOL_DESC_TERMINATOR: break; case ISO_WRITE_L_PATH_TABLE: target->state_data.path_tables.sectors = 0; break; case ISO_WRITE_M_PATH_TABLE: target->state_data.path_tables.sectors = 0; break; case ISO_WRITE_DIR_RECORDS: target->state_data.dir_records.sectors = 0; break; case ISO_WRITE_ER_AREA: break; case ISO_WRITE_FILES: target->state_data.dir_records.sectors = 0; break; case ISO_WRITE_DONE: break; } return; } int iso_source_generate(struct burn_source *src, unsigned char *buffer, int size) { int next = 0; enum burn_source_status err = BURN_SOURCE_OK; struct iso_write_target *target = src->data; assert(src->read == iso_source_generate && src->read_sub == NULL && src->get_size == iso_source_get_size); /* make sure the app didn't fuck up badly. */ if (size != target->phys_sector_size) return BURN_SOURCE_FAILED; /* XXX better code */ /* make sure 'buffer' doesn't have anything in it */ memset(buffer, 0, size); switch (target->state) { case ISO_WRITE_BEFORE: /* this should never occur */ assert(0); case ISO_WRITE_SYSTEM_AREA: next = iso_write_system_area(target, buffer, &err); break; case ISO_WRITE_PRI_VOL_DESC: /* set target->logical_block to the logical block containing the root directory record */ target->logical_block = 23; next = iso_write_pri_volume(target, buffer, &err); break; case ISO_WRITE_VOL_DESC_TERMINATOR: next = iso_write_volume_terminator(target, buffer, &err); break; case ISO_WRITE_L_PATH_TABLE: next = iso_write_path_table(target, buffer, &err, 1); break; case ISO_WRITE_M_PATH_TABLE: next = iso_write_path_table(target, buffer, &err, 0); break; case ISO_WRITE_DIR_RECORDS: next = iso_write_dir_records(target, buffer, &err); break; case ISO_WRITE_ER_AREA: next = iso_write_er_area(target, buffer, &err); break; case ISO_WRITE_FILES: next = iso_write_files(target, buffer, &err); break; case ISO_WRITE_DONE: err = BURN_SOURCE_EOF; break; } if (next) iso_next_state(target); return err; } /* writes 16 sectors of '0' */ int iso_write_system_area(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err) { struct iso_state_system_area *state = &t->state_data.system_area; memset(buffer, 0, t->phys_sector_size); return (++state->sectors == 16); } /* writes the primary volume descriptor */ int iso_write_pri_volume(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err) { /* volume descriptor type (8.4.1) */ buffer[0] = 1; /* standard identifier (8.4.2) */ memcpy(&buffer[1], "CD001", 5); /* volume descriptor version (8.4.3) */ buffer[6] = 1; /* unused field (8.4.4) */ buffer[7] = 0; /* system identifier (8.4.5) */ /* XXX mkisofs puts "LINUX" here */ memset(&buffer[8], ' ', 32); /* volume identifier (8.4.6) */ iso_d_strcpy(&buffer[40], 32, t->volset->volume_id[t->volume]); /* unused field (8.4.7) */ memset(&buffer[72], 0, 8); /* volume space size (8.4.8) */ iso_bb(&buffer[80], t->volume_space_size, 4); /* unused field (8.4.9) */ memset(&buffer[88], 0, 32); /* volume set size (8.4.10) */ iso_bb(&buffer[120], t->volset->volumeset_size, 2); /* volume sequence number (8.4.11) */ iso_bb(&buffer[124], t->volume + 1, 2); /* logical block size (8.4.12) */ iso_bb(&buffer[128], t->logical_block_size, 2); /* path table size (8.4.13) */ iso_bb(&buffer[132], t->path_table_size, 4); /* location of occurance of type l path table (8.4.14) */ iso_lsb(&buffer[140], t->l_path_table_pos, 4); /* location of optional occurance of type l path table (8.4.15) */ iso_lsb(&buffer[144], 0, 4); /* location of occurance of type m path table (8.4.16) */ iso_msb(&buffer[148], t->m_path_table_pos, 4); /* location of optional occurance of type m path table (8.4.17) */ iso_msb(&buffer[152], 0, 4); /* directory record for root directory (8.4.18) */ iso_write_dir_record(t, &buffer[156], t->volset->root, DIR_TYPE_ROOT); /* volume set identifier (8.4.19) */ iso_d_strcpy(&buffer[190], 128, t->volset->volumeset_id); /* publisher identifier (8.4.20) */ iso_a_strcpy(&buffer[318], 128, t->volset->publisher_id); /* data preparer identifier (8.4.21) */ iso_a_strcpy(&buffer[446], 128, t->volset->data_preparer_id); /* application identifier (8.4.22) */ iso_a_strcpy(&buffer[574], 128, APPLICATION_ID); /* copyright file identifier (8.4.23) */ iso_write_file_id(&buffer[702], 37, t->volset->copyright_file); /* abstract file identifier (8.4.24) */ iso_write_file_id(&buffer[739], 37, t->volset->abstract_file); /* bibliographic file identifier (8.4.25) */ iso_write_file_id(&buffer[776], 37, t->volset->bibliographic_file); /* volume creation date and time (8.4.26) */ /* XXX is this different for multisession? */ iso_datetime_17(&buffer[813], t->now); /* volume modification date and time (8.4.27) */ iso_datetime_17(&buffer[830], t->now); /* volume expiration date and time (8.4.28) */ iso_datetime_17(&buffer[847], t->volset->expiration_time); /* volume effective date and time (8.4.29) */ iso_datetime_17(&buffer[864], t->volset->effective_time == (time_t) - 1 ? t->now : t->volset->effective_time); /* file structure version (8.4.30) */ buffer[881] = 1; /* reserved for future standardization (8.4.31) */ buffer[882] = 0; /* application use (8.4.32) */ /* XXX put something in here? does mkisofs? */ memset(&buffer[883], 0, 512); /* reserved for future standardization (8.4.33) */ memset(&buffer[1395], 0, 653); return 1; } void iso_write_file_id(unsigned char *buf, int size, struct iso_tree_file **f) { if (!f) memset(buf, ' ', size); else iso_filecpy(buf, size, iso_tree_file_get_name(f, ISO_FILE_NAME_ISO), (*f)->version); } /* write the contents of the SP System Use Entry */ /* SUSP-112 5.3 */ static void fill_sp_sue(unsigned char *sp) { sp[0] = 'S'; sp[1] = 'P'; sp[2] = 7; sp[3] = 1; sp[4] = 190; sp[5] = 239; sp[6] = 0; return; } /* write the contents of the CE System Use Entry */ /* SUSP-112 5.1 */ static void fill_ce_sue(struct iso_write_target *t, unsigned char *ce) { ce[0] = 'C'; ce[1] = 'E'; ce[2] = 28; ce[3] = 1; iso_bb(&ce[4], 0, 4); iso_bb(&ce[12], t->susp_er_pos, 4); /* the length of the rock-ridge er area is 185, and that's all we * store in the ce area right now */ iso_bb(&ce[20], 185, 4); return; } /* return a string containing the System Use Sharing Protocol information * for 'ddir'. */ static unsigned char* write_susp_info(struct iso_write_target *t, struct RRInfo *rr, enum SUSPType type, int *len) { unsigned char *buffer = NULL; *len = 0; /* check to see if susp is even needed (right now, only the rock-ridge * extensions use it). */ if (iso_volumeset_get_rr(t->volset)) { int char_size, size = 0, offset = 0; int len_sp, len_ce, len_px, len_tf, len_nm; unsigned char *sp, *ce; char_size = sizeof (unsigned char *); len_sp = len_ce = 0; if (type & SUSP_TYPE_SP) { len_sp = 7; /* 7 bytes for the SP System Use Entry */ sp = malloc(char_size * len_sp); fill_sp_sue(sp); size += len_sp; } else { sp = NULL; } if (type & SUSP_TYPE_CE) { len_ce = 28; /* 28 bytes for the CE System Use Entry */ ce = malloc(char_size * len_ce); fill_ce_sue(t, ce); size += len_ce; } else { ce = NULL; } if (rr->px[0] != 0) { /* the 3rd byte of the PX field holds its length */ len_px = rr->px[2]; size += len_px; } else { len_px = 0; } if (rr->tf) { /* the 3rd byte of the TF field holds its length */ len_tf = rr->tf[2]; size += len_tf; } else { len_tf = 0; } if ((type & SUSP_TYPE_NM) && rr->nm) { /* the 3rd byte of the NM field holds its length */ len_nm = rr->nm[2]; size += len_nm; } else { len_nm = 0; } /* fill 'buffer' with the susp info */ buffer = malloc(char_size * (size + 1)); if (sp) { memcpy(buffer + offset, sp, len_sp); offset += len_sp; } if (ce) { memcpy(buffer + offset, ce, len_ce); offset += len_ce; } if (len_px) { memcpy(buffer + offset, rr->px, len_px); offset += len_px; } if (len_tf) { memcpy(buffer + offset, rr->tf, len_tf); offset += len_tf; } if (len_nm) { memcpy(buffer + offset, rr->nm, len_nm); offset += len_nm; } buffer[offset] = '\0'; *len = offset; } return buffer; } int iso_write_dir_record(struct iso_write_target *t, unsigned char *buffer, struct iso_tree_dir **ddir, enum DirType type) { int len_fi, len_susp, sz; const char *fi = NULL; unsigned char *susp; /* the contents, if any, of the "system use sharing protocol" buffer */ struct iso_tree_dir *dir = *ddir; if (type != DIR_TYPE_NORMAL) len_fi = 1; else { assert(dir->parent); fi = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); len_fi = strlen(fi); } /* determine if this is the root directory record */ if ((*t->volset->root == dir) && (type == DIR_TYPE_SELF)) susp = write_susp_info(t, &dir->rr, SUSP_TYPE_SP | SUSP_TYPE_CE, &len_susp); /* determine if this a SELF or PARENT directory record */ else if (type != DIR_TYPE_NORMAL) susp = write_susp_info(t, &dir->rr, 0, &len_susp); /* none of the above == a normal directory record */ else susp = write_susp_info(t, &dir->rr, SUSP_TYPE_NM, &len_susp); sz = 33 + len_fi + len_susp + !(len_fi % 2); /* length of directory record (9.1.1) */ buffer[0] = sz; /* extended attribute record length (9.1.2) */ buffer[1] = 0; /* XXX allow for extended attribute records */ /* location of extent (9.1.3) */ iso_bb(&buffer[2], dir->logical_block, 4); /* data length (9.1.4) */ iso_bb(&buffer[10], dir->size, 4); /* recording date and time (9.1.5) */ iso_datetime_7(&buffer[18], t->now); /* file flags (9.1.6) */ buffer[25] = ISO_FILE_FLAG_DIRECTORY; /* file unit size (9.1.7) */ buffer[26] = 0; /* XXX support this shit? */ /* interleave gap size (9.1.8) */ buffer[27] = 0; /* XXX support this shit? */ /* volume sequence number (9.1.9) */ iso_bb(&buffer[28], dir->volume + 1, 2); /* length of file identifier (9.1.10) */ buffer[32] = len_fi; /* file identifier */ switch (type) { case DIR_TYPE_ROOT: case DIR_TYPE_SELF: buffer[33] = 0; break; case DIR_TYPE_PARENT: buffer[33] = 1; break; case DIR_TYPE_NORMAL: iso_d_strcpy(&buffer[33], len_fi, fi); break; } if (!(len_fi % 2)) buffer[33 + len_fi] = 0; /* system use sharing protocol */ if (susp) { int offset; if (!(len_fi % 2)) offset = 34; else offset = 33; memcpy(&buffer[offset + len_fi], susp, len_susp); } return sz; } /* write the file record as per (9.1) */ int iso_write_file_record(struct iso_write_target *t, unsigned char *buffer, struct iso_tree_file **ffile, enum DirType type) { int len_fi, len_susp; const char *fi = NULL; unsigned char *susp; /* the contents, if any, of the "system use sharing protocol" buffer */ int sz; struct iso_tree_file *file = *ffile; if (type != DIR_TYPE_NORMAL) len_fi = 1; else { fi = iso_tree_file_get_name(ffile, ISO_FILE_NAME_ISO); len_fi = strlen(fi); } susp = write_susp_info(t, &file->rr, SUSP_TYPE_NM, &len_susp); sz = 33 + len_fi + len_susp + !(len_fi % 2); /* length of directory record (9.1.1) */ buffer[0] = sz; /* extended attribute record length (9.1.2) */ buffer[1] = 0; /* XXX allow for extended attribute records */ /* location of extent (9.1.3) */ iso_bb(&buffer[2], file->logical_block, 4); /* data length (9.1.4) */ iso_bb(&buffer[10], file->size, 4); /* recording date and time (9.1.5) */ iso_datetime_7(&buffer[18], t->now); /* file flags (9.1.6) */ buffer[25] = ISO_FILE_FLAG_NORMAL; /* file unit size (9.1.7) */ buffer[26] = 0; /* XXX support this shit? */ /* interleave gap size (9.1.8) */ buffer[27] = 0; /* XXX support this shit? */ /* volume sequence number (9.1.9) */ iso_bb(&buffer[28], file->volume + 1, 2); /* length of file identifier (9.1.10) */ buffer[32] = len_fi; /* file identifier */ switch (type) { case DIR_TYPE_ROOT: case DIR_TYPE_SELF: buffer[33] = 0; break; case DIR_TYPE_PARENT: buffer[33] = 1; break; case DIR_TYPE_NORMAL: memcpy(&buffer[33], fi, len_fi); break; } if (!(len_fi % 2)) buffer[33 + len_fi] = 0; /* system use sharing protocol */ if (susp) { int offset; if (!(len_fi % 2)) offset = 34; else offset = 33; memcpy(&buffer[offset + len_fi], susp, len_susp); } return sz; } /* writes the volume descriptor set terminator */ int iso_write_volume_terminator(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err) { /* volume descriptor type (8.3.1) */ buffer[0] = 255; /* standard identifier (8.3.2) */ memcpy(&buffer[1], "CD001", 5); /* volume descriptor version (8.3.3) */ buffer[6] = 1; /* reserved for future standardization (8.3.4) */ memset(&buffer[7], 0, 2041); return 1; } /* write the path record for 'ddir' into 'buffer' */ int write_path_table_record(unsigned char *buffer, struct iso_tree_dir **ddir, int *position, int lsb) { int len, bytes_written; short parent_position; const char *name; struct iso_tree_dir *dir = *ddir; /* set the position in the path table of this directory */ dir->position = *position; /* increment the position for the next path table record */ *position += 1; if (dir->parent) parent_position = dir->parent->position; else parent_position = 1; name = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); if (name) len = strlen(name); else len = 1; /* the directory identifier length (9.4.1) */ buffer[0] = len; /* the extended attribute record length (9.4.2) */ buffer[1] = 0; /* location of extent (9.4.3) */ if (lsb) iso_lsb(&buffer[2], dir->logical_block, 4); else iso_msb(&buffer[2], dir->logical_block, 4); /* parent directory record (9.4.4) */ if (lsb) iso_lsb(&buffer[6], parent_position, 2); else iso_msb(&buffer[6], parent_position, 2); /* the directory identifier (9.4.5) */ if (name) memcpy(&buffer[8], name, len); else memset(&buffer[8], 0, len); /* padding field (9.4.6) */ if (len % 2) { bytes_written = 9 + len; buffer[bytes_written] = 0; } else bytes_written = 8 + len; return bytes_written; } /* recursive function used to write the path records for each level * of the file system sequentially, i.e. root, then all children of * root, then all grandchildren of root, then all great-grandchildren * of root, etc. */ int write_path_table_records(unsigned char *buffer, struct iso_tree_dir **ddir, int level, int *position, int lsb) { int i, offset; struct iso_tree_dir *dir = *ddir; /* ISO9660 only allows directories to be nested 8-deep */ if (level >= 8) return 0; if (!level) { offset = write_path_table_record(buffer, ddir, position, lsb); } else { offset = 0; for (i = 0; i < dir->nchildren; ++i) { offset += write_path_table_records(buffer + offset, dir->children[i].me, level - 1, position, lsb); } } return offset; } /* writes set of path table records */ int iso_write_path_table(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err, int lsb) { int i, offset, position; struct iso_state_path_tables *state = &t->state_data.path_tables; if (state->sectors) { offset = 0; position = 1; for (i = 0; i < 8; ++i) { offset += write_path_table_records(buffer + offset, t->volset->root, i, &position, lsb); } } else { /* empty buffer sector */ memset(buffer, 0, t->phys_sector_size); } return (++state->sectors == 2); } struct iso_tree_dir **find_dir_at_block(struct iso_tree_dir **ddir, int block) { int i; struct iso_tree_dir *dir = *ddir; struct iso_tree_dir **to_write = NULL; if (dir->logical_block == block) to_write = ddir; for (i = 0; (i < dir->nchildren) && !to_write; ++i) { to_write = find_dir_at_block(dir->children[i].me, block); } return to_write; } struct iso_tree_file **find_file_at_block(struct iso_tree_dir **ddir, int block) { int i; struct iso_tree_dir *dir = *ddir; struct iso_tree_file **to_write = NULL; for (i = 0; (i < dir->nfiles) && !to_write; ++i) { if (dir->files[i].logical_block == block) to_write = dir->files[i].me; } for (i = 0; (i < dir->nchildren) && !to_write; ++i) { to_write = find_file_at_block(dir->children[i].me, block); } return to_write; } /* write the records for children of 'dir' */ void write_child_records(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err, struct iso_tree_dir *dir) { int file_counter, dir_counter, order, offset; file_counter = dir_counter = offset = 0; while ((file_counter < dir->nfiles) || (dir_counter < dir->nchildren)) { /* if there are files and dirs, compare them to figure out which comes 1st alphabetically */ if ((file_counter < dir->nfiles) && (dir_counter < dir->nchildren)) { const char *dirname, *filename; dirname = iso_tree_dir_get_name (dir->children[dir_counter].me, ISO_FILE_NAME_ISO); filename = iso_tree_file_get_name (dir->files[file_counter].me, ISO_FILE_NAME_ISO); order = strcmp(dirname, filename); } else { if (file_counter < dir->nfiles) /* only files are left */ order = 1; else /* only dirs are left */ order = -1; } if (order < 0) { offset += iso_write_dir_record(t, buffer + offset, dir-> children[dir_counter]. me, DIR_TYPE_NORMAL); dir_counter++; } else { offset += iso_write_file_record(t, buffer + offset, dir-> files[file_counter].me, DIR_TYPE_NORMAL); file_counter++; } } return; } /* write out the next directory record */ int iso_write_dir_records(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err) { int finished, offset; struct iso_tree_dir **ddir, *dir; struct iso_state_dir_records *state = &t->state_data.dir_records; finished = 0; /* set to 1 when all directory records have been written */ if (state->sectors++) { /* t->logical_block is the next block to write. find the dir record which belongs there and write it out. if none exists, we're done writing the directory records. */ ddir = find_dir_at_block(t->volset->root, t->logical_block); if (ddir) { /* 1) write the record for this directory 2) write the record for the parent directory 3) write the records for all child files and directories */ dir = *ddir; offset = iso_write_dir_record(t, buffer, ddir, DIR_TYPE_SELF); if (!dir->parent) { /* this is the root directory */ offset += iso_write_dir_record(t, buffer + offset, ddir, DIR_TYPE_PARENT); } else { offset += iso_write_dir_record(t, buffer + offset, dir->parent->me, DIR_TYPE_PARENT); } write_child_records(t, buffer + offset, err, dir); /* dir->size should always be a multiple of t->phys_sector_size */ t->logical_block += (dir->size / t->phys_sector_size); } else { /* we just wrote out 2048 0's, so we still need to increment this */ t->logical_block++; finished = 1; } } return finished; } /* write the 'ER' area of the disc (RRIP-112: 4.3) */ int iso_write_er_area(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err) { if (iso_volumeset_get_rr(t->volset)) { char *text; int len; buffer[0] = 'E'; buffer[1] = 'R'; buffer[2] = 237; buffer[3] = 1; buffer[4] = 10; buffer[5] = 84; buffer[6] = 135; buffer[7] = 1; text = strdup("" "RRIP_1991ATHE ROCK RIDGE INTERCHANGE PROTOCOL " "PROVIDES SUPPORT FOR POSIX FILE SYSTEM " "SEMANTICSPLEASE CONTACT DISC PUBLISHER FOR " "SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN " "PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION"); len = strlen (text); memcpy (buffer + 8, text, len); free (text); /* next logical block... */ t->logical_block++; } return 1; } int copy_file_to_buffer(FILE * fd, unsigned char *buffer, int length) { int read; read = fread(buffer, 1, length, fd); return read; } int iso_write_files(struct iso_write_target *t, unsigned char *buffer, enum burn_source_status *err) { FILE *fd; int finished, sectors; struct iso_tree_file **ffile, *file; struct iso_state_files *state = &t->state_data.files; finished = 0; if (state->sectors) { /* continue writing the file at 't->logical_block' to 'buffer', then incremenet 't->logical_block' to the next file section. */ fd = t->current_fd; file = *(t->current_file); /* copy one sector of 'ffile' into 'buffer' */ state->sectors += copy_file_to_buffer(fd, buffer, t->phys_sector_size); if (feof(fd)) { fclose(fd); state->sectors = 0; sectors = file->size / t->phys_sector_size; if (file->size % t->phys_sector_size) sectors++; t->logical_block += sectors; } } else { /* start writing the file at 't->logical_block' to 'buffer'. if none exists, we are done. */ ffile = find_file_at_block(t->volset->root, t->logical_block); if (ffile) { t->current_file = ffile; file = *ffile; fd = fopen(file->path, "r"); t->current_fd = fd; if (fd) { state->sectors += copy_file_to_buffer(fd, buffer, t-> phys_sector_size); } if (feof(fd)) { fclose(fd); fd = NULL; } if (!fd) { state->sectors = 0; sectors = file->size / t->phys_sector_size; if (file->size % t->phys_sector_size) sectors++; /* ensure we move past this logical block if the file was empty */ else if (!file->size) sectors++; t->logical_block += sectors; } } else finished = 1; } return finished; }