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.
This commit is contained in:
Vreixo Formoso 2007-12-11 22:47:04 +01:00
parent 0306bb5daf
commit d10ed353e2
9 changed files with 426 additions and 7 deletions

View File

@ -26,3 +26,4 @@ test/iso
test/test
demo/lsl
demo/cat
demo/tree

View File

@ -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

105
demo/tree.c Normal file
View File

@ -0,0 +1,105 @@
/*
* Little program that reads an existing ISO image and prints its
* contents to stdout.
*/
#include "libisofs.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
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;
}

View File

@ -10,6 +10,7 @@
#include "error.h"
#include "node.h"
#include "fsource.h"
#include "image.h"
#include <stdlib.h>
#include <string.h>
@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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_*/

View File

@ -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.
*

View File

@ -16,10 +16,13 @@
#include "image.h"
#include "fsource.h"
#include "builder.h"
#include "messages.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <stdio.h>
/**
* 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;
}