Imported Upstream version 3.3.3
[debian/amanda] / recover-src / extract_list.c
index d254022ed573a72b3efe060ef171ffec3fa746ab..9a453cb0fd8de01d3399efe9b7cd872d91c7659f 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
  * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
+ * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
  * All Rights Reserved.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
 #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;
@@ -72,6 +75,7 @@ typedef struct ctl_data_s {
   data_path_t              data_path;
   char                    *addrs;
   backup_support_option_t *bsu;
+  gint64                   bytes_read;
 } ctl_data_t;
 
 #define SKIP_TAPE 2
@@ -95,6 +99,9 @@ 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;
@@ -267,6 +274,7 @@ clear_tape_list(
     {
        next = this->next;
         amfree(this->path);
+        amfree(this->tpath);
         amfree(this);
        this = next;
     }
@@ -380,6 +388,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) {
@@ -391,10 +400,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;
@@ -578,7 +588,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);
@@ -593,16 +603,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;
        }
     }
@@ -614,7 +624,8 @@ 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;
 
@@ -625,7 +636,6 @@ add_extract_item(
     {
        this->next = extract_list;
        extract_list = this;
-       amfree(ditem_path);
        return 0;
     }
     for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
@@ -635,14 +645,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;
 }
 
@@ -673,6 +681,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)
@@ -688,6 +697,7 @@ delete_extract_item(
                {
                    prev->next = that->next;
                     amfree(that->path);
+                    amfree(that->tpath);
                    amfree(that);
                    amfree(ditem_path);
                    return 0;
@@ -821,12 +831,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;
@@ -860,34 +870,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, 0);
-       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);
@@ -898,7 +908,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);
@@ -906,25 +916,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) {
@@ -972,9 +985,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)) {
@@ -994,7 +1008,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;
@@ -1007,7 +1020,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"),
@@ -1043,14 +1056,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);
@@ -1061,9 +1074,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);
 
@@ -1175,11 +1189,11 @@ delete_regex(
 
 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;
@@ -1188,8 +1202,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;
@@ -1204,7 +1218,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 . */
@@ -1220,38 +1234,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, 0);
-       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);
@@ -1259,26 +1275,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 */
@@ -1286,7 +1303,7 @@ delete_file(
                {
                    if (i == -1) {
                        amfree(ditem_path);
-                       amfree(path_on_disk);
+                       amfree(tpath_on_disk);
                        exit(1);
                    }
                    if(err) {
@@ -1346,7 +1363,6 @@ delete_file(
                            err = _("bad reply: cannot parse fileno field");
                            continue;
                        }
-                       fileno = (off_t)fileno_;
                        skip_integer(s, ch);
                    }
 
@@ -1362,16 +1378,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:
@@ -1387,9 +1404,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
@@ -1400,15 +1417,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;
                }
            }
@@ -1416,12 +1433,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);
     }
 }
 
@@ -1473,7 +1491,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) {
@@ -1682,7 +1700,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"));
@@ -1836,7 +1861,7 @@ extract_files_child(
        /*NOTREACHED*/
     }
 
-    if (ctl_data->file.program != NULL) {
+    if (ctl_data->file.program[0] != '\0') {
        if (strcmp(ctl_data->file.program, "APPLICATION") == 0)
            dumptype = IS_APPLICATION_API;
 #ifdef GNUTAR
@@ -1886,6 +1911,8 @@ extract_files_child(
     case IS_TAR:
     case IS_GNUTAR:
        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 */
@@ -1899,7 +1926,6 @@ extract_files_child(
     case IS_DUMP:
        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)
@@ -1949,7 +1975,8 @@ extract_files_child(
            GSList   *scriptlist;
            script_t *script;
 
-           merge_properties(dump_dle->application_property, proplist);
+           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;
@@ -1961,7 +1988,7 @@ extract_files_child(
            }
 
        } else if (proplist) {
-           g_hash_table_foreach(proplist, &proplist_add_to_argv, argv_ptr);
+           property_add_to_argv(argv_ptr, proplist);
        }
        break;
     }
@@ -2100,7 +2127,9 @@ writer_intermediary(
     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, &ctl_data);
 
@@ -2169,6 +2198,7 @@ writer_intermediary(
            return -1;
        }
     }
+    g_debug("bytes read: %jd", (intmax_t)ctl_data.bytes_read);
     return(0);
 }
 
@@ -2278,12 +2308,12 @@ extract_files(void)
     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);
+       am_level_t *level = g_new0(am_level_t, 1);
        level->level = elist->level;
        all_level = g_slist_append(all_level, level);
     }
     if (dump_dle) {
-       g_slist_free_full(dump_dle->levellist);
+       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);
@@ -2320,18 +2350,18 @@ extract_files(void)
        dump_datestamp = newstralloc(dump_datestamp, elist->date);
 
        if (last_level != -1 && dump_dle) {
-           level_t *level;
+           am_level_t *level;
 
-           level = g_new0(level_t, 1);
+           level = g_new0(am_level_t, 1);
            level->level = last_level;
            dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
 
-           level = g_new0(level_t, 1);
+           level = g_new0(am_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);
-           g_slist_free_full(dump_dle->levellist);
+           slist_free_full(dump_dle->levellist, g_free);
            dump_dle->levellist = NULL;
        }
 
@@ -2343,9 +2373,9 @@ extract_files(void)
            return;
        }
        if (dump_dle) {
-           level_t *level;
+           am_level_t *level;
 
-           level = g_new0(level_t, 1);
+           level = g_new0(am_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,
@@ -2367,7 +2397,7 @@ extract_files(void)
        if (dump_dle) {
            run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options,
                               dump_dle, stderr);
-           g_slist_free_full(dump_dle->levellist);
+           slist_free_full(dump_dle->levellist, g_free);
            dump_dle->levellist = NULL;
        }
     }
@@ -2375,7 +2405,7 @@ extract_files(void)
        dump_dle->levellist = all_level;
        run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle,
                           stderr);
-       g_slist_free_full(dump_dle->levellist);
+       slist_free_full(dump_dle->levellist, g_free);
        all_level = NULL;
        dump_dle->levellist = NULL;
     }
@@ -2681,10 +2711,24 @@ read_amidxtaped_data(
        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) {
+           /* wait to read more data */
+           return;
+       } else if (header_size > 32768) {
+           error("header_size is %d\n", header_size);
+       }
+       assert (to_move == size);
+       security_stream_read_cancel(amidxtaped_streams[DATAFD].fd);
        /* parse the file header */
        fh_init(&ctl_data->file);
-       parse_file_header(buf, &ctl_data->file, (size_t)size);
+       parse_file_header(header_buf, &ctl_data->file, (size_t)header_size);
 
        /* call backup_support_option */
        g_options.config = get_config_name();
@@ -2739,13 +2783,12 @@ read_amidxtaped_data(
            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);
     }
 }
 
@@ -2818,6 +2861,43 @@ start_processing_data(
        /*NOTREACHED*/
     }
 
+    /* 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;
+    }
+
     /* okay, ready to extract. fork a child to do the actual work */
     if ((ctl_data->pid = fork()) == 0) {
        /* this is the child process */