Imported Upstream version 3.3.1
[debian/amanda] / recover-src / extract_list.c
index afb6278a58e9cfa7badda94f21b3615512e11742..a5a4251341a7b71670a96803c6338dcc623ec3b2 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: extract_list.c,v 1.117 2006/08/24 01:57:15 paddy_s Exp $
+ * $Id$
  *
  * implements the "extract" command in amrecover
  */
 
 #include "amanda.h"
-#include "version.h"
+#include "match.h"
 #include "amrecover.h"
 #include "fileheader.h"
 #include "dgram.h"
 #include "conffile.h"
 #include "protocol.h"
 #include "event.h"
+#include "client_util.h"
 #include "security.h"
+#include "pipespawn.h"
 
 typedef struct EXTRACT_LIST_ITEM {
     char *path;
+    char *tpath;
     struct EXTRACT_LIST_ITEM *next;
 }
 EXTRACT_LIST_ITEM;
@@ -62,6 +65,18 @@ typedef struct EXTRACT_LIST {
 }
 EXTRACT_LIST;
 
+typedef struct ctl_data_s {
+  int                      header_done;
+  int                      child_pipe[2];
+  int                      pid;
+  EXTRACT_LIST            *elist;
+  dumpfile_t               file;
+  data_path_t              data_path;
+  char                    *addrs;
+  backup_support_option_t *bsu;
+  gint64                   bytes_read;
+} ctl_data_t;
+
 #define SKIP_TAPE 2
 #define RETRY_TAPE 3
 
@@ -79,12 +94,13 @@ static struct {
 
 static void amidxtaped_response(void *, pkt_t *, security_handle_t *);
 static void stop_amidxtaped(void);
-char *amidxtaped_client_get_security_conf(char *, void *);
 static char *dump_device_name = NULL;
 static char *errstr;
 static char *amidxtaped_line = NULL;
-
 extern char *localhost;
+static char header_buf[32768];
+static int  header_size = 0;
+
 
 /* global pid storage for interrupt handler */
 pid_t extract_restore_child_pid = -1;
@@ -92,9 +108,7 @@ pid_t extract_restore_child_pid = -1;
 static EXTRACT_LIST *extract_list = NULL;
 static const security_driver_t *amidxtaped_secdrv;
 
-#ifdef SAMBA_CLIENT
 unsigned short samba_extract_method = SAMBA_TAR;
-#endif /* SAMBA_CLIENT */
 
 #define READ_TIMEOUT   240*60
 
@@ -131,11 +145,14 @@ static ssize_t read_buffer(int datafd,
                        size_t buflen,
                        long timeout_s);
 static void clear_tape_list(EXTRACT_LIST *tape_list);
-static void extract_files_child(int in_fd, EXTRACT_LIST *elist);
+static void extract_files_child(ctl_data_t *ctl_data);
 static void send_to_tape_server(security_stream_t *stream, char *cmd);
 int writer_intermediary(EXTRACT_LIST *elist);
 int get_amidxtaped_line(void);
 static void read_amidxtaped_data(void *, void *, ssize_t);
+static char *merge_path(char *path1, char *path2);
+static gboolean ask_file_overwrite(ctl_data_t *ctl_data);
+static void start_processing_data(ctl_data_t *ctl_data);
 
 /*
  * Function:  ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
@@ -249,13 +266,14 @@ clear_tape_list(
     EXTRACT_LIST *     tape_list)
 {
     EXTRACT_LIST_ITEM *this, *next;
-    
+
 
     this = tape_list->files;
     while (this != NULL)
     {
        next = this->next;
         amfree(this->path);
+        amfree(this->tpath);
         amfree(this);
        this = next;
     }
@@ -369,6 +387,7 @@ clean_tape_list(
                ofn2 = fn2;
                fn2 = fn2->next;
                amfree(ofn2->path);
+               amfree(ofn2->tpath);
                amfree(ofn2);
                pfn2->next = fn2;
            } else if (remove_fn1 == 0) {
@@ -380,10 +399,11 @@ clean_tape_list(
        if(remove_fn1 != 0) {
            /* fn2->path is always valid */
            /*@i@*/ dbprintf(_("removing path %s, it is included in %s\n"),
-           /*@i@*/           fn1->path, fn2->path);
+           /*@i@*/           fn1->tpath, fn2->tpath);
            ofn1 = fn1;
            fn1 = fn1->next;
            amfree(ofn1->path);
+           amfree(ofn1->tpath);
            if(pfn1 == NULL) {
                amfree(tape_list->files);
                tape_list->files = fn1;
@@ -399,6 +419,19 @@ clean_tape_list(
 }
 
 
+static char *
+file_of_path(
+    char *path,
+    char **dir)
+{
+    char *npath = g_path_get_basename(path);
+    *dir = g_path_get_dirname(path);
+    if (strcmp(*dir, ".") == 0) {
+       amfree(*dir);
+    }
+    return npath;
+}
+
 void
 clean_extract_list(void)
 {
@@ -554,7 +587,7 @@ add_extract_item(
 {
     EXTRACT_LIST *this, *this1;
     EXTRACT_LIST_ITEM *that, *curr;
-    char *ditem_path = NULL;
+    char *ditem_path;
 
     ditem_path = stralloc(ditem->path);
     clean_pathname(ditem_path);
@@ -569,16 +602,16 @@ add_extract_item(
            while(curr!=NULL)
            {
                if (strcmp(curr->path,ditem_path) == 0) {
-                   amfree(ditem_path);
+                   g_free(ditem_path);
                    return 1;
                }
                curr=curr->next;
            }
            that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
-            that->path = stralloc(ditem_path);
+            that->path = ditem_path;
+           that->tpath = clean_pathname(g_strdup(ditem->tpath));
            that->next = this->files;
            this->files = that;         /* add at front since easiest */
-           amfree(ditem_path);
            return 0;
        }
     }
@@ -590,18 +623,18 @@ add_extract_item(
     this->fileno = ditem->fileno;
     this->date = stralloc(ditem->date);
     that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
-    that->path = stralloc(ditem_path);
+    that->path = ditem_path;
+    that->tpath = clean_pathname(g_strdup(ditem->tpath));
     that->next = NULL;
     this->files = that;
 
     /* add this in date increasing order          */
     /* because restore must be done in this order */
     /* add at begining */
-    if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) 
+    if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0)
     {
        this->next = extract_list;
        extract_list = this;
-       amfree(ditem_path);
        return 0;
     }
     for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
@@ -611,14 +644,12 @@ add_extract_item(
        {
            this->next = this1->next;
            this1->next = this;
-           amfree(ditem_path);
            return 0;
        }
     }
     /* add at end */
     this->next = NULL;
     this1->next = this;
-    amfree(ditem_path);
     return 0;
 }
 
@@ -649,6 +680,7 @@ delete_extract_item(
                /* first on list */
                this->files = that->next;
                 amfree(that->path);
+                amfree(that->tpath);
                amfree(that);
                /* if list empty delete it */
                if (this->files == NULL)
@@ -664,6 +696,7 @@ delete_extract_item(
                {
                    prev->next = that->next;
                     amfree(that->path);
+                    amfree(that->tpath);
                    amfree(that);
                    amfree(ditem_path);
                    return 0;
@@ -680,6 +713,22 @@ delete_extract_item(
     return 1;
 }
 
+static char *
+merge_path(
+    char *path1,
+    char *path2)
+{
+    char *result;
+    int len = strlen(path1);
+    if (path1[len-1] == '/' && path2[0] == '/') {
+       result = stralloc2(path1, path2+1);
+    } else if (path1[len-1] != '/' && path2[0] != '/') {
+       result = vstralloc(path1, "/", path2, NULL);
+    } else {
+       result = stralloc2(path1, path2);
+    }
+    return result;
+}
 
 void
 add_glob(
@@ -688,29 +737,51 @@ add_glob(
     char *regex;
     char *regex_path;
     char *s;
-    char *uqglob = unquote_string(glob);
-    regex = glob_to_regex(uqglob);
-    dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex);
-    if ((s = validate_regexp(regex)) != NULL) {
-       g_printf(_("%s is not a valid shell wildcard pattern: "), glob);
-       puts(s);
-    } else {
-        /*
-         * glob_to_regex() anchors the beginning of the pattern with ^,
-         * but we will be tacking it onto the end of the current directory
-         * in add_file, so strip that off.  Also, it anchors the end with
-         * $, but we need to match an optional trailing /, so tack that on
-         * the end.
-         */
-        regex_path = stralloc(regex + 1);
-        regex_path[strlen(regex_path) - 1] = '\0';
-        strappend(regex_path, "[/]*$");
-        add_file(uqglob, regex_path);
-        amfree(regex_path);
+    char *uqglob;
+    char *dir;
+    char *sdir = NULL;
+    int   result = 1;
+
+    if (disk_path == NULL) {
+       g_printf(_("Must select directory before adding files\n"));
+       return;
     }
-    amfree(regex);
+
+    uqglob = unquote_string(glob);
+    glob = file_of_path(uqglob, &dir);
+    if (dir) {
+       sdir = merge_path(mount_point, disk_path);
+       result = cd_glob(dir, 0);
+       amfree(dir);
+    }
+    if (result) {
+       regex = glob_to_regex(glob);
+       dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex);
+       if ((s = validate_regexp(regex)) != NULL) {
+           g_printf(_("%s is not a valid shell wildcard pattern: "), glob);
+           puts(s);
+       } else {
+            /*
+             * glob_to_regex() anchors the beginning of the pattern with ^,
+             * but we will be tacking it onto the end of the current directory
+             * in add_file, so strip that off.  Also, it anchors the end with
+             * $, but we need to match an optional trailing /, so tack that on
+             * the end.
+             */
+            regex_path = stralloc(regex + 1);
+            regex_path[strlen(regex_path) - 1] = '\0';
+            strappend(regex_path, "[/]*$");
+            add_file(uqglob, regex_path);
+            amfree(regex_path);
+       }
+       if (sdir) {
+           set_directory(sdir, 0);
+       }
+       amfree(regex);
+    }
+    amfree(sdir);
     amfree(uqglob);
+    amfree(glob);
 }
 
 void
@@ -718,15 +789,39 @@ add_regex(
     char *     regex)
 {
     char *s;
-    char *uqregex = unquote_string(regex);
-    if ((s = validate_regexp(uqregex)) != NULL) {
-       g_printf(_("\"%s\" is not a valid regular expression: "), regex);
-       puts(s);
-    } else {
-        add_file(uqregex, regex);
+    char *dir;
+    char *sdir = NULL;
+    char *uqregex;
+    char *newregex;
+    int   result = 1;
+
+    if (disk_path == NULL) {
+       g_printf(_("Must select directory before adding files\n"));
+       return;
+    }
+
+    uqregex = unquote_string(regex);
+    newregex = file_of_path(uqregex, &dir);
+    if (dir) {
+       sdir = merge_path(mount_point, disk_path);
+       result = cd_regex(dir, 0);
+       amfree(dir);
+    }
+
+    if (result) { 
+       if ((s = validate_regexp(newregex)) != NULL) {
+           g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
+           puts(s);
+       } else {
+            add_file(uqregex, newregex);
+       }
+       if (sdir) {
+           set_directory(sdir, 0);
+       }
     }
+    amfree(sdir);
     amfree(uqregex);
+    amfree(newregex);
 }
 
 void
@@ -735,12 +830,12 @@ add_file(
     char *     regex)
 {
     DIR_ITEM *ditem, lditem;
-    char *path_on_disk = NULL;
+    char *tpath_on_disk = NULL;
     char *cmd = NULL;
     char *err = NULL;
     int i;
     ssize_t j;
-    char *dir, *dir_undo, dir_undo_ch = '\0';
+    char *dir_undo, dir_undo_ch = '\0';
     char *ditem_path = NULL;
     char *qditem_path = NULL;
     char *l = NULL;
@@ -774,34 +869,34 @@ add_file(
     if (strcmp(disk_path, "/") == 0) {
         if (*regex == '/') {
            /* No mods needed if already starts with '/' */
-           path_on_disk = stralloc(regex);
+           tpath_on_disk = g_strdup(regex);
        } else {
            /* Prepend '/' */
-           path_on_disk = stralloc2("/", regex);
+           tpath_on_disk = g_strconcat("/", regex, NULL);
        }
     } else {
-       char *clean_disk_path = clean_regex(disk_path);
-       path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
-       amfree(clean_disk_path);
+       char *clean_disk_tpath = clean_regex(disk_tpath, 0);
+       tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
+       amfree(clean_disk_tpath);
     }
 
-    dbprintf(_("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"),
-             regex, path_on_disk);
+    dbprintf(_("add_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
+             regex, tpath_on_disk);
 
     found_one = 0;
     dir_entries = 0;
     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
     {
        dir_entries++;
-       quoted = quote_string(ditem->path);
+       quoted = quote_string(ditem->tpath);
        dbprintf(_("add_file: Pondering ditem->path=%s\n"), quoted);
        amfree(quoted);
-       if (match(path_on_disk, ditem->path))
+       if (match(tpath_on_disk, ditem->tpath))
        {
            found_one = 1;
-           j = (ssize_t)strlen(ditem->path);
-           if((j > 0 && ditem->path[j-1] == '/')
-              || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
+           j = (ssize_t)strlen(ditem->tpath);
+           if((j > 0 && ditem->tpath[j-1] == '/')
+              || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
            {   /* It is a directory */
                ditem_path = newstralloc(ditem_path, ditem->path);
                clean_pathname(ditem_path);
@@ -812,7 +907,7 @@ add_file(
                if(send_command(cmd) == -1) {
                    amfree(cmd);
                    amfree(ditem_path);
-                   amfree(path_on_disk);
+                   amfree(tpath_on_disk);
                    exit(1);
                }
                amfree(cmd);
@@ -820,25 +915,28 @@ add_file(
                /* skip preamble */
                if ((i = get_reply_line()) == -1) {
                    amfree(ditem_path);
-                   amfree(path_on_disk);
+                   amfree(tpath_on_disk);
                    exit(1);
                }
                if(i==0) {              /* assume something wrong */
                    amfree(ditem_path);
-                   amfree(path_on_disk);
+                   amfree(tpath_on_disk);
                    l = reply_line();
                    g_printf("%s\n", l);
                    return;
                }
                dir_undo = NULL;
                added=0;
-                lditem.path = newstralloc(lditem.path, ditem->path);
+               g_free(lditem.path);
+               g_free(lditem.tpath);
+                lditem.path = g_strdup(ditem->path);
+                lditem.tpath = g_strdup(ditem->tpath);
                /* skip the last line -- duplicate of the preamble */
 
                while ((i = get_reply_line()) != 0) {
                    if (i == -1) {
                        amfree(ditem_path);
-                       amfree(path_on_disk);
+                       amfree(tpath_on_disk);
                        exit(1);
                    }
                    if(err) {
@@ -886,9 +984,10 @@ add_file(
                        continue;
                    }
                     fp = s-1;
-                    skip_non_whitespace(s, ch);
+                    skip_quoted_string(s, ch);
                     s[-1] = '\0';
-                    lditem.tape = newstralloc(lditem.tape, fp);
+                   amfree(lditem.tape);
+                   lditem.tape = unquote_string(fp);
                     s[-1] = (char)ch;
 
                    if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
@@ -908,7 +1007,6 @@ add_file(
                        err = _("bad reply: missing directory field");
                        continue;
                    }
-                   dir = s - 1;
                    skip_quoted_string(s, ch);
                    dir_undo = s - 1;
                    dir_undo_ch = *dir_undo;
@@ -921,7 +1019,7 @@ add_file(
                        break;
 
                    case  0:
-                       quoted = quote_string(lditem.path);
+                       quoted = quote_string(lditem.tpath);
                        g_printf(_("Added dir %s at date %s\n"),
                               quoted, lditem.date);
                        dbprintf(_("add_file: (Successful) Added dir %s at date %s\n"),
@@ -957,14 +1055,14 @@ add_file(
                    break;
 
                case  0:
-                   quoted = quote_string(ditem->path);
+                   quoted = quote_string(ditem->tpath);
                    g_printf(_("Added file %s\n"), quoted);
                    dbprintf(_("add_file: (Successful) Added %s\n"), quoted);
                    amfree(quoted);
                    break;
 
                case  1:
-                   quoted = quote_string(ditem->path);
+                   quoted = quote_string(ditem->tpath);
                    g_printf(_("File %s already added\n"), quoted);
                    dbprintf(_("add_file: file %s already added\n"), quoted);
                    amfree(quoted);
@@ -975,9 +1073,10 @@ add_file(
 
     amfree(cmd);
     amfree(ditem_path);
-    amfree(path_on_disk);
+    amfree(tpath_on_disk);
 
     amfree(lditem.path);
+    amfree(lditem.tpath);
     amfree(lditem.date);
     amfree(lditem.tape);
 
@@ -998,29 +1097,53 @@ delete_glob(
     char *regex;
     char *regex_path;
     char *s;
-    char *uqglob = unquote_string(glob);
-    regex = glob_to_regex(uqglob);
-    dbprintf(_("delete_glob (%s) -> %s\n"), uqglob, regex);
-    if ((s = validate_regexp(regex)) != NULL) {
-       g_printf(_("\"%s\" is not a valid shell wildcard pattern: "), glob);
-       puts(s);
-    } else {
-        /*
-         * glob_to_regex() anchors the beginning of the pattern with ^,
-         * but we will be tacking it onto the end of the current directory
-         * in add_file, so strip that off.  Also, it anchors the end with
-         * $, but we need to match an optional trailing /, so tack that on
-         * the end.
-         */
-        regex_path = stralloc(regex + 1);
-        regex_path[strlen(regex_path) - 1] = '\0';
-        strappend(regex_path, "[/]*$");
-        delete_file(uqglob, regex_path);
-        amfree(regex_path);
+    char *uqglob;
+    char *newglob;
+    char *dir;
+    char *sdir = NULL;
+    int   result = 1;
+
+    if (disk_path == NULL) {
+       g_printf(_("Must select directory before adding files\n"));
+       return;
+    }
+
+    uqglob = unquote_string(glob);
+    newglob = file_of_path(uqglob, &dir);
+    if (dir) {
+       sdir = merge_path(mount_point, disk_path);
+       result = cd_glob(dir, 0);
+       amfree(dir);
+    }
+    if (result) {
+       regex = glob_to_regex(newglob);
+       dbprintf(_("delete_glob (%s) -> %s\n"), newglob, regex);
+       if ((s = validate_regexp(regex)) != NULL) {
+           g_printf(_("\"%s\" is not a valid shell wildcard pattern: "),
+                    newglob);
+           puts(s);
+} else {
+            /*
+             * glob_to_regex() anchors the beginning of the pattern with ^,
+             * but we will be tacking it onto the end of the current directory
+             * in add_file, so strip that off.  Also, it anchors the end with
+             * $, but we need to match an optional trailing /, so tack that on
+             * the end.
+             */
+            regex_path = stralloc(regex + 1);
+            regex_path[strlen(regex_path) - 1] = '\0';
+            strappend(regex_path, "[/]*$");
+            delete_file(uqglob, regex_path);
+            amfree(regex_path);
+       }
+       if (sdir) {
+           set_directory(sdir, 0);
+       }
+       amfree(regex);
     }
-    amfree(regex);
+    amfree(sdir);
     amfree(uqglob);
+    amfree(newglob);
 }
 
 void
@@ -1028,24 +1151,48 @@ delete_regex(
     char *     regex)
 {
     char *s;
-    char *uqregex = unquote_string(regex);
+    char *dir;
+    char *sdir = NULL;
+    char *uqregex;
+    char *newregex;
+    int   result = 1;
 
-    if ((s = validate_regexp(regex)) != NULL) {
-       g_printf(_("\"%s\" is not a valid regular expression: "), regex);
-       puts(s);
-    } else {
-       delete_file(uqregex, uqregex);
+    if (disk_path == NULL) {
+       g_printf(_("Must select directory before adding files\n"));
+       return;
     }
+
+    uqregex = unquote_string(regex);
+    newregex = file_of_path(uqregex, &dir);
+    if (dir) {
+       sdir = merge_path(mount_point, disk_path);
+       result = cd_regex(dir, 0);
+       amfree(dir);
+    }
+
+    if (result == 1) {
+       if ((s = validate_regexp(newregex)) != NULL) {
+           g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
+           puts(s);
+       } else {
+           delete_file(newregex, regex);
+       }
+       if (sdir) {
+           set_directory(sdir, 0);
+       }
+    }
+    amfree(sdir);
     amfree(uqregex);
+    amfree(newregex);
 }
 
 void
 delete_file(
-    char *     path,
+    char *     tpath,
     char *     regex)
 {
     DIR_ITEM *ditem, lditem;
-    char *path_on_disk = NULL;
+    char *tpath_on_disk = NULL;
     char *cmd = NULL;
     char *err = NULL;
     int i;
@@ -1054,8 +1201,8 @@ delete_file(
     char *tape, *tape_undo, tape_undo_ch = '\0';
     char *dir_undo, dir_undo_ch = '\0';
     int  level = 0;
-    off_t fileno;
     char *ditem_path = NULL;
+    char *ditem_tpath = NULL;
     char *qditem_path;
     char *l = NULL;
     int  deleted;
@@ -1070,7 +1217,7 @@ delete_file(
     }
     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
 
-    dbprintf(_("delete_file: Looking for \"%s\"\n"), path);
+    dbprintf(_("delete_file: Looking for \"%s\"\n"), tpath);
 
     if (strcmp(regex, "[^/]*[/]*$") == 0) {
        /* Looking for * find everything but single . */
@@ -1086,38 +1233,40 @@ delete_file(
         if (*regex == '/') {
            if (strcmp(regex, "/[/]*$") == 0) {
                /* We want "/" to match the directory itself: "/." */
-               path_on_disk = stralloc("/\\.[/]*$");
+               tpath_on_disk = stralloc("/\\.[/]*$");
            } else {
                /* No mods needed if already starts with '/' */
-               path_on_disk = stralloc(regex);
+               tpath_on_disk = stralloc(regex);
            }
        } else {
            /* Prepend '/' */
-           path_on_disk = stralloc2("/", regex);
+           tpath_on_disk = g_strconcat("/", regex, NULL);
        }
     } else {
-       char *clean_disk_path = clean_regex(disk_path);
-       path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
-       amfree(clean_disk_path);
+       char *clean_disk_tpath = clean_regex(disk_tpath, 0);
+       tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
+       amfree(clean_disk_tpath);
     }
 
-    dbprintf(_("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"),
-             regex, path_on_disk);
+    dbprintf(_("delete_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
+             regex, tpath_on_disk);
     found_one = 0;
     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
     {
-       quoted = quote_string(ditem->path);
+       quoted = quote_string(ditem->tpath);
        dbprintf(_("delete_file: Pondering ditem->path=%s\n"), quoted);
        amfree(quoted);
-       if (match(path_on_disk, ditem->path))
+       if (match(tpath_on_disk, ditem->tpath))
        {
            found_one = 1;
-           j = (ssize_t)strlen(ditem->path);
-           if((j > 0 && ditem->path[j-1] == '/')
-              || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
+           j = (ssize_t)strlen(ditem->tpath);
+           if((j > 0 && ditem->tpath[j-1] == '/')
+              || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
            {   /* It is a directory */
                ditem_path = newstralloc(ditem_path, ditem->path);
+               ditem_tpath = newstralloc(ditem_tpath, ditem->tpath);
                clean_pathname(ditem_path);
+               clean_pathname(ditem_tpath);
 
                qditem_path = quote_string(ditem_path);
                cmd = newstralloc2(cmd, "ORLD ", qditem_path);
@@ -1125,26 +1274,27 @@ delete_file(
                if(send_command(cmd) == -1) {
                    amfree(cmd);
                    amfree(ditem_path);
-                   amfree(path_on_disk);
+                   amfree(tpath_on_disk);
                    exit(1);
                }
                amfree(cmd);
                /* skip preamble */
                if ((i = get_reply_line()) == -1) {
                    amfree(ditem_path);
-                   amfree(path_on_disk);
+                   amfree(tpath_on_disk);
                    exit(1);
                }
                if(i==0)                /* assume something wrong */
                {
                    amfree(ditem_path);
-                   amfree(path_on_disk);
+                   amfree(tpath_on_disk);
                    l = reply_line();
                    g_printf("%s\n", l);
                    return;
                }
                deleted=0;
                 lditem.path = newstralloc(lditem.path, ditem->path);
+                lditem.tpath = newstralloc(lditem.tpath, ditem->tpath);
                amfree(cmd);
                tape_undo = dir_undo = NULL;
                /* skip the last line -- duplicate of the preamble */
@@ -1152,7 +1302,7 @@ delete_file(
                {
                    if (i == -1) {
                        amfree(ditem_path);
-                       amfree(path_on_disk);
+                       amfree(tpath_on_disk);
                        exit(1);
                    }
                    if(err) {
@@ -1212,7 +1362,6 @@ delete_file(
                            err = _("bad reply: cannot parse fileno field");
                            continue;
                        }
-                       fileno = (off_t)fileno_;
                        skip_integer(s, ch);
                    }
 
@@ -1228,16 +1377,17 @@ delete_file(
 
                     lditem.date = newstralloc(lditem.date, date);
                    lditem.level=level;
-                    lditem.tape = newstralloc(lditem.tape, tape);
+                   g_free(lditem.tape);
+                   lditem.tape = unquote_string(tape);
                    switch(delete_extract_item(&lditem)) {
                    case -1:
                        g_printf(_("System error\n"));
                        dbprintf(_("delete_file: (Failed) System error\n"));
                        break;
                    case  0:
-                       g_printf(_("Deleted dir %s at date %s\n"), ditem_path, date);
+                       g_printf(_("Deleted dir %s at date %s\n"), ditem_tpath, date);
                        dbprintf(_("delete_file: (Successful) Deleted dir %s at date %s\n"),
-                                 ditem_path, date);
+                                 ditem_tpath, date);
                        deleted=1;
                        break;
                    case  1:
@@ -1253,9 +1403,9 @@ delete_file(
                        puts(cmd);
                } else if(deleted == 0) {
                    g_printf(_("Warning - dir '%s' not on tape list\n"),
-                          ditem_path);
+                          ditem_tpath);
                    dbprintf(_("delete_file: dir '%s' not on tape list\n"),
-                             ditem_path);
+                             ditem_tpath);
                }
            }
            else
@@ -1266,15 +1416,15 @@ delete_file(
                    dbprintf(_("delete_file: (Failed) System error\n"));
                    break;
                case  0:
-                   g_printf(_("Deleted %s\n"), ditem->path);
+                   g_printf(_("Deleted %s\n"), ditem->tpath);
                    dbprintf(_("delete_file: (Successful) Deleted %s\n"),
-                             ditem->path);
+                             ditem->tpath);
                    break;
                case  1:
                    g_printf(_("Warning - file '%s' not on tape list\n"),
-                          ditem->path);
+                          ditem->tpath);
                    dbprintf(_("delete_file: file '%s' not on tape list\n"),
-                             ditem->path);
+                             ditem->tpath);
                    break;
                }
            }
@@ -1282,12 +1432,13 @@ delete_file(
     }
     amfree(cmd);
     amfree(ditem_path);
-    amfree(path_on_disk);
+    amfree(ditem_tpath);
+    amfree(tpath_on_disk);
 
     if(! found_one) {
-       g_printf(_("File %s doesn't exist in directory\n"), path);
+       g_printf(_("File %s doesn't exist in directory\n"), tpath);
        dbprintf(_("delete_file: (Failed) File %s doesn't exist in directory\n"),
-                 path);
+                 tpath);
     }
 }
 
@@ -1339,7 +1490,7 @@ display_extract_list(
        g_fprintf(fp, _("TAPE %s LEVEL %d DATE %s\n"),
                this->tape, this->level, this->date);
        for (that = this->files; that != NULL; that = that->next)
-           g_fprintf(fp, "\t%s\n", that->path);
+           g_fprintf(fp, "\t%s\n", that->tpath);
     }
 
     if (file == NULL) {
@@ -1424,7 +1575,7 @@ okay_to_continue(
        dbprintf("User prompt: '%s'; response: '%s'\n", prompt, line);
 
        s = line;
-       while ((ch = *s++) != '\0' && isspace(ch)) {
+       while ((ch = *s++) != '\0' && g_ascii_isspace(ch)) {
            (void)ch;   /* Quiet empty loop compiler warning */
        }
        if (ch == '?') {
@@ -1482,6 +1633,7 @@ send_to_tape_server(
 {
     char *msg = stralloc2(cmd, "\r\n");
 
+    g_debug("send_to_tape_server: %s\n", cmd);
     if (security_stream_write(stream, msg, strlen(msg)) < 0)
     {
        error(_("Error writing to tape server"));
@@ -1529,53 +1681,8 @@ extract_files_setup(
        return -1;
     }
 
-    disk_regex = alloc(strlen(disk_name) * 2 + 3);
-
-    ch = disk_name;
-    ch1 = disk_regex;
-
-    /* we want to force amrestore to only match disk_name exactly */
-    *(ch1++) = '^';
-
-    /* We need to escape some characters first... NT compatibilty crap */
-    for (; *ch != 0; ch++, ch1++) {
-       switch (*ch) {     /* done this way in case there are more */
-       case '$':
-           *(ch1++) = '\\';
-           /* no break; we do want to fall through... */
-       default:
-           *ch1 = *ch;
-       }
-    }
-
-    /* we want to force amrestore to only match disk_name exactly */
-    *(ch1++) = '$';
-
-    *ch1 = '\0';
-
-    host_regex = alloc(strlen(dump_hostname) * 2 + 3);
-
-    ch = dump_hostname;
-    ch1 = host_regex;
-
-    /* we want to force amrestore to only match dump_hostname exactly */
-    *(ch1++) = '^';
-
-    /* We need to escape some characters first... NT compatibilty crap */
-    for (; *ch != 0; ch++, ch1++) {
-       switch (*ch) {     /* done this way in case there are more */
-       case '$':
-           *(ch1++) = '\\';
-           /* no break; we do want to fall through... */
-       default:
-           *ch1 = *ch;
-       }
-    }
-
-    /* we want to force amrestore to only match dump_hostname exactly */
-    *(ch1++) = '$';
-
-    *ch1 = '\0';
+    disk_regex = make_exact_disk_expression(disk_name);
+    host_regex = make_exact_host_expression(dump_hostname);
 
     clean_datestamp = stralloc(dump_datestamp);
     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
@@ -1592,7 +1699,14 @@ extract_files_setup(
        tt = newstralloc2(tt, "FEATURES=", our_features_string);
        send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
        get_amidxtaped_line();
-       if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) {
+       if (!amidxtaped_line) {
+           g_fprintf(stderr, _("amrecover - amidxtaped closed the connection\n"));
+           stop_amidxtaped();
+           amfree(disk_regex);
+           amfree(host_regex);
+           amfree(clean_datestamp);
+           return -1;
+       } else if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) {
            tapesrv_features = am_string_to_feature(amidxtaped_line+9);
        } else {
            g_fprintf(stderr, _("amrecover - expecting FEATURES line from amidxtaped\n"));
@@ -1602,7 +1716,8 @@ extract_files_setup(
            amfree(clean_datestamp);
            return -1;
        }
-       am_release_feature_set(tapesrv_features);
+    } else {
+       *tapesrv_features = *indexsrv_features;
     }
 
 
@@ -1613,7 +1728,7 @@ extract_files_setup(
        am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
 
        if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
-           tt = newstralloc2(tt, "CONFIG=", config_name);
+           tt = newstralloc2(tt, "CONFIG=", get_config_name());
            send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
        }
        if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
@@ -1708,26 +1823,23 @@ enum dumptypes {
        IS_TAR,
        IS_SAMBA,
        IS_SAMBA_TAR,
-       IS_BACKUP_API
+       IS_APPLICATION_API
 };
 
 static void
 extract_files_child(
-    int                        in_fd,
-    EXTRACT_LIST *     elist)
+    ctl_data_t         *ctl_data)
 {
     int save_errno;
-    int extra_params = 0;
-    int i,j=0;
-    char **restore_args = NULL;
+    int   i;
+    guint j;
+    GPtrArray *argv_ptr = g_ptr_array_new();
     int files_off_tape;
     EXTRACT_LIST_ITEM *fn;
     enum dumptypes dumptype = IS_UNKNOWN;
-    char buffer[DISK_BLOCK_BYTES];
-    dumpfile_t file;
     size_t len_program;
     char *cmd = NULL;
-    int passwd_field = -1;
+    guint passwd_field = 999999999;
 #ifdef SAMBA_CLIENT
     char *domain = NULL, *smbpass = NULL;
 #endif
@@ -1736,39 +1848,35 @@ extract_files_child(
     /* never returns */
 
     /* make in_fd be our stdin */
-    if (dup2(in_fd, STDIN_FILENO) == -1)
+    if (dup2(ctl_data->child_pipe[0], STDIN_FILENO) == -1)
     {
        error(_("dup2 failed in extract_files_child: %s"), strerror(errno));
        /*NOTREACHED*/
     }
 
-    /* read the file header */
-    fh_init(&file);
-    read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
-
-    if(file.type != F_DUMPFILE) {
-       print_header(stdout, &file);
+    if(ctl_data->file.type != F_DUMPFILE) {
+       dump_dumpfile_t(&ctl_data->file);
        error(_("bad header"));
        /*NOTREACHED*/
     }
 
-    if (file.program != NULL) {
-       if (strcmp(file.program, "BACKUP") == 0)
-           dumptype = IS_BACKUP_API;
+    if (ctl_data->file.program != NULL) {
+       if (strcmp(ctl_data->file.program, "APPLICATION") == 0)
+           dumptype = IS_APPLICATION_API;
 #ifdef GNUTAR
-       if (strcmp(file.program, GNUTAR) == 0)
+       if (strcmp(ctl_data->file.program, GNUTAR) == 0)
            dumptype = IS_GNUTAR;
 #endif
 
        if (dumptype == IS_UNKNOWN) {
-           len_program = strlen(file.program);
+           len_program = strlen(ctl_data->file.program);
            if(len_program >= 3 &&
-              strcmp(&file.program[len_program-3],"tar") == 0)
+              strcmp(&ctl_data->file.program[len_program-3],"tar") == 0)
                dumptype = IS_TAR;
        }
 
 #ifdef SAMBA_CLIENT
-       if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
+       if (dumptype == IS_UNKNOWN && strcmp(ctl_data->file.program, SAMBA_CLIENT) ==0) {
            if (samba_extract_method == SAMBA_TAR)
              dumptype = IS_SAMBA_TAR;
            else
@@ -1778,148 +1886,151 @@ extract_files_child(
     }
 
     /* form the arguments to restore */
-    files_off_tape = length_of_tape_list(elist);
-    switch (dumptype) {
+    files_off_tape = length_of_tape_list(ctl_data->elist);
+    switch(dumptype) {
     case IS_SAMBA:
 #ifdef SAMBA_CLIENT
-       extra_params = 10;
-       break;
-#endif
-    case IS_TAR:
-    case IS_GNUTAR:
-        extra_params = 4;
-        break;
-    case IS_SAMBA_TAR:
-        extra_params = 3;
-        break;
-    case IS_UNKNOWN:
-    case IS_DUMP:
-#ifdef AIX_BACKUP
-        extra_params = 2;
-#else
-#if defined(XFSDUMP)
-       if (strcmp(file.program, XFSDUMP) == 0) {
-            extra_params = 4 + files_off_tape;
-       } else
-#endif
-       {
-        extra_params = 4;
+       g_ptr_array_add(argv_ptr, stralloc("smbclient"));
+       smbpass = findpass(ctl_data->file.disk, &domain);
+       if (smbpass) {
+           g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
+           g_ptr_array_add(argv_ptr, stralloc("-U"));
+           passwd_field = argv_ptr->len;
+           g_ptr_array_add(argv_ptr, stralloc(smbpass));
+           if (domain) {
+               g_ptr_array_add(argv_ptr, stralloc("-W"));
+               g_ptr_array_add(argv_ptr, stralloc(domain));
+           }
        }
-#endif
-       break;
-    case IS_BACKUP_API:
-       extra_params = 5;
+       g_ptr_array_add(argv_ptr, stralloc("-d0"));
+       g_ptr_array_add(argv_ptr, stralloc("-Tx"));
+       g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
        break;
-    }
-    restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
-                                 * sizeof(char *)));
-    switch(dumptype) {
-    case IS_SAMBA:
-#ifdef SAMBA_CLIENT
-       restore_args[j++] = stralloc("smbclient");
-       smbpass = findpass(file.disk, &domain);
-       if (smbpass) {
-            restore_args[j++] = stralloc(file.disk);
-           passwd_field=j;
-           restore_args[j++] = stralloc("-U");
-           restore_args[j++] = smbpass;
-           if (domain) {
-               restore_args[j++] = stralloc("-W");
-               restore_args[j++] = stralloc(domain);
-           } else
-               extra_params -= 2;
-       } else
-           extra_params -= 6;
-       restore_args[j++] = stralloc("-d0");
-       restore_args[j++] = stralloc("-Tx");
-       restore_args[j++] = stralloc("-");      /* data on stdin */
-       break;
 #endif
     case IS_TAR:
     case IS_GNUTAR:
-       restore_args[j++] = stralloc("tar");
-       restore_args[j++] = stralloc("--numeric-owner");
-       restore_args[j++] = stralloc("-xpGvf");
-       restore_args[j++] = stralloc("-");      /* data on stdin */
+       g_ptr_array_add(argv_ptr, stralloc("tar"));
+       /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
+       g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
+       g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
+       g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
+       g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
        break;
     case IS_SAMBA_TAR:
-       restore_args[j++] = stralloc("tar");
-       restore_args[j++] = stralloc("-xpvf");
-       restore_args[j++] = stralloc("-");      /* data on stdin */
+       g_ptr_array_add(argv_ptr, stralloc("tar"));
+       g_ptr_array_add(argv_ptr, stralloc("-xpvf"));
+       g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
        break;
     case IS_UNKNOWN:
     case IS_DUMP:
-        restore_args[j++] = stralloc("restore");
+       g_ptr_array_add(argv_ptr, stralloc("restore"));
 #ifdef AIX_BACKUP
-        restore_args[j++] = stralloc("-xB");
+       g_ptr_array_add(argv_ptr, stralloc("-xB"));
 #else
 #if defined(XFSDUMP)
-       if (strcmp(file.program, XFSDUMP) == 0) {
-            restore_args[j++] = stralloc("-v");
-            restore_args[j++] = stralloc("silent");
+       if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
+           g_ptr_array_add(argv_ptr, stralloc("-v"));
+           g_ptr_array_add(argv_ptr, stralloc("silent"));
        } else
 #endif
 #if defined(VDUMP)
-       if (strcmp(file.program, VDUMP) == 0) {
-            restore_args[j++] = stralloc("xf");
-            restore_args[j++] = stralloc("-"); /* data on stdin */
+       if (strcmp(ctl_data->file.program, VDUMP) == 0) {
+           g_ptr_array_add(argv_ptr, stralloc("xf"));
+           g_ptr_array_add(argv_ptr, stralloc("-"));   /* data on stdin */
        } else
 #endif
        {
-        restore_args[j++] = stralloc("xbf");
-        restore_args[j++] = stralloc("2");     /* read in units of 1K */
-        restore_args[j++] = stralloc("-");     /* data on stdin */
+       g_ptr_array_add(argv_ptr, stralloc("xbf"));
+       g_ptr_array_add(argv_ptr, stralloc("2")); /* read in units of 1K */
+       g_ptr_array_add(argv_ptr, stralloc("-"));       /* data on stdin */
        }
 #endif
        break;
-    case IS_BACKUP_API:
-       restore_args[j++] = stralloc(file.dumper);
-       restore_args[j++] = stralloc("restore");
-       restore_args[j++] = stralloc("--config");
-       restore_args[j++] = stralloc(config_name);
-       restore_args[j++] = stralloc("--disk");
-       restore_args[j++] = stralloc(file.disk);
+    case IS_APPLICATION_API:
+       g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.application));
+       g_ptr_array_add(argv_ptr, stralloc("restore"));
+       g_ptr_array_add(argv_ptr, stralloc("--config"));
+       g_ptr_array_add(argv_ptr, stralloc(get_config_name()));
+       g_ptr_array_add(argv_ptr, stralloc("--disk"));
+       g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
+       if (dump_dle && dump_dle->device) {
+           g_ptr_array_add(argv_ptr, stralloc("--device"));
+           g_ptr_array_add(argv_ptr, stralloc(dump_dle->device));
+       }
+       if (ctl_data->data_path == DATA_PATH_DIRECTTCP) {
+           g_ptr_array_add(argv_ptr, stralloc("--data-path"));
+           g_ptr_array_add(argv_ptr, stralloc("DIRECTTCP"));
+           g_ptr_array_add(argv_ptr, stralloc("--direct-tcp"));
+           g_ptr_array_add(argv_ptr, stralloc(ctl_data->addrs));
+       }
+       if (ctl_data->bsu && ctl_data->bsu->smb_recover_mode &&
+           samba_extract_method == SAMBA_SMBCLIENT){
+           g_ptr_array_add(argv_ptr, stralloc("--recover-mode"));
+           g_ptr_array_add(argv_ptr, stralloc("smb"));
+       }
+       g_ptr_array_add(argv_ptr, stralloc("--level"));
+       g_ptr_array_add(argv_ptr, g_strdup_printf("%d", ctl_data->elist->level));
+       if (dump_dle) {
+           GSList   *scriptlist;
+           script_t *script;
+
+           merge_properties(dump_dle, NULL, dump_dle->application_property,
+                            proplist, 0);
+           application_property_add_to_argv(argv_ptr, dump_dle, NULL,
+                                            tapesrv_features);
+           for (scriptlist = dump_dle->scriptlist; scriptlist != NULL;
+                scriptlist = scriptlist->next) {
+               script = (script_t *)scriptlist->data;
+               if (script->result && script->result->proplist) {
+                   property_add_to_argv(argv_ptr, script->result->proplist);
+               }
+           }
+
+       } else if (proplist) {
+           property_add_to_argv(argv_ptr, proplist);
+       }
        break;
     }
-  
-    for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
+
+    for (i = 0, fn = ctl_data->elist->files; i < files_off_tape;
+                                            i++, fn = fn->next)
     {
        switch (dumptype) {
-       case IS_BACKUP_API:
+       case IS_APPLICATION_API:
        case IS_TAR:
        case IS_GNUTAR:
        case IS_SAMBA_TAR:
        case IS_SAMBA:
            if (strcmp(fn->path, "/") == 0)
-               restore_args[j++] = stralloc(".");
+               g_ptr_array_add(argv_ptr, stralloc("."));
            else
-               restore_args[j++] = stralloc2(".", fn->path);
+               g_ptr_array_add(argv_ptr, stralloc2(".", fn->path));
            break;
        case IS_UNKNOWN:
        case IS_DUMP:
 #if defined(XFSDUMP)
-           if (strcmp(file.program, XFSDUMP) == 0) {
+           if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
                /*
                 * xfsrestore needs a -s option before each file to be
                 * restored, and also wants them to be relative paths.
                 */
-               restore_args[j++] = stralloc("-s");
-               restore_args[j++] = stralloc(fn->path + 1);
+               g_ptr_array_add(argv_ptr, stralloc("-s"));
+               g_ptr_array_add(argv_ptr, stralloc(fn->path + 1));
            } else
 #endif
            {
-           restore_args[j++] = stralloc(fn->path);
+           g_ptr_array_add(argv_ptr, stralloc(fn->path));
            }
            break;
        }
     }
 #if defined(XFSDUMP)
-    if (strcmp(file.program, XFSDUMP) == 0) {
-       restore_args[j++] = stralloc("-");
-       restore_args[j++] = stralloc(".");
+    if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
+       g_ptr_array_add(argv_ptr, stralloc("-"));
+       g_ptr_array_add(argv_ptr, stralloc("."));
     }
 #endif
-    restore_args[j] = NULL;
+    g_ptr_array_add(argv_ptr, NULL);
 
     switch (dumptype) {
     case IS_SAMBA:
@@ -1943,51 +2054,48 @@ extract_files_child(
     case IS_DUMP:
        cmd = NULL;
 #if defined(DUMP)
-       if (strcmp(file.program, DUMP) == 0) {
+       if (strcmp(ctl_data->file.program, DUMP) == 0) {
            cmd = stralloc(RESTORE);
        }
 #endif
 #if defined(VDUMP)
-       if (strcmp(file.program, VDUMP) == 0) {
+       if (strcmp(ctl_data->file.program, VDUMP) == 0) {
            cmd = stralloc(VRESTORE);
        }
 #endif
 #if defined(VXDUMP)
-       if (strcmp(file.program, VXDUMP) == 0) {
+       if (strcmp(ctl_data->file.program, VXDUMP) == 0) {
            cmd = stralloc(VXRESTORE);
        }
 #endif
 #if defined(XFSDUMP)
-       if (strcmp(file.program, XFSDUMP) == 0) {
+       if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
            cmd = stralloc(XFSRESTORE);
        }
 #endif
        if (cmd == NULL) {
            g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
-                   file.program);
+                   ctl_data->file.program);
            cmd = stralloc("restore");
        }
        break;
-    case IS_BACKUP_API:
-       cmd = vstralloc(DUMPER_DIR, "/", file.dumper, NULL);
+    case IS_APPLICATION_API:
+       cmd = vstralloc(APPLICATION_DIR, "/", ctl_data->file.application, NULL);
        break;
     }
     if (cmd) {
         dbprintf(_("Exec'ing %s with arguments:\n"), cmd);
-       for (i = 0; i < j; i++) {
-           if( i == passwd_field)
+       for (j = 0; j < argv_ptr->len - 1; j++) {
+           if (j == passwd_field)
                dbprintf("\tXXXXX\n");
            else
-               dbprintf(_("\t%s\n"), restore_args[i]);
+               dbprintf(_("\t%s\n"), (char *)g_ptr_array_index(argv_ptr, j));
        }
        safe_fd(-1, 0);
-        (void)execv(cmd, restore_args);
+        (void)execv(cmd, (char **)argv_ptr->pdata);
        /* only get here if exec failed */
        save_errno = errno;
-       for (i = 0; i < j; i++) {
-           amfree(restore_args[i]);
-       }
-       amfree(restore_args);
+       g_ptr_array_free_full(argv_ptr);
        errno = save_errno;
         perror(_("amrecover couldn't exec"));
         g_fprintf(stderr, _(" problem executing %s\n"), cmd);
@@ -2006,39 +2114,28 @@ int
 writer_intermediary(
     EXTRACT_LIST *     elist)
 {
-    int child_pipe[2];
-    pid_t pid;
-    amwait_t extractor_status;
-
-    if(pipe(child_pipe) == -1) {
-       error(_("extract_list - error setting up pipe to extractor: %s\n"),
-             strerror(errno));
-       /*NOTREACHED*/
-    }
-
-    /* okay, ready to extract. fork a child to do the actual work */
-    if ((pid = fork()) == 0) {
-       /* this is the child process */
-       /* never gets out of this clause */
-       aclose(child_pipe[1]);
-        extract_files_child(child_pipe[0], elist);
-       /*NOTREACHED*/
-    }
-
-    /* This is the parent */
-    if (pid == -1) {
-       g_printf(_("writer_intermediary - error forking child"));
-       return -1;
-    }
-
-    aclose(child_pipe[0]);
-
+    ctl_data_t ctl_data;
+    amwait_t   extractor_status;
+
+    ctl_data.header_done   = 0;
+    ctl_data.child_pipe[0] = -1;
+    ctl_data.child_pipe[1] = -1;
+    ctl_data.pid           = -1;
+    ctl_data.elist         = elist;
+    fh_init(&ctl_data.file);
+    ctl_data.data_path     = DATA_PATH_AMANDA;
+    ctl_data.addrs         = NULL;
+    ctl_data.bsu           = NULL;
+    ctl_data.bytes_read    = 0;
+
+    header_size = 0;
     security_stream_read(amidxtaped_streams[DATAFD].fd,
-                        read_amidxtaped_data, &(child_pipe[1]));
+                        read_amidxtaped_data, &ctl_data);
 
     while(get_amidxtaped_line() >= 0) {
        char desired_tape[MAX_TAPE_LABEL_BUF];
-                
+       g_debug("get amidxtaped line: %s", amidxtaped_line);
+
        /* if prompted for a tape, relay said prompt to the user */
        if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
            int done;
@@ -2060,6 +2157,16 @@ writer_intermediary(
                send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
                break;
            }
+       } else if (strncmp_const(amidxtaped_line, "USE-DATAPATH ") == 0) {
+           if (strncmp_const(amidxtaped_line+13, "AMANDA") == 0) {
+               ctl_data.data_path = DATA_PATH_AMANDA;
+               g_debug("Using AMANDA data-path");
+           } else if (strncmp_const(amidxtaped_line+13, "DIRECT-TCP") == 0) {
+               ctl_data.data_path = DATA_PATH_DIRECTTCP;
+               ctl_data.addrs = stralloc(amidxtaped_line+24);
+               g_debug("Using DIRECT-TCP data-path with %s", ctl_data.addrs);
+           }
+           start_processing_data(&ctl_data);
        } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) {
            g_printf("%s\n",&amidxtaped_line[8]);
        } else {
@@ -2071,15 +2178,26 @@ writer_intermediary(
 
     /* CTL might be close before DATA */
     event_loop(0);
-    aclose(child_pipe[1]);
-
-    waitpid(pid, &extractor_status, 0);
-    if(WEXITSTATUS(extractor_status) != 0){
-       int ret = WEXITSTATUS(extractor_status);
-        if(ret == 255) ret = -1;
-       g_printf(_("Extractor child exited with status %d\n"), ret);
-       return -1;
+    dumpfile_free_data(&ctl_data.file);
+    amfree(ctl_data.addrs);
+    amfree(ctl_data.bsu);
+    if (ctl_data.child_pipe[1] != -1)
+       aclose(ctl_data.child_pipe[1]);
+
+    if (ctl_data.header_done == 0) {
+       g_printf(_("Got no header and data from server, check in amidxtaped.*.debug and amandad.*.debug files on server\n"));
+    }
+
+    if (ctl_data.pid != -1) {
+       waitpid(ctl_data.pid, &extractor_status, 0);
+       if(WEXITSTATUS(extractor_status) != 0){
+           int ret = WEXITSTATUS(extractor_status);
+            if(ret == 255) ret = -1;
+           g_printf(_("Extractor child exited with status %d\n"), ret);
+           return -1;
+       }
     }
+    g_debug("bytes read: %jd", (intmax_t)ctl_data.bytes_read);
     return(0);
 }
 
@@ -2106,11 +2224,13 @@ void
 extract_files(void)
 {
     EXTRACT_LIST *elist;
-    char * cwd;
     char *l;
     int first;
     int otc;
     tapelist_t *tlist = NULL, *a_tlist;
+    g_option_t g_options;
+    levellist_t all_level = NULL;
+    int last_level;
 
     if (!is_extract_list_nonempty())
     {
@@ -2156,7 +2276,7 @@ extract_files(void)
            }
            else
                g_printf("                               ");
-           tlist = unmarshal_tapelist_str(elist->tape); 
+           tlist = unmarshal_tapelist_str(elist->tape);
            for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next)
                g_printf(" %s", a_tlist->label);
            g_printf("\n");
@@ -2174,7 +2294,7 @@ extract_files(void)
            }
            else
                g_printf("                               ");
-           tlist = unmarshal_tapelist_str(elist->tape); 
+           tlist = unmarshal_tapelist_str(elist->tape);
            for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
                g_printf(" %s", a_tlist->label);
            g_printf("\n");
@@ -2183,40 +2303,28 @@ extract_files(void)
     }
     g_printf("\n");
 
-    cwd = g_get_current_dir();
-    if (cwd == NULL) {
-       perror(_("extract_list: Current working directory unavailable"));
-       exit(1);
-    }
-
-    g_printf(_("Restoring files into directory %s\n"), cwd);
-    check_file_overwrite(cwd);
-
-#ifdef SAMBA_CLIENT
-    if (samba_extract_method == SAMBA_SMBCLIENT)
-      g_printf(_("(unless it is a Samba backup, that will go through to the SMB server)\n"));
-#endif
-    dbprintf(_("Checking with user before restoring into directory %s\n"), cwd);
-    if (!okay_to_continue(0,0,0)) {
-        amfree(cwd);
-       return;
-    }
-    g_printf("\n");
-
-    if (!do_unlink_list()) {
-       g_fprintf(stderr, _("Can't recover because I can't cleanup the cwd (%s)\n"),
-               cwd);
-        amfree(cwd);
-       return;
-    }
-    free_unlink_list();
-
+    g_options.config = get_config_name();
+    g_options.hostname = dump_hostname;
+    for (elist = first_tape_list(); elist != NULL;
+        elist = next_tape_list(elist)) {
+       level_t *level = g_new0(level_t, 1);
+       level->level = elist->level;
+       all_level = g_slist_append(all_level, level);
+    }
+    if (dump_dle) {
+       slist_free_full(dump_dle->levellist, g_free);
+       dump_dle->levellist = all_level;
+       run_client_scripts(EXECUTE_ON_PRE_RECOVER, &g_options, dump_dle,
+                          stderr);
+       dump_dle->levellist = NULL;
+    }
+    last_level = -1;
     while ((elist = first_tape_list()) != NULL)
     {
        if(elist->tape[0]=='/') {
            dump_device_name = newstralloc(dump_device_name, elist->tape);
            g_printf(_("Extracting from file "));
-           tlist = unmarshal_tapelist_str(dump_device_name); 
+           tlist = unmarshal_tapelist_str(dump_device_name);
            for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
                g_printf(" %s", a_tlist->label);
            g_printf("\n");
@@ -2225,7 +2333,7 @@ extract_files(void)
        else {
            g_printf(_("Extracting files using tape drive %s on host %s.\n"),
                   tape_device_name, tape_server_name);
-           tlist = unmarshal_tapelist_str(elist->tape); 
+           tlist = unmarshal_tapelist_str(elist->tape);
            g_printf(_("Load tape %s now\n"), tlist->label);
            dbprintf(_("Requesting tape %s from user\n"), tlist->label);
            free_tapelist(tlist);
@@ -2240,6 +2348,22 @@ extract_files(void)
        }
        dump_datestamp = newstralloc(dump_datestamp, elist->date);
 
+       if (last_level != -1 && dump_dle) {
+           level_t *level;
+
+           level = g_new0(level_t, 1);
+           level->level = last_level;
+           dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
+
+           level = g_new0(level_t, 1);
+           level->level = elist->level;
+           dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
+           run_client_scripts(EXECUTE_ON_INTER_LEVEL_RECOVER, &g_options,
+                              dump_dle, stderr);
+           slist_free_full(dump_dle->levellist, g_free);
+           dump_dle->levellist = NULL;
+       }
+
        /* connect to the tape handler daemon on the tape drive server */
        if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
        {
@@ -2247,6 +2371,16 @@ extract_files(void)
                    errstr);
            return;
        }
+       if (dump_dle) {
+           level_t *level;
+
+           level = g_new0(level_t, 1);
+           level->level = elist->level;
+           dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
+           run_client_scripts(EXECUTE_ON_PRE_LEVEL_RECOVER, &g_options,
+                              dump_dle, stderr);
+       }
+       last_level = elist->level;
 
        /* if the server have fe_amrecover_feedme_tape, it has asked for
         * the tape itself, even if the restore didn't succeed, we should
@@ -2256,7 +2390,23 @@ extract_files(void)
           am_has_feature(indexsrv_features, fe_amrecover_feedme_tape))
            delete_tape_list(elist);    /* tape done so delete from list */
 
+       am_release_feature_set(tapesrv_features);
        stop_amidxtaped();
+
+       if (dump_dle) {
+           run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options,
+                              dump_dle, stderr);
+           slist_free_full(dump_dle->levellist, g_free);
+           dump_dle->levellist = NULL;
+       }
+    }
+    if (dump_dle) {
+       dump_dle->levellist = all_level;
+       run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle,
+                          stderr);
+       slist_free_full(dump_dle->levellist, g_free);
+       all_level = NULL;
+       dump_dle->levellist = NULL;
     }
 }
 
@@ -2498,6 +2648,9 @@ get_amidxtaped_line(void)
        ctl_buffer = stralloc("");
 
     while (!strstr(ctl_buffer,"\r\n")) {
+       if (amidxtaped_streams[CTLFD].fd == NULL)
+           return -1;
+
         size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
         if(size < 0) {
             return -1;
@@ -2511,6 +2664,7 @@ get_amidxtaped_line(void)
         newbuf[strlen(ctl_buffer)+size] = '\0';
         amfree(ctl_buffer);
         ctl_buffer = newbuf;
+       amfree(buf);
     }
 
     s = strstr(ctl_buffer,"\r\n");
@@ -2529,11 +2683,9 @@ read_amidxtaped_data(
     void *     buf,
     ssize_t    size)
 {
-    int fd;
-
+    ctl_data_t *ctl_data = (ctl_data_t *)cookie;
     assert(cookie != NULL);
 
-    fd = *(int *)cookie;
     if (size < 0) {
        errstr = newstralloc2(errstr, _("amidxtaped read: "),
                 security_stream_geterror(amidxtaped_streams[DATAFD].fd));
@@ -2554,28 +2706,217 @@ read_amidxtaped_data(
 
     assert(buf != NULL);
 
-    /*
-     * We ignore errors while writing to the index file.
-     */
-    (void)fullwrite(fd, buf, (size_t)size);
-    security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data, cookie);
+    if (ctl_data->header_done == 0) {
+       GPtrArray  *errarray;
+       g_option_t  g_options;
+       data_path_t data_path_set = DATA_PATH_AMANDA;
+       int to_move;
+
+       to_move = MIN(32768-header_size, size);
+       memcpy(header_buf+header_size, buf, to_move);
+       header_size += to_move;
+
+       g_debug("read header %zd => %d", size, header_size);
+       if (header_size < 32768) {
+            security_stream_read(amidxtaped_streams[DATAFD].fd,
+                                read_amidxtaped_data, cookie);
+           return;
+       } else if (header_size > 32768) {
+           error("header_size is %d\n", header_size);
+       }
+       assert (to_move == size);
+       /* parse the file header */
+       fh_init(&ctl_data->file);
+       parse_file_header(header_buf, &ctl_data->file, (size_t)header_size);
+
+       /* call backup_support_option */
+       g_options.config = get_config_name();
+       g_options.hostname = dump_hostname;
+       if (strcmp(ctl_data->file.program, "APPLICATION") == 0) {
+           if (dump_dle) {
+               ctl_data->bsu = backup_support_option(ctl_data->file.application,
+                                                     &g_options,
+                                                     ctl_data->file.disk,
+                                                     dump_dle->device,
+                                                     &errarray);
+           } else {
+               ctl_data->bsu = backup_support_option(ctl_data->file.application,
+                                                     &g_options,
+                                                     ctl_data->file.disk, NULL,
+                                                     &errarray);
+           }
+           if (!ctl_data->bsu) {
+               guint  i;
+               for (i=0; i < errarray->len; i++) {
+                   char *line;
+                   line = g_ptr_array_index(errarray, i);
+                   g_fprintf(stderr, "%s\n", line);
+               }
+               g_ptr_array_free_full(errarray);
+               exit(1);
+           }
+           data_path_set = ctl_data->bsu->data_path_set;
+       }
+       /* handle backup_support_option failure */
+
+       ctl_data->header_done = 1;
+       if (!ask_file_overwrite(ctl_data)) {
+           if (am_has_feature(tapesrv_features, fe_amidxtaped_abort)) {
+               send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ABORT");
+           }
+           stop_amidxtaped();
+           return;
+       }
+
+       if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
+           char       *msg;
+           /* send DATA-PATH request */
+           msg = stralloc("AVAIL-DATAPATH");
+           if (data_path_set & DATA_PATH_AMANDA)
+               vstrextend(&msg, " AMANDA", NULL);
+           if (data_path_set & DATA_PATH_DIRECTTCP)
+               vstrextend(&msg, " DIRECT-TCP", NULL);
+           send_to_tape_server(amidxtaped_streams[CTLFD].fd, msg);
+           amfree(msg);
+       } else {
+           start_processing_data(ctl_data);
+       }
+    } else {
+       ctl_data->bytes_read += size;
+       /* Only the data is sent to the child */
+       /*
+        * We ignore errors while writing to the index file.
+        */
+       (void)full_write(ctl_data->child_pipe[1], buf, (size_t)size);
+        security_stream_read(amidxtaped_streams[DATAFD].fd,
+                            read_amidxtaped_data, cookie);
+    }
+}
+
+static gboolean
+ask_file_overwrite(
+    ctl_data_t *ctl_data)
+{
+    char *restore_dir = NULL;
+
+    if (ctl_data->file.dumplevel == 0) {
+       property_t *property = g_hash_table_lookup(proplist, "directory");
+       if (property && property->values && property->values->data) {
+           /* take first property value */
+           restore_dir = strdup(property->values->data);
+       }
+       if (samba_extract_method == SAMBA_SMBCLIENT ||
+           (ctl_data->bsu &&
+            ctl_data->bsu->recover_path == RECOVER_PATH_REMOTE)) {
+           if (!restore_dir) {
+               restore_dir = g_strdup(ctl_data->file.disk);
+           }
+           g_printf(_("Restoring files into target host %s\n"), restore_dir);
+       } else {
+           if (!restore_dir) {
+               restore_dir = g_get_current_dir();
+           }
+           g_printf(_("Restoring files into directory %s\n"), restore_dir);
+       }
+
+       /* Collect files to delete befause of a bug in gnutar */
+       if (strcmp(ctl_data->file.program, "GNUTAR") == 0 ||
+           (strcmp(ctl_data->file.program, "APPLICATION") == 0 &&
+            strcmp(ctl_data->file.application, "amgtar") == 0)) {
+           check_file_overwrite(restore_dir);
+       } else {
+           g_printf(_("All existing files in %s can be deleted\n"),
+                    restore_dir);
+       }
+
+       if (!okay_to_continue(0,0,0)) {
+           free_unlink_list();
+           amfree(restore_dir);
+           return FALSE;
+       }
+       g_printf("\n");
+
+       /* delete the files for gnutar */
+       if (unlink_list) {
+           if (!do_unlink_list()) {
+               g_fprintf(stderr, _("Can't recover because I can't cleanup the restore directory (%s)\n"),
+                         restore_dir);
+               free_unlink_list();
+               amfree(restore_dir);
+               return FALSE;
+           }
+           free_unlink_list();
+       }
+       amfree(restore_dir);
+    }
+    return TRUE;
 }
 
-char *
-amidxtaped_client_get_security_conf(
-    char *     string,
-    void *     arg)
+static void
+start_processing_data(
+    ctl_data_t *ctl_data)
 {
-    (void)arg; /* Quiet unused parameter warning */
+    if (pipe(ctl_data->child_pipe) == -1) {
+       error(_("extract_list - error setting up pipe to extractor: %s\n"),
+             strerror(errno));
+       /*NOTREACHED*/
+    }
 
-    if(!string || !*string)
-       return(NULL);
+    /* decrypt */
+    if (ctl_data->file.encrypted) {
+       char *argv[3];
+       int  crypt_out;
+       int  errfd = fileno(stderr);
+
+       g_debug("image is encrypted %s %s", ctl_data->file.clnt_encrypt, ctl_data->file.clnt_decrypt_opt);
+       argv[0] = ctl_data->file.clnt_encrypt;
+       argv[1] = ctl_data->file.clnt_decrypt_opt;
+       argv[2] = NULL;
+       pipespawnv(ctl_data->file.clnt_encrypt, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &crypt_out, &errfd, argv);
+       ctl_data->child_pipe[0] = crypt_out;
+    }
+
+    /* decompress */
+    if (ctl_data->file.compressed) {
+       char *argv[3];
+       int  comp_out;
+       int  errfd = fileno(stderr);
+       char *comp_prog;
+       char *comp_arg;
+
+       g_debug("image is compressed %s", ctl_data->file.clntcompprog);
+       if (strlen(ctl_data->file.clntcompprog) > 0) {
+           comp_prog = ctl_data->file.clntcompprog;
+           comp_arg = "-d";
+       } else {
+           comp_prog = UNCOMPRESS_PATH;
+           comp_arg = UNCOMPRESS_OPT;
+       }
+       argv[0] = comp_prog;
+       argv[1] = comp_arg;
+       argv[2] = NULL;
+       pipespawnv(comp_prog, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &comp_out, &errfd, argv);
+       ctl_data->child_pipe[0] = comp_out;
+    }
 
-    if(strcmp(string, "auth")==0) {
-       return(getconf_str(CNF_AUTH));
+    /* okay, ready to extract. fork a child to do the actual work */
+    if ((ctl_data->pid = fork()) == 0) {
+       /* this is the child process */
+       /* never gets out of this clause */
+       aclose(ctl_data->child_pipe[1]);
+       extract_files_child(ctl_data);
+       /*NOTREACHED*/
+    }
+       
+    if (ctl_data->pid == -1) {
+       errstr = newstralloc(errstr, _("writer_intermediary - error forking child"));
+       g_printf(_("writer_intermediary - error forking child"));
+       return;
     }
-    if(strcmp(string, "ssh_keys")==0) {
-       return(getconf_str(CNF_SSH_KEYS));
+    aclose(ctl_data->child_pipe[0]);
+    security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data,
+                        ctl_data);
+    if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
+       send_to_tape_server(amidxtaped_streams[CTLFD].fd, "DATAPATH-OK");
     }
-    return(NULL);
 }