diff options
author | ailin-nemui <ailin-nemui@users.noreply.github.com> | 2018-04-24 13:58:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-24 13:58:15 +0200 |
commit | 0caf884e9231656b80aa244d04146c27193dd9f2 (patch) | |
tree | 1c915cea59cd129c4b1ac352da82e821c292a1a5 /src/lib-config/write.c | |
parent | 13b2f349b68bbef2550238c62519ab2acf013855 (diff) | |
parent | 8deb6182c692904733544568479035b31fe1007e (diff) | |
download | irssi-0caf884e9231656b80aa244d04146c27193dd9f2.zip |
Merge pull request #871 from dequis/atomic-config-write
Make config_write more atomic to prevent truncation when out of space
Diffstat (limited to 'src/lib-config/write.c')
-rw-r--r-- | src/lib-config/write.c | 45 |
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; } |