fis-gtm/sr_port/mupip_set_journal.c

837 lines
33 KiB
C

/****************************************************************
* *
* Copyright 2001, 2013 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 <stddef.h> /* for offsetof macro */
#if defined(VMS)
#include <climsgdef.h>
#include <fab.h>
#include <rms.h>
#include <errno.h>
#include <nam.h>
#include <psldef.h>
#include <rmsdef.h>
#include <descrip.h>
#include <math.h> /* needed for handling of epoch_interval (EPOCH_SECOND2SECOND macro uses ceil) */
#endif
#include "gtm_string.h"
#include "gtm_time.h"
#include "gtm_stdio.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "cli.h"
#include "iosp.h"
#include "jnl.h"
#include "mupip_set.h"
#include "eintr_wrappers.h"
#include "error.h"
#include "util.h"
#include "gvcst_protos.h" /* for gvcst_init prototype */
#include "mu_rndwn_file.h"
#include "dbfilop.h"
#include "gds_rundown.h"
#include "mu_gv_cur_reg_init.h"
#include "gtmmsg.h"
#include "wcs_flu.h"
#include "tp_change_reg.h"
#include "gtm_file_stat.h"
#include "min_max.h" /* for MAX and JNL_MAX_RECLEN macro */
#include "gtm_rename.h" /* for cre_jnl_file_intrpt_rename() prototype */
#include "send_msg.h"
#include "gtmio.h"
#include "is_file_identical.h"
#define DB_OR_REG_SIZE MAX(STR_LIT_LEN(FILE_STR), STR_LIT_LEN(REG_STR)) + 1 /* trailing null byte */
GBLREF bool region;
GBLREF gd_region *gv_cur_region;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF mu_set_rlist *grlist;
GBLREF char *before_image_lit[];
GBLREF char *jnl_state_lit[];
GBLREF char *repl_state_lit[];
GBLREF jnl_gbls_t jgbl;
error_def(ERR_BEFOREIMG);
error_def(ERR_DBFILERR);
error_def(ERR_DBOPNERR);
error_def(ERR_DBPRIVERR);
error_def(ERR_DBRDERR);
error_def(ERR_FILEEXISTS);
error_def(ERR_FILENAMETOOLONG);
error_def(ERR_FILEPARSE);
error_def(ERR_JNLALIGNTOOSM);
error_def(ERR_JNLALLOCGROW);
error_def(ERR_JNLBUFFDBUPD);
error_def(ERR_JNLBUFFREGUPD);
error_def(ERR_JNLCREATE);
error_def(ERR_JNLFNF);
error_def(ERR_JNLINVSWITCHLMT);
error_def(ERR_JNLNOCREATE);
error_def(ERR_JNLRDONLY);
error_def(ERR_JNLSTATE);
error_def(ERR_JNLSWITCHSZCHG);
error_def(ERR_JNLSWITCHTOOSM);
error_def(ERR_MUNOFINISH);
error_def(ERR_MUPCLIERR);
error_def(ERR_MUSTANDALONE);
error_def(ERR_PREVJNLLINKCUT);
error_def(ERR_REPLSTATE);
error_def(ERR_TEXT);
error_def(ERR_UNIMPLOP);
VMS_ONLY(static const unsigned short zero_fid[3];)
uint4 mupip_set_journal(unsigned short db_fn_len, char *db_fn)
{
jnl_create_info jnl_info;
GTMCRYPT_ONLY(
jnl_create_info *jnl_info_ptr;
)
GDS_INFO *gds_info;
file_control *fc;
int new_stat_res; /* gtm_file_stat() return value for new journal file name */
int curr_stat_res; /* gtm_file_stat() return value for current journal file name */
mu_set_rlist *rptr, dummy_rlist, *next_rptr;
sgmnt_data_ptr_t csd;
uint4 status, exit_status = EXIT_NRM, gds_rundown_status = EXIT_NRM;
seq_num max_reg_seqno;
unsigned int fn_len;
unsigned char tmp_full_jnl_fn[MAX_FN_LEN + 1], prev_jnl_fn[MAX_FN_LEN + 1];
char *db_reg_name, db_or_reg[DB_OR_REG_SIZE];
int db_reg_name_len, db_or_reg_len;
set_jnl_options jnl_options;
boolean_t curr_jnl_present, /* for current state 2, is current journal present? */
jnl_points_to_db, keep_prev_link, safe_to_switch, newjnlfiles, jnlname_same,
this_iter_prevlinkcut_error, do_prevlinkcut_error;
enum jnl_state_codes jnl_curr_state;
enum repl_state_codes repl_curr_state;
mstr jnlfile, jnldef, tmpjnlfile;
uint4 align_autoswitch;
jnl_private_control *jpc;
jnl_buffer_ptr_t jbp;
jnl_tm_t save_gbl_jrec_time;
# ifdef UNIX
int jnl_fd;
jnl_file_header header;
int4 status1;
uint4 status2;
boolean_t header_is_usable = FALSE;
# endif
boolean_t jnl_buffer_updated = FALSE, jnl_buffer_invalid = FALSE;
int jnl_buffer_size;
char s[JNLBUFFUPDAPNDX_SIZE]; /* JNLBUFFUPDAPNDX_SIZE is defined in jnl.h */
assert(SGMNT_HDR_LEN == ROUND_UP(SIZEOF(sgmnt_data), DISK_BLOCK_SIZE));
memset(&jnl_info, 0, SIZEOF(jnl_info));
jnl_info.status = jnl_info.status2 = SS_NORMAL;
max_reg_seqno = 1;
if (!mupip_set_journal_parse(&jnl_options, &jnl_info))
{
gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MUPCLIERR);
error_condition = ERR_MUPCLIERR;
return ERR_MUPCLIERR;
}
if (region && (NULL == grlist))
{ /* region and grlist are set by mu_getlst() invoked from mupip_set() */
assert(FALSE);
util_out_print("Invalid region name specified on command line. Can't continue any further", TRUE);
return (uint4)ERR_MUNOFINISH;
}
/* Now process the database file or region(s) */
if (region)
{ /* The command line specified one or more regions */
if ((NULL != grlist->fPtr) && jnl_options.filename_specified)
{
util_out_print("!/Multiple database regions cannot be journalled in a single file", TRUE);
return (uint4)ERR_MUNOFINISH;
}
} else
{ /* The command line specified a single database file; force the following do-loop to be one-trip */
dummy_rlist.fPtr = NULL;
grlist = &dummy_rlist;
}
ESTABLISH_RET(mupip_set_jnl_ch, (uint4)ERR_MUNOFINISH);
SET_GBL_JREC_TIME; /* set_jnl_file_close/cre_jnl_file/wcs_flu need gbl_jrec_time initialized */
for (rptr = grlist; (EXIT_ERR != exit_status) && NULL != rptr; rptr = rptr->fPtr)
{
rptr->exclusive = FALSE;
rptr->state = NONALLOCATED;
rptr->sd = NULL;
if (region)
{
if (dba_usr == rptr->reg->dyn.addr->acc_meth)
{
gtm_putmsg_csa(CSA_ARG(REG2CSA(rptr->reg)) VARLSTCNT(6) ERR_UNIMPLOP, 0, ERR_TEXT, 2,
LEN_AND_LIT("Journaling is not supported for access method USR"));
exit_status |= EXIT_WRN;
continue;
}
if (!mupfndfil(rptr->reg, NULL))
{
exit_status |= EXIT_ERR;
continue;
}
gv_cur_region = rptr->reg;
if (NULL == gv_cur_region->dyn.addr->file_cntl)
{
gv_cur_region->dyn.addr->acc_meth = dba_bg;
gv_cur_region->dyn.addr->file_cntl =
(file_control *)malloc(SIZEOF(*gv_cur_region->dyn.addr->file_cntl));
memset(gv_cur_region->dyn.addr->file_cntl, 0, SIZEOF(*gv_cur_region->dyn.addr->file_cntl));
gv_cur_region->dyn.addr->file_cntl->file_type = dba_bg;
gds_info = FILE_INFO(gv_cur_region);
gds_info = (GDS_INFO *)malloc(SIZEOF(GDS_INFO));
memset(gds_info, 0, SIZEOF(GDS_INFO));
}
} else
{
mu_gv_cur_reg_init();
rptr->reg = gv_cur_region;
gv_cur_region->dyn.addr->fname_len = db_fn_len;
memcpy(gv_cur_region->dyn.addr->fname, db_fn, db_fn_len);
}
fc = gv_cur_region->dyn.addr->file_cntl;
/* open shared to see what's possible */
gvcst_init(gv_cur_region);
tp_change_reg();
assert(!gv_cur_region->was_open);
rptr->sd = csd = cs_data;
rptr->state = ALLOCATED; /* This means gvcst_init() was called for this region */
assert(!gv_cur_region->was_open); /* In case mupip_set_file opened it, they must have closed */
if (gv_cur_region->read_only)
{
gtm_putmsg_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(4) ERR_DBPRIVERR, 2, DB_LEN_STR(gv_cur_region));
exit_status |= EXIT_RDONLY;
continue;
}
grab_crit(gv_cur_region); /* corresponding rel_crit() is done in mupip_set_jnl_cleanup() */
/* Now determine new journal state, replication state and before_image for this region.
* Information will be kept in "rptr", which is per region.
* Note that we have done grab_crit(), so we are safe on deciding state transitions */
if (EXIT_NRM != (status = mupip_set_journal_newstate(&jnl_options, &jnl_info, rptr)))
{
exit_status |= status;
UNIX_ONLY(gds_rundown_status =) gds_rundown();
exit_status |= gds_rundown_status;
rptr->sd = NULL;
rptr->state = NONALLOCATED; /* This means do not call gds_rundown() again for this region
* and do not process this region anymore. */
continue;
}
jnl_curr_state = (enum jnl_state_codes)csd->jnl_state;
repl_curr_state = (enum repl_state_codes)csd->repl_state;
/* Following is the transition table for replication states:
*
* repl_close repl_was_open repl_open
* --------------------------------------------------------
* repl_close - X S
* repl_was_open S - O
* repl_open S J -
*
* Where
* X ==> not allowed,
* S ==> Standalone access needed
* O ==> Online, standalone access is not needed.
* J ==> transition done by jnl_file_lost()
*/
if ((jnl_notallowed == jnl_curr_state && jnl_notallowed != rptr->jnl_new_state) ||
(jnl_notallowed != jnl_curr_state && jnl_notallowed == rptr->jnl_new_state) ||
(jnl_options.buffer_size_specified && (jnl_info.buffer != csd->jnl_buffer_size)) ||
(repl_closed != repl_curr_state && repl_closed == rptr->repl_new_state) ||
(repl_closed == repl_curr_state && repl_open == rptr->repl_new_state))
{
/* Since we did gvcst_init() and now will call mu_rndwn_file() */
UNIX_ONLY(gds_rundown_status =) gds_rundown();
exit_status |= gds_rundown_status;
rptr->state = NONALLOCATED;
rptr->sd = csd = NULL;
/* WARNING: The remaining code uses gv_cur_region and others
* on the assumption that gds_rundown does not deallocate the space when it closes the file */
assert(NULL != gv_cur_region);
assert(NULL != gv_cur_region->dyn.addr);
assert(NULL != gv_cur_region->dyn.addr->file_cntl);
assert(NULL != gv_cur_region->dyn.addr->file_cntl->file_info);
if (EXIT_NRM != gds_rundown_status) /* skip mu_rndwn_file (STANDALONE) */
continue;
if (STANDALONE(gv_cur_region))
{
rptr->exclusive = TRUE;
fc->op = FC_OPEN;
fc->file_type = dba_bg;
status = dbfilop(fc);
if (SS_NORMAL != status)
{
DBFILOP_FAIL_MSG(status, ERR_DBOPNERR);
exit_status |= EXIT_ERR;
continue;
}
/* Need to read file header again,
* because mu_rndwn_file does not have an interface to return fileheader */
csd = (sgmnt_data_ptr_t)malloc(SGMNT_HDR_LEN);
fc->op = FC_READ;
fc->op_buff = (sm_uc_ptr_t)csd;
fc->op_len = SGMNT_HDR_LEN;
fc->op_pos = 1;
status = dbfilop(fc);
if (SS_NORMAL != status)
{
DBFILOP_FAIL_MSG(status, ERR_DBRDERR);
fc->op = FC_CLOSE;
dbfilop(fc);
exit_status |= EXIT_ERR;
continue; /* Later mupip_set_jnl_cleanup() will do the cleanup */
}
cs_data = rptr->sd = csd;
rptr->state = ALLOCATED;
} else
{
gtm_putmsg_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(4) ERR_MUSTANDALONE, 2,
DB_LEN_STR(gv_cur_region));
exit_status |= EXIT_ERR;
continue;
}
} else
{
/* Now that we have crit, check if this region is actively journaled and if gbl_jrec_time needs to be
* adjusted (to ensure time ordering of journal records within this region's journal file).
* This needs to be done BEFORE writing any journal records for this region. The value of
* jgbl.gbl_jrec_time at the end of this loop will be used to write journal records for ALL
* regions so all regions will have same eov/bov timestamps.
*/
if (JNL_ENABLED(cs_data)
UNIX_ONLY( && (0 != cs_addrs->nl->jnl_file.u.inode))
VMS_ONLY( && (0 != memcmp(cs_addrs->nl->jnl_file.jnl_file_id.fid, zero_fid, SIZEOF(zero_fid)))))
{
jpc = cs_addrs->jnl;
jbp = jpc->jnl_buff;
ADJUST_GBL_JREC_TIME(jgbl, jbp);
}
}
if (max_reg_seqno < csd->reg_seqno)
max_reg_seqno = csd->reg_seqno;
}
DEBUG_ONLY(save_gbl_jrec_time = jgbl.gbl_jrec_time;)
jgbl.dont_reset_gbl_jrec_time = TRUE;
do_prevlinkcut_error = FALSE;
for (rptr = grlist; (EXIT_ERR != exit_status) && NULL != rptr; rptr = next_rptr)
{
this_iter_prevlinkcut_error = do_prevlinkcut_error;
do_prevlinkcut_error = FALSE;
next_rptr = rptr->fPtr;
gv_cur_region = rptr->reg;
if (gv_cur_region->read_only)
continue;
tp_change_reg(); /* cs_data and cs_addrs are used in functions called from here */
cs_data = csd = rptr->sd;
assert(NULL != csd || NONALLOCATED == rptr->state);
if (NULL == csd) /* Just to be safe. May be this is not necessary. */
continue;
jnl_curr_state = (enum jnl_state_codes)csd->jnl_state;
repl_curr_state = (enum repl_state_codes)csd->repl_state;
jnl_info.csd = csd;
jnl_info.csa = cs_addrs;
jnl_info.before_images = rptr->before_images;
jnl_info.repl_state = rptr->repl_new_state;
jnl_info.jnl_state = csd->jnl_state;
/* note that even replication on to off will create new journals */
newjnlfiles = (jnl_open == rptr->jnl_new_state) ? TRUE : FALSE;
fc = gv_cur_region->dyn.addr->file_cntl;
jnl_info.fn = gv_cur_region->dyn.addr->fname;
jnl_info.fn_len = gv_cur_region->dyn.addr->fname_len;
if (region)
{
strcpy(db_or_reg, REG_STR);
db_or_reg_len = SIZEOF(REG_STR) - 1;
db_reg_name = (char *)gv_cur_region->rname;
db_reg_name_len = (gv_cur_region)->rname_len;
} else
{
strcpy(db_or_reg, FILE_STR);
db_or_reg_len = SIZEOF(FILE_STR) - 1;
db_reg_name = (char *)jnl_info.fn;
db_reg_name_len = jnl_info.fn_len;
}
if (jnl_notallowed != rptr->jnl_new_state)
{ /* Fill in any unspecified information and process journal characteristics.
* If database contains journal characteristics (like allocation, alignsize etc.)
* which are smaller than the new journal minimums (increased in V54001), adjust
* them to ensure we honor the new minimums.
*/
if (!jnl_options.allocation_specified)
{
jnl_info.alloc = (0 == csd->jnl_alq) ? JNL_ALLOC_DEF : csd->jnl_alq;
assert(JNL_ALLOC_DEF >= JNL_ALLOC_MIN);
if (JNL_ALLOC_MIN > jnl_info.alloc)
jnl_info.alloc = JNL_ALLOC_MIN; /* Fix new journal settings. */
}
if (!jnl_options.alignsize_specified)
{
jnl_info.alignsize = (0 == csd->alignsize) ? (DISK_BLOCK_SIZE * JNL_DEF_ALIGNSIZE) :
csd->alignsize; /* In bytes */
assert(JNL_DEF_ALIGNSIZE >= JNL_MIN_ALIGNSIZE);
if ((DISK_BLOCK_SIZE * JNL_MIN_ALIGNSIZE) > jnl_info.alignsize)
jnl_info.alignsize = (DISK_BLOCK_SIZE * JNL_MIN_ALIGNSIZE); /* Fix new journal settings. */
}
if (jnl_info.alignsize <= csd->blk_size)
{
if (region)
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(9) ERR_JNLALIGNTOOSM, 7,
jnl_info.alignsize, csd->blk_size,
LEN_AND_LIT("region"), REG_LEN_STR(gv_cur_region),
(DISK_BLOCK_SIZE * JNL_DEF_ALIGNSIZE));
else
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(9) ERR_JNLALIGNTOOSM, 7,
jnl_info.alignsize, csd->blk_size,
LEN_AND_LIT("database file"), jnl_info.fn_len, jnl_info.fn,
(DISK_BLOCK_SIZE * JNL_DEF_ALIGNSIZE));
jnl_info.alignsize = (DISK_BLOCK_SIZE * JNL_DEF_ALIGNSIZE);
assert(jnl_info.alignsize > csd->blk_size); /* to accommodate a PBLK journal record */
}
if (!jnl_options.autoswitchlimit_specified)
{
jnl_info.autoswitchlimit = (0 == csd->autoswitchlimit) ?
JNL_AUTOSWITCHLIMIT_DEF : csd->autoswitchlimit;
assert(JNL_AUTOSWITCHLIMIT_MIN <= jnl_info.autoswitchlimit);
}
if (!jnl_options.extension_specified)
{
jnl_info.extend = (0 == csd->jnl_deq) ? JNL_EXTEND_DEF : csd->jnl_deq;
/* jnl_info.extend = (0 == csd->jnl_deq) ? jnl_info.alloc * JNL_EXTEND_DEF_PERC : csd->jnl_deq;
* Uncomment this section when code is ready to use extension = 10% of allocation */
}
if (!jnl_options.buffer_size_specified)
jnl_info.buffer = (0 == csd->jnl_buffer_size) ? JNL_BUFFER_DEF : csd->jnl_buffer_size;
ROUND_UP_JNL_BUFF_SIZE(jnl_buffer_size, jnl_info.buffer, csd);
if (jnl_buffer_size < JNL_BUFF_PORT_MIN(csd))
{
jnl_buffer_invalid = TRUE;
ROUND_UP_MIN_JNL_BUFF_SIZE(jnl_buffer_size, csd);
} else if (jnl_buffer_size > JNL_BUFFER_MAX)
{
jnl_buffer_invalid = TRUE;
ROUND_DOWN_MAX_JNL_BUFF_SIZE(jnl_buffer_size, csd);
}
if (jnl_buffer_size != jnl_info.buffer)
jnl_buffer_updated = TRUE;
/* ensure we have exclusive access in case csd->jnl_buffer_size is going to be changed */
assert(!(jnl_options.buffer_size_specified && jnl_buffer_updated) || rptr->exclusive);
if (!jnl_options.epoch_interval_specified)
jnl_info.epoch_interval = (0 == csd->epoch_interval) ? DEFAULT_EPOCH_INTERVAL : csd->epoch_interval;
JNL_MAX_RECLEN(&jnl_info, csd);
jnl_info.reg_seqno = max_reg_seqno;
jnl_info.prev_jnl = (char *)prev_jnl_fn;
jnl_info.prev_jnl_len = 0;
if (csd->jnl_file_len)
cre_jnl_file_intrpt_rename(((int)csd->jnl_file_len), csd->jnl_file_name);
assert(0 == csd->jnl_file_len || 0 == csd->jnl_file_name[csd->jnl_file_len]);
csd->jnl_file_name[csd->jnl_file_len] = 0;
if (!jnl_options.filename_specified)
mupip_set_journal_fname(&jnl_info);
/* the following autoswitchlimit check should be after the call to mupip_set_journal_fname()
* since it relies on jnl_info.jnl_len and jnl_info.jnl which is filled in by mupip_set_journal_fname()
*/
if (jnl_info.autoswitchlimit < jnl_info.alloc)
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_JNLSWITCHTOOSM, 5,
jnl_info.autoswitchlimit, jnl_info.alloc, DB_LEN_STR(gv_cur_region));
if (newjnlfiles)
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLNOCREATE, 2,
jnl_info.jnl_len, jnl_info.jnl);
exit_status |= EXIT_ERR;
break;
#ifdef UNIX
} else if (jnl_info.alloc + jnl_info.extend > jnl_info.autoswitchlimit
&& jnl_info.alloc != jnl_info.autoswitchlimit)
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_JNLALLOCGROW, 6, jnl_info.alloc,
jnl_info.autoswitchlimit, "database file", DB_LEN_STR(gv_cur_region));
jnl_info.alloc = jnl_info.autoswitchlimit;
#endif
} else
{
align_autoswitch = ALIGNED_ROUND_DOWN(jnl_info.autoswitchlimit, jnl_info.alloc, jnl_info.extend);
if (align_autoswitch != jnl_info.autoswitchlimit)
{ /* round down specified autoswitch to be aligned at a journal extension boundary.
* t_end/tp_tend earlier used to round up their transaction's journal space requirements
* to the nearest extension boundary to compare against the autoswitchlimit later.
* but now with autoswitchlimit being aligned at an extension boundary, they can
* compare their journal requirements directly against the autoswitchlimit.
*/
assert(align_autoswitch < jnl_info.autoswitchlimit || !jnl_info.extend);
if (jnl_options.autoswitchlimit_specified || jnl_options.extension_specified
|| jnl_options.allocation_specified)
{ /* print rounding down of autoswitchlimit only if journal options were specified */
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_JNLSWITCHSZCHG, 6,
jnl_info.autoswitchlimit, align_autoswitch,
jnl_info.alloc, jnl_info.extend, DB_LEN_STR(gv_cur_region));
}
jnl_info.autoswitchlimit = align_autoswitch;
if (JNL_AUTOSWITCHLIMIT_MIN > jnl_info.autoswitchlimit)
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_JNLINVSWITCHLMT, 3,
jnl_info.autoswitchlimit, JNL_AUTOSWITCHLIMIT_MIN, JNL_ALLOC_MAX);
exit_status |= EXIT_ERR;
break;
}
}
}
if (!jnl_options.yield_limit_specified)
jnl_options.yield_limit = csd->yield_lmt;
tmpjnlfile.addr = (char *)tmp_full_jnl_fn;
tmpjnlfile.len = SIZEOF(tmp_full_jnl_fn);
jnlfile.addr = (char *)jnl_info.jnl;
jnlfile.len = jnl_info.jnl_len;
jnldef.addr = JNL_EXT_DEF;
jnldef.len = SIZEOF(JNL_EXT_DEF) - 1;
if (FILE_STAT_ERROR == (new_stat_res = gtm_file_stat(&jnlfile, &jnldef, &tmpjnlfile, TRUE, &status)))
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_FILEPARSE, 2, jnlfile.len,
jnlfile.addr, status);
if (newjnlfiles)
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLNOCREATE, 2,
jnlfile.len, jnlfile.addr);
exit_status |= EXIT_ERR;
break;
}
memcpy(jnl_info.jnl, tmpjnlfile.addr, tmpjnlfile.len);
jnl_info.jnl_len = tmpjnlfile.len;
jnl_info.jnl[jnl_info.jnl_len] = 0;
/* Note: At this point jnlfile should have expanded journal name with extension */
if (MAX_FN_LEN + 1 < jnl_info.jnl_len)
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_FILENAMETOOLONG);
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLNOCREATE, 2,
jnl_info.jnl_len, jnl_info.jnl);
exit_status |= EXIT_ERR;
break;
}
jnlname_same = ((jnl_info.jnl_len == csd->jnl_file_len)
&& (0 == memcmp(jnl_info.jnl, csd->jnl_file_name, jnl_info.jnl_len))) ? TRUE : FALSE;
jnlfile.addr = (char *)csd->jnl_file_name;
jnlfile.len = csd->jnl_file_len;
if (!jnlname_same)
{
if (FILE_STAT_ERROR == (curr_stat_res = gtm_file_stat(&jnlfile, NULL, NULL, TRUE, &status)))
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_FILEPARSE, 2,
jnlfile.len, jnlfile.addr, status);
if (newjnlfiles)
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLNOCREATE, 2,
jnl_info.jnl_len, jnl_info.jnl);
exit_status |= EXIT_ERR;
break;
}
} else
curr_stat_res = new_stat_res; /* new_stat_res is already set */
# ifdef UNIX
if (newjnlfiles)
{
jnl_points_to_db = FALSE;
if (FILE_PRESENT & curr_stat_res)
{ /* Check if the journal file (if any) pointed to by the db file exists and points back to
* this database file.
*/
assert('\0' == jnlfile.addr[jnlfile.len]);
jnlfile.addr[jnlfile.len] = '\0'; /* just in case above assert is FALSE */
OPENFILE(jnlfile.addr, O_RDONLY, jnl_fd);
} else if (FILE_PRESENT & new_stat_res)
{ /* Check if the new journal file (that we know exists) points back to this database file.
* If not, the journal file prev links should be cut in the new journal file.
*/
assert('\0' == jnl_info.jnl[jnl_info.jnl_len]);
jnl_info.jnl[jnl_info.jnl_len] = '\0'; /* just in case above assert is FALSE */
OPENFILE((char *)jnl_info.jnl, O_RDONLY, jnl_fd);
} else
jnl_fd = FD_INVALID;
if (0 <= jnl_fd)
{
DO_FILE_READ(jnl_fd, 0, &header, SIZEOF(header), status1, status2);
if (SS_NORMAL == status1)
{
CHECK_JNL_FILE_IS_USABLE(&header, status1, FALSE, 0, NULL);
/* FALSE => NO gtm_putmsg even if errors */
if ((SS_NORMAL == status1)
&& ARRAYSIZE(header.data_file_name) > header.data_file_name_length)
{
assert('\0' == header.data_file_name[header.data_file_name_length]);
header.data_file_name[header.data_file_name_length] = '\0';
assert('\0' == jnl_info.fn[jnl_info.fn_len]);
jnl_info.fn[jnl_info.fn_len] = '\0';
if (is_file_identical((char *)header.data_file_name,
(char *)jnl_info.fn))
jnl_points_to_db = TRUE;
UNIX_ONLY(header_is_usable = TRUE;)
}
}
}
/* If journal file we are about to create exists, allow the switch only it is safe to do so.
* This way we prevent multiple environments from interfering with each other through a
* common journal file name. Also this way we disallow switching to a user-specified new
* journal file that already exists (say the database file itself due to a command line typo).
*/
if (FILE_PRESENT & curr_stat_res)
{
keep_prev_link = jnl_points_to_db;
safe_to_switch = (jnlname_same && keep_prev_link);
} else if (FILE_PRESENT & new_stat_res)
{
keep_prev_link = FALSE;
/* In this case, the current jnl file does not exist. And so the prevlinks are
* going to be cleared in the new jnl file. Therefore it is safe to rename the
* existing new jnl file (in order to create the jnl file we want) as long as
* it points back to this db.
*/
safe_to_switch = jnl_points_to_db;
} else
{
keep_prev_link = FALSE; /* since current and new jnl file both dont exist */
safe_to_switch = TRUE; /* since current and new jnl file both dont exist */
}
if ((FILE_PRESENT & new_stat_res) && !safe_to_switch)
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_FILEEXISTS, 2,
jnl_info.jnl_len, jnl_info.jnl);
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLNOCREATE, 2,
jnl_info.jnl_len, jnl_info.jnl);
exit_status |= EXIT_ERR;
break;
}
}
# else
keep_prev_link = TRUE;
# endif
if (jnl_open != jnl_curr_state)
curr_jnl_present = FALSE;
else
{ /* We expect that a journal file for this region is present when
* current journal state is jnl_open ("2"). See if it is really present.
*/
curr_jnl_present = (FILE_PRESENT & curr_stat_res) ? TRUE : FALSE;
if (curr_jnl_present)
{
if (FILE_READONLY & curr_stat_res)
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLRDONLY, 2,
JNL_LEN_STR(csd));
if (newjnlfiles)
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4)
ERR_JNLNOCREATE, 2, jnl_info.jnl_len, jnl_info.jnl);
exit_status |= EXIT_RDONLY;
continue;
}
} else
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLFNF, 2,
JNL_LEN_STR(csd));
}
if (!rptr->exclusive)
{
if (jnl_open == jnl_curr_state)
{
assert(NULL != cs_addrs->nl);
jpc = cs_addrs->jnl;
UNIX_ONLY(if (cs_addrs->nl->jnl_file.u.inode))
VMS_ONLY(if (memcmp(cs_addrs->nl->jnl_file.jnl_file_id.fid, zero_fid, SIZEOF(zero_fid))))
{
if (SS_NORMAL != (status = set_jnl_file_close(SET_JNL_FILE_CLOSE_SETJNL)))
{ /* Invoke jnl_file_lost to turn off journaling and retry journal creation
* to create fresh journal files.
*/
jnl_file_lost(jpc, status);
do_prevlinkcut_error = TRUE; /* In the next iteration issue PREVJNLLINKCUT
* information message */
next_rptr = rptr;
continue;
}
UNIX_ONLY(header.crash = FALSE;) /* Even if the journal was crashed, that
* should be fixed now */
} else
{ /* Ideally, no other process should have a journal file for this database open.
* But, As part of C9I03-002965, we realized it is possible for processes accessing
* the older journal file to continue to write to it even though
* csa->nl->jnl_file.u.inode field is 0. The only way to signal other proceses, that
* have the jnl file open, of a concurrent journal file switch, is by incrementing
* the "jpc->jnl_buff->cycle" field. Therefore be safe and increment this just in
* case some other process has the older generation journal file still open.
*/
assert(NULL != jpc);
jpc->jnl_buff->cycle++;
}
# ifdef UNIX
/* Cut the link if the journal is crashed and there is no shared memory around */
if (header_is_usable && header.crash)
keep_prev_link = FALSE;
# endif
/* For MM, set_jnl_file_close() can call wcs_flu() which can remap the file.
* So reset csd and rptr->sd since their value may have changed.
*/
assert((dba_mm == cs_data->acc_meth) || (csd == cs_data));
rptr->sd = csd = cs_data;
} else if ((jnl_closed == jnl_curr_state) && (jnl_open == rptr->jnl_new_state))
{ /* sync db for closed->open transition. for VMS WCSFLU_FSYNC_DB is ignored */
wcs_flu(WCSFLU_FSYNC_DB | WCSFLU_FLUSH_HDR);
/* In case this is MM and wcs_flu() remapped an extended database, reset csd and rptr->sd */
assert((dba_mm == cs_data->acc_meth) || (csd == cs_data));
rptr->sd = csd = cs_data;
}
}
if (newjnlfiles)
{
jnl_info.no_prev_link = TRUE;
jnl_info.no_rename = !(FILE_PRESENT & new_stat_res);
if (curr_jnl_present)
{
if (!(jnl_info.repl_state == repl_open && repl_open != repl_curr_state))
{ /* record the back link */
# ifdef VMS
if (!jnlname_same && (FILE_PRESENT & new_stat_res))
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4)
ERR_FILEEXISTS, 2, jnl_info.jnl_len, jnl_info.jnl);
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4)
ERR_JNLNOCREATE, 2, jnl_info.jnl_len, jnl_info.jnl);
exit_status |= EXIT_ERR;
break;
}
# endif
if (keep_prev_link)
{ /* Save journal link */
jnl_info.prev_jnl_len = csd->jnl_file_len;
memcpy(prev_jnl_fn, csd->jnl_file_name, jnl_info.prev_jnl_len);
prev_jnl_fn[jnl_info.prev_jnl_len] = '\0';
jnl_info.no_prev_link = FALSE;
}
}
}
if ((jnl_closed == jnl_curr_state) && (NULL != cs_addrs->nl))
{ /* Cleanup the jnl file info in shared memory before switching journal file.
This case occurs if mupip set -journal is run after jnl_file_lost() closes
journaling on a region */
NULLIFY_JNL_FILE_ID(cs_addrs);
}
jnl_info.blks_to_upgrd = csd->blks_to_upgrd;
jnl_info.free_blocks = csd->trans_hist.free_blocks;
jnl_info.total_blks = csd->trans_hist.total_blks;
GTMCRYPT_ONLY(
jnl_info_ptr = &jnl_info;
GTMCRYPT_COPY_HASH(csd, jnl_info_ptr);
)
if (EXIT_NRM != (status = cre_jnl_file(&jnl_info)))
{ /* There was an error attempting to create the journal file */
exit_status |= status;
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_JNLNOCREATE, 2,
jnl_info.jnl_len, jnl_info.jnl);
continue;
}
csd->jnl_checksum = jnl_info.checksum;
csd->jnl_eovtn = csd->trans_hist.curr_tn;
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(10) ERR_JNLCREATE, 8, jnl_info.jnl_len, jnl_info.jnl,
db_or_reg_len, db_or_reg, db_reg_name_len, db_reg_name,
LEN_AND_STR(before_image_lit[(jnl_info.before_images ? 1 : 0)]));
if ((!curr_jnl_present && (jnl_open == jnl_curr_state))
|| (curr_jnl_present && jnl_info.no_prev_link) || this_iter_prevlinkcut_error)
{
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) ERR_PREVJNLLINKCUT, 4,
jnl_info.jnl_len, jnl_info.jnl, DB_LEN_STR(gv_cur_region));
send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) ERR_PREVJNLLINKCUT, 4,
jnl_info.jnl_len, jnl_info.jnl, DB_LEN_STR(gv_cur_region));
}
}
/* Following jnl_before_image, jnl_state, repl_state are unique charecteristics per region */
csd->jnl_before_image = jnl_info.before_images;
csd->jnl_state = rptr->jnl_new_state;
csd->repl_state = jnl_info.repl_state;
/* All fields below are same for all regions */
csd->jnl_alq = jnl_info.alloc;
csd->alignsize = jnl_info.alignsize;
csd->autoswitchlimit = jnl_info.autoswitchlimit;
csd->jnl_buffer_size = jnl_buffer_size;
if (jnl_buffer_updated)
if (jnl_buffer_invalid)
{
SNPRINTF(s, JNLBUFFUPDAPNDX_SIZE, JNLBUFFUPDAPNDX, JNL_BUFF_PORT_MIN(csd), JNL_BUFFER_MAX);
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(10)
(region ? ERR_JNLBUFFREGUPD : ERR_JNLBUFFDBUPD), 4,
(region ? gv_cur_region->rname_len : jnl_info.fn_len),
(region ? gv_cur_region->rname : jnl_info.fn),
jnl_info.buffer, jnl_buffer_size, ERR_TEXT, 2, LEN_AND_STR(s));
} else
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6)
(region ? ERR_JNLBUFFREGUPD : ERR_JNLBUFFDBUPD), 4,
(region ? gv_cur_region->rname_len : jnl_info.fn_len),
(region ? gv_cur_region->rname : jnl_info.fn),
jnl_info.buffer, jnl_buffer_size);
csd->epoch_interval = jnl_info.epoch_interval;
csd->jnl_deq = jnl_info.extend;
memcpy(csd->jnl_file_name, jnl_info.jnl, jnl_info.jnl_len);
csd->jnl_file_len = jnl_info.jnl_len;
csd->jnl_file_name[jnl_info.jnl_len] = 0;
csd->reg_seqno = jnl_info.reg_seqno;
if (jnl_options.sync_io_specified)
csd->jnl_sync_io = jnl_options.sync_io;
UNIX_ONLY(csd->yield_lmt = jnl_options.yield_limit;)
} else
{ /* Journaling is to be disabled for this region. Reset all fields */
csd->jnl_before_image = FALSE;
csd->jnl_state = jnl_notallowed;
csd->repl_state = repl_closed;
csd->jnl_alq = 0;
csd->alignsize = 0;
csd->autoswitchlimit = 0;
csd->jnl_buffer_size = 0;
csd->epoch_interval = 0;
csd->jnl_deq = 0;
csd->jnl_file_len = 0;
csd->jnl_sync_io = FALSE;
UNIX_ONLY(csd->yield_lmt = DEFAULT_YIELD_LIMIT;)
}
if (CLI_ABSENT != jnl_options.cli_journal || CLI_ABSENT != jnl_options.cli_replic_on)
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_JNLSTATE, 6, db_or_reg_len, db_or_reg, db_reg_name_len,
db_reg_name, LEN_AND_STR(jnl_state_lit[rptr->jnl_new_state]));
if (CLI_ABSENT != jnl_options.cli_replic_on)
gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_REPLSTATE, 6, db_or_reg_len, db_or_reg, db_reg_name_len,
db_reg_name, LEN_AND_STR(repl_state_lit[jnl_info.repl_state]));
/* Write the updated information back to the database file */
fc->op = FC_WRITE;
fc->op_buff = (sm_uc_ptr_t)csd;
fc->op_len = SGMNT_HDR_LEN;
fc->op_pos = 1;
status = dbfilop(fc);
if (SS_NORMAL != status)
{
DBFILOP_FAIL_MSG(status, ERR_DBFILERR);
exit_status |= EXIT_ERR;
break;
}
}
jgbl.dont_reset_gbl_jrec_time = FALSE;
/* Ensure jgbl.gbl_jrec_time did not get reset by any of the jnl writing functions */
assert(save_gbl_jrec_time == jgbl.gbl_jrec_time);
REVERT;
mupip_set_jnl_cleanup();
if (EXIT_NRM == exit_status)
return (uint4)SS_NORMAL;
if (exit_status & EXIT_WRN)
return (uint4)MAKE_MSG_WARNING(ERR_MUNOFINISH);
return (uint4)ERR_MUNOFINISH;
}