/****************************************************************
 *								*
 * Copyright (c) 2001-2018 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.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gtm_string.h"

#include "copy.h"
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "mlkdef.h"
#include "mlk_ops.h"
#include "locklits.h"
#include "error.h"
#include "rc.h"
#include "gtcm.h"
#include "mlk_pvtblk_delete.h"
#include "mlk_unlock.h"
#include "mlk_pvtblk_insert.h"
#include "lckclr.h"
#include "mlk_lock.h"
#include "mlk_bckout.h"
#include "mmrhash.h"

GBLREF UINTPTR_T	rc_auxown;		/* From server, address to use in auxown field, same as OMI */
GBLREF unsigned short	lks_this_cmd;
GBLREF mlk_pvtblk	*mlk_pvt_root;
GBLREF int		omi_pid;
GBLREF gd_region	*gv_cur_region;
GBLREF sgmnt_addrs	*cs_addrs;

#define KEY_BUFF_SIZE 1024

int rc_prc_lock(rc_q_hdr *qhdr)
{
	rc_req_lock	*req;
	rc_sbkey	*sbk;
	short		i, len, pid_len;
	mval		lock, ext;
	mlk_pvtblk	**prior, *mp, *x;
	rc_lknam	*fl;
	unsigned char	action;
	bool		blocked;
	char		key_buff[KEY_BUFF_SIZE], *lkname;
	unsigned char	buff[12], *cp;
	short		subcnt;
	int		in_pid, locks_done, temp;

	ESTABLISH_RET(rc_dbms_ch, RC_SUCCESS);
	/*  Clean up dead locks in private list */
	for (prior = &mlk_pvt_root; *prior; )
	{	if (!(*prior)->granted || !(*prior)->nodptr || (*prior)->nodptr->owner != omi_pid)
			mlk_pvtblk_delete(prior);
		else
		{	(*prior)->trans = 0;
			prior = &(*prior)->next;
		}
	}

	req = (rc_req_lock *)qhdr;
	in_pid = ((qhdr->r.pid1.value << 16) | qhdr->r.pid2.value);
	cp = i2asc(buff,in_pid);
	pid_len = cp - buff;
	lks_this_cmd = 0;
	if (req->hdr.r.fmd.value & RC_MODE_CLEARLOCK)
	{	/*  Loop through all the locks, unlocking ones that belong to this client */
		for (prior = &mlk_pvt_root; *prior; )
		{
			MLK_PVTBLK_VALIDATE(*prior);
			if ((*prior)->nodptr->auxowner == rc_auxown
					&& pid_len == MLK_PVTBLK_TAIL(*prior)[0]
					&& !memcmp(MLK_PVTBLK_TAIL(*prior) + 1, buff, pid_len))
 			{	mlk_unlock(*prior);
				mlk_pvtblk_delete(prior);
			}else
				prior = &(*prior)->next;
		}
		action = LOCKED;
	} else if (req->hdr.r.fmd.value & RC_MODE_DECRLOCK)
	{	sbk = &req->dlocks[0].sb_key;
		fl = req->dlocks;

		/*  Loop through all requested locks */
		for ( i = 0; i < req->nlocks.value; i++)
		{
			if ((char *) fl - (char *) req + sbk->len.value + 1 > req->hdr.r.len.value)
			{	/* we are beyond the end of this packet and still have
				 * lock requests to process.  Bad packet.
				 */
				qhdr->a.erc.value = RC_BADXBUF;
				REVERT;
#ifdef DEBUG
				gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"Bad Rq: Invalid nLockNames value.");
#endif
				return -1;
			}

			if ((temp = rc_frmt_lck(key_buff, KEY_BUFF_SIZE, (unsigned char *)sbk->key, sbk->len.value, &subcnt)) < 0)
			{
			        temp = -temp;
			        qhdr->a.erc.value = temp;
				REVERT;

#ifdef DEBUG
				if (temp == RC_KEYTOOLONG)
				       gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"RC_KEYTOOLONG.");
				else
				       gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"Invalid lock request.");
#endif
				return -1;
			}
			temp += subcnt;

			/*  Loop through all owned locks looking for requested one */
			for (prior = &mlk_pvt_root; *prior; )
			{
				MLK_PVTBLK_VALIDATE(*prior);
				if ((*prior)->nodptr->auxowner == rc_auxown
						&& temp == (*prior)->nref_length
						&& !memcmp(&(*prior)->value[0], key_buff, temp)
						&& pid_len == MLK_PVTBLK_TAIL(*prior)[0]
						&& !memcmp(MLK_PVTBLK_TAIL(*prior) + 1, buff, pid_len))
	 			{
					(*prior)->level -= 1;
					if (!(*prior)->level)
					{	mlk_unlock(*prior);
						mlk_pvtblk_delete(prior);
					}
				} else
					prior = &(*prior)->next;
			}
		fl = (rc_lknam*)((char *)fl + sbk->len.value - 1 + SIZEOF(rc_lknam));
		sbk = &fl->sb_key;

		}
		qhdr->a.erc.value = RC_SUCCESS;
		REVERT;
		return 0;
	} else
		action = INCREMENTAL;
	lock.mvtype = ext.mvtype = MV_STR;
	lkname = (char*)req->dlocks[0].sb_key.key;
	sbk = &req->dlocks[0].sb_key;
	fl = req->dlocks;
	for ( i = 0; i < req->nlocks.value; i++)
	{
		if ((char *) fl - (char *) req + sbk->len.value + 1 > req->hdr.r.len.value)
		{	/* we are beyond the end of this packet and still have
			 * lock requests to process.  Bad packet.
			 */
			qhdr->a.erc.value = RC_BADXBUF;
			REVERT;
#ifdef DEBUG
			gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"Bad Rq: Invalid nLockNames value.");
#endif
			return -1;
		}

		if ((qhdr->a.erc.value = rc_fnd_file(&fl->xdsid)) != RC_SUCCESS)
		    {
			REVERT;
#ifdef DEBUG
	gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"rc_fnd_file failed.");
#endif
			return -1;
		    }

		if ((temp = rc_frmt_lck(key_buff, KEY_BUFF_SIZE, (unsigned char *)sbk->key, sbk->len.value, &subcnt)) < 0)
		{
			temp = -temp;
			qhdr->a.erc.value = temp;
			REVERT;
#ifdef DEBUG
			if (temp == RC_KEYTOOLONG)
				gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"RC_KEYTOOLONG.");
			else
				gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"Invalid lock request.");
#endif
			return -1;
		}
		temp += subcnt;
		MLK_PVTBLK_ALLOC(temp, subcnt, pid_len + 1, mp);
		mp->subscript_cnt = subcnt;
		mp->nref_length = temp;
		memcpy(mp->value, key_buff, mp->nref_length);
		MLK_PVTBLK_SUBHASH_GEN(mp);
		MLK_PVTCTL_INIT(mp->pvtctl, gv_cur_region);
		MLK_PVTBLK_TAIL(mp)[0] = pid_len;
		memcpy(MLK_PVTBLK_TAIL(mp) + 1, buff, pid_len);
		if (!mlk_pvtblk_insert(mp))
		{
			if (!(MLK_PVTBLK_TAIL(mp)[0] == MLK_PVTBLK_TAIL(mlk_pvt_root)[0]
				&& !memcmp(MLK_PVTBLK_TAIL(mp) + 1, MLK_PVTBLK_TAIL(mlk_pvt_root) + 1,
						MLK_PVTBLK_TAIL(mp)[0])))
			{
				free(mp);	/* Server owns lock on behalf of a different agent/client pair */
				REVERT;
				qhdr->a.erc.value = RC_LOCKCONFLICT;
				return 0;	/* Return lock not granted */
			}
			free(mp);
		} else if (mp != mlk_pvt_root)
		{	REVERT;
			qhdr->a.erc.value = RC_GLOBERRUNSPEC;
#ifdef DEBUG
	gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"RC_GLOBERRUNSPEC.");
#endif
			return -1;/* error */

		}
		MLK_PVTBLK_VALIDATE(mp);
		fl = (rc_lknam*)((char *)fl + sbk->len.value - 1 + SIZEOF(rc_lknam));
		sbk = &fl->sb_key;
	}
	blocked = FALSE;
	lckclr();
	for (x = mlk_pvt_root, locks_done = 0; locks_done < lks_this_cmd; x = x->next)
	{
	        locks_done++;
		/* If lock is obtained */
		if (!mlk_lock(x,rc_auxown,TRUE))
		{
			x->granted = TRUE;
			if (action == INCREMENTAL)
				x->level += x->translev;
			else
				x->level = 1;
		} else
		{
			blocked = TRUE;
			break;
		}
	}
	if (blocked)	/* need to back out all locks */
	{
		for (x = mlk_pvt_root, i = 0;
			i < locks_done ; x = x->next, i++)
		{
			mlk_bckout(x, action);
		}
		qhdr->a.erc.value = RC_LOCKCONFLICT;
	} else
		qhdr->a.erc.value = RC_SUCCESS;
	REVERT;
	return 0;
}

