Re: [PATCH 01/35] Btrfs xattr code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed,  7 Jan 2009 22:56:51 -0500 Chris Mason <chris.mason@xxxxxxxxxx> wrote:

> Xattrs in btrfs are stored in the btree close to the inode item.  They use an
> indexing scheme very similar to directories.
> 
> The max size of a btrfs xattr is limited by the size of a btree item,
> or about 4k.  A future version will support larger items.
> 
>
> ...
>
> +ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
> +				void *buffer, size_t size)
> +{
> +	struct btrfs_dir_item *di;
> +	struct btrfs_root *root = BTRFS_I(inode)->root;
> +	struct btrfs_path *path;
> +	struct extent_buffer *leaf;
> +	int ret = 0;
> +	unsigned long data_ptr;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	/* lookup the xattr by name */
> +	di = btrfs_lookup_xattr(NULL, root, path, inode->i_ino, name,
> +				strlen(name), 0);
> +	if (!di || IS_ERR(di)) {

This seems quite bad.  An error return from btrfs_lookup_xattr() can
arise from (I assume) OOM, corrupted filesystem, I/O error, etc.

Yet here we treat such errors as being the same as "not found".  This
seems grossly misleading to userspace and possible a security problem?

(I'm kind of guessing here, and might be wrong and wasting everyone's
time, because the semantics of the btrfs_lookup_xattr() and
btrfs_getxattr() return values aren't documented).

> +		ret = -ENODATA;
> +		goto out;
> +	}
> +
> +	leaf = path->nodes[0];
> +	/* if size is 0, that means we want the size of the attr */
> +	if (!size) {
> +		ret = btrfs_dir_data_len(leaf, di);
> +		goto out;
> +	}
> +
> +	/* now get the data out of our dir_item */
> +	if (btrfs_dir_data_len(leaf, di) > size) {

OK, where did we hide the btrfs_dir_data_len() definition?

> +		ret = -ERANGE;
> +		goto out;
> +	}
> +	data_ptr = (unsigned long)((char *)(di + 1) +
> +				   btrfs_dir_name_len(leaf, di));
> +	read_extent_buffer(leaf, buffer, data_ptr,
> +			   btrfs_dir_data_len(leaf, di));

Please document read_extent_buffer().

Please rename read_extent_buffer() to something more appropriate to a
kernel-wide symbol.

<spies map_private_extent_buffer as well>

Please review all kernel-wide identifiers in btrfs for appropriateness.

I'm scratching my head wondering about this `data_ptr' thing.  Is it a
disk offset?  Is it really a pointer to kernel memory?  According to
this code it is indeed a kernel pointer, but it then gets stuffed into
an unsigned long (wtf?) and then passed to the mysterious
read_extent_buffer().

<reviewer throws in the towel on this part of the code>

> +	ret = btrfs_dir_data_len(leaf, di);
> +
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +int __btrfs_setxattr(struct inode *inode, const char *name,
> +			    const void *value, size_t size, int flags)
> +{
> +	struct btrfs_dir_item *di;
> +	struct btrfs_root *root = BTRFS_I(inode)->root;
> +	struct btrfs_trans_handle *trans;
> +	struct btrfs_path *path;
> +	int ret = 0, mod = 0;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	trans = btrfs_start_transaction(root, 1);
> +	btrfs_set_trans_block_group(trans, inode);
> +
> +	/* first lets see if we already have this xattr */

let's :)

> +	di = btrfs_lookup_xattr(trans, root, path, inode->i_ino, name,
> +				strlen(name), -1);

<wonders what the -1 does>

<goes to the btrfs_lookup_xattr() definition site>

<towel goes flying again>

> +	if (IS_ERR(di)) {
> +		ret = PTR_ERR(di);
> +		goto out;
> +	}
> +
> +	/* ok we already have this xattr, lets remove it */
> +	if (di) {
> +		/* if we want create only exit */
> +		if (flags & XATTR_CREATE) {
> +			ret = -EEXIST;
> +			goto out;
> +		}
> +
> +		ret = btrfs_delete_one_dir_name(trans, root, path, di);
> +		if (ret)
> +			goto out;
> +		btrfs_release_path(root, path);
> +
> +		/* if we don't have a value then we are removing the xattr */
> +		if (!value) {
> +			mod = 1;
> +			goto out;
> +		}
> +	} else {
> +		btrfs_release_path(root, path);
> +
> +		if (flags & XATTR_REPLACE) {
> +			/* we couldn't find the attr to replace */
> +			ret = -ENODATA;
> +			goto out;
> +		}
> +	}
> +
> +	/* ok we have to create a completely new xattr */
> +	ret = btrfs_insert_xattr_item(trans, root, name, strlen(name),
> +				      value, size, inode->i_ino);
> +	if (ret)
> +		goto out;
> +	mod = 1;
> +
> +out:
> +	if (mod) {
> +		inode->i_ctime = CURRENT_TIME;
> +		ret = btrfs_update_inode(trans, root, inode);
> +	}
> +
> +	btrfs_end_transaction(trans, root);
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
> +{
> +	struct btrfs_key key, found_key;
> +	struct inode *inode = dentry->d_inode;
> +	struct btrfs_root *root = BTRFS_I(inode)->root;
> +	struct btrfs_path *path;
> +	struct btrfs_item *item;
> +	struct extent_buffer *leaf;
> +	struct btrfs_dir_item *di;
> +	int ret = 0, slot, advance;
> +	size_t total_size = 0, size_left = size;
> +	unsigned long name_ptr;
> +	size_t name_len;
> +	u32 nritems;
> +
> +	/*
> +	 * ok we want all objects associated with this id.
> +	 * NOTE: we set key.offset = 0; because we want to start with the
> +	 * first xattr that we find and walk forward
> +	 */
> +	key.objectid = inode->i_ino;
> +	btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
> +	key.offset = 0;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +	path->reada = 2;

<gets interested in btrfs_path.reada>

<greps for a while>

It's snowing towels in here!

	path->reada = 2;

gack!

<greps some more>

	int direction = path->reada;

	...

		if (direction < 0) {
			if (nr == 0)
				break;
			nr--;
		} else if (direction > 0) {
			nr++;
			if (nr >= nritems)
				break;
		}

<delicately moves on...>

> +	/* search for our xattrs */
> +	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
> +	if (ret < 0)
> +		goto err;
> +	ret = 0;

this statement isn't needed.

> +	advance = 0;
> +	while (1) {
> +		leaf = path->nodes[0];
> +		nritems = btrfs_header_nritems(leaf);
> +		slot = path->slots[0];
> +
> +		/* this is where we start walking through the path */
> +		if (advance || slot >= nritems) {
> +			/*
> +			 * if we've reached the last slot in this leaf we need
> +			 * to go to the next leaf and reset everything
> +			 */
> +			if (slot >= nritems-1) {
> +				ret = btrfs_next_leaf(root, path);
> +				if (ret)
> +					break;
> +				leaf = path->nodes[0];
> +				nritems = btrfs_header_nritems(leaf);
> +				slot = path->slots[0];
> +			} else {
> +				/*
> +				 * just walking through the slots on this leaf
> +				 */
> +				slot++;
> +				path->slots[0]++;
> +			}
> +		}
> +		advance = 1;
> +
> +		item = btrfs_item_nr(leaf, slot);
> +		btrfs_item_key_to_cpu(leaf, &found_key, slot);
> +
> +		/* check to make sure this item is what we want */
> +		if (found_key.objectid != key.objectid)
> +			break;
> +		if (btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY)
> +			break;
> +
> +		di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
> +
> +		name_len = btrfs_dir_name_len(leaf, di);
> +		total_size += name_len + 1;
> +
> +		/* we are just looking for how big our buffer needs to be */
> +		if (!size)
> +			continue;
> +
> +		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';
> +
> +		size_left -= name_len + 1;
> +		buffer += name_len + 1;
> +	}
> +	ret = total_size;
> +
> +err:
> +	btrfs_free_path(path);
> +
> +	return ret;
> +}
>
> ...
>

--
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

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux