libisofs-legacy/libisofs/tree.c

399 lines
8.3 KiB
C
Raw Normal View History

2006-08-15 20:37:04 +00:00
/* vim: set noet ts=8 sts=8 sw=8 : */
2006-08-24 19:23:37 +00:00
/**
* \file tree.c
*
* Implement filesystem trees.
*/
#include <assert.h>
2006-08-15 20:37:04 +00:00
#include <stdlib.h>
#include <string.h>
#include <time.h>
2006-08-24 19:23:37 +00:00
#include <dirent.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
2006-08-15 20:37:04 +00:00
#include <errno.h>
2006-08-24 19:23:37 +00:00
#include <err.h>
#include <stdio.h>
2006-08-15 20:37:04 +00:00
#include "tree.h"
#include "exclude.h"
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
static void
set_default_stat(struct stat *s)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
time_t now = time(NULL);
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
memset(s, 0, sizeof(struct stat));
s->st_mode = 0555;
2006-08-24 19:23:37 +00:00
s->st_atime = s->st_mtime = s->st_ctime = now;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
static void
append_node(struct iso_tree_node_dir *parent,
2006-08-24 19:23:37 +00:00
struct iso_tree_node *child)
{
assert( parent && child);
2006-08-24 19:23:37 +00:00
parent->nchildren++;
parent->children =
realloc(parent->children, parent->nchildren * sizeof(void*));
parent->children[parent->nchildren-1] = child;
child->parent = parent;
2006-08-24 19:23:37 +00:00
}
2006-08-15 20:37:04 +00:00
struct iso_tree_node_dir*
iso_tree_new_root()
2006-08-24 19:23:37 +00:00
{
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;
return root;
}
2006-08-15 20:37:04 +00:00
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;
2006-08-15 20:37:04 +00:00
}
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);
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) */
append_node(parent, (struct iso_tree_node*) f);
return (struct iso_tree_node*) f;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
struct iso_tree_node*
iso_tree_add_symlink(struct iso_tree_node_dir *parent,
const char *name, const char *dest)
2006-08-15 20:37:04 +00:00
{
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;
link->node.name = strdup(name);
link->dest = strdup(dest);
/* add to parent (this also sets link->node->parent) */
append_node(parent, (struct iso_tree_node*) link);
return (struct iso_tree_node*) link;
2006-08-24 19:23:37 +00:00
}
2006-08-15 20:37:04 +00:00
struct iso_tree_node_dir*
iso_tree_add_dir(struct iso_tree_node_dir *parent,
const char *name)
2006-08-24 19:23:37 +00:00
{
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.name = strdup(name);
append_node(parent, (struct iso_tree_node*) dir);
return dir;
}
2006-08-15 20:37:04 +00:00
void
iso_tree_node_set_name(struct iso_tree_node *node, const char *name)
{
free(node->name);
node->name = strdup(name);
}
2006-08-15 20:37:04 +00:00
void
iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs)
{
assert(node);
node->hide_flags = hide_attrs;
2006-08-15 20:37:04 +00:00
}
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;
}
}
2006-08-24 19:23:37 +00:00
struct iso_tree_node*
iso_tree_add_node(struct iso_tree_node_dir *parent,
const char *path)
2006-08-15 20:37:04 +00:00
{
struct stat st;
struct iso_tree_node *node;
assert( parent && path);
if (lstat(path, &st) == -1) {
libisofs_errno = NO_FILE;
2006-08-24 19:23:37 +00:00
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;
}
2006-08-15 20:37:04 +00:00
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);
2006-08-15 20:37:04 +00:00
}
static struct iso_tree_node*
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)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
struct iso_tree_node *new;
2006-08-15 20:37:04 +00:00
DIR *dir;
struct dirent *ent;
2006-08-24 19:23:37 +00:00
new = iso_tree_add_node(parent, path);
if (!new || !ISO_ISDIR(new)) {
2006-08-24 19:23:37 +00:00
return new;
}
2006-08-15 20:37:04 +00:00
dir = opendir(path);
if (!dir) {
2006-08-24 19:23:37 +00:00
warn("couldn't open directory %s: %s\n", path, strerror(errno));
return new;
2006-08-15 20:37:04 +00:00
}
while ((ent = readdir(dir))) {
2006-08-24 19:23:37 +00:00
char child[strlen(ent->d_name) + strlen(path) + 2];
2006-08-15 20:37:04 +00:00
if (strcmp(ent->d_name, ".") == 0 ||
2006-08-24 19:23:37 +00:00
strcmp(ent->d_name, "..") == 0)
2006-08-15 20:37:04 +00:00
continue;
//TODO check if path already finished in '/'
2006-08-24 19:23:37 +00:00
sprintf(child, "%s/%s", path, ent->d_name);
/* see if this child is excluded. */
if (iso_exclude_lookup(excludes, child))
continue;
if ( !iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child,
behavior, excludes) ) {
/* error */
behavior->error = 1;
if (behavior->stop_on_error)
break;
}
2006-08-15 20:37:04 +00:00
}
closedir(dir);
2006-08-24 19:23:37 +00:00
return new;
2006-08-15 20:37:04 +00:00
}
struct iso_tree_node_dir*
iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path,
struct iso_tree_radd_dir_behavior *behavior)
2006-08-15 20:37:04 +00:00
{
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);
}
2006-08-15 20:37:04 +00:00
}
/* recurse into dir */
dir = (struct iso_tree_node_dir*) iso_tree_radd_dir_aux(
parent, path, behavior, &table);
/* clear hashtable */
iso_exclude_empty(&table);
return dir;
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
void
iso_tree_print(const struct iso_tree_node *root, int spaces)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
char sp[spaces+1];
2006-08-15 20:37:04 +00:00
2006-08-24 19:23:37 +00:00
memset(sp, ' ', spaces);
sp[spaces] = '\0';
2006-08-15 20:37:04 +00:00
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);
}
2006-08-24 19:23:37 +00:00
}
2006-08-15 20:37:04 +00:00
}
2006-08-24 19:23:37 +00:00
void
iso_tree_print_verbose(const struct iso_tree_node *root,
print_dir_callback dir,
print_file_callback file,
void *callback_data,
int spaces)
2006-08-15 20:37:04 +00:00
{
2006-08-24 19:23:37 +00:00
(S_ISDIR(root->attrib.st_mode) ? 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);
}
2006-08-24 19:23:37 +00:00
}
2006-08-15 20:37:04 +00:00
}