2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: restore.c,v 1.52 2006/08/23 11:41:54 martinea Exp $
29 * retrieves files from an amanda tape
39 #include "fileheader.h"
44 #define LOAD_CHANGER -2
48 /* stuff we're stuck having global */
49 static size_t blocksize = (size_t)SSIZE_MAX;
50 static char *cur_tapedev = NULL;
51 static char *searchlabel = NULL;
53 static int exitassemble = 0;
56 char *rst_conf_logdir = NULL;
57 char *rst_conf_logfile = NULL;
60 typedef struct open_output_s {
61 struct open_output_s *next;
68 typedef struct dumplist_s {
69 struct dumplist_s *next;
73 typedef struct seentapes_s {
74 struct seentapes_s *next;
80 static open_output_t *open_outputs = NULL;
81 static dumplist_t *alldumps_list = NULL;
85 static ssize_t get_block(int tapefd, char *buffer, int isafile);
86 static void append_file_to_fd(char *filename, int fd);
87 static int headers_equal(dumpfile_t *file1, dumpfile_t *file2, int ignore_partnums);
88 static int already_have_dump(dumpfile_t *file);
89 static void handle_sigint(int sig);
90 static int scan_init(void *ud, int rc, int ns, int bk, int s);
91 int loadlabel_slot(void *ud, int rc, char *slotstr, char *device);
92 void drain_file(int tapefd, rst_flags_t *flags);
93 char *label_of_current_slot(char *cur_tapedev, FILE *prompt_out,
94 int *tapefd, dumpfile_t *file, rst_flags_t *flags,
95 am_feature_t *their_features,
96 ssize_t *read_result, tapelist_t *desired_tape);
98 int load_next_tape(char **cur_tapedev, FILE *prompt_out, int backwards,
99 rst_flags_t *flags, am_feature_t *their_features,
100 tapelist_t *desired_tape);
101 int load_manual_tape(char **cur_tapedev, FILE *prompt_out, FILE *prompt_in,
102 rst_flags_t *flags, am_feature_t *their_features,
103 tapelist_t *desired_tape);
104 void search_a_tape(char *cur_tapedev, FILE *prompt_out, rst_flags_t *flags,
105 am_feature_t *their_features, tapelist_t *desired_tape,
106 int isafile, match_list_t *match_list,
107 seentapes_t *tape_seen, dumpfile_t *file,
108 dumpfile_t *prev_rst_file, dumpfile_t *tapestart,
109 int slot_num, ssize_t *read_result);
112 * We might want to flush any open dumps and unmerged splits before exiting
113 * on SIGINT, so do so.
119 (void)sig; /* Quiet unused parameter warning */
121 flush_open_outputs(exitassemble, NULL);
122 if(rst_conf_logfile) unlink(rst_conf_logfile);
129 rst_conf_logdir = getconf_str(CNF_LOGDIR);
130 if (*rst_conf_logdir == '/') {
131 rst_conf_logdir = stralloc(rst_conf_logdir);
133 rst_conf_logdir = stralloc2(config_dir, rst_conf_logdir);
135 rst_conf_logfile = vstralloc(rst_conf_logdir, "/log", NULL);
136 if (access(rst_conf_logfile, F_OK) == 0) {
137 dbprintf(("%s exists: amdump or amflush is already running, "
138 "or you must run amcleanup\n", rst_conf_logfile));
141 log_add(L_INFO, get_pname());
146 * Return 1 if the two fileheaders match in name, disk, type, split chunk part
147 * number, and datestamp, and 0 if not. The part number can be optionally
156 if(!file1 || !file2) return(0);
158 if(file1->dumplevel == file2->dumplevel &&
159 file1->type == file2->type &&
160 !strcmp(file1->datestamp, file2->datestamp) &&
161 !strcmp(file1->name, file2->name) &&
162 !strcmp(file1->disk, file2->disk) &&
163 (ignore_partnums || file1->partnum == file2->partnum)){
171 * See whether we're already pulled an exact copy of the given file (chunk
172 * number and all). Returns 0 if not, 1 if so.
178 dumplist_t *fileentry = NULL;
181 for(fileentry=alldumps_list;fileentry;fileentry=fileentry->next){
182 if(headers_equal(file, fileentry->file, 0)) return(1);
188 * Open the named file and append its contents to the (hopefully open) file
189 * descriptor supplies.
201 if(blocksize == SIZE_MAX)
202 blocksize = DISK_BLOCK_BYTES;
203 buffer = alloc(blocksize);
205 if((tapefd = open(filename, O_RDONLY)) == -1) {
206 error("can't open %s: %s", filename, strerror(errno));
211 bytes_read = get_block(tapefd, buffer, 1); /* same as isafile = 1 */
213 error("read error: %s", strerror(errno));
220 s = fullwrite(fd, buffer, (size_t)bytes_read);
221 if (s < bytes_read) {
222 fprintf(stderr,"Error (%s) offset " OFF_T_FMT "+" OFF_T_FMT ", wrote " OFF_T_FMT "\n",
223 strerror(errno), (OFF_T_FMT_TYPE)wc,
224 (OFF_T_FMT_TYPE)bytes_read, (OFF_T_FMT_TYPE)s);
226 if((errno == EPIPE) || (errno == ECONNRESET)) {
227 error("%s: pipe reader has quit in middle of file.",
231 error("restore: write error = %s", strerror(errno));
234 error("Short write: wrote " SSIZE_T_FMT " bytes expected " SSIZE_T_FMT ".", s, bytes_read);
237 wc += (off_t)bytes_read;
245 * Tape changer support routines, stolen brazenly from amtape
255 (void)ud; /* Quiet unused parameter warning */
256 (void)ns; /* Quiet unused parameter warning */
257 (void)s; /* Quiet unused parameter warning */
260 error("could not get changer info: %s", changer_resultstr);
276 char *datestamp = NULL;
279 (void)ud; /* Quiet unused parameter warning */
282 error("could not load slot %s: %s", slotstr, changer_resultstr);
285 fprintf(stderr, "%s: slot %s: %s\n",
286 get_pname(), slotstr, changer_resultstr);
287 } else if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL) {
288 fprintf(stderr, "%s: slot %s: %s\n", get_pname(), slotstr, errstr);
290 if(strlen(datestamp)>8)
291 fprintf(stderr, "%s: slot %s: date %-14s label %s",
292 get_pname(), slotstr, datestamp, label);
294 fprintf(stderr, "%s: slot %s: date %-8s label %s",
295 get_pname(), slotstr, datestamp, label);
296 if(strcmp(label, FAKE_LABEL) != 0
297 && strcmp(label, searchlabel) != 0)
298 fprintf(stderr, " (wrong tape)\n");
300 fprintf(stderr, " (exact label match)\n");
301 if((errstr = tape_rewind(device)) != NULL) {
303 "%s: could not rewind %s: %s",
304 get_pname(), device, errstr);
308 curslot = newstralloc(curslot, slotstr);
312 cur_tapedev = stralloc(device);
320 curslot = newstralloc(curslot, slotstr);
321 if(!device) return(1);
322 cur_tapedev = stralloc(device);
328 /* non-local functions follow */
333 * Check whether we've read all of the preceding parts of a given split dump,
334 * generally used to see if we're done and can close the thing.
342 int *foundparts = NULL;
343 dumplist_t *fileentry = NULL;
345 if(!file || file->partnum < 1) return(0);
347 if(upto < 1) upto = file->totalparts;
349 foundparts = alloc(SIZEOF(*foundparts) * upto);
350 for(c = 0 ; c< upto; c++) foundparts[c] = 0;
352 for(fileentry=alldumps_list;fileentry; fileentry=fileentry->next){
353 dumpfile_t *cur_file = fileentry->file;
354 if(headers_equal(file, cur_file, 1)){
355 if(cur_file->partnum > upto){
360 foundparts[cur_file->partnum - 1] = 1;
364 for(c = 0 ; c< upto; c++){
376 * Free up the open filehandles and memory we were using to track in-progress
377 * dumpfiles (generally for split ones we're putting back together). If
378 * applicable, also find the ones that are continuations of one another and
379 * string them together. If given an optional file header argument, flush
380 * only that dump and do not flush/free any others.
385 dumpfile_t *only_file)
387 open_output_t *cur_out = NULL, *prev = NULL;
388 find_result_t *sorted_files = NULL;
389 amwait_t compress_status;
392 fprintf(stderr, "\n");
396 * Deal with any split dumps we've been working on, appending pieces
397 * that haven't yet been appended and closing filehandles we've been
401 find_result_t *cur_find_res = NULL;
402 int outfd = -1, lastpartnum = -1;
403 dumpfile_t *main_file = NULL;
404 cur_out = open_outputs;
406 /* stick the dumpfile_t's into a list find_result_t's so that we can
407 abuse existing sort functionality */
408 for(cur_out=open_outputs; cur_out; cur_out=cur_out->next){
409 find_result_t *cur_find_res = NULL;
410 dumpfile_t *cur_file = cur_out->file;
411 /* if we requested a particular file, do only that one */
412 if(only_file && !headers_equal(cur_file, only_file, 1)){
415 cur_find_res = alloc(SIZEOF(find_result_t));
416 memset(cur_find_res, '\0', SIZEOF(find_result_t));
417 cur_find_res->timestamp = stralloc(cur_file->datestamp);
418 cur_find_res->hostname = stralloc(cur_file->name);
419 cur_find_res->diskname = stralloc(cur_file->disk);
420 cur_find_res->level = cur_file->dumplevel;
421 if(cur_file->partnum < 1) cur_find_res->partnum = stralloc("--");
423 char part_str[NUM_STR_SIZE];
424 snprintf(part_str, SIZEOF(part_str), "%d", cur_file->partnum);
425 cur_find_res->partnum = stralloc(part_str);
427 cur_find_res->user_ptr = (void*)cur_out;
429 cur_find_res->next = sorted_files;
430 sorted_files = cur_find_res;
432 sort_find_result("hkdlp", &sorted_files);
434 /* now we have an in-order list of the files we need to concatenate */
435 cur_find_res = sorted_files;
436 for(cur_find_res=sorted_files;
438 cur_find_res=cur_find_res->next){
439 dumpfile_t *cur_file = NULL;
440 cur_out = (open_output_t*)cur_find_res->user_ptr;
441 cur_file = cur_out->file;
443 /* if we requested a particular file, do only that one */
444 if(only_file && !headers_equal(cur_file, only_file, 1)){
448 if(cur_file->type == F_SPLIT_DUMPFILE) {
449 /* is it a continuation of one we've been writing? */
450 if(main_file && cur_file->partnum > lastpartnum &&
451 headers_equal(cur_file, main_file, 1)){
455 /* effectively changing filehandles */
456 aclose(cur_out->outfd);
457 cur_out->outfd = outfd;
459 cur_filename = make_filename(cur_file);
460 main_filename = make_filename(main_file);
461 fprintf(stderr, "Merging %s with %s\n",
462 cur_filename, main_filename);
463 append_file_to_fd(cur_filename, outfd);
464 if(unlink(cur_filename) < 0){
465 fprintf(stderr, "Failed to unlink %s: %s\n",
466 cur_filename, strerror(errno));
468 amfree(cur_filename);
469 amfree(main_filename);
473 if(outfd >= 0) aclose(outfd);
475 main_file = alloc(SIZEOF(dumpfile_t));
476 memcpy(main_file, cur_file, SIZEOF(dumpfile_t));
477 outfd = cur_out->outfd;
479 char *cur_filename = make_filename(cur_file);
480 open(cur_filename, O_RDWR|O_APPEND);
482 error("Couldn't open %s for appending: %s",
483 cur_filename, strerror(errno));
486 amfree(cur_filename);
489 lastpartnum = cur_file->partnum;
492 aclose(cur_out->outfd);
500 free_find_result(&sorted_files);
504 * Now that the split dump closure is done, free up resources we don't
507 for(cur_out=open_outputs; cur_out; cur_out=cur_out->next){
508 dumpfile_t *cur_file = NULL;
510 cur_file = cur_out->file;
511 /* if we requested a particular file, do only that one */
512 if(only_file && !headers_equal(cur_file, only_file, 1)){
516 aclose(cur_out->outfd);
519 if(cur_out->comp_enc_pid > 0){
520 waitpid(cur_out->comp_enc_pid, &compress_status, 0);
522 amfree(cur_out->file);
530 * Turn a fileheader into a string suited for use on the filesystem.
536 char number[NUM_STR_SIZE];
537 char part[NUM_STR_SIZE];
538 char totalparts[NUM_STR_SIZE];
544 snprintf(number, SIZEOF(number), "%d", file->dumplevel);
545 snprintf(part, SIZEOF(part), "%d", file->partnum);
547 if(file->totalparts < 0) {
548 snprintf(totalparts, SIZEOF(totalparts), "UNKNOWN");
551 snprintf(totalparts, SIZEOF(totalparts), "%d", file->totalparts);
553 padlen = strlen(totalparts) + 1 - strlen(part);
555 memset(pad, '0', padlen);
556 pad[padlen - 1] = '\0';
558 snprintf(part, SIZEOF(part), "%s%d", pad, file->partnum);
560 sfn = sanitise_filename(file->disk);
561 fn = vstralloc(file->name,
569 if (file->partnum > 0) {
570 vstrextend(&fn, ".", part, NULL);
579 * XXX Making this thing a lib functiong broke a lot of assumptions everywhere,
580 * but I think I've found them all. Maybe. Damn globals all over the place.
590 return (fullread(tapefd, buffer, blocksize));
592 return(tapefd_read(tapefd, buffer, blocksize));
596 * Returns 1 if the current dump file matches the hostname and diskname
597 * regular expressions given on the command line, 0 otherwise. As a
598 * special case, empty regexs are considered equivalent to ".*": they
610 char level_str[NUM_STR_SIZE];
611 snprintf(level_str, SIZEOF(level_str), "%d", file->dumplevel);
613 if(file->type != F_DUMPFILE && file->type != F_SPLIT_DUMPFILE) return 0;
615 if((*hostname == '\0' || match_host(hostname, file->name)) &&
616 (*diskname == '\0' || match_disk(diskname, file->disk)) &&
617 (*datestamp == '\0' || match_datestamp(datestamp, file->datestamp)) &&
618 (*level == '\0' || match_level(level, level_str)))
626 * Reads the first block of a tape file.
639 if(flags->blocksize > 0)
640 blocksize = (size_t)flags->blocksize;
641 else if(blocksize == (size_t)SSIZE_MAX)
642 blocksize = DISK_BLOCK_BYTES;
643 buffer = alloc(blocksize);
645 bytes_read = get_block(tapefd, buffer, isafile);
647 fprintf(stderr, "%s: error reading file header: %s\n",
648 get_pname(), strerror(errno));
649 file->type = F_UNKNOWN;
650 } else if((size_t)bytes_read < DISK_BLOCK_BYTES) {
651 if(bytes_read == 0) {
652 fprintf(stderr, "%s: missing file header block\n", get_pname());
654 fprintf(stderr, "%s: short file header block: " OFF_T_FMT " byte%s\n",
655 get_pname(), (OFF_T_FMT_TYPE)bytes_read, (bytes_read == 1) ? "" : "s");
657 file->type = F_UNKNOWN;
659 parse_file_header(buffer, file, (size_t)bytes_read);
675 blocksize = (size_t)flags->blocksize;
676 else if(blocksize == (size_t)SSIZE_MAX)
677 blocksize = DISK_BLOCK_BYTES;
678 buffer = alloc(blocksize);
681 bytes_read = get_block(tapefd, buffer, 0);
683 error("drain read error: %s", strerror(errno));
686 } while (bytes_read > 0);
692 * Restore the current file from tape. Depending on the settings of
693 * the command line flags, the file might need to be compressed or
694 * uncompressed. If so, a pipe through compress or uncompress is set
695 * up. The final output usually goes to a file named host.disk.date.lev,
696 * but with the -p flag the output goes to stdout (and presumably is
710 int file_is_compressed;
711 int is_continuation = 0;
712 int check_for_aborted = 0;
713 char *tmp_filename = NULL, *final_filename = NULL;
714 struct stat statinfo;
715 open_output_t *myout = NULL, *oldout = NULL;
716 dumplist_t *tempdump = NULL, *fileentry = NULL;
718 int need_compress=0, need_uncompress=0, need_decrypt=0;
725 memset(pipes, -1, SIZEOF(pipes));
727 blocksize = (size_t)flags->blocksize;
728 else if(blocksize == (size_t)SSIZE_MAX)
729 blocksize = DISK_BLOCK_BYTES;
731 if(already_have_dump(file)){
732 char *filename = make_filename(file);
733 fprintf(stderr, " *** Duplicate file %s, one is probably an aborted write\n", filename);
735 check_for_aborted = 1;
738 /* store a shorthand record of this dump */
739 tempdump = alloc(SIZEOF(dumplist_t));
740 tempdump->file = alloc(SIZEOF(dumpfile_t));
741 tempdump->next = NULL;
742 memcpy(tempdump->file, file, SIZEOF(dumpfile_t));
745 * If we're appending chunked files to one another, and if this is a
746 * continuation of a file we just restored, and we've still got the
747 * output handle from that previous restore, we're golden. Phew.
749 if(flags->inline_assemble && file->type == F_SPLIT_DUMPFILE){
750 myout = open_outputs;
751 while(myout != NULL){
752 if(myout->file->type == F_SPLIT_DUMPFILE &&
753 headers_equal(file, myout->file, 1)){
754 if(file->partnum == myout->lastpartnum + 1){
761 if(myout != NULL) myout->lastpartnum = file->partnum;
762 else if(file->partnum != 1){
763 fprintf(stderr, "%s: Chunk out of order, will save to disk and append to output.\n", get_pname());
764 flags->pipe_to_fd = -1;
766 flags->leave_comp = 1;
769 myout = alloc(SIZEOF(open_output_t));
770 memset(myout, 0, SIZEOF(open_output_t));
774 myout = alloc(SIZEOF(open_output_t));
775 memset(myout, 0, SIZEOF(open_output_t));
779 if(is_continuation && flags->pipe_to_fd == -1){
781 filename = make_filename(myout->file);
782 fprintf(stderr, "%s: appending to %s\n", get_pname(),
787 /* adjust compression flag */
788 file_is_compressed = file->compressed;
789 if(!flags->compress && file_is_compressed && !known_compress_type(file)) {
791 "%s: unknown compression suffix %s, can't uncompress\n",
792 get_pname(), file->comp_suffix);
796 /* set up final destination file */
798 if(is_continuation && myout != NULL) {
801 if(flags->pipe_to_fd != -1) {
802 dest = flags->pipe_to_fd; /* standard output */
804 char *filename_ext = NULL;
806 if(flags->compress) {
807 filename_ext = file_is_compressed ? file->comp_suffix
809 } else if(flags->raw) {
810 filename_ext = ".RAW";
814 filename_ext = stralloc2(filename, filename_ext);
815 tmp_filename = stralloc(filename_ext);
816 if(flags->restore_dir != NULL) {
817 char *tmpstr = vstralloc(flags->restore_dir, "/",
819 amfree(tmp_filename);
820 tmp_filename = tmpstr;
822 final_filename = tmp_filename;
823 tmp_filename = vstralloc(final_filename, ".tmp", NULL);
824 if((dest = open(tmp_filename, (O_CREAT | O_RDWR | O_TRUNC),
826 error("could not create output file %s: %s",
827 tmp_filename, strerror(errno));
830 amfree(filename_ext);
837 * If -r or -h, write the header before compress or uncompress pipe.
838 * Only write DISK_BLOCK_BYTES, regardless of how much was read.
839 * This makes the output look like a holding disk image, and also
840 * makes it easier to remove the header (e.g. in amrecover) since
841 * it has a fixed size.
843 if(flags->raw || (flags->headers && !is_continuation)) {
847 if(flags->compress && !file_is_compressed) {
848 file->compressed = 1;
849 snprintf(file->uncompress_cmd, SIZEOF(file->uncompress_cmd),
850 " %s %s |", UNCOMPRESS_PATH,
851 #ifdef UNCOMPRESS_OPT
857 strncpy(file->comp_suffix,
859 SIZEOF(file->comp_suffix)-1);
860 file->comp_suffix[SIZEOF(file->comp_suffix)-1] = '\0';
863 memcpy(&tmp_hdr, file, SIZEOF(dumpfile_t));
865 /* remove CONT_FILENAME from header */
866 memset(file->cont_filename,'\0',SIZEOF(file->cont_filename));
867 file->blocksize = DISK_BLOCK_BYTES;
870 * Dumb down split file headers as well, so that older versions of
871 * things like amrecover won't gag on them.
873 if(file->type == F_SPLIT_DUMPFILE && flags->mask_splits){
874 file->type = F_DUMPFILE;
877 buffer = alloc(DISK_BLOCK_BYTES);
878 build_header(buffer, file, DISK_BLOCK_BYTES);
880 if((w = fullwrite(out, buffer, DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
882 error("write error: %s", strerror(errno));
885 error("write error: " SSIZE_T_FMT " instead of %d", w, DISK_BLOCK_BYTES);
891 memcpy(file, &tmp_hdr, SIZEOF(dumpfile_t));
894 /* find out if compression or uncompression is needed here */
895 if(flags->compress && !file_is_compressed && !is_continuation
896 && !flags->leave_comp
897 && (flags->inline_assemble || file->type != F_SPLIT_DUMPFILE))
900 if(!flags->raw && !flags->compress && file_is_compressed
901 && !is_continuation && !flags->leave_comp && (flags->inline_assemble
902 || file->type != F_SPLIT_DUMPFILE))
905 if(!flags->raw && file->encrypted && !is_continuation
906 && (flags->inline_assemble || file->type != F_SPLIT_DUMPFILE))
909 /* Setup pipes for decryption / compression / uncompression */
912 if (pipe(&pipes[stage].pipe[0]) < 0) {
913 error("error [pipe[%d]: %s]", stage, strerror(errno));
919 if (need_compress || need_uncompress) {
920 if (pipe(&pipes[stage].pipe[0]) < 0) {
921 error("error [pipe[%d]: %s]", stage, strerror(errno));
926 pipes[stage].pipe[0] = -1;
927 pipes[stage].pipe[1] = out;
931 /* decrypt first if it's encrypted and no -r */
933 switch(myout->comp_enc_pid = fork()) {
935 error("could not fork for decrypt: %s", strerror(errno));
939 aclose(pipes[stage].pipe[0]);
940 aclose(pipes[stage+1].pipe[1]);
945 if(dup2(pipes[stage].pipe[0], 0) == -1) {
946 error("error decrypt stdin [dup2 %d %d: %s]", stage,
947 pipes[stage].pipe[0], strerror(errno));
951 if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
952 error("error decrypt stdout [dup2 %d %d: %s]", stage + 1,
953 pipes[stage+1].pipe[1], strerror(errno));
958 if (*file->srv_encrypt) {
959 (void) execlp(file->srv_encrypt, file->srv_encrypt,
960 file->srv_decrypt_opt, (char *)NULL);
961 error("could not exec %s: %s", file->srv_encrypt, strerror(errno));
963 } else if (*file->clnt_encrypt) {
964 (void) execlp(file->clnt_encrypt, file->clnt_encrypt,
965 file->clnt_decrypt_opt, (char *)NULL);
966 error("could not exec %s: %s", file->clnt_encrypt, strerror(errno));
974 * Insert a compress pipe
976 switch(myout->comp_enc_pid = fork()) {
978 error("could not fork for %s: %s", COMPRESS_PATH, strerror(errno));
982 aclose(pipes[stage].pipe[0]);
983 aclose(pipes[stage+1].pipe[1]);
988 if(dup2(pipes[stage].pipe[0], 0) == -1) {
989 error("error compress stdin [dup2 %d %d: %s]", stage,
990 pipes[stage].pipe[0], strerror(errno));
994 if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
995 error("error compress stdout [dup2 %d %d: %s]", stage + 1,
996 pipes[stage+1].pipe[1], strerror(errno));
999 if (*flags->comp_type == '\0') {
1000 flags->comp_type = NULL;
1004 (void) execlp(COMPRESS_PATH, COMPRESS_PATH, flags->comp_type, (char *)0);
1005 error("could not exec %s: %s", COMPRESS_PATH, strerror(errno));
1008 } else if(need_uncompress) {
1010 * If not -r, -c, -l, and file is compressed, and split reassembly
1011 * options are sane, insert uncompress pipe
1015 * XXX for now we know that for the two compression types we
1016 * understand, .Z and optionally .gz, UNCOMPRESS_PATH will take
1017 * care of both. Later, we may need to reference a table of
1018 * possible uncompress programs.
1020 switch(myout->comp_enc_pid = fork()) {
1022 error("could not fork for %s: %s",
1023 UNCOMPRESS_PATH, strerror(errno));
1027 aclose(pipes[stage].pipe[0]);
1028 aclose(pipes[stage+1].pipe[1]);
1033 if(dup2(pipes[stage].pipe[0], 0) == -1) {
1034 error("error uncompress stdin [dup2 %d %d: %s]", stage,
1035 pipes[stage].pipe[0], strerror(errno));
1039 if(dup2(pipes[stage+1].pipe[1], 1) == -1) {
1040 error("error uncompress stdout [dup2 %d %d: %s]", stage + 1,
1041 pipes[stage+1].pipe[1], strerror(errno));
1046 if (*file->srvcompprog) {
1047 (void) execlp(file->srvcompprog, file->srvcompprog, "-d",
1049 error("could not exec %s: %s", file->srvcompprog,
1052 } else if (*file->clntcompprog) {
1053 (void) execlp(file->clntcompprog, file->clntcompprog, "-d",
1055 error("could not exec %s: %s", file->clntcompprog,
1059 (void) execlp(UNCOMPRESS_PATH, UNCOMPRESS_PATH,
1060 #ifdef UNCOMPRESS_OPT
1064 error("could not exec %s: %s", UNCOMPRESS_PATH, strerror(errno));
1070 /* copy the rest of the file from tape to the output */
1071 if(flags->blocksize > 0)
1072 blocksize = (size_t)flags->blocksize;
1073 else if(blocksize == SIZE_MAX)
1074 blocksize = DISK_BLOCK_BYTES;
1075 buffer = alloc(blocksize);
1078 bytes_read = get_block(tapefd, buffer, isafile);
1079 if(bytes_read < 0) {
1080 error("restore read error: %s", strerror(errno));
1084 if(bytes_read > 0) {
1085 if((s = fullwrite(pipes[0].pipe[1], buffer, (size_t)bytes_read)) < 0) {
1086 if ((errno == EPIPE) || (errno == ECONNRESET)) {
1088 * reading program has ended early
1089 * e.g: bzip2 closes pipe when it
1090 * trailing garbage after EOF
1094 error("restore: write error: %s", strerror(errno));
1096 } else if (s < bytes_read) {
1097 error("restore: wrote " SSIZE_T_FMT " of " SSIZE_T_FMT " bytes: %s",
1098 s, bytes_read, strerror(errno));
1104 * See if we need to switch to the next file in a holding restore
1106 if(file->cont_filename[0] == '\0') {
1107 break; /* no more files */
1110 if((tapefd = open(file->cont_filename, O_RDONLY)) == -1) {
1111 char *cont_filename = strrchr(file->cont_filename,'/');
1114 if((tapefd = open(cont_filename,O_RDONLY)) == -1) {
1115 error("can't open %s: %s", file->cont_filename,
1120 fprintf(stderr, "cannot open %s: %s\n",
1121 file->cont_filename, strerror(errno));
1122 fprintf(stderr, "using %s\n",
1127 error("can't open %s: %s", file->cont_filename,
1132 bytes_read = read_file_header(file, tapefd, isafile, flags);
1133 if(file->type != F_DUMPFILE && file->type != F_CONT_DUMPFILE
1134 && file->type != F_SPLIT_DUMPFILE) {
1135 fprintf(stderr, "unexpected header type: ");
1136 print_header(stderr, file);
1140 } while (bytes_read > 0);
1144 if(!flags->inline_assemble) {
1148 if(!is_continuation){
1149 if(tmp_filename && stat(tmp_filename, &statinfo) < 0){
1150 error("Can't stat the file I just created (%s)!", tmp_filename);
1153 statinfo.st_size = (off_t)0;
1155 if (check_for_aborted && final_filename) {
1156 char *old_dump = final_filename;
1157 struct stat oldstat;
1158 if(stat(old_dump, &oldstat) >= 0){
1159 if(oldstat.st_size <= statinfo.st_size){
1160 dumplist_t *prev_fileentry = NULL;
1161 open_output_t *prev_out = NULL;
1162 fprintf(stderr, "Newer restore is larger, using that\n");
1163 /* nuke the old dump's entry in alldump_list */
1164 for(fileentry=alldumps_list;
1166 fileentry=fileentry->next){
1167 if(headers_equal(file, fileentry->file, 0)){
1169 prev_fileentry->next = fileentry->next;
1172 alldumps_list = fileentry->next;
1177 prev_fileentry = fileentry;
1179 myout = open_outputs;
1180 while(myout != NULL){
1181 if(headers_equal(file, myout->file, 0)){
1182 if(myout->outfd >= 0)
1183 aclose(myout->outfd);
1185 prev_out->next = myout->next;
1187 else open_outputs = myout->next;
1192 myout = myout->next;
1196 fprintf(stderr, "Older restore is larger, using that\n");
1198 unlink(tmp_filename);
1199 amfree(tempdump->file);
1201 amfree(tmp_filename);
1202 amfree(final_filename);
1203 return (bytes_read);
1207 if(tmp_filename && final_filename &&
1208 rename(tmp_filename, final_filename) < 0) {
1209 error("Can't rename %s to %s: %s",
1210 tmp_filename, final_filename, strerror(errno));
1214 amfree(tmp_filename);
1215 amfree(final_filename);
1219 * actually insert tracking data for this file into our various
1220 * structures (we waited in case we needed to give up)
1222 if(!is_continuation){
1223 oldout = alloc(SIZEOF(open_output_t));
1224 oldout->file = alloc(SIZEOF(dumpfile_t));
1225 memcpy(oldout->file, file, SIZEOF(dumpfile_t));
1226 if(flags->inline_assemble) oldout->outfd = pipes[0].pipe[1];
1227 else oldout->outfd = -1;
1228 oldout->comp_enc_pid = -1;
1229 oldout->lastpartnum = file->partnum;
1230 oldout->next = open_outputs;
1231 open_outputs = oldout;
1234 fileentry = alldumps_list;
1235 while (fileentry->next != NULL)
1236 fileentry=fileentry->next;
1237 fileentry->next = tempdump;
1240 alldumps_list = tempdump;
1243 return (bytes_read);
1246 /* return NULL if the label is not the expected one */
1247 /* return the label if it is the expected one, and set *tapefd to a */
1248 /* file descriptor to the tapedev */
1250 label_of_current_slot(
1256 am_feature_t *their_features,
1257 ssize_t *read_result,
1258 tapelist_t *desired_tape)
1260 struct stat stat_tape;
1266 send_message(prompt_out, flags, their_features,
1267 "no tapedev specified");
1268 } else if (tape_stat(cur_tapedev, &stat_tape) !=0 ) {
1269 send_message(prompt_out, flags, their_features,
1270 "could not stat '%s': %s",
1271 cur_tapedev, strerror(errno));
1273 } else if((err = tape_rewind(cur_tapedev)) != NULL) {
1274 send_message(prompt_out, flags, their_features,
1275 "Could not rewind device '%s': %s",
1278 /* err should not be freed */
1279 } else if((*tapefd = tape_open(cur_tapedev, 0)) < 0){
1280 send_message(prompt_out, flags, their_features,
1281 "could not open tape device %s: %s",
1282 cur_tapedev, strerror(errno));
1287 *read_result = read_file_header(file, *tapefd, 0, flags);
1288 if (file->type != F_TAPESTART) {
1289 send_message(prompt_out, flags, their_features,
1290 "Not an amanda tape");
1291 tapefd_close(*tapefd);
1293 if (flags->check_labels && desired_tape &&
1294 strcmp(file->name, desired_tape->label) != 0) {
1295 send_message(prompt_out, flags, their_features,
1296 "Label mismatch, got %s and expected %s",
1297 file->name, desired_tape->label);
1298 tapefd_close(*tapefd);
1301 label = stralloc(file->name);
1308 /* return >0 the number of slot move */
1309 /* return LOAD_STOP if the search must be stopped */
1310 /* return LOAD_CHANGER if the changer search the library */
1317 am_feature_t *their_features,
1318 tapelist_t *desired_tape)
1323 send_message(prompt_out, flags, their_features,
1324 "Looking for tape %s...",
1325 desired_tape->label);
1327 searchlabel = desired_tape->label;
1328 changer_find(NULL, scan_init, loadlabel_slot,
1329 desired_tape->label);
1333 changer_loadslot("next", &curslot,
1338 assert(!flags->amidxtaped);
1340 changer_loadslot("next", &curslot, cur_tapedev);
1347 /* return 0 a new tape is loaded */
1348 /* return -1 no new tape */
1355 am_feature_t *their_features,
1356 tapelist_t *desired_tape)
1361 if (flags->amidxtaped) {
1362 if (their_features &&
1363 am_has_feature(their_features,
1364 fe_amrecover_FEEDME)) {
1365 fprintf(prompt_out, "FEEDME %s\r\n",
1366 desired_tape->label);
1368 input = agets(prompt_in);/* Strips \n but not \r */
1370 error("Connection lost with amrecover");
1372 } else if (strcmp("OK\r", input) == 0) {
1373 } else if (strncmp("TAPE ", input, 5) == 0) {
1374 amfree(*cur_tapedev);
1375 *cur_tapedev = alloc(1025);
1376 if (sscanf(input, "TAPE %1024s\r", *cur_tapedev) != 1) {
1377 error("Got bad response from amrecover: %s", input);
1381 send_message(prompt_out, flags, their_features,
1382 "Got bad response from amrecover: %s", input);
1383 error("Got bad response from amrecover: %s", input);
1387 send_message(prompt_out, flags, their_features,
1388 "Client doesn't support fe_amrecover_FEEDME");
1389 error("Client doesn't support fe_amrecover_FEEDME");
1396 "Insert tape labeled %s in device %s \n"
1397 "and press enter, ^D to finish reading tapes\n",
1398 desired_tape->label, *cur_tapedev);
1400 fprintf(prompt_out,"Insert a tape to search and press "
1401 "enter, ^D to finish reading tapes\n");
1404 if((input = agets(prompt_in)) == NULL)
1418 am_feature_t *their_features,
1419 tapelist_t *desired_tape,
1421 match_list_t *match_list,
1422 seentapes_t *tape_seen,
1424 dumpfile_t *prev_rst_file,
1425 dumpfile_t *tapestart,
1427 ssize_t *read_result)
1430 dumplist_t *fileentry = NULL;
1431 int tapefile_idx = -1;
1433 char *logline = NULL;
1434 FILE *logstream = NULL;
1438 if(desired_tape && desired_tape->numfiles > 0)
1442 dbprintf(("search_a_tape: desired_tape=%p label=%s\n",
1443 desired_tape, desired_tape->label));
1444 dbprintf(("tape: numfiles = %d\n", desired_tape->numfiles));
1445 for (i=0; i < desired_tape->numfiles; i++) {
1446 dbprintf(("tape: files[%d] = " OFF_T_FMT "\n",
1447 i, (OFF_T_FMT_TYPE)desired_tape->files[i]));
1450 dbprintf(("search_a_tape: no desired_tape\n"));
1452 dbprintf(("current tapefile_idx = %d\n", tapefile_idx));
1454 /* if we know where we're going, fastforward there */
1455 if(flags->fsf && !isafile){
1456 /* If we have a tapelist entry, filenums will be store there */
1457 if(tapefile_idx >= 0) {
1458 fsf_by = desired_tape->files[tapefile_idx];
1461 * older semantics assume we're restoring one file, with the fsf
1462 * flag being the filenum on tape for said file
1464 fsf_by = (flags->fsf == 0) ? (off_t)0 : (off_t)1;
1466 if(fsf_by > (off_t)0){
1467 if(tapefd_rewind(tapefd) < 0) {
1468 send_message(prompt_out, flags, their_features,
1469 "Could not rewind device %s: %s",
1470 cur_tapedev, strerror(errno));
1471 error("Could not rewind device %s: %s",
1472 cur_tapedev, strerror(errno));
1476 if(tapefd_fsf(tapefd, fsf_by) < 0) {
1477 send_message(prompt_out, flags, their_features,
1478 "Could not fsf device %s by " OFF_T_FMT ": %s",
1479 cur_tapedev, (OFF_T_FMT_TYPE)fsf_by,
1481 error("Could not fsf device %s by " OFF_T_FMT ": %s",
1482 cur_tapedev, (OFF_T_FMT_TYPE)fsf_by,
1489 *read_result = read_file_header(file, tapefd, isafile, flags);
1493 while((file->type == F_TAPESTART || file->type == F_DUMPFILE ||
1494 file->type == F_SPLIT_DUMPFILE) &&
1495 (tapefile_idx < 0 || tapefile_idx < desired_tape->numfiles)) {
1496 int found_match = 0;
1498 dumplist_t *tempdump = NULL;
1500 /* store record of this dump for inventorying purposes */
1501 tempdump = alloc(SIZEOF(dumplist_t));
1502 tempdump->file = alloc(SIZEOF(dumpfile_t));
1503 tempdump->next = NULL;
1504 memcpy(tempdump->file, &file, SIZEOF(dumpfile_t));
1505 if(tape_seen->files){
1506 fileentry = tape_seen->files;
1507 while (fileentry->next != NULL)
1508 fileentry = fileentry->next;
1509 fileentry->next = tempdump;
1512 tape_seen->files = tempdump;
1515 /* see if we need to restore the thing */
1518 else if(tapefile_idx >= 0){ /* do it by explicit file #s */
1519 if(filenum == desired_tape->files[tapefile_idx]){
1524 else{ /* search and match headers */
1525 for(me = match_list; me; me = me->next) {
1526 if(disk_match(file, me->datestamp, me->hostname,
1527 me->diskname, me->level) != 0){
1535 char *filename = make_filename(file);
1537 fprintf(stderr, "%s: " OFF_T_FMT ": restoring ",
1538 get_pname(), (OFF_T_FMT_TYPE)filenum);
1539 print_header(stderr, file);
1540 *read_result = restore(file, filename, tapefd, isafile, flags);
1545 /* advance to the next file, fast-forwarding where reasonable */
1547 if (*read_result == 0) {
1548 tapefd_close(tapefd);
1549 if((tapefd = tape_open(cur_tapedev, 0)) < 0) {
1550 send_message(prompt_out, flags, their_features,
1551 "could not open %s: %s",
1552 cur_tapedev, strerror(errno));
1553 error("could not open %s: %s",
1554 cur_tapedev, strerror(errno));
1557 /* if the file is not what we're looking for fsf to next one */
1559 else if (!found_match) {
1560 if (tapefd_fsf(tapefd, (off_t)1) < 0) {
1561 send_message(prompt_out, flags, their_features,
1562 "Could not fsf device %s: %s",
1563 cur_tapedev, strerror(errno));
1564 error("Could not fsf device %s: %s",
1565 cur_tapedev, strerror(errno));
1570 else if (flags->fsf && (tapefile_idx >= 0) &&
1571 (tapefile_idx < desired_tape->numfiles)) {
1572 fsf_by = desired_tape->files[tapefile_idx] - filenum;
1573 if (fsf_by > (off_t)0) {
1574 if(tapefd_fsf(tapefd, fsf_by) < 0) {
1575 send_message(prompt_out, flags, their_features,
1576 "Could not fsf device %s by "
1578 cur_tapedev, (OFF_T_FMT_TYPE)fsf_by,
1580 error("Could not fsf device %s by " OFF_T_FMT ": %s",
1581 cur_tapedev, (OFF_T_FMT_TYPE)fsf_by,
1585 filenum = desired_tape->files[tapefile_idx];
1590 memcpy(prev_rst_file, file, SIZEOF(dumpfile_t));
1594 *read_result = read_file_header(file, tapefd, isafile, flags);
1596 /* only restore a single dump, if piping to stdout */
1597 if (!headers_equal(prev_rst_file, file, 1) &&
1598 (flags->pipe_to_fd == fileno(stdout)) && found_match) {
1601 } /* while we keep seeing headers */
1604 if (file->type == F_EMPTY) {
1606 if((tapefd = tape_open(cur_tapedev, 0)) < 0) {
1607 send_message(prompt_out, flags, their_features,
1608 "could not open %s: %s",
1609 cur_tapedev, strerror(errno));
1610 error("could not open %s: %s",
1611 cur_tapedev, strerror(errno));
1615 if (tapefd_fsf(tapefd, (off_t)1) < 0) {
1616 send_message(prompt_out, flags, their_features,
1617 "could not fsf %s: %s",
1618 cur_tapedev, strerror(errno));;
1619 error("could not fsf %s: %s",
1620 cur_tapedev, strerror(errno));
1625 tapefd_close(tapefd);
1627 /* spit out our accumulated list of dumps, if we're inventorying */
1629 logline = log_genstring(L_START, "taper",
1630 "datestamp %s label %s tape %d",
1631 tapestart->datestamp, tapestart->name,
1633 fprintf(logstream, "%s", logline);
1634 for(fileentry=tape_seen->files; fileentry; fileentry=fileentry->next){
1636 switch (fileentry->file->type) {
1638 logline = log_genstring(L_SUCCESS, "taper",
1639 "%s %s %s %d [faked log entry]",
1640 fileentry->file->name,
1641 fileentry->file->disk,
1642 fileentry->file->datestamp,
1643 fileentry->file->dumplevel);
1645 case F_SPLIT_DUMPFILE:
1646 logline = log_genstring(L_CHUNK, "taper",
1647 "%s %s %s %d %d [faked log entry]",
1648 fileentry->file->name,
1649 fileentry->file->disk,
1650 fileentry->file->datestamp,
1651 fileentry->file->partnum,
1652 fileentry->file->dumplevel);
1658 fprintf(logstream, "%s", logline);
1667 * Take a pattern of dumps and restore it blind, a la amrestore. In addition,
1668 * be smart enough to change tapes and continue with minimal operator
1669 * intervention, and write out a record of what was found on tapes in the
1670 * the regular logging format. Can take a tapelist with a specific set of
1671 * tapes to search (rather than "everything I can find"), which in turn can
1672 * optionally list specific files to restore.
1679 tapelist_t * tapelist,
1680 match_list_t * match_list,
1681 rst_flags_t * flags,
1682 am_feature_t * their_features)
1684 int have_changer = 1;
1687 FILE *logstream = NULL;
1688 tapelist_t *desired_tape = NULL;
1689 struct sigaction act, oact;
1690 ssize_t read_result;
1693 seentapes_t *seentapes = NULL;
1696 if(!prompt_out) prompt_out = stderr;
1698 dbprintf(("search_tapes(prompt_out=%d, prompt_in=%d, use_changer=%d, "
1700 "match_list=%p, flags=%p, features=%p)\n",
1701 fileno(prompt_out), fileno(prompt_in), use_changer, tapelist,
1702 match_list, flags, their_features));
1704 if(flags->blocksize)
1705 blocksize = (size_t)flags->blocksize;
1706 else if(blocksize == (size_t)SSIZE_MAX)
1707 blocksize = DISK_BLOCK_BYTES;
1709 /* Don't die when child closes pipe */
1710 signal(SIGPIPE, SIG_IGN);
1712 /* catch SIGINT with something that'll flush unmerged splits */
1713 act.sa_handler = handle_sigint;
1714 sigemptyset(&act.sa_mask);
1716 if(sigaction(SIGINT, &act, &oact) != 0){
1717 error("error setting SIGINT handler: %s", strerror(errno));
1720 if(flags->delay_assemble || flags->inline_assemble) exitassemble = 1;
1721 else exitassemble = 0;
1723 /* if given a log file, print an inventory of stuff found */
1724 if(flags->inventory_log) {
1725 if(!strcmp(flags->inventory_log, "-")) logstream = stdout;
1726 else if((logstream = fopen(flags->inventory_log, "w+")) == NULL) {
1727 error("Couldn't open log file %s for writing: %s",
1728 flags->inventory_log, strerror(errno));
1733 /* Suss what tape device we're using, whether there's a changer, etc. */
1734 if(!use_changer || (have_changer = changer_init()) == 0) {
1735 if (flags->alt_tapedev) {
1736 cur_tapedev = stralloc(flags->alt_tapedev);
1737 } else if(!cur_tapedev) {
1738 cur_tapedev = getconf_str(CNF_TAPEDEV);
1739 if (cur_tapedev == NULL) {
1740 error("No tapedev specified");
1743 /* XXX oughta complain if no config is loaded */
1744 fprintf(stderr, "%s: Using tapedev %s\n", get_pname(), cur_tapedev);
1746 } else if (have_changer != 1) {
1747 error("changer initialization failed: %s", strerror(errno));
1750 else{ /* good, the changer works, see what it can do */
1752 changer_info(&slots, &curslot, &backwards);
1755 if(tapelist && !flags->amidxtaped){
1756 slots = num_entries(tapelist);
1758 Spit out a list of expected tapes, so people with manual changers know
1761 fprintf(prompt_out, "The following tapes are needed:");
1762 for(desired_tape = tapelist; desired_tape != NULL;
1763 desired_tape = desired_tape->next){
1764 fprintf(prompt_out, " %s", desired_tape->label);
1766 fprintf(prompt_out, "\n");
1768 if(flags->wait_tape_prompt){
1770 fprintf(prompt_out,"Press enter when ready\n");
1772 input = agets(prompt_in);
1774 fprintf(prompt_out, "\n");
1778 desired_tape = tapelist;
1780 if(use_changer && !cur_tapedev) { /* load current slot */
1782 changer_loadslot("current", &curslot, &cur_tapedev);
1786 * If we're not given a tapelist, iterate over everything our changer can
1787 * find. If there's no changer, we'll prompt to be handfed tapes.
1789 * If we *are* given a tapelist, restore from those tapes in the order in
1790 * which they're listed. Unless the changer (if we have one) can't go
1791 * backwards, in which case check every tape we see and restore from it if
1794 * (obnoxious, isn't this?)
1797 do { /* all desired tape */
1798 seentapes_t *tape_seen = NULL;
1799 dumpfile_t file, tapestart, prev_rst_file;
1805 memset(&file, 0, SIZEOF(file));
1807 if (desired_tape && desired_tape->isafile) {
1809 if ((tapefd = open(desired_tape->label, 0)) == -1) {
1810 send_message(prompt_out, flags, their_features,
1811 "could not open %s: %s",
1812 desired_tape->label, strerror(errno));
1815 fprintf(stderr, "Reading %s to fd %d\n",
1816 desired_tape->label, tapefd);
1818 read_result = read_file_header(&file, tapefd, 1, flags);
1819 label = stralloc(desired_tape->label);
1821 /* check current_slot */
1822 label = label_of_current_slot(cur_tapedev, prompt_out,
1823 &tapefd, &file, flags,
1824 their_features, &read_result,
1826 while (label==NULL && slot_num < slots &&
1829 * If we have an incorrect tape loaded, go try to find
1831 * (or just see what the next available one is).
1833 slot = load_next_tape(&cur_tapedev, prompt_out,
1835 their_features, desired_tape);
1836 if(slot == LOAD_STOP) {
1840 if (slot == LOAD_CHANGER)
1845 /* check current_slot */
1846 label = label_of_current_slot(cur_tapedev, prompt_out,
1847 &tapefd, &file, flags,
1848 their_features, &read_result,
1853 if (label == NULL) {
1854 ret = load_manual_tape(&cur_tapedev, prompt_out, prompt_in,
1856 their_features, desired_tape);
1858 label = label_of_current_slot(cur_tapedev, prompt_out,
1859 &tapefd, &file, flags,
1860 their_features, &read_result,
1866 memcpy(&tapestart, &file, SIZEOF(dumpfile_t));
1873 * Skip this tape if we did it already. Note that this would let
1874 * duplicate labels through, so long as they were in the same slot.
1875 * I'm over it, are you?
1878 for (tape_seen = seentapes; tape_seen;
1879 tape_seen = tape_seen->next) {
1880 if (!strcmp(tape_seen->label, label) &&
1881 !strcmp(tape_seen->slotstr, curslot)){
1882 send_message(prompt_out, flags, their_features,
1883 "Saw repeat tape %s in slot %s",
1895 curslot = stralloc("<none>");
1898 fprintf(stderr, "Scanning %s (slot %s)\n", label, curslot);
1902 tape_seen = alloc(SIZEOF(seentapes_t));
1903 memset(tape_seen, '\0', SIZEOF(seentapes_t));
1905 tape_seen->label = label;
1906 tape_seen->slotstr = stralloc(curslot);
1907 tape_seen->next = seentapes;
1908 tape_seen->files = NULL;
1909 seentapes = tape_seen;
1912 * Start slogging through the tape itself. If our tapelist (if we
1913 * have one) contains a list of files to restore, obey that instead
1914 * of checking for matching headers on all files.
1917 search_a_tape(cur_tapedev, prompt_out, flags, their_features,
1918 desired_tape, isafile, match_list, tape_seen,
1919 &file, &prev_rst_file, &tapestart, slot_num,
1922 fprintf(stderr, "%s: Search of %s complete\n",
1923 get_pname(), tape_seen->label);
1924 if (desired_tape) desired_tape = desired_tape->next;
1926 /* only restore a single dump, if piping to stdout */
1927 if (!headers_equal(&prev_rst_file, &file, 1) &&
1928 flags->pipe_to_fd == fileno(stdout))
1931 } while (desired_tape);
1933 while (seentapes != NULL) {
1934 seentapes_t *tape_seen = seentapes;
1935 seentapes = seentapes->next;
1936 while(tape_seen->files != NULL) {
1937 dumplist_t *temp_dump = tape_seen->files;
1938 tape_seen->files = temp_dump->next;
1939 amfree(temp_dump->file);
1942 amfree(tape_seen->label);
1943 amfree(tape_seen->slotstr);
1948 if(logstream && logstream != stderr && logstream != stdout){
1951 if(flags->delay_assemble || flags->inline_assemble){
1952 flush_open_outputs(1, NULL);
1954 else flush_open_outputs(0, NULL);
1958 * Create a new, clean set of restore flags with some sane default values.
1963 rst_flags_t *flags = alloc(SIZEOF(rst_flags_t));
1965 memset(flags, 0, SIZEOF(rst_flags_t));
1968 flags->comp_type = COMPRESS_FAST_OPT;
1969 flags->inline_assemble = 1;
1970 flags->pipe_to_fd = -1;
1971 flags->check_labels = 1;
1977 * Make sure the set of restore options given is sane. Print errors for
1978 * things that're odd, and return -1 for fatal errors.
1982 rst_flags_t * flags)
1986 if(!flags) return(-1);
1988 if(flags->compress && flags->leave_comp){
1989 fprintf(stderr, "Cannot specify 'compress output' and 'leave compression alone' together\n");
1993 if(flags->restore_dir != NULL){
1994 struct stat statinfo;
1996 if(flags->pipe_to_fd != -1){
1997 fprintf(stderr, "Specifying output directory and piping output are mutually exclusive\n");
2000 if(stat(flags->restore_dir, &statinfo) < 0){
2001 fprintf(stderr, "Cannot stat restore target dir '%s': %s\n",
2002 flags->restore_dir, strerror(errno));
2005 if((statinfo.st_mode & S_IFMT) != S_IFDIR){
2006 fprintf(stderr, "'%s' is not a directory\n", flags->restore_dir);
2011 if((flags->pipe_to_fd != -1 || flags->compress) &&
2012 (flags->delay_assemble || !flags->inline_assemble)){
2013 fprintf(stderr, "Split dumps *must* be automatically reassembled when piping output or compressing/uncompressing\n");
2017 if(flags->delay_assemble && flags->inline_assemble){
2018 fprintf(stderr, "Inline split assembling and delayed assembling are mutually exclusive\n");
2026 * Clean up after a rst_flags_t
2030 rst_flags_t * flags)
2034 amfree(flags->restore_dir);
2035 amfree(flags->alt_tapedev);
2036 amfree(flags->inventory_log);
2043 * Clean up after a match_list_t
2047 match_list_t * match_list)
2050 match_list_t *prev = NULL;
2052 for(me = match_list; me; me = me->next){
2053 /* XXX freeing these is broken? can't work out why */
2054 /* amfree(me->hostname);
2055 amfree(me->diskname);
2056 amfree(me->datestamp);
2057 amfree(me->level); */
2065 printf_arglist_function3(
2068 rst_flags_t *, flags,
2069 am_feature_t *, their_features,
2073 char linebuf[STR_SIZE];
2075 arglist_start(argp, format);
2076 vsnprintf(linebuf, SIZEOF(linebuf)-1, format, argp);
2079 fprintf(stderr,"%s\n", linebuf);
2080 if (flags->amidxtaped && their_features &&
2081 am_has_feature(their_features, fe_amrecover_message)) {
2082 fprintf(prompt_out, "MESSAGE %s\r\n", linebuf);