From cdccc69c62579b0c13b35fad0728079652b8f3c9 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 31 Jan 2023 11:21:30 +0100 Subject: Move library source into src sub-directory Signed-off-by: David Oberhollenzer --- bin/gensquashfs/Makemodule.am | 13 +- bin/gensquashfs/dirscan_xattr.c | 220 ------------ bin/gensquashfs/filemap_xattr.c | 254 -------------- bin/gensquashfs/fstree_from_dir.c | 493 --------------------------- bin/gensquashfs/fstree_from_file.c | 591 --------------------------------- bin/gensquashfs/mkfs.c | 215 ------------ bin/gensquashfs/mkfs.h | 137 -------- bin/gensquashfs/options.c | 383 --------------------- bin/gensquashfs/selinux.c | 78 ----- bin/gensquashfs/sort_by_file.c | 368 -------------------- bin/gensquashfs/src/dirscan_xattr.c | 220 ++++++++++++ bin/gensquashfs/src/filemap_xattr.c | 254 ++++++++++++++ bin/gensquashfs/src/fstree_from_dir.c | 493 +++++++++++++++++++++++++++ bin/gensquashfs/src/fstree_from_file.c | 591 +++++++++++++++++++++++++++++++++ bin/gensquashfs/src/mkfs.c | 215 ++++++++++++ bin/gensquashfs/src/mkfs.h | 137 ++++++++ bin/gensquashfs/src/options.c | 383 +++++++++++++++++++++ bin/gensquashfs/src/selinux.c | 78 +++++ bin/gensquashfs/src/sort_by_file.c | 368 ++++++++++++++++++++ 19 files changed, 2745 insertions(+), 2746 deletions(-) delete mode 100644 bin/gensquashfs/dirscan_xattr.c delete mode 100644 bin/gensquashfs/filemap_xattr.c delete mode 100644 bin/gensquashfs/fstree_from_dir.c delete mode 100644 bin/gensquashfs/fstree_from_file.c delete mode 100644 bin/gensquashfs/mkfs.c delete mode 100644 bin/gensquashfs/mkfs.h delete mode 100644 bin/gensquashfs/options.c delete mode 100644 bin/gensquashfs/selinux.c delete mode 100644 bin/gensquashfs/sort_by_file.c create mode 100644 bin/gensquashfs/src/dirscan_xattr.c create mode 100644 bin/gensquashfs/src/filemap_xattr.c create mode 100644 bin/gensquashfs/src/fstree_from_dir.c create mode 100644 bin/gensquashfs/src/fstree_from_file.c create mode 100644 bin/gensquashfs/src/mkfs.c create mode 100644 bin/gensquashfs/src/mkfs.h create mode 100644 bin/gensquashfs/src/options.c create mode 100644 bin/gensquashfs/src/selinux.c create mode 100644 bin/gensquashfs/src/sort_by_file.c (limited to 'bin/gensquashfs') diff --git a/bin/gensquashfs/Makemodule.am b/bin/gensquashfs/Makemodule.am index c6a98a2..7edc39a 100644 --- a/bin/gensquashfs/Makemodule.am +++ b/bin/gensquashfs/Makemodule.am @@ -1,10 +1,9 @@ -gensquashfs_SOURCES = bin/gensquashfs/mkfs.c bin/gensquashfs/mkfs.h -gensquashfs_SOURCES += bin/gensquashfs/options.c bin/gensquashfs/selinux.c -gensquashfs_SOURCES += bin/gensquashfs/dirscan_xattr.c -gensquashfs_SOURCES += bin/gensquashfs/filemap_xattr.c -gensquashfs_SOURCES += bin/gensquashfs/fstree_from_file.c -gensquashfs_SOURCES += bin/gensquashfs/fstree_from_dir.c -gensquashfs_SOURCES += bin/gensquashfs/sort_by_file.c +gensquashfs_SOURCES = bin/gensquashfs/src/mkfs.c bin/gensquashfs/src/mkfs.h \ + bin/gensquashfs/src/options.c bin/gensquashfs/src/selinux.c \ + bin/gensquashfs/src/dirscan_xattr.c bin/gensquashfs/src/filemap_xattr.c\ + bin/gensquashfs/src/fstree_from_file.c \ + bin/gensquashfs/src/fstree_from_dir.c \ + bin/gensquashfs/src/sort_by_file.c gensquashfs_LDADD = libcommon.a libsquashfs.la libfstree.a libio.a gensquashfs_LDADD += libutil.a libcompat.a $(LZO_LIBS) $(PTHREAD_LIBS) gensquashfs_CPPFLAGS = $(AM_CPPFLAGS) diff --git a/bin/gensquashfs/dirscan_xattr.c b/bin/gensquashfs/dirscan_xattr.c deleted file mode 100644 index 7d4e552..0000000 --- a/bin/gensquashfs/dirscan_xattr.c +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * dirscan_xattr.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -#ifdef HAVE_SYS_XATTR_H -static char *get_full_path(const char *prefix, tree_node_t *node) -{ - char *path = NULL, *new = NULL; - size_t path_len, prefix_len; - int ret; - - path = fstree_get_path(node); - if (path == NULL) - goto fail; - - ret = canonicalize_name(path); - assert(ret == 0); - - path_len = strlen(path); - prefix_len = strlen(prefix); - - while (prefix_len > 0 && prefix[prefix_len - 1] == '/') - --prefix_len; - - if (prefix_len > 0) { - new = realloc(path, path_len + prefix_len + 2); - if (new == NULL) - goto fail; - - path = new; - - memmove(path + prefix_len + 1, path, path_len + 1); - memcpy(path, prefix, prefix_len); - path[prefix_len] = '/'; - } - - return path; -fail: - perror("getting full path for xattr scan"); - free(path); - return NULL; -} - -static int xattr_from_path(sqfs_xattr_writer_t *xwr, const char *path) -{ - char *key, *value = NULL, *buffer = NULL; - ssize_t buflen, vallen, keylen; - int ret; - - buflen = llistxattr(path, NULL, 0); - if (buflen < 0) { - fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); - return -1; - } - - if (buflen == 0) - return 0; - - buffer = malloc(buflen); - if (buffer == NULL) { - perror("xattr name buffer"); - return -1; - } - - buflen = llistxattr(path, buffer, buflen); - if (buflen == -1) { - fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); - goto fail; - } - - key = buffer; - while (buflen > 0) { - vallen = lgetxattr(path, key, NULL, 0); - if (vallen == -1) { - fprintf(stderr, "lgetxattr %s: %s", - path, strerror(errno)); - goto fail; - } - - if (vallen > 0) { - value = calloc(1, vallen); - if (value == NULL) { - perror("allocating xattr value buffer"); - goto fail; - } - - vallen = lgetxattr(path, key, value, vallen); - if (vallen == -1) { - fprintf(stderr, "lgetxattr %s: %s\n", - path, strerror(errno)); - goto fail; - } - - ret = sqfs_xattr_writer_add(xwr, key, value, vallen); - if (ret) { - sqfs_perror(path, - "storing xattr key-value pairs", - ret); - goto fail; - } - - free(value); - value = NULL; - } - - keylen = strlen(key) + 1; - buflen -= keylen; - key += keylen; - } - - free(buffer); - return 0; -fail: - free(value); - free(buffer); - return -1; -} -#endif - -static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, - sqfs_xattr_writer_t *xwr, bool scan_xattr, void *xattr_map, - tree_node_t *node) -{ - char *path = NULL; - int ret; - - ret = sqfs_xattr_writer_begin(xwr, 0); - if (ret) { - sqfs_perror(node->name, "recoding xattr key-value pairs\n", - ret); - return -1; - } - -#ifdef HAVE_SYS_XATTR_H - if (scan_xattr) { - path = get_full_path(path_prefix, node); - if (path == NULL) - return -1; - - ret = xattr_from_path(xwr, path); - free(path); - path = NULL; - if (ret) { - ret = -1; - goto out; - } - } -#else - (void)path_prefix; -#endif - - if (selinux_handle != NULL || xattr_map != NULL) { - path = fstree_get_path(node); - - if (path == NULL) { - perror("reconstructing absolute path"); - ret = -1; - goto out; - } - } - - if (xattr_map != NULL) { - ret = xattr_apply_map_file(path, xattr_map, xwr); - - if (ret) { - ret = -1; - goto out; - } - } - - if (selinux_handle != NULL) { - ret = selinux_relable_node(selinux_handle, xwr, node, path); - - if (ret) { - ret = -1; - goto out; - } - } - - if (sqfs_xattr_writer_end(xwr, &node->xattr_idx)) { - sqfs_perror(node->name, "completing xattr key-value pairs", - ret); - ret = -1; - goto out; - } - - if (S_ISDIR(node->mode)) { - node = node->data.dir.children; - - while (node != NULL) { - if (xattr_xcan_dfs(path_prefix, selinux_handle, xwr, - scan_xattr, xattr_map, node)) { - ret = -1; - goto out; - } - - node = node->next; - } - } - -out: - free(path); - return ret; -} - -int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, - void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr) -{ - if (xwr == NULL) - return 0; - - if (selinux_handle == NULL && !scan_xattr && xattr_map == NULL) - return 0; - - return xattr_xcan_dfs(path, selinux_handle, xwr, scan_xattr, xattr_map, fs->root); -} diff --git a/bin/gensquashfs/filemap_xattr.c b/bin/gensquashfs/filemap_xattr.c deleted file mode 100644 index dd76b50..0000000 --- a/bin/gensquashfs/filemap_xattr.c +++ /dev/null @@ -1,254 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * filemap_xattr.c - * - * Copyright (C) 2022 Enno Boland - */ -#include "fstree.h" -#include "mkfs.h" -#include - -#define NEW_FILE_START "# file: " - -static void print_error(const char *filename, size_t line_num, const char *err) -{ - fprintf(stderr, "%s: " PRI_SZ ": %s\n", filename, line_num, err); -} - -// Taken from attr-2.5.1/tools/setfattr.c -static sqfs_u8 *decode(const char *filename, size_t line_num, - const char *value, size_t *size) -{ - sqfs_u8 *decoded = NULL; - - if (*size == 0) { - decoded = (sqfs_u8 *)strdup(""); - if (decoded == NULL) - goto fail_alloc; - return decoded; - } - - if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { - *size = ((*size) - 2) / 2; - - decoded = calloc(1, (*size) + 1); - if (decoded == NULL) - goto fail_alloc; - - if (hex_decode(value + 2, (*size) * 2, decoded, *size)) - goto fail_encode; - } else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) { - size_t input_len = *size - 2; - - *size = (input_len / 4) * 3; - - decoded = calloc(1, (*size) + 1); - if (decoded == NULL) - goto fail_alloc; - - if (base64_decode(value + 2, input_len, decoded, size)) - goto fail_encode; - } else { - const char *v = value, *end = value + *size; - sqfs_u8 *d; - - if (end > v + 1 && *v == '"' && *(end - 1) == '"') { - v++; - end--; - } - - decoded = calloc(1, (*size) + 1); - if (decoded == NULL) - goto fail_alloc; - - d = decoded; - - while (v < end) { - if (v[0] == '\\') { - if (v[1] == '\\' || v[1] == '"') { - *d++ = *++v; - v++; - } else if (v[1] >= '0' && v[1] <= '7') { - int c = 0; - v++; - c = (*v++ - '0'); - if (*v >= '0' && *v <= '7') - c = (c << 3) + (*v++ - '0'); - if (*v >= '0' && *v <= '7') - c = (c << 3) + (*v++ - '0'); - *d++ = c; - } else - *d++ = *v++; - } else - *d++ = *v++; - } - *size = d - decoded; - } - return decoded; -fail_alloc: - fprintf(stderr, "out of memory\n"); - return NULL; -fail_encode: - print_error(filename, line_num, "bad input encoding"); - free(decoded); - return NULL; -} - -static int parse_file_name(const char *filename, size_t line_num, - char *line, struct XattrMap *map) -{ - struct XattrMapPattern *current_file; - char *file_name = strdup(line + strlen(NEW_FILE_START)); - - if (file_name == NULL) - goto fail_alloc; - - current_file = calloc(1, sizeof(struct XattrMapPattern)); - if (current_file == NULL) - goto fail_alloc; - - current_file->next = map->patterns; - map->patterns = current_file; - - if (canonicalize_name(file_name)) { - print_error(filename, line_num, "invalid absolute path"); - free(current_file); - free(file_name); - return -1; - } - - current_file->path = file_name; - return 0; -fail_alloc: - fprintf(stderr, "out of memory\n"); - free(file_name); - return -1; -} - -static int parse_xattr(const char *filename, size_t line_num, char *key_start, - char *value_start, struct XattrMap *map) -{ - size_t len; - struct XattrMapPattern *current_pattern = map->patterns; - struct XattrMapEntry *current_entry; - - if (current_pattern == NULL) { - print_error(filename, line_num, "no file specified yet"); - return -1; - } - - current_entry = calloc(1, sizeof(struct XattrMapEntry)); - if (current_entry == NULL) { - return -1; - } - current_entry->next = current_pattern->entries; - current_pattern->entries = current_entry; - - current_entry->key = strdup(key_start); - len = strlen(value_start); - current_entry->value = decode(filename, line_num, value_start, &len); - current_entry->value_len = len; - - return 0; -} - -void * -xattr_open_map_file(const char *path) { - struct XattrMap *map; - size_t line_num = 1; - char *p = NULL; - istream_t *file = istream_open_file(path); - if (file == NULL) { - return NULL; - } - - map = calloc(1, sizeof(struct XattrMap)); - if (map == NULL) - goto fail_close; - - for (;;) { - char *line = NULL; - int ret = istream_get_line(file, &line, &line_num, - ISTREAM_LINE_LTRIM | - ISTREAM_LINE_RTRIM | - ISTREAM_LINE_SKIP_EMPTY); - if (ret < 0) - goto fail; - if (ret > 0) - break; - - if (strncmp(NEW_FILE_START, line, strlen(NEW_FILE_START)) == 0) { - ret = parse_file_name(path, line_num, line, map); - } else if ((p = strchr(line, '='))) { - *(p++) = '\0'; - ret = parse_xattr(path, line_num, line, p, map); - } else if (line[0] != '#') { - print_error(path, line_num, "not a key-value pair"); - ret = -1; - } - - ++line_num; - free(line); - if (ret < 0) - goto fail; - } - - sqfs_drop(file); - return map; -fail: - xattr_close_map_file(map); -fail_close: - sqfs_drop(file); - return NULL; -} - -void -xattr_close_map_file(void *xattr_map) { - struct XattrMap *map = xattr_map; - while (map->patterns != NULL) { - struct XattrMapPattern *file = map->patterns; - map->patterns = file->next; - while (file->entries != NULL) { - struct XattrMapEntry *entry = file->entries; - file->entries = entry->next; - free(entry->key); - free(entry->value); - free(entry); - } - free(file->path); - free(file); - } - free(xattr_map); -} - -int -xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr) { - struct XattrMap *xattr_map = map; - int ret = 0; - const struct XattrMapPattern *pat; - const struct XattrMapEntry *entry; - - for (pat = xattr_map->patterns; pat != NULL; pat = pat->next) { - char *patstr = pat->path; - const char *stripped = path; - - if (patstr[0] != '/' && stripped[0] == '/') { - stripped++; - } - - if (strcmp(patstr, stripped) == 0) { - printf("Applying xattrs for %s", path); - for (entry = pat->entries; entry != NULL; entry = entry->next) { - printf(" %s = \n", entry->key); - fwrite(entry->value, entry->value_len, 1, stdout); - puts("\n"); - ret = sqfs_xattr_writer_add( - xwr, entry->key, entry->value, entry->value_len); - if (ret < 0) { - return ret; - } - } - } - } - return ret; -} diff --git a/bin/gensquashfs/fstree_from_dir.c b/bin/gensquashfs/fstree_from_dir.c deleted file mode 100644 index 5b3f003..0000000 --- a/bin/gensquashfs/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 - */ -#include "config.h" -#include "mkfs.h" - -#include -#include -#include -#include - -#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/bin/gensquashfs/fstree_from_file.c b/bin/gensquashfs/fstree_from_file.c deleted file mode 100644 index e26d4b1..0000000 --- a/bin/gensquashfs/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 - */ -#include "config.h" - -#include "util/util.h" -#include "io/file.h" -#include "compat.h" -#include "mkfs.h" - -#include -#include -#include -#include -#include - -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 ' 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: []\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_drop(fp); - return ret; -} diff --git a/bin/gensquashfs/mkfs.c b/bin/gensquashfs/mkfs.c deleted file mode 100644 index c773dd7..0000000 --- a/bin/gensquashfs/mkfs.c +++ /dev/null @@ -1,215 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * mkfs.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -static int pack_files(sqfs_block_processor_t *data, fstree_t *fs, - options_t *opt) -{ - sqfs_u64 filesize; - sqfs_file_t *file; - tree_node_t *node; - const char *path; - char *node_path; - file_info_t *fi; - int flags; - int ret; - - if (opt->packdir != NULL && chdir(opt->packdir) != 0) { - perror(opt->packdir); - return -1; - } - - for (fi = fs->files; fi != NULL; fi = fi->next) { - if (fi->input_file == NULL) { - node = container_of(fi, tree_node_t, data.file); - - node_path = fstree_get_path(node); - if (node_path == NULL) { - perror("reconstructing file path"); - return -1; - } - - ret = canonicalize_name(node_path); - assert(ret == 0); - - path = node_path; - } else { - node_path = NULL; - path = fi->input_file; - } - - if (!opt->cfg.quiet) - printf("packing %s\n", path); - - file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); - if (file == NULL) { - perror(path); - free(node_path); - return -1; - } - - flags = fi->flags; - filesize = file->get_size(file); - - if (opt->no_tail_packing && filesize > opt->cfg.block_size) - flags |= SQFS_BLK_DONT_FRAGMENT; - - ret = write_data_from_file(path, data, &fi->inode, file, flags); - sqfs_drop(file); - free(node_path); - - if (ret) - return -1; - } - - return 0; -} - -static int relabel_tree_dfs(const char *filename, sqfs_xattr_writer_t *xwr, - tree_node_t *n, void *selinux_handle) -{ - char *path = fstree_get_path(n); - int ret; - - if (path == NULL) { - perror("getting absolute node path for SELinux relabeling"); - return -1; - } - - ret = sqfs_xattr_writer_begin(xwr, 0); - if (ret) { - sqfs_perror(filename, "recording xattr key-value pairs", ret); - return -1; - } - - if (selinux_relable_node(selinux_handle, xwr, n, path)) { - free(path); - return -1; - } - - ret = sqfs_xattr_writer_end(xwr, &n->xattr_idx); - if (ret) { - sqfs_perror(filename, "flushing completed key-value pairs", - ret); - return -1; - } - - free(path); - - if (S_ISDIR(n->mode)) { - for (n = n->data.dir.children; n != NULL; n = n->next) { - if (relabel_tree_dfs(filename, xwr, n, selinux_handle)) - return -1; - } - } - - return 0; -} - -static int read_fstree(fstree_t *fs, options_t *opt, sqfs_xattr_writer_t *xwr, - void *selinux_handle) -{ - int ret; - - ret = fstree_from_file(fs, opt->infile, opt->packdir); - - if (ret == 0 && selinux_handle != NULL) - ret = relabel_tree_dfs(opt->cfg.filename, xwr, - fs->root, selinux_handle); - - return ret; -} - -static void override_owner_dfs(const options_t *opt, tree_node_t *n) -{ - if (opt->force_uid) - n->uid = opt->force_uid_value; - - if (opt->force_gid) - n->gid = opt->force_gid_value; - - if (S_ISDIR(n->mode)) { - for (n = n->data.dir.children; n != NULL; n = n->next) - override_owner_dfs(opt, n); - } -} - -int main(int argc, char **argv) -{ - int status = EXIT_FAILURE; - istream_t *sortfile = NULL; - void *sehnd = NULL; - void *xattrmap = NULL; - sqfs_writer_t sqfs; - options_t opt; - - process_command_line(&opt, argc, argv); - - if (sqfs_writer_init(&sqfs, &opt.cfg)) - return EXIT_FAILURE; - - if (opt.selinux != NULL) { - sehnd = selinux_open_context_file(opt.selinux); - if (sehnd == NULL) - goto out; - } - if (opt.xattr_file != NULL) { - xattrmap = xattr_open_map_file(opt.xattr_file); - if (xattrmap == NULL) - goto out; - } - - if (opt.sortfile != NULL) { - sortfile = istream_open_file(opt.sortfile); - if (sortfile == NULL) - goto out; - } - - if (opt.infile == NULL) { - if (fstree_from_dir(&sqfs.fs, sqfs.fs.root, opt.packdir, - NULL, NULL, opt.dirscan_flags)) { - goto out; - } - } else { - if (read_fstree(&sqfs.fs, &opt, sqfs.xwr, sehnd)) - goto out; - } - - if (opt.force_uid || opt.force_gid) - override_owner_dfs(&opt, sqfs.fs.root); - - if (fstree_post_process(&sqfs.fs)) - goto out; - - if (opt.infile == NULL) { - if (xattrs_from_dir(&sqfs.fs, opt.packdir, sehnd, xattrmap, - sqfs.xwr, opt.scan_xattr)) { - goto out; - } - } - - if (sortfile != NULL) { - if (fstree_sort_files(&sqfs.fs, sortfile)) - goto out; - } - - if (pack_files(sqfs.data, &sqfs.fs, &opt)) - goto out; - - if (sqfs_writer_finish(&sqfs, &opt.cfg)) - goto out; - - status = EXIT_SUCCESS; -out: - sqfs_writer_cleanup(&sqfs, status); - if (sehnd != NULL) - selinux_close_context_file(sehnd); - if (sortfile != NULL) - sqfs_drop(sortfile); - free(opt.packdir); - return status; -} diff --git a/bin/gensquashfs/mkfs.h b/bin/gensquashfs/mkfs.h deleted file mode 100644 index 53fb018..0000000 --- a/bin/gensquashfs/mkfs.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * mkfs.h - * - * Copyright (C) 2019 David Oberhollenzer - * Copyright (C) 2022 Enno Boland - */ -#ifndef MKFS_H -#define MKFS_H - -#include "config.h" - -#include "common.h" -#include "fstree.h" -#include "util/util.h" -#include "io/file.h" - -#ifdef HAVE_SYS_XATTR_H -#include - -#if defined(__APPLE__) && defined(__MACH__) -#define llistxattr(path, list, size) \ - listxattr(path, list, size, XATTR_NOFOLLOW) - -#define lgetxattr(path, name, value, size) \ - getxattr(path, name, value, size, 0, XATTR_NOFOLLOW) -#endif -#endif - -#ifdef WITH_SELINUX -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct { - sqfs_writer_cfg_t cfg; - unsigned int dirscan_flags; - const char *infile; - const char *selinux; - const char *xattr_file; - const char *sortfile; - bool no_tail_packing; - - /* copied from command line or constructed from infile argument - if not specified. Must be free'd. */ - char *packdir; - - unsigned int force_uid_value; - unsigned int force_gid_value; - bool force_uid; - bool force_gid; - - bool scan_xattr; -} options_t; - -struct XattrMapEntry { - char *key; - sqfs_u8 *value; - size_t value_len; - struct XattrMapEntry *next; -}; - -struct XattrMapPattern { - char *path; - struct XattrMapEntry *entries; - struct XattrMapPattern *next; -}; - -struct XattrMap { - struct XattrMapPattern *patterns; -}; - -void process_command_line(options_t *opt, int argc, char **argv); - -int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, - void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr); - -void *xattr_open_map_file(const char *path); - -int -xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr); - -void xattr_close_map_file(void *xattr_map); - -void *selinux_open_context_file(const char *filename); - -int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, - tree_node_t *node, const char *path); - -void selinux_close_context_file(void *sehnd); - -/* - Parses the file format accepted by gensquashfs and produce a file system - tree from it. File input paths are interpreted as relative to the current - working directory. - - On failure, an error report with filename and line number is written - to stderr. - - Returns 0 on success. - */ -int fstree_from_file(fstree_t *fs, const char *filename, - const char *basepath); - -int fstree_from_file_stream(fstree_t *fs, istream_t *file, - const char *basepath); - -/* - Recursively scan a directory to build a file system tree. - - Returns 0 on success, prints to stderr on failure. - */ -int fstree_from_dir(fstree_t *fs, tree_node_t *root, - const char *path, scan_node_callback cb, void *user, - unsigned int flags); - -/* - Same as fstree_from_dir, but scans a sub-directory inside the specified path. - - Returns 0 on success, prints to stderr on failure. - */ -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); - -int fstree_sort_files(fstree_t *fs, istream_t *sortfile); - -#endif /* MKFS_H */ diff --git a/bin/gensquashfs/options.c b/bin/gensquashfs/options.c deleted file mode 100644 index f263bce..0000000 --- a/bin/gensquashfs/options.c +++ /dev/null @@ -1,383 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * options.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -enum { - ALL_ROOT_OPTION = 1, -}; - -static struct option long_opts[] = { - { "all-root", no_argument, NULL, ALL_ROOT_OPTION }, - { "set-uid", required_argument, NULL, 'u' }, - { "set-gid", required_argument, NULL, 'g' }, - { "compressor", required_argument, NULL, 'c' }, - { "block-size", required_argument, NULL, 'b' }, - { "dev-block-size", required_argument, NULL, 'B' }, - { "defaults", required_argument, NULL, 'd' }, - { "comp-extra", required_argument, NULL, 'X' }, - { "pack-file", required_argument, NULL, 'F' }, - { "pack-dir", required_argument, NULL, 'D' }, - { "num-jobs", required_argument, NULL, 'j' }, - { "queue-backlog", required_argument, NULL, 'Q' }, - { "keep-time", no_argument, NULL, 'k' }, -#ifdef HAVE_SYS_XATTR_H - { "keep-xattr", no_argument, NULL, 'x' }, -#endif - { "one-file-system", no_argument, NULL, 'o' }, - { "exportable", no_argument, NULL, 'e' }, - { "no-tail-packing", no_argument, NULL, 'T' }, - { "force", no_argument, NULL, 'f' }, - { "quiet", no_argument, NULL, 'q' }, -#ifdef WITH_SELINUX - { "selinux", required_argument, NULL, 's' }, -#endif - { "xattr-file", required_argument, NULL, 'A' }, - { "sort-file", required_argument, NULL, 'S' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - { NULL, 0, NULL, 0 }, -}; - -static const char *short_opts = "F:D:X:c:b:B:d:u:g:j:Q:S:A:kxoefqThV" -#ifdef WITH_SELINUX -"s:" -#endif -#ifdef HAVE_SYS_XATTR_H -"x" -#endif -; - -static const char *help_string = -"Usage: gensquashfs [OPTIONS...] \n" -"\n" -"Possible options:\n" -"\n"; - -static const char *pack_options = -" --pack-file, -F Use a `gen_init_cpio` style description file.\n" -" The file format is specified below.\n" -" If --pack-dir is used, input file paths are\n" -" relative to the pack directory, otherwise\n" -" they are relative to the directory the pack\n" -" file is in.\n" -" --pack-dir, -D If --pack-file is used, this is the root path\n" -" relative to which to read files. If no pack\n" -" file is specified, pack the contents of the\n" -" given directory. The directory becomes the\n" -" file system root.\n" -"\n" -" --compressor, -c Select the compressor to use.\n" -" A list of available compressors is below.\n" -" --comp-extra, -X A comma separated list of extra options for\n" -" the selected compressor. Specify 'help' to\n" -" get a list of available options.\n" -" --num-jobs, -j Number of compressor jobs to create.\n" -" --queue-backlog, -Q Maximum number of data blocks in the thread\n" -" worker queue before the packer starts waiting\n" -" for the block processors to catch up.\n" -" Defaults to 10 times the number of jobs.\n" -" --block-size, -b Block size to use for Squashfs image.\n" -" Defaults to %u.\n" -" --dev-block-size, -B Device block size to padd the image to.\n" -" Defaults to %u.\n" -" --defaults, -d A comma separated list of default values for\n" -" implicitly created directories.\n" -"\n" -" Possible options:\n" -" uid= 0 if not set.\n" -" gid= 0 if not set.\n" -" mode= 0755 if not set.\n" -" mtime= 0 if not set.\n" -"\n" -" --set-uid, -u Force the owners user ID for ALL inodes to\n" -" this value, no matter what the pack file or\n" -" directory entries actually specify.\n" -" --set-gid, -g Force the owners group ID for ALL inodes to\n" -" this value, no matter what the pack file or\n" -" directory entries actually specify.\n" -" --all-root A short hand for `--set-uid 0 --set-gid 0`.\n" -"\n"; - -const char *extra_options = -" --sort-file, -S Specify a \"sort file\" that can be used to\n" -" micro manage the order of files during packing\n" -" and behaviour (compression, fragmentation, ..)\n" -"\n" -#ifdef WITH_SELINUX -" --selinux, -s Specify an SELinux label file to get context\n" -" attributes from.\n" -#endif -" --xattr-file, -A Specify an Xattr file to get extended attributes\n" -" for loading xattrs\n" -" --keep-time, -k When using --pack-dir only, use the timestamps\n" -" from the input files instead of setting\n" -" defaults on all input paths.\n" -" --keep-xattr, -x When using --pack-dir only, read and pack the\n" -" extended attributes from the input files.\n" -" --one-file-system, -o When using --pack-dir only, stay in local file\n" -" system and do not cross mount points.\n" -" --exportable, -e Generate an export table for NFS support.\n" -" --no-tail-packing, -T Do not perform tail end packing on files that\n" -" are larger than block size.\n" -" --force, -f Overwrite the output file if it exists.\n" -" --quiet, -q Do not print out progress reports.\n" -" --help, -h Print help text and exit.\n" -" --version, -V Print version information and exit.\n" -"\n"; - -const char *pack_details = -"Example of a pack file:\n" -"\n" -" # A simple squashfs image\n" -" dir /dev 0755 0 0\n" -" nod /dev/console 0600 0 0 c 5 1\n" -" dir /root 0700 0 0\n" -" \n" -" # `slink` for symlink, `link` for hard links\n" -" slink /lib 0777 0 0 /usr/lib\n" -" link /init 0777 0 0 /sbin/init\n" -" \n" -" # Add a file. Input is relative to listing or pack dir.\n" -" file /sbin/init 0755 0 0 ../init/sbin/init\n" -" \n" -" # Read bin/bash, relative to listing or pack dir.\n" -" # Implicitly create /bin.\n" -" file /bin/bash 0755 0 0\n" -" \n" -" # file name with a space in it.\n" -" file \"/opt/my app/\\\"special\\\"/data\" 0600 0 0\n" -" \n" -" # collect the contents of ./lib and put it under /usr/lib\n" -" glob /usr/lib 0755 0 0 -type d ./lib\n" -" glob /usr/lib 0755 0 0 -type f -name \"*.so.*\" ./lib\n" -" glob /usr/lib 0777 0 0 -type l -name \"*.so.*\" ./lib\n" -"\n\n"; - -const char *sort_details = -"When using a sort file, the specified paths are within the SquashFS image.\n" -"Files with lower priority are packed first, default priority is 0.\n" -"The sorting is stable, files with the same priority do not change place\n" -"relative to each other.\n" -"\n" -"Example:\n" -" # Specify a packing order with file globbing\n" -" -8000 [glob] bin/*\n" -" -5000 [glob] lib/*\n" -"\n" -" # glob_no_path means * is allowed to match /\n" -" -1000 [glob_no_path] share/*\n" -"\n" -" # Our boot loader needs this\n" -" -100000 [dont_compress,dont_fragment,nosparse] boot/vmlinuz\n" -"\n" -" # For demonstration, a quoted filename and no flags\n" -" 1337 \"usr/share/my \\\"special\\\" file \"\n" -"\n\n"; - -static const char *xattr_details = -"The format of xattr files tries to immitate the format generated\n" -"by `getfattr --dump`.\n" -"\n" -"Example:\n" -" # file: dev/\n" -" security.selinux=\"system_u:object_r:device_t:s0\"\n" -" user.beverage_preference=0xCAFECAFEDECAFBAD\n" -"\n" -" # file: dev/rfkill\n" -" security.selinux=\"system_u:object_r:wireless_device_t:s0\"\n" -" system.posix_acl_access=0sSGVsbG8gdGhlcmUgOi0pCg==\n" -"\n\n"; - -void process_command_line(options_t *opt, int argc, char **argv) -{ - bool have_compressor; - int i, ret; - - memset(opt, 0, sizeof(*opt)); - sqfs_writer_cfg_init(&opt->cfg); - - for (;;) { - i = getopt_long(argc, argv, short_opts, long_opts, NULL); - if (i == -1) - break; - - switch (i) { - case ALL_ROOT_OPTION: - opt->force_uid_value = 0; - opt->force_gid_value = 0; - opt->force_uid = true; - opt->force_gid = true; - break; - case 'u': - opt->force_uid_value = strtol(optarg, NULL, 0); - opt->force_uid = true; - break; - case 'g': - opt->force_gid_value = strtol(optarg, NULL, 0); - opt->force_gid = true; - break; - case 'T': - opt->no_tail_packing = true; - break; - case 'c': - have_compressor = true; - ret = sqfs_compressor_id_from_name(optarg); - - if (ret < 0) { - have_compressor = false; -#ifdef WITH_LZO - if (opt->cfg.comp_id == SQFS_COMP_LZO) - have_compressor = true; -#endif - } - - if (!have_compressor) { - fprintf(stderr, "Unsupported compressor '%s'\n", - optarg); - exit(EXIT_FAILURE); - } - - opt->cfg.comp_id = ret; - break; - case 'b': - if (parse_size("Block size", &opt->cfg.block_size, - optarg, 0)) { - exit(EXIT_FAILURE); - } - break; - case 'j': - opt->cfg.num_jobs = strtol(optarg, NULL, 0); - break; - case 'Q': - opt->cfg.max_backlog = strtol(optarg, NULL, 0); - break; - case 'B': - if (parse_size("Device block size", - &opt->cfg.devblksize, optarg, 0)) { - exit(EXIT_FAILURE); - } - if (opt->cfg.devblksize < 1024) { - fputs("Device block size must be at " - "least 1024\n", stderr); - exit(EXIT_FAILURE); - } - break; - case 'd': - opt->cfg.fs_defaults = optarg; - break; - case 'k': - opt->dirscan_flags |= DIR_SCAN_KEEP_TIME; - break; -#ifdef HAVE_SYS_XATTR_H - case 'x': - opt->scan_xattr = true; - break; -#endif - case 'o': - opt->dirscan_flags |= DIR_SCAN_ONE_FILESYSTEM; - break; - case 'e': - opt->cfg.exportable = true; - break; - case 'f': - opt->cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; - break; - case 'q': - opt->cfg.quiet = true; - break; - case 'X': - opt->cfg.comp_extra = optarg; - break; - case 'F': - opt->infile = optarg; - break; - case 'D': - free(opt->packdir); - opt->packdir = strdup(optarg); - if (opt->packdir == NULL) { - perror(optarg); - exit(EXIT_FAILURE); - } - break; -#ifdef WITH_SELINUX - case 's': - opt->selinux = optarg; - break; -#endif - case 'A': - opt->xattr_file = optarg; - break; - case 'S': - opt->sortfile = optarg; - break; - case 'h': - fputs(help_string, stdout); - printf(pack_options, SQFS_DEFAULT_BLOCK_SIZE, - SQFS_DEVBLK_SIZE); - fputs(extra_options, stdout); - fputs(pack_details, stdout); - fputs(sort_details, stdout); - fputs(xattr_details, stdout); - compressor_print_available(); - exit(EXIT_SUCCESS); - case 'V': - print_version("gensquashfs"); - exit(EXIT_SUCCESS); - default: - goto fail_arg; - } - } - - if (opt->cfg.num_jobs < 1) - opt->cfg.num_jobs = 1; - - if (opt->cfg.max_backlog < 1) - opt->cfg.max_backlog = 10 * opt->cfg.num_jobs; - - if (opt->cfg.comp_extra != NULL && - strcmp(opt->cfg.comp_extra, "help") == 0) { - compressor_print_help(opt->cfg.comp_id); - exit(EXIT_SUCCESS); - } - - if (opt->infile == NULL && opt->packdir == NULL) { - fputs("No input file or directory specified.\n", stderr); - goto fail_arg; - } - - if (optind >= argc) { - fputs("No output file specified.\n", stderr); - goto fail_arg; - } - - opt->cfg.filename = argv[optind++]; - - if (optind < argc) { - fputs("Unknown extra arguments specified.\n", stderr); - goto fail_arg; - } - - /* construct packdir if not specified */ - if (opt->packdir == NULL && opt->infile != NULL) { - const char *split = strrchr(opt->infile, '/'); - - if (split != NULL) { - opt->packdir = strndup(opt->infile, - split - opt->infile); - - if (opt->packdir == NULL) { - perror("constructing input directory path"); - exit(EXIT_FAILURE); - } - } - } - return; -fail_arg: - fputs("Try `gensquashfs --help' for more information.\n", stderr); - free(opt->packdir); - exit(EXIT_FAILURE); -} diff --git a/bin/gensquashfs/selinux.c b/bin/gensquashfs/selinux.c deleted file mode 100644 index 678723b..0000000 --- a/bin/gensquashfs/selinux.c +++ /dev/null @@ -1,78 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * selinux.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -#define XATTR_NAME_SELINUX "security.selinux" -#define XATTR_VALUE_SELINUX "system_u:object_r:unlabeled_t:s0" - -#ifdef WITH_SELINUX -int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, - tree_node_t *node, const char *path) -{ - char *context = NULL; - int ret; - - if (selabel_lookup(sehnd, &context, path, node->mode) < 0) { - context = strdup(XATTR_VALUE_SELINUX); - if (context == NULL) - goto fail; - } - - ret = sqfs_xattr_writer_add(xwr, XATTR_NAME_SELINUX, - context, strlen(context)); - free(context); - - if (ret) - sqfs_perror(node->name, "storing SELinux xattr", ret); - - return ret; -fail: - perror("relabeling files"); - return -1; -} - -void *selinux_open_context_file(const char *filename) -{ - struct selabel_handle *sehnd; - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, filename }, - }; - - sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); - if (sehnd == NULL) - perror(filename); - - return sehnd; -} - -void selinux_close_context_file(void *sehnd) -{ - selabel_close(sehnd); -} -#else -int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, - tree_node_t *node, const char *path) -{ - (void)sehnd; (void)xwr; (void)node; (void)path; - fputs("Built without SELinux support, cannot add SELinux labels\n", - stderr); - return -1; -} - -void *selinux_open_context_file(const char *filename) -{ - (void)filename; - fputs("Built without SELinux support, cannot open contexts file\n", - stderr); - return NULL; -} - -void selinux_close_context_file(void *sehnd) -{ - (void)sehnd; -} -#endif diff --git a/bin/gensquashfs/sort_by_file.c b/bin/gensquashfs/sort_by_file.c deleted file mode 100644 index a555718..0000000 --- a/bin/gensquashfs/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 - */ -#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; -} diff --git a/bin/gensquashfs/src/dirscan_xattr.c b/bin/gensquashfs/src/dirscan_xattr.c new file mode 100644 index 0000000..7d4e552 --- /dev/null +++ b/bin/gensquashfs/src/dirscan_xattr.c @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * dirscan_xattr.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +#ifdef HAVE_SYS_XATTR_H +static char *get_full_path(const char *prefix, tree_node_t *node) +{ + char *path = NULL, *new = NULL; + size_t path_len, prefix_len; + int ret; + + path = fstree_get_path(node); + if (path == NULL) + goto fail; + + ret = canonicalize_name(path); + assert(ret == 0); + + path_len = strlen(path); + prefix_len = strlen(prefix); + + while (prefix_len > 0 && prefix[prefix_len - 1] == '/') + --prefix_len; + + if (prefix_len > 0) { + new = realloc(path, path_len + prefix_len + 2); + if (new == NULL) + goto fail; + + path = new; + + memmove(path + prefix_len + 1, path, path_len + 1); + memcpy(path, prefix, prefix_len); + path[prefix_len] = '/'; + } + + return path; +fail: + perror("getting full path for xattr scan"); + free(path); + return NULL; +} + +static int xattr_from_path(sqfs_xattr_writer_t *xwr, const char *path) +{ + char *key, *value = NULL, *buffer = NULL; + ssize_t buflen, vallen, keylen; + int ret; + + buflen = llistxattr(path, NULL, 0); + if (buflen < 0) { + fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); + return -1; + } + + if (buflen == 0) + return 0; + + buffer = malloc(buflen); + if (buffer == NULL) { + perror("xattr name buffer"); + return -1; + } + + buflen = llistxattr(path, buffer, buflen); + if (buflen == -1) { + fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); + goto fail; + } + + key = buffer; + while (buflen > 0) { + vallen = lgetxattr(path, key, NULL, 0); + if (vallen == -1) { + fprintf(stderr, "lgetxattr %s: %s", + path, strerror(errno)); + goto fail; + } + + if (vallen > 0) { + value = calloc(1, vallen); + if (value == NULL) { + perror("allocating xattr value buffer"); + goto fail; + } + + vallen = lgetxattr(path, key, value, vallen); + if (vallen == -1) { + fprintf(stderr, "lgetxattr %s: %s\n", + path, strerror(errno)); + goto fail; + } + + ret = sqfs_xattr_writer_add(xwr, key, value, vallen); + if (ret) { + sqfs_perror(path, + "storing xattr key-value pairs", + ret); + goto fail; + } + + free(value); + value = NULL; + } + + keylen = strlen(key) + 1; + buflen -= keylen; + key += keylen; + } + + free(buffer); + return 0; +fail: + free(value); + free(buffer); + return -1; +} +#endif + +static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, + sqfs_xattr_writer_t *xwr, bool scan_xattr, void *xattr_map, + tree_node_t *node) +{ + char *path = NULL; + int ret; + + ret = sqfs_xattr_writer_begin(xwr, 0); + if (ret) { + sqfs_perror(node->name, "recoding xattr key-value pairs\n", + ret); + return -1; + } + +#ifdef HAVE_SYS_XATTR_H + if (scan_xattr) { + path = get_full_path(path_prefix, node); + if (path == NULL) + return -1; + + ret = xattr_from_path(xwr, path); + free(path); + path = NULL; + if (ret) { + ret = -1; + goto out; + } + } +#else + (void)path_prefix; +#endif + + if (selinux_handle != NULL || xattr_map != NULL) { + path = fstree_get_path(node); + + if (path == NULL) { + perror("reconstructing absolute path"); + ret = -1; + goto out; + } + } + + if (xattr_map != NULL) { + ret = xattr_apply_map_file(path, xattr_map, xwr); + + if (ret) { + ret = -1; + goto out; + } + } + + if (selinux_handle != NULL) { + ret = selinux_relable_node(selinux_handle, xwr, node, path); + + if (ret) { + ret = -1; + goto out; + } + } + + if (sqfs_xattr_writer_end(xwr, &node->xattr_idx)) { + sqfs_perror(node->name, "completing xattr key-value pairs", + ret); + ret = -1; + goto out; + } + + if (S_ISDIR(node->mode)) { + node = node->data.dir.children; + + while (node != NULL) { + if (xattr_xcan_dfs(path_prefix, selinux_handle, xwr, + scan_xattr, xattr_map, node)) { + ret = -1; + goto out; + } + + node = node->next; + } + } + +out: + free(path); + return ret; +} + +int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, + void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr) +{ + if (xwr == NULL) + return 0; + + if (selinux_handle == NULL && !scan_xattr && xattr_map == NULL) + return 0; + + return xattr_xcan_dfs(path, selinux_handle, xwr, scan_xattr, xattr_map, fs->root); +} diff --git a/bin/gensquashfs/src/filemap_xattr.c b/bin/gensquashfs/src/filemap_xattr.c new file mode 100644 index 0000000..dd76b50 --- /dev/null +++ b/bin/gensquashfs/src/filemap_xattr.c @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * filemap_xattr.c + * + * Copyright (C) 2022 Enno Boland + */ +#include "fstree.h" +#include "mkfs.h" +#include + +#define NEW_FILE_START "# file: " + +static void print_error(const char *filename, size_t line_num, const char *err) +{ + fprintf(stderr, "%s: " PRI_SZ ": %s\n", filename, line_num, err); +} + +// Taken from attr-2.5.1/tools/setfattr.c +static sqfs_u8 *decode(const char *filename, size_t line_num, + const char *value, size_t *size) +{ + sqfs_u8 *decoded = NULL; + + if (*size == 0) { + decoded = (sqfs_u8 *)strdup(""); + if (decoded == NULL) + goto fail_alloc; + return decoded; + } + + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + *size = ((*size) - 2) / 2; + + decoded = calloc(1, (*size) + 1); + if (decoded == NULL) + goto fail_alloc; + + if (hex_decode(value + 2, (*size) * 2, decoded, *size)) + goto fail_encode; + } else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) { + size_t input_len = *size - 2; + + *size = (input_len / 4) * 3; + + decoded = calloc(1, (*size) + 1); + if (decoded == NULL) + goto fail_alloc; + + if (base64_decode(value + 2, input_len, decoded, size)) + goto fail_encode; + } else { + const char *v = value, *end = value + *size; + sqfs_u8 *d; + + if (end > v + 1 && *v == '"' && *(end - 1) == '"') { + v++; + end--; + } + + decoded = calloc(1, (*size) + 1); + if (decoded == NULL) + goto fail_alloc; + + d = decoded; + + while (v < end) { + if (v[0] == '\\') { + if (v[1] == '\\' || v[1] == '"') { + *d++ = *++v; + v++; + } else if (v[1] >= '0' && v[1] <= '7') { + int c = 0; + v++; + c = (*v++ - '0'); + if (*v >= '0' && *v <= '7') + c = (c << 3) + (*v++ - '0'); + if (*v >= '0' && *v <= '7') + c = (c << 3) + (*v++ - '0'); + *d++ = c; + } else + *d++ = *v++; + } else + *d++ = *v++; + } + *size = d - decoded; + } + return decoded; +fail_alloc: + fprintf(stderr, "out of memory\n"); + return NULL; +fail_encode: + print_error(filename, line_num, "bad input encoding"); + free(decoded); + return NULL; +} + +static int parse_file_name(const char *filename, size_t line_num, + char *line, struct XattrMap *map) +{ + struct XattrMapPattern *current_file; + char *file_name = strdup(line + strlen(NEW_FILE_START)); + + if (file_name == NULL) + goto fail_alloc; + + current_file = calloc(1, sizeof(struct XattrMapPattern)); + if (current_file == NULL) + goto fail_alloc; + + current_file->next = map->patterns; + map->patterns = current_file; + + if (canonicalize_name(file_name)) { + print_error(filename, line_num, "invalid absolute path"); + free(current_file); + free(file_name); + return -1; + } + + current_file->path = file_name; + return 0; +fail_alloc: + fprintf(stderr, "out of memory\n"); + free(file_name); + return -1; +} + +static int parse_xattr(const char *filename, size_t line_num, char *key_start, + char *value_start, struct XattrMap *map) +{ + size_t len; + struct XattrMapPattern *current_pattern = map->patterns; + struct XattrMapEntry *current_entry; + + if (current_pattern == NULL) { + print_error(filename, line_num, "no file specified yet"); + return -1; + } + + current_entry = calloc(1, sizeof(struct XattrMapEntry)); + if (current_entry == NULL) { + return -1; + } + current_entry->next = current_pattern->entries; + current_pattern->entries = current_entry; + + current_entry->key = strdup(key_start); + len = strlen(value_start); + current_entry->value = decode(filename, line_num, value_start, &len); + current_entry->value_len = len; + + return 0; +} + +void * +xattr_open_map_file(const char *path) { + struct XattrMap *map; + size_t line_num = 1; + char *p = NULL; + istream_t *file = istream_open_file(path); + if (file == NULL) { + return NULL; + } + + map = calloc(1, sizeof(struct XattrMap)); + if (map == NULL) + goto fail_close; + + for (;;) { + char *line = NULL; + int ret = istream_get_line(file, &line, &line_num, + ISTREAM_LINE_LTRIM | + ISTREAM_LINE_RTRIM | + ISTREAM_LINE_SKIP_EMPTY); + if (ret < 0) + goto fail; + if (ret > 0) + break; + + if (strncmp(NEW_FILE_START, line, strlen(NEW_FILE_START)) == 0) { + ret = parse_file_name(path, line_num, line, map); + } else if ((p = strchr(line, '='))) { + *(p++) = '\0'; + ret = parse_xattr(path, line_num, line, p, map); + } else if (line[0] != '#') { + print_error(path, line_num, "not a key-value pair"); + ret = -1; + } + + ++line_num; + free(line); + if (ret < 0) + goto fail; + } + + sqfs_drop(file); + return map; +fail: + xattr_close_map_file(map); +fail_close: + sqfs_drop(file); + return NULL; +} + +void +xattr_close_map_file(void *xattr_map) { + struct XattrMap *map = xattr_map; + while (map->patterns != NULL) { + struct XattrMapPattern *file = map->patterns; + map->patterns = file->next; + while (file->entries != NULL) { + struct XattrMapEntry *entry = file->entries; + file->entries = entry->next; + free(entry->key); + free(entry->value); + free(entry); + } + free(file->path); + free(file); + } + free(xattr_map); +} + +int +xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr) { + struct XattrMap *xattr_map = map; + int ret = 0; + const struct XattrMapPattern *pat; + const struct XattrMapEntry *entry; + + for (pat = xattr_map->patterns; pat != NULL; pat = pat->next) { + char *patstr = pat->path; + const char *stripped = path; + + if (patstr[0] != '/' && stripped[0] == '/') { + stripped++; + } + + if (strcmp(patstr, stripped) == 0) { + printf("Applying xattrs for %s", path); + for (entry = pat->entries; entry != NULL; entry = entry->next) { + printf(" %s = \n", entry->key); + fwrite(entry->value, entry->value_len, 1, stdout); + puts("\n"); + ret = sqfs_xattr_writer_add( + xwr, entry->key, entry->value, entry->value_len); + if (ret < 0) { + return ret; + } + } + } + } + return ret; +} diff --git a/bin/gensquashfs/src/fstree_from_dir.c b/bin/gensquashfs/src/fstree_from_dir.c new file mode 100644 index 0000000..5b3f003 --- /dev/null +++ b/bin/gensquashfs/src/fstree_from_dir.c @@ -0,0 +1,493 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * fstree_from_dir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "mkfs.h" + +#include +#include +#include +#include + +#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/bin/gensquashfs/src/fstree_from_file.c b/bin/gensquashfs/src/fstree_from_file.c new file mode 100644 index 0000000..e26d4b1 --- /dev/null +++ b/bin/gensquashfs/src/fstree_from_file.c @@ -0,0 +1,591 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * fstree_from_file.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "util/util.h" +#include "io/file.h" +#include "compat.h" +#include "mkfs.h" + +#include +#include +#include +#include +#include + +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 ' 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: []\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_drop(fp); + return ret; +} diff --git a/bin/gensquashfs/src/mkfs.c b/bin/gensquashfs/src/mkfs.c new file mode 100644 index 0000000..c773dd7 --- /dev/null +++ b/bin/gensquashfs/src/mkfs.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * mkfs.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +static int pack_files(sqfs_block_processor_t *data, fstree_t *fs, + options_t *opt) +{ + sqfs_u64 filesize; + sqfs_file_t *file; + tree_node_t *node; + const char *path; + char *node_path; + file_info_t *fi; + int flags; + int ret; + + if (opt->packdir != NULL && chdir(opt->packdir) != 0) { + perror(opt->packdir); + return -1; + } + + for (fi = fs->files; fi != NULL; fi = fi->next) { + if (fi->input_file == NULL) { + node = container_of(fi, tree_node_t, data.file); + + node_path = fstree_get_path(node); + if (node_path == NULL) { + perror("reconstructing file path"); + return -1; + } + + ret = canonicalize_name(node_path); + assert(ret == 0); + + path = node_path; + } else { + node_path = NULL; + path = fi->input_file; + } + + if (!opt->cfg.quiet) + printf("packing %s\n", path); + + file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); + if (file == NULL) { + perror(path); + free(node_path); + return -1; + } + + flags = fi->flags; + filesize = file->get_size(file); + + if (opt->no_tail_packing && filesize > opt->cfg.block_size) + flags |= SQFS_BLK_DONT_FRAGMENT; + + ret = write_data_from_file(path, data, &fi->inode, file, flags); + sqfs_drop(file); + free(node_path); + + if (ret) + return -1; + } + + return 0; +} + +static int relabel_tree_dfs(const char *filename, sqfs_xattr_writer_t *xwr, + tree_node_t *n, void *selinux_handle) +{ + char *path = fstree_get_path(n); + int ret; + + if (path == NULL) { + perror("getting absolute node path for SELinux relabeling"); + return -1; + } + + ret = sqfs_xattr_writer_begin(xwr, 0); + if (ret) { + sqfs_perror(filename, "recording xattr key-value pairs", ret); + return -1; + } + + if (selinux_relable_node(selinux_handle, xwr, n, path)) { + free(path); + return -1; + } + + ret = sqfs_xattr_writer_end(xwr, &n->xattr_idx); + if (ret) { + sqfs_perror(filename, "flushing completed key-value pairs", + ret); + return -1; + } + + free(path); + + if (S_ISDIR(n->mode)) { + for (n = n->data.dir.children; n != NULL; n = n->next) { + if (relabel_tree_dfs(filename, xwr, n, selinux_handle)) + return -1; + } + } + + return 0; +} + +static int read_fstree(fstree_t *fs, options_t *opt, sqfs_xattr_writer_t *xwr, + void *selinux_handle) +{ + int ret; + + ret = fstree_from_file(fs, opt->infile, opt->packdir); + + if (ret == 0 && selinux_handle != NULL) + ret = relabel_tree_dfs(opt->cfg.filename, xwr, + fs->root, selinux_handle); + + return ret; +} + +static void override_owner_dfs(const options_t *opt, tree_node_t *n) +{ + if (opt->force_uid) + n->uid = opt->force_uid_value; + + if (opt->force_gid) + n->gid = opt->force_gid_value; + + if (S_ISDIR(n->mode)) { + for (n = n->data.dir.children; n != NULL; n = n->next) + override_owner_dfs(opt, n); + } +} + +int main(int argc, char **argv) +{ + int status = EXIT_FAILURE; + istream_t *sortfile = NULL; + void *sehnd = NULL; + void *xattrmap = NULL; + sqfs_writer_t sqfs; + options_t opt; + + process_command_line(&opt, argc, argv); + + if (sqfs_writer_init(&sqfs, &opt.cfg)) + return EXIT_FAILURE; + + if (opt.selinux != NULL) { + sehnd = selinux_open_context_file(opt.selinux); + if (sehnd == NULL) + goto out; + } + if (opt.xattr_file != NULL) { + xattrmap = xattr_open_map_file(opt.xattr_file); + if (xattrmap == NULL) + goto out; + } + + if (opt.sortfile != NULL) { + sortfile = istream_open_file(opt.sortfile); + if (sortfile == NULL) + goto out; + } + + if (opt.infile == NULL) { + if (fstree_from_dir(&sqfs.fs, sqfs.fs.root, opt.packdir, + NULL, NULL, opt.dirscan_flags)) { + goto out; + } + } else { + if (read_fstree(&sqfs.fs, &opt, sqfs.xwr, sehnd)) + goto out; + } + + if (opt.force_uid || opt.force_gid) + override_owner_dfs(&opt, sqfs.fs.root); + + if (fstree_post_process(&sqfs.fs)) + goto out; + + if (opt.infile == NULL) { + if (xattrs_from_dir(&sqfs.fs, opt.packdir, sehnd, xattrmap, + sqfs.xwr, opt.scan_xattr)) { + goto out; + } + } + + if (sortfile != NULL) { + if (fstree_sort_files(&sqfs.fs, sortfile)) + goto out; + } + + if (pack_files(sqfs.data, &sqfs.fs, &opt)) + goto out; + + if (sqfs_writer_finish(&sqfs, &opt.cfg)) + goto out; + + status = EXIT_SUCCESS; +out: + sqfs_writer_cleanup(&sqfs, status); + if (sehnd != NULL) + selinux_close_context_file(sehnd); + if (sortfile != NULL) + sqfs_drop(sortfile); + free(opt.packdir); + return status; +} diff --git a/bin/gensquashfs/src/mkfs.h b/bin/gensquashfs/src/mkfs.h new file mode 100644 index 0000000..53fb018 --- /dev/null +++ b/bin/gensquashfs/src/mkfs.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * mkfs.h + * + * Copyright (C) 2019 David Oberhollenzer + * Copyright (C) 2022 Enno Boland + */ +#ifndef MKFS_H +#define MKFS_H + +#include "config.h" + +#include "common.h" +#include "fstree.h" +#include "util/util.h" +#include "io/file.h" + +#ifdef HAVE_SYS_XATTR_H +#include + +#if defined(__APPLE__) && defined(__MACH__) +#define llistxattr(path, list, size) \ + listxattr(path, list, size, XATTR_NOFOLLOW) + +#define lgetxattr(path, name, value, size) \ + getxattr(path, name, value, size, 0, XATTR_NOFOLLOW) +#endif +#endif + +#ifdef WITH_SELINUX +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + sqfs_writer_cfg_t cfg; + unsigned int dirscan_flags; + const char *infile; + const char *selinux; + const char *xattr_file; + const char *sortfile; + bool no_tail_packing; + + /* copied from command line or constructed from infile argument + if not specified. Must be free'd. */ + char *packdir; + + unsigned int force_uid_value; + unsigned int force_gid_value; + bool force_uid; + bool force_gid; + + bool scan_xattr; +} options_t; + +struct XattrMapEntry { + char *key; + sqfs_u8 *value; + size_t value_len; + struct XattrMapEntry *next; +}; + +struct XattrMapPattern { + char *path; + struct XattrMapEntry *entries; + struct XattrMapPattern *next; +}; + +struct XattrMap { + struct XattrMapPattern *patterns; +}; + +void process_command_line(options_t *opt, int argc, char **argv); + +int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, + void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr); + +void *xattr_open_map_file(const char *path); + +int +xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr); + +void xattr_close_map_file(void *xattr_map); + +void *selinux_open_context_file(const char *filename); + +int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, + tree_node_t *node, const char *path); + +void selinux_close_context_file(void *sehnd); + +/* + Parses the file format accepted by gensquashfs and produce a file system + tree from it. File input paths are interpreted as relative to the current + working directory. + + On failure, an error report with filename and line number is written + to stderr. + + Returns 0 on success. + */ +int fstree_from_file(fstree_t *fs, const char *filename, + const char *basepath); + +int fstree_from_file_stream(fstree_t *fs, istream_t *file, + const char *basepath); + +/* + Recursively scan a directory to build a file system tree. + + Returns 0 on success, prints to stderr on failure. + */ +int fstree_from_dir(fstree_t *fs, tree_node_t *root, + const char *path, scan_node_callback cb, void *user, + unsigned int flags); + +/* + Same as fstree_from_dir, but scans a sub-directory inside the specified path. + + Returns 0 on success, prints to stderr on failure. + */ +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); + +int fstree_sort_files(fstree_t *fs, istream_t *sortfile); + +#endif /* MKFS_H */ diff --git a/bin/gensquashfs/src/options.c b/bin/gensquashfs/src/options.c new file mode 100644 index 0000000..f263bce --- /dev/null +++ b/bin/gensquashfs/src/options.c @@ -0,0 +1,383 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * options.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +enum { + ALL_ROOT_OPTION = 1, +}; + +static struct option long_opts[] = { + { "all-root", no_argument, NULL, ALL_ROOT_OPTION }, + { "set-uid", required_argument, NULL, 'u' }, + { "set-gid", required_argument, NULL, 'g' }, + { "compressor", required_argument, NULL, 'c' }, + { "block-size", required_argument, NULL, 'b' }, + { "dev-block-size", required_argument, NULL, 'B' }, + { "defaults", required_argument, NULL, 'd' }, + { "comp-extra", required_argument, NULL, 'X' }, + { "pack-file", required_argument, NULL, 'F' }, + { "pack-dir", required_argument, NULL, 'D' }, + { "num-jobs", required_argument, NULL, 'j' }, + { "queue-backlog", required_argument, NULL, 'Q' }, + { "keep-time", no_argument, NULL, 'k' }, +#ifdef HAVE_SYS_XATTR_H + { "keep-xattr", no_argument, NULL, 'x' }, +#endif + { "one-file-system", no_argument, NULL, 'o' }, + { "exportable", no_argument, NULL, 'e' }, + { "no-tail-packing", no_argument, NULL, 'T' }, + { "force", no_argument, NULL, 'f' }, + { "quiet", no_argument, NULL, 'q' }, +#ifdef WITH_SELINUX + { "selinux", required_argument, NULL, 's' }, +#endif + { "xattr-file", required_argument, NULL, 'A' }, + { "sort-file", required_argument, NULL, 'S' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = "F:D:X:c:b:B:d:u:g:j:Q:S:A:kxoefqThV" +#ifdef WITH_SELINUX +"s:" +#endif +#ifdef HAVE_SYS_XATTR_H +"x" +#endif +; + +static const char *help_string = +"Usage: gensquashfs [OPTIONS...] \n" +"\n" +"Possible options:\n" +"\n"; + +static const char *pack_options = +" --pack-file, -F Use a `gen_init_cpio` style description file.\n" +" The file format is specified below.\n" +" If --pack-dir is used, input file paths are\n" +" relative to the pack directory, otherwise\n" +" they are relative to the directory the pack\n" +" file is in.\n" +" --pack-dir, -D If --pack-file is used, this is the root path\n" +" relative to which to read files. If no pack\n" +" file is specified, pack the contents of the\n" +" given directory. The directory becomes the\n" +" file system root.\n" +"\n" +" --compressor, -c Select the compressor to use.\n" +" A list of available compressors is below.\n" +" --comp-extra, -X A comma separated list of extra options for\n" +" the selected compressor. Specify 'help' to\n" +" get a list of available options.\n" +" --num-jobs, -j Number of compressor jobs to create.\n" +" --queue-backlog, -Q Maximum number of data blocks in the thread\n" +" worker queue before the packer starts waiting\n" +" for the block processors to catch up.\n" +" Defaults to 10 times the number of jobs.\n" +" --block-size, -b Block size to use for Squashfs image.\n" +" Defaults to %u.\n" +" --dev-block-size, -B Device block size to padd the image to.\n" +" Defaults to %u.\n" +" --defaults, -d A comma separated list of default values for\n" +" implicitly created directories.\n" +"\n" +" Possible options:\n" +" uid= 0 if not set.\n" +" gid= 0 if not set.\n" +" mode= 0755 if not set.\n" +" mtime= 0 if not set.\n" +"\n" +" --set-uid, -u Force the owners user ID for ALL inodes to\n" +" this value, no matter what the pack file or\n" +" directory entries actually specify.\n" +" --set-gid, -g Force the owners group ID for ALL inodes to\n" +" this value, no matter what the pack file or\n" +" directory entries actually specify.\n" +" --all-root A short hand for `--set-uid 0 --set-gid 0`.\n" +"\n"; + +const char *extra_options = +" --sort-file, -S Specify a \"sort file\" that can be used to\n" +" micro manage the order of files during packing\n" +" and behaviour (compression, fragmentation, ..)\n" +"\n" +#ifdef WITH_SELINUX +" --selinux, -s Specify an SELinux label file to get context\n" +" attributes from.\n" +#endif +" --xattr-file, -A Specify an Xattr file to get extended attributes\n" +" for loading xattrs\n" +" --keep-time, -k When using --pack-dir only, use the timestamps\n" +" from the input files instead of setting\n" +" defaults on all input paths.\n" +" --keep-xattr, -x When using --pack-dir only, read and pack the\n" +" extended attributes from the input files.\n" +" --one-file-system, -o When using --pack-dir only, stay in local file\n" +" system and do not cross mount points.\n" +" --exportable, -e Generate an export table for NFS support.\n" +" --no-tail-packing, -T Do not perform tail end packing on files that\n" +" are larger than block size.\n" +" --force, -f Overwrite the output file if it exists.\n" +" --quiet, -q Do not print out progress reports.\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n"; + +const char *pack_details = +"Example of a pack file:\n" +"\n" +" # A simple squashfs image\n" +" dir /dev 0755 0 0\n" +" nod /dev/console 0600 0 0 c 5 1\n" +" dir /root 0700 0 0\n" +" \n" +" # `slink` for symlink, `link` for hard links\n" +" slink /lib 0777 0 0 /usr/lib\n" +" link /init 0777 0 0 /sbin/init\n" +" \n" +" # Add a file. Input is relative to listing or pack dir.\n" +" file /sbin/init 0755 0 0 ../init/sbin/init\n" +" \n" +" # Read bin/bash, relative to listing or pack dir.\n" +" # Implicitly create /bin.\n" +" file /bin/bash 0755 0 0\n" +" \n" +" # file name with a space in it.\n" +" file \"/opt/my app/\\\"special\\\"/data\" 0600 0 0\n" +" \n" +" # collect the contents of ./lib and put it under /usr/lib\n" +" glob /usr/lib 0755 0 0 -type d ./lib\n" +" glob /usr/lib 0755 0 0 -type f -name \"*.so.*\" ./lib\n" +" glob /usr/lib 0777 0 0 -type l -name \"*.so.*\" ./lib\n" +"\n\n"; + +const char *sort_details = +"When using a sort file, the specified paths are within the SquashFS image.\n" +"Files with lower priority are packed first, default priority is 0.\n" +"The sorting is stable, files with the same priority do not change place\n" +"relative to each other.\n" +"\n" +"Example:\n" +" # Specify a packing order with file globbing\n" +" -8000 [glob] bin/*\n" +" -5000 [glob] lib/*\n" +"\n" +" # glob_no_path means * is allowed to match /\n" +" -1000 [glob_no_path] share/*\n" +"\n" +" # Our boot loader needs this\n" +" -100000 [dont_compress,dont_fragment,nosparse] boot/vmlinuz\n" +"\n" +" # For demonstration, a quoted filename and no flags\n" +" 1337 \"usr/share/my \\\"special\\\" file \"\n" +"\n\n"; + +static const char *xattr_details = +"The format of xattr files tries to immitate the format generated\n" +"by `getfattr --dump`.\n" +"\n" +"Example:\n" +" # file: dev/\n" +" security.selinux=\"system_u:object_r:device_t:s0\"\n" +" user.beverage_preference=0xCAFECAFEDECAFBAD\n" +"\n" +" # file: dev/rfkill\n" +" security.selinux=\"system_u:object_r:wireless_device_t:s0\"\n" +" system.posix_acl_access=0sSGVsbG8gdGhlcmUgOi0pCg==\n" +"\n\n"; + +void process_command_line(options_t *opt, int argc, char **argv) +{ + bool have_compressor; + int i, ret; + + memset(opt, 0, sizeof(*opt)); + sqfs_writer_cfg_init(&opt->cfg); + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case ALL_ROOT_OPTION: + opt->force_uid_value = 0; + opt->force_gid_value = 0; + opt->force_uid = true; + opt->force_gid = true; + break; + case 'u': + opt->force_uid_value = strtol(optarg, NULL, 0); + opt->force_uid = true; + break; + case 'g': + opt->force_gid_value = strtol(optarg, NULL, 0); + opt->force_gid = true; + break; + case 'T': + opt->no_tail_packing = true; + break; + case 'c': + have_compressor = true; + ret = sqfs_compressor_id_from_name(optarg); + + if (ret < 0) { + have_compressor = false; +#ifdef WITH_LZO + if (opt->cfg.comp_id == SQFS_COMP_LZO) + have_compressor = true; +#endif + } + + if (!have_compressor) { + fprintf(stderr, "Unsupported compressor '%s'\n", + optarg); + exit(EXIT_FAILURE); + } + + opt->cfg.comp_id = ret; + break; + case 'b': + if (parse_size("Block size", &opt->cfg.block_size, + optarg, 0)) { + exit(EXIT_FAILURE); + } + break; + case 'j': + opt->cfg.num_jobs = strtol(optarg, NULL, 0); + break; + case 'Q': + opt->cfg.max_backlog = strtol(optarg, NULL, 0); + break; + case 'B': + if (parse_size("Device block size", + &opt->cfg.devblksize, optarg, 0)) { + exit(EXIT_FAILURE); + } + if (opt->cfg.devblksize < 1024) { + fputs("Device block size must be at " + "least 1024\n", stderr); + exit(EXIT_FAILURE); + } + break; + case 'd': + opt->cfg.fs_defaults = optarg; + break; + case 'k': + opt->dirscan_flags |= DIR_SCAN_KEEP_TIME; + break; +#ifdef HAVE_SYS_XATTR_H + case 'x': + opt->scan_xattr = true; + break; +#endif + case 'o': + opt->dirscan_flags |= DIR_SCAN_ONE_FILESYSTEM; + break; + case 'e': + opt->cfg.exportable = true; + break; + case 'f': + opt->cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; + break; + case 'q': + opt->cfg.quiet = true; + break; + case 'X': + opt->cfg.comp_extra = optarg; + break; + case 'F': + opt->infile = optarg; + break; + case 'D': + free(opt->packdir); + opt->packdir = strdup(optarg); + if (opt->packdir == NULL) { + perror(optarg); + exit(EXIT_FAILURE); + } + break; +#ifdef WITH_SELINUX + case 's': + opt->selinux = optarg; + break; +#endif + case 'A': + opt->xattr_file = optarg; + break; + case 'S': + opt->sortfile = optarg; + break; + case 'h': + fputs(help_string, stdout); + printf(pack_options, SQFS_DEFAULT_BLOCK_SIZE, + SQFS_DEVBLK_SIZE); + fputs(extra_options, stdout); + fputs(pack_details, stdout); + fputs(sort_details, stdout); + fputs(xattr_details, stdout); + compressor_print_available(); + exit(EXIT_SUCCESS); + case 'V': + print_version("gensquashfs"); + exit(EXIT_SUCCESS); + default: + goto fail_arg; + } + } + + if (opt->cfg.num_jobs < 1) + opt->cfg.num_jobs = 1; + + if (opt->cfg.max_backlog < 1) + opt->cfg.max_backlog = 10 * opt->cfg.num_jobs; + + if (opt->cfg.comp_extra != NULL && + strcmp(opt->cfg.comp_extra, "help") == 0) { + compressor_print_help(opt->cfg.comp_id); + exit(EXIT_SUCCESS); + } + + if (opt->infile == NULL && opt->packdir == NULL) { + fputs("No input file or directory specified.\n", stderr); + goto fail_arg; + } + + if (optind >= argc) { + fputs("No output file specified.\n", stderr); + goto fail_arg; + } + + opt->cfg.filename = argv[optind++]; + + if (optind < argc) { + fputs("Unknown extra arguments specified.\n", stderr); + goto fail_arg; + } + + /* construct packdir if not specified */ + if (opt->packdir == NULL && opt->infile != NULL) { + const char *split = strrchr(opt->infile, '/'); + + if (split != NULL) { + opt->packdir = strndup(opt->infile, + split - opt->infile); + + if (opt->packdir == NULL) { + perror("constructing input directory path"); + exit(EXIT_FAILURE); + } + } + } + return; +fail_arg: + fputs("Try `gensquashfs --help' for more information.\n", stderr); + free(opt->packdir); + exit(EXIT_FAILURE); +} diff --git a/bin/gensquashfs/src/selinux.c b/bin/gensquashfs/src/selinux.c new file mode 100644 index 0000000..678723b --- /dev/null +++ b/bin/gensquashfs/src/selinux.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * selinux.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +#define XATTR_NAME_SELINUX "security.selinux" +#define XATTR_VALUE_SELINUX "system_u:object_r:unlabeled_t:s0" + +#ifdef WITH_SELINUX +int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, + tree_node_t *node, const char *path) +{ + char *context = NULL; + int ret; + + if (selabel_lookup(sehnd, &context, path, node->mode) < 0) { + context = strdup(XATTR_VALUE_SELINUX); + if (context == NULL) + goto fail; + } + + ret = sqfs_xattr_writer_add(xwr, XATTR_NAME_SELINUX, + context, strlen(context)); + free(context); + + if (ret) + sqfs_perror(node->name, "storing SELinux xattr", ret); + + return ret; +fail: + perror("relabeling files"); + return -1; +} + +void *selinux_open_context_file(const char *filename) +{ + struct selabel_handle *sehnd; + struct selinux_opt seopts[] = { + { SELABEL_OPT_PATH, filename }, + }; + + sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); + if (sehnd == NULL) + perror(filename); + + return sehnd; +} + +void selinux_close_context_file(void *sehnd) +{ + selabel_close(sehnd); +} +#else +int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, + tree_node_t *node, const char *path) +{ + (void)sehnd; (void)xwr; (void)node; (void)path; + fputs("Built without SELinux support, cannot add SELinux labels\n", + stderr); + return -1; +} + +void *selinux_open_context_file(const char *filename) +{ + (void)filename; + fputs("Built without SELinux support, cannot open contexts file\n", + stderr); + return NULL; +} + +void selinux_close_context_file(void *sehnd) +{ + (void)sehnd; +} +#endif diff --git a/bin/gensquashfs/src/sort_by_file.c b/bin/gensquashfs/src/sort_by_file.c new file mode 100644 index 0000000..a555718 --- /dev/null +++ b/bin/gensquashfs/src/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 + */ +#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; +} -- cgit v1.2.3