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