Imported Upstream version 2.4.5p1
[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.2.4 2005/09/20 21:31:52 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 r = 0;
481     char *e;
482     char *err;
483     char *label = NULL;
484     int count_error;
485
486     safe_fd(-1, 0);
487
488     set_pname("amrestore");
489
490     erroutput_type = ERR_INTERACTIVE;
491
492     onerror(errexit);
493     signal(SIGPIPE, handle_sigpipe);
494
495     /* handle options */
496     while( (opt = getopt(argc, argv, "b:cCd:rpkhf:l:")) != -1) {
497         switch(opt) {
498         case 'b':
499             blocksize = strtol(optarg, &e, 10);
500             if(*e == 'k' || *e == 'K') {
501                 blocksize *= 1024;
502             } else if(*e == 'm' || *e == 'M') {
503                 blocksize *= 1024 * 1024;
504             } else if(*e != '\0') {
505                 error("invalid blocksize value \"%s\"", optarg);
506             }
507             if(blocksize < DISK_BLOCK_BYTES) {
508                 error("minimum block size is %dk", DISK_BLOCK_BYTES / 1024);
509             }
510             break;
511         case 'c': compflag = 1; break;
512         case 'C': compflag = 1; compress_type = COMPRESS_BEST_OPT; break;
513         case 'r': rawflag = 1; break;
514         case 'p': pipeflag = 1; break;
515         case 'h': headerflag = 1; break;
516         case 'f':
517             filefsf = strtol(optarg, &e, 10);
518             if(*e != '\0') {
519                 error("invalid fileno value \"%s\"", optarg);
520             }
521             break;
522         case 'l':
523             label = stralloc(optarg);
524             break;
525         default:
526             usage();
527         }
528     }
529
530     if(compflag && rawflag) {
531         fprintf(stderr, 
532                 "Cannot specify both -r (raw) and -c (compressed) output.\n");
533         usage();
534     }
535
536     if(optind >= argc) {
537         fprintf(stderr, "%s: Must specify tape-device or holdingfile\n",
538                         get_pname());
539         usage();
540     }
541
542     tapename = argv[optind++];
543
544 #define ARG_GET_HOST 0
545 #define ARG_GET_DISK 1
546 #define ARG_GET_DATE 2
547
548     arg_state = ARG_GET_HOST;
549     while(optind < argc) {
550         switch(arg_state) {
551         case ARG_GET_HOST:
552             /*
553              * This is a new host/disk/date triple, so allocate a match_list.
554              */
555             me = alloc(sizeof(*me));
556             me->hostname = argv[optind++];
557             me->diskname = "";
558             me->datestamp = "";
559             me->next = match_list;
560             match_list = me;
561             if(me->hostname[0] != '\0'
562                && (errstr=validate_regexp(me->hostname)) != NULL) {
563                 fprintf(stderr, "%s: bad hostname regex \"%s\": %s\n",
564                         get_pname(), me->hostname, errstr);
565                 usage();
566             }
567             arg_state = ARG_GET_DISK;
568             break;
569         case ARG_GET_DISK:
570             me->diskname = argv[optind++];
571             if(me->diskname[0] != '\0'
572                && (errstr=validate_regexp(me->diskname)) != NULL) {
573                 fprintf(stderr, "%s: bad diskname regex \"%s\": %s\n",
574                         get_pname(), me->diskname, errstr);
575                 usage();
576             }
577             arg_state = ARG_GET_DATE;
578             break;
579         case ARG_GET_DATE:
580             me->datestamp = argv[optind++];
581             if(me->datestamp[0] != '\0'
582                && (errstr=validate_regexp(me->datestamp)) != NULL) {
583                 fprintf(stderr, "%s: bad datestamp regex \"%s\": %s\n",
584                         get_pname(), me->datestamp, errstr);
585                 usage();
586             }
587             arg_state = ARG_GET_HOST;
588             break;
589         }
590     }
591     if(match_list == NULL) {
592         match_list = alloc(sizeof(*match_list));
593         match_list->hostname = "";
594         match_list->diskname = "";
595         match_list->datestamp = "";
596         match_list->next = NULL;
597     }
598
599     if(tape_stat(tapename,&stat_tape)!=0) {
600         error("could not stat %s: %s", tapename, strerror(errno));
601     }
602     isafile=S_ISREG((stat_tape.st_mode));
603
604     if(label) {
605         if(isafile) {
606             fprintf(stderr,"%s: ignoring -l flag when restoring from a file.\n",
607                     get_pname());
608         }
609         else {
610             if((err = tape_rewind(tapename)) != NULL) {
611                 error("Could not rewind device '%s': %s", tapename, err);
612             }
613             tapedev = tape_open(tapename, 0);
614             read_file_header(&file, isafile);
615             if(file.type != F_TAPESTART) {
616                 fprintf(stderr,"Not an amanda tape\n");
617                 exit (1);
618             }
619             if(strcmp(label, file.name) != 0) {
620                 fprintf(stderr,"Wrong label: '%s'\n", file.name);
621                 exit (1);
622             }
623             tapefd_close(tapedev);
624             if((err = tape_rewind(tapename)) != NULL) {
625                 error("Could not rewind device '%s': %s", tapename, err);
626             }
627         }
628     }
629     file_number = 0;
630     if(filefsf != -1) {
631         if(isafile) {
632             fprintf(stderr,"%s: ignoring -f flag when restoring from a file.\n",
633                     get_pname());
634         }
635         else {
636             if((err = tape_rewind(tapename)) != NULL) {
637                 error("Could not rewind device '%s': %s", tapename, err);
638             }
639             if((err = tape_fsf(tapename,filefsf)) != NULL) {
640                 error("Could not fsf device '%s': %s", tapename, err);
641             }
642             file_number = filefsf;
643         }
644     }
645
646     if(isafile) {
647         tapedev = open(tapename, 0);
648     } else {
649         tapedev = tape_open(tapename, 0);
650     }
651     if(tapedev < 0) {
652         error("could not open %s: %s", tapename, strerror(errno));
653     }
654
655     read_file_header(&file, isafile);
656
657     if(file.type != F_TAPESTART && !isafile && filefsf == -1) {
658         fprintf(stderr, "%s: WARNING: not at start of tape, file numbers will be offset\n",
659                         get_pname());
660     }
661
662     count_error=0;
663     while(count_error < 10) {
664         if(file.type == F_TAPEEND) break;
665         found_match = 0;
666         if(file.type == F_DUMPFILE) {
667             amfree(filename);
668             filename = make_filename(&file);
669             for(me = match_list; me; me = me->next) {
670                 if(disk_match(&file,me->datestamp,me->hostname,me->diskname) != 0) {
671                     found_match = 1;
672                     break;
673                 }
674             }
675             fprintf(stderr, "%s: %3d: %s ",
676                             get_pname(),
677                             file_number,
678                             found_match ? "restoring" : "skipping");
679             if(file.type != F_DUMPFILE) {
680                 print_header(stderr, &file);
681             } else {
682                 fprintf(stderr, "%s\n", filename);
683             }
684         }
685         if(found_match) {
686             restore(&file, filename, isafile);
687             if(compress_pid > 0) {
688                 waitpid(compress_pid, &compress_status, 0);
689                 compress_pid = -1;
690             }
691             if(pipeflag) {
692                 file_number++;                  /* for the last message */
693                 break;
694             }
695         }
696         if(isafile) {
697             break;
698         }
699         /*
700          * Note that at this point we know we are working with a tape,
701          * not a holding disk file, so we can call the tape functions
702          * without checking.
703          */
704         if(bytes_read == 0) {
705             /*
706              * If the last read got EOF, how to get to the next
707              * file depends on how the tape device driver is acting.
708              * If it is BSD-like, we do not really need to do anything.
709              * If it is Sys-V-like, we need to either fsf or close/open.
710              * The good news is, a close/open works in either case,
711              * so that's what we do.
712              */
713             tapefd_close(tapedev);
714             if((tapedev = tape_open(tapename, 0)) < 0) {
715                 error("could not open %s: %s", tapename, strerror(errno));
716             }
717             count_error++;
718         } else {
719             /*
720              * If the last read got something (even an error), we can
721              * do an fsf to get to the next file.
722              */
723             if(tapefd_fsf(tapedev, 1) < 0) {
724                 error("could not fsf %s: %s", tapename, strerror(errno));
725             }
726             count_error=0;
727         }
728         file_number++;
729         read_file_header(&file, isafile);
730     }
731     if(isafile) {
732         close(tapedev);
733     } else {
734         /*
735          * See the notes above about advancing to the next file.
736          */
737         if(bytes_read == 0) {
738             tapefd_close(tapedev);
739             if((tapedev = tape_open(tapename, 0)) < 0) {
740                 error("could not open %s: %s", tapename, strerror(errno));
741             }
742         } else {
743             if(tapefd_fsf(tapedev, 1) < 0) {
744                 error("could not fsf %s: %s", tapename, strerror(errno));
745             }
746         }
747         tapefd_close(tapedev);
748     }
749
750     if((bytes_read <= 0 || file.type == F_TAPEEND) && !isafile) {
751         fprintf(stderr, "%s: %3d: reached ", get_pname(), file_number);
752         if(bytes_read <= 0) {
753             fprintf(stderr, "end of information\n");
754         } else {
755             print_header(stderr,&file);
756         }
757         r = 1;
758     }
759     return r;
760 }