From 99741eab20b9fe77cef44cdacdab9e46f2dcdc15 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Sun, 21 Jul 2019 16:00:07 +0200 Subject: Add support for storing xattr values out-of-line Signed-off-by: David Oberhollenzer --- lib/sqfs/write_xattr.c | 112 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 10 deletions(-) diff --git a/lib/sqfs/write_xattr.c b/lib/sqfs/write_xattr.c index 89c65db..33b0521 100644 --- a/lib/sqfs/write_xattr.c +++ b/lib/sqfs/write_xattr.c @@ -38,7 +38,8 @@ bool sqfs_has_xattr(const char *key) return get_prefix(key) >= 0; } -static int write_key(meta_writer_t *mw, const char *key, tree_xattr_t *xattr) +static int write_key(meta_writer_t *mw, const char *key, tree_xattr_t *xattr, + bool value_is_ool) { const char *orig_key = key; sqfs_xattr_entry_t kent; @@ -54,6 +55,9 @@ static int write_key(meta_writer_t *mw, const char *key, tree_xattr_t *xattr) assert(key != NULL); ++key; + if (value_is_ool) + type |= SQUASHFS_XATTR_FLAG_OOL; + kent.type = htole16(type); kent.size = htole16(strlen(key)); @@ -67,14 +71,20 @@ static int write_key(meta_writer_t *mw, const char *key, tree_xattr_t *xattr) } static int write_value(meta_writer_t *mw, const char *value, - tree_xattr_t *xattr) + tree_xattr_t *xattr, uint64_t *value_ref_out) { sqfs_xattr_value_t vent; + uint32_t offset; + uint64_t block; vent.size = htole32(strlen(value)); if (meta_writer_append(mw, &vent, sizeof(vent))) return -1; + + meta_writer_get_position(mw, &block, &offset); + *value_ref_out = (block << 16) | (offset & 0xFFFF); + if (meta_writer_append(mw, value, strlen(value))) return -1; @@ -82,10 +92,55 @@ static int write_value(meta_writer_t *mw, const char *value, return 0; } -static int write_kv_pairs(fstree_t *fs, meta_writer_t *mw, tree_xattr_t *xattr) +static int write_value_ool(meta_writer_t *mw, uint64_t location, + tree_xattr_t *xattr) +{ + sqfs_xattr_value_t vent; + uint64_t ref; + + vent.size = htole32(sizeof(location)); + if (meta_writer_append(mw, &vent, sizeof(vent))) + return -1; + + ref = htole64(location); + if (meta_writer_append(mw, &ref, sizeof(ref))) + return -1; + + xattr->size += sizeof(vent) + sizeof(ref); + return 0; +} + +static bool should_store_ool(fstree_t *fs, const char *value, size_t index) +{ + size_t refcount; + + refcount = str_table_get_ref_count(&fs->xattr_values, index); + if (refcount < 2) + return false; + + /* + Storing in line needs this many bytes: refcount * len + + Storing out-of-line needs this many: len + (refcount - 1) * 8 + + Out-of-line prefereable if: + refcount * len > len + (refcount - 1) * 8 + => refcount * len - len > (refcount - 1) * 8 + => (refcount - 1) * len > (refcount - 1) * 8 + => len > 8 + + Note that this only holds iff refcount - 1 != 0, i.e. refcount > 1, + otherwise we would be dividing by 0 in the 3rd step. + */ + return strlen(value) > sizeof(uint64_t); +} + +static int write_kv_pairs(fstree_t *fs, meta_writer_t *mw, tree_xattr_t *xattr, + uint64_t *ool_locations) { uint32_t key_idx, val_idx; const char *key, *value; + uint64_t ref; size_t i; for (i = 0; i < xattr->num_attr; ++i) { @@ -95,20 +150,50 @@ static int write_kv_pairs(fstree_t *fs, meta_writer_t *mw, tree_xattr_t *xattr) key = str_table_get_string(&fs->xattr_keys, key_idx); value = str_table_get_string(&fs->xattr_values, val_idx); - if (write_key(mw, key, xattr)) - return -1; + if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFF) { + if (write_key(mw, key, xattr, false)) + return -1; + + if (write_value(mw, value, xattr, &ref)) + return -1; - if (write_value(mw, value, xattr)) - return -1; + if (should_store_ool(fs, value, val_idx)) + ool_locations[val_idx] = ref; + } else { + if (write_key(mw, key, xattr, true)) + return -1; + + if (write_value_ool(mw, ool_locations[val_idx], xattr)) + return -1; + } } return 0; } +static uint64_t *create_ool_locations_table(fstree_t *fs) +{ + uint64_t *table; + size_t i; + + table = malloc(sizeof(uint64_t) * fs->xattr_values.num_strings); + + if (table == NULL) { + perror("allocating Xattr OOL locations table"); + return NULL; + } + + for (i = 0; i < fs->xattr_values.num_strings; ++i) { + table[i] = 0xFFFFFFFFFFFFFFFFUL; + } + + return table; +} + int write_xattr(int outfd, fstree_t *fs, sqfs_super_t *super, compressor_t *cmp) { - uint64_t kv_start, id_start, block, *tbl; + uint64_t kv_start, id_start, block, *tbl, *ool_locations; size_t i = 0, count = 0, blocks; sqfs_xattr_id_table_t idtbl; sqfs_xattr_id_t id_ent; @@ -119,9 +204,13 @@ int write_xattr(int outfd, fstree_t *fs, sqfs_super_t *super, if (fs->xattr == NULL) return 0; + ool_locations = create_ool_locations_table(fs); + if (ool_locations == NULL) + return -1; + mw = meta_writer_create(outfd, cmp, false); if (mw == NULL) - return -1; + goto fail_ool; /* write xattr key-value pairs */ kv_start = super->bytes_used; @@ -130,7 +219,7 @@ int write_xattr(int outfd, fstree_t *fs, sqfs_super_t *super, meta_writer_get_position(mw, &it->block, &it->offset); it->size = 0; - if (write_kv_pairs(fs, mw, it)) + if (write_kv_pairs(fs, mw, it, ool_locations)) goto fail_mw; ++count; @@ -201,10 +290,13 @@ int write_xattr(int outfd, fstree_t *fs, sqfs_super_t *super, free(tbl); meta_writer_destroy(mw); + free(ool_locations); return 0; fail_tbl: free(tbl); fail_mw: meta_writer_destroy(mw); +fail_ool: + free(ool_locations); return -1; } -- cgit v1.2.3