ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */
ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */
ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
+ ext2fs_inode_bitmap inode_ea_map; /* EA inodes which are non-orphan */
ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
* - A bitmap of which blocks are in use. (block_found_map)
* - A bitmap of which blocks are in use by two inodes (block_dup_map)
* - The data blocks of the directory inodes. (dir_map)
+ * - A bitmap of EA inodes. (inode_ea_map)
*
* Pass 1 is designed to stash away enough information so that the
* other passes should not need to read in the inode information
e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
}
+static void mark_inode_ea_map(e2fsck_t ctx, struct problem_context *pctx,
+ ext2_ino_t ino)
+{
+ if (!ctx->inode_ea_map) {
+ pctx->errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
+ _("EA inode map"),
+ &ctx->inode_ea_map);
+ if (pctx->errcode) {
+ fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR,
+ pctx);
+ exit(1);
+ }
+ }
+
+ ext2fs_mark_inode_bitmap2(ctx->inode_ea_map, ino);
+}
+
+/*
+ * Check validity of EA inode. Return 0 if EA inode is valid, otherwise return
+ * the problem code.
+ */
+static problem_t check_large_ea_inode(e2fsck_t ctx,
+ struct ext2_ext_attr_entry *entry,
+ struct problem_context *pctx)
+{
+ struct ext2_inode inode;
+
+ /* Check if inode is within valid range */
+ if ((entry->e_value_inum < EXT2_FIRST_INODE(ctx->fs->super)) ||
+ (entry->e_value_inum > ctx->fs->super->s_inodes_count))
+ return PR_1_ATTR_VALUE_EA_INODE;
+
+ e2fsck_read_inode(ctx, entry->e_value_inum, &inode, "pass1");
+ if (!(inode.i_flags & EXT4_EA_INODE_FL)) {
+ /* If EXT4_EA_INODE_FL flag is not present but back-pointer
+ * matches then we should set this flag */
+ if (inode.i_mtime == pctx->ino &&
+ inode.i_generation == pctx->inode->i_generation &&
+ fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) {
+ inode.i_flags |= EXT4_EA_INODE_FL;
+ ext2fs_write_inode(ctx->fs, entry->e_value_inum,&inode);
+ } else
+ return PR_1_ATTR_NO_EA_INODE_FL;
+ } else if (inode.i_mtime != pctx->ino ||
+ inode.i_generation != pctx->inode->i_generation)
+ return PR_1_ATTR_INVAL_EA_INODE;
+
+ return 0;
+}
+
static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
{
struct ext2_super_block *sb = ctx->fs->super;
/* attribute len eats this space */
remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
- /* check value size */
- if (entry->e_value_size > remain) {
- pctx->num = entry->e_value_size;
- problem = PR_1_ATTR_VALUE_SIZE;
- goto fix;
- }
+ if (entry->e_value_inum == 0) {
+ /* check value size */
+ if (entry->e_value_size > remain) {
+ pctx->num = entry->e_value_size;
+ problem = PR_1_ATTR_VALUE_SIZE;
+ goto fix;
+ }
- /* e_value_block must be 0 in inode's ea */
- if (entry->e_value_block != 0) {
- pctx->num = entry->e_value_block;
- problem = PR_1_ATTR_VALUE_BLOCK;
- goto fix;
- }
+ if (entry->e_value_size &&
+ region_allocate(region,
+ sizeof(__u32) + entry->e_value_offs,
+ EXT2_EXT_ATTR_SIZE(
+ entry->e_value_size))) {
+ problem = PR_1_INODE_EA_ALLOC_COLLISION;
+ goto fix;
+ }
+ } else {
+ problem = check_large_ea_inode(ctx, entry, pctx);
+ if (problem != 0)
+ goto fix;
- if (entry->e_value_size &&
- region_allocate(region, sizeof(__u32) + entry->e_value_offs,
- EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
- problem = PR_1_INODE_EA_ALLOC_COLLISION;
- goto fix;
+ mark_inode_ea_map(ctx, pctx, entry->e_value_inum);
}
hash = ext2fs_ext_attr_hash_entry(entry,
goto fix;
}
- remain -= entry->e_value_size;
+ /* If EA value is stored in external inode then it does not
+ * consume space here */
+ if (entry->e_value_inum == 0)
+ remain -= entry->e_value_size;
entry = EXT2_EXT_ATTR_NEXT(entry);
}
goto clear_extattr;
break;
}
- if (entry->e_value_block != 0) {
- if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
- goto clear_extattr;
- }
- if (entry->e_value_offs + entry->e_value_size > fs->blocksize) {
- if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
- goto clear_extattr;
- break;
- }
- if (entry->e_value_size &&
- region_allocate(region, entry->e_value_offs,
- EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
- if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+ if (entry->e_value_inum == 0) {
+ if (entry->e_value_offs + entry->e_value_size >
+ fs->blocksize) {
+ if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
+ goto clear_extattr;
+ break;
+ }
+ if (entry->e_value_size &&
+ region_allocate(region, entry->e_value_offs,
+ EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
+ if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION,
+ pctx))
+ goto clear_extattr;
+ }
+ } else {
+ problem_t problem;
+
+ problem = check_large_ea_inode(ctx, entry, pctx);
+ if (problem == 0)
+ mark_inode_ea_map(ctx, pctx,
+ entry->e_value_inum);
+ else if (fix_problem(ctx, problem, pctx))
goto clear_extattr;
}
* Pass 4 frees the following data structures:
* - A bitmap of which inodes are in bad blocks. (inode_bb_map)
* - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
+ * - A bitmap of EA inodes. (inode_ea_map)
*/
#include "config.h"
"pass4: disconnect_inode");
if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
extra_size = inode->i_extra_isize;
+
+ if (inode->i_flags & EXT4_EA_INODE_FL) {
+ if (ext2fs_test_inode_bitmap2(ctx->inode_ea_map, i)) {
+ ext2fs_icount_store(ctx->inode_count, i, 1);
+ return 0;
+ } else {
+ /* Zero the link count so that when inode is linked to
+ * lost+found it has correct link count */
+ inode->i_links_count = 0;
+ e2fsck_write_inode(ctx, i, EXT2_INODE(inode),
+ "disconnect_inode");
+ ext2fs_icount_store(ctx->inode_link_info, i, 0);
+ }
+ }
+
clear_problem_context(&pctx);
pctx.ino = i;
pctx.inode = EXT2_INODE(inode);
}
ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_ea_map);
+ ctx->inode_ea_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_bb_map);
ctx->inode_bb_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
N_("Timestamp(s) on @i %i beyond 2310-04-04 are likely pre-1970.\n"),
PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
+ /* Inode has illegal extended attribute value inode */
+ { PR_1_ATTR_VALUE_EA_INODE,
+ N_("@i %i has @I @a value @i %N.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Invalid backpointer from extended attribute inode to parent inode */
+ { PR_1_ATTR_INVAL_EA_INODE,
+ N_("@n backpointer from @a @i %N to parent @i %i.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Inode has invalid extended attribute. EA inode missing
+ * EA_INODE flag. */
+ { PR_1_ATTR_NO_EA_INODE_FL,
+ N_("@i %i has @n @a. EA @i %N missing EA_INODE flag.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* EA inode for parent inode missing EA_INODE flag. */
+ { PR_1_ATTR_SET_EA_INODE_FL,
+ N_("EA @i %N for parent @i %i missing EA_INODE flag.\n "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
/* Timestamp(s) on inode beyond 2310-04-04 are likely pre-1970. */
#define PR_1_EA_TIME_OUT_OF_RANGE 0x010082
+/* Inode has illegal EA value inode */
+#define PR_1_ATTR_VALUE_EA_INODE 0x010083
+
+/* Invalid backpointer from EA inode to parent inode */
+#define PR_1_ATTR_INVAL_EA_INODE 0x010084
+
+/* Parent inode has invalid EA entry. EA inode does not have
+ * EXT4_EA_INODE_FL flag. Delete EA entry? */
+#define PR_1_ATTR_NO_EA_INODE_FL 0x010085
+
+/* EA inode for parent inode does not have EXT4_EA_INODE_FL flag */
+#define PR_1_ATTR_SET_EA_INODE_FL 0x010086
+
+
/*
* Pass 1b errors
*/
__u8 e_name_len; /* length of name */
__u8 e_name_index; /* attribute name index */
__u16 e_value_offs; /* offset in disk block of value */
- __u32 e_value_block; /* disk block attribute is stored on (n/i) */
+ __u32 e_value_inum; /* inode in which the value is stored */
__u32 e_value_size; /* size of attribute value */
__u32 e_hash; /* hash value of name and value */
#if 0
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
EXT4_FEATURE_INCOMPAT_MMP| \
- EXT4_FEATURE_INCOMPAT_LARGEDIR)
+ EXT4_FEATURE_INCOMPAT_LARGEDIR| \
+ EXT4_FEATURE_INCOMPAT_EA_INODE)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
EXT3_FEATURE_INCOMPAT_RECOVER|\
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
+ EXT4_FEATURE_INCOMPAT_EA_INODE|\
EXT4_LIB_INCOMPAT_MMP|\
EXT4_FEATURE_INCOMPAT_64BIT|\
EXT4_FEATURE_INCOMPAT_INLINE_DATA|\
}
/* The hash needs to be calculated on the data in little-endian. */
- if (entry->e_value_block == 0 && entry->e_value_size != 0) {
+ if (entry->e_value_inum == 0 && entry->e_value_size != 0) {
__u32 *value = (__u32 *)data;
for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
EXT2_EXT_ATTR_PAD_BITS; n; n--) {
e->e_name_index = (ret ? idx : 0);
e->e_value_offs = end - value_size - (char *)entries_start +
value_offset_correction;
- e->e_value_block = 0;
+ e->e_value_inum = 0;
e->e_value_size = x->value_len;
/* Store name and value */
remain = storage_size;
while (remain >= sizeof(struct ext2_ext_attr_entry) &&
!EXT2_EXT_IS_LAST_ENTRY(entry)) {
- __u32 hash;
-
- /* header eats this space */
- remain -= sizeof(struct ext2_ext_attr_entry);
-
- /* attribute len eats this space */
- remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
-
- /* check value size */
- if (entry->e_value_size > remain)
- return EXT2_ET_EA_BAD_VALUE_SIZE;
-
- if (entry->e_value_offs + entry->e_value_size > values_size)
- return EXT2_ET_EA_BAD_VALUE_OFFSET;
-
- if (entry->e_value_size > 0 &&
- value_start + entry->e_value_offs <
- (char *)end + sizeof(__u32))
- return EXT2_ET_EA_BAD_VALUE_OFFSET;
-
- /* e_value_block must be 0 in inode's ea */
- if (entry->e_value_block != 0)
- return EXT2_ET_BAD_EA_BLOCK_NUM;
-
- hash = ext2fs_ext_attr_hash_entry(entry, value_start +
- entry->e_value_offs);
-
- /* e_hash may be 0 in older inode's ea */
- if (entry->e_hash != 0 && entry->e_hash != hash)
- return EXT2_ET_BAD_EA_HASH;
-
- remain -= entry->e_value_size;
/* Allocate space for more attrs? */
if (x == handle->attrs + handle->length) {
x = handle->attrs + handle->length - 4;
}
- /* Extract name/value */
+ /* header eats this space */
+ remain -= sizeof(struct ext2_ext_attr_entry);
+
+ /* attribute len eats this space */
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
+
+ /* Extract name */
prefix = find_ea_prefix(entry->e_name_index);
prefix_len = (prefix ? strlen(prefix) : 0);
err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
(char *)entry + sizeof(*entry),
entry->e_name_len);
- err = ext2fs_get_mem(entry->e_value_size, &x->value);
- if (err)
- return err;
+ /* Check & copy value */
+ if (!ext2fs_has_feature_ea_inode(handle->fs->super) &&
+ entry->e_value_inum != 0)
+ return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+ if (entry->e_value_inum == 0) {
+ if (entry->e_value_size > remain)
+ return EXT2_ET_EA_BAD_VALUE_SIZE;
+
+ if (entry->e_value_offs + entry->e_value_size > values_size)
+ return EXT2_ET_EA_BAD_VALUE_OFFSET;
+
+ if (entry->e_value_size > 0 &&
+ value_start + entry->e_value_offs <
+ (char *)end + sizeof(__u32))
+ return EXT2_ET_EA_BAD_VALUE_OFFSET;
+
+ remain -= entry->e_value_size;
+
+ err = ext2fs_get_mem(entry->e_value_size, &x->value);
+ if (err)
+ return err;
+ memcpy(x->value, value_start + entry->e_value_offs,
+ entry->e_value_size);
+ } else {
+ ext2_file_t ea_file;
+
+ if (entry->e_value_offs != 0)
+ return EXT2_ET_EA_BAD_VALUE_OFFSET;
+
+ if (entry->e_value_size > (64 * 1024))
+ return EXT2_ET_EA_BAD_VALUE_SIZE;
+
+ err = ext2fs_get_mem(entry->e_value_size, &x->value);
+ if (err)
+ return err;
+
+ err = ext2fs_file_open(handle->fs, entry->e_value_inum,
+ 0, &ea_file);
+ if (err)
+ return err;
+
+ if (ext2fs_file_get_size(ea_file) !=
+ entry->e_value_size)
+ err = EXT2_ET_EA_BAD_VALUE_SIZE;
+ else
+ err = ext2fs_file_read(ea_file, x->value,
+ entry->e_value_size, 0);
+ ext2fs_file_close(ea_file);
+ if (err)
+ return err;
+ }
+
x->value_len = entry->e_value_size;
- memcpy(x->value, value_start + entry->e_value_offs,
- entry->e_value_size);
+
+ /* e_hash may be 0 in older inode's ea */
+ if (entry->e_hash != 0) {
+ __u32 hash;
+ void *data = (entry->e_value_inum != 0) ?
+ 0 : value_start + entry->e_value_offs;
+
+ hash = ext2fs_ext_attr_hash_entry(entry, data);
+ if (entry->e_hash != hash)
+ return EXT2_ET_BAD_EA_HASH;
+ }
+
x++;
(*nr_read)++;
entry = EXT2_EXT_ATTR_NEXT(entry);
inode->i_extra_isize + sizeof(__u32);
entry = (struct ext2_ext_attr_entry *) start;
while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
- if (!entry->e_value_block && entry->e_value_size) {
+ if (!entry->e_value_inum && entry->e_value_size) {
unsigned int offs = entry->e_value_offs;
if (offs < minoff)
minoff = offs;
struct ext2_ext_attr_entry *from_entry)
{
to_entry->e_value_offs = ext2fs_swab16(from_entry->e_value_offs);
- to_entry->e_value_block = ext2fs_swab32(from_entry->e_value_block);
+ to_entry->e_value_inum = ext2fs_swab32(from_entry->e_value_inum);
to_entry->e_value_size = ext2fs_swab32(from_entry->e_value_size);
to_entry->e_hash = ext2fs_swab32(from_entry->e_hash);
}
EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
EXT2_FEATURE_INCOMPAT_META_BG|
EXT4_FEATURE_INCOMPAT_FLEX_BG|
+ EXT4_FEATURE_INCOMPAT_EA_INODE|
EXT4_FEATURE_INCOMPAT_MMP |
EXT4_FEATURE_INCOMPAT_64BIT|
EXT4_FEATURE_INCOMPAT_INLINE_DATA|
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT3_FEATURE_INCOMPAT_EXTENTS |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
+ EXT4_FEATURE_INCOMPAT_EA_INODE|
EXT4_FEATURE_INCOMPAT_MMP |
EXT4_FEATURE_INCOMPAT_64BIT |
EXT4_FEATURE_INCOMPAT_ENCRYPT |
/* Incompat */
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
+ EXT4_FEATURE_INCOMPAT_EA_INODE|
EXT4_FEATURE_INCOMPAT_MMP |
EXT4_FEATURE_INCOMPAT_64BIT |
EXT4_FEATURE_INCOMPAT_CSUM_SEED,