1 | /* env.h : managing the BDB environment |
---|
2 | * |
---|
3 | * ==================================================================== |
---|
4 | * Copyright (c) 2000-2005 CollabNet. All rights reserved. |
---|
5 | * |
---|
6 | * This software is licensed as described in the file COPYING, which |
---|
7 | * you should have received as part of this distribution. The terms |
---|
8 | * are also available at http://subversion.tigris.org/license-1.html. |
---|
9 | * If newer versions of this license are posted there, you may use a |
---|
10 | * newer version instead, at your option. |
---|
11 | * |
---|
12 | * This software consists of voluntary contributions made by many |
---|
13 | * individuals. For exact contribution history, see the revision |
---|
14 | * history and logs, available at http://subversion.tigris.org/. |
---|
15 | * ==================================================================== |
---|
16 | */ |
---|
17 | |
---|
18 | #include <assert.h> |
---|
19 | |
---|
20 | #include <apr.h> |
---|
21 | #if APR_HAS_THREADS |
---|
22 | #include <apr_thread_mutex.h> |
---|
23 | #include <apr_thread_proc.h> |
---|
24 | #include <apr_time.h> |
---|
25 | #endif |
---|
26 | |
---|
27 | #include <apr_strings.h> |
---|
28 | #include <apr_hash.h> |
---|
29 | |
---|
30 | #include "svn_path.h" |
---|
31 | #include "svn_pools.h" |
---|
32 | #include "svn_utf.h" |
---|
33 | #include "private/svn_atomic.h" |
---|
34 | |
---|
35 | #include "bdb-err.h" |
---|
36 | #include "bdb_compat.h" |
---|
37 | |
---|
38 | #include "env.h" |
---|
39 | |
---|
40 | /* A note about the BDB environment descriptor cache. |
---|
41 | |
---|
42 | With the advent of DB_REGISTER in BDB-4.4, a process may only open |
---|
43 | an environment handle once. This means that we must maintain a |
---|
44 | cache of open environment handles, with reference counts. We |
---|
45 | allocate each environment descriptor (a bdb_env_t) from its own |
---|
46 | pool. The cache itself (and the cache pool) are shared between |
---|
47 | threads, so all direct or indirect access to the pool is serialized |
---|
48 | with a global mutex. |
---|
49 | |
---|
50 | Because several threads can now use the same DB_ENV handle, we must |
---|
51 | use the DB_THREAD flag when opening the environments, otherwise the |
---|
52 | env handles (and all of libsvn_fs_base) won't be thread-safe. |
---|
53 | |
---|
54 | If we use DB_THREAD, however, all of the code that reads data from |
---|
55 | the database without a cursor must use either DB_DBT_MALLOC, |
---|
56 | DB_DBT_REALLOC, or DB_DBT_USERMEM, as described in the BDB |
---|
57 | documentation. |
---|
58 | |
---|
59 | (Oh, yes -- using DB_THREAD might not work on some systems. But |
---|
60 | then, it's quite probable that threading is seriously broken on |
---|
61 | those systems anyway, so we'll rely on APR_HAS_THREADS.) |
---|
62 | */ |
---|
63 | |
---|
64 | |
---|
65 | /* The cache key for a Berkeley DB environment descriptor. This is a |
---|
66 | combination of the device ID and INODE number of the Berkeley DB |
---|
67 | config file. |
---|
68 | |
---|
69 | XXX FIXME: Although the dev+inode combination is supposed do be |
---|
70 | unique, apparently that's not always the case with some remote |
---|
71 | filesystems. We /should/ be safe using this as a unique hash key, |
---|
72 | because the database must be on a local filesystem. We can hope, |
---|
73 | anyway. */ |
---|
74 | typedef struct |
---|
75 | { |
---|
76 | apr_dev_t device; |
---|
77 | apr_ino_t inode; |
---|
78 | } bdb_env_key_t; |
---|
79 | |
---|
80 | /* The cached Berkeley DB environment descriptor. */ |
---|
81 | struct bdb_env_t |
---|
82 | { |
---|
83 | /**************************************************************************/ |
---|
84 | /* Error Reporting */ |
---|
85 | |
---|
86 | /* A (char *) casted pointer to this structure is passed to BDB's |
---|
87 | set_errpfx(), which treats it as a NUL-terminated character |
---|
88 | string to prefix all BDB error messages. However, svn also |
---|
89 | registers bdb_error_gatherer() as an error handler with |
---|
90 | set_errcall() which turns off BDB's default printing of errors to |
---|
91 | stderr and anytime thereafter when BDB reports an error and |
---|
92 | before the BDB function returns, it calls bdb_error_gatherer() |
---|
93 | and passes the same error prefix (char *) pointer given to |
---|
94 | set_errpfx(). The bdb_error_gatherer() callback casts the |
---|
95 | (char *) it back to a (bdb_env_t *). |
---|
96 | |
---|
97 | To avoid problems should BDB ever try to interpret our baton as a |
---|
98 | string, the first field in the structure is a char |
---|
99 | errpfx_string[]. Initializers of this structure must strcpy the |
---|
100 | value of BDB_ERRPFX_STRING into this array. */ |
---|
101 | char errpfx_string[sizeof(BDB_ERRPFX_STRING)]; |
---|
102 | |
---|
103 | /* Extended error information. */ |
---|
104 | #if APR_HAS_THREADS |
---|
105 | apr_threadkey_t *error_info; /* Points to a bdb_error_info_t. */ |
---|
106 | #else |
---|
107 | bdb_error_info_t error_info; |
---|
108 | #endif |
---|
109 | |
---|
110 | /**************************************************************************/ |
---|
111 | /* BDB Environment Cache */ |
---|
112 | |
---|
113 | /* The Berkeley DB environment. */ |
---|
114 | DB_ENV *env; |
---|
115 | |
---|
116 | /* The flags with which this environment was opened. Reopening the |
---|
117 | environment with a different set of flags is not allowed. Trying |
---|
118 | to change the state of the DB_PRIVATE flag is an especially bad |
---|
119 | idea, so svn_fs_bdb__open() forbids any flag changes. */ |
---|
120 | u_int32_t flags; |
---|
121 | |
---|
122 | /* The home path of this environment; a canonical SVN path encoded in |
---|
123 | UTF-8 and allocated from this decriptor's pool. */ |
---|
124 | const char *path; |
---|
125 | |
---|
126 | /* The home path of this environment, in the form expected by BDB. */ |
---|
127 | const char *path_bdb; |
---|
128 | |
---|
129 | /* The reference count for this environment handle; this is |
---|
130 | essentially the difference between the number of calls to |
---|
131 | svn_fs_bdb__open and svn_fs_bdb__close. */ |
---|
132 | unsigned refcount; |
---|
133 | |
---|
134 | /* If this flag is TRUE, someone has detected that the environment |
---|
135 | descriptor is in a panicked state and should be removed from the |
---|
136 | cache. |
---|
137 | |
---|
138 | Note 1: Once this flag is set, it must not be cleared again. |
---|
139 | |
---|
140 | Note 2: Unlike other fields in this structure, this field is not |
---|
141 | protected by the cache mutex on threaded platforms, and |
---|
142 | should only be accesses via the svn_atomic functions. */ |
---|
143 | volatile svn_atomic_t panic; |
---|
144 | |
---|
145 | /* The key for the environment descriptor cache. */ |
---|
146 | bdb_env_key_t key; |
---|
147 | |
---|
148 | /* The handle of the open DB_CONFIG file. |
---|
149 | |
---|
150 | We keep the DB_CONFIG file open in this process as long as the |
---|
151 | environment handle itself is open. On Windows, this guarantees |
---|
152 | that the cache key remains unique; here's what the Windows SDK |
---|
153 | docs have to say about the file index (interpreted as the INODE |
---|
154 | number by APR): |
---|
155 | |
---|
156 | "This value is useful only while the file is open by at least |
---|
157 | one process. If no processes have it open, the index may |
---|
158 | change the next time the file is opened." |
---|
159 | |
---|
160 | Now, we certainly don't want a unique key to change while it's |
---|
161 | being used, do we... */ |
---|
162 | apr_file_t *dbconfig_file; |
---|
163 | |
---|
164 | /* The pool associated with this environment descriptor. |
---|
165 | |
---|
166 | Because the descriptor has a life of its own, the structure and |
---|
167 | any data associated with it are allocated from their own global |
---|
168 | pool. */ |
---|
169 | apr_pool_t *pool; |
---|
170 | |
---|
171 | }; |
---|
172 | |
---|
173 | |
---|
174 | #if APR_HAS_THREADS |
---|
175 | /* Get the thread-specific error info from a bdb_env_t. */ |
---|
176 | static bdb_error_info_t * |
---|
177 | get_error_info(bdb_env_t *bdb) |
---|
178 | { |
---|
179 | void *priv; |
---|
180 | apr_threadkey_private_get(&priv, bdb->error_info); |
---|
181 | if (!priv) |
---|
182 | { |
---|
183 | priv = calloc(1, sizeof(bdb_error_info_t)); |
---|
184 | apr_threadkey_private_set(priv, bdb->error_info); |
---|
185 | } |
---|
186 | return priv; |
---|
187 | } |
---|
188 | #else |
---|
189 | #define get_error_info(bdb) (&(bdb)->error_info) |
---|
190 | #endif /* APR_HAS_THREADS */ |
---|
191 | |
---|
192 | |
---|
193 | /* Convert a BDB error to a Subversion error. */ |
---|
194 | static svn_error_t * |
---|
195 | convert_bdb_error(bdb_env_t *bdb, int db_err) |
---|
196 | { |
---|
197 | if (db_err) |
---|
198 | { |
---|
199 | bdb_env_baton_t bdb_baton; |
---|
200 | bdb_baton.env = bdb->env; |
---|
201 | bdb_baton.bdb = bdb; |
---|
202 | bdb_baton.error_info = get_error_info(bdb); |
---|
203 | SVN_BDB_ERR(&bdb_baton, db_err); |
---|
204 | } |
---|
205 | return SVN_NO_ERROR; |
---|
206 | } |
---|
207 | |
---|
208 | |
---|
209 | /* Allocating an appropriate Berkeley DB environment object. */ |
---|
210 | |
---|
211 | /* BDB error callback. See bdb_error_info_t in env.h for more info. |
---|
212 | Note: bdb_error_gatherer is a macro with BDB < 4.3, so be careful how |
---|
213 | you use it! */ |
---|
214 | static void |
---|
215 | bdb_error_gatherer(const DB_ENV *dbenv, const char *baton, const char *msg) |
---|
216 | { |
---|
217 | /* See the documentation at bdb_env_t's definition why the |
---|
218 | (bdb_env_t *) cast is safe and why it is done. */ |
---|
219 | bdb_error_info_t *error_info = get_error_info((bdb_env_t *) baton); |
---|
220 | svn_error_t *new_err; |
---|
221 | |
---|
222 | SVN_BDB_ERROR_GATHERER_IGNORE(dbenv); |
---|
223 | |
---|
224 | new_err = svn_error_createf(SVN_NO_ERROR, NULL, "bdb: %s", msg); |
---|
225 | if (error_info->pending_errors) |
---|
226 | svn_error_compose(error_info->pending_errors, new_err); |
---|
227 | else |
---|
228 | error_info->pending_errors = new_err; |
---|
229 | |
---|
230 | if (error_info->user_callback) |
---|
231 | error_info->user_callback(NULL, (char *)msg); /* ### I hate this cast... */ |
---|
232 | } |
---|
233 | |
---|
234 | |
---|
235 | /* Pool cleanup for the cached environment descriptor. */ |
---|
236 | static apr_status_t |
---|
237 | cleanup_env(void *data) |
---|
238 | { |
---|
239 | bdb_env_t *bdb = data; |
---|
240 | bdb->pool = NULL; |
---|
241 | bdb->dbconfig_file = NULL; /* will be closed during pool destruction */ |
---|
242 | #if APR_HAS_THREADS |
---|
243 | apr_threadkey_private_delete(bdb->error_info); |
---|
244 | #endif /* APR_HAS_THREADS */ |
---|
245 | |
---|
246 | /* If there are no references to this descriptor, free its memory here, |
---|
247 | so that we don't leak it if create_env returns an error. |
---|
248 | See bdb_close, which takes care of freeing this memory if the |
---|
249 | environment is still open when the cache is destroyed. */ |
---|
250 | if (!bdb->refcount) |
---|
251 | free(data); |
---|
252 | |
---|
253 | return APR_SUCCESS; |
---|
254 | } |
---|
255 | |
---|
256 | #if APR_HAS_THREADS |
---|
257 | /* This cleanup is the fall back plan. If the thread exits and the |
---|
258 | environment hasn't been closed it's responsible for cleanup of the |
---|
259 | thread local error info variable, which would otherwise be leaked. |
---|
260 | Normally it will not be called, because svn_fs_bdb__close will |
---|
261 | set the thread's error info to NULL after cleaning it up. */ |
---|
262 | static void |
---|
263 | cleanup_error_info(void *baton) |
---|
264 | { |
---|
265 | bdb_error_info_t *error_info = baton; |
---|
266 | |
---|
267 | if (error_info) |
---|
268 | svn_error_clear(error_info->pending_errors); |
---|
269 | |
---|
270 | free(error_info); |
---|
271 | } |
---|
272 | #endif /* APR_HAS_THREADS */ |
---|
273 | |
---|
274 | /* Create a Berkeley DB environment. */ |
---|
275 | static svn_error_t * |
---|
276 | create_env(bdb_env_t **bdbp, const char *path, apr_pool_t *pool) |
---|
277 | { |
---|
278 | int db_err; |
---|
279 | bdb_env_t *bdb; |
---|
280 | const char *path_bdb; |
---|
281 | char *tmp_path, *tmp_path_bdb; |
---|
282 | apr_size_t path_size, path_bdb_size; |
---|
283 | |
---|
284 | #if SVN_BDB_PATH_UTF8 |
---|
285 | path_bdb = svn_path_local_style(path, pool); |
---|
286 | #else |
---|
287 | SVN_ERR(svn_utf_cstring_from_utf8(&path_bdb, |
---|
288 | svn_path_local_style(path, pool), |
---|
289 | pool)); |
---|
290 | #endif |
---|
291 | |
---|
292 | /* Allocate the whole structure, including strings, from the heap, |
---|
293 | because it must survive the cache pool cleanup. */ |
---|
294 | path_size = strlen(path) + 1; |
---|
295 | path_bdb_size = strlen(path_bdb) + 1; |
---|
296 | bdb = calloc(1, sizeof(*bdb) + path_size + path_bdb_size); |
---|
297 | |
---|
298 | /* We must initialize this now, as our callers may assume their bdb |
---|
299 | pointer is valid when checking for errors. */ |
---|
300 | apr_pool_cleanup_register(pool, bdb, cleanup_env, apr_pool_cleanup_null); |
---|
301 | apr_cpystrn(bdb->errpfx_string, BDB_ERRPFX_STRING, |
---|
302 | sizeof(bdb->errpfx_string)); |
---|
303 | bdb->path = tmp_path = (char*)(bdb + 1); |
---|
304 | bdb->path_bdb = tmp_path_bdb = tmp_path + path_size; |
---|
305 | apr_cpystrn(tmp_path, path, path_size); |
---|
306 | apr_cpystrn(tmp_path_bdb, path_bdb, path_bdb_size); |
---|
307 | bdb->pool = pool; |
---|
308 | *bdbp = bdb; |
---|
309 | |
---|
310 | #if APR_HAS_THREADS |
---|
311 | { |
---|
312 | apr_status_t apr_err = apr_threadkey_private_create(&bdb->error_info, |
---|
313 | cleanup_error_info, |
---|
314 | pool); |
---|
315 | if (apr_err) |
---|
316 | return svn_error_create(apr_err, NULL, |
---|
317 | "Can't allocate thread-specific storage" |
---|
318 | " for the Berkeley DB environment descriptor"); |
---|
319 | } |
---|
320 | #endif /* APR_HAS_THREADS */ |
---|
321 | |
---|
322 | db_err = db_env_create(&(bdb->env), 0); |
---|
323 | if (!db_err) |
---|
324 | { |
---|
325 | /* See the documentation at bdb_env_t's definition why the |
---|
326 | (char *) cast is safe and why it is done. */ |
---|
327 | bdb->env->set_errpfx(bdb->env, (char *) bdb); |
---|
328 | |
---|
329 | /* bdb_error_gatherer is in parens to stop macro expansion. */ |
---|
330 | bdb->env->set_errcall(bdb->env, (bdb_error_gatherer)); |
---|
331 | |
---|
332 | /* Needed on Windows in case Subversion and Berkeley DB are using |
---|
333 | different C runtime libraries */ |
---|
334 | db_err = bdb->env->set_alloc(bdb->env, malloc, realloc, free); |
---|
335 | |
---|
336 | /* If we detect a deadlock, select a transaction to abort at |
---|
337 | random from those participating in the deadlock. */ |
---|
338 | if (!db_err) |
---|
339 | db_err = bdb->env->set_lk_detect(bdb->env, DB_LOCK_RANDOM); |
---|
340 | } |
---|
341 | return convert_bdb_error(bdb, db_err); |
---|
342 | } |
---|
343 | |
---|
344 | |
---|
345 | |
---|
346 | /* The environment descriptor cache. */ |
---|
347 | |
---|
348 | /* The global pool used for this cache. */ |
---|
349 | static apr_pool_t *bdb_cache_pool = NULL; |
---|
350 | |
---|
351 | /* The cache. The items are bdb_env_t structures. */ |
---|
352 | static apr_hash_t *bdb_cache = NULL; |
---|
353 | |
---|
354 | #if APR_HAS_THREADS |
---|
355 | /* The mutex that protects bdb_cache. */ |
---|
356 | static apr_thread_mutex_t *bdb_cache_lock = NULL; |
---|
357 | |
---|
358 | /* Cleanup callback to NULL out the cache and its lock, so we don't try to |
---|
359 | use them after the pool has been cleared during global shutdown. */ |
---|
360 | static apr_status_t |
---|
361 | clear_cache(void *data) |
---|
362 | { |
---|
363 | bdb_cache = NULL; |
---|
364 | bdb_cache_lock = NULL; |
---|
365 | return APR_SUCCESS; |
---|
366 | } |
---|
367 | #endif /* APR_HAS_THREADS */ |
---|
368 | |
---|
369 | static volatile svn_atomic_t bdb_cache_state; |
---|
370 | |
---|
371 | static svn_error_t * |
---|
372 | bdb_init_cb(apr_pool_t *pool) |
---|
373 | { |
---|
374 | #if APR_HAS_THREADS |
---|
375 | apr_status_t apr_err; |
---|
376 | #endif |
---|
377 | bdb_cache_pool = svn_pool_create(pool); |
---|
378 | bdb_cache = apr_hash_make(bdb_cache_pool); |
---|
379 | #if APR_HAS_THREADS |
---|
380 | apr_err = apr_thread_mutex_create(&bdb_cache_lock, |
---|
381 | APR_THREAD_MUTEX_DEFAULT, |
---|
382 | bdb_cache_pool); |
---|
383 | if (apr_err) |
---|
384 | { |
---|
385 | return svn_error_create(apr_err, NULL, |
---|
386 | "Couldn't initialize the cache of" |
---|
387 | " Berkeley DB environment descriptors"); |
---|
388 | } |
---|
389 | apr_pool_cleanup_register(bdb_cache_pool, NULL, clear_cache, |
---|
390 | apr_pool_cleanup_null); |
---|
391 | #endif /* APR_HAS_THREADS */ |
---|
392 | |
---|
393 | return SVN_NO_ERROR; |
---|
394 | } |
---|
395 | |
---|
396 | svn_error_t * |
---|
397 | svn_fs_bdb__init(apr_pool_t* pool) |
---|
398 | { |
---|
399 | return svn_atomic__init_once(&bdb_cache_state, bdb_init_cb, pool); |
---|
400 | } |
---|
401 | |
---|
402 | static APR_INLINE void |
---|
403 | acquire_cache_mutex(void) |
---|
404 | { |
---|
405 | #if APR_HAS_THREADS |
---|
406 | if (bdb_cache_lock) |
---|
407 | apr_thread_mutex_lock(bdb_cache_lock); |
---|
408 | #endif |
---|
409 | } |
---|
410 | |
---|
411 | |
---|
412 | static APR_INLINE void |
---|
413 | release_cache_mutex(void) |
---|
414 | { |
---|
415 | #if APR_HAS_THREADS |
---|
416 | if (bdb_cache_lock) |
---|
417 | apr_thread_mutex_unlock(bdb_cache_lock); |
---|
418 | #endif |
---|
419 | } |
---|
420 | |
---|
421 | |
---|
422 | /* Construct a cache key for the BDB environment at PATH in *KEYP. |
---|
423 | if DBCONFIG_FILE is not NULL, return the opened file handle. |
---|
424 | Allocate from POOL. */ |
---|
425 | static svn_error_t * |
---|
426 | bdb_cache_key(bdb_env_key_t *keyp, apr_file_t **dbconfig_file, |
---|
427 | const char *path, apr_pool_t *pool) |
---|
428 | { |
---|
429 | const char *dbcfg_file_name = svn_path_join(path, BDB_CONFIG_FILE, pool); |
---|
430 | apr_file_t *dbcfg_file; |
---|
431 | apr_status_t apr_err; |
---|
432 | apr_finfo_t finfo; |
---|
433 | |
---|
434 | SVN_ERR(svn_io_file_open(&dbcfg_file, dbcfg_file_name, |
---|
435 | APR_READ, APR_OS_DEFAULT, pool)); |
---|
436 | |
---|
437 | apr_err = apr_file_info_get(&finfo, APR_FINFO_DEV | APR_FINFO_INODE, |
---|
438 | dbcfg_file); |
---|
439 | if (apr_err) |
---|
440 | return svn_error_wrap_apr |
---|
441 | (apr_err, "Can't create BDB environment cache key"); |
---|
442 | |
---|
443 | /* Make sure that any padding in the key is always cleared, so that |
---|
444 | the key's hash deterministic. */ |
---|
445 | memset(keyp, 0, sizeof *keyp); |
---|
446 | keyp->device = finfo.device; |
---|
447 | keyp->inode = finfo.inode; |
---|
448 | |
---|
449 | if (dbconfig_file) |
---|
450 | *dbconfig_file = dbcfg_file; |
---|
451 | else |
---|
452 | apr_file_close(dbcfg_file); |
---|
453 | |
---|
454 | return SVN_NO_ERROR; |
---|
455 | } |
---|
456 | |
---|
457 | |
---|
458 | /* Find a BDB environment in the cache. |
---|
459 | Return the environment's panic state in *PANICP. |
---|
460 | |
---|
461 | Note: You MUST acquire the cache mutex before calling this function. |
---|
462 | */ |
---|
463 | static bdb_env_t * |
---|
464 | bdb_cache_get(const bdb_env_key_t *keyp, svn_boolean_t *panicp) |
---|
465 | { |
---|
466 | bdb_env_t *bdb = apr_hash_get(bdb_cache, keyp, sizeof *keyp); |
---|
467 | if (bdb && bdb->env) |
---|
468 | { |
---|
469 | *panicp = !!svn_atomic_read(&bdb->panic); |
---|
470 | #if SVN_BDB_VERSION_AT_LEAST(4,2) |
---|
471 | if (!*panicp) |
---|
472 | { |
---|
473 | u_int32_t flags; |
---|
474 | if (bdb->env->get_flags(bdb->env, &flags) |
---|
475 | || (flags & DB_PANIC_ENVIRONMENT)) |
---|
476 | { |
---|
477 | /* Something is wrong with the environment. */ |
---|
478 | svn_atomic_set(&bdb->panic, TRUE); |
---|
479 | *panicp = TRUE; |
---|
480 | bdb = NULL; |
---|
481 | } |
---|
482 | } |
---|
483 | #endif /* at least bdb-4.2 */ |
---|
484 | } |
---|
485 | else |
---|
486 | { |
---|
487 | *panicp = FALSE; |
---|
488 | } |
---|
489 | return bdb; |
---|
490 | } |
---|
491 | |
---|
492 | |
---|
493 | |
---|
494 | /* Close and destroy a BDB environment descriptor. */ |
---|
495 | static svn_error_t * |
---|
496 | bdb_close(bdb_env_t *bdb) |
---|
497 | { |
---|
498 | svn_error_t *err = SVN_NO_ERROR; |
---|
499 | |
---|
500 | /* This bit is delcate; we must propagate the error from |
---|
501 | DB_ENV->close to the caller, and always destroy the pool. */ |
---|
502 | int db_err = bdb->env->close(bdb->env, 0); |
---|
503 | |
---|
504 | /* If automatic database recovery is enabled, ignore DB_RUNRECOVERY |
---|
505 | errors, since they're dealt with eventually by BDB itself. */ |
---|
506 | if (db_err && (!SVN_BDB_AUTO_RECOVER || db_err != DB_RUNRECOVERY)) |
---|
507 | err = convert_bdb_error(bdb, db_err); |
---|
508 | |
---|
509 | /* Free the environment descriptor. The pool cleanup will do this unless |
---|
510 | the cache has already been destroyed. */ |
---|
511 | if (bdb->pool) |
---|
512 | svn_pool_destroy(bdb->pool); |
---|
513 | else |
---|
514 | free(bdb); |
---|
515 | return err; |
---|
516 | } |
---|
517 | |
---|
518 | |
---|
519 | svn_error_t * |
---|
520 | svn_fs_bdb__close(bdb_env_baton_t *bdb_baton) |
---|
521 | { |
---|
522 | svn_error_t *err = SVN_NO_ERROR; |
---|
523 | bdb_env_t *bdb = bdb_baton->bdb; |
---|
524 | |
---|
525 | SVN_ERR_ASSERT(bdb_baton->env == bdb_baton->bdb->env); |
---|
526 | |
---|
527 | /* Neutralize bdb_baton's pool cleanup to prevent double-close. See |
---|
528 | cleanup_env_baton(). */ |
---|
529 | bdb_baton->bdb = NULL; |
---|
530 | |
---|
531 | /* Note that we only bother with this cleanup if the pool is non-NULL, to |
---|
532 | guard against potential races between this and the cleanup_env cleanup |
---|
533 | callback. It's not clear if that can actually happen, but better safe |
---|
534 | than sorry. */ |
---|
535 | if (0 == --bdb_baton->error_info->refcount && bdb->pool) |
---|
536 | { |
---|
537 | svn_error_clear(bdb_baton->error_info->pending_errors); |
---|
538 | #if APR_HAS_THREADS |
---|
539 | free(bdb_baton->error_info); |
---|
540 | apr_threadkey_private_set(NULL, bdb->error_info); |
---|
541 | #endif |
---|
542 | } |
---|
543 | |
---|
544 | acquire_cache_mutex(); |
---|
545 | if (--bdb->refcount != 0) |
---|
546 | { |
---|
547 | release_cache_mutex(); |
---|
548 | |
---|
549 | /* If the environment is panicked and automatic recovery is not |
---|
550 | enabled, return an appropriate error. */ |
---|
551 | #if !SVN_BDB_AUTO_RECOVER |
---|
552 | if (svn_atomic_read(&bdb->panic)) |
---|
553 | err = svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, |
---|
554 | db_strerror(DB_RUNRECOVERY)); |
---|
555 | #endif |
---|
556 | } |
---|
557 | else |
---|
558 | { |
---|
559 | /* If the bdb cache has been set to NULL that means we are |
---|
560 | shutting down, and the pool that holds the bdb cache has |
---|
561 | already been destroyed, so accessing it here would be a Bad |
---|
562 | Thing (tm) */ |
---|
563 | if (bdb_cache) |
---|
564 | apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, NULL); |
---|
565 | err = bdb_close(bdb); |
---|
566 | release_cache_mutex(); |
---|
567 | } |
---|
568 | return err; |
---|
569 | } |
---|
570 | |
---|
571 | |
---|
572 | |
---|
573 | /* Open and initialize a BDB environment. */ |
---|
574 | static svn_error_t * |
---|
575 | bdb_open(bdb_env_t *bdb, u_int32_t flags, int mode) |
---|
576 | { |
---|
577 | #if APR_HAS_THREADS |
---|
578 | flags |= DB_THREAD; |
---|
579 | #endif |
---|
580 | SVN_ERR(convert_bdb_error |
---|
581 | (bdb, (bdb->env->open)(bdb->env, bdb->path_bdb, flags, mode))); |
---|
582 | |
---|
583 | #if SVN_BDB_AUTO_COMMIT |
---|
584 | /* Assert the BDB_AUTO_COMMIT flag on the opened environment. This |
---|
585 | will force all operations on the environment (and handles that |
---|
586 | are opened within the environment) to be transactional. */ |
---|
587 | |
---|
588 | SVN_ERR(convert_bdb_error |
---|
589 | (bdb, bdb->env->set_flags(bdb->env, SVN_BDB_AUTO_COMMIT, 1))); |
---|
590 | #endif |
---|
591 | |
---|
592 | return bdb_cache_key(&bdb->key, &bdb->dbconfig_file, |
---|
593 | bdb->path, bdb->pool); |
---|
594 | } |
---|
595 | |
---|
596 | |
---|
597 | /* Pool cleanup for the environment baton. */ |
---|
598 | static apr_status_t |
---|
599 | cleanup_env_baton(void *data) |
---|
600 | { |
---|
601 | bdb_env_baton_t *bdb_baton = data; |
---|
602 | |
---|
603 | if (bdb_baton->bdb) |
---|
604 | svn_error_clear(svn_fs_bdb__close(bdb_baton)); |
---|
605 | |
---|
606 | return APR_SUCCESS; |
---|
607 | } |
---|
608 | |
---|
609 | |
---|
610 | svn_error_t * |
---|
611 | svn_fs_bdb__open(bdb_env_baton_t **bdb_batonp, const char *path, |
---|
612 | u_int32_t flags, int mode, |
---|
613 | apr_pool_t *pool) |
---|
614 | { |
---|
615 | svn_error_t *err = SVN_NO_ERROR; |
---|
616 | bdb_env_key_t key; |
---|
617 | bdb_env_t *bdb; |
---|
618 | svn_boolean_t panic; |
---|
619 | |
---|
620 | acquire_cache_mutex(); |
---|
621 | |
---|
622 | /* We can safely discard the open DB_CONFIG file handle. If the |
---|
623 | environment descriptor is in the cache, the key's immutability is |
---|
624 | guaranteed. If it's not, we don't care if the key changes, |
---|
625 | between here and the actual insertion of the newly-created |
---|
626 | environment into the cache, because no other thread can touch the |
---|
627 | cache in the meantime. */ |
---|
628 | err = bdb_cache_key(&key, NULL, path, pool); |
---|
629 | if (err) |
---|
630 | { |
---|
631 | release_cache_mutex(); |
---|
632 | return err; |
---|
633 | } |
---|
634 | |
---|
635 | bdb = bdb_cache_get(&key, &panic); |
---|
636 | if (panic) |
---|
637 | { |
---|
638 | release_cache_mutex(); |
---|
639 | return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, |
---|
640 | db_strerror(DB_RUNRECOVERY)); |
---|
641 | } |
---|
642 | |
---|
643 | /* Make sure that the environment's open flags haven't changed. */ |
---|
644 | if (bdb && bdb->flags != flags) |
---|
645 | { |
---|
646 | release_cache_mutex(); |
---|
647 | |
---|
648 | /* Handle changes to the DB_PRIVATE flag specially */ |
---|
649 | if ((flags ^ bdb->flags) & DB_PRIVATE) |
---|
650 | { |
---|
651 | if (flags & DB_PRIVATE) |
---|
652 | return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, |
---|
653 | "Reopening a public Berkeley DB" |
---|
654 | " environment with private attributes"); |
---|
655 | else |
---|
656 | return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, |
---|
657 | "Reopening a private Berkeley DB" |
---|
658 | " environment with public attributes"); |
---|
659 | } |
---|
660 | |
---|
661 | /* Otherwise return a generic "flags-mismatch" error. */ |
---|
662 | return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, |
---|
663 | "Reopening a Berkeley DB environment" |
---|
664 | " with different attributes"); |
---|
665 | } |
---|
666 | |
---|
667 | if (!bdb) |
---|
668 | { |
---|
669 | err = create_env(&bdb, path, svn_pool_create(bdb_cache_pool)); |
---|
670 | if (!err) |
---|
671 | { |
---|
672 | err = bdb_open(bdb, flags, mode); |
---|
673 | if (!err) |
---|
674 | { |
---|
675 | apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, bdb); |
---|
676 | bdb->flags = flags; |
---|
677 | bdb->refcount = 1; |
---|
678 | } |
---|
679 | else |
---|
680 | { |
---|
681 | /* Clean up, and we can't do anything about returned errors. */ |
---|
682 | svn_error_clear(bdb_close(bdb)); |
---|
683 | } |
---|
684 | } |
---|
685 | } |
---|
686 | else |
---|
687 | { |
---|
688 | ++bdb->refcount; |
---|
689 | } |
---|
690 | |
---|
691 | if (!err) |
---|
692 | { |
---|
693 | *bdb_batonp = apr_palloc(pool, sizeof **bdb_batonp); |
---|
694 | (*bdb_batonp)->env = bdb->env; |
---|
695 | (*bdb_batonp)->bdb = bdb; |
---|
696 | (*bdb_batonp)->error_info = get_error_info(bdb); |
---|
697 | ++(*bdb_batonp)->error_info->refcount; |
---|
698 | apr_pool_cleanup_register(pool, *bdb_batonp, cleanup_env_baton, |
---|
699 | apr_pool_cleanup_null); |
---|
700 | } |
---|
701 | |
---|
702 | release_cache_mutex(); |
---|
703 | return err; |
---|
704 | } |
---|
705 | |
---|
706 | |
---|
707 | svn_boolean_t |
---|
708 | svn_fs_bdb__get_panic(bdb_env_baton_t *bdb_baton) |
---|
709 | { |
---|
710 | /* An invalid baton is equivalent to a panicked environment; in both |
---|
711 | cases, database cleanups should be skipped. */ |
---|
712 | if (!bdb_baton->bdb) |
---|
713 | return TRUE; |
---|
714 | |
---|
715 | assert(bdb_baton->env == bdb_baton->bdb->env); |
---|
716 | return !!svn_atomic_read(&bdb_baton->bdb->panic); |
---|
717 | } |
---|
718 | |
---|
719 | void |
---|
720 | svn_fs_bdb__set_panic(bdb_env_baton_t *bdb_baton) |
---|
721 | { |
---|
722 | if (!bdb_baton->bdb) |
---|
723 | return; |
---|
724 | |
---|
725 | assert(bdb_baton->env == bdb_baton->bdb->env); |
---|
726 | svn_atomic_set(&bdb_baton->bdb->panic, TRUE); |
---|
727 | } |
---|
728 | |
---|
729 | |
---|
730 | /* This function doesn't actually open the environment, so it doesn't |
---|
731 | have to look in the cache. Callers are supposed to own an |
---|
732 | exclusive lock on the filesystem anyway. */ |
---|
733 | svn_error_t * |
---|
734 | svn_fs_bdb__remove(const char *path, apr_pool_t *pool) |
---|
735 | { |
---|
736 | bdb_env_t *bdb; |
---|
737 | |
---|
738 | SVN_ERR(create_env(&bdb, path, pool)); |
---|
739 | return convert_bdb_error |
---|
740 | (bdb, bdb->env->remove(bdb->env, bdb->path_bdb, DB_FORCE)); |
---|
741 | } |
---|