summaryrefslogtreecommitdiff
path: root/lib/sqfshelper/write_dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqfshelper/write_dir.c')
-rw-r--r--lib/sqfshelper/write_dir.c141
1 files changed, 141 insertions, 0 deletions
diff --git a/lib/sqfshelper/write_dir.c b/lib/sqfshelper/write_dir.c
new file mode 100644
index 0000000..23297ad
--- /dev/null
+++ b/lib/sqfshelper/write_dir.c
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * write_dir.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "meta_writer.h"
+#include "util.h"
+
+#include <assert.h>
+#include <endian.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+static int get_type(mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFSOCK: return SQFS_INODE_SOCKET;
+ case S_IFIFO: return SQFS_INODE_FIFO;
+ case S_IFLNK: return SQFS_INODE_SLINK;
+ case S_IFBLK: return SQFS_INODE_BDEV;
+ case S_IFCHR: return SQFS_INODE_CDEV;
+ case S_IFDIR: return SQFS_INODE_DIR;
+ case S_IFREG: return SQFS_INODE_FILE;
+ default:
+ assert(0);
+ }
+}
+
+static int dir_index_grow(dir_index_t **index)
+{
+ size_t size = sizeof(dir_index_t) + sizeof(idx_ref_t) * 10;
+ void *new;
+
+ if (*index == NULL) {
+ new = calloc(1, size);
+ } else {
+ if ((*index)->num_nodes < (*index)->max_nodes)
+ return 0;
+
+ size += sizeof(idx_ref_t) * (*index)->num_nodes;
+ new = realloc(*index, size);
+ }
+
+ if (new == NULL) {
+ perror("creating directory index");
+ return -1;
+ }
+
+ *index = new;
+ (*index)->max_nodes += 10;
+ return 0;
+}
+
+int meta_writer_write_dir(meta_writer_t *dm, dir_info_t *dir,
+ dir_index_t **index)
+{
+ size_t i, size, count;
+ sqfs_dir_header_t hdr;
+ sqfs_dir_entry_t ent;
+ tree_node_t *c, *d;
+ uint16_t *diff_u16;
+ uint32_t offset;
+ uint64_t block;
+ int32_t diff;
+
+ c = dir->children;
+ dir->size = 0;
+
+ meta_writer_get_position(dm, &dir->start_block, &dir->block_offset);
+
+ while (c != NULL) {
+ meta_writer_get_position(dm, &block, &offset);
+
+ count = 0;
+ size = (offset + sizeof(hdr)) % SQFS_META_BLOCK_SIZE;
+
+ for (d = c; d != NULL; d = d->next) {
+ if ((d->inode_ref >> 16) != (c->inode_ref >> 16))
+ break;
+
+ diff = d->inode_num - c->inode_num;
+
+ if (diff > 32767 || diff < -32767)
+ break;
+
+ size += sizeof(ent) + strlen(c->name);
+
+ if (count > 0 && size > SQFS_META_BLOCK_SIZE)
+ break;
+
+ count += 1;
+ }
+
+ if (count > SQFS_MAX_DIR_ENT)
+ count = SQFS_MAX_DIR_ENT;
+
+ if (dir_index_grow(index))
+ return -1;
+
+ meta_writer_get_position(dm, &block, &offset);
+
+ i = (*index)->num_nodes++;
+ (*index)->idx_nodes[i].node = c;
+ (*index)->idx_nodes[i].block = block;
+ (*index)->idx_nodes[i].index = dir->size;
+
+ hdr.count = htole32(count - 1);
+ hdr.start_block = htole32(c->inode_ref >> 16);
+ hdr.inode_number = htole32(c->inode_num);
+ dir->size += sizeof(hdr);
+
+ if (meta_writer_append(dm, &hdr, sizeof(hdr)))
+ return -1;
+
+ d = c;
+
+ for (i = 0; i < count; ++i) {
+ ent.inode_diff = c->inode_num - d->inode_num;
+
+ diff_u16 = (uint16_t *)&ent.inode_diff;
+ *diff_u16 = htole16(*diff_u16);
+
+ ent.offset = htole16(c->inode_ref & 0x0000FFFF);
+ ent.type = htole16(get_type(c->mode));
+ ent.size = htole16(strlen(c->name) - 1);
+ dir->size += sizeof(ent) + strlen(c->name);
+
+ if (meta_writer_append(dm, &ent, sizeof(ent)))
+ return -1;
+ if (meta_writer_append(dm, c->name, strlen(c->name)))
+ return -1;
+
+ c = c->next;
+ }
+ }
+ return 0;
+}