__u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
{
__u32 hash = 0;
- char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry);
+ unsigned char *name = (((unsigned char *) entry) +
+ sizeof(struct ext2_ext_attr_entry));
int n;
for (n = 0; n < entry->e_name_len; n++) {
return hash;
}
+__u32 ext2fs_ext_attr_hash_entry_signed(struct ext2_ext_attr_entry *entry,
+ void *data)
+{
+ __u32 hash = 0;
+ signed char *name = (((signed char *) entry) +
+ sizeof(struct ext2_ext_attr_entry));
+ int n;
+
+ for (n = 0; n < entry->e_name_len; n++) {
+ hash = (hash << NAME_HASH_SHIFT) ^
+ (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
+ *name++;
+ }
+
+ /* The hash needs to be calculated on the data in little-endian. */
+ if (entry->e_value_inum == 0 && entry->e_value_size != 0) {
+ __u32 *value = (__u32 *)data;
+ for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
+ EXT2_EXT_ATTR_PAD_BITS; n; n--) {
+ hash = (hash << VALUE_HASH_SHIFT) ^
+ (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
+ ext2fs_le32_to_cpu(*value++);
+ }
+ }
+
+ return hash;
+}
+
+
/*
- * ext2fs_ext_attr_hash_entry2()
+ * ext2fs_ext_attr_hash_entry3()
*
- * Compute the hash of an extended attribute.
- * This version of the function supports hashing entries that reference
- * external inodes (ea_inode feature).
+ * Compute the hash of an extended attribute. This version of the
+ * function supports hashing entries that reference external inodes
+ * (ea_inode feature) as well as calculating the old legacy signed
+ * hash variant.
*/
-errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
+errcode_t ext2fs_ext_attr_hash_entry3(ext2_filsys fs,
struct ext2_ext_attr_entry *entry,
- void *data, __u32 *hash)
+ void *data, __u32 *hash,
+ __u32 *signed_hash)
{
*hash = ext2fs_ext_attr_hash_entry(entry, data);
+ if (signed_hash)
+ *signed_hash = ext2fs_ext_attr_hash_entry_signed(entry, data);
if (entry->e_value_inum) {
__u32 ea_inode_hash;
*hash = (*hash << VALUE_HASH_SHIFT) ^
(*hash >> (8*sizeof(*hash) - VALUE_HASH_SHIFT)) ^
ea_inode_hash;
+ if (signed_hash)
+ *signed_hash = (*signed_hash << VALUE_HASH_SHIFT) ^
+ (*signed_hash >> (8*sizeof(*hash) -
+ VALUE_HASH_SHIFT)) ^
+ ea_inode_hash;
}
return 0;
}
+/*
+ * ext2fs_ext_attr_hash_entry2()
+ *
+ * Compute the hash of an extended attribute.
+ * This version of the function supports hashing entries that reference
+ * external inodes (ea_inode feature).
+ */
+errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
+ struct ext2_ext_attr_entry *entry,
+ void *data, __u32 *hash)
+{
+ return ext2fs_ext_attr_hash_entry3(fs, entry, data, hash, NULL);
+}
+
#undef NAME_HASH_SHIFT
#undef VALUE_HASH_SHIFT
/* Manipulate the contents of extended attribute regions */
struct ext2_xattr {
+ int name_index;
char *name;
+ char *short_name;
void *value;
unsigned int value_len;
ext2_ino_t ea_ino;
/* Keep these names sorted in order of decreasing specificity. */
static struct ea_name_index ea_names[] = {
+ {10, "gnu."},
{3, "system.posix_acl_default"},
{2, "system.posix_acl_access"},
{8, "system.richacl"},
s = sizeof(ext4_acl_header);
for (end = entry + count; entry != end;entry++) {
ext4_acl_entry *disk_entry = (ext4_acl_entry*) e;
- disk_entry->e_tag = ext2fs_cpu_to_le16(entry->e_tag);
- disk_entry->e_perm = ext2fs_cpu_to_le16(entry->e_perm);
+ disk_entry->e_tag = entry->e_tag;
+ disk_entry->e_perm = entry->e_perm;
- switch(entry->e_tag) {
+ switch(ext2fs_le16_to_cpu(entry->e_tag)) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
break;
case ACL_USER:
case ACL_GROUP:
- disk_entry->e_id = ext2fs_cpu_to_le32(entry->e_id);
+ disk_entry->e_id = entry->e_id;
e += sizeof(ext4_acl_entry);
s += sizeof(ext4_acl_entry);
break;
+ default:
+ return EINVAL;
}
}
*size_out = s;
while (size > 0) {
const ext4_acl_entry *disk_entry = (const ext4_acl_entry *) cp;
- entry->e_tag = ext2fs_le16_to_cpu(disk_entry->e_tag);
- entry->e_perm = ext2fs_le16_to_cpu(disk_entry->e_perm);
+ entry->e_tag = disk_entry->e_tag;
+ entry->e_perm = disk_entry->e_perm;
- switch(entry->e_tag) {
+ switch(ext2fs_le16_to_cpu(entry->e_tag)) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
break;
case ACL_USER:
case ACL_GROUP:
- entry->e_id = ext2fs_le32_to_cpu(disk_entry->e_id);
+ entry->e_id = disk_entry->e_id;
cp += sizeof(ext4_acl_entry);
size -= sizeof(ext4_acl_entry);
break;
- default:
- ext2fs_free_mem(&out);
- return EINVAL;
- break;
+ default:
+ ext2fs_free_mem(&out);
+ return EINVAL;
}
entry++;
}
struct ext2_xattr *x;
struct ext2_ext_attr_entry *e = entries_start;
char *end = (char *) entries_start + storage_size;
- const char *shortname;
unsigned int value_size;
- int idx, ret;
errcode_t err;
memset(entries_start, 0, storage_size);
for (x = attrs; x < attrs + count; x++) {
- /* Calculate index and shortname position */
- shortname = x->name;
- ret = find_ea_index(x->name, &shortname, &idx);
-
value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
/* Fill out e appropriately */
- e->e_name_len = strlen(shortname);
- e->e_name_index = (ret ? idx : 0);
+ e->e_name_len = strlen(x->short_name);
+ e->e_name_index = x->name_index;
e->e_value_size = x->value_len;
e->e_value_inum = x->ea_ino;
/* Store name */
- memcpy((char *)e + sizeof(*e), shortname, e->e_name_len);
+ memcpy((char *)e + sizeof(*e), x->short_name, e->e_name_len);
if (x->ea_ino) {
e->e_value_offs = 0;
} else {
memcpy(x->name + prefix_len,
(char *)entry + sizeof(*entry),
entry->e_name_len);
+ x->short_name = x->name + prefix_len;
+ x->name_index = entry->e_name_index;
/* Check & copy value */
if (!ext2fs_has_feature_ea_inode(handle->fs->super) &&
!(ea_inode->i_flags & EXT4_EA_INODE_FL) ||
ea_inode->i_links_count == 0)
err = EXT2_ET_EA_INODE_CORRUPTED;
- else if (ext2fs_file_get_size(ea_file) !=
- entry->e_value_size)
+ else if ((__u64) ext2fs_file_get_size(ea_file) !=
+ entry->e_value_size)
err = EXT2_ET_EA_BAD_VALUE_SIZE;
else
err = ext2fs_file_read(ea_file, x->value,
/* e_hash may be 0 in older inode's ea */
if (entry->e_hash != 0) {
- __u32 hash;
+ __u32 hash, signed_hash;
+
void *data = (entry->e_value_inum != 0) ?
0 : value_start + entry->e_value_offs;
- err = ext2fs_ext_attr_hash_entry2(handle->fs, entry,
- data, &hash);
+ err = ext2fs_ext_attr_hash_entry3(handle->fs, entry,
+ data, &hash,
+ &signed_hash);
if (err)
return err;
- if (entry->e_hash != hash) {
+ if ((entry->e_hash != hash) &&
+ (entry->e_hash != signed_hash)) {
struct ext2_inode child;
/* Check whether this is an old Lustre-style
}
static errcode_t xattr_update_entry(ext2_filsys fs, struct ext2_xattr *x,
- const char *name, const void *value,
+ const char *name, const char *short_name,
+ int index, const void *value,
size_t value_len, int in_inode)
{
ext2_ino_t ea_ino = 0;
goto fail;
}
- if (!x->name)
+ if (!x->name) {
x->name = new_name;
+ x->short_name = new_name + (short_name - name);
+ }
+ x->name_index = index;
if (x->value)
ext2fs_free_mem(&x->value);
}
static int xattr_find_position(struct ext2_xattr *attrs, int count,
- const char *name)
+ const char *shortname, int name_idx)
{
struct ext2_xattr *x;
int i;
- const char *shortname, *x_shortname;
- int name_idx, x_name_idx;
int shortname_len, x_shortname_len;
- find_ea_index(name, &shortname, &name_idx);
shortname_len = strlen(shortname);
for (i = 0, x = attrs; i < count; i++, x++) {
- find_ea_index(x->name, &x_shortname, &x_name_idx);
- if (name_idx < x_name_idx)
+ if (name_idx < x->name_index)
break;
- if (name_idx > x_name_idx)
+ if (name_idx > x->name_index)
continue;
- x_shortname_len = strlen(x_shortname);
+ x_shortname_len = strlen(x->short_name);
if (shortname_len < x_shortname_len)
break;
if (shortname_len > x_shortname_len)
continue;
- if (memcmp(shortname, x_shortname, shortname_len) <= 0)
+ if (memcmp(shortname, x->short_name, shortname_len) <= 0)
break;
}
return i;
struct ext2_xattr tmp;
int add_to_ibody;
int needed;
- int name_len, name_idx;
- const char *shortname;
+ int name_len, name_idx = 0;
+ const char *shortname = name;
int new_idx;
int ret;
/* Update the existing entry. */
ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
- value, value_len, in_inode);
+ shortname, name_idx, value,
+ value_len, in_inode);
if (ret)
return ret;
if (h->ibody_count <= old_idx) {
if (old_idx >= 0) {
/* Update the existing entry. */
ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
- value, value_len, in_inode);
+ shortname, name_idx, value,
+ value_len, in_inode);
if (ret)
return ret;
if (old_idx < h->ibody_count) {
* entries in the block are sorted.
*/
new_idx = xattr_find_position(h->attrs + h->ibody_count,
- h->count - h->ibody_count, name);
+ h->count - h->ibody_count,
+ shortname, name_idx);
new_idx += h->ibody_count - 1;
tmp = h->attrs[old_idx];
memmove(h->attrs + old_idx, h->attrs + old_idx + 1,
}
new_idx = xattr_find_position(h->attrs + h->ibody_count,
- h->count - h->ibody_count, name);
+ h->count - h->ibody_count,
+ shortname, name_idx);
new_idx += h->ibody_count;
add_to_ibody = 0;
return ret;
}
- ret = xattr_update_entry(h->fs, &h->attrs[h->count], name, value,
- value_len, in_inode);
+ ret = xattr_update_entry(h->fs, &h->attrs[h->count], name, shortname,
+ name_idx, value, value_len, in_inode);
if (ret)
return ret;
{
int total = 0;
struct ext2_xattr *x;
- const char *shortname;
- int i, len, name_idx;
+ int i, len;
for (i = 0, x = attrs; i < count; i++, x++) {
- find_ea_index(x->name, &shortname, &name_idx);
- len = strlen(shortname);
+ len = strlen(x->short_name);
total += EXT2_EXT_ATTR_LEN(len);
if (!x->ea_ino)
total += EXT2_EXT_ATTR_SIZE(x->value_len);
new_value, &value_len);
if (ret)
goto out;
- } else
+ } else if (value_len)
memcpy(new_value, value, value_len);
/* Imitate kernel behavior by skipping update if value is the same. */
for (x = h->attrs; x < h->attrs + h->count; x++) {
if (!strcmp(x->name, name)) {
if (!x->ea_ino && x->value_len == value_len &&
- !memcmp(x->value, new_value, value_len)) {
+ (!value_len ||
+ !memcmp(x->value, new_value, value_len))) {
ret = 0;
goto out;
}