config_pool.c (302408) | config_pool.c (362181) |
---|---|
1/* 2 * config_pool.c : pool of configuration objects 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file --- 11 unchanged lines hidden (view full) --- 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26 27#include "svn_checksum.h" | 1/* 2 * config_pool.c : pool of configuration objects 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file --- 11 unchanged lines hidden (view full) --- 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26 27#include "svn_checksum.h" |
28#include "svn_config.h" 29#include "svn_error.h" 30#include "svn_hash.h" | |
31#include "svn_path.h" 32#include "svn_pools.h" | 28#include "svn_path.h" 29#include "svn_pools.h" |
33#include "svn_repos.h" | |
34 | 30 |
35#include "private/svn_dep_compat.h" 36#include "private/svn_mutex.h" | |
37#include "private/svn_subr_private.h" 38#include "private/svn_repos_private.h" | 31#include "private/svn_subr_private.h" 32#include "private/svn_repos_private.h" |
39#include "private/svn_object_pool.h" | |
40 41#include "svn_private_config.h" 42 | 33 34#include "svn_private_config.h" 35 |
43 44/* Our wrapper structure for parsed svn_config_t* instances. All data in 45 * CS_CFG and CI_CFG is expanded (to make it thread-safe) and considered 46 * read-only. 47 */ 48typedef struct config_object_t 49{ 50 /* UUID of the configuration contents. 51 * This is a SHA1 checksum of the parsed textual representation of CFG. */ 52 svn_checksum_t *key; | 36#include "config_file.h" |
53 | 37 |
54 /* Parsed and expanded configuration. At least one of the following 55 * must not be NULL. */ 56 57 /* Case-sensitive config. May be NULL */ 58 svn_config_t *cs_cfg; 59 60 /* Case-insensitive config. May be NULL */ 61 svn_config_t *ci_cfg; 62} config_object_t; 63 64 65/* Data structure used to short-circuit the repository access for configs 66 * read via URL. After reading such a config successfully, we store key 67 * repository information here and will validate it without actually opening 68 * the repository. 69 * 70 * As this is only an optimization and may create many entries in 71 * svn_repos__config_pool_t's IN_REPO_HASH_POOL index, we clean them up 72 * once in a while. 73 */ 74typedef struct in_repo_config_t 75{ 76 /* URL used to open the configuration */ 77 const char *url; 78 79 /* Path of the repository that contained URL */ 80 const char *repo_root; 81 82 /* Head revision of that repository when last read */ 83 svn_revnum_t revision; 84 85 /* Contents checksum of the file stored under URL@REVISION */ 86 svn_checksum_t *key; 87} in_repo_config_t; 88 89 90/* Core data structure extending the encapsulated OBJECT_POOL. All access 91 * to it must be serialized using the OBJECT_POOL->MUTEX. 92 * 93 * To speed up URL@HEAD lookups, we maintain IN_REPO_CONFIGS as a secondary 94 * hash index. It maps URLs as provided by the caller onto in_repo_config_t 95 * instances. If that is still up-to-date, a further lookup into CONFIG 96 * may yield the desired configuration without the need to actually open 97 * the respective repository. 98 * 99 * Unused configurations that are kept in the IN_REPO_CONFIGS hash and may 100 * be cleaned up when the hash is about to grow. 101 */ 102struct svn_repos__config_pool_t 103{ 104 svn_object_pool__t *object_pool; 105 106 /* URL -> in_repo_config_t* mapping. 107 * This is only a partial index and will get cleared regularly. */ 108 apr_hash_t *in_repo_configs; 109 110 /* allocate the IN_REPO_CONFIGS index and in_repo_config_t here */ 111 apr_pool_t *in_repo_hash_pool; 112}; 113 | |
114 | 38 |
115/* Return an automatic reference to the CFG member in CONFIG that will be 116 * released when POOL gets cleaned up. The case sensitivity flag in *BATON 117 * selects the desired option and section name matching mode. 118 */ 119static void * 120getter(void *object, 121 void *baton, 122 apr_pool_t *pool) 123{ 124 config_object_t *wrapper = object; 125 svn_boolean_t *case_sensitive = baton; 126 svn_config_t *config = *case_sensitive ? wrapper->cs_cfg : wrapper->ci_cfg; 127 128 /* we need to duplicate the root structure as it contains temp. buffers */ 129 return config ? svn_config__shallow_copy(config, pool) : NULL; 130} 131 | |
132/* Return a memory buffer structure allocated in POOL and containing the 133 * data from CHECKSUM. 134 */ 135static svn_membuf_t * 136checksum_as_key(svn_checksum_t *checksum, 137 apr_pool_t *pool) 138{ 139 svn_membuf_t *result = apr_pcalloc(pool, sizeof(*result)); 140 apr_size_t size = svn_checksum_size(checksum); 141 142 svn_membuf__create(result, size, pool); 143 result->size = size; /* exact length is required! */ 144 memcpy(result->data, checksum->digest, size); 145 146 return result; 147} 148 | 39/* Return a memory buffer structure allocated in POOL and containing the 40 * data from CHECKSUM. 41 */ 42static svn_membuf_t * 43checksum_as_key(svn_checksum_t *checksum, 44 apr_pool_t *pool) 45{ 46 svn_membuf_t *result = apr_pcalloc(pool, sizeof(*result)); 47 apr_size_t size = svn_checksum_size(checksum); 48 49 svn_membuf__create(result, size, pool); 50 result->size = size; /* exact length is required! */ 51 memcpy(result->data, checksum->digest, size); 52 53 return result; 54} 55 |
149/* Copy the configuration from the wrapper in SOURCE to the wrapper in 150 * *TARGET with the case sensitivity flag in *BATON selecting the config 151 * to copy. This is usually done to add the missing case-(in)-sensitive 152 * variant. Since we must hold all data in *TARGET from the same POOL, 153 * a deep copy is required. 154 */ 155static svn_error_t * 156setter(void **target, 157 void *source, 158 void *baton, 159 apr_pool_t *pool) 160{ 161 svn_boolean_t *case_sensitive = baton; 162 config_object_t *target_cfg = *(config_object_t **)target; 163 config_object_t *source_cfg = source; 164 165 /* Maybe, we created a variant with different case sensitivity? */ 166 if (*case_sensitive && target_cfg->cs_cfg == NULL) 167 { 168 SVN_ERR(svn_config_dup(&target_cfg->cs_cfg, source_cfg->cs_cfg, pool)); 169 svn_config__set_read_only(target_cfg->cs_cfg, pool); 170 } 171 else if (!*case_sensitive && target_cfg->ci_cfg == NULL) 172 { 173 SVN_ERR(svn_config_dup(&target_cfg->ci_cfg, source_cfg->ci_cfg, pool)); 174 svn_config__set_read_only(target_cfg->ci_cfg, pool); 175 } 176 177 return SVN_NO_ERROR; 178} 179 180/* Set *CFG to the configuration passed in as text in CONTENTS and *KEY to 181 * the corresponding object pool key. If no such configuration exists in 182 * CONFIG_POOL, yet, parse CONTENTS and cache the result. CASE_SENSITIVE 183 * controls option and section name matching. | 56/* Set *CFG to the configuration serialized in STREAM and cache it in 57 * CONFIG_POOL under CHECKSUM. The configuration will only be parsed if 58 * we can't find it the CONFIG_POOL already. |
184 * 185 * RESULT_POOL determines the lifetime of the returned reference and 186 * SCRATCH_POOL is being used for temporary allocations. 187 */ 188static svn_error_t * | 59 * 60 * RESULT_POOL determines the lifetime of the returned reference and 61 * SCRATCH_POOL is being used for temporary allocations. 62 */ 63static svn_error_t * |
189auto_parse(svn_config_t **cfg, 190 svn_membuf_t **key, 191 svn_repos__config_pool_t *config_pool, 192 svn_stringbuf_t *contents, 193 svn_boolean_t case_sensitive, 194 apr_pool_t *result_pool, 195 apr_pool_t *scratch_pool) | 64find_config(svn_config_t **cfg, 65 svn_repos__config_pool_t *config_pool, 66 svn_stream_t *stream, 67 svn_checksum_t *checksum, 68 apr_pool_t *result_pool, 69 apr_pool_t *scratch_pool) |
196{ | 70{ |
197 svn_checksum_t *checksum; 198 config_object_t *config_object; 199 apr_pool_t *cfg_pool; | 71 /* First, attempt the cache lookup. */ 72 svn_membuf_t *key = checksum_as_key(checksum, scratch_pool); 73 SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool, key, 74 result_pool)); |
200 | 75 |
201 /* calculate SHA1 over the whole file contents */ 202 SVN_ERR(svn_stream_close 203 (svn_stream_checksummed2 204 (svn_stream_from_stringbuf(contents, scratch_pool), 205 &checksum, NULL, svn_checksum_sha1, TRUE, scratch_pool))); 206 207 /* return reference to suitable config object if that already exists */ 208 *key = checksum_as_key(checksum, result_pool); 209 SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool->object_pool, 210 *key, &case_sensitive, result_pool)); 211 if (*cfg) 212 return SVN_NO_ERROR; 213 214 /* create a pool for the new config object and parse the data into it */ 215 cfg_pool = svn_object_pool__new_wrapper_pool(config_pool->object_pool); 216 217 config_object = apr_pcalloc(cfg_pool, sizeof(*config_object)); 218 219 SVN_ERR(svn_config_parse(case_sensitive ? &config_object->cs_cfg 220 : &config_object->ci_cfg, 221 svn_stream_from_stringbuf(contents, scratch_pool), 222 case_sensitive, case_sensitive, cfg_pool)); 223 224 /* switch config data to r/o mode to guarantee thread-safe access */ 225 svn_config__set_read_only(case_sensitive ? config_object->cs_cfg 226 : config_object->ci_cfg, 227 cfg_pool); 228 229 /* add config in pool, handle loads races and return the right config */ 230 SVN_ERR(svn_object_pool__insert((void **)cfg, config_pool->object_pool, 231 *key, config_object, &case_sensitive, 232 cfg_pool, result_pool)); 233 234 return SVN_NO_ERROR; 235} 236 237/* Store a URL@REVISION to CHECKSUM, REPOS_ROOT in CONFIG_POOL. 238 */ 239static svn_error_t * 240add_checksum(svn_repos__config_pool_t *config_pool, 241 const char *url, 242 const char *repos_root, 243 svn_revnum_t revision, 244 svn_checksum_t *checksum) 245{ 246 apr_size_t path_len = strlen(url); 247 apr_pool_t *pool = config_pool->in_repo_hash_pool; 248 in_repo_config_t *config = apr_hash_get(config_pool->in_repo_configs, 249 url, path_len); 250 if (config) 251 { 252 /* update the existing entry */ 253 memcpy((void *)config->key->digest, checksum->digest, 254 svn_checksum_size(checksum)); 255 config->revision = revision; 256 257 /* duplicate the string only if necessary */ 258 if (strcmp(config->repo_root, repos_root)) 259 config->repo_root = apr_pstrdup(pool, repos_root); 260 } 261 else 262 { 263 /* insert a new entry. 264 * Limit memory consumption by cyclically clearing pool and hash. */ 265 if (2 * svn_object_pool__count(config_pool->object_pool) 266 < apr_hash_count(config_pool->in_repo_configs)) 267 { 268 svn_pool_clear(pool); 269 config_pool->in_repo_configs = svn_hash__make(pool); 270 } 271 272 /* construct the new entry */ 273 config = apr_pcalloc(pool, sizeof(*config)); 274 config->key = svn_checksum_dup(checksum, pool); 275 config->url = apr_pstrmemdup(pool, url, path_len); 276 config->repo_root = apr_pstrdup(pool, repos_root); 277 config->revision = revision; 278 279 /* add to index */ 280 apr_hash_set(config_pool->in_repo_configs, url, path_len, config); 281 } 282 283 return SVN_NO_ERROR; 284} 285 286/* Set *CFG to the configuration stored in URL@HEAD and cache it in 287 * CONFIG_POOL. CASE_SENSITIVE controls 288 * option and section name matching. If PREFERRED_REPOS is given, 289 * use that if it also matches URL. 290 * 291 * RESULT_POOL determines the lifetime of the returned reference and 292 * SCRATCH_POOL is being used for temporary allocations. 293 */ 294static svn_error_t * 295find_repos_config(svn_config_t **cfg, 296 svn_membuf_t **key, 297 svn_repos__config_pool_t *config_pool, 298 const char *url, 299 svn_boolean_t case_sensitive, 300 svn_repos_t *preferred_repos, 301 apr_pool_t *result_pool, 302 apr_pool_t *scratch_pool) 303{ 304 svn_repos_t *repos = NULL; 305 svn_fs_t *fs; 306 svn_fs_root_t *root; 307 svn_revnum_t youngest_rev; 308 svn_node_kind_t node_kind; 309 const char *dirent; 310 svn_stream_t *stream; 311 const char *fs_path; 312 const char *repos_root_dirent; 313 svn_checksum_t *checksum; 314 svn_stringbuf_t *contents; 315 316 *cfg = NULL; 317 SVN_ERR(svn_uri_get_dirent_from_file_url(&dirent, url, scratch_pool)); 318 319 /* maybe we can use the preferred repos instance instead of creating a 320 * new one */ 321 if (preferred_repos) 322 { 323 repos_root_dirent = svn_repos_path(preferred_repos, scratch_pool); 324 if (!svn_dirent_is_absolute(repos_root_dirent)) 325 SVN_ERR(svn_dirent_get_absolute(&repos_root_dirent, 326 repos_root_dirent, 327 scratch_pool)); 328 329 if (svn_dirent_is_ancestor(repos_root_dirent, dirent)) 330 repos = preferred_repos; 331 } 332 333 /* open repos if no suitable preferred repos was provided. */ 334 if (!repos) 335 { 336 /* Search for a repository in the full path. */ 337 repos_root_dirent = svn_repos_find_root_path(dirent, scratch_pool); 338 339 /* Attempt to open a repository at repos_root_dirent. */ 340 SVN_ERR(svn_repos_open3(&repos, repos_root_dirent, NULL, 341 scratch_pool, scratch_pool)); 342 } 343 344 fs_path = &dirent[strlen(repos_root_dirent)]; 345 346 /* Get the filesystem. */ 347 fs = svn_repos_fs(repos); 348 349 /* Find HEAD and the revision root */ 350 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, scratch_pool)); 351 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, scratch_pool)); 352 353 /* Fetch checksum and see whether we already have a matching config */ 354 SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, root, fs_path, 355 FALSE, scratch_pool)); 356 if (checksum) 357 { 358 *key = checksum_as_key(checksum, scratch_pool); 359 SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool->object_pool, 360 *key, &case_sensitive, result_pool)); 361 } 362 363 /* not parsed, yet? */ | 76 /* Not found? => parse and cache */ |
364 if (!*cfg) 365 { | 77 if (!*cfg) 78 { |
366 svn_filesize_t length; | 79 svn_config_t *config; |
367 | 80 |
368 /* fetch the file contents */ 369 SVN_ERR(svn_fs_check_path(&node_kind, root, fs_path, scratch_pool)); 370 if (node_kind != svn_node_file) 371 return SVN_NO_ERROR; | 81 /* create a pool for the new config object and parse the data into it */ 82 apr_pool_t *cfg_pool = svn_object_pool__new_item_pool(config_pool); 83 SVN_ERR(svn_config_parse(&config, stream, FALSE, FALSE, cfg_pool)); |
372 | 84 |
373 SVN_ERR(svn_fs_file_length(&length, root, fs_path, scratch_pool)); 374 SVN_ERR(svn_fs_file_contents(&stream, root, fs_path, scratch_pool)); 375 SVN_ERR(svn_stringbuf_from_stream(&contents, stream, 376 (apr_size_t)length, scratch_pool)); | 85 /* switch config data to r/o mode to guarantee thread-safe access */ 86 svn_config__set_read_only(config, cfg_pool); |
377 | 87 |
378 /* handle it like ordinary file contents and cache it */ 379 SVN_ERR(auto_parse(cfg, key, config_pool, contents, case_sensitive, 380 result_pool, scratch_pool)); | 88 /* add config in pool, handle loads races and return the right config */ 89 SVN_ERR(svn_object_pool__insert((void **)cfg, config_pool, key, 90 config, cfg_pool, result_pool)); |
381 } 382 | 91 } 92 |
383 /* store the (path,rev) -> checksum mapping as well */ 384 if (*cfg && checksum) 385 SVN_MUTEX__WITH_LOCK(svn_object_pool__mutex(config_pool->object_pool), 386 add_checksum(config_pool, url, repos_root_dirent, 387 youngest_rev, checksum)); 388 | |
389 return SVN_NO_ERROR; 390} 391 | 93 return SVN_NO_ERROR; 94} 95 |
392/* Given the URL, search the CONFIG_POOL for an entry that maps it URL to 393 * a content checksum and is still up-to-date. If this could be found, 394 * return the object's *KEY. Use POOL for allocations. 395 * 396 * Requires external serialization on CONFIG_POOL. 397 * 398 * Note that this is only the URL(+rev) -> Checksum lookup and does not 399 * guarantee that there is actually a config object available for *KEY. 400 */ 401static svn_error_t * 402key_by_url(svn_membuf_t **key, 403 svn_repos__config_pool_t *config_pool, 404 const char *url, 405 apr_pool_t *pool) 406{ 407 svn_error_t *err; 408 svn_stringbuf_t *contents; 409 apr_int64_t current; 410 411 /* hash lookup url -> sha1 -> config */ 412 in_repo_config_t *config = svn_hash_gets(config_pool->in_repo_configs, url); 413 *key = NULL; 414 if (!config) 415 return SVN_NO_ERROR; 416 417 /* found *some* reference to a configuration. 418 * Verify that it is still current. Will fail for BDB repos. */ 419 err = svn_stringbuf_from_file2(&contents, 420 svn_dirent_join(config->repo_root, 421 "db/current", pool), 422 pool); 423 if (!err) 424 err = svn_cstring_atoi64(¤t, contents->data); 425 426 if (err) 427 svn_error_clear(err); 428 else if (current == config->revision) 429 *key = checksum_as_key(config->key, pool); 430 431 return SVN_NO_ERROR; 432} 433 | |
434/* API implementation */ 435 436svn_error_t * 437svn_repos__config_pool_create(svn_repos__config_pool_t **config_pool, 438 svn_boolean_t thread_safe, 439 apr_pool_t *pool) 440{ | 96/* API implementation */ 97 98svn_error_t * 99svn_repos__config_pool_create(svn_repos__config_pool_t **config_pool, 100 svn_boolean_t thread_safe, 101 apr_pool_t *pool) 102{ |
441 svn_repos__config_pool_t *result; 442 svn_object_pool__t *object_pool; 443 444 SVN_ERR(svn_object_pool__create(&object_pool, getter, setter, 445 thread_safe, pool)); 446 447 /* construct the config pool in our private ROOT_POOL to survive POOL 448 * cleanup and to prevent threading issues with the allocator */ 449 result = apr_pcalloc(pool, sizeof(*result)); 450 451 result->object_pool = object_pool; 452 result->in_repo_hash_pool = svn_pool_create(pool); 453 result->in_repo_configs = svn_hash__make(result->in_repo_hash_pool); 454 455 *config_pool = result; 456 return SVN_NO_ERROR; | 103 return svn_error_trace(svn_object_pool__create(config_pool, 104 thread_safe, pool)); |
457} 458 459svn_error_t * 460svn_repos__config_pool_get(svn_config_t **cfg, | 105} 106 107svn_error_t * 108svn_repos__config_pool_get(svn_config_t **cfg, |
461 svn_membuf_t **key, | |
462 svn_repos__config_pool_t *config_pool, 463 const char *path, 464 svn_boolean_t must_exist, | 109 svn_repos__config_pool_t *config_pool, 110 const char *path, 111 svn_boolean_t must_exist, |
465 svn_boolean_t case_sensitive, | |
466 svn_repos_t *preferred_repos, 467 apr_pool_t *pool) 468{ 469 svn_error_t *err = SVN_NO_ERROR; 470 apr_pool_t *scratch_pool = svn_pool_create(pool); | 112 svn_repos_t *preferred_repos, 113 apr_pool_t *pool) 114{ 115 svn_error_t *err = SVN_NO_ERROR; 116 apr_pool_t *scratch_pool = svn_pool_create(pool); |
117 config_access_t *access = svn_repos__create_config_access(preferred_repos, 118 scratch_pool); 119 svn_stream_t *stream; 120 svn_checksum_t *checksum; |
|
471 | 121 |
472 /* make sure we always have a *KEY object */ 473 svn_membuf_t *local_key = NULL; 474 if (key == NULL) 475 key = &local_key; 476 else 477 *key = NULL; | 122 *cfg = NULL; 123 err = svn_repos__get_config(&stream, &checksum, access, path, must_exist, 124 scratch_pool); 125 if (!err) 126 err = svn_error_quick_wrapf(find_config(cfg, config_pool, stream, 127 checksum, pool, scratch_pool), 128 "Error while parsing config file: '%s':", 129 path); |
478 | 130 |
479 if (svn_path_is_url(path)) | 131 /* Let the standard implementation handle all the difficult cases. 132 * Note that for in-repo configs, there are no further special cases to 133 * check for and deal with. */ 134 if (!*cfg && !svn_path_is_url(path)) |
480 { | 135 { |
481 /* Read config file from repository. 482 * Attempt a quick lookup first. */ 483 SVN_MUTEX__WITH_LOCK(svn_object_pool__mutex(config_pool->object_pool), 484 key_by_url(key, config_pool, path, pool)); 485 if (*key) 486 { 487 SVN_ERR(svn_object_pool__lookup((void **)cfg, 488 config_pool->object_pool, 489 *key, &case_sensitive, pool)); 490 if (*cfg) 491 { 492 svn_pool_destroy(scratch_pool); 493 return SVN_NO_ERROR; 494 } 495 } 496 497 /* Read and cache the configuration. This may fail. */ 498 err = find_repos_config(cfg, key, config_pool, path, case_sensitive, 499 preferred_repos, pool, scratch_pool); 500 if (err || !*cfg) 501 { 502 /* let the standard implementation handle all the difficult cases */ 503 svn_error_clear(err); 504 err = svn_repos__retrieve_config(cfg, path, must_exist, 505 case_sensitive, pool); 506 } | 136 svn_error_clear(err); 137 err = svn_config_read3(cfg, path, must_exist, FALSE, FALSE, pool); |
507 } | 138 } |
508 else 509 { 510 /* Outside of repo file. Read it. */ 511 svn_stringbuf_t *contents; 512 err = svn_stringbuf_from_file2(&contents, path, scratch_pool); 513 if (err) 514 { 515 /* let the standard implementation handle all the difficult cases */ 516 svn_error_clear(err); 517 err = svn_config_read3(cfg, path, must_exist, case_sensitive, 518 case_sensitive, pool); 519 } 520 else 521 { 522 /* parsing and caching will always succeed */ 523 err = auto_parse(cfg, key, config_pool, contents, case_sensitive, 524 pool, scratch_pool); 525 } 526 } | |
527 | 139 |
140 svn_repos__destroy_config_access(access); |
|
528 svn_pool_destroy(scratch_pool); 529 | 141 svn_pool_destroy(scratch_pool); 142 |
530 return err; | 143 /* we need to duplicate the root structure as it contains temp. buffers */ 144 if (*cfg) 145 *cfg = svn_config__shallow_copy(*cfg, pool); 146 147 return svn_error_trace(err); |
531} | 148} |