diff options
Diffstat (limited to 'src/common/util.c')
-rw-r--r-- | src/common/util.c | 137 |
1 files changed, 104 insertions, 33 deletions
diff --git a/src/common/util.c b/src/common/util.c index b3d3932578..a0c8b0cf54 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1459,61 +1459,132 @@ write_str_to_file(const char *fname, const char *str, int bin) return write_bytes_to_file(fname, str, strlen(str), bin); } -/** Helper: given a set of flags as passed to open(2), open the file - * <b>fname</b> and write all the sized_chunk_t structs in <b>chunks</b> to - * the file. Do so as atomically as possible e.g. by opening temp files and - * renaming. */ -static int -write_chunks_to_file_impl(const char *fname, const smartlist_t *chunks, - int open_flags) -{ - size_t tempname_len; +/** DOCDOC */ +struct open_file_t { char *tempname; + char *filename; + int rename_on_close; int fd; - int result; +}; + +/** DOCDOC */ +int +start_writing_to_file(const char *fname, int open_flags, int mode, + open_file_t **data_out) +{ + size_t tempname_len = strlen(fname)+16; + open_file_t *new_file = tor_malloc_zero(sizeof(open_file_t)); + const char *open_name; + tor_assert(fname); + tor_assert(data_out); + new_file->fd = -1; tempname_len = strlen(fname)+16; tor_assert(tempname_len > strlen(fname)); /*check for overflow*/ - tempname = tor_malloc(tempname_len); + new_file->filename = tor_strdup(fname); if (open_flags & O_APPEND) { - strlcpy(tempname, fname, tempname_len); + open_name = fname; + new_file->rename_on_close = 0; } else { - if (tor_snprintf(tempname, tempname_len, "%s.tmp", fname)<0) { + new_file->tempname = tor_malloc(tempname_len); + if (tor_snprintf(new_file->tempname, tempname_len, "%s.tmp", fname)<0) { log(LOG_WARN, LD_GENERAL, "Failed to generate filename"); goto err; } + new_file->rename_on_close = 1; } - if ((fd = open(tempname, open_flags, 0600)) + + if ((new_file->fd = open(new_file->tempname, open_flags, mode)) < 0) { - log(LOG_WARN, LD_FS, "Couldn't open \"%s\" for writing: %s", tempname, - strerror(errno)); + log(LOG_WARN, LD_FS, "Couldn't open \"%s\" for writing: %s", + new_file->tempname, strerror(errno)); goto err; } + + *data_out = new_file; + + return new_file->fd; + err: + *data_out = NULL; + tor_free(new_file->filename); + tor_free(new_file->tempname); + tor_free(new_file); + return -1; +} + +/** DOCDOC */ +static int +finish_writing_to_file_impl(open_file_t *file_data, int abort_write) +{ + int r = 0; + tor_assert(file_data && file_data->filename); + if (file_data->fd >= 0 && close(file_data->fd) < 0) { + log_warn(LD_FS, "Error flushing \"%s\": %s", file_data->filename, + strerror(errno)); + abort_write = 1; + r = -1; + } + + if (file_data->rename_on_close) { + tor_assert(file_data->tempname && file_data->filename); + if (abort_write) { + unlink(file_data->tempname); + } else { + tor_assert(strcmp(file_data->filename, file_data->tempname)); + if (replace_file(file_data->tempname, file_data->filename)) { + log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename, + strerror(errno)); + r = -1; + } + } + } + + tor_free(file_data->filename); + tor_free(file_data->tempname); + tor_free(file_data); + + return r; +} + +/** DOCDOC */ +int +finish_writing_to_file(open_file_t *file_data) +{ + return finish_writing_to_file_impl(file_data, 0); +} + +/** DOCDOC */ +int +abort_writing_to_file(open_file_t *file_data) +{ + return finish_writing_to_file_impl(file_data, 1); +} + +/** Helper: given a set of flags as passed to open(2), open the file + * <b>fname</b> and write all the sized_chunk_t structs in <b>chunks</b> to + * the file. Do so as atomically as possible e.g. by opening temp files and + * renaming. */ +static int +write_chunks_to_file_impl(const char *fname, const smartlist_t *chunks, + int open_flags) +{ + open_file_t *file = NULL; + int fd, result; + fd = start_writing_to_file(fname, open_flags, 0600, &file); + if (fd<0) + return -1; SMARTLIST_FOREACH(chunks, sized_chunk_t *, chunk, { result = write_all(fd, chunk->bytes, chunk->len, 0); if (result < 0 || (size_t)result != chunk->len) { - log(LOG_WARN, LD_FS, "Error writing to \"%s\": %s", tempname, + log(LOG_WARN, LD_FS, "Error writing to \"%s\": %s", fname, strerror(errno)); - close(fd); goto err; } }); - if (close(fd)) { - log(LOG_WARN, LD_FS, "Error flushing to \"%s\": %s", tempname, - strerror(errno)); - goto err; - } - if (!(open_flags & O_APPEND)) { - if (replace_file(tempname, fname)) { - log(LOG_WARN, LD_FS, "Error replacing \"%s\": %s", fname, - strerror(errno)); - goto err; - } - } - tor_free(tempname); - return 0; + + return finish_writing_to_file(file); err: - tor_free(tempname); + abort_writing_to_file(file); return -1; } |