Imported Upstream version 2.4.4p3
[debian/amanda] / restore-src / amrestore.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: amrestore.c,v 1.28.2.4.4.3.2.8 2003/02/09 04:33:13 jrjackson Exp $
28  *
29  * retrieves files from an amanda tape
30  */
31 /*
32  * Pulls all files from the tape that match the hostname, diskname and
33  * datestamp regular expressions.
34  *
35  * If the header is output, only up to DISK_BLOCK_BYTES worth of it is
36  * sent, regardless of the tape blocksize.  This makes the disk image
37  * look like a holding disk image, and also makes it easier to remove
38  * the header (e.g. in amrecover) since it has a fixed size.
39  */
40
41 #include "amanda.h"
42 #include "tapeio.h"
43 #include "fileheader.h"
44 #include "util.h"
45
46 #define CREAT_MODE      0640
47
48 char *buffer = NULL;
49
50 int compflag, rawflag, pipeflag, headerflag;
51 int got_sigpipe, file_number;
52 pid_t compress_pid = -1;
53 char *compress_type = COMPRESS_FAST_OPT;
54 int tapedev;
55 int bytes_read;
56 long blocksize = -1;
57 long filefsf = -1;
58
59 /* local functions */
60
61 void errexit P((void));
62 void handle_sigpipe P((int sig));
63 int disk_match P((dumpfile_t *file, char *datestamp, 
64                   char *hostname, char *diskname));
65 char *make_filename P((dumpfile_t *file));
66 void read_file_header P((dumpfile_t *file, int isafile));
67 static int get_block P((int isafile));
68 void restore P((dumpfile_t *file, char *filename, int isafile));
69 void usage P((void));
70 int main P((int argc, char **argv));
71
72 void errexit()
73 /*
74  * Do exit(2) after an error, rather than exit(1).
75  */
76 {
77     exit(2);
78 }
79
80
81 void handle_sigpipe(sig)
82 int sig;
83 /*
84  * Signal handler for the SIGPIPE signal.  Just sets a flag and returns.
85  * The act of catching the signal causes the pipe write() to fail with
86  * EINTR.
87  */
88 {
89     got_sigpipe++;
90 }
91
92 int disk_match(file, datestamp, hostname, diskname)
93 dumpfile_t *file;
94 char *datestamp, *hostname, *diskname;
95
96 /*
97  * Returns 1 if the current dump file matches the hostname and diskname
98  * regular expressions given on the command line, 0 otherwise.  As a 
99  * special case, empty regexs are considered equivalent to ".*": they 
100  * match everything.
101  */
102 {
103     if(file->type != F_DUMPFILE) return 0;
104
105     if((*hostname == '\0' || match_host(hostname, file->name)) &&
106        (*diskname == '\0' || match_disk(diskname, file->disk)) &&
107        (*datestamp== '\0' || match_datestamp(datestamp, file->datestamp)))
108         return 1;
109     else
110         return 0;
111 }
112
113
114 char *make_filename(file)
115 dumpfile_t *file;
116 {
117     char number[NUM_STR_SIZE];
118     char *sfn;
119     char *fn;
120
121     ap_snprintf(number, sizeof(number), "%d", file->dumplevel);
122     sfn = sanitise_filename(file->disk);
123     fn = vstralloc(file->name,
124                    ".",
125                    sfn, 
126                    ".",
127                    file->datestamp,
128                    ".",
129                    number,
130                    NULL);
131     amfree(sfn);
132     return fn;
133 }
134
135
136 static int get_block(isafile)
137 int isafile;
138 {
139     static int test_blocksize = 1;
140     int buflen;
141
142     /*
143      * If this is the first call, set the blocksize if it was not on
144      * the command line.  Allocate the I/O buffer in any case.
145      *
146      * For files, the blocksize is always DISK_BLOCK_BYTES.  For tapes,
147      * we allocate a large buffer and set the size to the length of the
148      * first (successful) record.
149      */
150     buflen = blocksize;
151     if(test_blocksize) {
152         if(blocksize < 0) {
153             if(isafile) {
154                 blocksize = buflen = DISK_BLOCK_BYTES;
155             } else {
156                 buflen = MAX_TAPE_BLOCK_BYTES;
157             }
158         }
159         buffer = newalloc(buffer, buflen);
160     }
161     if(isafile) {
162         bytes_read = fullread(tapedev, buffer, buflen);
163     } else {
164         bytes_read = tapefd_read(tapedev, buffer, buflen);
165         if(blocksize < 0 && bytes_read > 0 && bytes_read < buflen) {
166             char *new_buffer;
167
168             blocksize = bytes_read;
169             new_buffer = alloc(blocksize);
170             memcpy(new_buffer, buffer, bytes_read);
171             amfree(buffer);
172             buffer = new_buffer;
173         }
174     }
175     if(blocksize > 0) {
176         test_blocksize = 0;
177     }
178     return bytes_read;
179 }
180
181
182 void read_file_header(file, isafile)
183 dumpfile_t *file;
184 int isafile;
185 /*
186  * Reads the first block of a tape file.
187  */
188 {
189     bytes_read = get_block(isafile);
190     if(bytes_read < 0) {
191         error("error reading file header: %s", strerror(errno));
192     } else if(bytes_read < blocksize) {
193         if(bytes_read == 0) {
194             fprintf(stderr, "%s: missing file header block\n", get_pname());
195         } else {
196             fprintf(stderr, "%s: short file header block: %d byte%s\n",
197                     get_pname(), bytes_read, (bytes_read == 1) ? "" : "s");
198         }
199         file->type = F_UNKNOWN;
200     } else {
201         parse_file_header(buffer, file, bytes_read);
202     }
203     return;
204 }
205
206
207 void restore(file, filename, isafile)
208 dumpfile_t *file;
209 char *filename;
210 int isafile;
211 /*
212  * Restore the current file from tape.  Depending on the settings of
213  * the command line flags, the file might need to be compressed or
214  * uncompressed.  If so, a pipe through compress or uncompress is set
215  * up.  The final output usually goes to a file named host.disk.date.lev,
216  * but with the -p flag the output goes to stdout (and presumably is
217  * piped to restore).
218  */
219 {
220     int rc = 0, dest, out, outpipe[2];
221     int wc;
222     int l, s;
223     int file_is_compressed;
224
225     /* adjust compression flag */
226
227     file_is_compressed = file->compressed;
228     if(!compflag && file_is_compressed && !known_compress_type(file)) {
229         fprintf(stderr, 
230                 "%s: unknown compression suffix %s, can't uncompress\n",
231                 get_pname(), file->comp_suffix);
232         compflag = 1;
233     }
234
235     /* set up final destination file */
236
237     if(pipeflag)
238         dest = 1;               /* standard output */
239     else {
240         char *filename_ext = NULL;
241
242         if(compflag) {
243             filename_ext = file_is_compressed ? file->comp_suffix
244                                               : COMPRESS_SUFFIX;
245         } else if(rawflag) {
246             filename_ext = ".RAW";
247         } else {
248             filename_ext = "";
249         }
250         filename_ext = stralloc2(filename, filename_ext);
251
252         if((dest = creat(filename, CREAT_MODE)) < 0)
253             error("could not create output file: %s", strerror(errno));
254         amfree(filename_ext);
255     }
256
257     out = dest;
258
259     /*
260      * If -r or -h, write the header before compress or uncompress pipe.
261      * Only write DISK_BLOCK_BYTES, regardless of how much was read.
262      * This makes the output look like a holding disk image, and also
263      * makes it easier to remove the header (e.g. in amrecover) since
264      * it has a fixed size.
265      */
266     if(rawflag || headerflag) {
267         int w;
268         char *cont_filename;
269
270         if(compflag && !file_is_compressed) {
271             file->compressed = 1;
272             ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
273                         " %s %s |", UNCOMPRESS_PATH,
274 #ifdef UNCOMPRESS_OPT
275                         UNCOMPRESS_OPT
276 #else
277                         ""
278 #endif
279                         );
280             strncpy(file->comp_suffix,
281                     COMPRESS_SUFFIX,
282                     sizeof(file->comp_suffix)-1);
283             file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
284         }
285
286         /* remove CONT_FILENAME from header */
287         cont_filename = stralloc(file->cont_filename);
288         memset(file->cont_filename,'\0',sizeof(file->cont_filename));
289         file->blocksize = DISK_BLOCK_BYTES;
290         build_header(buffer, file, bytes_read);
291
292         if((w = fullwrite(out, buffer, DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
293             if(w < 0) {
294                 error("write error: %s", strerror(errno));
295             } else {
296                 error("write error: %d instead of %d", w, DISK_BLOCK_BYTES);
297             }
298         }
299         /* add CONT_FILENAME to header */
300         strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename));
301     }
302
303     /* if -c and file not compressed, insert compress pipe */
304
305     if(compflag && !file_is_compressed) {
306         if(pipe(outpipe) < 0) error("error [pipe: %s]", strerror(errno));
307         out = outpipe[1];
308         switch(compress_pid = fork()) {
309         case -1: error("could not fork for %s: %s",
310                        COMPRESS_PATH, strerror(errno));
311         default:
312             aclose(outpipe[0]);
313             aclose(dest);
314             break;
315         case 0:
316             aclose(outpipe[1]);
317             if(outpipe[0] != 0) {
318                 if(dup2(outpipe[0], 0) == -1)
319                     error("error [dup2 pipe: %s]", strerror(errno));
320                 aclose(outpipe[0]);
321             }
322             if(dest != 1) {
323                 if(dup2(dest, 1) == -1)
324                     error("error [dup2 dest: %s]", strerror(errno));
325                 aclose(dest);
326             }
327             if (*compress_type == '\0') {
328                 compress_type = NULL;
329             }
330             execlp(COMPRESS_PATH, COMPRESS_PATH, compress_type, (char *)0);
331             error("could not exec %s: %s", COMPRESS_PATH, strerror(errno));
332         }
333     }
334
335     /* if not -r or -c, and file is compressed, insert uncompress pipe */
336
337     else if(!rawflag && !compflag && file_is_compressed) {
338         /* 
339          * XXX for now we know that for the two compression types we
340          * understand, .Z and optionally .gz, UNCOMPRESS_PATH will take
341          * care of both.  Later, we may need to reference a table of
342          * possible uncompress programs.
343          */
344         if(pipe(outpipe) < 0) error("error [pipe: %s]", strerror(errno));
345         out = outpipe[1];
346         switch(compress_pid = fork()) {
347         case -1: 
348             error("could not fork for %s: %s",
349                   UNCOMPRESS_PATH, strerror(errno));
350         default:
351             aclose(outpipe[0]);
352             aclose(dest);
353             break;
354         case 0:
355             aclose(outpipe[1]);
356             if(outpipe[0] != 0) {
357                 if(dup2(outpipe[0], 0) < 0)
358                     error("dup2 pipe: %s", strerror(errno));
359                 aclose(outpipe[0]);
360             }
361             if(dest != 1) {
362                 if(dup2(dest, 1) < 0)
363                     error("dup2 dest: %s", strerror(errno));
364                 aclose(dest);
365             }
366             (void) execlp(UNCOMPRESS_PATH, UNCOMPRESS_PATH,
367 #ifdef UNCOMPRESS_OPT
368                           UNCOMPRESS_OPT,
369 #endif
370                           (char *)0);
371             error("could not exec %s: %s", UNCOMPRESS_PATH, strerror(errno));
372         }
373     }
374
375
376     /* copy the rest of the file from tape to the output */
377     got_sigpipe = 0;
378     wc = 0;
379     do {
380         bytes_read = get_block(isafile);
381         if(bytes_read < 0) {
382             error("read error: %s", strerror(errno));
383         }
384         if(bytes_read == 0 && isafile) {
385             /*
386              * See if we need to switch to the next file.
387              */
388             if(file->cont_filename[0] == '\0') {
389                 break;                          /* no more files */
390             }
391             close(tapedev);
392             if((tapedev = open(file->cont_filename, O_RDONLY)) == -1) {
393                 char *cont_filename = strrchr(file->cont_filename,'/');
394                 if(cont_filename) {
395                     cont_filename++;
396                     if((tapedev = open(cont_filename,O_RDONLY)) == -1) {
397                         error("can't open %s: %s", file->cont_filename,
398                               strerror(errno));
399                     }
400                     else {
401                         fprintf(stderr, "cannot open %s: %s\n",
402                                 file->cont_filename, strerror(errno));
403                         fprintf(stderr, "using %s\n",
404                                 cont_filename);
405                     }
406                 }
407                 else {
408                     error("can't open %s: %s", file->cont_filename,
409                           strerror(errno));
410                 }
411             }
412             read_file_header(file, isafile);
413             if(file->type != F_DUMPFILE && file->type != F_CONT_DUMPFILE) {
414                 fprintf(stderr, "unexpected header type: ");
415                 print_header(stderr, file);
416                 exit(2);
417             }
418             continue;
419         }
420         for(l = 0; l < bytes_read; l += s) {
421             if((s = write(out, buffer + l, bytes_read - l)) < 0) {
422                 if(got_sigpipe) {
423                     fprintf(stderr,"Error %d (%s) offset %d+%d, wrote %d\n",
424                                    errno, strerror(errno), wc, bytes_read, rc);
425                     fprintf(stderr,  
426                             "%s: pipe reader has quit in middle of file.\n",
427                             get_pname());
428                 } else {
429                     perror("amrestore: write error");
430                 }
431                 exit(2);
432             }
433         }
434         wc += bytes_read;
435     } while (bytes_read > 0);
436     if(pipeflag) {
437         if(out != dest) {
438             aclose(out);
439         }
440     } else {
441         aclose(out);
442     }
443 }
444
445
446 void usage()
447 /*
448  * Print usage message and terminate.
449  */
450 {
451     error("Usage: amrestore [-b blocksize] [-r|-c] [-p] [-h] [-f fileno] [-l label] tape-device|holdingfile [hostname [diskname [datestamp [hostname [diskname [datestamp ... ]]]]]]");
452 }
453
454
455 int main(argc, argv)
456 int argc;
457 char **argv;
458 /*
459  * Parses command line, then loops through all files on tape, restoring
460  * files that match the command line criteria.
461  */
462 {
463     extern int optind;
464     int opt;
465     char *errstr;
466     int isafile;
467     struct stat stat_tape;
468     dumpfile_t file;
469     char *filename = NULL;
470     char *tapename = NULL;
471     struct match_list {
472         char *hostname;
473         char *diskname;
474         char *datestamp;
475         struct match_list *next;
476     } *match_list = NULL, *me = NULL;
477     int found_match;
478     int arg_state;
479     amwait_t compress_status;
480     int fd;
481     int r = 0;
482     char *e;
483     char *err;
484     char *label = NULL;
485
486     for(fd = 3; fd < FD_SETSIZE; fd++) {
487         /*
488          * Make sure nobody spoofs us with a lot of extra open files
489          * that would cause an open we do to get a very high file
490          * descriptor, which in turn might be used as an index into
491          * an array (e.g. an fd_set).
492          */
493         close(fd);
494     }
495
496     set_pname("amrestore");
497
498     erroutput_type = ERR_INTERACTIVE;
499
500     onerror(errexit);
501     signal(SIGPIPE, handle_sigpipe);
502
503     /* handle options */
504     while( (opt = getopt(argc, argv, "b:cCd:rpkhf:l:")) != -1) {
505         switch(opt) {
506         case 'b':
507             blocksize = strtol(optarg, &e, 10);
508             if(*e == 'k' || *e == 'K') {
509                 blocksize *= 1024;
510             } else if(*e == 'm' || *e == 'M') {
511                 blocksize *= 1024 * 1024;
512             } else if(*e != '\0') {
513                 error("invalid blocksize value \"%s\"", optarg);
514             }
515             if(blocksize < DISK_BLOCK_BYTES) {
516                 error("minimum block size is %dk", DISK_BLOCK_BYTES / 1024);
517             }
518             break;
519         case 'c': compflag = 1; break;
520         case 'C': compflag = 1; compress_type = COMPRESS_BEST_OPT; break;
521         case 'r': rawflag = 1; break;
522         case 'p': pipeflag = 1; break;
523         case 'h': headerflag = 1; break;
524         case 'f':
525             filefsf = strtol(optarg, &e, 10);
526             if(*e != '\0') {
527                 error("invalid fileno value \"%s\"", optarg);
528             }
529             break;
530         case 'l':
531             label = stralloc(optarg);
532             break;
533         default:
534             usage();
535         }
536     }
537
538     if(compflag && rawflag) {
539         fprintf(stderr, 
540                 "Cannot specify both -r (raw) and -c (compressed) output.\n");
541         usage();
542     }
543
544     if(optind >= argc) {
545         fprintf(stderr, "%s: Must specify tape-device or holdingfile\n",
546                         get_pname());
547         usage();
548     }
549
550     tapename = argv[optind++];
551
552 #define ARG_GET_HOST 0
553 #define ARG_GET_DISK 1
554 #define ARG_GET_DATE 2
555
556     arg_state = ARG_GET_HOST;
557     while(optind < argc) {
558         switch(arg_state) {
559         case ARG_GET_HOST:
560             /*
561              * This is a new host/disk/date triple, so allocate a match_list.
562              */
563             me = alloc(sizeof(*me));
564             me->hostname = argv[optind++];
565             me->diskname = "";
566             me->datestamp = "";
567             me->next = match_list;
568             match_list = me;
569             if(me->hostname[0] != '\0'
570                && (errstr=validate_regexp(me->hostname)) != NULL) {
571                 fprintf(stderr, "%s: bad hostname regex \"%s\": %s\n",
572                         get_pname(), me->hostname, errstr);
573                 usage();
574             }
575             arg_state = ARG_GET_DISK;
576             break;
577         case ARG_GET_DISK:
578             me->diskname = argv[optind++];
579             if(me->diskname[0] != '\0'
580                && (errstr=validate_regexp(me->diskname)) != NULL) {
581                 fprintf(stderr, "%s: bad diskname regex \"%s\": %s\n",
582                         get_pname(), me->diskname, errstr);
583                 usage();
584             }
585             arg_state = ARG_GET_DATE;
586             break;
587         case ARG_GET_DATE:
588             me->datestamp = argv[optind++];
589             if(me->datestamp[0] != '\0'
590                && (errstr=validate_regexp(me->datestamp)) != NULL) {
591                 fprintf(stderr, "%s: bad datestamp regex \"%s\": %s\n",
592                         get_pname(), me->datestamp, errstr);
593                 usage();
594             }
595             arg_state = ARG_GET_HOST;
596             break;
597         }
598     }
599     if(match_list == NULL) {
600         match_list = alloc(sizeof(*match_list));
601         match_list->hostname = "";
602         match_list->diskname = "";
603         match_list->datestamp = "";
604         match_list->next = NULL;
605     }
606
607     if(tape_stat(tapename,&stat_tape)!=0) {
608         error("could not stat %s: %s", tapename, strerror(errno));
609     }
610     isafile=S_ISREG((stat_tape.st_mode));
611
612     if(label) {
613         if(isafile) {
614             fprintf(stderr,"%s: ignoring -l flag when restoring from a file.\n",
615                     get_pname());
616         }
617         else {
618             if((err = tape_rewind(tapename)) != NULL) {
619                 error("Could not rewind device '%s': %s", tapename, err);
620             }
621             tapedev = tape_open(tapename, 0);
622             read_file_header(&file, isafile);
623             if(file.type != F_TAPESTART) {
624                 fprintf(stderr,"Not an amanda tape\n");
625                 exit (1);
626             }
627             if(strcmp(label, file.name) != 0) {
628                 fprintf(stderr,"Wrong label: '%s'\n", file.name);
629                 exit (1);
630             }
631             tapefd_close(tapedev);
632             if((err = tape_rewind(tapename)) != NULL) {
633                 error("Could not rewind device '%s': %s", tapename, err);
634             }
635         }
636     }
637     file_number = 0;
638     if(filefsf != -1) {
639         if(isafile) {
640             fprintf(stderr,"%s: ignoring -f flag when restoring from a file.\n",
641                     get_pname());
642         }
643         else {
644             if((err = tape_rewind(tapename)) != NULL) {
645                 error("Could not rewind device '%s': %s", tapename, err);
646             }
647             if((err = tape_fsf(tapename,filefsf)) != NULL) {
648                 error("Could not fsf device '%s': %s", tapename, err);
649             }
650             file_number = filefsf;
651         }
652     }
653
654     if(isafile) {
655         tapedev = open(tapename, 0);
656     } else {
657         tapedev = tape_open(tapename, 0);
658     }
659     if(tapedev < 0) {
660         error("could not open %s: %s", tapename, strerror(errno));
661     }
662
663     read_file_header(&file, isafile);
664
665     if(file.type != F_TAPESTART && !isafile && filefsf == -1) {
666         fprintf(stderr, "%s: WARNING: not at start of tape, file numbers will be offset\n",
667                         get_pname());
668     }
669
670     while(file.type == F_TAPESTART || file.type == F_DUMPFILE) {
671         amfree(filename);
672         filename = make_filename(&file);
673         found_match = 0;
674         for(me = match_list; me; me = me->next) {
675             if(disk_match(&file,me->datestamp,me->hostname,me->diskname) != 0) {
676                 found_match = 1;
677                 break;
678             }
679         }
680         fprintf(stderr, "%s: %3d: %s ",
681                         get_pname(),
682                         file_number,
683                         found_match ? "restoring" : "skipping");
684         if(file.type != F_DUMPFILE) {
685             print_header(stderr, &file);
686         } else {
687             fprintf(stderr, "%s\n", filename);
688         }
689         if(found_match) {
690             restore(&file, filename, isafile);
691             if(compress_pid > 0) {
692                 waitpid(compress_pid, &compress_status, 0);
693                 compress_pid = -1;
694             }
695             if(pipeflag) {
696                 file_number++;                  /* for the last message */
697                 break;
698             }
699         }
700         if(isafile) {
701             break;
702         }
703         /*
704          * Note that at this point we know we are working with a tape,
705          * not a holding disk file, so we can call the tape functions
706          * without checking.
707          */
708         if(bytes_read == 0) {
709             /*
710              * If the last read got EOF, how to get to the next
711              * file depends on how the tape device driver is acting.
712              * If it is BSD-like, we do not really need to do anything.
713              * If it is Sys-V-like, we need to either fsf or close/open.
714              * The good news is, a close/open works in either case,
715              * so that's what we do.
716              */
717             tapefd_close(tapedev);
718             if((tapedev = tape_open(tapename, 0)) < 0) {
719                 error("could not open %s: %s", tapename, strerror(errno));
720             }
721         } else {
722             /*
723              * If the last read got something (even an error), we can
724              * do an fsf to get to the next file.
725              */
726             if(tapefd_fsf(tapedev, 1) < 0) {
727                 error("could not fsf %s: %s", tapename, strerror(errno));
728             }
729         }
730         file_number++;
731         read_file_header(&file, isafile);
732     }
733     if(isafile) {
734         close(tapedev);
735     } else {
736         /*
737          * See the notes above about advancing to the next file.
738          */
739         if(bytes_read == 0) {
740             tapefd_close(tapedev);
741             if((tapedev = tape_open(tapename, 0)) < 0) {
742                 error("could not open %s: %s", tapename, strerror(errno));
743             }
744         } else {
745             if(tapefd_fsf(tapedev, 1) < 0) {
746                 error("could not fsf %s: %s", tapename, strerror(errno));
747             }
748         }
749         tapefd_close(tapedev);
750     }
751
752     if((bytes_read <= 0 || file.type == F_TAPEEND) && !isafile) {
753         fprintf(stderr, "%s: %3d: reached ", get_pname(), file_number);
754         if(bytes_read <= 0) {
755             fprintf(stderr, "end of information\n");
756         } else {
757             print_header(stderr,&file);
758         }
759         r = 1;
760     }
761     return r;
762 }