/* * PROGRAM: JRD Access Method * MODULE: old.c * DESCRIPTION: * * 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 "../jrd/ib_stdio.h" #include #include "../jrd/common.h" #include "../jrd/jrd.h" #include "../jrd/pio.h" #include "../jrd/ods.h" #include "../jrd/pag.h" #include "../jrd/jrn.h" #include "../jrd/old.h" #include "../jrd/llio.h" #include "gen/codes.h" #include "../wal/wal.h" #include "../jrd/ail.h" #include "../jrd/all_proto.h" #include "../jrd/cch_proto.h" #include "../jrd/err_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/isc_f_proto.h" #include "../jrd/jrn_proto.h" #include "../jrd/llio_proto.h" #include "../jrd/misc_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/old_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/thd_proto.h" #ifdef HAVE_UNISTD_H #include #endif static int close_cur_file(OLD, USHORT); static int create_file(OLD, SLONG *); static void get_wal_offset(USHORT, ULONG *, ULONG *, USHORT); static SLONG old_dump_all_pages(OLD, PIP, SLONG, ULONG); static int old_dump_page(OLD, ULONG); static void old_fini(OLD *, USHORT); static int old_init(OLD *, SCHAR *, USHORT, SSHORT, SCHAR **, ULONG, ULONG, SSHORT, SSHORT, ULONG, ULONG, ULONG); static int old_put(OLD, SCHAR *, USHORT); static void old_put_db_filename(OLD); static int old_put_rec(OLD, JRNH *, USHORT, UCHAR *, USHORT); static int open_next_file(OLD); int OLD_dump( TEXT * dbname, USHORT db_len, USHORT dump_id, ULONG file_size, ULONG start_page, ULONG start_seqno, USHORT start_file, USHORT num_files, SCHAR ** files) { /************************************** * * O L D _ d u m p * ************************************** * * Functional description * Do an online dump of the database. * RETURNS: * Success. * error conditions * Full disk - page number on which error occured. * Other relevent info to pick up where it leaves off. * **************************************/ TDBB tdbb; DBB dbb; WIN window; PGC pgc; UCHAR byte; SLONG sequence; ULONG seqno; ULONG offset; ULONG p_offset; OLD OLD_handle; PIP page; SLONG last_page; PIP temp_page; TEXT db[MAX_PATH_LENGTH]; SSHORT file_seqno; SLONG block_seqno; SCHAR journal_dir[MAX_PATH_LENGTH], data[MAX_PATH_LENGTH]; SSHORT jd_len, d_len; SLONG ret_val; tdbb = GET_THREAD_DATA; dbb = tdbb->tdbb_database; PAG_get_clump(HEADER_PAGE, HDR_backup_info, reinterpret_cast < USHORT * >(&d_len), reinterpret_cast < UCHAR * >(data)); PAG_get_clump(HEADER_PAGE, HDR_journal_server, reinterpret_cast < USHORT * >(&jd_len), reinterpret_cast < UCHAR * >(journal_dir)); journal_dir[jd_len] = 0; if (!jd_len) ERR_post(gds_jrn_enable, 0); if ((ret_val = JRN_init(tdbb->tdbb_status_vector, &dbb->dbb_journal, dbb->dbb_page_size, reinterpret_cast < UCHAR * >(journal_dir), jd_len, reinterpret_cast < UCHAR * >(data), d_len)) != SUCCESS) AIL_process_jrn_error(ret_val); if (db_len) { MOVE_FAST(dbname, db, db_len); db[db_len] = 0; } else strcpy(db, dbname); seqno = offset = p_offset = 0; if (!start_page) { p_offset = 0; dump_id = 0; /* First time around, send message to journal server */ get_wal_offset(dump_id, &seqno, &offset, JRN_START_ONLINE_DMP); /* * Journal the Start Online Dump info to journal server. * This will return the dump id of this online dump. */ if ((ret_val = JRN_put_old_start(tdbb->tdbb_status_vector, dbb->dbb_journal, seqno, offset, p_offset, &dump_id)) != SUCCESS) AIL_process_jrn_error(ret_val); } /* If file size is not specified, go for the maximum possible */ if (!file_size) file_size = -1; OLD_handle = NULL; if (old_init(&OLD_handle, db, dump_id, num_files, files, file_size, start_seqno, start_file, JRND_SIZE + dbb->dbb_page_size, seqno, offset, p_offset)) { old_fini(&OLD_handle, 0); ERR_post(gds_old_failure, gds_arg_string, "Online Dump Initialize failed", 0); } /* put all secondary database file names to OLD file */ if (!start_page) old_put_db_filename(OLD_handle); /* dump all 'in use' pages in database. */ temp_page = (PIP) ALL_malloc(dbb->dbb_page_size, ERR_jmp); pgc = dbb->dbb_pcontrol; for (sequence = 0;; sequence++) { window.win_page = (sequence) ? (SLONG) (sequence * pgc->pgc_ppp - 1) : (SLONG) pgc->pgc_pip; window.win_flags = 0; page = (PIP) CCH_FETCH(NULL_TDBB, &window, LCK_read, pag_pages); MOVE_FAST((UCHAR *) page, (UCHAR *) temp_page, dbb->dbb_page_size); CCH_RELEASE(NULL_TDBB, &window); if (start_page < ((sequence + 1) * pgc->pgc_ppp)) { last_page = old_dump_all_pages(OLD_handle, temp_page, sequence, start_page); /* Check for error */ if (last_page) break; } /* check if we are at end of database files */ byte = temp_page->pip_bits[pgc->pgc_bytes - 1]; /* * When pages are allocated, the last page available on a PIP * is formatted as the next PIP. A bit 1 represents a free page * and a bit 0 represents an allocated page. Since PIP pages * are never allocated, a free last bit on a PIP indicates that * it is the last PIP for this database. */ if (byte & 0x80) break; } ALL_free(reinterpret_cast < char *>(temp_page)); if (last_page) { file_seqno = OLD_handle->old_file_seqno; block_seqno = OLD_handle->old_block->ob_cur_seqno; old_fini(&OLD_handle, OLD_EOF); ERR_post(gds_old_no_space, gds_arg_number, (SLONG) dump_id, gds_arg_number, (SLONG) last_page, gds_arg_number, (SLONG) block_seqno, gds_arg_number, (SLONG) file_seqno, 0); } /* Add record to JOURNAL SERVER */ get_wal_offset(dump_id, &seqno, &offset, JRN_END_ONLINE_DMP); if ((ret_val = JRN_put_old_end(tdbb->tdbb_status_vector, dbb->dbb_journal, seqno, offset, p_offset, dump_id)) != SUCCESS) AIL_process_jrn_error(ret_val); old_fini(&OLD_handle, OLD_EOD); return SUCCESS; } static int close_cur_file(OLD old, USHORT code) { /************************************** * * c l o s e _ c u r _ f i l e * ************************************** * * Functional description * Closes the current file with a code record at end * returns SUCCESS - if things work * FAILURE - close fails * **************************************/ TDBB tdbb; OLD_HDR hdr; OLDBLK ob; SLONG len; tdbb = GET_THREAD_DATA; ob = old->old_block; hdr = ob->ob_hdr; hdr->oh_type = code; hdr->oh_hdr_len = OLD_HDROVHD; hdr->oh_seqno = 0; if (LLIO_write(0, old->old_fd, 0, 0L, LLIO_SEEK_NONE, (UCHAR *) hdr, old->old_rec_size, &len) == FAILURE || len != old->old_rec_size) return FAILURE; /* flush writes to disk and close file */ LLIO_sync(0, old->old_fd); if (LLIO_close(tdbb->tdbb_status_vector, old->old_fd) == FAILURE) ERR_punt(); old->old_fd = 0; old->old_cur_size = 0; return SUCCESS; } static int create_file(OLD old, SLONG * ret_fd) { /************************************** * * c r e a t e _ f i l e * ************************************** * * Functional description * Open next file and write header & journal new name * returns SUCCESS - if things work * FAILURE - open fails * **************************************/ TDBB tdbb; SLONG fd; SLONG len; OLD_HDR hdr; OLDBLK ob; OLD_HDR_PAGE hp; DBB dbb; dbb = GET_DBB; tdbb = GET_THREAD_DATA; *ret_fd = -1; ob = old->old_block; hdr = ob->ob_hdr; if (LLIO_open(tdbb->tdbb_status_vector, old->old_files[old->old_cur_file], LLIO_OPEN_NEW_RW, TRUE, &fd) == FAILURE) ERR_punt(); MOVE_CLEAR((SCHAR *) hdr, OLD_HEADER_SIZE); hdr->oh_type = OLD_HEADER; hdr->oh_hdr_len = OLD_HEADER_SIZE; hdr->oh_seqno = 0; hp = (OLD_HDR_PAGE) hdr->oh_buf; hp->hp_rec_size = old->old_rec_size; hp->hp_dump_id = old->old_dump_id; hp->hp_file_seqno = old->old_file_seqno; hp->hp_start_block = ob->ob_cur_seqno; hp->hp_file_size = old->old_file_size; hp->hp_version = OLD_VERSION; hp->hp_log_seqno = old->old_log_seqno; hp->hp_log_offset = old->old_log_offset; hp->hp_log_p_offset = old->old_log_p_offset; hp->hp_db_page_size = dbb->dbb_page_size; hp->hp_length = strlen(old->old_db); MOVE_FAST(old->old_db, hp->hp_db, hp->hp_length); if (LLIO_write(0, fd, 0, 0L, LLIO_SEEK_NONE, (UCHAR *) hdr, OLD_HEADER_SIZE, &len) == FAILURE || len != OLD_HEADER_SIZE) { LLIO_close(0, fd); unlink(old->old_files[old->old_cur_file]); return FAILURE; } old->old_cur_size = OLD_HEADER_SIZE; *ret_fd = fd; return SUCCESS; } static void get_wal_offset( USHORT dump_id, ULONG * seqno, ULONG * offset, USHORT type) { /************************************** * * g e t _ w a l _ o f f s e t * ************************************** * * Functional description * Write a record to WAL to get seqno offset pair * **************************************/ TDBB tdbb; DBB dbb; LTJW record; tdbb = GET_THREAD_DATA; dbb = GET_DBB; record.ltjw_header.jrnh_type = type; record.ltjw_mode = 0; record.ltjw_seqno = dump_id; record.ltjw_offset = 0; record.ltjw_count = 0; record.ltjw_length = 0; tdbb->tdbb_status_vector[1] = 0; AIL_put(dbb, tdbb->tdbb_status_vector, reinterpret_cast < jrnh * >(&record), LTJW_SIZE, 0, 0, 0, 0, seqno, offset); if (tdbb->tdbb_status_vector[1]) ERR_punt(); } static SLONG old_dump_all_pages( OLD OLD_handle, PIP pages, SLONG sequence, ULONG start_page) { /************************************** * * o l d _ d u m p _ a l l _ p a g e s * ************************************** * * Functional description * Dump all inuse pages in this PIP to WAL * Returns 0 Everything is ok * next page to be dumped * **************************************/ DBB dbb; PGC pgc; UCHAR *end, *bytes, bit; SSHORT i; SLONG relative_bit; ULONG page_num; dbb = GET_DBB; pgc = dbb->dbb_pcontrol; end = (UCHAR *) pages + dbb->dbb_page_size; for (bytes = pages->pip_bits; bytes < end; bytes++) { for (i = 0, bit = 1; i < 8; i++, bit <<= 1) if (!(bit & *bytes)) { /* page is inuse, dump it */ relative_bit = ((bytes - pages->pip_bits) << 3) + i; page_num = relative_bit + sequence * pgc->pgc_ppp; /* skip forward till the start page */ if (page_num < start_page) continue; if (old_dump_page(OLD_handle, page_num)) return page_num; } } return (0L); } static int old_dump_page(OLD OLD_handle, ULONG page_number) { /************************************** * * o l d _ d u m p _ p a g e * ************************************** * * Functional description * Dump a page to the WAL. If necessary, fetch it. * **************************************/ DBB dbb; WIN window; JRND record; SSHORT ret_val; dbb = GET_DBB; record.jrnd_header.jrnh_type = JRN_PAGE; record.jrnd_page = page_number; record.jrnd_length = dbb->dbb_page_size; window.win_page = page_number; window.win_flags = 0; CCH_FETCH_NO_CHECKSUM(NULL_TDBB, &window, LCK_read, pag_undefined); ret_val = old_put_rec(OLD_handle, reinterpret_cast < jrnh * >(&record), JRND_SIZE, reinterpret_cast < UCHAR * >(window.win_buffer), dbb->dbb_page_size); CCH_RELEASE(NULL_TDBB, &window); return ret_val;; } static void old_fini(OLD * OLD_handle, USHORT code) { /************************************** * * o l d _ f i n i * ************************************** * * Functional description * Finish an online dump. * **************************************/ OLD old; /* write a OLD_EOD record */ if (old = *OLD_handle) { if (old->old_fd > 0) { if (close_cur_file(old, code) == FAILURE) return; } ALL_free(reinterpret_cast < char *>(old->old_block->ob_hdr)); ALL_free(reinterpret_cast < char *>(old->old_block)); ALL_free(reinterpret_cast < char *>(old)); } *OLD_handle = NULL; } static int old_init( OLD * OLD_handle, SCHAR * dbname, USHORT dump_id, SSHORT num_files, SCHAR ** files, ULONG file_size, ULONG start_seqno, SSHORT start_file, SSHORT rec_size, ULONG log_seqno, ULONG log_offset, ULONG log_p_offset) { /************************************** * * o l d _ i n i t * ************************************** * * Functional description * Initialize online dump for this process. * Initialize and returns the OLD_handle. * **************************************/ OLD old; if (*OLD_handle != NULL) return FAILURE; *OLD_handle = old = (OLD) ALL_malloc(sizeof(struct old), ERR_jmp); MOVE_CLEAR(old, sizeof(struct old)); old->old_block = (OLDBLK) ALL_malloc(sizeof(struct oldblk), ERR_jmp); MOVE_CLEAR(old->old_block, sizeof(struct oldblk)); old->old_block->ob_hdr = (OLD_HDR) ALL_malloc(MAX_OLDBUFLEN, ERR_jmp); MOVE_CLEAR(old->old_block->ob_hdr, MAX_OLDBUFLEN); old->old_dump_id = dump_id; old->old_rec_size = rec_size + OLD_OVHD; old->old_log_seqno = log_seqno; old->old_log_offset = log_offset; old->old_log_p_offset = log_p_offset; old->old_num_files = num_files; old->old_files = files; old->old_file_size = file_size; old->old_db = dbname; old->old_file_seqno = start_file; old->old_block->ob_cur_seqno = start_seqno; /* Start the online dump processing */ return open_next_file(old); } static int old_put(OLD OLD_handle, SCHAR * logrec, USHORT len) { /************************************** * * o l d _ p u t * ************************************** * * Functional description * Put a given record in the OnLine Dump. * If file size is specified, open new file if required. * If we are out of space, return an error. * **************************************/ UCHAR *p, *q; OLD_HDR hdr; OLDBLK ob; SLONG l; USHORT ret; ob = OLD_handle->old_block; hdr = ob->ob_hdr; if (len > MAX_OLDBUFLEN - OLD_OVHD) len = MAX_OLDBUFLEN - OLD_OVHD; /* Leave space for trailer page. */ if (OLD_handle->old_cur_size + 2 * OLD_handle->old_rec_size > OLD_handle->old_file_size) { /* Rollover to next file */ if ((open_next_file(OLD_handle)) == FAILURE) return FAILURE; return old_put(OLD_handle, logrec, len); } hdr->oh_type = OLD_DATA; hdr->oh_hdr_len = len; hdr->oh_seqno = ob->ob_cur_seqno; p = (UCHAR *) hdr->oh_buf; MOVE_FAST(logrec, p, len); p += len; q = (UCHAR *) & len; *p++ = *q++; *p++ = *q++; ret = LLIO_write(0, OLD_handle->old_fd, 0, 0L, LLIO_SEEK_NONE, (UCHAR *) hdr, OLD_handle->old_rec_size, &l); if (ret == FAILURE || l != OLD_handle->old_rec_size) { /* Not enough space */ /* backup to beginning of block and go on to next file */ if (ret == SUCCESS) LLIO_seek(0, OLD_handle->old_fd, 0, -l, LLIO_SEEK_CURRENT); if ((open_next_file(OLD_handle)) == FAILURE) return FAILURE; return old_put(OLD_handle, logrec, len); } ob->ob_cur_seqno++; OLD_handle->old_cur_size += OLD_handle->old_rec_size; return SUCCESS; } static void old_put_db_filename(OLD OLD_handle) { /************************************** * * o l d _ p u t _ d b _ f i l e n a m e * ************************************** * * Functional description * Write the file name of all secondary files to OLD * **************************************/ DBB dbb; JRNF journal; FIL fil; dbb = GET_DBB; for (fil = dbb->dbb_file; fil; fil = fil->fil_next) { /* Skip primary db file */ if (!fil->fil_min_page) continue; journal.jrnf_header.jrnh_type = JRN_NEW_FILE; journal.jrnf_start = fil->fil_min_page; journal.jrnf_sequence = fil->fil_sequence; journal.jrnf_length = fil->fil_length; old_put_rec(OLD_handle, reinterpret_cast < jrnh * >(&journal), JRNF_SIZE, reinterpret_cast < UCHAR * >(fil->fil_string), fil->fil_length); } } static int old_put_rec( OLD OLD_handle, JRNH * header, USHORT h_length, UCHAR * data, USHORT d_length) { /************************************** * * o l d _ p u t _ r e c * ************************************** * * Functional description * Write a journal record to WAL. * **************************************/ USHORT length; UCHAR buffer[MAX_RECORD], *p, *q; /* Prepare OLD record for writing */ length = h_length + d_length; header->jrnh_handle = 0; header->jrnh_length = length; header->jrnh_series = 0; header->jrnh_version = JOURNAL_VERSION; header->jrnh_prev_seqno = 0; header->jrnh_prev_offset = 0; #ifdef DEV_BUILD /* Checksum the log record after zeroing out the field */ header->jrnh_series = MISC_checksum_log_rec(reinterpret_cast < UCHAR * >(header), h_length, data, d_length); #endif /* DEV_BUILD */ /* Concatenate header and data. */ p = buffer; q = (UCHAR *) header; do *p++ = *q++; while (--h_length); if (d_length) do *p++ = *data++; while (--d_length); return old_put(OLD_handle, reinterpret_cast < char *>(buffer), length); } static int open_next_file(OLD old) { /************************************** * * o p e n _ n e x t _ f i l e * ************************************** * * Functional description * Opens the next OnLine Dump file and writes out the header. * returns SUCCESS - if things work * FAILURE - open fails, no more file etc. * **************************************/ TDBB tdbb; DBB dbb; SLONG fd; SLONG ret_val; SCHAR name[MAX_PATH_LENGTH]; tdbb = GET_THREAD_DATA; dbb = tdbb->tdbb_database; if (old->old_fd > 0) { if (close_cur_file(old, OLD_EOF) == FAILURE) return FAILURE; } if (old->old_cur_file >= old->old_num_files) return FAILURE; if (create_file(old, &fd) == FAILURE) return FAILURE; ISC_expand_filename(old->old_files[old->old_cur_file], 0, name); if ( (ret_val = JRN_put_old_file(tdbb->tdbb_status_vector, dbb->dbb_journal, name, strlen(name), old->old_file_size, old->old_file_seqno, old->old_dump_id)) != SUCCESS) { LLIO_close(0, fd); AIL_process_jrn_error(ret_val); } old->old_cur_file++; old->old_file_seqno++; old->old_fd = fd; return SUCCESS; }