diff options
author | Xue jiufei <xuejiufei@huawei.com> | 2015-09-04 18:44:48 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-04 19:54:41 -0400 |
commit | d0c97d52f5e1de125394d748be7bd5763fd9ed9e (patch) | |
tree | 67497e2cedcce1eaf6aef906843d401eca7f2d14 /fs/ocfs2/alloc.c | |
parent | 7f27ec978b0ef37391262bbf15c587fd8526e268 (diff) |
ocfs2: do not set fs read-only if rec[0] is empty while committing truncate
While appending an extent to a file, it will call these functions:
ocfs2_insert_extent
-> call ocfs2_grow_tree() if there's no free rec
-> ocfs2_add_branch add a new branch to extent tree,
now rec[0] in the leaf of rightmost path is empty
-> ocfs2_do_insert_extent
-> ocfs2_rotate_tree_right
-> ocfs2_extend_rotate_transaction
-> jbd2_journal_restart if jbd2_journal_extend fail
-> ocfs2_insert_path
-> ocfs2_extend_trans
-> jbd2_journal_restart if jbd2_journal_extend fail
-> ocfs2_insert_at_leaf
-> ocfs2_et_update_clusters
Function jbd2_journal_restart() may be called and it may happened that
buffers dirtied in ocfs2_add_branch() are committed
while buffers dirtied in ocfs2_insert_at_leaf() and
ocfs2_et_update_clusters() are not.
So an empty rec[0] is left in rightmost path which will cause
read-only filesystem when call ocfs2_commit_truncate()
with the error message: "Inode %lu has an empty extent record".
This is not a serious problem, so remove the rightmost path when call
ocfs2_commit_truncate().
Signed-off-by: joyce.xue <xuejiufei@huawei.com>
Reviewed-by: Mark Fasheh <mfasheh@suse.de>
Cc: Joel Becker <jlbec@evilplan.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ocfs2/alloc.c')
-rw-r--r-- | fs/ocfs2/alloc.c | 44 |
1 files changed, 38 insertions, 6 deletions
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 9a0fd494fe74..77cbd1e3c950 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c | |||
@@ -3131,6 +3131,30 @@ out: | |||
3131 | return ret; | 3131 | return ret; |
3132 | } | 3132 | } |
3133 | 3133 | ||
3134 | static int ocfs2_remove_rightmost_empty_extent(struct ocfs2_super *osb, | ||
3135 | struct ocfs2_extent_tree *et, | ||
3136 | struct ocfs2_path *path, | ||
3137 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
3138 | { | ||
3139 | handle_t *handle; | ||
3140 | int ret; | ||
3141 | int credits = path->p_tree_depth * 2 + 1; | ||
3142 | |||
3143 | handle = ocfs2_start_trans(osb, credits); | ||
3144 | if (IS_ERR(handle)) { | ||
3145 | ret = PTR_ERR(handle); | ||
3146 | mlog_errno(ret); | ||
3147 | return ret; | ||
3148 | } | ||
3149 | |||
3150 | ret = ocfs2_remove_rightmost_path(handle, et, path, dealloc); | ||
3151 | if (ret) | ||
3152 | mlog_errno(ret); | ||
3153 | |||
3154 | ocfs2_commit_trans(osb, handle); | ||
3155 | return ret; | ||
3156 | } | ||
3157 | |||
3134 | /* | 3158 | /* |
3135 | * Left rotation of btree records. | 3159 | * Left rotation of btree records. |
3136 | * | 3160 | * |
@@ -7108,15 +7132,23 @@ start: | |||
7108 | * to check it up here before changing the tree. | 7132 | * to check it up here before changing the tree. |
7109 | */ | 7133 | */ |
7110 | if (root_el->l_tree_depth && rec->e_int_clusters == 0) { | 7134 | if (root_el->l_tree_depth && rec->e_int_clusters == 0) { |
7111 | ocfs2_error(inode->i_sb, "Inode %lu has an empty " | 7135 | mlog(ML_ERROR, "Inode %lu has an empty " |
7112 | "extent record, depth %u\n", inode->i_ino, | 7136 | "extent record, depth %u\n", inode->i_ino, |
7113 | le16_to_cpu(root_el->l_tree_depth)); | 7137 | le16_to_cpu(root_el->l_tree_depth)); |
7114 | status = -EROFS; | 7138 | status = ocfs2_remove_rightmost_empty_extent(osb, |
7115 | goto bail; | 7139 | &et, path, &dealloc); |
7140 | if (status) { | ||
7141 | mlog_errno(status); | ||
7142 | goto bail; | ||
7143 | } | ||
7144 | |||
7145 | ocfs2_reinit_path(path, 1); | ||
7146 | goto start; | ||
7147 | } else { | ||
7148 | trunc_cpos = le32_to_cpu(rec->e_cpos); | ||
7149 | trunc_len = 0; | ||
7150 | blkno = 0; | ||
7116 | } | 7151 | } |
7117 | trunc_cpos = le32_to_cpu(rec->e_cpos); | ||
7118 | trunc_len = 0; | ||
7119 | blkno = 0; | ||
7120 | } else if (le32_to_cpu(rec->e_cpos) >= new_highest_cpos) { | 7152 | } else if (le32_to_cpu(rec->e_cpos) >= new_highest_cpos) { |
7121 | /* | 7153 | /* |
7122 | * Truncate entire record. | 7154 | * Truncate entire record. |