/****************************************************************
 *								*
 *	Copyright 2003, 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 "gtm_string.h"
#include "gtm_stat.h"
#if defined(UNIX)
#include "gtm_fcntl.h"
#include "gtm_unistd.h"
#include "eintr_wrappers.h"
#include "gtm_permissions.h"
#if defined(__MVS__)
#include "gtm_zos_io.h"
#endif
#elif defined(VMS)
#include <rms.h>
#include <iodef.h>
#include <efndef.h>
#include "iosb_disk.h"
#endif

#include "gtm_file_stat.h"
#include "gtm_rename.h"
#include "error.h"
#include "gdsroot.h"
#include "gdsbt.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "gtmio.h"
#include "util.h"
#include "gtmmsg.h"
#include "send_msg.h"
#include "iosp.h"
#include "repl_sp.h"
#include "is_file_identical.h"
#include "jnl_get_checksum.h"
#include "gtmimagename.h"
#include "get_fs_block_size.h"
#include "wbox_test_init.h"
#include "gt_timer.h"
#include "anticipatory_freeze.h"

/* Note : Now all system error messages are issued here. So callers do not need to issue them again */
#define STATUS_MSG(info)										\
{													\
	if (SS_NORMAL != info->status2)									\
	{												\
		if (IS_GTM_IMAGE)									\
			send_msg(VARLSTCNT(12) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, info->jnl,	\
				info->fn_len, info->fn, info->status, 0, info->status2);		\
		else											\
			gtm_putmsg(VARLSTCNT1(11) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, info->jnl,\
				info->fn_len, info->fn, info->status, PUT_SYS_ERRNO(info->status2));	\
	} else if (SS_NORMAL != info->status)								\
	{												\
		if (IS_GTM_IMAGE)									\
			send_msg(VARLSTCNT(10) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, 		\
				info->jnl, info->fn_len, info->fn, info->status);			\
		else											\
			gtm_putmsg(VARLSTCNT(10) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, 		\
				info->jnl, info->fn_len, info->fn, info->status);			\
	}												\
}
#define RETURN_ON_ERROR(info)						\
if (SYSCALL_ERROR(info->status) || SYSCALL_ERROR(info->status2))	\
{									\
	int	status;							\
	F_CLOSE(channel, status);/* resets "channel" to FD_INVALID */	\
	if (NULL != jrecbuf_base)					\
	{								\
		free(jrecbuf_base);					\
		jrecbuf_base = NULL;					\
	}								\
	return EXIT_ERR;						\
}

#if defined(VMS)
#define ZERO_SIZE_IN_BLOCKS	127 /* 127 is RMS maximum blocks / write */
#define ZERO_SIZE		(ZERO_SIZE_IN_BLOCKS * DISK_BLOCK_SIZE)
#endif

GBLREF 	jnl_gbls_t		jgbl;
GBLREF	boolean_t		mupip_jnl_recover;
GBLREF	jnl_process_vector	*prc_vec;

ZOS_ONLY(error_def(ERR_BADTAG);)
error_def(ERR_FILERENAME);
error_def(ERR_JNLCRESTATUS);
error_def(ERR_JNLFNF);
error_def(ERR_PERMGENFAIL);
error_def(ERR_PREMATEOF);
error_def(ERR_RENAMEFAIL);
error_def(ERR_TEXT);

/* Create a journal file from info.
 * If necessary, it renames journal file of same name.
 * Note: jgbl.gbl_jrec_time must be set by callers
 */
uint4	cre_jnl_file(jnl_create_info *info)
{
	mstr 		filestr;
	int 		org_fn_len, rename_fn_len, fstat;
	uint4		ustatus;
	char		*org_fn, rename_fn[MAX_FN_LEN];

	if (!info->no_rename)	/* ***MAY*** be rename is required */
	{
		if (SS_NORMAL != (info->status = prepare_unique_name((char *)info->jnl, info->jnl_len, "", "",
				rename_fn, &rename_fn_len, &info->status2)))
		{	/* prepare_unique_name calls append_time_stamp which needs to open the info->jnl file.
			 * We are here because append_time_stamp failed to open info->jnl or something else.
			 * So check if info->jnl is present in the system */
			filestr.addr = (char *)info->jnl;
			filestr.len = info->jnl_len;
			if (FILE_NOT_FOUND != (fstat = gtm_file_stat(&filestr, NULL, NULL, FALSE, &ustatus)))
			{
				if (FILE_STAT_ERROR == fstat)
				{
					STATUS_MSG(info);	/* for prepare_unique_name call */
					info->status = ustatus;
					info->status2 = SS_NORMAL;
				}
				STATUS_MSG(info);
				return EXIT_ERR;
			}
			if (IS_GTM_IMAGE)
				send_msg(VARLSTCNT(4) ERR_JNLFNF, 2, filestr.len, filestr.addr);
			else
				gtm_putmsg(VARLSTCNT(4) ERR_JNLFNF, 2, filestr.len, filestr.addr);
			STATUS_MSG(info);
			info->status = info->status2 = SS_NORMAL;
			info->no_rename = TRUE;		/* We wanted to rename, but not required */
			info->no_prev_link = TRUE;	/* No rename => no prev_link */
		} else
		{
			/* Note if info->no_prev_link == TRUE, we do not keep previous link, though rename can happen */
			if (JNL_ENABLED(info) && !info->no_prev_link)
			{
				memcpy(info->prev_jnl, rename_fn, rename_fn_len + 1);
				info->prev_jnl_len = rename_fn_len;
			} else
				assert(info->no_prev_link);
		}
	} /* else we know for sure rename is not required */
	return (cre_jnl_file_common(info, rename_fn, rename_fn_len));
}

/* This creates info->jnl and if (!info->no_rename) then it renames existing info->jnl to be rename_fn */
uint4 cre_jnl_file_common(jnl_create_info *info, char *rename_fn, int rename_fn_len)
{
	jnl_file_header		*header;
	unsigned char		hdr_base[JNL_HDR_LEN + MAX_IO_BLOCK_SIZE];
	struct_jrec_pfin	*pfin_record;
	struct_jrec_pini	*pini_record;
	struct_jrec_epoch	*epoch_record;
	struct_jrec_eof		*eof_record;
	unsigned char		*create_fn, fn_buff[MAX_FN_LEN];
	int			create_fn_len, cre_jnl_rec_size, status, write_size, jrecbufbase_size;
	fd_type			channel;
	char            	*jrecbuf, *jrecbuf_base;
	gd_id			jnlfile_id;
#if defined(VMS)
	struct FAB      	fab;
	struct NAM      	nam;
	char            	es_buffer[MAX_FN_LEN], name_buffer[MAX_FN_LEN];
	uint4			blk, block, zero_size;
	io_status_block_disk	iosb;
#elif defined(UNIX)
	struct stat		stat_buf;
	int			fstat_res;
	ZOS_ONLY(int		realfiletag;)
	int			stat_res;
	int			group_id;
	struct stat		sb;
	int			perm;
	struct perm_diag_data	pdd;
#endif
	int			idx;
	trans_num		db_tn;
	uint4			temp_offset, temp_checksum, pfin_offset, eof_offset;
	uint4			jnl_fs_block_size;

	jrecbuf = NULL;
	if (info->no_rename)
	{	/* The only cases where no-renaming is possible are as follows
    		 * (i) MUPIP SET JOURNAL where the new journal file name is different from the current journal file name
		 * (ii) For MUPIP BACKUP, MUPIP SET JOURNAL, GT.M Runtime and forw_phase_recovery,
		 * 	in case the current journal file does not exist (due to some abnormal condition).
		 *	But in this case we cut the link and hence info->no_prev_link should be TRUE.
		 * The assert below tries to capture this as much as possible without introducing any new global variables.
		 */
		assert((IS_MUPIP_IMAGE && !jgbl.forw_phase_recovery) || (IS_GTM_IMAGE && info->no_prev_link));
		create_fn_len = info->jnl_len;
		create_fn = info->jnl;
		assert(0 == create_fn[create_fn_len]);
	} else
	{
		create_fn = &fn_buff[0];
		if (SS_NORMAL != (info->status = prepare_unique_name((char *)info->jnl, (int)info->jnl_len, "", EXT_NEW,
								     (char *)create_fn, &create_fn_len, &info->status2)))
		{
			STATUS_MSG(info);
			return EXIT_ERR;
		}
	}
#if defined(UNIX)
	OPENFILE3((char *)create_fn, O_CREAT | O_EXCL | O_RDWR, 0600, channel);
	if (-1 == channel)
	{
		info->status = errno;
		STATUS_MSG(info);
		return EXIT_ERR;
	}
#if defined(__MVS__)
 	if (-1 == gtm_zos_set_tag(channel, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag))
 		TAG_POLICY_SEND_MSG((char *)create_fn, errno, realfiletag, TAG_BINARY);
#endif
	FSTAT_FILE(channel, &stat_buf, fstat_res);
	if (-1 == fstat_res)
	{
		info->status = errno;
		STATUS_MSG(info);
		F_CLOSE(channel, status);
		return EXIT_ERR;
	}
	/* check database file again. It was checked earlier so should still be ok. */
	STAT_FILE((sm_c_ptr_t)info->fn, &sb, stat_res);
	if (-1 == stat_res)
	{
		info->status = errno;
		STATUS_MSG(info);
		F_CLOSE(channel, status);
		return EXIT_ERR;
	}
	/* setup new group and permissions if indicated by the security rules.
	 */
	if (gtm_set_group_and_perm(&sb, &group_id, &perm, PERM_FILE, &pdd) < 0)
	{
		send_msg(VARLSTCNT(6+PERMGENDIAG_ARG_COUNT)
			ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("journal file"), RTS_ERROR_STRING(info->fn),
			PERMGENDIAG_ARGS(pdd));
		if (IS_GTM_IMAGE)
			rts_error(VARLSTCNT(6+PERMGENDIAG_ARG_COUNT)
				ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("journal file"), RTS_ERROR_STRING(info->fn),
				PERMGENDIAG_ARGS(pdd));
		else
			gtm_putmsg(VARLSTCNT(6+PERMGENDIAG_ARG_COUNT)
				ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("journal file"), RTS_ERROR_STRING(info->fn),
				PERMGENDIAG_ARGS(pdd));
		F_CLOSE(channel, status);
		return EXIT_ERR;
	}
	/* if group not the same then change group of temporary file */
	if ((-1 != group_id) && (group_id != stat_buf.st_gid) && (-1 == fchown(channel, -1, group_id)))
	{
		info->status = errno;
		STATUS_MSG(info);
		F_CLOSE(channel, status);
		return EXIT_ERR;
	}
	if (-1 == FCHMOD(channel, perm))
	{
		info->status = errno;
		STATUS_MSG(info);
		F_CLOSE(channel, status);	/* resets "channel" to FD_INVALID */
		return EXIT_ERR;
	}
	jnl_fs_block_size = get_fs_block_size(channel);
	/* We need to write the journal file header, followed by pini/epoch/pfin/eof records followed by 0-padding
	 * to ensure the final size of the journal file is filesystem-block-size aligned.
	 * To have a "jnl_fs_block_size" aligned buffer that is also a multiple of jnl_fs_block_size long,
	 * we need to allocate the needed size + 2 * jnl_fs_block_size.  This will leave up to jnl_fs_block_size
	 * before the actual "used" part for alignment plus enough left after the "used" part to make up a buffer
	 * to write that is a multiple of jnl_fs_block_size.
	 */
	jrecbufbase_size = PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN + (2 * jnl_fs_block_size);
	jrecbuf_base = malloc(jrecbufbase_size);
	jrecbuf = (char *)ROUND_UP2((uintszofptr_t)jrecbuf_base, jnl_fs_block_size);
	memset(jrecbuf, 0, jnl_fs_block_size);
	set_gdid_from_stat(&jnlfile_id, &stat_buf);
#elif defined(VMS)
	nam = cc$rms_nam;
	nam.nam$l_rsa = name_buffer;
	nam.nam$b_rss = SIZEOF(name_buffer);
	nam.nam$l_esa = es_buffer;
	nam.nam$b_ess = SIZEOF(es_buffer);
	nam.nam$b_nop = NAM$M_NOCONCEAL;
	fab = cc$rms_fab;
	fab.fab$l_nam = &nam;
	fab.fab$b_org = FAB$C_SEQ;
	fab.fab$b_rfm = FAB$C_FIX;
	fab.fab$l_fop = FAB$M_UFO | FAB$M_MXV | FAB$M_CBT;
	fab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_BIO;
	fab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_UPI | FAB$M_NIL;
	fab.fab$w_mrs = DISK_BLOCK_SIZE;
	fab.fab$l_alq = info->alloc;
	fab.fab$w_deq = info->extend;
	fab.fab$l_fna = create_fn;
	fab.fab$b_fns = create_fn_len;
	info->status = sys$create(&fab);
	if (0 == (info->status & 1))
	{
		info->status2 = fab.fab$l_stv;	/* store secondary status information */
		STATUS_MSG(info);
		return EXIT_ERR;
	}
	channel = fab.fab$l_stv;
	jrecbufbase_size = ZERO_SIZE;
	jrecbuf_base = jrecbuf = malloc(ZERO_SIZE);
	memset(jrecbuf, 0, ZERO_SIZE);
	block = (JNL_HDR_LEN >> LOG2_DISK_BLOCK_SIZE);
	assert(block * DISK_BLOCK_SIZE == JNL_HDR_LEN);
	for (blk = block;  blk < info->alloc;  blk += ZERO_SIZE_IN_BLOCKS)
	{
		zero_size = (blk + ZERO_SIZE_IN_BLOCKS <= info->alloc) ?
			ZERO_SIZE : (info->alloc - blk) * DISK_BLOCK_SIZE;
		JNL_DO_FILE_WRITE(NULL, NULL, channel, blk * DISK_BLOCK_SIZE, jrecbuf, zero_size, info->status, info->status2);
		STATUS_MSG(info);
		RETURN_ON_ERROR(info);
	}
	memcpy(jnlfile_id.dvi, &nam.nam$t_dvi, SIZEOF(jnlfile_id.dvi));
	memcpy(jnlfile_id.did, &nam.nam$w_did, SIZEOF(jnlfile_id.did));
	memcpy(jnlfile_id.fid, &nam.nam$w_fid, SIZEOF(jnlfile_id.fid));
	jnl_fs_block_size = get_fs_block_size(channel);
#endif
	info->checksum = compute_checksum(INIT_CHECKSUM_SEED, (uint4 *)&jnlfile_id, SIZEOF(gd_id));
	/* Journal file header size relies on this assert */
	assert(256 == GTMCRYPT_RESERVED_HASH_LEN);
	header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_base, jnl_fs_block_size));
	/* We have already saved previous journal file name in info */
	jfh_from_jnl_info(info, header);
	header->recover_interrupted = mupip_jnl_recover;
	assert(ROUND_UP2(JNL_HDR_LEN, jnl_fs_block_size) == JNL_HDR_LEN);
	assert((unsigned char *)header + JNL_HDR_LEN <= ARRAYTOP(hdr_base));
	/* Although the real journal file header is REAL_JNL_HDR_LEN, we write the entire journal file header
	 * including padding (64K in Unix) so we write 0-padding instead of some garbage. Not necessary
	 * but makes analysis of journal file using dump utilities (like od) much easier. Also 0-initialization
	 * might help us in the future when we add some fields in the file header. The cost of the extra padding
	 * write is not so much since it is only once per journal file at creation time. All future writes of the
	 * file header write only the real file header and not the 0-padding.
	 */
	JNL_DO_FILE_WRITE(info->csa, create_fn, channel, 0, header, JNL_HDR_LEN, info->status, info->status2);
	STATUS_MSG(info);
	RETURN_ON_ERROR(info);
	assert(DISK_BLOCK_SIZE >= EPOCH_RECLEN + EOF_RECLEN + PFIN_RECLEN + PINI_RECLEN);
	pini_record = (struct_jrec_pini *)&jrecbuf[0];
	pini_record->prefix.jrec_type = JRT_PINI;
	pini_record->prefix.forwptr = pini_record->suffix.backptr = PINI_RECLEN;
	db_tn = info->csd->trans_hist.curr_tn;
	pini_record->prefix.tn = db_tn;
	pini_record->prefix.pini_addr = JNL_HDR_LEN;
	pini_record->prefix.time = jgbl.gbl_jrec_time;	/* callers must set it */
	pini_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE;
	assert(prc_vec);
	memcpy((unsigned char*)&pini_record->process_vector[CURR_JPV], (unsigned char*)prc_vec, SIZEOF(jnl_process_vector));
	/* Already process_vector[ORIG_JPV] is memset 0 */
	pini_record->filler = 0;
	pini_record->prefix.checksum = INIT_CHECKSUM_SEED;
	temp_checksum = compute_checksum(INIT_CHECKSUM_SEED, (uint4 *)pini_record, SIZEOF(struct_jrec_pini));
	temp_offset = JNL_HDR_LEN;
	ADJUST_CHECKSUM(temp_checksum, temp_offset, temp_checksum);
	ADJUST_CHECKSUM(temp_checksum, info->checksum, pini_record->prefix.checksum);
	/* EPOCHs are written unconditionally in Unix while they are written only for BEFORE_IMAGE in VMS */
	if (JNL_HAS_EPOCH(info))
	{
		epoch_record = (struct_jrec_epoch *)&jrecbuf[PINI_RECLEN];
		epoch_record->prefix.jrec_type = JRT_EPOCH;
		epoch_record->prefix.forwptr = epoch_record->suffix.backptr = EPOCH_RECLEN;
		epoch_record->prefix.tn = db_tn;
		epoch_record->prefix.pini_addr = JNL_HDR_LEN;
		epoch_record->prefix.time = jgbl.gbl_jrec_time;
		epoch_record->blks_to_upgrd = info->blks_to_upgrd;
		epoch_record->free_blocks   = info->free_blocks;
		epoch_record->total_blks    = info->total_blks;
		epoch_record->fully_upgraded = info->csd->fully_upgraded;
		epoch_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE;
		epoch_record->jnl_seqno = info->reg_seqno;
		UNIX_ONLY(
			for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
				epoch_record->strm_seqno[idx] = info->csd->strm_reg_seqno[idx];
			if (jgbl.forw_phase_recovery)
			{	/* If MUPIP JOURNAL -ROLLBACK, might need some adjustment. See macro definition for comments */
				MUR_ADJUST_STRM_REG_SEQNO_IF_NEEDED(info->csd, epoch_record->strm_seqno);
			}
		)
		VMS_ONLY(
			for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
				assert(0 == epoch_record->strm_seqno[idx]); /* should have been zeroed already by above memset */
		)
		epoch_record->filler = 0;
		epoch_record->prefix.checksum = INIT_CHECKSUM_SEED;
		temp_checksum = compute_checksum(INIT_CHECKSUM_SEED,
							(uint4 *)epoch_record, SIZEOF(struct_jrec_epoch));
		temp_offset = JNL_HDR_LEN + PINI_RECLEN;
		ADJUST_CHECKSUM(temp_checksum, temp_offset, temp_checksum);
		ADJUST_CHECKSUM(temp_checksum, info->checksum, epoch_record->prefix.checksum);
		pfin_record = (struct_jrec_pfin *)&jrecbuf[PINI_RECLEN + EPOCH_RECLEN];
		pfin_offset = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN;
		eof_record = (struct_jrec_eof *)&jrecbuf[PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN];
		eof_offset = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN;
		cre_jnl_rec_size = PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN;
	} else
	{
		pfin_record = (struct_jrec_pfin *)&jrecbuf[PINI_RECLEN];
		pfin_offset = JNL_HDR_LEN + PINI_RECLEN;
		eof_record = (struct_jrec_eof *)&jrecbuf[PINI_RECLEN + PFIN_RECLEN];
		eof_offset = JNL_HDR_LEN + PINI_RECLEN + PFIN_RECLEN;
		cre_jnl_rec_size = PINI_RECLEN + PFIN_RECLEN + EOF_RECLEN;
	}
	pfin_record->prefix.jrec_type = JRT_PFIN;
	pfin_record->prefix.forwptr = pfin_record->suffix.backptr = PFIN_RECLEN;
	pfin_record->prefix.tn = db_tn;
	pfin_record->prefix.pini_addr = JNL_HDR_LEN;
	pfin_record->prefix.time = jgbl.gbl_jrec_time;
	pfin_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE;
	pfin_record->filler = 0;
	pfin_record->prefix.checksum = INIT_CHECKSUM_SEED;
	temp_checksum = compute_checksum(INIT_CHECKSUM_SEED, (uint4 *)pfin_record, SIZEOF(struct_jrec_pfin));
	ADJUST_CHECKSUM(temp_checksum, pfin_offset, temp_checksum);
	ADJUST_CHECKSUM(temp_checksum, info->checksum, pfin_record->prefix.checksum);
	eof_record->prefix.jrec_type = JRT_EOF;
	eof_record->prefix.forwptr = eof_record->suffix.backptr = EOF_RECLEN;
	eof_record->prefix.tn = db_tn;
	eof_record->prefix.pini_addr = JNL_HDR_LEN;
	eof_record->prefix.time = jgbl.gbl_jrec_time;
	QWASSIGN(eof_record->jnl_seqno, info->reg_seqno);
	eof_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE;
	eof_record->filler = 0;
	eof_record->prefix.checksum = INIT_CHECKSUM_SEED;
	temp_checksum = compute_checksum(INIT_CHECKSUM_SEED, (uint4 *)eof_record, SIZEOF(struct_jrec_eof));
	ADJUST_CHECKSUM(temp_checksum, eof_offset, temp_checksum);
	ADJUST_CHECKSUM(temp_checksum, info->checksum, eof_record->prefix.checksum);
	/* Assert that the journal file header and journal records are all in sync with respect to the db tn. */
	assert(header->bov_tn == db_tn);
	assert(header->eov_tn == db_tn);
	/* Write the journal records, but also 0-fill the tail of the journal file enough to fill
	 * an aligned filesystem-block-size boundary. Take into account that the file header is already written.
	 */
	write_size = ROUND_UP2((JNL_HDR_LEN + cre_jnl_rec_size), jnl_fs_block_size) - JNL_HDR_LEN;
	assert((jrecbuf + write_size) <= (jrecbuf_base + jrecbufbase_size));
	/* Assert that initial virtual-size of the journal file (which is nothing but the journal allocation)
	 * gives us enough space to keep the journal file header + padding + PINI/PFIN/EPOCH/EOF records.
	 * Do the assertion in units of 512-byte blocks as that keeps the values well under 4G. Keeping the
	 * units in bytes causes the highest autoswitchlimit value to round up to 4G effectively becoming 0
	 * on certain platforms and therefore failing the assert.
	 */
	assert(ROUND_UP2(header->virtual_size, jnl_fs_block_size/DISK_BLOCK_SIZE)
			> DIVIDE_ROUND_UP(JNL_HDR_LEN + write_size, DISK_BLOCK_SIZE));
	JNL_DO_FILE_WRITE(info->csa, create_fn, channel, JNL_HDR_LEN, jrecbuf, write_size, info->status, info->status2);
	STATUS_MSG(info);
	RETURN_ON_ERROR(info);
	UNIX_ONLY(GTM_JNL_FSYNC(info->csa, channel, status);)
	F_CLOSE(channel, status);	/* resets "channel" to FD_INVALID */
	free(jrecbuf_base);
	jrecbuf_base = NULL;
	if (info->no_rename)
		return EXIT_NRM;
	/* Say, info->jnl = a.mjl
	 * So system will have a.mjl and a.mjl_new for a crash before the following call
	 * Following does rename of a.mjl to a.mjl_timestamp.
	 * So system will have a.mjl_timestamp and a.mjl_new for a crash after this call
	 */
	WAIT_FOR_REPL_INST_UNFREEZE_SAFE(info->csa);	/* wait for instance freeze before journal file renames */
	if (SS_NORMAL != (info->status = gtm_rename((char *)info->jnl, (int)info->jnl_len,
						    (char *)rename_fn, rename_fn_len, &info->status2)))
	{
		if (IS_GTM_IMAGE)
			send_msg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn);
		else
			gtm_putmsg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn);
		STATUS_MSG(info);
		return EXIT_ERR;
	}
	/* Following does rename of a.mjl_new to a.mjl.
	 * So system will have a.mjl_timestamp as previous generation and a.mjl as new/current journal file
	 */
	WAIT_FOR_REPL_INST_UNFREEZE_SAFE(info->csa);	/* wait for instance freeze before journal file renames */
	if (SS_NORMAL !=  (info->status = gtm_rename((char *)create_fn, create_fn_len,
						     (char *)info->jnl, (int)info->jnl_len, &info->status2)))
	{
		if (IS_GTM_IMAGE)
			send_msg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn);
		else
			gtm_putmsg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn);
		STATUS_MSG(info);
		return EXIT_ERR;
	}
	if (IS_GTM_IMAGE)
		send_msg(VARLSTCNT (6) ERR_FILERENAME, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn);
	else
		gtm_putmsg(VARLSTCNT (6) ERR_FILERENAME, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn);
	DEBUG_ONLY(
		if (gtm_white_box_test_case_enabled && (WBTEST_JNL_CREATE_INTERRUPT == gtm_white_box_test_case_number))
		{
			UNIX_ONLY(DBGFPF((stderr, "CRE_JNL_FILE: started a wait\n")));	/* this white-box test is for UNIX */
			LONG_SLEEP(600);
			assert(FALSE); /* Should be killed before that */
		}
	)
	return EXIT_NRM;
}
