1 /* env.h : managing the BDB environment
2 *
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
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
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
19 * under the License.
20 * ====================================================================
21 */
22
23 #include <assert.h>
24
25 #include <apr.h>
26 #if APR_HAS_THREADS
27 #include <apr_thread_proc.h>
28 #include <apr_time.h>
29 #endif
30
31 #include <apr_strings.h>
32 #include <apr_hash.h>
33
34 #include "svn_hash.h"
35 #include "svn_path.h"
36 #include "svn_pools.h"
37 #include "svn_utf.h"
38 #include "private/svn_atomic.h"
39 #include "private/svn_mutex.h"
40
41 #include "bdb-err.h"
42 #include "bdb_compat.h"
43
44 #include "env.h"
45
46 /* A note about the BDB environment descriptor cache.
47
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
54 with a global mutex.
55
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.
59
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
63 documentation.
64
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.)
68 */
69
70
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
73 config file.
74
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,
79 anyway. */
80 typedef struct bdb_env_key_t
81 {
82 apr_dev_t device;
83 apr_ino_t inode;
84 } bdb_env_key_t;
85
86 /* The cached Berkeley DB environment descriptor. */
87 struct bdb_env_t
88 {
89 /**************************************************************************/
90 /* Error Reporting */
91
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 *).
102
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)];
108
109 /* Extended error information. */
110 #if APR_HAS_THREADS
111 apr_threadkey_t *error_info; /* Points to a bdb_error_info_t. */
112 #else
113 bdb_error_info_t error_info;
114 #endif
115
116 /**************************************************************************/
117 /* BDB Environment Cache */
118
119 /* The Berkeley DB environment. */
120 DB_ENV *env;
121
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. */
126 u_int32_t flags;
127
128 /* The home path of this environment; a canonical SVN path encoded in
129 UTF-8 and allocated from this decriptor's pool. */
130 const char *path;
131
132 /* The home path of this environment, in the form expected by BDB. */
133 const char *path_bdb;
134
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. */
138 unsigned refcount;
139
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
142 cache.
143
144 Note 1: Once this flag is set, it must not be cleared again.
145
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;
150
151 /* The key for the environment descriptor cache. */
152 bdb_env_key_t key;
153
154 /* The handle of the open DB_CONFIG file.
155
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
160 number by APR):
161
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."
165
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;
169
170 /* The pool associated with this environment descriptor.
171
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
174 pool. */
175 apr_pool_t *pool;
176
177 };
178
179
180 #if APR_HAS_THREADS
181 /* Get the thread-specific error info from a bdb_env_t. */
182 static bdb_error_info_t *
get_error_info(const bdb_env_t * bdb)183 get_error_info(const bdb_env_t *bdb)
184 {
185 void *priv;
186 apr_threadkey_private_get(&priv, bdb->error_info);
187 if (!priv)
188 {
189 priv = calloc(1, sizeof(bdb_error_info_t));
190 apr_threadkey_private_set(priv, bdb->error_info);
191 }
192 return priv;
193 }
194 #else
195 #define get_error_info(bdb) (&(bdb)->error_info)
196 #endif /* APR_HAS_THREADS */
197
198
199 /* Convert a BDB error to a Subversion error. */
200 static svn_error_t *
convert_bdb_error(bdb_env_t * bdb,int db_err)201 convert_bdb_error(bdb_env_t *bdb, int db_err)
202 {
203 if (db_err)
204 {
205 bdb_env_baton_t bdb_baton;
206 bdb_baton.env = bdb->env;
207 bdb_baton.bdb = bdb;
208 bdb_baton.error_info = get_error_info(bdb);
209 SVN_BDB_ERR(&bdb_baton, db_err);
210 }
211 return SVN_NO_ERROR;
212 }
213
214
215 /* Allocating an appropriate Berkeley DB environment object. */
216
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
219 you use it! */
220 static void
bdb_error_gatherer(const DB_ENV * dbenv,const char * baton,const char * msg)221 bdb_error_gatherer(const DB_ENV *dbenv, const char *baton, const char *msg)
222 {
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;
227
228 SVN_BDB_ERROR_GATHERER_IGNORE(dbenv);
229
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);
233 else
234 error_info->pending_errors = new_err;
235
236 if (error_info->user_callback)
237 error_info->user_callback(NULL, (char *)msg); /* ### I hate this cast... */
238 }
239
240
241 /* Pool cleanup for the cached environment descriptor. */
242 static apr_status_t
cleanup_env(void * data)243 cleanup_env(void *data)
244 {
245 bdb_env_t *bdb = data;
246 bdb->pool = NULL;
247 bdb->dbconfig_file = NULL; /* will be closed during pool destruction */
248 #if APR_HAS_THREADS
249 apr_threadkey_private_delete(bdb->error_info);
250 #endif /* APR_HAS_THREADS */
251
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. */
256 if (!bdb->refcount)
257 free(data);
258
259 return APR_SUCCESS;
260 }
261
262 #if APR_HAS_THREADS
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. */
268 static void
cleanup_error_info(void * baton)269 cleanup_error_info(void *baton)
270 {
271 bdb_error_info_t *error_info = baton;
272
273 if (error_info)
274 svn_error_clear(error_info->pending_errors);
275
276 free(error_info);
277 }
278 #endif /* APR_HAS_THREADS */
279
280 /* Create a Berkeley DB environment. */
281 static svn_error_t *
create_env(bdb_env_t ** bdbp,const char * path,apr_pool_t * pool)282 create_env(bdb_env_t **bdbp, const char *path, apr_pool_t *pool)
283 {
284 int db_err;
285 bdb_env_t *bdb;
286 const char *path_bdb;
287 char *tmp_path, *tmp_path_bdb;
288 apr_size_t path_size, path_bdb_size;
289
290 #if SVN_BDB_PATH_UTF8
291 path_bdb = svn_dirent_local_style(path, pool);
292 #else
293 SVN_ERR(svn_utf_cstring_from_utf8(&path_bdb,
294 svn_dirent_local_style(path, pool),
295 pool));
296 #endif
297
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);
305
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);
315 bdb->pool = pool;
316 *bdbp = bdb;
317
318 #if APR_HAS_THREADS
319 {
320 apr_status_t apr_err = apr_threadkey_private_create(&bdb->error_info,
321 cleanup_error_info,
322 pool);
323 if (apr_err)
324 return svn_error_create(apr_err, NULL,
325 "Can't allocate thread-specific storage"
326 " for the Berkeley DB environment descriptor");
327 }
328 #endif /* APR_HAS_THREADS */
329
330 db_err = db_env_create(&(bdb->env), 0);
331 if (!db_err)
332 {
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);
336
337 /* bdb_error_gatherer is in parens to stop macro expansion. */
338 bdb->env->set_errcall(bdb->env, (bdb_error_gatherer));
339
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);
343
344 /* If we detect a deadlock, select a transaction to abort at
345 random from those participating in the deadlock. */
346 if (!db_err)
347 db_err = bdb->env->set_lk_detect(bdb->env, DB_LOCK_RANDOM);
348 }
349 return convert_bdb_error(bdb, db_err);
350 }
351
352
353
354 /* The environment descriptor cache. */
355
356 /* The global pool used for this cache. */
357 static apr_pool_t *bdb_cache_pool = NULL;
358
359 /* The cache. The items are bdb_env_t structures. */
360 static apr_hash_t *bdb_cache = NULL;
361
362 /* The mutex that protects bdb_cache. */
363 static svn_mutex__t *bdb_cache_lock = NULL;
364
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. */
367 static apr_status_t
clear_cache(void * data)368 clear_cache(void *data)
369 {
370 bdb_cache = NULL;
371 bdb_cache_lock = NULL;
372 return APR_SUCCESS;
373 }
374
375 static volatile svn_atomic_t bdb_cache_state = 0;
376
377 static svn_error_t *
bdb_init_cb(void * baton,apr_pool_t * pool)378 bdb_init_cb(void *baton, apr_pool_t *pool)
379 {
380 bdb_cache_pool = svn_pool_create(pool);
381 bdb_cache = apr_hash_make(bdb_cache_pool);
382
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);
386
387 return SVN_NO_ERROR;
388 }
389
390 svn_error_t *
svn_fs_bdb__init(apr_pool_t * pool)391 svn_fs_bdb__init(apr_pool_t* pool)
392 {
393 return svn_atomic__init_once(&bdb_cache_state, bdb_init_cb, NULL, pool);
394 }
395
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. */
399 static svn_error_t *
bdb_cache_key(bdb_env_key_t * keyp,apr_file_t ** dbconfig_file,const char * path,apr_pool_t * pool)400 bdb_cache_key(bdb_env_key_t *keyp, apr_file_t **dbconfig_file,
401 const char *path, apr_pool_t *pool)
402 {
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;
406 apr_finfo_t finfo;
407
408 SVN_ERR(svn_io_file_open(&dbcfg_file, dbcfg_file_name,
409 APR_READ, APR_OS_DEFAULT, pool));
410
411 apr_err = apr_file_info_get(&finfo, APR_FINFO_DEV | APR_FINFO_INODE,
412 dbcfg_file);
413 if (apr_err)
414 return svn_error_wrap_apr
415 (apr_err, "Can't create BDB environment cache key");
416
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;
422
423 if (dbconfig_file)
424 *dbconfig_file = dbcfg_file;
425 else
426 apr_file_close(dbcfg_file);
427
428 return SVN_NO_ERROR;
429 }
430
431
432 /* Find a BDB environment in the cache.
433 Return the environment's panic state in *PANICP.
434
435 Note: You MUST acquire the cache mutex before calling this function.
436 */
437 static bdb_env_t *
bdb_cache_get(const bdb_env_key_t * keyp,svn_boolean_t * panicp)438 bdb_cache_get(const bdb_env_key_t *keyp, svn_boolean_t *panicp)
439 {
440 bdb_env_t *bdb = apr_hash_get(bdb_cache, keyp, sizeof *keyp);
441 if (bdb && bdb->env)
442 {
443 *panicp = !!svn_atomic_read(&bdb->panic);
444 #if SVN_BDB_VERSION_AT_LEAST(4,2)
445 if (!*panicp)
446 {
447 u_int32_t flags;
448 if (bdb->env->get_flags(bdb->env, &flags)
449 || (flags & DB_PANIC_ENVIRONMENT))
450 {
451 /* Something is wrong with the environment. */
452 svn_atomic_set(&bdb->panic, TRUE);
453 *panicp = TRUE;
454 bdb = NULL;
455 }
456 }
457 #endif /* at least bdb-4.2 */
458 }
459 else
460 {
461 *panicp = FALSE;
462 }
463 return bdb;
464 }
465
466
467
468 /* Close and destroy a BDB environment descriptor. */
469 static svn_error_t *
bdb_close(bdb_env_t * bdb)470 bdb_close(bdb_env_t *bdb)
471 {
472 svn_error_t *err = SVN_NO_ERROR;
473
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);
477
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);
482
483 /* Free the environment descriptor. The pool cleanup will do this unless
484 the cache has already been destroyed. */
485 if (bdb->pool)
486 svn_pool_destroy(bdb->pool);
487 else
488 free(bdb);
489 return svn_error_trace(err);
490 }
491
492
493 static svn_error_t *
svn_fs_bdb__close_internal(bdb_env_t * bdb)494 svn_fs_bdb__close_internal(bdb_env_t *bdb)
495 {
496 svn_error_t *err = SVN_NO_ERROR;
497
498 if (--bdb->refcount != 0)
499 {
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));
506 #endif
507 }
508 else
509 {
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
513 Thing (tm) */
514 if (bdb_cache)
515 apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, NULL);
516 err = bdb_close(bdb);
517 }
518 return svn_error_trace(err);
519 }
520
521 svn_error_t *
svn_fs_bdb__close(bdb_env_baton_t * bdb_baton)522 svn_fs_bdb__close(bdb_env_baton_t *bdb_baton)
523 {
524 bdb_env_t *bdb = bdb_baton->bdb;
525
526 SVN_ERR_ASSERT(bdb_baton->env == bdb_baton->bdb->env);
527 SVN_ERR_ASSERT(bdb_baton->error_info->refcount > 0);
528
529 /* Neutralize bdb_baton's pool cleanup to prevent double-close. See
530 cleanup_env_baton(). */
531 bdb_baton->bdb = NULL;
532
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
536 than sorry. */
537 if (0 == --bdb_baton->error_info->refcount && bdb->pool)
538 {
539 svn_error_clear(bdb_baton->error_info->pending_errors);
540 #if APR_HAS_THREADS
541 free(bdb_baton->error_info);
542 apr_threadkey_private_set(NULL, bdb->error_info);
543 #endif
544 }
545
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));
548
549 return SVN_NO_ERROR;
550 }
551
552
553
554 /* Open and initialize a BDB environment. */
555 static svn_error_t *
bdb_open(bdb_env_t * bdb,u_int32_t flags,int mode)556 bdb_open(bdb_env_t *bdb, u_int32_t flags, int mode)
557 {
558 #if APR_HAS_THREADS
559 flags |= DB_THREAD;
560 #endif
561 SVN_ERR(convert_bdb_error
562 (bdb, (bdb->env->open)(bdb->env, bdb->path_bdb, flags, mode)));
563
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. */
568
569 SVN_ERR(convert_bdb_error
570 (bdb, bdb->env->set_flags(bdb->env, SVN_BDB_AUTO_COMMIT, 1)));
571 #endif
572
573 return bdb_cache_key(&bdb->key, &bdb->dbconfig_file,
574 bdb->path, bdb->pool);
575 }
576
577
578 /* Pool cleanup for the environment baton. */
579 static apr_status_t
cleanup_env_baton(void * data)580 cleanup_env_baton(void *data)
581 {
582 bdb_env_baton_t *bdb_baton = data;
583
584 if (bdb_baton->bdb)
585 svn_error_clear(svn_fs_bdb__close(bdb_baton));
586
587 return APR_SUCCESS;
588 }
589
590
591 static svn_error_t *
svn_fs_bdb__open_internal(bdb_env_baton_t ** bdb_batonp,const char * path,u_int32_t flags,int mode,apr_pool_t * pool)592 svn_fs_bdb__open_internal(bdb_env_baton_t **bdb_batonp,
593 const char *path,
594 u_int32_t flags, int mode,
595 apr_pool_t *pool)
596 {
597 bdb_env_key_t key;
598 bdb_env_t *bdb;
599 svn_boolean_t panic;
600
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));
608
609 bdb = bdb_cache_get(&key, &panic);
610 if (panic)
611 return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL,
612 db_strerror(DB_RUNRECOVERY));
613
614 /* Make sure that the environment's open flags haven't changed. */
615 if (bdb && bdb->flags != flags)
616 {
617 /* Handle changes to the DB_PRIVATE flag specially */
618 if ((flags ^ bdb->flags) & DB_PRIVATE)
619 {
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");
624 else
625 return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL,
626 "Reopening a private Berkeley DB"
627 " environment with public attributes");
628 }
629
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");
634 }
635
636 if (!bdb)
637 {
638 svn_error_t *err;
639
640 SVN_ERR(create_env(&bdb, path, svn_pool_create(bdb_cache_pool)));
641 err = bdb_open(bdb, flags, mode);
642 if (err)
643 {
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);
647 }
648
649 apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, bdb);
650 bdb->flags = flags;
651 bdb->refcount = 1;
652 }
653 else
654 {
655 ++bdb->refcount;
656 }
657
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);
665
666 return SVN_NO_ERROR;
667 }
668
669 svn_error_t *
svn_fs_bdb__open(bdb_env_baton_t ** bdb_batonp,const char * path,u_int32_t flags,int mode,apr_pool_t * pool)670 svn_fs_bdb__open(bdb_env_baton_t **bdb_batonp, const char *path,
671 u_int32_t flags, int mode,
672 apr_pool_t *pool)
673 {
674 SVN_MUTEX__WITH_LOCK(bdb_cache_lock,
675 svn_fs_bdb__open_internal(bdb_batonp,
676 path,
677 flags,
678 mode,
679 pool));
680
681 return SVN_NO_ERROR;
682 }
683
684
685 svn_boolean_t
svn_fs_bdb__get_panic(bdb_env_baton_t * bdb_baton)686 svn_fs_bdb__get_panic(bdb_env_baton_t *bdb_baton)
687 {
688 /* An invalid baton is equivalent to a panicked environment; in both
689 cases, database cleanups should be skipped. */
690 if (!bdb_baton->bdb)
691 return TRUE;
692
693 assert(bdb_baton->env == bdb_baton->bdb->env);
694 return !!svn_atomic_read(&bdb_baton->bdb->panic);
695 }
696
697 void
svn_fs_bdb__set_panic(bdb_env_baton_t * bdb_baton)698 svn_fs_bdb__set_panic(bdb_env_baton_t *bdb_baton)
699 {
700 if (!bdb_baton->bdb)
701 return;
702
703 assert(bdb_baton->env == bdb_baton->bdb->env);
704 svn_atomic_set(&bdb_baton->bdb->panic, TRUE);
705 }
706
707
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. */
711 svn_error_t *
svn_fs_bdb__remove(const char * path,apr_pool_t * pool)712 svn_fs_bdb__remove(const char *path, apr_pool_t *pool)
713 {
714 bdb_env_t *bdb;
715
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));
719 }
720