-$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 ????????????????)
======================================================================
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)
======================================================================
-$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:
---------------
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).
* 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>
__u16 di_uidhigh;
__u16 di_gidhigh;
__u32 di_spare;
+ __u16 di_extraisize;
+ __u16 di_pad2;
};
#define di_rdev di_db[0]
* 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 $
*/
/*
#define EXT_MACOSRESFORK 2
#define EXT_XATTR 3
-
/*
* compression flags for the tapebuf header.
*/
#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_ */
/* 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
{ (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
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
#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>
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)
}
#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.
*
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;
spcl.c_count = 0;
writeheader(ino);
spcl.c_flags &= ~DR_METAONLY;
+ dump_xattr(ino, dp);
return;
}
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__ */
case S_IFCHR:
case S_IFBLK:
writeheader(ino);
+ dump_xattr(ino, dp);
return;
default:
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;
+ }
#ifdef __linux__
bc.max = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
bc.buf = (int *)malloc (bc.max * sizeof (int));
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);
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;
}
(void)free(cdc.buf);
+ dump_xattr(ino, dp);
}
#endif /* __linux__ */
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);
+#endif
if (err) {
com_err(disk, err, "while reading inode #%ld\n", (long)inum);
exit(X_ABORT);
-# $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@
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 \
- utilities.o ../common/dumprmt.o
+ utilities.o xattr.o ../common/dumprmt.o
MAN8= restore.8
RMAN8= rrestore.8
#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>
uid_t uid;
gid_t gid;
unsigned int flags;
+ char xattr;
};
/*
#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));
-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));
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");
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:
skipfile();
break;
case EXT_XATTR:
- msg("EA/ACLs attributes not supported, skipping\n");
- skipfile();
+ if (readxattr(xattr) == GOOD)
+ xattr_found = 1;
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;
}
clearerr(mf);
for (;;) {
+ char xattr[XATTR_MAXSIZE];
(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)
#endif
#endif
utimes(cp, node.timep);
+ if (node.xattr)
+ xattr_extract(cp, xattr);
ep->e_flags &= ~NEW;
}
}
}
clearerr(mf);
for (;;) {
+ char xattr[XATTR_MAXSIZE];
(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);
}
}
#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;
}
}
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
-savemodeinfo(dump_ino_t ino, struct dinode *dip) {
+savemodeinfo(dump_ino_t ino, struct dinode *dip, char *xattr) {
#endif
struct modeinfo node;
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 (xattr)
+ if ( fwrite(xattr, 1, XATTR_MAXSIZE, mf) != XATTR_MAXSIZE)
+ err(1,"cannot write to file %s", modefile);
}
/*
* 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 $
*/
/*-
#endif
void skipxattr __P((void));
+int readxattr __P((char *));
+int xattr_compare __P((char *, char *));
+int xattr_extract __P((char *, char *));
* 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 $
*/
/*
exit(2); \
}
+#define XATTR_MAXSIZE 4096
#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>
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 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
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();
}
#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
*/
}
#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)
static void
compareattr(char *name)
{
+ int xattr_done = 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_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;
+ }
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
--- /dev/null
+/*
+ * 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);
+}
+