Browse Source

Merge branch '6044_nm_squashed'

Nick Mathewson 11 years ago
parent
commit
c3f526ed64
4 changed files with 139 additions and 0 deletions
  1. 5 0
      changes/6044
  2. 60 0
      src/common/util.c
  3. 3 0
      src/common/util.h
  4. 71 0
      src/test/test_util.c

+ 5 - 0
changes/6044

@@ -0,0 +1,5 @@
+  o Minor features:
+    - Enable Tor to read configuration, state, and key information from
+      a FIFO. Previously Tor would only read from files with a positive
+      stat.st_size. Code from meejah; fixes bug 6044.
+

+ 60 - 0
src/common/util.c

@@ -1833,6 +1833,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;
 }
@@ -2263,6 +2267,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 <b>fd</b> 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 <b>sz_out</b> 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 <b>filename</b> into a newly allocated
  * string; return the string on success or NULL on failure.
  *
@@ -2311,6 +2355,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;
 

+ 3 - 0
src/common/util.h

@@ -361,6 +361,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);

+ 71 - 0
src/test/test_util.c

@@ -31,6 +31,74 @@ tor_timegm_wrapper(const struct tm *tm)
 
 #define tor_timegm tor_timegm_wrapper
 
+static void
+test_util_read_until_eof_impl(const char *fname, size_t file_len,
+                              size_t read_limit)
+{
+  char *fifo_name = NULL;
+  char *test_str = NULL;
+  char *str = NULL;
+  size_t sz = 9999999;
+  int fd = -1;
+  int r;
+
+  fifo_name = tor_strdup(get_fname(fname));
+  test_str = tor_malloc(file_len);
+  crypto_rand(test_str, file_len);
+
+  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);
+
+  if (read_limit < file_len)
+    tt_int_op(sz, ==, read_limit);
+  else
+    tt_int_op(sz, ==, file_len);
+
+  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);
+}
+
+static void
+test_util_read_file_eof_tiny_limit(void *arg)
+{
+  (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
+
+  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.
+
+  test_util_read_until_eof_impl("tor_test_fifo_2k", 2048, 10000);
+}
+
+static void
+test_util_read_file_eof_zero_bytes(void *arg)
+{
+  (void)arg;
+  // zero-byte fifo
+  test_util_read_until_eof_impl("tor_test_fifo_empty", 0, 10000);
+}
+
 static void
 test_util_time(void)
 {
@@ -3214,6 +3282,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_eof_tiny_limit, 0),
+  UTIL_TEST(read_file_eof_two_loops, 0),
+  UTIL_TEST(read_file_eof_zero_bytes, 0),
   END_OF_TESTCASES
 };