summaryrefslogtreecommitdiff
path: root/lib/fstree/fstree.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fstree/fstree.c')
-rw-r--r--lib/fstree/fstree.c200
1 files changed, 200 insertions, 0 deletions
diff --git a/lib/fstree/fstree.c b/lib/fstree/fstree.c
new file mode 100644
index 0000000..2722b4a
--- /dev/null
+++ b/lib/fstree/fstree.c
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "fstree.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+static tree_node_t *mknode(tree_node_t *parent, const char *name,
+ size_t name_len, size_t extra_len,
+ uint16_t mode, uint32_t uid, uint32_t gid)
+{
+ size_t size = sizeof(tree_node_t) + extra_len;
+ tree_node_t *n;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ size += sizeof(*n->data.dir);
+ break;
+ case S_IFREG:
+ size += sizeof(*n->data.file);
+ break;
+ }
+
+ n = calloc(1, size + name_len + 1);
+ if (n == NULL)
+ return NULL;
+
+ if (parent != NULL) {
+ n->next = parent->data.dir->children;
+ parent->data.dir->children = n;
+ }
+
+ n->uid = uid;
+ n->gid = gid;
+ n->mode = mode;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ n->data.dir = (dir_info_t *)n->payload;
+ break;
+ case S_IFREG:
+ n->data.file = (file_info_t *)n->payload;
+ break;
+ case S_IFLNK:
+ n->data.slink_target = (char *)n->payload;
+ break;
+ }
+
+ n->name = (char *)n + size;
+ memcpy(n->name, name, name_len);
+ return n;
+}
+
+static void free_recursive(tree_node_t *n)
+{
+ tree_node_t *it;
+
+ if (S_ISDIR(n->mode)) {
+ while (n->data.dir->children != NULL) {
+ it = n->data.dir->children;
+ n->data.dir->children = it->next;
+
+ free_recursive(it);
+ }
+ }
+
+ free(n);
+}
+
+static tree_node_t *child_by_name(tree_node_t *root, const char *name,
+ size_t len)
+{
+ tree_node_t *n = root->data.dir->children;
+
+ while (n != NULL) {
+ if (strncmp(n->name, name, len) == 0 && n->name[len] == '\0')
+ break;
+
+ n = n->next;
+ }
+
+ return n;
+}
+
+static tree_node_t *get_parent_node(fstree_t *fs, tree_node_t *root,
+ const char *path)
+{
+ const char *end;
+ tree_node_t *n;
+
+ for (;;) {
+ if (!S_ISDIR(root->mode)) {
+ errno = ENOTDIR;
+ return NULL;
+ }
+
+ end = strchr(path, '/');
+ if (end == NULL)
+ break;
+
+ n = child_by_name(root, path, end - path);
+
+ if (n == NULL) {
+ n = mknode(root, path, end - path, 0,
+ S_IFDIR | fs->default_mode,
+ fs->default_uid, fs->default_gid);
+ if (n == NULL)
+ return NULL;
+
+ n->data.dir->created_implicitly = true;
+ }
+
+ root = n;
+ path = end + 1;
+ }
+
+ return root;
+}
+
+tree_node_t *fstree_add(fstree_t *fs, const char *path, uint16_t mode,
+ uint32_t uid, uint32_t gid, size_t extra_len)
+{
+ tree_node_t *child, *parent;
+ const char *name;
+
+ name = strrchr(path, '/');
+ name = (name == NULL ? path : (name + 1));
+
+ parent = get_parent_node(fs, fs->root, path);
+ if (parent == NULL)
+ return NULL;
+
+ child = child_by_name(parent, name, strlen(name));
+ if (child != NULL) {
+ if (S_ISDIR(child->mode) && S_ISDIR(mode) &&
+ child->data.dir->created_implicitly) {
+ child->data.dir->created_implicitly = false;
+ return child;
+ }
+
+ errno = EEXIST;
+ return NULL;
+ }
+
+ return mknode(parent, name, strlen(name), extra_len, mode, uid, gid);
+}
+
+tree_node_t *fstree_add_file(fstree_t *fs, const char *path, uint16_t mode,
+ uint32_t uid, uint32_t gid, uint64_t filesz,
+ const char *input)
+{
+ tree_node_t *node;
+ size_t count, extra;
+ char *ptr;
+
+ count = filesz / fs->block_size;
+ extra = sizeof(uint32_t) * count + strlen(input) + 1;
+
+ mode &= 07777;
+ node = fstree_add(fs, path, S_IFREG | mode, uid, gid, extra);
+
+ if (node != NULL) {
+ ptr = (char *)(node->data.file->blocksizes + count);
+ strcpy(ptr, input);
+
+ node->data.file->input_file = ptr;
+ node->data.file->size = filesz;
+ }
+ return node;
+}
+
+int fstree_init(fstree_t *fs, size_t block_size, uint32_t mtime,
+ uint16_t default_mode, uint32_t default_uid,
+ uint32_t default_gid)
+{
+ memset(fs, 0, sizeof(*fs));
+
+ fs->default_uid = default_uid;
+ fs->default_gid = default_gid;
+ fs->default_mode = default_mode & 07777;
+ fs->default_mtime = mtime;
+ fs->block_size = block_size;
+
+ fs->root = mknode(NULL, "", 0, 0, S_IFDIR | fs->default_mode,
+ default_uid, default_gid);
+
+ if (fs->root == NULL) {
+ perror("initializing file system tree");
+ return -1;
+ }
+
+ return 0;
+}
+
+void fstree_cleanup(fstree_t *fs)
+{
+ free_recursive(fs->root);
+ memset(fs, 0, sizeof(*fs));
+}