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"),
69 AM_NORMAL_RE("^Domain="),
72 AM_NORMAL_RE("^doing parameter"),
73 AM_NORMAL_RE("^pm_process\\(\\)"),
74 AM_NORMAL_RE("^adding IPC"),
75 AM_NORMAL_RE("^Opening"),
76 AM_NORMAL_RE("^Connect"),
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;
129 static char *amandates_file;
131 * doing similar to $ gtar | compression | encryption
144 int dumpin, dumpout, compout;
146 char *indexcmd = NULL;
147 char *dirname = NULL;
149 char dumptimestr[80];
151 amandates_t *amdates;
152 time_t prev_dumptime;
153 char *error_pn = NULL;
154 char *compopt = NULL;
155 char *encryptopt = skip_argument;
162 (void)dumpdate; /* Quiet unused parameter warning */
164 error_pn = stralloc2(get_pname(), "-smbclient");
166 qdisk = quote_string(disk);
167 dbprintf(("%s: start: %s:%s lev %d\n",
168 get_pname(), host, qdisk, level));
170 fprintf(stderr, "%s: start [%s:%s level %d]\n",
171 get_pname(), host, qdisk, level);
173 /* apply client-side encryption here */
174 if ( options->encrypt == ENCRYPT_CUST ) {
175 encpid = pipespawn(options->clnt_encrypt, STDIN_PIPE,
176 &compout, &dataf, &mesgf,
177 options->clnt_encrypt, encryptopt, NULL);
178 dbprintf(("%s: pid %ld: %s\n",
179 debug_prefix_time("-gnutar"), (long)encpid, options->clnt_encrypt));
184 /* now do the client-side compression */
185 if(options->compress == COMP_FAST || options->compress == COMP_BEST) {
186 compopt = skip_argument;
187 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
188 if(options->compress == COMP_BEST) {
189 compopt = COMPRESS_BEST_OPT;
191 compopt = COMPRESS_FAST_OPT;
194 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE,
195 &dumpout, &compout, &mesgf,
196 COMPRESS_PATH, compopt, NULL);
197 dbprintf(("%s: pid %ld: %s",
198 debug_prefix_time("-gnutar"), (long)comppid, COMPRESS_PATH));
199 if(compopt != skip_argument) {
200 dbprintf((" %s", compopt));
203 } else if (options->compress == COMP_CUST) {
204 compopt = skip_argument;
205 comppid = pipespawn(options->clntcompprog, STDIN_PIPE,
206 &dumpout, &compout, &mesgf,
207 options->clntcompprog, compopt, NULL);
208 dbprintf(("%s: pid %ld: %s",
209 debug_prefix_time("-gnutar-cust"), (long)comppid, options->clntcompprog));
210 if(compopt != skip_argument) {
211 dbprintf((" %s", compopt));
219 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
220 if (strlen(gnutar_list_dir) == 0)
221 gnutar_list_dir = NULL;
223 #ifdef SAMBA_CLIENT /* { */
224 if (amdevice[0] == '/' && amdevice[1]=='/')
228 if (gnutar_list_dir) {
229 char *basename = NULL;
230 char number[NUM_STR_SIZE];
233 char *inputname = NULL;
236 basename = vstralloc(gnutar_list_dir,
242 * The loop starts at the first character of the host name,
245 s = basename + strlen(gnutar_list_dir) + 1;
246 while((ch = *s++) != '\0') {
251 snprintf(number, SIZEOF(number), "%d", level);
252 incrname = vstralloc(basename, "_", number, ".new", NULL);
256 * Open the listed incremental file from the previous level. Search
257 * backward until one is found. If none are found (which will also
258 * be true for a level 0), arrange to read from /dev/null.
263 if (--baselevel >= 0) {
264 snprintf(number, SIZEOF(number), "%d", baselevel);
265 inputname = newvstralloc(inputname,
266 basename, "_", number, NULL);
268 inputname = newstralloc(inputname, "/dev/null");
270 if ((infd = open(inputname, O_RDONLY)) == -1) {
271 int save_errno = errno;
272 char *qname = quote_string(inputname);
274 dbprintf(("%s: error opening '%s': %s\n",
275 debug_prefix_time("-gnutar"),
277 strerror(save_errno)));
279 error("error [opening '%s': %s]", qname, strerror(save_errno));
287 * Copy the previous listed incremental file to the new one.
289 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
290 error("error [opening '%s': %s]", incrname, strerror(errno));
294 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
295 if (fullwrite(outfd, &buf, (size_t)nb) < nb) {
296 error("error [writing to '%s': %s]", incrname,
303 error("error [reading from '%s': %s]", inputname, strerror(errno));
307 if (close(infd) != 0) {
308 error("error [closing '%s': %s]", inputname, strerror(errno));
311 if (close(outfd) != 0) {
312 error("error [closing '%s': %s]", incrname, strerror(errno));
316 dbprintf(("%s: doing level %d dump as listed-incremental",
317 debug_prefix_time("-gnutar"), level));
319 quoted = quote_string(inputname);
320 dbprintf((" from '%s'", quoted));
323 quoted = quote_string(incrname);
324 dbprintf((" to '%s'\n", quoted));
330 /* find previous dump time */
332 amandates_file = getconf_str(CNF_AMANDATES);
333 if(!start_amandates(amandates_file, 0)) {
334 error("error [opening %s: %s]", amandates_file, strerror(errno));
338 amdates = amandates_lookup(disk);
340 prev_dumptime = EPOCH;
341 for(l = 0; l < level; l++) {
342 if(amdates->dates[l] > prev_dumptime)
343 prev_dumptime = amdates->dates[l];
349 gmtm = gmtime(&prev_dumptime);
350 snprintf(dumptimestr, SIZEOF(dumptimestr),
351 "%04d-%02d-%02d %2d:%02d:%02d GMT",
352 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
353 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
355 dbprintf(("%s: doing level %d dump from date: %s\n",
356 debug_prefix_time("-gnutar"), level, dumptimestr));
358 dirname = amname_to_dirname(amdevice);
360 cur_dumptime = time(0);
362 cur_disk = stralloc(disk);
364 # define PROGRAM_GNUTAR GNUTAR
366 # define PROGRAM_GNUTAR "tar"
368 indexcmd = vstralloc(
376 #ifdef SAMBA_CLIENT /* { */
377 /* Use sambatar if the disk to back up is a PC disk */
378 if (amdevice[0] == '/' && amdevice[1]=='/') {
379 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
380 char *share = NULL, *subdir = NULL;
388 parsesharename(amdevice, &share, &subdir);
394 error("cannot parse disk entry %s for share/subdir", qdisk);
397 if ((subdir) && (SAMBA_VERSION < 2)) {
402 error("subdirectory specified for share %s but samba not v2 or better", qdisk);
405 if ((user_and_password = findpass(share, &domain)) == NULL) {
407 memset(domain, '\0', strlen(domain));
412 error("error [invalid samba host or password not found?]");
415 lpass = strlen(user_and_password);
416 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
417 memset(user_and_password, '\0', lpass);
418 amfree(user_and_password);
420 memset(domain, '\0', strlen(domain));
425 error("password field not \'user%%pass\' for %s", qdisk);
429 pwtext_len = strlen(pwtext);
430 if ((sharename = makesharename(share, 0)) == 0) {
431 memset(user_and_password, '\0', lpass);
432 amfree(user_and_password);
434 memset(domain, '\0', strlen(domain));
439 error("error [can't make share name of %s]", share);
443 taropt = stralloc("-T");
444 if(options->exclude_file && options->exclude_file->nb_element == 1) {
445 strappend(taropt, "X");
447 #if SAMBA_VERSION >= 2
448 strappend(taropt, "q");
450 strappend(taropt, "c");
452 strappend(taropt, "g");
453 } else if (!options->no_record) {
454 strappend(taropt, "a");
457 dbprintf(("%s: backup of %s", debug_prefix_time("-gnutar"), sharename));
459 dbprintf(("/%s",subdir));
463 program->backup_name = program->restore_name = SAMBA_CLIENT;
464 cmd = stralloc(program->backup_name);
467 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
469 if (pwtext_len > 0) {
470 pw_fd_env = "PASSWD_FD";
472 pw_fd_env = "dummy_PASSWD_FD";
474 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE,
475 &dumpin, &dumpout, &mesgf,
479 *user_and_password ? "-U" : skip_argument,
480 *user_and_password ? user_and_password : skip_argument,
482 domain ? "-W" : skip_argument,
483 domain ? domain : skip_argument,
484 #if SAMBA_VERSION >= 2
485 subdir ? "-D" : skip_argument,
486 subdir ? subdir : skip_argument,
491 options->exclude_file && options->exclude_file->nb_element == 1 ? options->exclude_file->first->name : skip_argument,
494 memset(domain, '\0', strlen(domain));
497 if(pwtext_len > 0 && fullwrite(passwdf, pwtext, pwtext_len) < 0) {
498 int save_errno = errno;
501 memset(user_and_password, '\0', lpass);
502 amfree(user_and_password);
505 error("error [password write failed: %s]", strerror(save_errno));
508 memset(user_and_password, '\0', lpass);
509 amfree(user_and_password);
517 #endif /*end of samba */
524 char *file_exclude = NULL;
525 char *file_include = NULL;
527 if(options->exclude_file) nb_exclude+=options->exclude_file->nb_element;
528 if(options->exclude_list) nb_exclude+=options->exclude_list->nb_element;
529 if(options->include_file) nb_include+=options->include_file->nb_element;
530 if(options->include_list) nb_include+=options->include_list->nb_element;
532 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
533 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
535 my_argv = alloc(SIZEOF(char *) * (22 + (nb_exclude*2)+(nb_include*2)));
537 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
540 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
542 my_argv[i++] = "runtar";
543 if (g_options->config)
544 my_argv[i++] = g_options->config;
546 my_argv[i++] = "NOCONFIG";
547 my_argv[i++] = "gtar";
548 my_argv[i++] = "--create";
549 my_argv[i++] = "--file";
551 my_argv[i++] = "--directory";
552 my_argv[i++] = dirname;
553 my_argv[i++] = "--one-file-system";
554 if (gnutar_list_dir && incrname) {
555 my_argv[i++] = "--listed-incremental";
556 my_argv[i++] = incrname;
558 my_argv[i++] = "--incremental";
559 my_argv[i++] = "--newer";
560 my_argv[i++] = dumptimestr;
562 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
563 /* --atime-preserve causes gnutar to call
564 * utime() after reading files in order to
565 * adjust their atime. However, utime()
566 * updates the file's ctime, so incremental
567 * dumps will think the file has changed. */
568 my_argv[i++] = "--atime-preserve";
570 my_argv[i++] = "--sparse";
571 my_argv[i++] = "--ignore-failed-read";
572 my_argv[i++] = "--totals";
575 my_argv[i++] = "--exclude-from";
576 my_argv[i++] = file_exclude;
580 my_argv[i++] = "--files-from";
581 my_argv[i++] = file_include;
587 dumppid = pipespawnv(cmd, STDIN_PIPE,
588 &dumpin, &dumpout, &mesgf, my_argv);
590 amfree(file_exclude);
591 amfree(file_include);
594 dbprintf(("%s: %s: pid %ld\n",
595 debug_prefix_time("-gnutar"),
605 /* close the write ends of the pipes */
612 if (options->createindex)
620 if(!options->no_record && !goterror) {
621 if (incrname != NULL && strlen(incrname) > 4) {
624 nodotnew = stralloc(incrname);
625 nodotnew[strlen(nodotnew)-4] = '\0';
626 if (rename(incrname, nodotnew)) {
627 fprintf(stderr, "%s: warning [renaming %s to %s: %s]\n",
628 get_pname(), incrname, nodotnew, strerror(errno));
634 if(!start_amandates(amandates_file, 1)) {
635 fprintf(stderr, "%s: warning [opening %s: %s]", get_pname(),
636 amandates_file, strerror(errno));
639 amandates_updateone(cur_disk, cur_level, cur_dumptime);
646 backup_program_t gnutar_program = {
653 re_table, start_backup, end_backup