mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2024-11-30 13:33:33 +08:00
e2fsck: add replay for add_range, del_range, and inode tags
Add replay for inode's extent trees and inode itself. Signed-off-by: Harshad Shirwadkar <harshadshirwadkar@gmail.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
63b7192cae
commit
f5de3d7c1b
348
e2fsck/journal.c
348
e2fsck/journal.c
@ -394,6 +394,217 @@ static int __errcode_to_errno(errcode_t err, const char *func, int line)
|
||||
|
||||
#define errcode_to_errno(err) __errcode_to_errno(err, __func__, __LINE__)
|
||||
|
||||
#define ex_end(__ex) ((__ex)->e_lblk + (__ex)->e_len - 1)
|
||||
#define ex_pend(__ex) ((__ex)->e_pblk + (__ex)->e_len - 1)
|
||||
|
||||
static int make_room(struct extent_list *list, int i)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (list->count == list->size) {
|
||||
unsigned int new_size = (list->size + 341) *
|
||||
sizeof(struct ext2fs_extent);
|
||||
ret = errcode_to_errno(ext2fs_resize_mem(0, new_size, &list->extents));
|
||||
if (ret)
|
||||
return ret;
|
||||
list->size += 341;
|
||||
}
|
||||
|
||||
memmove(&list->extents[i + 1], &list->extents[i],
|
||||
sizeof(list->extents[0]) * (list->count - i));
|
||||
list->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ex_compar(const void *arg1, const void *arg2)
|
||||
{
|
||||
struct ext2fs_extent *ex1 = (struct ext2fs_extent *)arg1;
|
||||
struct ext2fs_extent *ex2 = (struct ext2fs_extent *)arg2;
|
||||
|
||||
if (ex1->e_lblk < ex2->e_lblk)
|
||||
return -1;
|
||||
if (ex1->e_lblk > ex2->e_lblk)
|
||||
return 1;
|
||||
return ex1->e_len - ex2->e_len;
|
||||
}
|
||||
|
||||
static int ex_len_compar(const void *arg1, const void *arg2)
|
||||
{
|
||||
struct ext2fs_extent *ex1 = (struct ext2fs_extent *)arg1;
|
||||
struct ext2fs_extent *ex2 = (struct ext2fs_extent *)arg2;
|
||||
|
||||
if (ex1->e_len < ex2->e_len)
|
||||
return 1;
|
||||
|
||||
if (ex1->e_lblk > ex2->e_lblk)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ex_sort_and_merge(e2fsck_t ctx, struct extent_list *list)
|
||||
{
|
||||
blk64_t ex_end;
|
||||
int i, j;
|
||||
|
||||
if (list->count < 2)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Reverse sort by length, that way we strip off all the 0 length
|
||||
* extents
|
||||
*/
|
||||
qsort(list->extents, list->count, sizeof(struct ext2fs_extent),
|
||||
ex_len_compar);
|
||||
|
||||
for (i = 0; i < list->count; i++) {
|
||||
if (list->extents[i].e_len == 0) {
|
||||
list->count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now sort by logical offset */
|
||||
qsort(list->extents, list->count, sizeof(list->extents[0]),
|
||||
ex_compar);
|
||||
|
||||
/* Merge adjacent extents if they are logically and physically contiguous */
|
||||
i = 0;
|
||||
while (i < list->count - 1) {
|
||||
if (ex_end(&list->extents[i]) + 1 != list->extents[i + 1].e_lblk ||
|
||||
ex_pend(&list->extents[i]) + 1 != list->extents[i + 1].e_pblk ||
|
||||
(list->extents[i].e_flags & EXT2_EXTENT_FLAGS_UNINIT) !=
|
||||
(list->extents[i + 1].e_flags & EXT2_EXTENT_FLAGS_UNINIT)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
list->extents[i].e_len += list->extents[i + 1].e_len;
|
||||
for (j = i + 1; j < list->count - 1; j++)
|
||||
list->extents[j] = list->extents[j + 1];
|
||||
list->count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* must free blocks that are released */
|
||||
static int ext4_modify_extent_list(e2fsck_t ctx, struct extent_list *list,
|
||||
struct ext2fs_extent *ex, int del)
|
||||
{
|
||||
int ret;
|
||||
int i, offset;
|
||||
struct ext2fs_extent add_ex = *ex, add_ex2;
|
||||
|
||||
/* First let's create a hole from ex->e_lblk of length ex->e_len */
|
||||
for (i = 0; i < list->count; i++) {
|
||||
if (ex_end(&list->extents[i]) < add_ex.e_lblk)
|
||||
continue;
|
||||
|
||||
/* Case 1: No overlap */
|
||||
if (list->extents[i].e_lblk > ex_end(&add_ex))
|
||||
break;
|
||||
/*
|
||||
* Unmark all the blocks in bb now. All the blocks get marked
|
||||
* before we exit this function.
|
||||
*/
|
||||
ext2fs_unmark_block_bitmap_range2(ctx->fs->block_map,
|
||||
list->extents[i].e_pblk, list->extents[i].e_len);
|
||||
/* Case 2: Split */
|
||||
if (list->extents[i].e_lblk < add_ex.e_lblk &&
|
||||
ex_end(&list->extents[i]) > ex_end(&add_ex)) {
|
||||
ret = make_room(list, i + 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
list->extents[i + 1] = list->extents[i];
|
||||
offset = ex_end(&add_ex) + 1 - list->extents[i].e_lblk;
|
||||
list->extents[i + 1].e_lblk += offset;
|
||||
list->extents[i + 1].e_pblk += offset;
|
||||
list->extents[i + 1].e_len -= offset;
|
||||
list->extents[i].e_len =
|
||||
add_ex.e_lblk - list->extents[i].e_lblk;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Case 3: Exact overlap */
|
||||
if (add_ex.e_lblk <= list->extents[i].e_lblk &&
|
||||
ex_end(&list->extents[i]) <= ex_end(&add_ex)) {
|
||||
|
||||
list->extents[i].e_len = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Case 4: Partial overlap */
|
||||
if (ex_end(&list->extents[i]) > ex_end(&add_ex)) {
|
||||
offset = ex_end(&add_ex) + 1 - list->extents[i].e_lblk;
|
||||
list->extents[i].e_lblk += offset;
|
||||
list->extents[i].e_pblk += offset;
|
||||
list->extents[i].e_len -= offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ex_end(&add_ex) >= ex_end(&list->extents[i]))
|
||||
list->extents[i].e_len =
|
||||
add_ex.e_lblk > list->extents[i].e_lblk ?
|
||||
add_ex.e_lblk - list->extents[i].e_lblk : 0;
|
||||
}
|
||||
|
||||
if (add_ex.e_len && !del) {
|
||||
make_room(list, list->count);
|
||||
list->extents[list->count - 1] = add_ex;
|
||||
}
|
||||
|
||||
ex_sort_and_merge(ctx, list);
|
||||
|
||||
/* Mark all occupied blocks allocated */
|
||||
for (i = 0; i < list->count; i++)
|
||||
ext2fs_mark_block_bitmap_range2(ctx->fs->block_map,
|
||||
list->extents[i].e_pblk, list->extents[i].e_len);
|
||||
ext2fs_mark_bb_dirty(ctx->fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext4_add_extent_to_list(e2fsck_t ctx, struct extent_list *list,
|
||||
struct ext2fs_extent *ex)
|
||||
{
|
||||
return ext4_modify_extent_list(ctx, list, ex, 0 /* add */);
|
||||
}
|
||||
|
||||
static int ext4_del_extent_from_list(e2fsck_t ctx, struct extent_list *list,
|
||||
struct ext2fs_extent *ex)
|
||||
{
|
||||
return ext4_modify_extent_list(ctx, list, ex, 1 /* delete */);
|
||||
}
|
||||
|
||||
static int ext4_fc_read_extents(e2fsck_t ctx, int ino)
|
||||
{
|
||||
struct extent_list *extent_list = &ctx->fc_replay_state.fc_extent_list;
|
||||
|
||||
if (extent_list->ino == ino)
|
||||
return 0;
|
||||
|
||||
extent_list->ino = ino;
|
||||
return errcode_to_errno(e2fsck_read_extents(ctx, extent_list));
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush extents in replay state on disk. @ino is the inode that is going
|
||||
* to be processed next. So, we hold back flushing of the extent list
|
||||
* if the next inode that's going to be processed is same as the one with
|
||||
* cached extents in our replay state. That allows us to gather multiple extents
|
||||
* for the inode so that we can flush all of them at once and it also saves us
|
||||
* from continuously growing and shrinking the extent tree.
|
||||
*/
|
||||
static void ext4_fc_flush_extents(e2fsck_t ctx, int ino)
|
||||
{
|
||||
struct extent_list *extent_list = &ctx->fc_replay_state.fc_extent_list;
|
||||
|
||||
if (extent_list->ino == ino || extent_list->ino == 0)
|
||||
return;
|
||||
e2fsck_rewrite_extent_tree(ctx, extent_list);
|
||||
ext2fs_free_mem(&extent_list->extents);
|
||||
memset(extent_list, 0, sizeof(*extent_list));
|
||||
}
|
||||
|
||||
/* Helper struct for dentry replay routines */
|
||||
struct dentry_info_args {
|
||||
int parent_ino, dname_len, ino, inode_len;
|
||||
@ -431,6 +642,7 @@ static int ext4_fc_handle_unlink(e2fsck_t ctx, struct ext4_fc_tl *tl)
|
||||
int ret;
|
||||
|
||||
tl_to_darg(&darg, tl);
|
||||
ext4_fc_flush_extents(ctx, darg.ino);
|
||||
ret = errcode_to_errno(
|
||||
ext2fs_unlink(ctx->fs, darg.parent_ino,
|
||||
darg.dname, darg.ino, 0));
|
||||
@ -447,6 +659,7 @@ static int ext4_fc_handle_link_and_create(e2fsck_t ctx, struct ext4_fc_tl *tl)
|
||||
int ret, filetype, mode;
|
||||
|
||||
tl_to_darg(&darg, tl);
|
||||
ext4_fc_flush_extents(ctx, 0);
|
||||
ret = errcode_to_errno(ext2fs_read_inode(fs, darg.ino,
|
||||
(struct ext2_inode *)&inode_large));
|
||||
if (ret)
|
||||
@ -488,6 +701,132 @@ out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* This function fixes the i_blocks field in the replayed indoe */
|
||||
static void ext4_fc_replay_fixup_iblocks(struct ext2_inode_large *ondisk_inode,
|
||||
struct ext2_inode_large *fc_inode)
|
||||
{
|
||||
if (le32_to_cpu(ondisk_inode->i_flags) & EXT4_EXTENTS_FL) {
|
||||
struct ext3_extent_header *eh;
|
||||
|
||||
eh = (struct ext3_extent_header *)(&ondisk_inode->i_block[0]);
|
||||
if (eh->eh_magic != EXT3_EXT_MAGIC) {
|
||||
memset(eh, 0, sizeof(*eh));
|
||||
eh->eh_magic = EXT3_EXT_MAGIC;
|
||||
eh->eh_max = cpu_to_le16(
|
||||
(sizeof(ondisk_inode->i_block) -
|
||||
sizeof(struct ext3_extent_header)) /
|
||||
sizeof(struct ext3_extent));
|
||||
}
|
||||
} else if (le32_to_cpu(ondisk_inode->i_flags) & EXT4_INLINE_DATA_FL) {
|
||||
memcpy(ondisk_inode->i_block, fc_inode->i_block,
|
||||
sizeof(fc_inode->i_block));
|
||||
}
|
||||
}
|
||||
|
||||
static int ext4_fc_handle_inode(e2fsck_t ctx, struct ext4_fc_tl *tl)
|
||||
{
|
||||
struct e2fsck_fc_replay_state *state = &ctx->fc_replay_state;
|
||||
int ino, inode_len = EXT2_GOOD_OLD_INODE_SIZE;
|
||||
struct ext2_inode_large *inode = NULL;
|
||||
struct ext4_fc_inode *fc_inode;
|
||||
errcode_t err;
|
||||
blk64_t blks;
|
||||
|
||||
fc_inode = (struct ext4_fc_inode *)ext4_fc_tag_val(tl);
|
||||
ino = le32_to_cpu(fc_inode->fc_ino);
|
||||
|
||||
if (EXT2_INODE_SIZE(ctx->fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
|
||||
inode_len += ext2fs_le16_to_cpu(
|
||||
((struct ext2_inode_large *)fc_inode->fc_raw_inode)
|
||||
->i_extra_isize);
|
||||
err = ext2fs_get_mem(inode_len, &inode);
|
||||
if (err)
|
||||
return errcode_to_errno(err);
|
||||
ext4_fc_flush_extents(ctx, ino);
|
||||
|
||||
err = ext2fs_read_inode_full(ctx->fs, ino, (struct ext2_inode *)inode,
|
||||
inode_len);
|
||||
if (err)
|
||||
goto out;
|
||||
memcpy(inode, fc_inode->fc_raw_inode,
|
||||
offsetof(struct ext2_inode_large, i_block));
|
||||
memcpy(&inode->i_generation,
|
||||
&((struct ext2_inode_large *)(fc_inode->fc_raw_inode))->i_generation,
|
||||
inode_len - offsetof(struct ext2_inode_large, i_generation));
|
||||
ext4_fc_replay_fixup_iblocks(inode,
|
||||
(struct ext2_inode_large *)fc_inode->fc_raw_inode);
|
||||
err = ext2fs_count_blocks(ctx->fs, ino, EXT2_INODE(inode), &blks);
|
||||
if (err)
|
||||
goto out;
|
||||
ext2fs_iblk_set(ctx->fs, EXT2_INODE(inode), blks);
|
||||
ext2fs_inode_csum_set(ctx->fs, ino, inode);
|
||||
|
||||
err = ext2fs_write_inode_full(ctx->fs, ino, (struct ext2_inode *)inode,
|
||||
inode_len);
|
||||
if (err)
|
||||
goto out;
|
||||
if (inode->i_links_count)
|
||||
ext2fs_mark_inode_bitmap2(ctx->fs->inode_map, ino);
|
||||
else
|
||||
ext2fs_unmark_inode_bitmap2(ctx->fs->inode_map, ino);
|
||||
ext2fs_mark_ib_dirty(ctx->fs);
|
||||
|
||||
out:
|
||||
ext2fs_free_mem(&inode);
|
||||
return errcode_to_errno(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle add extent replay tag.
|
||||
*/
|
||||
static int ext4_fc_handle_add_extent(e2fsck_t ctx, struct ext4_fc_tl *tl)
|
||||
{
|
||||
struct ext2fs_extent extent;
|
||||
struct ext4_fc_add_range *add_range;
|
||||
struct ext4_fc_del_range *del_range;
|
||||
int ret = 0, ino;
|
||||
|
||||
add_range = (struct ext4_fc_add_range *)ext4_fc_tag_val(tl);
|
||||
ino = le32_to_cpu(add_range->fc_ino);
|
||||
ext4_fc_flush_extents(ctx, ino);
|
||||
|
||||
ret = ext4_fc_read_extents(ctx, ino);
|
||||
if (ret)
|
||||
return ret;
|
||||
memset(&extent, 0, sizeof(extent));
|
||||
ret = errcode_to_errno(ext2fs_decode_extent(
|
||||
&extent, (void *)(add_range->fc_ex),
|
||||
sizeof(add_range->fc_ex)));
|
||||
if (ret)
|
||||
return ret;
|
||||
return ext4_add_extent_to_list(ctx,
|
||||
&ctx->fc_replay_state.fc_extent_list, &extent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle delete logical range replay tag.
|
||||
*/
|
||||
static int ext4_fc_handle_del_range(e2fsck_t ctx, struct ext4_fc_tl *tl)
|
||||
{
|
||||
struct ext2fs_extent extent;
|
||||
struct ext4_fc_del_range *del_range;
|
||||
int ret, ino;
|
||||
|
||||
del_range = (struct ext4_fc_del_range *)ext4_fc_tag_val(tl);
|
||||
ino = le32_to_cpu(del_range->fc_ino);
|
||||
ext4_fc_flush_extents(ctx, ino);
|
||||
|
||||
memset(&extent, 0, sizeof(extent));
|
||||
extent.e_lblk = ext2fs_le32_to_cpu(del_range->fc_lblk);
|
||||
extent.e_len = ext2fs_le16_to_cpu(del_range->fc_len);
|
||||
ret = ext4_fc_read_extents(ctx, ino);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ext4_del_extent_from_list(ctx,
|
||||
&ctx->fc_replay_state.fc_extent_list, &extent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main recovery path entry point. This function returns JBD2_FC_REPLAY_CONTINUE
|
||||
* to indicate that it is expecting more fast commit blocks. It returns
|
||||
@ -515,7 +854,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
|
||||
state->fc_current_pass = pass;
|
||||
/* We will reset checksums */
|
||||
ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
|
||||
ret = ext2fs_read_bitmaps(ctx->fs);
|
||||
ret = errcode_to_errno(ext2fs_read_bitmaps(ctx->fs));
|
||||
if (ret) {
|
||||
jbd_debug(1, "Error %d while reading bitmaps\n", ret);
|
||||
return ret;
|
||||
@ -551,9 +890,16 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
|
||||
ret = ext4_fc_handle_unlink(ctx, tl);
|
||||
break;
|
||||
case EXT4_FC_TAG_ADD_RANGE:
|
||||
ret = ext4_fc_handle_add_extent(ctx, tl);
|
||||
break;
|
||||
case EXT4_FC_TAG_DEL_RANGE:
|
||||
ret = ext4_fc_handle_del_range(ctx, tl);
|
||||
break;
|
||||
case EXT4_FC_TAG_INODE:
|
||||
ret = ext4_fc_handle_inode(ctx, tl);
|
||||
break;
|
||||
case EXT4_FC_TAG_TAIL:
|
||||
ext4_fc_flush_extents(ctx, 0);
|
||||
case EXT4_FC_TAG_PAD:
|
||||
case EXT4_FC_TAG_HEAD:
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user