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