Re: [Libguestfs] [PATCH] New API: copy-attributes.

Richard W.M. Jones
2014-01-13T13:49:02Z

On Mon, Jan 13, 2014 at 02:45:08PM +0100, Pino Toscano
wrote:
> This allows one to copy attributes (like permissions, xattrs,
> ownership) from a file to another.
> ---
>  daemon/daemon.h         |   3 +
>  daemon/file.c           |  72 ++++++++++++++++++++++
>  daemon/xattr.c          |  69 +++++++++++++++++++++
>  fish/Makefile.am        |   1 +
>  fish/test-file-attrs.sh | 157
++++++++++++++++++++++++++++++++++++++++++++++++
>  generator/actions.ml    |  38 ++++++++++++
>  src/MAX_PROC_NR         |   2 +-
>  7 files changed, 341 insertions(+), 1 deletion(-)
>  create mode 100755 fish/test-file-attrs.sh
>
> diff --git a/daemon/daemon.h b/daemon/daemon.h
> index b77d764..6535658 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 f348f87..856ab5f 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 ebacc02..abed5ff 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 bd0485f..fb0e99e 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 0000000..78bd817
> --- /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 <<EOF
> +scratch 50MB
> +run
> +part-disk /dev/sda mbr
> +mkfs ext2 /dev/sda1
> +mount /dev/sda1 /
> +
> +touch /foo
> +touch /bar
> +chmod 0712 /foo
> +stat /foo | grep mode:
> +copy-attributes /foo /bar mode:true
> +stat /bar | grep mode:
> +
> +echo -----
> +
> +stat /foo | grep uid:
> +stat /foo | grep gid:
> +chown 10 11 /foo
> +stat /foo | grep uid:
> +stat /foo | grep gid:
> +stat /bar | grep uid:
> +stat /bar | grep gid:
> +copy-attributes /foo /bar ownership:true
> +stat /bar | grep uid:
> +stat /bar | grep gid:
> +
> +echo -----
> +
> +setxattr user.test foo 3 /foo
> +setxattr user.test2 secondtest 10 /foo
> +setxattr user.foo another 7 /bar
> +lxattrlist / "foo bar"
> +copy-attributes /foo /bar xattributes:true
> +lxattrlist / "foo bar"
> +
> +echo -----
> +
> +touch /new
> +chmod 0111 /new
> +copy-attributes /foo /new all:true mode:false
> +stat /new | grep mode:
> +stat /new | grep uid:
> +stat /new | grep gid:
> +lxattrlist / new
> +copy-attributes /foo /new mode:true
> +stat /new | grep mode:
> +EOF
> +
> +if [ "$(cat test.out)" != "mode: 33226
> +mode: 33226
> +-----
> +uid: 0
> +gid: 0
> +uid: 10
> +gid: 11
> +uid: 0
> +gid: 0
> +uid: 10
> +gid: 11
> +-----
> +[0] = {
> +  attrname:
> +  attrval: 2\x00
> +}
> +[1] = {
> +  attrname: user.test
> +  attrval: foo
> +}
> +[2] = {
> +  attrname: user.test2
> +  attrval: secondtest
> +}
> +[3] = {
> +  attrname:
> +  attrval: 1\x00
> +}
> +[4] = {
> +  attrname: user.foo
> +  attrval: another
> +}
> +[0] = {
> +  attrname:
> +  attrval: 2\x00
> +}
> +[1] = {
> +  attrname: user.test
> +  attrval: foo
> +}
> +[2] = {
> +  attrname: user.test2
> +  attrval: secondtest
> +}
> +[3] = {
> +  attrname:
> +  attrval: 3\x00
> +}
> +[4] = {
> +  attrname: user.foo
> +  attrval: another
> +}
> +[5] = {
> +  attrname: user.test
> +  attrval: foo
> +}
> +[6] = {
> +  attrname: user.test2
> +  attrval: secondtest
> +}
> +-----
> +mode: 32841
> +uid: 10
> +gid: 11
> +[0] = {
> +  attrname:
> +  attrval: 2\x00
> +}
> +[1] = {
> +  attrname: user.test
> +  attrval: foo
> +}
> +[2] = {
> +  attrname: user.test2
> +  attrval: secondtest
> +}
> +mode: 33226" ]; then
> +    echo "$0: unexpected output:"
> +    cat test.out
> +    exit 1
> +fi
> +
> +rm test.out
> diff --git a/generator/actions.ml b/generator/actions.ml
> index 9fa7acb..b4b746f 100644
> --- a/generator/actions.ml
> +++ b/generator/actions.ml
> @@ -11647,6 +11647,44 @@ This function is used internally when setting up
the appliance." };
>  This function is used internally when closing the appliance.  Note
>  it's only called when ./configure --enable-valgrind-daemon is
used." };
>
> +  { defaults with
> +    name = "copy_attributes";
> +    style = RErr, [Pathname "src"; Pathname "dest"],
[OBool "all"; OBool "mode"; OBool "xattributes";
OBool "ownership"];
> +    proc_nr = Some 415;
> +    shortdesc = "copy the attributes of a path (file/directory) to
another";
> +    longdesc = "\
> +Copy the attributes of a path (which can be a file or a directory)
> +to another path.
> +
> +By default C<no> attribute is copied, so make sure to specify any
> +(or C<all> to copy everything).
> +
> +The optional arguments specify which attributes can be copied:
> +
> +=over 4
> +
> +=item C<mode>
> +
> +Copy part of the file mode from C to C<destination>.
Only the
> +UNIX permissions and the sticky/setuid/setgid bits can be copied.
> +
> +=item C<xattributes>
> +
> +Copy the Linux extended attributes (xattrs) from C to
C<destination>.
> +This flag does nothing if the I<linuxxattrs> feature is not
available
> +(see C<guestfs_feature_available>).
> +
> +=item C<ownership>
> +
> +Copy the owner uid and the group gid of C to
C<destination>.
> +
> +=item C<all>
> +
> +Copy B<all> the attributes from C to
C<destination>. Enabling it
> +enables all the other flags, if they are not specified already.
> +
> +=back" };
> +
>  ]
>
>  (* Non-API meta-commands available only in guestfish.
> diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
> index d1b9f6a..21c8d99 100644
> --- a/src/MAX_PROC_NR
> +++ b/src/MAX_PROC_NR
> @@ -1 +1 @@
> -414
> +415

ACK.

Rich.

--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming blog: http://rwmj.wordpress.com
Fedora now supports 80 OCaml packages (the OPEN alternative to F#)