You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
627 lines
17 KiB
627 lines
17 KiB
/* |
|
* Copyright (c) 2007 Vreixo Formoso |
|
* |
|
* This file is part of the libisofs project; you can redistribute it and/or |
|
* modify it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation. See COPYING file for details. |
|
*/ |
|
|
|
/* |
|
* Functions that act on the iso tree. |
|
*/ |
|
|
|
#include "libisofs.h" |
|
#include "node.h" |
|
#include "error.h" |
|
#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. |
|
* |
|
* @param parent |
|
* the dir where the new directory will be created |
|
* @param name |
|
* name for the new dir. If a node with same name already exists on |
|
* parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. |
|
* @param dir |
|
* place where to store a pointer to the newly created dir. No extra |
|
* ref is addded, so you will need to call iso_node_ref() if you really |
|
* need it. You can pass NULL in this parameter if you don't need the |
|
* pointer. |
|
* @return |
|
* number of nodes in dir if succes, < 0 otherwise |
|
* Possible errors: |
|
* ISO_NULL_POINTER, if parent or name are NULL |
|
* ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists |
|
*/ |
|
int iso_tree_add_new_dir(IsoDir *parent, const char *name, IsoDir **dir) |
|
{ |
|
IsoDir *node; |
|
IsoNode **pos; |
|
time_t now; |
|
|
|
if (parent == NULL || name == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
if (dir) { |
|
*dir = NULL; |
|
} |
|
|
|
/* find place where to insert */ |
|
pos = &(parent->children); |
|
while (*pos != NULL && strcmp((*pos)->name, name) < 0) { |
|
pos = &((*pos)->next); |
|
} |
|
if (*pos != NULL && !strcmp((*pos)->name, name)) { |
|
/* a node with same name already exists */ |
|
return ISO_NODE_NAME_NOT_UNIQUE; |
|
} |
|
|
|
node = calloc(1, sizeof(IsoDir)); |
|
if (node == NULL) { |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
node->node.refcount = 1; |
|
node->node.type = LIBISO_DIR; |
|
node->node.name = strdup(name); |
|
if (node->node.name == NULL) { |
|
free(node); |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
/* permissions from parent */ |
|
node->node.mode = parent->node.mode; |
|
node->node.uid = parent->node.uid; |
|
node->node.gid = parent->node.gid; |
|
node->node.hidden = parent->node.hidden; |
|
|
|
/* current time */ |
|
now = time(NULL); |
|
node->node.atime = now; |
|
node->node.ctime = now; |
|
node->node.mtime = now; |
|
|
|
/* add to dir */ |
|
node->node.parent = parent; |
|
node->node.next = *pos; |
|
*pos = (IsoNode*)node; |
|
|
|
if (dir) { |
|
*dir = node; |
|
} |
|
return ++parent->nchildren; |
|
} |
|
|
|
/** |
|
* Add a new symlink to the directory tree. Permissions are set to 0777, |
|
* owner and hidden atts are taken from parent. You can modify any of them |
|
* later. |
|
* |
|
* @param parent |
|
* the dir where the new symlink will be created |
|
* @param name |
|
* name for the new dir. If a node with same name already exists on |
|
* parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. |
|
* @param dest |
|
* destination of the link |
|
* @param link |
|
* place where to store a pointer to the newly created link. No extra |
|
* ref is addded, so you will need to call iso_node_ref() if you really |
|
* need it. You can pass NULL in this parameter if you don't need the |
|
* pointer |
|
* @return |
|
* number of nodes in parent if success, < 0 otherwise |
|
* Possible errors: |
|
* ISO_NULL_POINTER, if parent, name or dest are NULL |
|
* ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists |
|
* ISO_MEM_ERROR |
|
*/ |
|
int iso_tree_add_new_symlink(IsoDir *parent, const char *name, |
|
const char *dest, IsoSymlink **link) |
|
{ |
|
IsoSymlink *node; |
|
IsoNode **pos; |
|
time_t now; |
|
|
|
if (parent == NULL || name == NULL || dest == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
if (link) { |
|
*link = NULL; |
|
} |
|
|
|
/* find place where to insert */ |
|
pos = &(parent->children); |
|
while (*pos != NULL && strcmp((*pos)->name, name) < 0) { |
|
pos = &((*pos)->next); |
|
} |
|
if (*pos != NULL && !strcmp((*pos)->name, name)) { |
|
/* a node with same name already exists */ |
|
return ISO_NODE_NAME_NOT_UNIQUE; |
|
} |
|
|
|
node = calloc(1, sizeof(IsoSymlink)); |
|
if (node == NULL) { |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
node->node.refcount = 1; |
|
node->node.type = LIBISO_SYMLINK; |
|
node->node.name = strdup(name); |
|
if (node->node.name == NULL) { |
|
free(node); |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
node->dest = strdup(dest); |
|
if (node->dest == NULL) { |
|
free(node->node.name); |
|
free(node); |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
/* permissions from parent */ |
|
node->node.mode = S_IFLNK | 0777; |
|
node->node.uid = parent->node.uid; |
|
node->node.gid = parent->node.gid; |
|
node->node.hidden = parent->node.hidden; |
|
|
|
/* current time */ |
|
now = time(NULL); |
|
node->node.atime = now; |
|
node->node.ctime = now; |
|
node->node.mtime = now; |
|
|
|
/* add to dir */ |
|
node->node.parent = parent; |
|
node->node.next = *pos; |
|
*pos = (IsoNode*)node; |
|
|
|
if (link) { |
|
*link = node; |
|
} |
|
return ++parent->nchildren; |
|
} |
|
|
|
/** |
|
* Add a new special file to the directory tree. As far as libisofs concerns, |
|
* an special file is a block device, a character device, a FIFO (named pipe) |
|
* or a socket. You can choose the specific kind of file you want to add |
|
* by setting mode propertly (see man 2 stat). |
|
* |
|
* Note that special files are only written to image when Rock Ridge |
|
* extensions are enabled. Moreover, a special file is just a directory entry |
|
* in the image tree, no data is written beyond that. |
|
* |
|
* Owner and hidden atts are taken from parent. You can modify any of them |
|
* later. |
|
* |
|
* @param parent |
|
* the dir where the new special file will be created |
|
* @param name |
|
* name for the new special file. If a node with same name already exists |
|
* on parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. |
|
* @param mode |
|
* file type and permissions for the new node. Note that you can't |
|
* specify any kind of file here, only special types are allowed. i.e, |
|
* S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK, |
|
* S_IFREG and S_IFDIR aren't. |
|
* @param dev |
|
* device ID, equivalent to the st_rdev field in man 2 stat. |
|
* @param special |
|
* place where to store a pointer to the newly created special file. No |
|
* extra ref is addded, so you will need to call iso_node_ref() if you |
|
* really need it. You can pass NULL in this parameter if you don't need |
|
* the pointer. |
|
* @return |
|
* number of nodes in parent if success, < 0 otherwise |
|
* Possible errors: |
|
* ISO_NULL_POINTER, if parent, name or dest are NULL |
|
* ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists |
|
* ISO_MEM_ERROR |
|
* |
|
*/ |
|
int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode, |
|
dev_t dev, IsoSpecial **special) |
|
{ |
|
IsoSpecial *node; |
|
IsoNode **pos; |
|
time_t now; |
|
|
|
if (parent == NULL || name == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
if (S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)) { |
|
return ISO_WRONG_ARG_VALUE; |
|
} |
|
if (special) { |
|
*special = NULL; |
|
} |
|
|
|
/* find place where to insert */ |
|
pos = &(parent->children); |
|
while (*pos != NULL && strcmp((*pos)->name, name) < 0) { |
|
pos = &((*pos)->next); |
|
} |
|
if (*pos != NULL && !strcmp((*pos)->name, name)) { |
|
/* a node with same name already exists */ |
|
return ISO_NODE_NAME_NOT_UNIQUE; |
|
} |
|
|
|
node = calloc(1, sizeof(IsoSpecial)); |
|
if (node == NULL) { |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
node->node.refcount = 1; |
|
node->node.type = LIBISO_SPECIAL; |
|
node->node.name = strdup(name); |
|
if (node->node.name == NULL) { |
|
free(node); |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
node->node.mode = mode; |
|
node->dev = dev; |
|
|
|
/* atts from parent */ |
|
node->node.uid = parent->node.uid; |
|
node->node.gid = parent->node.gid; |
|
node->node.hidden = parent->node.hidden; |
|
|
|
/* current time */ |
|
now = time(NULL); |
|
node->node.atime = now; |
|
node->node.ctime = now; |
|
node->node.mtime = now; |
|
|
|
/* add to dir */ |
|
node->node.parent = parent; |
|
node->node.next = *pos; |
|
*pos = (IsoNode*)node; |
|
|
|
if (special) { |
|
*special = node; |
|
} |
|
return ++parent->nchildren; |
|
} |
|
|
|
static |
|
int iso_tree_add_node_builder(IsoImage *image, IsoDir *parent, |
|
IsoFileSource *src, IsoNodeBuilder *builder, |
|
IsoNode **node) |
|
{ |
|
int result; |
|
IsoNode *new; |
|
IsoNode **pos; |
|
char *name; |
|
|
|
if (parent == NULL || src == NULL || builder == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
if (node) { |
|
*node = NULL; |
|
} |
|
|
|
name = src->get_name(src); |
|
|
|
/* find place where to insert */ |
|
pos = &(parent->children); |
|
while (*pos != NULL && strcmp((*pos)->name, name) < 0) { |
|
pos = &((*pos)->next); |
|
} |
|
if (*pos != NULL && !strcmp((*pos)->name, name)) { |
|
/* a node with same name already exists */ |
|
return ISO_NODE_NAME_NOT_UNIQUE; |
|
} |
|
|
|
result = builder->create_node(builder, image, src, &new); |
|
if (result < 0) { |
|
return result; |
|
} |
|
|
|
/* finally, add node to parent */ |
|
new->parent = parent; |
|
new->next = *pos; |
|
*pos = new; |
|
|
|
if (node) { |
|
*node = new; |
|
} |
|
return ++parent->nchildren; |
|
} |
|
|
|
int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path, |
|
IsoNode **node) |
|
{ |
|
int result; |
|
IsoFilesystem *fs; |
|
IsoFileSource *file; |
|
|
|
if (image == NULL || parent == NULL || path == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
fs = image->fs; |
|
result = fs->get_by_path(fs, path, &file); |
|
if (result < 0) { |
|
return result; |
|
} |
|
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) { |
|
char msg[PATH_MAX]; |
|
sprintf(msg, "Can't open dir %s (%d)\n", dir->get_path(dir), result); |
|
iso_msg_debug(image, msg); |
|
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; |
|
} |
|
|
|
int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node) |
|
{ |
|
int result; |
|
IsoNode *n; |
|
IsoDir *dir; |
|
char *ptr, *brk_info, *component; |
|
|
|
if (image == NULL || path == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
/* get the first child at the root of the image that is "/" */ |
|
dir = image->root; |
|
n = (IsoNode *)dir; |
|
if (!strcmp(path, "/")) { |
|
if (node) { |
|
*node = n; |
|
} |
|
return ISO_SUCCESS; |
|
} |
|
|
|
ptr = strdup(path); |
|
result = 0; |
|
|
|
/* get the first component of the path */ |
|
component = strtok_r(ptr, "/", &brk_info); |
|
while (component) { |
|
if (n->type != LIBISO_DIR) { |
|
n = NULL; |
|
break; |
|
} |
|
dir = (IsoDir *)n; |
|
|
|
result = iso_dir_get_node(dir, component, &n); |
|
if (result != 1) { |
|
n = NULL; |
|
break; |
|
} |
|
|
|
component = strtok_r(NULL, "/", &brk_info); |
|
} |
|
|
|
free(ptr); |
|
if (node) { |
|
*node = n; |
|
} |
|
return result; |
|
}
|
|
|