496 lines
17 KiB
C
496 lines
17 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2010 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. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_fcntl.h"
|
|
#include "gtm_stdlib.h"
|
|
#include "gtm_unistd.h"
|
|
#include <errno.h>
|
|
#include "gtm_stat.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_string.h"
|
|
#include "gtm_permissions.h"
|
|
|
|
#ifdef __MVS__
|
|
#include "gtm_zos_io.h"
|
|
#endif
|
|
#include "gdsroot.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsblk.h"
|
|
#include "gdsfhead.h"
|
|
#include "filestruct.h"
|
|
#include "gtmio.h"
|
|
#include "mupipbckup.h"
|
|
#include "copy.h"
|
|
#include "eintr_wrappers.h"
|
|
#include "sleep_cnt.h"
|
|
#include "util.h"
|
|
#include "gtmmsg.h"
|
|
#include "wcs_sleep.h"
|
|
#include "gtm_file_stat.h"
|
|
#include "gtm_tempnam.h"
|
|
#include "gds_blk_downgrade.h"
|
|
#include "shmpool.h"
|
|
#include "wcs_phase2_commit_wait.h"
|
|
|
|
#define TMPDIR_ACCESS_MODE R_OK | W_OK | X_OK
|
|
#define COMMAND_ARRAY_SIZE 1024
|
|
#define MV_CMD "mv "
|
|
#define CP_CMD "cp "
|
|
|
|
#define FREE_COMMAND_STR_IF_NEEDED \
|
|
{ \
|
|
if (command != &cmdarray[0]) \
|
|
{ \
|
|
free(command); \
|
|
command = &cmdarray[0]; \
|
|
} \
|
|
}
|
|
|
|
#define CLEANUP_AND_RETURN_FALSE \
|
|
{ \
|
|
int rc; \
|
|
\
|
|
if (FD_INVALID != backup_fd) \
|
|
CLOSEFILE_RESET(backup_fd, rc); /* resets "backup_fd" to FD_INVALID */ \
|
|
if (!debug_mupip) \
|
|
UNLINK(tempfilename); \
|
|
return FALSE; \
|
|
}
|
|
|
|
GBLREF bool file_backed_up;
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF bool record;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF bool online;
|
|
GBLREF uint4 process_id;
|
|
GBLREF boolean_t debug_mupip;
|
|
|
|
bool mubfilcpy (backup_reg_list *list)
|
|
{
|
|
mstr *file, tempfile;
|
|
unsigned char cmdarray[COMMAND_ARRAY_SIZE], *command = &cmdarray[0];
|
|
sgmnt_data_ptr_t header_cpy;
|
|
int4 backup_fd = FD_INVALID, size, vbn, counter, hdrsize, rsize, ntries;
|
|
ssize_t status;
|
|
int4 adjust, blk_num, cmdlen, rv, save_errno, temp, tempfilelen, tmplen;
|
|
struct stat stat_buf;
|
|
off_t filesize, handled, offset;
|
|
char *inbuf, *zero_blk, *ptr, *errptr;
|
|
boolean_t done;
|
|
char tempfilename[MAX_FN_LEN + 1], tempdir[MAX_FN_LEN], prefix[MAX_FN_LEN];
|
|
int fstat_res;
|
|
uint4 ustatus;
|
|
muinc_blk_hdr_ptr_t sblkh_p;
|
|
ZOS_ONLY(int realfiletag;)
|
|
int group_id;
|
|
int perm;
|
|
|
|
error_def(ERR_BCKUPBUFLUSH);
|
|
error_def(ERR_COMMITWAITSTUCK);
|
|
error_def(ERR_DBCCERR);
|
|
error_def(ERR_ERRCALL);
|
|
error_def(ERR_TEXT);
|
|
error_def(ERR_TMPFILENOCRE);
|
|
ZOS_ONLY(error_def(ERR_BADTAG);)
|
|
|
|
file = &(list->backup_file);
|
|
file->addr[file->len] = '\0';
|
|
header_cpy = list->backup_hdr;
|
|
hdrsize = (int4)ROUND_UP(SIZEOF_FILE_HDR(header_cpy), DISK_BLOCK_SIZE);
|
|
|
|
/* the temporary file should be located in the destination directory */
|
|
ptr = file->addr + file->len - 1;
|
|
while (('/' != *ptr) && (ptr > file->addr))
|
|
ptr--;
|
|
if (ptr > file->addr)
|
|
{
|
|
memcpy(tempdir, file->addr, ptr - file->addr + 1);
|
|
tempdir[ptr - file->addr + 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
tempdir[0] = '.';
|
|
tempdir[1] = '\0';
|
|
}
|
|
memset(prefix, 0, MAX_FN_LEN);
|
|
memcpy(prefix, gv_cur_region->rname, gv_cur_region->rname_len);
|
|
SPRINTF(&prefix[gv_cur_region->rname_len], "_%d_", process_id);
|
|
|
|
/* verify that we have access to the temporary directory to avoid /tmp */
|
|
if (0 != ACCESS(tempdir, TMPDIR_ACCESS_MODE))
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("access : !AZ", TRUE, errptr);
|
|
if (online)
|
|
cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS;
|
|
util_out_print("ERROR: Do NOT have full access to directory !AD", TRUE,
|
|
LEN_AND_STR(tempdir));
|
|
return FALSE;
|
|
}
|
|
|
|
/* make this cp a two step process ==> cp followed by mv */
|
|
ntries = 0;
|
|
fstat_res = FILE_NOT_FOUND;
|
|
/* do go into the loop for the first time, irrespective of fstat_res*/
|
|
while ((FILE_NOT_FOUND != fstat_res) || (!ntries))
|
|
{
|
|
gtm_tempnam(tempdir, prefix, tempfilename);
|
|
tempfile.addr = tempfilename;
|
|
tempfile.len = STRLEN(tempfilename);
|
|
if ((FILE_STAT_ERROR == (fstat_res = gtm_file_stat(&tempfile, NULL, NULL, FALSE, &ustatus))) ||
|
|
(ntries > MAX_TEMP_OPEN_TRY))
|
|
{
|
|
if (FILE_STAT_ERROR != fstat_res)
|
|
gtm_putmsg(VARLSTCNT(8) ERR_TMPFILENOCRE, 2, tempfile.len, tempfile.addr,
|
|
ERR_TEXT, 2, LEN_AND_LIT("Tried a maximum number of times, clean-up temporary files " \
|
|
"in backup directory and retry."));
|
|
else
|
|
gtm_putmsg(VARLSTCNT(5) ERR_TMPFILENOCRE, 2, tempfile.len, tempfile.addr, ustatus);
|
|
return FALSE;
|
|
}
|
|
ntries++;
|
|
}
|
|
/* Calculate total line length for "cp" command. If cannot fit in local variable array, malloc space */
|
|
tmplen = gv_cur_region->dyn.addr->fname_len;
|
|
tempfilelen = STRLEN(tempfilename);
|
|
cmdlen = STR_LIT_LEN(CP_CMD) + tmplen + 1 /* space */ + tempfilelen + 1 /* terminating NULL byte */;
|
|
if (cmdlen > SIZEOF(cmdarray))
|
|
command = malloc(cmdlen); /* allocate memory and use that instead of local array "cmdarray" */
|
|
MEMCPY_LIT(command, CP_CMD);
|
|
cmdlen = STR_LIT_LEN(CP_CMD);
|
|
memcpy(&command[cmdlen], gv_cur_region->dyn.addr->fname, tmplen);
|
|
cmdlen += tmplen;
|
|
command[cmdlen++] = ' ';
|
|
memcpy(&command[cmdlen], tempfilename, tempfilelen);
|
|
cmdlen += tempfilelen;
|
|
command[cmdlen] = 0;
|
|
if (debug_mupip)
|
|
util_out_print("!/MUPIP INFO: !AD", TRUE, cmdlen, command);
|
|
if (0 != (rv = SYSTEM((char *)command)))
|
|
{
|
|
if (-1 == rv)
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("system : !AZ", TRUE, errptr);
|
|
}
|
|
if (online)
|
|
cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS;
|
|
util_out_print("Error doing !AD", TRUE, cmdlen, command);
|
|
FREE_COMMAND_STR_IF_NEEDED;
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
FREE_COMMAND_STR_IF_NEEDED;
|
|
assert(command == &cmdarray[0]);
|
|
|
|
/* give temporary files the group and permissions as other shared resources - like journal files */
|
|
OPENFILE(tempfilename, O_RDWR, backup_fd);
|
|
if (FD_INVALID == backup_fd)
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("open : !AZ", TRUE, errptr);
|
|
if (online)
|
|
cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS;
|
|
util_out_print("Error opening 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;
|
|
}
|
|
FSTAT_FILE(((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fd, &stat_buf, fstat_res);
|
|
if (-1 != fstat_res)
|
|
gtm_set_group_and_perm(&stat_buf, &group_id, &perm, (0770 & stat_buf.st_mode));
|
|
/* setup new group and permissions if indicated by the security rules.
|
|
* Use 0770 anded with current mode for the new mode if if masked permission selected.
|
|
*/
|
|
if ((-1 == fstat_res) || (-1 == FCHMOD(backup_fd, perm)) || ((-1 != group_id) && (-1 == fchown(backup_fd, -1, group_id))))
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("system : !AZ", TRUE, errptr);
|
|
if (online)
|
|
cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS;
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
#if defined(__MVS__)
|
|
if (-1 == gtm_zos_tag_to_policy(backup_fd, TAG_BINARY, &realfiletag))
|
|
TAG_POLICY_GTM_PUTMSG( tempfilename, realfiletag, TAG_BINARY, errno);
|
|
#endif
|
|
|
|
if (online)
|
|
{
|
|
cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS;
|
|
|
|
/* if there has been an extend, truncate it */
|
|
FSTAT_FILE(backup_fd, &stat_buf, fstat_res);
|
|
if (-1 == fstat_res)
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("fstat : !AZ", TRUE, errptr);
|
|
util_out_print("Error obtaining status of 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;
|
|
}
|
|
filesize = header_cpy->start_vbn * DISK_BLOCK_SIZE +
|
|
(off_t)header_cpy->trans_hist.total_blks * header_cpy->blk_size;
|
|
if (filesize != stat_buf.st_size)
|
|
{ /* file has been extended, so truncate it and set the end of database block */
|
|
int ftruncate_res;
|
|
FTRUNCATE(backup_fd, filesize, ftruncate_res);
|
|
if (-1 == ftruncate_res)
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("ftruncate : !AZ", TRUE, errptr);
|
|
util_out_print("Error truncating 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;
|
|
}
|
|
zero_blk = (char *)malloc(DISK_BLOCK_SIZE);
|
|
memset(zero_blk, 0, DISK_BLOCK_SIZE);
|
|
LSEEKWRITE(backup_fd, filesize - DISK_BLOCK_SIZE, zero_blk, DISK_BLOCK_SIZE, status);
|
|
if (0 != status)
|
|
{
|
|
util_out_print("Error writing the last block in database !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;
|
|
}
|
|
free(zero_blk);
|
|
}
|
|
/* 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) && (0 == cs_addrs->shmpool_buffer->failed))
|
|
{
|
|
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 recovery every few retries - this should not be happening */
|
|
if (FALSE == shmpool_lock_hdr(gv_cur_region))
|
|
{
|
|
assert(FALSE);
|
|
gtm_putmsg(VARLSTCNT(9) ERR_DBCCERR, 2, REG_LEN_STR(gv_cur_region),
|
|
ERR_ERRCALL, 3, CALLFROM);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
shmpool_abandoned_blk_chk(gv_cur_region, TRUE);
|
|
shmpool_unlock_hdr(gv_cur_region);
|
|
}
|
|
}
|
|
|
|
if (0 != cs_addrs->shmpool_buffer->failed)
|
|
{
|
|
assert(EACCES == cs_addrs->shmpool_buffer->backup_errno);
|
|
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("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
|
|
FSTAT_FILE(list->backup_fd, &stat_buf, fstat_res);
|
|
if (-1 == fstat_res)
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("fstat : !AZ", TRUE, errptr);
|
|
util_out_print("Error obtaining status of temporary file !AD.",
|
|
TRUE, LEN_AND_STR(list->backup_tempfile));
|
|
util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
|
|
if (0 < (filesize = stat_buf.st_size))
|
|
{
|
|
rsize = (int4)(SIZEOF(muinc_blk_hdr) + header_cpy->blk_size);
|
|
sblkh_p = (muinc_blk_hdr_ptr_t)malloc(rsize);
|
|
/* Do not use LSEEKREAD macro here because of dependence on setting filepointer for
|
|
subsequent reads.
|
|
*/
|
|
if (-1 != (status = (ssize_t)lseek(list->backup_fd, 0, SEEK_SET)))
|
|
{
|
|
DOREADRC(list->backup_fd, (sm_uc_ptr_t)sblkh_p, rsize, status);
|
|
} else
|
|
status = errno;
|
|
if (0 != status)
|
|
{
|
|
if (0 < status)
|
|
{
|
|
errptr = (char *)STRERROR((int)status);
|
|
util_out_print("read : ", TRUE, errptr);
|
|
util_out_print("Error reading the temporary file !AD.",
|
|
TRUE, LEN_AND_STR(list->backup_tempfile));
|
|
}
|
|
else
|
|
util_out_print("Premature end of temporary file !AD.",
|
|
TRUE, LEN_AND_STR(list->backup_tempfile));
|
|
util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr);
|
|
free(sblkh_p);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
while (TRUE)
|
|
{
|
|
assert(sblkh_p->valid_data);
|
|
blk_num = sblkh_p->blkid;
|
|
if (debug_mupip)
|
|
util_out_print("MUPIP INFO: Restoring block 0x!XL from temporary file.",
|
|
TRUE, blk_num);
|
|
if (blk_num < header_cpy->trans_hist.total_blks)
|
|
{
|
|
inbuf = (char_ptr_t)(sblkh_p + 1);
|
|
/* If the incoming block has an ondisk version of V4, convert it back to that
|
|
version before writing it out so it is the same as the block in the original
|
|
database.
|
|
*/
|
|
if (GDSV4 == sblkh_p->use.bkup.ondsk_blkver)
|
|
{ /* Need to downgrade this block back to a previous format. Downgrade in place. */
|
|
gds_blk_downgrade((v15_blk_hdr_ptr_t)inbuf, (blk_hdr_ptr_t)inbuf);
|
|
size = (((v15_blk_hdr_ptr_t)inbuf)->bsiz + 1) & ~1;
|
|
} else
|
|
size = (((blk_hdr_ptr_t)inbuf)->bsiz + 1) & ~1;
|
|
|
|
if (cs_addrs->do_fullblockwrites)
|
|
size = (int4)ROUND_UP(size, cs_addrs->fullblockwrite_len);
|
|
assert(cs_addrs->hdr->blk_size >= size);
|
|
offset = (header_cpy->start_vbn - 1) * DISK_BLOCK_SIZE
|
|
+ ((off_t)header_cpy->blk_size * blk_num);
|
|
LSEEKWRITE(backup_fd,
|
|
offset,
|
|
inbuf,
|
|
size,
|
|
save_errno);
|
|
if (0 != save_errno)
|
|
{
|
|
util_out_print("Error accessing output file !AD. Aborting restore.",
|
|
TRUE, file->len, file->addr);
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("write : !AZ", TRUE, errptr);
|
|
free(sblkh_p);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
} /* Else ignore block that is larger than file was at time backup initiated */
|
|
DOREADRC(list->backup_fd, (sm_uc_ptr_t)sblkh_p, rsize, save_errno);
|
|
if (0 != save_errno)
|
|
{
|
|
if (0 < save_errno)
|
|
{
|
|
util_out_print("Error accessing temporary file !AD.",
|
|
TRUE, LEN_AND_STR(list->backup_tempfile));
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("read : !AZ", TRUE, errptr);
|
|
free(sblkh_p);
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
} else
|
|
/* End of file .. Note this does not detect the difference between
|
|
clean end of file and partial record end of file.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
free(sblkh_p);
|
|
}
|
|
}
|
|
|
|
LSEEKWRITE(backup_fd, 0, header_cpy, hdrsize, save_errno);
|
|
if (0 != save_errno)
|
|
{
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("write : !AZ", TRUE, errptr);
|
|
util_out_print("Error writing data to 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;
|
|
}
|
|
CLOSEFILE_RESET(backup_fd, status); /* resets "backup_fd" to FD_INVALID */
|
|
|
|
/* mv it to destination */
|
|
/* Calculate total line length for "mv" command. If cannot fit in local variable array, malloc space */
|
|
assert(command == &cmdarray[0]);
|
|
tmplen = file->len;
|
|
cmdlen = STR_LIT_LEN(MV_CMD) + tempfilelen + 1 /* space */ + tmplen + 1 /* terminating NULL byte */;
|
|
if (cmdlen > SIZEOF(cmdarray))
|
|
command = malloc(cmdlen); /* allocate memory and use that instead of local array "cmdarray" */
|
|
MEMCPY_LIT(command, MV_CMD);
|
|
cmdlen = STR_LIT_LEN(MV_CMD);
|
|
memcpy(&command[cmdlen], tempfilename, tempfilelen);
|
|
cmdlen += tempfilelen;
|
|
command[cmdlen++] = ' ';
|
|
memcpy(&command[cmdlen], file->addr, tmplen);
|
|
cmdlen += tmplen;
|
|
command[cmdlen] = 0;
|
|
if (debug_mupip)
|
|
util_out_print("MUPIP INFO: !AD", TRUE, cmdlen, command);
|
|
if (0 != (rv = SYSTEM((char *)command)))
|
|
{
|
|
if (-1 == rv)
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("system : !AZ", TRUE, errptr);
|
|
}
|
|
util_out_print("Error executing command : !AD", TRUE, cmdlen, command);
|
|
FREE_COMMAND_STR_IF_NEEDED;
|
|
CLEANUP_AND_RETURN_FALSE;
|
|
}
|
|
FREE_COMMAND_STR_IF_NEEDED;
|
|
assert(command == &cmdarray[0]);
|
|
util_out_print("DB file !AD backed up in file !AD", TRUE, gv_cur_region->dyn.addr->fname_len,
|
|
gv_cur_region->dyn.addr->fname, file->len, file->addr);
|
|
util_out_print("Transactions up to 0x!16@XQ are backed up.", TRUE, &header_cpy->trans_hist.curr_tn);
|
|
cs_addrs->hdr->last_com_backup = header_cpy->trans_hist.curr_tn;
|
|
cs_addrs->hdr->last_com_bkup_last_blk = header_cpy->trans_hist.total_blks;
|
|
if (record)
|
|
{
|
|
cs_addrs->hdr->last_rec_backup = header_cpy->trans_hist.curr_tn;
|
|
cs_addrs->hdr->last_rec_bkup_last_blk = header_cpy->trans_hist.total_blks;
|
|
}
|
|
file_backed_up = TRUE;
|
|
return TRUE;
|
|
}
|