aboutsummaryrefslogtreecommitdiff
path: root/lib/common
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-10-07 13:54:24 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-10-07 13:54:24 +0200
commit1fad07ce86fc2a506c59501d7fb7c7d7481525f6 (patch)
tree6ba41c514e2ddc692cb95a0fb2070dd222897c7c /lib/common
parent5597dca9c6053cd19104e18d88edb199b32e3743 (diff)
Rename libsqfshelper to libcommon
That is IMO less confusing and express what it is (i.e. what it has become) more clearly, i.e. common code shared by the utilities. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/common')
-rw-r--r--lib/common/Makemodule.am9
-rw-r--r--lib/common/comp_opt.c379
-rw-r--r--lib/common/compress.c33
-rw-r--r--lib/common/data_reader_dump.c80
-rw-r--r--lib/common/data_writer.c55
-rw-r--r--lib/common/get_path.c41
-rw-r--r--lib/common/inode_stat.c76
-rw-r--r--lib/common/io_stdin.c174
-rw-r--r--lib/common/perror.c67
-rw-r--r--lib/common/print_version.c29
-rw-r--r--lib/common/serialize_fstree.c220
-rw-r--r--lib/common/statistics.c88
-rw-r--r--lib/common/write_export_table.c43
-rw-r--r--lib/common/writer.c281
14 files changed, 1575 insertions, 0 deletions
diff --git a/lib/common/Makemodule.am b/lib/common/Makemodule.am
new file mode 100644
index 0000000..75f771d
--- /dev/null
+++ b/lib/common/Makemodule.am
@@ -0,0 +1,9 @@
+libcommon_a_SOURCES = lib/common/serialize_fstree.c lib/common/statistics.c
+libcommon_a_SOURCES += lib/common/write_export_table.c lib/common/inode_stat.c
+libcommon_a_SOURCES += lib/common/print_version.c lib/common/data_reader_dump.c
+libcommon_a_SOURCES += lib/common/compress.c lib/common/comp_opt.c
+libcommon_a_SOURCES += lib/common/data_writer.c include/common.h
+libcommon_a_SOURCES += lib/common/get_path.c lib/common/io_stdin.c
+libcommon_a_SOURCES += lib/common/writer.c lib/common/perror.c
+
+noinst_LIBRARIES += libcommon.a
diff --git a/lib/common/comp_opt.c b/lib/common/comp_opt.c
new file mode 100644
index 0000000..2b92da3
--- /dev/null
+++ b/lib/common/comp_opt.c
@@ -0,0 +1,379 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * comp_opt.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+typedef struct {
+ const char *name;
+ sqfs_u16 flag;
+} flag_t;
+
+static const flag_t gzip_flags[] = {
+ { "default", SQFS_COMP_FLAG_GZIP_DEFAULT },
+ { "filtered", SQFS_COMP_FLAG_GZIP_FILTERED },
+ { "huffman", SQFS_COMP_FLAG_GZIP_HUFFMAN },
+ { "rle", SQFS_COMP_FLAG_GZIP_RLE },
+ { "fixed", SQFS_COMP_FLAG_GZIP_FIXED },
+};
+
+static const flag_t xz_flags[] = {
+ { "x86", SQFS_COMP_FLAG_XZ_X86 },
+ { "powerpc", SQFS_COMP_FLAG_XZ_POWERPC },
+ { "ia64", SQFS_COMP_FLAG_XZ_IA64 },
+ { "arm", SQFS_COMP_FLAG_XZ_ARM },
+ { "armthumb", SQFS_COMP_FLAG_XZ_ARMTHUMB },
+ { "sparc", SQFS_COMP_FLAG_XZ_SPARC },
+};
+
+static const flag_t lz4_flags[] = {
+ { "hc", SQFS_COMP_FLAG_LZ4_HC },
+};
+
+static const char *lzo_algs[] = {
+ [SQFS_LZO1X_1] = "lzo1x_1",
+ [SQFS_LZO1X_1_11] = "lzo1x_1_11",
+ [SQFS_LZO1X_1_12] = "lzo1x_1_12",
+ [SQFS_LZO1X_1_15] = "lzo1x_1_15",
+ [SQFS_LZO1X_999] = "lzo1x_999",
+};
+
+static int set_flag(sqfs_compressor_config_t *cfg, const char *name,
+ const flag_t *flags, size_t num_flags)
+{
+ size_t i;
+
+ for (i = 0; i < num_flags; ++i) {
+ if (strcmp(flags[i].name, name) == 0) {
+ cfg->flags |= flags[i].flag;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int find_lzo_alg(sqfs_compressor_config_t *cfg, const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) {
+ if (strcmp(lzo_algs[i], name) == 0) {
+ cfg->opt.lzo.algorithm = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int get_size_value(const char *value, sqfs_u32 *out, sqfs_u32 block_size)
+{
+ int i;
+
+ for (i = 0; isdigit(value[i]) && i < 9; ++i)
+ ;
+
+ if (i < 1 || i > 9)
+ return -1;
+
+ *out = atol(value);
+
+ switch (value[i]) {
+ case '\0':
+ break;
+ case 'm':
+ case 'M':
+ *out <<= 20;
+ break;
+ case 'k':
+ case 'K':
+ *out <<= 10;
+ break;
+ case '%':
+ *out = (*out * block_size) / 100;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+enum {
+ OPT_WINDOW = 0,
+ OPT_LEVEL,
+ OPT_ALG,
+ OPT_DICT,
+};
+static char *const token[] = {
+ [OPT_WINDOW] = (char *)"window",
+ [OPT_LEVEL] = (char *)"level",
+ [OPT_ALG] = (char *)"algorithm",
+ [OPT_DICT] = (char *)"dictsize",
+ NULL
+};
+
+int compressor_cfg_init_options(sqfs_compressor_config_t *cfg,
+ E_SQFS_COMPRESSOR id,
+ size_t block_size, char *options)
+{
+ size_t num_flags = 0, min_level = 0, max_level = 0, level;
+ const flag_t *flags = NULL;
+ char *subopts, *value;
+ int i, opt;
+
+ if (sqfs_compressor_config_init(cfg, id, block_size, 0))
+ return -1;
+
+ if (options == NULL)
+ return 0;
+
+ switch (cfg->id) {
+ case SQFS_COMP_GZIP:
+ min_level = SQFS_GZIP_MIN_LEVEL;
+ max_level = SQFS_GZIP_MAX_LEVEL;
+ flags = gzip_flags;
+ num_flags = sizeof(gzip_flags) / sizeof(gzip_flags[0]);
+ break;
+ case SQFS_COMP_LZO:
+ min_level = SQFS_LZO_MIN_LEVEL;
+ max_level = SQFS_LZO_MAX_LEVEL;
+ break;
+ case SQFS_COMP_ZSTD:
+ min_level = SQFS_ZSTD_MIN_LEVEL;
+ max_level = SQFS_ZSTD_MAX_LEVEL;
+ break;
+ case SQFS_COMP_XZ:
+ flags = xz_flags;
+ num_flags = sizeof(xz_flags) / sizeof(xz_flags[0]);
+ break;
+ case SQFS_COMP_LZ4:
+ flags = lz4_flags;
+ num_flags = sizeof(lz4_flags) / sizeof(lz4_flags[0]);
+ break;
+ default:
+ break;
+ }
+
+ subopts = options;
+
+ while (*subopts != '\0') {
+ opt = getsubopt(&subopts, token, &value);
+
+ switch (opt) {
+ case OPT_WINDOW:
+ if (cfg->id != SQFS_COMP_GZIP)
+ goto fail_opt;
+
+ if (value == NULL)
+ goto fail_value;
+
+ for (i = 0; isdigit(value[i]); ++i)
+ ;
+
+ if (i < 1 || i > 3 || value[i] != '\0')
+ goto fail_window;
+
+ cfg->opt.gzip.window_size = atoi(value);
+
+ if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW ||
+ cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW)
+ goto fail_window;
+ break;
+ case OPT_LEVEL:
+ if (value == NULL)
+ goto fail_value;
+
+ for (i = 0; isdigit(value[i]) && i < 3; ++i)
+ ;
+
+ if (i < 1 || i > 3 || value[i] != '\0')
+ goto fail_level;
+
+ level = atoi(value);
+
+ if (level < min_level || level > max_level)
+ goto fail_level;
+
+ switch (cfg->id) {
+ case SQFS_COMP_GZIP:
+ cfg->opt.gzip.level = level;
+ break;
+ case SQFS_COMP_LZO:
+ cfg->opt.lzo.level = level;
+ break;
+ case SQFS_COMP_ZSTD:
+ cfg->opt.zstd.level = level;
+ break;
+ default:
+ goto fail_opt;
+ }
+ break;
+ case OPT_ALG:
+ if (cfg->id != SQFS_COMP_LZO)
+ goto fail_opt;
+
+ if (value == NULL)
+ goto fail_value;
+
+ if (find_lzo_alg(cfg, value))
+ goto fail_lzo_alg;
+ break;
+ case OPT_DICT:
+ if (cfg->id != SQFS_COMP_XZ)
+ goto fail_opt;
+
+ if (value == NULL)
+ goto fail_value;
+
+ if (get_size_value(value, &cfg->opt.xz.dict_size,
+ cfg->block_size)) {
+ goto fail_dict;
+ }
+ break;
+ default:
+ if (set_flag(cfg, value, flags, num_flags))
+ goto fail_opt;
+ break;
+ }
+ }
+
+ return 0;
+fail_lzo_alg:
+ fprintf(stderr, "Unknown lzo variant '%s'.\n", value);
+ return -1;
+fail_window:
+ fputs("Window size must be a number between 8 and 15.\n", stderr);
+ return -1;
+fail_level:
+ fprintf(stderr,
+ "Compression level must be a number between %zu and %zu.\n",
+ min_level, max_level);
+ return -1;
+fail_opt:
+ fprintf(stderr, "Unknown compressor option '%s'.\n", value);
+ return -1;
+fail_value:
+ fprintf(stderr, "Missing value for compressor option '%s'.\n",
+ token[opt]);
+ return -1;
+fail_dict:
+ fputs("Dictionary size must be a number with the optional "
+ "suffix 'm','k' or '%'.\n", stderr);
+ return -1;
+}
+
+typedef void (*compressor_help_fun_t)(void);
+
+static void gzip_print_help(void)
+{
+ size_t i;
+
+ printf(
+"Available options for gzip compressor:\n"
+"\n"
+" level=<value> Compression level. Value from 1 to 9.\n"
+" Defaults to %d.\n"
+" window=<size> Deflate compression window size. Value from 8 to 15.\n"
+" Defaults to %d.\n"
+"\n"
+"In additon to the options, one or more strategies can be specified.\n"
+"If multiple stratgies are provided, the one yielding the best compression\n"
+"ratio will be used.\n"
+"\n"
+"The following strategies are available:\n",
+ SQFS_GZIP_DEFAULT_LEVEL, SQFS_GZIP_DEFAULT_WINDOW);
+
+ for (i = 0; i < sizeof(gzip_flags) / sizeof(gzip_flags[0]); ++i)
+ printf("\t%s\n", gzip_flags[i].name);
+}
+
+static void lz4_print_help(void)
+{
+ fputs("Available options for lz4 compressor:\n"
+ "\n"
+ " hc If present, use slower but better compressing\n"
+ " variant of lz4.\n"
+ "\n",
+ stdout);
+}
+
+static void lzo_print_help(void)
+{
+ size_t i;
+
+ fputs("Available options for lzo compressor:\n"
+ "\n"
+ " algorithm=<name> Specify the variant of lzo to use.\n"
+ " Defaults to 'lzo1x_999'.\n"
+ " level=<value> For lzo1x_999, the compression level.\n"
+ " Value from 1 to 9. Defaults to 8.\n"
+ " Ignored if algorithm is not lzo1x_999.\n"
+ "\n"
+ "Available algorithms:\n",
+ stdout);
+
+ for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i)
+ printf("\t%s\n", lzo_algs[i]);
+}
+
+static void xz_print_help(void)
+{
+ size_t i;
+
+ fputs(
+"Available options for xz compressor:\n"
+"\n"
+" dictsize=<value> Dictionary size. Either a value in bytes or a\n"
+" percentage of the block size. Defaults to 100%.\n"
+" The suffix '%' indicates a percentage. 'K' and 'M'\n"
+" can also be used for kibi and mebi bytes\n"
+" respecitively.\n"
+"\n"
+"In additon to the options, one or more bcj filters can be specified.\n"
+"If multiple filters are provided, the one yielding the best compression\n"
+"ratio will be used.\n"
+"\n"
+"The following filters are available:\n",
+ stdout);
+
+ for (i = 0; i < sizeof(xz_flags) / sizeof(xz_flags[0]); ++i)
+ printf("\t%s\n", xz_flags[i].name);
+}
+
+static void zstd_print_help(void)
+{
+ printf("Available options for zstd compressor:\n"
+ "\n"
+ " level=<value> Set compression level. Defaults to %d.\n"
+ " Maximum is %d.\n"
+ "\n",
+ SQFS_ZSTD_DEFAULT_LEVEL, SQFS_ZSTD_MAX_LEVEL);
+}
+
+static const compressor_help_fun_t helpfuns[SQFS_COMP_MAX + 1] = {
+ [SQFS_COMP_GZIP] = gzip_print_help,
+ [SQFS_COMP_XZ] = xz_print_help,
+ [SQFS_COMP_LZO] = lzo_print_help,
+ [SQFS_COMP_LZ4] = lz4_print_help,
+ [SQFS_COMP_ZSTD] = zstd_print_help,
+};
+
+void compressor_print_help(E_SQFS_COMPRESSOR id)
+{
+ if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX)
+ return;
+
+ if (helpfuns[id] == NULL)
+ return;
+
+ helpfuns[id]();
+}
diff --git a/lib/common/compress.c b/lib/common/compress.c
new file mode 100644
index 0000000..04e1f40
--- /dev/null
+++ b/lib/common/compress.c
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * compress.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+E_SQFS_COMPRESSOR compressor_get_default(void)
+{
+ if (sqfs_compressor_exists(SQFS_COMP_XZ))
+ return SQFS_COMP_XZ;
+
+ if (sqfs_compressor_exists(SQFS_COMP_ZSTD))
+ return SQFS_COMP_ZSTD;
+
+ return SQFS_COMP_GZIP;
+}
+
+void compressor_print_available(void)
+{
+ int i;
+
+ fputs("Available compressors:\n", stdout);
+
+ for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) {
+ if (sqfs_compressor_exists(i))
+ printf("\t%s\n", sqfs_compressor_name_from_id(i));
+ }
+
+ printf("\nDefault compressor: %s\n",
+ sqfs_compressor_name_from_id(compressor_get_default()));
+}
diff --git a/lib/common/data_reader_dump.c b/lib/common/data_reader_dump.c
new file mode 100644
index 0000000..140f527
--- /dev/null
+++ b/lib/common/data_reader_dump.c
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * data_reader_dump.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+int sqfs_data_reader_dump(const char *name, sqfs_data_reader_t *data,
+ const sqfs_inode_generic_t *inode,
+ int outfd, size_t block_size, bool allow_sparse)
+{
+ sqfs_block_t *blk;
+ sqfs_u64 filesz;
+ size_t i, diff;
+ int err;
+
+ sqfs_inode_get_file_size(inode, &filesz);
+
+ if (allow_sparse && ftruncate(outfd, filesz))
+ goto fail_sparse;
+
+ for (i = 0; i < inode->num_file_blocks; ++i) {
+ if (SQFS_IS_SPARSE_BLOCK(inode->block_sizes[i]) &&
+ allow_sparse) {
+ if (filesz < block_size) {
+ diff = filesz;
+ filesz = 0;
+ } else {
+ diff = block_size;
+ filesz -= block_size;
+ }
+
+ if (lseek(outfd, diff, SEEK_CUR) == (off_t)-1)
+ goto fail_sparse;
+ } else {
+ err = sqfs_data_reader_get_block(data, inode, i, &blk);
+ if (err) {
+ sqfs_perror(name, "reading data block", err);
+ return -1;
+ }
+
+ if (write_data("writing uncompressed block",
+ outfd, blk->data, blk->size)) {
+ free(blk);
+ return -1;
+ }
+
+ filesz -= blk->size;
+ free(blk);
+ }
+ }
+
+ if (filesz > 0) {
+ err = sqfs_data_reader_get_fragment(data, inode, &blk);
+ if (err) {
+ sqfs_perror(name, "reading fragment block", err);
+ return -1;
+ }
+
+ if (write_data("writing uncompressed fragment", outfd,
+ blk->data, blk->size)) {
+ free(blk);
+ return -1;
+ }
+
+ free(blk);
+ }
+
+ return 0;
+fail_sparse:
+ perror("creating sparse output file");
+ return -1;
+}
diff --git a/lib/common/data_writer.c b/lib/common/data_writer.c
new file mode 100644
index 0000000..36de154
--- /dev/null
+++ b/lib/common/data_writer.c
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * data_writer.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+#include "util.h"
+
+static sqfs_u8 buffer[4096];
+
+int write_data_from_file(const char *filename, sqfs_data_writer_t *data,
+ sqfs_inode_generic_t *inode, sqfs_file_t *file,
+ int flags)
+{
+ sqfs_u64 filesz, offset;
+ size_t diff;
+ int ret;
+
+ ret = sqfs_data_writer_begin_file(data, inode, flags);
+ if (ret) {
+ sqfs_perror(filename, "beginning file data blocks", ret);
+ return -1;
+ }
+
+ sqfs_inode_get_file_size(inode, &filesz);
+
+ for (offset = 0; offset < filesz; offset += diff) {
+ if (filesz - offset > sizeof(buffer)) {
+ diff = sizeof(buffer);
+ } else {
+ diff = filesz - offset;
+ }
+
+ ret = file->read_at(file, offset, buffer, diff);
+ if (ret) {
+ sqfs_perror(filename, "reading file range", ret);
+ return -1;
+ }
+
+ ret = sqfs_data_writer_append(data, buffer, diff);
+ if (ret) {
+ sqfs_perror(filename, "packing file data", ret);
+ return -1;
+ }
+ }
+
+ ret = sqfs_data_writer_end_file(data);
+ if (ret) {
+ sqfs_perror(filename, "finishing file data", ret);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lib/common/get_path.c b/lib/common/get_path.c
new file mode 100644
index 0000000..bdc6c3f
--- /dev/null
+++ b/lib/common/get_path.c
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * get_path.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+char *sqfs_tree_node_get_path(const sqfs_tree_node_t *node)
+{
+ const sqfs_tree_node_t *it;
+ char *str, *ptr;
+ size_t len = 0;
+
+ if (node->parent == NULL)
+ return strdup("/");
+
+ for (it = node; it != NULL && it->parent != NULL; it = it->parent) {
+ len += strlen((const char *)it->name) + 1;
+ }
+
+ str = malloc(len + 1);
+ if (str == NULL)
+ return NULL;
+
+ ptr = str + len;
+ *ptr = '\0';
+
+ for (it = node; it != NULL && it->parent != NULL; it = it->parent) {
+ len = strlen((const char *)it->name);
+ ptr -= len;
+
+ memcpy(ptr, (const char *)it->name, len);
+ *(--ptr) = '/';
+ }
+
+ return str;
+}
diff --git a/lib/common/inode_stat.c b/lib/common/inode_stat.c
new file mode 100644
index 0000000..a73436b
--- /dev/null
+++ b/lib/common/inode_stat.c
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * inode_stat.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int inode_stat(const sqfs_tree_node_t *node, struct stat *sb)
+{
+ memset(sb, 0, sizeof(*sb));
+
+ sb->st_mode = node->inode->base.mode;
+ sb->st_uid = node->uid;
+ sb->st_gid = node->gid;
+ sb->st_atime = node->inode->base.mod_time;
+ sb->st_mtime = node->inode->base.mod_time;
+ sb->st_ctime = node->inode->base.mod_time;
+ sb->st_ino = node->inode->base.inode_number;
+ sb->st_nlink = 1;
+ sb->st_blksize = 512;
+
+ switch (node->inode->base.type) {
+ case SQFS_INODE_BDEV:
+ case SQFS_INODE_CDEV:
+ sb->st_rdev = node->inode->data.dev.devno;
+ sb->st_nlink = node->inode->data.dev.nlink;
+ break;
+ case SQFS_INODE_EXT_BDEV:
+ case SQFS_INODE_EXT_CDEV:
+ sb->st_rdev = node->inode->data.dev_ext.devno;
+ sb->st_nlink = node->inode->data.dev_ext.nlink;
+ break;
+ case SQFS_INODE_FIFO:
+ case SQFS_INODE_SOCKET:
+ sb->st_nlink = node->inode->data.ipc.nlink;
+ break;
+ case SQFS_INODE_EXT_FIFO:
+ case SQFS_INODE_EXT_SOCKET:
+ sb->st_nlink = node->inode->data.ipc_ext.nlink;
+ break;
+ case SQFS_INODE_SLINK:
+ sb->st_size = node->inode->data.slink.target_size;
+ sb->st_nlink = node->inode->data.slink.nlink;
+ break;
+ case SQFS_INODE_EXT_SLINK:
+ sb->st_size = node->inode->data.slink_ext.target_size;
+ sb->st_nlink = node->inode->data.slink_ext.nlink;
+ break;
+ case SQFS_INODE_FILE:
+ sb->st_size = node->inode->data.file.file_size;
+ break;
+ case SQFS_INODE_EXT_FILE:
+ sb->st_size = node->inode->data.file_ext.file_size;
+ sb->st_nlink = node->inode->data.file_ext.nlink;
+ break;
+ case SQFS_INODE_DIR:
+ sb->st_size = node->inode->data.dir.size;
+ sb->st_nlink = node->inode->data.dir.nlink;
+ break;
+ case SQFS_INODE_EXT_DIR:
+ sb->st_size = node->inode->data.dir_ext.size;
+ sb->st_nlink = node->inode->data.dir_ext.nlink;
+ break;
+ }
+
+ sb->st_blocks = sb->st_size / sb->st_blksize;
+ if (sb->st_size % sb->st_blksize)
+ sb->st_blocks += 1;
+
+ return 0;
+}
diff --git a/lib/common/io_stdin.c b/lib/common/io_stdin.c
new file mode 100644
index 0000000..0e9fb17
--- /dev/null
+++ b/lib/common/io_stdin.c
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * io_stdin.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+
+typedef struct {
+ sqfs_file_t base;
+
+ const sparse_map_t *map;
+ sqfs_u64 offset;
+ sqfs_u64 size;
+} sqfs_file_stdin_t;
+
+
+static void stdin_destroy(sqfs_file_t *base)
+{
+ free(base);
+}
+
+static int stdin_read_at(sqfs_file_t *base, sqfs_u64 offset,
+ void *buffer, size_t size)
+{
+ sqfs_file_stdin_t *file = (sqfs_file_stdin_t *)base;
+ size_t temp_size = 0;
+ sqfs_u8 *temp = NULL;
+ sqfs_u64 diff;
+ ssize_t ret;
+
+ if (offset < file->offset)
+ return SQFS_ERROR_IO;
+
+ if (offset > file->offset) {
+ temp_size = 1024;
+ temp = alloca(temp_size);
+ }
+
+ if (offset >= file->size || (offset + size) > file->size)
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ while (size > 0) {
+ if (offset > file->offset) {
+ diff = file->offset - offset;
+ diff = diff > (sqfs_u64)temp_size ? temp_size : diff;
+
+ ret = read(STDIN_FILENO, temp, diff);
+ } else {
+ ret = read(STDIN_FILENO, buffer, size);
+ }
+
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ return SQFS_ERROR_IO;
+ }
+
+ if (ret == 0)
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ if (offset <= file->offset) {
+ buffer = (char *)buffer + ret;
+ size -= ret;
+ offset += ret;
+ }
+
+ file->offset += ret;
+ }
+
+ return 0;
+}
+
+static int stdin_read_condensed(sqfs_file_t *base, sqfs_u64 offset,
+ void *buffer, size_t size)
+{
+ sqfs_file_stdin_t *file = (sqfs_file_stdin_t *)base;
+ sqfs_u64 poffset = 0, src_start;
+ size_t dst_start, diff, count;
+ const sparse_map_t *it;
+ int err;
+
+ memset(buffer, 0, size);
+
+ for (it = file->map; it != NULL; it = it->next) {
+ if (it->offset + it->count <= offset) {
+ poffset += it->count;
+ continue;
+ }
+
+ if (it->offset >= offset + size) {
+ poffset += it->count;
+ continue;
+ }
+
+ count = size;
+
+ if (offset + count >= it->offset + it->count)
+ count = it->offset + it->count - offset;
+
+ if (it->offset < offset) {
+ diff = offset - it->offset;
+
+ src_start = poffset + diff;
+ dst_start = 0;
+ count -= diff;
+ } else if (it->offset > offset) {
+ diff = it->offset - offset;
+
+ src_start = poffset;
+ dst_start = diff;
+ } else {
+ src_start = poffset;
+ dst_start = 0;
+ }
+
+ err = stdin_read_at(base, src_start,
+ (char *)buffer + dst_start, count);
+ if (err)
+ return err;
+
+ poffset += it->count;
+ }
+
+ return 0;
+}
+
+static int stdin_write_at(sqfs_file_t *base, sqfs_u64 offset,
+ const void *buffer, size_t size)
+{
+ (void)base; (void)offset; (void)buffer; (void)size;
+ return SQFS_ERROR_IO;
+}
+
+static sqfs_u64 stdin_get_size(const sqfs_file_t *base)
+{
+ return ((const sqfs_file_stdin_t *)base)->size;
+}
+
+static int stdin_truncate(sqfs_file_t *base, sqfs_u64 size)
+{
+ (void)base; (void)size;
+ return SQFS_ERROR_IO;
+}
+
+sqfs_file_t *sqfs_get_stdin_file(const sparse_map_t *map, sqfs_u64 size)
+{
+ sqfs_file_stdin_t *file = calloc(1, sizeof(*file));
+ sqfs_file_t *base = (sqfs_file_t *)file;
+
+ if (file == NULL)
+ return NULL;
+
+ file->size = size;
+ file->map = map;
+
+ base->destroy = stdin_destroy;
+ base->write_at = stdin_write_at;
+ base->get_size = stdin_get_size;
+ base->truncate = stdin_truncate;
+
+ if (map == NULL) {
+ base->read_at = stdin_read_at;
+ } else {
+ base->read_at = stdin_read_condensed;
+ }
+ return base;
+}
diff --git a/lib/common/perror.c b/lib/common/perror.c
new file mode 100644
index 0000000..9b9f041
--- /dev/null
+++ b/lib/common/perror.c
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * print_version.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <stdio.h>
+
+void sqfs_perror(const char *file, const char *action, int error_code)
+{
+ const char *errstr;
+
+ switch (error_code) {
+ case SQFS_ERROR_ALLOC:
+ errstr = "out of memory";
+ break;
+ case SQFS_ERROR_IO:
+ errstr = "I/O error";
+ break;
+ case SQFS_ERROR_COMPRESSOR:
+ errstr = "internal compressor error";
+ break;
+ case SQFS_ERROR_INTERNAL:
+ errstr = "internal error";
+ break;
+ case SQFS_ERROR_CORRUPTED:
+ errstr = "data corrupted";
+ break;
+ case SQFS_ERROR_UNSUPPORTED:
+ errstr = "unknown or not supported";
+ break;
+ case SQFS_ERROR_OVERFLOW:
+ errstr = "numeric overflow";
+ break;
+ case SQFS_ERROR_OUT_OF_BOUNDS:
+ errstr = "location out of bounds";
+ break;
+ case SFQS_ERROR_SUPER_MAGIC:
+ errstr = "wrong magic value in super block";
+ break;
+ case SFQS_ERROR_SUPER_VERSION:
+ errstr = "wrong squashfs version in super block";
+ break;
+ case SQFS_ERROR_SUPER_BLOCK_SIZE:
+ errstr = "invalid block size specified in super block";
+ break;
+ case SQFS_ERROR_NOT_DIR:
+ errstr = "target is not a directory";
+ break;
+ case SQFS_ERROR_NO_ENTRY:
+ errstr = "no such file or directory";
+ break;
+ case SQFS_ERROR_LINK_LOOP:
+ errstr = "hard link loop detected";
+ break;
+ case SQFS_ERROR_NOT_FILE:
+ errstr = "target is not a file";
+ break;
+ default:
+ errstr = "libsquashfs returned an unknown error code";
+ break;
+ }
+
+ fprintf(stderr, "%s: %s: %s.\n", file, action, errstr);
+}
diff --git a/lib/common/print_version.c b/lib/common/print_version.c
new file mode 100644
index 0000000..b23e2bd
--- /dev/null
+++ b/lib/common/print_version.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * print_version.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "util.h"
+
+#include <stdio.h>
+
+#define LICENSE_SHORT "GPLv3+"
+#define LICENSE_LONG "GNU GPL version 3 or later"
+#define LICENSE_URL "https://gnu.org/licenses/gpl.html"
+
+extern char *__progname;
+
+static const char *version_string =
+"%s (%s) %s\n"
+"Copyright (c) 2019 David Oberhollenzer et al\n"
+"License " LICENSE_SHORT ": " LICENSE_LONG " <" LICENSE_URL ">.\n"
+"This is free software: you are free to change and redistribute it.\n"
+"There is NO WARRANTY, to the extent permitted by law.\n";
+
+void print_version(void)
+{
+ printf(version_string, __progname, PACKAGE_NAME, PACKAGE_VERSION);
+}
diff --git a/lib/common/serialize_fstree.c b/lib/common/serialize_fstree.c
new file mode 100644
index 0000000..14f0a42
--- /dev/null
+++ b/lib/common/serialize_fstree.c
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * serialize_fstree.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+static sqfs_inode_generic_t *tree_node_to_inode(tree_node_t *node)
+{
+ sqfs_inode_generic_t *inode;
+ size_t extra = 0;
+
+ if (S_ISLNK(node->mode))
+ extra = strlen(node->data.slink_target);
+
+ inode = alloc_flex(sizeof(*inode), 1, extra);
+ if (inode == NULL) {
+ perror("creating inode");
+ return NULL;
+ }
+
+ switch (node->mode & S_IFMT) {
+ case S_IFSOCK:
+ inode->base.type = SQFS_INODE_SOCKET;
+ inode->data.ipc.nlink = 1;
+ break;
+ case S_IFIFO:
+ inode->base.type = SQFS_INODE_FIFO;
+ inode->data.ipc.nlink = 1;
+ break;
+ case S_IFLNK:
+ inode->base.type = SQFS_INODE_SLINK;
+ inode->data.slink.nlink = 1;
+ inode->data.slink.target_size = extra;
+ inode->slink_target = (char *)inode->extra;
+ memcpy(inode->extra, node->data.slink_target, extra);
+ break;
+ case S_IFBLK:
+ inode->base.type = SQFS_INODE_BDEV;
+ inode->data.dev.nlink = 1;
+ inode->data.dev.devno = node->data.devno;
+ break;
+ case S_IFCHR:
+ inode->base.type = SQFS_INODE_CDEV;
+ inode->data.dev.nlink = 1;
+ inode->data.dev.devno = node->data.devno;
+ break;
+ default:
+ assert(0);
+ }
+
+ return inode;
+}
+
+static sqfs_inode_generic_t *write_dir_entries(const char *filename,
+ sqfs_dir_writer_t *dirw,
+ tree_node_t *node)
+{
+ sqfs_u32 xattr, parent_inode;
+ sqfs_inode_generic_t *inode;
+ tree_node_t *it;
+ int ret;
+
+ ret = sqfs_dir_writer_begin(dirw, 0);
+ if (ret)
+ goto fail;
+
+ for (it = node->data.dir.children; it != NULL; it = it->next) {
+ ret = sqfs_dir_writer_add_entry(dirw, it->name, it->inode_num,
+ it->inode_ref, it->mode);
+ if (ret)
+ goto fail;
+ }
+
+ ret = sqfs_dir_writer_end(dirw);
+ if (ret)
+ goto fail;
+
+ xattr = node->xattr_idx;
+ parent_inode = (node->parent == NULL) ? 0 : node->parent->inode_num;
+
+ inode = sqfs_dir_writer_create_inode(dirw, 0, xattr, parent_inode);
+ if (inode == NULL) {
+ ret = SQFS_ERROR_ALLOC;
+ goto fail;
+ }
+
+ return inode;
+fail:
+ sqfs_perror(filename, "recoding directory entries", ret);
+ return NULL;
+}
+
+int sqfs_serialize_fstree(const char *filename, sqfs_file_t *file,
+ sqfs_super_t *super, fstree_t *fs,
+ sqfs_compressor_t *cmp, sqfs_id_table_t *idtbl)
+{
+ sqfs_inode_generic_t *inode;
+ sqfs_meta_writer_t *im, *dm;
+ sqfs_dir_writer_t *dirwr;
+ sqfs_u32 offset;
+ sqfs_u64 block;
+ tree_node_t *n;
+ int ret = -1;
+ size_t i;
+
+ im = sqfs_meta_writer_create(file, cmp, 0);
+ if (im == NULL) {
+ ret = SQFS_ERROR_ALLOC;
+ goto out_err;
+ }
+
+ dm = sqfs_meta_writer_create(file, cmp,
+ SQFS_META_WRITER_KEEP_IN_MEMORY);
+ if (dm == NULL) {
+ ret = SQFS_ERROR_ALLOC;
+ goto out_im;
+ }
+
+ dirwr = sqfs_dir_writer_create(dm);
+ if (dirwr == NULL) {
+ ret = SQFS_ERROR_ALLOC;
+ goto out_dm;
+ }
+
+ super->inode_table_start = file->get_size(file);
+
+ for (i = 0; i < fs->inode_tbl_size; ++i) {
+ n = fs->inode_table[i];
+
+ if (S_ISDIR(n->mode)) {
+ inode = write_dir_entries(filename, dirwr, n);
+
+ if (inode == NULL) {
+ ret = 1;
+ goto out;
+ }
+ } else if (S_ISREG(n->mode)) {
+ inode = n->data.file.user_ptr;
+ n->data.file.user_ptr = NULL;
+
+ if (inode == NULL) {
+ ret = SQFS_ERROR_INTERNAL;
+ goto out;
+ }
+ } else {
+ inode = tree_node_to_inode(n);
+
+ if (inode == NULL) {
+ ret = SQFS_ERROR_ALLOC;
+ goto out;
+ }
+ }
+
+ inode->base.mode = n->mode;
+ inode->base.mod_time = n->mod_time;
+ inode->base.inode_number = n->inode_num;
+
+ sqfs_inode_set_xattr_index(inode, n->xattr_idx);
+
+ ret = sqfs_id_table_id_to_index(idtbl, n->uid,
+ &inode->base.uid_idx);
+ if (ret) {
+ free(inode);
+ goto out;
+ }
+
+ ret = sqfs_id_table_id_to_index(idtbl, n->gid,
+ &inode->base.gid_idx);
+ if (ret) {
+ free(inode);
+ goto out;
+ }
+
+ sqfs_meta_writer_get_position(im, &block, &offset);
+ fs->inode_table[i]->inode_ref = (block << 16) | offset;
+
+ ret = sqfs_meta_writer_write_inode(im, inode);
+ free(inode);
+
+ if (ret)
+ goto out;
+ }
+
+ ret = sqfs_meta_writer_flush(im);
+ if (ret)
+ goto out;
+
+ ret = sqfs_meta_writer_flush(dm);
+ if (ret)
+ goto out;
+
+ super->root_inode_ref = fs->root->inode_ref;
+ super->directory_table_start = file->get_size(file);
+
+ ret = sqfs_meta_write_write_to_file(dm);
+ if (ret)
+ goto out;
+
+ ret = 0;
+out:
+ sqfs_dir_writer_destroy(dirwr);
+out_dm:
+ sqfs_meta_writer_destroy(dm);
+out_im:
+ sqfs_meta_writer_destroy(im);
+out_err:
+ if (ret < 0) {
+ sqfs_perror(filename, "storing filesystem tree",
+ ret);
+ }
+ return ret;
+}
diff --git a/lib/common/statistics.c b/lib/common/statistics.c
new file mode 100644
index 0000000..a209461
--- /dev/null
+++ b/lib/common/statistics.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * statistics.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <stdio.h>
+
+static void post_block_write(void *user, const sqfs_block_t *block,
+ sqfs_file_t *file)
+{
+ data_writer_stats_t *stats = user;
+ (void)file;
+
+ if (block->size == 0)
+ return;
+
+ if (block->flags & SQFS_BLK_FRAGMENT_BLOCK) {
+ stats->frag_blocks_written += 1;
+ } else {
+ stats->blocks_written += 1;
+ }
+
+ stats->bytes_written += block->size;
+}
+
+static void pre_fragment_store(void *user, sqfs_block_t *block)
+{
+ data_writer_stats_t *stats = user;
+ (void)block;
+
+ stats->frag_count += 1;
+}
+
+static void notify_blocks_erased(void *user, size_t count, sqfs_u64 bytes)
+{
+ data_writer_stats_t *stats = user;
+
+ stats->bytes_written -= bytes;
+ stats->blocks_written -= count;
+ stats->duplicate_blocks += count;
+}
+
+static void notify_fragment_discard(void *user, const sqfs_block_t *block)
+{
+ data_writer_stats_t *stats = user;
+ (void)block;
+
+ stats->frag_dup += 1;
+}
+
+static const sqfs_block_hooks_t hooks = {
+ .size = sizeof(hooks),
+ .post_block_write = post_block_write,
+ .pre_fragment_store = pre_fragment_store,
+ .notify_blocks_erased = notify_blocks_erased,
+ .notify_fragment_discard = notify_fragment_discard,
+};
+
+void register_stat_hooks(sqfs_data_writer_t *data, data_writer_stats_t *stats)
+{
+ sqfs_data_writer_set_hooks(data, stats, &hooks);
+}
+
+void sqfs_print_statistics(sqfs_super_t *super, data_writer_stats_t *stats)
+{
+ size_t ratio;
+
+ if (stats->bytes_written > 0) {
+ ratio = (100 * stats->bytes_written) / stats->bytes_read;
+ } else {
+ ratio = 100;
+ }
+
+ fputs("---------------------------------------------------\n", stdout);
+ printf("Input files processed: %zu\n", stats->file_count);
+ printf("Data blocks actually written: %zu\n", stats->blocks_written);
+ printf("Fragment blocks written: %zu\n", stats->frag_blocks_written);
+ printf("Duplicate data blocks omitted: %zu\n", stats->duplicate_blocks);
+ printf("Sparse blocks omitted: %zu\n", stats->sparse_blocks);
+ printf("Fragments actually written: %zu\n", stats->frag_count);
+ printf("Duplicated fragments omitted: %zu\n", stats->frag_dup);
+ printf("Total number of inodes: %u\n", super->inode_count);
+ printf("Number of unique group/user IDs: %u\n", super->id_count);
+ printf("Data compression ratio: %zu%%\n", ratio);
+}
diff --git a/lib/common/write_export_table.c b/lib/common/write_export_table.c
new file mode 100644
index 0000000..c797577
--- /dev/null
+++ b/lib/common/write_export_table.c
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * write_export_table.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int write_export_table(const char *filename, sqfs_file_t *file,
+ fstree_t *fs, sqfs_super_t *super,
+ sqfs_compressor_t *cmp)
+{
+ sqfs_u64 *table, start;
+ size_t i, size;
+ int ret;
+
+ if (fs->inode_tbl_size < 1)
+ return 0;
+
+ table = alloc_array(sizeof(sqfs_u64), fs->inode_tbl_size);
+
+ if (table == NULL) {
+ perror("Allocating NFS export table");
+ return -1;
+ }
+
+ for (i = 0; i < fs->inode_tbl_size; ++i) {
+ table[i] = htole64(fs->inode_table[i]->inode_ref);
+ }
+
+ size = sizeof(sqfs_u64) * fs->inode_tbl_size;
+ ret = sqfs_write_table(file, cmp, table, size, &start);
+ if (ret)
+ sqfs_perror(filename, "writing NFS export table", ret);
+
+ super->export_table_start = start;
+ super->flags |= SQFS_FLAG_EXPORTABLE;
+ free(table);
+ return ret;
+}
diff --git a/lib/common/writer.c b/lib/common/writer.c
new file mode 100644
index 0000000..fa732ad
--- /dev/null
+++ b/lib/common/writer.c
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * writer.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "common.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_SYSINFO_H
+#include <sys/sysinfo.h>
+
+static size_t os_get_num_jobs(void)
+{
+ int nprocs;
+
+ nprocs = get_nprocs_conf();
+ return nprocs < 1 ? 1 : nprocs;
+}
+
+static size_t os_get_max_ram(void)
+{
+ struct sysinfo info;
+
+ if (sysinfo(&info)) {
+ perror("sysinfo");
+ return 0;
+ }
+
+ return info.totalram;
+}
+#else
+static size_t os_get_num_jobs(void)
+{
+ return 1;
+}
+
+static size_t os_get_max_ram(void)
+{
+ (void)cfg;
+ return 0;
+}
+#endif
+
+static int padd_sqfs(sqfs_file_t *file, sqfs_u64 size, size_t blocksize)
+{
+ size_t padd_sz = size % blocksize;
+ int status = -1;
+ sqfs_u8 *buffer;
+
+ if (padd_sz == 0)
+ return 0;
+
+ padd_sz = blocksize - padd_sz;
+
+ buffer = calloc(1, padd_sz);
+ if (buffer == NULL)
+ goto fail_errno;
+
+ if (file->write_at(file, file->get_size(file),
+ buffer, padd_sz)) {
+ goto fail_errno;
+ }
+
+ status = 0;
+out:
+ free(buffer);
+ return status;
+fail_errno:
+ perror("padding output file to block size");
+ goto out;
+}
+
+void sqfs_writer_cfg_init(sqfs_writer_cfg_t *cfg)
+{
+ size_t max_ram;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ cfg->num_jobs = os_get_num_jobs();
+ cfg->block_size = SQFS_DEFAULT_BLOCK_SIZE;
+ cfg->devblksize = SQFS_DEVBLK_SIZE;
+ cfg->comp_id = compressor_get_default();
+
+ max_ram = os_get_max_ram();
+ cfg->max_backlog = (max_ram / 2) / cfg->block_size;
+
+ if (cfg->max_backlog < 1)
+ cfg->max_backlog = 10 * cfg->num_jobs;
+}
+
+int sqfs_writer_init(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *wrcfg)
+{
+ sqfs_compressor_config_t cfg;
+ int ret;
+
+ if (compressor_cfg_init_options(&cfg, wrcfg->comp_id,
+ wrcfg->block_size,
+ wrcfg->comp_extra)) {
+ return -1;
+ }
+
+ sqfs->outfile = sqfs_open_file(wrcfg->filename, wrcfg->outmode);
+ if (sqfs->outfile == NULL) {
+ perror(wrcfg->filename);
+ return -1;
+ }
+
+ if (fstree_init(&sqfs->fs, wrcfg->fs_defaults))
+ goto fail_file;
+
+ sqfs->cmp = sqfs_compressor_create(&cfg);
+ if (sqfs->cmp == NULL) {
+ fputs("Error creating compressor\n", stderr);
+ goto fail_fs;
+ }
+
+ ret = sqfs_super_init(&sqfs->super, wrcfg->block_size,
+ sqfs->fs.defaults.st_mtime, wrcfg->comp_id);
+ if (ret) {
+ sqfs_perror(wrcfg->filename, "initializing super block", ret);
+ goto fail_cmp;
+ }
+
+ ret = sqfs_super_write(&sqfs->super, sqfs->outfile);
+ if (ret) {
+ sqfs_perror(wrcfg->filename, "writing super block", ret);
+ goto fail_cmp;
+ }
+
+ ret = sqfs->cmp->write_options(sqfs->cmp, sqfs->outfile);
+ if (ret < 0) {
+ sqfs_perror(wrcfg->filename, "writing compressor options", ret);
+ goto fail_cmp;
+ }
+
+ if (ret > 0)
+ sqfs->super.flags |= SQFS_FLAG_COMPRESSOR_OPTIONS;
+
+ sqfs->data = sqfs_data_writer_create(sqfs->super.block_size,
+ sqfs->cmp, wrcfg->num_jobs,
+ wrcfg->max_backlog,
+ wrcfg->devblksize,
+ sqfs->outfile);
+ if (sqfs->data == NULL) {
+ perror("creating data block processor");
+ goto fail_cmp;
+ }
+
+ register_stat_hooks(sqfs->data, &sqfs->stats);
+
+ sqfs->idtbl = sqfs_id_table_create();
+ if (sqfs->idtbl == NULL) {
+ sqfs_perror(wrcfg->filename, "creating ID table",
+ SQFS_ERROR_ALLOC);
+ goto fail_data;
+ }
+
+ if (!wrcfg->no_xattr) {
+ sqfs->xwr = sqfs_xattr_writer_create();
+
+ if (sqfs->xwr == NULL) {
+ sqfs_perror(wrcfg->filename, "creating xattr writer",
+ SQFS_ERROR_ALLOC);
+ goto fail;
+ }
+ }
+
+ return 0;
+fail:
+ if (sqfs->xwr != NULL)
+ sqfs_xattr_writer_destroy(sqfs->xwr);
+ sqfs_id_table_destroy(sqfs->idtbl);
+fail_data:
+ sqfs_data_writer_destroy(sqfs->data);
+fail_cmp:
+ sqfs->cmp->destroy(sqfs->cmp);
+fail_fs:
+ fstree_cleanup(&sqfs->fs);
+fail_file:
+ sqfs->outfile->destroy(sqfs->outfile);
+ return -1;
+}
+
+int sqfs_writer_finish(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *cfg)
+{
+ int ret;
+
+ if (!cfg->quiet)
+ fputs("Waiting for remaining data blocks...\n", stdout);
+
+ ret = sqfs_data_writer_finish(sqfs->data);
+ if (ret) {
+ sqfs_perror(cfg->filename, "finishing data blocks", ret);
+ return -1;
+ }
+
+ if (!cfg->quiet)
+ fputs("Writing inodes and directories...\n", stdout);
+
+ tree_node_sort_recursive(sqfs->fs.root);
+ if (fstree_gen_inode_table(&sqfs->fs))
+ return -1;
+
+ sqfs->super.inode_count = sqfs->fs.inode_tbl_size;
+
+ if (sqfs_serialize_fstree(cfg->filename, sqfs->outfile, &sqfs->super,
+ &sqfs->fs, sqfs->cmp, sqfs->idtbl)) {
+ return -1;
+ }
+
+ if (!cfg->quiet)
+ fputs("Writing fragment table...\n", stdout);
+
+ ret = sqfs_data_writer_write_fragment_table(sqfs->data, &sqfs->super);
+ if (ret) {
+ sqfs_perror(cfg->filename, "writing fragment table", ret);
+ return -1;
+ }
+
+ if (cfg->exportable) {
+ if (!cfg->quiet)
+ fputs("Writing export table...\n", stdout);
+
+ if (write_export_table(cfg->filename, sqfs->outfile, &sqfs->fs,
+ &sqfs->super, sqfs->cmp)) {
+ return -1;
+ }
+ }
+
+ if (!cfg->quiet)
+ fputs("Writing ID table...\n", stdout);
+
+ ret = sqfs_id_table_write(sqfs->idtbl, sqfs->outfile,
+ &sqfs->super, sqfs->cmp);
+ if (ret) {
+ sqfs_perror(cfg->filename, "writing ID table", ret);
+ return -1;
+ }
+
+ if (!cfg->quiet)
+ fputs("Writing extended attributes...\n", stdout);
+
+ ret = sqfs_xattr_writer_flush(sqfs->xwr, sqfs->outfile,
+ &sqfs->super, sqfs->cmp);
+ if (ret) {
+ sqfs_perror(cfg->filename, "writing extended attributes", ret);
+ return -1;
+ }
+
+ sqfs->super.bytes_used = sqfs->outfile->get_size(sqfs->outfile);
+
+ ret = sqfs_super_write(&sqfs->super, sqfs->outfile);
+ if (ret) {
+ sqfs_perror(cfg->filename, "updating super block", ret);
+ return -1;
+ }
+
+ if (padd_sqfs(sqfs->outfile, sqfs->super.bytes_used,
+ cfg->devblksize)) {
+ return -1;
+ }
+
+ if (!cfg->quiet)
+ sqfs_print_statistics(&sqfs->super, &sqfs->stats);
+
+ return 0;
+}
+
+void sqfs_writer_cleanup(sqfs_writer_t *sqfs)
+{
+ if (sqfs->xwr != NULL)
+ sqfs_xattr_writer_destroy(sqfs->xwr);
+ sqfs_id_table_destroy(sqfs->idtbl);
+ sqfs_data_writer_destroy(sqfs->data);
+ sqfs->cmp->destroy(sqfs->cmp);
+ fstree_cleanup(&sqfs->fs);
+ sqfs->outfile->destroy(sqfs->outfile);
+}