Progress on extracting nodes

This commit is contained in:
Nathan Fisher 2023-09-19 20:46:48 -04:00
parent 2e8e2e9a28
commit 58ba56f852

239
haggis.c
View file

@ -36,6 +36,8 @@
#include <errno.h>
#include <limits.h> // PATH_MAX
#include <stdint.h> // uint<x>_t
#include <sys/syslimits.h>
#include <sys/unistd.h>
#if defined(__FreeBSD__) || defined(__DragonFly__)
#include <sha.h>
@ -559,6 +561,7 @@ int haggis_init_file_node(
return 0;
}
}
node->filetype.tag = normal;
res = haggis_file_init(node->name.name, &node->filetype.f_type.file, a);
if (res != 0) {
haggis_node_deinit(node);
@ -570,6 +573,30 @@ int haggis_init_file_node(
return 0;
}
int haggis_init_dev_node(
haggis_node *node,
struct stat *st,
haggis_linkmap *map,
haggis_mq *mq
) {
haggis_message_body body;
haggis_msg *msg;
char *target;
if (st->st_nlink > 1) {
target = haggis_linkmap_get_or_add(map, st->st_ino, node->name.name);
if (target != NULL) {
haggis_init_hardlink_node(node, target, map, mq);
return 0;
}
}
haggis_device_init(st->st_rdev, &node->filetype.f_type.dev);
body.f_name = node->name.name;
msg = haggis_msg_init(NodeCreated, body);
haggis_mq_push(mq, msg);
return 0;
}
haggis_node* haggis_create_node(
char *file,
haggis_algorithm a,
@ -592,22 +619,6 @@ haggis_node* haggis_create_node(
free(node);
return NULL;
}
if (S_ISBLK(st.st_mode)) {
node->filetype.tag = block;
} else if (S_ISCHR(st.st_mode)) {
node->filetype.tag = character;
} else if (S_ISDIR(st.st_mode)) {
node->filetype.tag = directory;
} else if (S_ISFIFO(st.st_mode)) {
node->filetype.tag = fifo;
} else if (S_ISLNK(st.st_mode)) {
node->filetype.tag = softlink;
} else if (S_ISREG(st.st_mode)) {
node->filetype.tag = normal;
} else {
free(node);
return NULL;
}
errno = 0;
if (file[0] == '/') {
namlen = strnlen(file, PATH_MAX - 1);
@ -626,90 +637,157 @@ haggis_node* haggis_create_node(
node->mtime.val = (uint64_t)st.st_mtim.tv_sec;
mode.val = (uint16_t)(st.st_mode & 07777);
node->mode = mode;
switch (node->filetype.tag) {
case normal:
res = haggis_init_file_node(node, &st, a, map, mq);
if (res != 0)
return NULL;
break;
case block:
if (st.st_nlink > 1) {
target = haggis_linkmap_get_or_add(map, st.st_ino, file);
if (target != NULL) {
haggis_init_hardlink_node(node, target, map, mq);
return node;
}
if (S_ISBLK(st.st_mode)) {
node->filetype.tag = block;
res = haggis_init_dev_node(node, &st, map, mq);
if (res != 0)
return NULL;
} else if (S_ISCHR(st.st_mode)) {
node->filetype.tag = character;
res = haggis_init_dev_node(node, &st, map, mq);
if (res != 0)
return NULL;
} else if (S_ISDIR(st.st_mode)) {
node->filetype.tag = directory;
} else if (S_ISFIFO(st.st_mode)) {
node->filetype.tag = fifo;
if (st.st_nlink > 1) {
target = haggis_linkmap_get_or_add(map, st.st_ino, file);
if (target != NULL) {
haggis_init_hardlink_node(node, target, map, mq);
return node;
}
haggis_device_init(st.st_rdev, &node->filetype.f_type.dev);
break;
case character:
if (st.st_nlink > 1) {
target = haggis_linkmap_get_or_add(map, st.st_ino, file);
if (target != NULL) {
haggis_init_hardlink_node(node, target, map, mq);
return node;
}
}
haggis_device_init(st.st_rdev, &node->filetype.f_type.dev);
break;
case fifo:
if (st.st_nlink > 1) {
target = haggis_linkmap_get_or_add(map, st.st_ino, file);
if (target != NULL) {
haggis_init_hardlink_node(node, target, map, mq);
return node;
}
}
return node;
case directory:
case hardlink:
case eof:
body.f_name = NULL;
msg = haggis_msg_init(EndOfArchive, body);
haggis_mq_push(mq, msg);
return node;
case softlink:
node->filetype.tag = softlink;
ssize_t res = readlink(file, pathbuf, PATH_MAX);
if (res == -1) {
haggis_node_deinit(node);
return NULL;
}
target = malloc(res + 1);
memcpy(target, pathbuf, (unsigned long)res);
haggis_filename_init(target, &node->filetype.f_type.target);
body.f_name = file;
msg = haggis_msg_init(NodeCreated, body);
haggis_mq_push(mq, msg);
return node;
}
} else if (S_ISLNK(st.st_mode)) {
node->filetype.tag = softlink;
ssize_t res = readlink(file, pathbuf, PATH_MAX);
if (res == -1) {
haggis_node_deinit(node);
return NULL;
}
target = malloc(res + 1);
memcpy(target, pathbuf, (unsigned long)res);
haggis_filename_init(target, &node->filetype.f_type.target);
body.f_name = file;
msg = haggis_msg_init(NodeCreated, body);
haggis_mq_push(mq, msg);
} else if (S_ISREG(st.st_mode)) {
node->filetype.tag = normal;
res = haggis_init_file_node(node, &st, a, map, mq);
if (res != 0)
return NULL;
} else {
free(node);
return NULL;
}
body.f_name = file;
msg = haggis_msg_init(NodeCreated, body);
haggis_mq_push(mq, msg);
return node;
}
char* get_full_path(haggis_filename *fname, char *basedir) {
char *path;
int pathlen;
if (basedir == NULL) {
path = calloc(1, (int)fname->len.val + 1);
if (path == NULL)
return NULL;
memcpy(path, fname->name, fname->len.val);
} else {
if (fname->name[0] == '/') {
pathlen = strnlen(basedir, PATH_MAX) + (int)fname->len.val + 1;
path = calloc(1, pathlen);
if (path == NULL)
return NULL;
snprintf(path, pathlen, "%s%s", basedir, fname->name);
} else {
pathlen = strnlen(basedir, PATH_MAX) + (int)fname->len.val + 2;
path = calloc(1, pathlen);
if (path == NULL)
return NULL;
snprintf(path, pathlen, "%s/%s", basedir, fname->name);
}
}
return path;
}
int haggis_extract_dev(haggis_node *node, char *basedir) {
// todo
dev_t dev;
mode_t mode;
char *path;
int ret;
assert(geteuid() == 0);
assert(node->filetype.tag == block || node->filetype.tag == character);
return 0;
path = get_full_path(&node->name, basedir);
if (path == NULL)
return errno;
dev = makedev((int)node->filetype.f_type.dev.major.val, (int)node->filetype.f_type.dev.minor.val);
mode = (mode_t)node->mode.val;
ret = mknod(path, mode, dev);
free(path);
return ret;
}
int haggis_extract_fifo(haggis_node *node, char *basedir) {
// todo
mode_t mode;
char *path;
int ret;
assert(node->filetype.tag == fifo);
path = get_full_path(&node->name, basedir);
if (path == NULL)
return 1;
mode = (mode_t)node->mode.val;
ret = mkfifo(path, mode);
free(path);
if (ret !=0)
return errno;
return 0;
}
int haggis_extract_symlink(haggis_node *node, char *basedir) {
// todo
char *path;
int ret;
assert(node->filetype.tag == softlink);
path = get_full_path(&node->name, basedir);
if (path == NULL)
return 1;
ret = symlink(node->filetype.f_type.target.name, path);
free(path);
if (ret != 0)
return errno;
return 0;
}
int haggis_extract_hardlink(haggis_node *node, char *basedir) {
// todo
char *path, *target;
int ret;
FILE *fd;
assert(node->filetype.tag == hardlink);
path = get_full_path(&node->name, basedir);
if (path == NULL)
return 1;
target = get_full_path(&node->filetype.f_type.target, basedir);
if (target == NULL) {
free(path);
return 1;
}
if (access(target, F_OK) == -1) {
fd = fopen(target, "w");
if (fd == NULL) {
free(path);
free(target);
return errno;
}
fclose(fd);
}
ret = link(target, path);
free(path);
free(target);
if (ret != 0)
return errno;
return 0;
}
@ -730,7 +808,8 @@ int haggis_extract_node(FILE *stream, char *basedir, haggis_node *node) {
switch (node->filetype.tag) {
case block:
case character:
return haggis_extract_dev(node, basedir);
if (geteuid() == 0)
return haggis_extract_dev(node, basedir);
case fifo:
return haggis_extract_fifo(node, basedir);
case softlink: