fis-gtm/sr_unix/mu_rndwn_file.c

901 lines
34 KiB
C

/****************************************************************
* *
* Copyright 2001, 2011 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_ipc.h"
#include "gtm_string.h"
#include "gtm_time.h"
#include "gtm_unistd.h"
#include "gtm_stdio.h"
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include "gtm_sem.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gtmio.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "iosp.h"
#include "jnl.h"
#include "mutex.h"
#include "lockconst.h"
#include "interlock.h"
#include "aswp.h"
#include "gtm_c_stack_trace.h"
#include "eintr_wrappers.h"
#include "eintr_wrapper_semop.h"
#include "mu_rndwn_file.h"
#include "performcaslatchcheck.h"
#include "util.h"
#include "send_msg.h"
#include "tp_change_reg.h"
#include "dbfilop.h"
#include "gvcst_protos.h" /* for gvcst_init_sysops prototype */
#include "do_semop.h"
#include "ipcrmid.h"
#include "rc_cpt_ops.h"
#include "gtmmsg.h"
#include "wcs_flu.h"
#include "do_shmat.h"
#include "is_file_identical.h"
#include "io.h"
#include "gtmsecshr.h"
#include "ftok_sems.h"
#include "mu_rndwn_all.h"
#include "error.h"
#ifdef GTM_CRYPT
#include "gtmcrypt.h"
#endif
#include "db_snapshot.h"
#include "shmpool.h" /* Needed for the shmpool structures */
#include "is_proc_alive.h"
#include "ss_lock_facility.h"
#ifndef GTM_SNAPSHOT
# error "Snapshot facility not available in this platform"
#endif
GBLREF sgmnt_addrs *cs_addrs;
GBLREF gd_region *gv_cur_region;
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF uint4 process_id;
GBLREF boolean_t mupip_jnl_recover;
GBLREF ipcs_mesg db_ipcs;
GBLREF gd_region *standalone_reg;
GBLREF jnl_gbls_t jgbl;
GBLREF boolean_t mu_rndwn_file_dbjnl_flush;
GBLREF gd_region *ftok_sem_reg;
static gd_region *rundown_reg = NULL;
static gd_region *temp_region;
static sgmnt_data_ptr_t temp_cs_data;
static sgmnt_addrs *temp_cs_addrs;
static boolean_t restore_rndwn_gbl;
LITREF char gtm_release_name[];
LITREF int4 gtm_release_name_len;
error_def(ERR_BADDBVER);
error_def(ERR_DBFILERR);
error_def(ERR_DBNOTGDS);
error_def(ERR_DBRDONLY);
error_def(ERR_DBNAMEMISMATCH);
error_def(ERR_DBIDMISMATCH);
error_def(ERR_DBSHMNAMEDIFF);
error_def(ERR_TEXT);
error_def(ERR_VERMISMATCH);
error_def(ERR_SYSCALL);
error_def(ERR_SHMREMOVED);
error_def(ERR_SEMREMOVED);
#define RESET_GV_CUR_REGION \
{ \
gv_cur_region = temp_region; \
cs_addrs = temp_cs_addrs; \
cs_data = temp_cs_data; \
}
#define CLNUP_AND_RETURN(REG, UDI, TSD, SEM_CREATED, SEM_INCREMENTED) \
{ \
int rc; \
\
if (FD_INVALID != UDI->fd) \
{ \
CLOSEFILE_RESET(UDI->fd, rc); \
assert(FD_INVALID == UDI->fd); \
} \
if (NULL != TSD) \
{ \
free(TSD); \
TSD = NULL; \
} \
if (SEM_CREATED) \
{ \
if (-1 == semctl(UDI->semid, 0, IPC_RMID)) \
{ \
RNDWN_ERR("!AD -> Error removing semaphore.", REG); \
} else \
{ \
send_msg(VARLSTCNT(3) ERR_SEMREMOVED, 1, UDI->semid); \
SEM_CREATED = FALSE; \
} \
} else if (SEM_INCREMENTED) \
{ \
do_semop(udi->semid, 0, -1, IPC_NOWAIT | SEM_UNDO); \
SEM_INCREMENTED = FALSE; \
} \
REVERT; \
assert((NULL == ftok_sem_reg) || (REG == ftok_sem_reg)); \
if (REG == ftok_sem_reg) \
ftok_sem_release(REG, TRUE, TRUE); \
if (restore_rndwn_gbl) \
{ \
RESET_GV_CUR_REGION; \
restore_rndwn_gbl = FALSE; \
} \
return FALSE; \
}
#define SEG_SHMATTACH(addr, reg, udi, tsd, sem_created, sem_incremented) \
{ \
if (-1 == (sm_long_t)(cs_addrs->db_addrs[0] = (sm_uc_ptr_t) \
do_shmat(udi->shmid, addr, SHM_RND))) \
{ \
if (EINVAL != errno) \
RNDWN_ERR("!AD -> Error attaching to shared memory", (reg)); \
/* shared memory segment no longer exists */ \
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented); \
} \
}
#define SEG_MEMMAP(addr, reg, udi, tsd, sem_created, sem_incremented) \
{ \
if (-1 == (sm_long_t)(cs_addrs->db_addrs[0] = (sm_uc_ptr_t)mmap((caddr_t)addr, \
(size_t)stat_buf.st_size, PROT_READ | PROT_WRITE, GTM_MM_FLAGS, udi->fd, (off_t)0))) \
{ \
RNDWN_ERR("!AD -> Error mapping memory", (reg)); \
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented); \
} \
}
#define REMOVE_SEMID_IF_ORPHANED(REG, UDI, TSD, SEM_CREATED, SEM_INCREMENTED) \
{ \
assert(!standalone); \
if (is_orphaned_gtm_semaphore(UDI->semid)) \
{ \
if (0 != sem_rmid(UDI->semid)) \
{ \
RNDWN_ERR("!AD -> Error removing semaphore.", reg); \
CLNUP_AND_RETURN(REG, UDI, TSD, SEM_CREATED, SEM_INCREMENTED); \
} \
send_msg(VARLSTCNT(3) ERR_SEMREMOVED, 1, UDI->semid); \
UDI->semid = INVALID_SEMID; \
} \
}
/*
* Description:
* This routine is used for two reasons
* 1) get standalone access:
* First uses/creates ftok semaphore.
* Then create a new semaphore for that region.
* Releases ftok semaphore (does not remove).
* 2) rundown shared memory
* Uses/creates ftok semaphore.
* Parameters:
* standalone = TRUE => create semaphore to get standalone access
* standalone = FALSE => rundown shared memory
* Return Value:
* TRUE for success
* FALSE for failure
*/
bool mu_rndwn_file(gd_region *reg, bool standalone)
{
int status, save_errno, sopcnt, tsd_size, csd_size;
char now_running[MAX_REL_NAME];
boolean_t rc_cpt_removed = FALSE, sem_created = FALSE, sem_incremented = FALSE, is_gtm_shm;
boolean_t glob_sec_init, db_shm_in_sync, remove_shmid;
sgmnt_data_ptr_t csd, tsd = NULL;
jnl_private_control *jpc;
struct sembuf sop[4];
struct shmid_ds shm_buf;
file_control *fc;
unix_db_info *udi;
enum db_acc_method acc_meth;
struct stat stat_buf;
struct semid_ds semstat;
union semun semarg;
int semop_res, stat_res;
uint4 status_msg, ss_pid;
int rc;
GTMCRYPT_ONLY(
int init_status;
sgmnt_addrs *csa;
)
shm_snapshot_t *ss_shm_ptr;
gtm_uint64_t sec_size;
restore_rndwn_gbl = FALSE;
assert(!mupip_jnl_recover || standalone);
temp_region = gv_cur_region; /* save gv_cur_region wherever there is scope for it to be changed */
rundown_reg = gv_cur_region = reg;
# ifdef GTCM_RC
rc_cpt_removed = mupip_rundown_cpt();
# endif
fc = reg->dyn.addr->file_cntl;
fc->op = FC_OPEN;
status = dbfilop(fc);
gv_cur_region = temp_region;
udi = FILE_INFO(reg);
if (SS_NORMAL != status)
{
gtm_putmsg(VARLSTCNT(5) status, 2, DB_LEN_STR(reg), errno);
if (FD_INVALID != udi->fd) /* Since dbfilop failed, close udi->fd only if it was opened */
CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */
return FALSE;
}
/*
* read_only process cannot rundown database.
* read only process can succeed to get standalone access of the database,
* if the db is clean with no orphaned shared memory.
* Note: we use gtmsecshr for updating file header for semaphores id.
*/
if (reg->read_only && !standalone)
{
gtm_putmsg(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(reg));
CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */
return FALSE;
}
ESTABLISH_RET(mu_rndwn_file_ch, FALSE);
if (!ftok_sem_get(reg, TRUE, GTM_ID, !standalone))
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
/*
* Now we have standalone access of the database using ftok semaphore.
* Any other ftok conflicted database will suspend there operation at this point.
* At the end of this routine, we release ftok semaphore lock.
*/
/* We only need to read SIZEOF(sgmnt_data) here */
tsd_size = ROUND_UP(SIZEOF(sgmnt_data), DISK_BLOCK_SIZE);
tsd = (sgmnt_data_ptr_t)malloc(tsd_size);
LSEEKREAD(udi->fd, 0, tsd, tsd_size, status);
if (0 != status)
{
RNDWN_ERR("!AD -> Error reading from file.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
# ifdef GTM_CRYPT
/* During rundown gvcst_init is not called and hence we need to do the encryption setup here. */
/* We do some basic encryption initializations here.
* 1. Call hash check to figure out if the database file's hash matches with that of the db_key_file.
* 2. if the dat file is encrypted, then make a call to the encryption plugin with GTMCRYPT_GETKEY to obtain the
* symmetric key which can be used at later stages for encryption and decryption.
* 3. malloc csa->encrypted_blk_contents to be used in jnl_write_aimg_rec and dsk_write_nocache
*/
if (tsd->is_encrypted)
{
csa = &(udi->s_addrs);
INIT_PROC_ENCRYPTION(init_status);
if (0 == init_status)
INIT_DB_ENCRYPTION(reg->dyn.addr->fname, csa, tsd, init_status);
if (0 != init_status)
{
GC_GTM_PUTMSG(init_status, (reg->dyn.addr->fname));
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
}
# endif
udi->shmid = tsd->shmid;
udi->semid = tsd->semid;
udi->gt_sem_ctime = tsd->gt_sem_ctime.ctime;
udi->gt_shm_ctime = tsd->gt_shm_ctime.ctime;
if (standalone)
{
semarg.buf = &semstat;
if (INVALID_SEMID == udi->semid || -1 == semctl(udi->semid, 0, IPC_STAT, semarg) ||
tsd->gt_sem_ctime.ctime != semarg.buf->sem_ctime)
{
/*
* if no database access control semaphore id is found, or,
* if semaphore id is found but ctime does not match, create new semaphore.
*/
if (-1 == (udi->semid = semget(IPC_PRIVATE, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT)))
{
udi->semid = INVALID_SEMID;
RNDWN_ERR("!AD -> Error with semget with IPC_CREAT.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
sem_created = TRUE;
tsd->semid = udi->semid;
/*
* Set value of semaphore number 2 ( = FTOK_SEM_PER_ID - 1) as GTM_ID.
* In case we have orphaned semaphore for some reason, mupip rundown will be
* able to identify GTM semaphores from the value and can remove.
*/
semarg.val = GTM_ID;
if (-1 == semctl(udi->semid, FTOK_SEM_PER_ID - 1, SETVAL, semarg))
{
RNDWN_ERR("!AD -> Error with semctl with SETVAL.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
/*
* Warning: We must read the sem_ctime after SETVAL, which changes it.
* We must not do any more SETVAL after this.
* We consider sem_ctime as creation time of semaphore.
*/
semarg.buf = &semstat;
if (-1 == semctl(udi->semid, FTOK_SEM_PER_ID - 1, IPC_STAT, semarg))
{
RNDWN_ERR("!AD -> Error with semctl with IPC_STAT.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
udi->gt_sem_ctime = tsd->gt_sem_ctime.ctime = semarg.buf->sem_ctime;
}
/* Now lock the database using access control semaphore and increment counter */
sop[0].sem_num = 0; sop[0].sem_op = 0;
sop[1].sem_num = 0; sop[1].sem_op = 1;
sop[2].sem_num = 1; sop[2].sem_op = 0;
sop[3].sem_num = 1; sop[3].sem_op = 1;
sopcnt = 4;
sop[0].sem_flg = sop[1].sem_flg = sop[2].sem_flg = sop[3].sem_flg = SEM_UNDO | IPC_NOWAIT;
SEMOP(udi->semid, sop, sopcnt, semop_res, NO_WAIT);
if (-1 == semop_res)
{
RNDWN_ERR("!AD -> File already open by another process.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
/* At this point, we have the the database access control lock.
* We also incremented the counter semaphore.
* We also have the ftok semaphore lock acquired.
*/
sem_incremented = TRUE;
}
/* Now rundown database if shared memory segment exists.
* We try this for both values of "standalone"
*/
if (INVALID_SHMID == udi->shmid || -1 == shmctl(udi->shmid, IPC_STAT, &shm_buf) ||
tsd->gt_shm_ctime.ctime != shm_buf.shm_ctime) /* if no shared memory exists */
{
if (rc_cpt_removed)
{ /* reset RC values if we've rundown the RC CPT */
/* attempt to force-write header */
tsd->rc_srv_cnt = tsd->dsid = tsd->rc_node = 0;
tsd->owner_node = 0;
assert(FALSE); /* not sure what to do here. handle it if/when it happens */
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
} else
{ /* Note that if creation time does not match, we ignore that shared memory segment.
* It might result in orphaned shared memory segment.
*/
tsd->shmid = udi->shmid = INVALID_SHMID;
tsd->gt_shm_ctime.ctime = udi->gt_shm_ctime = 0;
if (standalone)
{
/* Reset ipc fields in file header */
if (!reg->read_only)
{
if (mupip_jnl_recover)
{
memset(tsd->machine_name, 0, MAX_MCNAMELEN);
tsd->freeze = 0;
tsd->owner_node = 0;
}
LSEEKWRITE(udi->fd, (off_t)0, tsd, tsd_size, status);
if (0 != status)
{
RNDWN_ERR("!AD -> Unable to write header to disk.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
} else
{
db_ipcs.semid = tsd->semid;
db_ipcs.gt_sem_ctime = tsd->gt_sem_ctime.ctime;
db_ipcs.shmid = tsd->shmid;
db_ipcs.gt_shm_ctime = tsd->gt_shm_ctime.ctime;
if (!get_full_path((char *)DB_STR_LEN(reg), db_ipcs.fn, &db_ipcs.fn_len,
MAX_TRANS_NAME_LEN, &status_msg))
{
gtm_putmsg(VARLSTCNT(1) status_msg);
RNDWN_ERR("!AD -> get_full_path failed.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
db_ipcs.fn[db_ipcs.fn_len] = 0;
if (0 != send_mesg2gtmsecshr(FLUSH_DB_IPCS_INFO, 0, (char *)NULL, 0))
{
RNDWN_ERR("!AD -> gtmsecshr was unable to write header to disk.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
}
if (!ftok_sem_release(reg, FALSE, FALSE))
{
RNDWN_ERR("!AD -> Error from ftok_sem_release.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */
REVERT;
free(tsd);
standalone_reg = reg;
return TRUE; /* For "standalone" and "no shared memory existing", we exit here */
} else
{ /* We are here for not standalone (basically the "mupip rundown" command).
* We have not created any new semaphore and no shared memory was existing.
* So just reset the file header ipc fields and exit.
*/
assert(!sem_created);
/* Eventhough no shared memory exists at this point, it is possible that we have an
* orphaned semaphore. If so, remove it.
*/
REMOVE_SEMID_IF_ORPHANED(reg, udi, tsd, sem_created, sem_incremented);
memset(tsd->machine_name, 0, MAX_MCNAMELEN);
tsd->freeze = 0;
tsd->owner_node = 0;
tsd->shmid = INVALID_SHMID;
tsd->semid = INVALID_SEMID;
tsd->gt_sem_ctime.ctime = 0;
tsd->gt_shm_ctime.ctime = 0;
}
}
assert(!standalone);
LSEEKWRITE(udi->fd, (off_t)0, tsd, tsd_size, status);
if (0 != status)
{
RNDWN_ERR("!AD -> Unable to write header to disk.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */
free(tsd);
REVERT;
/* For mupip rundown (standalone = FALSE), we release/remove ftok semaphore here. */
if (!ftok_sem_release(reg, TRUE, TRUE))
{
RNDWN_ERR("!AD -> Error from ftok_sem_release.", reg);
assert(!sem_created);
return FALSE;
}
return TRUE; /* For "!standalone" and "no shared memory existing", we exit here */
}
/*
* shared memory already exists, so now find the number of users attached to it
* if it is zero, it needs to be flushed to disk, otherwise, we cannot help.
*/
if (reg->read_only) /* read only process can't succeed beyond this point */
{
gtm_putmsg(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(reg));
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
/* Now we have a pre-existing shared memory section with no other processes attached. Do some setup */
if (memcmp(tsd->label, GDS_LABEL, GDS_LABEL_SZ - 1))
{
if (memcmp(tsd->label, GDS_LABEL, GDS_LABEL_SZ - 3))
gtm_putmsg(VARLSTCNT(4) ERR_DBNOTGDS, 2, DB_LEN_STR(reg));
else
gtm_putmsg(VARLSTCNT(4) ERR_BADDBVER, 2, DB_LEN_STR(reg));
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
reg->dyn.addr->acc_meth = acc_meth = tsd->acc_meth;
dbsecspc(reg, tsd, &sec_size);
# ifdef __MVS__
/* match gvcst_init_sysops.c shmget with __IPC_MEGA or _LP64 */
if (ROUND_UP(sec_size, MEGA_BOUND) != shm_buf.shm_segsz)
# else
if (sec_size != shm_buf.shm_segsz)
# endif
{
util_out_print("Expected shared memory size is !SL, but found !SL",
TRUE, sec_size, shm_buf.shm_segsz);
util_out_print("!AD -> Existing shared memory size do not match.", TRUE, DB_LEN_STR(reg));
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
/*
* save gv_cur_region, cs_data, cs_addrs, and restore them on return
*/
temp_region = gv_cur_region;
temp_cs_data = cs_data;
temp_cs_addrs = cs_addrs;
restore_rndwn_gbl = TRUE;
gv_cur_region = reg;
tp_change_reg();
SEG_SHMATTACH(0, reg, udi, tsd, sem_created, sem_incremented);
cs_addrs->nl = (node_local_ptr_t)cs_addrs->db_addrs[0];
/* The following checks for GDS_LABEL_GENERIC, gtm_release_name, and cs_addrs->nl->glob_sec_init ensure that the
* shared memory under consideration is valid. First, since cs_addrs->nl->label is in the same place for every
* version, a failing check means it is most likely NOT a GT.M created shared memory, so no attempt will be
* made to delete it. Next a successful match of the currently running release will guarantee that all fields in
* the structure are where they're expected to be -- without this check the glob_sec_init check should not be done
* as this field is at different offsets in different versions. Finally, we can check the glob_sec_init flag to
* verify that shared memory has been completely initialized. If not, we should delete it right away. [C9D07-002355]
*/
if (!MEMCMP_LIT(cs_addrs->nl->label, GDS_LABEL_GENERIC))
{
is_gtm_shm = TRUE;
remove_shmid = TRUE;
memcpy(now_running, cs_addrs->nl->now_running, MAX_REL_NAME);
if (memcmp(now_running, gtm_release_name, gtm_release_name_len + 1))
{
gtm_putmsg(VARLSTCNT(8) ERR_VERMISMATCH, 6, DB_LEN_STR(reg), gtm_release_name_len,
gtm_release_name, LEN_AND_STR(now_running));
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
if (cs_addrs->nl->glob_sec_init)
{
glob_sec_init = TRUE;
if (memcmp(cs_addrs->nl->label, GDS_LABEL, GDS_LABEL_SZ - 1))
{
if (memcmp(cs_addrs->nl->label, GDS_LABEL, GDS_LABEL_SZ - 3))
gtm_putmsg(VARLSTCNT(8) ERR_DBNOTGDS, 2, DB_LEN_STR(reg),
ERR_TEXT, 2, RTS_ERROR_LITERAL("(from shared segment - nl)"));
else
gtm_putmsg(VARLSTCNT(8) ERR_BADDBVER, 2, DB_LEN_STR(reg),
ERR_TEXT, 2, RTS_ERROR_LITERAL("(from shared segment - nl)"));
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
/* Since nl is memset to 0 initially and then fname is copied over from gv_cur_region and since "fname" is
* guaranteed to not exceed MAX_FN_LEN, we should have a terminating '\0' at least at
* cs_addrs->nl->fname[MAX_FN_LEN]
*/
assert(cs_addrs->nl->fname[MAX_FN_LEN] == '\0'); /* First '\0' in cs_addrs->nl->fname can be earlier */
db_shm_in_sync = TRUE; /* assume this unless proven otherwise */
/* Check whether cs_addrs->nl->fname exists. If not, then db & shm are not in sync. */
STAT_FILE((char *)cs_addrs->nl->fname, &stat_buf, stat_res);
if (-1 == stat_res)
{
save_errno = errno;
db_shm_in_sync = FALSE;
if( ENOENT == save_errno)
{
send_msg(VARLSTCNT(7) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg),
udi->shmid, cs_addrs->nl->fname, save_errno);
gtm_putmsg(VARLSTCNT(6) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg),
udi->shmid, cs_addrs->nl->fname);
/* In this case, the shared memory no longer points to a valid db file in the filesystem.
* So it is best that we remove this shmid. But remove_shmid is already TRUE. Assert that.*/
assert(remove_shmid);
}
else /*Could be permission issue*/
{
send_msg(VARLSTCNT(7) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg),
udi->shmid, cs_addrs->nl->fname, save_errno);
gtm_putmsg(VARLSTCNT(7) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg),
udi->shmid, cs_addrs->nl->fname, save_errno);
remove_shmid = FALSE; /* Shared memory might be pointing to valid database */
}
}
if (db_shm_in_sync)
{ /* Check if csa->nl->fname and csa->nl->dbfid are in sync. If not, then db & shm are not in sync */
if (FALSE == is_gdid_stat_identical(&cs_addrs->nl->unique_id.uid, &stat_buf))
{
send_msg(VARLSTCNT(10) MAKE_MSG_INFO(ERR_DBIDMISMATCH), 4, cs_addrs->nl->fname,
DB_LEN_STR(reg), udi->shmid, ERR_TEXT, 2,
LEN_AND_LIT("[MUPIP] Database filename and fileid in shared memory are not in sync"));
gtm_putmsg(VARLSTCNT(10) MAKE_MSG_INFO(ERR_DBIDMISMATCH), 4, cs_addrs->nl->fname,
DB_LEN_STR(reg), udi->shmid, ERR_TEXT, 2,
LEN_AND_LIT("[MUPIP] Database filename and fileid in shared memory are not in sync"));
db_shm_in_sync = FALSE;
/* In this case, the shared memory points to a file that exists in the filesystem but
* the fileid of the currently present file does not match the file id recorded in
* shared memory at shm creation time. Therefore the database file has since been
* overwritten. In this case, we have no other option but to remove this shmid.
* remove_shmid is already TRUE. Assert that.
*/
assert(remove_shmid);
}
}
/* Check if reg->dyn.addr->fname and csa->nl->fname are identical filenames.
* If not, then db that we want to rundown points to a shared memory which in turn points
* to a DIFFERENT db file. In this case, do NOT remove the shared memory as it is possible
* some other database file on the system points to it and passes the filename identicality check.
*/
if (db_shm_in_sync && !is_file_identical((char *)cs_addrs->nl->fname, (char *)reg->dyn.addr->fname))
{
send_msg(VARLSTCNT(6) MAKE_MSG_INFO(ERR_DBSHMNAMEDIFF), 4, DB_LEN_STR(reg),
udi->shmid, cs_addrs->nl->fname);
gtm_putmsg(VARLSTCNT(6) MAKE_MSG_INFO(ERR_DBSHMNAMEDIFF), 4, DB_LEN_STR(reg),
udi->shmid, cs_addrs->nl->fname);
db_shm_in_sync = FALSE;
remove_shmid = FALSE;
}
/* If db & shm are not in sync at this point, skip the part of flushing shm to db file on disk.
* We still need to reset the fields (shmid, semid etc.) in db file header.
* About deleting the shmid, it depends on the type of out-of-sync between db & shm. This is handled
* by setting the variable "remove_shmid" appropriately in each case.
*/
if (db_shm_in_sync)
{
/* If db & shm are in sync AND we aren't alone in using it, we can do nothing */
if (0 != shm_buf.shm_nattch)
{
util_out_print("!AD [!UL]-> File is in use by another process.",
TRUE, DB_LEN_STR(reg), udi->shmid);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
/* The shared section is valid and up-to-date with respect to the database file header;
* ignore the temporary storage and use the shared section from here on
*/
cs_addrs->critical = (mutex_struct_ptr_t)(cs_addrs->db_addrs[0] + NODE_LOCAL_SIZE);
assert(((INTPTR_T)cs_addrs->critical & 0xf) == 0); /* critical should be 16-byte aligned */
# ifdef CACHELINE_SIZE
assert(0 == ((INTPTR_T)cs_addrs->critical & (CACHELINE_SIZE - 1)));
# endif
JNL_INIT(cs_addrs, reg, tsd);
cs_addrs->shmpool_buffer = (shmpool_buff_hdr_ptr_t)(cs_addrs->db_addrs[0] + NODE_LOCAL_SPACE +
JNL_SHARE_SIZE(tsd));
cs_addrs->lock_addrs[0] = (sm_uc_ptr_t)cs_addrs->shmpool_buffer + SHMPOOL_SECTION_SIZE;
cs_addrs->lock_addrs[1] = cs_addrs->lock_addrs[0] + LOCK_SPACE_SIZE(tsd) - 1;
if (dba_bg == acc_meth)
cs_data = csd = cs_addrs->hdr = (sgmnt_data_ptr_t)(cs_addrs->lock_addrs[1] + 1 +
CACHE_CONTROL_SIZE(tsd));
else
{
cs_addrs->acc_meth.mm.mmblk_state = (mmblk_que_heads_ptr_t)(cs_addrs->lock_addrs[1] + 1);
FSTAT_FILE(udi->fd, &stat_buf, stat_res);
if (-1 == stat_res)
{
RNDWN_ERR("!AD -> Error with fstat.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
SEG_MEMMAP(NULL, reg, udi, tsd, sem_created, sem_incremented);
cs_data = csd = cs_addrs->hdr = (sgmnt_data_ptr_t)cs_addrs->db_addrs[0];
cs_addrs->db_addrs[1] = cs_addrs->db_addrs[0] + stat_buf.st_size - 1;
}
if (sem_created)
{
csd->semid = tsd->semid;
csd->gt_sem_ctime.ctime = tsd->gt_sem_ctime.ctime;
}
assert(JNL_ALLOWED(csd) == JNL_ALLOWED(tsd));
free(tsd);
tsd = NULL;
/* Check to see that the fileheader in the shared segment is valid, so we wont end up
* flushing garbage to the db file.
* Check the label in the header - keep adding any further appropriate checks in future here.
*/
if (memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 1))
{
if (memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 3))
{
status_msg = ERR_DBNOTGDS;
if (0 != shm_rmid(udi->shmid))
gtm_putmsg(VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg),
ERR_TEXT, 2, RTS_ERROR_TEXT("Error removing shared memory"));
else
send_msg(VARLSTCNT(3) ERR_SHMREMOVED, 1, udi->shmid);
} else
status_msg = ERR_BADDBVER;
gtm_putmsg(VARLSTCNT(8) status_msg, 2, DB_LEN_STR(reg),
ERR_TEXT, 2, RTS_ERROR_LITERAL("(File header in the shared segment seems corrupt)"));
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
if (dba_bg == acc_meth)
db_csh_ini(cs_addrs);
else
cs_addrs->acc_meth.mm.base_addr = (sm_uc_ptr_t)((sm_ulong_t)csd + (int)(csd->start_vbn - 1)
* DISK_BLOCK_SIZE);
db_common_init(reg, cs_addrs, csd); /* do initialization common to "db_init" and "mu_rndwn_file" */
/* cleanup mutex stuff */
cs_addrs->hdr->image_count = 0;
gtm_mutex_init(reg, NUM_CRIT_ENTRY, FALSE); /* it is ensured, this is the only process running */
assert(!cs_addrs->hold_onto_crit); /* so it is safe to do unconditional grab_crit/rel_crit below */
cs_addrs->nl->in_crit = 0;
cs_addrs->now_crit = FALSE;
reg->open = TRUE;
if (rc_cpt_removed) /* reset RC values if we've rundown the RC CPT */
csd->rc_srv_cnt = csd->dsid = csd->rc_node = 0;
mu_rndwn_file_dbjnl_flush = TRUE; /* indicate to wcs_recover() no need to write inctn or increment
db curr_tn */
/* If an orphaned snapshot is lying around, then clean it up */
/* SS_MULTI: If multiple snapshots are supported, then we must run-through each of the
* "possible" snapshots in progress and clean them up
*/
assert(1 == MAX_SNAPSHOTS);
ss_get_lock(gv_cur_region); /* Snapshot initiator will wait until this cleanup is done */
ss_shm_ptr = (shm_snapshot_ptr_t)SS_GETSTARTPTR(cs_addrs);
ss_pid = ss_shm_ptr->ss_info.ss_pid;
if (ss_pid && !is_proc_alive(ss_pid, 0))
ss_release(NULL);
ss_release_lock(gv_cur_region);
/* If csa->nl->donotflush_dbjnl is set, it means mupip recover/rollback was interrupted and
* therefore we should not flush shared memory contents to disk as they might be in an inconsistent
* state. In this case, we will go ahead and remove shared memory (without flushing the contents)
* in this routine. A reissue of the recover/rollback command will restore the database to a
* consistent state.
*/
if (!cs_addrs->nl->donotflush_dbjnl)
{
if (cs_addrs->nl->glob_sec_init)
{ /* WCSFLU_NONE only is done here, as we aren't sure of the state, so no EPOCHs are
* written. If we write an EPOCH record, recover may get confused. Note that for
* journaling we do not call jnl_file_close() with TRUE for second parameter.
* As a result journal file might not have an EOF record.
* So, a new process will switch the journal file and cut the journal file link,
* though it might be a good journal without an EOF
*/
wcs_flu(WCSFLU_NONE);
csd = cs_addrs->hdr;
}
jpc = cs_addrs->jnl;
if (NULL != jpc)
{
grab_crit(gv_cur_region);
/* If we own it or owner died, clear the fsync lock */
if (process_id == jpc->jnl_buff->fsync_in_prog_latch.u.parts.latch_pid)
{
RELEASE_SWAPLOCK(&jpc->jnl_buff->fsync_in_prog_latch);
} else
performCASLatchCheck(&jpc->jnl_buff->fsync_in_prog_latch, FALSE);
if (NOJNL != jpc->channel)
jnl_file_close(gv_cur_region, FALSE, FALSE);
free(jpc);
cs_addrs->jnl = NULL;
rel_crit(gv_cur_region);
}
}
/* Write master-map (in addition to file header) as wcs_flu done above would not have updated it */
csd_size = SIZEOF_FILE_HDR(csd); /* SIZEOF(sgmnt_data) + master-map */
} else
{ /* In this case, we just want to clear a few fields in the file header but we do NOT want to
* write the master map which we dont have access to at this point so set csd_size accordingly.
*/
csd = tsd;
csd_size = tsd_size; /* SIZEOF(sgmnt_data) */
}
mu_rndwn_file_dbjnl_flush = FALSE;
reg->open = FALSE;
/* Note: At this point we have write permission */
memset(csd->machine_name, 0, MAX_MCNAMELEN);
csd->owner_node = 0;
csd->freeze = 0;
csd->shmid = INVALID_SHMID;
csd->gt_shm_ctime.ctime = 0;
if (!standalone)
{
csd->semid = INVALID_SEMID;
csd->gt_sem_ctime.ctime = 0;
}
if (!db_shm_in_sync || (dba_bg == acc_meth))
{
LSEEKWRITE(udi->fd, (off_t)0, csd, csd_size, status);
if (0 != status)
{
RNDWN_ERR("!AD -> Error writing header to disk.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
if (NULL != tsd)
{
assert(!db_shm_in_sync);
free(tsd);
tsd = NULL;
}
} else
{
if (-1 == msync((caddr_t)cs_addrs->db_addrs[0], (size_t)stat_buf.st_size, MS_SYNC))
{
RNDWN_ERR("!AD -> Error synchronizing mapped memory.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
if (-1 == munmap((caddr_t)cs_addrs->db_addrs[0], (size_t)stat_buf.st_size))
{
RNDWN_ERR("!AD -> Error unmapping mapped memory.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
}
} else
glob_sec_init = FALSE;
} else
{
is_gtm_shm = FALSE;
assert(FALSE);
}
/* Detach from shared memory whether it is a GT.M shared memory or not */
if (-1 == shmdt((caddr_t)cs_addrs->nl))
{
assert(FALSE);
RNDWN_ERR("!AD -> Error detaching from shared memory.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
cs_addrs->nl = NULL;
/* Remove the shared memory only if it is a GT.M created one. */
if (is_gtm_shm)
{
if (remove_shmid) /* Note: remove_shmid is defined only if is_gtm_shm is TRUE */
{
if (0 != shm_rmid(udi->shmid))
{
assert(FALSE);
RNDWN_ERR("!AD -> Error removing shared memory.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
else
send_msg(VARLSTCNT(3) ERR_SHMREMOVED, 1, udi->shmid);
}
udi->shmid = INVALID_SHMID;
udi->gt_shm_ctime = 0;
}
/* Check if it is a GT.M created orphaned semaphore and if so remove it (only if standalone access is not requested) */
if (!standalone)
REMOVE_SEMID_IF_ORPHANED(reg, udi, tsd, sem_created, sem_incremented);
/* If the shared memory was found to a valid GT.M created segment and if it was properly initialized, we would
* have cleared shmid in the database file header to INVALID_SHMID. If not, do the reset now as we nevertheless
* dont want the database to connect to that non-GT.M-shmid or uninitialized-GT.M-shmid anymore.
*/
if (!is_gtm_shm || !glob_sec_init)
{
assert(NULL != tsd); /* should not have been freed */
assert(INVALID_SEMID != udi->semid);
tsd->shmid = udi->shmid = INVALID_SHMID;
tsd->gt_shm_ctime.ctime = udi->gt_shm_ctime = 0;
if (mupip_jnl_recover)
{
memset(tsd->machine_name, 0, MAX_MCNAMELEN);
tsd->freeze = 0;
tsd->owner_node = 0;
}
LSEEKWRITE(udi->fd, (off_t)0, tsd, tsd_size, status);
if (0 != status)
{
RNDWN_ERR("!AD -> Unable to write header to disk.", reg);
CLNUP_AND_RETURN(reg, udi, tsd, sem_created, sem_incremented);
}
free(tsd);
tsd = NULL;
}
assert(INVALID_SHMID == udi->shmid);
assert(0 == udi->gt_shm_ctime);
REVERT;
RESET_GV_CUR_REGION;
restore_rndwn_gbl = FALSE;
/* For mupip rundown, standalone == TRUE and we want to release/remove ftok semaphore.
* Otherwise, just release ftok semaphore lock. counter will be one more for this process.
* Exit handlers must take care of removing if necessary.
*/
if (!ftok_sem_release(reg, !standalone, !standalone))
return FALSE;
if (standalone)
standalone_reg = reg;
CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */
return TRUE;
}
CONDITION_HANDLER(mu_rndwn_file_ch)
{
unix_db_info *udi;
START_CH;
/* The mu_rndwn_file_ch was introduced in revision 1.54 to account for the ERR_DBIDMISMATCH rts_error that no longer
* exists. So, it is not expected that mu_rndwn_file_ch will ever be called. Add an assert(FALSE). If the assert does
* not trip after an year or two, we can remove this condition handler altogether. 2010/07
*/
assert(FALSE);
mu_rndwn_file_dbjnl_flush = FALSE;
assert(NULL != rundown_reg);
if (NULL != rundown_reg)
{
udi = FILE_INFO(rundown_reg);
if (udi->grabbed_ftok_sem)
ftok_sem_release(rundown_reg, FALSE, TRUE);
}
standalone_reg = NULL;
if (restore_rndwn_gbl)
{
RESET_GV_CUR_REGION;
}
NEXTCH;
}