In APIs such as guestfs_download, when the FileOut parameter exactly
matches "/dev/stdout" or "/dev/stderr", don't reopen the possibly
redirected output file with O_TRUNC (truncate). Instead dup the file
descriptor.
This magic behaviour doesn't happen for /dev/fd/* (or any other output
file) allowing callers the choice of using /dev/stderr or /dev/fd/2
depending on whether or not they want truncation.
This works around an annoying virt-builder bug. If you do:
$ virt-builder fedora-21 --install no_such_package -v -x >& /tmp/log
then when the `--install' command fails, virt-builder will download
the log file using `guestfs_download (g, log, "/dev/stderr")'. Since
this truncates the redirected /dev/stderr, the final log file is
truncated and corrupted.
With this patch the log file is no longer corrupted.
---
customize/customize_run.ml | 7 +------
src/proto.c | 13 +++++++++++--
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/customize/customize_run.ml b/customize/customize_run.ml
index 5a7e209..d9547a0 100644
--- a/customize/customize_run.ml
+++ b/customize/customize_run.ml
@@ -45,12 +45,7 @@ let run (g : Guestfs.guestfs) root (ops : ops) =
(* Function to cat the log file, for debugging and error messages. *)
let debug_logfile () =
- try
- (* XXX If stderr is redirected this actually truncates the
- * redirection file, which is pretty annoying to say the
- * least.
- *)
- g#download logfile "/dev/stderr"
+ try g#download logfile "/dev/stderr"
with exn ->
warning (f_"log file %s: %s (ignored)") logfile (Printexc.to_string exn)
in
diff --git a/src/proto.c b/src/proto.c
index a019625..a46a382 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -756,9 +756,18 @@ guestfs_int_recv_file (guestfs_h *g, const char *filename)
g->user_cancel = 0;
- fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0666);
+ /* If downloading to /dev/stdout or /dev/stderr, dup the file
+ * descriptor instead of reopening the file, so that redirected
+ * stdout/stderr work properly.
+ */
+ if (STREQ (filename, "/dev/stdout"))
+ fd = dup (1);
+ else if (STREQ (filename, "/dev/stderr"))
+ fd = dup (2);
+ else
+ fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0666);
if (fd == -1) {
- perrorf (g, "open: %s", filename);
+ perrorf (g, "%s", filename);
goto cancel;
}
--
2.3.1