From: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> Subject: [RFC][PATCH 07/15] nilfs2: implement functionality of xanode's checking This patch adds functionality of xanode checking. Signed-off-by: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> CC: Ryusuke Konishi <konishi.ryusuke@xxxxxxxxxxxxx> --- fs/nilfs2/xafile.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) diff --git a/fs/nilfs2/xafile.c b/fs/nilfs2/xafile.c index e3c27d1..1c95fb2 100644 --- a/fs/nilfs2/xafile.c +++ b/fs/nilfs2/xafile.c @@ -21,6 +21,9 @@ * Written by Vyacheslav Dubeyko <slava@xxxxxxxxxxx> */ +#include <linux/crc32.h> +#include <linux/jhash.h> + #include "nilfs.h" #include "alloc.h" #include "mdt.h" @@ -452,6 +455,81 @@ int nilfs_xafile_read(struct super_block *sb, struct nilfs_inode *raw_inode, } /* + * calc_name_hash - calculate complex name hash + * @name_index: name index + * @name: xattr name + * @name_hash: calculated complex name hash [out] + */ +static +int calc_name_hash(int name_index, const char *name, + struct nilfs_xattr_name_hash *name_hash) +{ + size_t name_len; + +#ifdef CONFIG_NILFS2_FS_DEBUG + BUG_ON(name_index < NILFS_USER_XATTR_ID || + name_index > NILFS_XATTR_MAX_ID); + BUG_ON(!name || !name_hash); +#endif + + name_len = strlen(name); + + if (name_len > XATTR_NAME_MAX) { + printk(KERN_ERR "NILFS: xattr name too long\n"); + return -EINVAL; + } + + if (name_len == 0) { + printk(KERN_WARNING "NILFS warning: empty name hash\n"); + memset(name_hash, 0, sizeof(*name_hash)); + return 0; + } + + name_hash->name_len = (u8)name_len; + name_hash->name_index = (u8)name_index; + + name_hash->first_symbol = *(const u8 *)(name); + name_hash->last_symbol = *(const u8 *)(name + name_len - 1); + + name_hash->hash = cpu_to_le32(jhash(name, name_len, 0)); + + return 0; +} + +/* + * calc_value_hash - calculate complex value hash + * @value: xattr value + * @len: value size + * @val_hash: calculated complex value hash [out] + */ +/*static +int calc_value_hash(const char *value, __u16 len, + struct nilfs_xattr_value_hash *val_hash) +{ +#ifdef CONFIG_NILFS2_FS_DEBUG + BUG_ON(!value || !val_hash); +#endif + + if (len > NILFS_XATTR_VALUE_LEN_MAX) { + printk(KERN_ERR "NILFS: xattr value too big\n"); + return -EINVAL; + } + + if (len == 0) { + printk(KERN_WARNING "NILFS warning: empty value hash\n"); + memset(val_hash, 0, sizeof(*val_hash)); + return 0; + } + + val_hash->value_len = cpu_to_le16(len); + val_hash->first_byte = *(const u8 *)(value); + val_hash->last_byte = *(const u8 *)(value + len - 1); + val_hash->hash = cpu_to_le32(jhash(value, len, 0)); + + return 0; +}*/ + +/* * nilfs_xattr_search_init - initialize xattr search * @inode: inode pointer * @name_index: xattr name index @@ -504,6 +582,192 @@ void nilfs_xattr_search_release(struct nilfs_xattr_search *xs) } /* + * nilfs_xafile_node_crc32 - calculate xanode checksum + * @inode: inode pointer + * @node_ptr: pointer on xanode's begin + * @node_size: size of xanode + * @node: xanode number (index in xafile) + */ +static +__le32 nilfs_xafile_node_crc32(struct inode *inode, + char *node_ptr, size_t node_size, + __u64 node) +{ + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + union nilfs_xanode_header *hdr = NILFS_XANODE_HDR(node_ptr); + __u32 csum; + __le32 save_csum; + __le64 blk = cpu_to_le64(node); + + save_csum = hdr->tree_hdr.checksum; + hdr->tree_hdr.checksum = 0; + + csum = crc32_le(nilfs->ns_crc_seed, (__u8 *)&blk, sizeof(blk)); + csum = crc32_le(csum, (__u8 *)node_ptr, node_size); + + hdr->tree_hdr.checksum = save_csum; + + return cpu_to_le32(csum); +} + +/* + * is_xanode_checksum_valid - check xanode checksum + * @inode: inode pointer + * @node_ptr: pointer on xanode's begin + * @node_size: size of xanode + * @node: xanode number (index in xafile) + */ +static inline +bool is_xanode_checksum_valid(struct inode *inode, char *node_ptr, + size_t node_size, __u64 node) +{ + union nilfs_xanode_header *hdr = NILFS_XANODE_HDR(node_ptr); + + if (!(NILFS_XANODE_FLAGS(hdr) & NILFS_XANODE_CRC32_FLAG)) + return true; + + if (hdr->tree_hdr.checksum != + nilfs_xafile_node_crc32(inode, node_ptr, node_size, node)) + return false; + + return true; +} + +/* + * nilfs_xafile_node_check_entries - check xanode's xattrs + * @node_ptr: pointer on xanode's begin + * @node_size: size of xanode + */ +static +int nilfs_xafile_node_check_entries(char *node_ptr, size_t node_size) +{ + union nilfs_xanode_header *hdr; + union nilfs_xattr_key *key = NULL; + __u32 *end_key; + char *entry; + __u16 entries; + int entry_index; + __u32 entry_size; + + hdr = NILFS_XANODE_HDR(node_ptr); + entries = NILFS_XANODE_ENTRIES(hdr); + + if (entries == 0) + return 0; + + end_key = NILFS_XANODE_END_KEY(hdr); + key = NILFS_XANODE_LAST_NOT_INDEX_KEY(hdr); + entry = (char *)NILFS_XANODE_LAST_ENTRY(hdr); + entry_index = entries - 1; + + for (; entry_index >= 0; entry_index--, key = PREV_KEY(key, 1)) { + if (IS_END_KEY(key)) + return -EIO; + + if ((char *)NILFS_XANODE_ENTRY(key, node_ptr) != entry) + return -EIO; + + entry_size = le16_to_cpu(key->leaf_key.entry_size); + if (((char *)entry + entry_size) > (node_ptr + node_size)) + return -EIO; + + if ((char *)entry < (char *)(end_key + 1)) + return -EIO; + + entry = (char *)entry + entry_size; + } + + if (!IS_END_KEY(key)) + return -EIO; + + return 0; +} + +/* + * __nilfs_xafile_check_node - check xanode's xattrs and checksum + * @inode: inode pointer + * @node_ptr: pointer on xanode's begin + * @node_size: size of xanode + * @node: xanode number (index in xafile) + */ +static +int __nilfs_xafile_check_node(struct inode *inode, char *node_ptr, + size_t node_size, __u64 node) +{ + union nilfs_xanode_header *hdr = NILFS_XANODE_HDR(node_ptr); + bool is_crc32_valid; + + if (hdr->base_hdr.magic != cpu_to_le16(NILFS_XANODE_MAGIC)) + return -EIO; + + if (node_size != NILFS_XANODE_SIZE) + return -EIO; + + is_crc32_valid = is_xanode_checksum_valid(inode, node_ptr, + node_size, node); + if (!is_crc32_valid) + return -EIO; + + return nilfs_xafile_node_check_entries(node_ptr, node_size); +} + +/* + * nilfs_xafile_check_node - check xanode and mark buffer as checked + * @inode: inode pointer + * @node: xanode number (index in xafile) + * @bh: xanode's buffer + */ +static inline +int nilfs_xafile_check_node(struct inode *inode, __u64 node, + struct buffer_head *bh) +{ + int err; + + if (buffer_nilfs_checked(bh)) + return 0; + + err = __nilfs_xafile_check_node(inode, BH_DATA(bh), + BH_SIZE(bh), node); + if (likely(!err)) + set_buffer_nilfs_checked(bh); + + return err; +} + +/* + * nilfs_xafile_get_checked_node - get xanode and check it + * @inode: inode pointer + * @node: xanode number (index in xafile) + * @bh: xanode's buffer [out] + */ +static +int nilfs_xafile_get_checked_node(struct inode *inode, __u64 node, + struct buffer_head **bh) +{ + int err; + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + struct inode *xafile = nilfs->ns_xafile; + + err = nilfs_xafile_get_node(xafile, node, bh); + if (unlikely(err)) { + printk(KERN_ERR "NILFS: can't get xafile node %llu\n", node); + return -ENODATA; + } + + lock_buffer(*bh); + err = nilfs_xafile_check_node(inode, node, *bh); + unlock_buffer(*bh); + + if (unlikely(err)) { + printk(KERN_ERR "NILFS: corrupted xafile node %llu\n", node); + brelse(*bh); + return err; + } + + return 0; +} + +/* * nilfs_xafile_check_entry - check xattr entry * @key: xattr's key * @entry: xattr's entry @@ -721,3 +985,49 @@ int nilfs_xafile_find_entry(struct inode *inode, return -ENODATA; } + +/* + * nilfs_xafile_find_node - search xanode that contains xattr + * @inode: inode pointer + * @data: internal xattr search structure + */ +static +int nilfs_xafile_find_node(struct inode *inode, + struct nilfs_xattr_search *data) +{ + int err = 0; + __u64 node; + +#ifdef CONFIG_NILFS2_FS_DEBUG + BUG_ON(!IS_NULL_XANODE_DESC(&data->node) && + !IS_RO_XANODE_DESC(&data->node)); +#endif + + if (IS_SEARCH_KEY_EMPTY(data)) + return -EINVAL; + + if (IS_NODE_DESC_INVALID(NODE_ID(&data->node))) + return -ENODATA; + + if (IS_RO_XANODE_DESC(&data->node)) { + if (IS_SEARCH_RESULT_EMPTY(data)) + goto start_entry_search; + else + BUG(); + } + + node = NODE_ID(&data->node); + + err = nilfs_xafile_get_checked_node(inode, node, + &NODE_BH(&data->node)); + if (unlikely(err)) + goto failed_find_node; + + data->node.flags = NILFS_RO_XANODE; + +start_entry_search: + err = nilfs_xafile_find_entry(inode, data); + +failed_find_node: + return err; +} -- 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