diff options
-rw-r--r-- | include/fstree.h | 4 | ||||
-rw-r--r-- | include/squashfs.h | 33 | ||||
-rw-r--r-- | mkfs/Makemodule.am | 2 | ||||
-rw-r--r-- | mkfs/mkfs.c | 3 | ||||
-rw-r--r-- | mkfs/mkfs.h | 2 | ||||
-rw-r--r-- | mkfs/xattr.c | 198 |
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; +} |