fis-gtm/sr_unix/mubinccpy.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);
}