From c7cd8bd5edeca20f1807dfc5ac7127417509e77a Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Thu, 16 Dec 2021 19:22:23 +0100 Subject: implement parser for xattr files as produced by getfattr --dump XXX --- bin/gensquashfs/Makemodule.am | 1 + bin/gensquashfs/dirscan_xattr.c | 54 ++++-- bin/gensquashfs/filemap_xattr.c | 352 ++++++++++++++++++++++++++++++++++++++++ bin/gensquashfs/mkfs.c | 8 +- bin/gensquashfs/mkfs.h | 10 +- bin/gensquashfs/options.c | 8 +- 6 files changed, 413 insertions(+), 20 deletions(-) create mode 100644 bin/gensquashfs/filemap_xattr.c (limited to 'bin') diff --git a/bin/gensquashfs/Makemodule.am b/bin/gensquashfs/Makemodule.am index b0b8a79..e7fad8e 100644 --- a/bin/gensquashfs/Makemodule.am +++ b/bin/gensquashfs/Makemodule.am @@ -1,6 +1,7 @@ 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_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 index 65f7f5c..7d4e552 100644 --- a/bin/gensquashfs/dirscan_xattr.c +++ b/bin/gensquashfs/dirscan_xattr.c @@ -122,10 +122,10 @@ fail: #endif static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, - sqfs_xattr_writer_t *xwr, bool scan_xattr, + sqfs_xattr_writer_t *xwr, bool scan_xattr, void *xattr_map, tree_node_t *node) { - char *path; + char *path = NULL; int ret; ret = sqfs_xattr_writer_begin(xwr, 0); @@ -143,32 +143,49 @@ static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, ret = xattr_from_path(xwr, path); free(path); - - if (ret) - return -1; + path = NULL; + if (ret) { + ret = -1; + goto out; + } } #else (void)path_prefix; #endif - if (selinux_handle != NULL) { + if (selinux_handle != NULL || xattr_map != NULL) { path = fstree_get_path(node); + if (path == NULL) { perror("reconstructing absolute path"); - return -1; + 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); - free(path); - if (ret) - return -1; + 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); - return -1; + ret = -1; + goto out; } if (S_ISDIR(node->mode)) { @@ -176,25 +193,28 @@ static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, while (node != NULL) { if (xattr_xcan_dfs(path_prefix, selinux_handle, xwr, - scan_xattr, node)) { - return -1; + scan_xattr, xattr_map, node)) { + ret = -1; + goto out; } node = node->next; } } - return 0; +out: + free(path); + return ret; } int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, - sqfs_xattr_writer_t *xwr, bool scan_xattr) + void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr) { if (xwr == NULL) return 0; - if (selinux_handle == NULL && !scan_xattr) + if (selinux_handle == NULL && !scan_xattr && xattr_map == NULL) return 0; - return xattr_xcan_dfs(path, selinux_handle, xwr, scan_xattr, fs->root); + 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 new file mode 100644 index 0000000..f241617 --- /dev/null +++ b/bin/gensquashfs/filemap_xattr.c @@ -0,0 +1,352 @@ +/* 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: " + +struct XattrMapEntry { + char *key; + char *value; + size_t value_len; + struct XattrMapEntry *next; +}; + +struct XattrMapPattern { + char *path; + struct XattrMapEntry *entries; + struct XattrMapPattern *next; +}; + +struct XattrMap { + struct XattrMapPattern *patterns; +}; + +// Taken from attr-2.5.1/tools/setfattr.c +static int +hex_digit(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return -1; +} + +// Taken from attr-2.5.1/tools/setfattr.c +static int +base64_digit(char c) { + if (c >= 'A' && c <= 'Z') + return c - 'A'; + else if (c >= 'a' && c <= 'z') + return 26 + c - 'a'; + else if (c >= '0' && c <= '9') + return 52 + c - '0'; + else if (c == '+') + return 62; + else if (c == '/') + return 63; + else if (c == '=') + return -2; + else + return -1; +} + +// Taken from attr-2.5.1/tools/setfattr.c +static char * +decode(const char *value, size_t *size) { + char *decoded = NULL; + + if (*size == 0) + return strdup(""); + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + const char *v = value + 2, *end = value + *size; + char *d; + + decoded = realloc(decoded, *size / 2); + if (decoded == NULL) { + return NULL; + } + d = decoded; + while (v < end) { + int d1, d0; + + while (v < end && isspace(*v)) + v++; + if (v == end) + break; + d1 = hex_digit(*v++); + while (v < end && isspace(*v)) + v++; + if (v == end) { + bad_hex_encoding: + fprintf(stderr, "bad input encoding\n"); + free(decoded); + return NULL; + } + d0 = hex_digit(*v++); + if (d1 < 0 || d0 < 0) + goto bad_hex_encoding; + *d++ = ((d1 << 4) | d0); + } + *size = d - decoded; + } else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) { + const char *v = value + 2, *end = value + *size; + int d0, d1, d2, d3; + char *d; + + decoded = realloc(decoded, *size / 4 * 3); + if (decoded == NULL) { + return NULL; + } + d = decoded; + for (;;) { + while (v < end && isspace(*v)) + v++; + if (v == end) { + d0 = d1 = d2 = d3 = -2; + break; + } + if (v + 4 > end) { + bad_base64_encoding: + free(decoded); + fprintf(stderr, "bad input encoding\n"); + return NULL; + } + d0 = base64_digit(*v++); + d1 = base64_digit(*v++); + d2 = base64_digit(*v++); + d3 = base64_digit(*v++); + if (d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0) + break; + + *d++ = (char)((d0 << 2) | (d1 >> 4)); + *d++ = (char)((d1 << 4) | (d2 >> 2)); + *d++ = (char)((d2 << 6) | d3); + } + if (d0 == -2) { + if (d1 != -2 || d2 != -2 || d3 != -2) + goto bad_base64_encoding; + goto base64_end; + } + if (d0 == -1 || d1 < 0 || d2 == -1 || d3 == -1) + goto bad_base64_encoding; + *d++ = (char)((d0 << 2) | (d1 >> 4)); + if (d2 != -2) + *d++ = (char)((d1 << 4) | (d2 >> 2)); + else { + if (d1 & 0x0F || d3 != -2) + goto bad_base64_encoding; + goto base64_end; + } + if (d3 != -2) + *d++ = (char)((d2 << 6) | d3); + else if (d2 & 0x03) + goto bad_base64_encoding; + base64_end: + while (v < end && isspace(*v)) + v++; + if (v + 4 <= end && *v == '=') { + if (*++v != '=' || *++v != '=' || *++v != '=') + goto bad_base64_encoding; + v++; + } + while (v < end && isspace(*v)) + v++; + if (v < end) + goto bad_base64_encoding; + *size = d - decoded; + } else { + const char *v = value, *end = value + *size; + char *d; + + if (end > v + 1 && *v == '"' && *(end - 1) == '"') { + v++; + end--; + } + + decoded = realloc(decoded, *size); + if (decoded == NULL) { + return NULL; + } + 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; +} + +static int +parse_file_name(char *line, struct XattrMap *map) { + char *p; + struct XattrMapPattern *current_file; + char *file_name = &line[strlen(NEW_FILE_START)]; + + p = strchr(file_name, '\n'); + if (p != NULL) { + *p = '\0'; + } + file_name = strdup(file_name); + if (file_name == NULL) { + return -1; + } + current_file = calloc(1, sizeof(struct XattrMapPattern)); + if (current_file == NULL) { + free(file_name); + return -1; + } + + current_file->next = map->patterns; + map->patterns = current_file; + canonicalize_name(file_name); + current_file->path = file_name; + + return 0; +} + +static int +parse_xattr(char *key_start, char *value_start, struct XattrMap *map) { + char *p; + size_t len; + struct XattrMapPattern *current_pattern = map->patterns; + struct XattrMapEntry *current_entry; + + *value_start = '\0'; + value_start += 1; + p = strchr(value_start, '\n'); + if (p != NULL) { + *p = '\0'; + } + + 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(value_start, &len); + current_entry->value_len = len; + + return 0; +} + +void * +xattr_open_map_file(const char *path) { + struct XattrMap *map; + char *line = NULL; + size_t line_size; + char *p = NULL; + FILE *file = fopen(path, "r"); + if (file == NULL) { + return NULL; + } + + map = calloc(1, sizeof(struct XattrMap)); + if (map == NULL) { + fclose(file); + return NULL; + } + + while (getline(&line, &line_size, file) != -1) { + if (strncmp(NEW_FILE_START, line, strlen(NEW_FILE_START)) == 0) { + if (parse_file_name(line, map) < 0) { + fclose(file); + xattr_close_map_file(map); + return NULL; + } + } else if ((p = strchr(line, '=')) && map->patterns) { + if (parse_xattr(line, p, map) < 0) { + fclose(file); + xattr_close_map_file(map); + return NULL; + } + } + } + + free(line); + fclose(file); + return map; +} + +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); + entry->key = NULL; + free(entry->value); + entry->value = NULL; + free(entry); + } + free(file->path); + file->path = NULL; + 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/mkfs.c b/bin/gensquashfs/mkfs.c index cb891f9..171a887 100644 --- a/bin/gensquashfs/mkfs.c +++ b/bin/gensquashfs/mkfs.c @@ -143,6 +143,7 @@ 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; @@ -156,6 +157,11 @@ int main(int argc, char **argv) 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); @@ -180,7 +186,7 @@ int main(int argc, char **argv) goto out; if (opt.infile == NULL) { - if (xattrs_from_dir(&sqfs.fs, opt.packdir, sehnd, + if (xattrs_from_dir(&sqfs.fs, opt.packdir, sehnd, xattrmap, sqfs.xwr, opt.scan_xattr)) { goto out; } diff --git a/bin/gensquashfs/mkfs.h b/bin/gensquashfs/mkfs.h index 2697d3e..1254770 100644 --- a/bin/gensquashfs/mkfs.h +++ b/bin/gensquashfs/mkfs.h @@ -44,6 +44,7 @@ typedef struct { unsigned int dirscan_flags; const char *infile; const char *selinux; + const char *xattr_file; const char *sortfile; bool no_tail_packing; @@ -62,7 +63,14 @@ typedef struct { void process_command_line(options_t *opt, int argc, char **argv); int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, - sqfs_xattr_writer_t *xwr, bool scan_xattr); + 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); diff --git a/bin/gensquashfs/options.c b/bin/gensquashfs/options.c index fc2d900..b554cb2 100644 --- a/bin/gensquashfs/options.c +++ b/bin/gensquashfs/options.c @@ -35,13 +35,14 @@ static struct option long_opts[] = { #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:kxoefqThV" +static const char *short_opts = "F:D:X:c:b:B:d:u:g:j:Q:S:A:kxoefqThV" #ifdef WITH_SELINUX "s:" #endif @@ -107,6 +108,8 @@ static const char *help_string = " --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" @@ -323,6 +326,9 @@ void process_command_line(options_t *opt, int argc, char **argv) opt->selinux = optarg; break; #endif + case 'A': + opt->xattr_file = optarg; + break; case 'S': opt->sortfile = optarg; break; -- cgit v1.2.3