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