/* Given a file (journal or database), the function extracts the buffer of the given length at the given offset and displays it * on the STDIN. Note that, the offset and length should match the values at the encryption time. In case of journal files, * this offset could be obtained for every record using a detailed journal extract. */ int mu_decrypt(char *fname, uint4 off, uint4 len) { # ifdef GTM_CRYPT int fd, n_len, save_errno, gtmcrypt_errno, i; char hash[GTMCRYPT_HASH_LEN], *buff; boolean_t is_encrypted; gtmcrypt_key_t key_handle; assert(fname); assert(STRLEN(fname)); n_len = STRLEN(fname); GET_FD_HASH(fname, n_len, fd, hash, is_encrypted); buff = (char *)malloc(len); LSEEKREAD(fd, off, buff, len, save_errno); if (0 != save_errno) { close(fd); GC_DISPLAY_FILE_ERROR_AND_RETURN("Error reading from file !AD", fname, n_len, save_errno); } if (is_encrypted) { INIT_PROC_ENCRYPTION(NULL, gtmcrypt_errno); GTMCRYPT_GETKEY(NULL, hash, key_handle, gtmcrypt_errno); if (0 == gtmcrypt_errno) GTMCRYPT_DECRYPT(NULL, key_handle, buff, len, NULL, gtmcrypt_errno); if (0 != gtmcrypt_errno) { close(fd); free(buff); GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, n_len, fname); } } for (i = 0; i < len; i++) { if (WITHIN_PRINTABLE_RANGE(buff[i])) PRINTF("%c", buff[i]); else PRINTF("%c", '.'); } free(buff); close(fd); # endif return SS_NORMAL; }
void mupip_restore(void) { static readonly char label[] = GDS_LABEL; char db_name[MAX_FN_LEN + 1], *inbuf, *p; inc_list_struct *ptr; inc_header *inhead; sgmnt_data *old_data; short iosb[4]; unsigned short n_len; int4 status, vbn, rsize, temp, save_errno; uint4 rest_blks, totblks; trans_num curr_tn; uint4 ii; block_id blk_num; bool extend; uint4 cli_status; BFILE *in; int i, db_fd; uint4 old_blk_size, old_tot_blks, bplmap; short old_start_vbn; off_t new_eof; char buff[DISK_BLOCK_SIZE]; char msg_buffer[1024], *newmap, *newmap_bptr; mstr msg_string; char addr[SA_MAXLEN+1]; unsigned char tcp[5]; backup_type type; unsigned short port; int4 timeout, cut, match; char debug_info[256]; void (*common_read)(); char *errptr; pid_t waitpid_res; error_def(ERR_MUPRESTERR); error_def(ERR_MUPCLIERR); error_def(ERR_IOEOF); extend = TRUE; if (CLI_NEGATED == (cli_status = cli_present("EXTEND"))) extend = FALSE; mu_outofband_setup(); mu_gv_cur_reg_init(); n_len = sizeof(db_name); if (cli_get_str("DATABASE", db_name, &n_len) == FALSE) mupip_exit(ERR_MUPCLIERR); strcpy((char *)gv_cur_region->dyn.addr->fname, db_name); gv_cur_region->dyn.addr->fname_len = n_len; if (!mu_rndwn_file(gv_cur_region, TRUE)) { util_out_print("Error securing stand alone access to output file !AD. Aborting restore.", TRUE, n_len, db_name); mupip_exit(ERR_MUPRESTERR); } OPENFILE(db_name, O_RDWR, db_fd); if (-1 == db_fd) { save_errno = errno; util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); mupip_exit(save_errno); } murgetlst(); inbuf = (char*)malloc(INC_BACKUP_CHUNK_SIZE); old_data = (sgmnt_data*)malloc(sizeof(sgmnt_data)); LSEEKREAD(db_fd, 0, old_data, sizeof(sgmnt_data), save_errno); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("read : !AZ", TRUE, errptr); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(save_errno); } else { db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_IOEOF); } } if (memcmp(&old_data->label[0], &label[0], GDS_LABEL_SZ)) { util_out_print("Output file !AD has an unrecognizable format", TRUE, n_len, db_name); free(old_data); free(inbuf); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } curr_tn = old_data->trans_hist.curr_tn; old_blk_size = old_data->blk_size; old_tot_blks = old_data->trans_hist.total_blks; old_start_vbn = old_data->start_vbn; bplmap = old_data->bplmap; free(old_data); msg_string.addr = msg_buffer; msg_string.len = sizeof(msg_buffer); inhead = (inc_header *)malloc(sizeof(inc_header) + 8); inhead = (inc_header *)((((int4)inhead) + 7) & -8); rest_blks = 0; for (ptr = in_files.next; ptr; ptr = ptr->next) { /* --- determine source type --- */ type = backup_to_file; if (0 == ptr->input_file.len) continue; else if ('|' == *(ptr->input_file.addr + ptr->input_file.len - 1)) { type = backup_to_exec; ptr->input_file.len--; *(ptr->input_file.addr + ptr->input_file.len) = '\0'; } else if (ptr->input_file.len > 5) { lower_to_upper(tcp, (uchar_ptr_t)ptr->input_file.addr, 5); if (0 == memcmp(tcp, "TCP:/", 5)) { type = backup_to_tcp; cut = 5; while ('/' == *(ptr->input_file.addr + cut)) cut++; ptr->input_file.len -= cut; p = ptr->input_file.addr; while (p < ptr->input_file.addr + ptr->input_file.len) { *p = *(p + cut); p++; } *p = '\0'; } } /* --- open the input stream --- */ restore_read_errno = 0; switch(type) { case backup_to_file: common_read = iob_read; if ((in = iob_open_rd(ptr->input_file.addr, DISK_BLOCK_SIZE, BLOCKING_FACTOR)) == NULL) { save_errno = errno; util_out_print("Error accessing input file !AD. Aborting restore.", TRUE, ptr->input_file.len, ptr->input_file.addr); errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(save_errno); } ESTABLISH(iob_io_error); break; case backup_to_exec: pipe_child = 0; common_read = exec_read; in = (BFILE *)malloc(sizeof(BFILE)); if (0 > (in->fd = gtm_pipe(ptr->input_file.addr, input_from_comm))) { util_out_print("Error creating input pipe from !AD.", TRUE, ptr->input_file.len, ptr->input_file.addr); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } #ifdef DEBUG_ONLINE PRINTF("file descriptor for the openned pipe is %d.\n", in->fd); PRINTF("the command passed to gtm_pipe is %s.\n", ptr->input_file.addr); #endif break; case backup_to_tcp: common_read = tcp_read; /* parse the input */ switch (match = SSCANF(ptr->input_file.addr, "%[^:]:%hu", addr, &port)) { case 1 : port = DEFAULT_BKRS_PORT; case 2 : break; default : util_out_print("Error : A hostname has to be specified.", TRUE); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } if ((0 == cli_get_int("NETTIMEOUT", &timeout)) || (0 > timeout)) timeout = DEFAULT_BKRS_TIMEOUT; in = (BFILE *)malloc(sizeof(BFILE)); iotcp_fillroutine(); if (0 > (in->fd = tcp_open(addr, port, timeout, TRUE))) { util_out_print("Error establishing TCP connection to !AD.", TRUE, ptr->input_file.len, ptr->input_file.addr); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } break; default: util_out_print("Aborting restore!/", TRUE); util_out_print("Unrecognized input format !AD", TRUE, ptr->input_file.len, ptr->input_file.addr); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } COMMON_READ(in, inhead, sizeof(inc_header)); if (memcmp(&inhead->label[0], INC_HEADER_LABEL, INC_HDR_LABEL_SZ)) { util_out_print("Input file !AD has an unrecognizable format", TRUE, ptr->input_file.len, ptr->input_file.addr); free(inbuf); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } if (curr_tn != inhead->start_tn) { util_out_print("Transaction in input file !AD does not align with database TN.!/DB: !XL!_Input file: !XL", TRUE, ptr->input_file.len, ptr->input_file.addr, curr_tn, inhead->start_tn); free(inbuf); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } if (old_blk_size != inhead->blk_size) { util_out_print("Incompatable block size. Output file !AD has block size !XL,", TRUE, n_len, db_name); util_out_print("while input file !AD is from a database with block size !XL,", TRUE, ptr->input_file.len, ptr->input_file.addr, inhead->blk_size); free(inbuf); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } if (old_tot_blks != inhead->db_total_blks) { if (old_tot_blks > inhead->db_total_blks || !extend) { totblks = old_tot_blks - DIVIDE_ROUND_UP(old_tot_blks, DISK_BLOCK_SIZE); util_out_print("Incompatable database sizes. Output file !AD has!/ !UL (!XL hex) total blocks,", TRUE, n_len, db_name, totblks, totblks); totblks = inhead->db_total_blks - DIVIDE_ROUND_UP(inhead->db_total_blks, DISK_BLOCK_SIZE); util_out_print("while input file !AD is from a database with!/ !UL (!XL hex) total blocks", TRUE, ptr->input_file.len, ptr->input_file.addr, totblks, totblks); free(inbuf); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } else { /* this part of the code is similar to gdsfilext except that you don't need to do * most of the work that gdsfilext does. However, for situations where the database * extended since the last backup (the beginning of this incremental backup), and * there are new bitmaps that are never touched later on by GT.M, these bitmaps * will have tn == 0, which prevents the backup process to pick up these blocks, * so, we need to initialize these bitmaps here */ new_eof = ((off_t)(old_start_vbn - 1) * DISK_BLOCK_SIZE) + ((off_t)inhead->db_total_blks * old_blk_size); memset(buff, 0, DISK_BLOCK_SIZE); LSEEKWRITE(db_fd, new_eof, buff, DISK_BLOCK_SIZE, status); if (0 != status) { util_out_print("Aborting restore!/", TRUE); util_out_print("lseek or write error : Unable to extend output file !AD!/", TRUE, n_len, db_name); util_out_print(" from !UL (!XL hex) total blocks to !UL (!XL hex) total blocks.!/", TRUE, old_tot_blks, old_tot_blks, inhead->db_total_blks, inhead->db_total_blks); util_out_print(" Current input file is !AD with !UL (!XL hex) total blocks!/", TRUE, ptr->input_file.len, ptr->input_file.addr, inhead->db_total_blks, inhead->db_total_blks); gtm_putmsg(VARLSTCNT(1) status); free(inbuf); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } /* --- initialize all new bitmaps, just in case they are not touched later --- */ if (DIVIDE_ROUND_DOWN(inhead->db_total_blks, bplmap) > DIVIDE_ROUND_DOWN(old_tot_blks, bplmap)) { /* -- similar logic exist in bml_newmap.c, which need to pick up any new updates here -- */ newmap = (char *)malloc(old_blk_size); ((blk_hdr *)newmap)->bsiz = BM_SIZE(bplmap); ((blk_hdr *)newmap)->levl = LCL_MAP_LEVL; ((blk_hdr *)newmap)->tn = curr_tn; newmap_bptr = newmap + sizeof(blk_hdr); *newmap_bptr++ = THREE_BLKS_FREE; memset(newmap_bptr, FOUR_BLKS_FREE, BM_SIZE(bplmap) - sizeof(blk_hdr) - 1); for (ii = ROUND_UP(old_tot_blks, bplmap); ii <= inhead->db_total_blks; ii += bplmap) { new_eof = (off_t)(old_start_vbn - 1) * DISK_BLOCK_SIZE + (off_t)ii * old_blk_size; LSEEKWRITE(db_fd, new_eof, newmap, old_blk_size, status); if (0 != status) { util_out_print("Aborting restore!/", TRUE); util_out_print("Bitmap 0x!XL initialization error!", TRUE, ii); gtm_putmsg(VARLSTCNT(1) status); free(inbuf); free(newmap); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } } free(newmap); } old_tot_blks = inhead->db_total_blks; } } COMMON_READ(in, &rsize, sizeof(int4)); for ( ; ;) { /* rsize is the size of the record, including the size, but, since the size has already been read in, this will read in the current record and the size for the next record */ /* ensure we have a reasonable record size, at least */ if (rsize - sizeof(int4) - sizeof(block_id) > old_blk_size) { util_out_print("Invalid information in restore file !AD. Aborting restore.", TRUE, ptr->input_file.len, ptr->input_file.addr); iob_close(in); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(ERR_MUPRESTERR); } COMMON_READ(in, inbuf, rsize); if (!memcmp(inbuf, &end_msg[0], sizeof end_msg - 1)) break; rest_blks++; blk_num = *(block_id*)inbuf; vbn = old_start_vbn - 1 + (old_blk_size / DISK_BLOCK_SIZE * blk_num); LSEEKWRITE(db_fd, (off_t)vbn * DISK_BLOCK_SIZE, inbuf + sizeof(block_id), rsize - sizeof(block_id) - sizeof(int4), save_errno); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(save_errno); } GET_LONG(temp, (inbuf + rsize - sizeof(int4))); rsize = temp; } GET_LONG(temp, (inbuf + rsize - sizeof(int4))); rsize = temp; vbn = 0; for (i = 0; ; i++) /* Restore file header */ { COMMON_READ(in, inbuf, rsize); if (!memcmp(inbuf, &hdr_msg[0], sizeof hdr_msg - 1)) break; LSEEKWRITE(db_fd, vbn, inbuf, rsize - sizeof(int4), save_errno); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); db_ipcs_reset(gv_cur_region, TRUE); mu_gv_cur_reg_free(); mupip_exit(save_errno); } vbn += rsize - sizeof(int4); GET_LONG(temp, (inbuf + rsize - sizeof(int4))); rsize = temp; } curr_tn = inhead->end_tn; switch (type) { case backup_to_file: REVERT; iob_close(in); break; case backup_to_exec: close(in->fd); if ((pipe_child > 0) && (FALSE != is_proc_alive(pipe_child, 0))) WAITPID(pipe_child, (int *)&status, 0, waitpid_res); break; case backup_to_tcp: break; } } util_out_print("!/RESTORE COMPLETED", TRUE); util_out_print("!UL blocks restored", TRUE, rest_blks); free(inbuf); db_ipcs_reset(gv_cur_region, FALSE); mu_gv_cur_reg_free(); mupip_exit(SS_NORMAL); }
/* Takes an entry from 'ipcs -am' and checks for its validity to be a GT.M replication segment. * Returns TRUE if the shared memory segment is a valid GT.M replication segment * (based on a check on some fields in the shared memory) else FALSE. * If the segment belongs to GT.M, it returns the replication id of the segment * by the second argument. * Sets exit_stat to ERR_MUNOTALLSEC if appropriate. */ boolean_t validate_replpool_shm_entry(shm_parms *parm_buff, replpool_id_ptr_t replpool_id, int *exit_stat) { boolean_t remove_shmid, jnlpool_segment; int fd; repl_inst_hdr repl_instance; sm_uc_ptr_t start_addr; int save_errno, status, shmid; struct shmid_ds shmstat; char msgbuff[OUT_BUFF_SIZE], *instfilename; if (NULL == parm_buff) return FALSE; /* Check for the bare minimum size of the replic shared segment that we expect */ /* if (parm_buff->sgmnt_siz < (SIZEOF(replpool_identifier) + MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE))) */ if (parm_buff->sgmnt_siz < MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE)) return FALSE; if (IPC_PRIVATE != parm_buff->key) return FALSE; shmid = parm_buff->shmid; /* we do not need to lock the shm for reading the rundown information as * the other rundowns (if any) can also be allowed to share reading the * same info concurrently. */ if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND))) return FALSE; memcpy((void *)replpool_id, (void *)start_addr, SIZEOF(replpool_identifier)); instfilename = replpool_id->instfilename; /* Even though we could be looking at a replication pool structure that has been created by an older version * or newer version of GT.M, the format of the "replpool_identifier" structure is expected to be the same * across all versions so we can safely dereference the "label" and "instfilename" fields in order to generate * user-friendly error messages. Asserts for the layout are in "mu_rndwn_repl_instance" (not here) with a * comment there as to why that location was chosen. */ if (memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 1)) { if (!memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 3)) { util_out_print("Cannot rundown replpool shmid = !UL as it has format !AD " "created by !AD but this mupip is version and uses format !AD", TRUE, shmid, GDS_LABEL_SZ - 1, replpool_id->label, LEN_AND_STR(replpool_id->now_running), gtm_release_name_len, gtm_release_name, GDS_LABEL_SZ - 1, GDS_RPL_LABEL); *exit_stat = ERR_MUNOTALLSEC; } shmdt((void *)start_addr); return FALSE; } assert(JNLPOOL_SEGMENT == replpool_id->pool_type || RECVPOOL_SEGMENT == replpool_id->pool_type); if(JNLPOOL_SEGMENT != replpool_id->pool_type && RECVPOOL_SEGMENT != replpool_id->pool_type) { shmdt((void *)start_addr); return FALSE; } jnlpool_segment = (JNLPOOL_SEGMENT == replpool_id->pool_type); if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */ util_out_print("!AD -> Error with shmctl for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } /* Check if instance filename reported in shared memory still exists. If not, clean this * shared memory section without even invoking "mu_rndwn_repl_instance" as that expects * the instance file to exist. Same case if shared memory points back to an instance file * whose file header does not have this shmid. */ OPENFILE(instfilename, O_RDONLY, fd); /* check if we can open it */ msgbuff[0] = '\0'; remove_shmid = FALSE; if (FD_INVALID == fd) { if (ENOENT == errno) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", instfilename); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else { /* open() errored out e.g. due to file permissions. Log that */ save_errno = errno; util_out_print("Cannot rundown replpool shmid !UL for instance file" " !AD as open() on the file returned the following error", TRUE, shmid, LEN_AND_STR(instfilename)); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } } else { LSEEKREAD(fd, 0, &repl_instance, SIZEOF(repl_inst_hdr), status); if (0 != status) { save_errno = errno; util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } if ((jnlpool_segment && (repl_instance.jnlpool_shmid != shmid)) || (!jnlpool_segment && (repl_instance.recvpool_shmid != shmid))) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "%s SHMID (%d) in the instance file header does not match with the" " one reported by \"ipcs\" command (%d)", jnlpool_segment ? "Journal Pool" : "Receive Pool", jnlpool_segment ? repl_instance.jnlpool_shmid : repl_instance.recvpool_shmid, shmid); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ } shmdt((void *)start_addr); if (remove_shmid) { assert('\0' != msgbuff[0]); if (0 != shm_rmid(shmid)) { save_errno = errno; util_out_print("!AD -> Error removing shared memory for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; return FALSE; } PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, STRLEN(instfilename), instfilename, shmid); *exit_stat = ERR_SHMREMOVED; } else *exit_stat = SS_NORMAL; return TRUE; }
/* Takes an entry from 'ipcs -m' and checks for its validity to be a GT.M db segment. * Returns TRUE if the shared memory segment is a valid GT.M db segment * (based on a check on some fields in the shared memory) else FALSE. * If the segment belongs to GT.M it returns the database file name by the second argument. * Sets exit_stat to ERR_MUNOTALLSEC if appropriate. */ boolean_t validate_db_shm_entry(shm_parms *parm_buff, char *fname, int *exit_stat) { boolean_t remove_shmid; file_control *fc; int fname_len, save_errno, status, shmid; node_local_ptr_t nl_addr; sm_uc_ptr_t start_addr; struct stat st_buff; struct shmid_ds shmstat; sgmnt_data tsd; unix_db_info *udi; char msgbuff[OUT_BUFF_SIZE]; if (NULL == parm_buff) return FALSE; /* check for the bare minimum size of the shared memory segment that we expect * (with no fileheader related information at hand) */ if (MIN_NODE_LOCAL_SPACE + SHMPOOL_SECTION_SIZE > parm_buff->sgmnt_siz) return FALSE; if (IPC_PRIVATE != parm_buff->key) return FALSE; shmid = parm_buff->shmid; /* we do not need to lock the shm for reading the rundown information as * the other rundowns (if any) can also be allowed to share reading the * same info concurrently. */ if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND))) return FALSE; nl_addr = (node_local_ptr_t)start_addr; memcpy(fname, nl_addr->fname, MAX_FN_LEN + 1); fname[MAX_FN_LEN] = '\0'; /* make sure the fname is null terminated */ fname_len = STRLEN(fname); msgbuff[0] = '\0'; if (memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 1)) { if (!memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 3)) { util_out_print("Cannot rundown shmid = !UL for database !AD as it has format !AD " "but this mupip uses format !AD", TRUE, shmid, fname_len, fname, GDS_LABEL_SZ - 1, nl_addr->label, GDS_LABEL_SZ - 1, GDS_LABEL); *exit_stat = ERR_MUNOTALLSEC; } shmdt((void *)start_addr); return FALSE; } if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */ util_out_print("!AD -> Error with shmctl for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = FALSE; /* Check if db filename reported in shared memory still exists. If not, clean this shared memory section * without even invoking "mu_rndwn_file" as that expects the db file to exist. Same case if shared memory * points back to a database whose file header does not have this shmid. */ if (-1 == Stat(fname, &st_buff)) { if (ENOENT == errno) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", fname); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else { /* Stat errored out e.g. due to file permissions. Log that */ save_errno = errno; util_out_print("Cannot rundown shmid !UL for database file !AD as stat() on the file" " returned the following error", TRUE, shmid, fname_len, fname); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } } else { mu_gv_cur_reg_init(); gv_cur_region->dyn.addr->fname_len = strlen(fname); STRNCPY_STR(gv_cur_region->dyn.addr->fname, fname, gv_cur_region->dyn.addr->fname_len); fc = gv_cur_region->dyn.addr->file_cntl; fc->op = FC_OPEN; status = dbfilop(fc); if (SS_NORMAL != status) { util_out_print("!AD -> Error with dbfilop for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) status, 2, DB_LEN_STR(gv_cur_region), errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } udi = FILE_INFO(gv_cur_region); LSEEKREAD(udi->fd, 0, &tsd, SIZEOF(sgmnt_data), status); if (0 != status) { save_errno = errno; util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } mu_gv_cur_reg_free(); if (tsd.shmid != shmid) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory ID (%d) in the DB file header does not match with the one" " reported by \"ipcs\" command (%d)", tsd.shmid, shmid); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else if (tsd.gt_shm_ctime.ctime != shmstat.shm_ctime) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory creation time in the DB file header does not match with" " the one reported by shmctl"); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } } shmdt((void *)start_addr); if (remove_shmid) { assert('\0' != msgbuff[0]); if (0 != shm_rmid(shmid)) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_DBFILERR, 2, fname_len, fname, ERR_TEXT, 2, RTS_ERROR_TEXT("Error removing shared memory")); util_out_print("!AD -> Error removing shared memory for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; return FALSE; } PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, fname_len, fname, shmid); *exit_stat = ERR_SHMREMOVED; } else *exit_stat = SS_NORMAL; return TRUE; }
/* * This is a plain way to read file header. * User needs to take care of concurrency issue etc. * Parameters : * fn : full name of a database file. * header: Pointer to database file header structure (may not be in shared memory) * len: size of header (may be just SGMNT_HDR_LEN or SIZEOF_FILE_HDR_MAX) */ boolean_t file_head_read(char *fn, sgmnt_data_ptr_t header, int4 len) { int save_errno, fd, header_size; struct stat stat_buf; error_def(ERR_DBFILOPERR); error_def(ERR_DBNOTGDS); header_size = sizeof(sgmnt_data); OPENFILE(fn, O_RDONLY, fd); if (-1 == fd) { save_errno = errno; gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); return FALSE; } FSTAT_FILE(fd, &stat_buf, save_errno); if (-1 == save_errno) { save_errno = errno; gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE(fd, save_errno); return FALSE; } if (!S_ISREG(stat_buf.st_mode) || stat_buf.st_size < header_size) { gtm_putmsg(VARLSTCNT(4) ERR_DBNOTGDS, 2, LEN_AND_STR(fn)); CLOSEFILE(fd, save_errno); return FALSE; } LSEEKREAD(fd, 0, header, header_size, save_errno); if (0 != save_errno) { gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE(fd, save_errno); return FALSE; } if (memcmp(header->label, GDS_LABEL, GDS_LABEL_SZ - 1)) { gtm_putmsg(VARLSTCNT(4) ERR_DBNOTGDS, 2, LEN_AND_STR(fn)); CLOSEFILE(fd, save_errno); return FALSE; } CHECK_DB_ENDIAN(header, strlen(fn), fn); assert(MASTER_MAP_SIZE_MAX >= MASTER_MAP_SIZE(header)); assert(SGMNT_HDR_LEN == len || SIZEOF_FILE_HDR(header) <= len); if (SIZEOF_FILE_HDR(header) <= len) { LSEEKREAD(fd, ROUND_UP(SGMNT_HDR_LEN + 1, DISK_BLOCK_SIZE), MM_ADDR(header), MASTER_MAP_SIZE(header), save_errno); if (0 != save_errno) { gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE(fd, save_errno); return FALSE; } } CLOSEFILE(fd, save_errno); if (0 != save_errno) { gtm_putmsg(VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); return FALSE; } return TRUE; }
int4 mupip_set_file(int db_fn_len, char *db_fn) { bool got_standalone; boolean_t bypass_partial_recov, need_standalone = FALSE; char acc_spec[MAX_ACC_METH_LEN], ver_spec[MAX_DB_VER_LEN], exit_stat, *fn; unsigned short acc_spec_len = MAX_ACC_METH_LEN, ver_spec_len = MAX_DB_VER_LEN; int fd, fn_len; int4 status; int4 status1; int glbl_buff_status, defer_status, rsrvd_bytes_status, extn_count_status, lock_space_status, disk_wait_status; int4 new_disk_wait, new_cache_size, new_extn_count, new_lock_space, reserved_bytes, defer_time; sgmnt_data_ptr_t csd; tp_region *rptr, single; enum db_acc_method access, access_new; enum db_ver desired_dbver; gd_region *temp_cur_region; char *errptr, *command = "MUPIP SET VERSION"; int save_errno; error_def(ERR_DBPREMATEOF); error_def(ERR_DBRDERR); error_def(ERR_DBRDONLY); error_def(ERR_INVACCMETHOD); error_def(ERR_MUNOACTION); error_def(ERR_RBWRNNOTCHG); error_def(ERR_WCERRNOTCHG); error_def(ERR_WCWRNNOTCHG); error_def(ERR_MMNODYNDWNGRD); exit_stat = EXIT_NRM; defer_status = cli_present("DEFER_TIME"); if (defer_status) need_standalone = TRUE; bypass_partial_recov = cli_present("PARTIAL_RECOV_BYPASS") == CLI_PRESENT; if (bypass_partial_recov) need_standalone = TRUE; if (disk_wait_status = cli_present("WAIT_DISK")) { if (cli_get_int("WAIT_DISK", &new_disk_wait)) { if (new_disk_wait < 0) { util_out_print("!UL negative, minimum WAIT_DISK allowed is 0.", TRUE, new_disk_wait); return (int4)ERR_WCWRNNOTCHG; } need_standalone = TRUE; } else { util_out_print("Error getting WAIT_DISK qualifier value", TRUE); return (int4)ERR_WCWRNNOTCHG; } } if (glbl_buff_status = cli_present("GLOBAL_BUFFERS")) { if (cli_get_int("GLOBAL_BUFFERS", &new_cache_size)) { if (new_cache_size > WC_MAX_BUFFS) { util_out_print("!UL too large, maximum write cache buffers allowed is !UL", TRUE, new_cache_size, WC_MAX_BUFFS); return (int4)ERR_WCWRNNOTCHG; } if (new_cache_size < WC_MIN_BUFFS) { util_out_print("!UL too small, minimum cache buffers allowed is !UL", TRUE, new_cache_size, WC_MIN_BUFFS); return (int4)ERR_WCWRNNOTCHG; } } else { util_out_print("Error getting GLOBAL BUFFER qualifier value", TRUE); return (int4)ERR_WCWRNNOTCHG; } need_standalone = TRUE; } /* EXTENSION_COUNT does not require standalone access and hence need_standalone will not be set to TRUE for this. */ if (extn_count_status = cli_present("EXTENSION_COUNT")) { if (cli_get_int("EXTENSION_COUNT", &new_extn_count)) { if (new_extn_count > MAX_EXTN_COUNT) { util_out_print("!UL too large, maximum extension count allowed is !UL", TRUE, new_extn_count, MAX_EXTN_COUNT); return (int4)ERR_WCWRNNOTCHG; } if (new_extn_count < MIN_EXTN_COUNT) { util_out_print("!UL too small, minimum extension count allowed is !UL", TRUE, new_extn_count, MIN_EXTN_COUNT); return (int4)ERR_WCWRNNOTCHG; } } else { util_out_print("Error getting EXTENSION COUNT qualifier value", TRUE); return (int4)ERR_WCWRNNOTCHG; } } if (lock_space_status = cli_present("LOCK_SPACE")) { if (cli_get_int("LOCK_SPACE", &new_lock_space)) { if (new_lock_space > MAX_LOCK_SPACE) { util_out_print("!UL too large, maximum lock space allowed is !UL", TRUE, new_lock_space, MAX_LOCK_SPACE); return (int4)ERR_WCWRNNOTCHG; } else if (new_lock_space < MIN_LOCK_SPACE) { util_out_print("!UL too small, minimum lock space allowed is !UL", TRUE, new_lock_space, MIN_LOCK_SPACE); return (int4)ERR_WCWRNNOTCHG; } } else { util_out_print("Error getting LOCK_SPACE qualifier value", TRUE); return (int4)ERR_WCWRNNOTCHG; } need_standalone = TRUE; } if (rsrvd_bytes_status = cli_present("RESERVED_BYTES")) { if (!cli_get_int("RESERVED_BYTES", &reserved_bytes)) { util_out_print("Error getting RESERVED BYTES qualifier value", TRUE); return (int4)ERR_RBWRNNOTCHG; } need_standalone = TRUE; } if (cli_present("ACCESS_METHOD")) { cli_get_str("ACCESS_METHOD", acc_spec, &acc_spec_len); cli_strupper(acc_spec); if (0 == memcmp(acc_spec, "MM", acc_spec_len)) access = dba_mm; else if (0 == memcmp(acc_spec, "BG", acc_spec_len)) access = dba_bg; else mupip_exit(ERR_INVACCMETHOD); need_standalone = TRUE; } else access = n_dba; /* really want to keep current method, which has not yet been read */ if (cli_present("VERSION")) { assert(!need_standalone); cli_get_str("VERSION", ver_spec, &ver_spec_len); cli_strupper(ver_spec); if (0 == memcmp(ver_spec, "V4", ver_spec_len)) desired_dbver = GDSV4; else if (0 == memcmp(ver_spec, "V5", ver_spec_len)) desired_dbver = GDSV5; else GTMASSERT; /* CLI should prevent us ever getting here */ } else desired_dbver = GDSVLAST; /* really want to keep version, which has not yet been read */ if (region) rptr = grlist; else { rptr = &single; memset(&single, 0, sizeof(single)); } csd = (sgmnt_data *)malloc(ROUND_UP(sizeof(sgmnt_data), DISK_BLOCK_SIZE)); in_backup = FALSE; /* Only want yes/no from mupfndfil, not an address */ for (; rptr != NULL; rptr = rptr->fPtr) { if (region) { if (dba_usr == rptr->reg->dyn.addr->acc_meth) { util_out_print("!/Region !AD is not a GDS access type", TRUE, REG_LEN_STR(rptr->reg)); exit_stat |= EXIT_WRN; continue; } if (!mupfndfil(rptr->reg, NULL)) continue; fn = (char *)rptr->reg->dyn.addr->fname; fn_len = rptr->reg->dyn.addr->fname_len; } else { fn = db_fn; fn_len = db_fn_len; } mu_gv_cur_reg_init(); strcpy((char *)gv_cur_region->dyn.addr->fname, fn); gv_cur_region->dyn.addr->fname_len = fn_len; if (!need_standalone) { gvcst_init(gv_cur_region); change_reg(); /* sets cs_addrs and cs_data */ if (gv_cur_region->read_only) { gtm_putmsg(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); exit_stat |= EXIT_ERR; gds_rundown(); mu_gv_cur_reg_free(); continue; } grab_crit(gv_cur_region); status = EXIT_NRM; access_new = (n_dba == access ? cs_data->acc_meth : access); /* recalculate; n_dba is a proxy for no change */ change_fhead_timer("FLUSH_TIME", cs_data->flush_time, (dba_bg == access_new ? TIM_FLU_MOD_BG : TIM_FLU_MOD_MM), FALSE); if (GDSVLAST != desired_dbver) { if ((dba_mm != access_new) || (GDSV4 != desired_dbver)) status1 = desired_db_format_set(gv_cur_region, desired_dbver, command); else { status1 = ERR_MMNODYNDWNGRD; gtm_putmsg(VARLSTCNT(4) status1, 2, REG_LEN_STR(gv_cur_region)); } if (SS_NORMAL != status1) { /* "desired_db_format_set" would have printed appropriate error messages */ if (ERR_MUNOACTION != status1) { /* real error occurred while setting the db format. skip to next region */ status = EXIT_ERR; } } } if (EXIT_NRM == status) { if (extn_count_status) cs_data->extension_size = (uint4)new_extn_count; wcs_flu(WCSFLU_FLUSH_HDR); if (extn_count_status) util_out_print("Database file !AD now has extension count !UL", TRUE, fn_len, fn, cs_data->extension_size); if (GDSVLAST != desired_dbver) util_out_print("Database file !AD now has desired DB format !AD", TRUE, fn_len, fn, LEN_AND_STR(gtm_dbversion_table[cs_data->desired_db_format])); } else exit_stat |= status; rel_crit(gv_cur_region); gds_rundown(); } else { /* Following part needs standalone access */ assert(GDSVLAST == desired_dbver); got_standalone = mu_rndwn_file(gv_cur_region, TRUE); if (FALSE == got_standalone) return (int4)ERR_WCERRNOTCHG; /* we should open it (for changing) after mu_rndwn_file, since mu_rndwn_file changes the file header too */ if (-1 == (fd = OPEN(fn, O_RDWR))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); exit_stat |= EXIT_ERR; db_ipcs_reset(gv_cur_region, FALSE); mu_gv_cur_reg_free(); continue; } LSEEKREAD(fd, 0, csd, sizeof(sgmnt_data), status); if (0 != status) { save_errno = errno; PERROR("Error reading header of file"); errptr = (char *)STRERROR(save_errno); util_out_print("read : !AZ", TRUE, errptr); util_out_print("Error reading header of file", TRUE); util_out_print("Database file !AD not changed: ", TRUE, fn_len, fn); if (-1 != status) rts_error(VARLSTCNT(4) ERR_DBRDERR, 2, fn_len, fn); else rts_error(VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); } if (rsrvd_bytes_status) { if (reserved_bytes > MAX_RESERVE_B(csd)) { util_out_print("!UL too large, maximum reserved bytes allowed is !UL for database file !AD", TRUE, reserved_bytes, MAX_RESERVE_B(csd), fn_len, fn); close(fd); db_ipcs_reset(gv_cur_region, FALSE); return (int4)ERR_RBWRNNOTCHG; } csd->reserved_bytes = reserved_bytes; } access_new = (n_dba == access ? csd->acc_meth : access); /* recalculate; n_dba is a proxy for no change */ change_fhead_timer("FLUSH_TIME", csd->flush_time, (dba_bg == access_new ? TIM_FLU_MOD_BG : TIM_FLU_MOD_MM), FALSE); if ((n_dba != access) && (csd->acc_meth != access)) /* n_dba is a proxy for no change */ { if (dba_mm == access) csd->defer_time = 1; /* defer defaults to 1 */ csd->acc_meth = access; if (0 == csd->n_bts) { csd->n_bts = WC_DEF_BUFFS; csd->bt_buckets = getprime(csd->n_bts); } } if (glbl_buff_status) { csd->n_bts = BT_FACTOR(new_cache_size); csd->bt_buckets = getprime(csd->n_bts); csd->n_wrt_per_flu = 7; csd->flush_trigger = FLUSH_FACTOR(csd->n_bts); } if (disk_wait_status) csd->wait_disk_space = new_disk_wait; if (extn_count_status) csd->extension_size = (uint4)new_extn_count; if (lock_space_status) csd->lock_space_size = (uint4)new_lock_space * OS_PAGELET_SIZE; if (bypass_partial_recov) { csd->file_corrupt = FALSE; util_out_print("Database file !AD now has partial recovery flag set to !UL(FALSE) ", TRUE, fn_len, fn, csd->file_corrupt); } if (dba_mm == access_new) { if (CLI_NEGATED == defer_status) csd->defer_time = 0; else if (CLI_PRESENT == defer_status) { if (!cli_get_num("DEFER_TIME", &defer_time)) { util_out_print("Error getting DEFER_TIME qualifier value", TRUE); db_ipcs_reset(gv_cur_region, FALSE); return (int4)ERR_RBWRNNOTCHG; } if (-1 > defer_time) { util_out_print("DEFER_TIME cannot take negative values less than -1", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); exit_stat |= EXIT_WRN; db_ipcs_reset(gv_cur_region, FALSE); mu_gv_cur_reg_free(); continue; } csd->defer_time = defer_time; } if (csd->blks_to_upgrd) { util_out_print("MM access method cannot be set if there are blocks to upgrade", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); exit_stat |= EXIT_WRN; db_ipcs_reset(gv_cur_region, FALSE); mu_gv_cur_reg_free(); continue; } if (GDSVCURR != csd->desired_db_format) { util_out_print("MM access method cannot be set in DB compatibility mode", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); exit_stat |= EXIT_WRN; db_ipcs_reset(gv_cur_region, FALSE); mu_gv_cur_reg_free(); continue; } if (JNL_ENABLED(csd) && csd->jnl_before_image) { util_out_print("MM access method cannot be set with BEFORE image journaling", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); exit_stat |= EXIT_WRN; db_ipcs_reset(gv_cur_region, FALSE); mu_gv_cur_reg_free(); continue; } csd->jnl_before_image = FALSE; } else { if (defer_status) { util_out_print("DEFER cannot be specified with BG access method.", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); exit_stat |= EXIT_WRN; db_ipcs_reset(gv_cur_region, FALSE); mu_gv_cur_reg_free(); continue; } } LSEEKWRITE(fd, 0, csd, sizeof(sgmnt_data), status); if (0 != status) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); util_out_print("Error writing header of file", TRUE); util_out_print("Database file !AD not changed: ", TRUE, fn_len, fn); rts_error(VARLSTCNT(4) ERR_DBRDERR, 2, fn_len, fn); } close(fd); /* --------------------- report results ------------------------- */ if (glbl_buff_status) util_out_print("Database file !AD now has !UL global buffers", TRUE, fn_len, fn, csd->n_bts); if (defer_status && (dba_mm == csd->acc_meth)) util_out_print("Database file !AD now has defer_time set to !SL", TRUE, fn_len, fn, csd->defer_time); if (rsrvd_bytes_status) util_out_print("Database file !AD now has !UL reserved bytes", TRUE, fn_len, fn, csd->reserved_bytes); if (extn_count_status) util_out_print("Database file !AD now has extension count !UL", TRUE, fn_len, fn, csd->extension_size); if (lock_space_status) util_out_print("Database file !AD now has lock space !UL pages", TRUE, fn_len, fn, csd->lock_space_size/OS_PAGELET_SIZE); if (disk_wait_status) util_out_print("Database file !AD now has wait disk set to !UL seconds", TRUE, fn_len, fn, csd->wait_disk_space); db_ipcs_reset(gv_cur_region, FALSE); } /* end of else part if (!need_standalone) */ mu_gv_cur_reg_free(); } free(csd); assert(!(exit_stat & EXIT_INF)); return (exit_stat & EXIT_ERR ? (int4)ERR_WCERRNOTCHG : (exit_stat & EXIT_WRN ? (int4)ERR_WCWRNNOTCHG : SS_NORMAL)); }
void mupip_upgrade(void) { bool rbno; unsigned char *upgrd_buff[2], upgrd_label[GDS_LABEL_SZ]="UPGRADE0304"; char fn[256]; char answer[4]; unsigned short fn_len; int4 fd, save_errno, old_hdr_size, new_hdr_size, status, bufsize, dsize, datasize[2]; int4 old_hdr_size_vbn, new_hdr_size_vbn; int fstat_res; off_t last_full_grp_startoff, old_file_len, old_file_len2, read_off, write_off, old_start_vbn_off; block_id last_full_grp_startblk; v3_sgmnt_data old_head_data, *old_head; sgmnt_data new_head_data, *new_head; struct stat stat_buf; error_def(ERR_MUNODBNAME); error_def(ERR_MUNOUPGRD); error_def(ERR_DBOPNERR); error_def(ERR_DBRDONLY); error_def(ERR_DBFILOPERR); error_def(ERR_DBPREMATEOF); ESTABLISH(mupip_upgrade_ch); fn_len = sizeof(fn); if (!cli_get_str("FILE", fn, &fn_len)) rts_error(VARLSTCNT(1) ERR_MUNODBNAME); if (!(mupip_upgrade_standalone(fn, &upgrade_standalone_sems))) rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); if (-1 == (fd = OPEN(fn, O_RDWR))) { save_errno = errno; if (-1 != (fd = OPEN(fn, O_RDONLY))) { util_out_print("Cannot update read-only database.", FLUSH); rts_error(VARLSTCNT(5) ERR_DBRDONLY, 2, fn_len, fn, errno); } rts_error(VARLSTCNT(5) ERR_DBRDONLY, 2, fn_len, fn, save_errno); } /* Confirm before proceed */ if (!mu_upgrd_confirmed(TRUE)) { util_out_print("Upgrade canceled by user", FLUSH); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } util_out_print("Do not interrupt to avoid damage in database!!", FLUSH); util_out_print("Mupip upgrade started ...!/", FLUSH); mu_upgrd_sig_init(); /* get file status */ FSTAT_FILE(fd, &stat_buf, fstat_res); if (-1 == fstat_res) rts_error(VARLSTCNT(5) ERR_DBOPNERR, 2, fn_len, fn, errno); old_file_len = stat_buf.st_size; /* Prepare v3.x file header buffer */ old_hdr_size = sizeof(*old_head); old_head = &old_head_data; /* Prepare v4.x file header buffer */ new_hdr_size = sizeof(*new_head); new_head = &new_head_data; memset(new_head, 0, new_hdr_size); old_hdr_size_vbn = DIVIDE_ROUND_UP(old_hdr_size, DISK_BLOCK_SIZE); new_hdr_size_vbn = DIVIDE_ROUND_UP(new_hdr_size, DISK_BLOCK_SIZE); /* READ header from V3.x file */ LSEEKREAD(fd, 0, old_head, old_hdr_size, status); if (0 != status) if (-1 == status) rts_error(VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); else rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); /* Check version */ if (memcmp(&old_head->label[0], GDS_LABEL, GDS_LABEL_SZ - 1)) { if (memcmp(&old_head->label[0], GDS_LABEL, GDS_LABEL_SZ - 3)) { /* it is not a GTM database */ close(fd); util_out_print("File !AD is not a GT.M database.!/", FLUSH, fn_len, fn); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); }else { /* it is GTM database */ /* is it not v3.x database? */ if (memcmp(&old_head->label[GDS_LABEL_SZ - 3],GDS_V30,2) !=0 && memcmp(&old_head->label[GDS_LABEL_SZ - 3],GDS_ALT_V30,2) != 0) { close(fd); util_out_print("File !AD has an unrecognized database version!/", FLUSH, fn_len, fn); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } } } else { /* Note: We assume that if the V4.x header and current GT.M file header * has same field names, they are at same offset */ /* READ the header from file again as V4.x header */ LSEEKREAD(fd, 0, new_head, new_hdr_size, status); if (0 != status) if (-1 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); else rts_error(VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); if (QWNE(new_head->reg_seqno, seq_num_zero) || QWNE(new_head->resync_seqno, seq_num_zero) || (new_head->resync_tn != 0) || new_head->repl_state != repl_closed) { util_out_print("!AD might already have been upgraded", FLUSH, fn_len, fn); util_out_print("Do you wish to continue with the upgrade? [y/n] ", FLUSH); SCANF("%s", answer); if (answer[0] != 'y' && answer[0] != 'Y') { close(fd); util_out_print("Upgrade canceled by user", FLUSH); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } } init_replication(new_head); new_head->max_update_array_size = new_head->max_non_bm_update_array_size = ROUND_UP2(MAX_NON_BITMAP_UPDATE_ARRAY_SIZE(new_head), UPDATE_ARRAY_ALIGN_SIZE); new_head->max_update_array_size += ROUND_UP2(MAX_BITMAP_UPDATE_ARRAY_SIZE, UPDATE_ARRAY_ALIGN_SIZE); new_head->mutex_spin_parms.mutex_hard_spin_count = MUTEX_HARD_SPIN_COUNT; new_head->mutex_spin_parms.mutex_sleep_spin_count = MUTEX_SLEEP_SPIN_COUNT; new_head->mutex_spin_parms.mutex_spin_sleep_mask = MUTEX_SPIN_SLEEP_MASK; new_head->semid = INVALID_SEMID; new_head->shmid = INVALID_SHMID; if (JNL_ALLOWED(new_head)) { /* Following 3 are new fields starting from V43001. * Initialize them appropriately. */ new_head->epoch_interval = DEFAULT_EPOCH_INTERVAL; new_head->alignsize = DISK_BLOCK_SIZE * JNL_DEF_ALIGNSIZE; if (!new_head->jnl_alq) new_head->jnl_alq = JNL_ALLOC_DEF; /* note new_head->jnl_deq is carried over without any change even if it is zero since a zero * jnl file extension size is supported starting V43001 */ new_head->autoswitchlimit = ALIGNED_ROUND_DOWN(JNL_ALLOC_MAX, new_head->jnl_alq, new_head->jnl_deq); /* following field is assumed as non-zero by set_jnl_info starting V43001A */ if (JNL_ALLOWED(new_head) && !new_head->jnl_buffer_size) new_head->jnl_buffer_size = JNL_BUFFER_DEF; } else { new_head->epoch_interval = 0; new_head->alignsize = 0; new_head->autoswitchlimit = 0; } new_head->yield_lmt = DEFAULT_YIELD_LIMIT; /* writing header */ LSEEKWRITE(fd, 0, new_head, new_hdr_size, status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); close(fd); util_out_print("File !AD successfully upgraded.!/", FLUSH, fn_len, fn); if (0 != sem_rmid(upgrade_standalone_sems)) { util_out_print("Error with sem_rmid : %d [0x%x]", TRUE, upgrade_standalone_sems, upgrade_standalone_sems); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } mupip_exit(SS_NORMAL); } util_out_print("Old header size: !SL", FLUSH, old_hdr_size); util_out_print("New header size: !SL", FLUSH, new_hdr_size); if (old_head->createinprogress) { close(fd); util_out_print("Database creation in progress on file !AD.!/", FLUSH, fn_len, fn); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } if (old_head->file_corrupt) { close(fd); util_out_print("Database !AD is corrupted.!/", FLUSH, fn_len, fn); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } if ((((off_t)old_head->start_vbn - 1) * DISK_BLOCK_SIZE + (off_t)old_head->trans_hist.total_blks * old_head->blk_size + (off_t)DISK_BLOCK_SIZE != old_file_len) && (((off_t)old_head->start_vbn - 1) * DISK_BLOCK_SIZE + (off_t)old_head->trans_hist.total_blks * old_head->blk_size + (off_t)old_head->blk_size != old_file_len)) { util_out_print("Incorrect start_vbn !SL or, block size !SL or, total blocks !SL", FLUSH, old_head->start_vbn, old_head->blk_size, old_head->trans_hist.total_blks); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } if (ROUND_DOWN(old_head->blk_size, DISK_BLOCK_SIZE) != old_head->blk_size) { util_out_print("Database block size !SL is not divisible by DISK_BLOCK_SIZE", FLUSH, old_head->blk_size); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } mu_upgrd_header(old_head, new_head); /* Update header from v3.x to v4.x */ new_head->start_vbn = new_hdr_size_vbn + 1; new_head->free_space = 0; new_head->wc_blocked_t_end_hist.evnt_cnt = old_head->wc_blocked_t_end_hist2.evnt_cnt; new_head->wc_blocked_t_end_hist.evnt_tn = old_head->wc_blocked_t_end_hist2.evnt_tn; init_replication(new_head); /* A simple way of doing mupip upgrade is to move all the data after file header towards the eof to make space and write down the header. This does not need any computation or, change in data/index blocks. This is a slow process because it has mainly I/O, though no manipulation of database structures. or index blocks. This is okay for small database. A time efficient way is to physically move second group of BLKS_PER_LMAP number of blocks towards the eof and move first group of BLKS_PER_LMAP number of blocks in place of 2nd group. Finally adjust all indices to point to the blocks correctly. Also adjust master bit map. (note: we cannot move first group from the beginning). Detail algorithm as follows: --------------------------- // Allocate two buffers each to hold one group of data. Read v3.x header and upgrade to v4.x if file is big enough read group 1 in buff[0] read_off = offset of starting block of 2nd group. read group 2 in buff[1] write buff[0] at offset read_off last_full_grp_startblk = points to the block where 2nd group of 512 blocks of old file will be written back. //Instead of searching for a free group we will write at the last full group //Say, we have 3000 blocks. last_full_grp_startblk = 2048 // (not 2560, because it is not full) //All data from that point upto eof will be read and saved in buffer read all remaining data from the point last_full_grp_startblk upto eof in buff[0] write buff[1] at the point of last_full_grp_startblk Now write buff[0] at the end of last write //Graphical Example: Each letter corresponds to a group of 512 blocks where first block // is local bit map. Last group U may be a group of less than 512 blocks. // Extend towards right -------------------------------------------------------> // old permutation: [v3 head] A B C D E F G H I J K L M N O P Q R S T U // new permutation: [v4 head ] A C D E F G H I J K L M N O P Q R S T B U Finally traverse the tree and adjust block pointers Adjust master map write new v4.x header at bof else bufsize = size of data for a group rbno = 0 // read buffer no. This switches between 0 and 1 read_off = 0 write_off = 0 upgrd_buff[rbno] = new header data_size[rbno] = new header size rbno = INVERT(rbno); do while not eof data_size[rbno] = MIN(bufsize, remaining_data_size) Read data of size data_size[rbno] in upgrd_buff[rbno] and adjust read_off rbno = INVERT(rbno); Write upgrd_buff[rbno] of datasize[rbno] at write_off and increase write_off Enddo rbno = INVERT(rbno) Write upgrd_buff[rbno] of datasize[rbno] at write_off endif */ bufsize = old_head->blk_size * BLKS_PER_LMAP; upgrd_buff[0] = (unsigned char*) malloc(bufsize); upgrd_buff[1] = (unsigned char*) malloc(bufsize); read_off = old_start_vbn_off = (off_t)(old_head->start_vbn - 1) * DISK_BLOCK_SIZE; /* start vbn offset in bytes */ last_full_grp_startblk = ROUND_DOWN(new_head->trans_hist.total_blks, BLKS_PER_LMAP); /* in block_id */ last_full_grp_startoff = old_start_vbn_off + (off_t)last_full_grp_startblk * new_head->blk_size; /* offset in bytes */ /* this calculation is used because some 3.2x database has GDS blk_size bytes at the end instead of DISK_BLOCK_SIZE bytes. */ old_file_len2 = old_head->start_vbn * DISK_BLOCK_SIZE + (off_t)old_head->blk_size * old_head->trans_hist.total_blks; /* Change Label to a temporary dummy value, so that other GTM process does not come while doing upgrade and corrupts database */ LSEEKWRITE(fd, 0, upgrd_label, GDS_LABEL_SZ - 1, status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); if (old_head->trans_hist.total_blks > BLKS_PER_LMAP * 2) { /* recalculate start_vbn and free space, because there will be a gap after header */ new_head->start_vbn = old_head->start_vbn + bufsize / DISK_BLOCK_SIZE; new_head->free_space = bufsize - (new_hdr_size_vbn - old_hdr_size_vbn) * DISK_BLOCK_SIZE; util_out_print("New starting VBN is: !SL !/", FLUSH, new_head->start_vbn); /* read 1st group of blocks */ LSEEKREAD(fd, read_off, upgrd_buff[0], bufsize, status); if (0 != status) if (-1 == status) rts_error(VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); else rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); read_off = read_off + bufsize; /* read 2nd group of blocks */ LSEEKREAD(fd, read_off, upgrd_buff[1], bufsize, status); if (0 != status) if (-1 == status) rts_error(VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); else rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); /* write 1st group of blocks in place of 2nd group */ write_off = old_start_vbn_off + bufsize; LSEEKWRITE(fd, write_off, upgrd_buff[0], bufsize, status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); /* read last group (# of blks <= BLKS_PER_LMAP) */ dsize = old_file_len2 - last_full_grp_startoff; assert (dsize <= bufsize); LSEEKREAD(fd, last_full_grp_startoff, upgrd_buff[0], dsize, status); if (0 != status) if (-1 == status) rts_error(VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); else rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); /* write 2nd group of blocks */ LSEEKWRITE(fd, last_full_grp_startoff, upgrd_buff[1], bufsize, status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); /* write last group read from old file */ LSEEKWRITE(fd, last_full_grp_startoff + bufsize, upgrd_buff[0], dsize, status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); util_out_print("Please wait while index is being adjusted...!/", FLUSH); mu_upgrd_adjust_blkptr(1L, TRUE, new_head, fd, fn, fn_len); mu_upgrd_adjust_mm(new_head->master_map, DIVIDE_ROUND_UP(new_head->trans_hist.total_blks+1,BLKS_PER_LMAP)); /* writing header */ LSEEKWRITE(fd, 0, new_head, new_hdr_size, status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); } else /* very small database */ { rbno = 0; write_off = 0; datasize[rbno] = new_hdr_size; memcpy(upgrd_buff[0], new_head, new_hdr_size); rbno = INVERT(rbno); while(read_off < old_file_len2) { datasize[rbno] = MIN (old_file_len2 - read_off, bufsize); LSEEKREAD(fd, read_off, upgrd_buff[rbno], datasize[rbno], status); if (0 != status) if (-1 == status) rts_error(VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); else rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); read_off += datasize[rbno]; rbno = INVERT(rbno); LSEEKWRITE(fd, write_off, upgrd_buff[rbno], datasize[rbno], status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); write_off+= datasize[rbno]; } rbno = INVERT(rbno); LSEEKWRITE(fd, write_off, upgrd_buff[rbno], datasize[rbno], status); if (0 != status) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, fn_len, fn, status); } /* end if small database */ free(upgrd_buff[0]); free(upgrd_buff[1]); close(fd); util_out_print("File !AD successfully upgraded.!/", FLUSH, fn_len, fn); REVERT; if (0 != sem_rmid(upgrade_standalone_sems)) { util_out_print("Error with sem_rmid : %d [0x%x]", TRUE, upgrade_standalone_sems, upgrade_standalone_sems); rts_error(VARLSTCNT(1) ERR_MUNOUPGRD); } mupip_exit(SS_NORMAL); }