--- a/restore/extern.h
+++ b/restore/extern.h
@@ -154,3 +154,5 @@ void	skipxattr (void);
 int	readxattr (char *);
 int	xattr_compare (char *, char *);
 int	xattr_extract (char *, char *);
+int xattr_count_file (char *);
+int xattr_count (char *, int *);
--- a/restore/tape.c
+++ b/restore/tape.c
@@ -1708,40 +1708,68 @@ cmpfiles(char *tapefile, char *diskfile,
 }
 #endif /* !COMPARE_ONTHEFLY */
 
+/* collect both extended attribute tape blocks, then
+	 compare the attributes */
 static void
 compareattr(char *name)
 {
-	int xattr_done = 0;
+	 int xattr_done = 0;
+	 int xattr_count_all = 0;
 
-	while (spcl.c_flags & DR_EXTATTRIBUTES) {
-		switch (spcl.c_extattributes) {
-		case EXT_MACOSFNDRINFO:
-			msg("MacOSX not supported for comparision in this version, skipping\n");
-			skipfile();
-			break;
-		case EXT_MACOSRESFORK:
-			msg("MacOSX not supported for comparision in this version, skipping\n");
-			skipfile();
-			break;
-		case EXT_XATTR: {
-			char xattr[XATTR_MAXSIZE];
+	 while (spcl.c_flags & DR_EXTATTRIBUTES) {
+			switch (spcl.c_extattributes) {
+				 case EXT_MACOSFNDRINFO:
+						msg("MacOSX not supported for comparision in this version, skipping\n");
+						skipfile();
+						break;
+				 case EXT_MACOSRESFORK:
+						msg("MacOSX not supported for comparision in this version, skipping\n");
+						skipfile();
+						break;
+				 case EXT_XATTR: {
+						char xattr[XATTR_MAXSIZE];
+						int xattr_count_thisblock = 0;
 
-			if (readxattr(xattr) == GOOD) {
-				if (xattr_compare(name, xattr) == FAIL)
-					do_compare_error;
-				xattr_done = 1;
-			}
-			else
-				do_compare_error;
-			break;
-		}
+						/* in-inode xattr record comes first, if present at all; then block xattr */
+						if (readxattr(xattr) == GOOD) {
+							 /* this returns fail if the buffer contains different attributes
+									than the file on disk; note that it mustn't return fail if the file on disk has more attributes! */
+							 if (xattr_compare(name, xattr) == FAIL)
+									do_compare_error;
+
+							 xattr_count(xattr, &xattr_count_thisblock);
+							 xattr_count_all += xattr_count_thisblock;
+							 xattr_done = 1;
+						}
+						else
+							 do_compare_error;
+						break;
+				 }
 		default:
 			msg("unexpected inode extension %ld, skipping\n", spcl.c_extattributes);
 			skipfile();
 			break;
 		}
 	}
-	if (!xattr_done && xattr_compare(name, NULL) == FAIL)
+
+	 /* cover the case of two xattr sources, the contents of which matched the file on disk:
+			the only uncovered case left is if there re more attributes on disk than observed on tape */
+	 if (xattr_done)
+	 {
+			int count_file = xattr_count_file(name);
+			if (count_file < 0)
+			{
+				 do_compare_error;
+			}
+			else if (count_file != xattr_count_all)
+			{
+				 fprintf(stderr, "%s: file does not have the same number of attributes (%d) as the tape (%d).\n",
+								 name, count_file, xattr_count_all );
+				 do_compare_error;
+			}
+	 }
+	 /* no attrs whatsoever? file must have none then as well */
+	 if (!xattr_done && xattr_compare(name, NULL) == FAIL)
 		do_compare_error;
 }
 
--- a/restore/xattr.c
+++ b/restore/xattr.c
@@ -198,7 +198,7 @@ static int xattr_cb_list (char *, char *
 static int xattr_cb_set (char *, char *, int, int, void *);
 static int xattr_cb_compare (char *, char *, int, int, void *);
 static int xattr_verify (char *);
-static int xattr_count (char *, int *);
+int xattr_count (char *, int *);
 static int xattr_walk (char *, int (*)(char *, char *, int, int, void *), void *);
 
 static int
@@ -466,7 +466,7 @@ xattr_cb_compare(char *name, char *value
 #endif
 		valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE);
 		if (valuesz < 0) {
-			warn("%s: EA compare lgetxattr failed\n", path);
+			 warn("%s: EA compare lgetxattr for %s failed\n", path, name);
 			return FAIL;
 		}
 #ifdef TRANSSELINUX			/*GAN6May06 SELinux MLS */
@@ -474,8 +474,9 @@ xattr_cb_compare(char *name, char *value
 #endif
 
 	if (valuesz != valuelen || memcmp(value, valuef, valuelen)) {
+		 valuef[valuesz] = '\0';
 		/* GAN24May06: show name and new value for user to compare */
-		fprintf(stderr, "%s: EA %s:%s value changed to %s\n", path, name, value, valuef);
+		fprintf(stderr, "%s: EA %s value changed from %s on tape to %s on disk\n", path, name, value, valuef);
 		return FAIL;
 	}
 
@@ -522,7 +523,7 @@ xattr_verify(char *buffer)
 	return GOOD;
 }
 
-static int
+int
 xattr_count(char *buffer, int *count)
 {
 	struct ext2_xattr_entry *entry;
@@ -586,6 +587,7 @@ xattr_walk(char *buffer, int (*xattr_cb)
 		size = entry->e_value_size;
 
 		memcpy(value, buffer + VALUE_OFFSET(buffer, entry), size);
+		value[size]='\0';
 
 		if (convertacl) {
 			struct posix_acl *acl;
@@ -633,42 +635,57 @@ xattr_walk(char *buffer, int (*xattr_cb)
 	return GOOD;
 }
 
-int
-xattr_compare(char *path, char *buffer)
+/* return count of the xattrs on the given file or FAIL if failed */
+int xattr_count_file(char *path)
 {
-	int countf, countt;
-	char *names = NULL, *end_names, *name;
+	 int size, count = 0;
+	 char *names = NULL, *end_names, *name;
 
-	countf = llistxattr(path, NULL, 0);
-	if (countf < 0) {
-		warn("%s: llistxattr failed", path);
-		return FAIL;
-	}
+	 /* return number of bytes a/v */
+	 size = llistxattr(path, NULL, 0);
+	 if (size < 0) {
+			warn("%s: llistxattr failed", path);
+			return FAIL;
+	 }
 
-	names = malloc(countf + 1);
-	if (!names) {
-		warn("%s: llistxattr failed", path);
-		return FAIL;
-	}
+	 names = malloc(size + 1);
+	 if (!names)
+	 {
+			warn("%s: malloc failed", path);
+			return FAIL;
+	 }
 
-	countf = llistxattr(path, names, countf);
-	if (countf < 0) {
-		warn("%s: llistxattr failed", path);
-		free(names);
-		return FAIL;
-	}
+	 /* return nr of bytes and park data in names array */
+	 size = llistxattr(path, names, size);
+	 if (size < 0)
+	 {
+			warn("%s: llistxattr failed", path);
+			free(names);
+			return FAIL;
+	 }
 
-	names[countf] = '\0';
-	end_names = names + countf;
+	 names[size] = '\0';
+	 end_names = names + size;
 
-	countf = 0;
-	for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
-		if (!*name)
-			continue;
-		countf++;
-	}
+	 for (name = names; name != end_names; name = strchr(name, '\0') + 1)
+	 {
+			if (!*name)
+				 continue;
+			count++;
+	 }
+	 free(names);
+
+	 return count;
+}
+
+int
+xattr_compare(char *path, char *buffer)
+{
+	 int countf, countt;
 
-	free(names);
+	 countf = xattr_count_file(path); /* already warns on failure */
+	 if (countf < 0)
+			return FAIL;
 
 	if (buffer) {
 		if (xattr_verify(buffer) == FAIL)
@@ -680,10 +697,20 @@ xattr_compare(char *path, char *buffer)
 	else
 		countt = 0;
 
-	if (countf != countt) {
-		fprintf(stderr, "%s: EA count changed from %d to %d\n", path, countt, countf);
-		return FAIL;
+	/* with ext4 there may be two tape blocks for attrs: one from the actual inode, plus
+		 one extra filesystem block. in this case countf must be at least >= countt;
+		 verification of that aspect is delayed and left to compareattr
+		 which loops over the different blocks */
+
+	/* nothing on tape but something on disk? sure fail. ditto for more one tape than on disk. */
+	if (countf > 0 && !countt)
+	{
+		 fprintf(stderr, "%s: file does not have the same number of attributes (%d) as the tape (%d).\n",
+						 path, countf, countt);
+		 return FAIL;
 	}
+	if (countf < countt)
+		 return FAIL;								/* warning left to compareattr */
 
 	if (!buffer)
 		return GOOD;
