diff options
author | Josef Bacik <josef@redhat.com> | 2009-10-13 16:46:49 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2009-10-14 10:32:47 -0400 |
commit | 5d5e103a70f74ae98e3965a4add1ab951d0651d1 (patch) | |
tree | 83b1cc73830f72f592aa804207a679912beef101 /fs | |
parent | 0eda294dfc980c1cbe4f8a0564bf543f86a01ddb (diff) |
Btrfs: fix possible ENOSPC problems with truncate
There's a problem where we don't do any space reservation for truncates, which
can cause you to OOPs because you will be allowed to go off in the weeds a bit
since we don't account for the delalloc bytes that are created as a result of
the truncate.
Signed-off-by: Josef Bacik <jbacik@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/inode.c | 27 |
1 files changed, 24 insertions, 3 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5b9567caba0a..78139efe41fc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -3032,12 +3032,22 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from) | |||
3032 | 3032 | ||
3033 | if ((offset & (blocksize - 1)) == 0) | 3033 | if ((offset & (blocksize - 1)) == 0) |
3034 | goto out; | 3034 | goto out; |
3035 | ret = btrfs_check_data_free_space(root, inode, PAGE_CACHE_SIZE); | ||
3036 | if (ret) | ||
3037 | goto out; | ||
3038 | |||
3039 | ret = btrfs_reserve_metadata_for_delalloc(root, inode, 1); | ||
3040 | if (ret) | ||
3041 | goto out; | ||
3035 | 3042 | ||
3036 | ret = -ENOMEM; | 3043 | ret = -ENOMEM; |
3037 | again: | 3044 | again: |
3038 | page = grab_cache_page(mapping, index); | 3045 | page = grab_cache_page(mapping, index); |
3039 | if (!page) | 3046 | if (!page) { |
3047 | btrfs_free_reserved_data_space(root, inode, PAGE_CACHE_SIZE); | ||
3048 | btrfs_unreserve_metadata_for_delalloc(root, inode, 1); | ||
3040 | goto out; | 3049 | goto out; |
3050 | } | ||
3041 | 3051 | ||
3042 | page_start = page_offset(page); | 3052 | page_start = page_offset(page); |
3043 | page_end = page_start + PAGE_CACHE_SIZE - 1; | 3053 | page_end = page_start + PAGE_CACHE_SIZE - 1; |
@@ -3070,6 +3080,10 @@ again: | |||
3070 | goto again; | 3080 | goto again; |
3071 | } | 3081 | } |
3072 | 3082 | ||
3083 | clear_extent_bits(&BTRFS_I(inode)->io_tree, page_start, page_end, | ||
3084 | EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING, | ||
3085 | GFP_NOFS); | ||
3086 | |||
3073 | ret = btrfs_set_extent_delalloc(inode, page_start, page_end); | 3087 | ret = btrfs_set_extent_delalloc(inode, page_start, page_end); |
3074 | if (ret) { | 3088 | if (ret) { |
3075 | unlock_extent(io_tree, page_start, page_end, GFP_NOFS); | 3089 | unlock_extent(io_tree, page_start, page_end, GFP_NOFS); |
@@ -3088,6 +3102,9 @@ again: | |||
3088 | unlock_extent(io_tree, page_start, page_end, GFP_NOFS); | 3102 | unlock_extent(io_tree, page_start, page_end, GFP_NOFS); |
3089 | 3103 | ||
3090 | out_unlock: | 3104 | out_unlock: |
3105 | if (ret) | ||
3106 | btrfs_free_reserved_data_space(root, inode, PAGE_CACHE_SIZE); | ||
3107 | btrfs_unreserve_metadata_for_delalloc(root, inode, 1); | ||
3091 | unlock_page(page); | 3108 | unlock_page(page); |
3092 | page_cache_release(page); | 3109 | page_cache_release(page); |
3093 | out: | 3110 | out: |
@@ -3111,7 +3128,9 @@ int btrfs_cont_expand(struct inode *inode, loff_t size) | |||
3111 | if (size <= hole_start) | 3128 | if (size <= hole_start) |
3112 | return 0; | 3129 | return 0; |
3113 | 3130 | ||
3114 | btrfs_truncate_page(inode->i_mapping, inode->i_size); | 3131 | err = btrfs_truncate_page(inode->i_mapping, inode->i_size); |
3132 | if (err) | ||
3133 | return err; | ||
3115 | 3134 | ||
3116 | while (1) { | 3135 | while (1) { |
3117 | struct btrfs_ordered_extent *ordered; | 3136 | struct btrfs_ordered_extent *ordered; |
@@ -5008,7 +5027,9 @@ static void btrfs_truncate(struct inode *inode) | |||
5008 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | 5027 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) |
5009 | return; | 5028 | return; |
5010 | 5029 | ||
5011 | btrfs_truncate_page(inode->i_mapping, inode->i_size); | 5030 | ret = btrfs_truncate_page(inode->i_mapping, inode->i_size); |
5031 | if (ret) | ||
5032 | return; | ||
5012 | btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1); | 5033 | btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1); |
5013 | 5034 | ||
5014 | trans = btrfs_start_transaction(root, 1); | 5035 | trans = btrfs_start_transaction(root, 1); |