From d10ed353e27d749054ed41046dc35c7068ca7ccc Mon Sep 17 00:00:00 2001 From: Vreixo Formoso Date: Tue, 11 Dec 2007 22:47:04 +0100 Subject: [PATCH] Implement function to recursively add a dir to an iso tree. This commit also to the following changes: - create_node() on builder never frees the IsoFileSource, it is responsability of the caller to free it. - Recursive addition options added to IsoImage (not exposed to public API yet) - create_node() takes care about follow_symlinks - Added little demo program to test it. --- .bzrignore | 1 + Makefile.am | 7 +- demo/tree.c | 105 ++++++++++++++++++++++++ src/builder.c | 12 ++- src/builder.h | 3 +- src/image.c | 10 +++ src/image.h | 64 +++++++++++++++ src/libisofs.h | 16 ++++ src/tree.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 426 insertions(+), 7 deletions(-) create mode 100644 demo/tree.c diff --git a/.bzrignore b/.bzrignore index 97f2a9d..860a9ed 100644 --- a/.bzrignore +++ b/.bzrignore @@ -26,3 +26,4 @@ test/iso test/test demo/lsl demo/cat +demo/tree diff --git a/Makefile.am b/Makefile.am index 40dc6aa..f5fcac9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,7 +35,8 @@ libinclude_HEADERS = \ ## Build demo applications noinst_PROGRAMS = \ demo/lsl \ - demo/cat + demo/cat \ + demo/tree demo_lsl_CPPFLAGS = -Isrc demo_lsl_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) @@ -45,6 +46,10 @@ demo_cat_CPPFLAGS = -Isrc demo_cat_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) demo_cat_SOURCES = demo/cat.c +demo_tree_CPPFLAGS = -Isrc +demo_tree_LDADD = $(src_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_tree_SOURCES = demo/tree.c + ## Build unit test diff --git a/demo/tree.c b/demo/tree.c new file mode 100644 index 0000000..1d4f837 --- /dev/null +++ b/demo/tree.c @@ -0,0 +1,105 @@ +/* + * Little program that reads an existing ISO image and prints its + * contents to stdout. + */ + +#include "libisofs.h" +#include +#include +#include +#include +#include + +static void +print_permissions(mode_t mode) +{ + char perm[10]; + + //TODO suid, sticky... + + perm[9] = '\0'; + perm[8] = mode & S_IXOTH ? 'x' : '-'; + perm[7] = mode & S_IWOTH ? 'w' : '-'; + perm[6] = mode & S_IROTH ? 'r' : '-'; + perm[5] = mode & S_IXGRP ? 'x' : '-'; + perm[4] = mode & S_IWGRP ? 'w' : '-'; + perm[3] = mode & S_IRGRP ? 'r' : '-'; + perm[2] = mode & S_IXUSR ? 'x' : '-'; + perm[1] = mode & S_IWUSR ? 'w' : '-'; + perm[0] = mode & S_IRUSR ? 'r' : '-'; + printf("[%s]",perm); +} + +static void +print_dir(IsoDir *dir, int level) +{ + int i; + IsoDirIter *iter; + IsoNode *node; + char sp[level * 2 + 1]; + + for (i = 0; i < level * 2; i += 2) { + sp[i] = '|'; + sp[i+1] = ' '; + } + + sp[level * 2-1] = '-'; + sp[level * 2] = '\0'; + + iso_dir_get_children(dir, &iter); + while (iso_dir_iter_next(iter, &node) == 1) { + + if (iso_node_get_type(node) == LIBISO_DIR) { + printf("%s+[D] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s\n", iso_node_get_name(node)); + print_dir((IsoDir*)node, level+1); + } else if (iso_node_get_type(node) == LIBISO_FILE) { + printf("%s-[F] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s\n", iso_node_get_name(node) ); + } else if (iso_node_get_type(node) == LIBISO_SYMLINK) { + printf("%s-[L] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s -> %s \n", iso_node_get_name(node), + iso_symlink_get_dest((IsoSymlink*)node) ); + } else { + printf("%s-[C] ", sp); + print_permissions(iso_node_get_permissions(node)); + printf(" %s\n", iso_node_get_name(node) ); + } + } + iso_dir_iter_free(iter); +} + +int main(int argc, char **argv) +{ + int result; + IsoImage *image; + + if (argc != 2) { + printf ("You need to specify a valid path\n"); + return 1; + } + + + 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", ""); + + result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); + if (result < 0) { + printf ("Error adding directory %d\n", result); + return 1; + } + + printf("================= IMAGE =================\n"); + print_dir(iso_image_get_root(image), 0); + printf("\n\n"); + + iso_image_unref(image); + return 0; +} diff --git a/src/builder.c b/src/builder.c index 3e33ea6..d1dd79a 100644 --- a/src/builder.c +++ b/src/builder.c @@ -10,6 +10,7 @@ #include "error.h" #include "node.h" #include "fsource.h" +#include "image.h" #include #include @@ -98,7 +99,11 @@ int default_create_node(IsoNodeBuilder *builder, IsoImage *image, name = src->get_name(src); /* get info about source */ - result = src->lstat(src, &info); + if (image->recOpts->follow_symlinks) { + result = src->stat(src, &info); + } else { + result = src->lstat(src, &info); + } if (result < 0) { return result; } @@ -114,6 +119,8 @@ int default_create_node(IsoNodeBuilder *builder, IsoImage *image, if (result < 0) { return result; } + /* take a ref to the src, as stream has taken our ref */ + iso_file_source_ref(src); file = calloc(1, sizeof(IsoFile)); if (file == NULL) { iso_stream_unref(stream); @@ -134,7 +141,6 @@ int default_create_node(IsoNodeBuilder *builder, IsoImage *image, return ISO_MEM_ERROR; } new->type = LIBISO_DIR; - iso_file_source_unref(src); } break; case S_IFLNK: @@ -154,7 +160,6 @@ int default_create_node(IsoNodeBuilder *builder, IsoImage *image, link->dest = strdup(dest); link->node.type = LIBISO_SYMLINK; new = (IsoNode*) link; - iso_file_source_unref(src); } break; case S_IFSOCK: @@ -171,7 +176,6 @@ int default_create_node(IsoNodeBuilder *builder, IsoImage *image, special->dev = info.st_rdev; special->node.type = LIBISO_SPECIAL; new = (IsoNode*) special; - iso_file_source_unref(src); } break; } diff --git a/src/builder.h b/src/builder.h index b951977..15d0dd4 100644 --- a/src/builder.h +++ b/src/builder.h @@ -48,8 +48,7 @@ struct Iso_Node_Builder * created is determined from the type of the file source. Name, * permissions and other attributes are taken from source file. * - * On sucess, the ref. to src will be owned by node, so you musn't - * unref it. + * Note that the src is never unref, so you need to free it. * * @return * 1 on success, < 0 on error diff --git a/src/image.c b/src/image.c index 5d8a0be..7f0bc6b 100644 --- a/src/image.c +++ b/src/image.c @@ -77,6 +77,16 @@ int iso_image_new(const char *name, IsoImage **image) return res; } img->refcount = 1; + img->recOpts = calloc(1,sizeof(IsoImageRecOpts)); + if (img->recOpts == NULL) { + libiso_msgs_destroy(&img->messenger, 0); + iso_node_builder_unref(img->builder); + iso_filesystem_unref(img->fs); + iso_node_unref((IsoNode*)img->root); + free(img); + return ISO_MEM_ERROR; + } + if (name != NULL) { img->volset_id = strdup(name); img->volume_id = strdup(name); diff --git a/src/image.h b/src/image.h index 6f9e03b..305d862 100644 --- a/src/image.h +++ b/src/image.h @@ -21,6 +21,8 @@ * (Usefull, for example, in Multiple-Document-Interface GUI apps. * [The stuff we have in init belongs really to image!] */ + +typedef struct Iso_Image_Rec_Opts IsoImageRecOpts; struct Iso_Image { @@ -51,6 +53,68 @@ struct Iso_Image { * Default builder to use when adding files to the image tree. */ IsoNodeBuilder *builder; + + /** + * Options for recursive directory addition + */ + IsoImageRecOpts *recOpts; +}; + +/** + * Options for recursive directory addition + */ +struct Iso_Image_Rec_Opts { + + /** + * Whether to follow symlinks or just add them as symlinks + */ + unsigned int follow_symlinks; + + /** + * Whether to skip hidden files + */ + unsigned int ignore_hidden; + + /** + * Whether to stop on an error. Some errors, such as memory errors, + * always cause a stop + */ + unsigned int stop_on_error; + + /** + * Files to exclude + * TODO add wildcard support + */ + char** excludes; + + /** + * if the dir already contains a node with the same name, whether to + * replace or not the old node with the new. + * - 0 not replace + * - 1 replace + * TODO #00006 define more values + * to replace only if both are the same kind of file + * if both are dirs, add contents (and what to do with conflicts?) + */ + int replace; + + /** + * When this is not NULL, it is a pointer to a function that will + * be called just before a file will be added, or when an error occurs. + * You can overwrite some of the above options by returning suitable + * values. + * + * @param action + * 1 file will be added + * 2 file will be skipped + * < 0 error adding file (return 3 to stop, 1 to continue) + * @param flag + * 0 no problem + * 1 file with same name already exists + * @return + * 1 add/continue, 2 skip, 3 stop + */ + int (*report)(IsoFileSource *src, int action, int flag); }; #endif /*LIBISO_IMAGE_H_*/ diff --git a/src/libisofs.h b/src/libisofs.h index f07da83..38e1353 100644 --- a/src/libisofs.h +++ b/src/libisofs.h @@ -638,6 +638,22 @@ int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode, int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path, IsoNode **node); +/** + * Add the contents of a dir to a given directory of the iso tree. + * + * TODO comment Builder and Filesystem related issues when exposing both + * + * @param image + * TODO expose dir rec options and explain that here + * @param parent + * Directory on the image tree where to add the contents of the dir + * @param dir + * Path to a dir in the filesystem + * @return + * number of nodes in parent if success, < 0 otherwise + */ +int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir); + /** * Locate a node by its path on image. * diff --git a/src/tree.c b/src/tree.c index 38c78d9..751e6f7 100644 --- a/src/tree.c +++ b/src/tree.c @@ -16,10 +16,13 @@ #include "image.h" #include "fsource.h" #include "builder.h" +#include "messages.h" #include #include #include +#include +#include /** * Add a new directory to the iso tree. @@ -356,6 +359,218 @@ int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path, } result = iso_tree_add_node_builder(image, parent, file, image->builder, node); + /* free the file */ + iso_file_source_unref(file); + return result; +} + +static +int check_excludes(IsoImage *image, const char *path) +{ + char **exclude; + if (image->recOpts->excludes == NULL) { + return 0; + } + exclude = image->recOpts->excludes; + while (*exclude) { + if (strcmp(*exclude, path) == 0) { + return 1; + } + ++exclude; + } + return 0; +} + +static +int check_hidden(IsoImage *image, const char *name) +{ + return (image->recOpts->ignore_hidden && name[0] == '.'); +} + +/** + * @return + * 1 continue, 0 stop, < 0 error + */ +static +int iso_add_dir_aux(IsoImage *image, IsoDir *parent, IsoFileSource *dir) +{ + int result; + int action; /* 1 add, 2 skip, 3 stop, < 0 error */ + IsoNodeBuilder *builder; + IsoFileSource *file; + IsoNode **pos; + + result = dir->open(dir); + if (result < 0) { + return result; + } + + builder = image->builder; + action = 1; + while ( (result = dir->readdir(dir, &file)) == 1) { + int flag; + char *name; + IsoNode *new; + + { + char msg[PATH_MAX]; + sprintf(msg, "Adding file %s\n", file->get_path(file)); + iso_msg_debug(image, msg); + } + + name = file->get_name(file); + + if (check_excludes(image, file->get_path(file))) { + action = 2; + } else if (check_hidden(image, name)) { + action = 2; + } else { + action = 1; + } + + /* find place where to insert */ + flag = 0; + pos = &(parent->children); + while (*pos != NULL && strcmp((*pos)->name, name) < 0) { + pos = &((*pos)->next); + } + if (*pos != NULL && !strcmp((*pos)->name, name)) { + flag = 1; + if (action == 1 && image->recOpts->replace == 0) { + action = 2; + } + } + + /* ask user if callback has been set */ + if (image->recOpts->report) { + action = image->recOpts->report(file, action, flag); + } + + if (action == 2) { + /* skip file */ + iso_file_source_unref(file); + continue; + } else if (action == 3) { + /* stop */ + iso_file_source_unref(file); + break; + } + + /* ok, file will be added */ + result = builder->create_node(builder, image, file, &new); + if (result < 0) { + + { + char msg[PATH_MAX]; + sprintf(msg, "Error %d when adding file %s\n", result, + file->get_path(file)); + iso_msg_debug(image, msg); + } + + if (image->recOpts->report) { + action = image->recOpts->report(file, result, flag); + } else { + action = image->recOpts->stop_on_error ? 3 : 1; + } + + /* free file */ + iso_file_source_unref(file); + + if (action == 3) { + result = 1; /* prevent error to be passing up */ + break; + } else { + /* TODO check that action is 1!!! */ + continue; + } + } + + /* ok, node has correctly created, we need to add it */ + if (flag) { + /* replace node */ + new->next = (*pos)->next; + (*pos)->parent = NULL; + (*pos)->next = NULL; + iso_node_unref(*pos); + *pos = new; + new->parent = parent; + } else { + /* just add */ + new->next = *pos; + *pos = new; + new->parent = parent; + ++parent->nchildren; + } + + /* finally, if the node is a directory we need to recurse */ + if (new->type == LIBISO_DIR) { + result = iso_add_dir_aux(image, (IsoDir*)new, file); + iso_file_source_unref(file); + if (result < 0) { + /* error */ + if (image->recOpts->stop_on_error) { + action = 3; /* stop */ + result = 1; /* prevent error to be passing up */ + break; + } + } else if (result == 0) { + /* stop */ + action = 3; + break; + } + } else { + iso_file_source_unref(file); + } + } + + if (result < 0) { + // TODO printf message + action = result; + } + + result = dir->close(dir); + if (result < 0) { + return result; + } + if (action < 0) { + return action; /* error */ + } else if (action == 3) { + return 0; /* stop */ + } else { + return 1; /* continue */ + } +} + +int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir) +{ + int result; + struct stat info; + IsoFilesystem *fs; + IsoFileSource *file; + + if (image == NULL || parent == NULL || dir == NULL) { + return ISO_NULL_POINTER; + } + + fs = image->fs; + result = fs->get_by_path(fs, dir, &file); + if (result < 0) { + return result; + } + + /* we also allow dir path to be a symlink to a dir */ + result = file->stat(file, &info); + if (result < 0) { + iso_file_source_unref(file); + return result; + } + + if (!S_ISDIR(info.st_mode)) { + iso_file_source_unref(file); + return ISO_FILE_IS_NOT_DIR; + } + result = iso_add_dir_aux(image, parent, file); + iso_file_source_unref(file); return result; }