/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 1991-1999 University of Maryland at College Park
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of U.M. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. U.M. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Authors: the Amanda Development Team. Its members are listed in a
* file named AUTHORS, in the root directory of this distribution.
*/
/*
* $Id: sendbackup-dump.c,v 1.90 2006/07/25 18:10:07 martinea Exp $
*
* send backup data using BSD dump
*/
#include "amanda.h"
#include "sendbackup.h"
#include "getfsent.h"
#include "clock.h"
#define LEAF_AND_DIRS "sed -e \'\ns/^leaf[ \t]*[0-9]*[ \t]*\\.//\nt\n/^dir[ \t]/ {\ns/^dir[ \t]*[0-9]*[ \t]*\\.//\ns%$%/%\nt\n}\nd\n\'"
static amregex_t re_table[] = {
/* the various encodings of dump size */
/* this should also match BSDI pre-3.0's buggy dump program, that
produced doubled DUMP: DUMP: messages */
AM_SIZE_RE("DUMP: [0-9][0-9]* tape blocks", 1024, 1),
AM_SIZE_RE("dump: Actual: [0-9][0-9]* tape blocks", 1024, 1),
AM_SIZE_RE("backup: There are [0-9][0-9]* tape blocks on [0-9][0-9]* tapes",
1024, 1),
AM_SIZE_RE("backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape\\(s\\)",
1024, 1),
AM_SIZE_RE("backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume\\(s\\)",
1024, 1),
AM_SIZE_RE("DUMP: [0-9][0-9]* blocks \\([0-9][0-9]*KB\\) on [0-9][0-9]* volume",
512, 1),
AM_SIZE_RE("DUMP: [0-9][0-9]* blocks \\([0-9][0-9]*\\.[0-9][0-9]*MB\\) on [0-9][0-9]* volume",
512, 1),
AM_SIZE_RE("DUMP: [0-9][0-9]* blocks \\([0-9][0-9]*KB\\)",
1024, 2),
AM_SIZE_RE("DUMP: [0-9][0-9]* blocks \\([0-9][0-9]*\\.[0-9][0-9]*MB\\)",
1048576, 2),
AM_SIZE_RE("DUMP: [0-9][0-9]* blocks", 512, 1),
AM_SIZE_RE("DUMP: [0-9][0-9]* bytes were dumped", 1, 1),
/* OSF's vdump */
AM_SIZE_RE("vdump: Dumped [0-9][0-9]* of [0-9][0-9]* bytes", 1, 1),
/* DU 4.0a dump */
AM_SIZE_RE("dump: Actual: [0-9][0-9]* blocks output to pipe", 1024, 1),
/* DU 4.0 vdump */
AM_SIZE_RE("dump: Dumped [0-9][0-9]* of [0-9][0-9]* bytes", 1, 1),
/* HPUX dump */
AM_SIZE_RE("DUMP: [0-9][0-9]* KB actual output", 1024, 1),
/* HPUX 10.20 and above vxdump */
AM_SIZE_RE("vxdump: [0-9][0-9]* tape blocks", 1024, 1),
/* UnixWare vxdump */
AM_SIZE_RE("vxdump: [0-9][0-9]* blocks", 1024, 1),
/* SINIX vxdump */
AM_SIZE_RE(" VXDUMP: [0-9][0-9]* blocks", 512, 1),
/* SINIX ufsdump */
AM_SIZE_RE(" UFSDUMP: [0-9][0-9]* blocks", 512, 1),
/* Irix 6.2 xfs dump */
AM_SIZE_RE("xfsdump: media file size [0-9][0-9]* bytes", 1, 1),
/* NetApp dump */
AM_SIZE_RE("DUMP: [0-9][0-9]* KB", 1024, 1),
/* strange dump lines */
AM_STRANGE_RE("should not happen"),
AM_STRANGE_RE("Cannot open"),
AM_STRANGE_RE("[Ee]rror"),
AM_STRANGE_RE("[Ff]ail"),
/* XXX add more ERROR entries here by scanning dump sources? */
/* any blank or non-strange DUMP: lines are marked as normal */
AM_NORMAL_RE("^ *DUMP:"),
AM_NORMAL_RE("^dump:"), /* OSF/1 */
AM_NORMAL_RE("^vdump:"), /* OSF/1 */
AM_NORMAL_RE("^ *vxdump:"), /* HPUX10 */
AM_NORMAL_RE("^ *vxfs *vxdump:"), /* Solaris */
AM_NORMAL_RE("^Dumping .* to stdout"), /* Sol vxdump */
AM_NORMAL_RE("^xfsdump:"), /* IRIX xfs */
AM_NORMAL_RE("^ *VXDUMP:"), /* Sinix */
AM_NORMAL_RE("^ *UFSDUMP:"), /* Sinix */
#ifdef VDUMP /* this is for OSF/1 3.2's vdump for advfs */
AM_NORMAL_RE("^The -s option is ignored"), /* OSF/1 */
AM_NORMAL_RE("^path"), /* OSF/1 */
AM_NORMAL_RE("^dev/fset"), /* OSF/1 */
AM_NORMAL_RE("^type"), /* OSF/1 */
AM_NORMAL_RE("^advfs id"), /* OSF/1 */
AM_NORMAL_RE("^[A-Z][a-z][a-z] [A-Z][a-z][a-z] .[0-9] [0-9]"), /* OSF/1 */
#endif
AM_NORMAL_RE("^backup:"), /* AIX */
AM_NORMAL_RE("^ Use the umount command to unmount the filesystem"),
AM_NORMAL_RE("^[ \t]*$"),
/* catch-all; DMP_STRANGE is returned for all other lines */
AM_STRANGE_RE(NULL)
};
static void start_backup(dle_t *dle, char *host,
int dataf, int mesgf, int indexf);
static void end_backup(dle_t *dle, int status);
/*
* doing similar to $ dump | compression | encryption
*/
static void
start_backup(
dle_t *dle,
char *host,
int dataf,
int mesgf,
int indexf)
{
int dumpin, dumpout, compout;
char *dumpkeys = NULL;
char *device = NULL;
char *fstype = NULL;
char *cmd = NULL;
char *cmdX = NULL;
char *indexcmd = NULL;
char level_str[NUM_STR_SIZE];
char *compopt = NULL;
char *encryptopt = skip_argument;
char *qdisk;
char *config;
am_level_t *alevel = (am_level_t *)dle->levellist->data;
int level = alevel->level;
int native_pipe[2];
int client_pipe[2];
int data_out = dataf;
have_filter = FALSE;
crc32_init(&native_crc.crc);
crc32_init(&client_crc.crc);
/* create pipes to compute the native CRC */
if (pipe(native_pipe) < 0) {
char *errmsg;
char *qerrmsg;
errmsg = g_strdup_printf(_("Program '%s': can't create pipe"),
dle->program);
qerrmsg = quote_string(errmsg);
fdprintf(mesgf, _("sendbackup: error [%s]\n"), errmsg);
dbprintf(_("ERROR %s\n"), qerrmsg);
amfree(qerrmsg);
amfree(errmsg);
return;
}
if (dle->encrypt == ENCRYPT_CUST ||
dle->compress == COMP_FAST ||
dle->compress == COMP_BEST ||
dle->compress == COMP_CUST) {
have_filter = TRUE;
/* create pipes to compute the client CRC */
if (pipe(client_pipe) < 0) {
char *errmsg;
char *qerrmsg;
errmsg = g_strdup_printf(_("Application '%s': can't create pipe"),
dle->program);
qerrmsg = quote_string(errmsg);
fdprintf(mesgf, _("sendbackup: error [%s]\n"), errmsg);
dbprintf(_("ERROR %s\n"), qerrmsg);
amfree(qerrmsg);
amfree(errmsg);
return;
}
data_out = client_pipe[1];
} else {
data_out = dataf;
}
g_snprintf(level_str, sizeof(level_str), "%d", level);
qdisk = quote_string(dle->disk);
dbprintf(_("start: %s:%s lev %d\n"), host, qdisk, level);
g_fprintf(stderr, _("%s: start [%s:%s level %d]\n"),
get_pname(), host, qdisk, level);
amfree(qdisk);
/* apply client-side encryption here */
if (dle->encrypt == ENCRYPT_CUST ) {
encpid = pipespawn(dle->clnt_encrypt, STDIN_PIPE, 0,
&compout, &data_out, &mesgf,
dle->clnt_encrypt, encryptopt, NULL);
dbprintf(_("gnutar: pid %ld: %s\n"), (long)encpid, dle->clnt_encrypt);
aclose(data_out);
} else {
compout = data_out;
encpid = -1;
}
/* now do the client-side compression */
if(dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
compopt = skip_argument;
#if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
if(dle->compress == COMP_BEST) {
compopt = COMPRESS_BEST_OPT;
} else {
compopt = COMPRESS_FAST_OPT;
}
#endif
comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE, 0,
&dumpout, &compout, &mesgf,
COMPRESS_PATH, compopt, NULL);
dbprintf(_("dump: pid %ld: %s"), (long)comppid, COMPRESS_PATH);
if(compopt != skip_argument) {
dbprintf(" %s", compopt);
}
dbprintf("\n");
aclose(compout);
} else if (dle->compress == COMP_CUST) {
compopt = skip_argument;
comppid = pipespawn(dle->compprog, STDIN_PIPE, 0,
&dumpout, &compout, &mesgf,
dle->compprog, compopt, NULL);
dbprintf(_("gnutar-cust: pid %ld: %s"),
(long)comppid, dle->compprog);
if(compopt != skip_argument) {
dbprintf(" %s", compopt);
}
dbprintf("\n");
aclose(compout);
} else {
dumpout = compout;
compout = -1;
comppid = -1;
}
/* invoke dump */
device = amname_to_devname(dle->device);
fstype = amname_to_fstype(dle->device);
dbprintf(_("dumping device '%s' with '%s'\n"), device, fstype);
#if defined(USE_RUNDUMP) || !defined(DUMP)
cmd = g_strjoin(NULL, amlibexecdir, "/", "rundump", NULL);
cmdX = cmd;
if (g_options->config)
config = g_options->config;
else
config = "NOCONFIG";
#else
cmd = g_strdup(DUMP);
cmdX = skip_argument;
config = skip_argument;
#endif
#ifndef AIX_BACKUP /* { */
/* normal dump */
#ifdef XFSDUMP /* { */
#ifdef DUMP /* { */
if (g_str_equal(amname_to_fstype(dle->device), "xfs"))
#else /* } { */
if (1)
#endif /* } */
{
char *progname;
g_free(cmd);
progname = cmd = g_strconcat(amlibexecdir, "/", "rundump", NULL);
cmdX = cmd;
if (g_options->config)
config = g_options->config;
else
config = "NOCONFIG";
program->backup_name = XFSDUMP;
program->restore_name = XFSRESTORE;
indexcmd = g_strjoin(NULL, XFSRESTORE, " -t", " -v", " silent", " -",
" 2>/dev/null", " | sed", " -e", " \'s/^/\\//\'", NULL);
info_tapeheader(dle);
start_index(dle->create_index, native_pipe[1], mesgf, indexf, indexcmd);
dumpkeys = g_strdup(level_str);
dumppid = pipespawn(progname, STDIN_PIPE, 0,
&dumpin, &native_pipe[1], &mesgf,
cmdX, config,
"xfsdump",
!dle->record ? "-J" : skip_argument,
"-F",
"-l", dumpkeys,
"-",
device,
NULL);
}
else
#endif /* } */
#ifdef VXDUMP /* { */
#ifdef DUMP
if (g_str_equal(amname_to_fstype(dle->device), "vxfs"))
#else
if (1)
#endif
{
char *progname;
#ifdef USE_RUNDUMP
g_free(cmd);
progname = cmd = g_strconcat(amlibexecdir, "/", "rundump", NULL);
cmdX = cmd;
if (g_options->config)
config = g_options->config;
else
config = "NOCONFIG";
#else
char *progname;
g_free(cmd);
progname = cmd = g_strdup(VXDUMP);
cmdX = skip_argument;
config = skip_argument;
#endif
program->backup_name = VXDUMP;
program->restore_name = VXRESTORE;
dumpkeys = g_strjoin(NULL, level_str,
!dle->record ? "" : "u",
"s",
"f",
NULL);
indexcmd = g_strjoin(NULL, VXRESTORE,
" -tvf", " -",
" 2>/dev/null",
" | ",
LEAF_AND_DIRS,
NULL);
info_tapeheader(dle);
start_index(dle->create_index, native_pipe[1], mesgf, indexf, indexcmd);
dumppid = pipespawn(progname, STDIN_PIPE, 0,
&dumpin, &native_pipe[1], &mesgf,
cmdX, config,
"vxdump",
dumpkeys,
"1048576",
"-",
device,
NULL);
}
else
#endif /* } */
#ifdef VDUMP /* { */
#ifdef DUMP
if (g_str_equal(amname_to_fstype(dle->device), "advfs"))
#else
if (1)
#endif
{
g_free(cmd);
cmd = g_strconcat(amlibexecdir, "/", "rundump", NULL);
cmdX = cmd;
if (g_options->config)
config = g_options->config;
else
config = "NOCONFIG";
g_free(device);
device = g_strdup(amname_to_dirname(dle->device));
program->backup_name = VDUMP;
program->restore_name = VRESTORE;
dumpkeys = g_strjoin(NULL, level_str,
!dle->record ? "" : "u",
"b",
"f",
NULL);
indexcmd = g_strjoin(NULL, VRESTORE,
" -tvf", " -",
" 2>/dev/null",
" | ",
"sed -e \'\n/^\\./ {\ns/^\\.//\ns/, [0-9]*$//\ns/^\\.//\ns/ @-> .*$//\nt\n}\nd\n\'",
NULL);
info_tapeheader(dle);
start_index(dle->create_index, native_pipe[1], mesgf, indexf, indexcmd);
dumppid = pipespawn(cmd, STDIN_PIPE, 0,
&dumpin, &native_pipe[1], &mesgf,
cmdX, config,
"vdump",
dumpkeys,
"60",
"-",
device,
NULL);
}
else
#endif /* } */
{
#ifndef RESTORE
#define RESTORE "restore"
#endif
#ifdef HAVE_HONOR_NODUMP
# define PARAM_HONOR_NODUMP "h"
#else
# define PARAM_HONOR_NODUMP ""
#endif
#ifdef __FreeBSD__
# if defined(__FreeBSD_version) && (__FreeBSD_version >= 500043)
# define FREEBSD_EXTRA_KEYS "bL"
# else
# define FREEBSD_EXTRA_KEYS "b"
# endif
#else
# define FREEBSD_EXTRA_KEYS ""
#endif
dumpkeys = g_strjoin(NULL, level_str,
!dle->record ? "" : "u",
FREEBSD_EXTRA_KEYS,
"s",
PARAM_HONOR_NODUMP,
"f",
NULL);
indexcmd = g_strjoin(NULL, RESTORE,
" -tvf", " -",
" 2>&1",
/* not to /dev/null because of DU's dump */
" | ",
LEAF_AND_DIRS,
NULL);
info_tapeheader(dle);
start_index(dle->create_index, native_pipe[1], mesgf, indexf, indexcmd);
dumppid = pipespawn(cmd, STDIN_PIPE, 0,
&dumpin, &native_pipe[1], &mesgf,
cmdX, config,
"dump",
dumpkeys,
#ifdef __FreeBSD__
"64",
#endif
"1048576",
#ifdef HAVE_HONOR_NODUMP
"0",
#endif
"-",
device,
NULL);
}
#else /* } { */
/* AIX backup program */
dumpkeys = g_strjoin(NULL, "-",
level_str,
!dle->record ? "" : "u",
"f",
NULL);
indexcmd = g_strjoin(NULL, RESTORE,
" -B",
" -tvf", " -",
" 2>/dev/null",
" | ",
LEAF_AND_DIRS,
NULL);
info_tapeheader(dle);
start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
dumppid = pipespawn(cmd, STDIN_PIPE, 0,
&dumpin, &native_pipe[1], &mesgf,
cmdX, config,
"backup",
dumpkeys,
"-",
device,
NULL);
#endif /* } */
amfree(dumpkeys);
amfree(fstype);
amfree(device);
amfree(cmd);
amfree(indexcmd);
/* close the write ends of the pipes */
aclose(dumpin);
aclose(native_pipe[1]);
aclose(mesgf);
if (dle->create_index)
aclose(indexf);
if (shm_control_name) {
shm_ring = shm_ring_link(shm_control_name);
shm_ring_producer_set_size(shm_ring, NETWORK_BLOCK_BYTES*16, NETWORK_BLOCK_BYTES*4);
native_crc.in = native_pipe[0];
if (!have_filter) {
native_crc.out = dumpout;
native_crc.shm_ring = shm_ring;
native_crc.thread = g_thread_create(handle_crc_to_shm_ring_thread,
(gpointer)&native_crc, TRUE, NULL);
} else {
native_crc.out = dumpout;
native_crc.thread = g_thread_create(handle_crc_thread,
(gpointer)&native_crc, TRUE, NULL);
close(client_pipe[1]);
client_crc.in = client_pipe[0];
client_crc.out = dumpout;
client_crc.shm_ring = shm_ring;
client_crc.thread = g_thread_create(handle_crc_to_shm_ring_thread,
(gpointer)&client_crc, TRUE, NULL);
}
} else {
native_crc.in = native_pipe[0];
native_crc.out = dumpout;
native_crc.thread = g_thread_create(handle_crc_thread,
(gpointer)&native_crc, TRUE, NULL);
if (have_filter) {
close(client_pipe[1]);
client_crc.in = client_pipe[0];
client_crc.out = dataf;
client_crc.thread = g_thread_create(handle_crc_thread,
(gpointer)&client_crc, TRUE, NULL);
}
}
}
static void
end_backup(
dle_t *dle,
int status)
{
(void)dle;
(void)status; /* Quiet unused parameter warning */
/* don't need to do anything for dump */
}
backup_program_t dump_program = {
"DUMP",
#ifdef DUMP
DUMP
#else
"dump"
#endif
,
RESTORE
,
re_table, start_backup, end_backup
};