/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sort_by_file.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "fstree.h" #include "mkfs.h" #include "sqfs/block.h" #include #include #include 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 ` ` " "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 ` ` " "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; }