mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Move conffile and storagedir to lib/fs
This commit is contained in:
@@ -1,164 +0,0 @@
|
||||
/* Copyright (c) 2001 Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "common/compat.h"
|
||||
#include "lib/encoding/confline.h"
|
||||
#include "common/conffile.h"
|
||||
#include "lib/log/torlog.h"
|
||||
#include "common/util.h"
|
||||
#include "lib/container/smartlist.h"
|
||||
|
||||
static smartlist_t *config_get_file_list(const char *path,
|
||||
smartlist_t *opened_files);
|
||||
static int config_get_included_config(const char *path, int recursion_level,
|
||||
int extended, config_line_t **config,
|
||||
config_line_t **config_last,
|
||||
smartlist_t *opened_lst);
|
||||
static int config_process_include(const char *path, int recursion_level,
|
||||
int extended, config_line_t **list,
|
||||
config_line_t **list_last,
|
||||
smartlist_t *opened_lst);
|
||||
|
||||
/** Helper: parse the config string and strdup into key/value
|
||||
* strings. Set *result to the list, or NULL if parsing the string
|
||||
* failed. Set *has_include to 1 if <b>result</b> has values from
|
||||
* %included files. <b>opened_lst</b> will have a list of opened files if
|
||||
* provided. 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_include(const char *string, config_line_t **result,
|
||||
int extended, int *has_include,
|
||||
smartlist_t *opened_lst)
|
||||
{
|
||||
return config_get_lines_aux(string, result, extended, 1, has_include,
|
||||
opened_lst, 1, NULL, config_process_include);
|
||||
}
|
||||
|
||||
/** Adds a list of configuration files present on <b>path</b> to
|
||||
* <b>file_list</b>. <b>path</b> can be a file or a directory. If it is a file,
|
||||
* only that file will be added to <b>file_list</b>. If it is a directory,
|
||||
* all paths for files on that directory root (no recursion) except for files
|
||||
* whose name starts with a dot will be added to <b>file_list</b>.
|
||||
* <b>opened_files</b> will have a list of files opened by this function
|
||||
* if provided. Return 0 on success, -1 on failure. Ignores empty files.
|
||||
*/
|
||||
static smartlist_t *
|
||||
config_get_file_list(const char *path, smartlist_t *opened_files)
|
||||
{
|
||||
smartlist_t *file_list = smartlist_new();
|
||||
|
||||
if (opened_files) {
|
||||
smartlist_add_strdup(opened_files, path);
|
||||
}
|
||||
|
||||
file_status_t file_type = file_status(path);
|
||||
if (file_type == FN_FILE) {
|
||||
smartlist_add_strdup(file_list, path);
|
||||
return file_list;
|
||||
} else if (file_type == FN_DIR) {
|
||||
smartlist_t *all_files = tor_listdir(path);
|
||||
if (!all_files) {
|
||||
smartlist_free(file_list);
|
||||
return NULL;
|
||||
}
|
||||
smartlist_sort_strings(all_files);
|
||||
SMARTLIST_FOREACH_BEGIN(all_files, char *, f) {
|
||||
if (f[0] == '.') {
|
||||
tor_free(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *fullname;
|
||||
tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
|
||||
tor_free(f);
|
||||
|
||||
if (opened_files) {
|
||||
smartlist_add_strdup(opened_files, fullname);
|
||||
}
|
||||
|
||||
if (file_status(fullname) != FN_FILE) {
|
||||
tor_free(fullname);
|
||||
continue;
|
||||
}
|
||||
smartlist_add(file_list, fullname);
|
||||
} SMARTLIST_FOREACH_END(f);
|
||||
smartlist_free(all_files);
|
||||
return file_list;
|
||||
} else if (file_type == FN_EMPTY) {
|
||||
return file_list;
|
||||
} else {
|
||||
smartlist_free(file_list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a list of config lines present on included <b>path</b>.
|
||||
* Set <b>config</b> to the list and <b>config_last</b> to the last element of
|
||||
* <b>config</b>. <b>opened_lst</b> will have a list of opened files if
|
||||
* provided. Return 0 on success, -1 on failure. */
|
||||
static int
|
||||
config_get_included_config(const char *path, int recursion_level, int extended,
|
||||
config_line_t **config, config_line_t **config_last,
|
||||
smartlist_t *opened_lst)
|
||||
{
|
||||
char *included_conf = read_file_to_str(path, 0, NULL);
|
||||
if (!included_conf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (config_get_lines_aux(included_conf, config, extended, 1, NULL,
|
||||
opened_lst, recursion_level+1, config_last,
|
||||
config_process_include) < 0) {
|
||||
tor_free(included_conf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tor_free(included_conf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Process an %include <b>path</b> in a config file. Set <b>list</b> to the
|
||||
* list of configuration settings obtained and <b>list_last</b> to the last
|
||||
* element of the same list. <b>opened_lst</b> will have a list of opened
|
||||
* files if provided. Return 0 on success, -1 on failure. */
|
||||
static int
|
||||
config_process_include(const char *path, int recursion_level, int extended,
|
||||
config_line_t **list, config_line_t **list_last,
|
||||
smartlist_t *opened_lst)
|
||||
{
|
||||
config_line_t *ret_list = NULL;
|
||||
config_line_t **next = &ret_list;
|
||||
|
||||
smartlist_t *config_files = config_get_file_list(path, opened_lst);
|
||||
if (!config_files) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rv = -1;
|
||||
SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
|
||||
config_line_t *included_config = NULL;
|
||||
if (config_get_included_config(config_file, recursion_level, extended,
|
||||
&included_config, list_last,
|
||||
opened_lst) < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
*next = included_config;
|
||||
if (*list_last)
|
||||
next = &(*list_last)->next;
|
||||
|
||||
} SMARTLIST_FOREACH_END(config_file);
|
||||
*list = ret_list;
|
||||
rv = 0;
|
||||
|
||||
done:
|
||||
SMARTLIST_FOREACH(config_files, char *, f, tor_free(f));
|
||||
smartlist_free(config_files);
|
||||
return rv;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/* Copyright (c) 2001 Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_CONFFILE_H
|
||||
#define TOR_CONFFILE_H
|
||||
|
||||
struct smartlist_t;
|
||||
struct config_line_t;
|
||||
|
||||
int config_get_lines_include(const char *string, struct config_line_t **result,
|
||||
int extended, int *has_include,
|
||||
struct smartlist_t *opened_lst);
|
||||
|
||||
#endif /* !defined(TOR_CONFLINE_H) */
|
||||
@@ -28,10 +28,8 @@ LIBOR_A_SRC = \
|
||||
src/common/buffers.c \
|
||||
src/common/compat.c \
|
||||
src/common/compat_time.c \
|
||||
src/common/conffile.c \
|
||||
src/common/util.c \
|
||||
src/common/util_process.c \
|
||||
src/common/storagedir.c \
|
||||
src/common/token_bucket.c \
|
||||
src/common/workqueue.c \
|
||||
$(libor_extra_source) \
|
||||
@@ -68,10 +66,8 @@ COMMONHEADERS = \
|
||||
src/common/compat.h \
|
||||
src/common/compat_libevent.h \
|
||||
src/common/compat_time.h \
|
||||
src/common/conffile.h \
|
||||
src/common/handles.h \
|
||||
src/common/procmon.h \
|
||||
src/common/storagedir.h \
|
||||
src/common/timers.h \
|
||||
src/common/token_bucket.h \
|
||||
src/common/util.h \
|
||||
|
||||
@@ -1,585 +0,0 @@
|
||||
/* Copyright (c) 2017-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "lib/container/smartlist.h"
|
||||
#include "common/compat.h"
|
||||
#include "lib/encoding/confline.h"
|
||||
#include "lib/memarea/memarea.h"
|
||||
#include "lib/sandbox/sandbox.h"
|
||||
#include "common/storagedir.h"
|
||||
#include "lib/log/torlog.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define FNAME_MIN_NUM 1000
|
||||
|
||||
/** A storage_dir_t represents a directory full of similar cached
|
||||
* files. Filenames are decimal integers. Files can be cleaned as needed
|
||||
* to limit total disk usage. */
|
||||
struct storage_dir_t {
|
||||
/** Directory holding the files for this storagedir. */
|
||||
char *directory;
|
||||
/** Either NULL, or a directory listing of the directory (as a smartlist
|
||||
* of strings */
|
||||
smartlist_t *contents;
|
||||
/** The largest number of non-temporary files we'll place in the
|
||||
* directory. */
|
||||
int max_files;
|
||||
/** If true, then 'usage' has been computed. */
|
||||
int usage_known;
|
||||
/** The total number of bytes used in this directory */
|
||||
uint64_t usage;
|
||||
};
|
||||
|
||||
/** Create or open a new storage directory at <b>dirname</b>, with
|
||||
* capacity for up to <b>max_files</b> files.
|
||||
*/
|
||||
storage_dir_t *
|
||||
storage_dir_new(const char *dirname, int max_files)
|
||||
{
|
||||
if (check_private_dir(dirname, CPD_CREATE, NULL) < 0)
|
||||
return NULL;
|
||||
|
||||
storage_dir_t *d = tor_malloc_zero(sizeof(storage_dir_t));
|
||||
d->directory = tor_strdup(dirname);
|
||||
d->max_files = max_files;
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all in-RAM storage for <b>d</b>. Does not delete any files.
|
||||
*/
|
||||
void
|
||||
storage_dir_free_(storage_dir_t *d)
|
||||
{
|
||||
if (d == NULL)
|
||||
return;
|
||||
tor_free(d->directory);
|
||||
if (d->contents) {
|
||||
SMARTLIST_FOREACH(d->contents, char *, cp, tor_free(cp));
|
||||
smartlist_free(d->contents);
|
||||
}
|
||||
tor_free(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the sandbox (if any) configured by <b>cfg</b> to allow the
|
||||
* operations that <b>d</b> will need.
|
||||
*
|
||||
* The presence of this function is why we need an upper limit on the
|
||||
* number of files in a storage_dir_t: we need to approve file operations
|
||||
* one by one.
|
||||
*/
|
||||
int
|
||||
storage_dir_register_with_sandbox(storage_dir_t *d, sandbox_cfg_t **cfg)
|
||||
{
|
||||
int problems = 0;
|
||||
int idx;
|
||||
for (idx = FNAME_MIN_NUM; idx < FNAME_MIN_NUM + d->max_files; ++idx) {
|
||||
char *path = NULL, *tmppath = NULL;
|
||||
tor_asprintf(&path, "%s/%d", d->directory, idx);
|
||||
tor_asprintf(&tmppath, "%s/%d.tmp", d->directory, idx);
|
||||
|
||||
problems += sandbox_cfg_allow_open_filename(cfg, tor_strdup(path));
|
||||
problems += sandbox_cfg_allow_open_filename(cfg, tor_strdup(tmppath));
|
||||
problems += sandbox_cfg_allow_stat_filename(cfg, tor_strdup(path));
|
||||
problems += sandbox_cfg_allow_stat_filename(cfg, tor_strdup(tmppath));
|
||||
problems += sandbox_cfg_allow_rename(cfg,
|
||||
tor_strdup(tmppath), tor_strdup(path));
|
||||
|
||||
tor_free(path);
|
||||
tor_free(tmppath);
|
||||
}
|
||||
|
||||
return problems ? -1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all files in <b>d</b> whose names end with ".tmp".
|
||||
*
|
||||
* Requires that the contents field of <b>d</b> is set.
|
||||
*/
|
||||
static void
|
||||
storage_dir_clean_tmpfiles(storage_dir_t *d)
|
||||
{
|
||||
if (!d->contents)
|
||||
return;
|
||||
SMARTLIST_FOREACH_BEGIN(d->contents, char *, fname) {
|
||||
if (strcmpend(fname, ".tmp"))
|
||||
continue;
|
||||
char *path = NULL;
|
||||
tor_asprintf(&path, "%s/%s", d->directory, fname);
|
||||
if (unlink(sandbox_intern_string(path))) {
|
||||
log_warn(LD_FS, "Unable to unlink %s while cleaning "
|
||||
"temporary files: %s", escaped(path), strerror(errno));
|
||||
tor_free(path);
|
||||
continue;
|
||||
}
|
||||
tor_free(path);
|
||||
SMARTLIST_DEL_CURRENT(d->contents, fname);
|
||||
tor_free(fname);
|
||||
} SMARTLIST_FOREACH_END(fname);
|
||||
|
||||
d->usage_known = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-scan the directory <b>d</b> to learn its contents.
|
||||
*/
|
||||
static int
|
||||
storage_dir_rescan(storage_dir_t *d)
|
||||
{
|
||||
if (d->contents) {
|
||||
SMARTLIST_FOREACH(d->contents, char *, cp, tor_free(cp));
|
||||
smartlist_free(d->contents);
|
||||
}
|
||||
d->usage = 0;
|
||||
d->usage_known = 0;
|
||||
if (NULL == (d->contents = tor_listdir(d->directory))) {
|
||||
return -1;
|
||||
}
|
||||
storage_dir_clean_tmpfiles(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a smartlist containing the filenames within <b>d</b>.
|
||||
*/
|
||||
const smartlist_t *
|
||||
storage_dir_list(storage_dir_t *d)
|
||||
{
|
||||
if (! d->contents)
|
||||
storage_dir_rescan(d);
|
||||
return d->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of bytes used for storage in <b>d</b>.
|
||||
*/
|
||||
uint64_t
|
||||
storage_dir_get_usage(storage_dir_t *d)
|
||||
{
|
||||
if (d->usage_known)
|
||||
return d->usage;
|
||||
|
||||
uint64_t total = 0;
|
||||
SMARTLIST_FOREACH_BEGIN(storage_dir_list(d), const char *, cp) {
|
||||
char *path = NULL;
|
||||
struct stat st;
|
||||
tor_asprintf(&path, "%s/%s", d->directory, cp);
|
||||
if (stat(sandbox_intern_string(path), &st) == 0) {
|
||||
total += st.st_size;
|
||||
}
|
||||
tor_free(path);
|
||||
} SMARTLIST_FOREACH_END(cp);
|
||||
|
||||
d->usage = total;
|
||||
d->usage_known = 1;
|
||||
return d->usage;
|
||||
}
|
||||
|
||||
/** Mmap a specified file within <b>d</b>.
|
||||
*
|
||||
* On failure, return NULL and set errno as for tor_mmap_file(). */
|
||||
tor_mmap_t *
|
||||
storage_dir_map(storage_dir_t *d, const char *fname)
|
||||
{
|
||||
char *path = NULL;
|
||||
tor_asprintf(&path, "%s/%s", d->directory, fname);
|
||||
tor_mmap_t *result = tor_mmap_file(path);
|
||||
int errval = errno;
|
||||
tor_free(path);
|
||||
if (result == NULL)
|
||||
errno = errval;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Read a file within <b>d</b> into a newly allocated buffer. Set
|
||||
* *<b>sz_out</b> to its size. */
|
||||
uint8_t *
|
||||
storage_dir_read(storage_dir_t *d, const char *fname, int bin, size_t *sz_out)
|
||||
{
|
||||
const int flags = bin ? RFTS_BIN : 0;
|
||||
|
||||
char *path = NULL;
|
||||
tor_asprintf(&path, "%s/%s", d->directory, fname);
|
||||
struct stat st;
|
||||
char *contents = read_file_to_str(path, flags, &st);
|
||||
if (contents && sz_out) {
|
||||
// it fits in RAM, so we know its size is less than SIZE_MAX
|
||||
#if UINT64_MAX > SIZE_MAX
|
||||
tor_assert((uint64_t)st.st_size <= SIZE_MAX);
|
||||
#endif
|
||||
*sz_out = (size_t) st.st_size;
|
||||
}
|
||||
|
||||
tor_free(path);
|
||||
return (uint8_t *) contents;
|
||||
}
|
||||
|
||||
/** Helper: Find an unused filename within the directory */
|
||||
static char *
|
||||
find_unused_fname(storage_dir_t *d)
|
||||
{
|
||||
if (!d->contents) {
|
||||
if (storage_dir_rescan(d) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char buf[16];
|
||||
int i;
|
||||
/* Yuck; this is quadratic. Fortunately, that shouldn't matter much,
|
||||
* since disk writes are more expensive by a lot. */
|
||||
for (i = FNAME_MIN_NUM; i < FNAME_MIN_NUM + d->max_files; ++i) {
|
||||
tor_snprintf(buf, sizeof(buf), "%d", i);
|
||||
if (!smartlist_contains_string(d->contents, buf)) {
|
||||
return tor_strdup(buf);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** 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_chunks_to_file(path, chunks, binary, 0);
|
||||
if (r == 0) {
|
||||
if (d->usage_known)
|
||||
d->usage += total_length;
|
||||
if (fname_out) {
|
||||
*fname_out = tor_strdup(fname);
|
||||
}
|
||||
if (d->contents)
|
||||
smartlist_add(d->contents, tor_strdup(fname));
|
||||
}
|
||||
tor_free(fname);
|
||||
tor_free(path);
|
||||
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>.
|
||||
*/
|
||||
int
|
||||
storage_dir_save_string_to_file(storage_dir_t *d,
|
||||
const char *str,
|
||||
int binary,
|
||||
char **fname_out)
|
||||
{
|
||||
return storage_dir_save_bytes_to_file(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_to_file(). On
|
||||
* failure, return NULL. On success, write a set of newly allocated labels
|
||||
* into *<b>labels_out</b>, a pointer to the data into *<b>data_out</b>, and
|
||||
* the data's size 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.
|
||||
*
|
||||
* On failure, set errno as for tor_mmap_file() if the file was missing or
|
||||
* empty, and set errno to EINVAL if the file was not in the labeled
|
||||
* format expected.
|
||||
*/
|
||||
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);
|
||||
int errval;
|
||||
if (! m) {
|
||||
errval = errno;
|
||||
goto err;
|
||||
}
|
||||
const char *nulp = memchr(m->data, '\0', m->size);
|
||||
if (! nulp) {
|
||||
errval = EINVAL;
|
||||
goto err;
|
||||
}
|
||||
if (labels_out && config_get_lines(m->data, labels_out, 0) < 0) {
|
||||
errval = EINVAL;
|
||||
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);
|
||||
errno = errval;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Reduce the cached usage amount in <b>d</b> by <b>removed_file_size</b>.
|
||||
* This function is a no-op if <b>d->usage_known</b> is 0. */
|
||||
static void
|
||||
storage_dir_reduce_usage(storage_dir_t *d, uint64_t removed_file_size)
|
||||
{
|
||||
if (d->usage_known) {
|
||||
if (! BUG(d->usage < removed_file_size)) {
|
||||
/* This bug can also be triggered if an external process resized a file
|
||||
* between the call to storage_dir_get_usage() that last checked
|
||||
* actual usage (rather than relaying on cached usage), and the call to
|
||||
* this function. */
|
||||
d->usage -= removed_file_size;
|
||||
} else {
|
||||
/* If we underflowed the cached directory size, re-check the sizes of all
|
||||
* the files in the directory. This makes storage_dir_shrink() quadratic,
|
||||
* but only if a process is continually changing file sizes in the
|
||||
* storage directory (in which case, we have bigger issues).
|
||||
*
|
||||
* We can't just reset usage_known, because storage_dir_shrink() relies
|
||||
* on knowing the usage. */
|
||||
storage_dir_rescan(d);
|
||||
(void)storage_dir_get_usage(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the file called <b>fname</b> from <b>d</b>.
|
||||
*/
|
||||
void
|
||||
storage_dir_remove_file(storage_dir_t *d,
|
||||
const char *fname)
|
||||
{
|
||||
char *path = NULL;
|
||||
tor_asprintf(&path, "%s/%s", d->directory, fname);
|
||||
const char *ipath = sandbox_intern_string(path);
|
||||
|
||||
uint64_t size = 0;
|
||||
if (d->usage_known) {
|
||||
struct stat st;
|
||||
if (stat(ipath, &st) == 0) {
|
||||
size = st.st_size;
|
||||
}
|
||||
}
|
||||
if (unlink(ipath) == 0) {
|
||||
storage_dir_reduce_usage(d, size);
|
||||
} else {
|
||||
log_warn(LD_FS, "Unable to unlink %s while removing file: %s",
|
||||
escaped(path), strerror(errno));
|
||||
tor_free(path);
|
||||
return;
|
||||
}
|
||||
if (d->contents) {
|
||||
smartlist_string_remove(d->contents, fname);
|
||||
}
|
||||
|
||||
tor_free(path);
|
||||
}
|
||||
|
||||
/** Helper type: used to sort the members of storage directory by mtime. */
|
||||
typedef struct shrinking_dir_entry_t {
|
||||
time_t mtime;
|
||||
uint64_t size;
|
||||
char *path;
|
||||
} shrinking_dir_entry_t;
|
||||
|
||||
/** Helper: use with qsort to sort shrinking_dir_entry_t structs. */
|
||||
static int
|
||||
shrinking_dir_entry_compare(const void *a_, const void *b_)
|
||||
{
|
||||
const shrinking_dir_entry_t *a = a_;
|
||||
const shrinking_dir_entry_t *b = b_;
|
||||
|
||||
if (a->mtime < b->mtime)
|
||||
return -1;
|
||||
else if (a->mtime > b->mtime)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to free space by removing the oldest files in <b>d</b>. Delete
|
||||
* until no more than <b>target_size</b> bytes are left, and at least
|
||||
* <b>min_to_remove</b> files have been removed... or until there is
|
||||
* nothing left to remove.
|
||||
*
|
||||
* Return 0 on success; -1 on failure.
|
||||
*/
|
||||
int
|
||||
storage_dir_shrink(storage_dir_t *d,
|
||||
uint64_t target_size,
|
||||
int min_to_remove)
|
||||
{
|
||||
if (d->usage_known && d->usage <= target_size && !min_to_remove) {
|
||||
/* Already small enough. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (storage_dir_rescan(d) < 0)
|
||||
return -1;
|
||||
|
||||
const uint64_t orig_usage = storage_dir_get_usage(d);
|
||||
if (orig_usage <= target_size && !min_to_remove) {
|
||||
/* Okay, small enough after rescan! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int n = smartlist_len(d->contents);
|
||||
shrinking_dir_entry_t *ents = tor_calloc(n, sizeof(shrinking_dir_entry_t));
|
||||
SMARTLIST_FOREACH_BEGIN(d->contents, const char *, fname) {
|
||||
shrinking_dir_entry_t *ent = &ents[fname_sl_idx];
|
||||
struct stat st;
|
||||
tor_asprintf(&ent->path, "%s/%s", d->directory, fname);
|
||||
if (stat(sandbox_intern_string(ent->path), &st) == 0) {
|
||||
ent->mtime = st.st_mtime;
|
||||
ent->size = st.st_size;
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(fname);
|
||||
|
||||
qsort(ents, n, sizeof(shrinking_dir_entry_t), shrinking_dir_entry_compare);
|
||||
|
||||
int idx = 0;
|
||||
while ((d->usage > target_size || min_to_remove > 0) && idx < n) {
|
||||
if (unlink(sandbox_intern_string(ents[idx].path)) == 0) {
|
||||
storage_dir_reduce_usage(d, ents[idx].size);
|
||||
--min_to_remove;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < n; ++idx) {
|
||||
tor_free(ents[idx].path);
|
||||
}
|
||||
tor_free(ents);
|
||||
|
||||
storage_dir_rescan(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Remove all files in <b>d</b>. */
|
||||
int
|
||||
storage_dir_remove_all(storage_dir_t *d)
|
||||
{
|
||||
return storage_dir_shrink(d, 0, d->max_files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the largest number of non-temporary files we're willing to
|
||||
* store in <b>d</b>.
|
||||
*/
|
||||
int
|
||||
storage_dir_get_max_files(storage_dir_t *d)
|
||||
{
|
||||
return d->max_files;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/* Copyright (c) 2017-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_STORAGEDIR_H
|
||||
#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);
|
||||
void storage_dir_free_(storage_dir_t *d);
|
||||
#define storage_dir_free(d) \
|
||||
FREE_AND_NULL(storage_dir_t, storage_dir_free_, (d))
|
||||
|
||||
int storage_dir_register_with_sandbox(storage_dir_t *d,
|
||||
struct sandbox_cfg_elem **cfg);
|
||||
const smartlist_t *storage_dir_list(storage_dir_t *d);
|
||||
uint64_t storage_dir_get_usage(storage_dir_t *d);
|
||||
tor_mmap_t *storage_dir_map(storage_dir_t *d, const char *fname);
|
||||
uint8_t *storage_dir_read(storage_dir_t *d, const char *fname, int bin,
|
||||
size_t *sz_out);
|
||||
int storage_dir_save_bytes_to_file(storage_dir_t *d,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
int binary,
|
||||
char **fname_out);
|
||||
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,
|
||||
uint64_t target_size,
|
||||
int min_to_remove);
|
||||
int storage_dir_remove_all(storage_dir_t *d);
|
||||
int storage_dir_get_max_files(storage_dir_t *d);
|
||||
|
||||
#endif /* !defined(TOR_STORAGEDIR_H) */
|
||||
|
||||
Reference in New Issue
Block a user