From d64bf286a175f590fc07f51f49584cf246eac9b2 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 12 Sep 2012 13:26:34 -0600 Subject: Handle FIFOs in read_file_to_str add read_file_to_str_until_eof which is used by read_file_to_str if the file happens to be a FIFO. change file_status() to return FN_FILE if st_mode matches S_IFIFO (on not-windows) so that init_key_from_file() will read from a FIFO. --- src/common/util.c | 60 +++++++++++++++++++++++++++ src/common/util.h | 3 ++ src/test/test_util.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) (limited to 'src') diff --git a/src/common/util.c b/src/common/util.c index 3efc25378..feeaf740b 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1807,6 +1807,10 @@ file_status(const char *fname) return FN_DIR; else if (st.st_mode & S_IFREG) return FN_FILE; +#ifndef _WIN32 + else if (st.st_mode & S_IFIFO) + return FN_FILE; +#endif else return FN_ERROR; } @@ -2237,6 +2241,46 @@ write_bytes_to_new_file(const char *fname, const char *str, size_t len, (bin?O_BINARY:O_TEXT)); } +/** + * Read the contents of the open file fd presuming it is a FIFO + * (or similar) file descriptor for which the size of the file isn't + * known ahead of time. Return NULL on failure, and a NUL-terminated + * string on success. On success, set sz_out to the number of + * bytes read. + */ +char * +read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out) +{ + ssize_t r; + size_t pos = 0; + char *string = NULL; + size_t string_max = 0; + + if (max_bytes_to_read+1 >= SIZE_T_CEILING) + return NULL; + + do { + /* XXXX This "add 1K" approach is a little goofy; if we care about + * performance here, we should be doubling. But in practice we shouldn't + * be using this function on big files anyway. */ + string_max = pos + 1024; + if (string_max > max_bytes_to_read) + string_max = max_bytes_to_read + 1; + string = tor_realloc(string, string_max); + r = read(fd, string + pos, string_max - pos - 1); + if (r < 0) { + tor_free(string); + return NULL; + } + + pos += r; + } while (r > 0 && pos < max_bytes_to_read); + + *sz_out = pos; + string[pos] = '\0'; + return string; +} + /** Read the contents of filename into a newly allocated * string; return the string on success or NULL on failure. * @@ -2285,6 +2329,22 @@ read_file_to_str(const char *filename, int flags, struct stat *stat_out) return NULL; } +#ifndef _WIN32 +/** When we detect that we're reading from a FIFO, don't read more than + * this many bytes. It's insane overkill for most uses. */ +#define FIFO_READ_MAX (1024*1024) + if (S_ISFIFO(statbuf.st_mode)) { + size_t sz = 0; + string = read_file_to_str_until_eof(fd, FIFO_READ_MAX, &sz); + if (string && stat_out) { + statbuf.st_size = sz; + memcpy(stat_out, &statbuf, sizeof(struct stat)); + } + close(fd); + return string; + } +#endif + if ((uint64_t)(statbuf.st_size)+1 >= SIZE_T_CEILING) return NULL; diff --git a/src/common/util.h b/src/common/util.h index a6944ac89..0cfc1b9d8 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -360,6 +360,9 @@ struct stat; #endif char *read_file_to_str(const char *filename, int flags, struct stat *stat_out) ATTR_MALLOC; +char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, + size_t *sz_out) + ATTR_MALLOC; const char *parse_config_line_from_str(const char *line, char **key_out, char **value_out); char *expand_filename(const char *filename); diff --git a/src/test/test_util.c b/src/test/test_util.c index 64976f339..cc3a03f84 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -18,6 +18,117 @@ #include #endif +static void +test_util_read_file_fifo_tiny_limit(void *arg) +{ +#ifndef _WIN32 + char *fifo_name = NULL; + char *str = NULL; + int fd = -1; + int read_fd = -1; + size_t sz; + (void)arg; + + fifo_name = tor_strdup(get_fname("tor_test_fifo_tiny")); + fd = open(fifo_name, O_WRONLY | O_CREAT, 0600); + test_neq(fd, -1); + test_eq(write(fd, "short", 6), 6); + close(fd); + + // purposely set limit shorter than what we wrote to the FIFO to + // test the maximum, and that it puts the NUL in the right spot + + read_fd = open(fifo_name, O_RDONLY); + str = read_file_to_str_until_eof(read_fd, 4, &sz); + close(read_fd); + + test_eq(str[0], 's'); + test_eq(str[1], 'h'); + test_eq(str[2], 'o'); + test_eq(str[3], 'r'); + test_eq(str[4], '\0'); + + done: + unlink(fifo_name); + tor_free(fifo_name); + tor_free(str); +#endif +} + +static void +test_util_read_file_fifo_two_loops(void *arg) +{ +#ifndef _WIN32 + char *fifo_name = NULL; + char *str = NULL; + char data[2048]; + int i = 0; + int fd = -1; + int read_fd = -1; + size_t sz; + (void)arg; + + while (i < 2048) { + data[i] = (char)(i & 0xff); + ++i; + } + + // write more than 1024 bytes to the FIFO to test two passes through + // the loop in the method; if the re-alloc size is changed this + // should be updated as well. + + fifo_name = tor_strdup(get_fname("tor_fifo_test_2k")); + fd = open(fifo_name, O_WRONLY | O_CREAT, 0600); + test_neq(fd, -1); + test_eq(write(fd, data, 2048), 2048); + close(fd); + + read_fd = open(fifo_name, O_RDONLY); + str = read_file_to_str_until_eof(read_fd, 1024*1024, &sz); + close(read_fd); + + for (i = 0; i < 2048; ++i) { + test_eq(str[i], (char)(i & 0xff)); + } + + done: + unlink(fifo_name); + tor_free(fifo_name); + tor_free(str); +#endif +} + +static void +test_util_read_file_fifo_zero_bytes(void *arg) +{ +#ifndef _WIN32 + char *fifo_name = NULL; + char *str = NULL; + int fd = -1; + int read_fd = -1; + size_t sz; + (void)arg; + + fifo_name = tor_strdup(get_fname("tor_fifo_test_zero_bytes")); + // zero-byte fifo + fd = open(fifo_name, O_WRONLY | O_CREAT, 0600); + test_neq(fd, -1); + close(fd); + + read_fd = open(fifo_name, O_RDONLY); + str = read_file_to_str_until_eof(read_fd, 1024, &sz); + close(read_fd); + + test_neq(str, NULL); + test_eq(str[0], '\0'); + + done: + unlink(fifo_name); + tor_free(fifo_name); + tor_free(str); +#endif +} + static void test_util_time(void) { @@ -3191,6 +3302,9 @@ struct testcase_t util_tests[] = { UTIL_TEST(envnames, 0), UTIL_TEST(make_environment, 0), UTIL_TEST(set_env_var_in_sl, 0), + UTIL_TEST(read_file_fifo_tiny_limit, 0), + UTIL_TEST(read_file_fifo_two_loops, 0), + UTIL_TEST(read_file_fifo_zero_bytes, 0), END_OF_TESTCASES }; -- cgit v1.2.3 From 4b362d002ccd2008ebfa4b44e3ca61e8efa96cac Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 14 Sep 2012 12:23:31 -0400 Subject: Turn the read_file_until_eof tests into a single implementation --- src/test/test_util.c | 119 ++++++++++++++++----------------------------------- 1 file changed, 38 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/test/test_util.c b/src/test/test_util.c index cc3a03f84..173410863 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -19,114 +19,71 @@ #endif static void -test_util_read_file_fifo_tiny_limit(void *arg) +test_util_read_until_eof_impl(const char *fname, size_t file_len, + size_t read_limit) { -#ifndef _WIN32 char *fifo_name = NULL; + char *test_str = NULL; char *str = NULL; + size_t sz = 9999999; int fd = -1; - int read_fd = -1; - size_t sz; - (void)arg; + int r; - fifo_name = tor_strdup(get_fname("tor_test_fifo_tiny")); - fd = open(fifo_name, O_WRONLY | O_CREAT, 0600); - test_neq(fd, -1); - test_eq(write(fd, "short", 6), 6); - close(fd); + fifo_name = tor_strdup(get_fname(fname)); + test_str = tor_malloc(file_len); + crypto_rand(test_str, file_len); - // purposely set limit shorter than what we wrote to the FIFO to - // test the maximum, and that it puts the NUL in the right spot + r = write_bytes_to_file(fifo_name, test_str, file_len, 1); + tt_int_op(r, ==, 0); + + fd = open(fifo_name, O_RDONLY|O_BINARY); + tt_int_op(fd, >=, 0); + str = read_file_to_str_until_eof(fd, read_limit, &sz); + close(fd); + tt_assert(str != NULL); - read_fd = open(fifo_name, O_RDONLY); - str = read_file_to_str_until_eof(read_fd, 4, &sz); - close(read_fd); + if (read_limit < file_len) + tt_int_op(sz, ==, read_limit); + else + tt_int_op(sz, ==, file_len); - test_eq(str[0], 's'); - test_eq(str[1], 'h'); - test_eq(str[2], 'o'); - test_eq(str[3], 'r'); - test_eq(str[4], '\0'); + test_mem_op(test_str, ==, str, sz); + test_assert(str[sz] == '\0'); done: unlink(fifo_name); tor_free(fifo_name); + tor_free(test_str); tor_free(str); -#endif } static void -test_util_read_file_fifo_two_loops(void *arg) +test_util_read_file_eof_tiny_limit(void *arg) { -#ifndef _WIN32 - char *fifo_name = NULL; - char *str = NULL; - char data[2048]; - int i = 0; - int fd = -1; - int read_fd = -1; - size_t sz; (void)arg; + // purposely set limit shorter than what we wrote to the FIFO to + // test the maximum, and that it puts the NUL in the right spot - while (i < 2048) { - data[i] = (char)(i & 0xff); - ++i; - } + test_util_read_until_eof_impl("tor_test_fifo_tiny", 5, 4); +} +static void +test_util_read_file_eof_two_loops(void *arg) +{ + (void)arg; // write more than 1024 bytes to the FIFO to test two passes through // the loop in the method; if the re-alloc size is changed this // should be updated as well. - fifo_name = tor_strdup(get_fname("tor_fifo_test_2k")); - fd = open(fifo_name, O_WRONLY | O_CREAT, 0600); - test_neq(fd, -1); - test_eq(write(fd, data, 2048), 2048); - close(fd); - - read_fd = open(fifo_name, O_RDONLY); - str = read_file_to_str_until_eof(read_fd, 1024*1024, &sz); - close(read_fd); - - for (i = 0; i < 2048; ++i) { - test_eq(str[i], (char)(i & 0xff)); - } - - done: - unlink(fifo_name); - tor_free(fifo_name); - tor_free(str); -#endif + test_util_read_until_eof_impl("tor_test_fifo_2k", 2048, 10000); } static void -test_util_read_file_fifo_zero_bytes(void *arg) +test_util_read_file_eof_zero_bytes(void *arg) { -#ifndef _WIN32 - char *fifo_name = NULL; - char *str = NULL; - int fd = -1; - int read_fd = -1; - size_t sz; (void)arg; - - fifo_name = tor_strdup(get_fname("tor_fifo_test_zero_bytes")); // zero-byte fifo - fd = open(fifo_name, O_WRONLY | O_CREAT, 0600); - test_neq(fd, -1); - close(fd); - - read_fd = open(fifo_name, O_RDONLY); - str = read_file_to_str_until_eof(read_fd, 1024, &sz); - close(read_fd); - - test_neq(str, NULL); - test_eq(str[0], '\0'); - - done: - unlink(fifo_name); - tor_free(fifo_name); - tor_free(str); -#endif + test_util_read_until_eof_impl("tor_test_fifo_empty", 0, 10000); } static void @@ -3302,9 +3259,9 @@ struct testcase_t util_tests[] = { UTIL_TEST(envnames, 0), UTIL_TEST(make_environment, 0), UTIL_TEST(set_env_var_in_sl, 0), - UTIL_TEST(read_file_fifo_tiny_limit, 0), - UTIL_TEST(read_file_fifo_two_loops, 0), - UTIL_TEST(read_file_fifo_zero_bytes, 0), + UTIL_TEST(read_file_eof_tiny_limit, 0), + UTIL_TEST(read_file_eof_two_loops, 0), + UTIL_TEST(read_file_eof_zero_bytes, 0), END_OF_TESTCASES }; -- cgit v1.2.3