Btrfs: fix some metadata enospc issues
authorJosef Bacik <josef@redhat.com>
Wed, 11 Nov 2009 02:23:48 +0000 (21:23 -0500)
committerChris Mason <chris.mason@oracle.com>
Wed, 11 Nov 2009 19:20:17 +0000 (14:20 -0500)
We weren't reserving metadata space for rename, rmdir and unlink, which could
cause problems.

Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/inode.c

index d8393ddc72a38ae7fc28fc66652454627116f35b..bb7fd8072802964577777310588e2cf40df732e7 100644 (file)
@@ -2488,7 +2488,19 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
 
        root = BTRFS_I(dir)->root;
 
+       /*
+        * 5 items for unlink inode
+        * 1 for orphan
+        */
+       ret = btrfs_reserve_metadata_space(root, 6);
+       if (ret)
+               return ret;
+
        trans = btrfs_start_transaction(root, 1);
+       if (IS_ERR(trans)) {
+               btrfs_unreserve_metadata_space(root, 6);
+               return PTR_ERR(trans);
+       }
 
        btrfs_set_trans_block_group(trans, dir);
 
@@ -2503,6 +2515,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
        nr = trans->blocks_used;
 
        btrfs_end_transaction_throttle(trans, root);
+       btrfs_unreserve_metadata_space(root, 6);
        btrfs_btree_balance_dirty(root, nr);
        return ret;
 }
@@ -2583,7 +2596,16 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
            inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
                return -ENOTEMPTY;
 
+       ret = btrfs_reserve_metadata_space(root, 5);
+       if (ret)
+               return ret;
+
        trans = btrfs_start_transaction(root, 1);
+       if (IS_ERR(trans)) {
+               btrfs_unreserve_metadata_space(root, 5);
+               return PTR_ERR(trans);
+       }
+
        btrfs_set_trans_block_group(trans, dir);
 
        if (unlikely(inode->i_ino == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) {
@@ -2606,6 +2628,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
 out:
        nr = trans->blocks_used;
        ret = btrfs_end_transaction_throttle(trans, root);
+       btrfs_unreserve_metadata_space(root, 5);
        btrfs_btree_balance_dirty(root, nr);
 
        if (ret && !err)
@@ -5297,11 +5320,14 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                return -ENOTEMPTY;
 
        /*
-        * 2 items for dir items
-        * 1 item for orphan entry
-        * 1 item for ref
+        * 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 1 for the new link, so 11 total items
+        * should cover the worst case number of items we'll modify.
         */
-       ret = btrfs_reserve_metadata_space(root, 4);
+       ret = btrfs_reserve_metadata_space(root, 11);
        if (ret)
                return ret;
 
@@ -5417,7 +5443,7 @@ out_fail:
        if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
                up_read(&root->fs_info->subvol_sem);
 
-       btrfs_unreserve_metadata_space(root, 4);
+       btrfs_unreserve_metadata_space(root, 11);
        return ret;
 }