1 /* env.h : managing the BDB environment
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
27 #include <apr_thread_proc.h>
31 #include <apr_strings.h>
36 #include "svn_pools.h"
38 #include "private/svn_atomic.h"
39 #include "private/svn_mutex.h"
42 #include "bdb_compat.h"
46 /* A note about the BDB environment descriptor cache.
48 With the advent of DB_REGISTER in BDB-4.4, a process may only open
49 an environment handle once. This means that we must maintain a
50 cache of open environment handles, with reference counts. We
51 allocate each environment descriptor (a bdb_env_t) from its own
52 pool. The cache itself (and the cache pool) are shared between
53 threads, so all direct or indirect access to the pool is serialized
56 Because several threads can now use the same DB_ENV handle, we must
57 use the DB_THREAD flag when opening the environments, otherwise the
58 env handles (and all of libsvn_fs_base) won't be thread-safe.
60 If we use DB_THREAD, however, all of the code that reads data from
61 the database without a cursor must use either DB_DBT_MALLOC,
62 DB_DBT_REALLOC, or DB_DBT_USERMEM, as described in the BDB
65 (Oh, yes -- using DB_THREAD might not work on some systems. But
66 then, it's quite probable that threading is seriously broken on
67 those systems anyway, so we'll rely on APR_HAS_THREADS.)
71 /* The cache key for a Berkeley DB environment descriptor. This is a
72 combination of the device ID and INODE number of the Berkeley DB
75 XXX FIXME: Although the dev+inode combination is supposed do be
76 unique, apparently that's not always the case with some remote
77 filesystems. We /should/ be safe using this as a unique hash key,
78 because the database must be on a local filesystem. We can hope,
80 typedef struct bdb_env_key_t
86 /* The cached Berkeley DB environment descriptor. */
89 /**************************************************************************/
92 /* A (char *) casted pointer to this structure is passed to BDB's
93 set_errpfx(), which treats it as a NUL-terminated character
94 string to prefix all BDB error messages. However, svn also
95 registers bdb_error_gatherer() as an error handler with
96 set_errcall() which turns off BDB's default printing of errors to
97 stderr and anytime thereafter when BDB reports an error and
98 before the BDB function returns, it calls bdb_error_gatherer()
99 and passes the same error prefix (char *) pointer given to
100 set_errpfx(). The bdb_error_gatherer() callback casts the
101 (char *) it back to a (bdb_env_t *).
103 To avoid problems should BDB ever try to interpret our baton as a
104 string, the first field in the structure is a char
105 errpfx_string[]. Initializers of this structure must strcpy the
106 value of BDB_ERRPFX_STRING into this array. */
107 char errpfx_string[sizeof(BDB_ERRPFX_STRING)];
109 /* Extended error information. */
111 apr_threadkey_t *error_info; /* Points to a bdb_error_info_t. */
113 bdb_error_info_t error_info;
116 /**************************************************************************/
117 /* BDB Environment Cache */
119 /* The Berkeley DB environment. */
122 /* The flags with which this environment was opened. Reopening the
123 environment with a different set of flags is not allowed. Trying
124 to change the state of the DB_PRIVATE flag is an especially bad
125 idea, so svn_fs_bdb__open() forbids any flag changes. */
128 /* The home path of this environment; a canonical SVN path encoded in
129 UTF-8 and allocated from this decriptor's pool. */
132 /* The home path of this environment, in the form expected by BDB. */
133 const char *path_bdb;
135 /* The reference count for this environment handle; this is
136 essentially the difference between the number of calls to
137 svn_fs_bdb__open and svn_fs_bdb__close. */
140 /* If this flag is TRUE, someone has detected that the environment
141 descriptor is in a panicked state and should be removed from the
144 Note 1: Once this flag is set, it must not be cleared again.
146 Note 2: Unlike other fields in this structure, this field is not
147 protected by the cache mutex on threaded platforms, and
148 should only be accesses via the svn_atomic functions. */
149 volatile svn_atomic_t panic;
151 /* The key for the environment descriptor cache. */
154 /* The handle of the open DB_CONFIG file.
156 We keep the DB_CONFIG file open in this process as long as the
157 environment handle itself is open. On Windows, this guarantees
158 that the cache key remains unique; here's what the Windows SDK
159 docs have to say about the file index (interpreted as the INODE
162 "This value is useful only while the file is open by at least
163 one process. If no processes have it open, the index may
164 change the next time the file is opened."
166 Now, we certainly don't want a unique key to change while it's
167 being used, do we... */
168 apr_file_t *dbconfig_file;
170 /* The pool associated with this environment descriptor.
172 Because the descriptor has a life of its own, the structure and
173 any data associated with it are allocated from their own global
181 /* Get the thread-specific error info from a bdb_env_t. */
182 static bdb_error_info_t *
183 get_error_info(const bdb_env_t *bdb)
186 apr_threadkey_private_get(&priv, bdb->error_info);
189 priv = calloc(1, sizeof(bdb_error_info_t));
190 apr_threadkey_private_set(priv, bdb->error_info);
195 #define get_error_info(bdb) (&(bdb)->error_info)
196 #endif /* APR_HAS_THREADS */
199 /* Convert a BDB error to a Subversion error. */
201 convert_bdb_error(bdb_env_t *bdb, int db_err)
205 bdb_env_baton_t bdb_baton;
206 bdb_baton.env = bdb->env;
208 bdb_baton.error_info = get_error_info(bdb);
209 SVN_BDB_ERR(&bdb_baton, db_err);
215 /* Allocating an appropriate Berkeley DB environment object. */
217 /* BDB error callback. See bdb_error_info_t in env.h for more info.
218 Note: bdb_error_gatherer is a macro with BDB < 4.3, so be careful how
221 bdb_error_gatherer(const DB_ENV *dbenv, const char *baton, const char *msg)
223 /* See the documentation at bdb_env_t's definition why the
224 (bdb_env_t *) cast is safe and why it is done. */
225 bdb_error_info_t *error_info = get_error_info((const bdb_env_t *) baton);
226 svn_error_t *new_err;
228 SVN_BDB_ERROR_GATHERER_IGNORE(dbenv);
230 new_err = svn_error_createf(SVN_ERR_FS_BERKELEY_DB, NULL, "bdb: %s", msg);
231 if (error_info->pending_errors)
232 svn_error_compose(error_info->pending_errors, new_err);
234 error_info->pending_errors = new_err;
236 if (error_info->user_callback)
237 error_info->user_callback(NULL, (char *)msg); /* ### I hate this cast... */
241 /* Pool cleanup for the cached environment descriptor. */
243 cleanup_env(void *data)
245 bdb_env_t *bdb = data;
247 bdb->dbconfig_file = NULL; /* will be closed during pool destruction */
249 apr_threadkey_private_delete(bdb->error_info);
250 #endif /* APR_HAS_THREADS */
252 /* If there are no references to this descriptor, free its memory here,
253 so that we don't leak it if create_env returns an error.
254 See bdb_close, which takes care of freeing this memory if the
255 environment is still open when the cache is destroyed. */
263 /* This cleanup is the fall back plan. If the thread exits and the
264 environment hasn't been closed it's responsible for cleanup of the
265 thread local error info variable, which would otherwise be leaked.
266 Normally it will not be called, because svn_fs_bdb__close will
267 set the thread's error info to NULL after cleaning it up. */
269 cleanup_error_info(void *baton)
271 bdb_error_info_t *error_info = baton;
274 svn_error_clear(error_info->pending_errors);
278 #endif /* APR_HAS_THREADS */
280 /* Create a Berkeley DB environment. */
282 create_env(bdb_env_t **bdbp, const char *path, apr_pool_t *pool)
286 const char *path_bdb;
287 char *tmp_path, *tmp_path_bdb;
288 apr_size_t path_size, path_bdb_size;
290 #if SVN_BDB_PATH_UTF8
291 path_bdb = svn_dirent_local_style(path, pool);
293 SVN_ERR(svn_utf_cstring_from_utf8(&path_bdb,
294 svn_dirent_local_style(path, pool),
298 /* Allocate the whole structure, including strings, from the heap,
299 because it must survive the cache pool cleanup. */
300 path_size = strlen(path) + 1;
301 path_bdb_size = strlen(path_bdb) + 1;
302 /* Using calloc() to ensure the padding bytes in bdb->key (which is used as
303 * a hash key) are zeroed. */
304 bdb = calloc(1, sizeof(*bdb) + path_size + path_bdb_size);
306 /* We must initialize this now, as our callers may assume their bdb
307 pointer is valid when checking for errors. */
308 apr_pool_cleanup_register(pool, bdb, cleanup_env, apr_pool_cleanup_null);
309 apr_cpystrn(bdb->errpfx_string, BDB_ERRPFX_STRING,
310 sizeof(bdb->errpfx_string));
311 bdb->path = tmp_path = (char*)(bdb + 1);
312 bdb->path_bdb = tmp_path_bdb = tmp_path + path_size;
313 apr_cpystrn(tmp_path, path, path_size);
314 apr_cpystrn(tmp_path_bdb, path_bdb, path_bdb_size);
320 apr_status_t apr_err = apr_threadkey_private_create(&bdb->error_info,
324 return svn_error_create(apr_err, NULL,
325 "Can't allocate thread-specific storage"
326 " for the Berkeley DB environment descriptor");
328 #endif /* APR_HAS_THREADS */
330 db_err = db_env_create(&(bdb->env), 0);
333 /* See the documentation at bdb_env_t's definition why the
334 (char *) cast is safe and why it is done. */
335 bdb->env->set_errpfx(bdb->env, (char *) bdb);
337 /* bdb_error_gatherer is in parens to stop macro expansion. */
338 bdb->env->set_errcall(bdb->env, (bdb_error_gatherer));
340 /* Needed on Windows in case Subversion and Berkeley DB are using
341 different C runtime libraries */
342 db_err = bdb->env->set_alloc(bdb->env, malloc, realloc, free);
344 /* If we detect a deadlock, select a transaction to abort at
345 random from those participating in the deadlock. */
347 db_err = bdb->env->set_lk_detect(bdb->env, DB_LOCK_RANDOM);
349 return convert_bdb_error(bdb, db_err);
354 /* The environment descriptor cache. */
356 /* The global pool used for this cache. */
357 static apr_pool_t *bdb_cache_pool = NULL;
359 /* The cache. The items are bdb_env_t structures. */
360 static apr_hash_t *bdb_cache = NULL;
362 /* The mutex that protects bdb_cache. */
363 static svn_mutex__t *bdb_cache_lock = NULL;
365 /* Cleanup callback to NULL out the cache, so we don't try to use it after
366 the pool has been cleared during global shutdown. */
368 clear_cache(void *data)
371 bdb_cache_lock = NULL;
375 static volatile svn_atomic_t bdb_cache_state = 0;
378 bdb_init_cb(void *baton, apr_pool_t *pool)
380 bdb_cache_pool = svn_pool_create(pool);
381 bdb_cache = apr_hash_make(bdb_cache_pool);
383 SVN_ERR(svn_mutex__init(&bdb_cache_lock, TRUE, bdb_cache_pool));
384 apr_pool_cleanup_register(bdb_cache_pool, NULL, clear_cache,
385 apr_pool_cleanup_null);
391 svn_fs_bdb__init(apr_pool_t* pool)
393 return svn_atomic__init_once(&bdb_cache_state, bdb_init_cb, NULL, pool);
396 /* Construct a cache key for the BDB environment at PATH in *KEYP.
397 if DBCONFIG_FILE is not NULL, return the opened file handle.
398 Allocate from POOL. */
400 bdb_cache_key(bdb_env_key_t *keyp, apr_file_t **dbconfig_file,
401 const char *path, apr_pool_t *pool)
403 const char *dbcfg_file_name = svn_dirent_join(path, BDB_CONFIG_FILE, pool);
404 apr_file_t *dbcfg_file;
405 apr_status_t apr_err;
408 SVN_ERR(svn_io_file_open(&dbcfg_file, dbcfg_file_name,
409 APR_READ, APR_OS_DEFAULT, pool));
411 apr_err = apr_file_info_get(&finfo, APR_FINFO_DEV | APR_FINFO_INODE,
414 return svn_error_wrap_apr
415 (apr_err, "Can't create BDB environment cache key");
417 /* Make sure that any padding in the key is always cleared, so that
418 the key's hash deterministic. */
419 memset(keyp, 0, sizeof *keyp);
420 keyp->device = finfo.device;
421 keyp->inode = finfo.inode;
424 *dbconfig_file = dbcfg_file;
426 apr_file_close(dbcfg_file);
432 /* Find a BDB environment in the cache.
433 Return the environment's panic state in *PANICP.
435 Note: You MUST acquire the cache mutex before calling this function.
438 bdb_cache_get(const bdb_env_key_t *keyp, svn_boolean_t *panicp)
440 bdb_env_t *bdb = apr_hash_get(bdb_cache, keyp, sizeof *keyp);
443 *panicp = !!svn_atomic_read(&bdb->panic);
444 #if SVN_BDB_VERSION_AT_LEAST(4,2)
448 if (bdb->env->get_flags(bdb->env, &flags)
449 || (flags & DB_PANIC_ENVIRONMENT))
451 /* Something is wrong with the environment. */
452 svn_atomic_set(&bdb->panic, TRUE);
457 #endif /* at least bdb-4.2 */
468 /* Close and destroy a BDB environment descriptor. */
470 bdb_close(bdb_env_t *bdb)
472 svn_error_t *err = SVN_NO_ERROR;
474 /* This bit is delcate; we must propagate the error from
475 DB_ENV->close to the caller, and always destroy the pool. */
476 int db_err = bdb->env->close(bdb->env, 0);
478 /* If automatic database recovery is enabled, ignore DB_RUNRECOVERY
479 errors, since they're dealt with eventually by BDB itself. */
480 if (db_err && (!SVN_BDB_AUTO_RECOVER || db_err != DB_RUNRECOVERY))
481 err = convert_bdb_error(bdb, db_err);
483 /* Free the environment descriptor. The pool cleanup will do this unless
484 the cache has already been destroyed. */
486 svn_pool_destroy(bdb->pool);
489 return svn_error_trace(err);
494 svn_fs_bdb__close_internal(bdb_env_t *bdb)
496 svn_error_t *err = SVN_NO_ERROR;
498 if (--bdb->refcount != 0)
500 /* If the environment is panicked and automatic recovery is not
501 enabled, return an appropriate error. */
502 #if !SVN_BDB_AUTO_RECOVER
503 if (svn_atomic_read(&bdb->panic))
504 err = svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL,
505 db_strerror(DB_RUNRECOVERY));
510 /* If the bdb cache has been set to NULL that means we are
511 shutting down, and the pool that holds the bdb cache has
512 already been destroyed, so accessing it here would be a Bad
515 apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, NULL);
516 err = bdb_close(bdb);
518 return svn_error_trace(err);
522 svn_fs_bdb__close(bdb_env_baton_t *bdb_baton)
524 bdb_env_t *bdb = bdb_baton->bdb;
526 SVN_ERR_ASSERT(bdb_baton->env == bdb_baton->bdb->env);
527 SVN_ERR_ASSERT(bdb_baton->error_info->refcount > 0);
529 /* Neutralize bdb_baton's pool cleanup to prevent double-close. See
530 cleanup_env_baton(). */
531 bdb_baton->bdb = NULL;
533 /* Note that we only bother with this cleanup if the pool is non-NULL, to
534 guard against potential races between this and the cleanup_env cleanup
535 callback. It's not clear if that can actually happen, but better safe
537 if (0 == --bdb_baton->error_info->refcount && bdb->pool)
539 svn_error_clear(bdb_baton->error_info->pending_errors);
541 free(bdb_baton->error_info);
542 apr_threadkey_private_set(NULL, bdb->error_info);
546 /* This may run during final pool cleanup when the lock is NULL. */
547 SVN_MUTEX__WITH_LOCK(bdb_cache_lock, svn_fs_bdb__close_internal(bdb));
554 /* Open and initialize a BDB environment. */
556 bdb_open(bdb_env_t *bdb, u_int32_t flags, int mode)
561 SVN_ERR(convert_bdb_error
562 (bdb, (bdb->env->open)(bdb->env, bdb->path_bdb, flags, mode)));
564 #if SVN_BDB_AUTO_COMMIT
565 /* Assert the BDB_AUTO_COMMIT flag on the opened environment. This
566 will force all operations on the environment (and handles that
567 are opened within the environment) to be transactional. */
569 SVN_ERR(convert_bdb_error
570 (bdb, bdb->env->set_flags(bdb->env, SVN_BDB_AUTO_COMMIT, 1)));
573 return bdb_cache_key(&bdb->key, &bdb->dbconfig_file,
574 bdb->path, bdb->pool);
578 /* Pool cleanup for the environment baton. */
580 cleanup_env_baton(void *data)
582 bdb_env_baton_t *bdb_baton = data;
585 svn_error_clear(svn_fs_bdb__close(bdb_baton));
592 svn_fs_bdb__open_internal(bdb_env_baton_t **bdb_batonp,
594 u_int32_t flags, int mode,
601 /* We can safely discard the open DB_CONFIG file handle. If the
602 environment descriptor is in the cache, the key's immutability is
603 guaranteed. If it's not, we don't care if the key changes,
604 between here and the actual insertion of the newly-created
605 environment into the cache, because no other thread can touch the
606 cache in the meantime. */
607 SVN_ERR(bdb_cache_key(&key, NULL, path, pool));
609 bdb = bdb_cache_get(&key, &panic);
611 return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL,
612 db_strerror(DB_RUNRECOVERY));
614 /* Make sure that the environment's open flags haven't changed. */
615 if (bdb && bdb->flags != flags)
617 /* Handle changes to the DB_PRIVATE flag specially */
618 if ((flags ^ bdb->flags) & DB_PRIVATE)
620 if (flags & DB_PRIVATE)
621 return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL,
622 "Reopening a public Berkeley DB"
623 " environment with private attributes");
625 return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL,
626 "Reopening a private Berkeley DB"
627 " environment with public attributes");
630 /* Otherwise return a generic "flags-mismatch" error. */
631 return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL,
632 "Reopening a Berkeley DB environment"
633 " with different attributes");
640 SVN_ERR(create_env(&bdb, path, svn_pool_create(bdb_cache_pool)));
641 err = bdb_open(bdb, flags, mode);
644 /* Clean up, and we can't do anything about returned errors. */
645 svn_error_clear(bdb_close(bdb));
646 return svn_error_trace(err);
649 apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, bdb);
658 *bdb_batonp = apr_palloc(pool, sizeof **bdb_batonp);
659 (*bdb_batonp)->env = bdb->env;
660 (*bdb_batonp)->bdb = bdb;
661 (*bdb_batonp)->error_info = get_error_info(bdb);
662 ++(*bdb_batonp)->error_info->refcount;
663 apr_pool_cleanup_register(pool, *bdb_batonp, cleanup_env_baton,
664 apr_pool_cleanup_null);
670 svn_fs_bdb__open(bdb_env_baton_t **bdb_batonp, const char *path,
671 u_int32_t flags, int mode,
674 SVN_MUTEX__WITH_LOCK(bdb_cache_lock,
675 svn_fs_bdb__open_internal(bdb_batonp,
686 svn_fs_bdb__get_panic(bdb_env_baton_t *bdb_baton)
688 /* An invalid baton is equivalent to a panicked environment; in both
689 cases, database cleanups should be skipped. */
693 assert(bdb_baton->env == bdb_baton->bdb->env);
694 return !!svn_atomic_read(&bdb_baton->bdb->panic);
698 svn_fs_bdb__set_panic(bdb_env_baton_t *bdb_baton)
703 assert(bdb_baton->env == bdb_baton->bdb->env);
704 svn_atomic_set(&bdb_baton->bdb->panic, TRUE);
708 /* This function doesn't actually open the environment, so it doesn't
709 have to look in the cache. Callers are supposed to own an
710 exclusive lock on the filesystem anyway. */
712 svn_fs_bdb__remove(const char *path, apr_pool_t *pool)
716 SVN_ERR(create_env(&bdb, path, pool));
717 return convert_bdb_error
718 (bdb, bdb->env->remove(bdb->env, bdb->path_bdb, DB_FORCE));