[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libguestfs] [PATCH 3/3] fish: edit: Preserve permissions, UID, GID, SELinux context when editing files (RHBZ#788641).



-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
libguestfs lets you edit virtual machines.  Supports shell scripting,
bindings from many languages.  http://libguestfs.org
>From 0c24a2dd904a2a33f75c6580e671633f1ee80b90 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones redhat com>
Date: Fri, 10 Feb 2012 10:32:55 +0000
Subject: [PATCH 3/3] fish: edit: Preserve permissions, UID, GID, SELinux
 context when editing files (RHBZ#788641).

---
 fish/edit.c       |   82 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fish/test-edit.sh |   11 ++++++-
 2 files changed, 92 insertions(+), 1 deletions(-)

diff --git a/fish/edit.c b/fish/edit.c
index 908a3a3..517c098 100644
--- a/fish/edit.c
+++ b/fish/edit.c
@@ -31,6 +31,8 @@
 #include "fish.h"
 
 static char *generate_random_name (const char *filename);
+static int copy_attributes (const char *src, const char *dest);
+static int feature_available (guestfs_h *g, const char *feature);
 
 /* guestfish edit command, suggested by Ján Ondrej, implemented by RWMJ */
 
@@ -127,6 +129,12 @@ run_edit (const char *cmd, size_t argc, char *argv[])
   if (guestfs_upload (g, filename, newname) == -1)
     goto error3;
 
+  /* Set the permissions, UID, GID and SELinux context of the new
+   * file to match the old file (RHBZ#788641).
+   */
+  if (copy_attributes (remotefilename, newname) == -1)
+    goto error3;
+
   if (guestfs_mv (g, newname, remotefilename) == -1)
     goto error3;
 
@@ -178,3 +186,77 @@ generate_random_name (const char *filename)
 
   return ret; /* caller will free */
 }
+
+static int
+copy_attributes (const char *src, const char *dest)
+{
+  struct guestfs_stat *stat;
+  int has_linuxxattrs;
+  char *selinux_context = NULL;
+  size_t selinux_context_size;
+
+  has_linuxxattrs = feature_available (g, "linuxxattrs");
+
+  /* Get the mode. */
+  stat = guestfs_stat (g, src);
+  if (stat == NULL)
+    return -1;
+
+  /* Get the SELinux context.  XXX Should we copy over other extended
+   * attributes too?
+   */
+  if (has_linuxxattrs) {
+    guestfs_error_handler_cb old_error_cb;
+    void *old_error_data;
+    old_error_cb = guestfs_get_error_handler (g, &old_error_data);
+    guestfs_set_error_handler (g, NULL, NULL);
+
+    selinux_context = guestfs_getxattr (g, src, "security.selinux",
+                                        &selinux_context_size);
+    /* selinux_context could be NULL.  This isn't an error. */
+
+    guestfs_set_error_handler (g, old_error_cb, old_error_data);
+  }
+
+  /* Set the permissions (inc. sticky and set*id bits), UID, GID. */
+  if (guestfs_chmod (g, stat->mode & 07777, dest) == -1) {
+    guestfs_free_stat (stat);
+    return -1;
+  }
+  if (guestfs_chown (g, stat->uid, stat->gid, dest) == -1) {
+    guestfs_free_stat (stat);
+    return -1;
+  }
+  guestfs_free_stat (stat);
+
+  /* Set the SELinux context. */
+  if (has_linuxxattrs && selinux_context) {
+    if (guestfs_setxattr (g, "security.selinux", selinux_context,
+                          (int) selinux_context_size, dest) == -1) {
+      free (selinux_context);
+      return -1;
+    }
+  }
+  free (selinux_context);
+
+  return 0;
+}
+
+static int
+feature_available (guestfs_h *g, const char *feature)
+{
+  /* If there's an error we should ignore it, so to do that we have to
+   * temporarily replace the error handler with a null one.
+   */
+  guestfs_error_handler_cb old_error_cb;
+  void *old_error_data;
+  old_error_cb = guestfs_get_error_handler (g, &old_error_data);
+  guestfs_set_error_handler (g, NULL, NULL);
+
+  const char *groups[] = { feature, NULL };
+  int r = guestfs_available (g, (char * const *) groups);
+
+  guestfs_set_error_handler (g, old_error_cb, old_error_data);
+
+  return r == 0 ? 1 : 0;
+}
diff --git a/fish/test-edit.sh b/fish/test-edit.sh
index ff38d1c..93b2e90 100755
--- a/fish/test-edit.sh
+++ b/fish/test-edit.sh
@@ -36,13 +36,22 @@ export EDITOR="echo second line of text >>"
 output=$(
 ../fish/guestfish -N fs -m /dev/sda1 <<EOF
 write /file.txt "this is a test\n"
+chmod 0600 /file.txt
+chown 10 11 /file.txt
 edit /file.txt
 cat /file.txt
+stat /file.txt | grep mode:
+stat /file.txt | grep uid:
+stat /file.txt | grep gid:
 EOF
 )
 
 if [ "$output" != "this is a test
-second line of text" ]; then
+second line of text
+
+mode: 33152
+uid: 10
+gid: 11" ]; then
     echo "$0: error: output of guestfish after edit command did not match expected output"
     echo "$output"
     exit 1
-- 
1.7.9


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]