summaryrefslogtreecommitdiff
path: root/bin/gensquashfs/sort_by_file.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-11-22 14:45:32 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-11-22 14:45:32 +0100
commit168ef9be32ad754d7bcb38ed70787237fc12630d (patch)
tree39d567fa667b2f170783329a07481769dd538d43 /bin/gensquashfs/sort_by_file.c
parent61a0dd71c4b69c21ee4aacdc0459dba58504a24a (diff)
Move gensquashfs specific code from libfstree to gensquashfs
The "from dir" and from "from file" code, as well as the "sort file" code is specific to gensquashfs, so move them there and the test cases as well. The medium term idea is to reduce libfstree to a stub, merge it into the generic writer and ultimately hoist that into libsquashfs. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'bin/gensquashfs/sort_by_file.c')
-rw-r--r--bin/gensquashfs/sort_by_file.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/bin/gensquashfs/sort_by_file.c b/bin/gensquashfs/sort_by_file.c
new file mode 100644
index 0000000..a555718
--- /dev/null
+++ b/bin/gensquashfs/sort_by_file.c
@@ -0,0 +1,368 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * sort_by_file.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "util/util.h"
+#include "fstree.h"
+#include "mkfs.h"
+
+#include "sqfs/block.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+static int decode_priority(const char *filename, size_t line_no,
+ char *line, sqfs_s64 *priority)
+{
+ bool negative = false;
+ size_t i = 0;
+
+ if (line[0] == '-') {
+ negative = true;
+ i = 1;
+ }
+
+ if (!isdigit(line[i]))
+ goto fail_number;
+
+ *priority = 0;
+
+ for (; isdigit(line[i]); ++i) {
+ sqfs_s64 x = line[i] - '0';
+
+ if ((*priority) >= ((0x7FFFFFFFFFFFFFFFL - x) / 10L))
+ goto fail_ov;
+
+ (*priority) = (*priority) * 10 + x;
+ }
+
+ if (!isspace(line[i]))
+ goto fail_filename;
+
+ while (isspace(line[i]))
+ ++i;
+
+ if (line[i] == '\0')
+ goto fail_filename;
+
+ if (negative)
+ (*priority) = -(*priority);
+
+ memmove(line, line + i, strlen(line + i) + 1);
+ return 0;
+fail_number:
+ fprintf(stderr, "%s: " PRI_SZ ": Line must start with "
+ "numeric sort priority.\n",
+ filename, line_no);
+ return -1;
+fail_ov:
+ fprintf(stderr, "%s: " PRI_SZ ": Numeric overflow in sort priority.\n",
+ filename, line_no);
+ return -1;
+fail_filename:
+ fprintf(stderr, "%s: " PRI_SZ ": Expacted `<space> <filename>` "
+ "after sort priority.\n",
+ filename, line_no);
+ return -1;
+}
+
+static int decode_filename(const char *filename, size_t line_no, char *buffer)
+{
+ char *src, *dst;
+
+ if (buffer[0] == '"') {
+ src = buffer + 1;
+ dst = buffer;
+
+ for (;;) {
+ if (src[0] == '\0')
+ goto fail_match;
+
+ if (src[0] == '"') {
+ ++src;
+ break;
+ }
+
+ if (src[0] == '\\') {
+ switch (src[1]) {
+ case '\\':
+ *(dst++) = '\\';
+ src += 2;
+ break;
+ case '"':
+ *(dst++) = '"';
+ src += 2;
+ break;
+ default:
+ goto fail_escape;
+ }
+ } else {
+ *(dst++) = *(src++);
+ }
+ }
+
+ if (*src != '\0')
+ return -1;
+ }
+
+ if (canonicalize_name(buffer))
+ goto fail_canon;
+ return 0;
+fail_canon:
+ fprintf(stderr, "%s: " PRI_SZ ": Malformed filename.\n",
+ filename, line_no);
+ return -1;
+fail_escape:
+ fprintf(stderr, "%s: " PRI_SZ ": Unknown escape sequence `\\%c` "
+ "in filename.\n", filename, line_no, src[1]);
+ return -1;
+fail_match:
+ fprintf(stderr, "%s: " PRI_SZ ": Unmatched '\"' in filename.\n",
+ filename, line_no);
+ return -1;
+}
+
+static int decode_flags(const char *filename, size_t line_no, bool *do_glob,
+ bool *path_glob, int *flags, char *line)
+{
+ char *start = line;
+
+ *do_glob = false;
+ *path_glob = false;
+ *flags = 0;
+
+ if (*(line++) != '[')
+ return 0;
+
+ for (;;) {
+ while (isspace(*line))
+ ++line;
+
+ if (*line == ']') {
+ ++line;
+ break;
+ }
+
+ if (strncmp(line, "glob_no_path", 12) == 0) {
+ line += 12;
+ *do_glob = true;
+ *path_glob = false;
+ } else if (strncmp(line, "glob", 4) == 0) {
+ line += 4;
+ *do_glob = true;
+ *path_glob = true;
+ } else if (strncmp(line, "dont_fragment", 13) == 0) {
+ line += 13;
+ (*flags) |= SQFS_BLK_DONT_FRAGMENT;
+ } else if (strncmp(line, "align", 5) == 0) {
+ line += 5;
+ (*flags) |= SQFS_BLK_ALIGN;
+ } else if (strncmp(line, "dont_compress", 13) == 0) {
+ line += 13;
+ (*flags) |= SQFS_BLK_DONT_COMPRESS;
+ } else if (strncmp(line, "dont_deduplicate", 16) == 0) {
+ line += 16;
+ (*flags) |= SQFS_BLK_DONT_DEDUPLICATE;
+ } else if (strncmp(line, "nosparse", 8) == 0) {
+ line += 8;
+ (*flags) |= SQFS_BLK_IGNORE_SPARSE;
+ } else {
+ goto fail_flag;
+ }
+
+ while (isspace(*line))
+ ++line;
+
+ if (*line == ']') {
+ ++line;
+ break;
+ }
+
+ if (*(line++) != ',')
+ goto fail_sep;
+ }
+
+ if (!isspace(*line))
+ goto fail_fname;
+
+ while (isspace(*line))
+ ++line;
+
+ memmove(start, line, strlen(line) + 1);
+ return 0;
+fail_fname:
+ fprintf(stderr, "%s: " PRI_SZ ": Expected `<space> <filename>` "
+ "after flag list.\n", filename, line_no);
+ return -1;
+fail_sep:
+ fprintf(stderr, "%s: " PRI_SZ ": Unexpected '%c' after flag.\n",
+ filename, line_no, *line);
+ return -1;
+fail_flag:
+ fprintf(stderr, "%s: " PRI_SZ ": Unknown flag `%.3s...`.\n",
+ filename, line_no, line);
+ return -1;
+}
+
+static void sort_file_list(fstree_t *fs)
+{
+ file_info_t *out = NULL, *out_last = NULL;
+
+ while (fs->files != NULL) {
+ sqfs_s64 lowest = fs->files->priority;
+ file_info_t *it, *prev;
+
+ for (it = fs->files; it != NULL; it = it->next) {
+ if (it->priority < lowest)
+ lowest = it->priority;
+ }
+
+ it = fs->files;
+ prev = NULL;
+
+ while (it != NULL) {
+ if (it->priority != lowest) {
+ prev = it;
+ it = it->next;
+ continue;
+ }
+
+ if (prev == NULL) {
+ fs->files = it->next;
+ } else {
+ prev->next = it->next;
+ }
+
+ if (out == NULL) {
+ out = it;
+ } else {
+ out_last->next = it;
+ }
+
+ out_last = it;
+ it = it->next;
+ out_last->next = NULL;
+ }
+ }
+
+ fs->files = out;
+}
+
+int fstree_sort_files(fstree_t *fs, istream_t *sortfile)
+{
+ const char *filename;
+ size_t line_num = 1;
+ file_info_t *it;
+
+ for (it = fs->files; it != NULL; it = it->next) {
+ it->priority = 0;
+ it->flags = 0;
+ it->already_matched = false;
+ }
+
+ filename = istream_get_filename(sortfile);
+
+ for (;;) {
+ bool do_glob, path_glob, have_match;
+ char *line = NULL;
+ sqfs_s64 priority;
+ int ret, flags;
+
+ ret = istream_get_line(sortfile, &line, &line_num,
+ ISTREAM_LINE_LTRIM |
+ ISTREAM_LINE_RTRIM |
+ ISTREAM_LINE_SKIP_EMPTY);
+ if (ret != 0) {
+ free(line);
+ if (ret < 0)
+ return -1;
+ break;
+ }
+
+ if (line[0] == '#') {
+ free(line);
+ continue;
+ }
+
+ if (decode_priority(filename, line_num, line, &priority)) {
+ free(line);
+ return -1;
+ }
+
+ if (decode_flags(filename, line_num, &do_glob, &path_glob,
+ &flags, line)) {
+ free(line);
+ return -1;
+ }
+
+ if (decode_filename(filename, line_num, line)) {
+ free(line);
+ return -1;
+ }
+
+ have_match = false;
+
+ for (it = fs->files; it != NULL; it = it->next) {
+ tree_node_t *node;
+ char *path;
+
+ if (it->already_matched)
+ continue;
+
+ node = container_of(it, tree_node_t, data.file);
+ path = fstree_get_path(node);
+ if (path == NULL) {
+ fprintf(stderr, "%s: " PRI_SZ ": out-of-memory\n",
+ filename, line_num);
+ free(line);
+ return -1;
+ }
+
+ if (canonicalize_name(path)) {
+ fprintf(stderr,
+ "%s: " PRI_SZ ": [BUG] error "
+ "reconstructing node path\n",
+ filename, line_num);
+ free(line);
+ free(path);
+ return -1;
+ }
+
+ if (do_glob) {
+ ret = fnmatch(line, path,
+ path_glob ? FNM_PATHNAME : 0);
+
+ } else {
+ ret = strcmp(path, line);
+ }
+
+ free(path);
+
+ if (ret == 0) {
+ have_match = true;
+ it->flags = flags;
+ it->priority = priority;
+ it->already_matched = true;
+
+ if (!do_glob)
+ break;
+ }
+ }
+
+ if (!have_match) {
+ fprintf(stderr, "WARNING: %s: " PRI_SZ ": no match "
+ "for '%s'.\n",
+ filename, line_num, line);
+ }
+
+ free(line);
+ }
+
+ sort_file_list(fs);
+ return 0;
+}