35818efec636aefba7ef59b310191248bf9009ac
[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,v 1.28 2006/03/14 13:12:01 martinea Exp $
28  *
29  * retrieves files from an amanda tape
30  */
31
32 #include "amanda.h"
33 #include "tapeio.h"
34 #include "util.h"
35 #include "restore.h"
36 #include "find.h"
37 #include "changer.h"
38 #include "logfile.h"
39 #include "fileheader.h"
40 #include <signal.h>
41
42 int file_number;
43
44 /* stuff we're stuck having global */
45 static long blocksize = -1;
46 static char *cur_tapedev = NULL;
47 static char *searchlabel = NULL;
48 static int backwards;
49 static int exitassemble = 0;
50 static int tapefd, nslots;
51
52 char *rst_conf_logdir = NULL;
53 char *rst_conf_logfile = NULL;
54 char *curslot = NULL;
55
56 typedef struct open_output_s {
57     struct open_output_s *next;
58     dumpfile_t *file;
59     int lastpartnum;
60     pid_t comp_enc_pid;
61     int outfd;
62 } open_output_t;
63
64
65 typedef struct dumplist_s {
66     struct dumplist_s *next;
67     dumpfile_t *file;
68 } dumplist_t;
69
70 static open_output_t *open_outputs = NULL;
71 static dumplist_t *alldumps_list = NULL;
72
73 static ssize_t get_block P((int tapefd, char *buffer, int isafile));
74 static void append_file_to_fd P((char *filename, int fd));
75 static int headers_equal P((dumpfile_t *file1, dumpfile_t *file2, int ignore_partnums));
76 static int already_have_dump P((dumpfile_t *file));
77
78 /* local functions */
79
80 static void handle_sigint(sig)
81 int sig;
82 /*
83  * We might want to flush any open dumps and unmerged splits before exiting
84  * on SIGINT, so do so.
85  */
86 {
87     flush_open_outputs(exitassemble, NULL);
88     if(rst_conf_logfile) unlink(rst_conf_logfile);
89     exit(0);
90 }
91
92 int lock_logfile()
93 {
94     rst_conf_logdir = getconf_str(CNF_LOGDIR);
95     if (*rst_conf_logdir == '/') {
96         rst_conf_logdir = stralloc(rst_conf_logdir);
97     } else {
98         rst_conf_logdir = stralloc2(config_dir, rst_conf_logdir);
99     }
100     rst_conf_logfile = vstralloc(rst_conf_logdir, "/log", NULL);
101     if (access(rst_conf_logfile, F_OK) == 0) {
102         error("%s exists: amdump or amflush is already running, or you must run amcleanup", rst_conf_logfile);
103     }
104     log_add(L_INFO, get_pname());
105     return 1;
106 }
107
108 /*
109  * Return 1 if the two fileheaders match in name, disk, type, split chunk part
110  * number, and datestamp, and 0 if not.  The part number can be optionally
111  * ignored.
112  */
113 int headers_equal (file1, file2, ignore_partnums)
114 dumpfile_t *file1, *file2;
115 int ignore_partnums;
116 {
117     if(!file1 || !file2) return(0);
118     
119     if(file1->dumplevel == file2->dumplevel &&
120            file1->type == file2->type &&
121            !strcmp(file1->datestamp, file2->datestamp) &&
122            !strcmp(file1->name, file2->name) &&
123            !strcmp(file1->disk, file2->disk) &&
124            (ignore_partnums || file1->partnum == file2->partnum)){
125         return(1);
126     }
127     return(0);
128 }
129
130
131 /*
132  * See whether we're already pulled an exact copy of the given file (chunk
133  * number and all).  Returns 0 if not, 1 if so.
134  */
135 int already_have_dump(file)
136 dumpfile_t *file;
137 {
138     dumplist_t *fileentry = NULL;
139
140     if(!file) return(0);
141     for(fileentry=alldumps_list;fileentry;fileentry=fileentry->next){
142         if(headers_equal(file, fileentry->file, 0)) return(1);
143     }
144     return(0);
145 }
146
147 /*
148  * Open the named file and append its contents to the (hopefully open) file
149  * descriptor supplies.
150  */
151 static void append_file_to_fd(filename, fd)
152 char *filename;
153 int fd;
154 {
155     ssize_t bytes_read;
156     ssize_t s;
157     off_t wc = 0;
158     char *buffer;
159
160     if(blocksize == -1)
161         blocksize = DISK_BLOCK_BYTES;
162     buffer = alloc(blocksize);
163
164     if((tapefd = open(filename, O_RDONLY)) == -1) {
165         error("can't open %s: %s", filename, strerror(errno));
166         /* NOTREACHED */
167     }
168
169     for (;;) {
170         bytes_read = get_block(tapefd, buffer, 1); /* same as isafile = 1 */
171         if(bytes_read < 0) {
172             error("read error: %s", strerror(errno));
173             /* NOTREACHED */
174         }
175
176         if (bytes_read == 0)
177                 break;
178
179         s = fullwrite(fd, buffer, bytes_read);
180         if (s < bytes_read) {
181             fprintf(stderr,"Error %d (%s) offset " OFF_T_FMT "+" AM64_FMT ", wrote " AM64_FMT "\n",
182                         errno, strerror(errno), wc, (am64_t)bytes_read, (am64_t)s);
183             if (s < 0) {
184                 if((errno == EPIPE) || (errno == ECONNRESET)) {
185                     error("%s: pipe reader has quit in middle of file.\n",
186                         get_pname());
187                     /* NOTREACHED */
188                 }
189                 error("restore: write error = %s", strerror(errno));
190                 /* NOTREACHED */
191             }
192             error("Short write: wrote %d bytes expected %d\n", s, bytes_read);
193             /* NOTREACHCED */
194         }
195         wc += bytes_read;
196     }
197
198     amfree(buffer);
199     aclose(tapefd);
200 }
201
202 /*
203  * Tape changer support routines, stolen brazenly from amtape
204  */
205 static int 
206 scan_init(ud, rc, ns, bk, s)
207      void * ud;
208      int rc, ns, bk, s;
209 {
210     if(rc)
211         error("could not get changer info: %s", changer_resultstr);
212
213     nslots = ns;
214     backwards = bk;
215
216     return 0;
217 }
218 int loadlabel_slot(ud, rc, slotstr, device)
219      void *ud;
220 int rc;
221 char *slotstr;
222 char *device;
223 {
224     char *errstr;
225     char *datestamp = NULL;
226     char *label = NULL;
227
228
229     if(rc > 1)
230         error("could not load slot %s: %s", slotstr, changer_resultstr);
231     else if(rc == 1)
232         fprintf(stderr, "%s: slot %s: %s\n",
233                 get_pname(), slotstr, changer_resultstr);
234     else if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL)
235         fprintf(stderr, "%s: slot %s: %s\n", get_pname(), slotstr, errstr);
236     else {
237         fprintf(stderr, "%s: slot %s: date %-8s label %s",
238                 get_pname(), slotstr, datestamp, label);
239         if(strcmp(label, FAKE_LABEL) != 0
240            && strcmp(label, searchlabel) != 0)
241             fprintf(stderr, " (wrong tape)\n");
242         else {
243             fprintf(stderr, " (exact label match)\n");
244             if((errstr = tape_rewind(device)) != NULL) {
245                 fprintf(stderr,
246                         "%s: could not rewind %s: %s",
247                         get_pname(), device, errstr);
248                 amfree(errstr);
249             }
250             amfree(cur_tapedev);
251             curslot = stralloc(slotstr);
252             amfree(datestamp);
253             amfree(label);
254             if(device)
255                 cur_tapedev = stralloc(device);
256             return 1;
257         }
258     }
259     amfree(datestamp);
260     amfree(label);
261
262     if(cur_tapedev) amfree(cur_tapedev);
263     curslot = stralloc(slotstr);
264     if(!device) return(1);
265     cur_tapedev = stralloc(device);
266
267     return 0;
268 }
269
270
271 /* non-local functions follow */
272
273
274
275 /*
276  * Check whether we've read all of the preceding parts of a given split dump,
277  * generally used to see if we're done and can close the thing.
278  */
279 int have_all_parts (file, upto)
280 dumpfile_t *file;
281 int upto;
282 {
283     int c;
284     int *foundparts = NULL;
285     dumplist_t *fileentry = NULL;
286
287     if(!file || file->partnum < 1) return(0);
288
289     if(upto < 1) upto = file->totalparts;
290
291     foundparts = alloc(sizeof(int) * upto); 
292     for(c = 0 ; c< upto; c++) foundparts[c] = 0;
293     
294     for(fileentry=alldumps_list;fileentry; fileentry=fileentry->next){
295         dumpfile_t *cur_file = fileentry->file;
296         if(headers_equal(file, cur_file, 1)){
297             if(cur_file->partnum > upto){
298                 amfree(foundparts);
299                 return(0);
300             }
301
302             foundparts[cur_file->partnum - 1] = 1;
303         }
304     }
305
306     for(c = 0 ; c< upto; c++){
307         if(!foundparts[c]){
308             amfree(foundparts);
309             return(0);
310         }
311     }
312     
313     amfree(foundparts);
314     return(1);
315 }
316
317 /*
318  * Free up the open filehandles and memory we were using to track in-progress
319  * dumpfiles (generally for split ones we're putting back together).  If
320  * applicable, also find the ones that are continuations of one another and
321  * string them together.  If given an optional file header argument, flush
322  * only that dump and do not flush/free any others.
323  */
324 void flush_open_outputs(reassemble, only_file)
325 int reassemble;
326 dumpfile_t *only_file;
327 {
328     open_output_t *cur_out = NULL, *prev = NULL;
329     find_result_t *sorted_files = NULL;
330     amwait_t compress_status;
331
332     if(!only_file){
333         fprintf(stderr, "\n");
334     }
335
336     /*
337      * Deal with any split dumps we've been working on, appending pieces
338      * that haven't yet been appended and closing filehandles we've been
339      * holding onto.
340      */
341     if(reassemble){
342         find_result_t *cur_find_res = NULL;
343         int outfd = -1, lastpartnum = -1;
344         dumpfile_t *main_file = NULL;
345         cur_out = open_outputs;
346         
347         /* stick the dumpfile_t's into a list find_result_t's so that we can
348            abuse existing sort functionality */
349         for(cur_out=open_outputs; cur_out; cur_out=cur_out->next){
350             find_result_t *cur_find_res = NULL;
351             dumpfile_t *cur_file = cur_out->file;
352             /* if we requested a particular file, do only that one */
353             if(only_file && !headers_equal(cur_file, only_file, 1)){
354                 continue;
355             }
356             cur_find_res = alloc(sizeof(find_result_t));
357             memset(cur_find_res, '\0', sizeof(find_result_t));
358             cur_find_res->datestamp = atoi(cur_file->datestamp);
359             cur_find_res->hostname = stralloc(cur_file->name);
360             cur_find_res->diskname = stralloc(cur_file->disk);
361             cur_find_res->level = cur_file->dumplevel;
362             if(cur_file->partnum < 1) cur_find_res->partnum = stralloc("--");
363             else{
364                 char part_str[NUM_STR_SIZE];
365                 snprintf(part_str, sizeof(part_str), "%d", cur_file->partnum);
366                 cur_find_res->partnum = stralloc(part_str);
367             }
368             cur_find_res->user_ptr = (void*)cur_out;
369
370             cur_find_res->next = sorted_files;
371             sorted_files = cur_find_res;
372         }
373         sort_find_result("hkdlp", &sorted_files);
374
375         /* now we have an in-order list of the files we need to concatenate */
376         cur_find_res = sorted_files;
377         for(cur_find_res=sorted_files;
378                 cur_find_res;
379                 cur_find_res=cur_find_res->next){
380             dumpfile_t *cur_file = NULL;
381             cur_out = (open_output_t*)cur_find_res->user_ptr;
382             cur_file = cur_out->file;
383
384             /* if we requested a particular file, do only that one */
385             if(only_file && !headers_equal(cur_file, only_file, 1)){
386                 continue;
387             }
388
389             if(cur_file->type == F_SPLIT_DUMPFILE) {
390                 /* is it a continuation of one we've been writing? */
391                 if(main_file && cur_file->partnum > lastpartnum &&
392                         headers_equal(cur_file, main_file, 1)){
393
394                     /* effectively changing filehandles */
395                     aclose(cur_out->outfd);
396                     cur_out->outfd = outfd;
397
398                     fprintf(stderr, "Merging %s with %s\n",
399                             make_filename(cur_file), make_filename(main_file));
400                     append_file_to_fd(make_filename(cur_file), outfd);
401                     if(unlink(make_filename(cur_file)) < 0){
402                         fprintf(stderr, "Failed to unlink %s: %s\n",
403                                      make_filename(cur_file), strerror(errno));
404                     }
405                 }
406                 /* or a new file? */
407                 else{
408                     if(outfd >= 0) aclose(outfd);
409                     if(main_file) amfree(main_file);
410                     main_file = alloc(sizeof(dumpfile_t));
411                     memcpy(main_file, cur_file, sizeof(dumpfile_t));
412                     outfd = cur_out->outfd;
413                     if(outfd < 0){
414                         if((outfd = open(make_filename(cur_file), O_RDWR|O_APPEND)) < 0){
415                           error("Couldn't open %s for appending: %s\n",
416                                 make_filename(cur_file), strerror(errno));
417                         }
418                     }
419                 }
420                 lastpartnum = cur_file->partnum;
421             }
422             else {
423                 aclose(cur_out->outfd);
424             }
425         }
426         if(outfd >= 0) {
427             aclose(outfd);
428         }
429
430         amfree(main_file);
431         free_find_result(&sorted_files);
432     }
433
434     /*
435      * Now that the split dump closure is done, free up resources we don't
436      * need anymore.
437      */
438     for(cur_out=open_outputs; cur_out; cur_out=cur_out->next){
439         dumpfile_t *cur_file = NULL;
440         if(prev) amfree(prev);
441         cur_file = cur_out->file;
442         /* if we requested a particular file, do only that one */
443         if(only_file && !headers_equal(cur_file, only_file, 1)){
444             continue;
445         }
446         if(!reassemble) {
447             aclose(cur_out->outfd);
448         }
449
450         if(cur_out->comp_enc_pid > 0){
451             waitpid(cur_out->comp_enc_pid, &compress_status, 0);
452         }
453         amfree(cur_out->file);
454         prev = cur_out;
455     }
456
457     open_outputs = NULL;
458 }
459
460 /*
461  * Turn a fileheader into a string suited for use on the filesystem.
462  */
463 char *make_filename(file)
464 dumpfile_t *file;
465 {
466     char number[NUM_STR_SIZE];
467     char part[NUM_STR_SIZE];
468     char totalparts[NUM_STR_SIZE];
469     char *sfn = NULL;
470     char *fn = NULL;
471     char *pad = NULL;
472     int padlen = 0;
473
474     snprintf(number, sizeof(number), "%d", file->dumplevel);
475     snprintf(part, sizeof(part), "%d", file->partnum);
476
477     if(file->totalparts < 0){
478         snprintf(totalparts, sizeof(totalparts), "UNKNOWN");
479     }
480     else{
481         snprintf(totalparts, sizeof(totalparts), "%d", file->totalparts);
482     }
483     padlen = strlen(totalparts) + 1 - strlen(part);
484     pad = alloc(padlen);
485     memset(pad, '0', padlen);
486     pad[padlen - 1] = '\0';
487
488     snprintf(part, sizeof(part), "%s%d", pad, file->partnum);
489
490     sfn = sanitise_filename(file->disk);
491     fn = vstralloc(file->name,
492                    ".",
493                    sfn, 
494                    ".",
495                    file->datestamp,
496                    ".",
497                    number,
498                    NULL);
499     if(file->partnum > 0){
500         fn = vstralloc(fn, ".", part, NULL);
501     }
502     amfree(sfn);
503     amfree(pad);
504     return fn;
505 }
506
507
508 /*
509 XXX Making this thing a lib functiong broke a lot of assumptions everywhere,
510 but I think I've found them all.  Maybe.  Damn globals all over the place.
511 */
512 static ssize_t get_block(tapefd, buffer, isafile)
513 int tapefd, isafile;
514 char *buffer;
515 {
516     if(isafile)
517         return (fullread(tapefd, buffer, blocksize));
518
519     return(tapefd_read(tapefd, buffer, blocksize));
520 }
521
522 int disk_match(file, datestamp, hostname, diskname, level)
523 dumpfile_t *file;
524 char *datestamp, *hostname, *diskname, *level;
525 /*
526  * Returns 1 if the current dump file matches the hostname and diskname
527  * regular expressions given on the command line, 0 otherwise.  As a 
528  * special case, empty regexs are considered equivalent to ".*": they 
529  * match everything.
530  */
531 {
532     char level_str[NUM_STR_SIZE];
533     snprintf(level_str, sizeof(level_str), "%d", file->dumplevel);
534
535     if(file->type != F_DUMPFILE && file->type != F_SPLIT_DUMPFILE) return 0;
536
537     if((*hostname == '\0' || match_host(hostname, file->name)) &&
538        (*diskname == '\0' || match_disk(diskname, file->disk)) &&
539        (*datestamp == '\0' || match_datestamp(datestamp, file->datestamp)) &&
540        (*level == '\0' || match_level(level, level_str)))
541         return 1;
542     else
543         return 0;
544 }
545
546
547 void read_file_header(file, tapefd, isafile, flags)
548 dumpfile_t *file;
549 int tapefd;
550 int isafile;
551 rst_flags_t *flags;
552 /*
553  * Reads the first block of a tape file.
554  */
555 {
556     ssize_t bytes_read;
557     char *buffer;
558   
559     if(flags->blocksize > 0)
560         blocksize = flags->blocksize;
561     else if(blocksize == -1)
562         blocksize = DISK_BLOCK_BYTES;
563     buffer = alloc(blocksize);
564
565     bytes_read = get_block(tapefd, buffer, isafile);
566     if(bytes_read < 0) {
567         error("error reading file header: %s", strerror(errno));
568         /* NOTREACHED */
569     }
570
571     if(bytes_read < blocksize) {
572         if(bytes_read == 0) {
573             fprintf(stderr, "%s: missing file header block\n", get_pname());
574         } else {
575             fprintf(stderr, "%s: short file header block: " AM64_FMT " byte%s\n",
576                     get_pname(), (am64_t)bytes_read, (bytes_read == 1) ? "" : "s");
577         }
578         file->type = F_UNKNOWN;
579     } else {
580         parse_file_header(buffer, file, bytes_read);
581     }
582     amfree(buffer);
583 }
584
585
586 void drain_file(tapefd, flags)
587 int tapefd;
588 rst_flags_t *flags;
589 {
590     ssize_t bytes_read;
591     char *buffer;
592
593     if(flags->blocksize)
594         blocksize = flags->blocksize;
595     else if(blocksize == -1)
596         blocksize = DISK_BLOCK_BYTES;
597     buffer = alloc(blocksize);
598
599     do {
600        bytes_read = get_block(tapefd, buffer, 0);
601        if(bytes_read < 0) {
602            error("drain read error: %s", strerror(errno));
603        }
604     } while (bytes_read > 0);
605
606     amfree(buffer);
607 }
608
609 ssize_t restore(file, filename, tapefd, isafile, flags)
610 dumpfile_t *file;
611 char *filename;
612 int tapefd;
613 int isafile;
614 rst_flags_t *flags;
615 /*
616  * Restore the current file from tape.  Depending on the settings of
617  * the command line flags, the file might need to be compressed or
618  * uncompressed.  If so, a pipe through compress or uncompress is set
619  * up.  The final output usually goes to a file named host.disk.date.lev,
620  * but with the -p flag the output goes to stdout (and presumably is
621  * piped to restore).
622  */
623 {
624     int dest = -1, out;
625     ssize_t s;
626     int file_is_compressed;
627     int is_continuation = 0;
628     int check_for_aborted = 0;
629     char *tmp_filename = NULL, *final_filename = NULL;
630     struct stat statinfo;
631     open_output_t *myout = NULL, *oldout = NULL;
632     dumplist_t *tempdump = NULL, *fileentry = NULL;
633     char *buffer;
634     int need_compress=0, need_uncompress=0, need_decrypt=0;
635     int stage=0;
636     ssize_t bytes_read;
637     struct pipeline {
638         int     pipe[2];
639     } pipes[3];
640
641     if(flags->blocksize)
642         blocksize = flags->blocksize;
643     else if(blocksize == -1)
644         blocksize = DISK_BLOCK_BYTES;
645
646     if(already_have_dump(file)){
647         fprintf(stderr, " *** Duplicate file %s, one is probably an aborted write\n", make_filename(file));
648         check_for_aborted = 1;
649     }
650
651     /* store a shorthand record of this dump */
652     tempdump = alloc(sizeof(dumplist_t));
653     tempdump->file = alloc(sizeof(dumpfile_t));
654     tempdump->next = NULL;
655     memcpy(tempdump->file, file, sizeof(dumpfile_t));
656
657     /*
658      * If we're appending chunked files to one another, and if this is a
659      * continuation of a file we just restored, and we've still got the
660      * output handle from that previous restore, we're golden.  Phew.
661      */
662     if(flags->inline_assemble && file->type == F_SPLIT_DUMPFILE){
663         myout = open_outputs;
664         while(myout != NULL){
665             if(myout->file->type == F_SPLIT_DUMPFILE &&
666                     headers_equal(file, myout->file, 1)){
667                 if(file->partnum == myout->lastpartnum + 1){
668                     is_continuation = 1;
669                     break;
670                 }
671             }
672             myout = myout->next;
673         }
674         if(myout != NULL) myout->lastpartnum = file->partnum;
675         else if(file->partnum != 1){
676             fprintf(stderr, "%s:      Chunk out of order, will save to disk and append to output.\n", get_pname());
677             flags->pipe_to_fd = -1;
678             flags->compress = 0;
679             flags->leave_comp = 1;
680         }
681         if(myout == NULL){
682             myout = alloc(sizeof(open_output_t));
683             memset(myout, 0, sizeof(open_output_t));
684         }
685     }
686     else{
687       myout = alloc(sizeof(open_output_t));
688       memset(myout, 0, sizeof(open_output_t));
689     }
690
691
692     if(is_continuation && flags->pipe_to_fd == -1){
693         fprintf(stderr, "%s:      appending to %s\n", get_pname(),
694                     make_filename(myout->file));
695     }
696
697     /* adjust compression flag */
698     file_is_compressed = file->compressed;
699     if(!flags->compress && file_is_compressed && !known_compress_type(file)) {
700         fprintf(stderr, 
701                 "%s: unknown compression suffix %s, can't uncompress\n",
702                 get_pname(), file->comp_suffix);
703         flags->compress = 1;
704     }
705
706     /* set up final destination file */
707
708     if(is_continuation && myout != NULL) {
709       out = myout->outfd;
710     } else {
711       if(flags->pipe_to_fd != -1) {
712           dest = flags->pipe_to_fd;     /* standard output */
713       } else {
714           char *filename_ext = NULL;
715   
716           if(flags->compress) {
717               filename_ext = file_is_compressed ? file->comp_suffix
718                                               : COMPRESS_SUFFIX;
719           } else if(flags->raw) {
720               filename_ext = ".RAW";
721           } else {
722               filename_ext = "";
723           }
724           filename_ext = stralloc2(filename, filename_ext);
725           tmp_filename = stralloc(filename_ext); 
726           if(flags->restore_dir != NULL) {
727               char *tmpstr = vstralloc(flags->restore_dir, "/",
728                                        tmp_filename, NULL);
729               amfree(tmp_filename);
730               tmp_filename = tmpstr;
731           } 
732           final_filename = stralloc(tmp_filename); 
733           tmp_filename = newvstralloc(tmp_filename, ".tmp", NULL);
734           if((dest = creat(tmp_filename, CREAT_MODE)) < 0) {
735               error("could not create output file %s: %s",
736                                                tmp_filename, strerror(errno));
737               /*NOTREACHED*/
738           }
739           amfree(filename_ext);
740       }
741   
742       out = dest;
743     }
744
745     /*
746      * If -r or -h, write the header before compress or uncompress pipe.
747      * Only write DISK_BLOCK_BYTES, regardless of how much was read.
748      * This makes the output look like a holding disk image, and also
749      * makes it easier to remove the header (e.g. in amrecover) since
750      * it has a fixed size.
751      */
752     if(flags->raw || (flags->headers && !is_continuation)) {
753         int w;
754         char *cont_filename;
755         dumpfile_t tmp_hdr;
756
757         if(flags->compress && !file_is_compressed) {
758             file->compressed = 1;
759             snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
760                         " %s %s |", UNCOMPRESS_PATH,
761 #ifdef UNCOMPRESS_OPT
762                         UNCOMPRESS_OPT
763 #else
764                         ""
765 #endif
766                         );
767             strncpy(file->comp_suffix,
768                     COMPRESS_SUFFIX,
769                     sizeof(file->comp_suffix)-1);
770             file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
771         }
772
773         memcpy(&tmp_hdr, file, sizeof(dumpfile_t));
774
775         /* remove CONT_FILENAME from header */
776         cont_filename = stralloc(file->cont_filename);
777         memset(file->cont_filename,'\0',sizeof(file->cont_filename));
778         file->blocksize = DISK_BLOCK_BYTES;
779
780         /*
781          * Dumb down split file headers as well, so that older versions of
782          * things like amrecover won't gag on them.
783          */
784         if(file->type == F_SPLIT_DUMPFILE && flags->mask_splits){
785             file->type = F_DUMPFILE;
786         }
787
788         buffer = alloc(DISK_BLOCK_BYTES);
789         build_header(buffer, file, DISK_BLOCK_BYTES);
790
791         if((w = fullwrite(out, buffer, DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
792             if(w < 0) {
793                 error("write error: %s", strerror(errno));
794             } else {
795                 error("write error: %d instead of %d", w, DISK_BLOCK_BYTES);
796             }
797         }
798         amfree(buffer);
799         /* add CONT_FILENAME to header */
800 #if 0
801 //      strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename));
802 #endif
803         amfree(cont_filename);
804         memcpy(file, &tmp_hdr, sizeof(dumpfile_t));
805     }
806  
807     /* find out if compression or uncompression is needed here */
808     if(flags->compress && !file_is_compressed && !is_continuation
809           && !flags->leave_comp
810           && (flags->inline_assemble || file->type != F_SPLIT_DUMPFILE))
811        need_compress=1;
812        
813     if(!flags->raw && !flags->compress && file_is_compressed
814           && !is_continuation && !flags->leave_comp && (flags->inline_assemble
815           || file->type != F_SPLIT_DUMPFILE))
816        need_uncompress=1;   
817
818     if(!flags->raw && file->encrypted)
819        need_decrypt=1;
820    
821     /* Setup pipes for decryption / compression / uncompression  */
822     stage = 0;
823     if (need_decrypt) {
824       if (pipe(&pipes[stage].pipe[0]) < 0) 
825         error("error [pipe[%d]: %s]", stage, strerror(errno));
826       stage++;
827     }
828
829     if (need_compress || need_uncompress) {
830       if (pipe(&pipes[stage].pipe[0]) < 0) 
831         error("error [pipe[%d]: %s]", stage, strerror(errno));
832       stage++;
833     }
834     pipes[stage].pipe[0] = -1; 
835     pipes[stage].pipe[1] = out; 
836
837     stage = 0;
838
839     /* decrypt first if it's encrypted and no -r */
840     if(need_decrypt) {
841       switch(myout->comp_enc_pid = fork()) {
842       case -1:
843         error("could not fork for decrypt: %s", strerror(errno));
844       default:
845         aclose(pipes[stage].pipe[0]);
846         aclose(pipes[stage+1].pipe[1]);
847         stage++;
848         break;
849       case 0:
850         if(dup2(pipes[stage].pipe[0], 0) == -1)
851             error("error decrypt stdin [dup2 %d %d: %s]", stage,
852                 pipes[stage].pipe[0], strerror(errno));
853
854         if(dup2(pipes[stage+1].pipe[1], 1) == -1)
855             error("error decrypt stdout [dup2 %d %d: %s]", stage + 1,
856                 pipes[stage+1].pipe[1], strerror(errno));
857
858         safe_fd(-1, 0);
859         if (*file->srv_encrypt) {
860           (void) execlp(file->srv_encrypt, file->srv_encrypt,
861                         file->srv_decrypt_opt, NULL);
862           error("could not exec %s: %s", file->srv_encrypt, strerror(errno));
863         }  else if (*file->clnt_encrypt) {
864           (void) execlp(file->clnt_encrypt, file->clnt_encrypt,
865                         file->clnt_decrypt_opt, NULL);
866           error("could not exec %s: %s", file->clnt_encrypt, strerror(errno));
867         }
868       }
869     }
870
871     if (need_compress) {
872         /*
873          * Insert a compress pipe
874          */
875         switch(myout->comp_enc_pid = fork()) {
876         case -1: error("could not fork for %s: %s",
877                        COMPRESS_PATH, strerror(errno));
878         default:
879             aclose(pipes[stage].pipe[0]);
880             aclose(pipes[stage+1].pipe[1]);
881             stage++;
882             break;
883         case 0:
884             if(dup2(pipes[stage].pipe[0], 0) == -1)
885                 error("error compress stdin [dup2 %d %d: %s]", stage,
886                   pipes[stage].pipe[0], strerror(errno));
887
888             if(dup2(pipes[stage+1].pipe[1], 1) == -1)
889                 error("error compress stdout [dup2 %d %d: %s]", stage + 1,
890                   pipes[stage+1].pipe[1], strerror(errno));
891
892             if (*flags->comp_type == '\0') {
893                 flags->comp_type = NULL;
894             }
895
896             safe_fd(-1, 0);
897             (void) execlp(COMPRESS_PATH, COMPRESS_PATH, flags->comp_type, (char *)0);
898             error("could not exec %s: %s", COMPRESS_PATH, strerror(errno));
899         }
900     } else if(need_uncompress) {
901         /*
902          * If not -r, -c, -l, and file is compressed, and split reassembly 
903          * options are sane, insert uncompress pipe
904          */
905
906         /* 
907          * XXX for now we know that for the two compression types we
908          * understand, .Z and optionally .gz, UNCOMPRESS_PATH will take
909          * care of both.  Later, we may need to reference a table of
910          * possible uncompress programs.
911          */ 
912         switch(myout->comp_enc_pid = fork()) {
913         case -1: 
914             error("could not fork for %s: %s",
915                   UNCOMPRESS_PATH, strerror(errno));
916         default:
917             aclose(pipes[stage].pipe[0]);
918             aclose(pipes[stage+1].pipe[1]);
919             stage++;
920             break;
921         case 0:
922             if(dup2(pipes[stage].pipe[0], 0) == -1)
923                 error("error uncompress stdin [dup2 %d %d: %s]", stage,
924                   pipes[stage].pipe[0], strerror(errno));
925
926             if(dup2(pipes[stage+1].pipe[1], 1) == -1)
927                 error("error uncompress stdout [dup2 %d %d: %s]", stage + 1,
928                   pipes[stage+1].pipe[1], strerror(errno));
929
930             safe_fd(-1, 0);
931             if (*file->srvcompprog) {
932               (void) execlp(file->srvcompprog, file->srvcompprog, "-d", NULL);
933               error("could not exec %s: %s", file->srvcompprog, strerror(errno));
934             } else if (*file->clntcompprog) {
935               (void) execlp(file->clntcompprog, file->clntcompprog, "-d", NULL);
936               error("could not exec %s: %s", file->clntcompprog, strerror(errno));
937             } else {
938               (void) execlp(UNCOMPRESS_PATH, UNCOMPRESS_PATH,
939 #ifdef UNCOMPRESS_OPT
940                           UNCOMPRESS_OPT,
941 #endif
942                           (char *)0);
943               error("could not exec %s: %s", UNCOMPRESS_PATH, strerror(errno));
944             }
945         }
946     }
947
948     /* copy the rest of the file from tape to the output */
949     if(flags->blocksize > 0)
950         blocksize = flags->blocksize;
951     else if(blocksize == -1)
952         blocksize = DISK_BLOCK_BYTES;
953     buffer = alloc(blocksize);
954
955     do {
956         bytes_read = get_block(tapefd, buffer, isafile);
957         if(bytes_read < 0) {
958             error("restore read error: %s", strerror(errno));
959             /* NOTREACHED */
960         }
961
962         if(bytes_read > 0) {
963             if((s = fullwrite(pipes[0].pipe[1], buffer, bytes_read)) < 0) {
964                 if ((errno == EPIPE) || (errno == ECONNRESET)) {
965                     /*
966                      * reading program has ended early
967                      * e.g: bzip2 closes pipe when it
968                      * trailing garbage after EOF
969                      */
970                     break;
971                 }
972                 perror("restore: write error");
973                 exit(2);
974             }
975         }
976         else if(isafile) {
977             /*
978              * See if we need to switch to the next file in a holding restore
979              */
980             if(file->cont_filename[0] == '\0') {
981                 break;                          /* no more files */
982             }
983             aclose(tapefd);
984             if((tapefd = open(file->cont_filename, O_RDONLY)) == -1) {
985                 char *cont_filename = strrchr(file->cont_filename,'/');
986                 if(cont_filename) {
987                     cont_filename++;
988                     if((tapefd = open(cont_filename,O_RDONLY)) == -1) {
989                         error("can't open %s: %s", file->cont_filename,
990                               strerror(errno));
991                     }
992                     else {
993                         fprintf(stderr, "cannot open %s: %s\n",
994                                 file->cont_filename, strerror(errno));
995                         fprintf(stderr, "using %s\n",
996                                 cont_filename);
997                     }
998                 }
999                 else {
1000                     error("can't open %s: %s", file->cont_filename,
1001                           strerror(errno));
1002                 }
1003             }
1004             read_file_header(file, tapefd, isafile, flags);
1005             if(file->type != F_DUMPFILE && file->type != F_CONT_DUMPFILE
1006                     && file->type != F_SPLIT_DUMPFILE) {
1007                 fprintf(stderr, "unexpected header type: ");
1008                 print_header(stderr, file);
1009                 exit(2);
1010             }
1011         }
1012     } while (bytes_read > 0);
1013
1014     amfree(buffer);
1015
1016     if(!flags->inline_assemble) {
1017         if(out != dest)
1018             aclose(out);
1019     }
1020     if(!is_continuation){
1021         if(tmp_filename && stat(tmp_filename, &statinfo) < 0){
1022             error("Can't stat the file I just created (%s)!\n", tmp_filename);
1023         }
1024         if(check_for_aborted){
1025             char *old_dump = final_filename;
1026             struct stat oldstat;
1027             if(stat(old_dump, &oldstat) >= 0){
1028                 if(oldstat.st_size <= statinfo.st_size){
1029                     dumplist_t *prev_fileentry = NULL;
1030                     open_output_t *prev_out = NULL;
1031                     fprintf(stderr, "Newer restore is larger, using that\n");
1032                     /* nuke the old dump's entry in alldump_list */
1033                     for(fileentry=alldumps_list;
1034                             fileentry->next;
1035                             fileentry=fileentry->next){
1036                         if(headers_equal(file, fileentry->file, 0)){
1037                             if(prev_fileentry){
1038                                 prev_fileentry->next = fileentry->next;
1039                             }
1040                             else {
1041                                 alldumps_list = fileentry->next;
1042                             }
1043                             amfree(fileentry);
1044                             break;
1045                         }
1046                         prev_fileentry = fileentry;
1047                     }
1048                     myout = open_outputs;
1049                     while(myout != NULL){
1050                         if(headers_equal(file, myout->file, 0)){
1051                             if(myout->outfd >= 0)
1052                                 aclose(myout->outfd);
1053                             if(prev_out){
1054                                 prev_out->next = myout->next;
1055                             }
1056                             else open_outputs = myout->next;
1057                             amfree(myout);
1058                             break;
1059                         }
1060                         prev_out = myout;
1061                         myout = myout->next;
1062                     }
1063                 }
1064                 else{
1065                     fprintf(stderr, "Older restore is larger, using that\n");
1066                     unlink(tmp_filename);
1067                     amfree(tempdump->file);
1068                     amfree(tempdump);
1069                     amfree(tmp_filename);
1070                     amfree(final_filename);
1071                     return (bytes_read);
1072                 }
1073             }
1074         }
1075         if(tmp_filename && final_filename &&
1076                 rename(tmp_filename, final_filename) < 0){
1077             error("Can't rename %s to %s: %s\n", tmp_filename, final_filename,
1078                                              strerror(errno));
1079         }
1080     }
1081     if(tmp_filename) amfree(tmp_filename);
1082     if(final_filename) amfree(final_filename);
1083
1084
1085     /*
1086      * actually insert tracking data for this file into our various
1087      * structures (we waited in case we needed to give up)
1088      */
1089     if(!is_continuation){
1090         oldout = alloc(sizeof(open_output_t));
1091         oldout->file = alloc(sizeof(dumpfile_t));
1092         memcpy(oldout->file, file, sizeof(dumpfile_t));
1093         if(flags->inline_assemble) oldout->outfd = pipes[0].pipe[1];
1094         else oldout->outfd = -1;
1095         oldout->comp_enc_pid = -1;
1096         oldout->lastpartnum = file->partnum;
1097         oldout->next = open_outputs;
1098         open_outputs = oldout;
1099     }
1100     if(alldumps_list){
1101         for(fileentry=alldumps_list;fileentry->next;fileentry=fileentry->next);
1102         fileentry->next = tempdump;
1103     }
1104     else {
1105         alldumps_list = tempdump;
1106     }
1107
1108     return (bytes_read);
1109 }
1110
1111
1112
1113 /* 
1114  * Take a pattern of dumps and restore it blind, a la amrestore.  In addition,
1115  * be smart enough to change tapes and continue with minimal operator
1116  * intervention, and write out a record of what was found on tapes in the
1117  * the regular logging format.  Can take a tapelist with a specific set of
1118  * tapes to search (rather than "everything I can find"), which in turn can
1119  * optionally list specific files to restore.
1120  */
1121 void search_tapes(prompt_out, use_changer, tapelist, match_list, flags, their_features)
1122 FILE *prompt_out;
1123 int use_changer;
1124 tapelist_t *tapelist;
1125 match_list_t *match_list;
1126 rst_flags_t *flags;
1127 am_feature_t *their_features;
1128 {
1129     struct stat stat_tape;
1130     char *err;
1131     int have_changer = 1;
1132     int slot_num = -1;
1133     int slots = -1;
1134     int filenum;
1135     FILE *logstream = NULL;
1136     dumplist_t *fileentry = NULL;
1137     tapelist_t *desired_tape = NULL;
1138     struct sigaction act, oact;
1139     int newtape = 1;
1140     ssize_t bytes_read = 0;
1141
1142     struct seentapes{
1143         struct seentapes *next;
1144         char *slotstr;
1145         char *label;
1146         dumplist_t *files;
1147     } *seentapes = NULL;
1148
1149     if(!prompt_out) prompt_out = stderr;
1150
1151     if(flags->blocksize) blocksize = flags->blocksize;
1152     else if(blocksize == -1) blocksize = DISK_BLOCK_BYTES;
1153
1154     /* Don't die when child closes pipe */
1155     signal(SIGPIPE, SIG_IGN);
1156
1157     /* catch SIGINT with something that'll flush unmerged splits */
1158     act.sa_handler = handle_sigint;
1159     sigemptyset(&act.sa_mask);
1160     act.sa_flags = 0;
1161     if(sigaction(SIGINT, &act, &oact) != 0){
1162         error("error setting SIGINT handler: %s", strerror(errno));
1163     }
1164     if(flags->delay_assemble || flags->inline_assemble) exitassemble = 1;
1165     else exitassemble = 0;
1166
1167     /* if given a log file, print an inventory of stuff found */
1168     if(flags->inventory_log){
1169         if(!strcmp(flags->inventory_log, "-")) logstream = stdout;
1170         else if((logstream = fopen(flags->inventory_log, "w+")) == NULL){
1171             error("Couldn't open log file %s for writing: %s\n",
1172                   flags->inventory_log, strerror(errno));
1173         }
1174     }
1175
1176     /* Suss what tape device we're using, whether there's a changer, etc. */
1177     if(!use_changer || (have_changer = changer_init()) == 0) {
1178         if(flags->alt_tapedev) cur_tapedev = stralloc(flags->alt_tapedev);
1179         else if(!cur_tapedev) cur_tapedev = getconf_str(CNF_TAPEDEV);
1180         /* XXX oughta complain if no config is loaded */
1181         fprintf(stderr, "%s: Using tapedev %s\n", get_pname(), cur_tapedev);
1182         have_changer = 0;
1183     } else if (have_changer != 1) {
1184         error("changer initialization failed: %s", strerror(errno));
1185     }
1186     else{ /* good, the changer works, see what it can do */
1187         changer_info(&slots, &curslot, &backwards);
1188     }
1189
1190     if(tapelist && !flags->amidxtaped){
1191       slots = num_entries(tapelist);
1192       /*
1193         Spit out a list of expected tapes, so people with manual changers know
1194         what to load
1195       */
1196       fprintf(prompt_out, "The following tapes are needed:");
1197       for(desired_tape = tapelist;
1198           desired_tape != NULL;
1199           desired_tape = desired_tape->next){
1200         fprintf(prompt_out, " %s", desired_tape->label);
1201       }
1202       fprintf(prompt_out, "\n");
1203       fflush(prompt_out);
1204       if(flags->wait_tape_prompt){
1205         char *input = NULL;
1206         fprintf(prompt_out,"Press enter when ready\n");
1207         fflush(prompt_out);
1208         input = agets(stdin);
1209         amfree(input);
1210         fprintf(prompt_out, "\n");
1211         fflush(prompt_out);
1212       }
1213     }
1214     desired_tape = tapelist;
1215
1216     /*
1217      * If we're not given a tapelist, iterate over everything our changer can
1218      * find.  If there's no changer, we'll prompt to be handfed tapes.
1219      *
1220      * If we *are* given a tapelist, restore from those tapes in the order in
1221      * which they're listed.  Unless the changer (if we have one) can't go
1222      * backwards, in which case check every tape we see and restore from it if
1223      * appropriate.
1224      *
1225      * (obnoxious, isn't this?)
1226      */
1227     slot_num = 0;
1228     curslot = stralloc("<none>");
1229     while(desired_tape || ((slot_num < slots || !have_changer) && !tapelist)){
1230         char *label = NULL;
1231         struct seentapes *tape_seen = NULL;
1232         dumpfile_t file, tapestart, prev_rst_file;
1233         char *logline = NULL;
1234         int tapefile_idx = -1;
1235         int wrongtape = 0;
1236         int isafile = 0;
1237
1238         /*
1239          * Deal with instances where we're being asked to restore from a file
1240          */
1241         if(desired_tape && desired_tape->isafile){
1242             isafile = 1;
1243             if ((tapefd = open(desired_tape->label, 0)) == -1){
1244                 fprintf(stderr, "could not open %s: %s\n",
1245                       desired_tape->label, strerror(errno));
1246                 continue;
1247             }
1248             fprintf(stderr, "Reading %s to fd %d\n", desired_tape->label, tapefd);
1249
1250             read_file_header(&file, tapefd, 1, flags);
1251             label = stralloc(desired_tape->label);
1252         }
1253         /*
1254          * Make sure we can read whatever tape is loaded, then grab the label.
1255          */
1256         else if(cur_tapedev && newtape){
1257             if(tape_stat(cur_tapedev,&stat_tape)!=0) {
1258                 error("could not stat %s: %s", cur_tapedev, strerror(errno));
1259             }
1260
1261             if((err = tape_rewind(cur_tapedev)) != NULL) {
1262                 fprintf(stderr, "Could not rewind device '%s': %s\n",
1263                         cur_tapedev, err);
1264                 wrongtape = 1;
1265             }
1266             if((tapefd = tape_open(cur_tapedev, 0)) < 0){
1267                 fprintf(stderr, "could not open tape device %s: %s\n",
1268                         cur_tapedev, strerror(errno));
1269                 wrongtape = 1;
1270             }
1271
1272             if (!wrongtape) {
1273                 read_file_header(&file, tapefd, 0, flags);
1274                 if (file.type != F_TAPESTART) {
1275                     fprintf(stderr, "Not an amanda tape\n");
1276                     tapefd_close(tapefd);
1277                     wrongtape = 1;
1278                 } else {
1279                     memcpy(&tapestart, &file, sizeof(dumpfile_t));
1280                     label = stralloc(file.name);
1281                 }
1282             }
1283         } else if(newtape) {
1284           wrongtape = 1; /* nothing loaded */
1285           bytes_read = -1;
1286         }
1287
1288         /*
1289          * Skip this tape if we did it already.  Note that this would let
1290          * duplicate labels through, so long as they were in the same slot.
1291          * I'm over it, are you?
1292          */
1293         if(label && newtape && !isafile && !wrongtape){
1294             for(tape_seen = seentapes; tape_seen; tape_seen = tape_seen->next){
1295                 if(!strcmp(tape_seen->label, label) &&
1296                         !strcmp(tape_seen->slotstr, curslot)){
1297                     fprintf(stderr, "Saw repeat tape %s in slot %s\n", label, curslot);
1298                     wrongtape = 1;
1299                     amfree(label);
1300                     break;
1301                 }
1302             }
1303         }
1304
1305         /*
1306          * See if we've got the tape we were looking for, if we were looking
1307          * for something specific.
1308          */
1309         if((desired_tape || !cur_tapedev) && newtape && !isafile && !wrongtape){
1310             if(!label || (flags->check_labels &&
1311                     desired_tape && strcmp(label, desired_tape->label) != 0)){
1312                 if(label){
1313                     fprintf(stderr, "Label mismatch, got %s and expected %s\n", label, desired_tape->label);
1314                     if(have_changer && !backwards){
1315                         fprintf(stderr, "Changer can't go backwards, restoring anyway\n");
1316                     }
1317                     else wrongtape = 1;
1318                 }
1319                 else fprintf(stderr, "No tape device initialized yet\n");
1320             }
1321         }
1322             
1323
1324         /*
1325          * If we have an incorrect tape loaded, go try to find the right one
1326          * (or just see what the next available one is).
1327          */
1328         if((wrongtape || !newtape) && !isafile){
1329             if(desired_tape){
1330                 tapefd_close(tapefd);
1331                 if(have_changer){
1332                     fprintf(stderr,"Looking for tape %s...\n", desired_tape->label);
1333                     if(backwards){
1334                         searchlabel = desired_tape->label; 
1335                         changer_find(NULL, scan_init, loadlabel_slot, desired_tape->label);
1336                     }
1337                     else{
1338                         changer_loadslot("next", &curslot, &cur_tapedev);
1339                     }
1340                     while(have_changer && !cur_tapedev){
1341                         fprintf(stderr, "Changer did not set the tape device (slot empty or changer misconfigured?)\n");
1342                         changer_loadslot("next", &curslot, &cur_tapedev);
1343                     }
1344                 }
1345                 else {
1346                     char *input = NULL;
1347
1348                     if (!flags->amidxtaped) {
1349                         fprintf(prompt_out,
1350                                 "Insert tape labeled %s in device %s "
1351                                 "and press return\n", 
1352                                 desired_tape->label, cur_tapedev);
1353                         fflush(prompt_out);
1354                         input = agets(stdin);
1355                         amfree(input);
1356                     } else if (their_features &&
1357                                am_has_feature(their_features,
1358                                               fe_amrecover_FEEDME)) {
1359                         fprintf(prompt_out, "FEEDME %s\n",
1360                                 desired_tape->label);
1361                         fflush(prompt_out);
1362                         input = agets(stdin); /* Strips \n but not \r */
1363                         if (strcmp("OK\r", input) != 0) {
1364                             error("Got bad response from amrecover: %s",
1365                                   input);
1366                         }
1367                         amfree(input);
1368                     } else {
1369                         error("Client doesn't support fe_amrecover_FEEDME");
1370                     }
1371                 }
1372             }
1373             else{
1374                 assert(!flags->amidxtaped);
1375                 if(have_changer){
1376                     if(slot_num == 0)
1377                         changer_loadslot("first", &curslot, &cur_tapedev);
1378                     else
1379                         changer_loadslot("next", &curslot, &cur_tapedev);
1380                     if(have_changer && !cur_tapedev)
1381                         error("Changer did not set the tape device, probably misconfigured");
1382                 }
1383                 else {
1384                     /* XXX need a condition for ending processing? */
1385                     char *input = NULL;
1386                     fprintf(prompt_out,"Insert a tape to search and press enter, ^D to finish reading tapes\n");
1387                     fflush(prompt_out);
1388                     if((input = agets(stdin)) == NULL) break;
1389                     amfree(input);
1390                 }
1391             }
1392             newtape = 1;
1393             amfree(label);
1394             continue;
1395         }
1396
1397         newtape = 0;
1398
1399         slot_num++;
1400
1401         if(!isafile){
1402             fprintf(stderr, "Scanning %s (slot %s)\n", label, curslot);
1403             fflush(stderr);
1404         }
1405
1406         tape_seen = alloc(sizeof(struct seentapes));
1407         memset(tape_seen, '\0', sizeof(struct seentapes));
1408
1409         tape_seen->label = label;
1410         tape_seen->slotstr = stralloc(curslot);
1411         tape_seen->next = seentapes;
1412         tape_seen->files = NULL;
1413         seentapes = tape_seen;
1414
1415         /*
1416          * Start slogging through the tape itself.  If our tapelist (if we
1417          * have one) contains a list of files to restore, obey that instead
1418          * of checking for matching headers on all files.
1419          */
1420         filenum = 0;
1421         if(desired_tape && desired_tape->numfiles > 0) tapefile_idx = 0;
1422
1423         /* if we know where we're going, fastforward there */
1424         if(flags->fsf && !isafile){
1425             int fsf_by = 0;
1426
1427             /* If we have a tapelist entry, filenums will be store there */
1428             if(tapefile_idx >= 0)
1429                 fsf_by = desired_tape->files[tapefile_idx]; 
1430             /*
1431              * older semantics assume we're restoring one file, with the fsf
1432              * flag being the filenum on tape for said file
1433              */
1434             else fsf_by = flags->fsf;
1435
1436             if(fsf_by > 0){
1437                 if(tapefd_rewind(tapefd) < 0) {
1438                     error("Could not rewind device %s: %s", cur_tapedev,
1439                                                           strerror(errno));
1440                 }
1441
1442                 if(tapefd_fsf(tapefd, fsf_by) < 0) {
1443                     error("Could not fsf device %s by %d: %s", cur_tapedev, fsf_by,
1444                                                            strerror(errno));
1445                 }
1446                 else {
1447                         filenum = fsf_by;
1448                 }
1449                 read_file_header(&file, tapefd, isafile, flags);
1450             }
1451         }
1452
1453         while((file.type == F_TAPESTART || file.type == F_DUMPFILE ||
1454               file.type == F_SPLIT_DUMPFILE) &&
1455               (tapefile_idx < 0 || tapefile_idx < desired_tape->numfiles)) {
1456             int found_match = 0;
1457             match_list_t *me;
1458             dumplist_t *tempdump = NULL;
1459
1460             /* store record of this dump for inventorying purposes */
1461             tempdump = alloc(sizeof(dumplist_t));
1462             tempdump->file = alloc(sizeof(dumpfile_t));
1463             tempdump->next = NULL;
1464             memcpy(tempdump->file, &file, sizeof(dumpfile_t));
1465             if(tape_seen->files){
1466                 for(fileentry=tape_seen->files;
1467                         fileentry->next;
1468                         fileentry=fileentry->next);
1469                 fileentry->next = tempdump;
1470             }
1471             else tape_seen->files = tempdump;
1472
1473             /* see if we need to restore the thing */
1474             if(isafile) found_match = 1;
1475             else if(tapefile_idx >= 0){ /* do it by explicit file #s */
1476                 if(filenum == desired_tape->files[tapefile_idx]){
1477                     found_match = 1;
1478                     tapefile_idx++;
1479                 }
1480             }
1481             else{ /* search and match headers */
1482                 for(me = match_list; me; me = me->next) {
1483                     if(disk_match(&file, me->datestamp, me->hostname,
1484                                 me->diskname, me->level) != 0){
1485                         found_match = 1;
1486                         break;
1487                     }
1488                 }
1489             }
1490
1491             if(found_match){
1492                 char *filename = make_filename(&file);
1493                 fprintf(stderr, "%s: %3d: restoring ", get_pname(), filenum);
1494                 print_header(stderr, &file);
1495                 bytes_read = restore(&file, filename, tapefd, isafile, flags);
1496                 filenum ++;
1497                 amfree(filename);
1498             }
1499
1500             /* advance to the next file, fast-forwarding where reasonable */
1501             if(bytes_read == 0 && !isafile) {
1502                 tapefd_close(tapefd);
1503                 if((tapefd = tape_open(cur_tapedev, 0)) < 0) {
1504                     error("could not open %s: %s",
1505                           cur_tapedev, strerror(errno));
1506                 }
1507             } else if(!isafile){
1508                 /* cheat and jump ahead to where we're going if we can */
1509                 if (!found_match && flags->fsf) {
1510                     drain_file(tapefd, flags);
1511                     filenum ++;
1512                 } else if(tapefile_idx >= 0 && 
1513                           tapefile_idx < desired_tape->numfiles &&
1514                           flags->fsf){
1515                     int fsf_by = desired_tape->files[tapefile_idx] - filenum;
1516                     if(fsf_by > 0){
1517                         if(tapefd_fsf(tapefd, fsf_by) < 0) {
1518                             error("Could not fsf device %s by %d: %s", cur_tapedev, fsf_by,
1519                                   strerror(errno));
1520                         }
1521                         else filenum = desired_tape->files[tapefile_idx];
1522                     }
1523                 } else if (!found_match && flags->fsf) {
1524                     /* ... or fsf by 1, whatever */
1525                     if(tapefd_fsf(tapefd, 1) < 0) {
1526                         error("could not fsf device %s: %s",
1527                               cur_tapedev, strerror(errno));
1528                     } else {
1529                         filenum ++;
1530                     }
1531                 }
1532             }
1533
1534
1535             memcpy(&prev_rst_file, &file, sizeof(dumpfile_t));
1536
1537               
1538             if(isafile)
1539                 break;
1540             read_file_header(&file, tapefd, isafile, flags);
1541
1542             /* only restore a single dump, if piping to stdout */
1543             if(!headers_equal(&prev_rst_file, &file, 1) &&
1544                flags->pipe_to_fd == fileno(stdout)) break;
1545         } /* while we keep seeing headers */
1546
1547         if(!isafile){
1548             if(bytes_read == 0) {
1549                 /* XXX is this dain-bramaged? */
1550                 aclose(tapefd);
1551                 if((tapefd = tape_open(cur_tapedev, 0)) < 0) {
1552                     error("could not open %s: %s",
1553                         cur_tapedev, strerror(errno));
1554                 }
1555             } else{
1556                 if(tapefd_fsf(tapefd, 1) < 0) {
1557                     error("could not fsf %s: %s",
1558                         cur_tapedev, strerror(errno));
1559                 }
1560             }
1561         }
1562         tapefd_close(tapefd);
1563
1564         /* spit out our accumulated list of dumps, if we're inventorying */
1565         if(logstream){
1566             logline = log_genstring(L_START, "taper",
1567                                    "datestamp %s label %s tape %d",
1568                                tapestart.datestamp, tapestart.name, slot_num);
1569             fprintf(logstream, logline);
1570             for(fileentry=tape_seen->files; fileentry; fileentry=fileentry->next){
1571                 logline = NULL;
1572                 switch(fileentry->file->type){
1573                     case F_DUMPFILE:
1574                         logline = log_genstring(L_SUCCESS, "taper",
1575                                        "%s %s %s %d [faked log entry]",
1576                                        fileentry->file->name,
1577                                        fileentry->file->disk,
1578                                        fileentry->file->datestamp,
1579                                        fileentry->file->dumplevel);
1580                     break;
1581                 case F_SPLIT_DUMPFILE:
1582                     logline = log_genstring(L_CHUNK, "taper", 
1583                                        "%s %s %s %d %d [faked log entry]",
1584                                        fileentry->file->name,
1585                                        fileentry->file->disk,
1586                                        fileentry->file->datestamp,
1587                                        fileentry->file->partnum,
1588                                        fileentry->file->dumplevel);
1589                     break;
1590                 default:
1591                     break;
1592                 }
1593                 if(logline){
1594                     fprintf(logstream, logline);
1595                     amfree(logline);
1596                     fflush(logstream);
1597                 }
1598             }
1599         }
1600         fprintf(stderr, "%s: Search of %s complete\n",
1601                         get_pname(), tape_seen->label);
1602         if(desired_tape) desired_tape = desired_tape->next;
1603
1604         /* only restore a single dump, if piping to stdout */
1605         if(!headers_equal(&prev_rst_file, &file, 1) &&
1606           flags->pipe_to_fd == fileno(stdout)) break;
1607     }
1608
1609     while(seentapes != NULL) {
1610         struct seentapes *tape_seen = seentapes;
1611         seentapes = seentapes->next;
1612         while(tape_seen->files != NULL) {
1613             dumplist_t *temp_dump = tape_seen->files;
1614             tape_seen->files = temp_dump->next;
1615             amfree(temp_dump->file);
1616             amfree(temp_dump);
1617         }
1618         amfree(tape_seen->label);
1619         amfree(tape_seen->slotstr);
1620         amfree(tape_seen);
1621         
1622     }
1623
1624     if(logstream && logstream != stderr && logstream != stdout){
1625         fclose(logstream);
1626     }
1627     if(flags->delay_assemble || flags->inline_assemble){
1628         flush_open_outputs(1, NULL);
1629     }
1630     else flush_open_outputs(0, NULL);
1631 }
1632
1633 /*
1634  * Create a new, clean set of restore flags with some sane default values.
1635  */
1636 rst_flags_t *new_rst_flags()
1637 {
1638     rst_flags_t *flags = alloc(sizeof(rst_flags_t));
1639
1640     memset(flags, 0, sizeof(rst_flags_t));
1641
1642     flags->fsf = 1;
1643     flags->comp_type = COMPRESS_FAST_OPT;
1644     flags->inline_assemble = 1;
1645     flags->pipe_to_fd = -1;
1646     flags->check_labels = 1;
1647
1648     return(flags);
1649 }
1650
1651 /*
1652  * Make sure the set of restore options given is sane.  Print errors for
1653  * things that're odd, and return -1 for fatal errors.
1654  */
1655 int check_rst_flags(rst_flags_t *flags)
1656 {
1657     int ret = 0;        
1658     
1659     if(!flags) return(-1);
1660
1661     if(flags->compress && flags->leave_comp){
1662         fprintf(stderr, "Cannot specify 'compress output' and 'leave compression alone' together\n");
1663         ret = -1;
1664     }
1665
1666     if(flags->restore_dir != NULL){
1667         struct stat statinfo;
1668
1669         if(flags->pipe_to_fd != -1){
1670             fprintf(stderr, "Specifying output directory and piping output are mutually exclusive\n");
1671             ret = -1;
1672         }
1673         if(stat(flags->restore_dir, &statinfo) < 0){
1674             fprintf(stderr, "Cannot stat restore target dir '%s': %s\n",
1675                       flags->restore_dir, strerror(errno));
1676             ret = -1;
1677         }
1678         if((statinfo.st_mode & S_IFMT) != S_IFDIR){
1679             fprintf(stderr, "'%s' is not a directory\n", flags->restore_dir);
1680             ret = -1;
1681         }
1682     }
1683
1684     if((flags->pipe_to_fd != -1 || flags->compress) &&
1685             (flags->delay_assemble || !flags->inline_assemble)){
1686         fprintf(stderr, "Split dumps *must* be automatically reassembled when piping output or compressing/uncompressing\n");
1687         ret = -1;
1688     }
1689
1690     if(flags->delay_assemble && flags->inline_assemble){
1691         fprintf(stderr, "Inline split assembling and delayed assembling are mutually exclusive\n");
1692         ret = -1;
1693     }
1694
1695     return(ret);
1696 }
1697
1698 /*
1699  * Clean up after a rst_flags_t
1700  */
1701 void free_rst_flags(flags)
1702 rst_flags_t *flags;
1703 {
1704     if(!flags) return;
1705
1706     if(flags->restore_dir) amfree(flags->restore_dir);
1707     if(flags->alt_tapedev) amfree(flags->alt_tapedev);
1708     if(flags->inventory_log) amfree(flags->inventory_log);
1709
1710     amfree(flags);
1711 }
1712
1713
1714 /*
1715  * Clean up after a match_list_t
1716  */
1717 void free_match_list(match_list)
1718 match_list_t *match_list;
1719 {
1720     match_list_t *me;
1721     match_list_t *prev = NULL;
1722   
1723     for(me = match_list; me; me = me->next){
1724         /* XXX freeing these is broken? can't work out why */
1725 /*      if(me->hostname) amfree(me->hostname);
1726         if(me->diskname) amfree(me->diskname);
1727         if(me->datestamp) amfree(me->datestamp);
1728         if(me->level) amfree(me->level); */
1729         if(prev) amfree(prev);
1730         prev = me;
1731     }
1732     if(prev) amfree(prev);
1733 }