diff options
author | Amir Goldstein <amir73il@gmail.com> | 2011-03-20 22:59:02 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2011-03-20 22:59:02 -0400 |
commit | d67d1218344009970ba0deb7eb15a3984518ddd0 (patch) | |
tree | 323e3fb76bdff2481fa35ce3aa82699434dae743 /fs | |
parent | 537a03103c67c4688b1e8e6671ad119aec5e2efb (diff) |
ext4: handle errors in ext4_clear_blocks()
Checking return code from ext4_journal_get_write_access() is important
with snapshots, because this function invokes COW, so may return new
errors, such as ENOSPC.
ext4_clear_blocks() now returns < 0 for fatal errors, in which case,
ext4_free_data() is aborted.
Signed-off-by: Amir Goldstein <amir73il@users.sf.net>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/inode.c | 46 |
1 files changed, 26 insertions, 20 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 67e7a3caf9ed..fc8c0ce84315 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4096,6 +4096,9 @@ no_top: | |||
4096 | * | 4096 | * |
4097 | * We release `count' blocks on disk, but (last - first) may be greater | 4097 | * We release `count' blocks on disk, but (last - first) may be greater |
4098 | * than `count' because there can be holes in there. | 4098 | * than `count' because there can be holes in there. |
4099 | * | ||
4100 | * Return 0 on success, 1 on invalid block range | ||
4101 | * and < 0 on fatal error. | ||
4099 | */ | 4102 | */ |
4100 | static int ext4_clear_blocks(handle_t *handle, struct inode *inode, | 4103 | static int ext4_clear_blocks(handle_t *handle, struct inode *inode, |
4101 | struct buffer_head *bh, | 4104 | struct buffer_head *bh, |
@@ -4122,25 +4125,21 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode, | |||
4122 | if (bh) { | 4125 | if (bh) { |
4123 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); | 4126 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); |
4124 | err = ext4_handle_dirty_metadata(handle, inode, bh); | 4127 | err = ext4_handle_dirty_metadata(handle, inode, bh); |
4125 | if (unlikely(err)) { | 4128 | if (unlikely(err)) |
4126 | ext4_std_error(inode->i_sb, err); | 4129 | goto out_err; |
4127 | return 1; | ||
4128 | } | ||
4129 | } | 4130 | } |
4130 | err = ext4_mark_inode_dirty(handle, inode); | 4131 | err = ext4_mark_inode_dirty(handle, inode); |
4131 | if (unlikely(err)) { | 4132 | if (unlikely(err)) |
4132 | ext4_std_error(inode->i_sb, err); | 4133 | goto out_err; |
4133 | return 1; | ||
4134 | } | ||
4135 | err = ext4_truncate_restart_trans(handle, inode, | 4134 | err = ext4_truncate_restart_trans(handle, inode, |
4136 | blocks_for_truncate(inode)); | 4135 | blocks_for_truncate(inode)); |
4137 | if (unlikely(err)) { | 4136 | if (unlikely(err)) |
4138 | ext4_std_error(inode->i_sb, err); | 4137 | goto out_err; |
4139 | return 1; | ||
4140 | } | ||
4141 | if (bh) { | 4138 | if (bh) { |
4142 | BUFFER_TRACE(bh, "retaking write access"); | 4139 | BUFFER_TRACE(bh, "retaking write access"); |
4143 | ext4_journal_get_write_access(handle, bh); | 4140 | err = ext4_journal_get_write_access(handle, bh); |
4141 | if (unlikely(err)) | ||
4142 | goto out_err; | ||
4144 | } | 4143 | } |
4145 | } | 4144 | } |
4146 | 4145 | ||
@@ -4149,6 +4148,9 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode, | |||
4149 | 4148 | ||
4150 | ext4_free_blocks(handle, inode, NULL, block_to_free, count, flags); | 4149 | ext4_free_blocks(handle, inode, NULL, block_to_free, count, flags); |
4151 | return 0; | 4150 | return 0; |
4151 | out_err: | ||
4152 | ext4_std_error(inode->i_sb, err); | ||
4153 | return err; | ||
4152 | } | 4154 | } |
4153 | 4155 | ||
4154 | /** | 4156 | /** |
@@ -4182,7 +4184,7 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, | |||
4182 | ext4_fsblk_t nr; /* Current block # */ | 4184 | ext4_fsblk_t nr; /* Current block # */ |
4183 | __le32 *p; /* Pointer into inode/ind | 4185 | __le32 *p; /* Pointer into inode/ind |
4184 | for current block */ | 4186 | for current block */ |
4185 | int err; | 4187 | int err = 0; |
4186 | 4188 | ||
4187 | if (this_bh) { /* For indirect block */ | 4189 | if (this_bh) { /* For indirect block */ |
4188 | BUFFER_TRACE(this_bh, "get_write_access"); | 4190 | BUFFER_TRACE(this_bh, "get_write_access"); |
@@ -4204,9 +4206,10 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, | |||
4204 | } else if (nr == block_to_free + count) { | 4206 | } else if (nr == block_to_free + count) { |
4205 | count++; | 4207 | count++; |
4206 | } else { | 4208 | } else { |
4207 | if (ext4_clear_blocks(handle, inode, this_bh, | 4209 | err = ext4_clear_blocks(handle, inode, this_bh, |
4208 | block_to_free, count, | 4210 | block_to_free, count, |
4209 | block_to_free_p, p)) | 4211 | block_to_free_p, p); |
4212 | if (err) | ||
4210 | break; | 4213 | break; |
4211 | block_to_free = nr; | 4214 | block_to_free = nr; |
4212 | block_to_free_p = p; | 4215 | block_to_free_p = p; |
@@ -4215,9 +4218,12 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, | |||
4215 | } | 4218 | } |
4216 | } | 4219 | } |
4217 | 4220 | ||
4218 | if (count > 0) | 4221 | if (!err && count > 0) |
4219 | ext4_clear_blocks(handle, inode, this_bh, block_to_free, | 4222 | err = ext4_clear_blocks(handle, inode, this_bh, block_to_free, |
4220 | count, block_to_free_p, p); | 4223 | count, block_to_free_p, p); |
4224 | if (err < 0) | ||
4225 | /* fatal error */ | ||
4226 | return; | ||
4221 | 4227 | ||
4222 | if (this_bh) { | 4228 | if (this_bh) { |
4223 | BUFFER_TRACE(this_bh, "call ext4_handle_dirty_metadata"); | 4229 | BUFFER_TRACE(this_bh, "call ext4_handle_dirty_metadata"); |