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