From cca7148b36e60b4671518602ff9a7c2d0c22a7b2 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Mon, 2 May 2005 15:10:43 +0000 Subject: [PATCH] Added EA/ACL support in dump and restore. --- CHANGES | 6 +- TODO | 7 +- compat/include/bsdcompat.h | 4 +- compat/include/protocols/dumprestore.h | 8 +- config.h.in | 4 + configure | 78 +++++ configure.in | 8 + dump/traverse.c | 82 ++++- restore/Makefile.in | 6 +- restore/dirs.c | 49 ++- restore/extern.h | 5 +- restore/restore.h | 3 +- restore/tape.c | 70 +++- restore/xattr.c | 444 +++++++++++++++++++++++++ 14 files changed, 738 insertions(+), 36 deletions(-) create mode 100644 restore/xattr.c diff --git a/CHANGES b/CHANGES index d6d93ff..49f1446 100644 --- 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 ????????????????) ====================================================================== @@ -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. +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) ====================================================================== diff --git a/TODO b/TODO index d3d4102..349b1d1 100644 --- 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: --------------- @@ -31,7 +31,4 @@ All others: 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 - . - -7. Better readline completition in restore (escape spaces etc). +6. Better readline completition in restore (escape spaces etc). diff --git a/compat/include/bsdcompat.h b/compat/include/bsdcompat.h index 4b9b907..7bc2160 100644 --- a/compat/include/bsdcompat.h +++ b/compat/include/bsdcompat.h @@ -5,7 +5,7 @@ * Stelian Pop , 1999-2000 * Stelian Pop - Alcôve , 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 @@ -130,6 +130,8 @@ struct dinode { __u16 di_uidhigh; __u16 di_gidhigh; __u32 di_spare; + __u16 di_extraisize; + __u16 di_pad2; }; #define di_rdev di_db[0] diff --git a/compat/include/protocols/dumprestore.h b/compat/include/protocols/dumprestore.h index 981b9f6..816a83f 100644 --- a/compat/include/protocols/dumprestore.h +++ b/compat/include/protocols/dumprestore.h @@ -5,7 +5,7 @@ * Stelian Pop , 1999-2000 * Stelian Pop - Alcôve , 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 - /* * compression flags for the tapebuf header. */ @@ -172,4 +171,9 @@ struct tapebuf { #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_ */ diff --git a/config.h.in b/config.h.in index 799b2f0..828ef11 100644 --- a/config.h.in +++ b/config.h.in @@ -21,6 +21,10 @@ /* Define to 1 if you have the 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 diff --git a/configure b/configure index 2a7478f..290a7fd 100755 --- 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 +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 diff --git a/configure.in b/configure.in index 57b2a09..3788737 100644 --- a/configure.in +++ b/configure.in @@ -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 +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 diff --git a/dump/traverse.c b/dump/traverse.c index fe5af09..64c32a5 100644 --- a/dump/traverse.c +++ b/dump/traverse.c @@ -37,7 +37,7 @@ #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 @@ -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 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) @@ -796,6 +797,68 @@ dumponeblock(UNUSED(ext2_filsys fs), blk_t *blocknr, e2_blkcnt_t blockcnt, } #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. * @@ -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; - 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; @@ -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; + dump_xattr(ino, dp); 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); + dump_xattr(ino, dp); 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); + dump_xattr(ino, dp); 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); - 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)); @@ -956,6 +1022,7 @@ dumpino(struct dinode *dp, dump_ino_t ino, int metaonly) 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); @@ -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; - 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; @@ -1142,6 +1207,7 @@ dumpdirino(struct dinode *dp, dump_ino_t ino) } (void)free(cdc.buf); + dump_xattr(ino, dp); } #endif /* __linux__ */ @@ -1299,7 +1365,11 @@ getino(dump_ino_t 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); +#endif if (err) { com_err(disk, err, "while reading inode #%ld\n", (long)inum); exit(X_ABORT); diff --git a/restore/Makefile.in b/restore/Makefile.in index cdc2004..79e03ea 100644 --- a/restore/Makefile.in +++ b/restore/Makefile.in @@ -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@ @@ -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 \ - 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 diff --git a/restore/dirs.c b/restore/dirs.c index 26c637b..b049f49 100644 --- a/restore/dirs.c +++ b/restore/dirs.c @@ -42,7 +42,7 @@ #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 @@ -114,6 +114,7 @@ struct modeinfo { 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)); -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)); @@ -186,6 +187,8 @@ extractdirs(int genmode) 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"); @@ -242,6 +245,7 @@ extractdirs(int genmode) 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: @@ -253,12 +257,15 @@ extractdirs(int genmode) 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; @@ -675,9 +682,15 @@ setdirmodes(int flags) } 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) @@ -707,6 +720,8 @@ setdirmodes(int flags) #endif #endif utimes(cp, node.timep); + if (node.xattr) + xattr_extract(cp, xattr); ep->e_flags &= ~NEW; } } @@ -741,9 +756,15 @@ comparedirmodes(void) } 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); @@ -790,6 +811,14 @@ comparedirmodes(void) } } #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; } } @@ -874,9 +903,9 @@ allocinotab(dump_ino_t ino, OFF_T seekpt) 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; @@ -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.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); } /* diff --git a/restore/extern.h b/restore/extern.h index 60f2f26..16e2440 100644 --- a/restore/extern.h +++ b/restore/extern.h @@ -5,7 +5,7 @@ * Stelian Pop , 1999-2000 * Stelian Pop - Alcôve , 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)); +int readxattr __P((char *)); +int xattr_compare __P((char *, char *)); +int xattr_extract __P((char *, char *)); diff --git a/restore/restore.h b/restore/restore.h index 7deb3f2..4f5236f 100644 --- a/restore/restore.h +++ b/restore/restore.h @@ -5,7 +5,7 @@ * Stelian Pop , 1999-2000 * Stelian Pop - Alcôve , 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); \ } +#define XATTR_MAXSIZE 4096 diff --git a/restore/tape.c b/restore/tape.c index ab6b113..e60921b 100644 --- a/restore/tape.c +++ b/restore/tape.c @@ -42,7 +42,7 @@ #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 @@ -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 xtrxattr __P((char *, size_t)); 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 char xattrbuf[XATTR_MAXSIZE]; +static int xattrlen; + #ifdef DUMP_MACOSX static DumpFinderInfo gFndrInfo; #endif @@ -1029,10 +1033,14 @@ extractattr(char *path) 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(); @@ -1213,6 +1221,29 @@ extractresourceufs(char *name) } #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 */ @@ -1480,6 +1511,17 @@ xtrcmpskip(UNUSED(char *buf), size_t size) } #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) @@ -1595,6 +1637,8 @@ cmpfiles(char *tapefile, char *diskfile, struct STAT *sbuf_disk) static void compareattr(char *name) { + int xattr_done = 0; + 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; - 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 diff --git a/restore/xattr.c b/restore/xattr.c new file mode 100644 index 0000000..6da39eb --- /dev/null +++ b/restore/xattr.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 1999-2004 + * Stelian Pop , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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<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); +} + -- 2.39.5