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