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<source> 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<source> 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<source> to C<destination>.
+
+=item C<all>
+
+Copy B<all> the attributes from C<source> 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
--
1.8.3.1