in property.c [348:475]
static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
u32 *block, unsigned int start_offset, size_t block_len)
{
unsigned int data_offset, dir_end;
const struct tb_property *property;
struct tb_property_entry *entry;
size_t dir_len, data_len = 0;
int ret;
/*
* The structure of property block looks like following. Leaf
* data/text is included right after the directory and each
* directory follows each other (even nested ones).
*
* +----------+ <-- start_offset
* | header | <-- root directory header
* +----------+ ---
* | entry 0 | -^--------------------.
* +----------+ | |
* | entry 1 | -|--------------------|--.
* +----------+ | | |
* | entry 2 | -|-----------------. | |
* +----------+ | | | |
* : : | dir_len | | |
* . . | | | |
* : : | | | |
* +----------+ | | | |
* | entry n | v | | |
* +----------+ <-- data_offset | | |
* | data 0 | <------------------|--' |
* +----------+ | |
* | data 1 | <------------------|-----'
* +----------+ |
* | 00000000 | padding |
* +----------+ <-- dir_end <------'
* | UUID | <-- directory UUID (child directory)
* +----------+
* | entry 0 |
* +----------+
* | entry 1 |
* +----------+
* : :
* . .
* : :
* +----------+
* | entry n |
* +----------+
* | data 0 |
* +----------+
*
* We use dir_end to hold pointer to the end of the directory. It
* will increase as we add directories and each directory should be
* added starting from previous dir_end.
*/
dir_len = tb_property_dir_length(dir, false, &data_len);
data_offset = start_offset + dir_len;
dir_end = start_offset + data_len + dir_len;
if (data_offset > dir_end)
return -EINVAL;
if (dir_end > block_len)
return -EINVAL;
/* Write headers first */
if (dir->uuid) {
struct tb_property_dir_entry *pe;
pe = (struct tb_property_dir_entry *)&block[start_offset];
memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
entry = pe->entries;
} else {
struct tb_property_rootdir_entry *re;
re = (struct tb_property_rootdir_entry *)&block[start_offset];
re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
re->length = dir_len - sizeof(*re) / 4;
entry = re->entries;
}
list_for_each_entry(property, &dir->properties, list) {
const struct tb_property_dir *child;
format_dwdata(entry, property->key, 2);
entry->type = property->type;
switch (property->type) {
case TB_PROPERTY_TYPE_DIRECTORY:
child = property->value.dir;
ret = __tb_property_format_dir(child, block, dir_end,
block_len);
if (ret < 0)
return ret;
entry->length = tb_property_dir_length(child, false,
NULL);
entry->value = dir_end;
dir_end = ret;
break;
case TB_PROPERTY_TYPE_DATA:
format_dwdata(&block[data_offset], property->value.data,
property->length);
entry->length = property->length;
entry->value = data_offset;
data_offset += entry->length;
break;
case TB_PROPERTY_TYPE_TEXT:
format_dwdata(&block[data_offset], property->value.text,
property->length);
entry->length = property->length;
entry->value = data_offset;
data_offset += entry->length;
break;
case TB_PROPERTY_TYPE_VALUE:
entry->length = property->length;
entry->value = property->value.immediate;
break;
default:
break;
}
entry++;
}
return dir_end;
}