summaryrefslogtreecommitdiff
path: root/subversion/libsvn_repos/config_pool.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_repos/config_pool.c')
-rw-r--r--subversion/libsvn_repos/config_pool.c485
1 files changed, 51 insertions, 434 deletions
diff --git a/subversion/libsvn_repos/config_pool.c b/subversion/libsvn_repos/config_pool.c
index 164bd983c02bc..acf4be34061e8 100644
--- a/subversion/libsvn_repos/config_pool.c
+++ b/subversion/libsvn_repos/config_pool.c
@@ -25,110 +25,17 @@
#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"
-
-/* Our wrapper structure for parsed svn_config_t* instances. All data in
- * CS_CFG and CI_CFG is expanded (to make it thread-safe) and considered
- * read-only.
- */
-typedef struct config_object_t
-{
- /* UUID of the configuration contents.
- * This is a SHA1 checksum of the parsed textual representation of CFG. */
- svn_checksum_t *key;
-
- /* Parsed and expanded configuration. At least one of the following
- * must not be NULL. */
-
- /* Case-sensitive config. May be NULL */
- svn_config_t *cs_cfg;
-
- /* Case-insensitive config. May be NULL */
- svn_config_t *ci_cfg;
-} config_object_t;
-
-
-/* Data structure used to short-circuit the repository access for configs
- * read via URL. After reading such a config successfully, we store key
- * repository information here and will validate it without actually opening
- * the repository.
- *
- * As this is only an optimization and may create many entries in
- * svn_repos__config_pool_t's IN_REPO_HASH_POOL index, we clean them up
- * once in a while.
- */
-typedef struct in_repo_config_t
-{
- /* URL used to open the configuration */
- const char *url;
-
- /* Path of the repository that contained URL */
- const char *repo_root;
-
- /* Head revision of that repository when last read */
- svn_revnum_t revision;
-
- /* Contents checksum of the file stored under URL@REVISION */
- svn_checksum_t *key;
-} in_repo_config_t;
-
-
-/* Core data structure extending the encapsulated OBJECT_POOL. All access
- * to it must be serialized using the OBJECT_POOL->MUTEX.
- *
- * To speed up URL@HEAD lookups, we maintain IN_REPO_CONFIGS as a secondary
- * hash index. It maps URLs as provided by the caller onto in_repo_config_t
- * instances. If that is still up-to-date, a further lookup into CONFIG
- * may yield the desired configuration without the need to actually open
- * the respective repository.
- *
- * Unused configurations that are kept in the IN_REPO_CONFIGS hash and may
- * be cleaned up when the hash is about to grow.
- */
-struct svn_repos__config_pool_t
-{
- svn_object_pool__t *object_pool;
-
- /* URL -> in_repo_config_t* mapping.
- * This is only a partial index and will get cleared regularly. */
- apr_hash_t *in_repo_configs;
-
- /* allocate the IN_REPO_CONFIGS index and in_repo_config_t here */
- apr_pool_t *in_repo_hash_pool;
-};
+#include "config_file.h"
-/* Return an automatic reference to the CFG member in CONFIG that will be
- * released when POOL gets cleaned up. The case sensitivity flag in *BATON
- * selects the desired option and section name matching mode.
- */
-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;
-
- /* we need to duplicate the root structure as it contains temp. buffers */
- return config ? svn_config__shallow_copy(config, pool) : NULL;
-}
-
/* Return a memory buffer structure allocated in POOL and containing the
* data from CHECKSUM.
*/
@@ -146,288 +53,43 @@ checksum_as_key(svn_checksum_t *checksum,
return result;
}
-/* Copy the configuration from the wrapper in SOURCE to the wrapper in
- * *TARGET with the case sensitivity flag in *BATON selecting the config
- * to copy. This is usually done to add the missing case-(in)-sensitive
- * variant. Since we must hold all data in *TARGET from the same POOL,
- * a deep copy is required.
- */
-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;
-
- /* Maybe, we created a variant with different case sensitivity? */
- 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;
-}
-
-/* Set *CFG to the configuration passed in as text in CONTENTS and *KEY to
- * the corresponding object pool key. If no such configuration exists in
- * CONFIG_POOL, yet, parse CONTENTS and cache the result. CASE_SENSITIVE
- * controls option and section name matching.
+/* Set *CFG to the configuration serialized in STREAM and cache it in
+ * CONFIG_POOL under CHECKSUM. The configuration will only be parsed if
+ * we can't find it the CONFIG_POOL already.
*
* RESULT_POOL determines the lifetime of the returned reference and
* SCRATCH_POOL is being used for temporary allocations.
*/
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;
-
- /* calculate SHA1 over the whole file contents */
- SVN_ERR(svn_stream_close
- (svn_stream_checksummed2
- (svn_stream_from_stringbuf(contents, scratch_pool),
- &checksum, NULL, svn_checksum_sha1, TRUE, scratch_pool)));
-
- /* return reference to suitable config object if that already exists */
- *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;
-
- /* create a pool for the new config object and parse the data into it */
- 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));
-
- /* switch config data to r/o mode to guarantee thread-safe access */
- svn_config__set_read_only(case_sensitive ? config_object->cs_cfg
- : config_object->ci_cfg,
- cfg_pool);
-
- /* add config in pool, handle loads races and return the right config */
- 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;
-}
-
-/* Store a URL@REVISION to CHECKSUM, REPOS_ROOT in CONFIG_POOL.
- */
-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)
+find_config(svn_config_t **cfg,
+ svn_repos__config_pool_t *config_pool,
+ svn_stream_t *stream,
+ svn_checksum_t *checksum,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- 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)
- {
- /* update the existing entry */
- memcpy((void *)config->key->digest, checksum->digest,
- svn_checksum_size(checksum));
- config->revision = revision;
-
- /* duplicate the string only if necessary */
- if (strcmp(config->repo_root, repos_root))
- config->repo_root = apr_pstrdup(pool, repos_root);
- }
- else
- {
- /* insert a new entry.
- * Limit memory consumption by cyclically clearing pool and hash. */
- 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);
- }
-
- /* construct the new entry */
- 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;
+ /* First, attempt the cache lookup. */
+ svn_membuf_t *key = checksum_as_key(checksum, scratch_pool);
+ SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool, key,
+ result_pool));
- /* add to index */
- apr_hash_set(config_pool->in_repo_configs, url, path_len, config);
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Set *CFG to the configuration stored in URL@HEAD and cache it in
- * CONFIG_POOL. CASE_SENSITIVE controls
- * option and section name matching. If PREFERRED_REPOS is given,
- * use that if it also matches URL.
- *
- * RESULT_POOL determines the lifetime of the returned reference and
- * SCRATCH_POOL is being used for temporary allocations.
- */
-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));
-
- /* maybe we can use the preferred repos instance instead of creating a
- * new one */
- 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;
- }
-
- /* open repos if no suitable preferred repos was provided. */
- if (!repos)
- {
- /* Search for a repository in the full path. */
- repos_root_dirent = svn_repos_find_root_path(dirent, scratch_pool);
-
- /* Attempt to open a repository at repos_root_dirent. */
- SVN_ERR(svn_repos_open3(&repos, repos_root_dirent, NULL,
- scratch_pool, scratch_pool));
- }
-
- fs_path = &dirent[strlen(repos_root_dirent)];
-
- /* Get the filesystem. */
- fs = svn_repos_fs(repos);
-
- /* Find HEAD and the revision root */
- SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, scratch_pool));
- SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, scratch_pool));
-
- /* Fetch checksum and see whether we already have a matching config */
- 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));
- }
-
- /* not parsed, yet? */
+ /* Not found? => parse and cache */
if (!*cfg)
{
- svn_filesize_t length;
+ svn_config_t *config;
- /* fetch the file contents */
- SVN_ERR(svn_fs_check_path(&node_kind, root, fs_path, scratch_pool));
- if (node_kind != svn_node_file)
- return SVN_NO_ERROR;
+ /* create a pool for the new config object and parse the data into it */
+ apr_pool_t *cfg_pool = svn_object_pool__new_item_pool(config_pool);
+ SVN_ERR(svn_config_parse(&config, stream, FALSE, FALSE, cfg_pool));
- 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));
+ /* switch config data to r/o mode to guarantee thread-safe access */
+ svn_config__set_read_only(config, cfg_pool);
- /* handle it like ordinary file contents and cache it */
- SVN_ERR(auto_parse(cfg, key, config_pool, contents, case_sensitive,
- result_pool, scratch_pool));
+ /* add config in pool, handle loads races and return the right config */
+ SVN_ERR(svn_object_pool__insert((void **)cfg, config_pool, key,
+ config, cfg_pool, result_pool));
}
- /* store the (path,rev) -> checksum mapping as well */
- 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;
-}
-
-/* Given the URL, search the CONFIG_POOL for an entry that maps it URL to
- * a content checksum and is still up-to-date. If this could be found,
- * return the object's *KEY. Use POOL for allocations.
- *
- * Requires external serialization on CONFIG_POOL.
- *
- * Note that this is only the URL(+rev) -> Checksum lookup and does not
- * guarantee that there is actually a config object available for *KEY.
- */
-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;
-
- /* hash lookup url -> sha1 -> config */
- in_repo_config_t *config = svn_hash_gets(config_pool->in_repo_configs, url);
- *key = NULL;
- if (!config)
- return SVN_NO_ERROR;
-
- /* found *some* reference to a configuration.
- * Verify that it is still current. Will fail for BDB repos. */
- err = svn_stringbuf_from_file2(&contents,
- svn_dirent_join(config->repo_root,
- "db/current", pool),
- pool);
- if (!err)
- err = svn_cstring_atoi64(&current, contents->data);
-
- if (err)
- svn_error_clear(err);
- else if (current == config->revision)
- *key = checksum_as_key(config->key, pool);
-
return SVN_NO_ERROR;
}
@@ -438,94 +100,49 @@ 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));
-
- /* construct the config pool in our private ROOT_POOL to survive POOL
- * cleanup and to prevent threading issues with the allocator */
- 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;
+ return svn_error_trace(svn_object_pool__create(config_pool,
+ thread_safe, pool));
}
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);
+ config_access_t *access = svn_repos__create_config_access(preferred_repos,
+ scratch_pool);
+ svn_stream_t *stream;
+ svn_checksum_t *checksum;
- /* make sure we always have a *KEY object */
- svn_membuf_t *local_key = NULL;
- if (key == NULL)
- key = &local_key;
- else
- *key = NULL;
-
- if (svn_path_is_url(path))
- {
- /* Read config file from repository.
- * Attempt a quick lookup first. */
- 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;
- }
- }
-
- /* Read and cache the configuration. This may fail. */
- err = find_repos_config(cfg, key, config_pool, path, case_sensitive,
- preferred_repos, pool, scratch_pool);
- if (err || !*cfg)
- {
- /* let the standard implementation handle all the difficult cases */
- svn_error_clear(err);
- err = svn_repos__retrieve_config(cfg, path, must_exist,
- case_sensitive, pool);
- }
- }
- else
+ *cfg = NULL;
+ err = svn_repos__get_config(&stream, &checksum, access, path, must_exist,
+ scratch_pool);
+ if (!err)
+ err = svn_error_quick_wrapf(find_config(cfg, config_pool, stream,
+ checksum, pool, scratch_pool),
+ "Error while parsing config file: '%s':",
+ path);
+
+ /* Let the standard implementation handle all the difficult cases.
+ * Note that for in-repo configs, there are no further special cases to
+ * check for and deal with. */
+ if (!*cfg && !svn_path_is_url(path))
{
- /* Outside of repo file. Read it. */
- svn_stringbuf_t *contents;
- err = svn_stringbuf_from_file2(&contents, path, scratch_pool);
- if (err)
- {
- /* let the standard implementation handle all the difficult cases */
- svn_error_clear(err);
- err = svn_config_read3(cfg, path, must_exist, case_sensitive,
- case_sensitive, pool);
- }
- else
- {
- /* parsing and caching will always succeed */
- err = auto_parse(cfg, key, config_pool, contents, case_sensitive,
- pool, scratch_pool);
- }
+ svn_error_clear(err);
+ err = svn_config_read3(cfg, path, must_exist, FALSE, FALSE, pool);
}
+ svn_repos__destroy_config_access(access);
svn_pool_destroy(scratch_pool);
- return err;
+ /* we need to duplicate the root structure as it contains temp. buffers */
+ if (*cfg)
+ *cfg = svn_config__shallow_copy(*cfg, pool);
+
+ return svn_error_trace(err);
}