From: Daniel Carl Date: Wed, 10 Oct 2018 22:41:02 +0000 (+0200) Subject: Replace g_file_set_contents(). X-Git-Url: https://git.owens.tech/git.owens.tech/git.owens.tech/git?a=commitdiff_plain;h=674e994d5ae087cae2e23abf8d4bc4842b8a68c2;p=vimb.git Replace g_file_set_contents(). The g_file_set_contents performs atomic write to file by creating a temporary file, writing to it and renaming it. But during the creation of the temporary file, the mode is set hard to 0666. So our files will silently always change their mode in case we processed their content. This patch adds the util_file_set_content() function which follows the same approach, but allows to set the mode that is used to create the temporary file. So the file is created with the right permissions. --- diff --git a/src/bookmark.c b/src/bookmark.c index 65c3158..4cc6bd7 100644 --- a/src/bookmark.c +++ b/src/bookmark.c @@ -90,7 +90,7 @@ gboolean bookmark_remove(const char *uri) g_string_append_printf(new, "%s\n", line); } g_strfreev(lines); - g_file_set_contents(vb.files[FILES_BOOKMARK], new->str, -1, NULL); + util_file_set_content(vb.files[FILES_BOOKMARK], new->str, 0600); g_string_free(new, TRUE); } @@ -217,7 +217,7 @@ gboolean bookmark_queue_unshift(const char *uri) */ char *bookmark_queue_pop(int *item_count) { - return util_file_pop_line(vb.files[FILES_QUEUE], item_count); + return util_file_pop_line(vb.files[FILES_QUEUE], item_count, 0600); } /** diff --git a/src/main.c b/src/main.c index 58b5f24..b6c3b5c 100644 --- a/src/main.c +++ b/src/main.c @@ -646,7 +646,7 @@ static void client_destroy(Client *c) * exists. */ if (c->state.uri && vb.config.closed_max && vb.files[FILES_CLOSED]) { util_file_prepend_line(vb.files[FILES_CLOSED], c->state.uri, - vb.config.closed_max); + vb.config.closed_max, 0600); } gtk_widget_destroy(c->window); diff --git a/src/normal.c b/src/normal.c index a2028a7..ca402ae 100644 --- a/src/normal.c +++ b/src/normal.c @@ -603,7 +603,7 @@ static VbResult normal_open(Client *c, const NormalCmdInfo *info) } a.i = info->key == 'U' ? TARGET_NEW : TARGET_CURRENT; - a.s = util_file_pop_line(vb.files[FILES_CLOSED], NULL); + a.s = util_file_pop_line(vb.files[FILES_CLOSED], NULL, 0600); if (!a.s) { return RESULT_ERROR; } diff --git a/src/util.c b/src/util.c index 76982c6..a3f4775 100644 --- a/src/util.c +++ b/src/util.c @@ -21,11 +21,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "ascii.h" #include "completion.h" @@ -104,7 +106,7 @@ void util_cleanup(void) gboolean util_create_dir_if_not_exists(const char *dirpath) { if (g_mkdir_with_parents(dirpath, 0755) == -1) { - g_critical("Could not create directory '%s': %s", dirpath, strerror(errno)); + g_critical("Could not create directory '%s': %s", dirpath, g_strerror(errno)); return FALSE; } @@ -252,9 +254,11 @@ gboolean util_file_prepend(const char *file, const char *format, ...) * @line: Line to be written as new first line into the file. * The line ending is inserted automatic. * @max_lines Maximum number of lines in file after the operation. + * @mode Mode (file permission as chmod(2)) used for the file when + * creating it. */ void util_file_prepend_line(const char *file, const char *line, - unsigned int max_lines) + unsigned int max_lines, int mode) { char **lines; GString *new_content; @@ -275,7 +279,7 @@ void util_file_prepend_line(const char *file, const char *line, } g_strfreev(lines); } - g_file_set_contents(file, new_content->str, -1, NULL); + util_file_set_content(file, new_content->str, mode); g_string_free(new_content, TRUE); } @@ -285,10 +289,12 @@ void util_file_prepend_line(const char *file, const char *line, * @file: file to read from * @item_count: will be filled with the number of remaining lines in file if it * is not NULL. + * @mode Mode (file permission as chmod(2)) used for the file when + * creating it. * * Returned string must be freed with g_free. */ -char *util_file_pop_line(const char *file, int *item_count) +char *util_file_pop_line(const char *file, int *item_count, int mode) { char **lines; char *new, @@ -308,7 +314,7 @@ char *util_file_pop_line(const char *file, int *item_count) /* minus one for last empty item and one for popped item */ count = len - 2; new = g_strjoinv("\n", lines + 1); - g_file_set_contents(file, new, -1, NULL); + util_file_set_content(file, new, mode); g_free(new); } g_strfreev(lines); @@ -350,6 +356,72 @@ char *util_get_file_contents(const char *filename, gsize *length) return content; } +/** + * Atomicly writes contents to given file. + * Returns TRUE on success, FALSE otherwise. + */ +char *util_file_set_content(const char *file, const char *contents, int mode) +{ + gboolean retval = FALSE; + char *tmp_name; + int fd; + gsize length; + + /* Create a temporary file. */ + tmp_name = g_strconcat(file, ".XXXXXX", NULL); + errno = 0; + fd = g_mkstemp_full(tmp_name, O_RDWR, mode); + length = strlen(contents); + + if (fd == -1) { + g_error("Failed to create file %s: %s", tmp_name, g_strerror(errno)); + + goto out; + } + + /* Write the contents to the temporary file. */ + while (length > 0) { + gssize s; + s = write(fd, contents, length); + if (s < 0) { + if (errno == EINTR) { + continue; + } + g_error("Failed to write to file %s: write() failed: %s", + tmp_name, g_strerror(errno)); + close(fd); + g_unlink(tmp_name); + + goto out; + } + + g_assert (s <= length); + + contents += s; + length -= s; + } + + if (!g_close(fd, NULL)) { + g_unlink(tmp_name); + goto out; + } + + /* Atomic rename the temporary file into the destination file. */ + if (g_rename(tmp_name, file) == -1) { + g_error("Failed to rename file %s to %s: g_rename() failed: %s", + tmp_name, file, g_strerror(errno)); + g_unlink(tmp_name); + goto out; + } + + retval = TRUE; + +out: + g_free(tmp_name); + + return retval; +} + /** * Buil the path from given directory and filename and checks if the file * exists. If the file does not exists and the create option is not set, this diff --git a/src/util.h b/src/util.h index cf52db2..816ed5f 100644 --- a/src/util.h +++ b/src/util.h @@ -38,10 +38,11 @@ char *util_expand(State state, const char *src, int expflags); gboolean util_file_append(const char *file, const char *format, ...); gboolean util_file_prepend(const char *file, const char *format, ...); void util_file_prepend_line(const char *file, const char *line, - unsigned int max_lines); -char *util_file_pop_line(const char *file, int *item_count); + unsigned int max_lines, int mode); +char *util_file_pop_line(const char *file, int *item_count, int mode); char *util_get_config_dir(void); char *util_get_file_contents(const char *filename, gsize *length); +char *util_file_set_content(const char *file, const char *contents, int mode); char *util_get_filepath(const char *dir, const char *filename, gboolean create, int mode); char **util_get_lines(const char *filename); GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,