1251881Speter/* env.h : managing the BDB environment 2251881Speter * 3251881Speter * ==================================================================== 4251881Speter * Licensed to the Apache Software Foundation (ASF) under one 5251881Speter * or more contributor license agreements. See the NOTICE file 6251881Speter * distributed with this work for additional information 7251881Speter * regarding copyright ownership. The ASF licenses this file 8251881Speter * to you under the Apache License, Version 2.0 (the 9251881Speter * "License"); you may not use this file except in compliance 10251881Speter * with the License. You may obtain a copy of the License at 11251881Speter * 12251881Speter * http://www.apache.org/licenses/LICENSE-2.0 13251881Speter * 14251881Speter * Unless required by applicable law or agreed to in writing, 15251881Speter * software distributed under the License is distributed on an 16251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17251881Speter * KIND, either express or implied. See the License for the 18251881Speter * specific language governing permissions and limitations 19251881Speter * under the License. 20251881Speter * ==================================================================== 21251881Speter */ 22251881Speter 23251881Speter#include <assert.h> 24251881Speter 25251881Speter#include <apr.h> 26251881Speter#if APR_HAS_THREADS 27251881Speter#include <apr_thread_proc.h> 28251881Speter#include <apr_time.h> 29251881Speter#endif 30251881Speter 31251881Speter#include <apr_strings.h> 32251881Speter#include <apr_hash.h> 33251881Speter 34251881Speter#include "svn_hash.h" 35251881Speter#include "svn_path.h" 36251881Speter#include "svn_pools.h" 37251881Speter#include "svn_utf.h" 38251881Speter#include "private/svn_atomic.h" 39251881Speter#include "private/svn_mutex.h" 40251881Speter 41251881Speter#include "bdb-err.h" 42251881Speter#include "bdb_compat.h" 43251881Speter 44251881Speter#include "env.h" 45251881Speter 46251881Speter/* A note about the BDB environment descriptor cache. 47251881Speter 48251881Speter With the advent of DB_REGISTER in BDB-4.4, a process may only open 49251881Speter an environment handle once. This means that we must maintain a 50251881Speter cache of open environment handles, with reference counts. We 51251881Speter allocate each environment descriptor (a bdb_env_t) from its own 52251881Speter pool. The cache itself (and the cache pool) are shared between 53251881Speter threads, so all direct or indirect access to the pool is serialized 54251881Speter with a global mutex. 55251881Speter 56251881Speter Because several threads can now use the same DB_ENV handle, we must 57251881Speter use the DB_THREAD flag when opening the environments, otherwise the 58251881Speter env handles (and all of libsvn_fs_base) won't be thread-safe. 59251881Speter 60251881Speter If we use DB_THREAD, however, all of the code that reads data from 61251881Speter the database without a cursor must use either DB_DBT_MALLOC, 62251881Speter DB_DBT_REALLOC, or DB_DBT_USERMEM, as described in the BDB 63251881Speter documentation. 64251881Speter 65251881Speter (Oh, yes -- using DB_THREAD might not work on some systems. But 66251881Speter then, it's quite probable that threading is seriously broken on 67251881Speter those systems anyway, so we'll rely on APR_HAS_THREADS.) 68251881Speter*/ 69251881Speter 70251881Speter 71251881Speter/* The cache key for a Berkeley DB environment descriptor. This is a 72251881Speter combination of the device ID and INODE number of the Berkeley DB 73251881Speter config file. 74251881Speter 75251881Speter XXX FIXME: Although the dev+inode combination is supposed do be 76251881Speter unique, apparently that's not always the case with some remote 77251881Speter filesystems. We /should/ be safe using this as a unique hash key, 78251881Speter because the database must be on a local filesystem. We can hope, 79251881Speter anyway. */ 80251881Spetertypedef struct bdb_env_key_t 81251881Speter{ 82251881Speter apr_dev_t device; 83251881Speter apr_ino_t inode; 84251881Speter} bdb_env_key_t; 85251881Speter 86251881Speter/* The cached Berkeley DB environment descriptor. */ 87251881Speterstruct bdb_env_t 88251881Speter{ 89251881Speter /**************************************************************************/ 90251881Speter /* Error Reporting */ 91251881Speter 92251881Speter /* A (char *) casted pointer to this structure is passed to BDB's 93251881Speter set_errpfx(), which treats it as a NUL-terminated character 94251881Speter string to prefix all BDB error messages. However, svn also 95251881Speter registers bdb_error_gatherer() as an error handler with 96251881Speter set_errcall() which turns off BDB's default printing of errors to 97251881Speter stderr and anytime thereafter when BDB reports an error and 98251881Speter before the BDB function returns, it calls bdb_error_gatherer() 99251881Speter and passes the same error prefix (char *) pointer given to 100251881Speter set_errpfx(). The bdb_error_gatherer() callback casts the 101251881Speter (char *) it back to a (bdb_env_t *). 102251881Speter 103251881Speter To avoid problems should BDB ever try to interpret our baton as a 104251881Speter string, the first field in the structure is a char 105251881Speter errpfx_string[]. Initializers of this structure must strcpy the 106251881Speter value of BDB_ERRPFX_STRING into this array. */ 107251881Speter char errpfx_string[sizeof(BDB_ERRPFX_STRING)]; 108251881Speter 109251881Speter /* Extended error information. */ 110251881Speter#if APR_HAS_THREADS 111251881Speter apr_threadkey_t *error_info; /* Points to a bdb_error_info_t. */ 112251881Speter#else 113251881Speter bdb_error_info_t error_info; 114251881Speter#endif 115251881Speter 116251881Speter /**************************************************************************/ 117251881Speter /* BDB Environment Cache */ 118251881Speter 119251881Speter /* The Berkeley DB environment. */ 120251881Speter DB_ENV *env; 121251881Speter 122251881Speter /* The flags with which this environment was opened. Reopening the 123251881Speter environment with a different set of flags is not allowed. Trying 124251881Speter to change the state of the DB_PRIVATE flag is an especially bad 125251881Speter idea, so svn_fs_bdb__open() forbids any flag changes. */ 126251881Speter u_int32_t flags; 127251881Speter 128251881Speter /* The home path of this environment; a canonical SVN path encoded in 129251881Speter UTF-8 and allocated from this decriptor's pool. */ 130251881Speter const char *path; 131251881Speter 132251881Speter /* The home path of this environment, in the form expected by BDB. */ 133251881Speter const char *path_bdb; 134251881Speter 135251881Speter /* The reference count for this environment handle; this is 136251881Speter essentially the difference between the number of calls to 137251881Speter svn_fs_bdb__open and svn_fs_bdb__close. */ 138251881Speter unsigned refcount; 139251881Speter 140251881Speter /* If this flag is TRUE, someone has detected that the environment 141251881Speter descriptor is in a panicked state and should be removed from the 142251881Speter cache. 143251881Speter 144251881Speter Note 1: Once this flag is set, it must not be cleared again. 145251881Speter 146251881Speter Note 2: Unlike other fields in this structure, this field is not 147251881Speter protected by the cache mutex on threaded platforms, and 148251881Speter should only be accesses via the svn_atomic functions. */ 149251881Speter volatile svn_atomic_t panic; 150251881Speter 151251881Speter /* The key for the environment descriptor cache. */ 152251881Speter bdb_env_key_t key; 153251881Speter 154251881Speter /* The handle of the open DB_CONFIG file. 155251881Speter 156251881Speter We keep the DB_CONFIG file open in this process as long as the 157251881Speter environment handle itself is open. On Windows, this guarantees 158251881Speter that the cache key remains unique; here's what the Windows SDK 159251881Speter docs have to say about the file index (interpreted as the INODE 160251881Speter number by APR): 161251881Speter 162251881Speter "This value is useful only while the file is open by at least 163251881Speter one process. If no processes have it open, the index may 164251881Speter change the next time the file is opened." 165251881Speter 166251881Speter Now, we certainly don't want a unique key to change while it's 167251881Speter being used, do we... */ 168251881Speter apr_file_t *dbconfig_file; 169251881Speter 170251881Speter /* The pool associated with this environment descriptor. 171251881Speter 172251881Speter Because the descriptor has a life of its own, the structure and 173251881Speter any data associated with it are allocated from their own global 174251881Speter pool. */ 175251881Speter apr_pool_t *pool; 176251881Speter 177251881Speter}; 178251881Speter 179251881Speter 180251881Speter#if APR_HAS_THREADS 181251881Speter/* Get the thread-specific error info from a bdb_env_t. */ 182251881Speterstatic bdb_error_info_t * 183251881Speterget_error_info(const bdb_env_t *bdb) 184251881Speter{ 185251881Speter void *priv; 186251881Speter apr_threadkey_private_get(&priv, bdb->error_info); 187251881Speter if (!priv) 188251881Speter { 189251881Speter priv = calloc(1, sizeof(bdb_error_info_t)); 190251881Speter apr_threadkey_private_set(priv, bdb->error_info); 191251881Speter } 192251881Speter return priv; 193251881Speter} 194251881Speter#else 195251881Speter#define get_error_info(bdb) (&(bdb)->error_info) 196251881Speter#endif /* APR_HAS_THREADS */ 197251881Speter 198251881Speter 199251881Speter/* Convert a BDB error to a Subversion error. */ 200251881Speterstatic svn_error_t * 201251881Speterconvert_bdb_error(bdb_env_t *bdb, int db_err) 202251881Speter{ 203251881Speter if (db_err) 204251881Speter { 205251881Speter bdb_env_baton_t bdb_baton; 206251881Speter bdb_baton.env = bdb->env; 207251881Speter bdb_baton.bdb = bdb; 208251881Speter bdb_baton.error_info = get_error_info(bdb); 209251881Speter SVN_BDB_ERR(&bdb_baton, db_err); 210251881Speter } 211251881Speter return SVN_NO_ERROR; 212251881Speter} 213251881Speter 214251881Speter 215251881Speter/* Allocating an appropriate Berkeley DB environment object. */ 216251881Speter 217251881Speter/* BDB error callback. See bdb_error_info_t in env.h for more info. 218251881Speter Note: bdb_error_gatherer is a macro with BDB < 4.3, so be careful how 219251881Speter you use it! */ 220251881Speterstatic void 221251881Speterbdb_error_gatherer(const DB_ENV *dbenv, const char *baton, const char *msg) 222251881Speter{ 223251881Speter /* See the documentation at bdb_env_t's definition why the 224251881Speter (bdb_env_t *) cast is safe and why it is done. */ 225251881Speter bdb_error_info_t *error_info = get_error_info((const bdb_env_t *) baton); 226251881Speter svn_error_t *new_err; 227251881Speter 228251881Speter SVN_BDB_ERROR_GATHERER_IGNORE(dbenv); 229251881Speter 230253734Speter new_err = svn_error_createf(SVN_ERR_FS_BERKELEY_DB, NULL, "bdb: %s", msg); 231251881Speter if (error_info->pending_errors) 232251881Speter svn_error_compose(error_info->pending_errors, new_err); 233251881Speter else 234251881Speter error_info->pending_errors = new_err; 235251881Speter 236251881Speter if (error_info->user_callback) 237251881Speter error_info->user_callback(NULL, (char *)msg); /* ### I hate this cast... */ 238251881Speter} 239251881Speter 240251881Speter 241251881Speter/* Pool cleanup for the cached environment descriptor. */ 242251881Speterstatic apr_status_t 243251881Spetercleanup_env(void *data) 244251881Speter{ 245251881Speter bdb_env_t *bdb = data; 246251881Speter bdb->pool = NULL; 247251881Speter bdb->dbconfig_file = NULL; /* will be closed during pool destruction */ 248251881Speter#if APR_HAS_THREADS 249251881Speter apr_threadkey_private_delete(bdb->error_info); 250251881Speter#endif /* APR_HAS_THREADS */ 251251881Speter 252251881Speter /* If there are no references to this descriptor, free its memory here, 253251881Speter so that we don't leak it if create_env returns an error. 254251881Speter See bdb_close, which takes care of freeing this memory if the 255251881Speter environment is still open when the cache is destroyed. */ 256251881Speter if (!bdb->refcount) 257251881Speter free(data); 258251881Speter 259251881Speter return APR_SUCCESS; 260251881Speter} 261251881Speter 262251881Speter#if APR_HAS_THREADS 263251881Speter/* This cleanup is the fall back plan. If the thread exits and the 264251881Speter environment hasn't been closed it's responsible for cleanup of the 265251881Speter thread local error info variable, which would otherwise be leaked. 266251881Speter Normally it will not be called, because svn_fs_bdb__close will 267251881Speter set the thread's error info to NULL after cleaning it up. */ 268251881Speterstatic void 269251881Spetercleanup_error_info(void *baton) 270251881Speter{ 271251881Speter bdb_error_info_t *error_info = baton; 272251881Speter 273251881Speter if (error_info) 274251881Speter svn_error_clear(error_info->pending_errors); 275251881Speter 276251881Speter free(error_info); 277251881Speter} 278251881Speter#endif /* APR_HAS_THREADS */ 279251881Speter 280251881Speter/* Create a Berkeley DB environment. */ 281251881Speterstatic svn_error_t * 282251881Spetercreate_env(bdb_env_t **bdbp, const char *path, apr_pool_t *pool) 283251881Speter{ 284251881Speter int db_err; 285251881Speter bdb_env_t *bdb; 286251881Speter const char *path_bdb; 287251881Speter char *tmp_path, *tmp_path_bdb; 288251881Speter apr_size_t path_size, path_bdb_size; 289251881Speter 290251881Speter#if SVN_BDB_PATH_UTF8 291251881Speter path_bdb = svn_dirent_local_style(path, pool); 292251881Speter#else 293251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&path_bdb, 294251881Speter svn_dirent_local_style(path, pool), 295251881Speter pool)); 296251881Speter#endif 297251881Speter 298251881Speter /* Allocate the whole structure, including strings, from the heap, 299251881Speter because it must survive the cache pool cleanup. */ 300251881Speter path_size = strlen(path) + 1; 301251881Speter path_bdb_size = strlen(path_bdb) + 1; 302251881Speter /* Using calloc() to ensure the padding bytes in bdb->key (which is used as 303251881Speter * a hash key) are zeroed. */ 304251881Speter bdb = calloc(1, sizeof(*bdb) + path_size + path_bdb_size); 305251881Speter 306251881Speter /* We must initialize this now, as our callers may assume their bdb 307251881Speter pointer is valid when checking for errors. */ 308251881Speter apr_pool_cleanup_register(pool, bdb, cleanup_env, apr_pool_cleanup_null); 309251881Speter apr_cpystrn(bdb->errpfx_string, BDB_ERRPFX_STRING, 310251881Speter sizeof(bdb->errpfx_string)); 311251881Speter bdb->path = tmp_path = (char*)(bdb + 1); 312251881Speter bdb->path_bdb = tmp_path_bdb = tmp_path + path_size; 313251881Speter apr_cpystrn(tmp_path, path, path_size); 314251881Speter apr_cpystrn(tmp_path_bdb, path_bdb, path_bdb_size); 315251881Speter bdb->pool = pool; 316251881Speter *bdbp = bdb; 317251881Speter 318251881Speter#if APR_HAS_THREADS 319251881Speter { 320251881Speter apr_status_t apr_err = apr_threadkey_private_create(&bdb->error_info, 321251881Speter cleanup_error_info, 322251881Speter pool); 323251881Speter if (apr_err) 324251881Speter return svn_error_create(apr_err, NULL, 325251881Speter "Can't allocate thread-specific storage" 326251881Speter " for the Berkeley DB environment descriptor"); 327251881Speter } 328251881Speter#endif /* APR_HAS_THREADS */ 329251881Speter 330251881Speter db_err = db_env_create(&(bdb->env), 0); 331251881Speter if (!db_err) 332251881Speter { 333251881Speter /* See the documentation at bdb_env_t's definition why the 334251881Speter (char *) cast is safe and why it is done. */ 335251881Speter bdb->env->set_errpfx(bdb->env, (char *) bdb); 336251881Speter 337251881Speter /* bdb_error_gatherer is in parens to stop macro expansion. */ 338251881Speter bdb->env->set_errcall(bdb->env, (bdb_error_gatherer)); 339251881Speter 340251881Speter /* Needed on Windows in case Subversion and Berkeley DB are using 341251881Speter different C runtime libraries */ 342251881Speter db_err = bdb->env->set_alloc(bdb->env, malloc, realloc, free); 343251881Speter 344251881Speter /* If we detect a deadlock, select a transaction to abort at 345251881Speter random from those participating in the deadlock. */ 346251881Speter if (!db_err) 347251881Speter db_err = bdb->env->set_lk_detect(bdb->env, DB_LOCK_RANDOM); 348251881Speter } 349251881Speter return convert_bdb_error(bdb, db_err); 350251881Speter} 351251881Speter 352251881Speter 353251881Speter 354251881Speter/* The environment descriptor cache. */ 355251881Speter 356251881Speter/* The global pool used for this cache. */ 357251881Speterstatic apr_pool_t *bdb_cache_pool = NULL; 358251881Speter 359251881Speter/* The cache. The items are bdb_env_t structures. */ 360251881Speterstatic apr_hash_t *bdb_cache = NULL; 361251881Speter 362251881Speter/* The mutex that protects bdb_cache. */ 363251881Speterstatic svn_mutex__t *bdb_cache_lock = NULL; 364251881Speter 365251881Speter/* Cleanup callback to NULL out the cache, so we don't try to use it after 366251881Speter the pool has been cleared during global shutdown. */ 367251881Speterstatic apr_status_t 368251881Speterclear_cache(void *data) 369251881Speter{ 370251881Speter bdb_cache = NULL; 371251881Speter bdb_cache_lock = NULL; 372251881Speter return APR_SUCCESS; 373251881Speter} 374251881Speter 375251881Speterstatic volatile svn_atomic_t bdb_cache_state = 0; 376251881Speter 377251881Speterstatic svn_error_t * 378251881Speterbdb_init_cb(void *baton, apr_pool_t *pool) 379251881Speter{ 380251881Speter bdb_cache_pool = svn_pool_create(pool); 381251881Speter bdb_cache = apr_hash_make(bdb_cache_pool); 382251881Speter 383251881Speter SVN_ERR(svn_mutex__init(&bdb_cache_lock, TRUE, bdb_cache_pool)); 384251881Speter apr_pool_cleanup_register(bdb_cache_pool, NULL, clear_cache, 385251881Speter apr_pool_cleanup_null); 386251881Speter 387251881Speter return SVN_NO_ERROR; 388251881Speter} 389251881Speter 390251881Spetersvn_error_t * 391251881Spetersvn_fs_bdb__init(apr_pool_t* pool) 392251881Speter{ 393251881Speter return svn_atomic__init_once(&bdb_cache_state, bdb_init_cb, NULL, pool); 394251881Speter} 395251881Speter 396251881Speter/* Construct a cache key for the BDB environment at PATH in *KEYP. 397251881Speter if DBCONFIG_FILE is not NULL, return the opened file handle. 398251881Speter Allocate from POOL. */ 399251881Speterstatic svn_error_t * 400251881Speterbdb_cache_key(bdb_env_key_t *keyp, apr_file_t **dbconfig_file, 401251881Speter const char *path, apr_pool_t *pool) 402251881Speter{ 403251881Speter const char *dbcfg_file_name = svn_dirent_join(path, BDB_CONFIG_FILE, pool); 404251881Speter apr_file_t *dbcfg_file; 405251881Speter apr_status_t apr_err; 406251881Speter apr_finfo_t finfo; 407251881Speter 408251881Speter SVN_ERR(svn_io_file_open(&dbcfg_file, dbcfg_file_name, 409251881Speter APR_READ, APR_OS_DEFAULT, pool)); 410251881Speter 411251881Speter apr_err = apr_file_info_get(&finfo, APR_FINFO_DEV | APR_FINFO_INODE, 412251881Speter dbcfg_file); 413251881Speter if (apr_err) 414251881Speter return svn_error_wrap_apr 415251881Speter (apr_err, "Can't create BDB environment cache key"); 416251881Speter 417251881Speter /* Make sure that any padding in the key is always cleared, so that 418251881Speter the key's hash deterministic. */ 419251881Speter memset(keyp, 0, sizeof *keyp); 420251881Speter keyp->device = finfo.device; 421251881Speter keyp->inode = finfo.inode; 422251881Speter 423251881Speter if (dbconfig_file) 424251881Speter *dbconfig_file = dbcfg_file; 425251881Speter else 426251881Speter apr_file_close(dbcfg_file); 427251881Speter 428251881Speter return SVN_NO_ERROR; 429251881Speter} 430251881Speter 431251881Speter 432251881Speter/* Find a BDB environment in the cache. 433251881Speter Return the environment's panic state in *PANICP. 434251881Speter 435251881Speter Note: You MUST acquire the cache mutex before calling this function. 436251881Speter*/ 437251881Speterstatic bdb_env_t * 438251881Speterbdb_cache_get(const bdb_env_key_t *keyp, svn_boolean_t *panicp) 439251881Speter{ 440251881Speter bdb_env_t *bdb = apr_hash_get(bdb_cache, keyp, sizeof *keyp); 441251881Speter if (bdb && bdb->env) 442251881Speter { 443251881Speter *panicp = !!svn_atomic_read(&bdb->panic); 444251881Speter#if SVN_BDB_VERSION_AT_LEAST(4,2) 445251881Speter if (!*panicp) 446251881Speter { 447251881Speter u_int32_t flags; 448251881Speter if (bdb->env->get_flags(bdb->env, &flags) 449251881Speter || (flags & DB_PANIC_ENVIRONMENT)) 450251881Speter { 451251881Speter /* Something is wrong with the environment. */ 452251881Speter svn_atomic_set(&bdb->panic, TRUE); 453251881Speter *panicp = TRUE; 454251881Speter bdb = NULL; 455251881Speter } 456251881Speter } 457251881Speter#endif /* at least bdb-4.2 */ 458251881Speter } 459251881Speter else 460251881Speter { 461251881Speter *panicp = FALSE; 462251881Speter } 463251881Speter return bdb; 464251881Speter} 465251881Speter 466251881Speter 467251881Speter 468251881Speter/* Close and destroy a BDB environment descriptor. */ 469251881Speterstatic svn_error_t * 470251881Speterbdb_close(bdb_env_t *bdb) 471251881Speter{ 472251881Speter svn_error_t *err = SVN_NO_ERROR; 473251881Speter 474251881Speter /* This bit is delcate; we must propagate the error from 475251881Speter DB_ENV->close to the caller, and always destroy the pool. */ 476251881Speter int db_err = bdb->env->close(bdb->env, 0); 477251881Speter 478251881Speter /* If automatic database recovery is enabled, ignore DB_RUNRECOVERY 479251881Speter errors, since they're dealt with eventually by BDB itself. */ 480251881Speter if (db_err && (!SVN_BDB_AUTO_RECOVER || db_err != DB_RUNRECOVERY)) 481251881Speter err = convert_bdb_error(bdb, db_err); 482251881Speter 483251881Speter /* Free the environment descriptor. The pool cleanup will do this unless 484251881Speter the cache has already been destroyed. */ 485251881Speter if (bdb->pool) 486251881Speter svn_pool_destroy(bdb->pool); 487251881Speter else 488251881Speter free(bdb); 489251881Speter return svn_error_trace(err); 490251881Speter} 491251881Speter 492251881Speter 493251881Speterstatic svn_error_t * 494251881Spetersvn_fs_bdb__close_internal(bdb_env_t *bdb) 495251881Speter{ 496251881Speter svn_error_t *err = SVN_NO_ERROR; 497251881Speter 498251881Speter if (--bdb->refcount != 0) 499251881Speter { 500251881Speter /* If the environment is panicked and automatic recovery is not 501251881Speter enabled, return an appropriate error. */ 502251881Speter#if !SVN_BDB_AUTO_RECOVER 503251881Speter if (svn_atomic_read(&bdb->panic)) 504251881Speter err = svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, 505251881Speter db_strerror(DB_RUNRECOVERY)); 506251881Speter#endif 507251881Speter } 508251881Speter else 509251881Speter { 510251881Speter /* If the bdb cache has been set to NULL that means we are 511251881Speter shutting down, and the pool that holds the bdb cache has 512251881Speter already been destroyed, so accessing it here would be a Bad 513251881Speter Thing (tm) */ 514251881Speter if (bdb_cache) 515251881Speter apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, NULL); 516251881Speter err = bdb_close(bdb); 517251881Speter } 518251881Speter return svn_error_trace(err); 519251881Speter} 520251881Speter 521251881Spetersvn_error_t * 522251881Spetersvn_fs_bdb__close(bdb_env_baton_t *bdb_baton) 523251881Speter{ 524251881Speter bdb_env_t *bdb = bdb_baton->bdb; 525251881Speter 526251881Speter SVN_ERR_ASSERT(bdb_baton->env == bdb_baton->bdb->env); 527251881Speter SVN_ERR_ASSERT(bdb_baton->error_info->refcount > 0); 528251881Speter 529251881Speter /* Neutralize bdb_baton's pool cleanup to prevent double-close. See 530251881Speter cleanup_env_baton(). */ 531251881Speter bdb_baton->bdb = NULL; 532251881Speter 533251881Speter /* Note that we only bother with this cleanup if the pool is non-NULL, to 534251881Speter guard against potential races between this and the cleanup_env cleanup 535251881Speter callback. It's not clear if that can actually happen, but better safe 536251881Speter than sorry. */ 537251881Speter if (0 == --bdb_baton->error_info->refcount && bdb->pool) 538251881Speter { 539251881Speter svn_error_clear(bdb_baton->error_info->pending_errors); 540251881Speter#if APR_HAS_THREADS 541251881Speter free(bdb_baton->error_info); 542251881Speter apr_threadkey_private_set(NULL, bdb->error_info); 543251881Speter#endif 544251881Speter } 545251881Speter 546251881Speter /* This may run during final pool cleanup when the lock is NULL. */ 547251881Speter SVN_MUTEX__WITH_LOCK(bdb_cache_lock, svn_fs_bdb__close_internal(bdb)); 548251881Speter 549251881Speter return SVN_NO_ERROR; 550251881Speter} 551251881Speter 552251881Speter 553251881Speter 554251881Speter/* Open and initialize a BDB environment. */ 555251881Speterstatic svn_error_t * 556251881Speterbdb_open(bdb_env_t *bdb, u_int32_t flags, int mode) 557251881Speter{ 558251881Speter#if APR_HAS_THREADS 559251881Speter flags |= DB_THREAD; 560251881Speter#endif 561251881Speter SVN_ERR(convert_bdb_error 562251881Speter (bdb, (bdb->env->open)(bdb->env, bdb->path_bdb, flags, mode))); 563251881Speter 564251881Speter#if SVN_BDB_AUTO_COMMIT 565251881Speter /* Assert the BDB_AUTO_COMMIT flag on the opened environment. This 566251881Speter will force all operations on the environment (and handles that 567251881Speter are opened within the environment) to be transactional. */ 568251881Speter 569251881Speter SVN_ERR(convert_bdb_error 570251881Speter (bdb, bdb->env->set_flags(bdb->env, SVN_BDB_AUTO_COMMIT, 1))); 571251881Speter#endif 572251881Speter 573251881Speter return bdb_cache_key(&bdb->key, &bdb->dbconfig_file, 574251881Speter bdb->path, bdb->pool); 575251881Speter} 576251881Speter 577251881Speter 578251881Speter/* Pool cleanup for the environment baton. */ 579251881Speterstatic apr_status_t 580251881Spetercleanup_env_baton(void *data) 581251881Speter{ 582251881Speter bdb_env_baton_t *bdb_baton = data; 583251881Speter 584251881Speter if (bdb_baton->bdb) 585251881Speter svn_error_clear(svn_fs_bdb__close(bdb_baton)); 586251881Speter 587251881Speter return APR_SUCCESS; 588251881Speter} 589251881Speter 590251881Speter 591251881Speterstatic svn_error_t * 592251881Spetersvn_fs_bdb__open_internal(bdb_env_baton_t **bdb_batonp, 593251881Speter const char *path, 594251881Speter u_int32_t flags, int mode, 595251881Speter apr_pool_t *pool) 596251881Speter{ 597251881Speter bdb_env_key_t key; 598251881Speter bdb_env_t *bdb; 599251881Speter svn_boolean_t panic; 600251881Speter 601251881Speter /* We can safely discard the open DB_CONFIG file handle. If the 602251881Speter environment descriptor is in the cache, the key's immutability is 603251881Speter guaranteed. If it's not, we don't care if the key changes, 604251881Speter between here and the actual insertion of the newly-created 605251881Speter environment into the cache, because no other thread can touch the 606251881Speter cache in the meantime. */ 607251881Speter SVN_ERR(bdb_cache_key(&key, NULL, path, pool)); 608251881Speter 609251881Speter bdb = bdb_cache_get(&key, &panic); 610251881Speter if (panic) 611251881Speter return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, 612251881Speter db_strerror(DB_RUNRECOVERY)); 613251881Speter 614251881Speter /* Make sure that the environment's open flags haven't changed. */ 615251881Speter if (bdb && bdb->flags != flags) 616251881Speter { 617251881Speter /* Handle changes to the DB_PRIVATE flag specially */ 618251881Speter if ((flags ^ bdb->flags) & DB_PRIVATE) 619251881Speter { 620251881Speter if (flags & DB_PRIVATE) 621251881Speter return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, 622251881Speter "Reopening a public Berkeley DB" 623251881Speter " environment with private attributes"); 624251881Speter else 625251881Speter return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, 626251881Speter "Reopening a private Berkeley DB" 627251881Speter " environment with public attributes"); 628251881Speter } 629251881Speter 630251881Speter /* Otherwise return a generic "flags-mismatch" error. */ 631251881Speter return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, 632251881Speter "Reopening a Berkeley DB environment" 633251881Speter " with different attributes"); 634251881Speter } 635251881Speter 636251881Speter if (!bdb) 637251881Speter { 638251881Speter svn_error_t *err; 639251881Speter 640251881Speter SVN_ERR(create_env(&bdb, path, svn_pool_create(bdb_cache_pool))); 641251881Speter err = bdb_open(bdb, flags, mode); 642251881Speter if (err) 643251881Speter { 644251881Speter /* Clean up, and we can't do anything about returned errors. */ 645251881Speter svn_error_clear(bdb_close(bdb)); 646251881Speter return svn_error_trace(err); 647251881Speter } 648251881Speter 649251881Speter apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, bdb); 650251881Speter bdb->flags = flags; 651251881Speter bdb->refcount = 1; 652251881Speter } 653251881Speter else 654251881Speter { 655251881Speter ++bdb->refcount; 656251881Speter } 657251881Speter 658251881Speter *bdb_batonp = apr_palloc(pool, sizeof **bdb_batonp); 659251881Speter (*bdb_batonp)->env = bdb->env; 660251881Speter (*bdb_batonp)->bdb = bdb; 661251881Speter (*bdb_batonp)->error_info = get_error_info(bdb); 662251881Speter ++(*bdb_batonp)->error_info->refcount; 663251881Speter apr_pool_cleanup_register(pool, *bdb_batonp, cleanup_env_baton, 664251881Speter apr_pool_cleanup_null); 665251881Speter 666251881Speter return SVN_NO_ERROR; 667251881Speter} 668251881Speter 669251881Spetersvn_error_t * 670251881Spetersvn_fs_bdb__open(bdb_env_baton_t **bdb_batonp, const char *path, 671251881Speter u_int32_t flags, int mode, 672251881Speter apr_pool_t *pool) 673251881Speter{ 674251881Speter SVN_MUTEX__WITH_LOCK(bdb_cache_lock, 675251881Speter svn_fs_bdb__open_internal(bdb_batonp, 676251881Speter path, 677251881Speter flags, 678251881Speter mode, 679251881Speter pool)); 680251881Speter 681251881Speter return SVN_NO_ERROR; 682251881Speter} 683251881Speter 684251881Speter 685251881Spetersvn_boolean_t 686251881Spetersvn_fs_bdb__get_panic(bdb_env_baton_t *bdb_baton) 687251881Speter{ 688251881Speter /* An invalid baton is equivalent to a panicked environment; in both 689251881Speter cases, database cleanups should be skipped. */ 690251881Speter if (!bdb_baton->bdb) 691251881Speter return TRUE; 692251881Speter 693251881Speter assert(bdb_baton->env == bdb_baton->bdb->env); 694251881Speter return !!svn_atomic_read(&bdb_baton->bdb->panic); 695251881Speter} 696251881Speter 697251881Spetervoid 698251881Spetersvn_fs_bdb__set_panic(bdb_env_baton_t *bdb_baton) 699251881Speter{ 700251881Speter if (!bdb_baton->bdb) 701251881Speter return; 702251881Speter 703251881Speter assert(bdb_baton->env == bdb_baton->bdb->env); 704251881Speter svn_atomic_set(&bdb_baton->bdb->panic, TRUE); 705251881Speter} 706251881Speter 707251881Speter 708251881Speter/* This function doesn't actually open the environment, so it doesn't 709251881Speter have to look in the cache. Callers are supposed to own an 710251881Speter exclusive lock on the filesystem anyway. */ 711251881Spetersvn_error_t * 712251881Spetersvn_fs_bdb__remove(const char *path, apr_pool_t *pool) 713251881Speter{ 714251881Speter bdb_env_t *bdb; 715251881Speter 716251881Speter SVN_ERR(create_env(&bdb, path, pool)); 717251881Speter return convert_bdb_error 718251881Speter (bdb, bdb->env->remove(bdb->env, bdb->path_bdb, DB_FORCE)); 719251881Speter} 720