diff options
Diffstat (limited to 'fish/edit.c')
-rw-r--r-- | fish/edit.c | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/fish/edit.c b/fish/edit.c index 908a3a3a..517c098a 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; +} |