mirror of https://github.com/vitalif/e2fsprogs
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
parent
8dae07fb55
commit
fa441f91f1
114
e2fsck/pass1.c
114
e2fsck/pass1.c
|
@ -759,6 +759,108 @@ static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino,
|
||||||
return; \
|
return; \
|
||||||
} while (0)
|
} 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)
|
void e2fsck_pass1(e2fsck_t ctx)
|
||||||
{
|
{
|
||||||
int i;
|
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. */
|
/* Test for incorrect inline_data flags settings. */
|
||||||
if ((inode->i_flags & EXT4_INLINE_DATA_FL) && !inlinedata_fs &&
|
if ((inode->i_flags & EXT4_INLINE_DATA_FL) && !inlinedata_fs &&
|
||||||
(ino >= EXT2_FIRST_INODE(fs->super))) {
|
(ino >= EXT2_FIRST_INODE(fs->super))) {
|
||||||
|
|
|
@ -1056,6 +1056,26 @@ static struct e2fsck_problem problem_table[] = {
|
||||||
"or inline-data flag set. "),
|
"or inline-data flag set. "),
|
||||||
PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
|
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 errors */
|
||||||
|
|
||||||
/* Pass 1B: Rescan for duplicate/bad blocks */
|
/* Pass 1B: Rescan for duplicate/bad blocks */
|
||||||
|
|
|
@ -615,6 +615,18 @@ struct problem_context {
|
||||||
/* extents/inlinedata set on fifo/socket/device */
|
/* extents/inlinedata set on fifo/socket/device */
|
||||||
#define PR_1_SPECIAL_EXTENTS_IDATA 0x010076
|
#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
|
* Pass 1b errors
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
|
@ -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.
|
@ -0,0 +1 @@
|
||||||
|
conflicting extents and inline_data inode flags
|
Loading…
Reference in New Issue