diff --git a/changes/bug33781 b/changes/bug33781
new file mode 100644
index 0000000000..9f63ab0a2c
--- /dev/null
+++ b/changes/bug33781
@@ -0,0 +1,7 @@
+ o Minor bugfixes (compatibility):
+ - Strip '\r' characters when reading text files on Unix platforms.
+ This should resolve an issue where a relay operator migrates a relay from
+ Windows to Unix, but does not change the line ending of Tor's various state
+ files to match the platform, the CRLF line endings from Windows ends up leaking
+ into other files such as the extra-info document. Fixes bug 33781; bugfix on
+ 0.0.9pre5.
diff --git a/src/lib/fs/files.c b/src/lib/fs/files.c
index aeaeb5daea..a0b5a40aac 100644
--- a/src/lib/fs/files.c
+++ b/src/lib/fs/files.c
@@ -607,6 +607,9 @@ read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out)
* If flags & RFTS_BIN, open the file in binary mode.
* If flags & RFTS_IGNORE_MISSING, don't warn if the file
* doesn't exist.
+ *
+ * Unless the RFTS_BIN flag is set in flags, this function will strip
+ * any CR characters in the return value on all platforms.
*/
/*
* This function may return an erroneous result if the file
@@ -685,7 +688,6 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
}
string[r] = '\0'; /* NUL-terminate the result. */
-#if defined(_WIN32) || defined(__CYGWIN__)
if (!bin && strchr(string, '\r')) {
log_debug(LD_FS, "We didn't convert CRLF to LF as well as we hoped "
"when reading %s. Coping.",
@@ -695,8 +697,7 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
}
if (!bin) {
statbuf.st_size = (size_t) r;
- } else
-#endif /* defined(_WIN32) || defined(__CYGWIN__) */
+ } else {
if (r != statbuf.st_size) {
/* Unless we're using text mode on win32, we'd better have an exact
* match for size. */
@@ -708,6 +709,7 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
errno = save_errno;
return NULL;
}
+ }
close(fd);
if (stat_out) {
memcpy(stat_out, &statbuf, sizeof(struct stat));
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 0d86a5ab5d..e7f4106d91 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -206,6 +206,54 @@ test_util_read_file_eof_zero_bytes(void *arg)
test_util_read_until_eof_impl("tor_test_fifo_empty", 0, 10000);
}
+static void
+test_util_read_file_endlines(void *arg)
+{
+ (void)arg;
+
+ char *fname = NULL;
+ char *read_content = NULL;
+ int r = -1;
+
+ /* Write a file that contains both \n and \r\n as line ending. */
+ const char *file_content = "foo bar\n"
+ "foo bar baz\r\n"
+ "foo bar\r\n";
+
+ const char *expected_file_content = "foo bar\n"
+ "foo bar baz\n"
+ "foo bar\n";
+
+ fname = tor_strdup(get_fname("file_with_crlf_ending"));
+
+ r = write_bytes_to_file(fname, file_content, strlen(file_content), 1);
+ tt_int_op(r, OP_EQ, 0);
+
+ /* Read the file in text mode: we strip \r's from the files on both Windows
+ * and UNIX. */
+ read_content = read_file_to_str(fname, 0, NULL);
+
+ tt_ptr_op(read_content, OP_NE, NULL);
+ tt_int_op(strlen(read_content), OP_EQ, strlen(expected_file_content));
+ tt_str_op(read_content, OP_EQ, expected_file_content);
+
+ tor_free(read_content);
+
+ /* Read the file in binary mode: we should preserve the \r here. */
+ read_content = read_file_to_str(fname, RFTS_BIN, NULL);
+
+ tt_ptr_op(read_content, OP_NE, NULL);
+ tt_int_op(strlen(read_content), OP_EQ, strlen(file_content));
+ tt_str_op(read_content, OP_EQ, file_content);
+
+ tor_free(read_content);
+
+ done:
+ unlink(fname);
+ tor_free(fname);
+ tor_free(read_content);
+}
+
/* Test the basic expected behaviour for write_chunks_to_file.
* NOTE: This will need to be updated if we ever change the tempfile location
* or extension */
@@ -6414,6 +6462,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(read_file_eof_two_loops, 0),
UTIL_TEST(read_file_eof_two_loops_b, 0),
UTIL_TEST(read_file_eof_zero_bytes, 0),
+ UTIL_TEST(read_file_endlines, 0),
UTIL_TEST(write_chunks_to_file, 0),
UTIL_TEST(mathlog, 0),
UTIL_TEST(fraction, 0),