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 src/common/include.am +2 −0 Original line number Diff line number Diff line Loading @@ -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 \ Loading Loading @@ -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 \ Loading src/common/storagedir.c +134 −12 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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); } Loading @@ -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>. Loading @@ -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>. */ Loading src/common/storagedir.h +14 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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 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
src/common/include.am +2 −0 Original line number Diff line number Diff line Loading @@ -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 \ Loading Loading @@ -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 \ Loading
src/common/storagedir.c +134 −12 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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); } Loading @@ -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>. Loading @@ -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>. */ Loading
src/common/storagedir.h +14 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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