]> git.wh0rd.org - dump.git/commitdiff
Added EA/ACL support in dump and restore.
authorStelian Pop <stelian@popies.net>
Mon, 2 May 2005 15:10:43 +0000 (15:10 +0000)
committerStelian Pop <stelian@popies.net>
Mon, 2 May 2005 15:10:43 +0000 (15:10 +0000)
14 files changed:
CHANGES
TODO
compat/include/bsdcompat.h
compat/include/protocols/dumprestore.h
config.h.in
configure
configure.in
dump/traverse.c
restore/Makefile.in
restore/dirs.c
restore/extern.h
restore/restore.h
restore/tape.c
restore/xattr.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index d6d93ff0bea45300ee43b4ead586f709d757eaaa..49f1446b2eb5fcaccf12605f90b4fd2927588f0e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,4 @@
-$Id: CHANGES,v 1.273 2005/03/18 22:12:54 stelian Exp $
+$Id: CHANGES,v 1.274 2005/05/02 15:10:43 stelian Exp $
 
 Changes between versions 0.4b39 and 0.4b40 (released ????????????????)
 ======================================================================
 
 Changes between versions 0.4b39 and 0.4b40 (released ????????????????)
 ======================================================================
@@ -24,6 +24,10 @@ Changes between versions 0.4b39 and 0.4b40 (released ????????????????)
        implementation. This one caused restore to stop saying
        "removenode: non-empty directory" in some cases.
 
        implementation. This one caused restore to stop saying
        "removenode: non-empty directory" in some cases.
 
+5.     Added support for dumping and restoring ext2/3 extended
+       attributes (EA), like the access control lists (ACL) or
+       the security labels used by SELinux.
+
 Changes between versions 0.4b38 and 0.4b39 (released January 21, 2005)
 ======================================================================
 
 Changes between versions 0.4b38 and 0.4b39 (released January 21, 2005)
 ======================================================================
 
diff --git a/TODO b/TODO
index d3d4102b842328f813ba7ae85fddfff29b79153b..349b1d15e0c9421445f06b84ca98d80804334d4f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,4 +1,4 @@
-$Id: TODO,v 1.24 2002/01/25 14:59:53 stelian Exp $
+$Id: TODO,v 1.25 2005/05/02 15:10:44 stelian Exp $
 
 Need to verify:
 ---------------
 
 Need to verify:
 ---------------
@@ -31,7 +31,4 @@ All others:
 
 5.     Make a bootable dump tape? I don't know if it is possible...
 
 
 5.     Make a bootable dump tape? I don't know if it is possible...
 
-6.     EA/ACL support in dump (requested by Michael Ju. Tokarev 
-       <mjt@tls.msk.ru>.
-
-7.     Better readline completition in restore (escape spaces etc).
+6.     Better readline completition in restore (escape spaces etc).
index 4b9b9077ac6edacbf38a9e27e2f793a5122a9b52..7bc216038c96b34f9b0804e140a0cbe1699e4e72 100644 (file)
@@ -5,7 +5,7 @@
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
- *     $Id: bsdcompat.h,v 1.23 2004/07/01 09:14:48 stelian Exp $
+ *     $Id: bsdcompat.h,v 1.24 2005/05/02 15:10:45 stelian Exp $
  */
 
 #include <config.h>
  */
 
 #include <config.h>
@@ -130,6 +130,8 @@ struct dinode {
        __u16   di_uidhigh;
        __u16   di_gidhigh;
        __u32   di_spare;
        __u16   di_uidhigh;
        __u16   di_gidhigh;
        __u32   di_spare;
+       __u16   di_extraisize;
+       __u16   di_pad2;
 };
 
 #define di_rdev                di_db[0]
 };
 
 #define di_rdev                di_db[0]
index 981b9f6014970c5b4b3b013100092eeee9899555..816a83f508a33e336ba30b9c74f92876a658b1dd 100644 (file)
@@ -5,7 +5,7 @@
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
- *     $Id: dumprestore.h,v 1.23 2004/12/15 11:00:01 stelian Exp $
+ *     $Id: dumprestore.h,v 1.24 2005/05/02 15:10:46 stelian Exp $
  */
 
 /*
  */
 
 /*
@@ -152,7 +152,6 @@ union u_spcl {
 #define EXT_MACOSRESFORK       2
 #define EXT_XATTR              3
 
 #define EXT_MACOSRESFORK       2
 #define EXT_XATTR              3
 
-
 /*
  * compression flags for the tapebuf header.
  */
 /*
  * compression flags for the tapebuf header.
  */
@@ -172,4 +171,9 @@ struct tapebuf {
 #endif
 };
 
 #endif
 };
 
+/* used for EA on tape */
+#define EXT2_GOOD_OLD_INODE_SIZE       128
+#define EXT2_XATTR_MAGIC               0xEA020000      /* block EA */
+#define EXT2_XATTR_MAGIC2              0xEA020001      /* in inode EA */
+
 #endif /* !_DUMPRESTORE_H_ */
 #endif /* !_DUMPRESTORE_H_ */
index 799b2f0ddf4c9cb9c4b4dab4ed25d32b9b2bf294..828ef11b7b5fbfcb871e297ac6360f0208cab18b 100644 (file)
 /* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
 #undef HAVE_EXT2FS_EXT2_FS_H
 
 /* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
 #undef HAVE_EXT2FS_EXT2_FS_H
 
+/* Define this if your ext2fs libs have the ext2fs_read_inode_full function.
+   */
+#undef HAVE_EXT2FS_READ_INODE_FULL
+
 /* Define if we have the ext2_ino_t type (from e2fsprogs 1.20+). */
 #undef HAVE_EXT2_INO_T
 
 /* Define if we have the ext2_ino_t type (from e2fsprogs 1.20+). */
 #undef HAVE_EXT2_INO_T
 
index 2a7478f0b2830d0dde38cea0a05e12b39fef5272..290a7fd2a9ffb49018978a3552e6db79bd28df31 100755 (executable)
--- a/configure
+++ b/configure
@@ -3936,6 +3936,84 @@ echo "$as_me: error: You need to install the Ext2fs libraries from the E2fsprogs
    { (exit 1); exit 1; }; }
 fi
 
    { (exit 1); exit 1; }; }
 fi
 
+echo "$as_me:$LINENO: checking for ext2fs_read_inode_full in -lext2fs" >&5
+echo $ECHO_N "checking for ext2fs_read_inode_full in -lext2fs... $ECHO_C" >&6
+if test "${ac_cv_lib_ext2fs_ext2fs_read_inode_full+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lext2fs -lcom_err $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char ext2fs_read_inode_full ();
+int
+main ()
+{
+ext2fs_read_inode_full ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_ext2fs_ext2fs_read_inode_full=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_ext2fs_ext2fs_read_inode_full=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_ext2fs_ext2fs_read_inode_full" >&5
+echo "${ECHO_T}$ac_cv_lib_ext2fs_ext2fs_read_inode_full" >&6
+if test $ac_cv_lib_ext2fs_ext2fs_read_inode_full = yes; then
+  rif=yes
+else
+  rif=no
+fi
+
+if test "$rif" = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_EXT2FS_READ_INODE_FULL 1
+_ACEOF
+
+fi
+
 
 for ac_header in ext2fs/ext2_fs.h
 do
 
 for ac_header in ext2fs/ext2_fs.h
 do
index 57b2a090601942a72b0271b78b1e1c8055787eea..3788737049e9edfe9a492949e03fc264e5f6f2d8 100644 (file)
@@ -393,6 +393,14 @@ if test "$ext2fs_h" = no -o "$ext2fs_lib" = no; then
        AC_MSG_ERROR(You need to install the Ext2fs libraries from the E2fsprogs distribution first - hint: make install-libs)
 fi
 
        AC_MSG_ERROR(You need to install the Ext2fs libraries from the E2fsprogs distribution first - hint: make install-libs)
 fi
 
+dnl
+dnl Check for ext2fs_read_inode_full
+dnl
+AC_CHECK_LIB(ext2fs, ext2fs_read_inode_full, [rif=yes], [rif=no], [-lcom_err])
+if test "$rif" = yes; then
+       AC_DEFINE([HAVE_EXT2FS_READ_INODE_FULL],1,[Define this if your ext2fs libs have the ext2fs_read_inode_full function.])
+fi
+
 dnl
 dnl Try to use ext2_fs.h header from libext2fs instead of from the kernel
 dnl
 dnl
 dnl Try to use ext2_fs.h header from libext2fs instead of from the kernel
 dnl
index fe5af09c2a839a74abcf76c5fdc3a065c91c42e4..64c32a5b75fc253e0ef4864c58f9c9c1b3fe81e3 100644 (file)
@@ -37,7 +37,7 @@
 
 #ifndef lint
 static const char rcsid[] =
 
 #ifndef lint
 static const char rcsid[] =
-       "$Id: traverse.c,v 1.65 2005/01/25 13:33:44 stelian Exp $";
+       "$Id: traverse.c,v 1.66 2005/05/02 15:10:46 stelian Exp $";
 #endif /* not lint */
 
 #include <config.h>
 #endif /* not lint */
 
 #include <config.h>
@@ -100,6 +100,7 @@ static      void dmpindir __P((dump_ino_t ino, daddr_t blk, int level, fsizeT *size))
 static int searchdir __P((dump_ino_t ino, daddr_t blkno, long size, long filesize));
 #endif
 static void mapfileino __P((dump_ino_t ino, struct dinode const *dp, long *tapesize, int *dirskipped));
 static int searchdir __P((dump_ino_t ino, daddr_t blkno, long size, long filesize));
 #endif
 static void mapfileino __P((dump_ino_t ino, struct dinode const *dp, long *tapesize, int *dirskipped));
+static void dump_xattr __P((dump_ino_t ino, struct dinode *dp));
 
 #ifdef HAVE_EXT2_JOURNAL_INUM
 #define ext2_journal_ino(sb) (sb->s_journal_inum)
 
 #ifdef HAVE_EXT2_JOURNAL_INUM
 #define ext2_journal_ino(sb) (sb->s_journal_inum)
@@ -796,6 +797,68 @@ dumponeblock(UNUSED(ext2_filsys fs), blk_t *blocknr, e2_blkcnt_t blockcnt,
 }
 #endif
 
 }
 #endif
 
+static void
+dump_xattr(dump_ino_t ino, struct dinode *dp) {
+
+       if (dp->di_extraisize != 0) {
+#ifdef HAVE_EXT2FS_READ_INODE_FULL
+               char inode[EXT2_INODE_SIZE(fs->super)];
+               errcode_t err;
+               u_int32_t *magic;
+
+               memset(inode, 0, EXT2_INODE_SIZE(fs->super));
+               err = ext2fs_read_inode_full(fs, (ext2_ino_t)ino,
+                                            (struct ext2_inode *) inode,
+                                            EXT2_INODE_SIZE(fs->super));
+               if (err) {
+                       com_err(disk, err, "while reading inode #%ld\n", (long)ino);
+                       exit(X_ABORT);
+               }
+
+               magic = (void *)inode + EXT2_GOOD_OLD_INODE_SIZE + dp->di_extraisize;
+               if (*magic == EXT2_XATTR_MAGIC) {
+                       char xattr[EXT2_INODE_SIZE(fs->super)];
+                       int i;
+                       char *cp;
+
+                       if (vflag)
+                               msg("dumping EA (inode) in inode #%ld\n", (long)ino);
+                       memset(xattr, 0, EXT2_INODE_SIZE(fs->super));
+                       memcpy(xattr, (void *)magic,
+                              EXT2_INODE_SIZE(fs->super) - 
+                              (EXT2_GOOD_OLD_INODE_SIZE + dp->di_extraisize));
+                       magic = (u_int32_t *)xattr;
+                       *magic = EXT2_XATTR_MAGIC2;
+
+                       spcl.c_type = TS_INODE;
+                       spcl.c_dinode.di_size = EXT2_INODE_SIZE(fs->super);
+                       spcl.c_flags |= DR_EXTATTRIBUTES;
+                       spcl.c_extattributes = EXT_XATTR;
+                       spcl.c_count = howmany(EXT2_INODE_SIZE(fs->super), TP_BSIZE);
+                       writeheader(ino);
+                       for (i = 0, cp = xattr; i < spcl.c_count; i++, cp += TP_BSIZE)
+                               writerec(cp, 0);
+                       spcl.c_flags &= ~DR_EXTATTRIBUTES;
+                       spcl.c_extattributes = 0;
+               }
+#endif
+       }
+
+       if (dp->di_file_acl) {
+
+               if (vflag)
+                       msg("dumping EA (block) in inode #%ld\n", (long)ino);
+
+               spcl.c_type = TS_INODE;
+               spcl.c_dinode.di_size = sblock->fs_bsize;
+               spcl.c_flags |= DR_EXTATTRIBUTES;
+               spcl.c_extattributes = EXT_XATTR;
+               blksout(&dp->di_file_acl, EXT2_FRAGS_PER_BLOCK(fs->super), ino);
+               spcl.c_flags &= ~DR_EXTATTRIBUTES;
+               spcl.c_extattributes = 0;
+       }
+}
+
 /*
  * Dump passes 3 and 4.
  *
 /*
  * Dump passes 3 and 4.
  *
@@ -842,8 +905,6 @@ dumpino(struct dinode *dp, dump_ino_t ino, int metaonly)
        nbi.di_gen = dp->di_gen;
        nbi.di_uid = (((int32_t)dp->di_uidhigh) << 16) | dp->di_uid;
        nbi.di_gid = (((int32_t)dp->di_gidhigh) << 16) | dp->di_gid;
        nbi.di_gen = dp->di_gen;
        nbi.di_uid = (((int32_t)dp->di_uidhigh) << 16) | dp->di_uid;
        nbi.di_gid = (((int32_t)dp->di_gidhigh) << 16) | dp->di_gid;
-       if (dp->di_file_acl)
-               msg("ACLs in inode #%ld won't be dumped\n", (long)ino);
        memmove(&spcl.c_dinode, &nbi, sizeof(nbi));
 #else  /* __linux__ */
        spcl.c_dinode = *dp;
        memmove(&spcl.c_dinode, &nbi, sizeof(nbi));
 #else  /* __linux__ */
        spcl.c_dinode = *dp;
@@ -856,6 +917,7 @@ dumpino(struct dinode *dp, dump_ino_t ino, int metaonly)
                spcl.c_count = 0;
                writeheader(ino);
                spcl.c_flags &= ~DR_METAONLY;
                spcl.c_count = 0;
                writeheader(ino);
                spcl.c_flags &= ~DR_METAONLY;
+               dump_xattr(ino, dp);
                return;
        }
 
                return;
        }
 
@@ -886,6 +948,7 @@ dumpino(struct dinode *dp, dump_ino_t ino, int metaonly)
                        memmove(buf, dp->di_db, (u_long)dp->di_size);
                        buf[dp->di_size] = '\0';
                        writerec(buf, 0);
                        memmove(buf, dp->di_db, (u_long)dp->di_size);
                        buf[dp->di_size] = '\0';
                        writerec(buf, 0);
+                       dump_xattr(ino, dp);
                        return;
                }
 #endif /* __linux__ */
                        return;
                }
 #endif /* __linux__ */
@@ -916,6 +979,7 @@ dumpino(struct dinode *dp, dump_ino_t ino, int metaonly)
        case S_IFCHR:
        case S_IFBLK:
                writeheader(ino);
        case S_IFCHR:
        case S_IFBLK:
                writeheader(ino);
+               dump_xattr(ino, dp);
                return;
 
        default:
                return;
 
        default:
@@ -931,8 +995,10 @@ dumpino(struct dinode *dp, dump_ino_t ino, int metaonly)
        else
                cnt = howmany(i_size, sblock->fs_fsize);
        blksout(&dp->di_db[0], cnt, ino);
        else
                cnt = howmany(i_size, sblock->fs_fsize);
        blksout(&dp->di_db[0], cnt, ino);
-       if ((quad_t) (size = i_size - NDADDR * sblock->fs_bsize) <= 0)
+       if ((quad_t) (size = i_size - NDADDR * sblock->fs_bsize) <= 0) {
+               dump_xattr(ino, dp);
                return;
                return;
+       }
 #ifdef __linux__
        bc.max = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
        bc.buf = (int *)malloc (bc.max * sizeof (int));
 #ifdef __linux__
        bc.max = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
        bc.buf = (int *)malloc (bc.max * sizeof (int));
@@ -956,6 +1022,7 @@ dumpino(struct dinode *dp, dump_ino_t ino, int metaonly)
                blksout (bc.buf, bc.cnt, bc.ino);
        }
        free(bc.buf);
                blksout (bc.buf, bc.cnt, bc.ino);
        }
        free(bc.buf);
+       dump_xattr(ino, dp);
 #else
        for (ind_level = 0; ind_level < NIADDR; ind_level++) {
                dmpindir(ino, dp->di_ib[ind_level], ind_level, &size);
 #else
        for (ind_level = 0; ind_level < NIADDR; ind_level++) {
                dmpindir(ino, dp->di_ib[ind_level], ind_level, &size);
@@ -1104,8 +1171,6 @@ dumpdirino(struct dinode *dp, dump_ino_t ino)
        nbi.di_gen = dp->di_gen;
        nbi.di_uid = (((int32_t)dp->di_uidhigh) << 16) | dp->di_uid;
        nbi.di_gid = (((int32_t)dp->di_gidhigh) << 16) | dp->di_gid;
        nbi.di_gen = dp->di_gen;
        nbi.di_uid = (((int32_t)dp->di_uidhigh) << 16) | dp->di_uid;
        nbi.di_gid = (((int32_t)dp->di_gidhigh) << 16) | dp->di_gid;
-       if (dp->di_file_acl)
-               msg("ACLs in inode #%ld won't be dumped\n", (long)ino);
        memmove(&spcl.c_dinode, &nbi, sizeof(nbi));
 #else  /* __linux__ */
        spcl.c_dinode = *dp;
        memmove(&spcl.c_dinode, &nbi, sizeof(nbi));
 #else  /* __linux__ */
        spcl.c_dinode = *dp;
@@ -1142,6 +1207,7 @@ dumpdirino(struct dinode *dp, dump_ino_t ino)
        }
 
        (void)free(cdc.buf);
        }
 
        (void)free(cdc.buf);
+       dump_xattr(ino, dp);
 }
 #endif /* __linux__ */
 
 }
 #endif /* __linux__ */
 
@@ -1299,7 +1365,11 @@ getino(dump_ino_t inum)
        errcode_t err;
 
        curino = inum;
        errcode_t err;
 
        curino = inum;
+#ifdef HAVE_EXT2FS_READ_INODE_FULL
+       err = ext2fs_read_inode_full(fs, (ext2_ino_t)inum, (struct ext2_inode *) &dinode, sizeof(struct dinode));
+#else
        err = ext2fs_read_inode(fs, (ext2_ino_t)inum, (struct ext2_inode *) &dinode);
        err = ext2fs_read_inode(fs, (ext2_ino_t)inum, (struct ext2_inode *) &dinode);
+#endif
        if (err) {
                com_err(disk, err, "while reading inode #%ld\n", (long)inum);
                exit(X_ABORT);
        if (err) {
                com_err(disk, err, "while reading inode #%ld\n", (long)inum);
                exit(X_ABORT);
index cdc2004cf1076daf784e0e939990ac04781cdacb..79e03ea609065b0e1a458734e3565386d05be94f 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.12 2003/05/08 21:11:39 stelian Exp $
+# $Id: Makefile.in,v 1.13 2005/05/02 15:10:46 stelian Exp $
 
 top_srcdir=    @top_srcdir@
 srcdir=                @srcdir@
 
 top_srcdir=    @top_srcdir@
 srcdir=                @srcdir@
@@ -16,9 +16,9 @@ PROG=         restore
 RPROG=         rrestore
 LINKS=         ${SBINDIR}/restore ${SBINDIR}/rrestore
 SRCS=          dirs.c interactive.c main.c restore.c symtab.c tape.c \
 RPROG=         rrestore
 LINKS=         ${SBINDIR}/restore ${SBINDIR}/rrestore
 SRCS=          dirs.c interactive.c main.c restore.c symtab.c tape.c \
-               utilities.c
+               utilities.c xattr.c
 OBJS=          dirs.o interactive.o main.o restore.o symtab.o tape.o \
 OBJS=          dirs.o interactive.o main.o restore.o symtab.o tape.o \
-               utilities.o ../common/dumprmt.o
+               utilities.o xattr.o ../common/dumprmt.o
 MAN8=          restore.8
 RMAN8=         rrestore.8
 
 MAN8=          restore.8
 RMAN8=         rrestore.8
 
index 26c637bffb69bebb46a58df6bf5c0869d6da8bc9..b049f49007801c6caa8c70d755b38ecce9dbc860 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static const char rcsid[] =
 
 #ifndef lint
 static const char rcsid[] =
-       "$Id: dirs.c,v 1.31 2005/01/24 10:32:14 stelian Exp $";
+       "$Id: dirs.c,v 1.32 2005/05/02 15:10:46 stelian Exp $";
 #endif /* not lint */
 
 #include <config.h>
 #endif /* not lint */
 
 #include <config.h>
@@ -114,6 +114,7 @@ struct modeinfo {
        uid_t uid;
        gid_t gid;
        unsigned int flags;
        uid_t uid;
        gid_t gid;
        unsigned int flags;
+       char xattr;
 };
 
 /*
 };
 
 /*
@@ -149,10 +150,10 @@ struct odirect {
 
 #if defined(__linux__) || defined(sunos)
 static struct inotab   *allocinotab __P((dump_ino_t, OFF_T));
 
 #if defined(__linux__) || defined(sunos)
 static struct inotab   *allocinotab __P((dump_ino_t, OFF_T));
-static void             savemodeinfo __P((dump_ino_t, struct new_bsd_inode *));
+static void            savemodeinfo __P((dump_ino_t, struct new_bsd_inode *, char *));
 #else
 static struct inotab   *allocinotab __P((dump_ino_t, OFF_T));
 #else
 static struct inotab   *allocinotab __P((dump_ino_t, OFF_T));
-static void             savemodeinfo __P((dump_ino_t, struct dinode *));
+static void            savemodeinfo __P((dump_ino_t, struct dinode *, char *));
 #endif
 static void             dcvt __P((struct odirect *, struct direct *));
 static void             flushent __P((void));
 #endif
 static void             dcvt __P((struct odirect *, struct direct *));
 static void             flushent __P((void));
@@ -186,6 +187,8 @@ extractdirs(int genmode)
        struct inotab *itp;
        struct direct nulldir;
        int fd;
        struct inotab *itp;
        struct direct nulldir;
        int fd;
+       char xattr[XATTR_MAXSIZE];
+       int xattr_found = 0;
        dump_ino_t ino;
 
        Vprintf(stdout, "Extract directories from tape\n");
        dump_ino_t ino;
 
        Vprintf(stdout, "Extract directories from tape\n");
@@ -242,6 +245,7 @@ extractdirs(int genmode)
                memcpy(&ip, curfile.dip, sizeof(ip));
                itp = allocinotab(ino, seekpt);
                getfile(putdir, xtrnull);
                memcpy(&ip, curfile.dip, sizeof(ip));
                itp = allocinotab(ino, seekpt);
                getfile(putdir, xtrnull);
+               xattr_found = 0;
                while (spcl.c_flags & DR_EXTATTRIBUTES) {
                        switch (spcl.c_extattributes) {
                        case EXT_MACOSFNDRINFO:
                while (spcl.c_flags & DR_EXTATTRIBUTES) {
                        switch (spcl.c_extattributes) {
                        case EXT_MACOSFNDRINFO:
@@ -253,12 +257,15 @@ extractdirs(int genmode)
                                skipfile();
                                break;
                        case EXT_XATTR:
                                skipfile();
                                break;
                        case EXT_XATTR:
-                               msg("EA/ACLs attributes not supported, skipping\n");
-                               skipfile();
+                               if (readxattr(xattr) == GOOD)
+                                       xattr_found = 1;
                                break;
                        }
                }
                                break;
                        }
                }
-               savemodeinfo(ino, &ip);
+               if (xattr_found)
+                       savemodeinfo(ino, &ip, xattr);
+               else
+                       savemodeinfo(ino, &ip, NULL);
                putent(&nulldir);
                flushent();
                itp->t_size = seekpt - itp->t_seekpt;
                putent(&nulldir);
                flushent();
                itp->t_size = seekpt - itp->t_seekpt;
@@ -675,9 +682,15 @@ setdirmodes(int flags)
        }
        clearerr(mf);
        for (;;) {
        }
        clearerr(mf);
        for (;;) {
+               char xattr[XATTR_MAXSIZE];
                (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
                if (feof(mf))
                        break;
                (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
                if (feof(mf))
                        break;
+               if (node.xattr) {
+                       (void) fread(xattr, 1, XATTR_MAXSIZE, mf);
+                       if (feof(mf))
+                               break;
+               }
                ep = lookupino(node.ino);
                if (command == 'i' || command == 'x') {
                        if (ep == NULL)
                ep = lookupino(node.ino);
                if (command == 'i' || command == 'x') {
                        if (ep == NULL)
@@ -707,6 +720,8 @@ setdirmodes(int flags)
 #endif
 #endif
                        utimes(cp, node.timep);
 #endif
 #endif
                        utimes(cp, node.timep);
+                       if (node.xattr)
+                               xattr_extract(cp, xattr);
                        ep->e_flags &= ~NEW;
                }
        }
                        ep->e_flags &= ~NEW;
                }
        }
@@ -741,9 +756,15 @@ comparedirmodes(void)
        }
        clearerr(mf);
        for (;;) {
        }
        clearerr(mf);
        for (;;) {
+               char xattr[XATTR_MAXSIZE];
                (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
                if (feof(mf))
                        break;
                (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
                if (feof(mf))
                        break;
+               if (node.xattr) {
+                       (void) fread(xattr, 1, XATTR_MAXSIZE, mf);
+                       if (feof(mf))
+                               break;
+               }
                ep = lookupino(node.ino);
                if (ep == NULL) {
                        panic("cannot find directory inode %d\n", node.ino);
                ep = lookupino(node.ino);
                if (ep == NULL) {
                        panic("cannot find directory inode %d\n", node.ino);
@@ -790,6 +811,14 @@ comparedirmodes(void)
                                }
                        }
 #endif
                                }
                        }
 #endif
+                       if (node.xattr) {
+                               if (xattr_compare(cp, xattr) == FAIL)
+                                       do_compare_error;
+                       }
+                       else {
+                               if (xattr_compare(cp, NULL) == FAIL)
+                                       do_compare_error;
+                       }
                        ep->e_flags &= ~NEW;
                }
        }
                        ep->e_flags &= ~NEW;
                }
        }
@@ -874,9 +903,9 @@ allocinotab(dump_ino_t ino, OFF_T seekpt)
 
 static void
 #if defined(__linux__) || defined(sunos)
 
 static void
 #if defined(__linux__) || defined(sunos)
-savemodeinfo(dump_ino_t ino, struct new_bsd_inode *dip) {
+savemodeinfo(dump_ino_t ino, struct new_bsd_inode *dip, char *xattr) {
 #else
 #else
-savemodeinfo(dump_ino_t ino, struct dinode *dip) {
+savemodeinfo(dump_ino_t ino, struct dinode *dip, char *xattr) {
 #endif
        struct modeinfo node;
 
 #endif
        struct modeinfo node;
 
@@ -898,8 +927,12 @@ savemodeinfo(dump_ino_t ino, struct dinode *dip) {
        node.flags = dip->di_flags;
        node.uid = dip->di_uid;
        node.gid = dip->di_gid;
        node.flags = dip->di_flags;
        node.uid = dip->di_uid;
        node.gid = dip->di_gid;
+       node.xattr = xattr ? 1 : 0;
        if ( fwrite((char *)&node, 1, sizeof(struct modeinfo), mf) != sizeof(struct modeinfo) )
                err(1,"cannot write to file %s", modefile);
        if ( fwrite((char *)&node, 1, sizeof(struct modeinfo), mf) != sizeof(struct modeinfo) )
                err(1,"cannot write to file %s", modefile);
+       if (xattr)
+               if ( fwrite(xattr, 1, XATTR_MAXSIZE, mf) != XATTR_MAXSIZE)
+                       err(1,"cannot write to file %s", modefile);
 }
 
 /*
 }
 
 /*
index 60f2f26c71157e86933ac9c2ec53e6e41d355ef8..16e2440e380e9b7d5fa915e183c05433ef8b1d53 100644 (file)
@@ -5,7 +5,7 @@
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
- *     $Id: extern.h,v 1.24 2004/12/15 11:00:01 stelian Exp $
+ *     $Id: extern.h,v 1.25 2005/05/02 15:10:46 stelian Exp $
  */
 
 /*-
  */
 
 /*-
@@ -157,3 +157,6 @@ int CreateAppleDoubleFileRes __P((char *, FndrFileInfo *, mode_t, int, struct ti
 #endif
 
 void   skipxattr __P((void));
 #endif
 
 void   skipxattr __P((void));
+int    readxattr __P((char *));
+int    xattr_compare __P((char *, char *));
+int    xattr_extract __P((char *, char *));
index 7deb3f24f10537e7efd8a8ee75adbf750f68f2fd..4f5236f4ac7466c17bbe8bf3c01a4c959db5e6df 100644 (file)
@@ -5,7 +5,7 @@
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
  *     Stelian Pop <stelian@popies.net>, 1999-2000
  *     Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
  *
- *     $Id: restore.h,v 1.31 2005/01/13 15:41:07 stelian Exp $
+ *     $Id: restore.h,v 1.32 2005/05/02 15:10:46 stelian Exp $
  */
 
 /*
  */
 
 /*
@@ -197,3 +197,4 @@ char        smtcpath[2048];
                exit(2); \
        }
 
                exit(2); \
        }
 
+#define XATTR_MAXSIZE  4096
index ab6b11356300871c62af8dcbe5a16dc9faed102f..e60921bf3b86b1afff5ce0a8666978da04fe89d7 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static const char rcsid[] =
 
 #ifndef lint
 static const char rcsid[] =
-       "$Id: tape.c,v 1.88 2005/02/25 13:44:32 stelian Exp $";
+       "$Id: tape.c,v 1.89 2005/05/02 15:10:46 stelian Exp $";
 #endif /* not lint */
 
 #include <config.h>
 #endif /* not lint */
 
 #include <config.h>
@@ -167,6 +167,7 @@ static void  xtrlnkskip __P((char *, size_t));
 static void     xtrmap __P((char *, size_t));
 static void     xtrmapskip __P((char *, size_t));
 static void     xtrskip __P((char *, size_t));
 static void     xtrmap __P((char *, size_t));
 static void     xtrmapskip __P((char *, size_t));
 static void     xtrskip __P((char *, size_t));
+static void     xtrxattr __P((char *, size_t));
 static void     setmagtapein __P((void));
 static int      extractattr __P((char *));
 static void     compareattr __P((char *));
 static void     setmagtapein __P((void));
 static int      extractattr __P((char *));
 static void     compareattr __P((char *));
@@ -196,6 +197,9 @@ static void xtrcmpskip __P((char *, size_t));
 static int readmapflag;
 static int readingmaps;                /* set to 1 while reading the maps */
 
 static int readmapflag;
 static int readingmaps;                /* set to 1 while reading the maps */
 
+static char xattrbuf[XATTR_MAXSIZE];
+static int xattrlen;
+
 #ifdef DUMP_MACOSX
 static DumpFinderInfo  gFndrInfo;
 #endif
 #ifdef DUMP_MACOSX
 static DumpFinderInfo  gFndrInfo;
 #endif
@@ -1029,10 +1033,14 @@ extractattr(char *path)
                        skipfile();
 #endif
                        break;
                        skipfile();
 #endif
                        break;
-               case EXT_XATTR:
-                       msg("EA/ACLs not supported in this version, skipping\n");
-                       skipfile();
-                       break;
+               case EXT_XATTR: {
+                       char xattr[XATTR_MAXSIZE];
+                       
+                       if (readxattr(xattr) == GOOD) {
+                               xattr_extract(path, xattr);
+                               break;
+                       }
+               }
                default:
                        msg("unexpected inode extension %ld, skipping\n", spcl.c_extattributes);
                        skipfile();
                default:
                        msg("unexpected inode extension %ld, skipping\n", spcl.c_extattributes);
                        skipfile();
@@ -1213,6 +1221,29 @@ extractresourceufs(char *name)
 }
 #endif /* DUMP_MACOSX */
 
 }
 #endif /* DUMP_MACOSX */
 
+int
+readxattr(char *buffer)
+{
+       if (dflag)
+               msg("reading EA data for inode %lu\n", curfile.ino);
+
+       curfile.name = "EA block";
+       if (curfile.dip->di_size > XATTR_MAXSIZE) {
+               fprintf(stderr, "EA size too big (%ld)", (long)curfile.dip->di_size);
+               skipfile();
+               return (FAIL);
+       }
+       
+       memset(xattrbuf, 0, XATTR_MAXSIZE);
+       xattrlen = 0;
+
+       getfile(xtrxattr, xtrnull);
+
+       memcpy(buffer, xattrbuf, XATTR_MAXSIZE);
+
+       return (GOOD);
+}
+
 /*
  * skip over bit maps on the tape
  */
 /*
  * skip over bit maps on the tape
  */
@@ -1480,6 +1511,17 @@ xtrcmpskip(UNUSED(char *buf), size_t size)
 }
 #endif /* COMPARE_ONTHEFLY */
 
 }
 #endif /* COMPARE_ONTHEFLY */
 
+static void
+xtrxattr(char *buf, size_t size)
+{
+       if (xattrlen + size > XATTR_MAXSIZE) {
+               fprintf(stderr, "EA size too big (%ld)", (long)xattrlen + size);
+               return;
+       }
+       memcpy(xattrbuf + xattrlen, buf, size);
+       xattrlen += size;
+}
+
 #if !COMPARE_ONTHEFLY
 static int
 do_cmpfiles(int fd_tape, int fd_disk, OFF_T size)
 #if !COMPARE_ONTHEFLY
 static int
 do_cmpfiles(int fd_tape, int fd_disk, OFF_T size)
@@ -1595,6 +1637,8 @@ cmpfiles(char *tapefile, char *diskfile, struct STAT *sbuf_disk)
 static void
 compareattr(char *name)
 {
 static void
 compareattr(char *name)
 {
+       int xattr_done = 0;
+       
        while (spcl.c_flags & DR_EXTATTRIBUTES) {
                switch (spcl.c_extattributes) {
                case EXT_MACOSFNDRINFO:
        while (spcl.c_flags & DR_EXTATTRIBUTES) {
                switch (spcl.c_extattributes) {
                case EXT_MACOSFNDRINFO:
@@ -1605,16 +1649,26 @@ compareattr(char *name)
                        msg("MacOSX not supported for comparision in this version, skipping\n");
                        skipfile();
                        break;
                        msg("MacOSX not supported for comparision in this version, skipping\n");
                        skipfile();
                        break;
-               case EXT_XATTR:
-                       msg("EA/ACLs not supported for comparision in this version, skipping\n");
-                       skipxattr();
+               case EXT_XATTR: {
+                       char xattr[XATTR_MAXSIZE];
+
+                       if (readxattr(xattr) == GOOD) {
+                               if (xattr_compare(name, xattr) == FAIL)
+                                       do_compare_error;
+                               xattr_done = 1;
+                       }
+                       else
+                               do_compare_error;
                        break;
                        break;
+               }
                default:
                        msg("unexpected inode extension %ld, skipping\n", spcl.c_extattributes);
                        skipfile();
                        break;
                }
        }
                default:
                        msg("unexpected inode extension %ld, skipping\n", spcl.c_extattributes);
                        skipfile();
                        break;
                }
        }
+       if (!xattr_done && xattr_compare(name, NULL) == FAIL)
+               do_compare_error;
 }
 
 #if !COMPARE_ONTHEFLY
 }
 
 #if !COMPARE_ONTHEFLY
diff --git a/restore/xattr.c b/restore/xattr.c
new file mode 100644 (file)
index 0000000..6da39eb
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 1999-2004
+ *     Stelian Pop <stelian@popies.net>, 1999-2004
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+       "$Id: xattr.c,v 1.1 2005/05/02 15:10:47 stelian Exp $";
+#endif /* not lint */
+
+#include <config.h>
+#include <compatlfs.h>
+#include <compaterr.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <bsdcompat.h>
+#include <protocols/dumprestore.h>
+#include "restore.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * Data structures below taken from the kernel
+ */
+
+/* Maximum number of references to one attribute block */
+#define EXT2_XATTR_REFCOUNT_MAX                1024
+
+/* Name indexes */
+#define EXT2_XATTR_INDEX_MAX                   10
+#define EXT2_XATTR_INDEX_USER                  1
+#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS      2
+#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT     3
+#define EXT2_XATTR_INDEX_TRUSTED               4
+#define        EXT2_XATTR_INDEX_LUSTRE                 5
+#define EXT2_XATTR_INDEX_SECURITY              6
+
+struct ext2_xattr_header {
+       u_int32_t       h_magic;        /* magic number for identification */
+       u_int32_t       h_refcount;     /* reference count */
+       u_int32_t       h_blocks;       /* number of disk blocks used */
+       u_int32_t       h_hash;         /* hash value of all attributes */
+       u_int32_t       h_reserved[4];  /* zero right now */
+};
+
+struct ext3_xattr_ibody_header {
+       u_int32_t       h_magic;        /* magic number for identification */
+};
+
+struct ext2_xattr_entry {
+       u_char          e_name_len;     /* length of name */
+       u_char          e_name_index;   /* attribute name index */
+       u_int16_t       e_value_offs;   /* offset in disk block of value */
+       u_int32_t       e_value_block;  /* disk block attribute is stored on (n/i) */
+       u_int32_t       e_value_size;   /* size of attribute value */
+       u_int32_t       e_hash;         /* hash value of name and value */
+       char            e_name[0];      /* attribute name */
+};
+
+#define EXT2_XATTR_PAD_BITS            2
+#define EXT2_XATTR_PAD         (1<<EXT2_XATTR_PAD_BITS)
+#define EXT2_XATTR_ROUND               (EXT2_XATTR_PAD-1)
+#define EXT2_XATTR_LEN(name_len) \
+       (((name_len) + EXT2_XATTR_ROUND + \
+       sizeof(struct ext2_xattr_entry)) & ~EXT2_XATTR_ROUND)
+#define EXT2_XATTR_NEXT(entry) \
+       ( (struct ext2_xattr_entry *)( \
+         (char *)(entry) + EXT2_XATTR_LEN((entry)->e_name_len)) )
+#define EXT3_XATTR_SIZE(size) \
+       (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND)
+
+#define HDR(buffer) ((struct ext2_xattr_header *)(buffer))
+#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr))
+#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+
+#define BFIRST(buffer) ENTRY(HDR(buffer)+1)
+#define IFIRST(buffer) ENTRY(((struct ext3_xattr_ibody_header *)(buffer))+1)
+
+#define FIRST_ENTRY(buffer) \
+       ((HDR(buffer)->h_magic == EXT2_XATTR_MAGIC2) ? \
+               IFIRST(buffer) : \
+               BFIRST(buffer))
+
+/*
+ * On-block xattr value offsets start at the beginning of the block, but
+ * on-inode xattr value offsets start after the initial header 
+ * (ext3_xattr_ibody_header).
+ */
+#define VALUE_OFFSET(buffer, entry) \
+       (((HDR(buffer)->h_magic == EXT2_XATTR_MAGIC2) ? \
+               (entry)->e_value_offs + sizeof(struct ext3_xattr_ibody_header) : \
+               (entry)->e_value_offs))
+       
+/*
+ * xattr syscalls do not exist yet in libc, get our own copy here,
+ * taken from libattr.
+ */
+#if defined (__i386__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                227
+# define __NR_lgetxattr                230
+# define __NR_llistxattr       233
+#elif defined (__sparc__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                170
+# define __NR_lgetxattr                173
+# define __NR_llistxattr       179
+#elif defined (__ia64__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                1218
+# define __NR_lgetxattr                1221
+# define __NR_llistxattr       1224
+#elif defined (__powerpc__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                210
+# define __NR_lgetxattr                213
+# define __NR_llistxattr       216
+#elif defined (__x86_64__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                189
+# define __NR_lgetxattr                192
+# define __NR_llistxattr       195
+#elif defined (__s390__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                225
+# define __NR_lgetxattr                228
+# define __NR_llistxattr       231
+#elif defined (__arm__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_SYSCALL_BASE 0x900000
+# define __NR_lsetxattr                (__NR_SYSCALL_BASE+227)
+# define __NR_lgetxattr                (__NR_SYSCALL_BASE+230)
+# define __NR_llistxattr       (__NR_SYSCALL_BASE+233)
+#elif defined (__mips64__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_Linux 5000
+# define __NR_lsetxattr                (__NR_Linux + 218)
+# define __NR_lgetxattr                (__NR_Linux + 221)
+# define __NR_llistxattr       (__NR_Linux + 224)
+#elif defined (__mips__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_Linux 4000
+# define __NR_lsetxattr                (__NR_Linux + 225)
+# define __NR_lgetxattr                (__NR_Linux + 228)
+# define __NR_llistxattr       (__NR_Linux + 231)
+#elif defined (__alpha__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                383
+# define __NR_lgetxattr                386
+# define __NR_llistxattr       389
+#elif defined (__mc68000__)
+# define HAVE_XATTR_SYSCALLS 1
+# define __NR_lsetxattr                224
+# define __NR_lgetxattr                227
+# define __NR_llistxattr       230
+#else
+# warning "Extended attribute syscalls undefined for this architecture"
+# define HAVE_XATTR_SYSCALLS 0
+#endif
+
+#if HAVE_XATTR_SYSCALLS
+# define SYSCALL(args...)      syscall(args)
+#else
+# define SYSCALL(args...)      ( errno = ENOSYS, -1 )
+#endif
+
+static int lsetxattr __P((const char *, const char *, void *, size_t, int));
+static ssize_t lgetxattr __P((const char *, const char *, void *, size_t));
+static ssize_t llistxattr __P((const char *, char *, size_t));
+static int xattr_cb_list __P((char *, char *, int, void *));
+static int xattr_cb_set __P((char *, char *, int, void *));
+static int xattr_cb_compare __P((char *, char *, int, void *));
+static int xattr_verify __P((char *));
+static int xattr_count __P((char *, int *));
+static int xattr_walk __P((char *, int (*)(char *, char *, int, void *), void *));
+
+static int
+lsetxattr(const char *path, const char *name, void *value, size_t size, int flags)
+{
+       return SYSCALL(__NR_lsetxattr, path, name, value, size, flags);
+}
+
+static ssize_t
+lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+       return SYSCALL(__NR_lgetxattr, path, name, value, size);
+}
+
+static ssize_t
+llistxattr(const char *path, char *list, size_t size)
+{
+       return SYSCALL(__NR_llistxattr, path, list, size);
+}
+
+/*
+ * Dump code starts here :)
+ */
+
+static int
+xattr_cb_list(char *name, char *value, int valuelen, void *private)
+{
+       value[valuelen] = '\0';
+       printf("EA: %s:%s\n", name, value);
+
+       return GOOD;
+}
+
+static int
+xattr_cb_set(char *name, char *value, int valuelen, void *private)
+{
+       char *path = (char *)private;
+
+       if (lsetxattr(path, name, value, valuelen, 0) < 0) {
+               warn("lsetxattr %s failed", path);
+               return FAIL;
+       }
+       return GOOD;
+}
+
+static int
+xattr_cb_compare(char *name, char *value, int valuelen, void *private)
+{
+       char *path = (char *)private;
+       char valuef[XATTR_MAXSIZE];
+       int valuesz;
+
+       valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE);
+       if (valuesz < 0) {
+               warn("%s: lgetxattr failed\n", path);
+               return FAIL;
+       }
+
+       if (valuesz != valuelen) {
+               fprintf(stderr, "%s: EA %s value changed\n", path, value);
+               return FAIL;
+       }
+
+       if (memcmp(value, valuef, valuelen)) {
+               fprintf(stderr, "%s: EA %s value changed\n", path, value);
+               return FAIL;
+       }
+
+       return GOOD;
+}
+
+static int
+xattr_verify(char *buffer)
+{
+       struct ext2_xattr_entry *entry;
+       char *end;
+
+       end = buffer + XATTR_MAXSIZE;
+
+       if (Bcvt)
+               swabst("4i", (u_char *)buffer);
+
+       if (HDR(buffer)->h_magic != EXT2_XATTR_MAGIC &&
+           HDR(buffer)->h_magic != EXT2_XATTR_MAGIC2) {
+               fprintf(stderr, "error in EA block 1\n");
+               fprintf(stderr, "magic = %x\n", HDR(buffer)->h_magic);
+               
+               return FAIL;
+       }
+
+       /* check the on-disk data structure */
+       entry = FIRST_ENTRY(buffer);
+       if (Bcvt) 
+               swabst("2b1s3i", (u_char *)entry);
+       while (!IS_LAST_ENTRY(entry)) {
+               struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);
+
+               if ((char *)next >= end) {
+                       fprintf(stderr, "error in EA block\n");
+                       return FAIL;
+               }
+               entry = next;
+               if (Bcvt) 
+                       swabst("2b1s3i", (u_char *)entry);
+       }
+       return GOOD;
+}
+
+static int
+xattr_count(char *buffer, int *count)
+{
+       struct ext2_xattr_entry *entry;
+       int result = 0;
+
+       /* list the attribute names */
+       for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
+            entry = EXT2_XATTR_NEXT(entry))
+               result++;
+
+       *count = result;
+       return GOOD;
+}
+
+static int
+xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, void *), void *private)
+{
+       struct ext2_xattr_entry *entry;
+
+       /* list the attribute names */
+       for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
+            entry = EXT2_XATTR_NEXT(entry)) {
+               char name[XATTR_MAXSIZE], value[XATTR_MAXSIZE];
+               int off;
+
+               switch (entry->e_name_index) {
+               case EXT2_XATTR_INDEX_USER:
+                       strcpy(name, "user.");
+                       break;
+               case EXT2_XATTR_INDEX_POSIX_ACL_ACCESS:
+               case EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT:
+                       strcpy(name, "system.");
+                       break;
+               case EXT2_XATTR_INDEX_TRUSTED:
+                       strcpy(name, "trusted.");
+                       break;
+               case EXT2_XATTR_INDEX_LUSTRE:
+                       strcpy(name, "lustre.");
+                       break;
+               case EXT2_XATTR_INDEX_SECURITY:
+                       strcpy(name, "security.");
+                       break;
+               default:
+                       fprintf(stderr, "Unknown EA index\n");
+                       return FAIL;
+               }
+
+               off = strlen(name);
+               memcpy(name + off, entry->e_name, entry->e_name_len);
+               name[off + entry->e_name_len] = '\0';
+
+
+               memcpy(value, buffer + VALUE_OFFSET(buffer, entry), entry->e_value_size);
+
+               if (xattr_cb(name, value, entry->e_value_size, private) != GOOD)
+                       return FAIL;
+       }
+
+       return GOOD;
+}
+
+int
+xattr_compare(char *path, char *buffer)
+{
+       int countf, countt;
+       char *names = NULL, *end_names, *name;
+
+       countf = llistxattr(path, NULL, 0);
+       if (countf < 0) {
+               warn("%s: llistxattr failed", path);
+               return FAIL;
+       }
+
+       names = malloc(countf + 1);
+       if (!names) {
+               warn("%s: llistxattr failed", path);
+               return FAIL;
+       }
+
+       countf = llistxattr(path, names, countf);
+       if (countf < 0) {
+               warn("%s: llistxattr failed", path);
+               free(names);
+               return FAIL;
+       }
+
+       names[countf] = '\0';
+       end_names = names + countf;
+
+       countf = 0;
+       for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
+               if (!*name)
+                       continue;
+               countf++;
+       }
+
+       free(names);
+
+       if (buffer) {
+               if (xattr_verify(buffer) == FAIL)
+                       return FAIL;
+
+               if (xattr_count(buffer, &countt) == FAIL)
+                       return FAIL;
+       }
+       else
+               countt = 0;
+
+       if (countf != countt) {
+               fprintf(stderr, "%s: EA count changed from %d to %d\n", path, countt, countf);
+               return FAIL;
+       }
+
+       if (!buffer)
+               return GOOD;
+
+       return xattr_walk(buffer, xattr_cb_compare, path);
+}
+
+int
+xattr_extract(char *path, char *buffer)
+{
+       if (dflag) {
+               fprintf(stderr, "xattr_extract(%s)\n", path);
+               xattr_walk(buffer, xattr_cb_list, NULL);
+       }
+
+       if (xattr_verify(buffer) == FAIL)
+               return FAIL;
+
+       return xattr_walk(buffer, xattr_cb_set, path);
+}
+