eaa32ca0cb7dadc9f4db2db026c5661736fe4bd7
[debian/amanda] / tape-src / output-file.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  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27
28 /*
29  * $Id: output-file.c,v 1.14.2.1 2006/11/20 22:34:39 martinea Exp $
30  *
31  * tapeio.c virtual tape interface for a file device.
32  *
33  * The following was based on testing with real tapes on Solaris 2.6.
34  * It is possible other OS drivers behave somewhat different in end
35  * cases, usually involving errors.
36  */
37
38 #include "amanda.h"
39
40 #include "token.h"
41 #include "tapeio.h"
42 #include "output-file.h"
43 #include "fileheader.h"
44
45 #ifndef SEEK_SET
46 #define SEEK_SET 0
47 #endif
48 #ifndef SEEK_CUR
49 #define SEEK_CUR 1
50 #endif
51 #ifndef SEEK_END
52 #define SEEK_END 2
53 #endif
54
55 #define MAX_TOKENS              10
56
57 #define DATA_INDICATOR          "."
58 #define RECORD_INDICATOR        "-"
59
60 static
61 struct volume_info {
62     char *basename;                     /* filename from open */
63     struct file_info *fi;               /* file info array */
64     size_t fi_limit;                    /* length of file info array */
65     int flags;                          /* open flags */
66     mode_t mask;                        /* open mask */
67     off_t file_count;                   /* number of files */
68     off_t file_current;                 /* current file position */
69     off_t record_current;               /* current record position */
70     int fd;                             /* data file descriptor */
71     int is_online;                      /* true if "tape" is "online" */
72     int at_bof;                         /* true if at begining of file */
73     int at_eof;                         /* true if at end of file */
74     int at_eom;                         /* true if at end of medium */
75     int last_operation_write;           /* true if last op was a write */
76     off_t amount_written;               /* KBytes written since open/rewind */
77 } *volume_info = NULL;
78
79 struct file_info {
80     char *name;                         /* file name (tapefd_getinfo_...) */
81     struct record_info *ri;             /* record info array */
82     size_t ri_count;                    /* number of record info entries */
83     size_t ri_limit;                    /* length of record info array */
84     int ri_altered;                     /* true if record info altered */
85 };
86
87 struct record_info {
88     size_t record_size;                 /* record size */
89     off_t start_record;                 /* first record in range */ 
90     off_t end_record;                   /* last record in range */ 
91 };
92
93 static size_t open_count = 0;
94
95 static int check_online(int fd);
96 static int file_open(int fd);
97 static void file_close(int fd);
98 static void file_release(int fd);
99 static size_t get_record_size(struct file_info *fi, off_t record);
100 static void put_record_size(struct file_info *fi, off_t record, size_t size);
101
102 /*
103  * "Open" the tape by scanning the "data" directory.  "Tape files"
104  * have five leading digits indicating the position (counting from zero)
105  * followed by a '.' and optional other information (e.g. host/disk/level
106  * image name).
107  *
108  * We allow for the following situations:
109  *
110  *   + If we see the same "file" (position number) more than once, the
111  *     last one seen wins.  This should not normally happen.
112  *
113  *   + We allow gaps in the positions.  This should not normally happen.
114  *
115  * Anything in the directory that does not match a "tape file" name
116  * pattern is ignored.
117  *
118  * If the data directory does not exist, the "tape" is considered offline.
119  * It is allowed to "appear" later.
120  */
121
122 static int
123 check_online(
124     int fd)
125 {
126     char *token[MAX_TOKENS];
127     DIR *tapedir;
128     struct dirent *entry;
129     struct file_info *fi;
130     struct file_info **fi_p;
131     char *line;
132     int f;
133     off_t pos;
134     int rc = 0;
135     char *qname = quote_string(volume_info[fd].basename);
136
137     /*
138      * If we are already online, there is nothing else to do.
139      */
140     if (volume_info[fd].is_online) {
141         goto common_exit;
142     }
143
144     if ((tapedir = opendir(volume_info[fd].basename)) == NULL) {
145         /*
146          * We have already opened the info file which is in the same
147          * directory as the data directory, so ENOENT has to mean the data
148          * directory is not there, which we treat as being "offline".
149          * We're already offline at this point (see the above test)
150          * and this is not an error, so just return success (no error).
151          */
152
153         rc = (errno != ENOENT);
154         fprintf(stderr,"ERROR: %s (%s)\n", qname, strerror(errno));
155         goto common_exit;
156     }
157     while ((entry = readdir(tapedir)) != NULL) {
158         if (is_dot_or_dotdot(entry->d_name)) {
159             continue;
160         }
161         if (isdigit((int)entry->d_name[0])
162             && isdigit((int)entry->d_name[1])
163             && isdigit((int)entry->d_name[2])
164             && isdigit((int)entry->d_name[3])
165             && isdigit((int)entry->d_name[4])
166             && entry->d_name[5] == '.') {
167
168             /*
169              * This is a "tape file".
170              */
171             pos = OFF_T_ATOI(entry->d_name);
172             assert((pos + 1) <= (off_t)SSIZE_MAX);
173             fi_p = &volume_info[fd].fi;
174             amtable_alloc((void **)fi_p,
175                           &volume_info[fd].fi_limit,
176                           SIZEOF(*volume_info[fd].fi),
177                           (size_t)(pos + 1),
178                           10,
179                           NULL);
180             fi = &volume_info[fd].fi[pos];
181             if (fi->name != NULL) {
182                 /*
183                  * Two files with the same position???
184                  */
185                 amfree(fi->name);
186                 fi->ri_count = 0;
187             }
188             fi->name = stralloc(&entry->d_name[6]);
189             if ((pos + 1) > volume_info[fd].file_count) {
190                 volume_info[fd].file_count = (pos + 1);
191             }
192         }
193     }
194     closedir(tapedir);
195
196     /*
197      * Parse the info file.  We know we are at beginning of file because
198      * the only thing that can happen to it prior to here is it being
199      * opened.
200      */
201     for (; (line = areads(fd)) != NULL; free(line)) {
202         f = split(line, token, (int)(sizeof(token) / sizeof(token[0])), " ");
203         if (f == 2 && strcmp(token[1], "position") == 0) {
204             volume_info[fd].file_current = OFF_T_ATOI(token[2]);
205             volume_info[fd].record_current = (off_t)0;
206         }
207     }
208
209     /*
210      * Set EOM and make sure we are not pre-BOI.
211      */
212     if (volume_info[fd].file_current >= volume_info[fd].file_count) {
213         volume_info[fd].at_eom = 1;
214     }
215     if (volume_info[fd].file_current < 0) {
216         volume_info[fd].file_current = 0;
217         volume_info[fd].record_current = (off_t)0;
218     }
219
220     volume_info[fd].is_online = 1;
221
222 common_exit:
223
224     amfree(qname);
225     return rc;
226 }
227
228 /*
229  * Open the tape file if not already.  If we are beyond the file count
230  * (end of tape) or the file is missing and we are only reading, set
231  * up to read /dev/null which will look like EOF.  If we are writing,
232  * create the file.
233  */
234
235 static int
236 file_open(
237     int fd)
238 {
239     struct file_info *fi;
240     struct file_info **fi_p;
241     char *datafilename = NULL;
242     char *recordfilename = NULL;
243     char *f = NULL;
244     off_t pos;
245     char *host;
246     char *disk;
247     int level;
248     char number[NUM_STR_SIZE];
249     int flags;
250     int rfd;
251     int n;
252     char *line;
253     struct record_info *ri;
254     struct record_info **ri_p;
255     off_t start_record;
256     off_t end_record;
257     size_t record_size = 0;
258
259     if (volume_info[fd].fd < 0) {
260         flags = volume_info[fd].flags;
261         pos = volume_info[fd].file_current;
262         assert((pos + 1) < (off_t)SSIZE_MAX);
263         fi_p = &volume_info[fd].fi;
264         amtable_alloc((void **)fi_p,
265                       &volume_info[fd].fi_limit,
266                       SIZEOF(*volume_info[fd].fi),
267                       (size_t)(pos + 1),
268                       10,
269                       NULL);
270         fi = &volume_info[fd].fi[pos];
271
272         /*
273          * See if we are creating a new file.
274          */
275         if (pos >= volume_info[fd].file_count) {
276             volume_info[fd].file_count = pos + 1;
277         }
278
279         /*
280          * Generate the file name to open.
281          */
282         if (fi->name == NULL) {
283             if ((volume_info[fd].flags & 3) != O_RDONLY) {
284
285                 /*
286                  * This is a new file, so make sure we create/truncate
287                  * it.  Generate the name based on the host/disk/level
288                  * information from the caller, if available, else
289                  * a constant.
290                  */
291                 flags |= (O_CREAT | O_TRUNC);
292                 host = tapefd_getinfo_host(fd);
293                 disk = tapefd_getinfo_disk(fd);
294                 level = tapefd_getinfo_level(fd);
295                 snprintf(number, SIZEOF(number), "%d", level);
296                 if (host != NULL) {
297                     f = stralloc(host);
298                 }
299                 if (disk != NULL) {
300                     disk = sanitise_filename(disk);
301                     if (f == NULL) {
302                         f = stralloc(disk);
303                     } else {
304                         vstrextend(&f, ".", disk, NULL);
305                     }
306                     amfree(disk);
307                 }
308                 if (level >= 0) {
309                     if (f == NULL) {
310                         f = stralloc(number);
311                     } else {
312                         vstrextend(&f, ".", number, NULL);
313                     }
314                 }
315                 if (f == NULL) {
316                     f = stralloc("unknown");
317                 }
318                 amfree(fi->name);
319                 fi->name = stralloc(f);
320                 fi->ri_count = 0;
321                 amfree(f);
322             } else {
323
324                 /*
325                  * This is a missing file, so set up to read nothing.
326                  */
327                 datafilename = stralloc("/dev/null");
328                 recordfilename = stralloc("/dev/null");
329             }
330         }
331         if (datafilename == NULL) {
332             snprintf(number, SIZEOF(number),
333                     "%05" OFF_T_RFMT, (OFF_T_FMT_TYPE)pos);
334             datafilename = vstralloc(volume_info[fd].basename,
335                                      number,
336                                      DATA_INDICATOR,
337                                      volume_info[fd].fi[pos].name,
338                                      NULL);
339             recordfilename = vstralloc(volume_info[fd].basename,
340                                        number,
341                                        RECORD_INDICATOR,
342                                        volume_info[fd].fi[pos].name,
343                                        NULL);
344         }
345
346         /*
347          * Do the data file open.
348          */
349         volume_info[fd].fd = open(datafilename, flags, volume_info[fd].mask);
350         amfree(datafilename);
351
352         /*
353          * Load the record information.
354          */
355         if (volume_info[fd].fd >= 0 && fi->ri_count == 0 &&
356                 (rfd = open(recordfilename, O_RDONLY)) >= 0) {
357             for (; (line = areads(rfd)) != NULL; free(line)) {
358                 /* We play this game because OFF_T_FMT_TYPE is not
359                    necessarily the same as off_t, and we need to cast the
360                    actual value (not just the pointer. */
361                 OFF_T_FMT_TYPE start_record_ = (OFF_T_FMT_TYPE)0;
362                 OFF_T_FMT_TYPE end_record_ = (OFF_T_FMT_TYPE)0;
363                 n = sscanf(line, OFF_T_FMT " " OFF_T_FMT " " SIZE_T_FMT,
364                            &start_record_, &end_record_, &record_size);
365                 start_record = start_record_;
366                 end_record = end_record_;
367
368                 if (n == 3) {
369                     ri_p = &fi->ri;
370                     amtable_alloc((void **)ri_p,
371                                   &fi->ri_limit,
372                                   SIZEOF(*fi->ri),
373                                   (size_t)fi->ri_count + 1,
374                                   10,
375                                   NULL);
376                     ri = &fi->ri[fi->ri_count];
377                     ri->start_record = start_record;
378                     ri->end_record = end_record;
379                     ri->record_size = record_size;
380                     fi->ri_count++;
381                 }
382             }
383             aclose(rfd);
384         }
385         amfree(recordfilename);
386     }
387     return volume_info[fd].fd;
388 }
389
390 /*
391  * Close the current data file, if open.  Dump the record information
392  * if it has been altered.
393  */
394
395 static void
396 file_close(
397     int fd)
398 {
399     struct file_info *fi;
400     struct file_info **fi_p;
401     off_t pos;
402     char number[NUM_STR_SIZE];
403     char *filename = NULL;
404     size_t r;
405     FILE *f;
406
407     aclose(volume_info[fd].fd);
408     pos = volume_info[fd].file_current;
409     assert((pos + 1) < (off_t)SSIZE_MAX);
410     fi_p = &volume_info[fd].fi;
411     amtable_alloc((void **)fi_p,
412                   &volume_info[fd].fi_limit,
413                   SIZEOF(*volume_info[fd].fi),
414                   (size_t)(pos + 1),
415                   10,
416                   NULL);
417     fi = &volume_info[fd].fi[pos];
418     if (fi->ri_altered) {
419         snprintf(number, SIZEOF(number),
420                  "%05" OFF_T_RFMT, (OFF_T_FMT_TYPE)pos);
421         filename = vstralloc(volume_info[fd].basename,
422                              number,
423                              RECORD_INDICATOR,
424                              fi->name,
425                              NULL);
426         if ((f = fopen(filename, "w")) == NULL) {
427             goto common_exit;
428         }
429         for (r = 0; r < fi->ri_count; r++) {
430             fprintf(f, OFF_T_FMT " " OFF_T_FMT " " SIZE_T_FMT "\n",
431                     (OFF_T_FMT_TYPE)fi->ri[r].start_record,
432                     (OFF_T_FMT_TYPE)fi->ri[r].end_record,
433                     (SIZE_T_FMT_TYPE)fi->ri[r].record_size);
434         }
435         afclose(f);
436         fi->ri_altered = 0;
437     }
438
439 common_exit:
440
441     amfree(filename);
442 }
443
444 /*
445  * Release any files beyond a given position current position and reset
446  * file_count to file_current to indicate EOM.
447  */
448
449 static void
450 file_release(
451     int fd)
452 {
453     off_t position;
454     char *filename;
455     off_t pos;
456     char number[NUM_STR_SIZE];
457     struct file_info **fi_p;
458
459     /*
460      * If the current file is open, release everything beyond it.
461      * If it is not open, release everything from current.
462      */
463     if (volume_info[fd].fd >= 0) {
464         position = volume_info[fd].file_current + 1;
465     } else {
466         position = volume_info[fd].file_current;
467     }
468     for (pos = position; pos < volume_info[fd].file_count; pos++) {
469         assert(pos < (off_t)SSIZE_MAX);
470         fi_p = &volume_info[fd].fi;
471         amtable_alloc((void **)fi_p,
472                       &volume_info[fd].fi_limit,
473                       SIZEOF(*volume_info[fd].fi),
474                       (size_t)(pos + 1),
475                       10,
476                       NULL);
477         if (volume_info[fd].fi[pos].name != NULL) {
478             snprintf(number, SIZEOF(number),
479                      "%05" OFF_T_RFMT, (OFF_T_FMT_TYPE)pos);
480             filename = vstralloc(volume_info[fd].basename,
481                                  number,
482                                  DATA_INDICATOR,
483                                  volume_info[fd].fi[pos].name,
484                                  NULL);
485             unlink(filename);
486             amfree(filename);
487             filename = vstralloc(volume_info[fd].basename,
488                                  number,
489                                  RECORD_INDICATOR,
490                                  volume_info[fd].fi[pos].name,
491                                  NULL);
492             unlink(filename);
493             amfree(filename);
494             amfree(volume_info[fd].fi[pos].name);
495             volume_info[fd].fi[pos].ri_count = 0;
496         }
497     }
498     volume_info[fd].file_count = position;
499 }
500
501 /*
502  * Get the size of a particular record.  We assume the record information is
503  * sorted, does not overlap and does not have gaps.
504  */
505
506 static size_t
507 get_record_size(
508     struct file_info *  fi,
509     off_t               record)
510 {
511     size_t r;
512     struct record_info *ri;
513
514     for(r = 0; r < fi->ri_count; r++) {
515         ri = &fi->ri[r];
516         if (record <= ri->end_record) {
517             return ri->record_size;
518         }
519     }
520
521     /*
522      * For historical reasons, the default record size is 32 KBytes.
523      * This allows us to read files written by Amanda with that block
524      * size before the record information was being kept.
525      */
526     return 32 * 1024;
527 }
528
529 /*
530  * Update the record information.  We assume the record information is
531  * sorted, does not overlap and does not have gaps.
532  */
533
534 static void
535 put_record_size(
536     struct file_info *  fi,
537     off_t               record,
538     size_t              size)
539 {
540     size_t r;
541     struct record_info *ri;
542     struct record_info **ri_p;
543
544     fi->ri_altered = 1;
545     if (record == (off_t)0) {
546         fi->ri_count = 0;                       /* start over */
547     }
548     for(r = 0; r < fi->ri_count; r++) {
549         ri = &fi->ri[r];
550         if ((record - (off_t)1) <= ri->end_record) {
551             /*
552              * If this record is the same size as the rest of the records
553              * in this entry, or it would replace the entire entry,
554              * reset the end record number and size, then zap the chain
555              * beyond this point.
556              */
557             if (record == ri->start_record || ri->record_size == size) {
558                 ri->end_record = record;
559                 ri->record_size = size;
560                 fi->ri_count = r + 1;
561                 return;
562             }
563             /*
564              * This record needs a new entry right after the current one.
565              */
566             ri->end_record = record - (off_t)1;
567             fi->ri_count = r + 1;
568             break;
569         }
570     }
571     /*
572      * Add a new entry.
573      */
574     ri_p = &fi->ri;
575     amtable_alloc((void **)ri_p,
576                   &fi->ri_limit,
577                   SIZEOF(*fi->ri),
578                   (size_t)fi->ri_count + 1,
579                   10,
580                   NULL);
581     ri = &fi->ri[fi->ri_count];
582     ri->start_record = record;
583     ri->end_record = record;
584     ri->record_size = size;
585     fi->ri_count++;
586 }
587
588 /*
589  * The normal interface routines ...
590  */
591
592 int
593 file_tape_open(
594     char *      filename,
595     int         flags,
596     mode_t      mask)
597 {
598     int fd;
599     int save_errno;
600     char *info_file;
601     struct volume_info **volume_info_p =  &volume_info;
602
603     /*
604      * Use only O_RDONLY and O_RDWR.
605      */
606     if ((flags & 3) != O_RDONLY) {
607         flags &= ~3;
608         flags |= O_RDWR;
609     }
610
611     /*
612      * If the caller did not set O_CREAT (and thus, pass a mask
613      * parameter), we may still end up creating data files and need a
614      * "reasonable" value.  Pick a "tight" value on the "better safe
615      * than sorry" theory.
616      */
617     if ((flags & O_CREAT) == 0) {
618         mask = 0600;
619     }
620
621     /*
622      * Open/create the info file for this "tape".
623      */
624     info_file = stralloc2(filename, "/info");
625     if ((fd = open(info_file, O_RDWR|O_CREAT, 0600)) < 0) {
626         goto common_exit;
627     }
628
629     /*
630      * Create the internal info structure for this "tape".
631      */
632     amtable_alloc((void **)volume_info_p,
633                   &open_count,
634                   SIZEOF(*volume_info),
635                   (size_t)fd + 1,
636                   10,
637                   NULL);
638     volume_info[fd].flags = flags;
639     volume_info[fd].mask = mask;
640     volume_info[fd].file_count = 0;
641     volume_info[fd].file_current = 0;
642     volume_info[fd].record_current = (off_t)0;
643     volume_info[fd].fd = -1;
644     volume_info[fd].is_online = 0;              /* true when .../data found */
645     volume_info[fd].at_bof = 1;                 /* by definition */
646     volume_info[fd].at_eof = 0;                 /* do not know yet */
647     volume_info[fd].at_eom = 0;                 /* may get reset below */
648     volume_info[fd].last_operation_write = 0;
649     volume_info[fd].amount_written = (off_t)0;
650
651     /*
652      * Save the base directory name and see if we are "online".
653      */
654     volume_info[fd].basename = stralloc2(filename, "/data/");
655     if (check_online(fd)) {
656         save_errno = errno;
657         aclose(fd);
658         fd = -1;
659         amfree(volume_info[fd].basename);
660         errno = save_errno;
661         goto common_exit;
662     }
663
664 common_exit:
665
666     amfree(info_file);
667
668     /*
669      * Return the info file descriptor as the unique descriptor for
670      * this open.
671      */
672     return fd;
673 }
674
675 ssize_t
676 file_tapefd_read(
677     int         fd,
678     void *      buffer,
679     size_t      count)
680 {
681     ssize_t result;
682     int file_fd;
683     off_t pos;
684     size_t record_size;
685     size_t read_size;
686
687     /*
688      * Make sure we are online.
689      */
690     if (check_online(fd) != 0) {
691         return -1;
692     }
693     if (! volume_info[fd].is_online) {
694         errno = EIO;
695         return -1;
696     }
697
698     /*
699      * Do not allow any more reads after we find EOF.
700      */
701     if (volume_info[fd].at_eof) {
702         errno = EIO;
703         return -1;
704     }
705
706     /*
707      * If we are at EOM, set EOF and return a zero length result.
708      */
709     if (volume_info[fd].at_eom) {
710         volume_info[fd].at_eof = 1;
711         return 0;
712     }
713
714     /*
715      * Open the file, if needed.
716      */
717     if ((file_fd = file_open(fd)) < 0) {
718         return -1;
719     }
720
721     /*
722      * Make sure we do not read too much.
723      */
724     pos = volume_info[fd].file_current;
725     record_size = get_record_size(&volume_info[fd].fi[pos],
726                                   volume_info[fd].record_current);
727     if (record_size <= count) {
728         read_size = record_size;
729     } else {
730         read_size = count;
731     }
732
733     /*
734      * Read the data.  If we ask for less than the record size, skip to
735      * the next record boundary.
736      */
737     result = read(file_fd, buffer, read_size);
738     if (result > 0) {
739         volume_info[fd].at_bof = 0;
740         if ((size_t)result < record_size) {
741             if (lseek(file_fd, (off_t)(record_size-result), SEEK_CUR) == (off_t)-1) {
742                 dbprintf(("file_tapefd_read: lseek failed: <%s>\n",
743                           strerror(errno)));
744             }
745         }
746         volume_info[fd].record_current += (off_t)1;
747     } else if (result == 0) {
748         volume_info[fd].at_eof = 1;
749     }
750     return result;
751 }
752
753 ssize_t
754 file_tapefd_write(
755     int         fd,
756     const void *buffer,
757     size_t      count)
758 {
759     int file_fd;
760     ssize_t write_count = (ssize_t)count;
761     off_t length;
762     off_t kbytes_left;
763     ssize_t result;
764     off_t pos;
765
766     /*
767      * Make sure we are online.
768      */
769     if (check_online(fd) != 0) {
770         return -1;
771     }
772     if (! volume_info[fd].is_online) {
773         errno = EIO;
774         return -1;
775     }
776
777     /*
778      * Check for write access first.
779      */
780     if ((volume_info[fd].flags & 3) == O_RDONLY) {
781         errno = EBADF;
782         return -1;
783     }
784
785     /*
786      * Special case: allow negative buffer size.
787      */
788     if (write_count <= 0) {
789         return 0;                               /* special case */
790     }
791
792     /*
793      * If we are at EOM, it takes precedence over EOF.
794      */
795     if (volume_info[fd].at_eom) {
796         volume_info[fd].at_eof = 0;
797     }
798
799 #if 0 /*JJ*/
800     /*
801      * Writes are only allowed at BOF and EOM.
802      */
803     if (! (volume_info[fd].at_bof || volume_info[fd].at_eom)) {
804         errno = EIO;
805         return -1;
806     }
807 #endif /*JJ*/
808
809     /*
810      * Writes are only allowed if we are not at EOF.
811      */
812     if (volume_info[fd].at_eof) {
813         errno = EIO;
814         return -1;
815     }
816
817     /*
818      * Open the file, if needed.
819      */
820     if((file_fd = volume_info[fd].fd) < 0) {
821         file_release(fd);
822         if ((file_fd = file_open(fd)) < 0) {
823             return -1;
824         }
825     }
826
827     /*
828      * Truncate the write if requested and return a simulated ENOSPC.
829      */
830     if ((length = tapefd_getinfo_length(fd)) > (off_t)0) {
831         kbytes_left = length - volume_info[fd].amount_written;
832         if ((off_t)(write_count / 1024) > kbytes_left) {
833             write_count = (ssize_t)kbytes_left * 1024;
834         }
835     }
836     volume_info[fd].amount_written += (off_t)((write_count + 1023) / 1024);
837     if (write_count <= 0) {
838         volume_info[fd].at_bof = 0;
839         volume_info[fd].at_eom = 1;
840         errno = ENOSPC;
841         return -1;
842     }
843
844     /*
845      * Do the write and truncate the file, if needed.  Checking for
846      * last_operation_write is an optimization so we only truncate
847      * once.
848      */
849     if (! volume_info[fd].last_operation_write) {
850         off_t curpos;
851
852         if ((curpos = lseek(file_fd, (off_t)0, SEEK_CUR)) < 0) {
853             dbprintf((": Can not determine current file position <%s>",
854                 strerror(errno)));
855             return -1;
856         }
857         if (ftruncate(file_fd, curpos) != 0) {
858             dbprintf(("ftruncate failed; Can not trim output file <%s>",
859                 strerror(errno)));
860             return -1;
861         }
862         volume_info[fd].at_bof = 0;
863         volume_info[fd].at_eom = 1;
864     }
865     result = fullwrite(file_fd, buffer, (size_t)write_count);
866     if (result >= 0) {
867         volume_info[fd].last_operation_write = 1;
868         pos = volume_info[fd].file_current;
869         put_record_size(&volume_info[fd].fi[pos],
870                         volume_info[fd].record_current,
871                         (size_t)result);
872         volume_info[fd].record_current += (off_t)1;
873     }
874
875     return result;
876 }
877
878 int
879 file_tapefd_close(
880     int fd)
881 {
882     off_t pos;
883     int save_errno;
884     char *line;
885     size_t len;
886     char number[NUM_STR_SIZE];
887     ssize_t result;
888     struct file_info **fi_p;
889     struct record_info **ri_p;
890
891     /*
892      * If our last operation was a write, write a tapemark.
893      */
894     if (volume_info[fd].last_operation_write) {
895         if ((result = (ssize_t)file_tapefd_weof(fd, (off_t)1)) != 0) {
896             return (int)result;
897         }
898     }
899
900     /*
901      * If we are not at BOF, fsf to the next file unless we
902      * are already at end of tape.
903      */
904     if (! volume_info[fd].at_bof && ! volume_info[fd].at_eom) {
905         if ((result = (ssize_t)file_tapefd_fsf(fd, (off_t)1)) != 0) {
906             return (int)result;
907         }
908     }
909
910     /*
911      * Close the file if it is still open.
912      */
913     file_close(fd);
914
915     /*
916      * Release the info structure areas.
917      */
918     for (pos = 0; pos < (off_t)volume_info[fd].fi_limit; pos++) {
919         amfree(volume_info[fd].fi[pos].name);
920         ri_p = &volume_info[fd].fi[pos].ri;
921         amtable_free((void **)ri_p,
922                      &volume_info[fd].fi[pos].ri_limit);
923         volume_info[fd].fi[pos].ri_count = 0;
924     }
925     fi_p = &volume_info[fd].fi;
926     amtable_free((void **)fi_p, &volume_info[fd].fi_limit);
927     volume_info[fd].file_count = 0;
928     amfree(volume_info[fd].basename);
929
930     /*
931      * Update the status file if we were online.
932      */
933     if (volume_info[fd].is_online) {
934         if (lseek(fd, (off_t)0, SEEK_SET) != (off_t)0) {
935             save_errno = errno;
936             aclose(fd);
937             errno = save_errno;
938             return -1;
939         }
940         if (ftruncate(fd, (off_t)0) != 0) {
941             save_errno = errno;
942             aclose(fd);
943             errno = save_errno;
944             return -1;
945         }
946         snprintf(number, SIZEOF(number), "%05" OFF_T_RFMT,
947                  (OFF_T_FMT_TYPE)volume_info[fd].file_current);
948         line = vstralloc("position ", number, "\n", NULL);
949         len = strlen(line);
950         result = write(fd, line, len);
951         amfree(line);
952         if (result != (ssize_t)len) {
953             if (result >= 0) {
954                 errno = ENOSPC;
955             }
956             save_errno = errno;
957             aclose(fd);
958             errno = save_errno;
959             return -1;
960         }
961     }
962
963     areads_relbuf(fd);
964     return close(fd);
965 }
966
967 void
968 file_tapefd_resetofs(
969     int fd)
970 {
971     (void)fd;   /* Quiet unused parameter warning */
972 }
973
974 int
975 file_tapefd_status(
976     int                  fd,
977     struct am_mt_status *stat)
978 {
979     int result;
980
981     /*
982      * See if we are online.
983      */
984     if ((result = check_online(fd)) != 0) {
985         return result;
986     }
987     memset((void *)stat, 0, SIZEOF(*stat));
988     stat->online_valid = 1;
989     stat->online = (char)volume_info[fd].is_online;
990     return 0;
991 }
992
993 int
994 file_tape_stat(
995      char *             filename,
996      struct stat *      buf)
997 {
998      return stat(filename, buf);
999 }
1000
1001 int
1002 file_tape_access(
1003      char *     filename,
1004      int        mode)
1005 {
1006      return access(filename, mode);
1007 }
1008
1009 int
1010 file_tapefd_rewind(
1011     int fd)
1012 {
1013     int result;
1014
1015     /*
1016      * Make sure we are online.
1017      */
1018     if ((result = check_online(fd)) != 0) {
1019         return result;
1020     }
1021     if (! volume_info[fd].is_online) {
1022         errno = EIO;
1023         return -1;
1024     }
1025
1026     /*
1027      * If our last operation was a write, write a tapemark.
1028      */
1029     if (volume_info[fd].last_operation_write) {
1030         if ((result = file_tapefd_weof(fd, (off_t)1)) != 0) {
1031             return result;
1032         }
1033     }
1034
1035     /*
1036      * Close the file if it is still open.
1037      */
1038     file_close(fd);
1039
1040     /*
1041      * Adjust the position and reset the flags.
1042      */
1043     volume_info[fd].file_current = 0;
1044     volume_info[fd].record_current = (off_t)0;
1045
1046     volume_info[fd].at_bof = 1;
1047     volume_info[fd].at_eof = 0;
1048     volume_info[fd].at_eom
1049       = (volume_info[fd].file_current >= volume_info[fd].file_count);
1050     volume_info[fd].last_operation_write = 0;
1051     volume_info[fd].amount_written = (off_t)0;
1052
1053     return result;
1054 }
1055
1056 int
1057 file_tapefd_unload(
1058     int fd)
1059 {
1060     int result;
1061
1062     /*
1063      * Make sure we are online.
1064      */
1065     if ((result = check_online(fd)) != 0) {
1066         return result;
1067     }
1068     if (! volume_info[fd].is_online) {
1069         errno = EIO;
1070         return -1;
1071     }
1072
1073     (void)file_tapefd_rewind(fd);
1074     return 0;
1075 }
1076
1077 int
1078 file_tapefd_fsf(
1079     int         fd,
1080     off_t       count)
1081 {
1082     int result;
1083
1084     /*
1085      * Make sure we are online.
1086      */
1087     if ((result = check_online(fd)) != 0) {
1088         return result;
1089     }
1090     if (! volume_info[fd].is_online) {
1091         errno = EIO;
1092         return -1;
1093     }
1094
1095     /*
1096      * If our last operation was a write and we are going to move
1097      * backward, write a tapemark.
1098      */
1099     if (volume_info[fd].last_operation_write && count < 0) {
1100         if ((result = file_tapefd_weof(fd, (off_t)1)) != 0) {
1101             errno = EIO;
1102             return -1;
1103         }
1104     }
1105
1106     /*
1107      * Close the file if it is still open.
1108      */
1109     file_close(fd);
1110
1111     /*
1112      * If we are at EOM and moving backward, adjust the count to go
1113      * one more file.
1114      */
1115     if (volume_info[fd].at_eom && count < 0) {
1116         count--;
1117     }
1118
1119     /*
1120      * Adjust the position and return an error if we go beyond either
1121      * end of the tape.
1122      */
1123     volume_info[fd].file_current += count;
1124     if (volume_info[fd].file_current > volume_info[fd].file_count) {
1125         volume_info[fd].file_current = volume_info[fd].file_count;
1126         errno = EIO;
1127         result = -1;
1128     } else if (volume_info[fd].file_current < 0) {
1129         volume_info[fd].file_current = 0;
1130         errno = EIO;
1131         result = -1;
1132     }
1133     volume_info[fd].record_current = (off_t)0;
1134
1135     /*
1136      * Set BOF to true so we can write.  Set to EOF to false if the
1137      * fsf succeeded or if it failed but we were moving backward (and
1138      * thus we are at beginning of tape), otherwise set it to true so
1139      * a subsequent read will fail.  Set EOM to whatever is right.
1140      * Reset amount_written if we ended up back at BOM.
1141      */
1142     volume_info[fd].at_bof = 1;
1143     if (result == 0 || count < 0) {
1144         volume_info[fd].at_eof = 0;
1145     } else {
1146         volume_info[fd].at_eof = 1;
1147     }
1148     volume_info[fd].at_eom
1149       = (volume_info[fd].file_current >= volume_info[fd].file_count);
1150     volume_info[fd].last_operation_write = 0;
1151     if (volume_info[fd].file_current == 0) {
1152         volume_info[fd].amount_written = (off_t)0;
1153     }
1154
1155     return result;
1156 }
1157
1158 int
1159 file_tapefd_weof(
1160     int         fd,
1161     off_t       count)
1162 {
1163     int file_fd;
1164     int result;
1165     char *save_host;
1166     char *save_disk;
1167     int save_level;
1168     int save_errno;
1169
1170     /*
1171      * Make sure we are online.
1172      */
1173     if ((result = check_online(fd)) != 0) {
1174         return result;
1175     }
1176     if (! volume_info[fd].is_online) {
1177         errno = EIO;
1178         return -1;
1179     }
1180
1181     /*
1182      * Check for write access first.
1183      */
1184     if ((volume_info[fd].flags & 3) == O_RDONLY) {
1185         errno = EACCES;
1186         return -1;
1187     }
1188
1189     /*
1190      * Special case: allow a zero count.
1191      */
1192     if (count == 0) {
1193         return 0;                               /* special case */
1194     }
1195
1196     /*
1197      * Disallow negative count.
1198      */
1199     if (count < 0) {
1200         errno = EINVAL;
1201         return -1;
1202     }
1203
1204     /*
1205      * Close out the current file if open.
1206      */
1207     if ((file_fd = volume_info[fd].fd) >= 0) {
1208         off_t curpos;
1209
1210         if ((curpos = lseek(file_fd, (off_t)0, SEEK_CUR)) < 0) {
1211             save_errno = errno;
1212             dbprintf((": Can not determine current file position <%s>",
1213                 strerror(errno)));
1214             file_close(fd);
1215             errno = save_errno;
1216             return -1;
1217         }
1218         if (ftruncate(file_fd, curpos) != 0) {
1219             save_errno = errno;
1220             dbprintf(("ftruncate failed; Can not trim output file <%s>",
1221                 strerror(errno)));
1222             file_close(fd);
1223             errno = save_errno;
1224             return -1;
1225         }
1226         
1227         file_close(fd);
1228         volume_info[fd].file_current++;
1229         volume_info[fd].record_current = (off_t)0;
1230         volume_info[fd].at_bof = 1;
1231         volume_info[fd].at_eof = 0;
1232         volume_info[fd].at_eom = 1;
1233         volume_info[fd].last_operation_write = 0;
1234         count--;
1235     }
1236
1237     /*
1238      * Release any data files from current through the end.
1239      */
1240     file_release(fd);
1241
1242     /*
1243      * Save any labelling information in case we clobber it.
1244      */
1245     if ((save_host = tapefd_getinfo_host(fd)) != NULL) {
1246         save_host = stralloc(save_host);
1247     }
1248     if ((save_disk = tapefd_getinfo_disk(fd)) != NULL) {
1249         save_disk = stralloc(save_disk);
1250     }
1251     save_level = tapefd_getinfo_level(fd);
1252
1253     /*
1254      * Add more tapemarks.
1255      */
1256     while (--count >= 0) {
1257         if (file_open(fd) < 0) {
1258             break;
1259         }
1260         file_close(fd);
1261         volume_info[fd].file_current++;
1262         volume_info[fd].file_count = volume_info[fd].file_current;
1263         volume_info[fd].record_current = (off_t)0;
1264         volume_info[fd].at_bof = 1;
1265         volume_info[fd].at_eof = 0;
1266         volume_info[fd].at_eom = 1;
1267         volume_info[fd].last_operation_write = 0;
1268
1269         /*
1270          * Only the first "file" terminated by an EOF gets the naming
1271          * information from the caller.
1272          */
1273         tapefd_setinfo_host(fd, NULL);
1274         tapefd_setinfo_disk(fd, NULL);
1275         tapefd_setinfo_level(fd, -1);
1276     }
1277
1278     /*
1279      * Restore the labelling information.
1280      */
1281     save_errno = errno;
1282     tapefd_setinfo_host(fd, save_host);
1283     amfree(save_host);
1284     tapefd_setinfo_disk(fd, save_disk);
1285     amfree(save_disk);
1286     tapefd_setinfo_level(fd, save_level);
1287     errno = save_errno;
1288
1289     return result;
1290 }
1291
1292 int
1293 file_tapefd_can_fork(
1294     int fd)
1295 {
1296     (void)fd;   /* Quiet unused parameter warning */
1297     return 0;
1298 }