| From: Omar Sandoval <osandov@fb.com> |
| Date: Fri, 11 May 2018 13:13:35 -0700 |
| Subject: Btrfs: don't return ino to ino cache if inode item removal fails |
| |
| commit c08db7d8d295a4f3a10faaca376de011afff7950 upstream. |
| |
| In btrfs_evict_inode(), if btrfs_truncate_inode_items() fails, the inode |
| item will still be in the tree but we still return the ino to the ino |
| cache. That will blow up later when someone tries to allocate that ino, |
| so don't return it to the cache. |
| |
| Fixes: 581bb050941b ("Btrfs: Cache free inode numbers in memory") |
| Reviewed-by: Josef Bacik <jbacik@fb.com> |
| Signed-off-by: Omar Sandoval <osandov@fb.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| [bwh: Backported to 3.16: |
| - Pass inode, not btrfs_inode, to btrfs_orphan_del() |
| - Pass btrfs_root, not btrfs_fs_info, to btrfs_free_block_rsv() |
| - Adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/btrfs/inode.c | 25 +++++++++++++------------ |
| 1 file changed, 13 insertions(+), 12 deletions(-) |
| |
| --- a/fs/btrfs/inode.c |
| +++ b/fs/btrfs/inode.c |
| @@ -4908,13 +4908,18 @@ void btrfs_evict_inode(struct inode *ino |
| trans->block_rsv = rsv; |
| |
| ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0); |
| - if (ret != -ENOSPC) |
| + if (ret) { |
| + trans->block_rsv = &root->fs_info->trans_block_rsv; |
| + btrfs_end_transaction(trans, root); |
| + btrfs_btree_balance_dirty(root); |
| + if (ret != -ENOSPC) { |
| + btrfs_orphan_del(NULL, inode); |
| + btrfs_free_block_rsv(root, rsv); |
| + goto no_delete; |
| + } |
| + } else { |
| break; |
| - |
| - trans->block_rsv = &root->fs_info->trans_block_rsv; |
| - btrfs_end_transaction(trans, root); |
| - trans = NULL; |
| - btrfs_btree_balance_dirty(root); |
| + } |
| } |
| |
| btrfs_free_block_rsv(root, rsv); |
| @@ -4923,12 +4928,8 @@ void btrfs_evict_inode(struct inode *ino |
| * Errors here aren't a big deal, it just means we leave orphan items |
| * in the tree. They will be cleaned up on the next mount. |
| */ |
| - if (ret == 0) { |
| - trans->block_rsv = root->orphan_block_rsv; |
| - btrfs_orphan_del(trans, inode); |
| - } else { |
| - btrfs_orphan_del(NULL, inode); |
| - } |
| + trans->block_rsv = root->orphan_block_rsv; |
| + btrfs_orphan_del(trans, inode); |
| |
| trans->block_rsv = &root->fs_info->trans_block_rsv; |
| if (!(root == root->fs_info->tree_root || |