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