/** Return a pointer to the microdescriptor cache, creating (but not loading) * it if necessary. */ static microdesc_cache_t * get_microdesc_cache_noload(void) { if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) { microdesc_cache_t *cache = tor_malloc_zero(sizeof(*cache)); HT_INIT(microdesc_map, &cache->map); cache->cache_fname = get_datadir_fname("cached-microdescs"); cache->journal_fname = get_datadir_fname("cached-microdescs.new"); the_microdesc_cache = cache; } return the_microdesc_cache; }
/** Write the persistent state to disk. Return 0 for success, <0 on failure. */ int or_state_save(time_t now) { char *state, *contents; char tbuf[ISO_TIME_LEN+1]; char *fname; tor_assert(global_state); if (global_state->next_write > now) return 0; /* Call everything else that might dirty the state even more, in order * to avoid redundant writes. */ entry_guards_update_state(global_state); rep_hist_update_state(global_state); circuit_build_times_update_state(&circ_times, global_state); if (accounting_is_enabled(get_options())) accounting_run_housekeeping(now); global_state->LastWritten = now; tor_free(global_state->TorVersion); tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); state = config_dump(&state_format, NULL, global_state, 1, 0); format_local_iso_time(tbuf, now); tor_asprintf(&contents, "# Tor state file last generated on %s local time\n" "# Other times below are in GMT\n" "# You *do not* need to edit this file.\n\n%s", tbuf, state); tor_free(state); fname = get_datadir_fname("state"); if (write_str_to_file(fname, contents, 0)<0) { log_warn(LD_FS, "Unable to write state to file \"%s\"; " "will try again later", fname); last_state_file_write_failed = 1; tor_free(fname); tor_free(contents); /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state * changes sooner). */ global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL; return -1; } last_state_file_write_failed = 0; log_info(LD_GENERAL, "Saved state to \"%s\"", fname); tor_free(fname); tor_free(contents); if (server_mode(get_options())) global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL; else global_state->next_write = TIME_MAX; return 0; }
/** Read stored accounting information from disk. Return 0 on success; * return -1 and change nothing on failure. */ static int read_bandwidth_usage(void) { or_state_t *state = get_or_state(); { char *fname = get_datadir_fname("bw_accounting"); unlink(fname); tor_free(fname); } if (!state) return -1; log_info(LD_ACCT, "Reading bandwidth accounting data from state file"); n_bytes_read_in_interval = state->AccountingBytesReadInInterval; n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval; n_seconds_active_in_interval = state->AccountingSecondsActive; interval_start_time = state->AccountingIntervalStart; expected_bandwidth_usage = state->AccountingExpectedUsage; /* Older versions of Tor (before 0.2.2.17-alpha or so) didn't generate these * fields. If you switch back and forth, you might get an * AccountingSoftLimitHitAt value from long before the most recent * interval_start_time. If that's so, then ignore the softlimit-related * values. */ if (state->AccountingSoftLimitHitAt > interval_start_time) { soft_limit_hit_at = state->AccountingSoftLimitHitAt; n_bytes_at_soft_limit = state->AccountingBytesAtSoftLimit; n_seconds_to_hit_soft_limit = state->AccountingSecondsToReachSoftLimit; } else { soft_limit_hit_at = 0; n_bytes_at_soft_limit = 0; n_seconds_to_hit_soft_limit = 0; } { char tbuf1[ISO_TIME_LEN+1]; char tbuf2[ISO_TIME_LEN+1]; format_iso_time(tbuf1, state->LastWritten); format_iso_time(tbuf2, state->AccountingIntervalStart); log_info(LD_ACCT, "Successfully read bandwidth accounting info from state written at %s " "for interval starting at %s. We have been active for %lu seconds in " "this interval. At the start of the interval, we expected to use " "about %lu KB per second. ("U64_FORMAT" bytes read so far, " U64_FORMAT" bytes written so far)", tbuf1, tbuf2, (unsigned long)n_seconds_active_in_interval, (unsigned long)(expected_bandwidth_usage*1024/60), U64_PRINTF_ARG(n_bytes_read_in_interval), U64_PRINTF_ARG(n_bytes_written_in_interval)); } return 0; }
/** Helper: Return a newly allocated string containing a path to the * file where we store our authentication cookie. */ char * get_controller_cookie_file_name(void) { const or_options_t *options = get_options(); if (options->CookieAuthFile && strlen(options->CookieAuthFile)) { return tor_strdup(options->CookieAuthFile); } else { return get_datadir_fname("control_auth_cookie"); } }
/* Load state from disk and put it into our disk state. If the state passes * validation, our global state will be updated with it. Return 0 on * success. On error, -EINVAL is returned if the state on disk did contained * something malformed or is unreadable. -ENOENT is returned indicating that * the state file is either empty of non existing. */ static int disk_state_load_from_disk(void) { int ret; char *fname; fname = get_datadir_fname(default_fname); ret = disk_state_load_from_disk_impl(fname); tor_free(fname); return ret; }
static void test_config_check_or_create_data_subdir(void *arg) { or_options_t *options = get_options_mutable(); char *datadir = options->DataDirectory = tor_strdup(get_fname("datadir-0")); const char *subdir = "test_stats"; char *subpath = get_datadir_fname(subdir); struct stat st; int r; #if !defined (_WIN32) || defined (WINCE) unsigned group_permission; #endif (void)arg; #if defined (_WIN32) && !defined (WINCE) tt_int_op(mkdir(options->DataDirectory), ==, 0); #else tt_int_op(mkdir(options->DataDirectory, 0700), ==, 0); #endif r = stat(subpath, &st); // The subdirectory shouldn't exist yet, // but should be created by the call to check_or_create_data_subdir. test_assert(r && (errno == ENOENT)); test_assert(!check_or_create_data_subdir(subdir)); test_assert(is_private_dir(subpath)); // The check should return 0, if the directory already exists // and is private to the user. test_assert(!check_or_create_data_subdir(subdir)); #if !defined (_WIN32) || defined (WINCE) group_permission = st.st_mode | 0070; r = chmod(subpath, group_permission); if (r) { test_fail_msg("Changing permissions for the subdirectory failed."); } // If the directory exists, but its mode is too permissive // a call to check_or_create_data_subdir should reset the mode. test_assert(!is_private_dir(subpath)); test_assert(!check_or_create_data_subdir(subdir)); test_assert(is_private_dir(subpath)); #endif done: rmdir(subpath); tor_free(datadir); tor_free(subpath); }
/** Read stored accounting information from disk. Return 0 on success; * return -1 and change nothing on failure. */ static int read_bandwidth_usage(void) { or_state_t *state = get_or_state(); { char *fname = get_datadir_fname("bw_accounting"); unlink(fname); tor_free(fname); } if (!state) return -1; /* Okay; it looks like the state file is more up-to-date than the * bw_accounting file, or the bw_accounting file is nonexistent, * or the bw_accounting file is corrupt. */ log_info(LD_ACCT, "Reading bandwidth accounting data from state file"); n_bytes_read_in_interval = state->AccountingBytesReadInInterval; n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval; n_seconds_active_in_interval = state->AccountingSecondsActive; interval_start_time = state->AccountingIntervalStart; expected_bandwidth_usage = state->AccountingExpectedUsage; { char tbuf1[ISO_TIME_LEN+1]; char tbuf2[ISO_TIME_LEN+1]; format_iso_time(tbuf1, state->LastWritten); format_iso_time(tbuf2, state->AccountingIntervalStart); log_info(LD_ACCT, "Successfully read bandwidth accounting info from state written at %s " "for interval starting at %s. We have been active for %lu seconds in " "this interval. At the start of the interval, we expected to use " "about %lu KB per second. ("U64_FORMAT" bytes read so far, " U64_FORMAT" bytes written so far)", tbuf1, tbuf2, (unsigned long)n_seconds_active_in_interval, (unsigned long)(expected_bandwidth_usage*1024/60), U64_PRINTF_ARG(n_bytes_read_in_interval), U64_PRINTF_ARG(n_bytes_written_in_interval)); } return 0; }
/* Save the disk state to disk but before that update it from the current * state so we always have the latest. Return 0 on success else -1. */ static int disk_state_save_to_disk(void) { int ret; char *state, *content = NULL, *fname = NULL; char tbuf[ISO_TIME_LEN + 1]; time_t now = time(NULL); /* If we didn't have the opportunity to setup an internal disk state, * don't bother saving something to disk. */ if (sr_disk_state == NULL) { ret = 0; goto done; } /* Make sure that our disk state is up to date with our memory state * before saving it to disk. */ disk_state_update(); state = config_dump(&state_format, NULL, sr_disk_state, 0, 0); format_local_iso_time(tbuf, now); tor_asprintf(&content, "# Tor shared random state file last generated on %s " "local time\n" "# Other times below are in UTC\n" "# Please *do not* edit this file.\n\n%s", tbuf, state); tor_free(state); fname = get_datadir_fname(default_fname); if (write_str_to_file(fname, content, 0) < 0) { log_warn(LD_FS, "SR: Unable to write SR state to file %s", fname); ret = -1; goto done; } ret = 0; log_debug(LD_DIR, "SR: Saved state to file %s", fname); done: tor_free(fname); tor_free(content); return ret; }
/** * Running as a server: load, reload, or refresh our ed25519 keys and * certificates, creating and saving new ones as needed. */ int load_ed_keys(const or_options_t *options, time_t now) { ed25519_keypair_t *id = NULL; ed25519_keypair_t *sign = NULL; ed25519_keypair_t *auth = NULL; const ed25519_keypair_t *sign_signing_key_with_id = NULL; const ed25519_keypair_t *use_signing = NULL; const tor_cert_t *check_signing_cert = NULL; tor_cert_t *sign_cert = NULL; tor_cert_t *auth_cert = NULL; #define FAIL(msg) do { \ log_warn(LD_OR, (msg)); \ goto err; \ } while (0) #define SET_KEY(key, newval) do { \ if ((key) != (newval)) \ ed25519_keypair_free(key); \ key = (newval); \ } while (0) #define SET_CERT(cert, newval) do { \ if ((cert) != (newval)) \ tor_cert_free(cert); \ cert = (newval); \ } while (0) #define EXPIRES_SOON(cert, interval) \ (!(cert) || (cert)->valid_until < now + (interval)) /* XXXX support encrypted identity keys fully */ /* First try to get the signing key to see how it is. */ { char *fname = options_get_datadir_fname2(options, "keys", "ed25519_signing"); sign = ed_key_init_from_file( fname, INIT_ED_KEY_NEEDCERT| INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT, LOG_INFO, NULL, 0, 0, CERT_TYPE_ID_SIGNING, &sign_cert); tor_free(fname); check_signing_cert = sign_cert; use_signing = sign; } if (!use_signing && master_signing_key) { check_signing_cert = signing_key_cert; use_signing = master_signing_key; } const int offline_master = options->OfflineMasterKey && options->command != CMD_KEYGEN; const int need_new_signing_key = NULL == use_signing || EXPIRES_SOON(check_signing_cert, 0) || (options->command == CMD_KEYGEN && ! options->change_key_passphrase); const int want_new_signing_key = need_new_signing_key || EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop); /* We can only create a master key if we haven't been told that the * master key will always be offline. Also, if we have a signing key, * then we shouldn't make a new master ID key. */ const int can_make_master_id_key = !offline_master && NULL == use_signing; if (need_new_signing_key) { log_notice(LD_OR, "It looks like I need to generate and sign a new " "medium-term signing key, because %s. To do that, I need to " "load%s the permanent master identity key.", (NULL == use_signing) ? "I don't have one" : EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" : "you asked me to make one with --keygen", can_make_master_id_key ? " (or create)" : ""); } else if (want_new_signing_key && !offline_master) { log_notice(LD_OR, "It looks like I should try to generate and sign a " "new medium-term signing key, because the one I have is " "going to expire soon. To do that, I'm going to have to try to " "load the permanent master identity key."); } else if (want_new_signing_key) { log_notice(LD_OR, "It looks like I should try to generate and sign a " "new medium-term signing key, because the one I have is " "going to expire soon. But OfflineMasterKey is set, so I " "won't try to load a permanent master identity key is set. " "You will need to use 'tor --keygen' make a new signing key " "and certificate."); } { uint32_t flags = (INIT_ED_KEY_SPLIT| INIT_ED_KEY_EXTRA_STRONG|INIT_ED_KEY_NO_REPAIR); if (can_make_master_id_key) flags |= INIT_ED_KEY_CREATE; if (! need_new_signing_key) flags |= INIT_ED_KEY_MISSING_SECRET_OK; if (! want_new_signing_key || offline_master) flags |= INIT_ED_KEY_OMIT_SECRET; if (offline_master) flags |= INIT_ED_KEY_OFFLINE_SECRET; if (options->command == CMD_KEYGEN) flags |= INIT_ED_KEY_TRY_ENCRYPTED; /* Check the key directory */ if (check_private_dir(options->DataDirectory, CPD_CREATE, options->User)) { log_err(LD_OR, "Can't create/check datadirectory %s", options->DataDirectory); goto err; } char *fname = get_datadir_fname("keys"); if (check_private_dir(fname, CPD_CREATE, options->User) < 0) { log_err(LD_OR, "Problem creating/checking key directory %s", fname); tor_free(fname); goto err; } tor_free(fname); if (options->master_key_fname) { fname = tor_strdup(options->master_key_fname); flags |= INIT_ED_KEY_EXPLICIT_FNAME; } else { fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id"); } id = ed_key_init_from_file( fname, flags, LOG_WARN, NULL, 0, 0, 0, NULL); tor_free(fname); if (!id) { if (need_new_signing_key) { if (offline_master) FAIL("Can't load master identity key; OfflineMasterKey is set."); else FAIL("Missing identity key"); } else { log_warn(LD_OR, "Master public key was absent; inferring from " "public key in signing certificate and saving to disk."); tor_assert(check_signing_cert); id = tor_malloc_zero(sizeof(*id)); memcpy(&id->pubkey, &check_signing_cert->signing_key, sizeof(ed25519_public_key_t)); fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id_public_key"); if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) { log_warn(LD_OR, "Error while attempting to write master public key " "to disk"); tor_free(fname); goto err; } tor_free(fname); } } if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) sign_signing_key_with_id = NULL; else sign_signing_key_with_id = id; } if (master_identity_key && !ed25519_pubkey_eq(&id->pubkey, &master_identity_key->pubkey)) { FAIL("Identity key on disk does not match key we loaded earlier!"); } if (need_new_signing_key && NULL == sign_signing_key_with_id) FAIL("Can't load master key make a new signing key."); if (sign_cert) { if (! sign_cert->signing_key_included) FAIL("Loaded a signing cert with no key included!"); if (! ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey)) FAIL("The signing cert we have was not signed with the master key " "we loaded!"); if (tor_cert_checksig(sign_cert, &id->pubkey, 0) < 0) FAIL("The signing cert we loaded was not signed correctly!"); } if (want_new_signing_key && sign_signing_key_with_id) { uint32_t flags = (INIT_ED_KEY_CREATE| INIT_ED_KEY_REPLACE| INIT_ED_KEY_EXTRA_STRONG| INIT_ED_KEY_NEEDCERT| INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT); char *fname = options_get_datadir_fname2(options, "keys", "ed25519_signing"); ed25519_keypair_free(sign); tor_cert_free(sign_cert); sign = ed_key_init_from_file(fname, flags, LOG_WARN, sign_signing_key_with_id, now, options->SigningKeyLifetime, CERT_TYPE_ID_SIGNING, &sign_cert); tor_free(fname); if (!sign) FAIL("Missing signing key"); use_signing = sign; tor_assert(sign_cert->signing_key_included); tor_assert(ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey)); tor_assert(ed25519_pubkey_eq(&sign_cert->signed_key, &sign->pubkey)); } else if (want_new_signing_key) { static ratelim_t missing_master = RATELIM_INIT(3600); log_fn_ratelim(&missing_master, LOG_WARN, LD_OR, "Signing key will expire soon, but I can't load the " "master key to sign a new one!"); } tor_assert(use_signing); /* At this point we no longer need our secret identity key. So wipe * it, if we loaded it in the first place. */ memwipe(id->seckey.seckey, 0, sizeof(id->seckey)); if (options->command == CMD_KEYGEN) goto end; if (!rsa_ed_crosscert && server_mode(options)) { uint8_t *crosscert; ssize_t crosscert_len = tor_make_rsa_ed25519_crosscert(&id->pubkey, get_server_identity_key(), now+10*365*86400,/*XXXX*/ &crosscert); rsa_ed_crosscert_len = crosscert_len; rsa_ed_crosscert = crosscert; } if (!current_auth_key || EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) { auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, now, options->TestingAuthKeyLifetime, CERT_TYPE_SIGNING_AUTH, &auth_cert); if (!auth) FAIL("Can't create auth key"); } /* We've generated or loaded everything. Put them in memory. */ end: if (! master_identity_key) { SET_KEY(master_identity_key, id); } else { tor_free(id); } if (sign) { SET_KEY(master_signing_key, sign); SET_CERT(signing_key_cert, sign_cert); } if (auth) { SET_KEY(current_auth_key, auth); SET_CERT(auth_key_cert, auth_cert); } return 0; err: ed25519_keypair_free(id); ed25519_keypair_free(sign); ed25519_keypair_free(auth); tor_cert_free(sign_cert); tor_cert_free(auth_cert); return -1; }
/** Run unit tests for private dir permission enforcement logic. */ static void test_checkdir_perms(void *testdata) { (void)testdata; or_options_t *options = get_options_mutable(); const char *subdir = "test_checkdir"; char *testdir = NULL; cpd_check_t cpd_chkopts; cpd_check_t unix_create_opts; cpd_check_t unix_verify_optsmask; struct stat st; umask(022); /* setup data directory before tests. */ tor_free(options->DataDirectory); options->DataDirectory = tor_strdup(get_fname(subdir)); tt_int_op(mkdir(options->DataDirectory, 0750), OP_EQ, 0); /* test: create new dir, no flags. */ testdir = get_datadir_fname("checkdir_new_none"); cpd_chkopts = CPD_CREATE; unix_verify_optsmask = 0077; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); tor_free(testdir); /* test: create new dir, CPD_GROUP_OK option set. */ testdir = get_datadir_fname("checkdir_new_groupok"); cpd_chkopts = CPD_CREATE|CPD_GROUP_OK; unix_verify_optsmask = 0077; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); tor_free(testdir); /* test: should get an error on existing dir with wrong perms */ testdir = get_datadir_fname("checkdir_new_groupok_err"); tt_int_op(0, OP_EQ, mkdir(testdir, 027)); cpd_chkopts = CPD_CHECK_MODE_ONLY|CPD_CREATE|CPD_GROUP_OK; tt_int_op_nowin(-1, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tor_free(testdir); /* test: create new dir, CPD_GROUP_READ option set. */ testdir = get_datadir_fname("checkdir_new_groupread"); cpd_chkopts = CPD_CREATE|CPD_GROUP_READ; unix_verify_optsmask = 0027; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); tor_free(testdir); /* test: check existing dir created with defaults, and verify with CPD_CREATE only. */ testdir = get_datadir_fname("checkdir_exists_none"); cpd_chkopts = CPD_CREATE; unix_create_opts = 0700; (void)unix_create_opts; unix_verify_optsmask = 0077; tt_int_op(0, OP_EQ, mkdir(testdir, unix_create_opts)); tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); tor_free(testdir); /* test: check existing dir created with defaults, and verify with CPD_GROUP_OK option set. */ testdir = get_datadir_fname("checkdir_exists_groupok"); cpd_chkopts = CPD_CREATE; unix_verify_optsmask = 0077; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); cpd_chkopts = CPD_GROUP_OK; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); tor_free(testdir); /* test: check existing dir created with defaults, and verify with CPD_GROUP_READ option set. */ testdir = get_datadir_fname("checkdir_exists_groupread"); cpd_chkopts = CPD_CREATE; unix_verify_optsmask = 0027; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); cpd_chkopts = CPD_GROUP_READ; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); tor_free(testdir); /* test: check existing dir created with CPD_GROUP_READ, and verify with CPD_GROUP_OK option set. */ testdir = get_datadir_fname("checkdir_existsread_groupok"); cpd_chkopts = CPD_CREATE|CPD_GROUP_READ; unix_verify_optsmask = 0027; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); cpd_chkopts = CPD_GROUP_OK; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); tor_free(testdir); /* test: check existing dir created with CPD_GROUP_READ, and verify with CPD_GROUP_READ option set. */ testdir = get_datadir_fname("checkdir_existsread_groupread"); cpd_chkopts = CPD_CREATE|CPD_GROUP_READ; unix_verify_optsmask = 0027; tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL)); tt_int_op(0, OP_EQ, stat(testdir, &st)); tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask)); done: tor_free(testdir); }
/** Reload the persistent state from disk, generating a new state as needed. * Return 0 on success, less than 0 on failure. */ int or_state_load(void) { or_state_t *new_state = NULL; char *contents = NULL, *fname; char *errmsg = NULL; int r = -1, badstate = 0; fname = get_datadir_fname("state"); switch (file_status(fname)) { case FN_FILE: if (!(contents = read_file_to_str(fname, 0, NULL))) { log_warn(LD_FS, "Unable to read state file \"%s\"", fname); goto done; } break; case FN_NOENT: break; case FN_ERROR: case FN_DIR: default: log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname); goto done; } new_state = tor_malloc_zero(sizeof(or_state_t)); new_state->_magic = OR_STATE_MAGIC; config_init(&state_format, new_state); if (contents) { config_line_t *lines=NULL; int assign_retval; if (config_get_lines(contents, &lines, 0)<0) goto done; assign_retval = config_assign(&state_format, new_state, lines, 0, 0, &errmsg); config_free_lines(lines); if (assign_retval<0) badstate = 1; if (errmsg) { log_warn(LD_GENERAL, "%s", errmsg); tor_free(errmsg); } } if (!badstate && or_state_validate(NULL, new_state, 1, &errmsg) < 0) badstate = 1; if (errmsg) { log_warn(LD_GENERAL, "%s", errmsg); tor_free(errmsg); } if (badstate && !contents) { log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state." " This is a bug in Tor."); goto done; } else if (badstate && contents) { or_state_save_broken(fname); tor_free(contents); config_free(&state_format, new_state); new_state = tor_malloc_zero(sizeof(or_state_t)); new_state->_magic = OR_STATE_MAGIC; config_init(&state_format, new_state); } else if (contents) { log_info(LD_GENERAL, "Loaded state from \"%s\"", fname); } else { log_info(LD_GENERAL, "Initialized state"); } if (or_state_set(new_state) == -1) { or_state_save_broken(fname); } new_state = NULL; if (!contents) { global_state->next_write = 0; or_state_save(time(NULL)); } r = 0; done: tor_free(fname); tor_free(contents); if (new_state) config_free(&state_format, new_state); return r; }