diff --git a/daemon/daemon.h b/daemon/daemon.h index b77d7647a..6535658ef 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -231,6 +231,9 @@ extern void journal_finalize (void); /*-- in proto.c --*/ extern void main_loop (int sock) __attribute__((noreturn)); +/*-- in xattr.c --*/ +extern int copy_xattrs (const char *src, const char *dest); + /* ordinary daemon functions use these to indicate errors * NB: you don't need to prefix the string with the current command, * it is added automatically by the client-side RPC stubs. diff --git a/daemon/file.c b/daemon/file.c index f348f8701..856ab5fca 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -28,6 +28,7 @@ #include "guestfs_protocol.h" #include "daemon.h" #include "actions.h" +#include "optgroups.h" GUESTFSD_EXT_CMD(str_file, file); GUESTFSD_EXT_CMD(str_zcat, zcat); @@ -584,3 +585,74 @@ do_filesize (const char *path) return buf.st_size; } + +int +do_copy_attributes (const char *src, const char *dest, int all, int mode, int xattributes, int ownership) +{ + int r; + struct stat srcstat, deststat; + + static const unsigned int file_mask = 07777; + + /* If it was specified to copy everything, manually enable all the flags + * not manually specified to avoid checking for flag || all everytime. + */ + if (all) { + if (!(optargs_bitmask & GUESTFS_COPY_ATTRIBUTES_MODE_BITMASK)) + mode = 1; + if (!(optargs_bitmask & GUESTFS_COPY_ATTRIBUTES_XATTRIBUTES_BITMASK)) + xattributes = 1; + if (!(optargs_bitmask & GUESTFS_COPY_ATTRIBUTES_OWNERSHIP_BITMASK)) + ownership = 1; + } + + CHROOT_IN; + r = stat (src, &srcstat); + CHROOT_OUT; + + if (r == -1) { + reply_with_perror ("stat: %s", src); + return -1; + } + + CHROOT_IN; + r = stat (dest, &deststat); + CHROOT_OUT; + + if (r == -1) { + reply_with_perror ("stat: %s", dest); + return -1; + } + + if (mode && + ((srcstat.st_mode & file_mask) != (deststat.st_mode & file_mask))) { + CHROOT_IN; + r = chmod (dest, (srcstat.st_mode & file_mask)); + CHROOT_OUT; + + if (r == -1) { + reply_with_perror ("chmod: %s", dest); + return -1; + } + } + + if (ownership && + (srcstat.st_uid != deststat.st_uid || srcstat.st_gid != deststat.st_gid)) { + CHROOT_IN; + r = chown (dest, srcstat.st_uid, srcstat.st_gid); + CHROOT_OUT; + + if (r == -1) { + reply_with_perror ("chown: %s", dest); + return -1; + } + } + + if (xattributes && optgroup_linuxxattrs_available ()) { + if (!copy_xattrs (src, dest)) + /* copy_xattrs replies with an error already. */ + return -1; + } + + return 0; +} diff --git a/daemon/xattr.c b/daemon/xattr.c index ebacc02c0..abed5ffdf 100644 --- a/daemon/xattr.c +++ b/daemon/xattr.c @@ -541,8 +541,77 @@ do_lgetxattr (const char *path, const char *name, size_t *size_r) return buf; /* caller frees */ } +int +copy_xattrs (const char *src, const char *dest) +{ + ssize_t len, vlen, ret, attrval_len = 0; + CLEANUP_FREE char *buf = NULL, *attrval = NULL; + size_t i; + + buf = _listxattrs (src, listxattr, &len); + if (buf == NULL) + /* _listxattrs issues reply_with_perror already. */ + goto error; + + /* What we get from the kernel is a string "foo\0bar\0baz" of length + * len. + */ + for (i = 0; i < (size_t) len; i += strlen (&buf[i]) + 1) { + CHROOT_IN; + vlen = getxattr (src, &buf[i], NULL, 0); + CHROOT_OUT; + if (vlen == -1) { + reply_with_perror ("getxattr: %s, %s", src, &buf[i]); + goto error; + } + + if (vlen > XATTR_SIZE_MAX) { + /* The next call to getxattr will fail anyway, so ... */ + reply_with_error ("extended attribute is too large"); + goto error; + } + + if (vlen > attrval_len) { + char *new = realloc (attrval, vlen); + if (new == NULL) { + reply_with_perror ("realloc"); + goto error; + } + attrval = new; + attrval_len = vlen; + } + + CHROOT_IN; + vlen = getxattr (src, &buf[i], attrval, vlen); + CHROOT_OUT; + if (vlen == -1) { + reply_with_perror ("getxattr: %s, %s", src, &buf[i]); + goto error; + } + + CHROOT_IN; + ret = setxattr (dest, &buf[i], attrval, vlen, 0); + CHROOT_OUT; + if (ret == -1) { + reply_with_perror ("setxattr: %s, %s", dest, &buf[i]); + goto error; + } + } + + return 1; + + error: + return 0; +} + #else /* no HAVE_LINUX_XATTRS */ OPTGROUP_LINUXXATTRS_NOT_AVAILABLE +int +copy_xattrs (const char *src, const char *dest) +{ + abort (); +} + #endif /* no HAVE_LINUX_XATTRS */ diff --git a/fish/Makefile.am b/fish/Makefile.am index bd0485fa0..fb0e99e5b 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -279,6 +279,7 @@ if ENABLE_APPLIANCE TESTS += \ test-copy.sh \ test-edit.sh \ + test-file-attrs.sh \ test-find0.sh \ test-inspect.sh \ test-glob.sh \ diff --git a/fish/test-file-attrs.sh b/fish/test-file-attrs.sh new file mode 100755 index 000000000..78bd817a1 --- /dev/null +++ b/fish/test-file-attrs.sh @@ -0,0 +1,157 @@ +#!/bin/bash - +# libguestfs +# Copyright (C) 2014 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# Test guestfish file attributes commands (chmod, copy-attributes, etc). + +set -e +export LANG=C + +rm -f test.out + +$VG ./guestfish > test.out <