aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2007-08-29 19:02:37 +0000
committerNick Mathewson <nickm@torproject.org>2007-08-29 19:02:37 +0000
commit8408122222bda77347e46dedafc12abcd2b45e1e (patch)
treef2f29a1c3194f5da885334375813c858d9534894
parent91f83cfc2d380aa76fd049d13cf9fb097fecaab6 (diff)
downloadtor-8408122222bda77347e46dedafc12abcd2b45e1e.tar
tor-8408122222bda77347e46dedafc12abcd2b45e1e.tar.gz
r14831@catbus: nickm | 2007-08-29 14:17:42 -0400
Refactor write_chunks_to_file_impl: break out the "pick a temporary name if it makes sense, and open the right filename" logic and the "close the file and unlink or rename if necessary" logic. This will let us write big files in a smarter way than "Build a big string" or "make a list of chunks", once we get around to using it. svn:r11300
-rw-r--r--src/common/util.c137
-rw-r--r--src/common/util.h5
2 files changed, 109 insertions, 33 deletions
diff --git a/src/common/util.c b/src/common/util.c
index b3d393257..a0c8b0cf5 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;
}
diff --git a/src/common/util.h b/src/common/util.h
index 80d4aad74..0b7dfc6cb 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -213,6 +213,11 @@ file_status_t file_status(const char *filename);
* directory; see that function's documentation for details. */
typedef enum { CPD_NONE, CPD_CREATE, CPD_CHECK } cpd_check_t;
int check_private_dir(const char *dirname, cpd_check_t check);
+typedef struct open_file_t open_file_t;
+int start_writing_to_file(const char *fname, int open_flags, int mode,
+ open_file_t **data_out);
+int finish_writing_to_file(open_file_t *file_data);
+int abort_writing_to_file(open_file_t *file_data);
int write_str_to_file(const char *fname, const char *str, int bin);
int write_bytes_to_file(const char *fname, const char *str, size_t len,
int bin);