2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 University of Maryland at College Park
4 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
28 * $Id: sendbackup-gnutar.c,v 1.98 2006/07/25 18:35:21 martinea Exp $
30 * send backup data using GNU tar
34 #include "sendbackup.h"
35 #include "amandates.h"
38 #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;
160 am_level_t *alevel = (am_level_t *)dle->levellist->data;
161 int level = alevel->level;
163 error_pn = stralloc2(get_pname(), "-smbclient");
165 qdisk = quote_string(dle->disk);
166 dbprintf(_("start: %s:%s lev %d\n"), host, qdisk, level);
168 g_fprintf(stderr, _("%s: start [%s:%s level %d]\n"),
169 get_pname(), host, qdisk, level);
171 /* apply client-side encryption here */
172 if ( dle->encrypt == ENCRYPT_CUST ) {
173 encpid = pipespawn(dle->clnt_encrypt, STDIN_PIPE, 0,
174 &compout, &dataf, &mesgf,
175 dle->clnt_encrypt, encryptopt, NULL);
176 dbprintf(_("gnutar: pid %ld: %s\n"), (long)encpid, dle->clnt_encrypt);
181 /* now do the client-side compression */
182 if(dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
183 compopt = skip_argument;
184 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
185 if(dle->compress == COMP_BEST) {
186 compopt = COMPRESS_BEST_OPT;
188 compopt = COMPRESS_FAST_OPT;
191 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE, 0,
192 &dumpout, &compout, &mesgf,
193 COMPRESS_PATH, compopt, NULL);
194 dbprintf(_("gnutar: pid %ld: %s"), (long)comppid, COMPRESS_PATH);
195 if(compopt != skip_argument) {
196 dbprintf(_("pid %ld: %s %s\n"),
197 (long)comppid, COMPRESS_PATH, compopt);
199 dbprintf(_("pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
201 } else if (dle->compress == COMP_CUST) {
202 compopt = skip_argument;
203 comppid = pipespawn(dle->compprog, STDIN_PIPE, 0,
204 &dumpout, &compout, &mesgf,
205 dle->compprog, compopt, NULL);
206 if(compopt != skip_argument) {
207 dbprintf(_("pid %ld: %s %s\n"),
208 (long)comppid, dle->compprog, compopt);
210 dbprintf(_("pid %ld: %s\n"), (long)comppid, dle->compprog);
217 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
218 if (strlen(gnutar_list_dir) == 0)
219 gnutar_list_dir = NULL;
221 #ifdef SAMBA_CLIENT /* { */
222 if (dle->device[0] == '/' && dle->device[1]=='/')
226 if (gnutar_list_dir) {
227 char *basename = NULL;
228 char number[NUM_STR_SIZE];
229 char *inputname = NULL;
231 char *sdisk = sanitise_filename(dle->disk);
233 basename = vstralloc(gnutar_list_dir,
240 g_snprintf(number, SIZEOF(number), "%d", level);
241 incrname = vstralloc(basename, "_", number, ".new", NULL);
245 * Open the listed incremental file from the previous level. Search
246 * backward until one is found. If none are found (which will also
247 * be true for a level 0), arrange to read from /dev/null.
252 if (--baselevel >= 0) {
253 g_snprintf(number, SIZEOF(number), "%d", baselevel);
254 inputname = newvstralloc(inputname,
255 basename, "_", number, NULL);
257 inputname = newstralloc(inputname, "/dev/null");
259 if ((infd = open(inputname, O_RDONLY)) == -1) {
260 int save_errno = errno;
261 char *qname = quote_string(inputname);
263 dbprintf(_("gnutar: error opening '%s': %s\n"),
265 strerror(save_errno));
267 error(_("error [opening '%s': %s]"), qname, strerror(save_errno));
275 * Copy the previous listed incremental file to the new one.
277 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
278 error(_("error [opening '%s': %s]"), incrname, strerror(errno));
282 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
283 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
284 error(_("error [writing to '%s': %s]"), incrname,
291 error(_("error [reading from '%s': %s]"), inputname, strerror(errno));
295 if (close(infd) != 0) {
296 error(_("error [closing '%s': %s]"), inputname, strerror(errno));
299 if (close(outfd) != 0) {
300 error(_("error [closing '%s': %s]"), incrname, strerror(errno));
304 tquoted = quote_string(incrname);
306 fquoted = quote_string(inputname);
307 dbprintf(_("doing level %d dump as listed-incremental from '%s' to '%s'\n"),
308 level, fquoted, tquoted);
311 dbprintf(_("doing level %d dump as listed-incremental to '%s'\n"),
318 /* no gnutar-listdir, so we're using amandates */
320 /* find previous dump time, failing completely if there's a problem */
321 amandates_file = getconf_str(CNF_AMANDATES);
322 if(!start_amandates(amandates_file, 0)) {
323 error(_("error [opening %s: %s]"), amandates_file, strerror(errno));
327 amdates = amandates_lookup(dle->disk);
329 prev_dumptime = EPOCH;
330 for(l = 0; l < level; l++) {
331 if(amdates->dates[l] > prev_dumptime)
332 prev_dumptime = amdates->dates[l];
338 gmtm = gmtime(&prev_dumptime);
339 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
340 "%04d-%02d-%02d %2d:%02d:%02d GMT",
341 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
342 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
344 dbprintf(_("gnutar: doing level %d dump from amandates-derived date: %s\n"),
348 dirname = amname_to_dirname(dle->device);
350 cur_dumptime = time(0);
352 cur_disk = stralloc(dle->disk);
354 # define PROGRAM_GNUTAR GNUTAR
356 # define PROGRAM_GNUTAR "tar"
358 indexcmd = vstralloc(
366 #ifdef SAMBA_CLIENT /* { */
367 /* Use sambatar if the disk to back up is a PC disk */
368 if (dle->device[0] == '/' && dle->device[1]=='/') {
369 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
370 char *share = NULL, *subdir = NULL;
378 parsesharename(dle->device, &share, &subdir);
384 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
387 if ((subdir) && (SAMBA_VERSION < 2)) {
392 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
395 if ((user_and_password = findpass(share, &domain)) == NULL) {
397 memset(domain, '\0', strlen(domain));
402 error(_("error [invalid samba host or password not found?]"));
405 lpass = strlen(user_and_password);
406 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
407 memset(user_and_password, '\0', lpass);
408 amfree(user_and_password);
410 memset(domain, '\0', strlen(domain));
415 error(_("password field not \'user%%pass\' for %s"), qdisk);
419 pwtext_len = strlen(pwtext);
420 if ((sharename = makesharename(share, 0)) == 0) {
421 memset(user_and_password, '\0', lpass);
422 amfree(user_and_password);
424 memset(domain, '\0', strlen(domain));
429 error(_("error [can't make share name of %s]"), share);
433 taropt = stralloc("-T");
434 if(dle->exclude_file && dle->exclude_file->nb_element == 1) {
435 strappend(taropt, "X");
437 #if SAMBA_VERSION >= 2
438 strappend(taropt, "q");
440 strappend(taropt, "c");
442 strappend(taropt, "g");
443 } else if (dle->record) {
444 strappend(taropt, "a");
448 dbprintf(_("gnutar: backup of %s/%s\n"), sharename, subdir);
450 dbprintf(_("gnutar: backup of %s\n"), sharename);
453 program->backup_name = program->restore_name = SAMBA_CLIENT;
454 cmd = stralloc(program->backup_name);
455 info_tapeheader(dle);
457 start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
459 if (pwtext_len > 0) {
460 pw_fd_env = "PASSWD_FD";
462 pw_fd_env = "dummy_PASSWD_FD";
464 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE, 0,
465 &dumpin, &dumpout, &mesgf,
469 *user_and_password ? "-U" : skip_argument,
470 *user_and_password ? user_and_password : skip_argument,
472 domain ? "-W" : skip_argument,
473 domain ? domain : skip_argument,
474 #if SAMBA_VERSION >= 2
475 subdir ? "-D" : skip_argument,
476 subdir ? subdir : skip_argument,
481 dle->exclude_file && dle->exclude_file->nb_element == 1 ? dle->exclude_file->first->name : skip_argument,
484 memset(domain, '\0', strlen(domain));
487 if(pwtext_len > 0 && full_write(passwdf, pwtext, pwtext_len) < pwtext_len) {
488 int save_errno = errno;
491 memset(user_and_password, '\0', lpass);
492 amfree(user_and_password);
495 error(_("error [password write failed: %s]"), strerror(save_errno));
498 memset(user_and_password, '\0', lpass);
499 amfree(user_and_password);
507 #endif /*end of samba */
512 GPtrArray *argv_ptr = g_ptr_array_new();
513 char *file_exclude = NULL;
514 char *file_include = NULL;
516 if (dle->exclude_file) nb_exclude+=dle->exclude_file->nb_element;
517 if (dle->exclude_list) nb_exclude+=dle->exclude_list->nb_element;
518 if (dle->include_file) nb_include+=dle->include_file->nb_element;
519 if (dle->include_list) nb_include+=dle->include_list->nb_element;
521 if (nb_exclude > 0) file_exclude = build_exclude(dle, 0);
522 if (nb_include > 0) file_include = build_include(dle, 0);
524 cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
525 info_tapeheader(dle);
527 start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
529 g_ptr_array_add(argv_ptr, stralloc("runtar"));
530 if (g_options->config)
531 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
533 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
535 g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
537 g_ptr_array_add(argv_ptr, stralloc("tar"));
539 g_ptr_array_add(argv_ptr, stralloc("--create"));
540 g_ptr_array_add(argv_ptr, stralloc("--file"));
541 g_ptr_array_add(argv_ptr, stralloc("-"));
542 g_ptr_array_add(argv_ptr, stralloc("--directory"));
543 canonicalize_pathname(dirname, tmppath);
544 g_ptr_array_add(argv_ptr, stralloc(tmppath));
545 g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
546 if (gnutar_list_dir && incrname) {
547 g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
548 g_ptr_array_add(argv_ptr, stralloc(incrname));
550 g_ptr_array_add(argv_ptr, stralloc("--incremental"));
551 g_ptr_array_add(argv_ptr, stralloc("--newer"));
552 g_ptr_array_add(argv_ptr, stralloc(dumptimestr));
554 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
555 /* --atime-preserve causes gnutar to call
556 * utime() after reading files in order to
557 * adjust their atime. However, utime()
558 * updates the file's ctime, so incremental
559 * dumps will think the file has changed. */
560 g_ptr_array_add(argv_ptr, stralloc("--atime-preserve"));
562 g_ptr_array_add(argv_ptr, stralloc("--sparse"));
563 g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
564 g_ptr_array_add(argv_ptr, stralloc("--totals"));
567 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
568 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
572 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
573 g_ptr_array_add(argv_ptr, stralloc(file_include));
576 g_ptr_array_add(argv_ptr, stralloc("."));
578 g_ptr_array_add(argv_ptr, NULL);
579 dumppid = pipespawnv(cmd, STDIN_PIPE, 0,
580 &dumpin, &dumpout, &mesgf,
581 (char **)argv_ptr->pdata);
583 amfree(file_exclude);
584 amfree(file_include);
585 g_ptr_array_free_full(argv_ptr);
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