diff options
Diffstat (limited to 'src/env/env_open.c')
-rw-r--r-- | src/env/env_open.c | 250 |
1 files changed, 160 insertions, 90 deletions
diff --git a/src/env/env_open.c b/src/env/env_open.c index 7eddca3a..85189369 100644 --- a/src/env/env_open.c +++ b/src/env/env_open.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1996, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ @@ -107,10 +107,16 @@ __env_open_pp(dbenv, db_home, flags, mode) __db_errx(env, DB_STR("1589", "DB_PRIVATE is not " "supported by 64-bit applications in " "mixed-size-addressing mode")); - return (EINVAL); - } + return (EINVAL); + } #endif + if (LF_ISSET(DB_PRIVATE) && PREFMAS_IS_SET(env)) { + __db_errx(env, DB_STR("1594", "DB_PRIVATE is not " + "supported in Replication Manager preferred master mode")); + return (EINVAL); + } + return (__env_open(dbenv, db_home, flags, mode)); } @@ -129,12 +135,20 @@ __env_open(dbenv, db_home, flags, mode) { DB_THREAD_INFO *ip; ENV *env; - u_int32_t orig_flags; - int register_recovery, ret, t_ret; + u_int32_t orig_flags, retry_flags; + int recovery_failed, register_recovery, ret, t_ret; + char *old_passwd; + size_t old_passwd_len; + u_int32_t old_encrypt_flags; ip = NULL; env = dbenv->env; + recovery_failed = 1; register_recovery = 0; + retry_flags = 0; + old_passwd = NULL; + old_passwd_len = 0; + old_encrypt_flags = 0; /* Initial configuration. */ if ((ret = __env_config(dbenv, db_home, &flags, mode)) != 0) @@ -171,13 +185,27 @@ __env_open(dbenv, db_home, flags, mode) dbenv->is_alive = __envreg_isalive; } - if ((ret = - __envreg_register(env, ®ister_recovery, flags)) != 0) + /* + * Backup the current key, because it would be consumed by + * __envreg_register below + */ + if (dbenv->passwd != NULL) { + if ((ret = __os_strdup(env, dbenv->passwd, &old_passwd)) != 0) + goto err; + old_passwd_len = dbenv->passwd_len; + (void)__env_get_encrypt_flags(dbenv, &old_encrypt_flags); + } + + F_SET(dbenv, DB_ENV_NOPANIC); + ret = __envreg_register(env, ®ister_recovery, flags); + dbenv->flags = orig_flags; + if (ret != 0) goto err; if (register_recovery) { if (!LF_ISSET(DB_RECOVER)) { __db_errx(env, DB_STR("1567", "The DB_RECOVER flag was not specified, and recovery is needed")); + recovery_failed = 0; ret = DB_RUNRECOVERY; goto err; } @@ -197,16 +225,27 @@ __env_open(dbenv, db_home, flags, mode) * want to remove files left over for any reason, from any session. */ retry: if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL)) + if ( #ifdef HAVE_REPLICATION - if ((ret = __rep_reset_init(env)) != 0 || - (ret = __env_remove_env(env)) != 0 || -#else - if ((ret = __env_remove_env(env)) != 0 || + (ret = __rep_reset_init(env)) != 0 || #endif - (ret = __env_refresh(dbenv, orig_flags, 0)) != 0) + (ret = __env_remove_env(env)) != 0 || + (ret = __env_refresh(dbenv, + orig_flags | retry_flags, 0)) != 0) goto err; - if ((ret = __env_attach_regions(dbenv, flags, orig_flags, 1)) != 0) + /* Restore the database key. */ + if (LF_ISSET(DB_REGISTER) && old_passwd != NULL) { + ret = __env_set_encrypt(dbenv, old_passwd, old_encrypt_flags); + memset(old_passwd, 0xff, old_passwd_len - 1); + __os_free(env, old_passwd); + if (ret != 0) + goto err; + } + + DB_ASSERT(env, ret == 0); + if ((ret = __env_attach_regions(dbenv, + flags, orig_flags | retry_flags, 1)) != 0) goto err; /* @@ -216,8 +255,18 @@ retry: if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL)) */ if (LF_ISSET(DB_FAILCHK) && !register_recovery) { ENV_ENTER(env, ip); - if ((ret = __env_failchk_int(dbenv)) != 0) + /* + * Set the thread state so that any waiting for a potentially + * dead thread will call is_alive() in order to avoid hanging. + */ + FAILCHK_THREAD(env, ip); + ret = __env_failchk_int(dbenv); + if (ret != 0) { + __db_err(env, ret, + DB_STR("1595", + "failchk crash after clean registry")); goto err; + } ENV_LEAVE(env, ip); } @@ -230,12 +279,12 @@ err: if (ret != 0) * processes can now proceed. * * If recovery failed, unregister now and let another process - * clean up. + * clean up and run recovery. */ if (ret == 0 && (t_ret = __envreg_xunlock(env)) != 0) ret = t_ret; if (ret != 0) - (void)__envreg_unregister(env, 1); + (void)__envreg_unregister(env, recovery_failed); } /* @@ -247,7 +296,11 @@ err: if (ret != 0) */ if (ret == DB_RUNRECOVERY && !register_recovery && !LF_ISSET(DB_RECOVER) && LF_ISSET(DB_REGISTER)) { + if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER)) + __db_msg(env, DB_STR("1596", + "env_open DB_REGISTER w/o RECOVER panic: trying w/recovery")); LF_SET(DB_RECOVER); + retry_flags = DB_ENV_NOPANIC; goto retry; } @@ -304,6 +357,9 @@ __env_open_arg(dbenv, flags) "replication requires transaction support")); return (EINVAL); } + if ((ret = + __log_set_config_int(dbenv, DB_LOG_BLOB, 1, 1)) != 0) + return (ret); } if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL)) { if ((ret = __db_fcchk(env, @@ -349,30 +405,6 @@ __env_open_arg(dbenv, flags) } #endif -#ifdef HAVE_MUTEX_FCNTL - /* - * !!! - * We need a file descriptor for fcntl(2) locking. We use the file - * handle from the REGENV file for this purpose. - * - * Since we may be using shared memory regions, e.g., shmget(2), and - * not a mapped-in regular file, the backing file may be only a few - * bytes in length. So, this depends on the ability to call fcntl to - * lock file offsets much larger than the actual physical file. I - * think that's safe -- besides, very few systems actually need this - * kind of support, SunOS is the only one still in wide use of which - * I'm aware. - * - * The error case is if an application lacks spinlocks and wants to be - * threaded. That doesn't work because fcntl will lock the underlying - * process, including all its threads. - */ - if (F_ISSET(env, ENV_THREAD)) { - __db_errx(env, DB_STR("1578", - "architecture lacks fast mutexes: applications cannot be threaded")); - return (EINVAL); - } -#endif return (ret); } @@ -506,7 +538,7 @@ __env_close_pp(dbenv, flags) { DB_THREAD_INFO *ip; ENV *env; - int rep_check, ret, t_ret; + int ret, t_ret; u_int32_t close_flags, flags_orig; env = dbenv->env; @@ -517,65 +549,75 @@ __env_close_pp(dbenv, flags) * Validate arguments, but as a DB_ENV handle destructor, we can't * fail. */ - if (flags != 0 && flags != DB_FORCESYNC && - (t_ret = __db_ferr(env, "DB_ENV->close", 0)) != 0 && ret == 0) - ret = t_ret; +#undef OKFLAGS +#define OKFLAGS (DB_FORCESYNC | DB_FORCESYNCENV) + + ret = __db_fchk(env, "DB_ENV->close", flags, OKFLAGS); #define DBENV_FORCESYNC 0x00000001 #define DBENV_CLOSE_REPCHECK 0x00000010 - if (flags == DB_FORCESYNC) + if (LF_ISSET(DB_FORCESYNC)) close_flags |= DBENV_FORCESYNC; + if (LF_ISSET(DB_FORCESYNCENV)) + F_SET(env, ENV_FORCESYNCENV); + + /* + * Call __env_close() to clean up resources even though the open + * didn't fully succeed. + * */ + if (!F_ISSET(env, ENV_OPEN_CALLED)) + goto do_close; /* * If the environment has panic'd, all we do is try and discard * the important resources. */ if (PANIC_ISSET(env)) { + /* + * Temporarily set no panic so we do not trigger the + * LAST_PANIC_CHECK_BEFORE_IO check in __os_physwrite thus + * allowing the unregister to happen correctly. + */ + flags_orig = dbenv->flags; + F_SET(dbenv, DB_ENV_NOPANIC); + ENV_ENTER(env, ip); /* clean up from registry file */ - if (dbenv->registry != NULL) { - /* - * Temporarily set no panic so we do not trigger the - * LAST_PANIC_CHECK_BEFORE_IO check in __os_physwr - * thus allowing the unregister to happen correctly. - */ - flags_orig = F_ISSET(dbenv, DB_ENV_NOPANIC); - F_SET(dbenv, DB_ENV_NOPANIC); + if (dbenv->registry != NULL) (void)__envreg_unregister(env, 0); - dbenv->registry = NULL; - if (!flags_orig) - F_CLR(dbenv, DB_ENV_NOPANIC); - } /* Close all underlying threads and sockets. */ - if (IS_ENV_REPLICATED(env)) - (void)__repmgr_close(env); + (void)__repmgr_close(env); /* Close all underlying file handles. */ (void)__file_handle_cleanup(env); + ENV_LEAVE(env, ip); + + dbenv->flags = flags_orig; + (void)__env_region_cleanup(env); - PANIC_CHECK(env); + return (__env_panic_msg(env)); } ENV_ENTER(env, ip); - rep_check = IS_ENV_REPLICATED(env) ? 1 : 0; - if (rep_check) { #ifdef HAVE_REPLICATION_THREADS - /* - * Shut down Replication Manager threads first of all. This - * must be done before __env_rep_enter to avoid a deadlock that - * could occur if repmgr's background threads try to do a rep - * operation that needs __rep_lockout. - */ - if ((t_ret = __repmgr_close(env)) != 0 && ret == 0) - ret = t_ret; + /* + * Shut down Replication Manager threads first of all. This + * must be done before __env_rep_enter to avoid a deadlock that + * could occur if repmgr's background threads try to do a rep + * operation that needs __rep_lockout. + */ + if ((t_ret = __repmgr_close(env)) != 0 && ret == 0) + ret = t_ret; #endif + if (IS_ENV_REPLICATED(env)) { if ((t_ret = __env_rep_enter(env, 0)) != 0 && ret == 0) ret = t_ret; + if (ret == 0) + close_flags |= DBENV_CLOSE_REPCHECK; } - if (rep_check) - close_flags |= DBENV_CLOSE_REPCHECK; +do_close: if ((t_ret = __env_close(dbenv, close_flags)) != 0 && ret == 0) ret = t_ret; @@ -640,8 +682,11 @@ __env_close(dbenv, flags) t_ret = dbp->alt_close(dbp, close_flags); else t_ret = __db_close(dbp, NULL, close_flags); - if (t_ret != 0 && ret == 0) - ret = t_ret; + if (t_ret != 0) { + if (ret == 0) + ret = t_ret; + break; + } } /* @@ -661,10 +706,8 @@ __env_close(dbenv, flags) #endif /* If we're registered, clean up. */ - if (dbenv->registry != NULL) { + if (dbenv->registry != NULL) (void)__envreg_unregister(env, 0); - dbenv->registry = NULL; - } /* Check we've closed all underlying file handles. */ if ((t_ret = __file_handle_cleanup(env)) != 0 && ret == 0) @@ -680,6 +723,9 @@ __env_close(dbenv, flags) if (dbenv->db_md_dir != NULL) __os_free(env, dbenv->db_md_dir); dbenv->db_md_dir = NULL; + if (dbenv->db_blob_dir != NULL) + __os_free(env, dbenv->db_blob_dir); + dbenv->db_blob_dir = NULL; if (dbenv->db_data_dir != NULL) { for (p = dbenv->db_data_dir; *p != NULL; ++p) __os_free(env, *p); @@ -761,9 +807,7 @@ __env_refresh(dbenv, orig_flags, rep_check) ret = t_ret; } - /* Discard the DB_ENV, ENV handle mutexes. */ - if ((t_ret = __mutex_free(env, &dbenv->mtx_db_env)) != 0 && ret == 0) - ret = t_ret; + /* Discard the ENV handle mutex. */ if ((t_ret = __mutex_free(env, &env->mtx_env)) != 0 && ret == 0) ret = t_ret; @@ -936,17 +980,38 @@ __file_handle_cleanup(env) ENV *env; { DB_FH *fhp; + DB_MPOOL *dbmp; + u_int i; - if (TAILQ_FIRST(&env->fdlist) == NULL) + if (TAILQ_EMPTY(&env->fdlist)) return (0); - __db_errx(env, DB_STR("1581", - "File handles still open at environment close")); + __db_errx(env, + DB_STR("1581", "File handles still open at environment close")); while ((fhp = TAILQ_FIRST(&env->fdlist)) != NULL) { - __db_errx(env, DB_STR_A("1582", "Open file handle: %s", "%s"), - fhp->name); - (void)__os_closehandle(env, fhp); + __db_errx(env, + DB_STR_A("1582", "Open file handle: %s", "%s"), fhp->name); + if (__os_closehandle(env, fhp) != 0) + break; } + if (env->lockfhp != NULL) + env->lockfhp = NULL; + /* Invalidate saved pointers to the regions' files: all are closed. */ + if (env->reginfo != NULL) + env->reginfo->fhp = NULL; + if (env->lg_handle != NULL) + env->lg_handle->reginfo.fhp = NULL; + if (env->lk_handle != NULL) + env->lk_handle->reginfo.fhp = NULL; +#ifdef HAVE_MUTEX_SUPPORT + if (env->mutex_handle != NULL) + env->mutex_handle->reginfo.fhp = NULL; +#endif + if (env->tx_handle != NULL) + env->tx_handle->reginfo.fhp = NULL; + if ((dbmp = env->mp_handle) != NULL && dbmp->reginfo != NULL) + for (i = 0; i < env->dbenv->mp_ncache; ++i) + dbmp->reginfo[i].fhp = NULL; return (EINVAL); } @@ -1109,11 +1174,9 @@ __env_attach_regions(dbenv, flags, orig_flags, retry_ok) goto err; /* - * Initialize the handle mutexes. + * Initialize the handle mutex. */ if ((ret = __mutex_alloc(env, - MTX_ENV_HANDLE, DB_MUTEX_PROCESS_ONLY, &dbenv->mtx_db_env)) != 0 || - (ret = __mutex_alloc(env, MTX_ENV_HANDLE, DB_MUTEX_PROCESS_ONLY, &env->mtx_env)) != 0) goto err; @@ -1125,8 +1188,15 @@ __env_attach_regions(dbenv, flags, orig_flags, retry_ok) goto err; rep_check = IS_ENV_REPLICATED(env) ? 1 : 0; - if (rep_check && (ret = __env_rep_enter(env, 0)) != 0) + if (rep_check && (ret = __env_rep_enter(env, 0)) != 0) { + /* + * If we get an error we didn't increment handle_cnt, + * so we don't want to decrement it later. Turn off + * rep_check here. + */ + rep_check = 0; goto err; + } if (LF_ISSET(DB_INIT_MPOOL)) { if ((ret = __memp_open(env, create_ok)) != 0) |