/* * PROGRAM: JRD Write Ahead Log File utilitles * MODULE: walf.c * DESCRIPTION: Write Ahead Log File utilities * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. */ #include "firebird.h" #include #include "../jrd/gds.h" #include "../wal/wal.h" #include "../jrd/dsc.h" #include "../jrd/llio.h" #include "../wal/walf_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/iberr_proto.h" #include "../jrd/llio_proto.h" #include "../jrd/misc_proto.h" SSHORT WALF_delink_log(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, SLONG log_partition_offset) { /************************************** * * W A L F _ d e l i n k _ l o g * ************************************** * * Functional description * Delink the given log from the chain of log files by * making the previous log name for the next log empty, * i.e., after this call, the header of the next log has a * 'null' previous pointer. * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of an error, status_vector would be updated. * **************************************/ SLONG log_fd, next_log_fd; SSHORT ret; WALFH log_header, next_log_header; SCHAR next_logname[MAXPATHLEN]; SLONG next_log_partition_offset; /* First open the current log to get its header */ log_header = (WALFH) gds__alloc(WALFH_LENGTH); /* NOMEM: error return, FREE: by error returns & WALF_dispose_log_header() */ if (!log_header) return FB_FAILURE; ret = WALF_open_log_file(status_vector, dbname, logname, log_partition_offset, log_header, &log_fd); if (ret != FB_SUCCESS) { gds__free((SLONG *) log_header); return FB_FAILURE; } if ((log_header->walfh_next_logname != NULL) && (strlen(log_header->walfh_next_logname) > 0)) { /* Get the info about the next log file */ strcpy(next_logname, log_header->walfh_next_logname); next_log_partition_offset = log_header->walfh_next_log_partition_offset; WALF_dispose_log_header(log_header); LLIO_close(0, log_fd); /* Open and update the next log file's previous log pointer */ next_log_header = (WALFH) gds__alloc(WALFH_LENGTH); /* NOMEM: error return, FREE: by error returns & WALF_dispose_log_header() */ if (!next_log_header) return FB_FAILURE; ret = WALF_open_log_file(status_vector, dbname, next_logname, next_log_partition_offset, next_log_header, &next_log_fd); if (ret != FB_SUCCESS) { gds__free((SLONG *) next_log_header); return FB_FAILURE; } *next_log_header->walfh_prev_logname = 0; WALF_update_log_header(status_vector, next_logname, next_log_partition_offset, next_log_header, next_log_fd); WALF_dispose_log_header(next_log_header); LLIO_close(0, next_log_fd); } else { WALF_dispose_log_header(log_header); LLIO_close(0, log_fd); } return FB_SUCCESS; } SSHORT WALF_delink_prev_log(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, SLONG log_partition_offset) { /************************************** * * W A L F _ d e l i n k _ p r e v _ l o g * ************************************** * * Functional description * Make the previous log name for the given logname empty, * i.e., after this call, the header of the logname has a * 'null' previous pointer. * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of an error, status_vector would be updated. * **************************************/ SLONG log_fd; SSHORT ret; WALFH log_header; /* First open the log to get its header */ log_header = (WALFH) gds__alloc(WALFH_LENGTH); /* NOMEM: error return, FREE: by error returns & WALF_dispose_log_header() */ if (!log_header) return FB_FAILURE; ret = WALF_open_log_file(status_vector, dbname, logname, log_partition_offset, log_header, &log_fd); if (ret != FB_SUCCESS) { gds__free((SLONG *) log_header); return FB_FAILURE; } if ((log_header->walfh_prev_logname != NULL) && (strlen(log_header->walfh_prev_logname) > 0)) { gds__free((SLONG *) log_header->walfh_prev_logname); log_header->walfh_prev_logname = NULL; WALF_update_log_header(status_vector, logname, log_partition_offset, log_header, log_fd); } WALF_dispose_log_header(log_header); LLIO_close(0, log_fd); return ret; } void WALF_dispose_log_header( WALFH log_header) { /************************************** * * W A L F _ d i s p o s e _ l o g _ h e a d e r * ************************************** * * Functional description * Free up memory taken up by the pointers in the * passed log_header. Free up the log_header memory also. * **************************************/ if (log_header->walfh_dbname) gds__free((SLONG *) log_header->walfh_dbname); if (log_header->walfh_prev_logname) gds__free((SLONG *) log_header->walfh_prev_logname); if (log_header->walfh_next_logname) gds__free((SLONG *) log_header->walfh_next_logname); gds__free((SLONG *) log_header); } SSHORT WALF_get_linked_logs_info(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * starting_logname, SLONG starting_log_partition_offset, int *prev_logs_count, SCHAR * last_logname, SLONG * last_log_partition_offset, SLONG * last_log_flags, SSHORT * any_log_to_be_archived) { /************************************** * * W A L F _ g e t _ l i n k e d _ l o g s _ i n f o * ************************************** * * Functional description * Get the number of all the previous logfiles * linked to the given starting_logname. Also pass the information * about the last linked log file, if any. * * If any log file in the chain (including the starting logname) * needs to be archived (i.e. its WALFH_KEEP_FOR_LONG_TERM_RECV * is set), the parameter any_log_to_be_archived would be set to * TRUE else it would be set to FALSE. * * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ SCHAR *curr_name; SLONG curr_log_partition_offset; SCHAR *prev_name; SLONG prev_log_partition_offset; SLONG log_flags; SCHAR log_name1[MAXPATHLEN]; SCHAR log_name2[MAXPATHLEN]; SCHAR *temp_name; SLONG log_seqno; SLONG log_length; int log_count; SSHORT ret; log_flags = 0; if ((ret = WALF_get_log_info(status_vector, dbname, starting_logname, starting_log_partition_offset, &log_seqno, &log_length, &log_flags)) != FB_SUCCESS) return FB_FAILURE; *any_log_to_be_archived = ((log_flags & WALFH_KEEP_FOR_LONG_TERM_RECV) ? TRUE : FALSE); strcpy(log_name1, starting_logname); curr_name = log_name1; curr_log_partition_offset = starting_log_partition_offset; prev_name = log_name2; log_count = 0; for (;;) { if (WALF_get_next_log_info (status_vector, dbname, curr_name, curr_log_partition_offset, prev_name, &prev_log_partition_offset, &log_seqno, &log_length, &log_flags, -1) != FB_SUCCESS) break; log_count++; temp_name = prev_name; prev_name = curr_name; curr_name = temp_name; curr_log_partition_offset = prev_log_partition_offset; if (!(*any_log_to_be_archived) && (log_flags & WALFH_KEEP_FOR_LONG_TERM_RECV)) *any_log_to_be_archived = TRUE; } /* Now initialize the passed parameters with the oldest log info. */ *prev_logs_count = log_count; strcpy(last_logname, curr_name); *last_log_partition_offset = curr_log_partition_offset; *last_log_flags = log_flags; return FB_SUCCESS; } SSHORT WALF_get_log_info(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, SLONG log_partition_offset, SLONG * log_seqno, SLONG * log_length, SLONG * log_flag) { /************************************** * * W A L F _ g e t _ l o g _ i n f o * ************************************** * * Functional description * Get the seqno, length and the header flag of the given log file. * * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ SLONG log_fd; SSHORT ret; WALFH log_header; log_header = (WALFH) gds__alloc(WALFH_LENGTH); /* NOMEM: error return, FREE: by error returns & WALF_dispose_log_header() */ if (!log_header) return FB_FAILURE; ret = WALF_open_log_file(status_vector, dbname, logname, log_partition_offset, log_header, &log_fd); if (ret != FB_SUCCESS) { gds__free((SLONG *) log_header); return FB_FAILURE; } *log_seqno = log_header->walfh_seqno; *log_length = log_header->walfh_length; *log_flag = log_header->walfh_flags; WALF_dispose_log_header(log_header); LLIO_close(0, log_fd); return ret; } SSHORT WALF_get_next_log_info(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, SLONG log_partition_offset, SCHAR * next_logname, SLONG * next_log_partition_offset, SLONG * next_log_seqno, SLONG * next_log_length, SLONG * next_log_flags, SSHORT direction) { /************************************** * * W A L F _ g e t _ n e x t _ l o g _ i n f o * ************************************** * * Functional description * Get the name, offset and flag of the next logfile. * If the direction is 1, the next logfile is chronologically * next log file. If the direction is -1, the next logfile is * chronologically previous log file. * * If the 'next' logfile name is empty, return -1. If there * is any error, return FB_FAILURE else return FB_SUCCESS. In case * of error, status_vector would be updated. * **************************************/ SLONG log_fd; int ret; WALFH log_header; /* First open the current log to get the 'next' logname and offset */ log_header = (WALFH) gds__alloc(WALFH_LENGTH); /* NOMEM: error return, FREE: by error returns & WALF_dispose_log_header() */ if (!log_header) return FB_FAILURE; ret = WALF_open_log_file(status_vector, dbname, logname, log_partition_offset, log_header, &log_fd); if (ret != FB_SUCCESS) { gds__free((SLONG *) log_header); return FB_FAILURE; } if ((direction == 1) && (log_header->walfh_next_logname != NULL) && (strlen(log_header->walfh_next_logname) > 0)) { strcpy(next_logname, log_header->walfh_next_logname); *next_log_partition_offset = log_header->walfh_next_log_partition_offset; } else if ((direction == -1) && (log_header->walfh_prev_logname != NULL) && (strlen(log_header->walfh_prev_logname) > 0)) { strcpy(next_logname, log_header->walfh_prev_logname); *next_log_partition_offset = log_header->walfh_prev_log_partition_offset; } else ret = -1; /* Now get the information about the next log file */ if (ret != -1) ret = WALF_get_log_info(status_vector, dbname, next_logname, *next_log_partition_offset, next_log_seqno, next_log_length, next_log_flags); WALF_dispose_log_header(log_header); LLIO_close(0, log_fd); return ret; } SSHORT WALF_get_all_next_logs_info(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * starting_logname, SLONG starting_log_partition_offset, int max_logs_count, SCHAR * lognames_buffer, int *next_logs_count, SCHAR ** next_logs_names, SLONG * next_log_partitions_offsets, SLONG * next_logs_seqnos, SLONG * next_logs_lengths, SLONG * next_logs_flags, SSHORT direction) { /************************************** * * W A L F _ g e t _ a l l _ n e x t _ l o g s _ i n f o * ************************************** * * Functional description * Get the names, offsets and flags of all the logfiles * linked to the given starting_logname. * * If the direction is 1, the next logfiles are chronologically * next log files. If the direction is -1, the next logfiles are * chronologically previous log files. * * Pass the information back through the parameters which are * arrays, each element of which would hold info about one * 'next' log file. The parameter next_logs_count returns the * total number of linked log files. The information about log * files is returned in the 'oldest log file first' order. * * If the total number of next log files exceeds * max_logs_count then the search stops there. In that case, * the user may call this routine again with the last * logname and with new or extended buffers. * * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ STK stack; SCHAR *curr_name; SLONG curr_log_partition_offset; SCHAR *next_name; SLONG next_log_partition_offset; SLONG next_log_seqno; SLONG next_log_length; SLONG next_log_flags; int log_count; struct log_info_stru { SCHAR *log_name; SLONG log_partition_offset; SLONG log_seqno; SLONG log_length; SLONG log_flags; } *log_info; curr_name = starting_logname; curr_log_partition_offset = starting_log_partition_offset; /* Now collect info about all the 'next' log names. */ stack = NULL; log_count = 0; next_name = lognames_buffer; for (;;) { /* Set the buffer address to get the next file name */ if (WALF_get_next_log_info(status_vector, dbname, curr_name, curr_log_partition_offset, next_name, &next_log_partition_offset, &next_log_seqno, &next_log_length, &next_log_flags, direction) != FB_SUCCESS) break; if (direction == 1) { /* We are getting the names in the correct chronological order */ *next_logs_names++ = next_name; *next_log_partitions_offsets++ = next_log_partition_offset; *next_logs_seqnos++ = next_log_seqno; *next_logs_lengths++ = next_log_length; *next_logs_flags++ = next_log_flags; } else { /* We are getting the log names in the reverse chronological order, so put them on stack for later retrieval in the correct order */ log_info = (struct log_info_stru *) gds__alloc(sizeof(struct log_info_stru)); /* NOMEM: free stack & error return, FREE: by popping the stack below */ if (!log_info) { while (log_info = reinterpret_cast < log_info_stru * >(MISC_pop(&stack))) gds__free(log_info); return FB_FAILURE; } log_info->log_name = next_name; log_info->log_partition_offset = next_log_partition_offset; log_info->log_seqno = next_log_seqno; log_info->log_length = next_log_length; log_info->log_flags = next_log_flags; /* MISC_push() returns NULL on NOMEM:, FREE: by MISC_pop */ if (!MISC_push((UCHAR *) log_info, &stack)) { gds__free(log_info); while (log_info = reinterpret_cast < log_info_stru * >(MISC_pop(&stack))) gds__free(log_info); return FB_FAILURE; } } log_count++; if (log_count == max_logs_count) break; curr_name = next_name; curr_log_partition_offset = next_log_partition_offset; next_name = curr_name + strlen(curr_name) + 1; /* slide in the passed buffer */ } *next_logs_count = log_count; if (direction == 1) return FB_SUCCESS; /* We are all set */ while (stack) { /* By creating the correct ordering of information from the stack, initialize the passed parameters starting from the oldest log. */ log_info = (struct log_info_stru *) MISC_pop(&stack); *next_logs_names++ = log_info->log_name; *next_log_partitions_offsets++ = log_info->log_partition_offset; *next_logs_seqnos++ = log_info->log_seqno; *next_logs_lengths++ = log_info->log_length; *next_logs_flags++ = log_info->log_flags; gds__free((SLONG *) log_info); } return FB_SUCCESS; } SSHORT WALF_init_p_log(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, SLONG logsize, SSHORT num_partitions) { /************************************** * * W A L F _ i n i t _ p _ l o g * ************************************** * * Functional description * Create and initialize a logname file with given number of * equal log partitions. The space (length logsize bytes) for * the file is also allocated. * * If num_partitions = 0, just allocate logsize bytes for the * given logfile and return. * * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ SLONG p_log_fd; int i; P_LOGFH p_log_header; /* Since we allow partitions for the RAW devices only, it is okay to overwrite the file in that case. */ if (LLIO_allocate_file_space(status_vector, logname, logsize, (UCHAR) WAL_TERMINATOR_CHAR, (num_partitions) ? TRUE : FALSE) != FB_SUCCESS) return FB_FAILURE; if (num_partitions <= 0) return FB_SUCCESS; if (LLIO_open(status_vector, logname, LLIO_OPEN_RW, FALSE, &p_log_fd)) return FB_FAILURE; p_log_header = (P_LOGFH) gds__alloc(P_LOGFH_LENGTH); /* NOMEM: error return, FREE: end of routine */ if (!p_log_header) return FB_FAILURE; p_log_header->p_logfh_version1 = P_LOGFH_VERSION; p_log_header->p_logfh_version2 = 0; p_log_header->p_logfh_length = logsize; p_log_header->p_logfh_maxp = num_partitions; p_log_header->p_logfh_firstp_offset = P_LOGFH_LENGTH; p_log_header->p_logfh_sizep = PARTITION_SIZE(logsize, num_partitions); p_log_header->p_logfh_curp = -1; p_log_header->p_logfh_flags = 0L; strcpy(p_log_header->p_logfh_dbname, dbname); for (i = 0; i < 10; i++) p_log_header->p_logfh_reserved[i] = 0L; if (LLIO_write(status_vector, p_log_fd, logname, 0L, LLIO_SEEK_NONE, (UCHAR *) p_log_header, P_LOGFH_LENGTH, 0)) { LLIO_close(0, p_log_fd); gds__free((SLONG *) p_log_header); return FB_FAILURE; } LLIO_close(0, p_log_fd); gds__free((SLONG *) p_log_header); return FB_SUCCESS; } SSHORT WALF_open_partitioned_log_file(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, P_LOGFH p_log_header, SLONG * p_log_fd) { /************************************** * * W A L F _ o p e n _ p a r t i t i o n e d _ l o g _ f i l e * ************************************** * * Functional description * Open and read the header of the given partioiotned log file. * Return the file handle through the parameter p_log_fd. * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ SLONG fd, read_len; #define ERROR_RETURN(error) {if (fd>0) LLIO_close(0,fd); \ WAL_ERROR (status_vector, error, logname);\ return FB_FAILURE;} #define IO_ERROR_RETURN {if (fd>0) LLIO_close(0,fd); return FB_FAILURE;} /* Open and position at the beginning of the partitioned log file. */ if (LLIO_open(status_vector, logname, LLIO_OPEN_EXISTING_RW, FALSE, &fd)) return FB_FAILURE; *p_log_fd = fd; /* Now try to read the partitioned logfile header and take appropriate steps afterwards. */ if (LLIO_read(status_vector, fd, logname, 0L, LLIO_SEEK_NONE, reinterpret_cast < UCHAR * >(p_log_header), P_LOGFH_LENGTH, &read_len)) IO_ERROR_RETURN; if (read_len < (SLONG) sizeof(struct p_logfh)) ERROR_RETURN(gds_logh_small); if (p_log_header->p_logfh_version1 != P_LOGFH_VERSION) ERROR_RETURN(gds_logh_inv_version); /* Make sure that the database name matches with the passed database name. */ if (strcmp(dbname, p_log_header->p_logfh_dbname) != 0) ERROR_RETURN(gds_logh_diff_dbname); return FB_SUCCESS; } SSHORT WALF_open_log_file(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, SLONG log_partition_offset, WALFH log_header, SLONG * log_fd) { /************************************** * * W A L F _ o p e n _ l o g _ f i l e * ************************************** * * Functional description * Open and read the header of the given log file. Return the * file handle through the parameter log_fd. * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ SLONG fd, read_len; #define ERROR_RETURN(error) {if (fd>0) LLIO_close(0,fd); \ WAL_ERROR (status_vector, error, logname);\ return FB_FAILURE;} #define IO_ERROR_RETURN {if (fd>0) LLIO_close(0,fd); return FB_FAILURE;} if (LLIO_open(0, logname, LLIO_OPEN_EXISTING_RW, FALSE, &fd)) return FB_FAILURE; *log_fd = fd; /* Position at the beginning of the logical log file and read the header. */ if (LLIO_read(status_vector, fd, logname, log_partition_offset, LLIO_SEEK_BEGIN, (UCHAR *) log_header, WALFH_LENGTH, &read_len)) IO_ERROR_RETURN; if (read_len < (SLONG) sizeof(struct walfh)) ERROR_RETURN(gds_logh_small); if (log_header->walfh_version != WALFH_VERSION) ERROR_RETURN(gds_logh_inv_version); /* Extract the database name from the log_header and make sure that it matches with the passed database name. */ log_header->walfh_dbname = NULL; log_header->walfh_prev_logname = NULL; log_header->walfh_next_logname = NULL; WALF_upd_log_hdr_frm_walfh_data(log_header, log_header->walfh_data); if (strcmp(dbname, log_header->walfh_dbname) != 0) { if (log_header->walfh_dbname) gds__free((SLONG *) log_header->walfh_dbname); if (log_header->walfh_prev_logname) gds__free((SLONG *) log_header->walfh_prev_logname); if (log_header->walfh_next_logname) gds__free((SLONG *) log_header->walfh_next_logname); ERROR_RETURN(gds_logh_diff_dbname); } return FB_SUCCESS; } SSHORT WALF_set_log_header_flag(ISC_STATUS * status_vector, SCHAR * dbname, SCHAR * logname, SLONG log_partition_offset, SLONG flag, int set) { /************************************** * * W A L F _ s e t _ l o g _ h e a d e r _ f l a g * ************************************** * * Functional description * Update the walfh_flags field of the log header in the log * file by the passed flag setting(s). If the parameter 'set' * is TRUE, the flag bit(s) would be set else they would be reset. * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ SLONG log_fd; SSHORT ret; WALFH log_header; /* First open the log and get its header */ log_header = (WALFH) gds__alloc(WALFH_LENGTH); /* NOMEM: error return, FREE: error return or WALF_dispose_log_header() */ if (!log_header) return FB_FAILURE; ret = WALF_open_log_file(status_vector, dbname, logname, log_partition_offset, log_header, &log_fd); if (ret != FB_SUCCESS) { gds__free((SLONG *) log_header); return FB_FAILURE; } /* Now set or reset the bits */ if (set) log_header->walfh_flags |= flag; else log_header->walfh_flags &= ~(flag); ret = WALF_update_log_header(status_vector, logname, log_partition_offset, log_header, log_fd); WALF_dispose_log_header(log_header); LLIO_close(0, log_fd); return ret; } SSHORT WALF_update_log_header(ISC_STATUS * status_vector, SCHAR * logname, SLONG log_partition_offset, WALFH log_header, SLONG log_fd) { /************************************** * * W A L F _ u p d a t e _ l o g _ h e a d e r * ************************************** * * Functional description * Rewrite the log header in the log file. * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ log_header->walfh_log_partition_offset = log_partition_offset; log_header->walfh_data_len = MISC_build_parameters_block(log_header->walfh_data, PARAM_BYTE(WALFH_dbname), PARAM_STRING(log_header->walfh_dbname), PARAM_BYTE(WALFH_prev_logname), PARAM_STRING(log_header-> walfh_prev_logname), PARAM_BYTE(WALFH_next_logname), PARAM_STRING(log_header-> walfh_next_logname), PARAM_BYTE(WALFH_end), 0); /* Position at the beginning of the logical log file. */ if (LLIO_write(status_vector, log_fd, logname, log_header->walfh_log_partition_offset, LLIO_SEEK_BEGIN, (UCHAR *) log_header, WALFH_LENGTH, 0)) return FB_FAILURE; return FB_SUCCESS; } SSHORT WALF_update_partitioned_log_hdr(ISC_STATUS * status_vector, SCHAR * logname, P_LOGFH p_log_header, SLONG p_log_fd) { /************************************** * * W A L F _ u p d a t e _ p a r t i t i o n e d _ l o g _ h d r * ************************************** * * Functional description * Rewrite the header of the log file containing partitions. * If there is any error, return FB_FAILURE else return FB_SUCCESS. * In case of error, status_vector would be updated. * **************************************/ /* Position at the beginning of the partitioned log file. */ if (LLIO_write(status_vector, p_log_fd, logname, 0L, LLIO_SEEK_BEGIN, (UCHAR *) p_log_header, (ULONG) P_LOGFH_LENGTH, 0)) return FB_FAILURE; return FB_SUCCESS; } void WALF_upd_log_hdr_frm_walfh_data( WALFH log_header, UCHAR * walfh_data) { /************************************** * * W A L F _ u p d _ l o g _ h d r _ f r m _ w a l f h _ d a t a * ************************************** * * Functional description * Decode walfh_data and update the fields of log_header structure. * **************************************/ UCHAR *p, *q; SCHAR filename[MAXPATHLEN]; p = walfh_data; for (;;) { switch (*p++) { case WALFH_dbname: q = (UCHAR *) filename; while (*q++ = *p++); if (log_header->walfh_dbname != NULL) gds__free((SLONG *) log_header->walfh_dbname); STRING_DUP(log_header->walfh_dbname, filename); /* NOMEM: walfh_dbname is set to NULL, FREE: unknown */ break; case WALFH_prev_logname: q = (UCHAR *) filename; while (*q++ = *p++); if (log_header->walfh_prev_logname != NULL) gds__free((SLONG *) log_header->walfh_prev_logname); STRING_DUP(log_header->walfh_prev_logname, filename); /* NOMEM: walfh_prev_logname is set to NULL, FREE: unknown */ break; case WALFH_next_logname: q = (UCHAR *) filename; while (*q++ = *p++); if (log_header->walfh_next_logname != NULL) gds__free((SLONG *) log_header->walfh_next_logname); STRING_DUP(log_header->walfh_next_logname, filename); /* NOMEM: walfh_next_logname is set to NULL, FREE: unknown */ break; case WALFH_end: return; } } }