btrfs: reserve correct number of items for inode creation
[platform/kernel/linux-starfive.git] / fs / btrfs / inode.c
index 1c8a43e..beafdb4 100644 (file)
@@ -222,15 +222,25 @@ static inline void btrfs_cleanup_ordered_extents(struct btrfs_inode *inode,
 static int btrfs_dirty_inode(struct inode *inode);
 
 static int btrfs_init_inode_security(struct btrfs_trans_handle *trans,
-                                    struct inode *inode,  struct inode *dir,
-                                    const struct qstr *qstr)
+                                    struct btrfs_new_inode_args *args)
 {
        int err;
 
-       err = btrfs_init_acl(trans, inode, dir);
-       if (!err)
-               err = btrfs_xattr_security_init(trans, inode, dir, qstr);
-       return err;
+       if (args->default_acl) {
+               err = __btrfs_set_acl(trans, args->inode, args->default_acl,
+                                     ACL_TYPE_DEFAULT);
+               if (err)
+                       return err;
+       }
+       if (args->acl) {
+               err = __btrfs_set_acl(trans, args->inode, args->acl, ACL_TYPE_ACCESS);
+               if (err)
+                       return err;
+       }
+       if (!args->default_acl && !args->acl)
+               cache_no_acl(args->inode);
+       return btrfs_xattr_security_init(trans, args->inode, args->dir,
+                                        &args->dentry->d_name);
 }
 
 /*
@@ -481,17 +491,6 @@ static noinline int add_async_extent(struct async_chunk *cow,
 }
 
 /*
- * Check if the inode has flags compatible with compression
- */
-static inline bool inode_can_compress(struct btrfs_inode *inode)
-{
-       if (inode->flags & BTRFS_INODE_NODATACOW ||
-           inode->flags & BTRFS_INODE_NODATASUM)
-               return false;
-       return true;
-}
-
-/*
  * Check if the inode needs to be submitted to compression, based on mount
  * options, defragmentation, properties or heuristics.
  */
@@ -500,7 +499,7 @@ static inline int inode_need_compress(struct btrfs_inode *inode, u64 start,
 {
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
 
-       if (!inode_can_compress(inode)) {
+       if (!btrfs_inode_can_compress(inode)) {
                WARN(IS_ENABLED(CONFIG_BTRFS_DEBUG),
                        KERN_ERR "BTRFS: unexpected compression for ino %llu\n",
                        btrfs_ino(inode));
@@ -2019,7 +2018,7 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct page *locked_page
                ASSERT(!zoned || btrfs_is_data_reloc_root(inode->root));
                ret = run_delalloc_nocow(inode, locked_page, start, end,
                                         page_started, nr_written);
-       } else if (!inode_can_compress(inode) ||
+       } else if (!btrfs_inode_can_compress(inode) ||
                   !inode_need_compress(inode, start, end)) {
                if (zoned)
                        ret = run_delalloc_zoned(inode, locked_page, start, end,
@@ -4210,8 +4209,9 @@ static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
         * 1 for the dir index
         * 1 for the inode ref
         * 1 for the inode
+        * 1 for the parent inode
         */
-       return btrfs_start_transaction_fallback_global_rsv(root, 5);
+       return btrfs_start_transaction_fallback_global_rsv(root, 6);
 }
 
 static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
@@ -5791,8 +5791,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
        struct list_head ins_list;
        struct list_head del_list;
        int ret;
-       struct extent_buffer *leaf;
-       int slot;
        char *name_ptr;
        int name_len;
        int entries = 0;
@@ -5819,35 +5817,19 @@ again:
        key.offset = ctx->pos;
        key.objectid = btrfs_ino(BTRFS_I(inode));
 
-       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
-       if (ret < 0)
-               goto err;
-
-       while (1) {
+       btrfs_for_each_slot(root, &key, &found_key, path, ret) {
                struct dir_entry *entry;
-
-               leaf = path->nodes[0];
-               slot = path->slots[0];
-               if (slot >= btrfs_header_nritems(leaf)) {
-                       ret = btrfs_next_leaf(root, path);
-                       if (ret < 0)
-                               goto err;
-                       else if (ret > 0)
-                               break;
-                       continue;
-               }
-
-               btrfs_item_key_to_cpu(leaf, &found_key, slot);
+               struct extent_buffer *leaf = path->nodes[0];
 
                if (found_key.objectid != key.objectid)
                        break;
                if (found_key.type != BTRFS_DIR_INDEX_KEY)
                        break;
                if (found_key.offset < ctx->pos)
-                       goto next;
+                       continue;
                if (btrfs_should_delete_dir_index(&del_list, found_key.offset))
-                       goto next;
-               di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+                       continue;
+               di = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
                name_len = btrfs_dir_name_len(leaf, di);
                if ((total_len + sizeof(struct dir_entry) + name_len) >=
                    PAGE_SIZE) {
@@ -5874,9 +5856,11 @@ again:
                entries++;
                addr += sizeof(struct dir_entry) + name_len;
                total_len += sizeof(struct dir_entry) + name_len;
-next:
-               path->slots[0]++;
        }
+       /* Catch error encountered during iteration */
+       if (ret < 0)
+               goto err;
+
        btrfs_release_path(path);
 
        ret = btrfs_filldir(private->filldir_buf, entries, ctx);
@@ -6064,6 +6048,54 @@ static int btrfs_insert_inode_locked(struct inode *inode)
                   btrfs_find_actor, &args);
 }
 
+int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
+                           unsigned int *trans_num_items)
+{
+       struct inode *dir = args->dir;
+       struct inode *inode = args->inode;
+       int ret;
+
+       ret = posix_acl_create(dir, &inode->i_mode, &args->default_acl, &args->acl);
+       if (ret)
+               return ret;
+
+       /* 1 to add inode item */
+       *trans_num_items = 1;
+       /* 1 to add compression property */
+       if (BTRFS_I(dir)->prop_compress)
+               (*trans_num_items)++;
+       /* 1 to add default ACL xattr */
+       if (args->default_acl)
+               (*trans_num_items)++;
+       /* 1 to add access ACL xattr */
+       if (args->acl)
+               (*trans_num_items)++;
+#ifdef CONFIG_SECURITY
+       /* 1 to add LSM xattr */
+       if (dir->i_security)
+               (*trans_num_items)++;
+#endif
+       if (args->orphan) {
+               /* 1 to add orphan item */
+               (*trans_num_items)++;
+       } else {
+               /*
+                * 1 to add inode ref
+                * 1 to add dir item
+                * 1 to add dir index
+                * 1 to update parent inode item
+                */
+               *trans_num_items += 4;
+       }
+       return 0;
+}
+
+void btrfs_new_inode_args_destroy(struct btrfs_new_inode_args *args)
+{
+       posix_acl_release(args->acl);
+       posix_acl_release(args->default_acl);
+}
+
 /*
  * Inherit flags from the parent inode.
  *
@@ -6095,38 +6127,41 @@ static void btrfs_inherit_iflags(struct inode *inode, struct inode *dir)
        btrfs_sync_inode_flags_to_i_flags(inode);
 }
 
-static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
-                                    struct btrfs_root *root,
-                                    struct user_namespace *mnt_userns,
-                                    struct inode *dir,
-                                    const char *name, int name_len,
-                                    u64 ref_objectid, u64 objectid,
-                                    umode_t mode, u64 *index)
+int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+                          struct btrfs_new_inode_args *args,
+                          u64 *index)
 {
-       struct btrfs_fs_info *fs_info = root->fs_info;
-       struct inode *inode;
+       struct inode *dir = args->subvol ? NULL : args->dir;
+       struct inode *inode = args->inode;
+       const char *name;
+       int name_len;
+       struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+       struct btrfs_root *root;
        struct btrfs_inode_item *inode_item;
        struct btrfs_key *location;
        struct btrfs_path *path;
+       u64 objectid;
        struct btrfs_inode_ref *ref;
        struct btrfs_key key[2];
        u32 sizes[2];
        struct btrfs_item_batch batch;
        unsigned long ptr;
-       unsigned int nofs_flag;
        int ret;
 
+       if (args->subvol) {
+               name = "..";
+               name_len = 2;
+       } else if (args->orphan) {
+               name = NULL;
+               name_len = 0;
+       } else {
+               name = args->dentry->d_name.name;
+               name_len = args->dentry->d_name.len;
+       }
+
        path = btrfs_alloc_path();
        if (!path)
-               return ERR_PTR(-ENOMEM);
-
-       nofs_flag = memalloc_nofs_save();
-       inode = new_inode(fs_info->sb);
-       memalloc_nofs_restore(nofs_flag);
-       if (!inode) {
-               btrfs_free_path(path);
-               return ERR_PTR(-ENOMEM);
-       }
+               return -ENOMEM;
 
        /*
         * O_TMPFILE, set link count to 0, so that after this point,
@@ -6135,10 +6170,15 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        if (!name)
                set_nlink(inode, 0);
 
-       /*
-        * we have to initialize this early, so we can reclaim the inode
-        * number if we fail afterwards in this function.
-        */
+       if (!args->subvol)
+               BTRFS_I(inode)->root = btrfs_grab_root(BTRFS_I(dir)->root);
+       root = BTRFS_I(inode)->root;
+
+       ret = btrfs_get_free_objectid(root, &objectid);
+       if (ret) {
+               btrfs_free_path(path);
+               return ret;
+       }
        inode->i_ino = objectid;
 
        if (dir && name) {
@@ -6147,8 +6187,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                ret = btrfs_set_inode_index(BTRFS_I(dir), index);
                if (ret) {
                        btrfs_free_path(path);
-                       iput(inode);
-                       return ERR_PTR(ret);
+                       return ret;
                }
        } else if (dir) {
                *index = 0;
@@ -6160,10 +6199,19 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
         */
        BTRFS_I(inode)->index_cnt = 2;
        BTRFS_I(inode)->dir_index = *index;
-       BTRFS_I(inode)->root = btrfs_grab_root(root);
        BTRFS_I(inode)->generation = trans->transid;
        inode->i_generation = BTRFS_I(inode)->generation;
 
+       btrfs_inherit_iflags(inode, dir);
+
+       if (S_ISREG(inode->i_mode)) {
+               if (btrfs_test_opt(fs_info, NODATASUM))
+                       BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
+               if (btrfs_test_opt(fs_info, NODATACOW))
+                       BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW |
+                               BTRFS_INODE_NODATASUM;
+       }
+
        /*
         * We could have gotten an inode number from somebody who was fsynced
         * and then removed in this same transaction, so let's just set full
@@ -6187,7 +6235,10 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                 */
                key[1].objectid = objectid;
                key[1].type = BTRFS_INODE_REF_KEY;
-               key[1].offset = ref_objectid;
+               if (dir)
+                       key[1].offset = btrfs_ino(BTRFS_I(dir));
+               else
+                       key[1].offset = objectid;
 
                sizes[1] = name_len + sizeof(*ref);
        }
@@ -6198,10 +6249,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        location->type = BTRFS_INODE_ITEM_KEY;
 
        ret = btrfs_insert_inode_locked(inode);
-       if (ret < 0) {
-               iput(inode);
+       if (ret < 0)
                goto fail;
-       }
 
        batch.keys = &key[0];
        batch.data_sizes = &sizes[0];
@@ -6211,9 +6260,6 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        if (ret != 0)
                goto fail_unlock;
 
-       inode_init_owner(mnt_userns, inode, dir, mode);
-       inode_set_bytes(inode, 0);
-
        inode->i_mtime = current_time(inode);
        inode->i_atime = inode->i_mtime;
        inode->i_ctime = inode->i_mtime;
@@ -6237,16 +6283,6 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(path->nodes[0]);
        btrfs_free_path(path);
 
-       btrfs_inherit_iflags(inode, dir);
-
-       if (S_ISREG(mode)) {
-               if (btrfs_test_opt(fs_info, NODATASUM))
-                       BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
-               if (btrfs_test_opt(fs_info, NODATACOW))
-                       BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW |
-                               BTRFS_INODE_NODATASUM;
-       }
-
        inode_tree_add(inode);
 
        trace_btrfs_inode_new(inode);
@@ -6260,15 +6296,20 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                          "error inheriting props for ino %llu (root %llu): %d",
                        btrfs_ino(BTRFS_I(inode)), root->root_key.objectid, ret);
 
-       return inode;
+       return 0;
 
 fail_unlock:
+       /*
+        * discard_new_inode() calls iput(), but the caller owns the reference
+        * to the inode.
+        */
+       ihold(inode);
        discard_new_inode(inode);
 fail:
        if (dir && name)
                BTRFS_I(dir)->index_cnt--;
        btrfs_free_path(path);
-       return ERR_PTR(ret);
+       return ret;
 }
 
 /*
@@ -6360,147 +6401,95 @@ fail_dir_item:
        return ret;
 }
 
-static int btrfs_add_nondir(struct btrfs_trans_handle *trans,
-                           struct btrfs_inode *dir, struct dentry *dentry,
-                           struct btrfs_inode *inode, int backref, u64 index)
-{
-       int err = btrfs_add_link(trans, dir, inode,
-                                dentry->d_name.name, dentry->d_name.len,
-                                backref, index);
-       if (err > 0)
-               err = -EEXIST;
-       return err;
-}
-
-static int btrfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
-                      struct dentry *dentry, umode_t mode, dev_t rdev)
+static int btrfs_create_common(struct inode *dir, struct dentry *dentry,
+                              struct inode *inode)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
-       struct btrfs_trans_handle *trans;
        struct btrfs_root *root = BTRFS_I(dir)->root;
-       struct inode *inode = NULL;
+       struct btrfs_new_inode_args new_inode_args = {
+               .dir = dir,
+               .dentry = dentry,
+               .inode = inode,
+       };
+       unsigned int trans_num_items;
+       struct btrfs_trans_handle *trans;
        int err;
-       u64 objectid;
        u64 index = 0;
 
-       /*
-        * 2 for inode item and ref
-        * 2 for dir items
-        * 1 for xattr if selinux is on
-        */
-       trans = btrfs_start_transaction(root, 5);
-       if (IS_ERR(trans))
-               return PTR_ERR(trans);
+       err = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items);
+       if (err) {
+               iput(inode);
+               return err;
+       }
 
-       err = btrfs_get_free_objectid(root, &objectid);
-       if (err)
-               goto out_unlock;
+       trans = btrfs_start_transaction(root, trans_num_items);
+       if (IS_ERR(trans)) {
+               iput(inode);
+               err = PTR_ERR(trans);
+               goto out_new_inode_args;
+       }
 
-       inode = btrfs_new_inode(trans, root, mnt_userns, dir,
-                       dentry->d_name.name, dentry->d_name.len,
-                       btrfs_ino(BTRFS_I(dir)), objectid, mode, &index);
-       if (IS_ERR(inode)) {
-               err = PTR_ERR(inode);
+       err = btrfs_create_new_inode(trans, &new_inode_args, &index);
+       if (err) {
+               iput(inode);
                inode = NULL;
                goto out_unlock;
        }
 
-       /*
-       * If the active LSM wants to access the inode during
-       * d_instantiate it needs these. Smack checks to see
-       * if the filesystem supports xattrs by looking at the
-       * ops vector.
-       */
-       inode->i_op = &btrfs_special_inode_operations;
-       init_special_inode(inode, inode->i_mode, rdev);
+       err = btrfs_init_inode_security(trans, &new_inode_args);
+       if (err)
+               goto out_unlock;
 
-       err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
+       err = btrfs_update_inode(trans, root, BTRFS_I(inode));
        if (err)
                goto out_unlock;
 
-       err = btrfs_add_nondir(trans, BTRFS_I(dir), dentry, BTRFS_I(inode),
-                       0, index);
+       err = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+                            dentry->d_name.name, dentry->d_name.len, 0, index);
        if (err)
                goto out_unlock;
 
-       btrfs_update_inode(trans, root, BTRFS_I(inode));
        d_instantiate_new(dentry, inode);
 
 out_unlock:
        btrfs_end_transaction(trans);
-       btrfs_btree_balance_dirty(fs_info);
        if (err && inode) {
                inode_dec_link_count(inode);
                discard_new_inode(inode);
        }
+       btrfs_btree_balance_dirty(fs_info);
+out_new_inode_args:
+       btrfs_new_inode_args_destroy(&new_inode_args);
        return err;
 }
 
-static int btrfs_create(struct user_namespace *mnt_userns, struct inode *dir,
-                       struct dentry *dentry, umode_t mode, bool excl)
+static int btrfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
+                      struct dentry *dentry, umode_t mode, dev_t rdev)
 {
-       struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
-       struct btrfs_trans_handle *trans;
-       struct btrfs_root *root = BTRFS_I(dir)->root;
-       struct inode *inode = NULL;
-       int err;
-       u64 objectid;
-       u64 index = 0;
+       struct inode *inode;
 
-       /*
-        * 2 for inode item and ref
-        * 2 for dir items
-        * 1 for xattr if selinux is on
-        */
-       trans = btrfs_start_transaction(root, 5);
-       if (IS_ERR(trans))
-               return PTR_ERR(trans);
+       inode = new_inode(dir->i_sb);
+       if (!inode)
+               return -ENOMEM;
+       inode_init_owner(mnt_userns, inode, dir, mode);
+       inode->i_op = &btrfs_special_inode_operations;
+       init_special_inode(inode, inode->i_mode, rdev);
+       return btrfs_create_common(dir, dentry, inode);
+}
 
-       err = btrfs_get_free_objectid(root, &objectid);
-       if (err)
-               goto out_unlock;
+static int btrfs_create(struct user_namespace *mnt_userns, struct inode *dir,
+                       struct dentry *dentry, umode_t mode, bool excl)
+{
+       struct inode *inode;
 
-       inode = btrfs_new_inode(trans, root, mnt_userns, dir,
-                       dentry->d_name.name, dentry->d_name.len,
-                       btrfs_ino(BTRFS_I(dir)), objectid, mode, &index);
-       if (IS_ERR(inode)) {
-               err = PTR_ERR(inode);
-               inode = NULL;
-               goto out_unlock;
-       }
-       /*
-       * If the active LSM wants to access the inode during
-       * d_instantiate it needs these. Smack checks to see
-       * if the filesystem supports xattrs by looking at the
-       * ops vector.
-       */
+       inode = new_inode(dir->i_sb);
+       if (!inode)
+               return -ENOMEM;
+       inode_init_owner(mnt_userns, inode, dir, mode);
        inode->i_fop = &btrfs_file_operations;
        inode->i_op = &btrfs_file_inode_operations;
        inode->i_mapping->a_ops = &btrfs_aops;
-
-       err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
-       if (err)
-               goto out_unlock;
-
-       err = btrfs_update_inode(trans, root, BTRFS_I(inode));
-       if (err)
-               goto out_unlock;
-
-       err = btrfs_add_nondir(trans, BTRFS_I(dir), dentry, BTRFS_I(inode),
-                       0, index);
-       if (err)
-               goto out_unlock;
-
-       d_instantiate_new(dentry, inode);
-
-out_unlock:
-       btrfs_end_transaction(trans);
-       if (err && inode) {
-               inode_dec_link_count(inode);
-               discard_new_inode(inode);
-       }
-       btrfs_btree_balance_dirty(fs_info);
-       return err;
+       return btrfs_create_common(dir, dentry, inode);
 }
 
 static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
@@ -6546,8 +6535,8 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
        ihold(inode);
        set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
 
-       err = btrfs_add_nondir(trans, BTRFS_I(dir), dentry, BTRFS_I(inode),
-                       1, index);
+       err = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+                            dentry->d_name.name, dentry->d_name.len, 1, index);
 
        if (err) {
                drop_inode = 1;
@@ -6584,66 +6573,15 @@ fail:
 static int btrfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
                       struct dentry *dentry, umode_t mode)
 {
-       struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
-       struct inode *inode = NULL;
-       struct btrfs_trans_handle *trans;
-       struct btrfs_root *root = BTRFS_I(dir)->root;
-       int err = 0;
-       u64 objectid = 0;
-       u64 index = 0;
-
-       /*
-        * 2 items for inode and ref
-        * 2 items for dir items
-        * 1 for xattr if selinux is on
-        */
-       trans = btrfs_start_transaction(root, 5);
-       if (IS_ERR(trans))
-               return PTR_ERR(trans);
-
-       err = btrfs_get_free_objectid(root, &objectid);
-       if (err)
-               goto out_fail;
-
-       inode = btrfs_new_inode(trans, root, mnt_userns, dir,
-                       dentry->d_name.name, dentry->d_name.len,
-                       btrfs_ino(BTRFS_I(dir)), objectid,
-                       S_IFDIR | mode, &index);
-       if (IS_ERR(inode)) {
-               err = PTR_ERR(inode);
-               inode = NULL;
-               goto out_fail;
-       }
+       struct inode *inode;
 
-       /* these must be set before we unlock the inode */
+       inode = new_inode(dir->i_sb);
+       if (!inode)
+               return -ENOMEM;
+       inode_init_owner(mnt_userns, inode, dir, S_IFDIR | mode);
        inode->i_op = &btrfs_dir_inode_operations;
        inode->i_fop = &btrfs_dir_file_operations;
-
-       err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
-       if (err)
-               goto out_fail;
-
-       btrfs_i_size_write(BTRFS_I(inode), 0);
-       err = btrfs_update_inode(trans, root, BTRFS_I(inode));
-       if (err)
-               goto out_fail;
-
-       err = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
-                       dentry->d_name.name,
-                       dentry->d_name.len, 0, index);
-       if (err)
-               goto out_fail;
-
-       d_instantiate_new(dentry, inode);
-
-out_fail:
-       btrfs_end_transaction(trans);
-       if (err && inode) {
-               inode_dec_link_count(inode);
-               discard_new_inode(inode);
-       }
-       btrfs_btree_balance_dirty(fs_info);
-       return err;
+       return btrfs_create_common(dir, dentry, inode);
 }
 
 static noinline int uncompress_inline(struct btrfs_path *path,
@@ -8775,34 +8713,41 @@ out:
        return ret;
 }
 
+struct inode *btrfs_new_subvol_inode(struct user_namespace *mnt_userns,
+                                    struct inode *dir)
+{
+       struct inode *inode;
+
+       inode = new_inode(dir->i_sb);
+       if (inode) {
+               /*
+                * Subvolumes don't inherit the sgid bit or the parent's gid if
+                * the parent's sgid bit is set. This is probably a bug.
+                */
+               inode_init_owner(mnt_userns, inode, NULL,
+                                S_IFDIR | (~current_umask() & S_IRWXUGO));
+               inode->i_op = &btrfs_dir_inode_operations;
+               inode->i_fop = &btrfs_dir_file_operations;
+       }
+       return inode;
+}
+
 /*
  * create a new subvolume directory/inode (helper for the ioctl).
  */
 int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
-                            struct btrfs_root *new_root,
                             struct btrfs_root *parent_root,
-                            struct user_namespace *mnt_userns)
+                            struct btrfs_new_inode_args *args)
 {
-       struct inode *inode;
+       struct inode *inode = args->inode;
+       struct btrfs_root *new_root = BTRFS_I(inode)->root;
        int err;
        u64 index = 0;
-       u64 ino;
 
-       err = btrfs_get_free_objectid(new_root, &ino);
-       if (err < 0)
+       err = btrfs_create_new_inode(trans, args, &index);
+       if (err)
                return err;
 
-       inode = btrfs_new_inode(trans, new_root, mnt_userns, NULL, "..", 2,
-                               ino, ino,
-                               S_IFDIR | (~current_umask() & S_IRWXUGO),
-                               &index);
-       if (IS_ERR(inode))
-               return PTR_ERR(inode);
-       inode->i_op = &btrfs_dir_inode_operations;
-       inode->i_fop = &btrfs_dir_file_operations;
-
-       set_nlink(inode, 1);
-       btrfs_i_size_write(BTRFS_I(inode), 0);
        unlock_new_inode(inode);
 
        err = btrfs_subvol_inherit_props(trans, new_root, parent_root);
@@ -8812,8 +8757,6 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
                          new_root->root_key.objectid, err);
 
        err = btrfs_update_inode(trans, new_root, BTRFS_I(inode));
-
-       iput(inode);
        return err;
 }
 
@@ -9061,6 +9004,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(old_dir->i_sb);
        struct btrfs_trans_handle *trans;
+       unsigned int trans_num_items;
        struct btrfs_root *root = BTRFS_I(old_dir)->root;
        struct btrfs_root *dest = BTRFS_I(new_dir)->root;
        struct inode *new_inode = new_dentry->d_inode;
@@ -9092,14 +9036,37 @@ static int btrfs_rename_exchange(struct inode *old_dir,
                down_read(&fs_info->subvol_sem);
 
        /*
-        * We want to reserve the absolute worst case amount of items.  So if
-        * both inodes are subvols and we need to unlink them then that would
-        * require 4 item modifications, but if they are both normal inodes it
-        * would require 5 item modifications, so we'll assume their normal
-        * inodes.  So 5 * 2 is 10, plus 2 for the new links, so 12 total items
-        * should cover the worst case number of items we'll modify.
+        * For each inode:
+        * 1 to remove old dir item
+        * 1 to remove old dir index
+        * 1 to add new dir item
+        * 1 to add new dir index
+        * 1 to update parent inode
+        *
+        * If the parents are the same, we only need to account for one
         */
-       trans = btrfs_start_transaction(root, 12);
+       trans_num_items = (old_dir == new_dir ? 9 : 10);
+       if (old_ino == BTRFS_FIRST_FREE_OBJECTID) {
+               /*
+                * 1 to remove old root ref
+                * 1 to remove old root backref
+                * 1 to add new root ref
+                * 1 to add new root backref
+                */
+               trans_num_items += 4;
+       } else {
+               /*
+                * 1 to update inode item
+                * 1 to remove old inode ref
+                * 1 to add new inode ref
+                */
+               trans_num_items += 3;
+       }
+       if (new_ino == BTRFS_FIRST_FREE_OBJECTID)
+               trans_num_items += 4;
+       else
+               trans_num_items += 3;
+       trans = btrfs_start_transaction(root, trans_num_items);
        if (IS_ERR(trans)) {
                ret = PTR_ERR(trans);
                goto out_notrans;
@@ -9266,45 +9233,43 @@ out_notrans:
        return ret;
 }
 
-static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans,
-                                    struct btrfs_root *root,
-                                    struct user_namespace *mnt_userns,
-                                    struct inode *dir,
-                                    struct dentry *dentry)
+static struct inode *new_whiteout_inode(struct user_namespace *mnt_userns,
+                                       struct inode *dir)
 {
-       int ret;
        struct inode *inode;
-       u64 objectid;
-       u64 index;
 
-       ret = btrfs_get_free_objectid(root, &objectid);
-       if (ret)
-               return ret;
+       inode = new_inode(dir->i_sb);
+       if (inode) {
+               inode_init_owner(mnt_userns, inode, dir,
+                                S_IFCHR | WHITEOUT_MODE);
+               inode->i_op = &btrfs_special_inode_operations;
+               init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
+       }
+       return inode;
+}
 
-       inode = btrfs_new_inode(trans, root, mnt_userns, dir,
-                               dentry->d_name.name,
-                               dentry->d_name.len,
-                               btrfs_ino(BTRFS_I(dir)),
-                               objectid,
-                               S_IFCHR | WHITEOUT_MODE,
-                               &index);
+static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans,
+                                    struct btrfs_new_inode_args *args)
+{
+       struct inode *inode = args->inode;
+       struct inode *dir = args->dir;
+       struct btrfs_root *root = BTRFS_I(dir)->root;
+       struct dentry *dentry = args->dentry;
+       int ret;
+       u64 index;
 
-       if (IS_ERR(inode)) {
-               ret = PTR_ERR(inode);
+       ret = btrfs_create_new_inode(trans, args, &index);
+       if (ret) {
+               iput(inode);
                return ret;
        }
 
-       inode->i_op = &btrfs_special_inode_operations;
-       init_special_inode(inode, inode->i_mode,
-               WHITEOUT_DEV);
-
-       ret = btrfs_init_inode_security(trans, inode, dir,
-                               &dentry->d_name);
+       ret = btrfs_init_inode_security(trans, args);
        if (ret)
                goto out;
 
-       ret = btrfs_add_nondir(trans, BTRFS_I(dir), dentry,
-                               BTRFS_I(inode), 0, index);
+       ret = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+                            dentry->d_name.name, dentry->d_name.len, 0, index);
        if (ret)
                goto out;
 
@@ -9324,6 +9289,10 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
                        unsigned int flags)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(old_dir->i_sb);
+       struct btrfs_new_inode_args whiteout_args = {
+               .dir = old_dir,
+               .dentry = old_dentry,
+       };
        struct btrfs_trans_handle *trans;
        unsigned int trans_num_items;
        struct btrfs_root *root = BTRFS_I(old_dir)->root;
@@ -9378,23 +9347,56 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
        if (new_inode && S_ISREG(old_inode->i_mode) && new_inode->i_size)
                filemap_flush(old_inode->i_mapping);
 
-       /* close the racy window with snapshot create/destroy ioctl */
-       if (old_ino == BTRFS_FIRST_FREE_OBJECTID)
+       if (flags & RENAME_WHITEOUT) {
+               whiteout_args.inode = new_whiteout_inode(mnt_userns, old_dir);
+               if (!whiteout_args.inode)
+                       return -ENOMEM;
+               ret = btrfs_new_inode_prepare(&whiteout_args, &trans_num_items);
+               if (ret)
+                       goto out_whiteout_inode;
+       } else {
+               /* 1 to update the old parent inode. */
+               trans_num_items = 1;
+       }
+
+       if (old_ino == BTRFS_FIRST_FREE_OBJECTID) {
+               /* Close the race window with snapshot create/destroy ioctl */
                down_read(&fs_info->subvol_sem);
+               /*
+                * 1 to remove old root ref
+                * 1 to remove old root backref
+                * 1 to add new root ref
+                * 1 to add new root backref
+                */
+               trans_num_items += 4;
+       } else {
+               /*
+                * 1 to update inode
+                * 1 to remove old inode ref
+                * 1 to add new inode ref
+                */
+               trans_num_items += 3;
+       }
        /*
-        * We want to reserve the absolute worst case amount of items.  So if
-        * both inodes are subvols and we need to unlink them then that would
-        * require 4 item modifications, but if they are both normal inodes it
-        * would require 5 item modifications, so we'll assume they are normal
-        * inodes.  So 5 * 2 is 10, plus 1 for the new link, so 11 total items
-        * should cover the worst case number of items we'll modify.
-        * If our rename has the whiteout flag, we need more 5 units for the
-        * new inode (1 inode item, 1 inode ref, 2 dir items and 1 xattr item
-        * when selinux is enabled).
+        * 1 to remove old dir item
+        * 1 to remove old dir index
+        * 1 to add new dir item
+        * 1 to add new dir index
         */
-       trans_num_items = 11;
-       if (flags & RENAME_WHITEOUT)
+       trans_num_items += 4;
+       /* 1 to update new parent inode if it's not the same as the old parent */
+       if (new_dir != old_dir)
+               trans_num_items++;
+       if (new_inode) {
+               /*
+                * 1 to update inode
+                * 1 to remove inode ref
+                * 1 to remove dir item
+                * 1 to remove dir index
+                * 1 to possibly add orphan item
+                */
                trans_num_items += 5;
+       }
        trans = btrfs_start_transaction(root, trans_num_items);
        if (IS_ERR(trans)) {
                ret = PTR_ERR(trans);
@@ -9490,9 +9492,8 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
                                   rename_ctx.index, new_dentry->d_parent);
 
        if (flags & RENAME_WHITEOUT) {
-               ret = btrfs_whiteout_for_rename(trans, root, mnt_userns,
-                                               old_dir, old_dentry);
-
+               ret = btrfs_whiteout_for_rename(trans, &whiteout_args);
+               whiteout_args.inode = NULL;
                if (ret) {
                        btrfs_abort_transaction(trans, ret);
                        goto out_fail;
@@ -9504,7 +9505,11 @@ out_fail:
 out_notrans:
        if (old_ino == BTRFS_FIRST_FREE_OBJECTID)
                up_read(&fs_info->subvol_sem);
-
+       if (flags & RENAME_WHITEOUT)
+               btrfs_new_inode_args_destroy(&whiteout_args);
+out_whiteout_inode:
+       if (flags & RENAME_WHITEOUT)
+               iput(whiteout_args.inode);
        return ret;
 }
 
@@ -9723,9 +9728,13 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
        struct btrfs_root *root = BTRFS_I(dir)->root;
        struct btrfs_path *path;
        struct btrfs_key key;
-       struct inode *inode = NULL;
+       struct inode *inode;
+       struct btrfs_new_inode_args new_inode_args = {
+               .dir = dir,
+               .dentry = dentry,
+       };
+       unsigned int trans_num_items;
        int err;
-       u64 objectid;
        u64 index = 0;
        int name_len;
        int datasize;
@@ -9737,42 +9746,38 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
        if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info))
                return -ENAMETOOLONG;
 
-       /*
-        * 2 items for inode item and ref
-        * 2 items for dir items
-        * 1 item for updating parent inode item
-        * 1 item for the inline extent item
-        * 1 item for xattr if selinux is on
-        */
-       trans = btrfs_start_transaction(root, 7);
-       if (IS_ERR(trans))
-               return PTR_ERR(trans);
+       inode = new_inode(dir->i_sb);
+       if (!inode)
+               return -ENOMEM;
+       inode_init_owner(mnt_userns, inode, dir, S_IFLNK | S_IRWXUGO);
+       inode->i_op = &btrfs_symlink_inode_operations;
+       inode_nohighmem(inode);
+       inode->i_mapping->a_ops = &btrfs_aops;
 
-       err = btrfs_get_free_objectid(root, &objectid);
-       if (err)
-               goto out_unlock;
+       new_inode_args.inode = inode;
+       err = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items);
+       if (err) {
+               iput(inode);
+               return err;
+       }
+       /* 1 additional item for the inline extent */
+       trans_num_items++;
 
-       inode = btrfs_new_inode(trans, root, mnt_userns, dir,
-                               dentry->d_name.name, dentry->d_name.len,
-                               btrfs_ino(BTRFS_I(dir)), objectid,
-                               S_IFLNK | S_IRWXUGO, &index);
-       if (IS_ERR(inode)) {
-               err = PTR_ERR(inode);
+       trans = btrfs_start_transaction(root, trans_num_items);
+       if (IS_ERR(trans)) {
+               iput(inode);
+               err = PTR_ERR(trans);
+               goto out_new_inode_args;
+       }
+
+       err = btrfs_create_new_inode(trans, &new_inode_args, &index);
+       if (err) {
+               iput(inode);
                inode = NULL;
                goto out_unlock;
        }
 
-       /*
-       * If the active LSM wants to access the inode during
-       * d_instantiate it needs these. Smack checks to see
-       * if the filesystem supports xattrs by looking at the
-       * ops vector.
-       */
-       inode->i_fop = &btrfs_file_operations;
-       inode->i_op = &btrfs_file_inode_operations;
-       inode->i_mapping->a_ops = &btrfs_aops;
-
-       err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
+       err = btrfs_init_inode_security(trans, &new_inode_args);
        if (err)
                goto out_unlock;
 
@@ -9807,8 +9812,6 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
        btrfs_mark_buffer_dirty(leaf);
        btrfs_free_path(path);
 
-       inode->i_op = &btrfs_symlink_inode_operations;
-       inode_nohighmem(inode);
        inode_set_bytes(inode, name_len);
        btrfs_i_size_write(BTRFS_I(inode), name_len);
        err = btrfs_update_inode(trans, root, BTRFS_I(inode));
@@ -9818,8 +9821,9 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
         * elsewhere above.
         */
        if (!err)
-               err = btrfs_add_nondir(trans, BTRFS_I(dir), dentry,
-                               BTRFS_I(inode), 0, index);
+               err = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+                                    dentry->d_name.name, dentry->d_name.len, 0,
+                                    index);
        if (err)
                goto out_unlock;
 
@@ -9832,6 +9836,8 @@ out_unlock:
                discard_new_inode(inode);
        }
        btrfs_btree_balance_dirty(fs_info);
+out_new_inode_args:
+       btrfs_new_inode_args_destroy(&new_inode_args);
        return err;
 }
 
@@ -10082,36 +10088,46 @@ static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir,
        struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
        struct btrfs_trans_handle *trans;
        struct btrfs_root *root = BTRFS_I(dir)->root;
-       struct inode *inode = NULL;
-       u64 objectid;
+       struct inode *inode;
+       struct btrfs_new_inode_args new_inode_args = {
+               .dir = dir,
+               .dentry = dentry,
+               .orphan = true,
+       };
+       unsigned int trans_num_items;
        u64 index;
-       int ret = 0;
+       int ret;
 
-       /*
-        * 5 units required for adding orphan entry
-        */
-       trans = btrfs_start_transaction(root, 5);
-       if (IS_ERR(trans))
-               return PTR_ERR(trans);
+       inode = new_inode(dir->i_sb);
+       if (!inode)
+               return -ENOMEM;
+       inode_init_owner(mnt_userns, inode, dir, mode);
+       inode->i_fop = &btrfs_file_operations;
+       inode->i_op = &btrfs_file_inode_operations;
+       inode->i_mapping->a_ops = &btrfs_aops;
 
-       ret = btrfs_get_free_objectid(root, &objectid);
-       if (ret)
-               goto out;
+       new_inode_args.inode = inode;
+       ret = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items);
+       if (ret) {
+               iput(inode);
+               return ret;
+       }
 
-       inode = btrfs_new_inode(trans, root, mnt_userns, dir, NULL, 0,
-                       btrfs_ino(BTRFS_I(dir)), objectid, mode, &index);
-       if (IS_ERR(inode)) {
-               ret = PTR_ERR(inode);
+       trans = btrfs_start_transaction(root, trans_num_items);
+       if (IS_ERR(trans)) {
+               iput(inode);
+               ret = PTR_ERR(trans);
+               goto out_new_inode_args;
+       }
+
+       ret = btrfs_create_new_inode(trans, &new_inode_args, &index);
+       if (ret) {
+               iput(inode);
                inode = NULL;
                goto out;
        }
 
-       inode->i_fop = &btrfs_file_operations;
-       inode->i_op = &btrfs_file_inode_operations;
-
-       inode->i_mapping->a_ops = &btrfs_aops;
-
-       ret = btrfs_init_inode_security(trans, inode, dir, NULL);
+       ret = btrfs_init_inode_security(trans, &new_inode_args);
        if (ret)
                goto out;
 
@@ -10123,9 +10139,9 @@ static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir,
                goto out;
 
        /*
-        * We set number of links to 0 in btrfs_new_inode(), and here we set
-        * it to 1 because d_tmpfile() will issue a warning if the count is 0,
-        * through:
+        * We set number of links to 0 in btrfs_create_new_inode(), and here we
+        * set it to 1 because d_tmpfile() will issue a warning if the count is
+        * 0, through:
         *
         *    d_tmpfile() -> inode_dec_link_count() -> drop_nlink()
         */
@@ -10138,6 +10154,8 @@ out:
        if (ret && inode)
                discard_new_inode(inode);
        btrfs_btree_balance_dirty(fs_info);
+out_new_inode_args:
+       btrfs_new_inode_args_destroy(&new_inode_args);
        return ret;
 }