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