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