diff options
| -rw-r--r-- | fs/btrfs/xattr.c | 65 | 
1 files changed, 41 insertions, 24 deletions
| diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c index f2a20d52b9db..145d2b89e62d 100644 --- a/fs/btrfs/xattr.c +++ b/fs/btrfs/xattr.c @@ -260,16 +260,12 @@ out:  ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)  { -	struct btrfs_key key, found_key; +	struct btrfs_key key;  	struct inode *inode = d_inode(dentry);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct btrfs_path *path; -	struct extent_buffer *leaf; -	struct btrfs_dir_item *di; -	int ret = 0, slot; +	int ret = 0;  	size_t total_size = 0, size_left = size; -	unsigned long name_ptr; -	size_t name_len;  	/*  	 * ok we want all objects associated with this id. @@ -291,6 +287,13 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)  		goto err;  	while (1) { +		struct extent_buffer *leaf; +		int slot; +		struct btrfs_dir_item *di; +		struct btrfs_key found_key; +		u32 item_size; +		u32 cur; +  		leaf = path->nodes[0];  		slot = path->slots[0]; @@ -316,31 +319,45 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)  		if (found_key.type > BTRFS_XATTR_ITEM_KEY)  			break;  		if (found_key.type < BTRFS_XATTR_ITEM_KEY) -			goto next; +			goto next_item;  		di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); -		if (verify_dir_item(root, leaf, di)) -			goto next; - -		name_len = btrfs_dir_name_len(leaf, di); -		total_size += name_len + 1; +		item_size = btrfs_item_size_nr(leaf, slot); +		cur = 0; +		while (cur < item_size) { +			u16 name_len = btrfs_dir_name_len(leaf, di); +			u16 data_len = btrfs_dir_data_len(leaf, di); +			u32 this_len = sizeof(*di) + name_len + data_len; +			unsigned long name_ptr = (unsigned long)(di + 1); + +			if (verify_dir_item(root, leaf, di)) { +				ret = -EIO; +				goto err; +			} -		/* we are just looking for how big our buffer needs to be */ -		if (!size) -			goto next; +			total_size += name_len + 1; +			/* +			 * We are just looking for how big our buffer needs to +			 * be. +			 */ +			if (!size) +				goto next; -		if (!buffer || (name_len + 1) > size_left) { -			ret = -ERANGE; -			goto err; -		} +			if (!buffer || (name_len + 1) > size_left) { +				ret = -ERANGE; +				goto err; +			} -		name_ptr = (unsigned long)(di + 1); -		read_extent_buffer(leaf, buffer, name_ptr, name_len); -		buffer[name_len] = '\0'; +			read_extent_buffer(leaf, buffer, name_ptr, name_len); +			buffer[name_len] = '\0'; -		size_left -= name_len + 1; -		buffer += name_len + 1; +			size_left -= name_len + 1; +			buffer += name_len + 1;  next: +			cur += this_len; +			di = (struct btrfs_dir_item *)((char *)di + this_len); +		} +next_item:  		path->slots[0]++;  	}  	ret = total_size; |