/****************************************************************
 *								*
 * Copyright (c) 2001-2020 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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.	*
 *								*
 ****************************************************************/

/* includes */
#include "mdef.h"

#include "gtm_stdio.h"
#include "gtm_string.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "gdsbml.h"
#include "testpt.h"
#include "filestruct.h"
#include "interlock.h"
#include "jnl.h"
#include "min_max.h"
#include "send_msg.h"
#include "cert_blk.h"
#include "memcoherency.h"
#ifdef DEBUG
#include "op.h"
#endif

/* global refs/defs */

/* defines */
#define FAKE_DIRTY		((trans_num)(-1))
#define SEND_MSG_CSA(...)	send_msg_csa(CSA_ARG(csa) __VA_ARGS__)	/* to avoid formatting various send_msg calls */

GBLREF 	uint4		process_id;
GBLREF sgmnt_addrs	*cs_addrs;

error_def(ERR_DBADDRALIGN);
error_def(ERR_DBADDRANGE);
error_def(ERR_DBADDRANGE8);
error_def(ERR_DBCRERR);
error_def(ERR_DBCRERR8);
error_def(ERR_DBFHEADERR4);
error_def(ERR_DBFHEADERR8);
error_def(ERR_DBFHEADERRANY);
error_def(ERR_DBFHEADLRU);
error_def(ERR_DBQUELINK);
error_def(ERR_DBWCVERIFYEND);
error_def(ERR_DBWCVERIFYSTART);

boolean_t	wcs_verify(gd_region *reg, boolean_t expect_damage, boolean_t caller_is_wcs_recover)
{	/* This routine verifies the shared memory structures used to manage the buffers of the bg access method.
	 * Changes to those structures or the way that they are managed may require changes to this routine
	 * some fields may not be rigorously tested if their interrelationships did not seem
	 * important, well defined or well understood, i.e. feel free to make improvements.
	 * It *corrects* errors which have a point nature and
	 * returns a FALSE for systemic problems that require a wcs_recover or something more drastic.
	 */

	uint4			cnt, lcnt ;
        ssize_t			offset ;
        trans_num 		max_tn, tmp_8byte;
	INTPTR_T		bp_lo, bp_top, bp, cr_base, cr_top, bt_top_off, bt_base_off;
	sm_uc_ptr_t		bptmp, bp_bmp;
	boolean_t		asyncio, do_cert_blk, ret;
	sgmnt_addrs		*csa;
	sgmnt_data_ptr_t	csd;
	node_local_ptr_t	cnl;
	cache_rec_ptr_t		cr, cr0, cr_bmp, cr_tmp, cr_tmp2, cr_prev, cr_hi, cr_lo, cr_qbase;
	bt_rec_ptr_t		bt, bt0, bt_prev, bt_hi, bt_lo;
	th_rec_ptr_t		th, th_prev;
	cache_que_head_ptr_t	que_head;
	cache_state_rec_ptr_t	cstt, cstt_prev;
	sm_uc_ptr_t		jnl_buff_expected;
	boolean_t		(*blkque_array)[] = NULL; /* TRUE indicates we saw the cr or bt of that array index */
	int4			n_bts;	/* a copy of csd->n_bts since it is used frequently in this routine */
	trans_num		dummy_tn;
	int4			bml_status, in_wtstart, intent_wtstart, wcs_phase2_commit_pidcnt;
	block_id		curbmp;
	jnl_private_control	*jpc;
	jnl_buffer_ptr_t	jbp;
#	ifdef DEBUG
	mval			rand_lru;
#	endif

	csa = &FILE_INFO(reg)->s_addrs;
	csd = csa->hdr;
	cnl = csa->nl;
	ret = TRUE;
	SEND_MSG_CSA(VARLSTCNT(7) ERR_DBWCVERIFYSTART, 5, DB_LEN_STR(reg), process_id, process_id, &csd->trans_hist.curr_tn);
	/* while some errors terminate loops, as of this writing, no errors are treated as terminal */
	if ((csa->now_crit == FALSE) && (csd->clustered == FALSE))
	{
		assert(expect_damage);
		assert(!csa->hold_onto_crit);
		ret = FALSE;
		SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg), RTS_ERROR_TEXT("now_crit"), csa->now_crit, TRUE);
		grab_crit(reg, WS_22);		/* what if it has it but lost track of it ??? should there be a crit reset ??? */
	}
	if (dba_mm != csd->acc_meth)
	{
		if (0 != cnl->wtfini_in_prog)
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("wtfini_in_prog"), cnl->wtfini_in_prog, 0);
		offset = ROUND_UP(SIZEOF_FILE_HDR(csd), (SIZEOF(int4) * 2));
		if (cnl->bt_header_off != offset)			/* bt_header is "quadword-aligned" after the header */
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("bt_header_off"), cnl->bt_header_off, offset);
			cnl->bt_header_off = offset;
		}
		if (csa->bt_header != (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_header_off))
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("bt_header"), csa->bt_header, (sm_uc_ptr_t)csd + cnl->bt_header_off);
			csa->bt_header = (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_header_off);
		}
		offset += csd->bt_buckets * SIZEOF(bt_rec);
		if (cnl->th_base_off != (offset + SIZEOF(bt->blkque)))	/* th_base follows, skipping the initial blkque heads */
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("th_base_off"), cnl->th_base_off, offset + SIZEOF(bt->blkque));
			cnl->th_base_off = (offset + SIZEOF(bt->blkque));
		}
		if (csa->th_base != (th_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->th_base_off))
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("th_base"), csa->th_base, (sm_uc_ptr_t)csd + cnl->th_base_off);
			csa->th_base = (th_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->th_base_off);
		}
		offset += SIZEOF(bt_rec);
		if (cnl->bt_base_off != offset)				/* bt_base just skips the item used as the tnque head */
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("bt_base_off"), cnl->bt_base_off, offset);
			cnl->bt_base_off = offset;
		}
		if (csa->bt_base != (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_base_off))
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("bt_base"), csa->bt_base, (sm_uc_ptr_t)csd + cnl->bt_base_off);
			csa->bt_base = (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_base_off);
		}
	} else
		n_bts = csd->n_bts;
	if (csa->ti != &csd->trans_hist)
	{
		assert(expect_damage);
		ret = FALSE;
		SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
			RTS_ERROR_TEXT("csa->ti"), (sm_uc_ptr_t)csa->ti, (sm_uc_ptr_t)&csd->trans_hist);
		csa->ti = &csd->trans_hist;
	}
	if (dba_mm != csd->acc_meth)
	{
		n_bts = csd->n_bts;
		offset += n_bts * SIZEOF(bt_rec);
		if (0 != (cnl->cache_off + CACHE_CONTROL_SIZE(csd)))
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("cache_off"), cnl->cache_off, -CACHE_CONTROL_SIZE(csd));
			cnl->cache_off = -CACHE_CONTROL_SIZE(csd);
		}
		if (csa->acc_meth.bg.cache_state != (cache_que_heads_ptr_t)((sm_uc_ptr_t)csd + cnl->cache_off))
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("cache_state"), csa->acc_meth.bg.cache_state, (sm_uc_ptr_t)csd + cnl->cache_off);
			csa->acc_meth.bg.cache_state = (cache_que_heads_ptr_t)((sm_uc_ptr_t)csd + cnl->cache_off);
		}
		if (csd->bt_buckets != getprime(n_bts))
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("bt_buckets"), csd->bt_buckets, getprime(n_bts));
			csd->bt_buckets = getprime(n_bts);
		}
	}
	if (JNL_ALLOWED(csd))
	{
		jpc = csa->jnl;
		jbp = NULL;
		if (NULL == jpc)
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("csa->jnl"), csa->jnl, (UINTPTR_T)-1);
		else if (NULL == (jbp = jpc->jnl_buff))
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("csa->jnl->jnl_buff"), jbp, (UINTPTR_T)-1);
		else
		{
			jnl_buff_expected = ((sm_uc_ptr_t)(cnl) + NODE_LOCAL_SPACE(csd) + JNL_NAME_EXP_SIZE);
			if (jbp != (jnl_buffer_ptr_t)jnl_buff_expected)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("csa->jnl->jnl_buff_expected"), jbp, jnl_buff_expected);
				jbp = (jnl_buffer_ptr_t)jnl_buff_expected;
				jpc->jnl_buff = jbp;
			}
		}
	}
	if (dba_mm != csd->acc_meth)
	{
		bt_lo = csa->bt_base;
		bt_hi = bt_lo + n_bts;
		cr_lo = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets;
		cr_hi = cr_lo + n_bts;
		cr_base = GDS_ANY_ABS2REL(csa, cr_lo);
		cr_top = GDS_ANY_ABS2REL(csa, cr_hi);
#		ifdef DEBUG
		if (gtm_white_box_test_case_enabled && (WBTEST_CRASH_SHUTDOWN_EXPECTED == gtm_white_box_test_case_number))
		{	/* if we are crashing anyway mess with the lru pointer to demonstrate the test below gives an error */
			op_fnrandom(E_9, &rand_lru);
			GTM_WHITE_BOX_TEST(WBTEST_CRASH_SHUTDOWN_EXPECTED, cnl->cur_lru_cache_rec_off, rand_lru.m[1])
			gtm_wbox_input_test_case_count++;
		}
#		endif
		cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, cnl->cur_lru_cache_rec_off);
		if ((cr < cr_lo) || (cr > cr_hi) || (0 != (((char *)cr - (char *)cr_lo) % SIZEOF(cache_rec))))
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(7) ERR_DBFHEADLRU, 5, DB_LEN_STR(reg), &cr, &cr_lo, &cr_hi);
			cnl->cur_lru_cache_rec_off = cr_base;
		}
		if (caller_is_wcs_recover)
		{	/* if wcs_recover is caller, it would have waited for the following fields to become 0.
			 * if called from DSE CACHE -VERIFY, none of these are guaranteed. So do these checks only for first case.
			 */
			if (WC_UNBLOCK == cnl->wc_blocked)
			{	/* in UNIX this blocks the writer */
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("wc_blocked"), cnl->wc_blocked, WC_BLOCK_RECOVER);
				SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER);
			}
			in_wtstart = cnl->in_wtstart;	/* store value in local variable in case the following assert fails */
			if (0 != in_wtstart)
			{	/* caller should outwait active writers */
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg), RTS_ERROR_TEXT("in_wtstart"),
					in_wtstart, 0);
				assert(expect_damage);
				cnl->in_wtstart = 0;
				csa->in_wtstart = FALSE; /* To allow "wcs_wtstart" after "wcs_recover" */
			}
			intent_wtstart = cnl->intent_wtstart;
			if (0 != intent_wtstart)
			{	/* Two situations are possible.
				 *	a) A "wcs_wtstart" call is concurrently in progress and that the process has just now
				 *		incremented intent_wtstart. It will notice cnl->wc_blocked to be
				 *		2 and decrement intent_wtstart right away and return. So we don't need to do
				 *		anything.
				 *	b) A "wcs_wtstart" call had previously increment intent_wtstart but got shot before it
				 *		could get a chance to decrement the field. In this case, we need to clear the
				 *		field to recover from this situation.
				 *	Since the writer uses the DECR_INTENT_WTSTART macro which does not do DECR_CNTs if the value
				 *	is already 0, it is okay to do the decrement even in case (a). There is a very small window
				 *	that still exists. If the DECR_INTENT_WTSTART macro did the > 0 check when the field was
				 *	positive and then wcs_verify reset the field, the DECR_CNT will happen and will cause it to
				 *	become negative. But that will last until the next INCR_INTENT_WTSTART or wcs_recover which
				 *	happens first. The INCR_INTENT_WTSTART macro has a double increment to take care of this
				 *	case.
				 */
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("intent_wtstart"), intent_wtstart, 0);
				cnl->intent_wtstart = 0;
				SHM_WRITE_MEMORY_BARRIER;
			}
			wcs_phase2_commit_pidcnt = cnl->wcs_phase2_commit_pidcnt; /* store value in local in case assert fails */
			if (0 != wcs_phase2_commit_pidcnt)
			{	/* caller should outwait active committers */
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("wcs_phase2_commit_pidcnt"), wcs_phase2_commit_pidcnt, 0);
				assert(expect_damage);
				cnl->wcs_phase2_commit_pidcnt = 0;
				csa->wcs_pidcnt_incremented = FALSE; /* Just to be safe */
			}
		}
		th = csa->th_base;
		if (th->blk != BT_QUEHEAD)
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("th_base->blk"), th->blk, BT_QUEHEAD);
			th->blk = BT_QUEHEAD;
		}
		/* loop through bt tnque */
		for (th_prev = th, th = (th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.fl), cnt = n_bts, max_tn = 0, cnt = n_bts + 1;
			(th != csa->th_base) && (cnt > 0);
				th_prev = th, cnt--, th = (th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.fl))
		{
			bt = (bt_rec_ptr_t)((sm_uc_ptr_t)th - SIZEOF(bt->blkque));
			if (BT_NOT_ALIGNED(bt, bt_lo))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), th_prev, -1,
					RTS_ERROR_TEXT("th->tnque"), bt, bt_lo, SIZEOF(bt_rec));
				break;
			}
			if (BT_NOT_IN_RANGE(bt, bt_lo, bt_hi))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), th_prev, -1,
					bt, RTS_ERROR_TEXT("th->tnque"), bt_lo, bt_hi);
				break;
			}
			if ((th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.bl) != th_prev)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), th, th->blk,
						RTS_ERROR_TEXT("tnque.bl"), (UINTPTR_T)th->tnque.bl,
						(sm_uc_ptr_t)th_prev - (sm_uc_ptr_t)th);
			}
			if (th->tn != 0)
			{
				if (th->tn < max_tn)
				{
					assert(expect_damage);
					ret = FALSE;
					tmp_8byte = 1;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg), th, th->blk, &max_tn,
							RTS_ERROR_TEXT("tnque transaction number"), &tmp_8byte, &th->tn);
				}
				/* ideally, the following max_tn assignment should have been in the else part of the above if. but
				 *  the issue with doing that is if there is a sequence of non-decreasing transaction numbers
				 * except for one (or few) numbers in the middle of the sequence that is larger than all others,
				 * it is more likely that those hiccups are incorrect. in that case we do not want max_tn to end
				 * up being an incorrect large value. hence the unconditional assignment below.
				 */
				max_tn = th->tn;
			}
			if (((int)(th->blk) != BT_NOTVALID) &&
				(((int)(th->blk) < 0) || ((int)(th->blk) > csd->trans_hist.total_blks)))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
					th, th->blk, th->blk, RTS_ERROR_TEXT("th->blk"), 0, csd->trans_hist.total_blks);
			}
			if (((int)(th->cache_index) != CR_NOTVALID) &&
				(((int)(th->cache_index) < cr_base) || ((int)(th->cache_index) >= cr_top)))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
					th, th->blk, th->cache_index, RTS_ERROR_TEXT("th->cache_index"), cr_base, cr_top);
			}
			if (th->flushing != FALSE) /* ??? this is a gt.cx item that may require more synchronization at the top */
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("th->flushing"), th->flushing, FALSE);
			}
			if (0 == th->tnque.fl)
			{	/* No point proceeding to next iteration of loop as "th + th->tnque.fl" will be the same as "th" */
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
					th, th->blk, RTS_ERROR_TEXT("tnque.fl"), (UINTPTR_T)th->tnque.fl, (UINTPTR_T)-1);
				break;
			}
		}
		if (cnt != 1)
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("tnque entries"), n_bts - cnt, n_bts - 1);
		} else if ((th == csa->th_base) && ((th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.bl) != th_prev))
		{	/* at this point "th" is csa->th_base and its backlink does not point to the last entry in the th queue */
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), th, th->blk,
				RTS_ERROR_TEXT("tnque th_base"), (UINTPTR_T)th->tnque.bl, (sm_uc_ptr_t)th_prev - (sm_uc_ptr_t)th);
		}
		if (max_tn > csd->trans_hist.curr_tn)
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR8, 6, DB_LEN_STR(reg),
				RTS_ERROR_TEXT("MAX(th_base->tn)"), &max_tn, &csd->trans_hist.curr_tn);
		}
		/* loop through bt blkques */
		blkque_array = malloc(n_bts * SIZEOF(boolean_t));
		memset(blkque_array, 0, n_bts * SIZEOF(boolean_t));	/* initially, we did not find any bt in the bt blkques */
		for (bt0 = csa->bt_header; bt0 < bt_lo; bt0++)
		{
			if (bt0->blk != BT_QUEHEAD)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("queue head bt->blk"), bt0->blk, BT_QUEHEAD);
				bt0->blk = BT_QUEHEAD;
			}
			for (bt_prev = bt0, bt = (bt_rec_ptr_t)((sm_uc_ptr_t)bt0 + bt0->blkque.fl), cnt = n_bts + 1;
				(bt != bt0) && (cnt > 0);
					bt_prev = bt, cnt--, bt = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.fl))
			{
				if (BT_NOT_ALIGNED(bt, bt_lo))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), bt_prev, -1,
						RTS_ERROR_TEXT("bt->blkque"), bt, bt_lo, SIZEOF(bt_rec));
					break;
				}
				if (BT_NOT_IN_RANGE(bt, bt_lo, bt_hi))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), bt_prev, -1,
						bt, RTS_ERROR_TEXT("bt->blkque"), bt_lo, bt_hi);
					break;
				}
				if ((bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.bl) != bt_prev)
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
						RTS_ERROR_TEXT("bt->blkque.bl"), (UINTPTR_T)bt->blkque.bl,
						(sm_uc_ptr_t)bt_prev - (sm_uc_ptr_t)bt);
				}
				if ((int)(bt->blk) != BT_NOTVALID)
				{
					if ((csa->bt_header + (bt->blk % csd->bt_buckets)) != bt0)
					{
						assert(expect_damage);
						ret = FALSE;
						SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
							RTS_ERROR_TEXT("bt hash"), (bt0 - csa->bt_header),
							(UINTPTR_T)(bt->blk % csd->bt_buckets));
					}
					if (CR_NOTVALID != bt->cache_index)
					{
						cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, bt->cache_index);
						/* Before checking if "cr->blk" is the same as "bt->blk", check if "cr" is valid */
						if (CR_NOT_IN_RANGE(cr, cr_lo, cr_hi))
						{
							assert(expect_damage);
							ret = FALSE;
							SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
								bt, bt->blk, cr, RTS_ERROR_TEXT("bt->cache_index"), cr_lo, cr_hi);
						} else if (CR_NOT_ALIGNED(cr, cr_lo))
						{
							assert(expect_damage);
							ret = FALSE;
							SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), bt, bt->blk,
								RTS_ERROR_TEXT("bt->cache_index"), cr, cr_lo, SIZEOF(cache_rec));
						} else if (cr->blk != bt->blk)
						{
							assert(expect_damage);
							ret = FALSE;
							SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr,
								bt->blk, RTS_ERROR_TEXT("bt block"), cr->blk, bt->blk,
								CALLFROM);
						}
					}
				}
				(*blkque_array)[bt - bt_lo] = TRUE; /* note: this bt's blkque hash validity is already checked */
				if (0 == bt->blkque.fl)
				{	/* No point proceeding to next iteration as "bt + bt->blkque.fl" will be the same as "bt" */
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
						RTS_ERROR_TEXT("bt->blkque.fl"), (UINTPTR_T)bt->blkque.fl, (UINTPTR_T)-1);
					break;
				}
			}
			if (cnt == 0)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
					RTS_ERROR_TEXT("btque entries"), n_bts + 1 - cnt, n_bts + 1);
			} else if ((bt == bt0) && ((bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.bl) != bt_prev))
			{	/* at this point "bt" is bt0 and its backlink does not point to last entry in the bt0'th queue */
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
					RTS_ERROR_TEXT("btque bt_base"), (UINTPTR_T)bt->blkque.bl,
					(sm_uc_ptr_t)bt_prev - (sm_uc_ptr_t)bt);
			}
		}
		/* scan all bts looking for valid bt->blks whose bts were not in any blkque */
		for (bt = bt_lo; bt < bt_hi; bt++)
		{
			if ((FALSE == (*blkque_array)[bt - bt_lo]) && ((int)(bt->blk) != BT_NOTVALID))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
					RTS_ERROR_TEXT("bt blkque hash"), (UINTPTR_T)-1, (UINTPTR_T)(bt->blk % csd->bt_buckets));
			}
		}

		bp_lo = ROUND_UP(cr_top, OS_PAGE_SIZE);
		bp_top = bp_lo + ((gtm_uint64_t)n_bts * csd->blk_size);
		bt_base_off = GDS_ANY_ABS2REL(csa, (sm_uc_ptr_t)csd + cnl->bt_base_off);
		bt_top_off = GDS_ANY_ABS2REL(csa, (sm_uc_ptr_t)csd + offset);

		/* loop through the cache_recs */
		memset(blkque_array, 0, n_bts * SIZEOF(boolean_t));	/* initially, we did not find any cr in the cr blkques */
		asyncio = csd->asyncio;
		for (bp = bp_lo, cr = cr_lo, cnt = n_bts; cnt > 0; cr++, bp += csd->blk_size, cnt--)
		{
			if (((int)(cr->blk) != CR_BLKEMPTY) &&
				(((int)(cr->blk) < 0) || ((int)(cr->blk) >= csd->trans_hist.total_blks)))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
					cr, cr->blk, cr->blk, RTS_ERROR_TEXT("cr->blk"), 0, csd->trans_hist.total_blks);
			}
			if (cr->tn > csd->trans_hist.curr_tn)
			{
				assert(expect_damage);
				ret = FALSE;
				tmp_8byte = 0;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg),
					cr, cr->blk, &cr->tn, RTS_ERROR_TEXT("cr->tn"), &tmp_8byte, &csd->trans_hist.curr_tn);
			}
			if (0 != cr->bt_index)
			{
				if (!IS_PTR_IN_RANGE(cr->bt_index, bt_base_off, bt_top_off))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
						cr, cr->blk, cr->bt_index, RTS_ERROR_TEXT("cr->bt_index"), bt_base_off,
						bt_top_off);
				} else if (!IS_PTR_ALIGNED(cr->bt_index, bt_base_off, SIZEOF(bt_rec)))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
						cr, cr->blk, RTS_ERROR_TEXT("cr->bt_index"), cr->bt_index, bt_base_off,
						SIZEOF(bt_rec));
				} else
				{
					bt = (bt_rec_ptr_t)GDS_ANY_REL2ABS(csa, cr->bt_index);
					if (cr->blk != bt->blk)
					{
						assert(expect_damage);
						ret = FALSE;
						SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr,
							cr->blk, RTS_ERROR_TEXT("cr block"), cr->blk, bt->blk,
							CALLFROM);
					}
				}
			}
			if (!IS_PTR_IN_RANGE(cr->buffaddr, bp_lo, bp_top))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
					cr, cr->blk, cr->buffaddr, RTS_ERROR_TEXT("cr->buffaddr"), bp_lo, bp_top);
			} else if (!IS_PTR_ALIGNED(cr->buffaddr, bp_lo, csd->blk_size))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
					cr, cr->blk, RTS_ERROR_TEXT("cr->buffaddr"), cr->buffaddr, bp_lo, csd->blk_size);
			} else if (cr->buffaddr != bp)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
					cr, cr->blk, RTS_ERROR_TEXT("cr->buffaddr"), cr->buffaddr, bp, CALLFROM);
			}
			if (cr->in_tend)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
					cr, cr->blk, RTS_ERROR_TEXT("cr->in_tend"), cr->in_tend, FALSE, CALLFROM);
			}
			if (cr->data_invalid)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
					cr, cr->blk, RTS_ERROR_TEXT("cr->data_invalid"), cr->data_invalid, FALSE, CALLFROM);
			}
			if (cr->r_epid != 0)
			{
				if (cr->read_in_progress < 0)
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
						cr, cr->blk, RTS_ERROR_TEXT("cr->r_epid"), cr->r_epid, 0, CALLFROM);
				}
			} else if ((-1 == cr->read_in_progress) && !caller_is_wcs_recover
						&& (CR_BLKEMPTY != cr->blk) && !cr->data_invalid)
			{	/* If the buffer is not being read into currently (checked both by cr->r_epid being 0 and
				 * cr->read_in_progress being -1) and we are being called from DSE CACHE -VERIFY and cr points
				 * to a valid non-empty block, check the content of cr->buffaddr through a "cert_blk".
				 * The reason why the "cert_blk" is done only from a DSE CACHE -VERIFY call and not from a
				 * "wcs_recover" call is that "wcs_recover" is supposed to check the integrity of the data
				 * structures in the cache and not the integrity of the data (global buffers) in the cache. If the
				 * database has an integrity error, a global buffer will fail "cert_blk" but the cache structures
				 * as such are not damaged. "wcs_recover" should not return failure in that case.
				 */
				/* Use "bp" to derive the buffer as cr->buffaddr might have been detected as corrupt
				 * by the buffaddr checks above.
				 */
				bptmp = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, bp);
				if (!IS_BITMAP_BLK(cr->blk))
				{	/* Note that it is possible a FREE/REUSABLE block was brought from disk in to the cache
					 * by "t_qread" due to restartable circumstances (e.g. a process read the wrong child
					 * block # while traversing an index block). In that case, this block would have a header
					 * of all 0's and "cert_blk" would issue a DBBSIZMN error. So skip the "cert_blk".
					 * We check the FREE status by examining the bitmap block if it is in the cache already.
					 * If not, we examine the block-header for all 0's and if so assume it is a FREE block.
					 */
					curbmp = ROUND_DOWN2(cr->blk, BLKS_PER_LMAP);
					cs_addrs = csa;		/* needed by "db_csh_get" so temporarily set global variable */
					cr_bmp = db_csh_get(curbmp);
					csa = cs_addrs;		/* undo temporary global variable set */
					if (((cache_rec_ptr_t)CR_NOTVALID != cr_bmp) && (NULL != cr_bmp))
					{
						bp_bmp = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, cr_bmp->buffaddr);
						GET_BM_STATUS(bp_bmp, (cr->blk - curbmp), bml_status);
						do_cert_blk = (BLK_BUSY == bml_status);
					} else
					{	/* We do the all 0's check by checking TWO 8-byte values in the 16-byte blk header.
						 * The asserts below ensure we adjust the below code if/ever the block header
						 * size changes.
						 */
						assert(16 == SIZEOF(blk_hdr));
						assert(8 == SIZEOF(tmp_8byte));
						tmp_8byte = ((trans_num *)bptmp)[0];
						if (!tmp_8byte)
							tmp_8byte = ((trans_num *)bptmp)[1];
						do_cert_blk = (0 != tmp_8byte);
					}
				} else
				{	/* If it is a bitmap block, we could have twins so do check only on newtest twin as
					 * older twin could have an incorrect master-bitmap full/free status (DBBMMSTR error).
					 */
					do_cert_blk = ((0 == cr->twin) || (0 != cr->bt_index));
				}
				if (do_cert_blk && !cert_blk(reg, cr->blk, (blk_hdr_ptr_t)bptmp, 0, RTS_ERROR_ON_CERT_FAIL, NULL))
				{
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
						cr, cr->blk, RTS_ERROR_TEXT("Block certification result"),
						FALSE, TRUE, CALLFROM);
					SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
						RTS_ERROR_TEXT("Block certification result buffer"),
						bptmp, csa->mlkctl, CALLFROM);
					assert(expect_damage);
				}
			}
			if (0 != cr->in_cw_set)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
					cr, cr->blk, RTS_ERROR_TEXT("cr->in_cw_set"), (uint4)cr->in_cw_set, 0, CALLFROM);
			}
			assert(!JNL_ALLOWED(csd) || (NULL != jpc) && (NULL != jbp));
			if (JNL_ENABLED(csd))
			{
				if ((0 != cr->dirty) && (cr->jnl_addr > jbp->rsrv_freeaddr))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), cr, cr->blk,
						(uint4)cr->jnl_addr, RTS_ERROR_TEXT("cr->jnl_addr"), 0, jbp->rsrv_freeaddr);
				}
			} else if (!JNL_ALLOWED(csd) && (cr->jnl_addr != 0))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
					cr, cr->blk, RTS_ERROR_TEXT("cr->jnl_addr"), (uint4)cr->jnl_addr, 0, CALLFROM);
			}
			if ((WRITE_LATCH_VAL(cr) < LATCH_CLEAR) || (WRITE_LATCH_VAL(cr) > LATCH_CONFLICT))
			{	/* the message would read cr->interlock.semaphore although in Unix it means cr->interlock.latch */
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), cr, cr->blk,
					WRITE_LATCH_VAL(cr), RTS_ERROR_TEXT("cr->interlock.semaphore"), LATCH_CLEAR,
					LATCH_CONFLICT);
			}
			/* "cycle" is a relative timestamp with no characteristics useful to verify. So skip that*/
			/* If "cr->stopped" is TRUE, then "cr->twin" can be non-zero (special case for "secshr_db_clnup"
			 * to communicate to "wcs_recover"). But in that case it is a one-way link so check that.
			 */
			if (cr->stopped)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
					RTS_ERROR_TEXT("cr->stopped"), cr->stopped, FALSE, CALLFROM);
			}
			if (0 != cr->twin)
			{	/* Validate twin links */
				assert(!cr->stopped);
				if (TWINNING_ON(csd))
				{
					cr_tmp = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, cr->twin);
					if (CR_NOT_IN_RANGE(cr_tmp, cr_lo, cr_hi))
					{
						assert(expect_damage);
						ret = FALSE;
						SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
							cr, cr->blk, cr_tmp, RTS_ERROR_TEXT("cr->twin"), cr_lo, cr_hi);
					} else if (CR_NOT_ALIGNED(cr_tmp, cr_lo))
					{
						assert(expect_damage);
						ret = FALSE;
						SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), cr, cr->blk,
							RTS_ERROR_TEXT("cr->twin"), cr_tmp, cr_lo, SIZEOF(cache_rec));
					} else
					{	/* We expect a two-way link. */
						cr_tmp2 = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, cr_tmp->twin);
						if (cr != cr_tmp2)
						{
							assert(expect_damage);
							ret = FALSE;
							SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr_tmp,
								cr->blk, RTS_ERROR_TEXT("cr->twin->twin"),
								GDS_ANY_REL2ABS(csa, cr_tmp->twin),
								cr, CALLFROM);
						}
					}
				} else
				{	/* We do not expect cr->twin to be non-zero */
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
						 cr, cr->blk, RTS_ERROR_TEXT("cr->twin"), cr->twin, 0, CALLFROM);
				}
				/* One twin's bt_index has to be zero and another non-zero.
				 * The only exception is if this is a "kill -9" situation in which case the process
				 * could have been shot in the middle of bg_update_phase1 where things are still not in sync.
				 */
				assert((0 == cr->bt_index) || (0 == cr_tmp->bt_index)	/* one has to be zero */
							|| WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED)
							|| WBTEST_ENABLED(WBTEST_MURUNDOWN_KILLCMT06));
				assert((0 != cr->bt_index) || (0 != cr_tmp->bt_index)	/* one has to be non-zero */
							|| WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED)
							|| WBTEST_ENABLED(WBTEST_MURUNDOWN_KILLCMT06));
			}
			/* If asyncio=OFF, we do not expect any concurrent writers. Check that.
			 * If asyncio=ON, it is possible to have concurrent writers. Do not check epid in that case.
			 */
			if (!asyncio && (0 != cr->epid) && caller_is_wcs_recover)
			{	/* If called from DSE CACHE -VERIFY, we do not wait for concurrent writers to finish
				 * so do not issue an error message in that case.
				 */
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
					cr, cr->blk, RTS_ERROR_TEXT("cr->epid"), cr->epid, 0, CALLFROM);
			}
		}
		/* loop through the cr blkques */
		for (cr0 = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array, cr_qbase = cr0; cr0 < cr_lo; cr0++)
		{
			if (cr0->blk != BT_QUEHEAD)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr0, cr0->blk,
					RTS_ERROR_TEXT("queue head cr->blk"), cr0->blk, BT_QUEHEAD, CALLFROM);
				cr0->blk = BT_QUEHEAD;
			}
			for (cr_prev = cr0, cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr0 + cr0->blkque.fl), cnt = n_bts + 1;
				(cr != cr0) && (cnt > 0);
					cr_prev = cr, cnt--, cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.fl))
			{
				if (CR_NOT_IN_RANGE(cr, cr_lo, cr_hi))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
						cr0, -1, cr, RTS_ERROR_TEXT("cr->blkque"), cr_lo, cr_hi);
					break;
				}
				if (CR_NOT_ALIGNED(cr, cr_lo))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
						cr0, -1, RTS_ERROR_TEXT("cr->blkque"), cr, cr_lo, SIZEOF(cache_rec));
					break;
				}
				if ((cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.bl) != cr_prev)
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cr, cr->blk,
						RTS_ERROR_TEXT("cr->blkque.bl"), (UINTPTR_T)cr->blkque.bl,
						(sm_uc_ptr_t)cr_prev - (sm_uc_ptr_t)cr);
				}
				if (((int)(cr->blk) != CR_BLKEMPTY) && ((cr_qbase + (cr->blk % csd->bt_buckets)) != cr0))
				{
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
						RTS_ERROR_TEXT("cr hash"), cr0 - cr_qbase, cr->blk % csd->bt_buckets, CALLFROM);
					if (caller_is_wcs_recover && !cr->stopped)
					{	/* if cr->stopped is TRUE, then the buffer was created by "secshr_db_clnup",
						 * and hence it is ok to have different hash value, but otherwise we believe
						 * the hash value and consider cr->blk to be invalid and hence make this buffer
						 * empty
						 *
						 * Possible causes of this condition are if a process gets shot (kill -9 or STOP/ID)
						 * in the midst of shuffling a cache-record from one blkque to another blkque (done
						 * through a call to shuffqth in db_csh_getn.c). Since the act of removing a
						 * cache-record from one hashqueue and adding it to another hashqueue is not
						 * atomic, we can end up with a cache-record that is not in the proper hashqueue
						 * if we get shot in the middle.
						 *
						 * Ideally we would like to dump the contents of this broken buffer to a file for
						 * later analysis. Since we hold crit now, we do not want to do that. It might be
						 * better to copy this buffer into another area in shared-memory dedicated to
						 * holding such information so a later DSE session can then dump the information.
						 *
						 * Ideally, it should be "wcs_recover" that fixes the cache-record, but then the
						 * blk_que traversing logic has to be redone there in order to determine this
						 * disparity in the hash value. To avoid that we reset cr->blk here itself but
						 * do it only if called from "wcs_recover".
						 */
						assert(expect_damage);
						cr->blk = CR_BLKEMPTY;
					}
				}
				(*blkque_array)[cr - cr_lo] = TRUE; /* note: this cr's blkque hash validity is already checked */
				if (0 == cr->blkque.fl)
				{	/* No point proceeding to next iteration as "cr + cr->blkque.fl" will be the same as "cr" */
					assert(expect_damage);
					ret = FALSE;
					SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cr, cr->blk,
						RTS_ERROR_TEXT("cr->blkque.fl"), (UINTPTR_T)cr->blkque.fl, (UINTPTR_T)-1);
					break;
				}
			}
			if (cnt == 0)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
					cr_qbase, 0, RTS_ERROR_TEXT("crque entries"), (UINTPTR_T)(n_bts + 1), (UINTPTR_T)(n_bts));
			} else if ((cr == cr0) && ((cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.bl) != cr_prev))
			{	/* at this point "cr" is cr0 and its backlink does not point to last entry in the cr0'th queue */
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cr, cr->blk,
					RTS_ERROR_TEXT("crque cr_base"), (UINTPTR_T)cr->blkque.bl,
					(sm_uc_ptr_t)cr_prev - (sm_uc_ptr_t)cr);
			}
		}
		/* scan all crs looking for non-empty cr->blks whose crs were not in any blkque */
		for (cr = cr_lo; cr < cr_hi; cr++)
		{
			if ((FALSE == (*blkque_array)[cr - cr_lo]) && ((int)(cr->blk) != CR_BLKEMPTY))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
					RTS_ERROR_TEXT("cr blkque hash"), -1, cr->blk % csd->bt_buckets, CALLFROM);
				if (caller_is_wcs_recover && !cr->stopped) /* see comment above ("cr hash") for similar handling */
				{
					assert(expect_damage);
					cr->blk = CR_BLKEMPTY;
				}
			}
		}
		que_head = &csa->acc_meth.bg.cache_state->cacheq_active;
		if ((sm_long_t)que_head % SIZEOF(que_head->fl) != 0)
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), que_head, 0, RTS_ERROR_TEXT("cacheq_active"),
				que_head, ((sm_long_t)que_head / SIZEOF(que_head->fl)) * SIZEOF(que_head->fl));
		}
		if ((0 != que_head->fl) != (0 != que_head->bl))
		{
			assert(expect_damage);
			ret = FALSE;
			if (que_head->fl)
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
					que_head, 0, RTS_ERROR_TEXT("cacheq_active1"), (UINTPTR_T)que_head->fl, 0);
			else
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
					que_head, 0, RTS_ERROR_TEXT("cacheq_active2"), (UINTPTR_T)que_head->bl, 0);
		}
		/* loop through the active queue */
		for (cstt_prev = (cache_state_rec_ptr_t)que_head,
			cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)que_head + que_head->fl), cnt = n_bts;
			(cstt != (cache_state_rec_ptr_t)que_head) && (cnt > 0);
			cstt_prev = cstt, cnt--, cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.fl))
		{
			cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cstt - SIZEOF(cr->blkque));
			if (CR_NOT_IN_RANGE((cache_rec_ptr_t)cr, cr_lo, cr_hi))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), que_head, -1,
					cr, RTS_ERROR_TEXT("active cstt->state_que"), cr_lo, cr_hi);
				break;
			}
			if (CR_NOT_ALIGNED(cr, cr_lo))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), que_head, -1,
					RTS_ERROR_TEXT("active cstt->state_que"), cr, cr_lo, SIZEOF(cache_rec));
				break;
			}
			if ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
					RTS_ERROR_TEXT("active queue.bl"), (UINTPTR_T)cstt->state_que.bl,
					(sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
			}
			if (0 == cstt->dirty)
			{
				assert(expect_damage);
				ret = FALSE;
				dummy_tn = (trans_num)TRUE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), cr, cstt->blk,
					RTS_ERROR_TEXT("active cr->dirty"), &cstt->dirty, &dummy_tn, CALLFROM);
			}
			if (((0 != cstt->flushed_dirty_tn) && (cstt->dirty <= cstt->flushed_dirty_tn))
				|| (cstt->dirty > csd->trans_hist.curr_tn))
			{
				assert(expect_damage);
				ret = FALSE;
				dummy_tn = cstt->flushed_dirty_tn + 1;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg), cstt + SIZEOF(que_head), cstt->blk,
					&cstt->dirty, RTS_ERROR_TEXT("active dirty (tn)"), &dummy_tn, &csd->trans_hist.curr_tn);
			}
			/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
			 * and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
			 */
			if (caller_is_wcs_recover)
				cstt->dirty = FAKE_DIRTY;	/* change the flag to indicate it was found in a state queue */
			if (0 == cstt->state_que.fl)
			{	/* No point proceeding to next iteration as "cstt + cstt->state_que.fl" will be same as "cstt" */
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
					RTS_ERROR_TEXT("active queue.fl"), (UINTPTR_T)cstt->state_que.fl, (UINTPTR_T)-1);
				break;
			}
		}
		if (cnt == 0)
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
				que_head, 0, RTS_ERROR_TEXT("active queue entries"), (UINTPTR_T)(n_bts + 1), (UINTPTR_T)n_bts);
		} else if ((cstt == (cache_state_rec_ptr_t)que_head)
			&& ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev))
		{	/* at this point "cstt" is active que_head and its backlink does not point to last entry in active queue */
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, 0, RTS_ERROR_TEXT("active queue base"),
				(UINTPTR_T)cstt->state_que.bl, (sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
		}
		/* loop through the wip queue */
		que_head = &csa->acc_meth.bg.cache_state->cacheq_wip;
		if ((sm_long_t)que_head % SIZEOF(que_head->fl) != 0)
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), que_head, 0, RTS_ERROR_TEXT("cacheq_wip"),
				que_head, ((sm_long_t)que_head / SIZEOF(que_head->fl)) * SIZEOF(que_head->fl));
		}
		if ((0 != que_head->fl) != (0 != que_head->bl))
		{
			assert(expect_damage);
			ret = FALSE;
			if (que_head->fl)
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
					que_head, 0, RTS_ERROR_TEXT("cacheq_wip1"), (UINTPTR_T)que_head->fl, 0);
			else
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
					que_head, 0, RTS_ERROR_TEXT("cacheq_wip2"), (UINTPTR_T)que_head->bl, 0);
		}
		for (cstt_prev = (cache_state_rec_ptr_t)que_head,
				cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)que_head + que_head->fl), cnt = n_bts;
			(cstt != (cache_state_rec_ptr_t)que_head) && (cnt > 0);
				cstt_prev = cstt, cnt--, cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.fl))
		{
			cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cstt - SIZEOF(cr->blkque));
			if (CR_NOT_IN_RANGE((cache_rec_ptr_t)cr, cr_lo, cr_hi))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), que_head, -1,
					cr, RTS_ERROR_TEXT("wip cstt->state_que"), cr_lo, cr_hi);
				break;
			}
			if (CR_NOT_ALIGNED(cr, cr_lo))
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), que_head, -1,
					RTS_ERROR_TEXT("wip cstt->state_que"), cr, cr_lo, SIZEOF(cache_rec));
				break;
			}
			if ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
					RTS_ERROR_TEXT("wip queue.bl"), (UINTPTR_T)cstt->state_que.bl,
					(sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
			}
			if (cstt->epid == 0)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
					cr, cstt->blk, RTS_ERROR_TEXT("wip cr->epid"), cstt->epid, -1, CALLFROM);
			}
			if (0 == cstt->dirty)
			{
				assert(expect_damage);
				ret = FALSE;
				dummy_tn = (trans_num)TRUE;
				SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), cr, cstt->blk,
					RTS_ERROR_TEXT("wip cr->dirty"), &cstt->dirty, &dummy_tn, CALLFROM);
			}
			if (((0 != cstt->flushed_dirty_tn) && (cstt->dirty <= cstt->flushed_dirty_tn))
								|| (cstt->dirty > csd->trans_hist.curr_tn))
			{
				assert(expect_damage);
				ret = FALSE;
				dummy_tn = cstt->flushed_dirty_tn + 1;
				SEND_MSG_CSA(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg),
					(sm_uc_ptr_t)cstt + SIZEOF(que_head), cstt->blk, &cstt->dirty,
					RTS_ERROR_TEXT("wip dirty (tn)"), &dummy_tn, &csd->trans_hist.curr_tn);
			}
			/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
			 * and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
			 */
			if (caller_is_wcs_recover)
				cstt->dirty = FAKE_DIRTY;	/* change the flag to indicate it was found in a state queue */
			if (0 == cstt->state_que.fl)
			{
				assert(expect_damage);
				ret = FALSE;
				SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
					RTS_ERROR_TEXT("wip queue.fl"), (UINTPTR_T)cstt->state_que.fl, (UINTPTR_T)-1);
				break;
			}
		}
		if (cnt == 0)
		{
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
				que_head, 0, RTS_ERROR_TEXT("wip queue entries"), (UINTPTR_T)(n_bts + 1), (UINTPTR_T)n_bts);
		} else if ((cstt == (cache_state_rec_ptr_t)que_head)
				&& ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev))
		{	/* at this point "cstt" is wip que_head and its backlink does not point to last entry in the wip queue */
			assert(expect_damage);
			ret = FALSE;
			SEND_MSG_CSA(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, 0, RTS_ERROR_TEXT("active queue base"),
				(UINTPTR_T)cstt->state_que.bl, (sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
		}
		/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
		 * and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
		 */
		if (caller_is_wcs_recover)
		{	/* loop through the cache_recs again to look for lost dirties */
			for (cr = cr_lo, cnt = n_bts; cnt > 0; cr++, cnt--)
			{
				if (cr->dirty == FAKE_DIRTY)
					cr->dirty = cr->flushed_dirty_tn + 1;
				else
				{
					if (0 != cr->dirty)
					{
						assert(expect_damage);
						ret = FALSE;
						dummy_tn = (trans_num)FALSE;
						SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), cr, cr->blk,
							RTS_ERROR_TEXT("non-state cr->dirty"), &cr->dirty, &dummy_tn, CALLFROM);
						SEND_MSG_CSA(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
							cr, cr->blk, RTS_ERROR_TEXT("cr->epid"), cr->epid, 0, CALLFROM);
					}
				}
			}
		}
	}
	SEND_MSG_CSA(VARLSTCNT(7) ERR_DBWCVERIFYEND, 5, DB_LEN_STR(reg), process_id, process_id, &csd->trans_hist.curr_tn);
	if (NULL != blkque_array)
		free(blkque_array);
	return ret;
}
