summaryrefslogtreecommitdiff
path: root/src/common/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/util.c')
-rw-r--r--src/common/util.c137
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;
}