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 7700cfa2b1..2aee07a26a 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 */ @@ -6495,6 +6543,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),