This patch completes the initial extended attribute/ACL support for
e2fsck.  We now check the entire EA block to make sure that the all
of the EA entries look sane.

diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index 3432de9..a8d0da7 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -1,3 +1,16 @@
+2001-07-19  Theodore Tso  <tytso@valinux.com>
+
+	* pass1.c (check_ext_attr): The entire EA block is now checked to
+		make sure that parts of the EA block aren't being used for
+		multiple purposes.
+
+	* Makefile.in e2fsck.h, region.c: New file which is used to detect
+		collisions in extended attribute block.
+
+	* problem.h, problem.c (PR_1_EA_MULTI_BLOCK, PR_1_EA_ALLOC_REGION,
+		PR_1_EA_ALLOC_COLLISION, PR_1_EA_BAD_NAME,
+		PR_1_EA_BAD_VALUE): Add new problem codes.
+
 2001-07-10  Theodore Tso  <tytso@valinux.com>
 
 	* journal.c (e2fsck_run_ext3_journal): Only call ext3_flush() if
diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in
index 2fde4b0..5e76e0d 100644
--- a/e2fsck/Makefile.in
+++ b/e2fsck/Makefile.in
@@ -56,7 +56,8 @@
 
 OBJS= unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \
 	pass5.o journal.o swapfs.o badblocks.o util.o dirinfo.o ehandler.o \
-	problem.o message.o recovery.o revoke.o ea_refcount.o $(MTRACE_OBJ)
+	problem.o message.o recovery.o region.o revoke.o ea_refcount.o \
+	$(MTRACE_OBJ)
 
 PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \
 	profiled/pass1.o profiled/pass1b.o \
@@ -64,7 +65,7 @@
 	profiled/journal.o profiled/badblocks.o profiled/util.o \
 	profiled/dirinfo.o profiled/ehandler.o profiled/message.o \
 	profiled/problem.o profiled/swapfs.o profiled/recovery.o \
-	profiled/revoke.o profiled/ea_refcount.o
+	profiled/region.o profiled/revoke.o profiled/ea_refcount.o
 
 SRCS= $(srcdir)/e2fsck.c \
 	$(srcdir)/super.c \
@@ -86,6 +87,7 @@
 	$(srcdir)/message.c \
 	$(srcdir)/swapfs.c \
 	$(srcdir)/ea_refcount.c \
+	$(srcdir)/region.c \
 	$(MTRACE_SRC)
 
 all:: profiled $(PROGS) e2fsck.static e2fsck.shared $(MANPAGES)
@@ -111,6 +113,10 @@
 	$(CC) -o tst_refcount $(srcdir)/ea_refcount.c \
 		$(ALL_CFLAGS) -DTEST_PROGRAM -lcom_err
 
+tst_region: region.c
+	$(CC) -o tst_region $(srcdir)/region.c \
+		$(ALL_CFLAGS) -DTEST_PROGRAM -lcom_err
+
 extend: extend.o
 	$(LD) $(ALL_LDFLAGS) -o extend extend.o $(CHECKLIB)
 
@@ -181,7 +187,8 @@
  $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
  $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \
  $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
- $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h
+ $(top_srcdir)/lib/ext2fs/bitops.h $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h \
+ $(srcdir)/problem.h
 pass1b.o: $(srcdir)/pass1b.c $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
@@ -266,3 +273,13 @@
  $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
  $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
  $(top_srcdir)/lib/ext2fs/bitops.h
+ea_refcount.o: $(srcdir)/ea_refcount.c $(srcdir)/e2fsck.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h
+region.o: $(srcdir)/region.c $(srcdir)/e2fsck.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 26f63cf..a5c58ad 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -250,6 +250,9 @@
 	void *priv_data;
 };
 
+/* Used by the region allocation code */
+typedef __u32 region_addr_t;
+typedef struct region_struct *region_t;
 
 /*
  * Procedure declarations
@@ -314,6 +317,11 @@
 /* pass3.c */
 extern int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
 
+/* region.c */
+extern region_t region_create(region_addr_t min, region_addr_t max);
+extern void region_free(region_t region);
+extern int region_allocate(region_t region, region_addr_t start, int n);
+
 /* super.c */
 void check_super_block(e2fsck_t ctx);
 errcode_t e2fsck_get_device_size(e2fsck_t ctx);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 11aaaec..ab8dcfe 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -939,8 +939,11 @@
 	ext2_ino_t	ino = pctx->ino;
 	struct ext2_inode *inode = pctx->inode;
 	blk_t		blk;
+	char *		end;
 	struct ext2_ext_attr_header *header;
+	struct ext2_ext_attr_entry *entry;
 	int		count;
+	region_t	region;
 	
 	blk = inode->i_file_acl;
 	if (blk == 0)
@@ -1001,7 +1004,7 @@
 				pctx->num = 2;
 				fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
 				ctx->flags |= E2F_FLAG_ABORT;
-				return 1;
+				return 0;
 			}
 		}
 		ea_refcount_increment(ctx->refcount_extra, blk, 0);
@@ -1017,14 +1020,55 @@
 	if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
 		goto clear_extattr;
 	header = (struct ext2_ext_attr_header *) block_buf;
-
+	pctx->blk = inode->i_file_acl;
 	if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
-		pctx->blk = inode->i_file_acl;
-		if (fix_problem, ctx, PR_1_BAD_EA_BLOCK, pctx)
+		if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
+			goto clear_extattr;
+	}
+	if (header->h_blocks != 1) {
+		if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
+			goto clear_extattr;
+	}
+
+	region = region_create(0, fs->blocksize);
+	if (!region) {
+		fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return 0;
+	}
+	if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
+		if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
 			goto clear_extattr;
 	}
 	
-	/* @@@ validate the contents of the EA block */
+	entry = (struct ext2_ext_attr_entry *)(header+1);
+	end = block_buf + fs->blocksize;
+	while ((char *)entry < end && *(__u32 *)entry) {
+		if (region_allocate(region, (char *)entry - (char *)header,
+			           EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
+			if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+				goto clear_extattr;
+		}
+		if (entry->e_name_len == 0 || entry->e_name_index != 0) {
+			if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
+				goto clear_extattr;
+		}
+		if (entry->e_value_block != 0) {
+			if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
+				goto clear_extattr;
+		}
+		if (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;
+		}
+		entry = EXT2_EXT_ATTR_NEXT(entry);
+	}
+	if (region_allocate(region, (char *)entry - (char *)header, 4)) {
+		if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+			goto clear_extattr;
+	}
+	region_free(region);
 
 	count = header->h_refcount - 1;
 	if (count)
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 4e13c53..2ded448 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -596,6 +596,31 @@
 	{ PR_1_EXTATTR_WRITE,
 	  N_("Error writing @a @b %b (%m).  "),
 	  PROMPT_ABORT, 0 },
+
+	/* Multiple EA blocks not supported */
+	{ PR_1_EA_MULTI_BLOCK,
+	  N_("@a @b %b has h_blocks > 1.  "),
+	  PROMPT_CLEAR, 0},	  
+
+	/* Error allocating EA region allocation structure */
+	{ PR_1_EA_ALLOC_REGION,
+	  N_("Error allocating @a @b %b.  "),
+	  PROMPT_ABORT, 0},
+	
+	/* Error EA allocation collision */
+	{ PR_1_EA_ALLOC_COLLISION,
+	  N_("@a @b %b is corrupt (allocation collision).  "),
+	  PROMPT_ABORT, 0},
+	
+	/* Bad extended attribute name */
+	{ PR_1_EA_BAD_NAME,
+	  N_("@a @b %b is corrupt (invalid name).  "),
+	  PROMPT_CLEAR, 0},	  
+
+	/* Bad extended attribute value */
+	{ PR_1_EA_BAD_VALUE,
+	  N_("@a @b %b is corrupt (invalid value).  "),
+	  PROMPT_CLEAR, 0},	  
 		  
 	/* Pass 1b errors */
 
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index aa2b706..be35fc9 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -344,6 +344,21 @@
 
 /* Error writing Extended Attribute block while fixing refcount */ 
 #define PR_1_EXTATTR_WRITE		0x01003D
+
+/* Multiple EA blocks not supported */
+#define PR_1_EA_MULTI_BLOCK		0x01003E
+
+/* Error allocating EA region allocation structure */
+#define PR_1_EA_ALLOC_REGION		0x01003F
+	
+/* Error EA allocation collision */
+#define PR_1_EA_ALLOC_COLLISION		0x010040
+	
+/* Bad extended attribute name */
+#define PR_1_EA_BAD_NAME		0x010041
+
+/* Bad extended attribute value */
+#define PR_1_EA_BAD_VALUE		0x0100423
 	
 /*
  * Pass 1b errors
diff --git a/e2fsck/region.c b/e2fsck/region.c
new file mode 100644
index 0000000..9ccb684
--- /dev/null
+++ b/e2fsck/region.c
@@ -0,0 +1,204 @@
+/*
+ * region.c --- code which manages allocations within a region.
+ * 
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+
+#include "e2fsck.h"
+
+struct region_el {
+	region_addr_t	start;
+	region_addr_t	end;
+	struct region_el *next;
+};
+
+struct region_struct {
+	region_addr_t	min;
+	region_addr_t	max;
+	struct region_el *allocated;
+};
+
+region_t region_create(region_addr_t min, region_addr_t max)
+{
+	region_t	region;
+
+	region = malloc(sizeof(struct region_struct));
+	if (!region)
+		return NULL;
+	memset(region, 0, sizeof(struct region_struct));
+	region->min = min;
+	region->max = max;
+	return region;
+}
+
+void region_free(region_t region)
+{
+	struct region_el	*r, *next;
+
+	for (r = region->allocated; r; r = next) {
+		next = r->next;
+		free(r);
+	}
+	memset(region, 0, sizeof(struct region_struct));
+	free(region);
+}
+
+int region_allocate(region_t region, region_addr_t start, int n)
+{
+	struct region_el	*r, *new_region, *prev, *next;
+	region_addr_t end;
+
+	end = start+n;
+	if ((start < region->min) || (end > region->max))
+		return -1;
+	if (n == 0)
+		return 1;
+
+	/*
+	 * Search through the linked list.  If we find that it
+	 * conflicts witih something that's already allocated, return
+	 * 1; if we can find an existing region which we can grow, do
+	 * so.  Otherwise, stop when we find the appropriate place
+	 * insert a new region element into the linked list.
+	 */
+	for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
+		if (((start >= r->start) && (start < r->end)) ||
+		    ((end > r->start) && (end <= r->end)) ||
+		    ((start <= r->start) && (end >= r->end)))
+			return 1;
+		if (end == r->start) {
+			r->start = start;
+			return 0;
+		}
+		if (start == r->end) {
+			if ((next = r->next)) {
+				if (end > next->start)
+					return 1;
+				if (end == next->start) {
+					r->end = next->end;
+					r->next = next->next;
+					free(next);
+					return 0;
+				}
+			}
+			r->end = end;
+			return 0;
+		}
+		if (start < r->start)
+			break;
+	}
+	/*
+	 * Insert a new region element structure into the linked list
+	 */
+	new_region = malloc(sizeof(struct region_el));
+	if (!new_region)
+		return -1;
+	new_region->start = start;
+	new_region->end = start + n;
+	new_region->next = r;
+	if (prev)
+		prev->next = new_region;
+	else
+		region->allocated = new_region;
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+#include <stdio.h>
+
+#define BCODE_END	0
+#define BCODE_CREATE	1
+#define BCODE_FREE	2
+#define BCODE_ALLOCATE	3
+#define BCODE_PRINT	4
+
+int bcode_program[] = {
+	BCODE_CREATE, 1, 1001,
+	BCODE_PRINT,
+	BCODE_ALLOCATE, 10, 10,
+	BCODE_ALLOCATE, 30, 10,
+	BCODE_PRINT,
+	BCODE_ALLOCATE, 1, 15,
+	BCODE_ALLOCATE, 15, 8,
+	BCODE_ALLOCATE, 1, 20,
+	BCODE_ALLOCATE, 1, 8,
+	BCODE_PRINT,
+	BCODE_ALLOCATE, 40, 10,
+	BCODE_PRINT,
+	BCODE_ALLOCATE, 22, 5,
+	BCODE_PRINT,
+	BCODE_ALLOCATE, 27, 3,
+	BCODE_PRINT,
+	BCODE_ALLOCATE, 20, 2,
+	BCODE_PRINT,
+	BCODE_ALLOCATE, 49, 1,
+	BCODE_ALLOCATE, 50, 5,
+	BCODE_ALLOCATE, 9, 2,
+	BCODE_ALLOCATE, 9, 1,
+	BCODE_PRINT,
+	BCODE_FREE,
+	BCODE_END
+};
+
+void region_print(region_t region, FILE *f)
+{
+	struct region_el	*r;
+	int	i = 0;
+	
+	fprintf(f, "Printing region (min=%d. max=%d)\n\t", region->min,
+		region->max);
+	for (r = region->allocated; r; r = r->next) {
+		fprintf(f, "(%d, %d)  ", r->start, r->end);
+		if (++i >= 8)
+			fprintf(f, "\n\t");
+	}
+	fprintf(f, "\n");
+}
+
+int main(int argc, char **argv)
+{
+	region_t	r;
+	int		pc = 0, ret;
+	region_addr_t	start, end, len;
+
+	
+	while (1) {
+		switch (bcode_program[pc++]) {
+		case BCODE_END:
+			exit(0);
+		case BCODE_CREATE:
+			start = bcode_program[pc++];
+			end = bcode_program[pc++];
+			printf("Creating region with args(%d, %d)\n",
+			       start, end);
+			r = region_create(start, end);
+			if (!r) {
+				fprintf(stderr, "Couldn't create region.\n");
+				exit(1);
+			}
+			break;
+		case BCODE_ALLOCATE:
+			start = bcode_program[pc++];
+			end = bcode_program[pc++];
+			ret = region_allocate(r, start, end);
+			printf("Region_allocate(%d, %d) returns %d\n",
+			       start, end, ret);
+			break;
+		case BCODE_PRINT:
+			region_print(r, stdout);
+			break;
+		}
+	}
+}
+
+#endif /* TEST_PROGRAM */