Commit 9d34a1e0 authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Merge branch 'storage_labeled_squashed'

parents 4c2ad485 5567a80c
Loading
Loading
Loading
Loading

src/common/confline.c

0 → 100644
+320 −0
Original line number Diff line number Diff line
/* Copyright (c) 2001 Matej Pfajfar.
 * Copyright (c) 2001-2004, Roger Dingledine.
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
 * Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */

#include "compat.h"
#include "confline.h"
#include "torlog.h"
#include "util.h"

/** Helper: allocate a new configuration option mapping 'key' to 'val',
 * append it to *<b>lst</b>. */
void
config_line_append(config_line_t **lst,
                   const char *key,
                   const char *val)
{
  tor_assert(lst);

  config_line_t *newline;

  newline = tor_malloc_zero(sizeof(config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = NULL;
  while (*lst)
    lst = &((*lst)->next);

  (*lst) = newline;
}

/** Return the first line in <b>lines</b> whose key is exactly <b>key</b>, or
 * NULL if no such key exists.
 *
 * (In options parsing, this is for handling commandline-only options only;
 * other options should be looked up in the appropriate data structure.) */
const config_line_t *
config_line_find(const config_line_t *lines,
                 const char *key)
{
  const config_line_t *cl;
  for (cl = lines; cl; cl = cl->next) {
    if (!strcmp(cl->key, key))
      return cl;
  }
  return NULL;
}

/** Helper: parse the config string and strdup into key/value
 * strings. Set *result to the list, or NULL if parsing the string
 * failed.  Return 0 on success, -1 on failure. Warn and ignore any
 * misformatted lines.
 *
 * If <b>extended</b> is set, then treat keys beginning with / and with + as
 * indicating "clear" and "append" respectively. */
int
config_get_lines(const char *string, config_line_t **result, int extended)
{
  config_line_t *list = NULL, **next;
  char *k, *v;
  const char *parse_err;

  next = &list;
  do {
    k = v = NULL;
    string = parse_config_line_from_str_verbose(string, &k, &v, &parse_err);
    if (!string) {
      log_warn(LD_CONFIG, "Error while parsing configuration: %s",
               parse_err?parse_err:"<unknown>");
      config_free_lines(list);
      tor_free(k);
      tor_free(v);
      return -1;
    }
    if (k && v) {
      unsigned command = CONFIG_LINE_NORMAL;
      if (extended) {
        if (k[0] == '+') {
          char *k_new = tor_strdup(k+1);
          tor_free(k);
          k = k_new;
          command = CONFIG_LINE_APPEND;
        } else if (k[0] == '/') {
          char *k_new = tor_strdup(k+1);
          tor_free(k);
          k = k_new;
          tor_free(v);
          v = tor_strdup("");
          command = CONFIG_LINE_CLEAR;
        }
      }
      /* This list can get long, so we keep a pointer to the end of it
       * rather than using config_line_append over and over and getting
       * n^2 performance. */
      *next = tor_malloc_zero(sizeof(config_line_t));
      (*next)->key = k;
      (*next)->value = v;
      (*next)->next = NULL;
      (*next)->command = command;
      next = &((*next)->next);
    } else {
      tor_free(k);
      tor_free(v);
    }
  } while (*string);

  *result = list;
  return 0;
}

/**
 * Free all the configuration lines on the linked list <b>front</b>.
 */
void
config_free_lines(config_line_t *front)
{
  config_line_t *tmp;

  while (front) {
    tmp = front;
    front = tmp->next;

    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
  }
}

/** Return a newly allocated deep copy of the lines in <b>inp</b>. */
config_line_t *
config_lines_dup(const config_line_t *inp)
{
  return config_lines_dup_and_filter(inp, NULL);
}

/** Return a newly allocated deep copy of the lines in <b>inp</b>,
 * but only the ones that match <b>key</b>. */
config_line_t *
config_lines_dup_and_filter(const config_line_t *inp,
                            const char *key)
{
  config_line_t *result = NULL;
  config_line_t **next_out = &result;
  while (inp) {
    if (key && strcasecmpstart(inp->key, key)) {
      inp = inp->next;
      continue;
    }
    *next_out = tor_malloc_zero(sizeof(config_line_t));
    (*next_out)->key = tor_strdup(inp->key);
    (*next_out)->value = tor_strdup(inp->value);
    inp = inp->next;
    next_out = &((*next_out)->next);
  }
  (*next_out) = NULL;
  return result;
}

/** Return true iff a and b contain identical keys and values in identical
 * order. */
int
config_lines_eq(config_line_t *a, config_line_t *b)
{
  while (a && b) {
    if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value))
      return 0;
    a = a->next;
    b = b->next;
  }
  if (a || b)
    return 0;
  return 1;
}

/** Return the number of lines in <b>a</b> whose key is <b>key</b>. */
int
config_count_key(const config_line_t *a, const char *key)
{
  int n = 0;
  while (a) {
    if (!strcasecmp(a->key, key)) {
      ++n;
    }
    a = a->next;
  }
  return n;
}

/** Given a string containing part of a configuration file or similar format,
 * advance past comments and whitespace and try to parse a single line.  If we
 * parse a line successfully, set *<b>key_out</b> to a new string holding the
 * key portion and *<b>value_out</b> to a new string holding the value portion
 * of the line, and return a pointer to the start of the next line.  If we run
 * out of data, return a pointer to the end of the string.  If we encounter an
 * error, return NULL and set *<b>err_out</b> (if provided) to an error
 * message.
 */
const char *
parse_config_line_from_str_verbose(const char *line, char **key_out,
                                   char **value_out,
                                   const char **err_out)
{
  /*
    See torrc_format.txt for a description of the (silly) format this parses.
   */
  const char *key, *val, *cp;
  int continuation = 0;

  tor_assert(key_out);
  tor_assert(value_out);

  *key_out = *value_out = NULL;
  key = val = NULL;
  /* Skip until the first keyword. */
  while (1) {
    while (TOR_ISSPACE(*line))
      ++line;
    if (*line == '#') {
      while (*line && *line != '\n')
        ++line;
    } else {
      break;
    }
  }

  if (!*line) { /* End of string? */
    *key_out = *value_out = NULL;
    return line;
  }

  /* Skip until the next space or \ followed by newline. */
  key = line;
  while (*line && !TOR_ISSPACE(*line) && *line != '#' &&
         ! (line[0] == '\\' && line[1] == '\n'))
    ++line;
  *key_out = tor_strndup(key, line-key);

  /* Skip until the value. */
  while (*line == ' ' || *line == '\t')
    ++line;

  val = line;

  /* Find the end of the line. */
  if (*line == '\"') { // XXX No continuation handling is done here
    if (!(line = unescape_string(line, value_out, NULL))) {
      if (err_out)
        *err_out = "Invalid escape sequence in quoted string";
      return NULL;
    }
    while (*line == ' ' || *line == '\t')
      ++line;
    if (*line == '\r' && *(++line) == '\n')
      ++line;
    if (*line && *line != '#' && *line != '\n') {
      if (err_out)
        *err_out = "Excess data after quoted string";
      return NULL;
    }
  } else {
    /* Look for the end of the line. */
    while (*line && *line != '\n' && (*line != '#' || continuation)) {
      if (*line == '\\' && line[1] == '\n') {
        continuation = 1;
        line += 2;
      } else if (*line == '#') {
        do {
          ++line;
        } while (*line && *line != '\n');
        if (*line == '\n')
          ++line;
      } else {
        ++line;
      }
    }

    if (*line == '\n') {
      cp = line++;
    } else {
      cp = line;
    }
    /* Now back cp up to be the last nonspace character */
    while (cp>val && TOR_ISSPACE(*(cp-1)))
      --cp;

    tor_assert(cp >= val);

    /* Now copy out and decode the value. */
    *value_out = tor_strndup(val, cp-val);
    if (continuation) {
      char *v_out, *v_in;
      v_out = v_in = *value_out;
      while (*v_in) {
        if (*v_in == '#') {
          do {
            ++v_in;
          } while (*v_in && *v_in != '\n');
          if (*v_in == '\n')
            ++v_in;
        } else if (v_in[0] == '\\' && v_in[1] == '\n') {
          v_in += 2;
        } else {
          *v_out++ = *v_in++;
        }
      }
      *v_out = '\0';
    }
  }

  if (*line == '#') {
    do {
      ++line;
    } while (*line && *line != '\n');
  }
  while (TOR_ISSPACE(*line)) ++line;

  return line;
}

src/common/confline.h

0 → 100644
+47 −0
Original line number Diff line number Diff line
/* Copyright (c) 2001 Matej Pfajfar.
 * Copyright (c) 2001-2004, Roger Dingledine.
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
 * Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */

#ifndef TOR_CONFLINE_H
#define TOR_CONFLINE_H

/** Ordinary configuration line. */
#define CONFIG_LINE_NORMAL 0
/** Appends to previous configuration for the same option, even if we
 * would ordinary replace it. */
#define CONFIG_LINE_APPEND 1
/* Removes all previous configuration for an option. */
#define CONFIG_LINE_CLEAR 2

/** A linked list of lines in a config file, or elsewhere */
typedef struct config_line_t {
  char *key;
  char *value;
  struct config_line_t *next;

  /** What special treatment (if any) does this line require? */
  unsigned int command:2;
  /** If true, subsequent assignments to this linelist should replace
   * it, not extend it.  Set only on the first item in a linelist in an
   * or_options_t. */
  unsigned int fragile:1;
} config_line_t;

void config_line_append(config_line_t **lst,
                        const char *key, const char *val);
config_line_t *config_lines_dup(const config_line_t *inp);
config_line_t *config_lines_dup_and_filter(const config_line_t *inp,
                                           const char *key);
const config_line_t *config_line_find(const config_line_t *lines,
                                      const char *key);
int config_lines_eq(config_line_t *a, config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
int config_get_lines(const char *string, config_line_t **result, int extended);
void config_free_lines(config_line_t *front);
const char *parse_config_line_from_str_verbose(const char *line,
                                       char **key_out, char **value_out,
                                       const char **err_out);
#endif
+2 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ LIBOR_A_SRC = \
  src/common/compat.c					\
  src/common/compat_threads.c				\
  src/common/compat_time.c				\
  src/common/confline.c					\
  src/common/container.c				\
  src/common/log.c					\
  src/common/memarea.c					\
@@ -144,6 +145,7 @@ COMMONHEADERS = \
  src/common/compat_openssl.h			\
  src/common/compat_threads.h			\
  src/common/compat_time.h			\
  src/common/confline.h				\
  src/common/container.h			\
  src/common/crypto.h				\
  src/common/crypto_curve25519.h		\
+134 −12
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@

#include "container.h"
#include "compat.h"
#include "confline.h"
#include "memarea.h"
#include "sandbox.h"
#include "storagedir.h"
#include "torlog.h"
@@ -237,28 +239,29 @@ find_unused_fname(storage_dir_t *d)
  return NULL;
}

/** Try to write the <b>length</b> bytes at <b>data</b> into a new file
 * in <b>d</b>.  On success, return 0 and set *<b>fname_out</b> to a
 * newly allocated string containing the filename.  On failure, return
 * -1. */
int
storage_dir_save_bytes_to_file(storage_dir_t *d,
                               const uint8_t *data,
                               size_t length,
/** Helper: As storage_dir_save_bytes_to_file, but store a smartlist of
 * sized_chunk_t rather than a single byte array. */
static int
storage_dir_save_chunks_to_file(storage_dir_t *d,
                                const smartlist_t *chunks,
                                int binary,
                                char **fname_out)
{
  uint64_t total_length = 0;
  char *fname = find_unused_fname(d);
  if (!fname)
    return -1;

  SMARTLIST_FOREACH(chunks, const sized_chunk_t *, ch,
                    total_length += ch->len);

  char *path = NULL;
  tor_asprintf(&path, "%s/%s", d->directory, fname);

  int r = write_bytes_to_file(path, (const char *)data, length, binary);
  int r = write_chunks_to_file(path, chunks, binary, 0);
  if (r == 0) {
    if (d->usage_known)
      d->usage += length;
      d->usage += total_length;
    if (fname_out) {
      *fname_out = tor_strdup(fname);
    }
@@ -270,6 +273,25 @@ storage_dir_save_bytes_to_file(storage_dir_t *d,
  return r;
}

/** Try to write the <b>length</b> bytes at <b>data</b> into a new file
 * in <b>d</b>.  On success, return 0 and set *<b>fname_out</b> to a
 * newly allocated string containing the filename.  On failure, return
 * -1. */
int
storage_dir_save_bytes_to_file(storage_dir_t *d,
                               const uint8_t *data,
                               size_t length,
                               int binary,
                               char **fname_out)
{
  smartlist_t *chunks = smartlist_new();
  sized_chunk_t chunk = { (const char *)data, length };
  smartlist_add(chunks, &chunk);
  int r = storage_dir_save_chunks_to_file(d, chunks, binary, fname_out);
  smartlist_free(chunks);
  return r;
}

/**
 * As storage_dir_save_bytes_to_file, but saves a NUL-terminated string
 * <b>str</b>.
@@ -284,6 +306,106 @@ storage_dir_save_string_to_file(storage_dir_t *d,
                (const uint8_t*)str, strlen(str), binary, fname_out);
}

/**
 * As storage_dir_save_bytes_to_file, but associates the data with the
 * key-value pairs in <b>labels</b>. Files
 * stored in this format can be recovered with storage_dir_map_labeled
 * or storage_dir_read_labeled().
 */
int
storage_dir_save_labeled_to_file(storage_dir_t *d,
                                  const config_line_t *labels,
                                  const uint8_t *data,
                                  size_t length,
                                  char **fname_out)
{
  /*
   * The storage format is to prefix the data with the key-value pairs in
   * <b>labels</b>, and a single NUL separator.  But code outside this module
   * MUST NOT rely on that format.
   */

  smartlist_t *chunks = smartlist_new();
  memarea_t *area = memarea_new();
  const config_line_t *line;
  for (line = labels; line; line = line->next) {
    sized_chunk_t *sz = memarea_alloc(area, sizeof(sized_chunk_t));
    sz->len = strlen(line->key) + 1 + strlen(line->value) + 1;
    const size_t allocated = sz->len + 1;
    char *bytes = memarea_alloc(area, allocated);
    tor_snprintf(bytes, allocated, "%s %s\n", line->key, line->value);
    sz->bytes = bytes;
    smartlist_add(chunks, sz);
  }

  sized_chunk_t *nul = memarea_alloc(area, sizeof(sized_chunk_t));
  nul->len = 1;
  nul->bytes = "\0";
  smartlist_add(chunks, nul);

  sized_chunk_t *datachunk = memarea_alloc(area, sizeof(sized_chunk_t));
  datachunk->bytes = (const char *)data;
  datachunk->len = length;
  smartlist_add(chunks, datachunk);

  int r = storage_dir_save_chunks_to_file(d, chunks, 1, fname_out);
  smartlist_free(chunks);
  memarea_drop_all(area);
  return r;
}

/**
 * Map a file that was created with storage_dir_save_labeled().  On failure,
 * return NULL.  On success, write a set of newly allocated labels into to
 * *<b>labels_out</b>, a pointer to the into *<b>data_out</b>, and the data's
 * into *<b>sz_out</b>. On success, also return a tor_mmap_t object whose
 * contents should not be used -- it needs to be kept around, though, for as
 * long as <b>data_out</b> is going to be valid.
 */
tor_mmap_t *
storage_dir_map_labeled(storage_dir_t *dir,
                         const char *fname,
                         config_line_t **labels_out,
                         const uint8_t **data_out,
                         size_t *sz_out)
{
  tor_mmap_t *m = storage_dir_map(dir, fname);
  if (! m)
    goto err;
  const char *nulp = memchr(m->data, '\0', m->size);
  if (! nulp)
    goto err;
  if (labels_out && config_get_lines(m->data, labels_out, 0) < 0)
    goto err;
  size_t offset = nulp - m->data + 1;
  tor_assert(offset <= m->size);
  *data_out = (const uint8_t *)(m->data + offset);
  *sz_out = m->size - offset;

  return m;
 err:
  tor_munmap_file(m);
  return NULL;
}

/** As storage_dir_map_labeled, but return a new byte array containing the
 * data. */
uint8_t *
storage_dir_read_labeled(storage_dir_t *dir,
                          const char *fname,
                          config_line_t **labels_out,
                          size_t *sz_out)
{
  const uint8_t *data = NULL;
  tor_mmap_t *m = storage_dir_map_labeled(dir, fname, labels_out,
                                           &data, sz_out);
  if (m == NULL)
    return NULL;
  uint8_t *result = tor_memdup(data, *sz_out);
  tor_munmap_file(m);
  return result;
}

/**
 * Remove the file called <b>fname</b> from <b>d</b>.
 */
+14 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
#define TOR_STORAGEDIR_H

typedef struct storage_dir_t storage_dir_t;

struct config_line_t;
struct sandbox_cfg_elem;

storage_dir_t * storage_dir_new(const char *dirname, int n_files);
@@ -26,6 +26,19 @@ int storage_dir_save_string_to_file(storage_dir_t *d,
                                    const char *data,
                                    int binary,
                                    char **fname_out);
int storage_dir_save_labeled_to_file(storage_dir_t *d,
                                      const struct config_line_t *labels,
                                      const uint8_t *data,
                                      size_t length,
                                      char **fname_out);
tor_mmap_t *storage_dir_map_labeled(storage_dir_t *dir,
                                     const char *fname,
                                     struct config_line_t **labels_out,
                                     const uint8_t **data_out,
                                     size_t *size_out);
uint8_t *storage_dir_read_labeled(storage_dir_t *d, const char *fname,
                                   struct config_line_t **labels_out,
                                   size_t *sz_out);
void storage_dir_remove_file(storage_dir_t *d,
                             const char *fname);
int storage_dir_shrink(storage_dir_t *d,
Loading