/****************************************************************
 *								*
 *	Copyright 2001, 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.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdsbml.h"
#include "dbfilop.h"
#include "gdsdbver.h"
#include "gdsblk.h"
#include "iosp.h"
#include "mupint.h"
#include "gds_blk_upgrade.h"
#ifdef GTM_CRYPT
#include "gtmcrypt.h"
#endif
#include "min_max.h"
#ifdef GTM_SNAPSHOT
#include "shmpool.h"		/* Needed for DBG_ENSURE_PTR_WITHIN_SS_BOUNDS */
#include "db_snapshot.h"
#endif
#include "mupip_exit.h"

GBLREF sgmnt_data	mu_int_data;
GBLREF int4		mu_int_ovrhd;
GBLREF gd_region	*gv_cur_region;
GTMCRYPT_ONLY(
GBLREF gtmcrypt_key_t	mu_int_encrypt_key_handle;
)
GBLREF	boolean_t	ointeg_this_reg;
GBLREF	sgmnt_addrs	*cs_addrs;
GBLREF	uint4		mu_int_errknt;
GBLREF	bool		region;

error_def(ERR_DBRDERR);
error_def(ERR_DBROLLEDBACK);
error_def(ERR_DYNUPGRDFAIL);
error_def(ERR_INTEGERRS);
error_def(ERR_REGSSFAIL);

uchar_ptr_t mu_int_read(block_id blk, enum db_ver *ondsk_blkver)
{
	int4			status;
	file_control		*fc;
	unsigned char		*tmp_ptr;
	GTMCRYPT_ONLY(
		int		req_dec_blk_size;
		int		crypt_status;
	)
	boolean_t 		have_blk = FALSE;
	sgmnt_addrs		*csa;
	GTM_SNAPSHOT_ONLY(
		boolean_t	read_failed;
		shm_snapshot_t	*ss_shm_ptr;
	)

	csa = cs_addrs;
#	ifdef UNIX
	if (region && csa->nl->onln_rlbk_pid)
	{
		gtm_putmsg(VARLSTCNT(1) ERR_DBROLLEDBACK);
		mupip_exit(ERR_INTEGERRS);
	}
#	endif
	tmp_ptr = malloc(mu_int_data.blk_size);
#	ifdef GTM_SNAPSHOT
	if (ointeg_this_reg)
	{
		assert(NULL != csa->ss_ctx);
		ss_shm_ptr = (SS_CTX_CAST(csa->ss_ctx))->ss_shm_ptr;
		DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr);
		if (!ss_shm_ptr->failure_errno)
			have_blk = ss_get_block(csa, blk, tmp_ptr);
		else
		{	/* GT.M has failed initiating snapshot resources. Report error and exit so that we don't continue with
			 * an INTEG which is unusable. Note that, it's okay to check failure_errno (in the shared memory) outside
			 * crit as GT.M is the only one that sets it and INTEG (while doing snapshot cleanup) is the only one
			 * that resets it
			 */
			gtm_putmsg(VARLSTCNT(5) ERR_REGSSFAIL, 3, ss_shm_ptr->failed_pid, DB_LEN_STR(gv_cur_region));
			mupip_exit(ERR_INTEGERRS);
		}
	}
#	endif
	if (!have_blk)
	{
		fc = gv_cur_region->dyn.addr->file_cntl;
		fc->op = FC_READ;
		fc->op_buff = tmp_ptr;
		fc->op_len = mu_int_data.blk_size;
		fc->op_pos = mu_int_ovrhd + ((gtm_int64_t)mu_int_data.blk_size / DISK_BLOCK_SIZE * blk);
		dbfilop(fc); /* No return if error */
#		ifdef GTM_SNAPSHOT
		if (ointeg_this_reg)
		{
			assert(NULL != ss_shm_ptr); /* should have been initialized above */
			/* Since we did not have locks up, ensure we didn't miss a before-image	while we were reading
			 * in this block. Note we don't have to check the return status since it will either get the
			 * before-image or not.
			 */
			if (!ss_shm_ptr->failure_errno)
				ss_get_block(csa, blk, tmp_ptr);
			else
			{
				gtm_putmsg(VARLSTCNT(5) ERR_REGSSFAIL, 3, ss_shm_ptr->failed_pid, DB_LEN_STR(gv_cur_region));
				mupip_exit(ERR_INTEGERRS);
			}
		}
#		endif
	}
#	ifdef GTM_CRYPT
	req_dec_blk_size = MIN(mu_int_data.blk_size, ((blk_hdr_ptr_t)tmp_ptr)->bsiz) - SIZEOF(blk_hdr);
	if (BLOCK_REQUIRE_ENCRYPTION(mu_int_data.is_encrypted, (((blk_hdr_ptr_t)tmp_ptr)->levl), req_dec_blk_size))
	{
		/* The below assert cannot be moved before BLOCK_REQUIRE_ENCRYPTION check done above as tmp_ptr could
		 * potentially point to a V4 block in which case the assert might fail when a V4 block is casted to
		 * a V5 block header.
		 */
		assert(((blk_hdr_ptr_t)tmp_ptr)->bsiz <= mu_int_data.blk_size);
		assert(((blk_hdr_ptr_t)tmp_ptr)->bsiz >= SIZEOF(blk_hdr));
		GTMCRYPT_DECODE_FAST(mu_int_encrypt_key_handle,
				    (char *)(tmp_ptr + SIZEOF(blk_hdr)),
				    req_dec_blk_size,
				    NULL,
				    crypt_status);
		if (0 != crypt_status)
			GC_RTS_ERROR(crypt_status, gv_cur_region->dyn.addr->fname);
	}
#	endif
	GDS_BLK_UPGRADE_IF_NEEDED(blk, tmp_ptr, tmp_ptr, &mu_int_data, ondsk_blkver, status, mu_int_data.fully_upgraded);
	if (SS_NORMAL != status)
		if (ERR_DYNUPGRDFAIL == status)
			rts_error(VARLSTCNT(5) status, 3, blk, DB_LEN_STR(gv_cur_region));
		else
			rts_error(VARLSTCNT(1) status);
	return tmp_ptr;
}
