/**************************************************************** * * * Copyright 2003, 2011 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 /* for offsetof macro */ #if defined(UNIX) #include "gtm_fcntl.h" #include "gtm_unistd.h" #elif defined(VMS) #include #include #include #include #include #include "iosb_disk.h" #endif #include "min_max.h" #include "gtm_string.h" #include "gtmio.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab.h" #include "muprec.h" #include "mur_read_file.h" #include "iosp.h" #include "copy.h" #include "eintr_wrappers.h" #include "util.h" #include "gtmmsg.h" #include "mur_validate_checksum.h" #include "repl_sp.h" /* for F_CLOSE (used by JNL_FD_CLOSE) */ error_def(ERR_BEGSEQGTENDSEQ); error_def(ERR_BOVTNGTEOVTN); error_def(ERR_GTMASSERT); error_def(ERR_JNLBADRECFMT); error_def(ERR_JNLFILOPN); error_def(ERR_JNLINVALID); error_def(ERR_JNLNOBIJBACK); error_def(ERR_JNLREAD); error_def(ERR_JNLREADBOF); error_def(ERR_JNLREADEOF); error_def(ERR_JNLUNXPCTERR); error_def(ERR_NOPREVLINK); error_def(ERR_PREMATEOF); error_def(ERR_REPLNOTON); error_def(ERR_RLBKJNLNOBIMG); error_def(ERR_TEXT); GBLREF reg_ctl_list *mur_ctl; GBLREF mur_opt_struct mur_options; GBLREF mur_gbls_t murgbl; GBLREF gd_region *gv_cur_region; GTMCRYPT_ONLY( GBLREF int process_exiting; ) /* * Function name: mur_prev_rec * PreCondition : mur_prev(offset > 0) must be done once, followed by zero or more calls to mur_prev_rec() * Input : jjctl (pointer to jctl) * Output : Sets jctl->reg_ctl->mur_desc->jnlrec, jctl->reg_ctl->mur_desc->jreclen and jctl->rec_offset to appropriate value. * Changes jjctl if it changes generation. * Return : SS_NORMAL on success, else error status * Description : This routine reads immediate previous journal record from the last call to this routine or mur_prev() * It can open previous generation journal and update rctl->jctl and passed in "jjctl" to reflect new jctl * It issues error message here as appropriate, so caller will not need to print the error again. */ uint4 mur_prev_rec(jnl_ctl_list **jjctl) { jnl_ctl_list *jctl; uint4 status; mur_read_desc_t *mur_desc; jctl = *jjctl; if (JNL_HDR_LEN < jctl->rec_offset) { if (SS_NORMAL == (status = mur_prev(jctl, 0))) { mur_desc = jctl->reg_ctl->mur_desc; assert(mur_desc->jreclen == mur_desc->jnlrec->prefix.forwptr); jctl->rec_offset -= mur_desc->jreclen; assert(jctl->rec_offset >= mur_desc->cur_buff->dskaddr); assert(JNL_HDR_LEN <= jctl->rec_offset); if (JRT_EOF != mur_desc->jnlrec->prefix.jrec_type) return SS_NORMAL; /* unexpected EOF record in the middle of the file */ gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, jctl->rec_offset, ERR_TEXT, 2, LEN_AND_LIT("Unexpected EOF record found [prev_rec]")); status = ERR_JNLBADRECFMT; } if (ERR_JNLBADRECFMT != status) return status; /* JNLBADRECFMT message already issued by mur_prev */ if (!mur_options.update && !jctl->tail_analysis) { /* If not in tail_analysis, allow EXTRACT/SHOW/VERIFY to proceed after printing the error, * if error_limit permits */ if (!mur_report_error(jctl, MUR_JNLBADRECFMT)) return status; return mur_valrec_prev(jctl, 0, jctl->rec_offset); /* continue in distress, look for other valid records */ } if (jctl->rec_offset < jctl->jfh->end_of_data) { /* This is an offset well before end_of_data so should contain valid records * irrespective of whether this is a backward or forward recovery. Issue error right away. * Notice that the offset jctl->rec_offset points to a good record. The badly formatted * journal record is actually one record BEFORE the printed offset. */ gtm_putmsg(VARLSTCNT(9) ERR_JNLBADRECFMT, 3, jctl->jnl_fn_len, jctl->jnl_fn, jctl->rec_offset, ERR_TEXT, 2, LEN_AND_LIT("Error accessing previous record")); } else { /* offset is at tail of journal file after crash. Caller could be backward recovery or * forward recovery or even extract/verify/show if they are in tail_analysis. We expect * caller to handle this situation. So do not issue any message here. Just return status. */ } return status; } assert(JNL_HDR_LEN == jctl->rec_offset); /* Go to previous generation */ if (NULL != jctl->prev_gen) { jctl = jctl->prev_gen; jctl->reg_ctl->jctl = jctl; } else { if (mur_options.forward) /* For forward we have already included all possible journal files, so this */ return ERR_JNLREADBOF; /* is not really an error, just an indication that we reached beginning of file */ /* open previous generation journal file as specified in journal file header */ if (0 == jctl->jfh->prev_jnl_file_name_length) { assert(mur_options.chain); return ERR_NOPREVLINK; } if (!mur_insert_prev(&jctl)) return ERR_JNLFILOPN; } jctl->rec_offset = jctl->lvrec_off; /* lvrec_off was set in fread_eof that was called when we opened the file(s) */ *jjctl = jctl; return mur_prev(jctl, jctl->rec_offset); } /* * Function Name: mur_next_rec * PreCondition : mur_next(offset > 0) must be done once, followed by zero or more calls to mur_next_rec() * Input : Pointer to jctl * Output : Sets jctl->reg_ctl->mur_desc->jnlrec, jctl->reg_ctl->mur_desc->jreclen and jctl->rec_offset to appropriate value. * May also change other globals if it changes generation. * Return : SS_NORMAL on success, else error status * Description : This function reads immediate next journal record from last call to this routine or mur_next() * It can open next generation journal and update rctl->jctl and passed in "jjctl" to reflect new jctl * It issues error message here when necessary, so caller does not need to print the error again. */ uint4 mur_next_rec(jnl_ctl_list **jjctl) { jnl_ctl_list *jctl; int rec_size; uint4 status; mur_read_desc_t *mur_desc; jctl = *jjctl; if (jctl->rec_offset < jctl->lvrec_off) { mur_desc = jctl->reg_ctl->mur_desc; assert(mur_desc->jreclen == mur_desc->jnlrec->prefix.forwptr); rec_size = mur_desc->jreclen; if (SS_NORMAL == (status = mur_next(jctl, 0))) { jctl->rec_offset += rec_size; assert(jctl->rec_offset <= jctl->lvrec_off); if (JRT_EOF != mur_desc->jnlrec->prefix.jrec_type || jctl->rec_offset == jctl->lvrec_off) return SS_NORMAL; gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, jctl->rec_offset, ERR_TEXT, 2, LEN_AND_LIT("Unexpected EOF record found [next_rec]")); status = ERR_JNLBADRECFMT; } if (ERR_JNLBADRECFMT != status) return status; if (!mur_options.update) { /* only allow EXTRACT/SHOW/VERIFY to proceed after printing the error if error_limit permits */ if (!mur_report_error(jctl, MUR_JNLBADRECFMT)) /* Issue error because mur_next_rec is called from * mur_forward(), from which errors are unexpected */ return status; /* continue in distress, look for other valid records */ return mur_valrec_next(jctl, jctl->rec_offset + rec_size); } gtm_putmsg(VARLSTCNT(9) ERR_JNLBADRECFMT, 3, jctl->jnl_fn_len, jctl->jnl_fn, jctl->rec_offset, ERR_TEXT, 2, LEN_AND_LIT("Error accessing next record")); return status; } assert(jctl->rec_offset == jctl->lvrec_off); if (NULL != jctl->next_gen) { jctl = jctl->next_gen; jctl->reg_ctl->jctl = jctl; *jjctl = jctl; jctl->rec_offset = JNL_HDR_LEN; return mur_next(jctl, JNL_HDR_LEN); } return ERR_JNLREADEOF; } /* * Routine name : mur_prev * Input : jctl & dskaddr * Output : Sets jctl->reg_ctl->mur_desc->jnlrec, jctl->reg_ctl->mur_desc->jreclen and jctl->rec_offset to appropriate value. * Return : SS_NORMAL on success * Pre-Condition: To read backward , first call must be with non_zero dskaddr * subsequent call will be with 0 as dskaddr * Pre-Condition: Always first call to a journal file is mur_prev(jctl, n > 0). * Then all following calls are mur_prev(jctl, 0) to read records sequentially backward. */ uint4 mur_prev(jnl_ctl_list *jctl, off_jnl_t dskaddr) { off_jnl_t buff_offset; uint4 status, partial_reclen; jrec_suffix *suffix; mur_buff_desc_t *swap_buff; boolean_t good_suffix, good_prefix; mur_read_desc_t *mur_desc; mur_desc = jctl->reg_ctl->mur_desc; if (0 != dskaddr) { /* read record at dskaddr */ assert(dskaddr < jctl->eof_addr); assert(dskaddr >= JNL_HDR_LEN); if (dskaddr >= jctl->eof_addr || dskaddr < JNL_HDR_LEN) { gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Requested offset out of range [prev]")); return (dskaddr >= jctl->eof_addr ? ERR_JNLREADEOF : ERR_JNLREADBOF); } assert(dskaddr == ROUND_UP2(dskaddr, JNL_REC_START_BNDRY)); /* dskaddr must be aligned at JNL_REC_START_BNDRY */ MUR_FREAD_CANCEL(jctl, mur_desc, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not cancel prior read [prev]"), jctl->status); return jctl->status; } mur_desc->buff_index = 1; mur_desc->cur_buff = &mur_desc->seq_buff[mur_desc->buff_index]; mur_desc->sec_buff = &mur_desc->seq_buff[1 - mur_desc->buff_index]; mur_desc->cur_buff->dskaddr = ROUND_DOWN2(dskaddr, MUR_BUFF_SIZE); mur_desc->cur_buff->blen = MIN(MUR_BUFF_SIZE, jctl->eof_addr - mur_desc->cur_buff->dskaddr); buff_offset = dskaddr - mur_desc->cur_buff->dskaddr; assert(JREC_PREFIX_UPTO_LEN_SIZE <= mur_desc->cur_buff->blen - buff_offset); /* we rely on reading at least up to the record length field (forwptr) */ if (SS_NORMAL != (status = mur_freadw(jctl, mur_desc->cur_buff))) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->cur_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Error from synchronous read into cur_buff [prev]"), status); return status; } mur_desc->jnlrec = (jnl_record *)(mur_desc->cur_buff->base + buff_offset); good_suffix = TRUE; if (FALSE != (good_prefix = IS_VALID_LEN_FROM_PREFIX(mur_desc->jnlrec, jctl->jfh))) { mur_desc->jreclen = mur_desc->jnlrec->prefix.forwptr; if (MUR_BUFF_SIZE <= mur_desc->cur_buff->dskaddr) { /* while we process the just read chunk, post a read for the immediately preceding chunk */ mur_desc->sec_buff->dskaddr = mur_desc->cur_buff->dskaddr - MUR_BUFF_SIZE; MUR_FREAD_START(jctl, mur_desc->sec_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->sec_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not initiate read into sec_buff in [prev] (dskaddr > 0)"), status); return status; } } else { /* we read the beginning MUR_BUFF_SIZE (or smaller) chunk from file, no previous chunk exists */ assert(0 == mur_desc->cur_buff->dskaddr); } if (buff_offset + mur_desc->jreclen > mur_desc->cur_buff->blen) { /* Journal record straddles MUR_BUFF_SIZE boundary, did not read the entire record, read what's left into * aux_buff2 which is located at the end of seq_buff[1], the current buffer */ mur_desc->aux_buff2.dskaddr = mur_desc->cur_buff->dskaddr + mur_desc->cur_buff->blen; mur_desc->aux_buff2.blen = (buff_offset + mur_desc->jreclen - mur_desc->cur_buff->blen); if (FALSE != (good_prefix = (jctl->eof_addr - mur_desc->aux_buff2.dskaddr >= mur_desc->aux_buff2.blen))) { if (SS_NORMAL != (status = mur_freadw(jctl, &mur_desc->aux_buff2))) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->aux_buff2.dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Error in synchronous read into aux_buff [prev]"), status); return status; } } else { gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Requested offset beyond end of file [prev] (dskaddr > 0)")); return ERR_JNLBADRECFMT; } } } /* end good_prefix */ } else { /* dskaddr == 0, locate the previous record in the buffer, reading from disk if necessary */ assert(JNL_HDR_LEN <= jctl->rec_offset); suffix = (jrec_suffix *)((char *)mur_desc->jnlrec - JREC_SUFFIX_SIZE); /* ok to test with possibly invalid backptr, we test for validity below */ if (((unsigned char *)suffix > mur_desc->cur_buff->base) && (((unsigned char *)mur_desc->jnlrec - suffix->backptr) >= mur_desc->cur_buff->base)) { /* prev record is contained completely in the current buffer */ if ((0 == mur_desc->buff_index) && ((unsigned char *)mur_desc->jnlrec + mur_desc->jreclen > mur_desc->cur_buff->top) && (0 < mur_desc->cur_buff->dskaddr)) /* end of rec in sec_buff and there is data to be read */ { /* we just finished processing the journal record that straddled seq_buff[0] and seq_buff[1], * start read in the now free secondary buffer (seq_buff[1]) to overlap with processing */ assert(MUR_BUFF_SIZE <= mur_desc->cur_buff->dskaddr); assert(!mur_desc->sec_buff->read_in_progress); mur_desc->sec_buff->dskaddr = mur_desc->cur_buff->dskaddr - MUR_BUFF_SIZE; MUR_FREAD_START(jctl, mur_desc->sec_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->sec_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not initiate read into sec_buff [prev] (dskaddr == 0)"), status); return status; } } } else { /* prev record completely in sec_buff or overlaps cur_buff and sec_buff */ if (0 == mur_desc->buff_index) { /* copy partial record to just past the end of seq_buff[1], i.e., aux_seq_buff[1] to make the record * available in contiguous memory */ partial_reclen = (uint4)((unsigned char *)mur_desc->jnlrec - mur_desc->seq_buff[0].base); if (0 < partial_reclen) memcpy(mur_desc->seq_buff[1].top, mur_desc->seq_buff[0].base, partial_reclen); suffix = (jrec_suffix *)(mur_desc->seq_buff[1].top + partial_reclen - JREC_SUFFIX_SIZE); } /* before switching the buffers, wait for completion of pending I/O */ if (mur_desc->sec_buff->read_in_progress) { MUR_FREAD_WAIT(jctl, mur_desc->sec_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->sec_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Error waiting for sec_buff read to complete [prev]"), status); return status; } } /* If possible, overlap I/O with processing, read into available buffer */ if ((0 == mur_desc->buff_index || /* we just copied partial record (if any), OR */ (unsigned char *)mur_desc->jnlrec == mur_desc->cur_buff->base) /* we completely processed cur_buff */ && 0 < mur_desc->sec_buff->dskaddr) /* there is data to be read */ { assert(mur_desc->sec_buff->dskaddr >= MUR_BUFF_SIZE); mur_desc->cur_buff->dskaddr = mur_desc->sec_buff->dskaddr - MUR_BUFF_SIZE; MUR_FREAD_START(jctl, mur_desc->cur_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->cur_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not initiate read into cur_buff [prev]"), status); return status; } } /* Make the buffer that has data that was previously read current */ mur_desc->buff_index = 1 - mur_desc->buff_index; swap_buff = mur_desc->cur_buff; mur_desc->cur_buff = mur_desc->sec_buff; mur_desc->sec_buff = swap_buff; } good_prefix = TRUE; if (FALSE != (good_suffix = IS_VALID_LEN_FROM_SUFFIX(suffix, jctl->jfh))) { mur_desc->jnlrec = (jnl_record *)((char *)suffix + JREC_SUFFIX_SIZE - suffix->backptr); assert((unsigned char *)mur_desc->jnlrec >= mur_desc->cur_buff->base); assert((unsigned char *)mur_desc->jnlrec < mur_desc->cur_buff->top); mur_desc->jreclen = suffix->backptr; if (jctl->rec_offset < mur_desc->jreclen + JNL_HDR_LEN) { gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, jctl->rec_offset, ERR_TEXT, 2, LEN_AND_LIT("Requested offset beyond beginning of file [prev]")); return ERR_JNLBADRECFMT; } } } /* end of dskaddr == 0 */ if (good_prefix && good_suffix && IS_VALID_JNLREC(mur_desc->jnlrec, jctl->jfh)) return SS_NORMAL; return ERR_JNLBADRECFMT; } /* * Routine name : mur_next * Input : jctl & dskaddr * Output : Sets jctl->reg_ctl->mur_desc->jnlrec, jctl->reg_ctl->mur_desc->jreclen and jctl->rec_offset to appropriate value. * Return : SS_NORMAL on success * Pre-Condition: Always first call to a journal file is mur_next(jctl, n > 0). * Then all following calls are mur_next(jctl, 0) to read records sequentially. */ uint4 mur_next(jnl_ctl_list *jctl, off_jnl_t dskaddr) { jrec_prefix *prefix; off_jnl_t buff_offset; uint4 status, partial_reclen; mur_buff_desc_t *swap_buff; boolean_t good_prefix; unsigned char *buf_top; mur_read_desc_t *mur_desc; mur_desc = jctl->reg_ctl->mur_desc; if (0 != dskaddr) { /* read record at dskaddr */ assert(jctl->tail_analysis || (dskaddr >= JNL_HDR_LEN && dskaddr <= jctl->lvrec_off)); assert(dskaddr < jctl->eof_addr); assert(dskaddr >= JNL_HDR_LEN); if (dskaddr >= jctl->eof_addr || dskaddr < JNL_HDR_LEN) { gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Requested offset out of range [next]")); return (dskaddr >= jctl->eof_addr ? ERR_JNLREADEOF : ERR_JNLREADBOF); } assert(dskaddr == ROUND_UP2(dskaddr, JNL_REC_START_BNDRY)); /* dskaddr must be aligned at JNL_REC_START_BNDRY */ MUR_FREAD_CANCEL(jctl, mur_desc, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not cancel prior read [next]"), jctl->status); return jctl->status; } mur_desc->buff_index = 0; mur_desc->cur_buff = &mur_desc->seq_buff[mur_desc->buff_index]; mur_desc->sec_buff = &mur_desc->seq_buff[1 - mur_desc->buff_index]; mur_desc->cur_buff->dskaddr = ROUND_DOWN2(dskaddr, MUR_BUFF_SIZE); mur_desc->cur_buff->blen = MIN(MUR_BUFF_SIZE, jctl->eof_addr - mur_desc->cur_buff->dskaddr); buff_offset = dskaddr - mur_desc->cur_buff->dskaddr; assert(JREC_PREFIX_UPTO_LEN_SIZE <= mur_desc->cur_buff->blen - buff_offset); /* we rely on reading at least up to * the record length field (forwptr) */ if (SS_NORMAL != (status = mur_freadw(jctl, mur_desc->cur_buff))) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->cur_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Error from synchronous read into cur_buff [next]"), status); return status; } mur_desc->jnlrec = (jnl_record *)(mur_desc->cur_buff->base + buff_offset); if (FALSE != (good_prefix = IS_VALID_LEN_FROM_PREFIX(mur_desc->jnlrec, jctl->jfh))) { mur_desc->jreclen = mur_desc->jnlrec->prefix.forwptr; if (MUR_BUFF_SIZE < jctl->eof_addr - mur_desc->cur_buff->dskaddr) /* data available to be read */ { /* while we process the just read chunk, post a read for the immediately succeeding chunk */ assert(MUR_BUFF_SIZE == mur_desc->cur_buff->blen); mur_desc->sec_buff->dskaddr = mur_desc->cur_buff->dskaddr + MUR_BUFF_SIZE; MUR_FREAD_START(jctl, mur_desc->sec_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->sec_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not initiate read into sec_buff [next] (dskaddr > 0)"), status); return status; } if (((unsigned char *)mur_desc->jnlrec + mur_desc->jreclen) > mur_desc->cur_buff->top) { /* Journal record straddles MUR_BUFF_SIZE boundary, did not read the record in its entirey, wait * for the rest of the record to be read into seq_buff[1], the secondary buffer */ MUR_FREAD_WAIT(jctl, mur_desc->sec_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->sec_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Error waiting for sec_buff read to complete [next] (dskaddr > 0)"), status); return status; } /* is the record available in its entirety? */ good_prefix = (buff_offset + mur_desc->jreclen <= mur_desc->cur_buff->blen + mur_desc->sec_buff->blen); } } else /* we just read the last chunk, nothing more to read */ good_prefix = (buff_offset + mur_desc->jreclen <= mur_desc->cur_buff->blen); /* is the record available in its entirety? */ if (!good_prefix) { gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Requested offset beyond end of file [next] (dskaddr > 0)")); return ERR_JNLBADRECFMT; } } /* end good_prefix */ } else { /* dskaddr == 0, locate the next record in the buffer, reading from disk if necessary */ prefix = (jrec_prefix *)((char *)mur_desc->jnlrec + mur_desc->jreclen); /* position to the next rec */ buf_top = mur_desc->cur_buff->base + mur_desc->cur_buff->blen; /* we might not have read a full MUR_BUFF_SIZE chunk */ good_prefix = ((unsigned char *)prefix + JREC_PREFIX_UPTO_LEN_SIZE < buf_top && /* ok to test with possibly */ (unsigned char *)prefix + prefix->forwptr <= buf_top); /* invalid forwptr, we test for */ /* validity below */ if (good_prefix) { /* next record is contained wholely in the current buffer */ if ((1 == mur_desc->buff_index) && ((unsigned char *)mur_desc->jnlrec < mur_desc->cur_buff->base) && /* beginning of rec in sec_buff */ (MUR_BUFF_SIZE < jctl->eof_addr - mur_desc->cur_buff->dskaddr)) /* there is data to be read */ { /* we just finished processing the journal record that straddled seq_buff[0] and seq_buff[1], * start read in the now free secondary buffer (seq_buff[0]) to overlap with processing */ assert(MUR_BUFF_SIZE == mur_desc->cur_buff->blen); assert(!mur_desc->sec_buff->read_in_progress); mur_desc->sec_buff->dskaddr = mur_desc->cur_buff->dskaddr + MUR_BUFF_SIZE; MUR_FREAD_START(jctl, mur_desc->sec_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->sec_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not initiate read into sec_buff [next] (dskaddr == 0)"), status); return status; } } } else if (jctl->eof_addr - jctl->rec_offset > mur_desc->jreclen + JREC_PREFIX_UPTO_LEN_SIZE)/* within eof? */ { /* next record completely in sec_buff or overlaps cur_buff and sec_buff */ if (1 == mur_desc->buff_index) { /* copy partial record to just prior to the beginning of seq_buf[0], i.e., aux_buff1 * to make the record available in contiguous memory */ partial_reclen = (uint4)(buf_top - (unsigned char *)prefix); if (0 < partial_reclen) memcpy(mur_desc->seq_buff[0].base - partial_reclen, (char *)prefix, partial_reclen); prefix = (jrec_prefix *)(mur_desc->seq_buff[0].base - partial_reclen); } /* before switching the buffers, wait for completion of pending I/O */ if (mur_desc->sec_buff->read_in_progress) { MUR_FREAD_WAIT(jctl, mur_desc->sec_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->sec_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Error waiting for sec_buff read to complete [next] (dskaddr == 0)"), status); return status; } } /* If possible, overlap I/O with processing, read into available buffer */ if ((1 == mur_desc->buff_index || /* we just copied partial record (if any), OR */ (unsigned char *)prefix >= mur_desc->cur_buff->top) && /* we completely processed cur_buff */ MUR_BUFF_SIZE < jctl->eof_addr - MAX(mur_desc->sec_buff->dskaddr, mur_desc->cur_buff->dskaddr)) /* there is data to be read; MAX magic is for when no read was posted to sec_buff (last chunk in file) */ { mur_desc->cur_buff->dskaddr = mur_desc->sec_buff->dskaddr + MUR_BUFF_SIZE; MUR_FREAD_START(jctl, mur_desc->cur_buff, status); if (SS_NORMAL != status) { gtm_putmsg(VARLSTCNT(10) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->cur_buff->dskaddr, ERR_TEXT, 2, LEN_AND_LIT("Could not initiate read into cur_buff [next]"), status); return status; } } /* Make the buffer that has data that was previously read current */ mur_desc->buff_index = 1 - mur_desc->buff_index; swap_buff = mur_desc->cur_buff; mur_desc->cur_buff = mur_desc->sec_buff; mur_desc->sec_buff = swap_buff; /* record available in its entirety? */ good_prefix = (jctl->eof_addr - jctl->rec_offset >= mur_desc->jreclen + prefix->forwptr); } if (good_prefix) { if (FALSE != (good_prefix = IS_VALID_LEN_FROM_PREFIX((jnl_record *)prefix, jctl->jfh))) { mur_desc->jnlrec = (jnl_record *)prefix; mur_desc->jreclen = prefix->forwptr; } } else { if (!jctl->tail_analysis) { gtm_putmsg(VARLSTCNT(9) ERR_JNLUNXPCTERR, 3, jctl->jnl_fn_len, jctl->jnl_fn, jctl->rec_offset, ERR_TEXT, 2, LEN_AND_LIT("Requested offset beyond end of file [next] (dskaddr == 0)")); return ERR_JNLBADRECFMT; } return ERR_JNLREADEOF; } } /* end of dskaddr == 0 */ if (good_prefix && IS_VALID_JNLREC(mur_desc->jnlrec, jctl->jfh)) return SS_NORMAL; return ERR_JNLBADRECFMT; } /* * Function Name: mur_read * Output: SS_NORMAL on successful * error status on unsuccessful * This function is called when reads are not sequential and double buffering will not help. * Also this only reads raw data without doing any record validation or processing. */ uint4 mur_read(jnl_ctl_list *jctl) { mur_buff_desc_t *random_buff; random_buff = &jctl->reg_ctl->mur_desc->random_buff; assert(0 < random_buff->blen); assert(random_buff->blen <= MUR_BUFF_SIZE); assert(random_buff->dskaddr == ROUND_UP2(random_buff->dskaddr, DISK_BLOCK_SIZE)); DO_FILE_READ(jctl->channel, random_buff->dskaddr, random_buff->base, random_buff->blen, jctl->status, jctl->status2); return jctl->status; } /* * Function name: mur_freadw * Input : struct mur_buffer_desc * buff * Output : SS_NORMAL on successful * error status on unsuccessful * This function reads synchronously * The caller function will set buff->blen, buff->dskaddr and check error status */ uint4 mur_freadw(jnl_ctl_list *jctl, mur_buff_desc_t *buff) { assert(jctl->eof_addr > buff->dskaddr); /* should never be reading at or beyond end of file */ assert(!buff->read_in_progress); buff->read_in_progress = FALSE; DO_FILE_READ(jctl->channel, buff->dskaddr, buff->base, buff->blen, jctl->status, jctl->status2); return jctl->status; } /* * Function name: mur_fread_eof * Input: jctl * Return: SS_NORMAL on success else failure status * Description: * This routine sets following fields (already mur_fopen must be called) : * jctl->properly_closed * jctl->lvrec_off * jctl->eof_addr * jctl->lvrec_time */ uint4 mur_fread_eof(jnl_ctl_list *jctl, reg_ctl_list *rctl) { jnl_record *rec; jnl_file_header *jfh; uint4 status, lvrec_off; jctl->reg_ctl = rctl; /* fill in reg_ctl backpointer from jctl to rctl */ if (mur_options.show_head_only) /* only SHOW HEADER, no need for time consuming search for valid eof */ return SS_NORMAL; jctl->tail_analysis = TRUE; jfh = jctl->jfh; if (0 != jfh->prev_recov_end_of_data) { /* regardless of jfh->crash, prev_recov_end_of_data and end_of_data must point to valid records since * earlier recovery processed the file to determine these values */ assert(!jfh->recover_interrupted); assert(!jfh->crash); lvrec_off = !mur_options.extract_full ? jfh->end_of_data : jfh->prev_recov_end_of_data; if (SS_NORMAL != (status = mur_prev(jctl, lvrec_off))) return status; jctl->lvrec_off = lvrec_off; rec = rctl->mur_desc->jnlrec; jctl->eof_addr = lvrec_off + rec->prefix.forwptr; jctl->lvrec_time = rec->prefix.time; jctl->properly_closed = TRUE; jctl->tail_analysis = FALSE; return SS_NORMAL; } if (!jfh->crash && (SS_NORMAL == mur_prev(jctl, jfh->end_of_data))) { rec = rctl->mur_desc->jnlrec; if (JRT_EOF == rec->prefix.jrec_type && rec->prefix.tn >= jfh->bov_tn && (!mur_options.rollback || ((struct_jrec_eof *)rec)->jnl_seqno >= jfh->end_seqno)) { /* valid EOF */ jctl->lvrec_off = jfh->end_of_data; jctl->eof_addr = jctl->lvrec_off + EOF_RECLEN; jctl->lvrec_time = rec->prefix.time; jctl->properly_closed = TRUE; jctl->tail_analysis = FALSE; return SS_NORMAL; } } /* crash or invalid end */ return mur_fread_eof_crash(jctl, jfh->end_of_data, jctl->eof_addr); } /* * Function name: mur_fread_eof_crash * Input: jctl, lo_off, hi_off * Return: SS_NORMAL on success else failure status * Description: This routine is same as mur_fread_eof except it is called when journal is improper for a crash */ uint4 mur_fread_eof_crash(jnl_ctl_list *jctl, off_jnl_t lo_off, off_jnl_t hi_off) { uint4 status; mur_read_desc_t *mur_desc; assert(JNL_HDR_LEN <= jctl->jfh->alignsize); if (lo_off < jctl->jfh->end_of_data || lo_off > hi_off) GTMASSERT; jctl->properly_closed = FALSE; if (SS_NORMAL != (status = mur_valrec_prev(jctl, lo_off, hi_off))) return status; jctl->lvrec_off = jctl->rec_offset; mur_desc = jctl->reg_ctl->mur_desc; jctl->eof_addr = jctl->rec_offset + mur_desc->jreclen; jctl->lvrec_time = mur_desc->jnlrec->prefix.time; return SS_NORMAL; } /* * module name: mur_valrec_prev * input:jnl_clt_list *jctl * off_jnl_t lo_off * off_jnl_t hi_off (hi_off is the end of the last record, not the start of last record) * Return: SS_NORMAL on success else failure status * Description: * This function searches for a valid record at align offset, between hi_off and lo_off * After finding a valid journal record at any mid_off at a aligned boundary, * it scans forward to find the last valid record in that align_sized block and sets * jctl->rec_offset as the offset of the valid record of that journal file * "mur_fread_eof" and "mur_prev" call this function */ uint4 mur_valrec_prev(jnl_ctl_list *jctl, off_jnl_t lo_off, off_jnl_t hi_off) { off_jnl_t mid_off, new_mid_off, rec_offset, mid_further; boolean_t this_rec_valid; jnl_file_header *jfh; jrec_prefix *prefix; uint4 status, rec_len, blen; jnl_record *rec; trans_num rec_tn; seq_num rec_seqno; mur_read_desc_t *mur_desc; jfh = jctl->jfh; assert(jctl->tail_analysis || jctl->after_end_of_data); assert(lo_off <= jctl->eof_addr); assert(hi_off >= lo_off); assert(hi_off <= jctl->eof_addr); if (lo_off == jctl->eof_addr) { /* special case when system crashes immediately after jnl file extension, back off by not more than an alignsize chunk */ assert(hi_off == jctl->eof_addr); if (lo_off > jfh->alignsize) lo_off -= jfh->alignsize; else lo_off = 0; /* start from the beginning of the file */ } mur_desc = jctl->reg_ctl->mur_desc; for (mid_off = ROUND_DOWN2(((lo_off >> 1) + (hi_off >> 1)), jfh->alignsize), jctl->rec_offset = 0; ; ) { assert(lo_off <= hi_off); assert(mid_off < hi_off); /* Note : It is also possible that, mid_off < lo_off */ assert(MUR_BUFF_SIZE >= jfh->max_jrec_len); /* max_jrec_len size read enough to verify valid record */ if (0 != mid_off) { blen = MIN(jfh->max_jrec_len, jctl->eof_addr - mid_off); mur_desc->random_buff.dskaddr = mid_off; } else { blen = MIN(JNL_HDR_LEN + jfh->max_jrec_len, jctl->eof_addr) - JNL_HDR_LEN; mur_desc->random_buff.dskaddr = JNL_HDR_LEN; } assert(MUR_BUFF_SIZE >= blen); mur_desc->random_buff.blen = blen; if (SS_NORMAL != (status = mur_read(jctl))) { gtm_putmsg(VARLSTCNT(6) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, mur_desc->random_buff.dskaddr, status); return status; } rec = (jnl_record *)mur_desc->random_buff.base; /* check the validity of the record */ this_rec_valid = (blen > JREC_PREFIX_UPTO_LEN_SIZE && blen >= rec->prefix.forwptr && IS_VALID_JNLREC(rec, jfh)); if (this_rec_valid) { assert(mid_off >= jctl->rec_offset || 0 == mid_off); /* are we by any chance going backwards? */ rec_offset = jctl->rec_offset; /* save this in case we need to restore it */ jctl->rec_offset = MAX(mid_off, JNL_HDR_LEN); mur_desc->jnlrec = rec; mur_desc->jreclen = rec->prefix.forwptr; this_rec_valid = (jctl->rec_offset + mur_desc->jreclen <= hi_off) && mur_validate_checksum(jctl); if (!this_rec_valid) { /* Initial validity checks were good but second check of this_rec_valid returned FALSE * (most likely checksum did not match). So restore jctl->rec_offset to what it was before. * The reason why it is set above before confirming the second validation check is that * mur_validate_checksum relies on jctl->rec_offset being set for its calculations. */ jctl->rec_offset = rec_offset; } } if (mid_off <= lo_off) break; if (this_rec_valid) lo_off = mid_off; else hi_off = mid_off; new_mid_off = ROUND_DOWN2(((lo_off >> 1) + (hi_off >> 1)), jfh->alignsize); mid_further = (new_mid_off != mid_off) ? 0 : jfh->alignsize; /* if necessary, move further to avoid repeat search */ if (hi_off - new_mid_off <= mid_further) break; mid_off = new_mid_off + mid_further; } if (0 == jctl->rec_offset) { /* Unexpected condition: * a) If lo_off was 0 while entering this routine, this means there was no good record at or after the beginning * of the journal file (i.e. offset of JNL_HDR_LEN). This is impossible since a PINI record was written at * journal file creation time and at least that should have been seen as a good record. * b) If lo_off was non-zero while entering this routine, this implies there was no good record anywhere at or * after an offset of lo_off in the journal file. But this is impossible since a non-zero value of lo_off * implies it is equal to jfh->end_of_data which means that an EPOCH/EOF record written at that offset * and the journal file was synced so we should at least see that good record. */ GTMASSERT; } /* We found a valid record at mid_off, go forward to find the last valid record. Before the for loop * of mur_next(0) following call is necessary to initialize buffers and for the assumptions in mur_next() */ rec_offset = jctl->rec_offset; if (SS_NORMAL != (status = mur_next(jctl, rec_offset))) { gtm_putmsg(VARLSTCNT(9) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, rec_offset, ERR_TEXT, 2, LEN_AND_LIT("Error received from mur_valrec_prev calling mur_next(rec_offset)")); return status; } /* Note: though currently it is required not to change system time when journaling is on, * we do not check time continuity here. We hope that in future we will remove the restriction */ rec_len = mur_desc->jnlrec->prefix.forwptr; rec_tn = mur_desc->jnlrec->prefix.tn; rec_seqno = 0; assert(rec_offset + rec_len <= hi_off); /* We get inside for loop below at least once */ for ( ; jctl->rec_offset + rec_len <= hi_off; rec_len = mur_desc->jnlrec->prefix.forwptr, rec_tn = mur_desc->jnlrec->prefix.tn) { rec_offset = jctl->rec_offset; if ((SS_NORMAL != mur_next(jctl, 0)) || ((mur_desc->jnlrec->prefix.tn != rec_tn) && (mur_desc->jnlrec->prefix.tn != (rec_tn + 1)))) break; rec = mur_desc->jnlrec; if (mur_options.rollback && REC_HAS_TOKEN_SEQ(rec->prefix.jrec_type)) { if (GET_JNL_SEQNO(rec) < rec_seqno) /* if jnl seqno is not in sequence */ break; rec_seqno = GET_JNL_SEQNO(rec); } jctl->rec_offset += rec_len; /* jctl->rec_offset is used in checksum calculation */ if (!mur_validate_checksum(jctl)) break; } jctl->rec_offset = rec_offset; /* now set mur_desc fields to point to valid record */ assert(rec_offset); if (SS_NORMAL != mur_prev(jctl, rec_offset)) { /* Since valid record may have been overwritten we must do this. * We call mur_prev, not mur_next, although both position mur_desc to the same * record (when called with non zero arg) to make sure no assumptions in * successive calls to mur_prev(jctl, 0) are violated. */ GTMASSERT; } return SS_NORMAL; } /* * Function Name: mur_valrec_next() * Input:jnl_ctl_list *jctl * off_jnl_t lo_off * Return: SS_NORMAL on succees, error status if unsuccessful * mur_next() calls this function */ uint4 mur_valrec_next(jnl_ctl_list *jctl, off_jnl_t lo_off) { jnl_file_header *jfh; uint4 status; mur_read_desc_t *mur_desc; jfh = jctl->jfh; jctl->rec_offset = ROUND_UP2(lo_off, jfh->alignsize); if (jctl->rec_offset == lo_off) jctl->rec_offset += jfh->alignsize; if (jctl->rec_offset > jctl->lvrec_off) jctl->rec_offset = jctl->lvrec_off; for (; jctl->rec_offset <= jctl->lvrec_off; jctl->rec_offset = MIN(jctl->lvrec_off, jctl->rec_offset + jfh->alignsize)) { /* a linear search for the alignsize block beginning with valid record is sufficient since we believe the *next* valid * record is located nearby */ if (SS_NORMAL == (status = mur_prev(jctl, jctl->rec_offset))) break; if (ERR_JNLBADRECFMT != status) return status; /* I/O error or unexpected failure */ if (jctl->rec_offset >= jctl->lvrec_off) /* lvrec_off must have a valid record */ GTMASSERT; } assert(SS_NORMAL == status); /* now work backwards to find the earliest valid record in the immediately preceding alignsize chunk */ mur_desc = jctl->reg_ctl->mur_desc; for ( ; SS_NORMAL == mur_prev(jctl, 0); jctl->rec_offset -= mur_desc->jreclen) ; assert(JNL_FILE_FIRST_RECORD <= jctl->rec_offset); /* now set mur_desc fields to point to valid record */ assert(jctl->rec_offset > lo_off); return mur_next(jctl, jctl->rec_offset); /* A conservative and safe approach since valid record may have been overwritten. * We call mur_next, not mur_prev, although both position mur_desc to the same * record (when called with non zero arg) to make sure no assumptions in a * following call to mur_next(jctl,0) are violated. We leave the buffers in a * state suitable for following mur_next(jctl,0) call. */ } /* * Function name: mur_fopen * Input: jnl_ctl_list * * Return value : TRUE or False * This function opens the journal file , checks JNLLABEL in header, endianness etc. */ boolean_t mur_fopen(jnl_ctl_list *jctl) { jnl_file_header *jfh; char jrecbuf[PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN]; jnl_record *jrec; int cre_jnl_rec_size; GTMCRYPT_ONLY( int crypt_status; ) if (!mur_fopen_sp(jctl)) return FALSE; jctl->eof_addr = jctl->os_filesize; assert(NULL == jctl->jfh); jfh = jctl->jfh = (jnl_file_header *)malloc(REAL_JNL_HDR_LEN); if (REAL_JNL_HDR_LEN < jctl->os_filesize) { DO_FILE_READ(jctl->channel, 0, jfh, REAL_JNL_HDR_LEN, jctl->status, jctl->status2); if (SS_NORMAL != jctl->status) jctl->status = ERR_JNLINVALID; } else jctl->status = ERR_JNLINVALID; if (SS_NORMAL != jctl->status) /* read failed or size of jnl file less than minimum expected */ { /* error while reading journal file header implies we cannot rely on fields inside the file header * so reset db file name length to 0 before printing JNLINVALID message. */ jfh->data_file_name_length = 0; if (ERR_JNLINVALID == jctl->status) gtm_putmsg(VARLSTCNT(10) ERR_JNLINVALID, 4, jctl->jnl_fn_len, jctl->jnl_fn, jfh->data_file_name_length, jfh->data_file_name, ERR_TEXT, 2, LEN_AND_LIT("Journal file does not have complete file header")); else if (SS_NORMAL != jctl->status2) gtm_putmsg(VARLSTCNT1(7) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, 0, jctl->status, PUT_SYS_ERRNO(jctl->status2)); else gtm_putmsg(VARLSTCNT(6) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, 0, jctl->status); return FALSE; } if (SS_NORMAL == jctl->status) { CHECK_JNL_FILE_IS_USABLE(jfh, jctl->status, TRUE, jctl->jnl_fn_len, jctl->jnl_fn); if (SS_NORMAL != jctl->status) return FALSE; /* gtm_putmsg would have already been done by CHECK_JNL_FILE_IS_USABLE macro */ } /* Now that we know for sure, jfh is of the format we expect it to be, we can safely access fields inside it */ cre_jnl_rec_size = JNL_HAS_EPOCH(jfh) ? PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN : PINI_RECLEN + PFIN_RECLEN + EOF_RECLEN; if ((cre_jnl_rec_size + JNL_HDR_LEN) <= jctl->os_filesize) { DO_FILE_READ(jctl->channel, JNL_HDR_LEN, jrecbuf, cre_jnl_rec_size, jctl->status, jctl->status2); if (SS_NORMAL != jctl->status) { gtm_putmsg(VARLSTCNT(6) ERR_JNLREAD, 3, jctl->jnl_fn_len, jctl->jnl_fn, JNL_HDR_LEN, jctl->status); return FALSE; } } else { gtm_putmsg(VARLSTCNT(10) ERR_JNLINVALID, 4, jctl->jnl_fn_len, jctl->jnl_fn, jfh->data_file_name_length, jfh->data_file_name, ERR_TEXT, 2, LEN_AND_LIT("File size is less than minimum expected for a valid journal file")); return FALSE; } if (!mur_options.forward && !jfh->before_images) { VMS_ONLY(assert(!mur_options.rollback_losttnonly);) if (mur_options.rollback_losttnonly) { /* Already prepared for a LOSTTNONLY rollback. Allow NOBEFORE_IMAGE journal file but issue a warning. */ gtm_putmsg(VARLSTCNT(4) ERR_RLBKJNLNOBIMG, 2, jctl->jnl_fn_len, jctl->jnl_fn); } else { gtm_putmsg(VARLSTCNT(4) ERR_JNLNOBIJBACK, 2, jctl->jnl_fn_len, jctl->jnl_fn); return FALSE; } } if (!REPL_ALLOWED(jfh) && mur_options.rollback) { gtm_putmsg(VARLSTCNT(4) ERR_REPLNOTON, 2, jctl->jnl_fn_len, jctl->jnl_fn); return FALSE; } jrec = (jnl_record *)jrecbuf; if (!IS_VALID_JNLREC(jrec, jfh) || JRT_PINI != jrec->prefix.jrec_type) { gtm_putmsg(VARLSTCNT(9) ERR_JNLBADRECFMT, 3, jctl->jnl_fn_len, jctl->jnl_fn, JNL_HDR_LEN, ERR_TEXT, 2, LEN_AND_LIT("Invalid or no PINI record found")); return FALSE; } /* We have at least one good record */ if (JNL_HAS_EPOCH(jfh)) { jrec = (jnl_record *)((char *)jrec + PINI_RECLEN); if (!IS_VALID_JNLREC(jrec, jfh) || JRT_EPOCH != jrec->prefix.jrec_type) { gtm_putmsg(VARLSTCNT(9) ERR_JNLBADRECFMT, 3, jctl->jnl_fn_len, jctl->jnl_fn, JNL_HDR_LEN + PINI_RECLEN, ERR_TEXT, 2, LEN_AND_LIT("Invalid or no EPOCH record found")); return FALSE; } /* We have at least one valid EPOCH */ } if (mur_options.update && jfh->bov_tn > jfh->eov_tn) { gtm_putmsg(VARLSTCNT(6) ERR_BOVTNGTEOVTN, 4, jctl->jnl_fn_len, jctl->jnl_fn, &jfh->bov_tn, &jfh->eov_tn); return FALSE; } if (jfh->bov_timestamp > jfh->eov_timestamp) { /* This is not a severe error to exit, may be user changed system time which we do not allow now. * But we can still try to continue recovery. We already removed time continuty check from mur_fread_eof(). * So if error limit allows, we will continue recovery */ if (!mur_report_error(jctl, MUR_BOVTMGTEOVTM)) return FALSE; } if (mur_options.rollback && (jfh->start_seqno > jfh->end_seqno)) { gtm_putmsg(VARLSTCNT(6) ERR_BEGSEQGTENDSEQ, 4, jctl->jnl_fn_len, jctl->jnl_fn, &jfh->start_seqno, &jfh->end_seqno); return FALSE; } init_hashtab_int4(&jctl->pini_list, MUR_PINI_LIST_INIT_ELEMS, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); /* Please investigate if murgbl.max_extr_record_length is more than what a VMS record (in a line) can handle ??? */ if (murgbl.max_extr_record_length < ZWR_EXP_RATIO(jctl->jfh->max_jrec_len)) murgbl.max_extr_record_length = ZWR_EXP_RATIO(jctl->jfh->max_jrec_len); # ifdef GTM_CRYPT jctl->is_same_hash_as_db = TRUE; if (FALSE == process_exiting && jfh->is_encrypted) { /* Encryption initialization will not happen in db_init for all cases. * Eg: MUPIP JOURNAL -BACKWARD -SHOW -NOVERIFY */ INIT_PROC_ENCRYPTION(crypt_status); /* If the encryption init failed in db_init, the below MACRO should return an error. * But, since this will be called for those operations that might not actually require encryption/decryption, * we don't report the error immediately. Instead wait for the first encryption/decryption task and report * accordingly. */ GTMCRYPT_GETKEY(jfh->encryption_hash, jctl->encr_key_handle, crypt_status); if (NULL != mur_ctl->csd && (0 != memcmp(mur_ctl->csd->encryption_hash, jfh->encryption_hash, GTMCRYPT_HASH_LEN))) jctl->is_same_hash_as_db = FALSE; } # endif return TRUE; } boolean_t mur_fclose(jnl_ctl_list *jctl) { error_def(ERR_JNLFILECLOSERR); if (NOJNL == jctl->channel) /* possible if mur_fopen() errored out */ return TRUE; if (NULL != jctl->jfh) { free(jctl->jfh); jctl->jfh = NULL; } free_hashtab_int4(&jctl->pini_list); JNL_FD_CLOSE(jctl->channel, jctl->status); /* sets jctl->channel to NOJNL */ if (SS_NORMAL == jctl->status) return TRUE; UNIX_ONLY(jctl->status = errno;) assert(FALSE); gtm_putmsg(VARLSTCNT(5) ERR_JNLFILECLOSERR, 2, jctl->jnl_fn_len, jctl->jnl_fn, jctl->status); return FALSE; }