806 lines
28 KiB
C
806 lines
28 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
/* mubinccpy.c
|
|
*
|
|
* -- online incremental online && incremental
|
|
* -- incremental !online && incremental
|
|
* -------- requires cs_addrs, cs_data and gv_cur_region be current.
|
|
*/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_string.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_stat.h"
|
|
#include "gtm_unistd.h"
|
|
#include "gtm_fcntl.h"
|
|
#include "gtm_socket.h"
|
|
#include "gtm_inet.h"
|
|
|
|
#include <sys/wait.h>
|
|
#include <errno.h>
|
|
|
|
#include "gdsroot.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsfhead.h"
|
|
#include "filestruct.h"
|
|
#include "gdsblk.h"
|
|
#include "gdsbml.h"
|
|
#include "stringpool.h"
|
|
#include "muextr.h"
|
|
#include "murest.h"
|
|
#include "iob.h"
|
|
#include "error.h"
|
|
#include "mupipbckup.h"
|
|
#include "gtmio.h"
|
|
#include "gtm_pipe.h"
|
|
#include "iotcproutine.h"
|
|
#include "iotcpdef.h"
|
|
#include "iotimer.h"
|
|
#include "eintr_wrappers.h"
|
|
#include "sleep_cnt.h"
|
|
#include "util.h"
|
|
#include "cli.h"
|
|
#include "op.h"
|
|
#include "io.h"
|
|
#include "is_proc_alive.h"
|
|
#include "is_raw_dev.h"
|
|
#include "gtmmsg.h"
|
|
#include "wcs_sleep.h"
|
|
#include "gds_blk_upgrade.h"
|
|
#include "iosp.h"
|
|
#include "shmpool.h"
|
|
#include "min_max.h"
|
|
#include "gvcst_lbm_check.h"
|
|
#include "wcs_phase2_commit_wait.h"
|
|
#include "gtm_permissions.h"
|
|
#ifdef GTM_CRYPT
|
|
#include "gtmcrypt.h"
|
|
#endif
|
|
|
|
GBLREF bool record;
|
|
GBLREF bool online;
|
|
GBLREF bool incremental;
|
|
GBLREF bool file_backed_up;
|
|
GBLREF bool mubtomag;
|
|
GBLREF bool mu_ctrly_occurred;
|
|
GBLREF bool mu_ctrlc_occurred;
|
|
GBLREF int4 mubmaxblk;
|
|
GBLREF spdesc stringpool;
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF uchar_ptr_t mubbuf;
|
|
GBLREF tcp_library_struct tcp_routines;
|
|
GBLREF uint4 pipe_child;
|
|
GBLREF uint4 process_id;
|
|
GBLREF boolean_t debug_mupip;
|
|
GBLREF int4 backup_write_errno;
|
|
|
|
LITREF mval literal_null;
|
|
|
|
error_def(ERR_BCKUPBUFLUSH);
|
|
error_def(ERR_COMMITWAITSTUCK);
|
|
error_def(ERR_DBCCERR);
|
|
error_def(ERR_DBROLLEDBACK);
|
|
error_def(ERR_ERRCALL);
|
|
|
|
#ifdef DEBUG_INCBKUP
|
|
# define DEBUG_INCBKUP_ONLY(X) X
|
|
#else
|
|
# define DEBUG_INCBKUP_ONLY(X)
|
|
#endif
|
|
|
|
#define COMMON_WRITE(A, B, C) { \
|
|
(*common_write)(A, B, (int)C); \
|
|
if (0 != backup_write_errno) \
|
|
return FALSE; \
|
|
DEBUG_INCBKUP_ONLY(backup_write_offset += (int)C;) \
|
|
}
|
|
|
|
#define CLEANUP_AND_RETURN_FALSE { \
|
|
if (backup_to_file == list->backup_to) \
|
|
{ \
|
|
if (NULL != backup) \
|
|
iob_close(backup); \
|
|
if (!debug_mupip) \
|
|
UNLINK(file->addr); \
|
|
} \
|
|
return FALSE; \
|
|
}
|
|
|
|
#define MAX_FILENAME_LENGTH 256
|
|
|
|
static char incbackupfile[MAX_FILENAME_LENGTH];
|
|
static BFILE *backup;
|
|
|
|
void exec_write(BFILE *bf, char *buf, int nbytes);
|
|
void tcp_write(BFILE *bf, char *buf, int nbytes);
|
|
|
|
error_def(ERR_BCKUPBUFLUSH);
|
|
error_def(ERR_COMMITWAITSTUCK);
|
|
error_def(ERR_DBCCERR);
|
|
error_def(ERR_ERRCALL);
|
|
error_def(ERR_IOEOF);
|
|
error_def(ERR_PERMGENFAIL);
|
|
|
|
bool mubinccpy (backup_reg_list *list)
|
|
{
|
|
mstr *file;
|
|
uchar_ptr_t bm_blk_buff, ptr1, ptr1_top;
|
|
char_ptr_t outptr, data_ptr;
|
|
char *c, addr[SA_MAXLEN + 1];
|
|
sgmnt_data_ptr_t header;
|
|
uint4 total_blks, bplmap, gds_ratio, save_blks;
|
|
int4 status;
|
|
int4 size1, bsize, bm_num, hint, lmsize, rsize, timeout, outsize,
|
|
blks_per_buff, counter, i, write_size, read_size, match;
|
|
size_t copysize;
|
|
off_t copied;
|
|
int db_fd, exec_fd;
|
|
enum db_acc_method access;
|
|
blk_hdr_ptr_t bp, bptr;
|
|
inc_header *outbuf;
|
|
mval val;
|
|
unsigned short port;
|
|
void (*common_write)(BFILE *, char *, int);
|
|
muinc_blk_hdr_ptr_t sblkh_p;
|
|
trans_num blk_tn;
|
|
int4 blk_bsiz;
|
|
block_id blk_num_base, blk_num;
|
|
boolean_t is_bitmap_blk, backup_this_blk;
|
|
enum db_ver dummy_odbv;
|
|
int rc;
|
|
int fstat_res;
|
|
struct stat stat_buf;
|
|
int group_id;
|
|
int perm;
|
|
struct perm_diag_data pdd;
|
|
DEBUG_INCBKUP_ONLY(int blks_this_lmap;)
|
|
DEBUG_INCBKUP_ONLY(gtm_uint64_t backup_write_offset = 0;)
|
|
|
|
assert(list->reg == gv_cur_region);
|
|
assert(incremental);
|
|
/* Make sure inc_header can be same size on all platforms. Some platforms pad 8 byte aligned structures
|
|
that end on a 4 byte boundary and some do not. It is critical that this structure is the same size on
|
|
all platforms as it is sent across TCP connections when doing TCP backup.
|
|
*/
|
|
assert(0 == (SIZEOF(inc_header) % 8));
|
|
|
|
/* ================= Initialization and some checks ======================== */
|
|
header = list->backup_hdr;
|
|
file = &(list->backup_file);
|
|
|
|
if (list->tn >= header->trans_hist.curr_tn)
|
|
{
|
|
util_out_print("!/TRANSACTION number is greater than or equal to current transaction,", TRUE);
|
|
util_out_print("no blocks backed up from database !AD", TRUE, DB_LEN_STR(gv_cur_region));
|
|
return TRUE;
|
|
}
|
|
if (!mubtomag)
|
|
mubmaxblk = (64 * 1024);
|
|
db_fd = ((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fd;
|
|
|
|
/* =================== open backup destination ============================= */
|
|
backup_write_errno = 0;
|
|
switch(list->backup_to)
|
|
{
|
|
case backup_to_file:
|
|
common_write = iob_write;
|
|
backup = iob_open_wt(file->addr, DISK_BLOCK_SIZE, BLOCKING_FACTOR);
|
|
if (NULL == backup)
|
|
{
|
|
PERROR("open error: ");
|
|
util_out_print("Error: Cannot create backup file !AD.", TRUE, file->len, file->addr);
|
|
return FALSE;
|
|
}
|
|
if (is_raw_dev(file->addr))
|
|
{
|
|
ESTABLISH_RET(iob_io_error1, FALSE);
|
|
} else
|
|
{
|
|
FSTAT_FILE(db_fd, &stat_buf, fstat_res);
|
|
if (-1 != fstat_res)
|
|
if (gtm_set_group_and_perm(&stat_buf, &group_id, &perm, PERM_FILE, &pdd) < 0)
|
|
{
|
|
send_msg(VARLSTCNT(6+PERMGENDIAG_ARG_COUNT)
|
|
ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("backup file"),
|
|
RTS_ERROR_STRING(((unix_db_info *)
|
|
(gv_cur_region->dyn.addr->file_cntl->file_info))->fn),
|
|
PERMGENDIAG_ARGS(pdd));
|
|
gtm_putmsg(VARLSTCNT(6+PERMGENDIAG_ARG_COUNT)
|
|
ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("backup file"),
|
|
RTS_ERROR_STRING(((unix_db_info *)
|
|
(gv_cur_region->dyn.addr->file_cntl->file_info))->fn),
|
|
PERMGENDIAG_ARGS(pdd));
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
/* setup new group and permissions if indicated by the security rules. */
|
|
if ((-1 == fstat_res) || (-1 == FCHMOD(backup->fd, perm))
|
|
|| ((-1 != group_id) && (-1 == fchown(backup->fd, -1, group_id))))
|
|
{
|
|
PERROR("fchmod/fchown error: ");
|
|
util_out_print("ERROR: Cannot access incremental backup file !AD.",
|
|
TRUE, file->len, file->addr);
|
|
util_out_print("WARNING: Backup file !AD is not valid.", TRUE, file->len, file->addr);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
memcpy(incbackupfile, file->addr, file->len);
|
|
incbackupfile[file->len] = 0;
|
|
ESTABLISH_RET(iob_io_error2, FALSE);
|
|
}
|
|
break;
|
|
case backup_to_exec:
|
|
pipe_child = 0;
|
|
common_write = exec_write;
|
|
backup = (BFILE *)malloc(SIZEOF(BFILE));
|
|
backup->blksiz = DISK_BLOCK_SIZE;
|
|
backup->remaining = 0; /* number of zeros to be added in the end, just use this field */
|
|
if (0 > (backup->fd = gtm_pipe(file->addr, output_to_comm)))
|
|
{
|
|
util_out_print("ERROR: Cannot create backup pipe.", TRUE);
|
|
util_out_print("WARNING: backup !AD is not valid.", TRUE, file->len, file->addr);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case backup_to_tcp:
|
|
common_write = tcp_write;
|
|
iotcp_fillroutine();
|
|
backup = (BFILE *)malloc(SIZEOF(BFILE));
|
|
backup->blksiz = DISK_BLOCK_SIZE;
|
|
backup->remaining = 0; /* number of zeros to be added in the end, just use this field */
|
|
/* parse it first */
|
|
switch (match = SSCANF(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 to backup through a TCP connection.",
|
|
TRUE);
|
|
return FALSE;
|
|
}
|
|
assert(SIZEOF(timeout) == SIZEOF(int));
|
|
if ((0 == cli_get_int("NETTIMEOUT", (int4 *)&timeout)) || (0 > timeout))
|
|
timeout = DEFAULT_BKRS_TIMEOUT;
|
|
if (0 > (backup->fd = tcp_open(addr, port, timeout, FALSE)))
|
|
{
|
|
util_out_print("ERROR: Cannot open tcp connection due to the above error.", TRUE);
|
|
util_out_print("WARNING: Backup !AD is not valid.", TRUE, file->len, file->addr);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default :
|
|
util_out_print("ERROR: Backup format not supported.", TRUE);
|
|
util_out_print("WARNING: Backup not valid.", TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* ============================= write inc_header =========================================== */
|
|
outbuf = (inc_header*)malloc(SIZEOF(inc_header));
|
|
if (header->is_encrypted)
|
|
MEMCPY_LIT(&outbuf->label[0], INC_HEADER_LABEL);
|
|
else
|
|
MEMCPY_LIT(&outbuf->label[0], V5_INC_HEADER_LABEL);
|
|
stringpool.free = stringpool.base;
|
|
op_horolog(&val);
|
|
stringpool.free = stringpool.base;
|
|
op_fnzdate(&val, (mval *)&mu_bin_datefmt, (mval *)&literal_null, (mval *)&literal_null, &val);
|
|
memcpy(&outbuf->date[0], val.str.addr, val.str.len);
|
|
memcpy(&outbuf->reg[0], gv_cur_region->rname, MAX_RN_LEN);
|
|
outbuf->start_tn = list->tn;
|
|
outbuf->end_tn = header->trans_hist.curr_tn;
|
|
outbuf->db_total_blks = header->trans_hist.total_blks;
|
|
outbuf->blk_size = header->blk_size;
|
|
outbuf->blks_to_upgrd = header->blks_to_upgrd;
|
|
/* is_encrypted field of incremental header is defined for all platforms.
|
|
* Hence set the is_encrypted field unconditionally.
|
|
*/
|
|
outbuf->is_encrypted = header->is_encrypted;
|
|
util_out_print("MUPIP backup of database file !AD to !AD", TRUE, DB_LEN_STR(gv_cur_region), file->len, file->addr);
|
|
COMMON_WRITE(backup, (char *)outbuf, SIZEOF(inc_header));
|
|
# ifdef GTM_CRYPT
|
|
if (header->is_encrypted)
|
|
COMMON_WRITE(backup, (char *)header->encryption_hash, GTMCRYPT_HASH_LEN);
|
|
# endif
|
|
free(outbuf);
|
|
|
|
if (mu_ctrly_occurred || mu_ctrlc_occurred)
|
|
{
|
|
util_out_print("WARNING: DB file !AD backup aborted, file !AD not valid", TRUE,
|
|
DB_LEN_STR(gv_cur_region), file->len, file->addr);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
|
|
/* ============================ read/write appropriate blocks =============================== */
|
|
bsize = header->blk_size;
|
|
gds_ratio = bsize / DISK_BLOCK_SIZE;
|
|
blks_per_buff = BACKUP_READ_SIZE / bsize; /* Worse case holds one block */
|
|
read_size = blks_per_buff * bsize;
|
|
outsize = SIZEOF(muinc_blk_hdr) + bsize;
|
|
outptr = (char_ptr_t)malloc(MAX(outsize, mubmaxblk));
|
|
sblkh_p = (muinc_blk_hdr_ptr_t)outptr;
|
|
data_ptr = (char_ptr_t)(sblkh_p + 1);
|
|
bp = (blk_hdr_ptr_t)mubbuf;
|
|
bm_blk_buff = (uchar_ptr_t)malloc(SIZEOF(blk_hdr) + (BLKS_PER_LMAP * BML_BITS_PER_BLK / BITS_PER_UCHAR));
|
|
save_blks = 0;
|
|
memset(sblkh_p, 0, SIZEOF(*sblkh_p));
|
|
sblkh_p->use.bkup.ondsk_blkver = GDSNOVER;
|
|
|
|
if (-1 == lseek(db_fd, (off_t)(header->start_vbn - 1) * DISK_BLOCK_SIZE, SEEK_SET))
|
|
{
|
|
PERROR("fseek error: ");
|
|
util_out_print("Error reading from database file !AD.", TRUE, DB_LEN_STR(gv_cur_region));
|
|
util_out_print("WARNING: backup file !AD is not valid.", TRUE, DB_LEN_STR(gv_cur_region));
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
DEBUG_INCBKUP_ONLY(blks_this_lmap = 0);
|
|
if (cs_addrs->nl->onln_rlbk_pid)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_DBROLLEDBACK);
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
for (blk_num_base = 0; blk_num_base < header->trans_hist.total_blks; blk_num_base += blks_per_buff)
|
|
{
|
|
if (online && (0 != cs_addrs->shmpool_buffer->failed))
|
|
break;
|
|
if (header->trans_hist.total_blks - blk_num_base < blks_per_buff)
|
|
{
|
|
blks_per_buff = header->trans_hist.total_blks - blk_num_base;
|
|
read_size = blks_per_buff * bsize;
|
|
}
|
|
DOREADRC(db_fd, bp, read_size, status);
|
|
if (0 != status)
|
|
{
|
|
PERROR("read error: ");
|
|
util_out_print("Error reading from database file !AD.", TRUE, DB_LEN_STR(gv_cur_region));
|
|
util_out_print("WARNING: backup file !AD is not valid.", TRUE, DB_LEN_STR(gv_cur_region));
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
bptr = (blk_hdr *)bp;
|
|
/* The blocks we back up will be whatever version they are. There is no implicit conversion in this
|
|
* part of the backup/restore. Since we aren't even looking at the blocks (and indeed some of these blocks
|
|
* could potentially contain unintialized garbage data), we set the block version to GDSNOVER to signal
|
|
* that the block version is unknown. The above applies to "regular" blocks but not to bitmap blocks which
|
|
* we know are initialized. Because we have to read the bitmap blocks, they will be converted as necessary.
|
|
*/
|
|
for (i = 0; i < blks_per_buff; i++, bptr = (blk_hdr *)((char *)bptr + bsize))
|
|
{
|
|
blk_num = blk_num_base + i;
|
|
if (mu_ctrly_occurred || mu_ctrlc_occurred)
|
|
{
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
util_out_print("WARNING: DB file !AD backup aborted, file !AD not valid", TRUE,
|
|
DB_LEN_STR(gv_cur_region), file->len, file->addr);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
if (cs_addrs->nl->onln_rlbk_pid)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_DBROLLEDBACK);
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
/* Before we check if this block needs backing up, check if this is a new bitmap block or not. If it is,
|
|
* we can fall through and back it up as normal. But if this is NOT a bitmap block, use the
|
|
* existing bitmap to determine if this block has ever been allocated or not. If not, we don't want to
|
|
* even look at this block. It could be uninitialized which will just make things run slower if we
|
|
* go to read it and back it up.
|
|
*/
|
|
if (0 != ((BLKS_PER_LMAP - 1) & blk_num))
|
|
{ /* Not a local bitmap block */
|
|
if (!gvcst_blk_ever_allocated(bm_blk_buff + SIZEOF(blk_hdr),
|
|
((blk_num * BML_BITS_PER_BLK)
|
|
% (BLKS_PER_LMAP * BML_BITS_PER_BLK))))
|
|
continue; /* Bypass never-set blocks to avoid conversion problems */
|
|
is_bitmap_blk = FALSE;
|
|
if (SIZEOF(v15_blk_hdr) <= (blk_bsiz = ((v15_blk_hdr_ptr_t)bptr)->bsiz))
|
|
{ /* We have either a V4 block or uninitialized garbage */
|
|
if (blk_bsiz > bsize)
|
|
/* This is not a valid V4 block so ignore it */
|
|
continue;
|
|
blk_tn = ((v15_blk_hdr_ptr_t)bptr)->tn;
|
|
} else
|
|
{ /* Assume V5 block */
|
|
if ((blk_bsiz = bptr->bsiz) > bsize)
|
|
/* Not a valid V5 block either */
|
|
continue;
|
|
blk_tn = bptr->tn;
|
|
}
|
|
} else
|
|
{ /* This is a bitmap block so save it into our bitmap block buffer. It is used as the
|
|
* basis of whether or not we have to process a given block or not. We process allocated and
|
|
* recycled blocks leaving free (never used) blocks alone as they have no data worth saving.
|
|
* But after saving it, upgrade it to the current format if necessary.
|
|
*/
|
|
#ifdef DEBUG_INCBKUP
|
|
if (0 != blk_num) /* Skip first time thorugh loop */
|
|
{
|
|
PRINTF("Dumped %d blks from lcl bitmap blk 0x%016lx\n", blks_this_lmap,
|
|
(blk_num - BLKS_PER_LMAP));
|
|
blks_this_lmap = 0;
|
|
}
|
|
#endif
|
|
is_bitmap_blk = TRUE;
|
|
memcpy(bm_blk_buff, bptr, BM_SIZE(header->bplmap));
|
|
if (SIZEOF(v15_blk_hdr) <= ((v15_blk_hdr_ptr_t)bm_blk_buff)->bsiz)
|
|
{ /* This is a V4 format block -- needs upgrading */
|
|
status = gds_blk_upgrade(bm_blk_buff, bm_blk_buff, bsize, &dummy_odbv);
|
|
if (SS_NORMAL != status)
|
|
{
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
util_out_print("Error: Block 0x!XL is too large for automatic upgrade", TRUE,
|
|
blk_num);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
}
|
|
assert(BM_SIZE(header->bplmap) == ((blk_hdr_ptr_t)bm_blk_buff)->bsiz);
|
|
assert(LCL_MAP_LEVL == ((blk_hdr_ptr_t)bm_blk_buff)->levl);
|
|
assert(gvcst_blk_is_allocated(bm_blk_buff + SIZEOF(blk_hdr),
|
|
((blk_num * BML_BITS_PER_BLK)
|
|
% (BLKS_PER_LMAP * BML_BITS_PER_BLK))));
|
|
blk_bsiz = BM_SIZE(header->bplmap);
|
|
blk_tn = ((blk_hdr_ptr_t)bm_blk_buff)->tn;
|
|
}
|
|
/* The conditions for backing up a block or ignoring it (in order of evaluation):
|
|
* 1) If blk is larger than size of db at time backup was initiated, we ignore the block.
|
|
* 2) Always backup blocks 0, 1, and 2 as these are the only blocks that can contain data
|
|
* and still have a transaction number of 0.
|
|
* 3) For bitmap blocks, if blks_to_upgrd != 0 and the TN is 0 and the block number >=
|
|
* last_blk_at_last_bkup, then backup the block. This way we get the correct version of
|
|
* the bitmap block in the restore (otherwise have no clue what version to create them in
|
|
* as bitmaps are created with a TN of 0 when before image journaling is enabled).
|
|
* Given the possibility of a db file truncate occurring between backups, it is now possible
|
|
* for gdsfilext to create a new bitmap block such that blk_num < list->last_blk_at_last_bkup.
|
|
* This means the previous state (at the last backup) of a bitmap block with tn=0 is
|
|
* indeterminate: it might have held data and it might have had a different version. So
|
|
* backup all bitmap blocks with tn=0.
|
|
* One concern is that incremental backup files can end up somewhat larger (due to backing
|
|
* up extra bitmap blocks), even if no truncate occurred between backups. Users can zip their
|
|
* incremental backup files which should deflate almost all of the tn=0 bitmap blocks.
|
|
* Future enhancement: Instead of backing up bitmap blocks with tn=0, make a list of the
|
|
* the blk_nums and versions of all such blocks. The bitmap blocks can be created in
|
|
* mupip_restore from that information alone.
|
|
* 4) If the block TN is below our TN threshold, ignore the block.
|
|
* 5) Else if none of the above conditions, backup the block.
|
|
*/
|
|
if (online && (header->trans_hist.curr_tn <= blk_tn))
|
|
backup_this_blk = FALSE;
|
|
else if ((3 > blk_num) || (is_bitmap_blk && (0 != header->blks_to_upgrd) && ((trans_num)0 == blk_tn)
|
|
&& (blk_num >= list->last_blk_at_last_bkup)))
|
|
backup_this_blk = TRUE;
|
|
# ifdef GTM_TRUNCATE
|
|
else if (is_bitmap_blk && ((trans_num)0 == blk_tn))
|
|
backup_this_blk = TRUE;
|
|
# endif
|
|
else if ((blk_tn < list->tn))
|
|
backup_this_blk = FALSE;
|
|
else
|
|
backup_this_blk = TRUE;
|
|
if (!backup_this_blk)
|
|
{
|
|
if (online)
|
|
cs_addrs->nl->nbb = blk_num;
|
|
continue; /* not applicable */
|
|
}
|
|
DEBUG_INCBKUP_ONLY(++blks_this_lmap);
|
|
sblkh_p->blkid = blk_num;
|
|
memcpy(data_ptr, bptr, blk_bsiz);
|
|
sblkh_p->valid_data = TRUE; /* Validation marker */
|
|
DEBUG_INCBKUP_ONLY(PRINTF("DEBUG - write block: blk_num = 0x%08lx, start = 0x%016lx, size = 0x%x \n",
|
|
blk_num, backup_write_offset, outsize);)
|
|
COMMON_WRITE(backup, outptr, outsize);
|
|
if (online)
|
|
{
|
|
if (0 != cs_addrs->shmpool_buffer->failed)
|
|
break;
|
|
cs_addrs->nl->nbb = blk_num;
|
|
}
|
|
save_blks++;
|
|
}
|
|
DEBUG_INCBKUP_ONLY(PRINTF("Dumped %d blks from lcl bitmap blk 0x%016lx\n", blks_this_lmap,
|
|
(blk_num & ~(BLKS_PER_LMAP - 1))));
|
|
}
|
|
if (cs_addrs->nl->onln_rlbk_pid)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_DBROLLEDBACK);
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
/* After this point, if an Online Rollback is detected, the BACKUP will NOT e affected as all the before images it needs
|
|
* is already written by GT.M in the temporary file and the resulting BACKUP will be valid*/
|
|
/* ============================ write saved information for online backup ========================== */
|
|
if (online && (0 == cs_addrs->shmpool_buffer->failed))
|
|
{
|
|
cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS;
|
|
/* By getting crit here, we ensure that there is no process still in transaction logic that sees
|
|
* (nbb != BACKUP_NOT_IN_PRORESS). After rel_crit(), any process that enters transaction logic will
|
|
* see (nbb == BACKUP_NOT_IN_PRORESS) because we just set it to that value. At this point, backup
|
|
* buffer is complete and there will not be any more new entries in the backup buffer until the next
|
|
* backup.
|
|
*/
|
|
assert(!cs_addrs->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */
|
|
grab_crit(gv_cur_region);
|
|
assert(cs_data == cs_addrs->hdr);
|
|
if (dba_bg == cs_data->acc_meth)
|
|
{ /* Now that we have crit, wait for any pending phase2 updates to finish. Since phase2 updates happen
|
|
* outside of crit, we dont want them to keep writing to the backup temporary file even after the
|
|
* backup is complete and the temporary file has been deleted.
|
|
*/
|
|
if (cs_addrs->nl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(cs_addrs, NULL))
|
|
{
|
|
assert(FALSE);
|
|
gtm_putmsg(VARLSTCNT(7) ERR_COMMITWAITSTUCK, 5, process_id, 1,
|
|
cs_addrs->nl->wcs_phase2_commit_pidcnt, DB_LEN_STR(gv_cur_region));
|
|
rel_crit(gv_cur_region);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
}
|
|
if (debug_mupip)
|
|
{
|
|
util_out_print("MUPIP INFO: Current Transaction # at end of backup is 0x!16@XQ", TRUE,
|
|
&cs_data->trans_hist.curr_tn);
|
|
}
|
|
rel_crit(gv_cur_region);
|
|
counter = 0;
|
|
while (0 != cs_addrs->shmpool_buffer->backup_cnt)
|
|
{
|
|
backup_buffer_flush(gv_cur_region);
|
|
if (++counter > MAX_BACKUP_FLUSH_TRY)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_BCKUPBUFLUSH);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
if (counter & 0xF)
|
|
wcs_sleep(counter);
|
|
else
|
|
{ /* Force shmpool recovery to see if it can find the lost blocks */
|
|
if (!shmpool_lock_hdr(gv_cur_region))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(9) ERR_DBCCERR, 2, REG_LEN_STR(gv_cur_region),
|
|
ERR_ERRCALL, 3, CALLFROM);
|
|
assert(FALSE);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
shmpool_abandoned_blk_chk(gv_cur_region, TRUE);
|
|
shmpool_unlock_hdr(gv_cur_region);
|
|
}
|
|
}
|
|
if (-1 == lseek(list->backup_fd, 0, SEEK_SET))
|
|
{
|
|
PERROR("lseek error : ");
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
copysize = BACKUP_READ_SIZE;
|
|
for (copied = 0; copied < cs_addrs->shmpool_buffer->dskaddr; copied += copysize)
|
|
{
|
|
if (cs_addrs->shmpool_buffer->dskaddr < copied + copysize)
|
|
copysize = (size_t)(cs_addrs->shmpool_buffer->dskaddr - copied);
|
|
DOREADRC(list->backup_fd, mubbuf, copysize, status);
|
|
if (0 != status)
|
|
{
|
|
PERROR("read error : ");
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
COMMON_WRITE(backup, (char *)mubbuf, copysize);
|
|
}
|
|
}
|
|
/* Write one last (zero-filled) block into this file that designates the end of the blocks */
|
|
memset(outptr, 0, SIZEOF(muinc_blk_hdr) + bsize);
|
|
COMMON_WRITE(backup, outptr, outsize);
|
|
|
|
/* ============================= write end_msg and fileheader =============================== */
|
|
if ((!online) || (0 == cs_addrs->shmpool_buffer->failed))
|
|
{ /* Write a secondary end-of-block list marker used for further validation on restore */
|
|
rsize = SIZEOF(END_MSG) + SIZEOF(int4);
|
|
COMMON_WRITE(backup, (char *)&rsize, SIZEOF(int4));
|
|
COMMON_WRITE(backup, END_MSG, SIZEOF(END_MSG));
|
|
|
|
ptr1 = (uchar_ptr_t)header;
|
|
ptr1_top = ptr1 + ROUND_UP(SIZEOF(sgmnt_data), DISK_BLOCK_SIZE);
|
|
for ( ; ptr1 < ptr1_top; ptr1 += size1)
|
|
{
|
|
if ((size1 = (int4)(ptr1_top - ptr1)) > mubmaxblk)
|
|
size1 = (mubmaxblk / DISK_BLOCK_SIZE) * DISK_BLOCK_SIZE;
|
|
size1 += SIZEOF(int4);
|
|
COMMON_WRITE(backup, (char *)&size1, SIZEOF(int4));
|
|
size1 -= SIZEOF(int4);
|
|
COMMON_WRITE(backup, (char *)ptr1, size1);
|
|
}
|
|
rsize = SIZEOF(HDR_MSG) + SIZEOF(int4);
|
|
COMMON_WRITE(backup, (char *)&rsize, SIZEOF(int4));
|
|
COMMON_WRITE(backup, HDR_MSG, SIZEOF(HDR_MSG));
|
|
ptr1 = MM_ADDR(header);
|
|
ptr1_top = ptr1 + ROUND_UP(MASTER_MAP_SIZE(header), DISK_BLOCK_SIZE);
|
|
for ( ; ptr1 < ptr1_top ; ptr1 += size1)
|
|
{
|
|
if ((size1 = (int4)(ptr1_top - ptr1)) > mubmaxblk)
|
|
size1 = (mubmaxblk / DISK_BLOCK_SIZE) * DISK_BLOCK_SIZE;
|
|
size1 += SIZEOF(int4);
|
|
COMMON_WRITE(backup, (char *)&size1, SIZEOF(int4));
|
|
size1 -= SIZEOF(int4);
|
|
COMMON_WRITE(backup, (char *)ptr1, size1);
|
|
}
|
|
rsize = SIZEOF(MAP_MSG) + SIZEOF(int4);
|
|
COMMON_WRITE(backup, (char *)&rsize, SIZEOF(int4));
|
|
COMMON_WRITE(backup, MAP_MSG, SIZEOF(MAP_MSG));
|
|
rsize = 0;
|
|
COMMON_WRITE(backup, (char *)&rsize, SIZEOF(rsize));
|
|
}
|
|
|
|
/* ========================== close backup destination ======================================== */
|
|
switch(list->backup_to)
|
|
{
|
|
case backup_to_file:
|
|
REVERT;
|
|
iob_close(backup);
|
|
backup = NULL;
|
|
break;
|
|
case backup_to_exec:
|
|
if (0 != backup->remaining)
|
|
{
|
|
assert(backup->blksiz > backup->remaining);
|
|
memset(outptr, 0, backup->blksiz - backup->remaining);
|
|
COMMON_WRITE(backup, outptr, backup->blksiz - backup->remaining);
|
|
}
|
|
CLOSEFILE_RESET(backup->fd, rc); /* resets "backup->fd" to FD_INVALID */
|
|
/* needs to wait till the child dies, because of the rundown issues */
|
|
if ((pipe_child > 0) && (FALSE != is_proc_alive(pipe_child, 0)))
|
|
{
|
|
pid_t waitpid_res;
|
|
|
|
WAITPID(pipe_child, (int *)&status, 0, waitpid_res);
|
|
}
|
|
break;
|
|
case backup_to_tcp:
|
|
if (0 != backup->remaining)
|
|
{
|
|
assert(backup->blksiz > backup->remaining);
|
|
memset(outptr, 0, backup->blksiz - backup->remaining);
|
|
COMMON_WRITE(backup, outptr, backup->blksiz - backup->remaining);
|
|
}
|
|
CLOSEFILE_RESET(backup->fd, rc); /* resets "backup->fd" to FD_INVALID */
|
|
break;
|
|
}
|
|
|
|
/* ============================ output and return =========================================== */
|
|
free(outptr);
|
|
free(bm_blk_buff);
|
|
if (online && (0 != cs_addrs->shmpool_buffer->failed))
|
|
{
|
|
util_out_print("Process !UL encountered the following error.", TRUE,
|
|
cs_addrs->shmpool_buffer->failed);
|
|
if (0 != cs_addrs->shmpool_buffer->backup_errno)
|
|
gtm_putmsg(VARLSTCNT(1) cs_addrs->shmpool_buffer->backup_errno);
|
|
util_out_print("!AD, backup for DB file !AD, is not valid.", TRUE,
|
|
file->len, file->addr, DB_LEN_STR(gv_cur_region));
|
|
} else
|
|
{
|
|
util_out_print("DB file !AD incrementally backed up in file !AD", TRUE,
|
|
DB_LEN_STR(gv_cur_region), file->len, file->addr);
|
|
util_out_print("!UL blocks saved.", TRUE, save_blks);
|
|
util_out_print("Transactions from 0x!16@XQ to 0x!16@XQ are backed up.", TRUE,
|
|
&list->tn, &header->trans_hist.curr_tn);
|
|
cs_addrs->hdr->last_inc_backup = header->trans_hist.curr_tn;
|
|
cs_addrs->hdr->last_inc_bkup_last_blk = (block_id)header->trans_hist.total_blks;
|
|
if (record)
|
|
{
|
|
cs_addrs->hdr->last_rec_backup = header->trans_hist.curr_tn;
|
|
cs_addrs->hdr->last_com_bkup_last_blk = (block_id)header->trans_hist.total_blks;
|
|
}
|
|
file_backed_up = TRUE;
|
|
return TRUE;
|
|
}
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
|
|
void exec_write(BFILE *bf, char *buf, int nbytes)
|
|
{
|
|
int nwritten;
|
|
uint4 status;
|
|
pid_t waitpid_res;
|
|
int rc;
|
|
|
|
DOWRITERL(bf->fd, buf, nbytes, nwritten);
|
|
|
|
bf->remaining += nwritten;
|
|
bf->remaining %= bf->blksiz;
|
|
|
|
if ((nwritten < nbytes) && (-1 == nwritten))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) errno);
|
|
CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */
|
|
if ((pipe_child > 0) && (FALSE != is_proc_alive(pipe_child, 0)))
|
|
WAITPID(pipe_child, (int *)&status, 0, waitpid_res);
|
|
backup_write_errno = errno;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void tcp_write(BFILE *bf, char *buf, int nbytes)
|
|
{
|
|
int nwritten, iostatus;
|
|
int send_retry;
|
|
int rc;
|
|
|
|
nwritten = 0;
|
|
send_retry = 5;
|
|
do
|
|
{
|
|
if (-1 != (iostatus = tcp_routines.aa_send(bf->fd, buf + nwritten, nbytes - nwritten, 0)))
|
|
{
|
|
nwritten += iostatus;
|
|
if (nwritten == nbytes)
|
|
break;
|
|
} else
|
|
break;
|
|
} while (0 < send_retry--);
|
|
|
|
bf->remaining += nwritten;
|
|
bf->remaining %= bf->blksiz;
|
|
|
|
if ((nwritten != nbytes) && (-1 == iostatus))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) errno);
|
|
CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */
|
|
backup_write_errno = errno;
|
|
}
|
|
return;
|
|
}
|
|
|
|
CONDITION_HANDLER(iob_io_error1)
|
|
{
|
|
int dummy1, dummy2;
|
|
char s[80];
|
|
char *fgets_res;
|
|
|
|
START_CH;
|
|
if (SIGNAL == ERR_IOEOF)
|
|
{
|
|
PRINTF("End of media reached, please mount next volume and press Enter: ");
|
|
FGETS(s, 79, stdin, fgets_res);
|
|
util_out_print(0, 2, 0); /* clear error message */
|
|
if (mu_ctrly_occurred || mu_ctrlc_occurred)
|
|
{
|
|
util_out_print("WARNING: DB file backup aborted, backup file is not valid.", TRUE);
|
|
UNWIND(dummy1, dummy2);
|
|
}
|
|
CONTINUE;
|
|
}
|
|
PRN_ERROR;
|
|
UNWIND(dummy1, dummy2);
|
|
}
|
|
|
|
CONDITION_HANDLER(iob_io_error2)
|
|
{
|
|
int dummy1, dummy2;
|
|
char s[80];
|
|
|
|
START_CH;
|
|
PRN_ERROR;
|
|
if (!debug_mupip)
|
|
UNLINK(incbackupfile);
|
|
UNWIND(dummy1, dummy2);
|
|
}
|