lintian doesn't like orphan packages with uploaders...
[debian/amanda] / recover-src / extract_list.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2000 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$
29  *
30  * implements the "extract" command in amrecover
31  */
32
33 #include "amanda.h"
34 #include "match.h"
35 #include "amrecover.h"
36 #include "fileheader.h"
37 #include "dgram.h"
38 #include "stream.h"
39 #include "tapelist.h"
40 #ifdef SAMBA_CLIENT
41 #include "findpass.h"
42 #endif
43 #include "util.h"
44 #include "conffile.h"
45 #include "protocol.h"
46 #include "event.h"
47 #include "client_util.h"
48 #include "security.h"
49 #include "pipespawn.h"
50
51 typedef struct EXTRACT_LIST_ITEM {
52     char *path;
53     char *tpath;
54     struct EXTRACT_LIST_ITEM *next;
55 }
56 EXTRACT_LIST_ITEM;
57
58 typedef struct EXTRACT_LIST {
59     char *date;                 /* date tape created */
60     int  level;                 /* level of dump */
61     char *tape;                 /* tape label */
62     off_t fileno;               /* fileno on tape */
63     EXTRACT_LIST_ITEM *files;   /* files to get off tape */
64
65     struct EXTRACT_LIST *next;
66 }
67 EXTRACT_LIST;
68
69 typedef struct ctl_data_s {
70   int                      header_done;
71   int                      child_pipe[2];
72   int                      pid;
73   EXTRACT_LIST            *elist;
74   dumpfile_t               file;
75   data_path_t              data_path;
76   char                    *addrs;
77   backup_support_option_t *bsu;
78   gint64                   bytes_read;
79 } ctl_data_t;
80
81 #define SKIP_TAPE 2
82 #define RETRY_TAPE 3
83
84 static struct {
85     const char *name;
86     security_stream_t *fd;
87 } amidxtaped_streams[] = {
88 #define CTLFD  0
89     { "CTL", NULL },
90 #define DATAFD  1
91     { "DATA", NULL },
92 };
93 #define NSTREAMS  (int)(sizeof(amidxtaped_streams) / sizeof(amidxtaped_streams[0]))
94
95
96 static void amidxtaped_response(void *, pkt_t *, security_handle_t *);
97 static void stop_amidxtaped(void);
98 static char *dump_device_name = NULL;
99 static char *errstr;
100 static char *amidxtaped_line = NULL;
101 extern char *localhost;
102 static char header_buf[32768];
103 static int  header_size = 0;
104
105
106 /* global pid storage for interrupt handler */
107 pid_t extract_restore_child_pid = -1;
108
109 static EXTRACT_LIST *extract_list = NULL;
110 static const security_driver_t *amidxtaped_secdrv;
111
112 unsigned short samba_extract_method = SAMBA_TAR;
113
114 #define READ_TIMEOUT    240*60
115
116 EXTRACT_LIST *first_tape_list(void);
117 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
118 static int is_empty_dir(char *fname);
119 int is_extract_list_nonempty(void);
120 int length_of_tape_list(EXTRACT_LIST *tape_list);
121 void add_file(char *path, char *regex);
122 void add_glob(char *glob);
123 void add_regex(char *regex);
124 void clear_extract_list(void);
125 void clean_tape_list(EXTRACT_LIST *tape_list);
126 void clean_extract_list(void);
127 void check_file_overwrite(char *filename);
128 void delete_file(char *path, char *regex);
129 void delete_glob(char *glob);
130 void delete_regex(char *regex);
131 void delete_tape_list(EXTRACT_LIST *tape_list);
132 void display_extract_list(char *file);
133 void extract_files(void);
134 void read_file_header(char *buffer,
135                         dumpfile_t *file,
136                         size_t buflen,
137                         int tapedev);
138 static int add_extract_item(DIR_ITEM *ditem);
139 static int delete_extract_item(DIR_ITEM *ditem);
140 static int extract_files_setup(char *label, off_t fsf);
141 static int okay_to_continue(int allow_tape,
142                         int allow_skip,
143                         int allow_retry);
144 static ssize_t read_buffer(int datafd,
145                         char *buffer,
146                         size_t buflen,
147                         long timeout_s);
148 static void clear_tape_list(EXTRACT_LIST *tape_list);
149 static void extract_files_child(ctl_data_t *ctl_data);
150 static void send_to_tape_server(security_stream_t *stream, char *cmd);
151 int writer_intermediary(EXTRACT_LIST *elist);
152 int get_amidxtaped_line(void);
153 static void read_amidxtaped_data(void *, void *, ssize_t);
154 static char *merge_path(char *path1, char *path2);
155 static gboolean ask_file_overwrite(ctl_data_t *ctl_data);
156 static void start_processing_data(ctl_data_t *ctl_data);
157
158 /*
159  * Function:  ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
160  *
161  * Description:
162  *      read data from input file desciptor waiting up to timeout_s
163  *      seconds before returning data.
164  *
165  * Inputs:
166  *      datafd    - File descriptor to read from.
167  *      buffer    - Buffer to read into.
168  *      buflen    - Maximum number of bytes to read into buffer.
169  *      timeout_s - Seconds to wait before returning what was already read.
170  *
171  * Returns:
172  *      >0        - Number of data bytes in buffer.
173  *       0        - EOF
174  *      -1        - errno == ETIMEDOUT if no data available in specified time.
175  *                  errno == ENFILE if datafd is invalid.
176  *                  otherwise errno is set by select or read..
177  */
178
179 static ssize_t
180 read_buffer(
181     int         datafd,
182     char *      buffer,
183     size_t      buflen,
184     long        timeout_s)
185 {
186     ssize_t size = 0;
187     SELECT_ARG_TYPE readset;
188     struct timeval timeout;
189     char *dataptr;
190     ssize_t spaceleft;
191     int nfound;
192
193     if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
194         errno = EMFILE;                                 /* out of range */
195         return -1;
196     }
197
198     dataptr = buffer;
199     spaceleft = (ssize_t)buflen;
200
201     do {
202         FD_ZERO(&readset);
203         FD_SET(datafd, &readset);
204         timeout.tv_sec = timeout_s;
205         timeout.tv_usec = 0;
206         nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
207         if(nfound < 0 ) {
208             /* Select returned an error. */
209             g_fprintf(stderr,_("select error: %s\n"), strerror(errno));
210             size = -1;
211             break;
212         }
213
214         if (nfound == 0) {
215             /* Select timed out. */
216             if (timeout_s != 0)  {
217                 /* Not polling: a real read timeout */
218                 g_fprintf(stderr,_("timeout waiting for restore\n"));
219                 g_fprintf(stderr,_("increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"));
220             }
221             errno = ETIMEDOUT;
222             size = -1;
223             break;
224         }
225
226         if(!FD_ISSET(datafd, &readset))
227             continue;
228
229         /* Select says data is available, so read it.  */
230         size = read(datafd, dataptr, (size_t)spaceleft);
231         if (size < 0) {
232             if ((errno == EINTR) || (errno == EAGAIN)) {
233                 continue;
234             }
235             if (errno != EPIPE) {
236                 g_fprintf(stderr, _("read_buffer: read error - %s"),
237                     strerror(errno));
238                 break;
239             }
240             size = 0;
241         }
242         spaceleft -= size;
243         dataptr += size;
244     } while ((size > 0) && (spaceleft > 0));
245
246     return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
247 }
248
249
250 EXTRACT_LIST *
251 first_tape_list(void)
252 {
253     return extract_list;
254 }
255
256 EXTRACT_LIST *
257 next_tape_list(
258     /*@keep@*/EXTRACT_LIST *list)
259 {
260     if (list == NULL)
261         return NULL;
262     return list->next;
263 }
264
265 static void
266 clear_tape_list(
267     EXTRACT_LIST *      tape_list)
268 {
269     EXTRACT_LIST_ITEM *this, *next;
270
271
272     this = tape_list->files;
273     while (this != NULL)
274     {
275         next = this->next;
276         amfree(this->path);
277         amfree(this->tpath);
278         amfree(this);
279         this = next;
280     }
281     tape_list->files = NULL;
282 }
283
284
285 /* remove a tape list from the extract list, clearing the tape list
286    beforehand if necessary */
287 void
288 delete_tape_list(
289     EXTRACT_LIST *tape_list)
290 {
291     EXTRACT_LIST *this, *prev;
292
293     if (tape_list == NULL)
294         return;
295
296     /* is it first on the list? */
297     if (tape_list == extract_list)
298     {
299         extract_list = tape_list->next;
300         clear_tape_list(tape_list);
301         amfree(tape_list->date);
302         amfree(tape_list->tape);
303         amfree(tape_list);
304         return;
305     }
306
307     /* so not first on list - find it and delete */
308     prev = extract_list;
309     this = extract_list->next;
310     while (this != NULL)
311     {
312         if (this == tape_list)
313         {
314             prev->next = tape_list->next;
315             clear_tape_list(tape_list);
316             amfree(tape_list->date);
317             amfree(tape_list->tape);
318             amfree(tape_list);
319             return;
320         }
321         prev = this;
322         this = this->next;
323     }
324     /*NOTREACHED*/
325 }
326
327
328 /* return the number of files on a tape's list */
329 int
330 length_of_tape_list(
331     EXTRACT_LIST *tape_list)
332 {
333     EXTRACT_LIST_ITEM *fn;
334     int n;
335
336     n = 0;
337     for (fn = tape_list->files; fn != NULL; fn = fn->next)
338         n++;
339
340     return n;
341 }
342
343
344 void
345 clear_extract_list(void)
346 {
347     while (extract_list != NULL)
348         delete_tape_list(extract_list);
349 }
350
351
352 void
353 clean_tape_list(
354     EXTRACT_LIST *tape_list)
355 {
356     EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
357     EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
358     int remove_fn1;
359     int remove_fn2;
360
361     pfn1 = NULL;
362     fn1 = tape_list->files;
363     while (fn1 != NULL) {
364         remove_fn1 = 0;
365
366         pfn2 = fn1;
367         fn2 = fn1->next;
368         while (fn2 != NULL && remove_fn1 == 0) {
369             remove_fn2 = 0;
370             if(strcmp(fn1->path, fn2->path) == 0) {
371                 remove_fn2 = 1;
372             } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
373                        ((strlen(fn2->path) > strlen(fn1->path) &&
374                          fn2->path[strlen(fn1->path)] == '/') ||
375                        (fn1->path[strlen(fn1->path)-1] == '/'))) {
376                 remove_fn2 = 1;
377             } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
378                        ((strlen(fn1->path) > strlen(fn2->path) &&
379                          fn1->path[strlen(fn2->path)] == '/')  ||
380                        (fn2->path[strlen(fn2->path)-1] == '/'))) {
381                 remove_fn1 = 1;
382                 break;
383             }
384
385             if (remove_fn2) {
386                 dbprintf(_("removing path %s, it is included in %s\n"),
387                           fn2->path, fn1->path);
388                 ofn2 = fn2;
389                 fn2 = fn2->next;
390                 amfree(ofn2->path);
391                 amfree(ofn2->tpath);
392                 amfree(ofn2);
393                 pfn2->next = fn2;
394             } else if (remove_fn1 == 0) {
395                 pfn2 = fn2;
396                 fn2 = fn2->next;
397             }
398         }
399
400         if(remove_fn1 != 0) {
401             /* fn2->path is always valid */
402             /*@i@*/ dbprintf(_("removing path %s, it is included in %s\n"),
403             /*@i@*/           fn1->tpath, fn2->tpath);
404             ofn1 = fn1;
405             fn1 = fn1->next;
406             amfree(ofn1->path);
407             amfree(ofn1->tpath);
408             if(pfn1 == NULL) {
409                 amfree(tape_list->files);
410                 tape_list->files = fn1;
411             } else {
412                 amfree(pfn1->next);
413                 pfn1->next = fn1;
414             }
415         } else {
416             pfn1 = fn1;
417             fn1 = fn1->next;
418         }
419     }
420 }
421
422
423 static char *
424 file_of_path(
425     char *path,
426     char **dir)
427 {
428     char *npath = g_path_get_basename(path);
429     *dir = g_path_get_dirname(path);
430     if (strcmp(*dir, ".") == 0) {
431         amfree(*dir);
432     }
433     return npath;
434 }
435
436 void
437 clean_extract_list(void)
438 {
439     EXTRACT_LIST *this;
440
441     for (this = extract_list; this != NULL; this = this->next)
442         clean_tape_list(this);
443 }
444
445
446 int add_to_unlink_list(char *path);
447 int do_unlink_list(void);
448 void free_unlink_list(void);
449
450 typedef struct s_unlink_list {
451     char *path;
452     struct s_unlink_list *next;
453 } t_unlink_list;
454 t_unlink_list *unlink_list = NULL;
455
456 int
457 add_to_unlink_list(
458     char *path)
459 {
460     t_unlink_list *ul;
461
462     if (!unlink_list) {
463         unlink_list = alloc(SIZEOF(*unlink_list));
464         unlink_list->path = stralloc(path);
465         unlink_list->next = NULL;
466     } else {
467         for (ul = unlink_list; ul != NULL; ul = ul->next) {
468             if (strcmp(ul->path, path) == 0)
469                 return 0;
470         }
471         ul = alloc(SIZEOF(*ul));
472         ul->path = stralloc(path);
473         ul->next = unlink_list;
474         unlink_list = ul;
475     }
476     return 1;
477 }
478
479 int
480 do_unlink_list(void)
481 {
482     t_unlink_list *ul;
483     int ret = 1;
484
485     for (ul = unlink_list; ul != NULL; ul = ul->next) {
486         if (unlink(ul->path) < 0) {
487             g_fprintf(stderr,_("Can't unlink %s: %s\n"), ul->path, strerror(errno));
488             ret = 0;
489         }
490     }
491     return ret;
492 }
493
494
495 void
496 free_unlink_list(void)
497 {
498     t_unlink_list *ul, *ul1;
499
500     for (ul = unlink_list; ul != NULL; ul = ul1) {
501         amfree(ul->path);
502         ul1 = ul->next;
503         amfree(ul);
504     }
505
506     unlink_list = NULL;
507 }
508
509
510
511 void
512 check_file_overwrite(
513     char *dir)
514 {
515     EXTRACT_LIST      *this;
516     EXTRACT_LIST_ITEM *fn;
517     struct stat        stat_buf;
518     char              *filename;
519     char              *path, *s;
520
521     for (this = extract_list; this != NULL; this = this->next) {
522         for (fn = this->files; fn != NULL ; fn = fn->next) {
523
524             /* Check path component of fn->path */
525
526             path = stralloc2(dir, fn->path);
527             if (path[strlen(path)-1] == '/') {
528                 path[strlen(path)-1] = '\0';
529             }
530
531             s = path + strlen(dir) + 1;
532             while((s = strchr(s, '/'))) {
533                 *s = '\0';
534                 if (lstat(path, &stat_buf) == 0) {
535                     if(!S_ISDIR(stat_buf.st_mode)) {
536                         if (add_to_unlink_list(path)) {
537                             g_printf(_("WARNING: %s is not a directory, "
538                                    "it will be deleted.\n"),
539                                    path);
540                         }
541                     }
542                 }
543                 else if (errno != ENOENT) {
544                     g_printf(_("Can't stat %s: %s\n"), path, strerror(errno));
545                 }
546                 *s = '/';
547                 s++;
548             }
549             amfree(path);
550
551             /* Check fn->path */
552
553             filename = stralloc2(dir, fn->path);
554             if (filename[strlen(filename)-1] == '/') {
555                 filename[strlen(filename)-1] = '\0';
556             }
557
558             if (lstat(filename, &stat_buf) == 0) {
559                 if(S_ISDIR(stat_buf.st_mode)) {
560                     if(!is_empty_dir(filename)) {
561                         g_printf(_("WARNING: All existing files in %s "
562                                "will be deleted.\n"), filename);
563                     }
564                 } else if(S_ISREG(stat_buf.st_mode)) {
565                     g_printf(_("WARNING: Existing file %s will be overwritten\n"),
566                            filename);
567                 } else {
568                     if (add_to_unlink_list(filename)) {
569                         g_printf(_("WARNING: Existing entry %s will be deleted\n"),
570                                filename);
571                     }
572                 }
573             } else if (errno != ENOENT) {
574                 g_printf(_("Can't stat %s: %s\n"), filename, strerror(errno));
575             }
576             amfree(filename);
577         }
578     }
579 }
580
581
582 /* returns -1 if error */
583 /* returns  0 on succes */
584 /* returns  1 if already added */
585 static int
586 add_extract_item(
587     DIR_ITEM *ditem)
588 {
589     EXTRACT_LIST *this, *this1;
590     EXTRACT_LIST_ITEM *that, *curr;
591     char *ditem_path;
592
593     ditem_path = stralloc(ditem->path);
594     clean_pathname(ditem_path);
595
596     for (this = extract_list; this != NULL; this = this->next)
597     {
598         /* see if this is the list for the tape */      
599         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
600         {
601             /* yes, so add to list */
602             curr=this->files;
603             while(curr!=NULL)
604             {
605                 if (strcmp(curr->path,ditem_path) == 0) {
606                     g_free(ditem_path);
607                     return 1;
608                 }
609                 curr=curr->next;
610             }
611             that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
612             that->path = ditem_path;
613             that->tpath = clean_pathname(g_strdup(ditem->tpath));
614             that->next = this->files;
615             this->files = that;         /* add at front since easiest */
616             return 0;
617         }
618     }
619
620     /* so this is the first time we have seen this tape */
621     this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
622     this->tape = stralloc(ditem->tape);
623     this->level = ditem->level;
624     this->fileno = ditem->fileno;
625     this->date = stralloc(ditem->date);
626     that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
627     that->path = ditem_path;
628     that->tpath = clean_pathname(g_strdup(ditem->tpath));
629     that->next = NULL;
630     this->files = that;
631
632     /* add this in date increasing order          */
633     /* because restore must be done in this order */
634     /* add at begining */
635     if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0)
636     {
637         this->next = extract_list;
638         extract_list = this;
639         return 0;
640     }
641     for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
642     {
643         /* add in the middle */
644         if(strcmp(this->date,this1->next->date) < 0)
645         {
646             this->next = this1->next;
647             this1->next = this;
648             return 0;
649         }
650     }
651     /* add at end */
652     this->next = NULL;
653     this1->next = this;
654     return 0;
655 }
656
657
658 /* returns -1 if error */
659 /* returns  0 on deletion */
660 /* returns  1 if not there */
661 static int
662 delete_extract_item(
663     DIR_ITEM *ditem)
664 {
665     EXTRACT_LIST *this;
666     EXTRACT_LIST_ITEM *that, *prev;
667     char *ditem_path = NULL;
668
669     ditem_path = stralloc(ditem->path);
670     clean_pathname(ditem_path);
671
672     for (this = extract_list; this != NULL; this = this->next)
673     {
674         /* see if this is the list for the tape */      
675         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
676         {
677             /* yes, so find file on list */
678             that = this->files;
679             if (strcmp(that->path, ditem_path) == 0)
680             {
681                 /* first on list */
682                 this->files = that->next;
683                 amfree(that->path);
684                 amfree(that->tpath);
685                 amfree(that);
686                 /* if list empty delete it */
687                 if (this->files == NULL)
688                     delete_tape_list(this);
689                 amfree(ditem_path);
690                 return 0;
691             }
692             prev = that;
693             that = that->next;
694             while (that != NULL)
695             {
696                 if (strcmp(that->path, ditem_path) == 0)
697                 {
698                     prev->next = that->next;
699                     amfree(that->path);
700                     amfree(that->tpath);
701                     amfree(that);
702                     amfree(ditem_path);
703                     return 0;
704                 }
705                 prev = that;
706                 that = that->next;
707             }
708             amfree(ditem_path);
709             return 1;
710         }
711     }
712
713     amfree(ditem_path);
714     return 1;
715 }
716
717 static char *
718 merge_path(
719     char *path1,
720     char *path2)
721 {
722     char *result;
723     int len = strlen(path1);
724     if (path1[len-1] == '/' && path2[0] == '/') {
725         result = stralloc2(path1, path2+1);
726     } else if (path1[len-1] != '/' && path2[0] != '/') {
727         result = vstralloc(path1, "/", path2, NULL);
728     } else {
729         result = stralloc2(path1, path2);
730     }
731     return result;
732 }
733
734 void
735 add_glob(
736     char *      glob)
737 {
738     char *regex;
739     char *regex_path;
740     char *s;
741     char *uqglob;
742     char *dir;
743     char *sdir = NULL;
744     int   result = 1;
745
746     if (disk_path == NULL) {
747         g_printf(_("Must select directory before adding files\n"));
748         return;
749     }
750
751     uqglob = unquote_string(glob);
752     glob = file_of_path(uqglob, &dir);
753     if (dir) {
754         sdir = merge_path(mount_point, disk_path);
755         result = cd_glob(dir, 0);
756         amfree(dir);
757     }
758     if (result) {
759         regex = glob_to_regex(glob);
760         dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex);
761         if ((s = validate_regexp(regex)) != NULL) {
762             g_printf(_("%s is not a valid shell wildcard pattern: "), glob);
763             puts(s);
764         } else {
765             /*
766              * glob_to_regex() anchors the beginning of the pattern with ^,
767              * but we will be tacking it onto the end of the current directory
768              * in add_file, so strip that off.  Also, it anchors the end with
769              * $, but we need to match an optional trailing /, so tack that on
770              * the end.
771              */
772             regex_path = stralloc(regex + 1);
773             regex_path[strlen(regex_path) - 1] = '\0';
774             strappend(regex_path, "[/]*$");
775             add_file(uqglob, regex_path);
776             amfree(regex_path);
777         }
778         if (sdir) {
779             set_directory(sdir, 0);
780         }
781         amfree(regex);
782     }
783     amfree(sdir);
784     amfree(uqglob);
785     amfree(glob);
786 }
787
788 void
789 add_regex(
790     char *      regex)
791 {
792     char *s;
793     char *dir;
794     char *sdir = NULL;
795     char *uqregex;
796     char *newregex;
797     int   result = 1;
798
799     if (disk_path == NULL) {
800         g_printf(_("Must select directory before adding files\n"));
801         return;
802     }
803
804     uqregex = unquote_string(regex);
805     newregex = file_of_path(uqregex, &dir);
806     if (dir) {
807         sdir = merge_path(mount_point, disk_path);
808         result = cd_regex(dir, 0);
809         amfree(dir);
810     }
811
812     if (result) { 
813         if ((s = validate_regexp(newregex)) != NULL) {
814             g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
815             puts(s);
816         } else {
817             add_file(uqregex, newregex);
818         }
819         if (sdir) {
820             set_directory(sdir, 0);
821         }
822     }
823     amfree(sdir);
824     amfree(uqregex);
825     amfree(newregex);
826 }
827
828 void
829 add_file(
830     char *      path,
831     char *      regex)
832 {
833     DIR_ITEM *ditem, lditem;
834     char *tpath_on_disk = NULL;
835     char *cmd = NULL;
836     char *err = NULL;
837     int i;
838     ssize_t j;
839     char *dir_undo, dir_undo_ch = '\0';
840     char *ditem_path = NULL;
841     char *qditem_path = NULL;
842     char *l = NULL;
843     int  added;
844     char *s, *fp, *quoted;
845     int ch;
846     int found_one;
847     int dir_entries;
848
849     if (disk_path == NULL) {
850         g_printf(_("Must select directory before adding files\n"));
851         return;
852     }
853     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
854
855     dbprintf(_("add_file: Looking for \"%s\"\n"), regex);
856
857     if(strcmp(regex, "/[/]*$") == 0) {  /* "/" behave like "." */
858         regex = "\\.[/]*$";
859     }
860     else if(strcmp(regex, "[^/]*[/]*$") == 0) {         /* "*" */
861         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
862     } else {
863         /* remove "/" at end of path */
864         j = (ssize_t)(strlen(regex) - 1);
865         while(j >= 0 && regex[j] == '/')
866             regex[j--] = '\0';
867     }
868
869     /* convert path (assumed in cwd) to one on disk */
870     if (strcmp(disk_path, "/") == 0) {
871         if (*regex == '/') {
872             /* No mods needed if already starts with '/' */
873             tpath_on_disk = g_strdup(regex);
874         } else {
875             /* Prepend '/' */
876             tpath_on_disk = g_strconcat("/", regex, NULL);
877         }
878     } else {
879         char *clean_disk_tpath = clean_regex(disk_tpath, 0);
880         tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
881         amfree(clean_disk_tpath);
882     }
883
884     dbprintf(_("add_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
885               regex, tpath_on_disk);
886
887     found_one = 0;
888     dir_entries = 0;
889     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
890     {
891         dir_entries++;
892         quoted = quote_string(ditem->tpath);
893         dbprintf(_("add_file: Pondering ditem->path=%s\n"), quoted);
894         amfree(quoted);
895         if (match(tpath_on_disk, ditem->tpath))
896         {
897             found_one = 1;
898             j = (ssize_t)strlen(ditem->tpath);
899             if((j > 0 && ditem->tpath[j-1] == '/')
900                || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
901             {   /* It is a directory */
902                 ditem_path = newstralloc(ditem_path, ditem->path);
903                 clean_pathname(ditem_path);
904
905                 qditem_path = quote_string(ditem_path);
906                 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
907                 amfree(qditem_path);
908                 if(send_command(cmd) == -1) {
909                     amfree(cmd);
910                     amfree(ditem_path);
911                     amfree(tpath_on_disk);
912                     exit(1);
913                 }
914                 amfree(cmd);
915                 cmd = NULL;
916                 /* skip preamble */
917                 if ((i = get_reply_line()) == -1) {
918                     amfree(ditem_path);
919                     amfree(tpath_on_disk);
920                     exit(1);
921                 }
922                 if(i==0) {              /* assume something wrong */
923                     amfree(ditem_path);
924                     amfree(tpath_on_disk);
925                     l = reply_line();
926                     g_printf("%s\n", l);
927                     return;
928                 }
929                 dir_undo = NULL;
930                 added=0;
931                 g_free(lditem.path);
932                 g_free(lditem.tpath);
933                 lditem.path = g_strdup(ditem->path);
934                 lditem.tpath = g_strdup(ditem->tpath);
935                 /* skip the last line -- duplicate of the preamble */
936
937                 while ((i = get_reply_line()) != 0) {
938                     if (i == -1) {
939                         amfree(ditem_path);
940                         amfree(tpath_on_disk);
941                         exit(1);
942                     }
943                     if(err) {
944                         if(cmd == NULL) {
945                             if(dir_undo) *dir_undo = dir_undo_ch;
946                             dir_undo = NULL;
947                             cmd = stralloc(l);  /* save for error report */
948                         }
949                         continue;       /* throw the rest of the lines away */
950                     }
951                     l=reply_line();
952                     if (!server_happy()) {
953                         puts(l);
954                         continue;
955                     }
956
957                     s = l;
958                     if(strncmp_const_skip(l, "201-", s, ch) != 0) {
959                         err = _("bad reply: not 201-");
960                         continue;
961                     }
962                     ch = *s++;
963
964                     skip_whitespace(s, ch);
965                     if(ch == '\0') {
966                         err = _("bad reply: missing date field");
967                         continue;
968                     }
969                     fp = s-1;
970                     skip_non_whitespace(s, ch);
971                     s[-1] = '\0';
972                     lditem.date = newstralloc(lditem.date, fp);
973                     s[-1] = (char)ch;
974
975                     skip_whitespace(s, ch);
976                     if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
977                         err = _("bad reply: cannot parse level field");
978                         continue;
979                     }
980                     skip_integer(s, ch);
981
982                     skip_whitespace(s, ch);
983                     if(ch == '\0') {
984                         err = _("bad reply: missing tape field");
985                         continue;
986                     }
987                     fp = s-1;
988                     skip_quoted_string(s, ch);
989                     s[-1] = '\0';
990                     amfree(lditem.tape);
991                     lditem.tape = unquote_string(fp);
992                     s[-1] = (char)ch;
993
994                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
995                         long long fileno_ = (long long)0;
996                         skip_whitespace(s, ch);
997                         if(ch == '\0' ||
998                            sscanf(s - 1, "%lld", &fileno_) != 1) {
999                             err = _("bad reply: cannot parse fileno field");
1000                             continue;
1001                         }
1002                         lditem.fileno = (off_t)fileno_;
1003                         skip_integer(s, ch);
1004                     }
1005
1006                     skip_whitespace(s, ch);
1007                     if(ch == '\0') {
1008                         err = _("bad reply: missing directory field");
1009                         continue;
1010                     }
1011                     skip_quoted_string(s, ch);
1012                     dir_undo = s - 1;
1013                     dir_undo_ch = *dir_undo;
1014                     *dir_undo = '\0';
1015
1016                     switch(add_extract_item(&lditem)) {
1017                     case -1:
1018                         g_printf(_("System error\n"));
1019                         dbprintf(_("add_file: (Failed) System error\n"));
1020                         break;
1021
1022                     case  0:
1023                         quoted = quote_string(lditem.tpath);
1024                         g_printf(_("Added dir %s at date %s\n"),
1025                                quoted, lditem.date);
1026                         dbprintf(_("add_file: (Successful) Added dir %s at date %s\n"),
1027                                   quoted, lditem.date);
1028                         amfree(quoted);
1029                         added=1;
1030                         break;
1031
1032                     case  1:
1033                         break;
1034                     }
1035                 }
1036                 if(!server_happy()) {
1037                     puts(reply_line());
1038                 } else if(err) {
1039                     if (*err)
1040                         puts(err);
1041                     if (cmd)
1042                         puts(cmd);
1043                 } else if(added == 0) {
1044                     quoted = quote_string(ditem_path);
1045                     g_printf(_("dir %s already added\n"), quoted);
1046                     dbprintf(_("add_file: dir %s already added\n"), quoted);
1047                     amfree(quoted);
1048                 }
1049             }
1050             else /* It is a file */
1051             {
1052                 switch(add_extract_item(ditem)) {
1053                 case -1:
1054                     g_printf(_("System error\n"));
1055                     dbprintf(_("add_file: (Failed) System error\n"));
1056                     break;
1057
1058                 case  0:
1059                     quoted = quote_string(ditem->tpath);
1060                     g_printf(_("Added file %s\n"), quoted);
1061                     dbprintf(_("add_file: (Successful) Added %s\n"), quoted);
1062                     amfree(quoted);
1063                     break;
1064
1065                 case  1:
1066                     quoted = quote_string(ditem->tpath);
1067                     g_printf(_("File %s already added\n"), quoted);
1068                     dbprintf(_("add_file: file %s already added\n"), quoted);
1069                     amfree(quoted);
1070                 }
1071             }
1072         }
1073     }
1074
1075     amfree(cmd);
1076     amfree(ditem_path);
1077     amfree(tpath_on_disk);
1078
1079     amfree(lditem.path);
1080     amfree(lditem.tpath);
1081     amfree(lditem.date);
1082     amfree(lditem.tape);
1083
1084     if(! found_one) {
1085         quoted = quote_string(path);
1086         g_printf(_("File %s doesn't exist in directory\n"), quoted);
1087         dbprintf(_("add_file: (Failed) File %s doesn't exist in directory\n"),
1088                   quoted);
1089         amfree(quoted);
1090     }
1091 }
1092
1093
1094 void
1095 delete_glob(
1096     char *      glob)
1097 {
1098     char *regex;
1099     char *regex_path;
1100     char *s;
1101     char *uqglob;
1102     char *newglob;
1103     char *dir;
1104     char *sdir = NULL;
1105     int   result = 1;
1106
1107     if (disk_path == NULL) {
1108         g_printf(_("Must select directory before adding files\n"));
1109         return;
1110     }
1111
1112     uqglob = unquote_string(glob);
1113     newglob = file_of_path(uqglob, &dir);
1114     if (dir) {
1115         sdir = merge_path(mount_point, disk_path);
1116         result = cd_glob(dir, 0);
1117         amfree(dir);
1118     }
1119     if (result) {
1120         regex = glob_to_regex(newglob);
1121         dbprintf(_("delete_glob (%s) -> %s\n"), newglob, regex);
1122         if ((s = validate_regexp(regex)) != NULL) {
1123             g_printf(_("\"%s\" is not a valid shell wildcard pattern: "),
1124                      newglob);
1125             puts(s);
1126 } else {
1127             /*
1128              * glob_to_regex() anchors the beginning of the pattern with ^,
1129              * but we will be tacking it onto the end of the current directory
1130              * in add_file, so strip that off.  Also, it anchors the end with
1131              * $, but we need to match an optional trailing /, so tack that on
1132              * the end.
1133              */
1134             regex_path = stralloc(regex + 1);
1135             regex_path[strlen(regex_path) - 1] = '\0';
1136             strappend(regex_path, "[/]*$");
1137             delete_file(uqglob, regex_path);
1138             amfree(regex_path);
1139         }
1140         if (sdir) {
1141             set_directory(sdir, 0);
1142         }
1143         amfree(regex);
1144     }
1145     amfree(sdir);
1146     amfree(uqglob);
1147     amfree(newglob);
1148 }
1149
1150 void
1151 delete_regex(
1152     char *      regex)
1153 {
1154     char *s;
1155     char *dir;
1156     char *sdir = NULL;
1157     char *uqregex;
1158     char *newregex;
1159     int   result = 1;
1160
1161     if (disk_path == NULL) {
1162         g_printf(_("Must select directory before adding files\n"));
1163         return;
1164     }
1165
1166     uqregex = unquote_string(regex);
1167     newregex = file_of_path(uqregex, &dir);
1168     if (dir) {
1169         sdir = merge_path(mount_point, disk_path);
1170         result = cd_regex(dir, 0);
1171         amfree(dir);
1172     }
1173
1174     if (result == 1) {
1175         if ((s = validate_regexp(newregex)) != NULL) {
1176             g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
1177             puts(s);
1178         } else {
1179             delete_file(newregex, regex);
1180         }
1181         if (sdir) {
1182             set_directory(sdir, 0);
1183         }
1184     }
1185     amfree(sdir);
1186     amfree(uqregex);
1187     amfree(newregex);
1188 }
1189
1190 void
1191 delete_file(
1192     char *      tpath,
1193     char *      regex)
1194 {
1195     DIR_ITEM *ditem, lditem;
1196     char *tpath_on_disk = NULL;
1197     char *cmd = NULL;
1198     char *err = NULL;
1199     int i;
1200     ssize_t j;
1201     char *date;
1202     char *tape, *tape_undo, tape_undo_ch = '\0';
1203     char *dir_undo, dir_undo_ch = '\0';
1204     int  level = 0;
1205     char *ditem_path = NULL;
1206     char *ditem_tpath = NULL;
1207     char *qditem_path;
1208     char *l = NULL;
1209     int  deleted;
1210     char *s;
1211     int ch;
1212     int found_one;
1213     char *quoted;
1214
1215     if (disk_path == NULL) {
1216         g_printf(_("Must select directory before deleting files\n"));
1217         return;
1218     }
1219     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
1220
1221     dbprintf(_("delete_file: Looking for \"%s\"\n"), tpath);
1222
1223     if (strcmp(regex, "[^/]*[/]*$") == 0) {
1224         /* Looking for * find everything but single . */
1225         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
1226     } else {
1227         /* remove "/" at end of path */
1228         j = (ssize_t)(strlen(regex) - 1);
1229         while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
1230     }
1231
1232     /* convert path (assumed in cwd) to one on disk */
1233     if (strcmp(disk_path, "/") == 0) {
1234         if (*regex == '/') {
1235             if (strcmp(regex, "/[/]*$") == 0) {
1236                 /* We want "/" to match the directory itself: "/." */
1237                 tpath_on_disk = stralloc("/\\.[/]*$");
1238             } else {
1239                 /* No mods needed if already starts with '/' */
1240                 tpath_on_disk = stralloc(regex);
1241             }
1242         } else {
1243             /* Prepend '/' */
1244             tpath_on_disk = g_strconcat("/", regex, NULL);
1245         }
1246     } else {
1247         char *clean_disk_tpath = clean_regex(disk_tpath, 0);
1248         tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
1249         amfree(clean_disk_tpath);
1250     }
1251
1252     dbprintf(_("delete_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
1253               regex, tpath_on_disk);
1254     found_one = 0;
1255     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
1256     {
1257         quoted = quote_string(ditem->tpath);
1258         dbprintf(_("delete_file: Pondering ditem->path=%s\n"), quoted);
1259         amfree(quoted);
1260         if (match(tpath_on_disk, ditem->tpath))
1261         {
1262             found_one = 1;
1263             j = (ssize_t)strlen(ditem->tpath);
1264             if((j > 0 && ditem->tpath[j-1] == '/')
1265                || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
1266             {   /* It is a directory */
1267                 ditem_path = newstralloc(ditem_path, ditem->path);
1268                 ditem_tpath = newstralloc(ditem_tpath, ditem->tpath);
1269                 clean_pathname(ditem_path);
1270                 clean_pathname(ditem_tpath);
1271
1272                 qditem_path = quote_string(ditem_path);
1273                 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
1274                 amfree(qditem_path);
1275                 if(send_command(cmd) == -1) {
1276                     amfree(cmd);
1277                     amfree(ditem_path);
1278                     amfree(tpath_on_disk);
1279                     exit(1);
1280                 }
1281                 amfree(cmd);
1282                 /* skip preamble */
1283                 if ((i = get_reply_line()) == -1) {
1284                     amfree(ditem_path);
1285                     amfree(tpath_on_disk);
1286                     exit(1);
1287                 }
1288                 if(i==0)                /* assume something wrong */
1289                 {
1290                     amfree(ditem_path);
1291                     amfree(tpath_on_disk);
1292                     l = reply_line();
1293                     g_printf("%s\n", l);
1294                     return;
1295                 }
1296                 deleted=0;
1297                 lditem.path = newstralloc(lditem.path, ditem->path);
1298                 lditem.tpath = newstralloc(lditem.tpath, ditem->tpath);
1299                 amfree(cmd);
1300                 tape_undo = dir_undo = NULL;
1301                 /* skip the last line -- duplicate of the preamble */
1302                 while ((i = get_reply_line()) != 0)
1303                 {
1304                     if (i == -1) {
1305                         amfree(ditem_path);
1306                         amfree(tpath_on_disk);
1307                         exit(1);
1308                     }
1309                     if(err) {
1310                         if(cmd == NULL) {
1311                             if(tape_undo) *tape_undo = tape_undo_ch;
1312                             if(dir_undo) *dir_undo = dir_undo_ch;
1313                             tape_undo = dir_undo = NULL;
1314                             cmd = stralloc(l);  /* save for the error report */
1315                         }
1316                         continue;       /* throw the rest of the lines away */
1317                     }
1318                     l=reply_line();
1319                     if (!server_happy()) {
1320                         puts(l);
1321                         continue;
1322                     }
1323
1324                     s = l;
1325                     if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1326                         err = _("bad reply: not 201-");
1327                         continue;
1328                     }
1329                     ch = *s++;
1330
1331                     skip_whitespace(s, ch);
1332                     if(ch == '\0') {
1333                         err = _("bad reply: missing date field");
1334                         continue;
1335                     }
1336                     date = s - 1;
1337                     skip_non_whitespace(s, ch);
1338                     *(s - 1) = '\0';
1339
1340                     skip_whitespace(s, ch);
1341                     if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1342                         err = _("bad reply: cannot parse level field");
1343                         continue;
1344                     }
1345                     skip_integer(s, ch);
1346
1347                     skip_whitespace(s, ch);
1348                     if(ch == '\0') {
1349                         err = _("bad reply: missing tape field");
1350                         continue;
1351                     }
1352                     tape = s - 1;
1353                     skip_non_whitespace(s, ch);
1354                     tape_undo = s - 1;
1355                     tape_undo_ch = *tape_undo;
1356                     *tape_undo = '\0';
1357
1358                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1359                         long long fileno_ = (long long)0;
1360                         skip_whitespace(s, ch);
1361                         if(ch == '\0' ||
1362                            sscanf(s - 1, "%lld", &fileno_) != 1) {
1363                             err = _("bad reply: cannot parse fileno field");
1364                             continue;
1365                         }
1366                         skip_integer(s, ch);
1367                     }
1368
1369                     skip_whitespace(s, ch);
1370                     if(ch == '\0') {
1371                         err = _("bad reply: missing directory field");
1372                         continue;
1373                     }
1374                     skip_non_whitespace(s, ch);
1375                     dir_undo = s - 1;
1376                     dir_undo_ch = *dir_undo;
1377                     *dir_undo = '\0';
1378
1379                     lditem.date = newstralloc(lditem.date, date);
1380                     lditem.level=level;
1381                     g_free(lditem.tape);
1382                     lditem.tape = unquote_string(tape);
1383                     switch(delete_extract_item(&lditem)) {
1384                     case -1:
1385                         g_printf(_("System error\n"));
1386                         dbprintf(_("delete_file: (Failed) System error\n"));
1387                         break;
1388                     case  0:
1389                         g_printf(_("Deleted dir %s at date %s\n"), ditem_tpath, date);
1390                         dbprintf(_("delete_file: (Successful) Deleted dir %s at date %s\n"),
1391                                   ditem_tpath, date);
1392                         deleted=1;
1393                         break;
1394                     case  1:
1395                         break;
1396                     }
1397                 }
1398                 if(!server_happy()) {
1399                     puts(reply_line());
1400                 } else if(err) {
1401                     if (*err)
1402                         puts(err);
1403                     if (cmd)
1404                         puts(cmd);
1405                 } else if(deleted == 0) {
1406                     g_printf(_("Warning - dir '%s' not on tape list\n"),
1407                            ditem_tpath);
1408                     dbprintf(_("delete_file: dir '%s' not on tape list\n"),
1409                               ditem_tpath);
1410                 }
1411             }
1412             else
1413             {
1414                 switch(delete_extract_item(ditem)) {
1415                 case -1:
1416                     g_printf(_("System error\n"));
1417                     dbprintf(_("delete_file: (Failed) System error\n"));
1418                     break;
1419                 case  0:
1420                     g_printf(_("Deleted %s\n"), ditem->tpath);
1421                     dbprintf(_("delete_file: (Successful) Deleted %s\n"),
1422                               ditem->tpath);
1423                     break;
1424                 case  1:
1425                     g_printf(_("Warning - file '%s' not on tape list\n"),
1426                            ditem->tpath);
1427                     dbprintf(_("delete_file: file '%s' not on tape list\n"),
1428                               ditem->tpath);
1429                     break;
1430                 }
1431             }
1432         }
1433     }
1434     amfree(cmd);
1435     amfree(ditem_path);
1436     amfree(ditem_tpath);
1437     amfree(tpath_on_disk);
1438
1439     if(! found_one) {
1440         g_printf(_("File %s doesn't exist in directory\n"), tpath);
1441         dbprintf(_("delete_file: (Failed) File %s doesn't exist in directory\n"),
1442                   tpath);
1443     }
1444 }
1445
1446
1447 /* print extract list into file. If NULL ptr passed print to screen */
1448 void
1449 display_extract_list(
1450     char *      file)
1451 {
1452     EXTRACT_LIST *this;
1453     EXTRACT_LIST_ITEM *that;
1454     FILE *fp;
1455     char *pager;
1456     char *pager_command;
1457     char *uqfile;
1458
1459     if (file == NULL)
1460     {
1461         if ((pager = getenv("PAGER")) == NULL)
1462         {
1463             pager = "more";
1464         }
1465         /*
1466          * Set up the pager command so if the pager is terminated, we do
1467          * not get a SIGPIPE back.
1468          */
1469         pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1470         if ((fp = popen(pager_command, "w")) == NULL)
1471         {
1472             g_printf(_("Warning - can't pipe through %s\n"), pager);
1473             fp = stdout;
1474         }
1475         amfree(pager_command);
1476     }
1477     else
1478     {
1479         uqfile = unquote_string(file);
1480         if ((fp = fopen(uqfile, "w")) == NULL)
1481         {
1482             g_printf(_("Can't open file %s to print extract list into\n"), file);
1483             amfree(uqfile);
1484             return;
1485         }
1486         amfree(uqfile);
1487     }
1488
1489     for (this = extract_list; this != NULL; this = this->next)
1490     {
1491         g_fprintf(fp, _("TAPE %s LEVEL %d DATE %s\n"),
1492                 this->tape, this->level, this->date);
1493         for (that = this->files; that != NULL; that = that->next)
1494             g_fprintf(fp, "\t%s\n", that->tpath);
1495     }
1496
1497     if (file == NULL) {
1498         apclose(fp);
1499     } else {
1500         g_printf(_("Extract list written to file %s\n"), file);
1501         afclose(fp);
1502     }
1503 }
1504
1505
1506 static int
1507 is_empty_dir(
1508     char *fname)
1509 {
1510     DIR *dir;
1511     struct dirent *entry;
1512     int gotentry;
1513
1514     if((dir = opendir(fname)) == NULL)
1515         return 1;
1516
1517     gotentry = 0;
1518     while(!gotentry && (entry = readdir(dir)) != NULL) {
1519         gotentry = !is_dot_or_dotdot(entry->d_name);
1520     }
1521
1522     closedir(dir);
1523     return !gotentry;
1524
1525 }
1526
1527 /* returns 0 if extract list empty and 1 if it isn't */
1528 int
1529 is_extract_list_nonempty(void)
1530 {
1531     return (extract_list != NULL);
1532 }
1533
1534
1535 /* prints continue prompt and waits for response,
1536    returns 0 if don't, non-0 if do */
1537 static int
1538 okay_to_continue(
1539     int allow_tape,
1540     int allow_skip,
1541     int allow_retry)
1542 {
1543     int ch;
1544     int ret = -1;
1545     char *line = NULL;
1546     char *s;
1547     char *prompt;
1548     int get_device;
1549
1550     get_device = 0;
1551     while (ret < 0) {
1552         if (get_device) {
1553             prompt = _("New device name [?]: ");
1554         } else if (allow_tape && allow_skip) {
1555             prompt = _("Continue [?/Y/n/s/d]? ");
1556         } else if (allow_tape && !allow_skip) {
1557             prompt = _("Continue [?/Y/n/d]? ");
1558         } else if (allow_retry) {
1559             prompt = _("Continue [?/Y/n/r]? ");
1560         } else {
1561             prompt = _("Continue [?/Y/n]? ");
1562         }
1563         fputs(prompt, stdout);
1564         fflush(stdout); fflush(stderr);
1565         amfree(line);
1566         if ((line = agets(stdin)) == NULL) {
1567             putchar('\n');
1568             clearerr(stdin);
1569             if (get_device) {
1570                 get_device = 0;
1571                 continue;
1572             }
1573             ret = 0;
1574             break;
1575         }
1576         dbprintf("User prompt: '%s'; response: '%s'\n", prompt, line);
1577
1578         s = line;
1579         while ((ch = *s++) != '\0' && g_ascii_isspace(ch)) {
1580             (void)ch;   /* Quiet empty loop compiler warning */
1581         }
1582         if (ch == '?') {
1583             if (get_device) {
1584                 g_printf(_("Enter a new device name or \"default\"\n"));
1585             } else {
1586                 g_printf(_("Enter \"y\"es to continue, \"n\"o to stop"));
1587                 if(allow_skip) {
1588                     g_printf(_(", \"s\"kip this tape"));
1589                 }
1590                 if(allow_retry) {
1591                     g_printf(_(" or \"r\"etry this tape"));
1592                 }
1593                 if (allow_tape) {
1594                     g_printf(_(" or \"d\" to change to a new device"));
1595                 }
1596                 putchar('\n');
1597             }
1598         } else if (get_device) {
1599             char *tmp = stralloc(tape_server_name);
1600
1601             if (strncmp_const(s - 1, "default") == 0) {
1602                 set_device(tmp, NULL); /* default device, existing host */
1603             } else if (s[-1] != '\0') {
1604                 set_device(tmp, s - 1); /* specified device, existing host */
1605             } else {
1606                 g_printf(_("No change.\n"));
1607             }
1608
1609             amfree(tmp);
1610
1611             get_device = 0;
1612         } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1613             ret = 1;
1614         } else if (allow_tape && (ch == 'D' || ch == 'd' || ch == 'T' || ch == 't')) {
1615             get_device = 1; /* ('T' and 't' are for backward-compatibility) */
1616         } else if (ch == 'N' || ch == 'n') {
1617             ret = 0;
1618         } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1619             ret = RETRY_TAPE;
1620         } else if (allow_skip && (ch == 'S' || ch == 's')) {
1621             ret = SKIP_TAPE;
1622         }
1623     }
1624     /*@ignore@*/
1625     amfree(line);
1626     /*@end@*/
1627     return ret;
1628 }
1629
1630 static void
1631 send_to_tape_server(
1632     security_stream_t * stream,
1633     char *              cmd)
1634 {
1635     char *msg = stralloc2(cmd, "\r\n");
1636
1637     g_debug("send_to_tape_server: %s\n", cmd);
1638     if (security_stream_write(stream, msg, strlen(msg)) < 0)
1639     {
1640         error(_("Error writing to tape server"));
1641         exit(101);
1642         /*NOTREACHED*/
1643     }
1644     amfree(msg);
1645 }
1646
1647
1648 /* start up connection to tape server and set commands to initiate
1649    transfer of dump image.
1650    Return tape server socket on success, -1 on error. */
1651 static int
1652 extract_files_setup(
1653     char *      label,
1654     off_t       fsf)
1655 {
1656     char *disk_regex = NULL;
1657     char *host_regex = NULL;
1658     char *clean_datestamp, *ch, *ch1;
1659     char *tt = NULL;
1660     char *req;
1661     int response_error;
1662
1663     amidxtaped_secdrv = security_getdriver(authopt);
1664     if (amidxtaped_secdrv == NULL) {
1665         error(_("no '%s' security driver available for host '%s'"),
1666               authopt, tape_server_name);
1667     }
1668
1669     /* We assume that amidxtaped support fe_amidxtaped_options_features */
1670     /*                               and fe_amidxtaped_options_auth     */
1671     /* We should send a noop to really know                             */
1672     req = vstralloc("SERVICE amidxtaped\n",
1673                     "OPTIONS ", "features=", our_features_string, ";",
1674                                 "auth=", authopt, ";",
1675                     "\n", NULL);
1676     protocol_sendreq(tape_server_name, amidxtaped_secdrv,
1677                      generic_client_get_security_conf, req, STARTUP_TIMEOUT,
1678                      amidxtaped_response, &response_error);
1679     amfree(req);
1680     protocol_run();
1681     if(response_error != 0) {
1682         return -1;
1683     }
1684
1685     disk_regex = make_exact_disk_expression(disk_name);
1686     host_regex = make_exact_host_expression(dump_hostname);
1687
1688     clean_datestamp = stralloc(dump_datestamp);
1689     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1690         if(*ch1 != '-') {
1691             *ch = *ch1;
1692             ch++;
1693         }
1694     }
1695     *ch = '\0';
1696     /* push our feature list off to the tape server */
1697     /* XXX assumes that index server and tape server are equivalent, ew */
1698
1699     if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1700         tt = newstralloc2(tt, "FEATURES=", our_features_string);
1701         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1702         get_amidxtaped_line();
1703         if (!amidxtaped_line) {
1704             g_fprintf(stderr, _("amrecover - amidxtaped closed the connection\n"));
1705             stop_amidxtaped();
1706             amfree(disk_regex);
1707             amfree(host_regex);
1708             amfree(clean_datestamp);
1709             return -1;
1710         } else if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) {
1711             tapesrv_features = am_string_to_feature(amidxtaped_line+9);
1712         } else {
1713             g_fprintf(stderr, _("amrecover - expecting FEATURES line from amidxtaped\n"));
1714             stop_amidxtaped();
1715             amfree(disk_regex);
1716             amfree(host_regex);
1717             amfree(clean_datestamp);
1718             return -1;
1719         }
1720     } else {
1721         *tapesrv_features = *indexsrv_features;
1722     }
1723
1724
1725     if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1726        am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1727        am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1728        am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1729        am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1730
1731         if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1732             tt = newstralloc2(tt, "CONFIG=", get_config_name());
1733             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1734         }
1735         if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1736            label && label[0] != '/') {
1737             tt = newstralloc2(tt,"LABEL=",label);
1738             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1739         }
1740         if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1741             char v_fsf[100];
1742             g_snprintf(v_fsf, 99, "%lld", (long long)fsf);
1743             tt = newstralloc2(tt, "FSF=",v_fsf);
1744             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1745         }
1746         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER");
1747         tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1748         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1749         tt = newstralloc2(tt, "HOST=", host_regex);
1750         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1751         tt = newstralloc2(tt, "DISK=", disk_regex);
1752         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1753         tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1754         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1755         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END");
1756         amfree(tt);
1757     }
1758     else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1759         /* send to the tape server what tape file we want */
1760         /* 6 args:
1761          *   "-h"
1762          *   "-p"
1763          *   "tape device"
1764          *   "hostname"
1765          *   "diskname"
1766          *   "datestamp"
1767          */
1768         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "6");
1769         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-h");
1770         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-p");
1771         send_to_tape_server(amidxtaped_streams[CTLFD].fd, dump_device_name);
1772         send_to_tape_server(amidxtaped_streams[CTLFD].fd, host_regex);
1773         send_to_tape_server(amidxtaped_streams[CTLFD].fd, disk_regex);
1774         send_to_tape_server(amidxtaped_streams[CTLFD].fd, clean_datestamp);
1775
1776         dbprintf(_("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n"),
1777                   dump_device_name, host_regex, disk_regex, clean_datestamp);
1778     }
1779
1780     amfree(disk_regex);
1781     amfree(host_regex);
1782     amfree(clean_datestamp);
1783
1784     return 0;
1785 }
1786
1787
1788 /*
1789  * Reads the first block of a tape file.
1790  */
1791
1792 void
1793 read_file_header(
1794     char *      buffer,
1795     dumpfile_t *file,
1796     size_t      buflen,
1797     int         tapedev)
1798 {
1799     ssize_t bytes_read;
1800     bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1801     if(bytes_read < 0) {
1802         error(_("error reading header (%s), check amidxtaped.*.debug on server"),
1803               strerror(errno));
1804         /*NOTREACHED*/
1805     }
1806
1807     if((size_t)bytes_read < buflen) {
1808         g_fprintf(stderr, plural(_("%s: short block %d byte\n"),
1809                                _("%s: short block %d bytes\n"), bytes_read),
1810                 get_pname(), (int)bytes_read);
1811         print_header(stdout, file);
1812         error(_("Can't read file header"));
1813         /*NOTREACHED*/
1814     }
1815
1816     /* bytes_read == buflen */
1817     parse_file_header(buffer, file, (size_t)bytes_read);
1818 }
1819
1820 enum dumptypes {
1821         IS_UNKNOWN,
1822         IS_DUMP,
1823         IS_GNUTAR,
1824         IS_TAR,
1825         IS_SAMBA,
1826         IS_SAMBA_TAR,
1827         IS_APPLICATION_API
1828 };
1829
1830 static void
1831 extract_files_child(
1832     ctl_data_t         *ctl_data)
1833 {
1834     int save_errno;
1835     int   i;
1836     guint j;
1837     GPtrArray *argv_ptr = g_ptr_array_new();
1838     int files_off_tape;
1839     EXTRACT_LIST_ITEM *fn;
1840     enum dumptypes dumptype = IS_UNKNOWN;
1841     size_t len_program;
1842     char *cmd = NULL;
1843     guint passwd_field = 999999999;
1844 #ifdef SAMBA_CLIENT
1845     char *domain = NULL, *smbpass = NULL;
1846 #endif
1847
1848     /* code executed by child to do extraction */
1849     /* never returns */
1850
1851     /* make in_fd be our stdin */
1852     if (dup2(ctl_data->child_pipe[0], STDIN_FILENO) == -1)
1853     {
1854         error(_("dup2 failed in extract_files_child: %s"), strerror(errno));
1855         /*NOTREACHED*/
1856     }
1857
1858     if(ctl_data->file.type != F_DUMPFILE) {
1859         dump_dumpfile_t(&ctl_data->file);
1860         error(_("bad header"));
1861         /*NOTREACHED*/
1862     }
1863
1864     if (ctl_data->file.program[0] != '\0') {
1865         if (strcmp(ctl_data->file.program, "APPLICATION") == 0)
1866             dumptype = IS_APPLICATION_API;
1867 #ifdef GNUTAR
1868         if (strcmp(ctl_data->file.program, GNUTAR) == 0)
1869             dumptype = IS_GNUTAR;
1870 #endif
1871
1872         if (dumptype == IS_UNKNOWN) {
1873             len_program = strlen(ctl_data->file.program);
1874             if(len_program >= 3 &&
1875                strcmp(&ctl_data->file.program[len_program-3],"tar") == 0)
1876                 dumptype = IS_TAR;
1877         }
1878
1879 #ifdef SAMBA_CLIENT
1880         if (dumptype == IS_UNKNOWN && strcmp(ctl_data->file.program, SAMBA_CLIENT) ==0) {
1881             if (samba_extract_method == SAMBA_TAR)
1882               dumptype = IS_SAMBA_TAR;
1883             else
1884               dumptype = IS_SAMBA;
1885         }
1886 #endif
1887     }
1888
1889     /* form the arguments to restore */
1890     files_off_tape = length_of_tape_list(ctl_data->elist);
1891     switch(dumptype) {
1892     case IS_SAMBA:
1893 #ifdef SAMBA_CLIENT
1894         g_ptr_array_add(argv_ptr, stralloc("smbclient"));
1895         smbpass = findpass(ctl_data->file.disk, &domain);
1896         if (smbpass) {
1897             g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
1898             g_ptr_array_add(argv_ptr, stralloc("-U"));
1899             passwd_field = argv_ptr->len;
1900             g_ptr_array_add(argv_ptr, stralloc(smbpass));
1901             if (domain) {
1902                 g_ptr_array_add(argv_ptr, stralloc("-W"));
1903                 g_ptr_array_add(argv_ptr, stralloc(domain));
1904             }
1905         }
1906         g_ptr_array_add(argv_ptr, stralloc("-d0"));
1907         g_ptr_array_add(argv_ptr, stralloc("-Tx"));
1908         g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
1909         break;
1910 #endif
1911     case IS_TAR:
1912     case IS_GNUTAR:
1913         g_ptr_array_add(argv_ptr, stralloc("tar"));
1914         /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1915         g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1916         g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1917         g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1918         g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
1919         break;
1920     case IS_SAMBA_TAR:
1921         g_ptr_array_add(argv_ptr, stralloc("tar"));
1922         g_ptr_array_add(argv_ptr, stralloc("-xpvf"));
1923         g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
1924         break;
1925     case IS_UNKNOWN:
1926     case IS_DUMP:
1927         g_ptr_array_add(argv_ptr, stralloc("restore"));
1928 #ifdef AIX_BACKUP
1929         g_ptr_array_add(argv_ptr, stralloc("-xB"));
1930 #else
1931 #if defined(XFSDUMP)
1932         if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
1933             g_ptr_array_add(argv_ptr, stralloc("-v"));
1934             g_ptr_array_add(argv_ptr, stralloc("silent"));
1935         } else
1936 #endif
1937 #if defined(VDUMP)
1938         if (strcmp(ctl_data->file.program, VDUMP) == 0) {
1939             g_ptr_array_add(argv_ptr, stralloc("xf"));
1940             g_ptr_array_add(argv_ptr, stralloc("-"));   /* data on stdin */
1941         } else
1942 #endif
1943         {
1944         g_ptr_array_add(argv_ptr, stralloc("xbf"));
1945         g_ptr_array_add(argv_ptr, stralloc("2")); /* read in units of 1K */
1946         g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
1947         }
1948 #endif
1949         break;
1950     case IS_APPLICATION_API:
1951         g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.application));
1952         g_ptr_array_add(argv_ptr, stralloc("restore"));
1953         g_ptr_array_add(argv_ptr, stralloc("--config"));
1954         g_ptr_array_add(argv_ptr, stralloc(get_config_name()));
1955         g_ptr_array_add(argv_ptr, stralloc("--disk"));
1956         g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
1957         if (dump_dle && dump_dle->device) {
1958             g_ptr_array_add(argv_ptr, stralloc("--device"));
1959             g_ptr_array_add(argv_ptr, stralloc(dump_dle->device));
1960         }
1961         if (ctl_data->data_path == DATA_PATH_DIRECTTCP) {
1962             g_ptr_array_add(argv_ptr, stralloc("--data-path"));
1963             g_ptr_array_add(argv_ptr, stralloc("DIRECTTCP"));
1964             g_ptr_array_add(argv_ptr, stralloc("--direct-tcp"));
1965             g_ptr_array_add(argv_ptr, stralloc(ctl_data->addrs));
1966         }
1967         if (ctl_data->bsu && ctl_data->bsu->smb_recover_mode &&
1968             samba_extract_method == SAMBA_SMBCLIENT){
1969             g_ptr_array_add(argv_ptr, stralloc("--recover-mode"));
1970             g_ptr_array_add(argv_ptr, stralloc("smb"));
1971         }
1972         g_ptr_array_add(argv_ptr, stralloc("--level"));
1973         g_ptr_array_add(argv_ptr, g_strdup_printf("%d", ctl_data->elist->level));
1974         if (dump_dle) {
1975             GSList   *scriptlist;
1976             script_t *script;
1977
1978             merge_properties(dump_dle, NULL, dump_dle->application_property,
1979                              proplist, 0);
1980             application_property_add_to_argv(argv_ptr, dump_dle, NULL,
1981                                              tapesrv_features);
1982             for (scriptlist = dump_dle->scriptlist; scriptlist != NULL;
1983                  scriptlist = scriptlist->next) {
1984                 script = (script_t *)scriptlist->data;
1985                 if (script->result && script->result->proplist) {
1986                     property_add_to_argv(argv_ptr, script->result->proplist);
1987                 }
1988             }
1989
1990         } else if (proplist) {
1991             property_add_to_argv(argv_ptr, proplist);
1992         }
1993         break;
1994     }
1995
1996     for (i = 0, fn = ctl_data->elist->files; i < files_off_tape;
1997                                              i++, fn = fn->next)
1998     {
1999         switch (dumptype) {
2000         case IS_APPLICATION_API:
2001         case IS_TAR:
2002         case IS_GNUTAR:
2003         case IS_SAMBA_TAR:
2004         case IS_SAMBA:
2005             if (strcmp(fn->path, "/") == 0)
2006                 g_ptr_array_add(argv_ptr, stralloc("."));
2007             else
2008                 g_ptr_array_add(argv_ptr, stralloc2(".", fn->path));
2009             break;
2010         case IS_UNKNOWN:
2011         case IS_DUMP:
2012 #if defined(XFSDUMP)
2013             if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2014                 /*
2015                  * xfsrestore needs a -s option before each file to be
2016                  * restored, and also wants them to be relative paths.
2017                  */
2018                 g_ptr_array_add(argv_ptr, stralloc("-s"));
2019                 g_ptr_array_add(argv_ptr, stralloc(fn->path + 1));
2020             } else
2021 #endif
2022             {
2023             g_ptr_array_add(argv_ptr, stralloc(fn->path));
2024             }
2025             break;
2026         }
2027     }
2028 #if defined(XFSDUMP)
2029     if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2030         g_ptr_array_add(argv_ptr, stralloc("-"));
2031         g_ptr_array_add(argv_ptr, stralloc("."));
2032     }
2033 #endif
2034     g_ptr_array_add(argv_ptr, NULL);
2035
2036     switch (dumptype) {
2037     case IS_SAMBA:
2038 #ifdef SAMBA_CLIENT
2039         cmd = stralloc(SAMBA_CLIENT);
2040         break;
2041 #else
2042         /* fall through to ... */
2043 #endif
2044     case IS_TAR:
2045     case IS_GNUTAR:
2046     case IS_SAMBA_TAR:
2047 #ifndef GNUTAR
2048         g_fprintf(stderr, _("warning: GNUTAR program not available.\n"));
2049         cmd = stralloc("tar");
2050 #else
2051         cmd = stralloc(GNUTAR);
2052 #endif
2053         break;
2054     case IS_UNKNOWN:
2055     case IS_DUMP:
2056         cmd = NULL;
2057 #if defined(DUMP)
2058         if (strcmp(ctl_data->file.program, DUMP) == 0) {
2059             cmd = stralloc(RESTORE);
2060         }
2061 #endif
2062 #if defined(VDUMP)
2063         if (strcmp(ctl_data->file.program, VDUMP) == 0) {
2064             cmd = stralloc(VRESTORE);
2065         }
2066 #endif
2067 #if defined(VXDUMP)
2068         if (strcmp(ctl_data->file.program, VXDUMP) == 0) {
2069             cmd = stralloc(VXRESTORE);
2070         }
2071 #endif
2072 #if defined(XFSDUMP)
2073         if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2074             cmd = stralloc(XFSRESTORE);
2075         }
2076 #endif
2077         if (cmd == NULL) {
2078             g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
2079                     ctl_data->file.program);
2080             cmd = stralloc("restore");
2081         }
2082         break;
2083     case IS_APPLICATION_API:
2084         cmd = vstralloc(APPLICATION_DIR, "/", ctl_data->file.application, NULL);
2085         break;
2086     }
2087     if (cmd) {
2088         dbprintf(_("Exec'ing %s with arguments:\n"), cmd);
2089         for (j = 0; j < argv_ptr->len - 1; j++) {
2090             if (j == passwd_field)
2091                 dbprintf("\tXXXXX\n");
2092             else
2093                 dbprintf(_("\t%s\n"), (char *)g_ptr_array_index(argv_ptr, j));
2094         }
2095         safe_fd(-1, 0);
2096         (void)execv(cmd, (char **)argv_ptr->pdata);
2097         /* only get here if exec failed */
2098         save_errno = errno;
2099         g_ptr_array_free_full(argv_ptr);
2100         errno = save_errno;
2101         perror(_("amrecover couldn't exec"));
2102         g_fprintf(stderr, _(" problem executing %s\n"), cmd);
2103         amfree(cmd);
2104     }
2105     exit(1);
2106     /*NOT REACHED */
2107 }
2108
2109 /*
2110  * Interpose something between the process writing out the dump (writing it to
2111  * some extraction program, really) and the socket from which we're reading, so
2112  * that we can do things like prompt for human interaction for multiple tapes.
2113  */
2114 int
2115 writer_intermediary(
2116     EXTRACT_LIST *      elist)
2117 {
2118     ctl_data_t ctl_data;
2119     amwait_t   extractor_status;
2120
2121     ctl_data.header_done   = 0;
2122     ctl_data.child_pipe[0] = -1;
2123     ctl_data.child_pipe[1] = -1;
2124     ctl_data.pid           = -1;
2125     ctl_data.elist         = elist;
2126     fh_init(&ctl_data.file);
2127     ctl_data.data_path     = DATA_PATH_AMANDA;
2128     ctl_data.addrs         = NULL;
2129     ctl_data.bsu           = NULL;
2130     ctl_data.bytes_read    = 0;
2131
2132     header_size = 0;
2133     security_stream_read(amidxtaped_streams[DATAFD].fd,
2134                          read_amidxtaped_data, &ctl_data);
2135
2136     while(get_amidxtaped_line() >= 0) {
2137         char desired_tape[MAX_TAPE_LABEL_BUF];
2138         g_debug("get amidxtaped line: %s", amidxtaped_line);
2139
2140         /* if prompted for a tape, relay said prompt to the user */
2141         if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
2142             int done;
2143             g_printf(_("Load tape %s now\n"), desired_tape);
2144             dbprintf(_("Requesting tape %s from user\n"), desired_tape);
2145             done = okay_to_continue(am_has_feature(indexsrv_features,
2146                                                    fe_amrecover_feedme_tape),
2147                                     0, 0);
2148             if (done == 1) {
2149                 if (am_has_feature(indexsrv_features,
2150                                    fe_amrecover_feedme_tape)) {
2151                     char *reply = stralloc2("TAPE ", tape_device_name);
2152                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, reply);
2153                     amfree(reply);
2154                 } else {
2155                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK");
2156                 }
2157             } else {
2158                 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
2159                 break;
2160             }
2161         } else if (strncmp_const(amidxtaped_line, "USE-DATAPATH ") == 0) {
2162             if (strncmp_const(amidxtaped_line+13, "AMANDA") == 0) {
2163                 ctl_data.data_path = DATA_PATH_AMANDA;
2164                 g_debug("Using AMANDA data-path");
2165             } else if (strncmp_const(amidxtaped_line+13, "DIRECT-TCP") == 0) {
2166                 ctl_data.data_path = DATA_PATH_DIRECTTCP;
2167                 ctl_data.addrs = stralloc(amidxtaped_line+24);
2168                 g_debug("Using DIRECT-TCP data-path with %s", ctl_data.addrs);
2169             }
2170             start_processing_data(&ctl_data);
2171         } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) {
2172             g_printf("%s\n",&amidxtaped_line[8]);
2173         } else {
2174             g_fprintf(stderr, _("Strange message from tape server: %s"),
2175                     amidxtaped_line);
2176             break;
2177         }
2178     }
2179
2180     /* CTL might be close before DATA */
2181     event_loop(0);
2182     dumpfile_free_data(&ctl_data.file);
2183     amfree(ctl_data.addrs);
2184     amfree(ctl_data.bsu);
2185     if (ctl_data.child_pipe[1] != -1)
2186         aclose(ctl_data.child_pipe[1]);
2187
2188     if (ctl_data.header_done == 0) {
2189         g_printf(_("Got no header and data from server, check in amidxtaped.*.debug and amandad.*.debug files on server\n"));
2190     }
2191
2192     if (ctl_data.pid != -1) {
2193         waitpid(ctl_data.pid, &extractor_status, 0);
2194         if(WEXITSTATUS(extractor_status) != 0){
2195             int ret = WEXITSTATUS(extractor_status);
2196             if(ret == 255) ret = -1;
2197             g_printf(_("Extractor child exited with status %d\n"), ret);
2198             return -1;
2199         }
2200     }
2201     g_debug("bytes read: %jd", (intmax_t)ctl_data.bytes_read);
2202     return(0);
2203 }
2204
2205 /* exec restore to do the actual restoration */
2206
2207 /* does the actual extraction of files */
2208 /*
2209  * The original design had the dump image being returned exactly as it
2210  * appears on the tape, and this routine getting from the index server
2211  * whether or not it is compressed, on the assumption that the tape
2212  * server may not know how to uncompress it. But
2213  * - Amrestore can't do that. It returns either compressed or uncompressed
2214  * (always). Amrestore assumes it can uncompress files. It is thus a good
2215  * idea to run the tape server on a machine with gzip.
2216  * - The information about compression in the disklist is really only
2217  * for future dumps. It is possible to change compression on a drive
2218  * so the information in the disklist may not necessarily relate to
2219  * the dump image on the tape.
2220  *   Consequently the design was changed to assuming that amrestore can
2221  * uncompress any dump image and have it return an uncompressed file
2222  * always.
2223  */
2224 void
2225 extract_files(void)
2226 {
2227     EXTRACT_LIST *elist;
2228     char *l;
2229     int first;
2230     int otc;
2231     tapelist_t *tlist = NULL, *a_tlist;
2232     g_option_t g_options;
2233     levellist_t all_level = NULL;
2234     int last_level;
2235
2236     if (!is_extract_list_nonempty())
2237     {
2238         g_printf(_("Extract list empty - No files to extract!\n"));
2239         return;
2240     }
2241
2242     clean_extract_list();
2243
2244     /* get tape device name from index server if none specified */
2245     if (tape_server_name == NULL) {
2246         tape_server_name = newstralloc(tape_server_name, server_name);
2247     }
2248     if (tape_device_name == NULL) {
2249         if (send_command("TAPE") == -1)
2250             exit(1);
2251         if (get_reply_line() == -1)
2252             exit(1);
2253         l = reply_line();
2254         if (!server_happy())
2255         {
2256             g_printf("%s\n", l);
2257             exit(1);
2258         }
2259         /* skip reply number */
2260         tape_device_name = newstralloc(tape_device_name, l+4);
2261     }
2262
2263     if (strcmp(tape_device_name, "/dev/null") == 0)
2264     {
2265         g_printf(_("amrecover: warning: using %s as the tape device will not work\n"),
2266                tape_device_name);
2267     }
2268
2269     first=1;
2270     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2271         if(elist->tape[0]!='/') {
2272             if(first) {
2273                 g_printf(_("\nExtracting files using tape drive %s on host %s.\n"),
2274                         tape_device_name, tape_server_name);
2275                 g_printf(_("The following tapes are needed:"));
2276                 first=0;
2277             }
2278             else
2279                 g_printf("                               ");
2280             tlist = unmarshal_tapelist_str(elist->tape);
2281             for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next)
2282                 g_printf(" %s", a_tlist->label);
2283             g_printf("\n");
2284             free_tapelist(tlist);
2285         }
2286     }
2287     first=1;
2288     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2289         if(elist->tape[0]=='/') {
2290             if(first) {
2291                 g_printf(_("\nExtracting files from holding disk on host %s.\n"),
2292                         tape_server_name);
2293                 g_printf(_("The following files are needed:"));
2294                 first=0;
2295             }
2296             else
2297                 g_printf("                               ");
2298             tlist = unmarshal_tapelist_str(elist->tape);
2299             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2300                 g_printf(" %s", a_tlist->label);
2301             g_printf("\n");
2302             free_tapelist(tlist);
2303         }
2304     }
2305     g_printf("\n");
2306
2307     g_options.config = get_config_name();
2308     g_options.hostname = dump_hostname;
2309     for (elist = first_tape_list(); elist != NULL;
2310          elist = next_tape_list(elist)) {
2311         am_level_t *level = g_new0(am_level_t, 1);
2312         level->level = elist->level;
2313         all_level = g_slist_append(all_level, level);
2314     }
2315     if (dump_dle) {
2316         slist_free_full(dump_dle->levellist, g_free);
2317         dump_dle->levellist = all_level;
2318         run_client_scripts(EXECUTE_ON_PRE_RECOVER, &g_options, dump_dle,
2319                            stderr);
2320         dump_dle->levellist = NULL;
2321     }
2322     last_level = -1;
2323     while ((elist = first_tape_list()) != NULL)
2324     {
2325         if(elist->tape[0]=='/') {
2326             dump_device_name = newstralloc(dump_device_name, elist->tape);
2327             g_printf(_("Extracting from file "));
2328             tlist = unmarshal_tapelist_str(dump_device_name);
2329             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2330                 g_printf(" %s", a_tlist->label);
2331             g_printf("\n");
2332             free_tapelist(tlist);
2333         }
2334         else {
2335             g_printf(_("Extracting files using tape drive %s on host %s.\n"),
2336                    tape_device_name, tape_server_name);
2337             tlist = unmarshal_tapelist_str(elist->tape);
2338             g_printf(_("Load tape %s now\n"), tlist->label);
2339             dbprintf(_("Requesting tape %s from user\n"), tlist->label);
2340             free_tapelist(tlist);
2341             otc = okay_to_continue(1,1,0);
2342             if (otc == 0)
2343                 return;
2344             else if (otc == SKIP_TAPE) {
2345                 delete_tape_list(elist); /* skip this tape */
2346                 continue;
2347             }
2348             dump_device_name = newstralloc(dump_device_name, tape_device_name);
2349         }
2350         dump_datestamp = newstralloc(dump_datestamp, elist->date);
2351
2352         if (last_level != -1 && dump_dle) {
2353             am_level_t *level;
2354
2355             level = g_new0(am_level_t, 1);
2356             level->level = last_level;
2357             dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2358
2359             level = g_new0(am_level_t, 1);
2360             level->level = elist->level;
2361             dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2362             run_client_scripts(EXECUTE_ON_INTER_LEVEL_RECOVER, &g_options,
2363                                dump_dle, stderr);
2364             slist_free_full(dump_dle->levellist, g_free);
2365             dump_dle->levellist = NULL;
2366         }
2367
2368         /* connect to the tape handler daemon on the tape drive server */
2369         if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
2370         {
2371             g_fprintf(stderr, _("amrecover - can't talk to tape server: %s\n"),
2372                     errstr);
2373             return;
2374         }
2375         if (dump_dle) {
2376             am_level_t *level;
2377
2378             level = g_new0(am_level_t, 1);
2379             level->level = elist->level;
2380             dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2381             run_client_scripts(EXECUTE_ON_PRE_LEVEL_RECOVER, &g_options,
2382                                dump_dle, stderr);
2383         }
2384         last_level = elist->level;
2385
2386         /* if the server have fe_amrecover_feedme_tape, it has asked for
2387          * the tape itself, even if the restore didn't succeed, we should
2388          * remove it.
2389          */
2390         if(writer_intermediary(elist) == 0 ||
2391            am_has_feature(indexsrv_features, fe_amrecover_feedme_tape))
2392             delete_tape_list(elist);    /* tape done so delete from list */
2393
2394         am_release_feature_set(tapesrv_features);
2395         stop_amidxtaped();
2396
2397         if (dump_dle) {
2398             run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options,
2399                                dump_dle, stderr);
2400             slist_free_full(dump_dle->levellist, g_free);
2401             dump_dle->levellist = NULL;
2402         }
2403     }
2404     if (dump_dle) {
2405         dump_dle->levellist = all_level;
2406         run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle,
2407                            stderr);
2408         slist_free_full(dump_dle->levellist, g_free);
2409         all_level = NULL;
2410         dump_dle->levellist = NULL;
2411     }
2412 }
2413
2414 static void
2415 amidxtaped_response(
2416     void *              datap,
2417     pkt_t *             pkt,
2418     security_handle_t * sech)
2419 {
2420     int ports[NSTREAMS], *response_error = datap, i;
2421     char *p;
2422     char *tok;
2423     char *extra = NULL;
2424
2425     assert(response_error != NULL);
2426     assert(sech != NULL);
2427     memset(ports, -1, SIZEOF(ports));
2428
2429     if (pkt == NULL) {
2430         errstr = newvstrallocf(errstr, _("[request failed: %s]"), security_geterror(sech));
2431         *response_error = 1;
2432         return;
2433     }
2434     security_close_connection(sech, dump_hostname);
2435
2436     if (pkt->type == P_NAK) {
2437 #if defined(PACKET_DEBUG)
2438         g_fprintf(stderr, _("got nak response:\n----\n%s\n----\n\n"), pkt->body);
2439 #endif
2440
2441         tok = strtok(pkt->body, " ");
2442         if (tok == NULL || strcmp(tok, "ERROR") != 0)
2443             goto bad_nak;
2444
2445         tok = strtok(NULL, "\n");
2446         if (tok != NULL) {
2447             errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
2448             *response_error = 1;
2449         } else {
2450 bad_nak:
2451             errstr = newstralloc(errstr, "request NAK");
2452             *response_error = 2;
2453         }
2454         return;
2455     }
2456
2457     if (pkt->type != P_REP) {
2458         errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
2459                               pkt_type2str(pkt->type), pkt->body);
2460         *response_error = 1;
2461         return;
2462     }
2463
2464 #if defined(PACKET_DEBUG)
2465     g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
2466 #endif
2467
2468     for(i = 0; i < NSTREAMS; i++) {
2469         ports[i] = -1;
2470         amidxtaped_streams[i].fd = NULL;
2471     }
2472
2473     p = pkt->body;
2474     while((tok = strtok(p, " \n")) != NULL) {
2475         p = NULL;
2476
2477         /*
2478          * Error response packets have "ERROR" followed by the error message
2479          * followed by a newline.
2480          */
2481         if (strcmp(tok, "ERROR") == 0) {
2482             tok = strtok(NULL, "\n");
2483             if (tok == NULL)
2484                 tok = _("[bogus error packet]");
2485             errstr = newstralloc(errstr, tok);
2486             *response_error = 2;
2487             return;
2488         }
2489
2490
2491         /*
2492          * Regular packets have CONNECT followed by three streams
2493          */
2494         if (strcmp(tok, "CONNECT") == 0) {
2495
2496             /*
2497              * Parse the three stream specifiers out of the packet.
2498              */
2499             for (i = 0; i < NSTREAMS; i++) {
2500                 tok = strtok(NULL, " ");
2501                 if (tok == NULL || strcmp(tok, amidxtaped_streams[i].name) != 0) {
2502                     extra = vstrallocf(_("CONNECT token is \"%s\": expected \"%s\""),
2503                                       tok ? tok : "(null)",
2504                                       amidxtaped_streams[i].name);
2505                     goto parse_error;
2506                 }
2507                 tok = strtok(NULL, " \n");
2508                 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
2509                     extra = vstrallocf(_("CONNECT %s token is \"%s\": expected a port number"),
2510                                       amidxtaped_streams[i].name,
2511                                       tok ? tok : "(null)");
2512                     goto parse_error;
2513                 }
2514             }
2515             continue;
2516         }
2517
2518         /*
2519          * OPTIONS [options string] '\n'
2520          */
2521         if (strcmp(tok, "OPTIONS") == 0) {
2522             tok = strtok(NULL, "\n");
2523             if (tok == NULL) {
2524                 extra = stralloc(_("OPTIONS token is missing"));
2525                 goto parse_error;
2526             }
2527 /*
2528             while((p = strchr(tok, ';')) != NULL) {
2529                 *p++ = '\0';
2530                 if(strncmp_const(tok, "features=") == 0) {
2531                     tok += sizeof("features=") - 1;
2532                     am_release_feature_set(their_features);
2533                     if((their_features = am_string_to_feature(tok)) == NULL) {
2534                         errstr = newvstralloc(errstr,
2535                                               _("OPTIONS: bad features value: "),
2536                                               tok,
2537                                               NULL);
2538                         goto parse_error;
2539                     }
2540                 }
2541                 tok = p;
2542             }
2543 */
2544             continue;
2545         }
2546 /*
2547         extra = vstrallocf("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""),
2548                           tok ? tok : _("(null)"));
2549         goto parse_error;
2550 */
2551     }
2552
2553     /*
2554      * Connect the streams to their remote ports
2555      */
2556     for (i = 0; i < NSTREAMS; i++) {
2557         if (ports[i] == -1)
2558             continue;
2559         amidxtaped_streams[i].fd = security_stream_client(sech, ports[i]);
2560         dbprintf(_("amidxtaped_streams[%d].fd = %p\n"),i, amidxtaped_streams[i].fd);
2561         if (amidxtaped_streams[i].fd == NULL) {
2562             errstr = newvstrallocf(errstr,\
2563                         _("[could not connect %s stream: %s]"),
2564                         amidxtaped_streams[i].name,
2565                         security_geterror(sech));
2566             goto connect_error;
2567         }
2568     }
2569     /*
2570      * Authenticate the streams
2571      */
2572     for (i = 0; i < NSTREAMS; i++) {
2573         if (amidxtaped_streams[i].fd == NULL)
2574             continue;
2575         if (security_stream_auth(amidxtaped_streams[i].fd) < 0) {
2576             errstr = newvstrallocf(errstr,
2577                 _("[could not authenticate %s stream: %s]"),
2578                 amidxtaped_streams[i].name,
2579                 security_stream_geterror(amidxtaped_streams[i].fd));
2580             goto connect_error;
2581         }
2582     }
2583
2584     /*
2585      * The CTLFD and DATAFD streams are mandatory.  If we didn't get
2586      * them, complain.
2587      */
2588     if (amidxtaped_streams[CTLFD].fd == NULL) {
2589         errstr = newvstrallocf(errstr, _("[couldn't open CTL streams]"));
2590         goto connect_error;
2591     }
2592     if (amidxtaped_streams[DATAFD].fd == NULL) {
2593         errstr = newvstrallocf(errstr, _("[couldn't open DATA streams]"));
2594         goto connect_error;
2595     }
2596
2597     /* everything worked */
2598     *response_error = 0;
2599     return;
2600
2601 parse_error:
2602     if (extra) {
2603         errstr = newvstrallocf(errstr,
2604                           _("[parse of reply message failed: %s]"), extra);
2605     } else {
2606         errstr = newvstrallocf(errstr,
2607                           _("[parse of reply message failed: (no additional information)"));
2608     }
2609     amfree(extra);
2610     *response_error = 2;
2611     return;
2612
2613 connect_error:
2614     stop_amidxtaped();
2615     *response_error = 1;
2616 }
2617
2618 /*
2619  * This is called when everything needs to shut down so event_loop()
2620  * will exit.
2621  */
2622 static void
2623 stop_amidxtaped(void)
2624 {
2625     int i;
2626
2627     for (i = 0; i < NSTREAMS; i++) {
2628         if (amidxtaped_streams[i].fd != NULL) {
2629             security_stream_close(amidxtaped_streams[i].fd);
2630             amidxtaped_streams[i].fd = NULL;
2631         }
2632     }
2633 }
2634
2635 static char* ctl_buffer = NULL;
2636 /* gets a "line" from server and put in server_line */
2637 /* server_line is terminated with \0, \r\n is striped */
2638 /* returns -1 if error */
2639
2640 int
2641 get_amidxtaped_line(void)
2642 {
2643     ssize_t size;
2644     char *newbuf, *s;
2645     void *buf;
2646
2647     amfree(amidxtaped_line);
2648     if (!ctl_buffer)
2649         ctl_buffer = stralloc("");
2650
2651     while (!strstr(ctl_buffer,"\r\n")) {
2652         if (amidxtaped_streams[CTLFD].fd == NULL)
2653             return -1;
2654
2655         size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
2656         if(size < 0) {
2657             return -1;
2658         }
2659         else if(size == 0) {
2660             return -1;
2661         }
2662         newbuf = alloc(strlen(ctl_buffer)+size+1);
2663         strncpy(newbuf, ctl_buffer, (size_t)(strlen(ctl_buffer) + size + 1));
2664         memcpy(newbuf+strlen(ctl_buffer), buf, (size_t)size);
2665         newbuf[strlen(ctl_buffer)+size] = '\0';
2666         amfree(ctl_buffer);
2667         ctl_buffer = newbuf;
2668         amfree(buf);
2669     }
2670
2671     s = strstr(ctl_buffer,"\r\n");
2672     *s = '\0';
2673     newbuf = stralloc(s+2);
2674     amidxtaped_line = stralloc(ctl_buffer);
2675     amfree(ctl_buffer);
2676     ctl_buffer = newbuf;
2677     return 0;
2678 }
2679
2680
2681 static void
2682 read_amidxtaped_data(
2683     void *      cookie,
2684     void *      buf,
2685     ssize_t     size)
2686 {
2687     ctl_data_t *ctl_data = (ctl_data_t *)cookie;
2688     assert(cookie != NULL);
2689
2690     if (size < 0) {
2691         errstr = newstralloc2(errstr, _("amidxtaped read: "),
2692                  security_stream_geterror(amidxtaped_streams[DATAFD].fd));
2693         return;
2694     }
2695
2696     /*
2697      * EOF.  Stop and return.
2698      */
2699     if (size == 0) {
2700         security_stream_close(amidxtaped_streams[DATAFD].fd);
2701         amidxtaped_streams[DATAFD].fd = NULL;
2702         /*
2703          * If the mesg fd has also shut down, then we're done.
2704          */
2705         return;
2706     }
2707
2708     assert(buf != NULL);
2709
2710     if (ctl_data->header_done == 0) {
2711         GPtrArray  *errarray;
2712         g_option_t  g_options;
2713         data_path_t data_path_set = DATA_PATH_AMANDA;
2714         int to_move;
2715
2716         to_move = MIN(32768-header_size, size);
2717         memcpy(header_buf+header_size, buf, to_move);
2718         header_size += to_move;
2719
2720         g_debug("read header %zd => %d", size, header_size);
2721         if (header_size < 32768) {
2722             /* wait to read more data */
2723             return;
2724         } else if (header_size > 32768) {
2725             error("header_size is %d\n", header_size);
2726         }
2727         assert (to_move == size);
2728         security_stream_read_cancel(amidxtaped_streams[DATAFD].fd);
2729         /* parse the file header */
2730         fh_init(&ctl_data->file);
2731         parse_file_header(header_buf, &ctl_data->file, (size_t)header_size);
2732
2733         /* call backup_support_option */
2734         g_options.config = get_config_name();
2735         g_options.hostname = dump_hostname;
2736         if (strcmp(ctl_data->file.program, "APPLICATION") == 0) {
2737             if (dump_dle) {
2738                 ctl_data->bsu = backup_support_option(ctl_data->file.application,
2739                                                       &g_options,
2740                                                       ctl_data->file.disk,
2741                                                       dump_dle->device,
2742                                                       &errarray);
2743             } else {
2744                 ctl_data->bsu = backup_support_option(ctl_data->file.application,
2745                                                       &g_options,
2746                                                       ctl_data->file.disk, NULL,
2747                                                       &errarray);
2748             }
2749             if (!ctl_data->bsu) {
2750                 guint  i;
2751                 for (i=0; i < errarray->len; i++) {
2752                     char *line;
2753                     line = g_ptr_array_index(errarray, i);
2754                     g_fprintf(stderr, "%s\n", line);
2755                 }
2756                 g_ptr_array_free_full(errarray);
2757                 exit(1);
2758             }
2759             data_path_set = ctl_data->bsu->data_path_set;
2760         }
2761         /* handle backup_support_option failure */
2762
2763         ctl_data->header_done = 1;
2764         if (!ask_file_overwrite(ctl_data)) {
2765             if (am_has_feature(tapesrv_features, fe_amidxtaped_abort)) {
2766                 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ABORT");
2767             }
2768             stop_amidxtaped();
2769             return;
2770         }
2771
2772         if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
2773             char       *msg;
2774             /* send DATA-PATH request */
2775             msg = stralloc("AVAIL-DATAPATH");
2776             if (data_path_set & DATA_PATH_AMANDA)
2777                 vstrextend(&msg, " AMANDA", NULL);
2778             if (data_path_set & DATA_PATH_DIRECTTCP)
2779                 vstrextend(&msg, " DIRECT-TCP", NULL);
2780             send_to_tape_server(amidxtaped_streams[CTLFD].fd, msg);
2781             amfree(msg);
2782         } else {
2783             start_processing_data(ctl_data);
2784         }
2785     } else {
2786         ctl_data->bytes_read += size;
2787         /* Only the data is sent to the child */
2788         /*
2789          * We ignore errors while writing to the index file.
2790          */
2791         (void)full_write(ctl_data->child_pipe[1], buf, (size_t)size);
2792     }
2793 }
2794
2795 static gboolean
2796 ask_file_overwrite(
2797     ctl_data_t *ctl_data)
2798 {
2799     char *restore_dir = NULL;
2800
2801     if (ctl_data->file.dumplevel == 0) {
2802         property_t *property = g_hash_table_lookup(proplist, "directory");
2803         if (property && property->values && property->values->data) {
2804             /* take first property value */
2805             restore_dir = strdup(property->values->data);
2806         }
2807         if (samba_extract_method == SAMBA_SMBCLIENT ||
2808             (ctl_data->bsu &&
2809              ctl_data->bsu->recover_path == RECOVER_PATH_REMOTE)) {
2810             if (!restore_dir) {
2811                 restore_dir = g_strdup(ctl_data->file.disk);
2812             }
2813             g_printf(_("Restoring files into target host %s\n"), restore_dir);
2814         } else {
2815             if (!restore_dir) {
2816                 restore_dir = g_get_current_dir();
2817             }
2818             g_printf(_("Restoring files into directory %s\n"), restore_dir);
2819         }
2820
2821         /* Collect files to delete befause of a bug in gnutar */
2822         if (strcmp(ctl_data->file.program, "GNUTAR") == 0 ||
2823             (strcmp(ctl_data->file.program, "APPLICATION") == 0 &&
2824              strcmp(ctl_data->file.application, "amgtar") == 0)) {
2825             check_file_overwrite(restore_dir);
2826         } else {
2827             g_printf(_("All existing files in %s can be deleted\n"),
2828                      restore_dir);
2829         }
2830
2831         if (!okay_to_continue(0,0,0)) {
2832             free_unlink_list();
2833             amfree(restore_dir);
2834             return FALSE;
2835         }
2836         g_printf("\n");
2837
2838         /* delete the files for gnutar */
2839         if (unlink_list) {
2840             if (!do_unlink_list()) {
2841                 g_fprintf(stderr, _("Can't recover because I can't cleanup the restore directory (%s)\n"),
2842                           restore_dir);
2843                 free_unlink_list();
2844                 amfree(restore_dir);
2845                 return FALSE;
2846             }
2847             free_unlink_list();
2848         }
2849         amfree(restore_dir);
2850     }
2851     return TRUE;
2852 }
2853
2854 static void
2855 start_processing_data(
2856     ctl_data_t *ctl_data)
2857 {
2858     if (pipe(ctl_data->child_pipe) == -1) {
2859         error(_("extract_list - error setting up pipe to extractor: %s\n"),
2860               strerror(errno));
2861         /*NOTREACHED*/
2862     }
2863
2864     /* decrypt */
2865     if (ctl_data->file.encrypted) {
2866         char *argv[3];
2867         int  crypt_out;
2868         int  errfd = fileno(stderr);
2869
2870         g_debug("image is encrypted %s %s", ctl_data->file.clnt_encrypt, ctl_data->file.clnt_decrypt_opt);
2871         argv[0] = ctl_data->file.clnt_encrypt;
2872         argv[1] = ctl_data->file.clnt_decrypt_opt;
2873         argv[2] = NULL;
2874         pipespawnv(ctl_data->file.clnt_encrypt, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &crypt_out, &errfd, argv);
2875         ctl_data->child_pipe[0] = crypt_out;
2876     }
2877
2878     /* decompress */
2879     if (ctl_data->file.compressed) {
2880         char *argv[3];
2881         int  comp_out;
2882         int  errfd = fileno(stderr);
2883         char *comp_prog;
2884         char *comp_arg;
2885
2886         g_debug("image is compressed %s", ctl_data->file.clntcompprog);
2887         if (strlen(ctl_data->file.clntcompprog) > 0) {
2888             comp_prog = ctl_data->file.clntcompprog;
2889             comp_arg = "-d";
2890         } else {
2891             comp_prog = UNCOMPRESS_PATH;
2892             comp_arg = UNCOMPRESS_OPT;
2893         }
2894         argv[0] = comp_prog;
2895         argv[1] = comp_arg;
2896         argv[2] = NULL;
2897         pipespawnv(comp_prog, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &comp_out, &errfd, argv);
2898         ctl_data->child_pipe[0] = comp_out;
2899     }
2900
2901     /* okay, ready to extract. fork a child to do the actual work */
2902     if ((ctl_data->pid = fork()) == 0) {
2903         /* this is the child process */
2904         /* never gets out of this clause */
2905         aclose(ctl_data->child_pipe[1]);
2906         extract_files_child(ctl_data);
2907         /*NOTREACHED*/
2908     }
2909         
2910     if (ctl_data->pid == -1) {
2911         errstr = newstralloc(errstr, _("writer_intermediary - error forking child"));
2912         g_printf(_("writer_intermediary - error forking child"));
2913         return;
2914     }
2915     aclose(ctl_data->child_pipe[0]);
2916     security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data,
2917                          ctl_data);
2918     if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
2919         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "DATAPATH-OK");
2920     }
2921 }