2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: sendbackup-gnutar.c,v 1.98 2006/07/25 18:35:21 martinea Exp $
29 * send backup data using GNU tar
33 #include "sendbackup.h"
34 #include "amandates.h"
37 #include "getfsent.h" /* for amname_to_dirname lookup */
45 static amregex_t re_table[] = {
46 /* tar prints the size in bytes */
47 AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1, 1),
48 AM_NORMAL_RE("^Elapsed time:"),
49 AM_NORMAL_RE("^Throughput"),
51 /* GNU tar 1.13.17 will print this warning when (not) backing up a
53 AM_NORMAL_RE(": socket ignored$"),
55 /* GNUTAR produces a few error messages when files are modified or
56 removed while it is running. They may cause data to be lost, but
57 then they may not. We shouldn't consider them NORMAL until
58 further investigation. */
59 #ifdef IGNORE_TAR_ERRORS
60 AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
61 AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
62 AM_NORMAL_RE(": Error exit delayed from previous errors"),
65 /* samba may produce these output messages */
66 AM_NORMAL_RE("^[Aa]dded interface"),
67 AM_NORMAL_RE("^session request to "),
68 AM_NORMAL_RE("^tar: dumped [0-9][0-9]* (tar )?files"),
71 AM_NORMAL_RE("^doing parameter"),
72 AM_NORMAL_RE("^pm_process\\(\\)"),
73 AM_NORMAL_RE("^adding IPC"),
74 AM_NORMAL_RE("^Opening"),
75 AM_NORMAL_RE("^Connect"),
76 AM_NORMAL_RE("^Domain="),
78 AM_NORMAL_RE("^security="),
79 AM_NORMAL_RE("^capabilities"),
80 AM_NORMAL_RE("^Sec mode "),
81 AM_NORMAL_RE("^Got "),
82 AM_NORMAL_RE("^Chose protocol "),
83 AM_NORMAL_RE("^Server "),
84 AM_NORMAL_RE("^Timezone "),
85 AM_NORMAL_RE("^received"),
86 AM_NORMAL_RE("^FINDFIRST"),
87 AM_NORMAL_RE("^FINDNEXT"),
88 AM_NORMAL_RE("^dos_clean_name"),
89 AM_NORMAL_RE("^file"),
90 AM_NORMAL_RE("^getting file"),
91 AM_NORMAL_RE("^Rejected chained"),
92 AM_NORMAL_RE("^nread="),
93 AM_NORMAL_RE("^\\([0-9][0-9]* kb/s\\)"),
94 AM_NORMAL_RE("^\\([0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
95 AM_NORMAL_RE("^[ \t]*[0-9][0-9]* \\([ \t]*[0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
96 AM_NORMAL_RE("^[ \t]*directory "),
97 AM_NORMAL_RE("^load_client_codepage"),
100 #ifdef IGNORE_SMBCLIENT_ERRORS
101 /* This will cause amanda to ignore real errors, but that may be
102 * unavoidable when you're backing up system disks. It seems to be
103 * a safe thing to do if you know what you're doing. */
104 AM_NORMAL_RE("^ERRDOS - ERRbadshare opening remote file"),
105 AM_NORMAL_RE("^ERRDOS - ERRbadfile opening remote file"),
106 AM_NORMAL_RE("^ERRDOS - ERRnoaccess opening remote file"),
107 AM_NORMAL_RE("^ERRSRV - ERRaccess setting attributes on file"),
108 AM_NORMAL_RE("^ERRDOS - ERRnoaccess setting attributes on file"),
111 #if SAMBA_VERSION >= 2
112 /* Backup attempt of nonexisting directory */
113 AM_ERROR_RE("ERRDOS - ERRbadpath (Directory invalid.)"),
114 AM_NORMAL_RE("^Domain="),
117 /* catch-all: DMP_STRANGE is returned for all other lines */
127 static char *gnutar_list_dir = NULL;
128 static char *incrname = NULL;
130 * doing similar to $ gtar | compression | encryption
140 char tmppath[PATH_MAX];
141 int dumpin, dumpout, compout;
143 char *indexcmd = NULL;
144 char *dirname = NULL;
146 char dumptimestr[80] = "UNUSED";
148 amandates_t *amdates = NULL;
149 time_t prev_dumptime = 0;
150 char *error_pn = NULL;
151 char *compopt = NULL;
152 char *encryptopt = skip_argument;
159 char *amandates_file = NULL;
161 error_pn = stralloc2(get_pname(), "-smbclient");
163 qdisk = quote_string(dle->disk);
164 dbprintf(_("start: %s:%s lev %d\n"), host, qdisk, GPOINTER_TO_INT(dle->level->data));
166 g_fprintf(stderr, _("%s: start [%s:%s level %d]\n"),
167 get_pname(), host, qdisk, GPOINTER_TO_INT(dle->level->data));
169 /* apply client-side encryption here */
170 if ( dle->encrypt == ENCRYPT_CUST ) {
171 encpid = pipespawn(dle->clnt_encrypt, STDIN_PIPE, 0,
172 &compout, &dataf, &mesgf,
173 dle->clnt_encrypt, encryptopt, NULL);
174 dbprintf(_("gnutar: pid %ld: %s\n"), (long)encpid, dle->clnt_encrypt);
179 /* now do the client-side compression */
180 if(dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
181 compopt = skip_argument;
182 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
183 if(dle->compress == COMP_BEST) {
184 compopt = COMPRESS_BEST_OPT;
186 compopt = COMPRESS_FAST_OPT;
189 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE, 0,
190 &dumpout, &compout, &mesgf,
191 COMPRESS_PATH, compopt, NULL);
192 dbprintf(_("gnutar: pid %ld: %s"), (long)comppid, COMPRESS_PATH);
193 if(compopt != skip_argument) {
194 dbprintf(_("pid %ld: %s %s\n"),
195 (long)comppid, COMPRESS_PATH, compopt);
197 dbprintf(_("pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
199 } else if (dle->compress == COMP_CUST) {
200 compopt = skip_argument;
201 comppid = pipespawn(dle->compprog, STDIN_PIPE, 0,
202 &dumpout, &compout, &mesgf,
203 dle->compprog, compopt, NULL);
204 if(compopt != skip_argument) {
205 dbprintf(_("pid %ld: %s %s\n"),
206 (long)comppid, dle->compprog, compopt);
208 dbprintf(_("pid %ld: %s\n"), (long)comppid, dle->compprog);
215 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
216 if (strlen(gnutar_list_dir) == 0)
217 gnutar_list_dir = NULL;
219 #ifdef SAMBA_CLIENT /* { */
220 if (dle->device[0] == '/' && dle->device[1]=='/')
224 if (gnutar_list_dir) {
225 char *basename = NULL;
226 char number[NUM_STR_SIZE];
227 char *inputname = NULL;
229 char *sdisk = sanitise_filename(dle->disk);
231 basename = vstralloc(gnutar_list_dir,
238 g_snprintf(number, SIZEOF(number), "%d", GPOINTER_TO_INT(dle->level->data));
239 incrname = vstralloc(basename, "_", number, ".new", NULL);
243 * Open the listed incremental file from the previous level. Search
244 * backward until one is found. If none are found (which will also
245 * be true for a level 0), arrange to read from /dev/null.
247 baselevel = GPOINTER_TO_INT(dle->level->data);
250 if (--baselevel >= 0) {
251 g_snprintf(number, SIZEOF(number), "%d", baselevel);
252 inputname = newvstralloc(inputname,
253 basename, "_", number, NULL);
255 inputname = newstralloc(inputname, "/dev/null");
257 if ((infd = open(inputname, O_RDONLY)) == -1) {
258 int save_errno = errno;
259 char *qname = quote_string(inputname);
261 dbprintf(_("gnutar: error opening '%s': %s\n"),
263 strerror(save_errno));
265 error(_("error [opening '%s': %s]"), qname, strerror(save_errno));
273 * Copy the previous listed incremental file to the new one.
275 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
276 error(_("error [opening '%s': %s]"), incrname, strerror(errno));
280 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
281 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
282 error(_("error [writing to '%s': %s]"), incrname,
289 error(_("error [reading from '%s': %s]"), inputname, strerror(errno));
293 if (close(infd) != 0) {
294 error(_("error [closing '%s': %s]"), inputname, strerror(errno));
297 if (close(outfd) != 0) {
298 error(_("error [closing '%s': %s]"), incrname, strerror(errno));
302 tquoted = quote_string(incrname);
304 fquoted = quote_string(inputname);
305 dbprintf(_("doing level %d dump as listed-incremental from '%s' to '%s'\n"),
306 GPOINTER_TO_INT(dle->level->data), fquoted, tquoted);
309 dbprintf(_("doing level %d dump as listed-incremental to '%s'\n"),
310 GPOINTER_TO_INT(dle->level->data), tquoted);
316 /* no gnutar-listdir, so we're using amandates */
318 /* find previous dump time, failing completely if there's a problem */
319 amandates_file = getconf_str(CNF_AMANDATES);
320 if(!start_amandates(amandates_file, 0)) {
321 error(_("error [opening %s: %s]"), amandates_file, strerror(errno));
325 amdates = amandates_lookup(dle->disk);
327 prev_dumptime = EPOCH;
328 for(l = 0; l < GPOINTER_TO_INT(dle->level->data); l++) {
329 if(amdates->dates[l] > prev_dumptime)
330 prev_dumptime = amdates->dates[l];
336 gmtm = gmtime(&prev_dumptime);
337 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
338 "%04d-%02d-%02d %2d:%02d:%02d GMT",
339 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
340 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
342 dbprintf(_("gnutar: doing level %d dump from amandates-derived date: %s\n"),
343 GPOINTER_TO_INT(dle->level->data), dumptimestr);
346 dirname = amname_to_dirname(dle->device);
348 cur_dumptime = time(0);
349 cur_level = GPOINTER_TO_INT(dle->level->data);
350 cur_disk = stralloc(dle->disk);
352 # define PROGRAM_GNUTAR GNUTAR
354 # define PROGRAM_GNUTAR "tar"
356 indexcmd = vstralloc(
364 #ifdef SAMBA_CLIENT /* { */
365 /* Use sambatar if the disk to back up is a PC disk */
366 if (dle->device[0] == '/' && dle->device[1]=='/') {
367 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
368 char *share = NULL, *subdir = NULL;
376 parsesharename(dle->device, &share, &subdir);
382 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
385 if ((subdir) && (SAMBA_VERSION < 2)) {
390 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
393 if ((user_and_password = findpass(share, &domain)) == NULL) {
395 memset(domain, '\0', strlen(domain));
400 error(_("error [invalid samba host or password not found?]"));
403 lpass = strlen(user_and_password);
404 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
405 memset(user_and_password, '\0', lpass);
406 amfree(user_and_password);
408 memset(domain, '\0', strlen(domain));
413 error(_("password field not \'user%%pass\' for %s"), qdisk);
417 pwtext_len = strlen(pwtext);
418 if ((sharename = makesharename(share, 0)) == 0) {
419 memset(user_and_password, '\0', lpass);
420 amfree(user_and_password);
422 memset(domain, '\0', strlen(domain));
427 error(_("error [can't make share name of %s]"), share);
431 taropt = stralloc("-T");
432 if(dle->exclude_file && dle->exclude_file->nb_element == 1) {
433 strappend(taropt, "X");
435 #if SAMBA_VERSION >= 2
436 strappend(taropt, "q");
438 strappend(taropt, "c");
439 if (GPOINTER_TO_INT(dle->level->data) != 0) {
440 strappend(taropt, "g");
441 } else if (dle->record) {
442 strappend(taropt, "a");
446 dbprintf(_("gnutar: backup of %s/%s\n"), sharename, subdir);
448 dbprintf(_("gnutar: backup of %s\n"), sharename);
451 program->backup_name = program->restore_name = SAMBA_CLIENT;
452 cmd = stralloc(program->backup_name);
453 info_tapeheader(dle);
455 start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
457 if (pwtext_len > 0) {
458 pw_fd_env = "PASSWD_FD";
460 pw_fd_env = "dummy_PASSWD_FD";
462 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE, 0,
463 &dumpin, &dumpout, &mesgf,
467 *user_and_password ? "-U" : skip_argument,
468 *user_and_password ? user_and_password : skip_argument,
470 domain ? "-W" : skip_argument,
471 domain ? domain : skip_argument,
472 #if SAMBA_VERSION >= 2
473 subdir ? "-D" : skip_argument,
474 subdir ? subdir : skip_argument,
479 dle->exclude_file && dle->exclude_file->nb_element == 1 ? dle->exclude_file->first->name : skip_argument,
482 memset(domain, '\0', strlen(domain));
485 if(pwtext_len > 0 && full_write(passwdf, pwtext, pwtext_len) < pwtext_len) {
486 int save_errno = errno;
489 memset(user_and_password, '\0', lpass);
490 amfree(user_and_password);
493 error(_("error [password write failed: %s]"), strerror(save_errno));
496 memset(user_and_password, '\0', lpass);
497 amfree(user_and_password);
505 #endif /*end of samba */
512 char *file_exclude = NULL;
513 char *file_include = NULL;
515 if (dle->exclude_file) nb_exclude+=dle->exclude_file->nb_element;
516 if (dle->exclude_list) nb_exclude+=dle->exclude_list->nb_element;
517 if (dle->include_file) nb_include+=dle->include_file->nb_element;
518 if (dle->include_list) nb_include+=dle->include_list->nb_element;
520 if (nb_exclude > 0) file_exclude = build_exclude(dle, 0);
521 if (nb_include > 0) file_include = build_include(dle, 0);
523 my_argv = alloc(SIZEOF(char *) * (22 + (nb_exclude*2)+(nb_include*2)));
525 cmd = vstralloc(amlibexecdir, "/", "runtar", versionsuffix(), NULL);
526 info_tapeheader(dle);
528 start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
530 my_argv[i++] = "runtar";
531 if (g_options->config)
532 my_argv[i++] = g_options->config;
534 my_argv[i++] = "NOCONFIG";
536 my_argv[i++] = GNUTAR;
538 my_argv[i++] = "tar";
540 my_argv[i++] = "--create";
541 my_argv[i++] = "--file";
543 my_argv[i++] = "--directory";
544 canonicalize_pathname(dirname, tmppath);
545 my_argv[i++] = tmppath;
546 my_argv[i++] = "--one-file-system";
547 if (gnutar_list_dir && incrname) {
548 my_argv[i++] = "--listed-incremental";
549 my_argv[i++] = incrname;
551 my_argv[i++] = "--incremental";
552 my_argv[i++] = "--newer";
553 my_argv[i++] = dumptimestr;
555 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
556 /* --atime-preserve causes gnutar to call
557 * utime() after reading files in order to
558 * adjust their atime. However, utime()
559 * updates the file's ctime, so incremental
560 * dumps will think the file has changed. */
561 my_argv[i++] = "--atime-preserve";
563 my_argv[i++] = "--sparse";
564 my_argv[i++] = "--ignore-failed-read";
565 my_argv[i++] = "--totals";
568 my_argv[i++] = "--exclude-from";
569 my_argv[i++] = file_exclude;
573 my_argv[i++] = "--files-from";
574 my_argv[i++] = file_include;
580 dumppid = pipespawnv(cmd, STDIN_PIPE, 0,
581 &dumpin, &dumpout, &mesgf, my_argv);
583 amfree(file_exclude);
584 amfree(file_include);
587 dbprintf(_("gnutar: %s: pid %ld\n"), cmd, (long)dumppid);
595 /* close the write ends of the pipes */
602 if (dle->create_index)
611 char *amandates_file = NULL;
613 if(dle->record && !goterror) {
614 if (incrname != NULL && strlen(incrname) > 4) {
617 nodotnew = stralloc(incrname);
618 nodotnew[strlen(nodotnew)-4] = '\0';
619 if (rename(incrname, nodotnew)) {
620 g_fprintf(stderr, _("%s: warning [renaming %s to %s: %s]\n"),
621 get_pname(), incrname, nodotnew, strerror(errno));
627 /* update the amandates file */
628 amandates_file = getconf_str(CNF_AMANDATES);
629 if(start_amandates(amandates_file, 1)) {
630 amandates_updateone(cur_disk, cur_level, cur_dumptime);
634 /* failure is only fatal if we didn't get a gnutar-listdir */
635 char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
636 if (!gnutar_list_dir || !*gnutar_list_dir) {
637 error(_("error [opening %s for writing: %s]"), amandates_file, strerror(errno));
640 g_debug(_("non-fatal error opening '%s' for writing: %s]"),
641 amandates_file, strerror(errno));
647 backup_program_t gnutar_program = {
654 re_table, start_backup, end_backup