diff options
Diffstat (limited to 'lib/fstree')
-rw-r--r-- | lib/fstree/Makemodule.am | 6 | ||||
-rw-r--r-- | lib/fstree/fstree_from_dir.c | 493 | ||||
-rw-r--r-- | lib/fstree/fstree_from_file.c | 591 | ||||
-rw-r--r-- | lib/fstree/sort_by_file.c | 368 |
4 files changed, 2 insertions, 1456 deletions
diff --git a/lib/fstree/Makemodule.am b/lib/fstree/Makemodule.am index 4388bde..09cd9ac 100644 --- a/lib/fstree/Makemodule.am +++ b/lib/fstree/Makemodule.am @@ -1,9 +1,7 @@ -libfstree_a_SOURCES = lib/fstree/fstree.c lib/fstree/fstree_from_file.c -libfstree_a_SOURCES += lib/fstree/hardlink.c +libfstree_a_SOURCES = include/fstree.h lib/fstree/fstree.c libfstree_a_SOURCES += lib/fstree/post_process.c lib/fstree/get_path.c -libfstree_a_SOURCES += lib/fstree/mknode.c lib/fstree/fstree_from_dir.c +libfstree_a_SOURCES += lib/fstree/mknode.c lib/fstree/hardlink.c libfstree_a_SOURCES += lib/fstree/add_by_path.c lib/fstree/get_by_path.c -libfstree_a_SOURCES += include/fstree.h lib/fstree/sort_by_file.c libfstree_a_CFLAGS = $(AM_CFLAGS) libfstree_a_CPPFLAGS = $(AM_CPPFLAGS) diff --git a/lib/fstree/fstree_from_dir.c b/lib/fstree/fstree_from_dir.c deleted file mode 100644 index 01f331d..0000000 --- a/lib/fstree/fstree_from_dir.c +++ /dev/null @@ -1,493 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * fstree_from_dir.c - * - * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> - */ -#include "config.h" -#include "fstree.h" - -#include <dirent.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#if defined(_WIN32) || defined(__WINDOWS__) -#define UNIX_EPOCH_ON_W32 11644473600UL -#define W32_TICS_PER_SEC 10000000UL - -static sqfs_u32 w32time_to_sqfs_time(const FILETIME *ft) -{ - sqfs_u64 w32ts; - - w32ts = ft->dwHighDateTime; - w32ts <<= 32UL; - w32ts |= ft->dwLowDateTime; - - w32ts /= W32_TICS_PER_SEC; - - if (w32ts <= UNIX_EPOCH_ON_W32) - return 0; - - w32ts -= UNIX_EPOCH_ON_W32; - - return (w32ts < 0x0FFFFFFFFUL) ? w32ts : 0xFFFFFFFF; -} - -static int add_node(fstree_t *fs, tree_node_t *root, - scan_node_callback cb, void *user, - unsigned int flags, - const LPWIN32_FIND_DATAW entry) -{ - tree_node_t *n; - DWORD length; - - if (entry->cFileName[0] == '.') { - if (entry->cFileName[1] == '\0') - return 0; - - if (entry->cFileName[1] == '.' && entry->cFileName[2] == '\0') - return 0; - } - - length = WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, - -1, NULL, 0, NULL, NULL); - if (length <= 0) { - w32_perror("converting path to UTF-8"); - return -1; - } - - n = calloc(1, sizeof(*n) + length + 1); - if (n == NULL) { - fprintf(stderr, "creating tree node: out-of-memory\n"); - return -1; - } - - n->name = (char *)n->payload; - WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, -1, - n->name, length + 1, NULL, NULL); - - if (entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (flags & DIR_SCAN_NO_DIR) { - free(n); - return 0; - } - - n->mode = S_IFDIR | 0755; - } else { - if (flags & DIR_SCAN_NO_FILE) { - free(n); - return 0; - } - - n->mode = S_IFREG | 0644; - } - - if (cb != NULL) { - int ret = cb(user, fs, n); - - if (ret != 0) { - free(n); - return ret < 0 ? ret : 0; - } - } - - if (flags & DIR_SCAN_KEEP_TIME) { - n->mod_time = w32time_to_sqfs_time(&(entry->ftLastWriteTime)); - } else { - n->mod_time = fs->defaults.st_mtime; - } - - fstree_insert_sorted(root, n); - return 0; -} - -static int scan_dir(fstree_t *fs, tree_node_t *root, - const char *path, const WCHAR *wpath, - scan_node_callback cb, void *user, - unsigned int flags) -{ - WIN32_FIND_DATAW entry; - HANDLE dirhnd; - - dirhnd = FindFirstFileW(wpath, &entry); - - if (dirhnd == INVALID_HANDLE_VALUE) - goto fail_perror; - - do { - if (add_node(fs, root, cb, user, flags, &entry)) - goto fail; - } while (FindNextFileW(dirhnd, &entry)); - - if (GetLastError() != ERROR_NO_MORE_FILES) - goto fail_perror; - - FindClose(dirhnd); - return 0; -fail_perror: - w32_perror(path); -fail: - if (dirhnd != INVALID_HANDLE_VALUE) - FindClose(dirhnd); - return -1; -} - -int fstree_from_dir(fstree_t *fs, tree_node_t *root, - const char *path, scan_node_callback cb, - void *user, unsigned int flags) -{ - WCHAR *wpath = NULL, *new = NULL; - size_t len, newlen; - tree_node_t *n; - - /* path -> to_wchar(path) + L"\*" */ - wpath = path_to_windows(path); - if (wpath == NULL) { - fprintf(stderr, "%s: allocation failure.\n", path); - return -1; - } - - for (len = 0; wpath[len] != '\0'; ++len) - ; - - newlen = len + 1; - - if (len > 0 && wpath[len - 1] != '\\') - newlen += 1; - - new = realloc(wpath, sizeof(wpath[0]) * (newlen + 1)); - if (new == NULL) { - fprintf(stderr, "%s: allocation failure.\n", path); - goto fail; - } - - wpath = new; - - if (len > 0 && wpath[len - 1] != '\\') - wpath[len++] = '\\'; - - wpath[len++] = '*'; - wpath[len++] = '\0'; - - /* scan directory contents */ - if (scan_dir(fs, root, path, wpath, cb, user, flags)) - goto fail; - - free(wpath); - wpath = NULL; - - /* recursion step */ - if (flags & DIR_SCAN_NO_RECURSION) - return 0; - - for (n = root->data.dir.children; n != NULL; n = n->next) { - if (!S_ISDIR(n->mode)) - continue; - - if (fstree_from_subdir(fs, n, path, n->name, cb, user, flags)) - return -1; - } - - return 0; -fail: - free(wpath); - return -1; -} - -int fstree_from_subdir(fstree_t *fs, tree_node_t *root, - const char *path, const char *subdir, - scan_node_callback cb, void *user, - unsigned int flags) -{ - size_t len, plen, slen; - WCHAR *wpath = NULL; - char *temp = NULL; - tree_node_t *n; - - plen = strlen(path); - slen = subdir == NULL ? 0 : strlen(subdir); - - if (slen == 0) - return fstree_from_dir(fs, root, path, cb, user, flags); - - len = plen + 1 + slen + 2; - - temp = calloc(1, len + 1); - if (temp == NULL) { - fprintf(stderr, "%s/%s: allocation failure.\n", path, subdir); - return -1; - } - - memcpy(temp, path, plen); - temp[plen] = '/'; - memcpy(temp + plen + 1, subdir, slen); - temp[plen + 1 + slen ] = '/'; - temp[plen + 1 + slen + 1] = '*'; - temp[plen + 1 + slen + 2] = '\0'; - - wpath = path_to_windows(temp); - if (wpath == NULL) { - fprintf(stderr, "%s: allocation failure.\n", temp); - goto fail; - } - - if (scan_dir(fs, root, temp, wpath, cb, user, flags)) - goto fail; - - free(wpath); - wpath = NULL; - - if (flags & DIR_SCAN_NO_RECURSION) { - free(temp); - return 0; - } - - temp[plen + 1 + slen] = '\0'; - - for (n = root->data.dir.children; n != NULL; n = n->next) { - if (!S_ISDIR(n->mode)) - continue; - - if (fstree_from_subdir(fs, n, temp, n->name, cb, user, flags)) - goto fail; - } - - free(temp); - return 0; -fail: - free(temp); - free(wpath); - return -1; - -} -#else -static void discard_node(tree_node_t *root, tree_node_t *n) -{ - tree_node_t *it; - - if (n == root->data.dir.children) { - root->data.dir.children = n->next; - } else { - it = root->data.dir.children; - - while (it != NULL && it->next != n) - it = it->next; - - if (it != NULL) - it->next = n->next; - } - - free(n); -} - -static int populate_dir(int dir_fd, fstree_t *fs, tree_node_t *root, - dev_t devstart, scan_node_callback cb, - void *user, unsigned int flags) -{ - char *extra = NULL; - struct dirent *ent; - int ret, childfd; - struct stat sb; - tree_node_t *n; - DIR *dir; - - dir = fdopendir(dir_fd); - if (dir == NULL) { - perror("fdopendir"); - close(dir_fd); - return -1; - } - - /* XXX: fdopendir can dup and close dir_fd internally - and still be compliant with the spec. */ - dir_fd = dirfd(dir); - - for (;;) { - errno = 0; - ent = readdir(dir); - - if (ent == NULL) { - if (errno) { - perror("readdir"); - goto fail; - } - break; - } - - if (!strcmp(ent->d_name, "..") || !strcmp(ent->d_name, ".")) - continue; - - if (fstatat(dir_fd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { - perror(ent->d_name); - goto fail; - } - - switch (sb.st_mode & S_IFMT) { - case S_IFSOCK: - if (flags & DIR_SCAN_NO_SOCK) - continue; - break; - case S_IFLNK: - if (flags & DIR_SCAN_NO_SLINK) - continue; - break; - case S_IFREG: - if (flags & DIR_SCAN_NO_FILE) - continue; - break; - case S_IFBLK: - if (flags & DIR_SCAN_NO_BLK) - continue; - break; - case S_IFCHR: - if (flags & DIR_SCAN_NO_CHR) - continue; - break; - case S_IFIFO: - if (flags & DIR_SCAN_NO_FIFO) - continue; - break; - default: - break; - } - - if ((flags & DIR_SCAN_ONE_FILESYSTEM) && sb.st_dev != devstart) - continue; - - if (S_ISLNK(sb.st_mode)) { - size_t size; - - if ((sizeof(sb.st_size) > sizeof(size_t)) && - sb.st_size > SIZE_MAX) { - errno = EOVERFLOW; - goto fail_rdlink; - } - - if (SZ_ADD_OV((size_t)sb.st_size, 1, &size)) { - errno = EOVERFLOW; - goto fail_rdlink; - } - - extra = calloc(1, size); - if (extra == NULL) - goto fail_rdlink; - - if (readlinkat(dir_fd, ent->d_name, - extra, (size_t)sb.st_size) < 0) { - goto fail_rdlink; - } - - extra[sb.st_size] = '\0'; - } - - if (!(flags & DIR_SCAN_KEEP_TIME)) - sb.st_mtime = fs->defaults.st_mtime; - - if (S_ISDIR(sb.st_mode) && (flags & DIR_SCAN_NO_DIR)) { - n = fstree_get_node_by_path(fs, root, ent->d_name, - false, false); - if (n == NULL) - continue; - - ret = 0; - } else { - n = fstree_mknode(root, ent->d_name, - strlen(ent->d_name), extra, &sb); - if (n == NULL) { - perror("creating tree node"); - goto fail; - } - - ret = (cb == NULL) ? 0 : cb(user, fs, n); - } - - free(extra); - extra = NULL; - - if (ret < 0) - goto fail; - - if (ret > 0) { - discard_node(root, n); - continue; - } - - if (S_ISDIR(n->mode) && !(flags & DIR_SCAN_NO_RECURSION)) { - childfd = openat(dir_fd, n->name, O_DIRECTORY | - O_RDONLY | O_CLOEXEC); - if (childfd < 0) { - perror(n->name); - goto fail; - } - - if (populate_dir(childfd, fs, n, devstart, - cb, user, flags)) { - goto fail; - } - } - } - - closedir(dir); - return 0; -fail_rdlink: - perror("readlink"); -fail: - closedir(dir); - free(extra); - return -1; -} - -int fstree_from_subdir(fstree_t *fs, tree_node_t *root, - const char *path, const char *subdir, - scan_node_callback cb, void *user, - unsigned int flags) -{ - struct stat sb; - int fd, subfd; - - if (!S_ISDIR(root->mode)) { - fprintf(stderr, - "scanning %s/%s into %s: target is not a directory\n", - path, subdir == NULL ? "" : subdir, root->name); - return -1; - } - - fd = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC); - if (fd < 0) { - perror(path); - return -1; - } - - if (subdir != NULL) { - subfd = openat(fd, subdir, O_DIRECTORY | O_RDONLY | O_CLOEXEC); - - if (subfd < 0) { - fprintf(stderr, "%s/%s: %s\n", path, subdir, - strerror(errno)); - close(fd); - return -1; - } - - close(fd); - fd = subfd; - } - - if (fstat(fd, &sb)) { - fprintf(stderr, "%s/%s: %s\n", path, - subdir == NULL ? "" : subdir, - strerror(errno)); - close(fd); - return -1; - } - - return populate_dir(fd, fs, root, sb.st_dev, cb, user, flags); -} - -int fstree_from_dir(fstree_t *fs, tree_node_t *root, - const char *path, scan_node_callback cb, - void *user, unsigned int flags) -{ - return fstree_from_subdir(fs, root, path, NULL, cb, user, flags); -} -#endif diff --git a/lib/fstree/fstree_from_file.c b/lib/fstree/fstree_from_file.c deleted file mode 100644 index 411c64f..0000000 --- a/lib/fstree/fstree_from_file.c +++ /dev/null @@ -1,591 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * fstree_from_file.c - * - * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> - */ -#include "config.h" - -#include "util/util.h" -#include "io/file.h" -#include "fstree.h" -#include "compat.h" - -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <ctype.h> - -struct glob_context { - const char *filename; - size_t line_num; - - struct stat *basic; - unsigned int glob_flags; - - char *name_pattern; -}; - -enum { - GLOB_MODE_FROM_SRC = 0x01, - GLOB_UID_FROM_SRC = 0x02, - GLOB_GID_FROM_SRC = 0x04, - GLOB_FLAG_PATH = 0x08, -}; - -static const struct { - const char *name; - unsigned int clear_flag; - unsigned int set_flag; -} glob_scan_flags[] = { - { "-type b", DIR_SCAN_NO_BLK, 0 }, - { "-type c", DIR_SCAN_NO_CHR, 0 }, - { "-type d", DIR_SCAN_NO_DIR, 0 }, - { "-type p", DIR_SCAN_NO_FIFO, 0 }, - { "-type f", DIR_SCAN_NO_FILE, 0 }, - { "-type l", DIR_SCAN_NO_SLINK, 0 }, - { "-type s", DIR_SCAN_NO_SOCK, 0 }, - { "-xdev", 0, DIR_SCAN_ONE_FILESYSTEM }, - { "-mount", 0, DIR_SCAN_ONE_FILESYSTEM }, - { "-keeptime", 0, DIR_SCAN_KEEP_TIME }, - { "-nonrecursive", 0, DIR_SCAN_NO_RECURSION }, -}; - -static int add_generic(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *sb, - const char *basepath, unsigned int glob_flags, - const char *extra) -{ - (void)basepath; - (void)glob_flags; - - if (fstree_add_generic(fs, path, sb, extra) == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", - filename, line_num, path, strerror(errno)); - return -1; - } - - return 0; -} - -static int add_device(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *sb, const char *basepath, - unsigned int glob_flags, const char *extra) -{ - unsigned int maj, min; - char c; - - if (sscanf(extra, "%c %u %u", &c, &maj, &min) != 3) { - fprintf(stderr, "%s: " PRI_SZ ": " - "expected '<c|b> major minor'\n", - filename, line_num); - return -1; - } - - if (c == 'c' || c == 'C') { - sb->st_mode |= S_IFCHR; - } else if (c == 'b' || c == 'B') { - sb->st_mode |= S_IFBLK; - } else { - fprintf(stderr, "%s: " PRI_SZ ": unknown device type '%c'\n", - filename, line_num, c); - return -1; - } - - sb->st_rdev = makedev(maj, min); - return add_generic(fs, filename, line_num, path, sb, basepath, - glob_flags, NULL); -} - -static int add_file(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *basic, const char *basepath, - unsigned int glob_flags, const char *extra) -{ - if (extra == NULL || *extra == '\0') - extra = path; - - return add_generic(fs, filename, line_num, path, basic, - basepath, glob_flags, extra); -} - -static int add_hard_link(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *basic, - const char *basepath, unsigned int glob_flags, - const char *extra) -{ - (void)basepath; - (void)glob_flags; - (void)basic; - - if (fstree_add_hard_link(fs, path, extra) == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s\n", - filename, line_num, strerror(errno)); - return -1; - } - return 0; -} - -static int glob_node_callback(void *user, fstree_t *fs, tree_node_t *node) -{ - struct glob_context *ctx = user; - char *path; - int ret; - (void)fs; - - if (!(ctx->glob_flags & GLOB_MODE_FROM_SRC)) { - node->mode &= ~(07777); - node->mode |= ctx->basic->st_mode & 07777; - } - - if (!(ctx->glob_flags & GLOB_UID_FROM_SRC)) - node->uid = ctx->basic->st_uid; - - if (!(ctx->glob_flags & GLOB_GID_FROM_SRC)) - node->gid = ctx->basic->st_gid; - - if (ctx->name_pattern != NULL) { - if (ctx->glob_flags & GLOB_FLAG_PATH) { - path = fstree_get_path(node); - if (path == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s\n", - ctx->filename, ctx->line_num, - strerror(errno)); - return -1; - } - - ret = canonicalize_name(path); - assert(ret == 0); - - ret = fnmatch(ctx->name_pattern, path, FNM_PATHNAME); - free(path); - } else { - ret = fnmatch(ctx->name_pattern, node->name, 0); - } - - if (ret != 0) - return 1; - } - - return 0; -} - -static size_t name_string_length(const char *str) -{ - size_t len = 0; - int start; - - if (*str == '"' || *str == '\'') { - start = *str; - ++len; - - while (str[len] != '\0' && str[len] != start) - ++len; - - if (str[len] == start) - ++len; - } else { - while (str[len] != '\0' && !isspace(str[len])) - ++len; - } - - return len; -} - -static void quote_remove(char *str) -{ - char *dst = str; - int start = *(str++); - - if (start != '\'' && start != '"') - return; - - while (*str != start && *str != '\0') - *(dst++) = *(str++); - - *(dst++) = '\0'; -} - -static int glob_files(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *basic, - const char *basepath, unsigned int glob_flags, - const char *extra) -{ - unsigned int scan_flags = 0, all_flags; - struct glob_context ctx; - bool first_clear_flag; - size_t i, count, len; - tree_node_t *root; - int ret; - - memset(&ctx, 0, sizeof(ctx)); - ctx.filename = filename; - ctx.line_num = line_num; - ctx.basic = basic; - ctx.glob_flags = glob_flags; - - /* fetch the actual target node */ - root = fstree_get_node_by_path(fs, fs->root, path, true, false); - if (root == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", - filename, line_num, path, strerror(errno)); - return -1; - } - - /* process options */ - first_clear_flag = true; - - all_flags = DIR_SCAN_NO_BLK | DIR_SCAN_NO_CHR | DIR_SCAN_NO_DIR | - DIR_SCAN_NO_FIFO | DIR_SCAN_NO_FILE | DIR_SCAN_NO_SLINK | - DIR_SCAN_NO_SOCK; - - while (extra != NULL && *extra != '\0') { - count = sizeof(glob_scan_flags) / sizeof(glob_scan_flags[0]); - - for (i = 0; i < count; ++i) { - len = strlen(glob_scan_flags[i].name); - if (strncmp(extra, glob_scan_flags[i].name, len) != 0) - continue; - - if (isspace(extra[len])) { - extra += len; - while (isspace(*extra)) - ++extra; - break; - } - } - - if (i < count) { - if (glob_scan_flags[i].clear_flag != 0 && - first_clear_flag) { - scan_flags |= all_flags; - first_clear_flag = false; - } - - scan_flags &= ~(glob_scan_flags[i].clear_flag); - scan_flags |= glob_scan_flags[i].set_flag; - continue; - } - - if (strncmp(extra, "-name", 5) == 0 && isspace(extra[5])) { - for (extra += 5; isspace(*extra); ++extra) - ; - - len = name_string_length(extra); - - free(ctx.name_pattern); - ctx.name_pattern = strndup(extra, len); - extra += len; - - while (isspace(*extra)) - ++extra; - - quote_remove(ctx.name_pattern); - continue; - } - - if (strncmp(extra, "-path", 5) == 0 && isspace(extra[5])) { - for (extra += 5; isspace(*extra); ++extra) - ; - - len = name_string_length(extra); - - free(ctx.name_pattern); - ctx.name_pattern = strndup(extra, len); - extra += len; - - while (isspace(*extra)) - ++extra; - - quote_remove(ctx.name_pattern); - ctx.glob_flags |= GLOB_FLAG_PATH; - continue; - } - - if (extra[0] == '-') { - if (extra[1] == '-' && isspace(extra[2])) { - extra += 2; - while (isspace(*extra)) - ++extra; - break; - } - - fprintf(stderr, "%s: " PRI_SZ ": unknown option.\n", - filename, line_num); - free(ctx.name_pattern); - return -1; - } else { - break; - } - } - - if (extra != NULL && *extra == '\0') - extra = NULL; - - /* do the scan */ - if (basepath == NULL) { - if (extra == NULL) { - ret = fstree_from_dir(fs, root, ".", glob_node_callback, - &ctx, scan_flags); - } else { - ret = fstree_from_dir(fs, root, extra, - glob_node_callback, - &ctx, scan_flags); - } - } else { - ret = fstree_from_subdir(fs, root, basepath, extra, - glob_node_callback, &ctx, - scan_flags); - } - - free(ctx.name_pattern); - return ret; -} - -static const struct callback_t { - const char *keyword; - unsigned int mode; - bool need_extra; - bool is_glob; - bool allow_root; - int (*callback)(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *sb, - const char *basepath, unsigned int glob_flags, - const char *extra); -} file_list_hooks[] = { - { "dir", S_IFDIR, false, false, true, add_generic }, - { "slink", S_IFLNK, true, false, false, add_generic }, - { "link", 0, true, false, false, add_hard_link }, - { "nod", 0, true, false, false, add_device }, - { "pipe", S_IFIFO, false, false, false, add_generic }, - { "sock", S_IFSOCK, false, false, false, add_generic }, - { "file", S_IFREG, false, false, false, add_file }, - { "glob", 0, false, true, true, glob_files }, -}; - -#define NUM_HOOKS (sizeof(file_list_hooks) / sizeof(file_list_hooks[0])) - -static char *skip_space(char *str) -{ - if (!isspace(*str)) - return NULL; - while (isspace(*str)) - ++str; - return str; -} - -static char *read_u32(char *str, sqfs_u32 *out, sqfs_u32 base) -{ - *out = 0; - - if (!isdigit(*str)) - return NULL; - - while (isdigit(*str)) { - sqfs_u32 x = *(str++) - '0'; - - if (x >= base || (*out) > (0xFFFFFFFF - x) / base) - return NULL; - - (*out) = (*out) * base + x; - } - - return str; -} - -static char *read_str(char *str, char **out) -{ - *out = str; - - if (*str == '"') { - char *ptr = str++; - - while (*str != '\0' && *str != '"') { - if (str[0] == '\\' && - (str[1] == '"' || str[1] == '\\')) { - *(ptr++) = str[1]; - str += 2; - } else { - *(ptr++) = *(str++); - } - } - - if (str[0] != '"' || !isspace(str[1])) - return NULL; - - *ptr = '\0'; - ++str; - } else { - while (*str != '\0' && !isspace(*str)) - ++str; - - if (!isspace(*str)) - return NULL; - - *(str++) = '\0'; - } - - while (isspace(*str)) - ++str; - - return str; -} - -static int handle_line(fstree_t *fs, const char *filename, - size_t line_num, char *line, - const char *basepath) -{ - const char *extra = NULL, *msg = NULL; - const struct callback_t *cb = NULL; - unsigned int glob_flags = 0; - sqfs_u32 uid, gid, mode; - struct stat sb; - char *path; - - for (size_t i = 0; i < NUM_HOOKS; ++i) { - size_t len = strlen(file_list_hooks[i].keyword); - if (strncmp(file_list_hooks[i].keyword, line, len) != 0) - continue; - - if (isspace(line[len])) { - cb = file_list_hooks + i; - line = skip_space(line + len); - break; - } - } - - if (cb == NULL) - goto fail_kw; - - if ((line = read_str(line, &path)) == NULL) - goto fail_ent; - - if (canonicalize_name(path)) - goto fail_ent; - - if (*path == '\0' && !cb->allow_root) - goto fail_root; - - if (cb->is_glob && *line == '*') { - ++line; - mode = 0; - glob_flags |= GLOB_MODE_FROM_SRC; - } else { - if ((line = read_u32(line, &mode, 8)) == NULL || mode > 07777) - goto fail_mode; - } - - if ((line = skip_space(line)) == NULL) - goto fail_ent; - - if (cb->is_glob && *line == '*') { - ++line; - uid = 0; - glob_flags |= GLOB_UID_FROM_SRC; - } else { - if ((line = read_u32(line, &uid, 10)) == NULL) - goto fail_uid_gid; - } - - if ((line = skip_space(line)) == NULL) - goto fail_ent; - - if (cb->is_glob && *line == '*') { - ++line; - gid = 0; - glob_flags |= GLOB_GID_FROM_SRC; - } else { - if ((line = read_u32(line, &gid, 10)) == NULL) - goto fail_uid_gid; - } - - if ((line = skip_space(line)) != NULL && *line != '\0') - extra = line; - - if (cb->need_extra && extra == NULL) - goto fail_no_extra; - - /* forward to callback */ - memset(&sb, 0, sizeof(sb)); - sb.st_mtime = fs->defaults.st_mtime; - sb.st_mode = mode | cb->mode; - sb.st_uid = uid; - sb.st_gid = gid; - - return cb->callback(fs, filename, line_num, path, - &sb, basepath, glob_flags, extra); -fail_root: - fprintf(stderr, "%s: " PRI_SZ ": cannot use / as argument for %s.\n", - filename, line_num, cb->keyword); - return -1; -fail_no_extra: - fprintf(stderr, "%s: " PRI_SZ ": missing argument for %s.\n", - filename, line_num, cb->keyword); - return -1; -fail_uid_gid: - msg = "uid & gid must be decimal numbers < 2^32"; - goto out_desc; -fail_mode: - msg = "mode must be an octal number <= 07777"; - goto out_desc; -fail_kw: - msg = "unknown entry type"; - goto out_desc; -fail_ent: - msg = "error in entry description"; - goto out_desc; -out_desc: - fprintf(stderr, "%s: " PRI_SZ ": %s.\n", filename, line_num, msg); - fputs("expected: <type> <path> <mode> <uid> <gid> [<extra>]\n", - stderr); - return -1; -} - -int fstree_from_file_stream(fstree_t *fs, istream_t *fp, const char *basepath) -{ - const char *filename; - size_t line_num = 1; - char *line; - int ret; - - filename = istream_get_filename(fp); - - for (;;) { - ret = istream_get_line(fp, &line, &line_num, - ISTREAM_LINE_LTRIM | ISTREAM_LINE_SKIP_EMPTY); - if (ret < 0) - return -1; - if (ret > 0) - break; - - if (line[0] != '#') { - if (handle_line(fs, filename, line_num, - line, basepath)) { - goto fail_line; - } - } - - free(line); - ++line_num; - } - - return 0; -fail_line: - free(line); - return -1; -} - -int fstree_from_file(fstree_t *fs, const char *filename, const char *basepath) -{ - istream_t *fp; - int ret; - - fp = istream_open_file(filename); - if (fp == NULL) - return -1; - - ret = fstree_from_file_stream(fs, fp, basepath); - - sqfs_destroy(fp); - return ret; -} diff --git a/lib/fstree/sort_by_file.c b/lib/fstree/sort_by_file.c deleted file mode 100644 index ed4a58c..0000000 --- a/lib/fstree/sort_by_file.c +++ /dev/null @@ -1,368 +0,0 @@ -/* 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 "compat.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; -} |