diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2018-03-19 18:59:54 +0000 |
---|---|---|
committer | P. F. Chimento <philip.chimento@gmail.com> | 2018-03-21 12:14:59 -0700 |
commit | 86d4fe3900e00fd32ee89417810d07505204fdb5 (patch) | |
tree | f7c0534213ef3d55934955e1d86a647542f86d7f | |
parent | d27e9a379ba24d215b73d3bd3c606a3f6833ecd2 (diff) |
Handle output file correctly when generating a diff
If the output temporary file cannot be atomically renamed to its final
destination, we need to copy its contents.
-rw-r--r-- | tools/eos-profile-tool/eos-profile-cmd-diff.c | 95 |
1 files changed, 91 insertions, 4 deletions
diff --git a/tools/eos-profile-tool/eos-profile-cmd-diff.c b/tools/eos-profile-tool/eos-profile-cmd-diff.c index efff656..48dc4b6 100644 --- a/tools/eos-profile-tool/eos-profile-cmd-diff.c +++ b/tools/eos-profile-tool/eos-profile-cmd-diff.c @@ -13,6 +13,8 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> static const double scale_val (double val) @@ -263,6 +265,80 @@ append_probe (const char *probe_name, return TRUE; } +#define AUTO_FD_INVALID (-1) + +typedef int AutoFd; + +static void +auto_fd_clear (AutoFd *fd) +{ + if (fd == NULL || *fd == AUTO_FD_INVALID) + return; + + int rfd = *fd; + *fd = AUTO_FD_INVALID; + close (rfd); +} + +static int +auto_fd_steal (AutoFd *fd) +{ + if (fd == NULL) + return AUTO_FD_INVALID; + + int rfd = *fd; + *fd = AUTO_FD_INVALID; + + return rfd; +} + +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (AutoFd, auto_fd_clear) + +static int +copy_fallback (const char *src, + const char *dest) +{ + g_auto(AutoFd) src_fd = open (src, O_RDONLY); + + if (src_fd < 0) + return EXIT_FAILURE; + + g_auto(AutoFd) dest_fd = open (dest, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (dest_fd < 0) + return EXIT_FAILURE; + + int res = 0; + + do + { + char buf[8192]; + + res = read (src_fd, buf, 8192); + if (res < 0) + { + int errno_sv = errno; + + eos_profile_util_print_error ("Unable to read from '%s': %s", + src, + g_strerror (errno_sv)); + return EXIT_FAILURE; + } + + if (write (dest_fd, buf, res) < 0) + { + int errno_sv = errno; + + eos_profile_util_print_error ("Unable to write to '%s': %s", + dest, + g_strerror (errno_sv)); + return EXIT_FAILURE; + } + } + while (res != 0); + + return EXIT_SUCCESS; +} + int eos_profile_cmd_diff_main (void) { @@ -406,13 +482,24 @@ eos_profile_cmd_diff_main (void) if (rename (output_tmpfile, opt_output) != 0) { int errno_sv = errno; + int res = EXIT_SUCCESS; + + /* Fall back to a real copy if the temp file and the real output + * file are not on the same device + */ + if (errno_sv == EXDEV) + res = copy_fallback (output_tmpfile, opt_output); + else + { + eos_profile_util_print_error ("Unable to save output to '%s': %s", + opt_output, + g_strerror (errno_sv)); + res = EXIT_FAILURE; + } - eos_profile_util_print_error ("Unable to save output to '%s': %s", - opt_output, - g_strerror (errno_sv)); unlink (output_tmpfile); - return EXIT_FAILURE; + return res; } } else |