summaryrefslogtreecommitdiffstats
path: root/daemon
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2010-05-06 21:36:24 +0100
committerRichard Jones <rjones@redhat.com>2010-05-07 15:27:28 +0100
commit07369cb77a07f965cbf8e02f485c78a22c091f85 (patch)
treea62c684ab028167cbc44e96a6e0517f01b449167 /daemon
parent34067b5c362d5070e91364cc1ed6487497462b42 (diff)
downloadlibguestfs-07369cb77a07f965cbf8e02f485c78a22c091f85.tar.gz
libguestfs-07369cb77a07f965cbf8e02f485c78a22c091f85.tar.xz
libguestfs-07369cb77a07f965cbf8e02f485c78a22c091f85.zip
daemon: Fix for commands working on absolute symbolic links (RHBZ#579608).
The original idea (suggested by Al Viro) was to fork and chroot into the sysroot and read the file from there. Because of the separate process being chrooted, absolute links would be resolved correctly. The slightly modified idea is to open the file in the daemon process (but temporarily chrooted, so symlinks resolve correctly), fork, and have the subprocess just be responsible for copying the file. (Strictly speaking we don't need to fork, but this implementation is simpler). This commit just includes the changes needed to the command*() functions in daemon/guestfsd.c and adds an absolute symlink to the test ISO for testing it. Later commits will fix the broken daemon commands themselves.
Diffstat (limited to 'daemon')
-rw-r--r--daemon/daemon.h4
-rw-r--r--daemon/guestfsd.c103
2 files changed, 102 insertions, 5 deletions
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 81a9f366..977dfdcb 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -53,7 +53,9 @@ extern void free_stringslen (char **argv, int len);
#define commandv(out,err,argv) commandvf((out),(err),0,(argv))
#define commandrv(out,err,argv) commandrvf((out),(err),0,(argv))
-#define COMMAND_FLAG_FOLD_STDOUT_ON_STDERR 1
+#define COMMAND_FLAG_FD_MASK (1024-1)
+#define COMMAND_FLAG_FOLD_STDOUT_ON_STDERR 1024
+#define COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN 2048
extern int commandf (char **stdoutput, char **stderror, int flags,
const char *name, ...);
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index be793002..cd51f442 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -715,6 +715,14 @@ commandvf (char **stdoutput, char **stderror, int flags,
* error messages in the *stderror buffer. If using this flag,
* you should pass stdoutput as NULL because nothing could ever be
* captured in that buffer.
+ *
+ * COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN: For running external
+ * commands on chrooted files correctly (see RHBZ#579608) specifying
+ * this flag causes another process to be forked which chroots into
+ * sysroot and just copies the input file to stdin of the specified
+ * command. The file descriptor is ORed with the flags, and that file
+ * descriptor is always closed by this function. See hexdump.c for an
+ * example of usage.
*/
int
commandrvf (char **stdoutput, char **stderror, int flags,
@@ -722,7 +730,9 @@ commandrvf (char **stdoutput, char **stderror, int flags,
{
int so_size = 0, se_size = 0;
int so_fd[2], se_fd[2];
- pid_t pid;
+ int flag_copy_stdin = flags & COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN;
+ int stdin_fd[2] = { -1, -1 };
+ pid_t pid, stdin_pid = -1;
int r, quit, i;
fd_set rset, rset2;
char buf[256];
@@ -752,15 +762,28 @@ commandrvf (char **stdoutput, char **stderror, int flags,
abort ();
}
+ if (flag_copy_stdin) {
+ if (pipe (stdin_fd) == -1) {
+ perror ("pipe");
+ abort ();
+ }
+ }
+
pid = fork ();
if (pid == -1) {
perror ("fork");
abort ();
}
- if (pid == 0) { /* Child process. */
+ if (pid == 0) { /* Child process running the command. */
close (0);
- open ("/dev/null", O_RDONLY); /* Set stdin to /dev/null (ignore failure) */
+ if (flag_copy_stdin) {
+ dup2 (stdin_fd[0], 0);
+ close (stdin_fd[0]);
+ close (stdin_fd[1]);
+ } else
+ /* Set stdin to /dev/null (ignore failure) */
+ open ("/dev/null", O_RDONLY);
close (so_fd[0]);
close (se_fd[0]);
if (!(flags & COMMAND_FLAG_FOLD_STDOUT_ON_STDERR))
@@ -773,7 +796,61 @@ commandrvf (char **stdoutput, char **stderror, int flags,
execvp (argv[0], (void *) argv);
perror (argv[0]);
- _exit (1);
+ _exit (EXIT_FAILURE);
+ }
+
+ if (flag_copy_stdin) {
+ int fd = flags & COMMAND_FLAG_FD_MASK;
+
+ stdin_pid = fork ();
+ if (stdin_pid == -1) {
+ perror ("fork");
+ abort ();
+ }
+
+ if (stdin_pid == 0) { /* Child process copying stdin. */
+ close (so_fd[0]);
+ close (so_fd[1]);
+ close (se_fd[0]);
+ close (se_fd[1]);
+
+ close (1);
+ dup2 (stdin_fd[1], 1);
+ close (stdin_fd[0]);
+ close (stdin_fd[1]);
+
+ if (chroot (sysroot) == -1) {
+ perror ("chroot");
+ _exit (EXIT_FAILURE);
+ }
+
+ ssize_t n;
+ char buffer[BUFSIZ];
+ while ((n = read (fd, buffer, sizeof buffer)) > 0) {
+ if (xwrite (1, buffer, n) == -1)
+ /* EPIPE error indicates the command process has exited
+ * early. If the command process fails that will be caught
+ * by the daemon, and if not, then it's not an error.
+ */
+ _exit (errno == EPIPE ? EXIT_SUCCESS : EXIT_FAILURE);
+ }
+
+ if (n == -1) {
+ perror ("read");
+ _exit (EXIT_FAILURE);
+ }
+
+ if (close (fd) == -1) {
+ perror ("close");
+ _exit (EXIT_FAILURE);
+ }
+
+ _exit (EXIT_SUCCESS);
+ }
+
+ close (fd);
+ close (stdin_fd[0]);
+ close (stdin_fd[1]);
}
/* Parent process. */
@@ -796,6 +873,7 @@ commandrvf (char **stdoutput, char **stderror, int flags,
close (so_fd[0]);
close (se_fd[0]);
waitpid (pid, NULL, 0);
+ if (stdin_pid >= 0) waitpid (stdin_pid, NULL, 0);
return -1;
}
@@ -876,6 +954,23 @@ commandrvf (char **stdoutput, char **stderror, int flags,
}
}
+ if (flag_copy_stdin) {
+ /* Check copy process didn't fail. */
+ if (waitpid (stdin_pid, &r, 0) != stdin_pid) {
+ perror ("waitpid");
+ kill (pid, 9);
+ waitpid (pid, NULL, 0);
+ return -1;
+ }
+
+ if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
+ fprintf (stderr, "failed copying from input file, see earlier messages\n");
+ kill (pid, 9);
+ waitpid (pid, NULL, 0);
+ return -1;
+ }
+ }
+
/* Get the exit status of the command. */
if (waitpid (pid, &r, 0) != pid) {
perror ("waitpid");