From patchwork Mon Nov 17 20:36:19 2008 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kalpak Shah X-Patchwork-Id: 9214 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by ozlabs.org (Postfix) with ESMTP id 83797DDDE3 for ; Tue, 18 Nov 2008 07:46:12 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752066AbYKQUqK (ORCPT ); Mon, 17 Nov 2008 15:46:10 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752107AbYKQUqK (ORCPT ); Mon, 17 Nov 2008 15:46:10 -0500 Received: from sineb-mail-2.sun.com ([192.18.19.7]:36684 "EHLO sineb-mail-2.sun.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752066AbYKQUqI (ORCPT ); Mon, 17 Nov 2008 15:46:08 -0500 X-Greylist: delayed 631 seconds by postgrey-1.27 at vger.kernel.org; Mon, 17 Nov 2008 15:45:59 EST Received: from fe-apac-05.sun.com (fe-apac-05.sun.com [192.18.19.176] (may be forged)) by sineb-mail-2.sun.com (8.13.6+Sun/8.12.9) with ESMTP id mAHKZhkP015658 for ; Mon, 17 Nov 2008 20:35:43 GMT Received: from conversion-daemon.mail-apac.sun.com by mail-apac.sun.com (Sun Java System Messaging Server 6.2-6.01 (built Apr 3 2006)) id <0KAH00H01VU6YJ00@mail-apac.sun.com> (original mail from Kalpak.Shah@Sun.COM) for linux-ext4@vger.kernel.org; Tue, 18 Nov 2008 04:35:43 +0800 (SGT) Received: from [192.168.1.5] ([59.97.192.36]) by mail-apac.sun.com (Sun Java System Messaging Server 6.2-6.01 (built Apr 3 2006)) with ESMTPSA id <0KAH0011OVVIGRWS@mail-apac.sun.com>; Tue, 18 Nov 2008 04:35:43 +0800 (SGT) Date: Tue, 18 Nov 2008 02:06:19 +0530 From: Kalpak Shah Subject: [PATCH 1/2 e2fsprogs] support for large EAs To: linux-ext4 Cc: TheodoreTso , Andreas Dilger Message-id: <1226954180.3972.72.camel@localhost> MIME-version: 1.0 X-Mailer: Evolution 2.8.3 (2.8.3-2.fc6) Content-type: multipart/mixed; boundary="Boundary_(ID_NiA00lpwhxkyOJKfl6ehcw)" Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org Hi, This patch adds large EA support for e2fsprogs. - add EXT4_FEATURE_INCOMPAT_EA_INODE feature - inode_ea_map bitmap is added for tracking non-orphan EA inodes. Orphan EA inodes get linked to lost+found. - xattr handling is needed for deleting corrupt EA entries Ted, this patch is actually based on top of the e2fsprogs-expand-extra-isize.patch I had sent about a month back. If you are satisfied with the approach, I can send a patch series which applies to the tip of the e2fsprogs tree. Signed-off-by: Andreas Dilger Signed-off-by: Kalpak Shah e2fsck/e2fsck.h | 1 e2fsck/pass1.c | 199 ++++++++++++++++++++++++++++++++++++--------- e2fsck/pass4.c | 17 +++ e2fsck/problem.c | 24 +++++ e2fsck/problem.h | 13 ++ lib/blkid/probe.h | 1 lib/e2p/feature.c | 2 lib/ext2fs/ext2_ext_attr.h | 5 - lib/ext2fs/ext2_fs.h | 5 - lib/ext2fs/ext2fs.h | 6 - lib/ext2fs/ext_attr.c | 27 ++++-- lib/ext2fs/swapfs.c | 2 misc/mke2fs.c | 3 misc/tune2fs.c | 6 + 14 files changed, 259 insertions(+), 52 deletions(-) Thanks, Kalpak Index: e2fsprogs-1.41.1/lib/blkid/probe.h =================================================================== --- e2fsprogs-1.41.1.orig/lib/blkid/probe.h +++ e2fsprogs-1.41.1/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.41.1/lib/e2p/feature.c =================================================================== --- e2fsprogs-1.41.1.orig/lib/e2p/feature.c +++ e2fsprogs-1.41.1/lib/e2p/feature.c @@ -75,6 +75,8 @@ static struct feature feature_list[] = { "flex_bg"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP, "mmp" }, + { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE, + "large_xattr" }, { 0, 0, 0 }, }; Index: e2fsprogs-1.41.1/lib/ext2fs/ext2_fs.h =================================================================== --- e2fsprogs-1.41.1.orig/lib/ext2fs/ext2_fs.h +++ e2fsprogs-1.41.1/lib/ext2fs/ext2_fs.h @@ -273,6 +273,7 @@ struct ext2_dx_countlimit { #define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ #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 */ @@ -649,11 +650,13 @@ 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_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.41.1/misc/mke2fs.c =================================================================== --- e2fsprogs-1.41.1.orig/misc/mke2fs.c +++ e2fsprogs-1.41.1/misc/mke2fs.c @@ -841,7 +841,8 @@ static __u32 ok_features[3] = { EXT3_FEATURE_INCOMPAT_JOURNAL_DEV| EXT2_FEATURE_INCOMPAT_META_BG| EXT4_FEATURE_INCOMPAT_FLEX_BG| - EXT4_FEATURE_INCOMPAT_MMP, + EXT4_FEATURE_INCOMPAT_MMP| + EXT4_FEATURE_INCOMPAT_EA_INODE, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| EXT4_FEATURE_RO_COMPAT_HUGE_FILE| Index: e2fsprogs-1.41.1/misc/tune2fs.c =================================================================== --- e2fsprogs-1.41.1.orig/misc/tune2fs.c +++ e2fsprogs-1.41.1/misc/tune2fs.c @@ -122,7 +122,8 @@ static __u32 ok_features[3] = { EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FLEX_BG | - EXT4_FEATURE_INCOMPAT_MMP, + EXT4_FEATURE_INCOMPAT_MMP | + EXT4_FEATURE_INCOMPAT_EA_INODE, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_HUGE_FILE| @@ -477,6 +478,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.41.1/e2fsck/pass1.c =================================================================== --- e2fsprogs-1.41.1.orig/e2fsck/pass1.c +++ e2fsprogs-1.41.1/e2fsck/pass1.c @@ -28,6 +28,7 @@ * - 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 @@ -269,6 +270,125 @@ static void check_size(e2fsck_t ctx, str e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1"); } +extern char *ext2_attr_index_prefix[]; + +static void e2fsck_block_alloc_stats(ext2_filsys fs, blk64_t blk, int inuse) +{ + e2fsck_t ctx = (e2fsck_t) fs->priv_data; + + if (ctx->block_found_map) { + if (inuse > 0) + ext2fs_mark_block_bitmap(ctx->block_found_map, + (blk_t) blk); + else + ext2fs_unmark_block_bitmap(ctx->block_found_map, + (blk_t) blk); + } +} + +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_bitmap(ctx->inode_ea_map, ino); +} + +/* + * Delete an EA entry. If this is the last entry to be deleted, then i_file_acl + * must have been freed, so we must update e2fsck block statistics and set + * i_file_acl_deleted. + * When we delete the entry successfully, this function returns 0, else + * non-zero value. + */ +static int e2fsck_ea_entry_delete(e2fsck_t ctx, struct ext2_ext_attr_entry *entry, + struct problem_context *pctx, + int *i_file_acl_deleted, problem_t prob) +{ + blk_t i_file_acl = pctx->inode->i_file_acl; + int err = 1; + + pctx->num = entry->e_value_inum; + + if (fix_problem(ctx, prob, pctx)) { + /* Delete corrupt EA entry */ + err = ext2fs_attr_set(ctx->fs, pctx->ino, pctx->inode, + entry->e_name_index, entry->e_name, + 0, 0, 0); + if (err == 0) { + if (i_file_acl && pctx->inode->i_file_acl == 0) { + e2fsck_block_alloc_stats(ctx->fs, i_file_acl, -1); + *i_file_acl_deleted = 1; + } + return 0; + } + } + + return err; +} + +/* + * Check validity of EA inode. Return 0 if EA inode is valid, nonzero otherwise. + */ +static int check_large_ea_inode(e2fsck_t ctx, struct ext2_ext_attr_entry *entry, + struct problem_context *pctx, + int *i_file_acl_deleted) +{ + struct ext2_inode inode; + int ret; + + /* 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)) { + ret = e2fsck_ea_entry_delete(ctx, entry, pctx, + i_file_acl_deleted, + PR_1_ATTR_VALUE_EA_INODE); + /* If user refuses to delete this entry, caller may try to set + * the bit for this out-of-bound inode in inode_ea_map, so + * always return failure */ + return 1; + } + + 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); + + return 0; + } + + ret = e2fsck_ea_entry_delete(ctx, entry, pctx, + i_file_acl_deleted, + PR_1_ATTR_NO_EA_INODE_FL); + return ret; + } + + /* Validate the inode back-pointer */ + if (inode.i_mtime != pctx->ino || + inode.i_generation != pctx->inode->i_generation) { + ret = e2fsck_ea_entry_delete(ctx, entry, pctx, + i_file_acl_deleted, + PR_1_ATTR_INVAL_EA_INODE); + return ret; + } + + return 0; +} + static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; @@ -307,18 +427,25 @@ 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 { + int ret, tmp; + + ret = check_large_ea_inode(ctx, entry, pctx, &tmp); + if (ret == 0) + mark_inode_ea_map(ctx, pctx, entry->e_value_inum); } hash = ext2fs_ext_attr_hash_entry(entry, @@ -331,7 +458,10 @@ static void check_ea_in_inode(e2fsck_t c 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); } @@ -510,8 +640,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) { @@ -568,7 +696,7 @@ int e2fsck_pass1_delete_attr(e2fsck_t ct if (EXT2_EXT_IS_LAST_ENTRY(entry)) { if (in_inode) { entry = entry_blk; - len = sizeof(entry->e_name); + len = sizeof(entry->e_name); entry_size = ext2fs_attr_get_next_attr(entry, index, name, len, 1); in_inode = 0; @@ -1609,6 +1737,7 @@ static int check_ext_attr(e2fsck_t ctx, struct ext2_ext_attr_entry *entry; int count; region_t region = 0; + int ret; blk = inode->i_file_acl; if (blk == 0) @@ -1730,19 +1859,27 @@ 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)) + 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 { + int i_file_acl_deleted = 0; + + ret = check_large_ea_inode(ctx, entry, pctx, + &i_file_acl_deleted); + if (ret == 0) + mark_inode_ea_map(ctx, pctx, entry->e_value_inum); + + if (i_file_acl_deleted) goto clear_extattr; } @@ -2883,20 +3020,6 @@ static errcode_t e2fsck_get_alloc_block( return (0); } -static void e2fsck_block_alloc_stats(ext2_filsys fs, blk64_t blk, int inuse) -{ - e2fsck_t ctx = (e2fsck_t) fs->priv_data; - - if (ctx->block_found_map) { - if (inuse > 0) - ext2fs_mark_block_bitmap(ctx->block_found_map, - (blk_t) blk); - else - ext2fs_unmark_block_bitmap(ctx->block_found_map, - (blk_t) blk); - } -} - void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool) { ext2_filsys fs = ctx->fs; Index: e2fsprogs-1.41.1/e2fsck/problem.c =================================================================== --- e2fsprogs-1.41.1.orig/e2fsck/problem.c +++ e2fsprogs-1.41.1/e2fsck/problem.c @@ -928,6 +928,30 @@ 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 %i has @I EA @i %N.\n"), + PROMPT_FIX, PR_PREEN_OK }, + + /* Invalid back-pointer from EA-inode to parent inode */ + { PR_1_ATTR_INVAL_EA_INODE, + N_("@I backpointer from EA @i %N to parent @i %i. " + "Delete EA inode?\n"), + PROMPT_FIX, PR_PREEN_OK }, + + /* Parent inode has invalid EA entry. EA inode does not have + * EXT4_EA_INODE_FL flag */ + { PR_1_ATTR_NO_EA_INODE_FL, + N_("Parent @i %i has @I EA entry. EA @i %N does not have " + "EXT4_EA_INODE_FL flag. Delete EA entry?\n"), + PROMPT_FIX, PR_PREEN_OK }, + + /* EA inode for parent inode does not have EXT4_EA_INODE_FL flag */ + { PR_1_ATTR_SET_EA_INODE_FL, + N_("EA @i %N for parent @i %i does not have EXT4_EA_INODE_FL flag. " + "Set the flag?\n"), + PROMPT_FIX, PR_PREEN_OK }, + /* Pass 1b errors */ Index: e2fsprogs-1.41.1/e2fsck/problem.h =================================================================== --- e2fsprogs-1.41.1.orig/e2fsck/problem.h +++ e2fsprogs-1.41.1/e2fsck/problem.h @@ -548,6 +548,19 @@ struct problem_context { */ #define PR_1_CLEAR_EXTRA_ISIZE 0x01006C +/* Inode has illegal EA value inode */ +#define PR_1_ATTR_VALUE_EA_INODE 0x01006D + +/* Invalid back-pointer from EA-inode to parent inode */ +#define PR_1_ATTR_INVAL_EA_INODE 0x01006E + +/* Parent inode has invalid EA entry. EA inode does not have + * EXT4_EA_INODE_FL flag */ +#define PR_1_ATTR_NO_EA_INODE_FL 0x01006F + +/* EA inode for parent inode does not have EXT4_EA_INODE_FL flag */ +#define PR_1_ATTR_SET_EA_INODE_FL 0x010070 + /* * Pass 1b errors */ Index: e2fsprogs-1.41.1/lib/ext2fs/ext2_ext_attr.h =================================================================== --- e2fsprogs-1.41.1.orig/lib/ext2fs/ext2_ext_attr.h +++ e2fsprogs-1.41.1/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(b) ((b) >> 1) +#define EXT4_XATTR_MAX_LARGE_EA_SIZE (1024 * 1024) + #define BHDR(block) ((struct ext2_ext_attr_header *) block) #define IHDR(inode) \ ((__u32 *) ((char *)inode + \ Index: e2fsprogs-1.41.1/lib/ext2fs/ext_attr.c =================================================================== --- e2fsprogs-1.41.1.orig/lib/ext2fs/ext_attr.c +++ e2fsprogs-1.41.1/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--) { @@ -190,6 +190,7 @@ struct ext2_attr_ibody_find { }; struct ext2_attr_block_find { + ext2_ino_t ino; struct ext2_attr_search s; char *block; }; @@ -202,7 +203,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; } @@ -221,7 +222,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; @@ -360,7 +361,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) @@ -370,7 +371,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); } @@ -393,7 +394,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; @@ -422,7 +423,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); @@ -540,9 +541,20 @@ static errcode_t ext2fs_attr_block_set(e /* Update the i_blocks if we added a new EA block */ if (!inode->i_file_acl && new_buf) inode->i_blocks += fs->blocksize / 512; + + /* Drop the previous xattr block. */ + if (!new_buf) { + if (!fs->block_map) + ext2fs_read_block_bitmap(fs); + ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1); + inode->i_blocks -= fs->blocksize / 512; + } + /* Update the inode. */ inode->i_file_acl = new_buf ? blk : 0; + ext2fs_write_inode(fs, bs->ino, inode); + cleanup: if (clear_flag) ext2fs_free_mem(&s->base); @@ -829,6 +841,7 @@ errcode_t ext2fs_expand_extra_isize(ext2 .s = { .not_found = EXT2_ET_EA_NO_SPACE, }, }; struct ext2_attr_block_find bs = { + .ino = ino, .s = { .not_found = EXT2_ET_EA_NO_SPACE, }, }; char *start, *end, *block_buf = NULL, *buffer =NULL, *b_entry_name=NULL; Index: e2fsprogs-1.41.1/lib/ext2fs/swapfs.c =================================================================== --- e2fsprogs-1.41.1.orig/lib/ext2fs/swapfs.c +++ e2fsprogs-1.41.1/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.41.1/e2fsck/pass4.c =================================================================== --- e2fsprogs-1.41.1.orig/e2fsck/pass4.c +++ e2fsprogs-1.41.1/e2fsck/pass4.c @@ -11,6 +11,7 @@ * 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 "e2fsck.h" @@ -39,6 +40,20 @@ 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) { + if (ext2fs_test_inode_bitmap(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, inode, "disconnect_inode"); + ext2fs_icount_store(ctx->inode_link_info, i, 0); + } + } + clear_problem_context(&pctx); pctx.ino = i; pctx.inode = inode; @@ -182,6 +197,8 @@ void e2fsck_pass4(e2fsck_t ctx) ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; ext2fs_free_icount(ctx->inode_badness); ctx->inode_badness = 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); Index: e2fsprogs-1.41.1/lib/ext2fs/ext2fs.h =================================================================== --- e2fsprogs-1.41.1.orig/lib/ext2fs/ext2fs.h +++ e2fsprogs-1.41.1/lib/ext2fs/ext2fs.h @@ -550,7 +550,8 @@ typedef struct ext2_icount *ext2_icount_ EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG|\ - EXT4_FEATURE_INCOMPAT_MMP) + EXT4_FEATURE_INCOMPAT_MMP|\ + EXT4_FEATURE_INCOMPAT_EA_INODE) #else #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ @@ -558,7 +559,8 @@ typedef struct ext2_icount *ext2_icount_ EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG|\ - 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|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ Index: e2fsprogs-1.41.1/e2fsck/e2fsck.h =================================================================== --- e2fsprogs-1.41.1.orig/e2fsck/e2fsck.h +++ e2fsprogs-1.41.1/e2fsck/e2fsck.h @@ -251,6 +251,7 @@ struct e2fsck_struct { 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 */