diff --git a/src/or/config.c b/src/or/config.c index 681955e7eb..cb27dac3c0 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -510,12 +510,6 @@ static int options_transition_affects_workers( static int options_transition_affects_descriptor( const or_options_t *old_options, const or_options_t *new_options); static int check_nickname_list(char **lst, const char *name, char **msg); - -static int parse_client_transport_line(const or_options_t *options, - const char *line, int validate_only); - -static int parse_server_transport_line(const or_options_t *options, - const char *line, int validate_only); static char *get_bindaddr_from_transport_listen_line(const char *line, const char *transport); static int parse_dir_authority_line(const char *line, @@ -1413,7 +1407,7 @@ options_act(const or_options_t *old_options) if (!options->DisableNetwork) { if (options->ClientTransportPlugin) { for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { - if (parse_client_transport_line(options, cl->value, 0)<0) { + if (parse_transport_line(options, cl->value, 0, 0) < 0) { log_warn(LD_BUG, "Previously validated ClientTransportPlugin line " "could not be added!"); @@ -1424,7 +1418,7 @@ options_act(const or_options_t *old_options) if (options->ServerTransportPlugin && server_mode(options)) { for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { - if (parse_server_transport_line(options, cl->value, 0)<0) { + if (parse_transport_line(options, cl->value, 0, 1) < 0) { log_warn(LD_BUG, "Previously validated ServerTransportPlugin line " "could not be added!"); @@ -3299,12 +3293,12 @@ options_validate(or_options_t *old_options, or_options_t *options, } for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { - if (parse_client_transport_line(options, cl->value, 1)<0) + if (parse_transport_line(options, cl->value, 1, 0) < 0) REJECT("Invalid client transport line. See logs for details."); } for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { - if (parse_server_transport_line(options, cl->value, 1)<0) + if (parse_transport_line(options, cl->value, 1, 1) < 0) REJECT("Invalid server transport line. See logs for details."); } @@ -4760,46 +4754,52 @@ parse_bridge_line(const char *line) return bridge_line; } -/** Read the contents of a ClientTransportPlugin line from - * line. Return 0 if the line is well-formed, and -1 if it - * isn't. +/** Read the contents of a ClientTransportPlugin or ServerTransportPlugin + * line from line, depending on the value of server. Return 0 + * if the line is well-formed, and -1 if it isn't. * - * If validate_only is 0, the line is well-formed, and the - * transport is needed by some bridge: + * If validate_only is 0, the line is well-formed, and the transport is + * needed by some bridge: * - If it's an external proxy line, add the transport described in the line to * our internal transport list. - * - If it's a managed proxy line, launch the managed proxy. */ -static int -parse_client_transport_line(const or_options_t *options, - const char *line, int validate_only) + * - If it's a managed proxy line, launch the managed proxy. + */ + +STATIC int +parse_transport_line(const or_options_t *options, + const char *line, int validate_only, + int server) { + smartlist_t *items = NULL; int r; - char *field2=NULL; - - const char *transports=NULL; - smartlist_t *transport_list=NULL; - char *addrport=NULL; + const char *transports = NULL; + smartlist_t *transport_list = NULL; + char *type = NULL; + char *addrport = NULL; tor_addr_t addr; uint16_t port = 0; - int socks_ver=PROXY_NONE; + int socks_ver = PROXY_NONE; /* managed proxy options */ - int is_managed=0; - char **proxy_argv=NULL; - char **tmp=NULL; + int is_managed = 0; + char **proxy_argv = NULL; + char **tmp = NULL; int proxy_argc, i; - int is_useless_proxy=1; + int is_useless_proxy = 1; int line_length; + /* Split the line into space-separated tokens */ items = smartlist_new(); smartlist_split_string(items, line, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + line_length = smartlist_len(items); - line_length = smartlist_len(items); if (line_length < 3) { - log_warn(LD_CONFIG, "Too few arguments on ClientTransportPlugin line."); + log_warn(LD_CONFIG, + "Too few arguments on %sTransportPlugin line.", + server ? "Server" : "Client"); goto err; } @@ -4823,71 +4823,97 @@ parse_client_transport_line(const or_options_t *options, is_useless_proxy = 0; } SMARTLIST_FOREACH_END(transport_name); - /* field2 is either a SOCKS version or "exec" */ - field2 = smartlist_get(items, 1); - - if (!strcmp(field2,"socks4")) { + type = smartlist_get(items, 1); + if (!strcmp(type, "exec")) { + is_managed = 1; + } else if (server && !strcmp(type, "proxy")) { + /* 'proxy' syntax only with ServerTransportPlugin */ + is_managed = 0; + } else if (!server && !strcmp(type, "socks4")) { + /* 'socks4' syntax only with ClientTransportPlugin */ + is_managed = 0; socks_ver = PROXY_SOCKS4; - } else if (!strcmp(field2,"socks5")) { + } else if (!server && !strcmp(type, "socks5")) { + /* 'socks5' syntax only with ClientTransportPlugin */ + is_managed = 0; socks_ver = PROXY_SOCKS5; - } else if (!strcmp(field2,"exec")) { - is_managed=1; } else { - log_warn(LD_CONFIG, "Strange ClientTransportPlugin field '%s'.", - field2); + log_warn(LD_CONFIG, + "Strange %sTransportPlugin type '%s'", + server ? "Server" : "Client", type); goto err; } if (is_managed && options->Sandbox) { - log_warn(LD_CONFIG, "Managed proxies are not compatible with Sandbox mode." - "(ClientTransportPlugin line was %s)", escaped(line)); + log_warn(LD_CONFIG, + "Managed proxies are not compatible with Sandbox mode." + "(%sTransportPlugin line was %s)", + server ? "Server" : "Client", escaped(line)); goto err; } - if (is_managed) { /* managed */ - if (!validate_only && is_useless_proxy) { - log_info(LD_GENERAL, "Pluggable transport proxy (%s) does not provide " - "any needed transports and will not be launched.", line); + if (is_managed) { + /* managed */ + + if (!server && !validate_only && is_useless_proxy) { + log_info(LD_GENERAL, + "Pluggable transport proxy (%s) does not provide " + "any needed transports and will not be launched.", + line); } - /* If we are not just validating, use the rest of the line as the - argv of the proxy to be launched. Also, make sure that we are - only launching proxies that contribute useful transports. */ - if (!validate_only && !is_useless_proxy) { - proxy_argc = line_length-2; + /* + * If we are not just validating, use the rest of the line as the + * argv of the proxy to be launched. Also, make sure that we are + * only launching proxies that contribute useful transports. + */ + + if (!validate_only && (server || !is_useless_proxy)) { + proxy_argc = line_length - 2; tor_assert(proxy_argc > 0); proxy_argv = tor_calloc((proxy_argc + 1), sizeof(char *)); tmp = proxy_argv; - for (i=0;iSocks4Proxy || options->Socks5Proxy || options->HTTPSProxy) { + if (!server && + (options->Socks4Proxy || options->Socks5Proxy || options->HTTPSProxy)) { log_warn(LD_CONFIG, "You have configured an external proxy with another " "proxy type. (Socks4Proxy|Socks5Proxy|HTTPSProxy)"); goto err; } if (smartlist_len(transport_list) != 1) { - log_warn(LD_CONFIG, "You can't have an external proxy with " - "more than one transports."); + log_warn(LD_CONFIG, + "You can't have an external proxy with more than " + "one transport."); goto err; } addrport = smartlist_get(items, 2); - if (tor_addr_port_lookup(addrport, &addr, &port)<0) { - log_warn(LD_CONFIG, "Error parsing transport " - "address '%s'", addrport); + if (tor_addr_port_lookup(addrport, &addr, &port) < 0) { + log_warn(LD_CONFIG, + "Error parsing transport address '%s'", addrport); goto err; } + if (!port) { log_warn(LD_CONFIG, "Transport address '%s' has no port.", addrport); @@ -4895,11 +4921,15 @@ parse_client_transport_line(const or_options_t *options, } if (!validate_only) { - transport_add_from_config(&addr, port, smartlist_get(transport_list, 0), - socks_ver); - - log_info(LD_DIR, "Transport '%s' found at %s", + log_info(LD_DIR, "%s '%s' at %s.", + server ? "Server transport" : "Transport", transports, fmt_addrport(&addr, port)); + + if (!server) { + transport_add_from_config(&addr, port, + smartlist_get(transport_list, 0), + socks_ver); + } } } @@ -5071,133 +5101,6 @@ get_options_for_server_transport(const char *transport) return NULL; } -/** Read the contents of a ServerTransportPlugin line from - * line. Return 0 if the line is well-formed, and -1 if it - * isn't. - * If validate_only is 0, the line is well-formed, and it's a - * managed proxy line, launch the managed proxy. */ -static int -parse_server_transport_line(const or_options_t *options, - const char *line, int validate_only) -{ - smartlist_t *items = NULL; - int r; - const char *transports=NULL; - smartlist_t *transport_list=NULL; - char *type=NULL; - char *addrport=NULL; - tor_addr_t addr; - uint16_t port = 0; - - /* managed proxy options */ - int is_managed=0; - char **proxy_argv=NULL; - char **tmp=NULL; - int proxy_argc,i; - - int line_length; - - items = smartlist_new(); - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - - line_length = smartlist_len(items); - if (line_length < 3) { - log_warn(LD_CONFIG, "Too few arguments on ServerTransportPlugin line."); - goto err; - } - - /* Get the first line element, split it to commas into - transport_list (in case it's multiple transports) and validate - the transport names. */ - transports = smartlist_get(items, 0); - transport_list = smartlist_new(); - smartlist_split_string(transport_list, transports, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) { - if (!string_is_C_identifier(transport_name)) { - log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).", - transport_name); - goto err; - } - } SMARTLIST_FOREACH_END(transport_name); - - type = smartlist_get(items, 1); - - if (!strcmp(type, "exec")) { - is_managed=1; - } else if (!strcmp(type, "proxy")) { - is_managed=0; - } else { - log_warn(LD_CONFIG, "Strange ServerTransportPlugin type '%s'", type); - goto err; - } - - if (is_managed && options->Sandbox) { - log_warn(LD_CONFIG, "Managed proxies are not compatible with Sandbox mode." - "(ServerTransportPlugin line was %s)", escaped(line)); - goto err; - } - - if (is_managed) { /* managed */ - if (!validate_only) { - proxy_argc = line_length-2; - tor_assert(proxy_argc > 0); - proxy_argv = tor_calloc((proxy_argc + 1), sizeof(char *)); - tmp = proxy_argv; - - for (i=0;iline. If * validate_only is 0, and the line is well-formed, and it * shares any bits with required_type or required_type diff --git a/src/or/config.h b/src/or/config.h index bd26123d62..6cc81ab948 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -141,6 +141,9 @@ STATIC int options_validate(or_options_t *old_options, or_options_t *options, or_options_t *default_options, int from_setconf, char **msg); +STATIC int parse_transport_line(const or_options_t *options, + const char *line, int validate_only, + int server); #endif #endif diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 5bd0268dfd..b18aabe1f4 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -1919,8 +1919,8 @@ bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port, /** Return True if we have a bridge that uses a transport with name * transport_name. */ -int -transport_is_needed(const char *transport_name) +MOCK_IMPL(int, +transport_is_needed, (const char *transport_name)) { if (!bridge_list) return 0; diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index 80ac5e4a1c..5416398430 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -154,7 +154,7 @@ struct transport_t; int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, const struct transport_t **transport); -int transport_is_needed(const char *transport_name); +MOCK_DECL(int, transport_is_needed, (const char *transport_name)); int validate_pluggable_transports_config(void); double pathbias_get_close_success_count(entry_guard_t *guard); diff --git a/src/or/transports.c b/src/or/transports.c index 584454feb1..2623f807d0 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -326,9 +326,9 @@ transport_add(transport_t *t) /** Remember a new pluggable transport proxy at addr:port. * name is set to the name of the protocol this proxy uses. * socks_ver is set to the SOCKS version of the proxy. */ -int -transport_add_from_config(const tor_addr_t *addr, uint16_t port, - const char *name, int socks_ver) +MOCK_IMPL(int, +transport_add_from_config, (const tor_addr_t *addr, uint16_t port, + const char *name, int socks_ver)) { transport_t *t = transport_new(addr, port, name, socks_ver, NULL); @@ -1456,9 +1456,9 @@ managed_proxy_create(const smartlist_t *transport_list, * Requires that proxy_argv be a NULL-terminated array of command-line * elements, containing at least one element. **/ -void -pt_kickstart_proxy(const smartlist_t *transport_list, - char **proxy_argv, int is_server) +MOCK_IMPL(void, +pt_kickstart_proxy, (const smartlist_t *transport_list, + char **proxy_argv, int is_server)) { managed_proxy_t *mp=NULL; transport_t *old_transport = NULL; diff --git a/src/or/transports.h b/src/or/transports.h index 6393a4e172..2958d5e187 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -32,14 +32,16 @@ typedef struct transport_t { void mark_transport_list(void); void sweep_transport_list(void); -int transport_add_from_config(const tor_addr_t *addr, uint16_t port, - const char *name, int socks_ver); +MOCK_DECL(int, transport_add_from_config, + (const tor_addr_t *addr, uint16_t port, + const char *name, int socks_ver)); void transport_free(transport_t *transport); transport_t *transport_get_by_name(const char *name); -void pt_kickstart_proxy(const smartlist_t *transport_list, char **proxy_argv, - int is_server); +MOCK_DECL(void, pt_kickstart_proxy, + (const smartlist_t *transport_list, char **proxy_argv, + int is_server)); #define pt_kickstart_client_proxy(tl, pa) \ pt_kickstart_proxy(tl, pa, 0) diff --git a/src/test/test_config.c b/src/test/test_config.c index 823d067abf..1ecb816352 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -14,6 +14,8 @@ #include "test.h" #include "util.h" #include "address.h" +#include "entrynodes.h" +#include "transports.h" static void test_config_addressmap(void *arg) @@ -552,6 +554,251 @@ test_config_parse_transport_options_line(void *arg) } } +/* Mocks needed for the transport plugin line test */ + +static void pt_kickstart_proxy_mock(const smartlist_t *transport_list, + char **proxy_argv, int is_server); +static int transport_add_from_config_mock(const tor_addr_t *addr, + uint16_t port, const char *name, + int socks_ver); +static int transport_is_needed_mock(const char *transport_name); + +static int pt_kickstart_proxy_mock_call_count = 0; +static int transport_add_from_config_mock_call_count = 0; +static int transport_is_needed_mock_call_count = 0; +static int transport_is_needed_mock_return = 0; + +static void +pt_kickstart_proxy_mock(const smartlist_t *transport_list, + char **proxy_argv, int is_server) +{ + ++pt_kickstart_proxy_mock_call_count; +} + +static int +transport_add_from_config_mock(const tor_addr_t *addr, + uint16_t port, const char *name, + int socks_ver) +{ + ++transport_add_from_config_mock_call_count; + + return 0; +} + +static int +transport_is_needed_mock(const char *transport_name) +{ + ++transport_is_needed_mock_call_count; + + return transport_is_needed_mock_return; +} + +/** + * Test parsing for the ClientTransportPlugin and ServerTransportPlugin config + * options. + */ + +static void +test_config_parse_transport_plugin_line(void *arg) +{ + or_options_t *options = get_options_mutable(); + int r, tmp; + int old_pt_kickstart_proxy_mock_call_count; + int old_transport_add_from_config_mock_call_count; + int old_transport_is_needed_mock_call_count; + + /* Bad transport lines - too short */ + r = parse_transport_line(options, "bad", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, "bad", 1, 1); + tt_assert(r < 0); + r = parse_transport_line(options, "bad bad", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, "bad bad", 1, 1); + tt_assert(r < 0); + + /* Test transport list parsing */ + r = parse_transport_line(options, + "transport_1 exec /usr/bin/fake-transport", 1, 0); + tt_assert(r == 0); + r = parse_transport_line(options, + "transport_1 exec /usr/bin/fake-transport", 1, 1); + tt_assert(r == 0); + r = parse_transport_line(options, + "transport_1,transport_2 exec /usr/bin/fake-transport", 1, 0); + tt_assert(r == 0); + r = parse_transport_line(options, + "transport_1,transport_2 exec /usr/bin/fake-transport", 1, 1); + tt_assert(r == 0); + /* Bad transport identifiers */ + r = parse_transport_line(options, + "transport_* exec /usr/bin/fake-transport", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, + "transport_* exec /usr/bin/fake-transport", 1, 1); + tt_assert(r < 0); + + /* Check SOCKS cases for client transport */ + r = parse_transport_line(options, + "transport_1 socks4 1.2.3.4:567", 1, 0); + tt_assert(r == 0); + r = parse_transport_line(options, + "transport_1 socks5 1.2.3.4:567", 1, 0); + tt_assert(r == 0); + /* Proxy case for server transport */ + r = parse_transport_line(options, + "transport_1 proxy 1.2.3.4:567", 1, 1); + tt_assert(r == 0); + /* Multiple-transport error exit */ + r = parse_transport_line(options, + "transport_1,transport_2 socks5 1.2.3.4:567", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, + "transport_1,transport_2 proxy 1.2.3.4:567", 1, 1); + /* No port error exit */ + r = parse_transport_line(options, + "transport_1 socks5 1.2.3.4", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, + "transport_1 proxy 1.2.3.4", 1, 1); + tt_assert(r < 0); + /* Unparsable address error exit */ + r = parse_transport_line(options, + "transport_1 socks5 1.2.3:6x7", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, + "transport_1 proxy 1.2.3:6x7", 1, 1); + tt_assert(r < 0); + + /* "Strange {Client|Server}TransportPlugin field" error exit */ + r = parse_transport_line(options, + "transport_1 foo bar", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, + "transport_1 foo bar", 1, 1); + tt_assert(r < 0); + + /* No sandbox mode error exit */ + tmp = options->Sandbox; + options->Sandbox = 1; + r = parse_transport_line(options, + "transport_1 exec /usr/bin/fake-transport", 1, 0); + tt_assert(r < 0); + r = parse_transport_line(options, + "transport_1 exec /usr/bin/fake-transport", 1, 1); + tt_assert(r < 0); + options->Sandbox = tmp; + + /* + * These final test cases cover code paths that only activate without + * validate_only, so they need mocks in place. + */ + MOCK(pt_kickstart_proxy, pt_kickstart_proxy_mock); + old_pt_kickstart_proxy_mock_call_count = + pt_kickstart_proxy_mock_call_count; + r = parse_transport_line(options, + "transport_1 exec /usr/bin/fake-transport", 0, 1); + tt_assert(r == 0); + tt_assert(pt_kickstart_proxy_mock_call_count == + old_pt_kickstart_proxy_mock_call_count + 1); + UNMOCK(pt_kickstart_proxy); + + /* This one hits a log line in the !validate_only case only */ + r = parse_transport_line(options, + "transport_1 proxy 1.2.3.4:567", 0, 1); + tt_assert(r == 0); + + /* Check mocked client transport cases */ + MOCK(pt_kickstart_proxy, pt_kickstart_proxy_mock); + MOCK(transport_add_from_config, transport_add_from_config_mock); + MOCK(transport_is_needed, transport_is_needed_mock); + + /* Unnecessary transport case */ + transport_is_needed_mock_return = 0; + old_pt_kickstart_proxy_mock_call_count = + pt_kickstart_proxy_mock_call_count; + old_transport_add_from_config_mock_call_count = + transport_add_from_config_mock_call_count; + old_transport_is_needed_mock_call_count = + transport_is_needed_mock_call_count; + r = parse_transport_line(options, + "transport_1 exec /usr/bin/fake-transport", 0, 0); + /* Should have succeeded */ + tt_assert(r == 0); + /* transport_is_needed() should have been called */ + tt_assert(transport_is_needed_mock_call_count == + old_transport_is_needed_mock_call_count + 1); + /* + * pt_kickstart_proxy() and transport_add_from_config() should + * not have been called. + */ + tt_assert(pt_kickstart_proxy_mock_call_count == + old_pt_kickstart_proxy_mock_call_count); + tt_assert(transport_add_from_config_mock_call_count == + old_transport_add_from_config_mock_call_count); + + /* Necessary transport case */ + transport_is_needed_mock_return = 1; + old_pt_kickstart_proxy_mock_call_count = + pt_kickstart_proxy_mock_call_count; + old_transport_add_from_config_mock_call_count = + transport_add_from_config_mock_call_count; + old_transport_is_needed_mock_call_count = + transport_is_needed_mock_call_count; + r = parse_transport_line(options, + "transport_1 exec /usr/bin/fake-transport", 0, 0); + /* Should have succeeded */ + tt_assert(r == 0); + /* + * transport_is_needed() and pt_kickstart_proxy() should have been + * called. + */ + tt_assert(pt_kickstart_proxy_mock_call_count == + old_pt_kickstart_proxy_mock_call_count + 1); + tt_assert(transport_is_needed_mock_call_count == + old_transport_is_needed_mock_call_count + 1); + /* transport_add_from_config() should not have been called. */ + tt_assert(transport_add_from_config_mock_call_count == + old_transport_add_from_config_mock_call_count); + + /* proxy case */ + transport_is_needed_mock_return = 1; + old_pt_kickstart_proxy_mock_call_count = + pt_kickstart_proxy_mock_call_count; + old_transport_add_from_config_mock_call_count = + transport_add_from_config_mock_call_count; + old_transport_is_needed_mock_call_count = + transport_is_needed_mock_call_count; + r = parse_transport_line(options, + "transport_1 socks5 1.2.3.4:567", 0, 0); + /* Should have succeeded */ + tt_assert(r == 0); + /* + * transport_is_needed() and transport_add_from_config() should have + * been called. + */ + tt_assert(transport_add_from_config_mock_call_count == + old_transport_add_from_config_mock_call_count + 1); + tt_assert(transport_is_needed_mock_call_count == + old_transport_is_needed_mock_call_count + 1); + /* pt_kickstart_proxy() should not have been called. */ + tt_assert(pt_kickstart_proxy_mock_call_count == + old_pt_kickstart_proxy_mock_call_count); + + /* Done with mocked client transport cases */ + UNMOCK(transport_is_needed); + UNMOCK(transport_add_from_config); + UNMOCK(pt_kickstart_proxy); + + done: + /* Make sure we undo all mocks */ + UNMOCK(pt_kickstart_proxy); + UNMOCK(transport_add_from_config); + UNMOCK(transport_is_needed); + + return; +} + // Tests if an options with MyFamily fingerprints missing '$' normalises // them correctly and also ensure it also works with multiple fingerprints static void @@ -596,6 +843,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), + CONFIG_TEST(parse_transport_plugin_line, TT_FORK), CONFIG_TEST(check_or_create_data_subdir, TT_FORK), CONFIG_TEST(write_to_data_subdir, TT_FORK), CONFIG_TEST(fix_my_family, 0),