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 */
44 static amregex_t re_table[] = {
45 /* tar prints the size in bytes */
46 AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1, 1),
47 AM_NORMAL_RE("^Elapsed time:"),
48 AM_NORMAL_RE("^Throughput"),
50 /* GNU tar 1.13.17 will print this warning when (not) backing up a
52 AM_NORMAL_RE(": socket ignored$"),
54 /* GNUTAR produces a few error messages when files are modified or
55 removed while it is running. They may cause data to be lost, but
56 then they may not. We shouldn't consider them NORMAL until
57 further investigation. */
58 #ifdef IGNORE_TAR_ERRORS
59 AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
60 AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
61 AM_NORMAL_RE(": Error exit delayed from previous errors"),
64 /* samba may produce these output messages */
65 AM_NORMAL_RE("^[Aa]dded interface"),
66 AM_NORMAL_RE("^session request to "),
67 AM_NORMAL_RE("^tar: dumped [0-9][0-9]* (tar )?files"),
70 AM_NORMAL_RE("^doing parameter"),
71 AM_NORMAL_RE("^pm_process\\(\\)"),
72 AM_NORMAL_RE("^adding IPC"),
73 AM_NORMAL_RE("^Opening"),
74 AM_NORMAL_RE("^Connect"),
75 AM_NORMAL_RE("^Domain="),
77 AM_NORMAL_RE("^security="),
78 AM_NORMAL_RE("^capabilities"),
79 AM_NORMAL_RE("^Sec mode "),
80 AM_NORMAL_RE("^Got "),
81 AM_NORMAL_RE("^Chose protocol "),
82 AM_NORMAL_RE("^Server "),
83 AM_NORMAL_RE("^Timezone "),
84 AM_NORMAL_RE("^received"),
85 AM_NORMAL_RE("^FINDFIRST"),
86 AM_NORMAL_RE("^FINDNEXT"),
87 AM_NORMAL_RE("^dos_clean_name"),
88 AM_NORMAL_RE("^file"),
89 AM_NORMAL_RE("^getting file"),
90 AM_NORMAL_RE("^Rejected chained"),
91 AM_NORMAL_RE("^nread="),
92 AM_NORMAL_RE("^\\([0-9][0-9]* kb/s\\)"),
93 AM_NORMAL_RE("^\\([0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
94 AM_NORMAL_RE("^[ \t]*[0-9][0-9]* \\([ \t]*[0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
95 AM_NORMAL_RE("^[ \t]*directory "),
96 AM_NORMAL_RE("^load_client_codepage"),
99 #ifdef IGNORE_SMBCLIENT_ERRORS
100 /* This will cause amanda to ignore real errors, but that may be
101 * unavoidable when you're backing up system disks. It seems to be
102 * a safe thing to do if you know what you're doing. */
103 AM_NORMAL_RE("^ERRDOS - ERRbadshare opening remote file"),
104 AM_NORMAL_RE("^ERRDOS - ERRbadfile opening remote file"),
105 AM_NORMAL_RE("^ERRDOS - ERRnoaccess opening remote file"),
106 AM_NORMAL_RE("^ERRSRV - ERRaccess setting attributes on file"),
107 AM_NORMAL_RE("^ERRDOS - ERRnoaccess setting attributes on file"),
110 #if SAMBA_VERSION >= 2
111 /* Backup attempt of nonexisting directory */
112 AM_ERROR_RE("ERRDOS - ERRbadpath (Directory invalid.)"),
113 AM_NORMAL_RE("^Domain="),
116 /* catch-all: DMP_STRANGE is returned for all other lines */
126 static char *gnutar_list_dir = NULL;
127 static char *incrname = NULL;
129 * doing similar to $ gtar | compression | encryption
139 char tmppath[PATH_MAX];
140 int dumpin, dumpout, compout;
142 char *indexcmd = NULL;
143 char *dirname = NULL;
145 char dumptimestr[80] = "UNUSED";
147 amandates_t *amdates = NULL;
148 time_t prev_dumptime = 0;
149 char *error_pn = NULL;
150 char *compopt = NULL;
151 char *encryptopt = skip_argument;
158 char *amandates_file = NULL;
159 level_t *alevel = (level_t *)dle->levellist->data;
160 int level = alevel->level;
162 error_pn = stralloc2(get_pname(), "-smbclient");
164 qdisk = quote_string(dle->disk);
165 dbprintf(_("start: %s:%s lev %d\n"), host, qdisk, level);
167 g_fprintf(stderr, _("%s: start [%s:%s level %d]\n"),
168 get_pname(), host, qdisk, level);
170 /* apply client-side encryption here */
171 if ( dle->encrypt == ENCRYPT_CUST ) {
172 encpid = pipespawn(dle->clnt_encrypt, STDIN_PIPE, 0,
173 &compout, &dataf, &mesgf,
174 dle->clnt_encrypt, encryptopt, NULL);
175 dbprintf(_("gnutar: pid %ld: %s\n"), (long)encpid, dle->clnt_encrypt);
180 /* now do the client-side compression */
181 if(dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
182 compopt = skip_argument;
183 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
184 if(dle->compress == COMP_BEST) {
185 compopt = COMPRESS_BEST_OPT;
187 compopt = COMPRESS_FAST_OPT;
190 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE, 0,
191 &dumpout, &compout, &mesgf,
192 COMPRESS_PATH, compopt, NULL);
193 dbprintf(_("gnutar: pid %ld: %s"), (long)comppid, COMPRESS_PATH);
194 if(compopt != skip_argument) {
195 dbprintf(_("pid %ld: %s %s\n"),
196 (long)comppid, COMPRESS_PATH, compopt);
198 dbprintf(_("pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
200 } else if (dle->compress == COMP_CUST) {
201 compopt = skip_argument;
202 comppid = pipespawn(dle->compprog, STDIN_PIPE, 0,
203 &dumpout, &compout, &mesgf,
204 dle->compprog, compopt, NULL);
205 if(compopt != skip_argument) {
206 dbprintf(_("pid %ld: %s %s\n"),
207 (long)comppid, dle->compprog, compopt);
209 dbprintf(_("pid %ld: %s\n"), (long)comppid, dle->compprog);
216 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
217 if (strlen(gnutar_list_dir) == 0)
218 gnutar_list_dir = NULL;
220 #ifdef SAMBA_CLIENT /* { */
221 if (dle->device[0] == '/' && dle->device[1]=='/')
225 if (gnutar_list_dir) {
226 char *basename = NULL;
227 char number[NUM_STR_SIZE];
228 char *inputname = NULL;
230 char *sdisk = sanitise_filename(dle->disk);
232 basename = vstralloc(gnutar_list_dir,
239 g_snprintf(number, SIZEOF(number), "%d", level);
240 incrname = vstralloc(basename, "_", number, ".new", NULL);
244 * Open the listed incremental file from the previous level. Search
245 * backward until one is found. If none are found (which will also
246 * be true for a level 0), arrange to read from /dev/null.
251 if (--baselevel >= 0) {
252 g_snprintf(number, SIZEOF(number), "%d", baselevel);
253 inputname = newvstralloc(inputname,
254 basename, "_", number, NULL);
256 inputname = newstralloc(inputname, "/dev/null");
258 if ((infd = open(inputname, O_RDONLY)) == -1) {
259 int save_errno = errno;
260 char *qname = quote_string(inputname);
262 dbprintf(_("gnutar: error opening '%s': %s\n"),
264 strerror(save_errno));
266 error(_("error [opening '%s': %s]"), qname, strerror(save_errno));
274 * Copy the previous listed incremental file to the new one.
276 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
277 error(_("error [opening '%s': %s]"), incrname, strerror(errno));
281 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
282 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
283 error(_("error [writing to '%s': %s]"), incrname,
290 error(_("error [reading from '%s': %s]"), inputname, strerror(errno));
294 if (close(infd) != 0) {
295 error(_("error [closing '%s': %s]"), inputname, strerror(errno));
298 if (close(outfd) != 0) {
299 error(_("error [closing '%s': %s]"), incrname, strerror(errno));
303 tquoted = quote_string(incrname);
305 fquoted = quote_string(inputname);
306 dbprintf(_("doing level %d dump as listed-incremental from '%s' to '%s'\n"),
307 level, fquoted, tquoted);
310 dbprintf(_("doing level %d dump as listed-incremental to '%s'\n"),
317 /* no gnutar-listdir, so we're using amandates */
319 /* find previous dump time, failing completely if there's a problem */
320 amandates_file = getconf_str(CNF_AMANDATES);
321 if(!start_amandates(amandates_file, 0)) {
322 error(_("error [opening %s: %s]"), amandates_file, strerror(errno));
326 amdates = amandates_lookup(dle->disk);
328 prev_dumptime = EPOCH;
329 for(l = 0; l < level; l++) {
330 if(amdates->dates[l] > prev_dumptime)
331 prev_dumptime = amdates->dates[l];
337 gmtm = gmtime(&prev_dumptime);
338 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
339 "%04d-%02d-%02d %2d:%02d:%02d GMT",
340 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
341 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
343 dbprintf(_("gnutar: doing level %d dump from amandates-derived date: %s\n"),
347 dirname = amname_to_dirname(dle->device);
349 cur_dumptime = time(0);
351 cur_disk = stralloc(dle->disk);
353 # define PROGRAM_GNUTAR GNUTAR
355 # define PROGRAM_GNUTAR "tar"
357 indexcmd = vstralloc(
365 #ifdef SAMBA_CLIENT /* { */
366 /* Use sambatar if the disk to back up is a PC disk */
367 if (dle->device[0] == '/' && dle->device[1]=='/') {
368 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
369 char *share = NULL, *subdir = NULL;
377 parsesharename(dle->device, &share, &subdir);
383 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
386 if ((subdir) && (SAMBA_VERSION < 2)) {
391 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
394 if ((user_and_password = findpass(share, &domain)) == NULL) {
396 memset(domain, '\0', strlen(domain));
401 error(_("error [invalid samba host or password not found?]"));
404 lpass = strlen(user_and_password);
405 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
406 memset(user_and_password, '\0', lpass);
407 amfree(user_and_password);
409 memset(domain, '\0', strlen(domain));
414 error(_("password field not \'user%%pass\' for %s"), qdisk);
418 pwtext_len = strlen(pwtext);
419 if ((sharename = makesharename(share, 0)) == 0) {
420 memset(user_and_password, '\0', lpass);
421 amfree(user_and_password);
423 memset(domain, '\0', strlen(domain));
428 error(_("error [can't make share name of %s]"), share);
432 taropt = stralloc("-T");
433 if(dle->exclude_file && dle->exclude_file->nb_element == 1) {
434 strappend(taropt, "X");
436 #if SAMBA_VERSION >= 2
437 strappend(taropt, "q");
439 strappend(taropt, "c");
441 strappend(taropt, "g");
442 } else if (dle->record) {
443 strappend(taropt, "a");
447 dbprintf(_("gnutar: backup of %s/%s\n"), sharename, subdir);
449 dbprintf(_("gnutar: backup of %s\n"), sharename);
452 program->backup_name = program->restore_name = SAMBA_CLIENT;
453 cmd = stralloc(program->backup_name);
454 info_tapeheader(dle);
456 start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
458 if (pwtext_len > 0) {
459 pw_fd_env = "PASSWD_FD";
461 pw_fd_env = "dummy_PASSWD_FD";
463 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE, 0,
464 &dumpin, &dumpout, &mesgf,
468 *user_and_password ? "-U" : skip_argument,
469 *user_and_password ? user_and_password : skip_argument,
471 domain ? "-W" : skip_argument,
472 domain ? domain : skip_argument,
473 #if SAMBA_VERSION >= 2
474 subdir ? "-D" : skip_argument,
475 subdir ? subdir : skip_argument,
480 dle->exclude_file && dle->exclude_file->nb_element == 1 ? dle->exclude_file->first->name : skip_argument,
483 memset(domain, '\0', strlen(domain));
486 if(pwtext_len > 0 && full_write(passwdf, pwtext, pwtext_len) < pwtext_len) {
487 int save_errno = errno;
490 memset(user_and_password, '\0', lpass);
491 amfree(user_and_password);
494 error(_("error [password write failed: %s]"), strerror(save_errno));
497 memset(user_and_password, '\0', lpass);
498 amfree(user_and_password);
506 #endif /*end of samba */
511 GPtrArray *argv_ptr = g_ptr_array_new();
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 cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
524 info_tapeheader(dle);
526 start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
528 g_ptr_array_add(argv_ptr, stralloc("runtar"));
529 if (g_options->config)
530 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
532 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
534 g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
536 g_ptr_array_add(argv_ptr, stralloc("tar"));
538 g_ptr_array_add(argv_ptr, stralloc("--create"));
539 g_ptr_array_add(argv_ptr, stralloc("--file"));
540 g_ptr_array_add(argv_ptr, stralloc("-"));
541 g_ptr_array_add(argv_ptr, stralloc("--directory"));
542 canonicalize_pathname(dirname, tmppath);
543 g_ptr_array_add(argv_ptr, stralloc(tmppath));
544 g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
545 if (gnutar_list_dir && incrname) {
546 g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
547 g_ptr_array_add(argv_ptr, stralloc(incrname));
549 g_ptr_array_add(argv_ptr, stralloc("--incremental"));
550 g_ptr_array_add(argv_ptr, stralloc("--newer"));
551 g_ptr_array_add(argv_ptr, stralloc(dumptimestr));
553 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
554 /* --atime-preserve causes gnutar to call
555 * utime() after reading files in order to
556 * adjust their atime. However, utime()
557 * updates the file's ctime, so incremental
558 * dumps will think the file has changed. */
559 g_ptr_array_add(argv_ptr, stralloc("--atime-preserve"));
561 g_ptr_array_add(argv_ptr, stralloc("--sparse"));
562 g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
563 g_ptr_array_add(argv_ptr, stralloc("--totals"));
566 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
567 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
571 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
572 g_ptr_array_add(argv_ptr, stralloc(file_include));
575 g_ptr_array_add(argv_ptr, stralloc("."));
577 g_ptr_array_add(argv_ptr, NULL);
578 dumppid = pipespawnv(cmd, STDIN_PIPE, 0,
579 &dumpin, &dumpout, &mesgf,
580 (char **)argv_ptr->pdata);
582 amfree(file_exclude);
583 amfree(file_include);
584 g_ptr_array_free_full(argv_ptr);
586 dbprintf(_("gnutar: %s: pid %ld\n"), cmd, (long)dumppid);
594 /* close the write ends of the pipes */
601 if (dle->create_index)
610 char *amandates_file = NULL;
612 if(dle->record && !goterror) {
613 if (incrname != NULL && strlen(incrname) > 4) {
616 nodotnew = stralloc(incrname);
617 nodotnew[strlen(nodotnew)-4] = '\0';
618 if (rename(incrname, nodotnew)) {
619 g_fprintf(stderr, _("%s: warning [renaming %s to %s: %s]\n"),
620 get_pname(), incrname, nodotnew, strerror(errno));
626 /* update the amandates file */
627 amandates_file = getconf_str(CNF_AMANDATES);
628 if(start_amandates(amandates_file, 1)) {
629 amandates_updateone(cur_disk, cur_level, cur_dumptime);
633 /* failure is only fatal if we didn't get a gnutar-listdir */
634 char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
635 if (!gnutar_list_dir || !*gnutar_list_dir) {
636 error(_("error [opening %s for writing: %s]"), amandates_file, strerror(errno));
639 g_debug(_("non-fatal error opening '%s' for writing: %s]"),
640 amandates_file, strerror(errno));
646 backup_program_t gnutar_program = {
653 re_table, start_backup, end_backup