diff options
Diffstat (limited to 'lib/fstree/hardlink.c')
-rw-r--r-- | lib/fstree/hardlink.c | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/lib/fstree/hardlink.c b/lib/fstree/hardlink.c new file mode 100644 index 0000000..8a79d46 --- /dev/null +++ b/lib/fstree/hardlink.c @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * hardlink.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "config.h" + +#include "fstree.h" + +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +tree_node_t *fstree_add_hard_link(fstree_t *fs, const char *path, + const char *target) +{ + struct stat sb; + tree_node_t *n; + + memset(&sb, 0, sizeof(sb)); + sb.st_mode = S_IFLNK | 0777; + + n = fstree_add_generic(fs, path, &sb, target); + if (n != NULL) { + if (canonicalize_name(n->data.target)) { + free(n); + errno = EINVAL; + return NULL; + } + + n->mode = FSTREE_MODE_HARD_LINK; + } + + return n; +} + +int fstree_resolve_hard_link(fstree_t *fs, tree_node_t *node) +{ + tree_node_t *start = node; + + while (node->mode == FSTREE_MODE_HARD_LINK || + node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { + if (node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { + node = node->data.target_node; + } else { + node = fstree_get_node_by_path(fs, fs->root, + node->data.target, + false, false); + if (node == NULL) + return -1; + } + + if (node == start) { + errno = EMLINK; + return -1; + } + } + + start->mode = FSTREE_MODE_HARD_LINK_RESOLVED; + start->data.target_node = node; + + node->link_count += 1; + return 0; +} |