summaryrefslogtreecommitdiff
path: root/bin/gensquashfs/filemap_xattr.c
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 /bin/gensquashfs/filemap_xattr.c
parentb7877c45fc7fe47709c963e15214a3dd5fc71e32 (diff)
implement parser for xattr files as produced by getfattr --dump XXX
Diffstat (limited to 'bin/gensquashfs/filemap_xattr.c')
-rw-r--r--bin/gensquashfs/filemap_xattr.c352
1 files changed, 352 insertions, 0 deletions
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;
+}