Imported Upstream version 2.6.1
[debian/amanda] / restore-src / amrestore.c
index e1161ba755d96ef8c44ef4c297a9ba0b0eb7b039..aef4bafda198f8936f54f31ce99c05ec842394d5 100644 (file)
@@ -24,7 +24,7 @@
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: amrestore.c,v 1.28.2.4.4.3.2.8 2003/02/09 04:33:13 jrjackson Exp $
+ * $Id: amrestore.c 6512 2007-05-24 17:00:24Z ian $
  *
  * retrieves files from an amanda tape
  */
  */
 
 #include "amanda.h"
-#include "tapeio.h"
-#include "fileheader.h"
 #include "util.h"
+#include "fileheader.h"
+#include "restore.h"
+#include "conffile.h"
+#include "device.h"
+#include "cmdline.h"
 
 #define CREAT_MODE     0640
 
-char *buffer = NULL;
-
-int compflag, rawflag, pipeflag, headerflag;
-int got_sigpipe, file_number;
-pid_t compress_pid = -1;
-char *compress_type = COMPRESS_FAST_OPT;
-int tapedev;
-int bytes_read;
-long blocksize = -1;
-long filefsf = -1;
-
-/* local functions */
-
-void errexit P((void));
-void handle_sigpipe P((int sig));
-int disk_match P((dumpfile_t *file, char *datestamp, 
-                 char *hostname, char *diskname));
-char *make_filename P((dumpfile_t *file));
-void read_file_header P((dumpfile_t *file, int isafile));
-static int get_block P((int isafile));
-void restore P((dumpfile_t *file, char *filename, int isafile));
-void usage P((void));
-int main P((int argc, char **argv));
-
-void errexit()
 /*
- * Do exit(2) after an error, rather than exit(1).
- */
-{
-    exit(2);
-}
-
-
-void handle_sigpipe(sig)
-int sig;
-/*
- * Signal handler for the SIGPIPE signal.  Just sets a flag and returns.
- * The act of catching the signal causes the pipe write() to fail with
- * EINTR.
+ * Print usage message and terminate.
  */
-{
-    got_sigpipe++;
-}
-
-int disk_match(file, datestamp, hostname, diskname)
-dumpfile_t *file;
-char *datestamp, *hostname, *diskname;
 
-/*
- * Returns 1 if the current dump file matches the hostname and diskname
- * regular expressions given on the command line, 0 otherwise.  As a 
- * special case, empty regexs are considered equivalent to ".*": they 
- * match everything.
- */
+static void
+usage(void)
 {
-    if(file->type != F_DUMPFILE) return 0;
-
-    if((*hostname == '\0' || match_host(hostname, file->name)) &&
-       (*diskname == '\0' || match_disk(diskname, file->disk)) &&
-       (*datestamp== '\0' || match_datestamp(datestamp, file->datestamp)))
-       return 1;
-    else
-       return 0;
+    error(_("Usage: amrestore [-b blocksize] [-r|-c] [-p] [-h] [-f fileno] "
+         "[-l label] tape-device|holdingfile [hostname [diskname [datestamp "
+         "[hostname [diskname [datestamp ... ]]]]]]"));
+    /*NOTREACHED*/
 }
 
-
-char *make_filename(file)
-dumpfile_t *file;
-{
-    char number[NUM_STR_SIZE];
-    char *sfn;
-    char *fn;
-
-    ap_snprintf(number, sizeof(number), "%d", file->dumplevel);
-    sfn = sanitise_filename(file->disk);
-    fn = vstralloc(file->name,
-                  ".",
-                  sfn, 
-                  ".",
-                  file->datestamp,
-                  ".",
-                  number,
-                  NULL);
-    amfree(sfn);
-    return fn;
+/* Checks if the given tape device is actually a holding disk file. We
+   accomplish this by stat()ing the file; if it is a regular file, we
+   assume (somewhat dangerously) that it's a holding disk file. If
+   it doesn't exist or is not a regular file, we assume it's a device
+   name.
+
+   Returns TRUE if we suspect the device is a holding disk, FALSE
+   otherwise. */
+static gboolean check_device_type(char * device_name) {
+    struct stat stat_buf;
+    int result;
+    
+    result = stat(device_name, &stat_buf);
+
+    return !((result != 0 || !S_ISREG(stat_buf.st_mode)));
 }
 
+static void handle_holding_disk_restore(char * filename, rst_flags_t * flags,
+                                        GSList * dumpspecs) {
+    dumpfile_t this_header;
+    tapelist_t this_disk;
 
-static int get_block(isafile)
-int isafile;
-{
-    static int test_blocksize = 1;
-    int buflen;
+    bzero(&this_disk, sizeof(this_disk));
+    this_disk.label = filename;
 
-    /*
-     * If this is the first call, set the blocksize if it was not on
-     * the command line.  Allocate the I/O buffer in any case.
-     *
-     * For files, the blocksize is always DISK_BLOCK_BYTES.  For tapes,
-     * we allocate a large buffer and set the size to the length of the
-     * first (successful) record.
-     */
-    buflen = blocksize;
-    if(test_blocksize) {
-       if(blocksize < 0) {
-           if(isafile) {
-               blocksize = buflen = DISK_BLOCK_BYTES;
-           } else {
-               buflen = MAX_TAPE_BLOCK_BYTES;
-           }
-       }
-       buffer = newalloc(buffer, buflen);
-    }
-    if(isafile) {
-       bytes_read = fullread(tapedev, buffer, buflen);
-    } else {
-       bytes_read = tapefd_read(tapedev, buffer, buflen);
-       if(blocksize < 0 && bytes_read > 0 && bytes_read < buflen) {
-           char *new_buffer;
-
-           blocksize = bytes_read;
-           new_buffer = alloc(blocksize);
-           memcpy(new_buffer, buffer, bytes_read);
-           amfree(buffer);
-           buffer = new_buffer;
-       }
+    if (!restore_holding_disk(stderr, flags, NULL, &this_disk, NULL,
+                              dumpspecs, &this_header, NULL)) {
+        g_fprintf(stderr, "%s did not match requested host.\n", filename);
+        return;
     }
-    if(blocksize > 0) {
-       test_blocksize = 0;
-    }
-    return bytes_read;
 }
 
+static void handle_tape_restore(char * device_name, rst_flags_t * flags,
+                                GSList * dumpspecs, char * check_label) {
+    Device * device;
+    DeviceStatusFlags device_status;
 
-void read_file_header(file, isafile)
-dumpfile_t *file;
-int isafile;
-/*
- * Reads the first block of a tape file.
- */
-{
-    bytes_read = get_block(isafile);
-    if(bytes_read < 0) {
-       error("error reading file header: %s", strerror(errno));
-    } else if(bytes_read < blocksize) {
-       if(bytes_read == 0) {
-           fprintf(stderr, "%s: missing file header block\n", get_pname());
-       } else {
-           fprintf(stderr, "%s: short file header block: %d byte%s\n",
-                   get_pname(), bytes_read, (bytes_read == 1) ? "" : "s");
-       }
-       file->type = F_UNKNOWN;
-    } else {
-       parse_file_header(buffer, file, bytes_read);
-    }
-    return;
-}
+    dumpfile_t first_restored_file;
 
+    device_api_init();
 
-void restore(file, filename, isafile)
-dumpfile_t *file;
-char *filename;
-int isafile;
-/*
- * Restore the current file from tape.  Depending on the settings of
- * the command line flags, the file might need to be compressed or
- * uncompressed.  If so, a pipe through compress or uncompress is set
- * up.  The final output usually goes to a file named host.disk.date.lev,
- * but with the -p flag the output goes to stdout (and presumably is
- * piped to restore).
- */
-{
-    int rc = 0, dest, out, outpipe[2];
-    int wc;
-    int l, s;
-    int file_is_compressed;
-
-    /* adjust compression flag */
-
-    file_is_compressed = file->compressed;
-    if(!compflag && file_is_compressed && !known_compress_type(file)) {
-       fprintf(stderr, 
-               "%s: unknown compression suffix %s, can't uncompress\n",
-               get_pname(), file->comp_suffix);
-       compflag = 1;
+    fh_init(&first_restored_file);
+    
+    device = device_open(device_name);
+    g_assert(device != NULL);
+    if (device->status != DEVICE_STATUS_SUCCESS) {
+        error("Could not open device %s: %s.\n", device_name, device_error(device));
     }
-
-    /* set up final destination file */
-
-    if(pipeflag)
-       dest = 1;               /* standard output */
-    else {
-       char *filename_ext = NULL;
-
-       if(compflag) {
-           filename_ext = file_is_compressed ? file->comp_suffix
-                                             : COMPRESS_SUFFIX;
-       } else if(rawflag) {
-           filename_ext = ".RAW";
-       } else {
-           filename_ext = "";
-       }
-       filename_ext = stralloc2(filename, filename_ext);
-
-       if((dest = creat(filename, CREAT_MODE)) < 0)
-           error("could not create output file: %s", strerror(errno));
-       amfree(filename_ext);
+    
+    if (!set_restore_device_read_buffer_size(device, flags)) {
+        error("Error setting read block size: %s.\n", device_error_or_status(device));
     }
-
-    out = dest;
-
-    /*
-     * If -r or -h, write the header before compress or uncompress pipe.
-     * Only write DISK_BLOCK_BYTES, regardless of how much was read.
-     * This makes the output look like a holding disk image, and also
-     * makes it easier to remove the header (e.g. in amrecover) since
-     * it has a fixed size.
-     */
-    if(rawflag || headerflag) {
-       int w;
-       char *cont_filename;
-
-       if(compflag && !file_is_compressed) {
-           file->compressed = 1;
-           ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
-                       " %s %s |", UNCOMPRESS_PATH,
-#ifdef UNCOMPRESS_OPT
-                       UNCOMPRESS_OPT
-#else
-                       ""
-#endif
-                       );
-           strncpy(file->comp_suffix,
-                   COMPRESS_SUFFIX,
-                   sizeof(file->comp_suffix)-1);
-           file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
-       }
-
-       /* remove CONT_FILENAME from header */
-       cont_filename = stralloc(file->cont_filename);
-       memset(file->cont_filename,'\0',sizeof(file->cont_filename));
-       file->blocksize = DISK_BLOCK_BYTES;
-       build_header(buffer, file, bytes_read);
-
-       if((w = fullwrite(out, buffer, DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
-           if(w < 0) {
-               error("write error: %s", strerror(errno));
-           } else {
-               error("write error: %d instead of %d", w, DISK_BLOCK_BYTES);
-           }
-       }
-       /* add CONT_FILENAME to header */
-       strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename));
+    device_status = device_read_label(device);
+    if (device_status != DEVICE_STATUS_SUCCESS) {
+        error("Error reading volume label: %s.\n", device_error_or_status(device));
     }
 
-    /* if -c and file not compressed, insert compress pipe */
+    g_assert(device->volume_label != NULL);
 
-    if(compflag && !file_is_compressed) {
-       if(pipe(outpipe) < 0) error("error [pipe: %s]", strerror(errno));
-       out = outpipe[1];
-       switch(compress_pid = fork()) {
-       case -1: error("could not fork for %s: %s",
-                      COMPRESS_PATH, strerror(errno));
-       default:
-           aclose(outpipe[0]);
-           aclose(dest);
-           break;
-       case 0:
-           aclose(outpipe[1]);
-           if(outpipe[0] != 0) {
-               if(dup2(outpipe[0], 0) == -1)
-                   error("error [dup2 pipe: %s]", strerror(errno));
-               aclose(outpipe[0]);
-           }
-           if(dest != 1) {
-               if(dup2(dest, 1) == -1)
-                   error("error [dup2 dest: %s]", strerror(errno));
-               aclose(dest);
-           }
-           if (*compress_type == '\0') {
-               compress_type = NULL;
-           }
-           execlp(COMPRESS_PATH, COMPRESS_PATH, compress_type, (char *)0);
-           error("could not exec %s: %s", COMPRESS_PATH, strerror(errno));
-       }
+    if (!device_start(device, ACCESS_READ, NULL, NULL)) {
+        error("Could not open device %s for reading: %s.\n", device_name,
+             device_error(device));
     }
 
-    /* if not -r or -c, and file is compressed, insert uncompress pipe */
-
-    else if(!rawflag && !compflag && file_is_compressed) {
-       /* 
-        * XXX for now we know that for the two compression types we
-        * understand, .Z and optionally .gz, UNCOMPRESS_PATH will take
-        * care of both.  Later, we may need to reference a table of
-        * possible uncompress programs.
-        */
-       if(pipe(outpipe) < 0) error("error [pipe: %s]", strerror(errno));
-       out = outpipe[1];
-       switch(compress_pid = fork()) {
-       case -1: 
-           error("could not fork for %s: %s",
-                 UNCOMPRESS_PATH, strerror(errno));
-       default:
-           aclose(outpipe[0]);
-           aclose(dest);
-           break;
-       case 0:
-           aclose(outpipe[1]);
-           if(outpipe[0] != 0) {
-               if(dup2(outpipe[0], 0) < 0)
-                   error("dup2 pipe: %s", strerror(errno));
-               aclose(outpipe[0]);
-           }
-           if(dest != 1) {
-               if(dup2(dest, 1) < 0)
-                   error("dup2 dest: %s", strerror(errno));
-               aclose(dest);
-           }
-           (void) execlp(UNCOMPRESS_PATH, UNCOMPRESS_PATH,
-#ifdef UNCOMPRESS_OPT
-                         UNCOMPRESS_OPT,
-#endif
-                         (char *)0);
-           error("could not exec %s: %s", UNCOMPRESS_PATH, strerror(errno));
-       }
+    if (check_label != NULL && strcmp(check_label,
+                                      device->volume_label) != 0) {
+        error("Wrong label: Expected %s, found %s.\n",
+              check_label, device->volume_label);
     }
 
-
-    /* copy the rest of the file from tape to the output */
-    got_sigpipe = 0;
-    wc = 0;
-    do {
-       bytes_read = get_block(isafile);
-       if(bytes_read < 0) {
-           error("read error: %s", strerror(errno));
-       }
-       if(bytes_read == 0 && isafile) {
-           /*
-            * See if we need to switch to the next file.
-            */
-           if(file->cont_filename[0] == '\0') {
-               break;                          /* no more files */
-           }
-           close(tapedev);
-           if((tapedev = open(file->cont_filename, O_RDONLY)) == -1) {
-               char *cont_filename = strrchr(file->cont_filename,'/');
-               if(cont_filename) {
-                   cont_filename++;
-                   if((tapedev = open(cont_filename,O_RDONLY)) == -1) {
-                       error("can't open %s: %s", file->cont_filename,
-                             strerror(errno));
-                   }
-                   else {
-                       fprintf(stderr, "cannot open %s: %s\n",
-                               file->cont_filename, strerror(errno));
-                       fprintf(stderr, "using %s\n",
-                               cont_filename);
-                   }
-               }
-               else {
-                   error("can't open %s: %s", file->cont_filename,
-                         strerror(errno));
-               }
-           }
-           read_file_header(file, isafile);
-           if(file->type != F_DUMPFILE && file->type != F_CONT_DUMPFILE) {
-               fprintf(stderr, "unexpected header type: ");
-               print_header(stderr, file);
-               exit(2);
-           }
-           continue;
-       }
-       for(l = 0; l < bytes_read; l += s) {
-           if((s = write(out, buffer + l, bytes_read - l)) < 0) {
-               if(got_sigpipe) {
-                   fprintf(stderr,"Error %d (%s) offset %d+%d, wrote %d\n",
-                                  errno, strerror(errno), wc, bytes_read, rc);
-                   fprintf(stderr,  
-                           "%s: pipe reader has quit in middle of file.\n",
-                           get_pname());
-               } else {
-                   perror("amrestore: write error");
-               }
-               exit(2);
-           }
-       }
-       wc += bytes_read;
-    } while (bytes_read > 0);
-    if(pipeflag) {
-       if(out != dest) {
-           aclose(out);
-       }
-    } else {
-       aclose(out);
-    }
+    search_a_tape(device, stderr, flags, NULL, NULL, dumpspecs,
+                  NULL, &first_restored_file, 0, NULL);
 }
 
-
-void usage()
-/*
- * Print usage message and terminate.
- */
-{
-    error("Usage: amrestore [-b blocksize] [-r|-c] [-p] [-h] [-f fileno] [-l label] tape-device|holdingfile [hostname [diskname [datestamp [hostname [diskname [datestamp ... ]]]]]]");
-}
-
-
-int main(argc, argv)
-int argc;
-char **argv;
 /*
  * Parses command line, then loops through all files on tape, restoring
  * files that match the command line criteria.
  */
+
+int
+main(
+    int                argc,
+    char **    argv)
 {
     extern int optind;
     int opt;
-    char *errstr;
-    int isafile;
-    struct stat stat_tape;
-    dumpfile_t file;
-    char *filename = NULL;
+    int holding_disk_mode;
     char *tapename = NULL;
-    struct match_list {
-       char *hostname;
-       char *diskname;
-       char *datestamp;
-       struct match_list *next;
-    } *match_list = NULL, *me = NULL;
-    int found_match;
-    int arg_state;
-    amwait_t compress_status;
-    int fd;
-    int r = 0;
     char *e;
-    char *err;
     char *label = NULL;
+    rst_flags_t *rst_flags;
+    long tmplong;
+    GSList *dumpspecs;
+    config_overwrites_t *cfg_ovr;
 
-    for(fd = 3; fd < FD_SETSIZE; fd++) {
-       /*
-        * Make sure nobody spoofs us with a lot of extra open files
-        * that would cause an open we do to get a very high file
-        * descriptor, which in turn might be used as an index into
-        * an array (e.g. an fd_set).
-        */
-       close(fd);
-    }
+    /*
+     * Configure program for internationalization:
+     *   1) Only set the message locale for now.
+     *   2) Set textdomain for all amanda related programs to "amanda"
+     *      We don't want to be forced to support dozens of message catalogs.
+     */  
+    setlocale(LC_MESSAGES, "C");
+    textdomain("amanda"); 
+
+    safe_fd(-1, 0);
 
     set_pname("amrestore");
 
+    dbopen(DBG_SUBDIR_SERVER);
+
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
+
     erroutput_type = ERR_INTERACTIVE;
+    error_exit_status = 2;
 
-    onerror(errexit);
-    signal(SIGPIPE, handle_sigpipe);
+    rst_flags = new_rst_flags();
+    rst_flags->inline_assemble = 0;
 
+    cfg_ovr = new_config_overwrites(argc/2);
     /* handle options */
-    while( (opt = getopt(argc, argv, "b:cCd:rpkhf:l:")) != -1) {
+    while( (opt = getopt(argc, argv, "b:cCd:rphf:l:o:")) != -1) {
        switch(opt) {
        case 'b':
-           blocksize = strtol(optarg, &e, 10);
+           tmplong = strtol(optarg, &e, 10);
+           rst_flags->blocksize = (ssize_t)tmplong;
            if(*e == 'k' || *e == 'K') {
-               blocksize *= 1024;
+               rst_flags->blocksize *= 1024;
            } else if(*e == 'm' || *e == 'M') {
-               blocksize *= 1024 * 1024;
+               rst_flags->blocksize *= 1024 * 1024;
            } else if(*e != '\0') {
-               error("invalid blocksize value \"%s\"", optarg);
-           }
-           if(blocksize < DISK_BLOCK_BYTES) {
-               error("minimum block size is %dk", DISK_BLOCK_BYTES / 1024);
+               error(_("invalid blocksize value \"%s\""), optarg);
+               /*NOTREACHED*/
            }
            break;
-       case 'c': compflag = 1; break;
-       case 'C': compflag = 1; compress_type = COMPRESS_BEST_OPT; break;
-       case 'r': rawflag = 1; break;
-       case 'p': pipeflag = 1; break;
-       case 'h': headerflag = 1; break;
-       case 'f':
-           filefsf = strtol(optarg, &e, 10);
+       case 'c': rst_flags->compress = 1; break;
+       case 'o':
+           add_config_overwrite_opt(cfg_ovr, optarg);
+           break;
+       case 'C':
+           rst_flags->compress = 1;
+           rst_flags->comp_type = COMPRESS_BEST_OPT;
+           break;
+       case 'r': rst_flags->raw = 1; break;
+       case 'p': rst_flags->pipe_to_fd = fileno(stdout); break;
+       case 'h': rst_flags->headers = 1; break;
+       case 'f': rst_flags->fsf = (off_t)OFF_T_STRTOL(optarg, &e, 10);
+           /*@ignore@*/
            if(*e != '\0') {
-               error("invalid fileno value \"%s\"", optarg);
-           }
+               error(_("invalid fileno value \"%s\""), optarg);
+                g_assert_not_reached();
+            }
+           /*@end@*/
            break;
        case 'l':
+            if (label) {
+                error(_("Cannot specify multiple labels.\n"));
+            }
            label = stralloc(optarg);
            break;
        default:
@@ -535,228 +226,55 @@ char **argv;
        }
     }
 
-    if(compflag && rawflag) {
-       fprintf(stderr, 
-               "Cannot specify both -r (raw) and -c (compressed) output.\n");
+    /* initialize a generic configuration without reading anything */
+    config_init(0, NULL);
+    apply_config_overwrites(cfg_ovr);
+
+    if (config_errors(NULL) >= CFGERR_WARNINGS) {
+       config_print_errors();
+       if (config_errors(NULL) >= CFGERR_ERRORS) {
+           g_critical(_("errors processing config file"));
+       }
+    }
+
+    if(rst_flags->compress && rst_flags->raw) {
+       g_fprintf(stderr,
+               _("Cannot specify both -r (raw) and -c (compressed) output.\n"));
        usage();
     }
 
     if(optind >= argc) {
-       fprintf(stderr, "%s: Must specify tape-device or holdingfile\n",
+       g_fprintf(stderr, _("%s: Must specify tape-device or holdingfile\n"),
                        get_pname());
        usage();
     }
 
     tapename = argv[optind++];
 
-#define ARG_GET_HOST 0
-#define ARG_GET_DISK 1
-#define ARG_GET_DATE 2
-
-    arg_state = ARG_GET_HOST;
-    while(optind < argc) {
-       switch(arg_state) {
-       case ARG_GET_HOST:
-           /*
-            * This is a new host/disk/date triple, so allocate a match_list.
-            */
-           me = alloc(sizeof(*me));
-           me->hostname = argv[optind++];
-           me->diskname = "";
-           me->datestamp = "";
-           me->next = match_list;
-           match_list = me;
-           if(me->hostname[0] != '\0'
-              && (errstr=validate_regexp(me->hostname)) != NULL) {
-               fprintf(stderr, "%s: bad hostname regex \"%s\": %s\n",
-                       get_pname(), me->hostname, errstr);
-               usage();
-           }
-           arg_state = ARG_GET_DISK;
-           break;
-       case ARG_GET_DISK:
-           me->diskname = argv[optind++];
-           if(me->diskname[0] != '\0'
-              && (errstr=validate_regexp(me->diskname)) != NULL) {
-               fprintf(stderr, "%s: bad diskname regex \"%s\": %s\n",
-                       get_pname(), me->diskname, errstr);
-               usage();
-           }
-           arg_state = ARG_GET_DATE;
-           break;
-       case ARG_GET_DATE:
-           me->datestamp = argv[optind++];
-           if(me->datestamp[0] != '\0'
-              && (errstr=validate_regexp(me->datestamp)) != NULL) {
-               fprintf(stderr, "%s: bad datestamp regex \"%s\": %s\n",
-                       get_pname(), me->datestamp, errstr);
-               usage();
-           }
-           arg_state = ARG_GET_HOST;
-           break;
-       }
-    }
-    if(match_list == NULL) {
-       match_list = alloc(sizeof(*match_list));
-       match_list->hostname = "";
-       match_list->diskname = "";
-       match_list->datestamp = "";
-       match_list->next = NULL;
-    }
+    dumpspecs = cmdline_parse_dumpspecs(argc - optind, argv + optind, 
+                                       CMDLINE_PARSE_DATESTAMP |
+                                       CMDLINE_EMPTY_TO_WILDCARD);
 
-    if(tape_stat(tapename,&stat_tape)!=0) {
-       error("could not stat %s: %s", tapename, strerror(errno));
-    }
-    isafile=S_ISREG((stat_tape.st_mode));
+    holding_disk_mode = check_device_type(tapename);
 
-    if(label) {
-       if(isafile) {
-           fprintf(stderr,"%s: ignoring -l flag when restoring from a file.\n",
+    if (holding_disk_mode) {
+        if (label) {
+           g_fprintf(stderr,_("%s: ignoring -l flag when restoring from a file.\n"),
                    get_pname());
-       }
-       else {
-           if((err = tape_rewind(tapename)) != NULL) {
-               error("Could not rewind device '%s': %s", tapename, err);
-           }
-           tapedev = tape_open(tapename, 0);
-           read_file_header(&file, isafile);
-           if(file.type != F_TAPESTART) {
-               fprintf(stderr,"Not an amanda tape\n");
-               exit (1);
-           }
-           if(strcmp(label, file.name) != 0) {
-               fprintf(stderr,"Wrong label: '%s'\n", file.name);
-               exit (1);
-           }
-           tapefd_close(tapedev);
-           if((err = tape_rewind(tapename)) != NULL) {
-               error("Could not rewind device '%s': %s", tapename, err);
-           }
-       }
-    }
-    file_number = 0;
-    if(filefsf != -1) {
-       if(isafile) {
-           fprintf(stderr,"%s: ignoring -f flag when restoring from a file.\n",
+        }
+
+        if (rst_flags->fsf > 0) {
+            g_fprintf(stderr,
+                    "%s: ignoring -f flag when restoring from a file.\n",
                    get_pname());
-       }
-       else {
-           if((err = tape_rewind(tapename)) != NULL) {
-               error("Could not rewind device '%s': %s", tapename, err);
-           }
-           if((err = tape_fsf(tapename,filefsf)) != NULL) {
-               error("Could not fsf device '%s': %s", tapename, err);
-           }
-           file_number = filefsf;
-       }
-    }
+        }
 
-    if(isafile) {
-       tapedev = open(tapename, 0);
+        handle_holding_disk_restore(tapename, rst_flags, dumpspecs);
     } else {
-       tapedev = tape_open(tapename, 0);
-    }
-    if(tapedev < 0) {
-       error("could not open %s: %s", tapename, strerror(errno));
-    }
-
-    read_file_header(&file, isafile);
-
-    if(file.type != F_TAPESTART && !isafile && filefsf == -1) {
-       fprintf(stderr, "%s: WARNING: not at start of tape, file numbers will be offset\n",
-                       get_pname());
+        handle_tape_restore(tapename, rst_flags, dumpspecs, label);
     }
 
-    while(file.type == F_TAPESTART || file.type == F_DUMPFILE) {
-       amfree(filename);
-       filename = make_filename(&file);
-       found_match = 0;
-       for(me = match_list; me; me = me->next) {
-           if(disk_match(&file,me->datestamp,me->hostname,me->diskname) != 0) {
-               found_match = 1;
-               break;
-           }
-       }
-       fprintf(stderr, "%s: %3d: %s ",
-                       get_pname(),
-                       file_number,
-                       found_match ? "restoring" : "skipping");
-       if(file.type != F_DUMPFILE) {
-           print_header(stderr, &file);
-       } else {
-           fprintf(stderr, "%s\n", filename);
-       }
-       if(found_match) {
-           restore(&file, filename, isafile);
-           if(compress_pid > 0) {
-               waitpid(compress_pid, &compress_status, 0);
-               compress_pid = -1;
-           }
-           if(pipeflag) {
-               file_number++;                  /* for the last message */
-               break;
-           }
-       }
-       if(isafile) {
-           break;
-       }
-       /*
-        * Note that at this point we know we are working with a tape,
-        * not a holding disk file, so we can call the tape functions
-        * without checking.
-        */
-       if(bytes_read == 0) {
-           /*
-            * If the last read got EOF, how to get to the next
-            * file depends on how the tape device driver is acting.
-            * If it is BSD-like, we do not really need to do anything.
-            * If it is Sys-V-like, we need to either fsf or close/open.
-            * The good news is, a close/open works in either case,
-            * so that's what we do.
-            */
-           tapefd_close(tapedev);
-           if((tapedev = tape_open(tapename, 0)) < 0) {
-               error("could not open %s: %s", tapename, strerror(errno));
-           }
-       } else {
-           /*
-            * If the last read got something (even an error), we can
-            * do an fsf to get to the next file.
-            */
-           if(tapefd_fsf(tapedev, 1) < 0) {
-               error("could not fsf %s: %s", tapename, strerror(errno));
-           }
-       }
-       file_number++;
-       read_file_header(&file, isafile);
-    }
-    if(isafile) {
-       close(tapedev);
-    } else {
-       /*
-        * See the notes above about advancing to the next file.
-        */
-       if(bytes_read == 0) {
-           tapefd_close(tapedev);
-           if((tapedev = tape_open(tapename, 0)) < 0) {
-               error("could not open %s: %s", tapename, strerror(errno));
-           }
-       } else {
-           if(tapefd_fsf(tapedev, 1) < 0) {
-               error("could not fsf %s: %s", tapename, strerror(errno));
-           }
-       }
-       tapefd_close(tapedev);
-    }
+    dumpspec_list_free(dumpspecs);
 
-    if((bytes_read <= 0 || file.type == F_TAPEEND) && !isafile) {
-       fprintf(stderr, "%s: %3d: reached ", get_pname(), file_number);
-       if(bytes_read <= 0) {
-           fprintf(stderr, "end of information\n");
-       } else {
-           print_header(stderr,&file);
-       }
-       r = 1;
-    }
-    return r;
+    return 0;
 }