e2fsck: fix conflicting extents|inlinedata inode flags

If we come across an inode with the inline data and extents inode flag
set, try to figure out the correct flag settings from the contents of
i_block and i_size.  If i_blocks looks like an extent tree head, we'll
make it an extent inode; if it's small enough for inline data, set it
to that.  This leaves the weird gray area where there's no extent
tree but it's too big for the inode -- if /could/ be a block map,
change it to that; otherwise, just clear the inode.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
crypto
Darrick J. Wong 2014-08-10 18:42:59 -04:00 committed by Theodore Ts'o
parent 8dae07fb55
commit fa441f91f1
7 changed files with 189 additions and 0 deletions

View File

@ -759,6 +759,108 @@ static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino,
return; \
} while (0)
static int could_be_block_map(ext2_filsys fs, struct ext2_inode *inode)
{
__u32 x;
int i;
for (i = 0; i < EXT2_N_BLOCKS; i++) {
x = inode->i_block[i];
#ifdef WORDS_BIGENDIAN
x = ext2fs_swab32(x);
#endif
if (x >= ext2fs_blocks_count(fs->super))
return 0;
}
return 1;
}
/*
* Figure out what to do with an inode that has both extents and inline data
* inode flags set. Returns -1 if we decide to erase the inode, 0 otherwise.
*/
static int fix_inline_data_extents_file(e2fsck_t ctx,
ext2_ino_t ino,
struct ext2_inode *inode,
int inode_size,
struct problem_context *pctx)
{
size_t max_inline_ea_size;
ext2_filsys fs = ctx->fs;
int dirty = 0;
/* Both feature flags not set? Just run the regular checks */
if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
EXT3_FEATURE_INCOMPAT_EXTENTS) &&
!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
EXT4_FEATURE_INCOMPAT_INLINE_DATA))
return 0;
/* Clear both flags if it's a special file */
if (LINUX_S_ISCHR(inode->i_mode) ||
LINUX_S_ISBLK(inode->i_mode) ||
LINUX_S_ISFIFO(inode->i_mode) ||
LINUX_S_ISSOCK(inode->i_mode)) {
check_extents_inlinedata(ctx, pctx);
return 0;
}
/* If it looks like an extent tree, try to clear inlinedata */
if (ext2fs_extent_header_verify(inode->i_block,
sizeof(inode->i_block)) == 0 &&
fix_problem(ctx, PR_1_CLEAR_INLINE_DATA_FOR_EXTENT, pctx)) {
inode->i_flags &= ~EXT4_INLINE_DATA_FL;
dirty = 1;
goto out;
}
/* If it looks short enough to be inline data, try to clear extents */
if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
max_inline_ea_size = EXT2_INODE_SIZE(fs->super) -
(EXT2_GOOD_OLD_INODE_SIZE +
((struct ext2_inode_large *)inode)->i_extra_isize);
else
max_inline_ea_size = 0;
if (EXT2_I_SIZE(inode) <
EXT4_MIN_INLINE_DATA_SIZE + max_inline_ea_size &&
fix_problem(ctx, PR_1_CLEAR_EXTENT_FOR_INLINE_DATA, pctx)) {
inode->i_flags &= ~EXT4_EXTENTS_FL;
dirty = 1;
goto out;
}
/*
* Too big for inline data, but no evidence of extent tree -
* maybe it's a block map file? If the mappings all look valid?
*/
if (could_be_block_map(fs, inode) &&
fix_problem(ctx, PR_1_CLEAR_EXTENT_INLINE_DATA_FLAGS, pctx)) {
#ifdef WORDS_BIGENDIAN
int i;
for (i = 0; i < EXT2_N_BLOCKS; i++)
inode->i_block[i] = ext2fs_swab32(inode->i_block[i]);
#endif
inode->i_flags &= ~(EXT4_EXTENTS_FL | EXT4_INLINE_DATA_FL);
dirty = 1;
goto out;
}
/* Oh well, just clear the busted inode. */
if (fix_problem(ctx, PR_1_CLEAR_EXTENT_INLINE_DATA_INODE, pctx)) {
e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
return -1;
}
out:
if (dirty)
e2fsck_write_inode(ctx, ino, inode, "pass1");
return 0;
}
void e2fsck_pass1(e2fsck_t ctx)
{
int i;
@ -1007,6 +1109,18 @@ void e2fsck_pass1(e2fsck_t ctx)
}
}
/* Conflicting inlinedata/extents inode flags? */
if ((inode->i_flags & EXT4_INLINE_DATA_FL) &&
(inode->i_flags & EXT4_EXTENTS_FL)) {
int res = fix_inline_data_extents_file(ctx, ino, inode,
inode_size,
&pctx);
if (res < 0) {
/* skip FINISH_INODE_LOOP */
continue;
}
}
/* Test for incorrect inline_data flags settings. */
if ((inode->i_flags & EXT4_INLINE_DATA_FL) && !inlinedata_fs &&
(ino >= EXT2_FIRST_INODE(fs->super))) {

View File

@ -1056,6 +1056,26 @@ static struct e2fsck_problem problem_table[] = {
"or inline-data flag set. "),
PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
/* Inode has extent header but inline data flag is set */
{ PR_1_CLEAR_INLINE_DATA_FOR_EXTENT,
N_("@i %i has @x header but inline data flag is set.\n"),
PROMPT_FIX, 0 },
/* Inode seems to have inline data but extent flag is set */
{ PR_1_CLEAR_EXTENT_FOR_INLINE_DATA,
N_("@i %i seems to have inline data but @x flag is set.\n"),
PROMPT_FIX, 0 },
/* Inode seems to have block map but inline data and extent flags set */
{ PR_1_CLEAR_EXTENT_INLINE_DATA_FLAGS,
N_("@i %i seems to have @b map but inline data and @x flags set.\n"),
PROMPT_FIX, 0 },
/* Inode has inline data and extent flags but i_block contains junk */
{ PR_1_CLEAR_EXTENT_INLINE_DATA_INODE,
N_("@i %i has inline data and @x flags set but i_block contains junk.\n"),
PROMPT_CLEAR_INODE, 0 },
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */

View File

@ -615,6 +615,18 @@ struct problem_context {
/* extents/inlinedata set on fifo/socket/device */
#define PR_1_SPECIAL_EXTENTS_IDATA 0x010076
/* idata/extent flag set and extent header found, clear idata flag */
#define PR_1_CLEAR_INLINE_DATA_FOR_EXTENT 0x010077
/* inlinedata/extent set and no extent header found, clear extent flag */
#define PR_1_CLEAR_EXTENT_FOR_INLINE_DATA 0x010078
/* inlinedata/extent set, clear both flags */
#define PR_1_CLEAR_EXTENT_INLINE_DATA_FLAGS 0x010079
/* inlinedata/extent set, clear inode */
#define PR_1_CLEAR_EXTENT_INLINE_DATA_INODE 0x01007A
/*
* Pass 1b errors
*/

View File

@ -0,0 +1,35 @@
Pass 1: Checking inodes, blocks, and sizes
Special (device/socket/fifo) file (inode 19) has extents
or inline-data flag set. Clear? yes
Inode 20 has extent header but inline data flag is set.
Fix? yes
Inode 21 has inline data and extent flags set but i_block contains junk.
Clear inode? yes
Inode 22 seems to have block map but inline data and extent flags set.
Fix? yes
Inode 23 seems to have inline data but extent flag is set.
Fix? yes
Pass 2: Checking directory structure
Entry 'garbage' in /bad (18) has deleted/unused inode 21. Clear? yes
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
Inode bitmap differences: -21
Fix? yes
Free inodes count wrong for group #0 (105, counted=106).
Fix? yes
Free inodes count wrong (105, counted=106).
Fix? yes
test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
test_filesys: 22/128 files (0.0% non-contiguous), 21/512 blocks
Exit status is 1

View File

@ -0,0 +1,7 @@
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
test_filesys: 22/128 files (0.0% non-contiguous), 21/512 blocks
Exit status is 0

Binary file not shown.

View File

@ -0,0 +1 @@
conflicting extents and inline_data inode flags