From: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> Subject: [RFC][PATCH 10/15] nilfs2: implement setxattr and removexattr functionality This patch adds functionality of setxattr and removexattr operations. Signed-off-by: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> CC: Ryusuke Konishi <konishi.ryusuke@xxxxxxxxxxxxx> --- fs/nilfs2/xafile.c | 598 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/nilfs2/xattr.c | 26 +++ fs/nilfs2/xattr.h | 15 ++ 3 files changed, 639 insertions(+) diff --git a/fs/nilfs2/xafile.c b/fs/nilfs2/xafile.c index e89e95b..dcc524f 100644 --- a/fs/nilfs2/xafile.c +++ b/fs/nilfs2/xafile.c @@ -1059,6 +1059,65 @@ failed_find_node: } /* + * is_value_size_valid - check that xattr can be stored in xanode + * @xafile: xafile's inode + * @node: pointer on xanode's begin + * @name: xattr name + * @value_size: xattr's value size + */ +static inline +bool is_value_size_valid(struct inode *xafile, char *node, + char *name, size_t value_size) +{ + return value_size <= + (NILFS_XAFILE_I(xafile)->mi.mi_entry_size - + (NILFS_XANODE_HDR_SIZE(NILFS_XANODE_HDR(node)) + + NILFS_XANODE_NOT_INDEX_KEY_SIZE(NILFS_XANODE_HDR(node)) + + NILFS_XANODE_END_KEY_SIZE + + strlen(name) + + value_size)); +} + +/* + * __can_insert_entries - check that entries can be stored in xanode + * @node_ptr: pointer on xanode's begin + * @entries_count: count of xattrs + * @entries_size: size of entries in bytes + */ +static inline +bool __can_insert_entries(char *node_ptr, + int entries_count, size_t entries_size) +{ + union nilfs_xanode_header *hdr = NILFS_XANODE_HDR(node_ptr); + char *begin_free_area = NULL; + char *end_free_area = NULL; + + begin_free_area = (char *)NILFS_XANODE_END_KEY(hdr); + begin_free_area += + (entries_count * NILFS_XANODE_NOT_INDEX_KEY_SIZE(hdr)); + begin_free_area += NILFS_XANODE_END_KEY_SIZE; + + end_free_area = (char*)NILFS_XANODE_LAST_ENTRY(hdr); + end_free_area -= entries_size; + + return begin_free_area <= end_free_area; +} + +/* + * can_insert_entry - check opportunity to insert xattr + * @node_ptr: pointer on xanode's begin + * @name: xattr's name + * @value_size: xattr's value size + */ +static inline +bool can_insert_entry(char *node_ptr, char *name, size_t value_size) +{ + u16 entry_size = NILFS_XATTR_ENTRY_SIZE(name, value_size); + + return __can_insert_entries(node_ptr, 1, entry_size); +} + +/* * __nilfs_xafile_node_init - initialize newly created xanode in memory * @inode: inode pointer * @node_type: xanode type @@ -1328,6 +1387,431 @@ void nilfs_xafile_abort_new_node(struct inode *inode, } /* + * nilfs_xafile_set_entry_for_key - add xattr as last entry by key + * @inode: inode pointer + * @name_index: xattr's name index + * @name: xattr's name + * @value: xattr's value + * @value_size: xattr's value size + * @node_ptr: pointer on xanode's begin + * @key: xanode's key + * @entry: xanode's entry + */ +static +int nilfs_xafile_set_entry_for_key(struct inode *inode, + int name_index, + const char *name, + const void *value, + size_t value_size, + char *node_ptr, + union nilfs_xattr_key *key, + struct nilfs_xanode_entry *entry) +{ + int err; + union nilfs_xanode_header *hdr = NILFS_XANODE_HDR(node_ptr); + ptrdiff_t entry_offset = NILFS_XATTR_ENTRY_OFFSET(node_ptr, entry); + __u32 entry_size = NILFS_XATTR_ENTRY_SIZE(name, value_size); + __u16 name_len = strlen(name); + __u32 *end_key; + + /* TODO: entry can have different structure */ + + /* insert entry */ + memcpy(entry->name, name, name_len); + memcpy(entry->name + name_len, value, value_size); + + /* insert key */ + switch (NILFS_XANODE_TYPE(node_ptr)) { + case NILFS_XATTR_TREE_XANODE_TYPE: + NILFS_XANODE_KEY_SET_TYPE(NILFS_XATTR_TREE_LEAF_KEY_TYPE, key); + + err = calc_name_hash(name_index, name, + &key->leaf_key.name_hash); + if (unlikely(err)) + return err; + + key->leaf_key.entry_offset = cpu_to_le16(entry_offset); + key->leaf_key.entry_size = cpu_to_le16(entry_size); + break; + + default: + BUG(); + }; + + NILFS_XANODE_ADD_ENTRIES(1, hdr); + end_key = NILFS_XANODE_END_KEY(hdr); + *end_key = NILFS_XANODE_END_KEY_VALUE; + + return 0; +} + +/* + * nilfs_xafile_insert_entry_for_key - insert xattr in xanode by key + * @inode: inode pointer + * @name_index: xattr's name index + * @name: xattr's name + * @value: xattr's value + * @value_size: xattr's value size + * @node_ptr: pointer on xanode's begin + * @key: xanode's key + */ +static +int nilfs_xafile_insert_entry_for_key(struct inode *inode, + int name_index, + const char *name, + const void *value, + size_t value_size, + char *node_ptr, + union nilfs_xattr_key *key) +{ + int err; + __u32 entry_size; + union nilfs_xattr_key *last_key; + char *old_last_entry_ptr; + char *new_last_entry_ptr; + __u32 shifted_entries_size; + char *end_key; + char *old_key_ptr = (char *)key; + char *new_key_ptr; + ptrdiff_t shifted_keys_size; + struct nilfs_xanode_entry *inserted; + union nilfs_xattr_key *cur_key; + union nilfs_xanode_header *hdr = NILFS_XANODE_HDR(node_ptr); + +#ifdef CONFIG_NILFS2_FS_DEBUG + BUG_ON(IS_END_KEY(key)); +#endif + + entry_size = NILFS_XATTR_ENTRY_SIZE(name, value_size); + last_key = NILFS_XANODE_LAST_NOT_INDEX_KEY(hdr); + +#ifdef CONFIG_NILFS2_FS_DEBUG + BUG_ON(IS_END_KEY(last_key)); +#endif + + old_last_entry_ptr = (char *)NILFS_XANODE_LAST_ENTRY(hdr); + new_last_entry_ptr = old_last_entry_ptr - entry_size; + shifted_entries_size = (NILFS_XANODE_ENTRY_OFFSET(last_key) - + NILFS_XANODE_ENTRY_OFFSET(key)) + + NILFS_XANODE_ENTRY_SIZE(key); + end_key = (char *)NILFS_XANODE_END_KEY(hdr); + new_key_ptr = old_key_ptr + NILFS_XANODE_NOT_INDEX_KEY_SIZE(hdr); + shifted_keys_size = (end_key - old_key_ptr) + NILFS_XANODE_END_KEY_SIZE; + inserted = (struct nilfs_xanode_entry *)(new_last_entry_ptr + + shifted_entries_size); + + /* shift entries */ + memmove(new_last_entry_ptr, + old_last_entry_ptr, shifted_entries_size); + + /* shift keys */ + memmove(new_key_ptr, old_key_ptr, shifted_keys_size); + + /* correct shifted keys */ + cur_key = NILFS_XANODE_KEY(new_key_ptr); + for (; !IS_END_KEY(cur_key); cur_key = NEXT_KEY(cur_key, 1)) + NILFS_XANODE_ADD_ENTRY_OFFSET(entry_size, cur_key); + + err = nilfs_xafile_set_entry_for_key(inode, + name_index, name, + value, value_size, + node_ptr, key, inserted); + + return err; +} + +/* + * nilfs_xafile_node_insert_entry - insert xattr in xanode + * @inode: inode pointer + * @data: internal xattr search structure + * @value: xattr's value + * @size: xattr's value size + */ +static +int nilfs_xafile_node_insert_entry(struct inode *inode, + struct nilfs_xattr_search *data, + const void *value, + size_t size) +{ + int err; + union nilfs_xanode_header *hdr; + struct nilfs_xanode_entry *inserted = NULL; + __u32 entry_size; + u8 name_index; + char *name; + +#ifdef CONFIG_NILFS2_FS_DEBUG + BUG_ON(!IS_SHADOW_XANODE_DESC(&data->node)); +#endif + + hdr = data->result.hdr; + + if (!can_insert_entry((char *)hdr, data->search.name, size)) + return -ENOSPC; + + /* TODO: entry size can be calculated on different basis */ + entry_size = NILFS_XATTR_ENTRY_SIZE(data->search.name, size); + + name_index = data->search.name_hash.name_index; + name = data->search.name; + + if (IS_END_KEY(data->result.key)) { + char *old_ptr = (char *)NILFS_XANODE_LAST_ENTRY(hdr); + + /* add entry */ + inserted = (struct nilfs_xanode_entry *)(old_ptr - entry_size); + err = nilfs_xafile_set_entry_for_key(inode, name_index, name, + value, size, + (char *)hdr, + data->result.key, + inserted); + if (unlikely(err)) + return err; + } else { + /* insert entry */ + err = nilfs_xafile_insert_entry_for_key(inode, name_index, name, + value, size, + (char *)hdr, + data->result.key); + if (unlikely(err)) + return err; + } + + return 0; +} + +/* + * nilfs_xafile_node_delete_entries - delete xattrs from xanode + * @inode: inode pointer + * @data: internal xattr search structure + * @entries_count: deleted xattrs count + */ +static +int nilfs_xafile_node_delete_entries(struct inode *inode, + struct nilfs_xattr_search *data, + int entries_count) +{ + int err; + union nilfs_xanode_header *hdr; + size_t node_size; + size_t key_size; + union nilfs_xattr_key *cur_key; + union nilfs_xattr_key *last_key; + union nilfs_xattr_key *last_del_key; + union nilfs_xattr_key *first_shift_key; + __u32 *end_key; + char *cur_entry; + char *last_entry; + __u32 removed_keys_size; + __u32 removed_entries_size; + +#ifdef CONFIG_NILFS2_FS_DEBUG + BUG_ON(IS_SEARCH_KEY_EMPTY(data)); + BUG_ON(IS_SEARCH_RESULT_EMPTY(data)); + BUG_ON(!IS_SHADOW_XANODE_DESC(&data->node)); +#endif + + if (!data->result.found) + return -EINVAL; + + cur_key = data->result.key; + cur_entry = (char *)data->result.entry; + + if (IS_END_KEY(cur_key)) { + printk(KERN_WARNING "NILFS warning: can't delete xattr\n"); + return -ENOENT; + } + + hdr = data->result.hdr; + node_size = NILFS_XANODE_SIZE; + end_key = NILFS_XANODE_END_KEY(hdr); + key_size = NILFS_XANODE_NOT_INDEX_KEY_SIZE(hdr); + removed_keys_size = entries_count * key_size; + + if (((char *)cur_key + removed_keys_size) > (char *)end_key) + return -EINVAL; + + err = nilfs_xafile_node_check_entries((char *)hdr, node_size); + if (unlikely(err < 0)) + return err; + + last_key = NILFS_XANODE_LAST_NOT_INDEX_KEY(hdr); + last_entry = (char *)NILFS_XANODE_LAST_ENTRY(hdr); + first_shift_key = NILFS_XANODE_KEY((char *)cur_key + removed_keys_size); + last_del_key = NILFS_XANODE_KEY((char *)first_shift_key - key_size); + + removed_entries_size = NILFS_XANODE_ENTRY_OFFSET(cur_key); + removed_entries_size -= NILFS_XANODE_ENTRY_OFFSET(last_del_key); + removed_entries_size += NILFS_XANODE_ENTRY_SIZE(last_del_key); + + /* shift entries */ + if (last_entry == (char *)cur_entry) { + size_t entry_size = NILFS_XANODE_ENTRY_SIZE(cur_key); + + memset((char *)data->result.entry, 0, entry_size); + } else { + char *new_last_entry; + __u32 shifted_size; + + new_last_entry = last_entry + removed_entries_size; + shifted_size = NILFS_XANODE_ENTRY_OFFSET(last_key); + shifted_size -= NILFS_XANODE_ENTRY_OFFSET(cur_key); + memmove(new_last_entry, last_entry, shifted_size); + } + + /* shift keys */ + if (last_key == cur_key) + memset(last_key, 0, key_size); + else { + char *old_key; + char *new_key; + + new_key = (char *)cur_key; + old_key = (char *)first_shift_key; + + if (IS_END_KEY(old_key)) + memset(new_key, 0, old_key - new_key); + else { + ptrdiff_t shifted_size; + + shifted_size = (char *)end_key - old_key; + shifted_size += NILFS_XANODE_END_KEY_SIZE; + memmove(new_key, old_key, shifted_size); + } + } + + /* correct shifted keys */ + for (; !IS_END_KEY(cur_key); cur_key = NEXT_KEY(cur_key, 1)) { + NILFS_XANODE_ADD_ENTRY_OFFSET(-1 * removed_entries_size, + cur_key); + } + + NILFS_XANODE_ADD_ENTRIES(-1 * entries_count, hdr); + return 0; +} + +/* + * nilfs_xafile_node_delete_entry - delete one xattr from xanode + * @inode: inode pointer + * @data: internal xattr search structure + */ +static inline +int nilfs_xafile_node_delete_entry(struct inode *inode, + struct nilfs_xattr_search *data) +{ + return nilfs_xafile_node_delete_entries(inode, data, 1); +} + +/* + * nilfs_xafile_set_entry - set xattr by shadow change and commit + * @inode: inode pointer + * @data: internal xattr search structure + * @value: xattr's value + * @size: xattr's value size + */ +static +int nilfs_xafile_set_entry(struct inode *inode, + struct nilfs_xattr_search *data, + const void *value, + size_t size) +{ + int err = 0; + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + struct inode *xafile = nilfs->ns_xafile; + bool can_insert = false; + + if (!value) + return -EINVAL; + if (!is_value_size_valid(xafile, BH_DATA(NODE_BH(&data->node)), + data->search.name, size)) + return -ENOSPC; + if (IS_SEARCH_RESULT_EMPTY(data)) { + printk(KERN_ERR "NILFS: inconsistent xattr search\n"); + return -EINVAL; + } + + err = nilfs_xafile_prepare_node_for_change(inode, data); + if (unlikely(err)) + goto set_entry_cleanup; + + if (data->result.found) { + err = nilfs_xafile_node_delete_entry(inode, data); + if (unlikely(err)) + goto set_entry_cleanup; + } + + can_insert = can_insert_entry((char *)data->result.hdr, + data->search.name, size); + + if (can_insert) { + err = nilfs_xafile_node_insert_entry(inode, data, value, size); + if (unlikely(err)) + goto set_entry_cleanup; + + nilfs_xafile_commit_changed_node(inode, data); + } else { + err = -ENOSPC; + goto set_entry_cleanup; + } + + return 0; + +set_entry_cleanup: + nilfs_xafile_abort_node_change(data); + return err; +} + +/* + * nilfs_xafile_delete_entry - delete xattr by shadow change and commit + * @inode: inode pointer + * @data: internal xattr search structure + */ +static +int nilfs_xafile_delete_entry(struct inode *inode, + struct nilfs_xattr_search *data) +{ + int err = 0; + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + struct inode *xafile = nilfs->ns_xafile; + + if (IS_SEARCH_RESULT_EMPTY(data)) { + printk(KERN_ERR "NILFS: inconsistent xattr search\n"); + return -EINVAL; + } + + err = nilfs_xafile_prepare_node_for_change(inode, data); + if (unlikely(err)) + goto delete_entry_cleanup; + + err = nilfs_xafile_node_delete_entry(inode, data); + if (unlikely(err)) + goto delete_entry_cleanup; + + if (NILFS_XANODE_ENTRIES(data->result.hdr) == 0) { + err = nilfs_xafile_prepare_node_deletion(xafile, + &data->node.req); + if (unlikely(err)) + goto delete_entry_cleanup; + + if (NILFS_I(inode)->i_xattr == NODE_ID(&data->node)) { + NILFS_I(inode)->i_xattr = NILFS_INVALID_XANODE; + nilfs_mark_inode_dirty(inode); + } + + nilfs_xafile_commit_node_deletion(xafile, &data->node.req); + } + + nilfs_xafile_commit_changed_node(inode, data); + + return 0; + +delete_entry_cleanup: + nilfs_xafile_abort_node_change(data); + + return err; +} + +/* * nilfs_xattr_list_entries - list inode's xattrs from one xanode * @dentry: dentry object * @bh: xanode's buffer @@ -1467,3 +1951,117 @@ failed_xafile_getxattr: nilfs_xattr_search_release(&data); return ret; } + +/* + * nilfs_xafile_setxattr - set/delete xattr + * @inode: inode pointer + * @name_index: name index of xattr + * @name: xattr's name + * @value: pointer on xattr's value + * @size: xattr's value size + * @flags: operation flags + */ +int nilfs_xafile_setxattr(struct inode *inode, + int name_index, const char *name, + const void *value, size_t size, int flags) +{ + int err = 0; + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + struct nilfs_xattr_search data; + + if (!nilfs_has_xafile(nilfs)) + return -EOPNOTSUPP; + + if (!name) + return -EINVAL; + if (strlen(name) > XATTR_NAME_MAX) + return -ERANGE; + + err = nilfs_xattr_search_init(inode, name_index, (char *)name, &data); + if (unlikely(err)) + return err; + + if (IS_NODE_DESC_INVALID(NODE_ID(&data.node))) { + err = nilfs_xafile_prepare_new_node(inode, &data); + if (unlikely(err)) { + nilfs_xafile_abort_new_node(inode, &data); + goto failed_xafile_setxattr; + } + + nilfs_xafile_commit_new_node(inode, &data); + + NILFS_I(inode)->i_xattr = NODE_ID(&data.node); + nilfs_mark_inode_dirty(inode); + } else + err = nilfs_xafile_find_node(inode, &data); + + if (unlikely(err && err != -ENODATA)) + goto failed_xafile_setxattr; + else if (!data.result.found) { + err = -ENODATA; + if (flags & XATTR_REPLACE) + goto cleanup; + err = 0; + if (!value) + goto cleanup; + } else { + err = -EEXIST; + if (flags & XATTR_CREATE) + goto cleanup; + } + + if (flags & XATTR_REPLACE) { + if (!value) { + err = nilfs_xafile_delete_entry(inode, &data); + if (unlikely(err)) + goto cleanup; + } else { + err = nilfs_xafile_set_entry(inode, &data, value, size); + if (unlikely(err)) + goto cleanup; + } + } else { + err = nilfs_xafile_set_entry(inode, &data, value, size); + if (unlikely(err)) + goto cleanup; + } + +cleanup: + brelse(NODE_BH(&data.node)); + +failed_xafile_setxattr: + nilfs_xattr_search_release(&data); + return err; +} + +/* + * nilfs_xafile_delete_inode - delete all xattrs during inode eviction + * @inode: inode pointer + */ +int nilfs_xafile_delete_inode(struct inode *inode) +{ + int err; + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + struct inode *xafile = nilfs->ns_xafile; + __u64 node = NILFS_I(inode)->i_xattr; + struct nilfs_palloc_req req = {0}; + + if (!nilfs_has_xafile(nilfs)) + return 0; + + if (node == NILFS_INVALID_XANODE) + return 0; + + req.pr_entry_nr = node; + + err = nilfs_xafile_prepare_node_deletion(xafile, &req); + if (unlikely(err)) { + printk(KERN_ERR "NILFS: can't delete xanode %llu\n", + node); + return err; + } + + nilfs_xafile_commit_node_deletion(xafile, &req); + + return 0; +} diff --git a/fs/nilfs2/xattr.c b/fs/nilfs2/xattr.c index 7be523a..2e2f5f5 100644 --- a/fs/nilfs2/xattr.c +++ b/fs/nilfs2/xattr.c @@ -62,3 +62,29 @@ ssize_t __nilfs_getxattr(struct inode *inode, return ret; } + +int __nilfs_setxattr(struct inode *inode, + int name_index, const char *name, + const void *value, size_t size, + int flags) +{ + int err; + + down_write(&NILFS_I(inode)->xattr_sem); + err = nilfs_xafile_setxattr(inode, name_index, name, + value, size, flags); + up_write(&NILFS_I(inode)->xattr_sem); + + return err; +} + +int nilfs_xattr_delete_inode(struct inode *inode) +{ + int err; + + down_write(&NILFS_I(inode)->xattr_sem); + err = nilfs_xafile_delete_inode(inode); + up_write(&NILFS_I(inode)->xattr_sem); + + return err; +} diff --git a/fs/nilfs2/xattr.h b/fs/nilfs2/xattr.h index 0736086..104a60d 100644 --- a/fs/nilfs2/xattr.h +++ b/fs/nilfs2/xattr.h @@ -39,6 +39,21 @@ static inline ssize_t nilfs_getxattr(struct dentry *dentry, return __nilfs_getxattr(dentry->d_inode, name_index, name, value, size); } +int __nilfs_setxattr(struct inode *inode, + int name_index, const char *name, + const void *value, size_t size, int flags); + +static inline int nilfs_setxattr(struct dentry *dentry, + int name_index, const char *name, + const void *value, size_t size, + int flags) +{ + return __nilfs_setxattr(dentry->d_inode, + name_index, name, + value, size, flags); +} + ssize_t nilfs_listxattr(struct dentry *dentry, char *buffer, size_t size); +int nilfs_xattr_delete_inode(struct inode *inode); #endif /* _NILFS_XATTR_H */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html