Imported Upstream version 2.6.0
[debian/amanda] / restore-src / restore.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: restore.c 6512 2007-05-24 17:00:24Z ian $
28  *
29  * retrieves files from an amanda tape
30  */
31
32 #include "amanda.h"
33 #include "util.h"
34 #include "restore.h"
35 #include "find.h"
36 #include "changer.h"
37 #include "logfile.h"
38 #include "fileheader.h"
39 #include "arglist.h"
40 #include "cmdline.h"
41 #include <signal.h>
42 #include <timestamp.h>
43
44 #include <device.h>
45 #include <queueing.h>
46 #include <glib.h>
47
48 typedef enum {
49     LOAD_NEXT = 1,     /* An unknown new slot has been loaded. */
50     LOAD_CHANGER = -2, /* The requested slot has been loaded. */
51     LOAD_STOP = -1,    /* The search is complete. */
52 } LoadStatus;
53
54 typedef enum {
55     RESTORE_STATUS_NEXT_FILE,
56     RESTORE_STATUS_NEXT_TAPE,
57     RESTORE_STATUS_STOP
58 } RestoreFileStatus;
59
60 int file_number;
61
62 /* stuff we're stuck having global */
63 static int backwards;
64 static int exitassemble = 0;
65
66 char *rst_conf_logdir = NULL;
67 char *rst_conf_logfile = NULL;
68 static char *curslot = NULL;
69
70 typedef struct open_output_s {
71     struct open_output_s *next;
72     dumpfile_t *file;
73     int lastpartnum;
74     pid_t comp_enc_pid;
75     int outfd;
76 } open_output_t;
77
78 typedef struct dumplist_s {
79     struct dumplist_s *next;
80     dumpfile_t *file;
81 } dumplist_t;
82
83 struct seentapes_s {
84     struct seentapes_s *next;
85     char *slotstr;
86     char *label;
87     dumplist_t *files;
88 };
89
90 static open_output_t *open_outputs = NULL;
91 static dumplist_t *alldumps_list = NULL;
92
93 /* local functions */
94
95 static void append_file_to_fd(char *filename, int fd);
96 static int headers_equal(dumpfile_t *file1, dumpfile_t *file2, int ignore_partnums);
97 static int already_have_dump(dumpfile_t *file);
98 static void handle_sigint(int sig);
99 static int scan_init(void *ud, int rc, int ns, int bk, int s);
100 static Device * conditional_device_open(char * tapedev, FILE * orompt_out,
101                                         rst_flags_t * flags,
102                                         am_feature_t * their_features,
103                                         tapelist_t * desired_tape);
104 int loadlabel_slot(void *ud, int rc, char *slotstr, char *device);
105 char *label_of_current_slot(char *cur_tapedev, FILE *prompt_out,
106                             int *tapefd, dumpfile_t *file, rst_flags_t *flags,
107                             am_feature_t *their_features,
108                             ssize_t *read_result, tapelist_t *desired_tape);
109
110 LoadStatus load_next_tape(char **cur_tapedev, FILE *prompt_out, int backwards,
111                    rst_flags_t *flags, am_feature_t *their_features,
112                    tapelist_t *desired_tape);
113 LoadStatus load_manual_tape(char **cur_tapedev, FILE *prompt_out, FILE *prompt_in,
114                      rst_flags_t *flags, am_feature_t *their_features,
115                      tapelist_t *desired_tape);
116
117 /*
118  * We might want to flush any open dumps and unmerged splits before exiting
119  * on SIGINT, so do so.
120  */
121 static void
122 handle_sigint(
123     int         sig)
124 {
125     (void)sig;  /* Quiet unused parameter warning */
126
127     flush_open_outputs(exitassemble, NULL);
128     if(rst_conf_logfile) unlink(rst_conf_logfile);
129     exit(0);
130 }
131
132 int
133 lock_logfile(void)
134 {
135     rst_conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
136     rst_conf_logfile = vstralloc(rst_conf_logdir, "/log", NULL);
137     if (access(rst_conf_logfile, F_OK) == 0) {
138         dbprintf(_("%s exists: amdump or amflush is already running, "
139                   "or you must run amcleanup\n"), rst_conf_logfile);
140         return 0;
141     }
142     log_add(L_INFO, get_pname());
143     return 1;
144 }
145
146 /*
147  * Return 1 if the two fileheaders match in name, disk, type, split chunk part
148  * number, and datestamp, and 0 if not.  The part number can be optionally
149  * ignored.
150  */
151 int
152 headers_equal(
153     dumpfile_t *file1,
154     dumpfile_t *file2,
155     int         ignore_partnums)
156 {
157     if(!file1 || !file2) return(0);
158     
159     if(file1->dumplevel == file2->dumplevel &&
160            file1->type == file2->type &&
161            !strcmp(file1->datestamp, file2->datestamp) &&
162            !strcmp(file1->name, file2->name) &&
163            !strcmp(file1->disk, file2->disk) &&
164            (ignore_partnums || file1->partnum == file2->partnum)){
165         return(1);
166     }
167     return(0);
168 }
169
170
171 /*
172  * See whether we're already pulled an exact copy of the given file (chunk
173  * number and all).  Returns 0 if not, 1 if so.
174  */
175 int
176 already_have_dump(
177     dumpfile_t *file)
178 {
179     dumplist_t *fileentry = NULL;
180
181     if(!file) return(0);
182     for(fileentry=alldumps_list;fileentry;fileentry=fileentry->next){
183         if(headers_equal(file, fileentry->file, 0)) return(1);
184     }
185     return(0);
186 }
187
188 /*
189  * Open the named file and append its contents to the (hopefully open) file
190  * descriptor supplies.
191  */
192 static void
193 append_file_to_fd(
194     char *      filename,
195     int         write_fd)
196 {
197     int read_fd;
198
199     read_fd = robust_open(filename, O_RDONLY, 0);
200     if (read_fd < 0) {
201         error(_("can't open %s: %s"), filename, strerror(errno));
202         /*NOTREACHED*/
203     }
204
205     if (!do_consumer_producer_queue(fd_read_producer, GINT_TO_POINTER(read_fd),
206                                     fd_write_consumer,
207                                     GINT_TO_POINTER(write_fd))) {
208         error("Error copying data from file \"%s\" to fd %d.\n",
209               filename, write_fd);
210         g_assert_not_reached();
211     }
212
213     aclose(read_fd);
214 }
215
216 /* A user_init function for changer_find(). See changer.h for
217    documentation. */
218 static int 
219 scan_init(G_GNUC_UNUSED void *  ud, int rc, G_GNUC_UNUSED int ns,
220           int bk, G_GNUC_UNUSED int s) {
221     if(rc) {
222         error(_("could not get changer info: %s"), changer_resultstr);
223         /*NOTREACHED*/
224     }
225     backwards = bk;
226
227     return 0;
228 }
229
230 typedef struct {
231     char ** cur_tapedev;
232     char * searchlabel;
233 } loadlabel_data;
234
235 /* DANGER WILL ROBINSON: This function references globals:
236           char * curslot;
237  */
238 int
239 loadlabel_slot(void *   datap,
240                int      rc,
241                char *   slotstr,
242                char *   device_name)
243 {
244     loadlabel_data * data = (loadlabel_data*)datap;
245     Device * device;
246     ReadLabelStatusFlags label_status;
247
248     g_return_val_if_fail(rc > 1 || device_name != NULL, 0);
249     g_return_val_if_fail(slotstr != NULL, 0);
250
251     amfree(curslot);
252
253     if(rc > 1) {
254         error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
255         g_assert_not_reached();
256     }
257
258     if(rc == 1) {
259         g_fprintf(stderr, _("%s: slot %s: %s\n"),
260                 get_pname(), slotstr, changer_resultstr);
261         return 0;
262     } 
263     
264     device = device_open(device_name);
265     if (device == NULL) {
266         g_fprintf(stderr, "%s: slot %s: Could not open device.\n",
267                 get_pname(), slotstr);
268         return 0;
269     }
270
271     device_set_startup_properties_from_config(device);
272     label_status = device_read_label(device);
273     if (label_status != READ_LABEL_STATUS_SUCCESS) {
274         char * errstr =
275             g_english_strjoinv_and_free
276                 (g_flags_nick_to_strv(label_status,
277                                       READ_LABEL_STATUS_FLAGS_TYPE), "or");
278         g_fprintf(stderr, "%s: slot %s: Error reading tape label:\n"
279                 "%s: slot %s: %s\n",
280                 get_pname(), slotstr, get_pname(), slotstr, errstr);
281         g_object_unref(device);
282         return 0;
283     }
284
285     g_assert(device->volume_label != NULL);
286     if (device->volume_label == NULL) {
287         g_fprintf(stderr, "%s: slot %s: Could not read tape label.\n",
288                 get_pname(), slotstr);
289         g_object_unref(device);
290         return 0;
291     }
292
293     if (!device_start(device, ACCESS_READ, NULL, NULL)) {
294         g_fprintf(stderr, "%s: slot %s: Could not open device for reading.\n",
295                 get_pname(), slotstr);
296         return 0;
297     }
298
299     g_fprintf(stderr, "%s: slot %s: time %-14s label %s",
300             get_pname(), slotstr, device->volume_time, device->volume_label);
301
302     if(strcmp(device->volume_label, data->searchlabel) != 0) {
303         g_fprintf(stderr, " (wrong tape)\n");
304         g_object_unref(device);
305         return 0;
306     }
307
308     g_fprintf(stderr, " (exact label match)\n");
309
310     g_object_unref(device);
311     curslot = newstralloc(curslot, slotstr);
312     amfree(*(data->cur_tapedev));
313     *(data->cur_tapedev) = stralloc(device_name);
314     return 1;
315 }
316
317
318 /* non-local functions follow */
319
320
321
322 /*
323  * Check whether we've read all of the preceding parts of a given split dump,
324  * generally used to see if we're done and can close the thing.
325  */
326 int
327 have_all_parts (
328     dumpfile_t *file,
329     int         upto)
330 {
331     int c;
332     int *foundparts = NULL;
333     dumplist_t *fileentry = NULL;
334
335     if(!file || file->partnum < 1) return(0);
336
337     if(upto < 1) upto = file->totalparts;
338
339     foundparts = alloc(SIZEOF(*foundparts) * upto); 
340     for(c = 0 ; c< upto; c++) foundparts[c] = 0;
341     
342     for(fileentry=alldumps_list;fileentry; fileentry=fileentry->next){
343         dumpfile_t *cur_file = fileentry->file;
344         if(headers_equal(file, cur_file, 1)){
345             if(cur_file->partnum > upto){
346                 amfree(foundparts);
347                 return(0);
348             }
349
350             foundparts[cur_file->partnum - 1] = 1;
351         }
352     }
353
354     for(c = 0 ; c< upto; c++){
355         if(!foundparts[c]){
356             amfree(foundparts);
357             return(0);
358         }
359     }
360     
361     amfree(foundparts);
362     return(1);
363 }
364
365 /*
366  * Free up the open filehandles and memory we were using to track in-progress
367  * dumpfiles (generally for split ones we're putting back together).  If
368  * applicable, also find the ones that are continuations of one another and
369  * string them together.  If given an optional file header argument, flush
370  * only that dump and do not flush/free any others.
371  */
372 void
373 flush_open_outputs(
374     int         reassemble,
375     dumpfile_t *only_file)
376 {
377     open_output_t *cur_out = NULL, *prev = NULL;
378     find_result_t *sorted_files = NULL;
379     amwait_t compress_status;
380
381     if(!only_file){
382         g_fprintf(stderr, "\n");
383     }
384
385     /*
386      * Deal with any split dumps we've been working on, appending pieces
387      * that haven't yet been appended and closing filehandles we've been
388      * holding onto.
389      */
390     if(reassemble){
391         find_result_t *cur_find_res = NULL;
392         int outfd = -1, lastpartnum = -1;
393         dumpfile_t *main_file = NULL;
394         cur_out = open_outputs;
395         
396         /* stick the dumpfile_t's into a list find_result_t's so that we can
397            abuse existing sort functionality */
398         for(cur_out=open_outputs; cur_out; cur_out=cur_out->next){
399             find_result_t *cur_find_res = NULL;
400             dumpfile_t *cur_file = cur_out->file;
401             /* if we requested a particular file, do only that one */
402             if(only_file && !headers_equal(cur_file, only_file, 1)){
403                 continue;
404             }
405             cur_find_res = alloc(SIZEOF(find_result_t));
406             memset(cur_find_res, '\0', SIZEOF(find_result_t));
407             cur_find_res->timestamp = stralloc(cur_file->datestamp);
408             cur_find_res->hostname = stralloc(cur_file->name);
409             cur_find_res->diskname = stralloc(cur_file->disk);
410             cur_find_res->level = cur_file->dumplevel;
411             if(cur_file->partnum < 1) cur_find_res->partnum = stralloc("--");
412             else{
413                 char part_str[NUM_STR_SIZE];
414                 g_snprintf(part_str, SIZEOF(part_str), "%d", cur_file->partnum);
415                 cur_find_res->partnum = stralloc(part_str);
416             }
417             cur_find_res->user_ptr = (void*)cur_out;
418
419             cur_find_res->next = sorted_files;
420             sorted_files = cur_find_res;
421         }
422         sort_find_result("hkdlp", &sorted_files);
423
424         /* now we have an in-order list of the files we need to concatenate */
425         cur_find_res = sorted_files;
426         for(cur_find_res=sorted_files;
427                 cur_find_res;
428                 cur_find_res=cur_find_res->next){
429             dumpfile_t *cur_file = NULL;
430             cur_out = (open_output_t*)cur_find_res->user_ptr;
431             cur_file = cur_out->file;
432
433             /* if we requested a particular file, do only that one */
434             if(only_file && !headers_equal(cur_file, only_file, 1)){
435                 continue;
436             }
437
438             if(cur_file->type == F_SPLIT_DUMPFILE) {
439                 /* is it a continuation of one we've been writing? */
440                 if(main_file && cur_file->partnum > lastpartnum &&
441                         headers_equal(cur_file, main_file, 1)){
442                     char *cur_filename;
443                     char *main_filename;
444
445                     /* effectively changing filehandles */
446                     aclose(cur_out->outfd);
447                     cur_out->outfd = outfd;
448
449                     cur_filename  = make_filename(cur_file);
450                     main_filename = make_filename(main_file);
451                     g_fprintf(stderr, _("Merging %s with %s\n"),
452                             cur_filename, main_filename);
453                     append_file_to_fd(cur_filename, outfd);
454                     if(unlink(cur_filename) < 0){
455                         g_fprintf(stderr, _("Failed to unlink %s: %s\n"),
456                                      cur_filename, strerror(errno));
457                     }
458                     amfree(cur_filename);
459                     amfree(main_filename);
460                 }
461                 /* or a new file? */
462                 else {
463                     if(outfd >= 0) aclose(outfd);
464                     amfree(main_file);
465                     main_file = alloc(SIZEOF(dumpfile_t));
466                     memcpy(main_file, cur_file, SIZEOF(dumpfile_t));
467                     outfd = cur_out->outfd;
468                     if(outfd < 0) {
469                         char *cur_filename = make_filename(cur_file);
470                         open(cur_filename, O_RDWR|O_APPEND);
471                         if (outfd < 0) {
472                           error(_("Couldn't open %s for appending: %s"),
473                                 cur_filename, strerror(errno));
474                           /*NOTREACHED*/
475                         }
476                         amfree(cur_filename);
477                     }
478                 }
479                 lastpartnum = cur_file->partnum;
480             }
481             else {
482                 aclose(cur_out->outfd);
483             }
484         }
485         if(outfd >= 0) {
486             aclose(outfd);
487         }
488
489         amfree(main_file);
490         free_find_result(&sorted_files);
491     }
492
493     /*
494      * Now that the split dump closure is done, free up resources we don't
495      * need anymore.
496      */
497     for(cur_out=open_outputs; cur_out; cur_out=cur_out->next){
498         dumpfile_t *cur_file = NULL;
499         amfree(prev);
500         cur_file = cur_out->file;
501         /* if we requested a particular file, do only that one */
502         if(only_file && !headers_equal(cur_file, only_file, 1)){
503             continue;
504         }
505         if(!reassemble) {
506             aclose(cur_out->outfd);
507         }
508
509         if(cur_out->comp_enc_pid > 0){
510             waitpid(cur_out->comp_enc_pid, &compress_status, 0);
511         }
512         amfree(cur_out->file);
513         prev = cur_out;
514     }
515
516     open_outputs = NULL;
517 }
518
519 /*
520  * Turn a fileheader into a string suited for use on the filesystem.
521  */
522 char *
523 make_filename(
524     dumpfile_t *file)
525 {
526     char number[NUM_STR_SIZE];
527     char part[NUM_STR_SIZE];
528     char totalparts[NUM_STR_SIZE];
529     char *sfn = NULL;
530     char *fn = NULL;
531     char *pad = NULL;
532     size_t padlen = 0;
533
534     g_snprintf(number, SIZEOF(number), "%d", file->dumplevel);
535     g_snprintf(part, SIZEOF(part), "%d", file->partnum);
536
537     if(file->totalparts < 0) {
538         g_snprintf(totalparts, SIZEOF(totalparts), "UNKNOWN");
539     }
540     else {
541         g_snprintf(totalparts, SIZEOF(totalparts), "%d", file->totalparts);
542     }
543     padlen = strlen(totalparts) + 1 - strlen(part);
544     pad = alloc(padlen);
545     memset(pad, '0', padlen);
546     pad[padlen - 1] = '\0';
547
548     g_snprintf(part, SIZEOF(part), "%s%d", pad, file->partnum);
549
550     sfn = sanitise_filename(file->disk);
551     fn = vstralloc(file->name,
552                    ".",
553                    sfn, 
554                    ".",
555                    file->datestamp,
556                    ".",
557                    number,
558                    NULL);
559     if (file->partnum > 0) {
560         vstrextend(&fn, ".", part, NULL);
561     }
562     amfree(sfn);
563     amfree(pad);
564     return fn;
565 }
566
567 /* Returns 1 if the dump file matches the hostname and diskname
568  * regular expressions given on the command line, 0 otherwise.  As a 
569  * special case, empty regexs and NULLs are considered equivalent to 
570  * ".*": they match everything.
571  *
572  * @param file: the file to examine
573  * @param datestamp: the datestamp regex, or NULL for any
574  * @param hostname: the hostname regex, or NULL for any
575  * @param diskname: the diskname regex, or NULL for any
576  * @param level: the level regex, or NULL for any
577  * @returns: 1 if the dump file matches
578  */
579 static int
580 disk_match(
581     dumpfile_t *file,
582     char *      datestamp,
583     char *      hostname,
584     char *      diskname,
585     char *      level)
586 {
587     char level_str[NUM_STR_SIZE];
588     g_snprintf(level_str, SIZEOF(level_str), "%d", file->dumplevel);
589
590     if(file->type != F_DUMPFILE && file->type != F_SPLIT_DUMPFILE) return 0;
591
592     if((!hostname || *hostname == '\0' || match_host(hostname, file->name)) &&
593        (!diskname || *diskname == '\0' || match_disk(diskname, file->disk)) &&
594        (!datestamp || *datestamp == '\0' || match_datestamp(datestamp, file->datestamp)) &&
595        (!level || *level == '\0' || match_level(level, level_str)))
596         return 1;
597     else
598         return 0;
599 }
600
601 /*
602  * Reads the first block of a holding disk file.
603  */
604
605 static gboolean
606 read_holding_disk_header(
607     dumpfile_t *       file,
608     int                        tapefd,
609     rst_flags_t *      flags)
610 {
611     ssize_t bytes_read;
612     char *buffer;
613     size_t blocksize;
614
615     if(flags->blocksize > 0)
616         blocksize = (size_t)flags->blocksize;
617     else
618         blocksize = DISK_BLOCK_BYTES;
619     buffer = alloc(blocksize);
620
621     bytes_read = fullread(tapefd, buffer, blocksize);
622     if(bytes_read < 0) {
623         g_fprintf(stderr, _("%s: error reading file header: %s\n"),
624                 get_pname(), strerror(errno));
625         file->type = F_UNKNOWN;
626     } else if((size_t)bytes_read < DISK_BLOCK_BYTES) {
627         if(bytes_read == 0) {
628             g_fprintf(stderr, _("%s: missing file header block\n"), get_pname());
629         } else {
630             g_fprintf(stderr,
631                     plural(_("%s: short file header block: %zd byte"),
632                            _("%s: short file header block: %zd bytes\n"),
633                            bytes_read),
634                     get_pname(), (size_t)bytes_read);
635         }
636         file->type = F_UNKNOWN;
637     } else {
638         parse_file_header(buffer, file, (size_t)bytes_read);
639     }
640     amfree(buffer);
641     return (file->type != F_UNKNOWN &&
642             file->type != F_EMPTY &&
643             file->type != F_WEIRD);
644 }
645
646 /*
647  * Restore the current file from tape.  Depending on the settings of
648  * the command line flags, the file might need to be compressed or
649  * uncompressed.  If so, a pipe through compress or uncompress is set
650  * up.  The final output usually goes to a file named host.disk.date.lev,
651  * but with the -p flag the output goes to stdout (and presumably is
652  * piped to restore).
653  */
654
655
656 /* FIXME: Mondo function that needs refactoring. */
657 void restore(RestoreSource * source,
658              rst_flags_t *      flags)
659 {
660     int dest = -1, out;
661     int file_is_compressed;
662     int is_continuation = 0;
663     int check_for_aborted = 0;
664     char *tmp_filename = NULL, *final_filename = NULL;
665     struct stat statinfo;
666     open_output_t *myout = NULL, *oldout = NULL;
667     dumplist_t *tempdump = NULL, *fileentry = NULL;
668     char *buffer;
669     int need_compress=0, need_uncompress=0, need_decrypt=0;
670     int stage=0;
671     struct pipeline {
672         int     pipe[2];
673     } pipes[3];
674     char * filename;
675
676     filename = make_filename(source->header);
677
678     memset(pipes, -1, SIZEOF(pipes));
679
680     if(already_have_dump(source->header)){
681         g_fprintf(stderr, _(" *** Duplicate file %s, one is probably an aborted write\n"), filename);
682         check_for_aborted = 1;
683     }
684
685     /* store a shorthand record of this dump */
686     tempdump = malloc(SIZEOF(dumplist_t));
687     tempdump->file = malloc(SIZEOF(dumpfile_t));
688     tempdump->next = NULL;
689     memcpy(tempdump->file, source->header, SIZEOF(dumpfile_t));
690
691     /*
692      * If we're appending chunked files to one another, and if this is a
693      * continuation of a file we just restored, and we've still got the
694      * output handle from that previous restore, we're golden.  Phew.
695      */
696     if(flags->inline_assemble && source->header->type == F_SPLIT_DUMPFILE){
697         myout = open_outputs;
698         while(myout != NULL){
699             if(myout->file->type == F_SPLIT_DUMPFILE &&
700                headers_equal(source->header, myout->file, 1)){
701                 if(source->header->partnum == myout->lastpartnum + 1){
702                     is_continuation = 1;
703                     break;
704                 }
705             }
706             myout = myout->next;
707         }
708         if(myout != NULL) myout->lastpartnum = source->header->partnum;
709         else if(source->header->partnum != 1){
710             g_fprintf(stderr, _("%s:      Chunk out of order, will save to disk and append to output.\n"), get_pname());
711             flags->pipe_to_fd = -1;
712             flags->compress = 0;
713             flags->leave_comp = 1;
714         }
715         if(myout == NULL){
716             myout = alloc(SIZEOF(open_output_t));
717             memset(myout, 0, SIZEOF(open_output_t));
718         }
719     }
720     else{
721       myout = alloc(SIZEOF(open_output_t));
722       memset(myout, 0, SIZEOF(open_output_t));
723     }
724
725
726     if(is_continuation && flags->pipe_to_fd == -1){
727         char *filename;
728         filename = make_filename(myout->file);
729         g_fprintf(stderr, _("%s:      appending to %s\n"), get_pname(),
730                 filename);
731         amfree(filename);
732     }
733
734     /* adjust compression flag */
735     file_is_compressed = source->header->compressed;
736     if(!flags->compress && file_is_compressed &&
737        !known_compress_type(source->header)) {
738         g_fprintf(stderr, 
739                 _("%s: unknown compression suffix %s, can't uncompress\n"),
740                 get_pname(), source->header->comp_suffix);
741         flags->compress = 1;
742     }
743
744     /* set up final destination file */
745
746     if(is_continuation && myout != NULL) {
747       out = myout->outfd;
748     } else {
749       if(flags->pipe_to_fd != -1) {
750           dest = flags->pipe_to_fd;
751       } else {
752           char *filename_ext = NULL;
753   
754           if(flags->compress) {
755               filename_ext = file_is_compressed ? source->header->comp_suffix
756                                               : COMPRESS_SUFFIX;
757           } else if(flags->raw) {
758               filename_ext = ".RAW";
759           } else {
760               filename_ext = "";
761           }
762           filename_ext = stralloc2(filename, filename_ext);
763           tmp_filename = stralloc(filename_ext); 
764           if(flags->restore_dir != NULL) {
765               char *tmpstr = vstralloc(flags->restore_dir, "/",
766                                        tmp_filename, NULL);
767               amfree(tmp_filename);
768               tmp_filename = tmpstr;
769           } 
770           final_filename = tmp_filename; 
771           tmp_filename = vstralloc(final_filename, ".tmp", NULL);
772           if((dest = open(tmp_filename, (O_CREAT | O_RDWR | O_TRUNC),
773                           CREAT_MODE)) < 0) {
774               error(_("could not create output file %s: %s"),
775                     tmp_filename, strerror(errno));
776               /*NOTREACHED*/
777           }
778           amfree(filename_ext);
779       }
780   
781       out = dest;
782     }
783
784     /*
785      * If -r or -h, write the header before compress or uncompress pipe.
786      * Only write DISK_BLOCK_BYTES, regardless of how much was read.
787      * This makes the output look like a holding disk image, and also
788      * makes it easier to remove the header (e.g. in amrecover) since
789      * it has a fixed size.
790      */
791     if(flags->raw || (flags->headers && !is_continuation)) {
792         ssize_t w;
793         dumpfile_t tmp_hdr;
794
795         if(flags->compress && !file_is_compressed) {
796             source->header->compressed = 1;
797             g_snprintf(source->header->uncompress_cmd,
798                      SIZEOF(source->header->uncompress_cmd),
799                         " %s %s |", UNCOMPRESS_PATH,
800 #ifdef UNCOMPRESS_OPT
801                         UNCOMPRESS_OPT
802 #else
803                         ""
804 #endif
805                         );
806             strncpy(source->header->comp_suffix,
807                     COMPRESS_SUFFIX,
808                     SIZEOF(source->header->comp_suffix)-1);
809             source->header->comp_suffix[SIZEOF(source->header->comp_suffix)-1]
810                 = '\0';
811         }
812
813         memcpy(&tmp_hdr, source->header, SIZEOF(dumpfile_t));
814
815         /* remove CONT_FILENAME from header */
816         memset(source->header->cont_filename, '\0',
817                SIZEOF(source->header->cont_filename));
818         source->header->blocksize = DISK_BLOCK_BYTES;
819
820         /*
821          * Dumb down split file headers as well, so that older versions of
822          * things like amrecover won't gag on them.
823          */
824         if(source->header->type == F_SPLIT_DUMPFILE && flags->mask_splits){
825             source->header->type = F_DUMPFILE;
826         }
827
828         buffer = alloc(DISK_BLOCK_BYTES);
829         buffer = build_header(source->header, DISK_BLOCK_BYTES);
830
831         if((w = fullwrite(out, buffer,
832                           DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
833             if(w < 0) {
834                 error(_("write error: %s"), strerror(errno));
835                 /*NOTREACHED*/
836             } else {
837                 error(_("write error: %zd instead of %d"), w, DISK_BLOCK_BYTES);
838                 /*NOTREACHED*/
839             }
840         }
841         amfree(buffer);
842         memcpy(source->header, &tmp_hdr, SIZEOF(dumpfile_t));
843     }
844  
845     /* find out if compression or uncompression is needed here */
846     if(flags->compress && !file_is_compressed && !is_continuation
847           && !flags->leave_comp
848           && (flags->inline_assemble ||
849               source->header->type != F_SPLIT_DUMPFILE))
850        need_compress=1;
851        
852     if(!flags->raw && !flags->compress && file_is_compressed
853           && !is_continuation && !flags->leave_comp && (flags->inline_assemble
854           || source->header->type != F_SPLIT_DUMPFILE))
855        need_uncompress=1;   
856
857     if(!flags->raw && source->header->encrypted && !is_continuation &&
858        (flags->inline_assemble || source->header->type != F_SPLIT_DUMPFILE)) {
859        need_decrypt=1;
860     }
861    
862     /* Setup pipes for decryption / compression / uncompression  */
863     stage = 0;
864     if (need_decrypt) {
865       if (pipe(&pipes[stage].pipe[0]) < 0) {
866         error(_("error [pipe[%d]: %s]"), stage, strerror(errno));
867         /*NOTREACHED*/
868       }
869       stage++;
870     }
871
872     if (need_compress || need_uncompress) {
873       if (pipe(&pipes[stage].pipe[0]) < 0) {
874         error(_("error [pipe[%d]: %s]"), stage, strerror(errno));
875         /*NOTREACHED*/
876       }
877       stage++;
878     }
879     pipes[stage].pipe[0] = -1; 
880     pipes[stage].pipe[1] = out; 
881
882     stage = 0;
883
884     /* decrypt first if it's encrypted and no -r */
885     if(need_decrypt) {
886       switch(myout->comp_enc_pid = fork()) {
887       case -1:
888         error(_("could not fork for decrypt: %s"), strerror(errno));
889         /*NOTREACHED*/
890
891       default:
892         aclose(pipes[stage].pipe[0]);
893         aclose(pipes[stage+1].pipe[1]);
894         stage++;
895         break;
896
897       case 0:
898         if(dup2(pipes[stage].pipe[0], 0) == -1) {
899             error(_("error decrypt stdin [dup2 %d %d: %s]"), stage,
900                 pipes[stage].pipe[0], strerror(errno));
901                 /*NOTREACHED*/
902         }
903
904         if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
905             error(_("error decrypt stdout [dup2 %d %d: %s]"), stage + 1,
906                 pipes[stage+1].pipe[1], strerror(errno));
907                 /*NOTREACHED*/
908         }
909
910         safe_fd(-1, 0);
911         if (source->header->srv_encrypt[0] != '\0') {
912           (void) execlp(source->header->srv_encrypt,
913                         source->header->srv_encrypt,
914                         source->header->srv_decrypt_opt, NULL);
915           error("could not exec %s: %s",
916                 source->header->srv_encrypt, strerror(errno));
917           g_assert_not_reached();
918         }  else if (source->header->clnt_encrypt[0] != '\0') {
919           (void) execlp(source->header->clnt_encrypt,
920                         source->header->clnt_encrypt,
921                         source->header->clnt_decrypt_opt, NULL);
922           error("could not exec %s: %s",
923                 source->header->clnt_encrypt, strerror(errno));
924           g_assert_not_reached();
925         }
926       }
927     }
928
929     if (need_compress) {
930         /*
931          * Insert a compress pipe
932          */
933         switch(myout->comp_enc_pid = fork()) {
934         case -1:
935             error(_("could not fork for %s: %s"), COMPRESS_PATH, strerror(errno));
936             /*NOTREACHED*/
937
938         default:
939             aclose(pipes[stage].pipe[0]);
940             aclose(pipes[stage+1].pipe[1]);
941             stage++;
942             break;
943
944         case 0:
945             if(dup2(pipes[stage].pipe[0], 0) == -1) {
946                 error(_("error compress stdin [dup2 %d %d: %s]"), stage,
947                   pipes[stage].pipe[0], strerror(errno));
948                 /*NOTREACHED*/
949             }
950
951             if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
952                 error(_("error compress stdout [dup2 %d %d: %s]"), stage + 1,
953                   pipes[stage+1].pipe[1], strerror(errno));
954                   /*NOTREACHED*/
955             }
956             if (*flags->comp_type == '\0') {
957                 flags->comp_type = NULL;
958             }
959
960             safe_fd(-1, 0);
961             (void) execlp(COMPRESS_PATH, COMPRESS_PATH, flags->comp_type, (char *)0);
962             error(_("could not exec %s: %s"), COMPRESS_PATH, strerror(errno));
963             /*NOTREACHED*/
964         }
965     } else if(need_uncompress) {
966         /*
967          * If not -r, -c, -l, and file is compressed, and split reassembly 
968          * options are sane, insert uncompress pipe
969          */
970
971         /* 
972          * XXX for now we know that for the two compression types we
973          * understand, .Z and optionally .gz, UNCOMPRESS_PATH will take
974          * care of both.  Later, we may need to reference a table of
975          * possible uncompress programs.
976          */ 
977         switch(myout->comp_enc_pid = fork()) {
978         case -1: 
979             error(_("could not fork for %s: %s"),
980                   UNCOMPRESS_PATH, strerror(errno));
981             /*NOTREACHED*/
982
983         default:
984             aclose(pipes[stage].pipe[0]);
985             aclose(pipes[stage+1].pipe[1]);
986             stage++;
987             break;
988
989         case 0:
990             if(dup2(pipes[stage].pipe[0], 0) == -1) {
991                 error(_("error uncompress stdin [dup2 %d %d: %s]"), stage,
992                   pipes[stage].pipe[0], strerror(errno));
993                 /*NOTREACHED*/
994             }
995
996             if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
997                 error(_("error uncompress stdout [dup2 %d %d: %s]"), stage + 1,
998                   pipes[stage+1].pipe[1], strerror(errno));
999                 /*NOTREACHED*/
1000             }
1001
1002             safe_fd(-1, 0);
1003             if (source->header->srvcompprog[0] != '\0') {
1004               (void) execlp(source->header->srvcompprog,
1005                             source->header->srvcompprog, "-d", NULL);
1006               error("could not exec %s: %s", source->header->srvcompprog,
1007                     strerror(errno));
1008               g_assert_not_reached();
1009             } else if (source->header->clntcompprog[0] != '\0') {
1010               (void) execlp(source->header->clntcompprog,
1011                             source->header->clntcompprog, "-d", NULL);
1012               error("could not exec %s: %s", source->header->clntcompprog,
1013                     strerror(errno));
1014               g_assert_not_reached();
1015             } else {
1016               (void) execlp(UNCOMPRESS_PATH, UNCOMPRESS_PATH,
1017 #ifdef UNCOMPRESS_OPT
1018                           UNCOMPRESS_OPT,
1019 #endif
1020                           (char *)NULL);
1021               error(_("could not exec %s: %s"), UNCOMPRESS_PATH, strerror(errno));
1022               /*NOTREACHED*/
1023             }
1024         }
1025     }
1026
1027     /* copy the rest of the file from tape to the output */
1028     if (source->restore_mode == HOLDING_MODE) {
1029         dumpfile_t file;
1030         int fd = source->u.holding_fd;
1031         memcpy(& file, source->header, sizeof(file));
1032         for (;;) {
1033             do_consumer_producer_queue(fd_read_producer,
1034                                        GINT_TO_POINTER(fd),
1035                                        fd_write_consumer,
1036                                        GINT_TO_POINTER(pipes[0].pipe[1]));
1037             /*
1038              * See if we need to switch to the next file in a holding restore
1039              */
1040             if(file.cont_filename[0] == '\0') {
1041                 break;                          /* no more files */
1042             }
1043             aclose(fd);
1044             if((fd = open(file.cont_filename, O_RDONLY)) == -1) {
1045                 char *cont_filename =
1046                     strrchr(file.cont_filename,'/');
1047                 if(cont_filename) {
1048                     cont_filename++;
1049                     if((fd = open(cont_filename,O_RDONLY)) == -1) {
1050                         error(_("can't open %s: %s"), file.cont_filename,
1051                               strerror(errno));
1052                         /*NOTREACHED*/
1053                     }
1054                     else {
1055                         g_fprintf(stderr, _("cannot open %s: %s\n"),
1056                                 file.cont_filename, strerror(errno));
1057                         g_fprintf(stderr, _("using %s\n"),
1058                                 cont_filename);
1059                     }
1060                 }
1061                 else {
1062                     error(_("can't open %s: %s"), file.cont_filename,
1063                           strerror(errno));
1064                     /*NOTREACHED*/
1065                 }
1066             }
1067             read_holding_disk_header(&file, fd, flags);
1068             if(file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE
1069                     && file.type != F_SPLIT_DUMPFILE) {
1070                 g_fprintf(stderr, _("unexpected header type: "));
1071                 print_header(stderr, source->header);
1072                 exit(2);
1073             }
1074         }            
1075     } else {
1076         device_read_to_fd(source->u.device, pipes[0].pipe[1]);
1077     }
1078
1079     if(!flags->inline_assemble) {
1080         if(out != dest)
1081             aclose(out);
1082     }
1083     if(!is_continuation){
1084         if(tmp_filename && stat(tmp_filename, &statinfo) < 0){
1085             error(_("Can't stat the file I just created (%s)!"), tmp_filename);
1086             /*NOTREACHED*/
1087         } else {
1088             statinfo.st_size = (off_t)0;
1089         }
1090         if (check_for_aborted && final_filename) {
1091             char *old_dump = final_filename;
1092             struct stat oldstat;
1093             if(stat(old_dump, &oldstat) >= 0){
1094                 if(oldstat.st_size <= statinfo.st_size){
1095                     dumplist_t *prev_fileentry = NULL;
1096                     open_output_t *prev_out = NULL;
1097                     g_fprintf(stderr, _("Newer restore is larger, using that\n"));
1098                     /* nuke the old dump's entry in alldump_list */
1099                     for(fileentry=alldumps_list;
1100                             fileentry->next;
1101                             fileentry=fileentry->next){
1102                         if(headers_equal(source->header,
1103                                          fileentry->file, 0)){
1104                             if(prev_fileentry){
1105                                 prev_fileentry->next = fileentry->next;
1106                             }
1107                             else {
1108                                 alldumps_list = fileentry->next;
1109                             }
1110                             amfree(fileentry);
1111                             break;
1112                         }
1113                         prev_fileentry = fileentry;
1114                     }
1115                     myout = open_outputs;
1116                     while(myout != NULL){
1117                         if(headers_equal(source->header, myout->file, 0)){
1118                             if(myout->outfd >= 0)
1119                                 aclose(myout->outfd);
1120                             if(prev_out){
1121                                 prev_out->next = myout->next;
1122                             }
1123                             else open_outputs = myout->next;
1124                             amfree(myout);
1125                             break;
1126                         }
1127                         prev_out = myout;
1128                         myout = myout->next;
1129                     }
1130                 }
1131                 else{
1132                     g_fprintf(stderr, _("Older restore is larger, using that\n"));
1133                     if (tmp_filename)
1134                         unlink(tmp_filename);
1135                     amfree(tempdump->file);
1136                     amfree(tempdump);
1137                     amfree(tmp_filename);
1138                     amfree(final_filename);
1139                     amfree(filename);
1140                     return;
1141                 }
1142             }
1143         }
1144         if(tmp_filename && final_filename &&
1145            rename(tmp_filename, final_filename) < 0) {
1146             error(_("Can't rename %s to %s: %s"),
1147                    tmp_filename, final_filename, strerror(errno));
1148             /*NOTREACHED*/
1149         }
1150     }
1151     amfree(tmp_filename);
1152     amfree(final_filename);
1153
1154
1155     /*
1156      * actually insert tracking data for this file into our various
1157      * structures (we waited in case we needed to give up)
1158      */
1159     if(!is_continuation){
1160         oldout = alloc(SIZEOF(open_output_t));
1161         oldout->file = alloc(SIZEOF(dumpfile_t));
1162         memcpy(oldout->file, source->header, SIZEOF(dumpfile_t));
1163         if(flags->inline_assemble) oldout->outfd = pipes[0].pipe[1];
1164         else oldout->outfd = -1;
1165         oldout->comp_enc_pid = -1;
1166         oldout->lastpartnum = source->header->partnum;
1167         oldout->next = open_outputs;
1168         open_outputs = oldout;
1169     }
1170     if(alldumps_list){
1171         fileentry = alldumps_list;
1172         while (fileentry->next != NULL)
1173             fileentry=fileentry->next;
1174         fileentry->next = tempdump;
1175     }
1176     else {
1177         alldumps_list = tempdump;
1178     }
1179 }
1180
1181 /* return NULL if the label is not the expected one                     */
1182 /* returns a Device handle if it is the expected one. */
1183 /* FIXME: Was label_of_current_slot */
1184 static Device *
1185 conditional_device_open(char         *tapedev,
1186                         FILE         *prompt_out,
1187                         rst_flags_t  *flags,
1188                         am_feature_t *their_features,
1189                         tapelist_t   *desired_tape)
1190 {
1191     Device * rval;
1192
1193     if (tapedev == NULL) {
1194         send_message(prompt_out, flags, their_features,
1195                      _("Volume labeled '%s' not found."), desired_tape->label);
1196         return NULL;
1197     }
1198
1199     rval = device_open(tapedev);
1200     if (rval == NULL) {
1201         send_message(prompt_out, flags, their_features, 
1202                      "Error opening device '%s'.",
1203                      tapedev);
1204         return NULL;
1205     }
1206
1207     device_set_startup_properties_from_config(rval);
1208     device_read_label(rval);
1209
1210     if (rval->volume_label == NULL) {
1211         send_message(prompt_out, flags, their_features,
1212                      "Not an amanda tape");
1213         g_object_unref(rval);
1214         return NULL;
1215     }
1216
1217     if (!device_start(rval, ACCESS_READ, NULL, NULL)) {
1218         send_message(prompt_out, flags, their_features,
1219                      "Colud not open device %s for reading.\n",
1220                      tapedev);
1221         return NULL;
1222     }
1223
1224     if (flags->check_labels && desired_tape &&
1225         strcmp(rval->volume_label, desired_tape->label) != 0) {
1226         send_message(prompt_out, flags, their_features,
1227                      "Label mismatch, got %s and expected %s",
1228                      rval->volume_label, desired_tape->label);
1229         g_object_unref(rval);
1230         return NULL;
1231     }
1232
1233     return rval;
1234 }
1235
1236 /* Do the right thing to try and load the next required tape. See
1237    LoadStatus above for return value meaning. */
1238 LoadStatus
1239 load_next_tape(
1240     char         **cur_tapedev,
1241     FILE          *prompt_out,
1242     int            backwards,
1243     rst_flags_t   *flags,
1244     am_feature_t  *their_features,
1245     tapelist_t    *desired_tape)
1246 {
1247     if (desired_tape) {
1248         send_message(prompt_out, flags, their_features,
1249                      _("Looking for tape %s..."),
1250                      desired_tape->label);
1251         if (backwards) {
1252             loadlabel_data data;
1253             data.cur_tapedev = cur_tapedev;
1254             data.searchlabel = desired_tape->label;
1255             changer_find(&data, scan_init, loadlabel_slot,
1256                          desired_tape->label);
1257             return LOAD_CHANGER;
1258         } else {
1259             amfree(curslot);
1260             changer_loadslot("next", &curslot,
1261                              cur_tapedev);
1262             return LOAD_NEXT;
1263         }
1264     } else {
1265         assert(!flags->amidxtaped);
1266         amfree(curslot);
1267         changer_loadslot("next", &curslot, cur_tapedev);
1268         return LOAD_NEXT;
1269     }
1270
1271     g_assert_not_reached();
1272 }
1273
1274
1275 /* will never return LOAD_CHANGER. */
1276 LoadStatus
1277 load_manual_tape(
1278     char         **tapedev_ptr,
1279     FILE          *prompt_out,
1280     FILE          *prompt_in,
1281     rst_flags_t   *flags,
1282     am_feature_t  *their_features,
1283     tapelist_t    *desired_tape)
1284 {
1285     char *input = NULL;
1286
1287     if (flags->amidxtaped) {
1288         if (their_features &&
1289             am_has_feature(their_features,
1290                            fe_amrecover_FEEDME)) {
1291             g_fprintf(prompt_out, "FEEDME %s\r\n",
1292                     desired_tape->label);
1293             fflush(prompt_out);
1294             input = agets(prompt_in);/* Strips \n but not \r */
1295             if(!input) {
1296                 error(_("Connection lost with amrecover"));
1297                 /*NOTREACHED*/
1298             } else if (strcmp("OK\r", input) == 0) {
1299             } else if (strncmp("TAPE ", input, 5) == 0) {
1300                 amfree(*tapedev_ptr);
1301                 *tapedev_ptr = alloc(1025);
1302                 if (sscanf(input, "TAPE %1024s\r", *tapedev_ptr) != 1) {
1303                     error(_("Got bad response from amrecover: %s"), input);
1304                     /*NOTREACHED*/
1305                 }
1306             } else {
1307                 send_message(prompt_out, flags, their_features,
1308                              _("Got bad response from amrecover: %s"), input);
1309                 error(_("Got bad response from amrecover: %s"), input);
1310                 /*NOTREACHED*/
1311             }
1312         } else {
1313             send_message(prompt_out, flags, their_features,
1314                          _("Client doesn't support fe_amrecover_FEEDME"));
1315             error(_("Client doesn't support fe_amrecover_FEEDME"));
1316             /*NOTREACHED*/
1317         }
1318     }
1319     else {
1320         if (desired_tape) {
1321             g_fprintf(prompt_out,
1322                     _("Insert tape labeled %s in device %s \n"
1323                     "and press enter, ^D to finish reading tapes\n"),
1324                     desired_tape->label, *tapedev_ptr);
1325         } else {
1326             g_fprintf(prompt_out,_("Insert a tape to search and press "
1327                     "enter, ^D to finish reading tapes\n"));
1328         }
1329         fflush(prompt_out);
1330         if((input = agets(prompt_in)) == NULL)
1331             return LOAD_STOP;
1332     }
1333
1334     amfree(input);
1335     return LOAD_NEXT;
1336 }
1337
1338 /* Search a seen-tapes list for a particular name, to see if we've already
1339  * processed this tape. Returns TRUE if this label has already been seen. */
1340 static gboolean check_volume_seen(seentapes_t * list, char * label) {
1341     seentapes_t * cur_tape;
1342     for (cur_tape = list; cur_tape != NULL; cur_tape = cur_tape->next) {
1343         if (strcmp(cur_tape->label, label) == 0) {
1344             return TRUE;
1345         }
1346     }
1347     return FALSE;
1348 }
1349
1350 /* Add a volume to the seen tapes list. */
1351 static void record_seen_volume(seentapes_t ** list, char * label,
1352                                char * slotstr) {
1353     seentapes_t * new_entry;
1354
1355     if (list == NULL)
1356         return;
1357
1358     new_entry = malloc(sizeof(seentapes_t));
1359     new_entry->label = stralloc(label);
1360     if (slotstr == NULL) {
1361         new_entry->slotstr = NULL;
1362     } else {
1363         new_entry->slotstr = stralloc(slotstr);
1364     }
1365     new_entry->files = NULL;
1366     new_entry->next = *list;
1367     *list = new_entry;
1368 }
1369
1370 /* Record a specific dump on a volume. */
1371 static void record_seen_dump(seentapes_t * volume, dumpfile_t * header) {
1372     dumplist_t * this_dump;
1373
1374     if (volume == NULL)
1375         return;
1376     
1377     this_dump = malloc(sizeof(*this_dump));
1378     this_dump->file = g_memdup(header, sizeof(*header));
1379     this_dump->next = NULL;
1380     if (volume->files) {
1381         dumplist_t * tmp_dump = volume->files;
1382         while (tmp_dump->next != NULL) {
1383             tmp_dump = tmp_dump->next;
1384         }
1385         tmp_dump->next = this_dump;
1386     } else {
1387         volume->files = this_dump;
1388     }
1389 }
1390
1391 static void print_tape_inventory(FILE * logstream, seentapes_t * tape_seen,
1392                                  char * timestamp, char * label,
1393                                  int tape_count) {
1394     char * logline;
1395     dumplist_t * fileentry;
1396
1397     logline = log_genstring(L_START, "taper",
1398                             "datestamp %s label %s tape %d",
1399                             timestamp, label, tape_count);
1400     fputs(logline, logstream);
1401     amfree(logline);
1402     for(fileentry=tape_seen->files; fileentry; fileentry=fileentry->next){
1403         switch (fileentry->file->type) {
1404         case F_DUMPFILE:
1405             logline = log_genstring(L_SUCCESS, "taper",
1406                                     "%s %s %s %d [faked log entry]",
1407                                     fileentry->file->name,
1408                                     fileentry->file->disk,
1409                                     fileentry->file->datestamp,
1410                                     fileentry->file->dumplevel);
1411             break;
1412         case F_SPLIT_DUMPFILE:
1413             logline = log_genstring(L_CHUNK, "taper", 
1414                                     "%s %s %s %d %d [faked log entry]",
1415                                     fileentry->file->name,
1416                                     fileentry->file->disk,
1417                                     fileentry->file->datestamp,
1418                                     fileentry->file->partnum,
1419                                     fileentry->file->dumplevel);
1420             break;
1421         default:
1422             break;
1423         }
1424         if(logline != NULL){
1425             fputs(logline, logstream);
1426             amfree(logline);
1427             fflush(logstream);
1428         }
1429     }
1430 }
1431
1432 /* Check if the given header matches the given dumpspecs. Returns
1433    TRUE if dumpspecs is NULL and false if the header is NULL. Returns
1434    true if the header matches the  match list. */
1435 static gboolean run_dumpspecs(GSList * dumpspecs,
1436                                dumpfile_t * header) {
1437     dumpspec_t *ds;
1438
1439     if (dumpspecs == NULL)
1440         return TRUE;
1441     if (header == NULL)
1442         return FALSE;
1443
1444     while (dumpspecs) {
1445         ds = (dumpspec_t *)dumpspecs->data;
1446         if (disk_match(header, ds->datestamp, ds->host,
1447                        ds->disk, ds->level) != 0) {
1448             return TRUE;
1449         }
1450         dumpspecs = dumpspecs->next;
1451     }
1452
1453     return FALSE;
1454 }
1455
1456 /* A wrapper around restore() above. This function does some extra
1457    checking to seek to the file in question and ensure that we really,
1458    really want to use it.
1459
1460    The next_file argument provides instruction on what to do if the
1461    requested file does not exist on the volume: If next_file is NULL
1462    then if the requested file is missing the function will return
1463    RESTORE_STATUS_NEXT_FILE. If next_file is not NULL then the first
1464    extant file whose number is equal to or greater than file_num will
1465    be attempted. *next_file will be filled in with the number of the
1466    file following the one that was attempted. */
1467 static RestoreFileStatus
1468 try_restore_single_file(Device * device, int file_num, int* next_file,
1469                         FILE * prompt_out,
1470                         rst_flags_t * flags,
1471                         am_feature_t * their_features,
1472                         dumpfile_t * first_restored_file,
1473                         GSList * dumpspecs,
1474                         seentapes_t * tape_seen) {
1475     RestoreSource source;
1476     source.u.device = device;
1477     source.restore_mode = DEVICE_MODE;
1478
1479     source.header = device_seek_file(device, file_num);
1480
1481     if (source.header == NULL) {
1482         /* This definitely indicates an error. */
1483         send_message(prompt_out, flags, their_features,
1484                      "Could not seek device %s to file %d.",
1485                      device->device_name, file_num);
1486         return RESTORE_STATUS_NEXT_TAPE;
1487     } else if (source.header->type == F_TAPEEND) {
1488         amfree(source.header);
1489         return RESTORE_STATUS_NEXT_TAPE;
1490     } else if (device->file != file_num) {
1491         if (next_file == NULL) {
1492             send_message(prompt_out, flags, their_features,
1493                          "Requested file %d does not exist.",
1494                          file_num);
1495             return RESTORE_STATUS_NEXT_FILE;
1496         } else {
1497             send_message(prompt_out, flags, their_features,
1498                          "Skipped from file %d to file %d.", 
1499                          file_num, device->file);
1500             file_num = device->file;
1501         }
1502     }
1503
1504     if (next_file != NULL) {
1505         *next_file = file_num + 1;
1506     }
1507     
1508     g_return_val_if_fail(source.header->type == F_DUMPFILE ||
1509                          source.header->type == F_CONT_DUMPFILE ||
1510                          source.header->type == F_SPLIT_DUMPFILE,
1511                          RESTORE_STATUS_NEXT_FILE);
1512     
1513
1514     if (!run_dumpspecs(dumpspecs, source.header)) {
1515         if(!flags->amidxtaped) {
1516             g_fprintf(prompt_out, "%s: %d: skipping ",
1517                     get_pname(), file_num);
1518             print_header(prompt_out, source.header);
1519         }
1520         return RESTORE_STATUS_NEXT_FILE;
1521     }
1522
1523     if (first_restored_file != NULL &&
1524         first_restored_file->type != F_UNKNOWN &&
1525         !headers_equal(first_restored_file, source.header, 1) &&
1526         (flags->pipe_to_fd == fileno(stdout))) {
1527         return RESTORE_STATUS_STOP;
1528     }
1529
1530     if (!flags->amidxtaped) {
1531         g_fprintf(stderr, "%s: %d: restoring ",
1532                 get_pname(), file_num);
1533         print_header(stderr, source.header);
1534     }
1535     record_seen_dump(tape_seen, source.header);
1536     restore(&source, flags);
1537     return RESTORE_STATUS_NEXT_FILE;
1538 }
1539
1540 /* This function handles processing of a particular tape or holding
1541    disk file. It returns TRUE if it is useful to load another tape.*/
1542
1543 gboolean
1544 search_a_tape(Device      * device,
1545               FILE         *prompt_out, /* Where to send any prompts */
1546               rst_flags_t  *flags,      /* Restore options. */
1547               am_feature_t *their_features, 
1548               tapelist_t   *desired_tape, /* A list of desired tape files */
1549               GSList *dumpspecs, /* What disks to restore. */
1550               seentapes_t **tape_seen,  /* Where to record data on
1551                                            this tape. */
1552               /* May be NULL. If zeroed, will be filled in with the
1553                  first restored file. If already filled in, then we
1554                  may only restore other files from the same dump. */
1555               dumpfile_t   * first_restored_file,
1556               int           tape_count,
1557               FILE * logstream) {
1558     seentapes_t * tape_seen_head = NULL;
1559     RestoreSource source;
1560     off_t       filenum;
1561
1562     int         tapefile_idx = -1;
1563     int         i;
1564     RestoreFileStatus restore_status = RESTORE_STATUS_NEXT_TAPE;
1565
1566     source.restore_mode = DEVICE_MODE;
1567     source.u.device = device;
1568
1569     filenum = (off_t)0;
1570     if(desired_tape && desired_tape->numfiles > 0)
1571         tapefile_idx = 0;
1572
1573     if (desired_tape) {
1574         dbprintf(_("search_a_tape: desired_tape=%p label=%s\n"),
1575                   desired_tape, desired_tape->label);
1576         dbprintf(_("tape:   numfiles = %d\n"), desired_tape->numfiles);
1577         for (i=0; i < desired_tape->numfiles; i++) {
1578             dbprintf(_("tape:   files[%d] = %lld\n"),
1579                       i, (long long)desired_tape->files[i]);
1580         }
1581     } else {
1582         dbprintf(_("search_a_tape: no desired_tape\n"));
1583     }
1584     dbprintf(_("current tapefile_idx = %d\n"), tapefile_idx);
1585
1586     if (tape_seen) {
1587         if (check_volume_seen(*tape_seen, device->volume_label)) {
1588             send_message(prompt_out, flags, their_features,
1589                          "Skipping repeat tape %s in slot %s",
1590                          device->volume_label, curslot);
1591             return TRUE;
1592         }
1593         record_seen_volume(tape_seen, device->volume_label, curslot);
1594         tape_seen_head = *tape_seen;
1595     }
1596         
1597     if (desired_tape && desired_tape->numfiles > 0) {
1598         /* Iterate the tape list, handle each file in order. */
1599         int file_index;
1600         for (file_index = 0; file_index < desired_tape->numfiles;
1601              file_index ++) {
1602             int file_num = desired_tape->files[file_index];
1603             restore_status = try_restore_single_file(device, file_num, NULL,
1604                                                      prompt_out, flags,
1605                                                      their_features,
1606                                                      first_restored_file,
1607                                                      NULL, tape_seen_head);
1608             if (restore_status != RESTORE_STATUS_NEXT_FILE)
1609                 break;
1610         }
1611     } else if(flags->fsf && flags->amidxtaped) {
1612         /* Restore a single file, then quit. */
1613         restore_status =
1614             try_restore_single_file(device, flags->fsf, NULL, prompt_out, flags,
1615                                     their_features, first_restored_file,
1616                                     dumpspecs, tape_seen_head);
1617     } else {
1618         /* Search the tape from beginning to end. */
1619         int file_num;
1620
1621         if (flags->fsf > 0) {
1622             file_num = flags->fsf;
1623         } else {
1624             file_num = 1;
1625         }
1626
1627         if (!flags->amidxtaped) {
1628             g_fprintf(prompt_out, "Restoring from tape %s starting with file %d.\n",
1629                     device->volume_label, file_num);
1630             fflush(prompt_out);
1631         }
1632
1633         for (;;) {
1634             restore_status =
1635                 try_restore_single_file(device, file_num, &file_num,
1636                                         prompt_out, flags,
1637                                         their_features, first_restored_file,
1638                                         dumpspecs, tape_seen_head);
1639             if (restore_status != RESTORE_STATUS_NEXT_FILE)
1640                 break;
1641         }
1642     }
1643     
1644     /* spit out our accumulated list of dumps, if we're inventorying */
1645     if (logstream != NULL) {
1646         print_tape_inventory(logstream, tape_seen_head, device->volume_time,
1647                              device->volume_label, tape_count);
1648     }
1649     return (restore_status != RESTORE_STATUS_STOP);
1650 }
1651
1652 static void free_seen_tapes(seentapes_t * seentapes) {
1653     while (seentapes != NULL) {
1654         seentapes_t *tape_seen = seentapes;
1655         seentapes = seentapes->next;
1656         while(tape_seen->files != NULL) {
1657             dumplist_t *temp_dump = tape_seen->files;
1658             tape_seen->files = temp_dump->next;
1659             amfree(temp_dump->file);
1660             amfree(temp_dump);
1661         }
1662         amfree(tape_seen->label);
1663         amfree(tape_seen->slotstr);
1664         amfree(tape_seen);
1665         
1666     }
1667 }
1668
1669 /* Spit out a list of expected tapes, so people with manual changers know
1670    what to load */
1671 static void print_expected_tape_list(FILE* prompt_out, FILE* prompt_in,
1672                                      tapelist_t *tapelist,
1673                                      rst_flags_t * flags) {
1674     tapelist_t * cur_volume;
1675
1676     g_fprintf(prompt_out, "The following tapes are needed:");
1677     for(cur_volume = tapelist; cur_volume != NULL;
1678         cur_volume = cur_volume->next){
1679         g_fprintf(prompt_out, " %s", cur_volume->label);
1680     }
1681     g_fprintf(prompt_out, "\n");
1682     fflush(prompt_out);
1683     if(flags->wait_tape_prompt){
1684         char *input = NULL;
1685         g_fprintf(prompt_out,"Press enter when ready\n");
1686         fflush(prompt_out);
1687         input = agets(prompt_in);
1688         amfree(input);
1689         g_fprintf(prompt_out, "\n");
1690         fflush(prompt_out);
1691     }
1692 }
1693
1694 /* Restore a single holding-disk file. We will fill in this_header
1695    with the header from this restore (if it is not null), and in the
1696    stdout-pipe case, we abort according to last_header. Returns TRUE
1697    if the restore should continue, FALSE if we are done. */
1698 gboolean restore_holding_disk(FILE * prompt_out,
1699                               rst_flags_t * flags,
1700                               am_feature_t * features,
1701                               tapelist_t * file,
1702                               seentapes_t ** seen,
1703                               GSList * dumpspecs,
1704                               dumpfile_t * this_header,
1705                               dumpfile_t * last_header) {
1706     RestoreSource source;
1707     gboolean read_result;
1708     dumpfile_t header;
1709
1710     source.header = &header;
1711     source.restore_mode = HOLDING_MODE;
1712     source.u.holding_fd = robust_open(file->label, 0, 0);
1713     if (source.u.holding_fd < 0) {
1714         send_message(prompt_out, flags, features, 
1715                      "could not open %s: %s",
1716                      file->label, strerror(errno));
1717         return TRUE;
1718     }
1719
1720     g_fprintf(stderr, "Reading %s from fd %d\n",
1721             file->label, source.u.holding_fd);
1722     
1723     read_result = read_holding_disk_header(source.header,
1724                                            source.u.holding_fd, flags);
1725     if (!read_result) {
1726         send_message(prompt_out, flags, features, 
1727                      "Invalid header reading %s.",
1728                      file->label);
1729         aclose(source.u.holding_fd);
1730         return TRUE;
1731     }
1732
1733     if (!run_dumpspecs(dumpspecs, source.header)) {
1734         return FALSE;
1735     }
1736
1737     if (last_header != NULL && !flags->amidxtaped &&
1738         flags->pipe_to_fd == STDOUT_FILENO &&
1739         !headers_equal(last_header, source.header, 1)) {
1740         return FALSE;
1741     } else if (this_header != NULL) {
1742         memcpy(this_header, source.header, sizeof(*this_header));
1743     }
1744
1745     if (seen != NULL) {
1746         record_seen_volume(seen, file->label, "<none>");
1747         record_seen_dump(*seen, source.header);
1748     }
1749
1750     print_header(stderr, source.header);
1751
1752     restore(&source, flags);
1753     aclose(source.u.holding_fd);
1754     return TRUE;
1755 }
1756
1757 /* Ask for a specific manual tape. If we find the right one, then open it
1758  * and return a Device handle. If not, return NULL. Pass a device name, but
1759  * it might be overridden. */
1760 static Device* manual_find_tape(char ** cur_tapedev, tapelist_t * cur_volume,
1761                                 FILE * prompt_out, FILE * prompt_in,
1762                                 rst_flags_t * flags,
1763                                 am_feature_t * features) {
1764     LoadStatus status = LOAD_NEXT;
1765     Device * rval;
1766
1767     for (;;) {
1768         status = load_manual_tape(cur_tapedev, prompt_out, prompt_in,
1769                                   flags, features, cur_volume);
1770         
1771         if (status == LOAD_STOP)
1772             return NULL;
1773
1774         rval =  conditional_device_open(*cur_tapedev, prompt_out, flags,
1775                                         features, cur_volume);
1776         if (rval != NULL)
1777             return rval;
1778     }
1779 }
1780
1781 /* If we have a tapelist, then we mandate restoring in tapelist
1782    order. The logic is simple: Get the next tape, and deal with it,
1783    then move on to the next one. */
1784 static void
1785 restore_from_tapelist(FILE * prompt_out,
1786                       FILE * prompt_in,
1787                       tapelist_t * tapelist,
1788                       GSList * dumpspecs,
1789                       rst_flags_t * flags,
1790                       am_feature_t * features,
1791                       char * cur_tapedev,
1792                       gboolean use_changer,
1793                       FILE * logstream) {
1794     tapelist_t * cur_volume;
1795     dumpfile_t first_restored_file;
1796     seentapes_t * seentapes = NULL;
1797
1798     fh_init(&first_restored_file);
1799
1800     for(cur_volume = tapelist; cur_volume != NULL;
1801         cur_volume = cur_volume->next){
1802         if (cur_volume->isafile) {
1803             /* Restore from holding disk; just go. */
1804             if (first_restored_file.type == F_UNKNOWN) {
1805                 if (!restore_holding_disk(prompt_out, flags,
1806                                           features, cur_volume, &seentapes,
1807                                           NULL, NULL, &first_restored_file)) {
1808                     break;
1809                 }
1810             } else {
1811                 restore_holding_disk(prompt_out, flags, features,
1812                                      cur_volume, &seentapes,
1813                                      NULL, &first_restored_file, NULL);
1814             }
1815         } else {
1816             Device * device = NULL;
1817             if (use_changer) {
1818                 char * tapedev = NULL;
1819                 loadlabel_data data;
1820                 data.cur_tapedev = &tapedev;
1821                 data.searchlabel =  cur_volume->label;
1822                 changer_find(&data, scan_init, loadlabel_slot,
1823                              cur_volume->label);
1824                 device = conditional_device_open(tapedev, prompt_out,
1825                                                  flags, features,
1826                                                  cur_volume);
1827                 amfree(tapedev);
1828             }
1829
1830             if (device == NULL)
1831                 device = manual_find_tape(&cur_tapedev, cur_volume, prompt_out,
1832                                           prompt_in, flags, features);
1833
1834             if (device == NULL)
1835                 break;
1836
1837             if (use_changer) {
1838                 g_fprintf(stderr, "Scanning volume %s (slot %s)\n",
1839                           device->volume_label,
1840                           curslot);
1841             } else {
1842                 g_fprintf(stderr, "Scanning volume %s\n",
1843                           device->volume_label);
1844             }
1845
1846             if (!search_a_tape(device, prompt_out, flags, features,
1847                                cur_volume, dumpspecs, &seentapes,
1848                                &first_restored_file, 0, logstream)) {
1849                 g_object_unref(device);
1850                 break;;
1851             }
1852             g_object_unref(device);
1853         }            
1854     }
1855
1856     free_seen_tapes(seentapes);
1857 }
1858
1859 /* This function works when we are operating without a tapelist
1860    (regardless of whether or not we have a changer). This only happens
1861    when we are using amfetchdump without dump logs, but in the future
1862    may include amrestore as well. The philosophy is to keep loading
1863    tapes until we run out. */
1864 static void
1865 restore_without_tapelist(FILE * prompt_out,
1866                          FILE * prompt_in,
1867                          GSList * dumpspecs,
1868                          rst_flags_t * flags,
1869                          am_feature_t * features,
1870                          char * cur_tapedev,
1871                          /* -1 if no changer. */
1872                          int slot_count,
1873                          FILE * logstream) {
1874     int cur_slot = 1;
1875     seentapes_t * seentapes;
1876     int tape_count = 0;
1877     dumpfile_t first_restored_file;
1878
1879     /* This loop also aborts if we run out of manual tapes, or
1880        encounter a changer error. */
1881     for (;;) {
1882         Device * device = NULL;
1883         if (slot_count > 0) {
1884             while (cur_slot < slot_count && device == NULL) {
1885                 amfree(curslot);
1886                 changer_loadslot("next", &curslot, &cur_tapedev);
1887                 device = conditional_device_open(cur_tapedev, prompt_out,
1888                                                  flags, features,
1889                                                  NULL);
1890                 amfree(cur_tapedev);
1891                 cur_slot ++;
1892             }
1893             if (cur_slot >= slot_count)
1894                 break;
1895             
1896             g_fprintf(stderr, "Scanning %s (slot %s)\n", device->volume_label,
1897                     curslot);
1898         } else {
1899             device = manual_find_tape(&cur_tapedev, NULL, prompt_out,
1900                                       prompt_in, flags, features);
1901         }
1902         
1903         if (device == NULL)
1904             break;;
1905         
1906         if (!search_a_tape(device, prompt_out, flags, features,
1907                            NULL, dumpspecs, &seentapes, &first_restored_file,
1908                            tape_count, logstream)) {
1909             g_object_unref(device);
1910             break;
1911         }
1912         g_object_unref(device);
1913         tape_count ++;
1914     }
1915            
1916     free_seen_tapes(seentapes);
1917 }
1918
1919 /* 
1920  * Take a pattern of dumps and restore it blind, a la amrestore.  In addition,
1921  * be smart enough to change tapes and continue with minimal operator
1922  * intervention, and write out a record of what was found on tapes in the
1923  * the regular logging format.  Can take a tapelist with a specific set of
1924  * tapes to search (rather than "everything I can find"), which in turn can
1925  * optionally list specific files to restore.
1926  */
1927  void
1928 search_tapes(
1929     FILE *              prompt_out,
1930     FILE *              prompt_in,
1931     int                 use_changer,
1932     tapelist_t *        tapelist,
1933     GSList *            dumpspecs,
1934     rst_flags_t *       flags,
1935     am_feature_t *      their_features)
1936 {
1937     char *cur_tapedev;
1938     int slots = -1;
1939     FILE *logstream = NULL;
1940     tapelist_t *desired_tape = NULL;
1941     struct sigaction act, oact;
1942
1943     device_api_init();
1944
1945     if(!prompt_out) prompt_out = stderr;
1946
1947     /* Don't die when child closes pipe */
1948     signal(SIGPIPE, SIG_IGN);
1949
1950     /* catch SIGINT with something that'll flush unmerged splits */
1951     act.sa_handler = handle_sigint;
1952     sigemptyset(&act.sa_mask);
1953     act.sa_flags = 0;
1954     if(sigaction(SIGINT, &act, &oact) != 0){
1955         error(_("error setting SIGINT handler: %s"), strerror(errno));
1956         /*NOTREACHED*/
1957     }
1958     if(flags->delay_assemble || flags->inline_assemble) exitassemble = 1;
1959     else exitassemble = 0;
1960
1961     /* if given a log file, print an inventory of stuff found */
1962     if(flags->inventory_log) {
1963         if(!strcmp(flags->inventory_log, "-")) logstream = stdout;
1964         else if((logstream = fopen(flags->inventory_log, "w+")) == NULL) {
1965             error(_("Couldn't open log file %s for writing: %s"),
1966                   flags->inventory_log, strerror(errno));
1967             /*NOTREACHED*/
1968         }
1969     }
1970
1971     /* Suss what tape device we're using, whether there's a changer, etc. */
1972     if (use_changer) {
1973         use_changer = changer_init();
1974     }
1975     if (!use_changer) {
1976         cur_tapedev = NULL;
1977         if (flags->alt_tapedev) {
1978             cur_tapedev = stralloc(flags->alt_tapedev);
1979         } else if(!cur_tapedev) {
1980             cur_tapedev = getconf_str(CNF_TAPEDEV);
1981             if (cur_tapedev == NULL) {
1982                 error(_("No tapedev specified"));
1983             }
1984         }
1985         /* XXX oughta complain if no config is loaded */
1986         g_fprintf(stderr, _("%s: Using tapedev %s\n"), get_pname(), cur_tapedev);
1987     }
1988     else{ /* good, the changer works, see what it can do */
1989         amfree(curslot);
1990         changer_info(&slots, &curslot, &backwards);
1991     }
1992
1993     if (tapelist && !flags->amidxtaped) {
1994         print_expected_tape_list(prompt_out, prompt_in, tapelist, flags);
1995     }
1996     desired_tape = tapelist;
1997
1998     if (use_changer) { /* load current slot */
1999         amfree(curslot);
2000         cur_tapedev = NULL;
2001         changer_loadslot("current", &curslot, &cur_tapedev);
2002     }
2003
2004     /*
2005      * If we're not given a tapelist, iterate over everything our changer can
2006      * find.  If there's no changer, we'll prompt to be handfed tapes.
2007      *
2008      * If we *are* given a tapelist, restore from those tapes in the order in
2009      * which they're listed.  Unless the changer (if we have one) can't go
2010      * backwards, in which case check every tape we see and restore from it if
2011      * appropriate.
2012      *
2013      * (obnoxious, isn't this?)
2014      */
2015
2016     if (tapelist) {
2017         restore_from_tapelist(prompt_out, prompt_in, tapelist, dumpspecs,
2018                               flags, their_features, cur_tapedev, use_changer,
2019                               logstream);
2020     } else {
2021         restore_without_tapelist(prompt_out, prompt_in, dumpspecs, flags,
2022                                  their_features, cur_tapedev,
2023                                  (use_changer ? slots : -1),
2024                                  logstream);
2025     }
2026
2027     if(logstream && logstream != stderr && logstream != stdout){
2028         fclose(logstream);
2029     }
2030     if(flags->delay_assemble || flags->inline_assemble){
2031         flush_open_outputs(1, NULL);
2032     }
2033     else flush_open_outputs(0, NULL);
2034 }
2035
2036 /*
2037  * Create a new, clean set of restore flags with some sane default values.
2038  */
2039 rst_flags_t *
2040 new_rst_flags(void)
2041 {
2042     rst_flags_t *flags = alloc(SIZEOF(rst_flags_t));
2043
2044     memset(flags, 0, SIZEOF(rst_flags_t));
2045
2046     flags->fsf = 0;
2047     flags->comp_type = COMPRESS_FAST_OPT;
2048     flags->inline_assemble = 1;
2049     flags->pipe_to_fd = -1;
2050     flags->check_labels = 1;
2051
2052     return(flags);
2053 }
2054
2055 /*
2056  * Make sure the set of restore options given is sane.  Print errors for
2057  * things that're odd, and return -1 for fatal errors.
2058  */
2059 int
2060 check_rst_flags(
2061     rst_flags_t *       flags)
2062 {
2063     int ret = 0;        
2064     
2065     if(!flags) return(-1);
2066
2067     if(flags->compress && flags->leave_comp){
2068         g_fprintf(stderr, _("Cannot specify 'compress output' and 'leave compression alone' together\n"));
2069         ret = -1;
2070     }
2071
2072     if(flags->restore_dir != NULL){
2073         struct stat statinfo;
2074
2075         if(flags->pipe_to_fd != -1){
2076             g_fprintf(stderr, _("Specifying output directory and piping output are mutually exclusive\n"));
2077             ret = -1;
2078         }
2079         if(stat(flags->restore_dir, &statinfo) < 0){
2080             g_fprintf(stderr, _("Cannot stat restore target dir '%s': %s\n"),
2081                       flags->restore_dir, strerror(errno));
2082             ret = -1;
2083         }
2084         if((statinfo.st_mode & S_IFMT) != S_IFDIR){
2085             g_fprintf(stderr, _("'%s' is not a directory\n"), flags->restore_dir);
2086             ret = -1;
2087         }
2088     }
2089
2090     if((flags->pipe_to_fd != -1 || flags->compress) &&
2091             (flags->delay_assemble || !flags->inline_assemble)){
2092         g_fprintf(stderr, _("Split dumps *must* be automatically reassembled when piping output or compressing/uncompressing\n"));
2093         ret = -1;
2094     }
2095
2096     if(flags->delay_assemble && flags->inline_assemble){
2097         g_fprintf(stderr, _("Inline split assembling and delayed assembling are mutually exclusive\n"));
2098         ret = -1;
2099     }
2100
2101     return(ret);
2102 }
2103
2104 /*
2105  * Clean up after a rst_flags_t
2106  */
2107 void
2108 free_rst_flags(
2109     rst_flags_t *       flags)
2110 {
2111     if(!flags) return;
2112
2113     amfree(flags->restore_dir);
2114     amfree(flags->alt_tapedev);
2115     amfree(flags->inventory_log);
2116
2117     amfree(flags);
2118 }
2119
2120
2121 printf_arglist_function3(
2122     void send_message,
2123     FILE *, prompt_out,
2124     rst_flags_t *, flags,
2125     am_feature_t *, their_features,
2126     char *, format)
2127 {
2128     va_list argp;
2129     char linebuf[STR_SIZE];
2130
2131     arglist_start(argp, format);
2132     g_vsnprintf(linebuf, SIZEOF(linebuf)-1, format, argp);
2133     arglist_end(argp);
2134
2135     g_fprintf(stderr,"%s\n", linebuf);
2136     if (flags->amidxtaped && their_features &&
2137         am_has_feature(their_features, fe_amrecover_message)) {
2138         g_fprintf(prompt_out, "MESSAGE %s\r\n", linebuf);
2139         fflush(prompt_out);
2140     }
2141 }
2142