/* vim: set noet ts=8 sts=8 sw=8 : */ /** * \file tree.c * * Implement filesystem trees. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tree.h" #include "exclude.h" static void set_default_stat(struct stat *s) { time_t now = time(NULL); memset(s, 0, sizeof(struct stat)); s->st_mode = 0555; s->st_atime = s->st_mtime = s->st_ctime = now; } void iso_tree_add_child(struct iso_tree_node_dir *parent, struct iso_tree_node *child) { assert( parent && child); parent->nchildren++; parent->children = realloc(parent->children, parent->nchildren * sizeof(void*)); parent->children[parent->nchildren-1] = child; child->parent = parent; } struct iso_tree_node_dir* iso_tree_new_root() { struct iso_tree_node_dir *root; root = calloc(1, sizeof(struct iso_tree_node_dir)); set_default_stat(&root->node.attrib); root->node.attrib.st_mode = S_IFDIR | 0777; root->node.type = LIBISO_NODE_DIR; return root; } struct iso_tree_node* iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path) { struct iso_tree_node_file *f; char *p; struct stat st; assert( parent && path); if (lstat(path, &st) == -1) { libisofs_errno = NO_FILE; return NULL; } if ( !S_ISREG(st.st_mode) ) { libisofs_errno = UNEXPECTED_FILE_TYPE; return NULL; } if ( access(path, R_OK) ) { libisofs_errno = NO_READ_ACCESS; return NULL; } f = calloc(1, sizeof(struct iso_tree_node_file)); /* fill fields */ f->node.attrib = st; f->path = strdup(path); f->node.type = LIBISO_NODE_FILE; p = strdup(path); /* because basename() might modify its arg */ f->node.name = strdup( basename(p) ); free(p); /* add to parent (this also sets f->node->parent) */ iso_tree_add_child(parent, (struct iso_tree_node*) f); return (struct iso_tree_node*) f; } struct iso_tree_node* iso_tree_add_symlink(struct iso_tree_node_dir *parent, const char *name, const char *dest) { struct iso_tree_node_symlink *link; assert( parent && name && dest); link = calloc(1, sizeof(struct iso_tree_node_symlink)); /* fill fields */ set_default_stat(&link->node.attrib); link->node.attrib.st_mode |= S_IFLNK;//TODO Not needed link->node.name = strdup(name); link->node.type = LIBISO_NODE_SYMLINK; link->dest = strdup(dest); /* add to parent (this also sets link->node->parent) */ iso_tree_add_child(parent, (struct iso_tree_node*) link); return (struct iso_tree_node*) link; } struct iso_tree_node_dir* iso_tree_add_dir(struct iso_tree_node_dir *parent, const char *name) { struct iso_tree_node_dir *dir; assert( parent && name ); dir = calloc(1, sizeof(struct iso_tree_node_dir)); dir->node.attrib = parent->node.attrib; dir->node.type = LIBISO_NODE_DIR; dir->node.name = strdup(name); iso_tree_add_child(parent, (struct iso_tree_node*) dir); return dir; } void iso_tree_node_set_name(struct iso_tree_node *node, const char *name) { free(node->name); node->name = strdup(name); } void iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs) { assert(node); node->hide_flags = hide_attrs; } void iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid) { assert(node); node->attrib.st_gid = gid; } void iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid) { assert(node); node->attrib.st_uid = uid; } void iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode) { assert(node); node->attrib.st_mode = (node->attrib.st_mode & S_IFMT) | (mode & ~S_IFMT); } void iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w) { assert(node); if ( ISO_ISDIR(node) ) { size_t i; struct iso_tree_node_dir *dir; dir = (struct iso_tree_node_dir *) node; for (i=0; i < dir->nchildren; i++) { iso_tree_node_set_sort_weight(dir->children[i], w); } free(dir->children); } else if ( ISO_ISREG(node) ) { struct iso_tree_node_file *file; file = (struct iso_tree_node_file *) node; file->sort_weight = w; } } struct iso_tree_node* iso_tree_add_node(struct iso_tree_node_dir *parent, const char *path) { struct stat st; struct iso_tree_node *node; assert( parent && path); if (lstat(path, &st) == -1) { libisofs_errno = NO_FILE; return NULL; } if ( access(path, R_OK) ) { libisofs_errno = NO_READ_ACCESS; return NULL; } switch (st.st_mode & S_IFMT) { case S_IFREG: /* regular file */ node = iso_tree_add_file(parent, path); break; case S_IFLNK: /* symlink */ { char dest[PATH_MAX]; char *p; int n; n = readlink(path, dest, PATH_MAX); if ( n == -1 ) { libisofs_errno = INTERNAL_ERROR; return NULL; } dest[n] = '\0'; p = strdup(path); /* because basename() might modify its arg */ node = iso_tree_add_symlink(parent, basename(p), dest); free(p); node->attrib = st; } break; case S_IFDIR: /* directory */ { char *p; p = strdup(path); /* because basename() might modify its arg */ node = (struct iso_tree_node*) iso_tree_add_dir(parent, basename(p)); free(p); node->attrib = st; } break; default: libisofs_errno = UNEXPECTED_FILE_TYPE; node = NULL; break; } return node; } void iso_tree_free(struct iso_tree_node *root) { if ( ISO_ISDIR(root) ) { size_t i; struct iso_tree_node_dir *dir; dir = (struct iso_tree_node_dir *) root; for (i=0; i < dir->nchildren; i++) { iso_tree_free(dir->children[i]); } free(dir->children); } else if ( ISO_ISLNK(root) ) { struct iso_tree_node_symlink *link; link = (struct iso_tree_node_symlink *) root; free(link->dest); } else if ( ISO_ISREG(root) ) { struct iso_tree_node_file *file; file = (struct iso_tree_node_file *) root; free(file->path); } free(root->name); free(root); } static void iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path, struct iso_tree_radd_dir_behavior *behavior, struct iso_hash_table *excludes) { struct iso_tree_node *new; DIR *dir; struct dirent *ent; dir = opendir(path); if (!dir) { warn("couldn't open directory %s: %s\n", path, strerror(errno)); return; } while ((ent = readdir(dir))) { char child[strlen(ent->d_name) + strlen(path) + 2]; if (behavior->stop_on_error & behavior->error) break; if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; //TODO check if path already finished in '/' sprintf(child, "%s/%s", path, ent->d_name); /* see if this child is excluded. */ if (iso_exclude_lookup(excludes, child)) continue; new = iso_tree_add_node(parent, child); if (!new || !ISO_ISDIR(new)) { if (!new) behavior->error = 1; continue; } iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child, behavior, excludes); } closedir(dir); return; } void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, struct iso_tree_radd_dir_behavior *behavior) { struct iso_tree_node_dir *dir; struct iso_hash_table table = { {0,}, 0}; assert ( parent && path ); behavior->error = 0; /* initialize exclude hash_table */ if ( behavior->excludes ) { char *exclude; int i = 0; while ( (exclude = behavior->excludes[i++]) ) { iso_exclude_add_path(&table, exclude); } } /* recurse into dir */ iso_tree_radd_dir_aux(parent, path, behavior, &table); /* clear hashtable */ iso_exclude_empty(&table); return dir; } void iso_tree_print(const struct iso_tree_node *root, int spaces) { char sp[spaces+1]; memset(sp, ' ', spaces); sp[spaces] = '\0'; printf("%s%s\n", sp, root->name); if ( ISO_ISDIR(root) ) { size_t i; struct iso_tree_node_dir *dir; dir = (struct iso_tree_node_dir *) root; for (i=0; i < dir->nchildren; i++) { iso_tree_print(dir->children[i], spaces+2); } } } void iso_tree_print_verbose(const struct iso_tree_node *root, print_dir_callback dir, print_file_callback file, void *callback_data, int spaces) { (ISO_ISDIR(root) ? dir : file) (root, callback_data, spaces); if ( ISO_ISDIR(root) ) { size_t i; struct iso_tree_node_dir *dir_node; dir_node = (struct iso_tree_node_dir *) root; for (i=0; i < dir_node->nchildren; i++) { iso_tree_print_verbose(dir_node->children[i], dir, file, callback_data, spaces+2); } } }