Imported Upstream version 3.3.2
[debian/amanda] / client-src / sendbackup-gnutar.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* 
27  * $Id: sendbackup-gnutar.c,v 1.98 2006/07/25 18:35:21 martinea Exp $
28  *
29  * send backup data using GNU tar
30  */
31
32 #include "amanda.h"
33 #include "sendbackup.h"
34 #include "amandates.h"
35 #include "clock.h"
36 #include "util.h"
37 #include "getfsent.h"                   /* for amname_to_dirname lookup */
38 #include "conffile.h"
39
40 #ifdef SAMBA_CLIENT
41 #include "findpass.h"
42 #endif
43
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"),
49
50   /* GNU tar 1.13.17 will print this warning when (not) backing up a
51      Unix named socket.  */
52   AM_NORMAL_RE(": socket ignored$"),
53
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"),
62 #endif
63   
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"),
68
69 #if SAMBA_VERSION < 2
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="),
76   AM_NORMAL_RE("^max"),
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"),
97 #endif
98
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"),
108 #endif
109
110 #if SAMBA_VERSION >= 2
111   /* Backup attempt of nonexisting directory */
112   AM_ERROR_RE("ERRDOS - ERRbadpath (Directory invalid.)"),
113   AM_NORMAL_RE("^Domain="),
114 #endif
115
116   /* catch-all: DMP_STRANGE is returned for all other lines */
117   AM_STRANGE_RE(NULL)
118 };
119
120 extern char *efile;
121
122 int cur_level;
123 char *cur_disk;
124 time_t cur_dumptime;
125
126 static char *gnutar_list_dir = NULL;
127 static char *incrname = NULL;
128 /*
129  *  doing similar to $ gtar | compression | encryption 
130  */
131 static void
132 start_backup(
133     dle_t      *dle,
134     char       *host,
135     int         dataf,
136     int         mesgf,
137     int         indexf)
138 {
139     char tmppath[PATH_MAX];
140     int dumpin, dumpout, compout;
141     char *cmd = NULL;
142     char *indexcmd = NULL;
143     char *dirname = NULL;
144     int l;
145     char dumptimestr[80] = "UNUSED";
146     struct tm *gmtm;
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;
152     char *tquoted;
153     char *fquoted;
154     char *qdisk;
155     int infd, outfd;
156     ssize_t nb;
157     char buf[32768];
158     char *amandates_file = NULL;
159     am_level_t *alevel = (am_level_t *)dle->levellist->data;
160     int      level  = alevel->level;
161
162     error_pn = stralloc2(get_pname(), "-smbclient");
163
164     qdisk = quote_string(dle->disk);
165     dbprintf(_("start: %s:%s lev %d\n"), host, qdisk, level);
166
167     g_fprintf(stderr, _("%s: start [%s:%s level %d]\n"),
168             get_pname(), host, qdisk, level);
169
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);
176     } else {
177        compout = dataf;
178        encpid = -1;
179     } 
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;
186         } else {
187             compopt = COMPRESS_FAST_OPT;
188         }
189 #endif
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);
197         } else {
198             dbprintf(_("pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
199         }
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);
208         } else {
209             dbprintf(_("pid %ld: %s\n"), (long)comppid, dle->compprog);
210         }
211     } else {
212         dumpout = compout;
213         comppid = -1;
214     }
215
216     gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
217     if (strlen(gnutar_list_dir) == 0)
218         gnutar_list_dir = NULL;
219
220 #ifdef SAMBA_CLIENT                                                     /* { */
221     if (dle->device[0] == '/' && dle->device[1]=='/')
222         amfree(incrname);
223     else
224 #endif                                                                  /* } */
225     if (gnutar_list_dir) {
226         char *basename = NULL;
227         char number[NUM_STR_SIZE];
228         char *inputname = NULL;
229         int baselevel;
230         char *sdisk = sanitise_filename(dle->disk);
231
232         basename = vstralloc(gnutar_list_dir,
233                              "/",
234                              host,
235                              sdisk,
236                              NULL);
237         amfree(sdisk);
238
239         g_snprintf(number, SIZEOF(number), "%d", level);
240         incrname = vstralloc(basename, "_", number, ".new", NULL);
241         unlink(incrname);
242
243         /*
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.
247          */
248         baselevel = level;
249         infd = -1;
250         while (infd == -1) {
251             if (--baselevel >= 0) {
252                 g_snprintf(number, SIZEOF(number), "%d", baselevel);
253                 inputname = newvstralloc(inputname,
254                                          basename, "_", number, NULL);
255             } else {
256                 inputname = newstralloc(inputname, "/dev/null");
257             }
258             if ((infd = open(inputname, O_RDONLY)) == -1) {
259                 int save_errno = errno;
260                 char *qname = quote_string(inputname);
261
262                 dbprintf(_("gnutar: error opening '%s': %s\n"),
263                           qname,
264                           strerror(save_errno));
265                 if (baselevel < 0) {
266                     error(_("error [opening '%s': %s]"), qname, strerror(save_errno));
267                     /*NOTREACHED*/
268                 }
269                 amfree(qname);
270             }
271         }
272
273         /*
274          * Copy the previous listed incremental file to the new one.
275          */
276         if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
277             error(_("error [opening '%s': %s]"), incrname, strerror(errno));
278             /*NOTREACHED*/
279         }
280
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,
284                        strerror(errno));
285                 /*NOTREACHED*/
286             }
287         }
288
289         if (nb < 0) {
290             error(_("error [reading from '%s': %s]"), inputname, strerror(errno));
291             /*NOTREACHED*/
292         }
293
294         if (close(infd) != 0) {
295             error(_("error [closing '%s': %s]"), inputname, strerror(errno));
296             /*NOTREACHED*/
297         }
298         if (close(outfd) != 0) {
299             error(_("error [closing '%s': %s]"), incrname, strerror(errno));
300             /*NOTREACHED*/
301         }
302
303         tquoted = quote_string(incrname);
304         if(baselevel >= 0) {
305             fquoted = quote_string(inputname);
306             dbprintf(_("doing level %d dump as listed-incremental from '%s' to '%s'\n"),
307                      level, fquoted, tquoted);
308             amfree(fquoted);
309         } else {
310             dbprintf(_("doing level %d dump as listed-incremental to '%s'\n"),
311                      level, tquoted);
312         }
313         amfree(tquoted);
314         amfree(inputname);
315         amfree(basename);
316     } else {
317         /* no gnutar-listdir, so we're using amandates */
318
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));
323             /*NOTREACHED*/
324         }
325
326         amdates = amandates_lookup(dle->disk);
327
328         prev_dumptime = EPOCH;
329         for(l = 0; l < level; l++) {
330             if(amdates->dates[l] > prev_dumptime)
331                 prev_dumptime = amdates->dates[l];
332         }
333
334         finish_amandates();
335         free_amandates();
336
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);
342
343         dbprintf(_("gnutar: doing level %d dump from amandates-derived date: %s\n"),
344                   level, dumptimestr);
345     }
346
347     dirname = amname_to_dirname(dle->device);
348
349     cur_dumptime = time(0);
350     cur_level = level;
351     cur_disk = stralloc(dle->disk);
352 #ifdef GNUTAR
353 #  define PROGRAM_GNUTAR GNUTAR
354 #else
355 #  define PROGRAM_GNUTAR "tar"
356 #endif
357     indexcmd = vstralloc(
358                          PROGRAM_GNUTAR,
359                          " -tf", " -",
360                          " 2>/dev/null",
361                          " | sed", " -e",
362                          " \'s/^\\.//\'",
363                          NULL);
364
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;
370         char *pwtext = NULL;
371         char *taropt;
372         int passwdf = -1;
373         size_t lpass;
374         size_t pwtext_len;
375         char *pw_fd_env;
376
377         parsesharename(dle->device, &share, &subdir);
378         if (!share) {
379             amfree(share);
380             amfree(subdir);
381             set_pname(error_pn);
382             amfree(error_pn);
383             error(_("cannot parse disk entry %s for share/subdir"), qdisk);
384             /*NOTREACHED*/
385         }
386         if ((subdir) && (SAMBA_VERSION < 2)) {
387             amfree(share);
388             amfree(subdir);
389             set_pname(error_pn);
390             amfree(error_pn);
391             error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
392             /*NOTREACHED*/
393         }
394         if ((user_and_password = findpass(share, &domain)) == NULL) {
395             if(domain) {
396                 memset(domain, '\0', strlen(domain));
397                 amfree(domain);
398             }
399             set_pname(error_pn);
400             amfree(error_pn);
401             error(_("error [invalid samba host or password not found?]"));
402             /*NOTREACHED*/
403         }
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);
408             if(domain) {
409                 memset(domain, '\0', strlen(domain));
410                 amfree(domain);
411             }
412             set_pname(error_pn);
413             amfree(error_pn);
414             error(_("password field not \'user%%pass\' for %s"), qdisk);
415             /*NOTREACHED*/
416         }
417         *pwtext++ = '\0';
418         pwtext_len = strlen(pwtext);
419         if ((sharename = makesharename(share, 0)) == 0) {
420             memset(user_and_password, '\0', lpass);
421             amfree(user_and_password);
422             if(domain) {
423                 memset(domain, '\0', strlen(domain));
424                 amfree(domain);
425             }
426             set_pname(error_pn);
427             amfree(error_pn);
428             error(_("error [can't make share name of %s]"), share);
429             /*NOTREACHED*/
430         }
431
432         taropt = stralloc("-T");
433         if(dle->exclude_file && dle->exclude_file->nb_element == 1) {
434             strappend(taropt, "X");
435         }
436 #if SAMBA_VERSION >= 2
437         strappend(taropt, "q");
438 #endif
439         strappend(taropt, "c");
440         if (level != 0) {
441             strappend(taropt, "g");
442         } else if (dle->record) {
443             strappend(taropt, "a");
444         }
445
446         if (subdir) {
447             dbprintf(_("gnutar: backup of %s/%s\n"), sharename, subdir);
448         } else {
449             dbprintf(_("gnutar: backup of %s\n"), sharename);
450         }
451
452         program->backup_name = program->restore_name = SAMBA_CLIENT;
453         cmd = stralloc(program->backup_name);
454         info_tapeheader(dle);
455
456         start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
457
458         if (pwtext_len > 0) {
459             pw_fd_env = "PASSWD_FD";
460         } else {
461             pw_fd_env = "dummy_PASSWD_FD";
462         }
463         dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE, 0,
464                             &dumpin, &dumpout, &mesgf,
465                             pw_fd_env, &passwdf,
466                             "smbclient",
467                             sharename,
468                             *user_and_password ? "-U" : skip_argument,
469                             *user_and_password ? user_and_password : skip_argument,
470                             "-E",
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,
476 #endif
477                             "-d0",
478                             taropt,
479                             "-",
480                             dle->exclude_file && dle->exclude_file->nb_element == 1 ? dle->exclude_file->first->name : skip_argument,
481                             NULL);
482         if(domain) {
483             memset(domain, '\0', strlen(domain));
484             amfree(domain);
485         }
486         if(pwtext_len > 0 && full_write(passwdf, pwtext, pwtext_len) < pwtext_len) {
487             int save_errno = errno;
488
489             aclose(passwdf);
490             memset(user_and_password, '\0', lpass);
491             amfree(user_and_password);
492             set_pname(error_pn);
493             amfree(error_pn);
494             error(_("error [password write failed: %s]"), strerror(save_errno));
495             /*NOTREACHED*/
496         }
497         memset(user_and_password, '\0', lpass);
498         amfree(user_and_password);
499         aclose(passwdf);
500         amfree(sharename);
501         amfree(share);
502         amfree(subdir);
503         amfree(taropt);
504         tarpid = dumppid;
505     } else
506 #endif                  /*end of samba */
507     {
508
509         int nb_exclude = 0;
510         int nb_include = 0;
511         GPtrArray *argv_ptr = g_ptr_array_new();
512         char *file_exclude = NULL;
513         char *file_include = NULL;
514
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;
519
520         if (nb_exclude > 0) file_exclude = build_exclude(dle, 0);
521         if (nb_include > 0) file_include = build_include(dle, 0);
522
523         cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
524         info_tapeheader(dle);
525
526         start_index(dle->create_index, dumpout, mesgf, indexf, indexcmd);
527
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));
531         else
532             g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
533 #ifdef GNUTAR
534         g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
535 #else
536         g_ptr_array_add(argv_ptr, stralloc("tar"));
537 #endif
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));
548         } else {
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));
552         }
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"));
560 #endif
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"));
564
565         if(file_exclude) {
566             g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
567             g_ptr_array_add(argv_ptr, stralloc(file_exclude));
568         }
569
570         if(file_include) {
571             g_ptr_array_add(argv_ptr, stralloc("--files-from"));
572             g_ptr_array_add(argv_ptr, stralloc(file_include));
573         }
574         else {
575             g_ptr_array_add(argv_ptr, stralloc("."));
576         }
577             g_ptr_array_add(argv_ptr, NULL);
578         dumppid = pipespawnv(cmd, STDIN_PIPE, 0,
579                              &dumpin, &dumpout, &mesgf,
580                              (char **)argv_ptr->pdata);
581         tarpid = dumppid;
582         amfree(file_exclude);
583         amfree(file_include);
584         g_ptr_array_free_full(argv_ptr);
585     }
586     dbprintf(_("gnutar: %s: pid %ld\n"), cmd, (long)dumppid);
587
588     amfree(qdisk);
589     amfree(dirname);
590     amfree(cmd);
591     amfree(indexcmd);
592     amfree(error_pn);
593
594     /* close the write ends of the pipes */
595
596     aclose(dumpin);
597     aclose(dumpout);
598     aclose(compout);
599     aclose(dataf);
600     aclose(mesgf);
601     if (dle->create_index)
602         aclose(indexf);
603 }
604
605 static void
606 end_backup(
607     dle_t      *dle,
608     int         goterror)
609 {
610     char *amandates_file = NULL;
611
612     if(dle->record && !goterror) {
613         if (incrname != NULL && strlen(incrname) > 4) {
614             char *nodotnew;
615         
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));
621             }
622             amfree(nodotnew);
623             amfree(incrname);
624         }
625
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);
630             finish_amandates();
631             free_amandates();
632         } else {
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));
637                 /* NOTREACHED */
638             } else {
639                 g_debug(_("non-fatal error opening '%s' for writing: %s]"),
640                         amandates_file, strerror(errno));
641             }
642         }
643     }
644 }
645
646 backup_program_t gnutar_program = {
647   "GNUTAR",
648 #ifdef GNUTAR
649   GNUTAR, GNUTAR,
650 #else
651   "gtar", "gtar",
652 #endif
653   re_table, start_backup, end_backup
654 };