fis-gtm/sr_port/mupip_downgrade.c

398 lines
14 KiB
C

/****************************************************************
* *
* Copyright 2005, 2012 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. *
* *
****************************************************************/
/* mupip_downgrade.c: Driver program to downgrade v5.0-000 database files to v4.x */
#include "mdef.h"
#ifdef UNIX
#include "gtm_stat.h"
#include "gtm_fcntl.h"
#include "gtm_unistd.h"
#include "eintr_wrappers.h"
#include "gtm_stdlib.h"
#else
#include <descrip.h>
#include <fab.h>
#include <ssdef.h>
#include <rms.h>
#include <iodef.h>
#include <efndef.h>
#endif
#include "gtm_string.h"
#ifdef __MVS__
#include "gtm_zos_io.h"
#endif
#include "gtmio.h"
#include "iosp.h"
#include "gdsroot.h"
#include "v15_gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "v15_gdsbt.h"
#include "gdsfhead.h"
#include "v15_gdsfhead.h"
#include "filestruct.h"
#include "v15_filestruct.h"
#include "jnl.h" /* For fd_type */
#include "error.h"
#include "util.h"
#include "gtmmsg.h"
#include "cli.h"
#include "repl_sp.h"
#include "mupip_exit.h"
#include "mupip_downgrade.h"
#include "mu_upgrd_dngrd_hdr.h"
#include "mu_upgrd_dngrd_confirmed.h"
#include "mu_outofband_setup.h"
#include "anticipatory_freeze.h"
#ifdef UNIX
#include "mu_all_version_standalone.h"
#endif
LITREF char gtm_release_name[];
LITREF int4 gtm_release_name_len;
UNIX_ONLY(static sem_info *sem_inf;)
UNIX_ONLY(static void mupip_downgrade_cleanup(void);)
error_def(ERR_BADDBVER);
error_def(ERR_DBFILOPERR);
error_def(ERR_DBNOTGDS);
error_def(ERR_DBOPNERR);
error_def(ERR_DBPREMATEOF);
error_def(ERR_PREMATEOF);
error_def(ERR_DBRDONLY);
error_def(ERR_MUINFOUINT4);
error_def(ERR_MUINFOUINT8);
error_def(ERR_MUPGRDSUCC);
error_def(ERR_MUNODBNAME);
error_def(ERR_MUNODWNGRD);
error_def(ERR_MUDWNGRDTN);
error_def(ERR_MUDWNGRDNOTPOS);
error_def(ERR_MUDWNGRDNRDY);
error_def(ERR_MUSTANDALONE);
error_def(ERR_SYSCALL);
error_def(ERR_TEXT);
ZOS_ONLY(error_def(ERR_BADTAG);)
#define MAX_DB_VER_LEN 2
void mupip_downgrade(void)
{
char db_fn[MAX_FN_LEN + 1], ver_spec[MAX_DB_VER_LEN + 1];
unsigned short db_fn_len; /* cli_get_str expects short */
unsigned short ver_spec_len=MAX_DB_VER_LEN;
fd_type channel;
int save_errno, csd_size, rec_size;
int fstat_res, idx;
int4 status, rc;
uint4 status2;
off_t file_size;
v15_sgmnt_data v15_csd;
sgmnt_data csd;
#ifdef UNIX
boolean_t recovery_interrupted;
struct stat stat_buf;
#elif VMS
struct FAB mupfab;
struct XABFHC xabfhc;
$DESCRIPTOR(dbver_v4, "V4");
$DESCRIPTOR(dbver_v5, "V5");
$DESCRIPTOR(dbver_qualifier, "VERSION");
#endif
ZOS_ONLY(int realfiletag;)
unsigned char new_master_map[MASTER_MAP_SIZE_V4];
enum db_ver desired_dbver;
/* Structure checks .. */
assert((24 * 1024) == SIZEOF(v15_sgmnt_data)); /* Verify V4 file header hasn't suddenly increased for some odd reason */
UNIX_ONLY(sem_inf = (sem_info *)malloc(SIZEOF(sem_info) * FTOK_ID_CNT);
memset(sem_inf, 0, SIZEOF(sem_info) * FTOK_ID_CNT);
atexit(mupip_downgrade_cleanup);
);
db_fn_len = SIZEOF(db_fn) - 1;
if (!cli_get_str("FILE", db_fn, &db_fn_len))
rts_error(VARLSTCNT(1) ERR_MUNODBNAME);
db_fn[db_fn_len] = '\0'; /* Null terminate */
#ifdef VMS
if (CLI$_ABSENT != cli$present(&dbver_qualifier))
{
if (CLI$_PRESENT == cli$present(&dbver_v4))
desired_dbver = GDSV4;
else if (CLI$_PRESENT == cli$present(&dbver_v5))
{
desired_dbver = GDSV5;
gtm_putmsg(VARLSTCNT(8) ERR_MUPGRDSUCC, 6, db_fn_len, db_fn,
RTS_ERROR_LITERAL("downgraded"), RTS_ERROR_LITERAL("GT.M V5"));
mupip_exit(SS_NORMAL);
}
else
assertpro(FALSE); /* CLI should prevent us ever getting here */
} else
desired_dbver = GDSV4; /* really want to keep current format, which has not yet been read */
#else
if (cli_present("VERSION"))
{
cli_get_str("VERSION", ver_spec, &ver_spec_len);
ver_spec[ver_spec_len] = '\0';
cli_strupper(ver_spec);
if (0 == memcmp(ver_spec, "V4", ver_spec_len))
desired_dbver = GDSV4;
else if (0 == memcmp(ver_spec, "V5", ver_spec_len))
desired_dbver = GDSV5;
else
assertpro(FALSE); /* CLI should prevent us ever getting here */
} else
desired_dbver = GDSV4; /* really want to keep version, which has not yet been read */
#endif
if (!mu_upgrd_dngrd_confirmed())
{
gtm_putmsg(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Downgrade canceled by user"));
mupip_exit(ERR_MUNODWNGRD);
}
gtm_putmsg(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Mupip downgrade started"));
UNIX_ONLY(mu_all_version_get_standalone(db_fn, sem_inf));
mu_outofband_setup(); /* Will ignore user interrupts. Note that the
* elapsed time for this is order of milliseconds */
#ifdef VMS
mupfab = cc$rms_fab;
mupfab.fab$l_fna = db_fn;
mupfab.fab$b_fns = db_fn_len;
mupfab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_UPD ;
mupfab.fab$l_fop = FAB$M_UFO;
xabfhc = cc$rms_xabfhc;
mupfab.fab$l_xab = &xabfhc;
status = sys$open(&mupfab);
if (0 == (status & 1))
{
if (RMS$_FLK == status)
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, db_fn_len, db_fn);
else
gtm_putmsg(VARLSTCNT(6) ERR_DBOPNERR, 2, db_fn_len, db_fn, status, mupfab.fab$l_stv);
mupip_exit(ERR_MUNODWNGRD);
}
channel = mupfab.fab$l_stv;
file_size = xabfhc.xab$l_ebk * DISK_BLOCK_SIZE;
#else
if (FD_INVALID == (channel = OPEN(db_fn, O_RDWR)))
{
save_errno = errno;
if (FD_INVALID != (channel = OPEN(db_fn, O_RDONLY)))
gtm_putmsg(VARLSTCNT(10) ERR_DBRDONLY, 2, db_fn_len, db_fn, errno, 0,
MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2, LEN_AND_LIT("Cannot downgrade read-only database"));
else
gtm_putmsg(VARLSTCNT(5) ERR_DBOPNERR, 2, db_fn_len, db_fn, save_errno);
mupip_exit(ERR_MUNODWNGRD);
}
/* get file status */
FSTAT_FILE(channel, &stat_buf, fstat_res);
if (-1 == fstat_res)
{
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, errno);
gtm_putmsg(VARLSTCNT(4) ERR_DBOPNERR, 2, db_fn_len, db_fn);
mupip_exit(ERR_MUNODWNGRD);
}
file_size = stat_buf.st_size;
#if defined(__MVS__)
if (-1 == gtm_zos_tag_to_policy(channel, TAG_BINARY, &realfiletag))
TAG_POLICY_GTM_PUTMSG(db_fn, errno, realfiletag, TAG_BINARY);
#endif
#endif
csd_size = SIZEOF(sgmnt_data);
DO_FILE_READ(channel, 0, &csd, csd_size, status, status2);
if (SS_NORMAL != status)
{
gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, db_fn_len, db_fn, status);
mupip_exit(ERR_MUNODWNGRD);
}
if (memcmp(csd.label, GDS_LABEL, STR_LIT_LEN(GDS_LABEL)))
{ /* It is not V5.0-000 */
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
if (memcmp(csd.label, GDS_LABEL, GDS_LABEL_SZ - 3))
gtm_putmsg(VARLSTCNT(4) ERR_DBNOTGDS, 2, db_fn_len, db_fn);
else
gtm_putmsg(VARLSTCNT(4) ERR_BADDBVER, 2, db_fn_len, db_fn);
mupip_exit(ERR_MUNODWNGRD);
}
UNIX_ONLY(CHECK_DB_ENDIAN(&csd, db_fn_len, db_fn));
/* It is V5.x version: So proceed with downgrade */
if (csd.createinprogress)
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2, LEN_AND_LIT("Database creation in progress"));
mupip_exit(ERR_MUNODWNGRD);
}
if (csd.freeze)
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2, LEN_AND_LIT("Database is frozen"));
mupip_exit(ERR_MUNODWNGRD);
}
# ifdef UNIX
/* The following used to be a check for wc_blocked which is now unreachable because it resides
* in the shared memory.
*/
if (csd.machine_name[0])
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR),
2, LEN_AND_LIT("Machine name in file header is non-null implying possible crash"));
mupip_exit(ERR_MUNODWNGRD);
}
# endif
if (csd.file_corrupt)
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2, LEN_AND_LIT("Database corrupt"));
mupip_exit(ERR_MUNODWNGRD);
}
UNIX_ONLY(
recovery_interrupted = FALSE;
for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
{
if (csd.intrpt_recov_resync_strm_seqno[idx])
recovery_interrupted = TRUE;
}
)
if (csd.intrpt_recov_tp_resolve_time || csd.intrpt_recov_resync_seqno || csd.recov_interrupted
|| csd.intrpt_recov_jnl_state || csd.intrpt_recov_repl_state
UNIX_ONLY(|| recovery_interrupted))
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2, LEN_AND_LIT("Recovery was interrupted"));
mupip_exit(ERR_MUNODWNGRD);
}
UNIX_ONLY(
if (desired_dbver == GDSV5) /*Downgrading to V5 version*/
{
if ((START_VBN_V6 == csd.start_vbn) || (MASTER_MAP_BLOCKS_DFLT == csd.master_map_len))
{ /* DB is created with V6 version*/
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2,
LEN_AND_LIT("Database is created with V6 version."));
mupip_exit(ERR_MUNODWNGRD);
}
if (!csd.span_node_absent)
{ /* DB might contain spanning node */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2,
LEN_AND_LIT("Spanning node might be present."));
mupip_exit(ERR_MUNODWNGRD);
}
if (!csd.maxkeysz_assured)
{ /* DB might contain keys larger than max_key_sz in db header */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2,
LEN_AND_LIT("Database might contain keys larger than MAX KEY SIZE in DB header"));
mupip_exit(ERR_MUNODWNGRD);
}
if (csd.max_key_size > OLD_MAX_KEY_SZ)
{ /* DB might contain keys larger than 255 */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2,
LEN_AND_LIT("Database might contain keys larger than 255 bytes"));
mupip_exit(ERR_MUNODWNGRD);
}
/* Determine the max record size which is safe from spanning node perspective */
rec_size = csd.blk_size - csd.reserved_bytes - SIZEOF(blk_hdr);
if (csd.max_rec_size > rec_size)
{ /* max_rec_size is not supported for given blk_size in V5 */
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2,
LEN_AND_LIT("MAX REC SIZE is not supported for given BLK SIZE in V5"));
mupip_exit(ERR_MUNODWNGRD);
}
csd.freeze_on_fail = FALSE;
csd.span_node_absent = TRUE;
csd.maxkeysz_assured = FALSE;
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT4, 4,
LEN_AND_LIT("V5 supportable record size for current DB configuration "),rec_size, rec_size);
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT4, 4,
LEN_AND_LIT("V5 supportable max key size for current DB configuration"),
OLD_MAX_KEY_SZ, OLD_MAX_KEY_SZ);
gtm_putmsg(VARLSTCNT(8) ERR_MUPGRDSUCC, 6, db_fn_len, db_fn, RTS_ERROR_LITERAL("downgraded"),
RTS_ERROR_LITERAL("GT.M V5"));
DB_DO_FILE_WRITE(channel, 0, &csd, csd_size, status, status2);
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
UNIX_ONLY(mu_all_version_release_standalone(sem_inf));
mupip_exit(SS_NORMAL);
}
)
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT4, 4, LEN_AND_LIT("Old file header size"), csd_size, csd_size);
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT8, 4, LEN_AND_LIT("Old file length"), &file_size, &file_size);
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT4, 4, LEN_AND_LIT("Old file start_vbn"), csd.start_vbn, csd.start_vbn);
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT4, 4, LEN_AND_LIT("Old file gds blk_size"), csd.blk_size, csd.blk_size);
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT4, 4, LEN_AND_LIT("Old file total_blks"),
csd.trans_hist.total_blks, csd.trans_hist.total_blks);
assert(ROUND_DOWN2(csd.blk_size, DISK_BLOCK_SIZE) == csd.blk_size);
assert((((off_t)csd.start_vbn - 1) * DISK_BLOCK_SIZE +
(off_t)csd.trans_hist.total_blks * csd.blk_size + (off_t)DISK_BLOCK_SIZE == file_size) ||
(((off_t)csd.start_vbn - 1) * DISK_BLOCK_SIZE +
(off_t)csd.trans_hist.total_blks * csd.blk_size + (off_t)csd.blk_size == file_size));
if (START_VBN_V4 != csd.start_vbn)
{ /* start_vbn is not something that GT.M V4 can handle. signal downgrade not possible */
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(4) ERR_MUDWNGRDNOTPOS, 2, csd.start_vbn, START_VBN_V4);
mupip_exit(ERR_MUNODWNGRD);
}
if ((trans_num)MAX_TN_V4 < csd.trans_hist.curr_tn)
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(5) ERR_MUDWNGRDTN, 3, &csd.trans_hist.curr_tn, db_fn_len, db_fn);
mupip_exit(ERR_MUNODWNGRD);
}
if (csd.blks_to_upgrd != (csd.trans_hist.total_blks - csd.trans_hist.free_blocks))
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(5) ERR_MUDWNGRDNRDY, 3, db_fn_len, db_fn,
(csd.trans_hist.total_blks - csd.trans_hist.free_blocks - csd.blks_to_upgrd));
mupip_exit(ERR_MUNODWNGRD);
}
if (MASTER_MAP_SIZE_V4 < csd.master_map_len || MAXTOTALBLKS_V4 < csd.trans_hist.total_blks)
{
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
gtm_putmsg(VARLSTCNT(6) ERR_MUINFOUINT4, 4, LEN_AND_LIT("master_map_len"),
csd.master_map_len, csd.master_map_len);
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2, LEN_AND_LIT("Master map is too large"));
mupip_exit(ERR_MUNODWNGRD);
}
DO_FILE_READ(channel, 0, new_master_map, csd.master_map_len, status, status2);
if (SS_NORMAL != status)
{
gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, db_fn_len, db_fn, status);
mupip_exit(ERR_MUNODWNGRD);
}
if (csd.master_map_len < MASTER_MAP_SIZE_V4)
memset(new_master_map + csd.master_map_len, 0xFF, MASTER_MAP_SIZE_V4 - csd.master_map_len);
/* Now call mu_dwngrd_header to do file header downgrade */
mu_dwngrd_header(&csd, &v15_csd);
memcpy(v15_csd.master_map, new_master_map, MASTER_MAP_SIZE_V4);
DB_DO_FILE_WRITE(channel, 0, &v15_csd, csd_size, status, status2);
if (SS_NORMAL != status)
{
gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, db_fn_len, db_fn, status);
mupip_exit(ERR_MUNODWNGRD);
}
F_CLOSE(channel, rc); /* resets "channel" to FD_INVALID */
UNIX_ONLY(mu_all_version_release_standalone(sem_inf));
gtm_putmsg(VARLSTCNT(8) ERR_MUPGRDSUCC, 6, db_fn_len, db_fn, RTS_ERROR_LITERAL("downgraded"),
RTS_ERROR_LITERAL("GT.M V4"));
mupip_exit(SS_NORMAL);
}
#ifdef UNIX
static void mupip_downgrade_cleanup(void)
{
if (sem_inf)
mu_all_version_release_standalone(sem_inf);
}
#endif