aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnno Boland <g@s01.de>2021-12-16 19:22:23 +0100
committerDavid Oberhollenzer <goliath@infraroot.at>2022-11-07 15:39:15 +0100
commitc7cd8bd5edeca20f1807dfc5ac7127417509e77a (patch)
tree0a93a1a3de4f06cab9df00a3d382e16b18728116
parentb7877c45fc7fe47709c963e15214a3dd5fc71e32 (diff)
implement parser for xattr files as produced by getfattr --dump XXX
-rw-r--r--bin/gensquashfs/Makemodule.am1
-rw-r--r--bin/gensquashfs/dirscan_xattr.c54
-rw-r--r--bin/gensquashfs/filemap_xattr.c352
-rw-r--r--bin/gensquashfs/mkfs.c8
-rw-r--r--bin/gensquashfs/mkfs.h10
-rw-r--r--bin/gensquashfs/options.c8
6 files changed, 413 insertions, 20 deletions
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 <mail@eboland.de>
+ */
+#include "fstree.h"
+#include "mkfs.h"
+#include <stdio.h>
+
+#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 <file> Specify an SELinux label file to get context\n"
" attributes from.\n"
#endif
+" --xattr-file, -A <file> 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;