summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/fstree.h4
-rw-r--r--include/squashfs.h33
-rw-r--r--mkfs/Makemodule.am2
-rw-r--r--mkfs/mkfs.c3
-rw-r--r--mkfs/mkfs.h2
-rw-r--r--mkfs/xattr.c198
6 files changed, 241 insertions, 1 deletions
diff --git a/include/fstree.h b/include/fstree.h
index 96294b8..e8324a9 100644
--- a/include/fstree.h
+++ b/include/fstree.h
@@ -28,6 +28,10 @@ struct tree_xattr_t {
size_t num_attr;
size_t max_attr;
+ uint32_t block;
+ uint32_t offset;
+ uint32_t size;
+
/**
* @brief Incremental index within all xattr blocks
*/
diff --git a/include/squashfs.h b/include/squashfs.h
index e599863..5574b29 100644
--- a/include/squashfs.h
+++ b/include/squashfs.h
@@ -164,6 +164,30 @@ typedef struct {
uint8_t name[];
} sqfs_dir_index_t;
+typedef struct {
+ uint16_t type;
+ uint16_t size;
+ uint8_t key[];
+} sqfs_xattr_entry_t;
+
+typedef struct {
+ uint32_t size;
+ uint8_t value[];
+} sqfs_xattr_value_t;
+
+typedef struct {
+ uint64_t xattr;
+ uint32_t count;
+ uint32_t size;
+} sqfs_xattr_id_t;
+
+typedef struct {
+ uint64_t xattr_table_start;
+ uint32_t xattr_ids;
+ uint32_t unused;
+} sqfs_xattr_id_table_t;
+
+
typedef enum {
SQFS_COMP_GZIP = 1,
SQFS_COMP_LZMA = 2,
@@ -207,6 +231,15 @@ typedef enum {
SQFS_INODE_EXT_SOCKET = 14,
} E_SQFS_INODE_TYPE;
+typedef enum {
+ SQUASHFS_XATTR_USER = 0,
+ SQUASHFS_XATTR_TRUSTED = 1,
+ SQUASHFS_XATTR_SECURITY = 2,
+
+ SQUASHFS_XATTR_FLAG_OOL = 0x100,
+ SQUASHFS_XATTR_PREFIX_MASK = 0xFF,
+} E_SQFS_XATTR_TYPE;
+
int sqfs_super_init(sqfs_super_t *super, size_t block_size, uint32_t mtime,
E_SQFS_COMPRESSOR compressor);
diff --git a/mkfs/Makemodule.am b/mkfs/Makemodule.am
index d8e75a2..1dedc33 100644
--- a/mkfs/Makemodule.am
+++ b/mkfs/Makemodule.am
@@ -1,5 +1,5 @@
gensquashfs_SOURCES = mkfs/mkfs.c mkfs/mkfs.h mkfs/block.c
-gensquashfs_SOURCES += mkfs/options.c mkfs/meta.c
+gensquashfs_SOURCES += mkfs/options.c mkfs/meta.c mkfs/xattr.c
gensquashfs_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a
gensquashfs_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c
index d028a9d..56f8842 100644
--- a/mkfs/mkfs.c
+++ b/mkfs/mkfs.c
@@ -116,6 +116,9 @@ int main(int argc, char **argv)
if (id_table_write(&info.idtbl, info.outfd, &info.super, info.cmp))
goto out_cmp;
+ if (write_xattr(&info))
+ goto out_cmp;
+
if (sqfs_super_write(&info.super, info.outfd))
goto out_cmp;
diff --git a/mkfs/mkfs.h b/mkfs/mkfs.h
index 33e9970..a555500 100644
--- a/mkfs/mkfs.h
+++ b/mkfs/mkfs.h
@@ -60,4 +60,6 @@ int write_data_to_image(sqfs_info_t *info);
int sqfs_write_inodes(sqfs_info_t *info);
+int write_xattr(sqfs_info_t *info);
+
#endif /* MKFS_H */
diff --git a/mkfs/xattr.c b/mkfs/xattr.c
new file mode 100644
index 0000000..8f1a816
--- /dev/null
+++ b/mkfs/xattr.c
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "meta_writer.h"
+#include "mkfs.h"
+#include "util.h"
+
+#include <assert.h>
+
+static const struct {
+ const char *prefix;
+ E_SQFS_XATTR_TYPE type;
+} xattr_types[] = {
+ { "user.", SQUASHFS_XATTR_USER },
+ { "trusted.", SQUASHFS_XATTR_TRUSTED },
+ { "security.", SQUASHFS_XATTR_SECURITY },
+};
+
+static int get_prefix(const char *key)
+{
+ size_t i, len;
+
+ for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) {
+ len = strlen(xattr_types[i].prefix);
+
+ if (strncmp(key, xattr_types[i].prefix, len) == 0 &&
+ strlen(key) > len) {
+ return xattr_types[i].type;
+ }
+ }
+
+ fprintf(stderr, "unknown xattr key '%s'\n", key);
+ return -1;
+}
+
+static int write_pair(meta_writer_t *mw, const char *key, const char *value)
+{
+ sqfs_xattr_entry_t kent;
+ sqfs_xattr_value_t vent;
+ int type;
+
+ type = get_prefix(key);
+ if (type < 0)
+ return -1;
+
+ key = strchr(key, '.');
+ assert(key != NULL);
+ ++key;
+
+ kent.type = htole16(type);
+ kent.size = htole16(strlen(key));
+
+ if (meta_writer_append(mw, &kent, sizeof(kent)))
+ return -1;
+ if (meta_writer_append(mw, key, strlen(key)))
+ return -1;
+
+ vent.size = htole32(strlen(value));
+
+ if (meta_writer_append(mw, &vent, sizeof(vent)))
+ return -1;
+ if (meta_writer_append(mw, value, strlen(value)))
+ return -1;
+
+ return 0;
+}
+
+static int write_kv_pairs(sqfs_info_t *info, meta_writer_t *mw,
+ tree_xattr_t *xattr)
+{
+ uint32_t key_idx, val_idx;
+ const char *key, *value;
+ size_t i;
+
+ for (i = 0; i < xattr->num_attr; ++i) {
+ key_idx = (xattr->ref[i] >> 32) & 0xFFFFFFFF;
+ val_idx = xattr->ref[i] & 0xFFFFFFFF;
+
+ key = str_table_get_string(&info->fs.xattr_keys, key_idx);
+ value = str_table_get_string(&info->fs.xattr_values, val_idx);
+
+ if (write_pair(mw, key, value))
+ return -1;
+
+ xattr->size += sizeof(sqfs_xattr_entry_t) + strlen(key);
+ xattr->size += sizeof(sqfs_xattr_value_t) + strlen(value);
+ }
+
+ return 0;
+}
+
+int write_xattr(sqfs_info_t *info)
+{
+ uint64_t kv_start, id_start, *tbl;
+ size_t i = 0, count = 0, blocks;
+ sqfs_xattr_id_table_t idtbl;
+ sqfs_xattr_id_t id_ent;
+ meta_writer_t *mw;
+ tree_xattr_t *it;
+ ssize_t ret;
+
+ if (info->fs.xattr == NULL)
+ return 0;
+
+ mw = meta_writer_create(info->outfd, info->cmp);
+ if (mw == NULL)
+ return -1;
+
+ /* write xattr key-value pairs */
+ kv_start = info->super.bytes_used;
+
+ for (it = info->fs.xattr; it != NULL; it = it->next) {
+ it->block = mw->block_offset;
+ it->offset = mw->offset;
+ it->size = 0;
+
+ if (write_kv_pairs(info, mw, it))
+ goto fail_mw;
+
+ ++count;
+ }
+
+ if (meta_writer_flush(mw))
+ goto fail_mw;
+
+ info->super.bytes_used += mw->block_offset;
+ mw->block_offset = 0;
+
+ /* allocate location table */
+ blocks = (count * sizeof(id_ent)) / SQFS_META_BLOCK_SIZE;
+
+ if ((count * sizeof(id_ent)) % SQFS_META_BLOCK_SIZE)
+ ++blocks;
+
+ tbl = calloc(sizeof(uint64_t), blocks);
+
+ if (tbl == NULL) {
+ perror("generating xattr ID table");
+ goto fail_mw;
+ }
+
+ /* write ID table referring to key value pairs, record offsets */
+ id_start = mw->block_offset;
+ tbl[i++] = htole64(info->super.bytes_used + id_start);
+
+ for (it = info->fs.xattr; it != NULL; it = it->next) {
+ id_ent.xattr = htole64((it->block << 16) | it->offset);
+ id_ent.count = htole32(it->num_attr);
+ id_ent.size = htole32(it->size);
+
+ if (meta_writer_append(mw, &id_ent, sizeof(id_ent)))
+ goto fail_tbl;
+
+ if (mw->block_offset != id_start) {
+ id_start = mw->block_offset;
+ tbl[i++] = htole64(info->super.bytes_used + id_start);
+ }
+ }
+
+ if (meta_writer_flush(mw))
+ goto fail_tbl;
+
+ info->super.bytes_used += mw->block_offset;
+ mw->block_offset = 0;
+
+ /* write offset table */
+ idtbl.xattr_table_start = htole64(kv_start);
+ idtbl.xattr_ids = htole32(count);
+ idtbl.unused = 0;
+
+ ret = write_retry(info->outfd, &idtbl, sizeof(idtbl));
+ if (ret < 0)
+ goto fail_wr;
+ if ((size_t)ret < sizeof(idtbl))
+ goto fail_trunc;
+
+ write_retry(info->outfd, tbl, sizeof(tbl[0]) * blocks);
+ if (ret < 0)
+ goto fail_wr;
+ if ((size_t)ret < sizeof(tbl[0]) * blocks)
+ goto fail_trunc;
+
+ info->super.xattr_id_table_start = info->super.bytes_used;
+ info->super.bytes_used += sizeof(idtbl) + sizeof(tbl[0]) * blocks;
+
+ free(tbl);
+ meta_writer_destroy(mw);
+ return 0;
+fail_wr:
+ perror("writing xattr ID table");
+ goto fail_tbl;
+fail_trunc:
+ fputs("writing xattr ID table: truncated write\n", stderr);
+ goto fail_tbl;
+fail_tbl:
+ free(tbl);
+fail_mw:
+ meta_writer_destroy(mw);
+ return -1;
+}