aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/sqfs/write_xattr.c112
1 files 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;
}