Whamcloud - gitweb
e2fsck: clean up xattr checking code, add test
authorAndreas Dilger <andreas.dilger@intel.com>
Fri, 13 Apr 2012 08:01:12 +0000 (02:01 -0600)
committerAndreas Dilger <andreas.dilger@intel.com>
Tue, 10 May 2016 05:12:32 +0000 (23:12 -0600)
Clean up xattr header/list processing for in-inode xattrs instead
of doing lots of explicit pointer math.  Add a regression test for
in-inode xattrs.

Signed-off-by: Andreas Dilger <andreas.dilger@intel.com>
configure
configure.in
e2fsck/pass1.c
lib/config.h.in
lib/ext2fs/Makefile.in
lib/ext2fs/ext2_ext_attr.h
lib/ext2fs/ext2fs.h
lib/ext2fs/ext_attr.c
lib/ext2fs/tst_read_ea.c [new file with mode: 0644]

index 0c59235..c73a1bf 100755 (executable)
--- a/configure
+++ b/configure
@@ -10342,7 +10342,7 @@ fi
 done
 
 fi
-for ac_header in       dirent.h        errno.h         execinfo.h      getopt.h        malloc.h        mntent.h        paths.h         semaphore.h     setjmp.h        signal.h        stdarg.h        stdint.h        stdlib.h        termios.h       termio.h        unistd.h        utime.h         linux/falloc.h  linux/fd.h      linux/major.h   linux/loop.h    net/if_dl.h     netinet/in.h    sys/disklabel.h         sys/disk.h      sys/file.h      sys/ioctl.h     sys/mkdev.h     sys/mman.h      sys/mount.h     sys/prctl.h     sys/resource.h  sys/select.h    sys/socket.h    sys/sockio.h    sys/stat.h      sys/syscall.h   sys/sysmacros.h         sys/time.h      sys/types.h     sys/un.h        sys/wait.h
+for ac_header in       attr/xattr.h    dirent.h        errno.h         execinfo.h      getopt.h        malloc.h        mntent.h        paths.h         semaphore.h     setjmp.h        signal.h        stdarg.h        stdint.h        stdlib.h        termios.h       termio.h        unistd.h        utime.h         linux/falloc.h  linux/fd.h      linux/major.h   linux/loop.h    net/if_dl.h     netinet/in.h    sys/disklabel.h         sys/disk.h      sys/file.h      sys/ioctl.h     sys/mkdev.h     sys/mman.h      sys/mount.h     sys/prctl.h     sys/resource.h  sys/select.h    sys/socket.h    sys/sockio.h    sys/stat.h      sys/syscall.h   sys/sysmacros.h         sys/time.h      sys/types.h     sys/un.h        sys/wait.h      sys/xattr.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
index 6ed9a5b..1726bee 100644 (file)
@@ -856,6 +856,7 @@ else
   AC_CHECK_PROGS(BUILD_CC, gcc cc)
 fi
 AC_CHECK_HEADERS(m4_flatten([
+       attr/xattr.h
        dirent.h
        errno.h
        execinfo.h
@@ -898,6 +899,7 @@ AC_CHECK_HEADERS(m4_flatten([
        sys/types.h
        sys/un.h
        sys/wait.h
+       sys/xattr.h
 ]))
 AC_CHECK_HEADERS(net/if.h,,,
 [[
index 150dd9f..e171df4 100644 (file)
@@ -282,11 +282,10 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
        problem_t problem = 0;
 
        inode = (struct ext2_inode_large *) pctx->inode;
-       storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
-               inode->i_extra_isize;
-       start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
-               inode->i_extra_isize + sizeof(__u32);
-       entry = (struct ext2_ext_attr_entry *) start;
+       storage_size = EXT2_INODE_SIZE(ctx->fs->super) -
+               EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize;
+       entry = &IHDR(inode)->h_first_entry[0];
+       start = (char *)entry;
 
        /* scan all entry's headers first */
 
@@ -405,7 +404,7 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
                e2fsck_mark_inode_bad(ctx, pctx->ino, BADNESS_HIGH);
        }
 
-       eamagic = IHDR(inode);
+       eamagic = &IHDR(inode)->h_magic;
        if (*eamagic != EXT2_EXT_ATTR_MAGIC &&
            (ctx->flags & E2F_FLAG_EXPAND_EISIZE) &&
            (inode->i_extra_isize < ctx->want_extra_isize)) {
@@ -587,9 +586,8 @@ int e2fsck_pass1_delete_attr(e2fsck_t ctx, struct ext2_inode_large *inode,
        int in_inode = 1, error;
        unsigned int freed_bytes = inode->i_extra_isize;
 
-       start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
-                       inode->i_extra_isize + sizeof(__u32);
-       entry_ino = (struct ext2_ext_attr_entry *)start;
+       entry_ino = &IHDR(inode)->h_first_entry[0];
+       start = (char *)entry_ino;
 
        if (inode->i_file_acl) {
                error = ext2fs_read_ext_attr(ctx->fs, inode->i_file_acl,
index 49b7d08..75aae51 100644 (file)
@@ -52,6 +52,9 @@
 /* Define to 1 if you have the `asprintf' function. */
 #undef HAVE_ASPRINTF
 
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+#undef HAVE_ATTR_XATTR_H
+
 /* Define to 1 if you have the `backtrace' function. */
 #undef HAVE_BACKTRACE
 
 /* Define to 1 if you have the <sys/wait.h> header file. */
 #undef HAVE_SYS_WAIT_H
 
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#undef HAVE_SYS_XATTR_H
+
 /* Define to 1 if you have the <termios.h> header file. */
 #undef HAVE_TERMIOS_H
 
index 09bf9aa..f6f7ac6 100644 (file)
@@ -186,6 +186,7 @@ SRCS= ext2_err.c \
        $(srcdir)/tst_byteswap.c \
        $(srcdir)/tst_getsize.c \
        $(srcdir)/tst_iscan.c \
+       $(srcdir)/tst_read_ea.c \
        $(srcdir)/undo_io.c \
        $(srcdir)/unix_io.c \
        $(srcdir)/unlink.c \
@@ -262,6 +263,11 @@ tst_iscan: tst_iscan.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
        $(Q) $(CC) -o tst_iscan tst_iscan.o $(ALL_LDFLAGS) \
                $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
 
+tst_read_ea: tst_read_ea.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+       $(E) "  LD $@"
+       $(Q) $(CC) -o tst_read_ea tst_read_ea.o $(STATIC_LIBEXT2FS) \
+               $(STATIC_LIBCOM_ERR)
+
 tst_getsize: tst_getsize.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
        $(E) "  LD $@"
        $(Q) $(CC) -o tst_getsize tst_getsize.o $(ALL_LDFLAGS) \
@@ -451,7 +457,7 @@ mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
        $(Q) $(CC) -o mkjournal $(srcdir)/mkjournal.c -DDEBUG \
                $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(ALL_CFLAGS) $(SYSLIBS)
 
-check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
+check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount tst_read_ea \
     tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
     tst_inline tst_libext2fs
        $(TESTENV) ./tst_bitops
@@ -459,6 +465,7 @@ check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
        $(TESTENV) ./tst_iscan
        $(TESTENV) ./tst_types
        $(TESTENV) ./tst_icount
+       $(TESTENV) ./tst_read_ea
        $(TESTENV) ./tst_super_size
        $(TESTENV) ./tst_inode_size
        $(TESTENV) ./tst_csum
@@ -504,7 +511,7 @@ clean::
                tst_badblocks tst_iscan ext2_err.et ext2_err.c ext2_err.h \
                tst_byteswap tst_ismounted tst_getsize tst_sectgetsize \
                tst_bitops tst_types tst_icount tst_super_size tst_csum \
-               tst_bitmaps tst_bitmaps_out tst_extents tst_inline \
+               tst_read_ea tst_bitmaps tst_bitmaps_out tst_extents tst_inline \
                tst_inline_data tst_inode_size tst_bitmaps_cmd.c \
                ext2_tdbtool mkjournal debug_cmds.c tst_cmds.c extent_cmds.c \
                ../libext2fs.a ../libext2fs_p.a ../libext2fs_chk.a \
@@ -982,6 +989,11 @@ tst_iscan.o: $(srcdir)/tst_iscan.c $(top_builddir)/lib/config.h \
  $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
  $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
+tst_read_ea.o: $(srcdir)/tst_read_ea.c $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
 undo_io.o: $(srcdir)/undo_io.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/tdb.h $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
index 923fe47..755024e 100644 (file)
@@ -38,9 +38,14 @@ struct ext2_ext_attr_entry {
 #endif
 };
 
+struct ext2_xattr_ibody_header {
+       __u32                           h_magic; /* EXT2_EXT_ATTR_MAGIC */
+       struct ext2_ext_attr_entry      h_first_entry[0];
+};
+
 #define BHDR(block) ((struct ext2_ext_attr_header *)block)
-#define IHDR(inode) ((__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE + \
-                               (inode)->i_extra_isize))
+#define IHDR(inode) ((struct ext2_xattr_ibody_header *)((char *)inode + \
+                   EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize))
 #define ENTRY(ptr) ((struct ext2_ext_attr_entry *)(ptr))
 
 /* Name indexes */
index 6d412ef..15b4ee5 100644 (file)
@@ -1073,6 +1073,10 @@ extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
 extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir);
 
 /* ext_attr.c */
+extern errcode_t ext2fs_attr_get(ext2_filsys fs, struct ext2_inode *inode,
+                                int name_index, const char *name, char *buffer,
+                                size_t buffer_size, int *easize);
+
 extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry,
                                        void *data);
 int ext2fs_attr_get_next_attr(struct ext2_ext_attr_entry *entry, int name_index,
index 91a7f16..e69c087 100644 (file)
@@ -264,9 +264,12 @@ static errcode_t ext2fs_attr_check_names(struct ext2_ext_attr_entry *entry,
        return 0;
 }
 
+/* The unused parameter used to be the blocksize, but with in-inode xattrs
+ * the xattr storage area size depends on where the xattrs are kept.  Keep
+ * this parameter for API/ABI compatibility, but it is not needed. */
 static errcode_t ext2fs_attr_find_entry(struct ext2_ext_attr_entry **pentry,
                                        int name_index, const char *name,
-                                       int size, int sorted)
+                                       int unused, int sorted)
 {
        struct ext2_ext_attr_entry *entry;
        int name_len;
@@ -338,8 +341,6 @@ static errcode_t ext2fs_attr_ibody_find(ext2_filsys fs,
                                        struct ext2_attr_info *i,
                                        struct ext2_attr_ibody_find *is)
 {
-       __u32 *eamagic;
-       char *start;
        errcode_t error;
 
        if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
@@ -347,17 +348,13 @@ static errcode_t ext2fs_attr_ibody_find(ext2_filsys fs,
 
        if (inode->i_extra_isize == 0)
                return 0;
-       eamagic = IHDR(inode);
 
-       start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
-                       inode->i_extra_isize + sizeof(__u32);
-       is->s.first = (struct ext2_ext_attr_entry *)start;
-       is->s.base = start;
+       is->s.first = &IHDR(inode)->h_first_entry[0];
+       is->s.base = (char *)is->s.first;
        is->s.here = is->s.first;
        is->s.end = (char *)inode + EXT2_INODE_SIZE(fs->super);
-       if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
-               error = ext2fs_attr_check_names((struct ext2_ext_attr_entry *)
-                                               start, is->s.end);
+       if (IHDR(inode)->h_magic == EXT2_EXT_ATTR_MAGIC) {
+               error = ext2fs_attr_check_names(is->s.first, is->s.end);
                if (error)
                        return error;
                /* Find the named attribute. */
@@ -575,7 +572,6 @@ static errcode_t ext2fs_attr_ibody_set(ext2_filsys fs,
                                       struct ext2_attr_info *i,
                                       struct ext2_attr_ibody_find *is)
 {
-       __u32 *eamagic;
        struct ext2_attr_search *s = &is->s;
        errcode_t error;
 
@@ -586,11 +582,10 @@ static errcode_t ext2fs_attr_ibody_set(ext2_filsys fs,
        if (error)
                return error;
 
-       eamagic = IHDR(inode);
        if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
-               *eamagic = EXT2_EXT_ATTR_MAGIC;
+               IHDR(inode)->h_magic = EXT2_EXT_ATTR_MAGIC;
        else
-               *eamagic = 0;
+               IHDR(inode)->h_magic = 0;
 
        return ext2fs_write_inode_full(fs, is->ino, (struct ext2_inode *)inode,
                                       EXT2_INODE_SIZE(fs->super));
@@ -755,6 +750,7 @@ static errcode_t ext2fs_attr_block_get(ext2_filsys fs, struct ext2_inode *inode,
                }
                memcpy(buffer, block_buf + entry->e_value_offs,
                       entry->e_value_size);
+               error = 0;
        }
 
 cleanup:
@@ -763,6 +759,22 @@ cleanup:
        return error;
 }
 
+static errcode_t ext2fs_attr_check_ibody(ext2_filsys fs,
+                                        struct ext2_inode_large *inode)
+{
+       const int inode_size = EXT2_INODE_SIZE(fs->super);
+
+       if (inode_size == EXT2_GOOD_OLD_INODE_SIZE)
+               return EXT2_ET_EA_NAME_NOT_FOUND;
+
+       if (IHDR(inode)->h_magic != EXT2_EXT_ATTR_MAGIC)
+               return EXT2_ET_EA_BAD_MAGIC;
+
+       return ext2fs_attr_check_names(&IHDR(inode)->h_first_entry[0],
+                                      (char *)inode + inode_size);
+}
+
+
 static errcode_t ext2fs_attr_ibody_get(ext2_filsys fs,
                                       struct ext2_inode_large *inode,
                                       int name_index, const char *name,
@@ -771,26 +783,16 @@ static errcode_t ext2fs_attr_ibody_get(ext2_filsys fs,
 {
        struct ext2_ext_attr_entry *entry;
        int error;
-       char *end, *start;
-       __u32 *eamagic;
-
-       if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
-               return EXT2_ET_EA_NAME_NOT_FOUND;
 
-       eamagic = IHDR(inode);
-       error = ext2fs_attr_check_block(fs, buffer);
+       error = ext2fs_attr_check_ibody(fs, inode);
        if (error)
                return error;
 
-       start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
-                       inode->i_extra_isize + sizeof(__u32);
-       entry = (struct ext2_ext_attr_entry *)start;
-       end = (char *)inode + EXT2_INODE_SIZE(fs->super);
-       error = ext2fs_attr_check_names(entry, end);
-       if (error)
-               goto cleanup;
+       entry = &IHDR(inode)->h_first_entry[0];
+
        error = ext2fs_attr_find_entry(&entry, name_index, name,
-                                      end - (char *)entry, 0);
+                               (char *)inode + EXT2_INODE_SIZE(fs->super) -
+                               (char *)entry, 0);
        if (error)
                goto cleanup;
        if (easize)
@@ -800,7 +802,8 @@ static errcode_t ext2fs_attr_ibody_get(ext2_filsys fs,
                        error = EXT2_ET_EA_TOO_BIG;
                        goto cleanup;
                }
-               memcpy(buffer, start + entry->e_value_offs,entry->e_value_size);
+               memcpy(buffer, (char *)&IHDR(inode)->h_first_entry[0] +
+                              entry->e_value_offs, entry->e_value_size);
        }
 
 cleanup:
@@ -817,7 +820,7 @@ errcode_t ext2fs_attr_get(ext2_filsys fs, struct ext2_inode *inode,
        error = ext2fs_attr_ibody_get(fs, (struct ext2_inode_large *)inode,
                                      name_index, name, buffer, buffer_size,
                                      easize);
-       if (error == EXT2_ET_EA_NAME_NOT_FOUND)
+       if (error == EXT2_ET_EA_NAME_NOT_FOUND || error == EXT2_ET_EA_BAD_MAGIC)
                error = ext2fs_attr_block_get(fs, inode, name_index, name,
                                              buffer, buffer_size, easize);
 
@@ -860,7 +863,6 @@ errcode_t ext2fs_expand_extra_isize(ext2_filsys fs, ext2_ino_t ino,
                                    int *needed_size)
 {
        struct ext2_inode *inode_buf = NULL;
-       __u32 *eamagic = NULL;
        struct ext2_ext_attr_header *header = NULL;
        struct ext2_ext_attr_entry *entry = NULL, *last = NULL;
        struct ext2_attr_ibody_find is = {
@@ -898,10 +900,9 @@ retry:
        if (inode->i_extra_isize >= new_extra_isize)
                goto cleanup;
 
-       eamagic = IHDR(inode);
        start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize;
        /* No extended attributes present */
-       if (*eamagic != EXT2_EXT_ATTR_MAGIC) {
+       if (IHDR(inode)->h_magic != EXT2_EXT_ATTR_MAGIC) {
                memset(start, 0,
                       EXT2_INODE_SIZE(fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
                       inode->i_extra_isize);
diff --git a/lib/ext2fs/tst_read_ea.c b/lib/ext2fs/tst_read_ea.c
new file mode 100644 (file)
index 0000000..21fdf9b
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * tst_getsize.c --- this function tests the getsize function
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE    /* for asprintf */
+#endif
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#else
+/* This is just a test program, let's try to work around the lack of header */
+extern ssize_t fgetxattr (int __filedes, const char *__name,
+                               void *__value, size_t __size);
+extern int fsetxattr (int __filedes, const char *__name,
+                     const void *__value, size_t __size, int __flags);
+#endif
+#if HAVE_MNTENT_H
+#include <mntent.h>
+#include <assert.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#define NR_XATTRS 256
+char tmpvalue[NR_XATTRS + 1];
+
+struct ea {
+       char *name;
+       char *value;
+};
+
+struct ea *ea_table;
+
+static void init_ea_table(void)
+{
+       int i;
+
+       ea_table = malloc(sizeof(struct ea) * NR_XATTRS);
+       if (ea_table == NULL) {
+               perror("maloc failed");
+               exit(1);
+       }
+       for (i = 0; i < NR_XATTRS; i ++) {
+               ea_table[i].name = malloc(i + 2 + strlen("user."));
+               if (ea_table[i].name == NULL) {
+                       perror("malloc failed");
+                       exit(1);
+               }
+               strcpy(ea_table[i].name, "user.");
+               memset(ea_table[i].name + strlen("user."), 'X', i + 1);
+               ea_table[i].name[i + 1 + strlen("user.")] = 0;
+
+               ea_table[i].value = malloc(NR_XATTRS - i + 1);
+               if (ea_table[i].value == NULL) {
+                       perror("malloc failed");
+                       exit(1);
+               }
+               memset(ea_table[i].value, 'Y', NR_XATTRS - i);
+               ea_table[i].value[NR_XATTRS - i] = 0;
+       }
+}
+
+static int set_xattrs(int fd)
+{
+       int i;
+
+       for (i = 0; i < NR_XATTRS; i ++) {
+               if (fsetxattr(fd, ea_table[i].name, ea_table[i].value,
+                             NR_XATTRS - i + 1, XATTR_CREATE) == -1) {
+                       if (errno != ENOSPC) {
+                               perror("fsetxattr failed");
+                               exit(1);
+                       }
+                       break;
+               }
+       }
+       printf("\t%d xattrs are set\n", i);
+       return i;
+}
+
+void get_xattrs1(int fd, int nr)
+{
+       int i;
+       ssize_t size;
+
+       printf("\ttesting fgetxattr .. "); fflush(stdout);
+
+       for (i = 0; i < nr; i ++) {
+               size = fgetxattr(fd, ea_table[i].name, tmpvalue,
+                                NR_XATTRS - i + 1);
+               if (size == -1) {
+                       perror("fgetxattr failed");
+                       exit(1);
+               }
+               if (memcmp(ea_table[i].value, tmpvalue, nr - i + 1)) {
+                       fprintf(stderr, "value mismatch");
+                       exit(1);
+               }
+       }
+
+       printf("%d xattrs are checked, ok\n", i);
+}
+
+void get_xattrs2(const char *device, ext2_ino_t ino, int nr)
+{
+       ext2_filsys fs;
+       int i;
+       struct ext2_inode *inode;
+       errcode_t err;
+       int size;
+
+       printf("\ttesting ext2fs_attr_get .. "); fflush(stdout);
+
+       err = ext2fs_open(device, 0, 0, 0, unix_io_manager, &fs);
+       assert(err == 0);
+
+       err = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode);
+       if (err) {
+               com_err("get_xattrs2", err, "allocating memory");
+               exit(1);
+       }
+
+       err = ext2fs_read_inode_full(fs, ino, inode,
+                                    EXT2_INODE_SIZE(fs->super));
+       if (err) {
+               com_err("get_xattrs2", err, "reading inode");
+               exit(1);
+       }
+       for (i = 0; i < nr; i ++) {
+               err = ext2fs_attr_get(fs, inode, EXT2_ATTR_INDEX_USER,
+                                     ea_table[i].name + strlen("user."),
+                                     tmpvalue, sizeof(tmpvalue), &size);
+               if (err) {
+                       com_err("get_xattrs2", err, "getting xattr");
+                       exit(1);
+               }
+               assert(size == (NR_XATTRS - i + 1));
+
+               if (memcmp(ea_table[i].value, tmpvalue, size)) {
+                       fprintf(stderr, "value mismatch");
+                       exit(1);
+               }
+       }
+       ext2fs_close(fs);
+
+       printf("%d xattrs are checked, ok\n", i);
+}
+#endif /* HAVE_MNTENT_H */
+
+int main(int argc, const char *argv[])
+{
+#if HAVE_MNTENT_H
+       ext2_filsys fs;
+       FILE *f;
+       struct mntent *mnt;
+       char *name;
+       int fd;
+       errcode_t err;
+       struct stat st;
+       int nr;
+       int tested = 0;
+
+       initialize_ext2_error_table();
+
+       init_ea_table();
+
+       f = setmntent(MOUNTED, "r");
+       if (!f) {
+               fprintf(stderr, "failed to setmntent\n");
+               return 1;
+       }
+
+       while ((mnt = getmntent(f)) != NULL) {
+               if (hasmntopt(mnt, "user_xattr") == NULL)
+                       continue;
+               err = ext2fs_open(mnt->mnt_fsname, 0, 0, 0,
+                                 unix_io_manager, &fs);
+               if (err) {
+                       com_err("tst_read_ea", err, "opening fs %s:%s",
+                               mnt->mnt_fsname, mnt->mnt_type);
+                       continue;
+               }
+               ext2fs_close(fs);
+
+               printf("type=%s, fsname=%s, mtpt=%s\n", mnt->mnt_type,
+                      mnt->mnt_fsname, mnt->mnt_dir);
+
+               asprintf(&name, "%s/readeaXXXXXX", mnt->mnt_dir);
+               fd = mkstemp(name);
+               if (fd == -1) {
+                       fprintf(stderr, "tst_read_ea: mkstemp failed on %s: %s",
+                               name, strerror(errno));
+                       continue;
+               }
+               if (fstat(fd, &st)) {
+                       fprintf(stderr, "tst_read_ea: fstat failed on %s: %s\n",
+                               name, strerror(errno));
+                       exit(1);
+               }
+               nr = set_xattrs(fd);
+
+               sync();
+               get_xattrs1(fd, nr);
+               close(fd);
+
+               get_xattrs2(mnt->mnt_fsname, st.st_ino, nr);
+
+               unlink(name);
+               free(name);
+               tested = 1;
+       }
+       endmntent(f);
+
+       if (!tested)
+               fprintf(stderr,
+                       "\tno ext2 based filesystems mounted with user_xattr\n"
+                       "\thope it is ok\n");
+#endif /* HAVE_MNTENT_H */
+       return 0;
+}