lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 26 Jun 2017 06:43:43 -0700
From:   Tahsin Erdogan <tahsin@...gle.com>
To:     Andreas Dilger <adilger@...ger.ca>,
        "Darrick J . Wong" <darrick.wong@...cle.com>,
        Theodore Ts'o <tytso@....edu>, linux-ext4@...r.kernel.org
Cc:     Tahsin Erdogan <tahsin@...gle.com>
Subject: [PATCH 07/12] e2fsck: track ea_inode references

An extended attribute inode has a ref count to track how many entries
point to it. Update e2fsck to verify that the stored ref count is
correct.

Signed-off-by: Tahsin Erdogan <tahsin@...gle.com>
---
 e2fsck/e2fsck.c  |   4 +++
 e2fsck/e2fsck.h  |   6 +++-
 e2fsck/message.c |   7 ++++
 e2fsck/pass1.c   |  66 ++++++++++++++++++++++--------------
 e2fsck/pass4.c   | 101 +++++++++++++++++++++++++++++++++++++++++++++----------
 e2fsck/problem.c |   4 +++
 e2fsck/problem.h |   5 ++-
 7 files changed, 148 insertions(+), 45 deletions(-)

diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c
index e0f449ee2047..63a986b92af9 100644
--- a/e2fsck/e2fsck.c
+++ b/e2fsck/e2fsck.c
@@ -102,6 +102,10 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx)
 		ea_refcount_free(ctx->ea_block_quota);
 		ctx->ea_block_quota = 0;
 	}
+	if (ctx->ea_inode_refs) {
+		ea_refcount_free(ctx->ea_inode_refs);
+		ctx->ea_inode_refs = 0;
+	}
 	if (ctx->block_dup_map) {
 		ext2fs_free_block_bitmap(ctx->block_dup_map);
 		ctx->block_dup_map = 0;
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 79eeda9fbafb..b25c5eb9179b 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -254,7 +254,6 @@ 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 */
@@ -274,6 +273,11 @@ struct e2fsck_struct {
 	 */
 	ext2_refcount_t ea_block_quota;
 
+	/*
+	 * ea_inode references from attr entries.
+	 */
+	ext2_refcount_t ea_inode_refs;
+
 	/*
 	 * Array of flags indicating whether an inode bitmap, block
 	 * bitmap, or inode table is invalid
diff --git a/e2fsck/message.c b/e2fsck/message.c
index 34201a37fd4b..525f4a1e7628 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -466,6 +466,13 @@ static _INLINE_ void expand_percent_expression(FILE *f, ext2_filsys fs,
 		fprintf(f, "%*u", width, ctx->num);
 #else
 		fprintf(f, "%*llu", width, (long long)ctx->num);
+#endif
+		break;
+	case 'n':
+#ifdef EXT2_NO_64_TYPE
+		fprintf(f, "%*u", width, ctx->num2);
+#else
+		fprintf(f, "%*llu", width, (long long)ctx->num2);
 #endif
 		break;
 	case 'p':
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index cc88a7d05cef..1033b7646cb6 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -26,7 +26,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)
+ * 	- Ref counts for ea_inodes.			(ea_inode_refs)
  *
  * Pass 1 is designed to stash away enough information so that the
  * other passes should not need to read in the inode information
@@ -335,23 +335,6 @@ static void check_size(e2fsck_t ctx, struct problem_context *pctx)
 	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);
-}
-
 /*
  * For a given size, calculate how many blocks would be charged towards quota.
  */
@@ -428,13 +411,38 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
 	return 0;
 }
 
+static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
+			      struct ext2_ext_attr_entry *first, void *end)
+{
+	struct ext2_ext_attr_entry *entry;
+
+	for (entry = first;
+	     (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry);
+	     entry = EXT2_EXT_ATTR_NEXT(entry)) {
+		if (!entry->e_value_inum)
+			continue;
+		if (!ctx->ea_inode_refs) {
+			pctx->errcode = ea_refcount_create(0,
+							   &ctx->ea_inode_refs);
+			if (pctx->errcode) {
+				pctx->num = 4;
+				fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+				ctx->flags |= E2F_FLAG_ABORT;
+				return;
+			}
+		}
+		ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum,
+				      0);
+	}
+}
+
 static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
 			      blk64_t *ea_ibody_quota_blocks)
 {
 	struct ext2_super_block *sb = ctx->fs->super;
 	struct ext2_inode_large *inode;
 	struct ext2_ext_attr_entry *entry;
-	char *start, *header;
+	char *start, *header, *end;
 	unsigned int storage_size, remain;
 	problem_t problem = 0;
 	region_t region = 0;
@@ -447,6 +455,7 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
 		inode->i_extra_isize;
 	header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
 		 inode->i_extra_isize;
+	end = header + storage_size;
 	start = header + sizeof(__u32);
 	entry = (struct ext2_ext_attr_entry *) start;
 
@@ -524,7 +533,6 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
 			if (problem != 0)
 				goto fix;
 
-			mark_inode_ea_map(ctx, pctx, entry->e_value_inum);
 			quota_blocks += entry_quota_blocks;
 		}
 
@@ -549,6 +557,8 @@ fix:
 	 * EA(s) in automatic fashion -bzzz
 	 */
 	if (problem == 0 || !fix_problem(ctx, problem, pctx)) {
+		inc_ea_inode_refs(ctx, pctx,
+				  (struct ext2_ext_attr_entry *)start, end);
 		*ea_ibody_quota_blocks = quota_blocks;
 		return;
 	}
@@ -1988,6 +1998,11 @@ void e2fsck_pass1(e2fsck_t ctx)
 		ctx->refcount_extra = 0;
 	}
 
+	if (ctx->ea_block_quota) {
+		ea_refcount_free(ctx->ea_block_quota);
+		ctx->ea_block_quota = 0;
+	}
+
 	if (ctx->invalid_bitmaps)
 		handle_fs_bad_blocks(ctx);
 
@@ -2349,7 +2364,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 	blk64_t		blk;
 	char *		end;
 	struct ext2_ext_attr_header *header;
-	struct ext2_ext_attr_entry *entry;
+	struct ext2_ext_attr_entry *first, *entry;
 	blk64_t		quota_blocks = EXT2FS_C2B(fs, 1);
 	region_t	region = 0;
 	int		failed_csum = 0;
@@ -2473,8 +2488,9 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 			goto clear_extattr;
 	}
 
-	entry = (struct ext2_ext_attr_entry *)(header+1);
+	first = (struct ext2_ext_attr_entry *)(header+1);
 	end = block_buf + fs->blocksize;
+	entry = first;
 	while ((char *)entry < end && *(__u32 *)entry) {
 		__u32 hash;
 
@@ -2522,10 +2538,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 
 			problem = check_large_ea_inode(ctx, entry, pctx,
 						       &entry_quota_blocks);
-			if (problem == 0)
-				mark_inode_ea_map(ctx, pctx,
-						  entry->e_value_inum);
-			else if (fix_problem(ctx, problem, pctx))
+			if (problem && fix_problem(ctx, problem, pctx))
 				goto clear_extattr;
 
 			quota_blocks += entry_quota_blocks;
@@ -2565,6 +2578,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 		}
 		ea_refcount_store(ctx->ea_block_quota, blk, quota_blocks);
 	}
+	inc_ea_inode_refs(ctx, pctx, first, end);
 	ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
 	mark_block_used(ctx, blk);
 	ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c
index e44fc69c1613..663f87ab59c0 100644
--- a/e2fsck/pass4.c
+++ b/e2fsck/pass4.c
@@ -11,7 +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)
+ *	- Ref counts for ea_inodes.			(ea_inode_refs)
  */
 
 #include "config.h"
@@ -40,20 +40,6 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i,
 	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);
@@ -101,6 +87,77 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i,
 	return 0;
 }
 
+/*
+ * Get/set ref functions below could later be moved to somewhere in lib/ext2fs/.
+ * Currently the only user is e2fsck so we rather not expose it in common
+ * library until there are more users.
+ */
+static __u64 ea_inode_get_ref(struct ext2_inode_large *inode)
+{
+	return ((__u64)inode->i_ctime << 32) | inode->osd1.linux1.l_i_version;
+}
+
+static void ea_inode_set_ref(struct ext2_inode_large *inode, __u64 ref_count)
+{
+	inode->i_ctime = (__u32)(ref_count >> 32);
+	inode->osd1.linux1.l_i_version = (__u32)ref_count;
+}
+
+static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i,
+			   struct ext2_inode_large *inode, __u16 *link_counted)
+{
+	__u64 actual_refs = 0;
+	__u64 ref_count;
+
+	/*
+	 * This function is called when link_counted is zero. So this may not
+	 * be an xattr inode at all. Return immediately if EA_INODE flag is not
+	 * set.
+	 */
+	e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode),
+			       EXT2_INODE_SIZE(ctx->fs->super),
+			       "pass4: check_ea_inode");
+	if (!(inode->i_flags & EXT4_EA_INODE_FL))
+		return;
+
+	if (ctx->ea_inode_refs)
+		ea_refcount_fetch(ctx->ea_inode_refs, i, &actual_refs);
+	if (!actual_refs) {
+		/*
+		 * There are no attribute references to the ea_inode.
+		 * 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), "check_ea_inode");
+		ext2fs_icount_store(ctx->inode_link_info, i, 0);
+		return;
+	}
+
+	/*
+	 * There are some attribute references, link_counted is now considered
+	 * to be 1.
+	 */
+	*link_counted = 1;
+
+	ref_count = ea_inode_get_ref(inode);
+
+	/* Old Lustre-style xattr inodes do not have a stored refcount.
+	 * However, their i_ctime and i_atime should be the same.
+	 */
+	if (ref_count != actual_refs && inode->i_ctime != inode->i_atime) {
+		struct problem_context pctx;
+
+		clear_problem_context(&pctx);
+		pctx.ino = i;
+		pctx.num = ref_count;
+		pctx.num2 = actual_refs;
+		if (fix_problem(ctx, PR_4_EA_INODE_REF_COUNT, &pctx)) {
+			ea_inode_set_ref(inode, actual_refs);
+			e2fsck_write_inode(ctx, i, EXT2_INODE(inode), "pass4");
+		}
+	}
+}
 
 void e2fsck_pass4(e2fsck_t ctx)
 {
@@ -168,6 +225,16 @@ void e2fsck_pass4(e2fsck_t ctx)
 			continue;
 		ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
 		ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
+
+		if (link_counted == 0) {
+			/*
+			 * link_counted is expected to be 0 for an ea_inode.
+			 * check_ea_inode() will update link_counted if
+			 * necessary.
+			 */
+			check_ea_inode(ctx, i, inode, &link_counted);
+		}
+
 		if (link_counted == 0) {
 			if (!buf)
 				buf = e2fsck_allocate_memory(ctx,
@@ -211,10 +278,10 @@ 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_inode_bitmap(ctx->inode_ea_map);
-	ctx->inode_ea_map = 0;
 	ext2fs_free_inode_bitmap(ctx->inode_bb_map);
 	ctx->inode_bb_map = 0;
+	ea_refcount_free(ctx->ea_inode_refs);
+	ctx->ea_inode_refs = 0;
 	ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
 	ctx->inode_imagic_map = 0;
 errout:
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index deffa4d66a89..97069335156c 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1869,6 +1869,10 @@ static struct e2fsck_problem problem_table[] = {
 	  "They @s the same!\n"),
 	  PROMPT_NONE, 0 },
 
+	{ PR_4_EA_INODE_REF_COUNT,
+	  N_("@a @i %i ref count is %N, @s %n. "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
 	/* Pass 5 errors */
 
 	/* Pass 5: Checking group summary information */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 8e07c10895c1..54eb1cf9a519 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -20,7 +20,7 @@ struct problem_context {
 	e2_blkcnt_t	blkcount;
 	dgrp_t		group;
 	__u32		csum1, csum2;
-	__u64	num;
+	__u64		num, num2;
 	const char *str;
 };
 
@@ -1131,6 +1131,9 @@ struct problem_context {
 /* Inconsistent inode count information cached */
 #define PR_4_INCONSISTENT_COUNT		0x040004
 
+/* Extended attribute inode ref count wrong */
+#define PR_4_EA_INODE_REF_COUNT		0x040005
+
 /*
  * Pass 5 errors
  */
-- 
2.13.1.611.g7e3b11ae1-goog

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ