Example #1
0
static int do_setxattr(struct btrfs_trans_handle *trans,
		       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_path *path;
	size_t name_len = strlen(name);
	int ret = 0;

	if (name_len + size > BTRFS_MAX_XATTR_SIZE(root))
		return -ENOSPC;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	/* first lets see if we already have this xattr */
	di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name,
				strlen(name), -1);
	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);
		BUG_ON(ret);
		btrfs_release_path(path);

		/* if we don't have a value then we are removing the xattr */
		if (!value)
			goto out;
	} else {
		btrfs_release_path(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, path, btrfs_ino(inode),
				      name, name_len, value, size);
	BUG_ON(ret);
out:
	btrfs_free_path(path);
	return ret;
}
Example #2
0
static int do_setxattr(struct btrfs_trans_handle *trans,
		       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_path *path;
	size_t name_len = strlen(name);
	int ret = 0;

	if (name_len + size > BTRFS_MAX_XATTR_SIZE(root))
		return -ENOSPC;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	if (flags & XATTR_REPLACE) {
		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name,
					name_len, -1);
		if (IS_ERR(di)) {
			ret = PTR_ERR(di);
			goto out;
		} else if (!di) {
			ret = -ENODATA;
			goto out;
		}
		ret = btrfs_delete_one_dir_name(trans, root, path, di);
		if (ret)
			goto out;
		btrfs_release_path(path);

		/*
		 * remove the attribute
		 */
		if (!value)
			goto out;
	}

again:
	ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode),
				      name, name_len, value, size);
	if (ret == -EEXIST) {
		if (flags & XATTR_CREATE)
			goto out;
		/*
		 * We can't use the path we already have since we won't have the
		 * proper locking for a delete, so release the path and
		 * re-lookup to delete the thing.
		 */
		btrfs_release_path(path);
		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode),
					name, name_len, -1);
		if (IS_ERR(di)) {
			ret = PTR_ERR(di);
			goto out;
		} else if (!di) {
			/* Shouldn't happen but just in case... */
			btrfs_release_path(path);
			goto again;
		}

		ret = btrfs_delete_one_dir_name(trans, root, path, di);
		if (ret)
			goto out;

		/*
		 * We have a value to set, so go back and try to insert it now.
		 */
		if (value) {
			btrfs_release_path(path);
			goto again;
		}
	}
out:
	btrfs_free_path(path);
	return ret;
}
Example #3
0
File: xattr.c Project: 020gzh/linux
static int do_setxattr(struct btrfs_trans_handle *trans,
		       struct inode *inode, const char *name,
		       const void *value, size_t size, int flags)
{
	struct btrfs_dir_item *di = NULL;
	struct btrfs_root *root = BTRFS_I(inode)->root;
	struct btrfs_path *path;
	size_t name_len = strlen(name);
	int ret = 0;

	if (name_len + size > BTRFS_MAX_XATTR_SIZE(root))
		return -ENOSPC;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;
	path->skip_release_on_error = 1;

	if (!value) {
		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode),
					name, name_len, -1);
		if (!di && (flags & XATTR_REPLACE))
			ret = -ENODATA;
		else if (IS_ERR(di))
			ret = PTR_ERR(di);
		else if (di)
			ret = btrfs_delete_one_dir_name(trans, root, path, di);
		goto out;
	}

	/*
	 * For a replace we can't just do the insert blindly.
	 * Do a lookup first (read-only btrfs_search_slot), and return if xattr
	 * doesn't exist. If it exists, fall down below to the insert/replace
	 * path - we can't race with a concurrent xattr delete, because the VFS
	 * locks the inode's i_mutex before calling setxattr or removexattr.
	 */
	if (flags & XATTR_REPLACE) {
		ASSERT(inode_is_locked(inode));
		di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode),
					name, name_len, 0);
		if (!di)
			ret = -ENODATA;
		else if (IS_ERR(di))
			ret = PTR_ERR(di);
		if (ret)
			goto out;
		btrfs_release_path(path);
		di = NULL;
	}

	ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode),
				      name, name_len, value, size);
	if (ret == -EOVERFLOW) {
		/*
		 * We have an existing item in a leaf, split_leaf couldn't
		 * expand it. That item might have or not a dir_item that
		 * matches our target xattr, so lets check.
		 */
		ret = 0;
		btrfs_assert_tree_locked(path->nodes[0]);
		di = btrfs_match_dir_item_name(root, path, name, name_len);
		if (!di && !(flags & XATTR_REPLACE)) {
			ret = -ENOSPC;
			goto out;
		}
	} else if (ret == -EEXIST) {
		ret = 0;
		di = btrfs_match_dir_item_name(root, path, name, name_len);
		ASSERT(di); /* logic error */
	} else if (ret) {
		goto out;
	}

	if (di && (flags & XATTR_CREATE)) {
		ret = -EEXIST;
		goto out;
	}

	if (di) {
		/*
		 * We're doing a replace, and it must be atomic, that is, at
		 * any point in time we have either the old or the new xattr
		 * value in the tree. We don't want readers (getxattr and
		 * listxattrs) to miss a value, this is specially important
		 * for ACLs.
		 */
		const int slot = path->slots[0];
		struct extent_buffer *leaf = path->nodes[0];
		const u16 old_data_len = btrfs_dir_data_len(leaf, di);
		const u32 item_size = btrfs_item_size_nr(leaf, slot);
		const u32 data_size = sizeof(*di) + name_len + size;
		struct btrfs_item *item;
		unsigned long data_ptr;
		char *ptr;

		if (size > old_data_len) {
			if (btrfs_leaf_free_space(root, leaf) <
			    (size - old_data_len)) {
				ret = -ENOSPC;
				goto out;
			}
		}

		if (old_data_len + name_len + sizeof(*di) == item_size) {
			/* No other xattrs packed in the same leaf item. */
			if (size > old_data_len)
				btrfs_extend_item(root, path,
						  size - old_data_len);
			else if (size < old_data_len)
				btrfs_truncate_item(root, path, data_size, 1);
		} else {
			/* There are other xattrs packed in the same item. */
			ret = btrfs_delete_one_dir_name(trans, root, path, di);
			if (ret)
				goto out;
			btrfs_extend_item(root, path, data_size);
		}

		item = btrfs_item_nr(slot);
		ptr = btrfs_item_ptr(leaf, slot, char);
		ptr += btrfs_item_size(leaf, item) - data_size;
		di = (struct btrfs_dir_item *)ptr;
		btrfs_set_dir_data_len(leaf, di, size);
		data_ptr = ((unsigned long)(di + 1)) + name_len;
		write_extent_buffer(leaf, value, data_ptr, size);
		btrfs_mark_buffer_dirty(leaf);
	} else {
		/*
		 * Insert, and we had space for the xattr, so path->slots[0] is
		 * where our xattr dir_item is and btrfs_insert_xattr_item()
		 * filled it.
		 */
	}
Example #4
0
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_join_transaction(root, 1);
	btrfs_set_trans_block_group(trans, inode);

	/* first lets see if we already have this xattr */
	di = btrfs_lookup_xattr(trans, root, path, inode->i_ino, name,
				strlen(name), -1);
	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;
}
Example #5
0
/*
 * Unlink an inode, which will remove its backref and corresponding dir_index/
 * dir_item if any of them exists.
 *
 * If an inode's nlink is reduced to 0 and 'add_orphan' is true, it will be
 * added to orphan inode and waiting to be deleted by next kernel mount.
 */
int btrfs_unlink(struct btrfs_trans_handle *trans, struct btrfs_root *root,
		 u64 ino, u64 parent_ino, u64 index, const char *name,
		 int namelen, int add_orphan)
{
	struct btrfs_path *path;
	struct btrfs_key key;
	struct btrfs_inode_item *inode_item;
	struct btrfs_inode_ref *inode_ref;
	struct btrfs_dir_item *dir_item;
	u64 inode_size;
	u32 nlinks;
	int del_inode_ref = 0;
	int del_dir_item = 0;
	int del_dir_index = 0;
	int ret = 0;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	/* check the ref and backref exists */
	inode_ref = btrfs_lookup_inode_ref(trans, root, path, name, namelen,
					   ino, parent_ino, index, 0);
	if (IS_ERR(inode_ref)) {
		ret = PTR_ERR(inode_ref);
		goto out;
	}
	if (inode_ref)
		del_inode_ref = 1;
	btrfs_release_path(path);

	dir_item = btrfs_lookup_dir_item(NULL, root, path, parent_ino,
					 name, namelen, 0);
	if (IS_ERR(dir_item)) {
		ret = PTR_ERR(dir_item);
		goto out;
	}
	if (dir_item)
		del_dir_item = 1;
	btrfs_release_path(path);

	dir_item = btrfs_lookup_dir_index(NULL, root, path, parent_ino,
					  name, namelen, index, 0);
	/*
	 * Since lookup_dir_index() will return -ENOENT when not found,
	 * we need to do extra check.
	 */
	if (IS_ERR(dir_item) && PTR_ERR(dir_item) == -ENOENT)
		dir_item = NULL;
	if (IS_ERR(dir_item)) {
		ret = PTR_ERR(dir_item);
		goto out;
	}
	if (dir_item)
		del_dir_index = 1;
	btrfs_release_path(path);

	if (!del_inode_ref && !del_dir_item && !del_dir_index) {
		/* All not found, shouldn't happen */
		ret = -ENOENT;
		goto out;
	}

	if (del_inode_ref) {
		/* Only decrease nlink when deleting inode_ref */
		key.objectid = ino;
		key.type = BTRFS_INODE_ITEM_KEY;
		key.offset = 0;
		ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
		if (ret) {
			if (ret > 0)
				ret = -ENOENT;
			goto out;
		}
		inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
					    struct btrfs_inode_item);
		nlinks = btrfs_inode_nlink(path->nodes[0], inode_item);
		if (nlinks > 0)
			nlinks--;
		btrfs_set_inode_nlink(path->nodes[0], inode_item, nlinks);
		btrfs_mark_buffer_dirty(path->nodes[0]);
		btrfs_release_path(path);

		/* For nlinks == 0, add it to orphan list if needed */
		if (nlinks == 0 && add_orphan) {
			ret = btrfs_add_orphan_item(trans, root, path, ino);
			if (ret < 0)
				goto out;
			btrfs_mark_buffer_dirty(path->nodes[0]);
			btrfs_release_path(path);
		}

		ret = btrfs_del_inode_ref(trans, root, name, namelen, ino,
					  parent_ino, &index);
		if (ret < 0)
			goto out;
	}

	if (del_dir_index) {
		dir_item = btrfs_lookup_dir_index(trans, root, path,
						  parent_ino, name, namelen,
						  index, -1);
		if (IS_ERR(dir_item)) {
			ret = PTR_ERR(dir_item);
			goto out;
		}
		if (!dir_item) {
			ret = -ENOENT;
			goto out;
		}
		ret = btrfs_delete_one_dir_name(trans, root, path, dir_item);
		if (ret)
			goto out;
		btrfs_release_path(path);

		/* Update inode size of the parent inode */
		key.objectid = parent_ino;
		key.type = BTRFS_INODE_ITEM_KEY;
		key.offset = 0;
		ret = btrfs_search_slot(trans, root, &key, path, 1, 1);
		if (ret)
			goto out;
		inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
					    struct btrfs_inode_item);
		inode_size = btrfs_inode_size(path->nodes[0], inode_item);
		if (inode_size >= namelen)
			inode_size -= namelen;
		btrfs_set_inode_size(path->nodes[0], inode_item, inode_size);
		btrfs_mark_buffer_dirty(path->nodes[0]);
		btrfs_release_path(path);
	}

	if (del_dir_item) {
		dir_item = btrfs_lookup_dir_item(trans, root, path, parent_ino,
						 name, namelen, -1);
		if (IS_ERR(dir_item)) {
			ret = PTR_ERR(dir_item);
			goto out;
		}
		if (!dir_item) {
			ret = -ENOENT;
			goto out;
		}
		ret = btrfs_delete_one_dir_name(trans, root, path, dir_item);
		if (ret < 0)
			goto out;
		btrfs_release_path(path);

		/* Update inode size of the parent inode */
		key.objectid = parent_ino;
		key.type = BTRFS_INODE_ITEM_KEY;
		key.offset = 0;
		ret = btrfs_search_slot(trans, root, &key, path, 1, 1);
		if (ret)
			goto out;
		inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
					    struct btrfs_inode_item);
		inode_size = btrfs_inode_size(path->nodes[0], inode_item);
		if (inode_size >= namelen)
			inode_size -= namelen;
		btrfs_set_inode_size(path->nodes[0], inode_item, inode_size);
		btrfs_mark_buffer_dirty(path->nodes[0]);
		btrfs_release_path(path);
	}

out:
	btrfs_free_path(path);
	return ret;
}
Example #6
0
static int do_setxattr(struct btrfs_trans_handle *trans,
		       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_path *path;
	size_t name_len = strlen(name);
	int ret = 0;

	if (name_len + size > BTRFS_MAX_XATTR_SIZE(root))
		return -ENOSPC;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	if (flags & XATTR_REPLACE) {
		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name,
					name_len, -1);
		if (IS_ERR(di)) {
			ret = PTR_ERR(di);
			goto out;
		} else if (!di) {
			ret = -ENODATA;
			goto out;
		}
		ret = btrfs_delete_one_dir_name(trans, root, path, di);
		if (ret)
			goto out;
		btrfs_release_path(path);

		/*
                         
   */
		if (!value)
			goto out;
	}

again:
	ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode),
				      name, name_len, value, size);
	/*
                                                                     
                                                                        
                                                                       
                                                                        
                                                                       
                                           
  */
	if (ret == -EOVERFLOW)
		ret = -EEXIST;

	if (ret == -EEXIST) {
		if (flags & XATTR_CREATE)
			goto out;
		/*
                                                                  
                                                         
                                   
   */
		btrfs_release_path(path);
		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode),
					name, name_len, -1);
		if (IS_ERR(di)) {
			ret = PTR_ERR(di);
			goto out;
		} else if (!di) {
			/*                                      */
			btrfs_release_path(path);
			goto again;
		}

		ret = btrfs_delete_one_dir_name(trans, root, path, di);
		if (ret)
			goto out;

		/*
                                                                 
   */
		if (value) {
			btrfs_release_path(path);
			goto again;
		}
	}
out:
	btrfs_free_path(path);
	return ret;
}
Example #7
0
static int do_setxattr(struct btrfs_trans_handle *trans,
                       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_path *path;
    size_t name_len = strlen(name);
    int ret = 0;

    if (name_len + size > BTRFS_MAX_XATTR_SIZE(root))
        return -ENOSPC;

    path = btrfs_alloc_path();
    if (!path)
        return -ENOMEM;

    if (flags & XATTR_REPLACE) {
        di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name,
                                name_len, -1);
        if (IS_ERR(di)) {
            ret = PTR_ERR(di);
            goto out;
        } else if (!di) {
            ret = -ENODATA;
            goto out;
        }
        ret = btrfs_delete_one_dir_name(trans, root, path, di);
        if (ret)
            goto out;
        btrfs_release_path(path);

        /*
         * remove the attribute
         */
        if (!value)
            goto out;
    } else {
        di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode),
                                name, name_len, 0);
        if (IS_ERR(di)) {
            ret = PTR_ERR(di);
            goto out;
        }
        if (!di && !value)
            goto out;
        btrfs_release_path(path);
    }

again:
    ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode),
                                  name, name_len, value, size);
    /*
     * If we're setting an xattr to a new value but the new value is say
     * exactly BTRFS_MAX_XATTR_SIZE, we could end up with EOVERFLOW getting
     * back from split_leaf.  This is because it thinks we'll be extending
     * the existing item size, but we're asking for enough space to add the
     * item itself.  So if we get EOVERFLOW just set ret to EEXIST and let
     * the rest of the function figure it out.
     */
    if (ret == -EOVERFLOW)
        ret = -EEXIST;

    if (ret == -EEXIST) {
        if (flags & XATTR_CREATE)
            goto out;
        /*
         * We can't use the path we already have since we won't have the
         * proper locking for a delete, so release the path and
         * re-lookup to delete the thing.
         */
        btrfs_release_path(path);
        di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode),
                                name, name_len, -1);
        if (IS_ERR(di)) {
            ret = PTR_ERR(di);
            goto out;
        } else if (!di) {
            /* Shouldn't happen but just in case... */
            btrfs_release_path(path);
            goto again;
        }

        ret = btrfs_delete_one_dir_name(trans, root, path, di);
        if (ret)
            goto out;

        /*
         * We have a value to set, so go back and try to insert it now.
         */
        if (value) {
            btrfs_release_path(path);
            goto again;
        }
    }
out:
    btrfs_free_path(path);
    return ret;
}