summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorailin-nemui <ailin-nemui@users.noreply.github.com>2018-04-24 13:58:15 +0200
committerGitHub <noreply@github.com>2018-04-24 13:58:15 +0200
commit0caf884e9231656b80aa244d04146c27193dd9f2 (patch)
tree1c915cea59cd129c4b1ac352da82e821c292a1a5
parent13b2f349b68bbef2550238c62519ab2acf013855 (diff)
parent8deb6182c692904733544568479035b31fe1007e (diff)
downloadirssi-0caf884e9231656b80aa244d04146c27193dd9f2.zip
Merge pull request #871 from dequis/atomic-config-write
Make config_write more atomic to prevent truncation when out of space
-rw-r--r--src/lib-config/write.c45
1 files changed, 41 insertions, 4 deletions
diff --git a/src/lib-config/write.c b/src/lib-config/write.c
index 37e51f09..ab2c6975 100644
--- a/src/lib-config/write.c
+++ b/src/lib-config/write.c
@@ -301,30 +301,67 @@ int config_write(CONFIG_REC *rec, const char *fname, int create_mode)
{
int ret;
int fd;
+ int save_errno;
+ char *tmp_name;
+ const char *dest_name;
g_return_val_if_fail(rec != NULL, -1);
g_return_val_if_fail(fname != NULL || rec->fname != NULL, -1);
g_return_val_if_fail(create_mode != -1 || rec->create_mode != -1, -1);
- fd = open(fname != NULL ? fname : rec->fname,
+ dest_name = fname != NULL ? fname : rec->fname;
+ tmp_name = g_strdup_printf("%s.XXXXXX", dest_name);
+
+ fd = g_mkstemp_full(tmp_name,
O_WRONLY | O_TRUNC | O_CREAT,
create_mode != -1 ? create_mode : rec->create_mode);
- if (fd == -1)
- return config_error(rec, g_strerror(errno));
+ if (fd == -1) {
+ config_error(rec, g_strerror(errno));
+ ret = -1;
+ goto out;
+ }
rec->handle = g_io_channel_unix_new(fd);
g_io_channel_set_encoding(rec->handle, NULL, NULL);
g_io_channel_set_close_on_unref(rec->handle, TRUE);
+
rec->tmp_indent_level = 0;
rec->tmp_last_lf = TRUE;
ret = config_write_block(rec, rec->mainnode, FALSE, TRUE);
+ save_errno = errno;
+
if (ret == -1) {
/* write error */
- config_error(rec, errno == 0 ? "bug" : g_strerror(errno));
+ unlink(tmp_name);
+ config_error(rec, save_errno == 0 ? "bug" : g_strerror(save_errno));
+ goto out;
+ }
+
+ ret = fsync(fd);
+ save_errno = errno;
+
+ if (ret == -1) {
+ unlink(tmp_name);
+ config_error(rec, g_strerror(errno));
+ goto out;
}
g_io_channel_unref(rec->handle);
rec->handle = NULL;
+ if (rename(tmp_name, dest_name) == -1) {
+ unlink(tmp_name);
+ config_error(rec, g_strerror(errno));
+ goto out;
+ }
+
+out:
+ if (rec->handle) {
+ g_io_channel_unref(rec->handle);
+ rec->handle = NULL;
+ }
+
+ g_free(tmp_name);
+
return ret;
}