Index: e2fsprogs-1.40.11/lib/blkid/probe.h =================================================================== --- e2fsprogs-1.40.11.orig/lib/blkid/probe.h +++ e2fsprogs-1.40.11/lib/blkid/probe.h @@ -119,6 +119,7 @@ struct ext2_super_block { #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ Index: e2fsprogs-1.40.11/lib/e2p/feature.c =================================================================== --- e2fsprogs-1.40.11.orig/lib/e2p/feature.c +++ e2fsprogs-1.40.11/lib/e2p/feature.c @@ -71,6 +71,8 @@ static struct feature feature_list[] = { "64bit" }, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP, "mmp" }, + { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE, + "large_xattr" }, { 0, 0, 0 }, }; Index: e2fsprogs-1.40.11/lib/ext2fs/ext2_fs.h =================================================================== --- e2fsprogs-1.40.11.orig/lib/ext2fs/ext2_fs.h +++ e2fsprogs-1.40.11/lib/ext2fs/ext2_fs.h @@ -265,6 +265,7 @@ struct ext2_dx_countlimit { #define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ #define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define EXT2_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */ @@ -661,11 +662,13 @@ struct ext2_super_block { #define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 #define EXT2_FEATURE_COMPAT_SUPP 0 #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ - EXT4_FEATURE_INCOMPAT_MMP) + EXT4_FEATURE_INCOMPAT_MMP| \ + 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| \ Index: e2fsprogs-1.40.11/misc/mke2fs.c =================================================================== --- e2fsprogs-1.40.11.orig/misc/mke2fs.c +++ e2fsprogs-1.40.11/misc/mke2fs.c @@ -925,7 +925,8 @@ static __u32 ok_features[3] = { EXT2_FEATURE_INCOMPAT_FILETYPE| EXT3_FEATURE_INCOMPAT_JOURNAL_DEV| EXT2_FEATURE_INCOMPAT_META_BG| - EXT4_FEATURE_INCOMPAT_MMP, + EXT4_FEATURE_INCOMPAT_MMP| + EXT4_FEATURE_INCOMPAT_EA_INODE, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| Index: e2fsprogs-1.40.11/misc/tune2fs.c =================================================================== --- e2fsprogs-1.40.11.orig/misc/tune2fs.c +++ e2fsprogs-1.40.11/misc/tune2fs.c @@ -113,7 +113,8 @@ static __u32 ok_features[3] = { EXT2_FEATURE_COMPAT_DIR_INDEX, /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE | - EXT4_FEATURE_INCOMPAT_MMP, + EXT4_FEATURE_INCOMPAT_MMP | + EXT4_FEATURE_INCOMPAT_EA_INODE, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | @@ -457,6 +458,9 @@ mmp_error: ext2fs_free_mem(&buf); } + if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE)) + sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_EA_INODE; + if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { /* * If adding a journal flag, let the create journal Index: e2fsprogs-1.40.11/e2fsck/pass1.c =================================================================== --- e2fsprogs-1.40.11.orig/e2fsck/pass1.c +++ e2fsprogs-1.40.11/e2fsck/pass1.c @@ -248,7 +248,9 @@ static void check_size(e2fsck_t ctx, str inode->i_size_high = 0; e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1"); } - + +extern char *ext2_attr_index_prefix[]; + static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; @@ -287,18 +289,38 @@ static void check_ea_in_inode(e2fsck_t c /* attribute len eats this space */ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len); - /* check value size */ - if (entry->e_value_size == 0 || entry->e_value_size > remain) { + if (entry->e_value_size == 0) { 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_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; + } + } else { + if ((entry->e_value_inum < EXT2_FIRST_INODE(ctx->fs->super)) || + (entry->e_value_inum > ctx->fs->super->s_inodes_count)) { + pctx->num = pctx->ino; + pctx->ino = entry->e_value_inum; + + if (fix_problem(ctx, PR_1_ATTR_VALUE_EA_INODE, pctx)) { + int i; + + /* Delete corrupt EA entry */ + i = strlen(ext2_attr_index_prefix[ + entry->e_name_index]); + ext2fs_attr_set(ctx->fs, pctx->ino, + pctx->inode, + entry->e_name_index, + &entry->e_name[i], 0, + 0, 0); + } + } } hash = ext2fs_ext_attr_hash_entry(entry, @@ -314,7 +336,11 @@ static void check_ea_in_inode(e2fsck_t c e2fsck_lfsck_found_ea(ctx, pctx->ino, inode, entry, start + entry->e_value_offs); - 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); } @@ -488,8 +514,6 @@ extern void e2fsck_setup_tdb_icount(e2fs *ret = 0; } -extern char *ext2_attr_index_prefix[]; - int e2fsck_pass1_delete_attr(e2fsck_t ctx, struct ext2_inode_large *inode, struct problem_context *pctx, int needed_size) { @@ -1705,20 +1729,35 @@ static int check_ext_attr(e2fsck_t ctx, 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)) - goto clear_extattr; + if (entry->e_value_inum != 0) { + if ((entry->e_value_inum < EXT2_FIRST_INODE(ctx->fs->super)) || + (entry->e_value_inum > ctx->fs->super->s_inodes_count)) { + pctx->num = pctx->ino; + pctx->ino = entry->e_value_inum; + if (fix_problem(ctx, PR_1_ATTR_VALUE_EA_INODE, pctx)) { + int i; + + /* Delete corrupt EA entry */ + i = strlen(ext2_attr_index_prefix[ + entry->e_name_index]); + ext2fs_attr_set(fs, pctx->ino, inode, + entry->e_name_index, + &entry->e_name[i], 0, + 0,0); + } + } + } else { + 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; + } } hash = ext2fs_ext_attr_hash_entry(entry, block_buf + Index: e2fsprogs-1.40.11/e2fsck/problem.c =================================================================== --- e2fsprogs-1.40.11.orig/e2fsck/problem.c +++ e2fsprogs-1.40.11/e2fsck/problem.c @@ -898,6 +898,10 @@ static struct e2fsck_problem problem_tab "without deletion of an EA.\n"), PROMPT_FIX, 0 }, + /* Inode has illegal EA value inode */ + { PR_1_ATTR_VALUE_EA_INODE, + N_("@i %n has @I EA value @i %i.\n"), + PROMPT_FIX, PR_PREEN_OK }, /* Pass 1b errors */ Index: e2fsprogs-1.40.11/e2fsck/problem.h =================================================================== --- e2fsprogs-1.40.11.orig/e2fsck/problem.h +++ e2fsprogs-1.40.11/e2fsck/problem.h @@ -533,6 +533,9 @@ struct problem_context { */ #define PR_1_CLEAR_EXTRA_ISIZE 0x01006C +/* Invalid EA inode */ +#define PR_1_ATTR_VALUE_EA_INODE 0x01006D + /* * Pass 1b errors */ Index: e2fsprogs-1.40.11/lib/ext2fs/ext2_ext_attr.h =================================================================== --- e2fsprogs-1.40.11.orig/lib/ext2fs/ext2_ext_attr.h +++ e2fsprogs-1.40.11/lib/ext2fs/ext2_ext_attr.h @@ -30,7 +30,7 @@ struct ext2_ext_attr_entry { __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 1 @@ -38,6 +38,9 @@ struct ext2_ext_attr_entry { #endif }; +#define EXT4_XATTR_MIN_LARGE_EA_SIZE 2048 +#define EXT4_XATTR_MAX_LARGE_EA_SIZE (64 * 1024) + #define BHDR(block) ((struct ext2_ext_attr_header *) block) #define IHDR(inode) \ ((__u32 *) ((char *)inode + \ Index: e2fsprogs-1.40.11/lib/ext2fs/ext_attr.c =================================================================== --- e2fsprogs-1.40.11.orig/lib/ext2fs/ext_attr.c +++ e2fsprogs-1.40.11/lib/ext2fs/ext_attr.c @@ -45,7 +45,7 @@ __u32 ext2fs_ext_attr_hash_entry(struct } /* 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--) { @@ -206,7 +206,7 @@ void ext2fs_attr_shift_entries(struct ex /* Adjust the value offsets of the entries */ for (; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) { - if (!last->e_value_block && last->e_value_size) { + if (last->e_value_inum == 0 && last->e_value_size) { last->e_value_offs = last->e_value_offs + value_offs_shift; } @@ -225,7 +225,7 @@ int ext2fs_attr_free_space(struct ext2_e { for (; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) { *total += EXT2_EXT_ATTR_LEN(last->e_name_len); - if (!last->e_value_block && last->e_value_size) { + if (last->e_value_inum == 0 && last->e_value_size) { int offs = last->e_value_offs; if (offs < *min_offs) *min_offs = offs; @@ -364,7 +364,7 @@ static errcode_t ext2fs_attr_set_entry(e /* Compute min_offs and last. */ for (last = s->first; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) { - if (!last->e_value_block && last->e_value_size) { + if (last->e_value_inum == 0 && last->e_value_size) { int offs = last->e_value_offs; if (offs < min_offs) @@ -374,7 +374,7 @@ static errcode_t ext2fs_attr_set_entry(e free = min_offs - ((char *)last - s->base) - sizeof(__u32); if (!s->not_found) { - if (!s->here->e_value_block && s->here->e_value_size) { + if (s->here->e_value_inum == 0 && s->here->e_value_size) { int size = s->here->e_value_size; free += EXT2_EXT_ATTR_SIZE(size); } @@ -397,7 +397,7 @@ static errcode_t ext2fs_attr_set_entry(e s->here->e_name_len = name_len; memcpy(s->here->e_name, i->name, name_len); } else { - if (!s->here->e_value_block && s->here->e_value_size) { + if (s->here->e_value_inum == 0 && s->here->e_value_size) { char *first_val = s->base + min_offs; int offs = s->here->e_value_offs; char *val = s->base + offs; @@ -426,7 +426,7 @@ static errcode_t ext2fs_attr_set_entry(e while (!EXT2_EXT_IS_LAST_ENTRY(last)) { int o = last->e_value_offs; - if (!last->e_value_block && + if (last->e_value_inum == 0 && last->e_value_size && o < offs) last->e_value_offs = o + size; last = EXT2_EXT_ATTR_NEXT(last); Index: e2fsprogs-1.40.11/lib/ext2fs/swapfs.c =================================================================== --- e2fsprogs-1.40.11.orig/lib/ext2fs/swapfs.c +++ e2fsprogs-1.40.11/lib/ext2fs/swapfs.c @@ -110,7 +110,7 @@ void ext2fs_swap_ext_attr_entry(struct e 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); } Index: e2fsprogs-1.40.11/e2fsck/pass4.c =================================================================== --- e2fsprogs-1.40.11.orig/e2fsck/pass4.c +++ e2fsprogs-1.40.11/e2fsck/pass4.c @@ -39,6 +39,12 @@ static int disconnect_inode(e2fsck_t ctx } else { e2fsck_read_inode(ctx, i, inode, "pass4: disconnect_inode"); } + + if (inode->i_flags & EXT4_EA_INODE_FL) { + ext2fs_icount_store(ctx->inode_count, i, 1); + return 0; + } + clear_problem_context(&pctx); pctx.ino = i; pctx.inode = inode; Index: e2fsprogs-1.40.11/lib/ext2fs/ext2fs.h =================================================================== --- e2fsprogs-1.40.11.orig/lib/ext2fs/ext2fs.h +++ e2fsprogs-1.40.11/lib/ext2fs/ext2fs.h @@ -485,7 +485,8 @@ typedef struct ext2_icount *ext2_icount_ EXT2_FEATURE_INCOMPAT_META_BG|\ EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ - EXT4_FEATURE_INCOMPAT_MMP) + EXT4_FEATURE_INCOMPAT_MMP|\ + EXT4_FEATURE_INCOMPAT_EA_INODE) #else #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ @@ -493,7 +494,8 @@ typedef struct ext2_icount *ext2_icount_ EXT2_FEATURE_INCOMPAT_META_BG|\ EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ - EXT4_FEATURE_INCOMPAT_MMP) + EXT4_FEATURE_INCOMPAT_MMP|\ + EXT4_FEATURE_INCOMPAT_EA_INODE) #endif #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\