diff --git a/.bzrignore b/.bzrignore index 382935d..c875645 100644 --- a/.bzrignore +++ b/.bzrignore @@ -33,3 +33,4 @@ demo/catbuffer demo/isoread demo/isocat demo/isomodify +demo/isoms diff --git a/Makefile.am b/Makefile.am index 1cefbc9..65d5cd3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -60,7 +60,8 @@ noinst_PROGRAMS = \ demo/iso \ demo/isoread \ demo/isocat \ - demo/isomodify + demo/isomodify \ + demo/isoms demo_lsl_CPPFLAGS = -Isrc demo_lsl_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) @@ -98,6 +99,10 @@ demo_isomodify_CPPFLAGS = -Isrc demo_isomodify_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) demo_isomodify_SOURCES = demo/iso_modify.c +demo_isoms_CPPFLAGS = -Isrc +demo_isoms_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isoms_SOURCES = demo/iso_ms.c + ## Build unit test diff --git a/demo/iso.c b/demo/iso.c index b16891c..7604012 100644 --- a/demo/iso.c +++ b/demo/iso.c @@ -41,7 +41,10 @@ int main(int argc, char **argv) 0, /* file_mode */ 0, /* uid */ 0, /* gid */ - NULL /* output charset */ + NULL, /* output charset */ + 0, /* appendable */ + 0, /* ms_block */ + NULL /* overwrite */ }; if (argc < 2) { diff --git a/demo/iso_modify.c b/demo/iso_modify.c index dcd6606..689a8e5 100644 --- a/demo/iso_modify.c +++ b/demo/iso_modify.c @@ -41,7 +41,10 @@ int main(int argc, char **argv) 0, /* file_mode */ 0, /* uid */ 0, /* gid */ - NULL /* output charset */ + NULL, /* output charset */ + 0, /* appendable */ + 0, /* ms_block */ + NULL /* overwrite */ }; struct iso_read_opts ropts = { 0, /* block */ diff --git a/demo/iso_ms.c b/demo/iso_ms.c new file mode 100644 index 0000000..fb3f412 --- /dev/null +++ b/demo/iso_ms.c @@ -0,0 +1,122 @@ +/* + * Little program to show how to create a multisession iso image. + */ + +#include "libisofs.h" +#include "libburn/libburn.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +void usage(char **argv) +{ + printf("%s LSS NWA DISC DIRECTORY OUTPUT\n", argv[0]); +} + +int main(int argc, char **argv) +{ + int result; + IsoImage *image; + IsoDataSource *src; + struct burn_source *burn_src; + unsigned char buf[2048]; + FILE *fd; + Ecma119WriteOpts opts = { + 1, /* level */ + 1, /* rockridge */ + 0, /* omit_version_numbers */ + 0, /* allow_deep_paths */ + 1, /* sort files */ + 0, /* replace_dir_mode */ + 0, /* replace_file_mode */ + 0, /* replace_uid */ + 0, /* replace_gid */ + 0, /* dir_mode */ + 0, /* file_mode */ + 0, /* uid */ + 0, /* gid */ + NULL, /* output charset */ + 0, /* appendable */ + 0, /* ms_block */ + NULL /* overwrite */ + }; + struct iso_read_opts ropts = { + 0, /* block */ + 0, /* norock */ + 0, /* nojoliet */ + 0, /* preferjoliet */ + 0, /* uid; */ + 0, /* gid; */ + 0, /* mode */ + "UTF-8" /* input_charset */ + }; + + if (argc < 6) { + usage(argv); + return 1; + } + + fd = fopen(argv[5], "w"); + if (!fd) { + err(1, "error opening output file"); + } + + /* create the data source to accesss previous image */ + result = iso_data_source_new_from_file(argv[3], &src); + if (result < 0) { + printf ("Error creating data source\n"); + return 1; + } + + /* create the image context */ + result = iso_image_new("volume_id", &image); + if (result < 0) { + printf ("Error creating image\n"); + return 1; + } + iso_image_set_msgs_severities(image, "NEVER", "ALL", ""); + iso_tree_set_follow_symlinks(image, 0); + iso_tree_set_ignore_hidden(image, 0); + iso_tree_set_stop_on_error(image, 0); + + /* import previous image */ + ropts.block = atoi(argv[1]); + result = iso_image_import(image, src, &ropts, NULL); + iso_data_source_unref(src); + if (result < 0) { + printf ("Error importing previous session %d\n", result); + return 1; + } + + /* add new dir */ + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[4]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + /* generate a multisession image with new contents */ + opts.ms_block = atoi(argv[2]); + opts.appendable = 1; + result = iso_image_create(image, &opts, &burn_src); + if (result < 0) { + printf ("Cant create image, error %d\n", result); + return 1; + } + + while (burn_src->read(burn_src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + burn_src->free_data(burn_src); + free(burn_src); + + iso_image_unref(image); + return 0; +} diff --git a/src/ecma119.c b/src/ecma119.c index cf3283b..67b2bda 100644 --- a/src/ecma119.c +++ b/src/ecma119.c @@ -754,7 +754,8 @@ int ecma119_image_new(IsoImage *src, Ecma119WriteOpts *opts, Ecma119Image **img) target->file_mode = opts->replace_file_mode == 2 ? opts->file_mode : 0444; target->now = time(NULL); - target->ms_block = 0; + target->ms_block = opts->ms_block; + target->appendable = opts->appendable; /* default to locale charset */ setlocale(LC_CTYPE, ""); diff --git a/src/ecma119.h b/src/ecma119.h index 9f29d18..d0bb207 100644 --- a/src/ecma119.h +++ b/src/ecma119.h @@ -66,6 +66,7 @@ struct ecma119_image char *input_charset; char *output_charset; + unsigned int appendable : 1; uint32_t ms_block; /**< start block for a ms image */ time_t now; /**< Time at which writing began. */ diff --git a/src/filesrc.c b/src/filesrc.c index 1ac53a0..302e6c7 100644 --- a/src/filesrc.c +++ b/src/filesrc.c @@ -113,12 +113,20 @@ static int cmp_by_weight(const void *f1, const void *f2) return g->sort_weight - f->sort_weight; } +static +int is_ms_file(void *arg) +{ + IsoFileSrc *f = (IsoFileSrc *)arg; + return f->prev_img ? 0 : 1; +} + static int filesrc_writer_compute_data_blocks(IsoImageWriter *writer) { size_t i, size; Ecma119Image *t; IsoFileSrc **filelist; + int (*inc_item)(void *); if (writer == NULL) { return ISO_MEM_ERROR; @@ -126,14 +134,19 @@ int filesrc_writer_compute_data_blocks(IsoImageWriter *writer) t = writer->target; + /* on appendable images, ms files shouldn't be included */ + if (t->appendable) { + inc_item = is_ms_file; + } else { + inc_item = NULL; + } + /* store the filesrcs in a array */ - filelist = (IsoFileSrc**)iso_rbtree_to_array(t->files); + filelist = (IsoFileSrc**)iso_rbtree_to_array(t->files, inc_item, &size); if (filelist == NULL) { return ISO_MEM_ERROR; } - size = iso_rbtree_get_size(t->files); - /* sort files by weight, if needed */ if (t->sort_files) { qsort(filelist, size, sizeof(void*), cmp_by_weight); @@ -207,8 +220,9 @@ static int filesrc_writer_write_data(IsoImageWriter *writer) { int res; - size_t i, b, nfiles; + size_t i, b; Ecma119Image *t; + IsoFileSrc *file; IsoFileSrc **filelist; char buffer[BLOCK_SIZE]; @@ -221,9 +235,8 @@ int filesrc_writer_write_data(IsoImageWriter *writer) iso_msg_debug(t->image->messenger, "Writing Files..."); - nfiles = iso_rbtree_get_size(t->files); - for (i = 0; i < nfiles; ++i) { - IsoFileSrc *file = filelist[i]; + i = 0; + while ((file = filelist[i++]) != NULL) { /* * TODO WARNING diff --git a/src/fs_image.c b/src/fs_image.c index a1e7d41..04517cb 100644 --- a/src/fs_image.c +++ b/src/fs_image.c @@ -1475,7 +1475,7 @@ int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts, /* get our ref to IsoDataSource */ data->src = src; iso_data_source_ref(src); - data->open_count = 0; //TODO + data->open_count = 0; /* get an id for the filesystem */ data->id = ++fs_dev_id; diff --git a/src/libisofs.h b/src/libisofs.h index b4d479e..26b6026 100644 --- a/src/libisofs.h +++ b/src/libisofs.h @@ -104,36 +104,69 @@ typedef struct uid_t uid; /** uid to use when replace_uid == 2. */ gid_t gid; /** gid to use when replace_gid == 2. */ - char *output_charset; /**< NULL to use default charset */ - // uint32_t ms_block; - /**< - * Start block for multisession. When this is greater than 0, - * it's suppossed to be the lba of the next writable address - * on disc; all block lba on image will take this into account, - * and files from a previous session will not be written on - * image. This behavior is only suitable for images to be - * appended to a multisession disc. - * When this is 0, no multisession image will be created. If - * some files are taken from a previous image, its contents - * will be written again to the new image. Use this with new - * images or if you plan to modify an existin image. + /** + * Charset for the RR filenames that will be created. + * NULL to use default charset, the locale one. */ - // struct data_source* src; - // /**< - // * When modifying a image, this is the source of the original - // * image, used to read file contents. - // * Otherwise it can be NULL. - // */ - // uint8_t *overwrite; - // /**< - // * When not NULL, it should point to a buffer of at least - // * 64KiB, where libisofs will write the contents that should - // * be written at the beginning of a overwriteable media, to - // * grow the image. - // * You shoudl initialize the buffer either with 0s, or with - // * the contents of the first blocks of the image you're - // * growing. In most cases, 0 is good enought. - // */ + char *output_charset; + + /** + * This flags control the type of the image to create. Libisofs support + * two kind of images: stand-alone and appendable. + * + * A stand-alone image is an image that is valid alone, and that can be + * mounted by its own. This is the kind of image you will want to create + * in most cases. A stand-alone image can be burned in an empty CD or DVD, + * or write to an .iso file for future burning or distribution. + * + * On the other side, an appendable image is not self contained, it refers + * to serveral files that are stored outside the image. Its usage is for + * multisession discs, where you add data in a new session, while the + * previous session data can still be accessed. In those cases, the old + * data is not written again. Instead, the new image refers to it, and thus + * it's only valid when appended to the original. Note that in those cases + * the image will be written after the original, and thus you will want + * to use a ms_block greater than 0. + * + * Note that if you haven't import a previous image (by means of + * iso_image_import()), the image will always be a stand-alone image, as + * there is no previous data to refer to. + */ + unsigned int appendable : 1; + + /** + * Start block of the image. It is supposed to be the lba where the first + * block of the image will be written on disc. All references inside the + * ISO image will take this into account, thus providing a mountable image. + * + * For appendable images, that are written to a new session, you should + * pass here the lba of the next writable address on disc. + * + * In stand alone images this is usually 0. However, you may want to + * provide a different ms_block if you don't plan to burn the image in the + * first session on disc, such as in some CD-Extra disc whether the data + * image is written in a new session after some audio tracks. + */ + uint32_t ms_block; + + /** + * When not NULL, it should point to a buffer of at least 64KiB, where + * libisofs will write the contents that should be written at the beginning + * of a overwriteable media, to grow the image. The growing of an image is + * a way, used by first time in growisofs by Andy Polyakov, to allow the + * appending of new data to non-multisession media, such as DVD+RW, in the + * same way you append a new session to a multisession disc, i.e., without + * need to write again the contents of the previous image. + * + * Note that if you want this kind of image growing, you will also need to + * set appendable to "1" and provide a valid ms_block after the previous + * image. + * + * You should initialize the buffer either with 0s, or with the contents of + * the first blocks of the image you're growing. In most cases, 0 is good + * enought. + */ + uint8_t *overwrite; } Ecma119WriteOpts; typedef struct Iso_Data_Source IsoDataSource; diff --git a/src/util.h b/src/util.h index 6f5c3b5..16537b2 100644 --- a/src/util.h +++ b/src/util.h @@ -193,12 +193,21 @@ size_t iso_rbtree_get_size(IsoRBTree *tree); /** * Get an array view of the elements of the tree. * + * @param include_item + * Function to select which elements to include in the array. It that takes + * a pointer to an element and returns 1 if the element should be included, + * 0 if not. If you want to add all elements to the array, you can pass a + * NULL pointer. + * @param size + * If not null, will be filled with the number of elements in the array, + * without counting the final NULL item. * @return * A sorted array with the contents of the tree, or NULL if there is not * enought memory to allocate the array. You should free(3) the array when * no more needed. Note that the array is NULL-terminated, and thus it * has size + 1 length. */ -void **iso_rbtree_to_array(IsoRBTree *tree); +void **iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *), + size_t *size); #endif /*LIBISO_UTIL_H_*/ diff --git a/src/util_rbtree.c b/src/util_rbtree.c index 55afed0..2c5b7b1 100644 --- a/src/util_rbtree.c +++ b/src/util_rbtree.c @@ -244,28 +244,38 @@ size_t iso_rbtree_get_size(IsoRBTree *tree) } static -int rbtree_to_array_aux(struct iso_rbnode *root, void **array, size_t pos) +size_t rbtree_to_array_aux(struct iso_rbnode *root, void **array, size_t pos, + int (*include_item)(void *)) { if (root == NULL) { return pos; } - pos = rbtree_to_array_aux(root->ch[0], array, pos); - array[pos++] = root->data; - pos = rbtree_to_array_aux(root->ch[1], array, pos); + pos = rbtree_to_array_aux(root->ch[0], array, pos, include_item); + if (include_item == NULL || include_item(root->data)) { + array[pos++] = root->data; + } + pos = rbtree_to_array_aux(root->ch[1], array, pos, include_item); return pos; } /** * Get an array view of the elements of the tree. * + * @param include_item + * Function to select which elements to include in the array. It that takes + * a pointer to an element and returns 1 if the element should be included, + * 0 if not. If you want to add all elements to the array, you can pass a + * NULL pointer. * @return * A sorted array with the contents of the tree, or NULL if there is not * enought memory to allocate the array. You should free(3) the array when * no more needed. Note that the array is NULL-terminated, and thus it * has size + 1 length. */ -void ** iso_rbtree_to_array(IsoRBTree *tree) +void ** iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *), + size_t *size) { + size_t pos; void **array; array = malloc((tree->size + 1) * sizeof(void*)); @@ -274,9 +284,13 @@ void ** iso_rbtree_to_array(IsoRBTree *tree) } /* fill array */ - rbtree_to_array_aux(tree->root, array, 0); - array[tree->size] = NULL; + pos = rbtree_to_array_aux(tree->root, array, 0, include_item); + array[pos] = NULL; + array = realloc(array, (pos + 1) * sizeof(void*)); + if (size) { + *size = pos; + } return array; }