fish: edit: Preserve permissions, UID, GID, SELinux context when editing files (RHBZ#788641).

This commit is contained in:
Richard W.M. Jones
2012-02-10 10:32:55 +00:00
parent ddae5abf80
commit 7c21e49c2c
2 changed files with 92 additions and 1 deletions

View File

@@ -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;
}

View File

@@ -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