Imported Upstream version 2.6.0p1
[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     if (first_restored_file) {
1538         memcpy(first_restored_file, source.header, sizeof(dumpfile_t));
1539     }
1540     return RESTORE_STATUS_NEXT_FILE;
1541 }
1542
1543 /* This function handles processing of a particular tape or holding
1544    disk file. It returns TRUE if it is useful to load another tape.*/
1545
1546 gboolean
1547 search_a_tape(Device      * device,
1548               FILE         *prompt_out, /* Where to send any prompts */
1549               rst_flags_t  *flags,      /* Restore options. */
1550               am_feature_t *their_features, 
1551               tapelist_t   *desired_tape, /* A list of desired tape files */
1552               GSList *dumpspecs, /* What disks to restore. */
1553               seentapes_t **tape_seen,  /* Where to record data on
1554                                            this tape. */
1555               /* May be NULL. If zeroed, will be filled in with the
1556                  first restored file. If already filled in, then we
1557                  may only restore other files from the same dump. */
1558               dumpfile_t   * first_restored_file,
1559               int           tape_count,
1560               FILE * logstream) {
1561     seentapes_t * tape_seen_head = NULL;
1562     RestoreSource source;
1563     off_t       filenum;
1564
1565     int         tapefile_idx = -1;
1566     int         i;
1567     RestoreFileStatus restore_status = RESTORE_STATUS_NEXT_TAPE;
1568
1569     source.restore_mode = DEVICE_MODE;
1570     source.u.device = device;
1571
1572     filenum = (off_t)0;
1573     if(desired_tape && desired_tape->numfiles > 0)
1574         tapefile_idx = 0;
1575
1576     if (desired_tape) {
1577         dbprintf(_("search_a_tape: desired_tape=%p label=%s\n"),
1578                   desired_tape, desired_tape->label);
1579         dbprintf(_("tape:   numfiles = %d\n"), desired_tape->numfiles);
1580         for (i=0; i < desired_tape->numfiles; i++) {
1581             dbprintf(_("tape:   files[%d] = %lld\n"),
1582                       i, (long long)desired_tape->files[i]);
1583         }
1584     } else {
1585         dbprintf(_("search_a_tape: no desired_tape\n"));
1586     }
1587     dbprintf(_("current tapefile_idx = %d\n"), tapefile_idx);
1588
1589     if (tape_seen) {
1590         if (check_volume_seen(*tape_seen, device->volume_label)) {
1591             send_message(prompt_out, flags, their_features,
1592                          "Skipping repeat tape %s in slot %s",
1593                          device->volume_label, curslot);
1594             return TRUE;
1595         }
1596         record_seen_volume(tape_seen, device->volume_label, curslot);
1597         tape_seen_head = *tape_seen;
1598     }
1599         
1600     if (desired_tape && desired_tape->numfiles > 0) {
1601         /* Iterate the tape list, handle each file in order. */
1602         int file_index;
1603         for (file_index = 0; file_index < desired_tape->numfiles;
1604              file_index ++) {
1605             int file_num = desired_tape->files[file_index];
1606             restore_status = try_restore_single_file(device, file_num, NULL,
1607                                                      prompt_out, flags,
1608                                                      their_features,
1609                                                      first_restored_file,
1610                                                      NULL, tape_seen_head);
1611             if (restore_status != RESTORE_STATUS_NEXT_FILE)
1612                 break;
1613         }
1614     } else if(flags->fsf && flags->amidxtaped) {
1615         /* Restore a single file, then quit. */
1616         restore_status =
1617             try_restore_single_file(device, flags->fsf, NULL, prompt_out, flags,
1618                                     their_features, first_restored_file,
1619                                     dumpspecs, tape_seen_head);
1620     } else {
1621         /* Search the tape from beginning to end. */
1622         int file_num;
1623
1624         if (flags->fsf > 0) {
1625             file_num = flags->fsf;
1626         } else {
1627             file_num = 1;
1628         }
1629
1630         if (!flags->amidxtaped) {
1631             g_fprintf(prompt_out, "Restoring from tape %s starting with file %d.\n",
1632                     device->volume_label, file_num);
1633             fflush(prompt_out);
1634         }
1635
1636         for (;;) {
1637             restore_status =
1638                 try_restore_single_file(device, file_num, &file_num,
1639                                         prompt_out, flags,
1640                                         their_features, first_restored_file,
1641                                         dumpspecs, tape_seen_head);
1642             if (restore_status != RESTORE_STATUS_NEXT_FILE)
1643                 break;
1644         }
1645     }
1646     
1647     /* spit out our accumulated list of dumps, if we're inventorying */
1648     if (logstream != NULL) {
1649         print_tape_inventory(logstream, tape_seen_head, device->volume_time,
1650                              device->volume_label, tape_count);
1651     }
1652     return (restore_status != RESTORE_STATUS_STOP);
1653 }
1654
1655 static void free_seen_tapes(seentapes_t * seentapes) {
1656     while (seentapes != NULL) {
1657         seentapes_t *tape_seen = seentapes;
1658         seentapes = seentapes->next;
1659         while(tape_seen->files != NULL) {
1660             dumplist_t *temp_dump = tape_seen->files;
1661             tape_seen->files = temp_dump->next;
1662             amfree(temp_dump->file);
1663             amfree(temp_dump);
1664         }
1665         amfree(tape_seen->label);
1666         amfree(tape_seen->slotstr);
1667         amfree(tape_seen);
1668         
1669     }
1670 }
1671
1672 /* Spit out a list of expected tapes, so people with manual changers know
1673    what to load */
1674 static void print_expected_tape_list(FILE* prompt_out, FILE* prompt_in,
1675                                      tapelist_t *tapelist,
1676                                      rst_flags_t * flags) {
1677     tapelist_t * cur_volume;
1678
1679     g_fprintf(prompt_out, "The following tapes are needed:");
1680     for(cur_volume = tapelist; cur_volume != NULL;
1681         cur_volume = cur_volume->next){
1682         g_fprintf(prompt_out, " %s", cur_volume->label);
1683     }
1684     g_fprintf(prompt_out, "\n");
1685     fflush(prompt_out);
1686     if(flags->wait_tape_prompt){
1687         char *input = NULL;
1688         g_fprintf(prompt_out,"Press enter when ready\n");
1689         fflush(prompt_out);
1690         input = agets(prompt_in);
1691         amfree(input);
1692         g_fprintf(prompt_out, "\n");
1693         fflush(prompt_out);
1694     }
1695 }
1696
1697 /* Restore a single holding-disk file. We will fill in this_header
1698    with the header from this restore (if it is not null), and in the
1699    stdout-pipe case, we abort according to last_header. Returns TRUE
1700    if the restore should continue, FALSE if we are done. */
1701 gboolean restore_holding_disk(FILE * prompt_out,
1702                               rst_flags_t * flags,
1703                               am_feature_t * features,
1704                               tapelist_t * file,
1705                               seentapes_t ** seen,
1706                               GSList * dumpspecs,
1707                               dumpfile_t * this_header,
1708                               dumpfile_t * last_header) {
1709     RestoreSource source;
1710     gboolean read_result;
1711     dumpfile_t header;
1712
1713     source.header = &header;
1714     source.restore_mode = HOLDING_MODE;
1715     source.u.holding_fd = robust_open(file->label, 0, 0);
1716     if (source.u.holding_fd < 0) {
1717         send_message(prompt_out, flags, features, 
1718                      "could not open %s: %s",
1719                      file->label, strerror(errno));
1720         return TRUE;
1721     }
1722
1723     g_fprintf(stderr, "Reading %s from fd %d\n",
1724             file->label, source.u.holding_fd);
1725     
1726     read_result = read_holding_disk_header(source.header,
1727                                            source.u.holding_fd, flags);
1728     if (!read_result) {
1729         send_message(prompt_out, flags, features, 
1730                      "Invalid header reading %s.",
1731                      file->label);
1732         aclose(source.u.holding_fd);
1733         return TRUE;
1734     }
1735
1736     if (!run_dumpspecs(dumpspecs, source.header)) {
1737         return FALSE;
1738     }
1739
1740     if (last_header != NULL && !flags->amidxtaped &&
1741         flags->pipe_to_fd == STDOUT_FILENO &&
1742         last_header->type != F_UNKNOWN &&
1743         !headers_equal(last_header, source.header, 1)) {
1744         return FALSE;
1745     } else if (this_header != NULL) {
1746         memcpy(this_header, source.header, sizeof(*this_header));
1747     }
1748
1749     if (seen != NULL) {
1750         record_seen_volume(seen, file->label, "<none>");
1751         record_seen_dump(*seen, source.header);
1752     }
1753
1754     print_header(stderr, source.header);
1755
1756     restore(&source, flags);
1757     aclose(source.u.holding_fd);
1758     return TRUE;
1759 }
1760
1761 /* Ask for a specific manual tape. If we find the right one, then open it
1762  * and return a Device handle. If not, return NULL. Pass a device name, but
1763  * it might be overridden. */
1764 static Device* manual_find_tape(char ** cur_tapedev, tapelist_t * cur_volume,
1765                                 FILE * prompt_out, FILE * prompt_in,
1766                                 rst_flags_t * flags,
1767                                 am_feature_t * features) {
1768     LoadStatus status = LOAD_NEXT;
1769     Device * rval;
1770
1771     for (;;) {
1772         status = load_manual_tape(cur_tapedev, prompt_out, prompt_in,
1773                                   flags, features, cur_volume);
1774         
1775         if (status == LOAD_STOP)
1776             return NULL;
1777
1778         rval =  conditional_device_open(*cur_tapedev, prompt_out, flags,
1779                                         features, cur_volume);
1780         if (rval != NULL)
1781             return rval;
1782     }
1783 }
1784
1785 /* If we have a tapelist, then we mandate restoring in tapelist
1786    order. The logic is simple: Get the next tape, and deal with it,
1787    then move on to the next one. */
1788 static void
1789 restore_from_tapelist(FILE * prompt_out,
1790                       FILE * prompt_in,
1791                       tapelist_t * tapelist,
1792                       GSList * dumpspecs,
1793                       rst_flags_t * flags,
1794                       am_feature_t * features,
1795                       char * cur_tapedev,
1796                       gboolean use_changer,
1797                       FILE * logstream) {
1798     tapelist_t * cur_volume;
1799     dumpfile_t first_restored_file;
1800     seentapes_t * seentapes = NULL;
1801
1802     fh_init(&first_restored_file);
1803
1804     for(cur_volume = tapelist; cur_volume != NULL;
1805         cur_volume = cur_volume->next){
1806         if (cur_volume->isafile) {
1807             /* Restore from holding disk; just go. */
1808             if (first_restored_file.type == F_UNKNOWN) {
1809                 if (!restore_holding_disk(prompt_out, flags,
1810                                           features, cur_volume, &seentapes,
1811                                           NULL, NULL, &first_restored_file)) {
1812                     break;
1813                 }
1814             } else {
1815                 restore_holding_disk(prompt_out, flags, features,
1816                                      cur_volume, &seentapes,
1817                                      NULL, &first_restored_file, NULL);
1818             }
1819             if (flags->pipe_to_fd == fileno(stdout)) {
1820                 break;
1821             }
1822         } else {
1823             Device * device = NULL;
1824             if (use_changer) {
1825                 char * tapedev = NULL;
1826                 loadlabel_data data;
1827                 data.cur_tapedev = &tapedev;
1828                 data.searchlabel =  cur_volume->label;
1829                 changer_find(&data, scan_init, loadlabel_slot,
1830                              cur_volume->label);
1831                 device = conditional_device_open(tapedev, prompt_out,
1832                                                  flags, features,
1833                                                  cur_volume);
1834                 amfree(tapedev);
1835             }
1836
1837             if (device == NULL)
1838                 device = manual_find_tape(&cur_tapedev, cur_volume, prompt_out,
1839                                           prompt_in, flags, features);
1840
1841             if (device == NULL)
1842                 break;
1843
1844             if (use_changer) {
1845                 g_fprintf(stderr, "Scanning volume %s (slot %s)\n",
1846                           device->volume_label,
1847                           curslot);
1848             } else {
1849                 g_fprintf(stderr, "Scanning volume %s\n",
1850                           device->volume_label);
1851             }
1852
1853             if (!search_a_tape(device, prompt_out, flags, features,
1854                                cur_volume, dumpspecs, &seentapes,
1855                                &first_restored_file, 0, logstream)) {
1856                 g_object_unref(device);
1857                 break;
1858             }
1859             g_object_unref(device);
1860         }            
1861     }
1862
1863     free_seen_tapes(seentapes);
1864 }
1865
1866 /* This function works when we are operating without a tapelist
1867    (regardless of whether or not we have a changer). This only happens
1868    when we are using amfetchdump without dump logs, but in the future
1869    may include amrestore as well. The philosophy is to keep loading
1870    tapes until we run out. */
1871 static void
1872 restore_without_tapelist(FILE * prompt_out,
1873                          FILE * prompt_in,
1874                          GSList * dumpspecs,
1875                          rst_flags_t * flags,
1876                          am_feature_t * features,
1877                          char * cur_tapedev,
1878                          /* -1 if no changer. */
1879                          int slot_count,
1880                          FILE * logstream) {
1881     int cur_slot = 1;
1882     seentapes_t * seentapes;
1883     int tape_count = 0;
1884     dumpfile_t first_restored_file;
1885
1886     fh_init(&first_restored_file);
1887
1888     /* This loop also aborts if we run out of manual tapes, or
1889        encounter a changer error. */
1890     for (;;) {
1891         Device * device = NULL;
1892         if (slot_count > 0) {
1893             while (cur_slot < slot_count && device == NULL) {
1894                 amfree(curslot);
1895                 changer_loadslot("next", &curslot, &cur_tapedev);
1896                 device = conditional_device_open(cur_tapedev, prompt_out,
1897                                                  flags, features,
1898                                                  NULL);
1899                 amfree(cur_tapedev);
1900                 cur_slot ++;
1901             }
1902             if (cur_slot >= slot_count)
1903                 break;
1904             
1905             g_fprintf(stderr, "Scanning %s (slot %s)\n", device->volume_label,
1906                     curslot);
1907         } else {
1908             device = manual_find_tape(&cur_tapedev, NULL, prompt_out,
1909                                       prompt_in, flags, features);
1910         }
1911         
1912         if (device == NULL)
1913             break;;
1914         
1915         if (!search_a_tape(device, prompt_out, flags, features,
1916                            NULL, dumpspecs, &seentapes, &first_restored_file,
1917                            tape_count, logstream)) {
1918             g_object_unref(device);
1919             break;
1920         }
1921         g_object_unref(device);
1922         tape_count ++;
1923     }
1924            
1925     free_seen_tapes(seentapes);
1926 }
1927
1928 /* 
1929  * Take a pattern of dumps and restore it blind, a la amrestore.  In addition,
1930  * be smart enough to change tapes and continue with minimal operator
1931  * intervention, and write out a record of what was found on tapes in the
1932  * the regular logging format.  Can take a tapelist with a specific set of
1933  * tapes to search (rather than "everything I can find"), which in turn can
1934  * optionally list specific files to restore.
1935  */
1936  void
1937 search_tapes(
1938     FILE *              prompt_out,
1939     FILE *              prompt_in,
1940     int                 use_changer,
1941     tapelist_t *        tapelist,
1942     GSList *            dumpspecs,
1943     rst_flags_t *       flags,
1944     am_feature_t *      their_features)
1945 {
1946     char *cur_tapedev;
1947     int slots = -1;
1948     FILE *logstream = NULL;
1949     tapelist_t *desired_tape = NULL;
1950     struct sigaction act, oact;
1951
1952     device_api_init();
1953
1954     if(!prompt_out) prompt_out = stderr;
1955
1956     /* Don't die when child closes pipe */
1957     signal(SIGPIPE, SIG_IGN);
1958
1959     /* catch SIGINT with something that'll flush unmerged splits */
1960     act.sa_handler = handle_sigint;
1961     sigemptyset(&act.sa_mask);
1962     act.sa_flags = 0;
1963     if(sigaction(SIGINT, &act, &oact) != 0){
1964         error(_("error setting SIGINT handler: %s"), strerror(errno));
1965         /*NOTREACHED*/
1966     }
1967     if(flags->delay_assemble || flags->inline_assemble) exitassemble = 1;
1968     else exitassemble = 0;
1969
1970     /* if given a log file, print an inventory of stuff found */
1971     if(flags->inventory_log) {
1972         if(!strcmp(flags->inventory_log, "-")) logstream = stdout;
1973         else if((logstream = fopen(flags->inventory_log, "w+")) == NULL) {
1974             error(_("Couldn't open log file %s for writing: %s"),
1975                   flags->inventory_log, strerror(errno));
1976             /*NOTREACHED*/
1977         }
1978     }
1979
1980     /* Suss what tape device we're using, whether there's a changer, etc. */
1981     if (use_changer) {
1982         use_changer = changer_init();
1983     }
1984     if (!use_changer) {
1985         cur_tapedev = NULL;
1986         if (flags->alt_tapedev) {
1987             cur_tapedev = stralloc(flags->alt_tapedev);
1988         } else if(!cur_tapedev) {
1989             cur_tapedev = getconf_str(CNF_TAPEDEV);
1990             if (cur_tapedev == NULL) {
1991                 error(_("No tapedev specified"));
1992             }
1993         }
1994         /* XXX oughta complain if no config is loaded */
1995         g_fprintf(stderr, _("%s: Using tapedev %s\n"), get_pname(), cur_tapedev);
1996     }
1997     else{ /* good, the changer works, see what it can do */
1998         amfree(curslot);
1999         changer_info(&slots, &curslot, &backwards);
2000     }
2001
2002     if (tapelist && !flags->amidxtaped) {
2003         print_expected_tape_list(prompt_out, prompt_in, tapelist, flags);
2004     }
2005     desired_tape = tapelist;
2006
2007     if (use_changer) { /* load current slot */
2008         amfree(curslot);
2009         cur_tapedev = NULL;
2010         changer_loadslot("current", &curslot, &cur_tapedev);
2011     }
2012
2013     /*
2014      * If we're not given a tapelist, iterate over everything our changer can
2015      * find.  If there's no changer, we'll prompt to be handfed tapes.
2016      *
2017      * If we *are* given a tapelist, restore from those tapes in the order in
2018      * which they're listed.  Unless the changer (if we have one) can't go
2019      * backwards, in which case check every tape we see and restore from it if
2020      * appropriate.
2021      *
2022      * (obnoxious, isn't this?)
2023      */
2024
2025     if (tapelist) {
2026         restore_from_tapelist(prompt_out, prompt_in, tapelist, dumpspecs,
2027                               flags, their_features, cur_tapedev, use_changer,
2028                               logstream);
2029     } else {
2030         restore_without_tapelist(prompt_out, prompt_in, dumpspecs, flags,
2031                                  their_features, cur_tapedev,
2032                                  (use_changer ? slots : -1),
2033                                  logstream);
2034     }
2035
2036     if(logstream && logstream != stderr && logstream != stdout){
2037         fclose(logstream);
2038     }
2039     if(flags->delay_assemble || flags->inline_assemble){
2040         flush_open_outputs(1, NULL);
2041     }
2042     else flush_open_outputs(0, NULL);
2043 }
2044
2045 /*
2046  * Create a new, clean set of restore flags with some sane default values.
2047  */
2048 rst_flags_t *
2049 new_rst_flags(void)
2050 {
2051     rst_flags_t *flags = alloc(SIZEOF(rst_flags_t));
2052
2053     memset(flags, 0, SIZEOF(rst_flags_t));
2054
2055     flags->fsf = 0;
2056     flags->comp_type = COMPRESS_FAST_OPT;
2057     flags->inline_assemble = 1;
2058     flags->pipe_to_fd = -1;
2059     flags->check_labels = 1;
2060
2061     return(flags);
2062 }
2063
2064 /*
2065  * Make sure the set of restore options given is sane.  Print errors for
2066  * things that're odd, and return -1 for fatal errors.
2067  */
2068 int
2069 check_rst_flags(
2070     rst_flags_t *       flags)
2071 {
2072     int ret = 0;        
2073     
2074     if(!flags) return(-1);
2075
2076     if(flags->compress && flags->leave_comp){
2077         g_fprintf(stderr, _("Cannot specify 'compress output' and 'leave compression alone' together\n"));
2078         ret = -1;
2079     }
2080
2081     if(flags->restore_dir != NULL){
2082         struct stat statinfo;
2083
2084         if(flags->pipe_to_fd != -1){
2085             g_fprintf(stderr, _("Specifying output directory and piping output are mutually exclusive\n"));
2086             ret = -1;
2087         }
2088         if(stat(flags->restore_dir, &statinfo) < 0){
2089             g_fprintf(stderr, _("Cannot stat restore target dir '%s': %s\n"),
2090                       flags->restore_dir, strerror(errno));
2091             ret = -1;
2092         }
2093         if((statinfo.st_mode & S_IFMT) != S_IFDIR){
2094             g_fprintf(stderr, _("'%s' is not a directory\n"), flags->restore_dir);
2095             ret = -1;
2096         }
2097     }
2098
2099     if((flags->pipe_to_fd != -1 || flags->compress) &&
2100             (flags->delay_assemble || !flags->inline_assemble)){
2101         g_fprintf(stderr, _("Split dumps *must* be automatically reassembled when piping output or compressing/uncompressing\n"));
2102         ret = -1;
2103     }
2104
2105     if(flags->delay_assemble && flags->inline_assemble){
2106         g_fprintf(stderr, _("Inline split assembling and delayed assembling are mutually exclusive\n"));
2107         ret = -1;
2108     }
2109
2110     return(ret);
2111 }
2112
2113 /*
2114  * Clean up after a rst_flags_t
2115  */
2116 void
2117 free_rst_flags(
2118     rst_flags_t *       flags)
2119 {
2120     if(!flags) return;
2121
2122     amfree(flags->restore_dir);
2123     amfree(flags->alt_tapedev);
2124     amfree(flags->inventory_log);
2125
2126     amfree(flags);
2127 }
2128
2129
2130 printf_arglist_function3(
2131     void send_message,
2132     FILE *, prompt_out,
2133     rst_flags_t *, flags,
2134     am_feature_t *, their_features,
2135     char *, format)
2136 {
2137     va_list argp;
2138     char linebuf[STR_SIZE];
2139
2140     arglist_start(argp, format);
2141     g_vsnprintf(linebuf, SIZEOF(linebuf)-1, format, argp);
2142     arglist_end(argp);
2143
2144     g_fprintf(stderr,"%s\n", linebuf);
2145     if (flags->amidxtaped && their_features &&
2146         am_has_feature(their_features, fe_amrecover_message)) {
2147         g_fprintf(prompt_out, "MESSAGE %s\r\n", linebuf);
2148         fflush(prompt_out);
2149     }
2150 }
2151