diff --git a/liberty.c b/liberty.c index 9d5efd3..62eab4e 100644 --- a/liberty.c +++ b/liberty.c @@ -3379,11 +3379,11 @@ write_file (const char *filename, const struct str *data, struct error **e) return false; } - errno = 0; fwrite (data->str, data->len, 1, fp); + bool success = !ferror (fp) && !fflush (fp) && !fsync (fileno (fp)); fclose (fp); - if (errno) + if (!success) { error_set (e, "writing to `%s' failed: %s", filename, strerror (errno)); return false; @@ -3391,6 +3391,22 @@ write_file (const char *filename, const struct str *data, struct error **e) return true; } +/// Wrapper for write_file() that makes sure that the new data has been written +/// to disk in its entirety before overriding the old file +static bool +write_file_safe (const char *filename, const struct str *data, struct error **e) +{ + // XXX: ideally we would also open the directory, use *at() versions + // of functions and call fsync() on the directory as appropriate + char *temp = xstrdup_printf ("%s.new", filename); + bool success = write_file (temp, data, e); + if (success && !(success = !rename (temp, filename))) + error_set (e, "could not rename `%s' to `%s': %s", + temp, filename, strerror (errno)); + free (temp); + return success; +} + // --- Simple configuration ---------------------------------------------------- // The keys are stripped of surrounding whitespace, the values are not. @@ -3482,7 +3498,7 @@ write_configuration_file (const char *path_hint, const struct str *data, str_append (&path, "/" PROGRAM_NAME "/" PROGRAM_NAME ".conf"); } - if (!write_file (path.str, data, e)) + if (!write_file_safe (path.str, data, e)) { str_free (&path); return NULL;