diff --git a/haggis.c b/haggis.c index c7e8dfa..d6d893c 100644 --- a/haggis.c +++ b/haggis.c @@ -36,6 +36,8 @@ #include #include // PATH_MAX #include // uint_t +#include +#include #if defined(__FreeBSD__) || defined(__DragonFly__) #include @@ -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: