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;
129 static char *amandates_file;
131 * doing similar to $ gtar | compression | encryption
144 char tmppath[PATH_MAX];
145 int dumpin, dumpout, compout;
147 char *indexcmd = NULL;
148 char *dirname = NULL;
150 char dumptimestr[80];
152 amandates_t *amdates;
153 time_t prev_dumptime;
154 char *error_pn = NULL;
155 char *compopt = NULL;
156 char *encryptopt = skip_argument;
164 (void)dumpdate; /* Quiet unused parameter warning */
166 error_pn = stralloc2(get_pname(), "-smbclient");
168 qdisk = quote_string(disk);
169 dbprintf(_("start: %s:%s lev %d\n"), host, qdisk, level);
171 g_fprintf(stderr, _("%s: start [%s:%s level %d]\n"),
172 get_pname(), host, qdisk, level);
174 /* apply client-side encryption here */
175 if ( options->encrypt == ENCRYPT_CUST ) {
176 encpid = pipespawn(options->clnt_encrypt, STDIN_PIPE,
177 &compout, &dataf, &mesgf,
178 options->clnt_encrypt, encryptopt, NULL);
179 dbprintf(_("gnutar: pid %ld: %s\n"), (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(_("gnutar: pid %ld: %s"), (long)comppid, COMPRESS_PATH);
198 if(compopt != skip_argument) {
199 dbprintf(_("pid %ld: %s %s\n"),
200 (long)comppid, COMPRESS_PATH, compopt);
202 dbprintf(_("pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
204 } else if (options->compress == COMP_CUST) {
205 compopt = skip_argument;
206 comppid = pipespawn(options->clntcompprog, STDIN_PIPE,
207 &dumpout, &compout, &mesgf,
208 options->clntcompprog, compopt, NULL);
209 if(compopt != skip_argument) {
210 dbprintf(_("pid %ld: %s %s\n"),
211 (long)comppid, options->clntcompprog, compopt);
213 dbprintf(_("pid %ld: %s\n"), (long)comppid, options->clntcompprog);
220 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
221 if (strlen(gnutar_list_dir) == 0)
222 gnutar_list_dir = NULL;
224 #ifdef SAMBA_CLIENT /* { */
225 if (amdevice[0] == '/' && amdevice[1]=='/')
229 if (gnutar_list_dir) {
230 char *basename = NULL;
231 char number[NUM_STR_SIZE];
232 char *inputname = NULL;
234 char *sdisk = sanitise_filename(disk);
236 basename = vstralloc(gnutar_list_dir,
243 g_snprintf(number, SIZEOF(number), "%d", level);
244 incrname = vstralloc(basename, "_", number, ".new", NULL);
248 * Open the listed incremental file from the previous level. Search
249 * backward until one is found. If none are found (which will also
250 * be true for a level 0), arrange to read from /dev/null.
255 if (--baselevel >= 0) {
256 g_snprintf(number, SIZEOF(number), "%d", baselevel);
257 inputname = newvstralloc(inputname,
258 basename, "_", number, NULL);
260 inputname = newstralloc(inputname, "/dev/null");
262 if ((infd = open(inputname, O_RDONLY)) == -1) {
263 int save_errno = errno;
264 char *qname = quote_string(inputname);
266 dbprintf(_("gnutar: error opening '%s': %s\n"),
268 strerror(save_errno));
270 error(_("error [opening '%s': %s]"), qname, strerror(save_errno));
278 * Copy the previous listed incremental file to the new one.
280 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
281 error(_("error [opening '%s': %s]"), incrname, strerror(errno));
285 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
286 if (fullwrite(outfd, &buf, (size_t)nb) < nb) {
287 error(_("error [writing to '%s': %s]"), incrname,
294 error(_("error [reading from '%s': %s]"), inputname, strerror(errno));
298 if (close(infd) != 0) {
299 error(_("error [closing '%s': %s]"), inputname, strerror(errno));
302 if (close(outfd) != 0) {
303 error(_("error [closing '%s': %s]"), incrname, strerror(errno));
307 tquoted = quote_string(incrname);
309 fquoted = quote_string(inputname);
310 dbprintf(_("doing level %d dump as listed-incremental from '%s' to '%s'\n"),
311 level, fquoted, tquoted);
314 dbprintf(_("doing level %d dump as listed-incremental to '%s'\n"),
322 /* find previous dump time */
324 amandates_file = getconf_str(CNF_AMANDATES);
325 if(!start_amandates(amandates_file, 0)) {
326 error(_("error [opening %s: %s]"), amandates_file, strerror(errno));
330 amdates = amandates_lookup(disk);
332 prev_dumptime = EPOCH;
333 for(l = 0; l < level; l++) {
334 if(amdates->dates[l] > prev_dumptime)
335 prev_dumptime = amdates->dates[l];
341 gmtm = gmtime(&prev_dumptime);
342 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
343 "%04d-%02d-%02d %2d:%02d:%02d GMT",
344 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
345 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
347 dbprintf(_("gnutar: doing level %d dump from date: %s\n"),
350 dirname = amname_to_dirname(amdevice);
352 cur_dumptime = time(0);
354 cur_disk = stralloc(disk);
356 # define PROGRAM_GNUTAR GNUTAR
358 # define PROGRAM_GNUTAR "tar"
360 indexcmd = vstralloc(
368 #ifdef SAMBA_CLIENT /* { */
369 /* Use sambatar if the disk to back up is a PC disk */
370 if (amdevice[0] == '/' && amdevice[1]=='/') {
371 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
372 char *share = NULL, *subdir = NULL;
380 parsesharename(amdevice, &share, &subdir);
386 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
389 if ((subdir) && (SAMBA_VERSION < 2)) {
394 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
397 if ((user_and_password = findpass(share, &domain)) == NULL) {
399 memset(domain, '\0', strlen(domain));
404 error(_("error [invalid samba host or password not found?]"));
407 lpass = strlen(user_and_password);
408 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
409 memset(user_and_password, '\0', lpass);
410 amfree(user_and_password);
412 memset(domain, '\0', strlen(domain));
417 error(_("password field not \'user%%pass\' for %s"), qdisk);
421 pwtext_len = strlen(pwtext);
422 if ((sharename = makesharename(share, 0)) == 0) {
423 memset(user_and_password, '\0', lpass);
424 amfree(user_and_password);
426 memset(domain, '\0', strlen(domain));
431 error(_("error [can't make share name of %s]"), share);
435 taropt = stralloc("-T");
436 if(options->exclude_file && options->exclude_file->nb_element == 1) {
437 strappend(taropt, "X");
439 #if SAMBA_VERSION >= 2
440 strappend(taropt, "q");
442 strappend(taropt, "c");
444 strappend(taropt, "g");
445 } else if (!options->no_record) {
446 strappend(taropt, "a");
450 dbprintf(_("gnutar: backup of %s/%s\n"), sharename, subdir);
452 dbprintf(_("gnutar: backup of %s\n"), sharename);
455 program->backup_name = program->restore_name = SAMBA_CLIENT;
456 cmd = stralloc(program->backup_name);
459 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
461 if (pwtext_len > 0) {
462 pw_fd_env = "PASSWD_FD";
464 pw_fd_env = "dummy_PASSWD_FD";
466 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE,
467 &dumpin, &dumpout, &mesgf,
471 *user_and_password ? "-U" : skip_argument,
472 *user_and_password ? user_and_password : skip_argument,
474 domain ? "-W" : skip_argument,
475 domain ? domain : skip_argument,
476 #if SAMBA_VERSION >= 2
477 subdir ? "-D" : skip_argument,
478 subdir ? subdir : skip_argument,
483 options->exclude_file && options->exclude_file->nb_element == 1 ? options->exclude_file->first->name : skip_argument,
486 memset(domain, '\0', strlen(domain));
489 if(pwtext_len > 0 && fullwrite(passwdf, pwtext, pwtext_len) < 0) {
490 int save_errno = errno;
493 memset(user_and_password, '\0', lpass);
494 amfree(user_and_password);
497 error(_("error [password write failed: %s]"), strerror(save_errno));
500 memset(user_and_password, '\0', lpass);
501 amfree(user_and_password);
509 #endif /*end of samba */
516 char *file_exclude = NULL;
517 char *file_include = NULL;
519 if(options->exclude_file) nb_exclude+=options->exclude_file->nb_element;
520 if(options->exclude_list) nb_exclude+=options->exclude_list->nb_element;
521 if(options->include_file) nb_include+=options->include_file->nb_element;
522 if(options->include_list) nb_include+=options->include_list->nb_element;
524 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
525 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
527 my_argv = alloc(SIZEOF(char *) * (22 + (nb_exclude*2)+(nb_include*2)));
529 cmd = vstralloc(amlibexecdir, "/", "runtar", versionsuffix(), NULL);
532 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
534 my_argv[i++] = "runtar";
535 if (g_options->config)
536 my_argv[i++] = g_options->config;
538 my_argv[i++] = "NOCONFIG";
540 my_argv[i++] = GNUTAR;
542 my_argv[i++] = "tar";
544 my_argv[i++] = "--create";
545 my_argv[i++] = "--file";
547 my_argv[i++] = "--directory";
548 canonicalize_pathname(dirname, tmppath);
549 my_argv[i++] = tmppath;
550 my_argv[i++] = "--one-file-system";
551 if (gnutar_list_dir && incrname) {
552 my_argv[i++] = "--listed-incremental";
553 my_argv[i++] = incrname;
555 my_argv[i++] = "--incremental";
556 my_argv[i++] = "--newer";
557 my_argv[i++] = dumptimestr;
559 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
560 /* --atime-preserve causes gnutar to call
561 * utime() after reading files in order to
562 * adjust their atime. However, utime()
563 * updates the file's ctime, so incremental
564 * dumps will think the file has changed. */
565 my_argv[i++] = "--atime-preserve";
567 my_argv[i++] = "--sparse";
568 my_argv[i++] = "--ignore-failed-read";
569 my_argv[i++] = "--totals";
572 my_argv[i++] = "--exclude-from";
573 my_argv[i++] = file_exclude;
577 my_argv[i++] = "--files-from";
578 my_argv[i++] = file_include;
584 dumppid = pipespawnv(cmd, STDIN_PIPE,
585 &dumpin, &dumpout, &mesgf, my_argv);
587 amfree(file_exclude);
588 amfree(file_include);
591 dbprintf(_("gnutar: %s: pid %ld\n"), cmd, (long)dumppid);
599 /* close the write ends of the pipes */
606 if (options->createindex)
614 if(!options->no_record && !goterror) {
615 if (incrname != NULL && strlen(incrname) > 4) {
618 nodotnew = stralloc(incrname);
619 nodotnew[strlen(nodotnew)-4] = '\0';
620 if (rename(incrname, nodotnew)) {
621 g_fprintf(stderr, _("%s: warning [renaming %s to %s: %s]\n"),
622 get_pname(), incrname, nodotnew, strerror(errno));
628 if(!start_amandates(amandates_file, 1)) {
629 g_fprintf(stderr, _("%s: warning [opening %s: %s]"), get_pname(),
630 amandates_file, strerror(errno));
633 amandates_updateone(cur_disk, cur_level, cur_dumptime);
640 backup_program_t gnutar_program = {
647 re_table, start_backup, end_backup