/*
  * Needs to be called with fs_mutex held
  */
-static int btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+static int btrfs_set_acl(struct btrfs_trans_handle *trans,
+                        struct inode *inode, struct posix_acl *acl, int type)
 {
        int ret, size = 0;
        const char *name;
                        goto out;
        }
 
-       ret = __btrfs_setxattr(inode, name, value, size, 0);
-
+       ret = __btrfs_setxattr(trans, inode, name, value, size, 0);
 out:
        kfree(value);
 
 static int btrfs_xattr_set_acl(struct inode *inode, int type,
                               const void *value, size_t size)
 {
-       int ret = 0;
+       int ret;
        struct posix_acl *acl = NULL;
 
        if (value) {
                }
        }
 
-       ret = btrfs_set_acl(inode, acl, type);
+       ret = btrfs_set_acl(NULL, inode, acl, type);
 
        posix_acl_release(acl);
 
  * stuff has been fixed to work with that.  If the locking stuff changes, we
  * need to re-evaluate the acl locking stuff.
  */
-int btrfs_init_acl(struct inode *inode, struct inode *dir)
+int btrfs_init_acl(struct btrfs_trans_handle *trans,
+                  struct inode *inode, struct inode *dir)
 {
        struct posix_acl *acl = NULL;
        int ret = 0;
                mode_t mode;
 
                if (S_ISDIR(inode->i_mode)) {
-                       ret = btrfs_set_acl(inode, acl, ACL_TYPE_DEFAULT);
+                       ret = btrfs_set_acl(trans, inode, acl,
+                                           ACL_TYPE_DEFAULT);
                        if (ret)
                                goto failed;
                }
                        inode->i_mode = mode;
                        if (ret > 0) {
                                /* we need an acl */
-                               ret = btrfs_set_acl(inode, clone,
+                               ret = btrfs_set_acl(trans, inode, clone,
                                                    ACL_TYPE_ACCESS);
                        }
                }
 
        ret = posix_acl_chmod_masq(clone, inode->i_mode);
        if (!ret)
-               ret = btrfs_set_acl(inode, clone, ACL_TYPE_ACCESS);
+               ret = btrfs_set_acl(NULL, inode, clone, ACL_TYPE_ACCESS);
 
        posix_acl_release(clone);
 
        return 0;
 }
 
-int btrfs_init_acl(struct inode *inode, struct inode *dir)
+int btrfs_init_acl(struct btrfs_trans_handle *trans,
+                  struct inode *inode, struct inode *dir)
 {
        return 0;
 }
 
 #define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \
                                        sizeof(struct btrfs_item) - \
                                        sizeof(struct btrfs_file_extent_item))
+#define BTRFS_MAX_XATTR_SIZE(r)        (BTRFS_LEAF_DATA_SIZE(r) - \
+                                sizeof(struct btrfs_item) -\
+                                sizeof(struct btrfs_dir_item))
 
 
 /*
                              struct btrfs_path *path,
                              struct btrfs_dir_item *di);
 int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
-                           struct btrfs_root *root, const char *name,
-                           u16 name_len, const void *data, u16 data_len,
-                           u64 dir);
+                           struct btrfs_root *root,
+                           struct btrfs_path *path, u64 objectid,
+                           const char *name, u16 name_len,
+                           const void *data, u16 data_len);
 struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
                                          struct btrfs_root *root,
                                          struct btrfs_path *path, u64 dir,
 #else
 #define btrfs_check_acl NULL
 #endif
-int btrfs_init_acl(struct inode *inode, struct inode *dir);
+int btrfs_init_acl(struct btrfs_trans_handle *trans,
+                  struct inode *inode, struct inode *dir);
 int btrfs_acl_chmod(struct inode *inode);
 
 /* relocation.c */
 
  * into the tree
  */
 int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
-                           struct btrfs_root *root, const char *name,
-                           u16 name_len, const void *data, u16 data_len,
-                           u64 dir)
+                           struct btrfs_root *root,
+                           struct btrfs_path *path, u64 objectid,
+                           const char *name, u16 name_len,
+                           const void *data, u16 data_len)
 {
        int ret = 0;
-       struct btrfs_path *path;
        struct btrfs_dir_item *dir_item;
        unsigned long name_ptr, data_ptr;
        struct btrfs_key key, location;
        struct extent_buffer *leaf;
        u32 data_size;
 
-       key.objectid = dir;
+       BUG_ON(name_len + data_len > BTRFS_MAX_XATTR_SIZE(root));
+
+       key.objectid = objectid;
        btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
        key.offset = btrfs_name_hash(name, name_len);
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-       if (name_len + data_len + sizeof(struct btrfs_dir_item) >
-           BTRFS_LEAF_DATA_SIZE(root) - sizeof(struct btrfs_item))
-               return -ENOSPC;
 
        data_size = sizeof(*dir_item) + name_len + data_len;
        dir_item = insert_with_overflow(trans, root, path, &key, data_size,
        write_extent_buffer(leaf, data, data_ptr, data_len);
        btrfs_mark_buffer_dirty(path->nodes[0]);
 
-       btrfs_free_path(path);
        return ret;
 }
 
 
                                   u64 start, u64 end, int *page_started,
                                   unsigned long *nr_written, int unlock);
 
-static int btrfs_init_inode_security(struct inode *inode,  struct inode *dir)
+static int btrfs_init_inode_security(struct btrfs_trans_handle *trans,
+                                    struct inode *inode,  struct inode *dir)
 {
        int err;
 
-       err = btrfs_init_acl(inode, dir);
+       err = btrfs_init_acl(trans, inode, dir);
        if (!err)
-               err = btrfs_xattr_security_init(inode, dir);
+               err = btrfs_xattr_security_init(trans, inode, dir);
        return err;
 }
 
        if (IS_ERR(inode))
                goto out_unlock;
 
-       err = btrfs_init_inode_security(inode, dir);
+       err = btrfs_init_inode_security(trans, inode, dir);
        if (err) {
                drop_inode = 1;
                goto out_unlock;
        if (IS_ERR(inode))
                goto out_unlock;
 
-       err = btrfs_init_inode_security(inode, dir);
+       err = btrfs_init_inode_security(trans, inode, dir);
        if (err) {
                drop_inode = 1;
                goto out_unlock;
 
        drop_on_err = 1;
 
-       err = btrfs_init_inode_security(inode, dir);
+       err = btrfs_init_inode_security(trans, inode, dir);
        if (err)
                goto out_fail;
 
        if (IS_ERR(inode))
                goto out_unlock;
 
-       err = btrfs_init_inode_security(inode, dir);
+       err = btrfs_init_inode_security(trans, inode, dir);
        if (err) {
                drop_inode = 1;
                goto out_unlock;
 
        return ret;
 }
 
-int __btrfs_setxattr(struct inode *inode, const char *name,
-                           const void *value, size_t size, int flags)
+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_trans_handle *trans;
        struct btrfs_path *path;
-       int ret = 0, mod = 0;
+       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;
 
-       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);
                }
 
                ret = btrfs_delete_one_dir_name(trans, root, path, di);
-               if (ret)
-                       goto out;
+               BUG_ON(ret);
                btrfs_release_path(root, path);
 
                /* if we don't have a value then we are removing the xattr */
-               if (!value) {
-                       mod = 1;
+               if (!value)
                        goto out;
-               }
        } else {
                btrfs_release_path(root, path);
 
        }
 
        /* ok we have to create a completely new xattr */
-       ret = btrfs_insert_xattr_item(trans, root, name, strlen(name),
-                                     value, size, inode->i_ino);
+       ret = btrfs_insert_xattr_item(trans, root, path, inode->i_ino,
+                                     name, name_len, value, size);
+       BUG_ON(ret);
+out:
+       btrfs_free_path(path);
+       return ret;
+}
+
+int __btrfs_setxattr(struct btrfs_trans_handle *trans,
+                    struct inode *inode, const char *name,
+                    const void *value, size_t size, int flags)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       int ret;
+
+       if (trans)
+               return do_setxattr(trans, inode, name, value, size, flags);
+
+       ret = btrfs_reserve_metadata_space(root, 2);
        if (ret)
-               goto out;
-       mod = 1;
+               return ret;
 
-out:
-       if (mod) {
-               inode->i_ctime = CURRENT_TIME;
-               ret = btrfs_update_inode(trans, root, inode);
+       trans = btrfs_start_transaction(root, 1);
+       if (!trans) {
+               ret = -ENOMEM;
+               goto out;
        }
+       btrfs_set_trans_block_group(trans, inode);
 
-       btrfs_end_transaction(trans, root);
-       btrfs_free_path(path);
+       ret = do_setxattr(trans, inode, name, value, size, flags);
+       if (ret)
+               goto out;
+
+       inode->i_ctime = CURRENT_TIME;
+       ret = btrfs_update_inode(trans, root, inode);
+       BUG_ON(ret);
+out:
+       btrfs_end_transaction_throttle(trans, root);
+       btrfs_unreserve_metadata_space(root, 2);
        return ret;
 }
 
 
        if (size == 0)
                value = "";  /* empty EA, do not remove */
-       return __btrfs_setxattr(dentry->d_inode, name, value, size, flags);
+
+       return __btrfs_setxattr(NULL, dentry->d_inode, name, value, size,
+                               flags);
 }
 
 int btrfs_removexattr(struct dentry *dentry, const char *name)
 
        if (!btrfs_is_valid_xattr(name))
                return -EOPNOTSUPP;
-       return __btrfs_setxattr(dentry->d_inode, name, NULL, 0, XATTR_REPLACE);
+
+       return __btrfs_setxattr(NULL, dentry->d_inode, name, NULL, 0,
+                               XATTR_REPLACE);
 }
 
-int btrfs_xattr_security_init(struct inode *inode, struct inode *dir)
+int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
+                             struct inode *inode, struct inode *dir)
 {
        int err;
        size_t len;
        } else {
                strcpy(name, XATTR_SECURITY_PREFIX);
                strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
-               err = __btrfs_setxattr(inode, name, value, len, 0);
+               err = __btrfs_setxattr(trans, inode, name, value, len, 0);
                kfree(name);
        }
 
 
 
 extern ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
                void *buffer, size_t size);
-extern int __btrfs_setxattr(struct inode *inode, const char *name,
-               const void *value, size_t size, int flags);
-
+extern int __btrfs_setxattr(struct btrfs_trans_handle *trans,
+                           struct inode *inode, const char *name,
+                           const void *value, size_t size, int flags);
 extern ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
                void *buffer, size_t size);
 extern int btrfs_setxattr(struct dentry *dentry, const char *name,
                const void *value, size_t size, int flags);
 extern int btrfs_removexattr(struct dentry *dentry, const char *name);
 
-extern int btrfs_xattr_security_init(struct inode *inode, struct inode *dir);
+extern int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
+                                    struct inode *inode, struct inode *dir);
 
 #endif /* __XATTR__ */