#include "svn_checksum.h"
#include "svn_config.h"
#include "svn_error.h"
#include "svn_hash.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "svn_repos.h"
#include "private/svn_dep_compat.h"
#include "private/svn_mutex.h"
#include "private/svn_subr_private.h"
#include "private/svn_repos_private.h"
#include "private/svn_object_pool.h"
#include "svn_private_config.h"
typedef struct config_object_t
{
svn_checksum_t *key;
svn_config_t *cs_cfg;
svn_config_t *ci_cfg;
} config_object_t;
typedef struct in_repo_config_t
{
const char *url;
const char *repo_root;
svn_revnum_t revision;
svn_checksum_t *key;
} in_repo_config_t;
struct svn_repos__config_pool_t
{
svn_object_pool__t *object_pool;
apr_hash_t *in_repo_configs;
apr_pool_t *in_repo_hash_pool;
};
static void *
getter(void *object,
void *baton,
apr_pool_t *pool)
{
config_object_t *wrapper = object;
svn_boolean_t *case_sensitive = baton;
svn_config_t *config = *case_sensitive ? wrapper->cs_cfg : wrapper->ci_cfg;
return config ? svn_config__shallow_copy(config, pool) : NULL;
}
static svn_membuf_t *
checksum_as_key(svn_checksum_t *checksum,
apr_pool_t *pool)
{
svn_membuf_t *result = apr_pcalloc(pool, sizeof(*result));
apr_size_t size = svn_checksum_size(checksum);
svn_membuf__create(result, size, pool);
result->size = size;
memcpy(result->data, checksum->digest, size);
return result;
}
static svn_error_t *
setter(void **target,
void *source,
void *baton,
apr_pool_t *pool)
{
svn_boolean_t *case_sensitive = baton;
config_object_t *target_cfg = *(config_object_t **)target;
config_object_t *source_cfg = source;
if (*case_sensitive && target_cfg->cs_cfg == NULL)
{
SVN_ERR(svn_config_dup(&target_cfg->cs_cfg, source_cfg->cs_cfg, pool));
svn_config__set_read_only(target_cfg->cs_cfg, pool);
}
else if (!*case_sensitive && target_cfg->ci_cfg == NULL)
{
SVN_ERR(svn_config_dup(&target_cfg->ci_cfg, source_cfg->ci_cfg, pool));
svn_config__set_read_only(target_cfg->ci_cfg, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
auto_parse(svn_config_t **cfg,
svn_membuf_t **key,
svn_repos__config_pool_t *config_pool,
svn_stringbuf_t *contents,
svn_boolean_t case_sensitive,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_checksum_t *checksum;
config_object_t *config_object;
apr_pool_t *cfg_pool;
SVN_ERR(svn_stream_close
(svn_stream_checksummed2
(svn_stream_from_stringbuf(contents, scratch_pool),
&checksum, NULL, svn_checksum_sha1, TRUE, scratch_pool)));
*key = checksum_as_key(checksum, result_pool);
SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool->object_pool,
*key, &case_sensitive, result_pool));
if (*cfg)
return SVN_NO_ERROR;
cfg_pool = svn_object_pool__new_wrapper_pool(config_pool->object_pool);
config_object = apr_pcalloc(cfg_pool, sizeof(*config_object));
SVN_ERR(svn_config_parse(case_sensitive ? &config_object->cs_cfg
: &config_object->ci_cfg,
svn_stream_from_stringbuf(contents, scratch_pool),
case_sensitive, case_sensitive, cfg_pool));
svn_config__set_read_only(case_sensitive ? config_object->cs_cfg
: config_object->ci_cfg,
cfg_pool);
SVN_ERR(svn_object_pool__insert((void **)cfg, config_pool->object_pool,
*key, config_object, &case_sensitive,
cfg_pool, result_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
add_checksum(svn_repos__config_pool_t *config_pool,
const char *url,
const char *repos_root,
svn_revnum_t revision,
svn_checksum_t *checksum)
{
apr_size_t path_len = strlen(url);
apr_pool_t *pool = config_pool->in_repo_hash_pool;
in_repo_config_t *config = apr_hash_get(config_pool->in_repo_configs,
url, path_len);
if (config)
{
memcpy((void *)config->key->digest, checksum->digest,
svn_checksum_size(checksum));
config->revision = revision;
if (strcmp(config->repo_root, repos_root))
config->repo_root = apr_pstrdup(pool, repos_root);
}
else
{
if (2 * svn_object_pool__count(config_pool->object_pool)
< apr_hash_count(config_pool->in_repo_configs))
{
svn_pool_clear(pool);
config_pool->in_repo_configs = svn_hash__make(pool);
}
config = apr_pcalloc(pool, sizeof(*config));
config->key = svn_checksum_dup(checksum, pool);
config->url = apr_pstrmemdup(pool, url, path_len);
config->repo_root = apr_pstrdup(pool, repos_root);
config->revision = revision;
apr_hash_set(config_pool->in_repo_configs, url, path_len, config);
}
return SVN_NO_ERROR;
}
static svn_error_t *
find_repos_config(svn_config_t **cfg,
svn_membuf_t **key,
svn_repos__config_pool_t *config_pool,
const char *url,
svn_boolean_t case_sensitive,
svn_repos_t *preferred_repos,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_repos_t *repos = NULL;
svn_fs_t *fs;
svn_fs_root_t *root;
svn_revnum_t youngest_rev;
svn_node_kind_t node_kind;
const char *dirent;
svn_stream_t *stream;
const char *fs_path;
const char *repos_root_dirent;
svn_checksum_t *checksum;
svn_stringbuf_t *contents;
*cfg = NULL;
SVN_ERR(svn_uri_get_dirent_from_file_url(&dirent, url, scratch_pool));
if (preferred_repos)
{
repos_root_dirent = svn_repos_path(preferred_repos, scratch_pool);
if (!svn_dirent_is_absolute(repos_root_dirent))
SVN_ERR(svn_dirent_get_absolute(&repos_root_dirent,
repos_root_dirent,
scratch_pool));
if (svn_dirent_is_ancestor(repos_root_dirent, dirent))
repos = preferred_repos;
}
if (!repos)
{
repos_root_dirent = svn_repos_find_root_path(dirent, scratch_pool);
SVN_ERR(svn_repos_open3(&repos, repos_root_dirent, NULL,
scratch_pool, scratch_pool));
}
fs_path = &dirent[strlen(repos_root_dirent)];
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, scratch_pool));
SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, scratch_pool));
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, root, fs_path,
FALSE, scratch_pool));
if (checksum)
{
*key = checksum_as_key(checksum, scratch_pool);
SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool->object_pool,
*key, &case_sensitive, result_pool));
}
if (!*cfg)
{
svn_filesize_t length;
SVN_ERR(svn_fs_check_path(&node_kind, root, fs_path, scratch_pool));
if (node_kind != svn_node_file)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_file_length(&length, root, fs_path, scratch_pool));
SVN_ERR(svn_fs_file_contents(&stream, root, fs_path, scratch_pool));
SVN_ERR(svn_stringbuf_from_stream(&contents, stream,
(apr_size_t)length, scratch_pool));
SVN_ERR(auto_parse(cfg, key, config_pool, contents, case_sensitive,
result_pool, scratch_pool));
}
if (*cfg && checksum)
SVN_MUTEX__WITH_LOCK(svn_object_pool__mutex(config_pool->object_pool),
add_checksum(config_pool, url, repos_root_dirent,
youngest_rev, checksum));
return SVN_NO_ERROR;
}
static svn_error_t *
key_by_url(svn_membuf_t **key,
svn_repos__config_pool_t *config_pool,
const char *url,
apr_pool_t *pool)
{
svn_error_t *err;
svn_stringbuf_t *contents;
apr_int64_t current;
in_repo_config_t *config = svn_hash_gets(config_pool->in_repo_configs, url);
*key = NULL;
if (!config)
return SVN_NO_ERROR;
err = svn_stringbuf_from_file2(&contents,
svn_dirent_join(config->repo_root,
"db/current", pool),
pool);
if (!err)
err = svn_cstring_atoi64(¤t, contents->data);
if (err)
svn_error_clear(err);
else if (current == config->revision)
*key = checksum_as_key(config->key, pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos__config_pool_create(svn_repos__config_pool_t **config_pool,
svn_boolean_t thread_safe,
apr_pool_t *pool)
{
svn_repos__config_pool_t *result;
svn_object_pool__t *object_pool;
SVN_ERR(svn_object_pool__create(&object_pool, getter, setter,
thread_safe, pool));
result = apr_pcalloc(pool, sizeof(*result));
result->object_pool = object_pool;
result->in_repo_hash_pool = svn_pool_create(pool);
result->in_repo_configs = svn_hash__make(result->in_repo_hash_pool);
*config_pool = result;
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos__config_pool_get(svn_config_t **cfg,
svn_membuf_t **key,
svn_repos__config_pool_t *config_pool,
const char *path,
svn_boolean_t must_exist,
svn_boolean_t case_sensitive,
svn_repos_t *preferred_repos,
apr_pool_t *pool)
{
svn_error_t *err = SVN_NO_ERROR;
apr_pool_t *scratch_pool = svn_pool_create(pool);
svn_membuf_t *local_key = NULL;
if (key == NULL)
key = &local_key;
else
*key = NULL;
if (svn_path_is_url(path))
{
SVN_MUTEX__WITH_LOCK(svn_object_pool__mutex(config_pool->object_pool),
key_by_url(key, config_pool, path, pool));
if (*key)
{
SVN_ERR(svn_object_pool__lookup((void **)cfg,
config_pool->object_pool,
*key, &case_sensitive, pool));
if (*cfg)
{
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
}
err = find_repos_config(cfg, key, config_pool, path, case_sensitive,
preferred_repos, pool, scratch_pool);
if (err || !*cfg)
{
svn_error_clear(err);
err = svn_repos__retrieve_config(cfg, path, must_exist,
case_sensitive, pool);
}
}
else
{
svn_stringbuf_t *contents;
err = svn_stringbuf_from_file2(&contents, path, scratch_pool);
if (err)
{
svn_error_clear(err);
err = svn_config_read3(cfg, path, must_exist, case_sensitive,
case_sensitive, pool);
}
else
{
err = auto_parse(cfg, key, config_pool, contents, case_sensitive,
pool, scratch_pool);
}
}
svn_pool_destroy(scratch_pool);
return err;
}