diff --git a/include/haggis.h b/include/haggis.h index 9a49de0..645fc3c 100644 --- a/include/haggis.h +++ b/include/haggis.h @@ -33,6 +33,7 @@ #ifndef HAGGIS_H #define HAGGIS_H +#include "linklist.h" #include #include @@ -118,7 +119,7 @@ typedef struct { haggis_filetype *filetype; } haggis_node; -haggis_node* haggis_create_node(char *file); +haggis_node* haggis_create_node(char *file, haggis_hardlink_list *list); int haggis_extract_node(FILE *stram, haggis_node *node); int haggis_load_node(FILE *stream, haggis_node *node); int haggis_store_node(FILE *stream, haggis_node *node); diff --git a/include/jobq.h b/include/jobq.h index df9a0c2..2b09e68 100644 --- a/include/jobq.h +++ b/include/jobq.h @@ -36,7 +36,6 @@ #include #include #include -#include #include "haggis.h" diff --git a/include/linklist.h b/include/linklist.h index 837180d..772a5f7 100644 --- a/include/linklist.h +++ b/include/linklist.h @@ -17,4 +17,6 @@ typedef struct { haggis_hardlink *head; } haggis_hardlink_list; +char* haggis_linklist_get_or_put(haggis_hardlink_list *list, ino_t inode, char *fname); + #endif // !HAGGIS_LINKLIST diff --git a/src/haggis.c b/src/haggis.c index 2c43fbd..c1f70bf 100644 --- a/src/haggis.c +++ b/src/haggis.c @@ -30,8 +30,11 @@ * other than his own. */ -#include +#include +#include +#include #include + #if defined(__FreeBSD__) || defined(__DragonFly__) #include #include @@ -45,14 +48,17 @@ #include #endif /* if defined (__FreeBSD__) */ -#include #include #include #include #include +#include +#include +#include #include "bytes.h" #include "haggis.h" +#include "linklist.h" static unsigned char header[7] = {0x89, 'h', 'a', 'g', 'g', 'i', 's'}; @@ -71,6 +77,19 @@ int haggis_check_header(FILE *stream) { return 1; } +haggis_device* haggis_device_init(dev_t rdev) { + haggis_device *dev; + dev = malloc(sizeof(haggis_device)); + if (dev == NULL) return NULL; + dev->major.val = (uint32_t)major(rdev); + dev->minor.val = (uint32_t)minor(rdev); + return dev; +} + +void haggis_device_deinit(haggis_device *dev) { + free(dev); +} + int haggis_store_device(FILE *stream, haggis_device *dev) { if (fwrite(dev->major.bytes, 1, 4, stream) != 4) return 1; @@ -227,6 +246,37 @@ int haggis_validate_cksum(haggis_file *file) { return 0; } +haggis_file* haggis_file_init(char *path) { + FILE *f; + long len; + haggis_file *hf; + + f = fopen(path, "r"); + if (f == NULL) return NULL; + if (fseek(f, 0, SEEK_END) == -1) { + fclose(f); + return NULL; + } + len = ftell(f); + if (len == -1) { + fclose(f); + return NULL; + } + hf = malloc(sizeof(haggis_file)); + if (hf == NULL) return NULL; + hf->len.val = (uint64_t)len; + rewind(f); + hf->data = malloc((size_t)len); + if (fread(hf->data, 1, (size_t)len, f) != (size_t)len) { + free(hf->data); + free(hf); + fclose(f); + return NULL; + } + fclose(f); + return hf; +} + int haggis_store_file(FILE *stream, haggis_file *file) { if (store_u64(stream, file->len) != 8) return 1; @@ -263,6 +313,27 @@ int haggis_load_file(FILE *stream, haggis_ft *ft) { return 0; } +haggis_filename* haggis_filename_init(char *target) { + size_t len; + haggis_filename *fname; + + len = strlen(target) - 1; + fname = malloc(sizeof(haggis_filename)); + if (fname == NULL) { + free(target); + return NULL; + } + fname->len.val = (uint16_t)len; + fname->name = target; + return fname; +} + +void haggis_filename_deinit(haggis_filename *fname) { + if (fname->name != NULL) + free(fname->name); + free(fname); +} + int haggis_load_filename(FILE *stream, haggis_filename *n) { u16 len; char *name; @@ -383,17 +454,55 @@ u16 haggis_derive_mode(u16 raw, haggis_filetype *ft) { return mode; } -haggis_node* haggis_create_node(char *file) { +void haggis_node_deinit(haggis_node *node) { + if (node == NULL) return; + if (node->name != NULL) free(node->name); + switch (node->filetype->tag) { + case normal: + if (node->filetype->f_type->file != NULL) { + free(node->filetype->f_type->file); + } + break; + case hardlink: + case softlink: + if (node->filetype->f_type->target != NULL) { + haggis_filename_deinit(node->filetype->f_type->target); + } + break; + case character: + case block: + if (node->filetype->f_type->dev != NULL) { + haggis_device_deinit(node->filetype->f_type->dev); + } + break; + case directory: + case fifo: + case eof: + break; + }; + free(node); +} + +haggis_node* haggis_create_node(char *file, haggis_hardlink_list *list) { struct stat *st = NULL; haggis_typeflag tf; u16 mode; u32 uid; u32 gid; u64 mtime; - haggis_node *node = malloc(sizeof(haggis_node)); + char *target; + char pathbuf[PATH_MAX]; + haggis_filename *fname; + haggis_device *dev; + haggis_file *f; + haggis_node *node; + node = malloc(sizeof(haggis_node)); if (node == NULL) return NULL; + node->filetype = malloc(sizeof(haggis_filetype)); + if (node->filetype == NULL) + return NULL; if (stat(file, st) != 0) { free(node); return NULL; @@ -419,21 +528,108 @@ haggis_node* haggis_create_node(char *file) { gid.val = (uint32_t)st->st_gid; node->gid = gid; mtime.val = (uint64_t)st->st_mtim.tv_sec; + node->mtime = mtime; mode.val = (uint16_t)(st->st_mode & 07777); node->mode = mode; switch (tf) { case normal: + target = haggis_linklist_get_or_put(list, st->st_ino, file); + if (target == NULL) { + node->filetype->tag = normal; + f = haggis_file_init(file); + if (f == NULL) { + haggis_node_deinit(node); + return NULL; + } + } else { + node->filetype->tag = hardlink; + fname = haggis_filename_init(target); + if (fname == NULL) { + haggis_node_deinit(node); + return NULL; + } + node->filetype->f_type->target = fname; + } + break; case block: + target = haggis_linklist_get_or_put(list, st->st_ino, file); + if (target == NULL) { + node->filetype->tag = block; + dev = haggis_device_init(st->st_rdev); + if (dev == NULL) { + haggis_node_deinit(node); + return NULL; + } + node->filetype->f_type->dev = dev; + } else { + node->filetype->tag = hardlink; + fname = haggis_filename_init(target); + if (fname == NULL) { + haggis_node_deinit(node); + return NULL; + } + node->filetype->f_type->target = fname; + } + break; case character: + target = haggis_linklist_get_or_put(list, st->st_ino, file); + if (target == NULL) { + node->filetype->tag = character; + dev = haggis_device_init(st->st_rdev); + if (dev == NULL) { + haggis_node_deinit(node); + return NULL; + } + node->filetype->f_type->dev = dev; + } else { + node->filetype->tag = hardlink; + fname = haggis_filename_init(target); + if (fname == NULL) { + haggis_node_deinit(node); + return NULL; + } + fname->name = target; + node->filetype->f_type->target = fname; + } + break; case fifo: + target = haggis_linklist_get_or_put(list, st->st_ino, file); + if (target == NULL) { + node->filetype->tag = fifo; + } else { + node->filetype->tag = hardlink; + fname = haggis_filename_init(target); + if (fname == NULL) { + haggis_node_deinit(node); + return NULL; + } + fname->name = target; + node->filetype->f_type->target = fname; + } break; case directory: + node->filetype->tag = directory; break; case hardlink: + node->filetype->tag = hardlink; break; case softlink: + node->filetype->tag = softlink; + ssize_t res = readlink(file, pathbuf, PATH_MAX); + if (res == -1) { + haggis_node_deinit(node); + return NULL; + } + char *target = malloc(res + 1); + memcpy(target, pathbuf, (unsigned long)res); + node->filetype->f_type->target = haggis_filename_init(target); + if (node->filetype->f_type->target == NULL) { + haggis_node_deinit(node); + return NULL; + } break; case eof: + node->filetype->tag = eof; break; } // todo diff --git a/src/test/Makefile b/src/test/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/src/test/test.c b/src/test/test.c new file mode 100644 index 0000000..e69de29