Imported Upstream version 2.5.1
[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 2006/07/06 15:04:18 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                 n = sscanf(line,
359                            OFF_T_FMT " "  OFF_T_FMT " " SIZE_T_FMT,
360                            (OFF_T_FMT_TYPE *)&start_record,
361                            (OFF_T_FMT_TYPE *)&end_record,
362                            (SIZE_T_FMT_TYPE *)&record_size);
363                 if (n == 3) {
364                     ri_p = &fi->ri;
365                     amtable_alloc((void **)ri_p,
366                                   &fi->ri_limit,
367                                   SIZEOF(*fi->ri),
368                                   (size_t)fi->ri_count + 1,
369                                   10,
370                                   NULL);
371                     ri = &fi->ri[fi->ri_count];
372                     ri->start_record = start_record;
373                     ri->end_record = end_record;
374                     ri->record_size = record_size;
375                     fi->ri_count++;
376                 }
377             }
378             aclose(rfd);
379         }
380         amfree(recordfilename);
381     }
382     return volume_info[fd].fd;
383 }
384
385 /*
386  * Close the current data file, if open.  Dump the record information
387  * if it has been altered.
388  */
389
390 static void
391 file_close(
392     int fd)
393 {
394     struct file_info *fi;
395     struct file_info **fi_p;
396     off_t pos;
397     char number[NUM_STR_SIZE];
398     char *filename = NULL;
399     size_t r;
400     FILE *f;
401
402     aclose(volume_info[fd].fd);
403     pos = volume_info[fd].file_current;
404     assert((pos + 1) < (off_t)SSIZE_MAX);
405     fi_p = &volume_info[fd].fi;
406     amtable_alloc((void **)fi_p,
407                   &volume_info[fd].fi_limit,
408                   SIZEOF(*volume_info[fd].fi),
409                   (size_t)(pos + 1),
410                   10,
411                   NULL);
412     fi = &volume_info[fd].fi[pos];
413     if (fi->ri_altered) {
414         snprintf(number, SIZEOF(number),
415                  "%05" OFF_T_RFMT, (OFF_T_FMT_TYPE)pos);
416         filename = vstralloc(volume_info[fd].basename,
417                              number,
418                              RECORD_INDICATOR,
419                              fi->name,
420                              NULL);
421         if ((f = fopen(filename, "w")) == NULL) {
422             goto common_exit;
423         }
424         for (r = 0; r < fi->ri_count; r++) {
425             fprintf(f, OFF_T_FMT " " OFF_T_FMT " " SIZE_T_FMT "\n",
426                     (OFF_T_FMT_TYPE)fi->ri[r].start_record,
427                     (OFF_T_FMT_TYPE)fi->ri[r].end_record,
428                     (SIZE_T_FMT_TYPE)fi->ri[r].record_size);
429         }
430         afclose(f);
431         fi->ri_altered = 0;
432     }
433
434 common_exit:
435
436     amfree(filename);
437 }
438
439 /*
440  * Release any files beyond a given position current position and reset
441  * file_count to file_current to indicate EOM.
442  */
443
444 static void
445 file_release(
446     int fd)
447 {
448     off_t position;
449     char *filename;
450     off_t pos;
451     char number[NUM_STR_SIZE];
452     struct file_info **fi_p;
453
454     /*
455      * If the current file is open, release everything beyond it.
456      * If it is not open, release everything from current.
457      */
458     if (volume_info[fd].fd >= 0) {
459         position = volume_info[fd].file_current + 1;
460     } else {
461         position = volume_info[fd].file_current;
462     }
463     for (pos = position; pos < volume_info[fd].file_count; pos++) {
464         assert(pos < (off_t)SSIZE_MAX);
465         fi_p = &volume_info[fd].fi;
466         amtable_alloc((void **)fi_p,
467                       &volume_info[fd].fi_limit,
468                       SIZEOF(*volume_info[fd].fi),
469                       (size_t)(pos + 1),
470                       10,
471                       NULL);
472         if (volume_info[fd].fi[pos].name != NULL) {
473             snprintf(number, SIZEOF(number),
474                      "%05" OFF_T_RFMT, (OFF_T_FMT_TYPE)pos);
475             filename = vstralloc(volume_info[fd].basename,
476                                  number,
477                                  DATA_INDICATOR,
478                                  volume_info[fd].fi[pos].name,
479                                  NULL);
480             unlink(filename);
481             amfree(filename);
482             filename = vstralloc(volume_info[fd].basename,
483                                  number,
484                                  RECORD_INDICATOR,
485                                  volume_info[fd].fi[pos].name,
486                                  NULL);
487             unlink(filename);
488             amfree(filename);
489             amfree(volume_info[fd].fi[pos].name);
490             volume_info[fd].fi[pos].ri_count = 0;
491         }
492     }
493     volume_info[fd].file_count = position;
494 }
495
496 /*
497  * Get the size of a particular record.  We assume the record information is
498  * sorted, does not overlap and does not have gaps.
499  */
500
501 static size_t
502 get_record_size(
503     struct file_info *  fi,
504     off_t               record)
505 {
506     size_t r;
507     struct record_info *ri;
508
509     for(r = 0; r < fi->ri_count; r++) {
510         ri = &fi->ri[r];
511         if (record <= ri->end_record) {
512             return ri->record_size;
513         }
514     }
515
516     /*
517      * For historical reasons, the default record size is 32 KBytes.
518      * This allows us to read files written by Amanda with that block
519      * size before the record information was being kept.
520      */
521     return 32 * 1024;
522 }
523
524 /*
525  * Update the record information.  We assume the record information is
526  * sorted, does not overlap and does not have gaps.
527  */
528
529 static void
530 put_record_size(
531     struct file_info *  fi,
532     off_t               record,
533     size_t              size)
534 {
535     size_t r;
536     struct record_info *ri;
537     struct record_info **ri_p;
538
539     fi->ri_altered = 1;
540     if (record == (off_t)0) {
541         fi->ri_count = 0;                       /* start over */
542     }
543     for(r = 0; r < fi->ri_count; r++) {
544         ri = &fi->ri[r];
545         if ((record - (off_t)1) <= ri->end_record) {
546             /*
547              * If this record is the same size as the rest of the records
548              * in this entry, or it would replace the entire entry,
549              * reset the end record number and size, then zap the chain
550              * beyond this point.
551              */
552             if (record == ri->start_record || ri->record_size == size) {
553                 ri->end_record = record;
554                 ri->record_size = size;
555                 fi->ri_count = r + 1;
556                 return;
557             }
558             /*
559              * This record needs a new entry right after the current one.
560              */
561             ri->end_record = record - (off_t)1;
562             fi->ri_count = r + 1;
563             break;
564         }
565     }
566     /*
567      * Add a new entry.
568      */
569     ri_p = &fi->ri;
570     amtable_alloc((void **)ri_p,
571                   &fi->ri_limit,
572                   SIZEOF(*fi->ri),
573                   (size_t)fi->ri_count + 1,
574                   10,
575                   NULL);
576     ri = &fi->ri[fi->ri_count];
577     ri->start_record = record;
578     ri->end_record = record;
579     ri->record_size = size;
580     fi->ri_count++;
581 }
582
583 /*
584  * The normal interface routines ...
585  */
586
587 int
588 file_tape_open(
589     char *      filename,
590     int         flags,
591     mode_t      mask)
592 {
593     int fd;
594     int save_errno;
595     char *info_file;
596     struct volume_info **volume_info_p =  &volume_info;
597
598     /*
599      * Use only O_RDONLY and O_RDWR.
600      */
601     if ((flags & 3) != O_RDONLY) {
602         flags &= ~3;
603         flags |= O_RDWR;
604     }
605
606     /*
607      * If the caller did not set O_CREAT (and thus, pass a mask
608      * parameter), we may still end up creating data files and need a
609      * "reasonable" value.  Pick a "tight" value on the "better safe
610      * than sorry" theory.
611      */
612     if ((flags & O_CREAT) == 0) {
613         mask = 0600;
614     }
615
616     /*
617      * Open/create the info file for this "tape".
618      */
619     info_file = stralloc2(filename, "/info");
620     if ((fd = open(info_file, O_RDWR|O_CREAT, 0600)) < 0) {
621         goto common_exit;
622     }
623
624     /*
625      * Create the internal info structure for this "tape".
626      */
627     amtable_alloc((void **)volume_info_p,
628                   &open_count,
629                   SIZEOF(*volume_info),
630                   (size_t)fd + 1,
631                   10,
632                   NULL);
633     volume_info[fd].flags = flags;
634     volume_info[fd].mask = mask;
635     volume_info[fd].file_count = 0;
636     volume_info[fd].file_current = 0;
637     volume_info[fd].record_current = (off_t)0;
638     volume_info[fd].fd = -1;
639     volume_info[fd].is_online = 0;              /* true when .../data found */
640     volume_info[fd].at_bof = 1;                 /* by definition */
641     volume_info[fd].at_eof = 0;                 /* do not know yet */
642     volume_info[fd].at_eom = 0;                 /* may get reset below */
643     volume_info[fd].last_operation_write = 0;
644     volume_info[fd].amount_written = (off_t)0;
645
646     /*
647      * Save the base directory name and see if we are "online".
648      */
649     volume_info[fd].basename = stralloc2(filename, "/data/");
650     if (check_online(fd)) {
651         save_errno = errno;
652         aclose(fd);
653         fd = -1;
654         amfree(volume_info[fd].basename);
655         errno = save_errno;
656         goto common_exit;
657     }
658
659 common_exit:
660
661     amfree(info_file);
662
663     /*
664      * Return the info file descriptor as the unique descriptor for
665      * this open.
666      */
667     return fd;
668 }
669
670 ssize_t
671 file_tapefd_read(
672     int         fd,
673     void *      buffer,
674     size_t      count)
675 {
676     ssize_t result;
677     int file_fd;
678     off_t pos;
679     size_t record_size;
680     size_t read_size;
681
682     /*
683      * Make sure we are online.
684      */
685     if (check_online(fd) != 0) {
686         return -1;
687     }
688     if (! volume_info[fd].is_online) {
689         errno = EIO;
690         return -1;
691     }
692
693     /*
694      * Do not allow any more reads after we find EOF.
695      */
696     if (volume_info[fd].at_eof) {
697         errno = EIO;
698         return -1;
699     }
700
701     /*
702      * If we are at EOM, set EOF and return a zero length result.
703      */
704     if (volume_info[fd].at_eom) {
705         volume_info[fd].at_eof = 1;
706         return 0;
707     }
708
709     /*
710      * Open the file, if needed.
711      */
712     if ((file_fd = file_open(fd)) < 0) {
713         return -1;
714     }
715
716     /*
717      * Make sure we do not read too much.
718      */
719     pos = volume_info[fd].file_current;
720     record_size = get_record_size(&volume_info[fd].fi[pos],
721                                   volume_info[fd].record_current);
722     if (record_size <= count) {
723         read_size = record_size;
724     } else {
725         read_size = count;
726     }
727
728     /*
729      * Read the data.  If we ask for less than the record size, skip to
730      * the next record boundary.
731      */
732     result = read(file_fd, buffer, read_size);
733     if (result > 0) {
734         volume_info[fd].at_bof = 0;
735         if ((size_t)result < record_size) {
736             if (lseek(file_fd, (off_t)(record_size-result), SEEK_CUR) == (off_t)-1) {
737                 dbprintf(("file_tapefd_read: lseek failed: <%s>\n",
738                           strerror(errno)));
739             }
740         }
741         volume_info[fd].record_current += (off_t)1;
742     } else if (result == 0) {
743         volume_info[fd].at_eof = 1;
744     }
745     return result;
746 }
747
748 ssize_t
749 file_tapefd_write(
750     int         fd,
751     const void *buffer,
752     size_t      count)
753 {
754     int file_fd;
755     ssize_t write_count = (ssize_t)count;
756     off_t length;
757     off_t kbytes_left;
758     ssize_t result;
759     off_t pos;
760
761     /*
762      * Make sure we are online.
763      */
764     if (check_online(fd) != 0) {
765         return -1;
766     }
767     if (! volume_info[fd].is_online) {
768         errno = EIO;
769         return -1;
770     }
771
772     /*
773      * Check for write access first.
774      */
775     if ((volume_info[fd].flags & 3) == O_RDONLY) {
776         errno = EBADF;
777         return -1;
778     }
779
780     /*
781      * Special case: allow negative buffer size.
782      */
783     if (write_count <= 0) {
784         return 0;                               /* special case */
785     }
786
787     /*
788      * If we are at EOM, it takes precedence over EOF.
789      */
790     if (volume_info[fd].at_eom) {
791         volume_info[fd].at_eof = 0;
792     }
793
794 #if 0 /*JJ*/
795     /*
796      * Writes are only allowed at BOF and EOM.
797      */
798     if (! (volume_info[fd].at_bof || volume_info[fd].at_eom)) {
799         errno = EIO;
800         return -1;
801     }
802 #endif /*JJ*/
803
804     /*
805      * Writes are only allowed if we are not at EOF.
806      */
807     if (volume_info[fd].at_eof) {
808         errno = EIO;
809         return -1;
810     }
811
812     /*
813      * Open the file, if needed.
814      */
815     if((file_fd = volume_info[fd].fd) < 0) {
816         file_release(fd);
817         if ((file_fd = file_open(fd)) < 0) {
818             return -1;
819         }
820     }
821
822     /*
823      * Truncate the write if requested and return a simulated ENOSPC.
824      */
825     if ((length = tapefd_getinfo_length(fd)) > (off_t)0) {
826         kbytes_left = length - volume_info[fd].amount_written;
827         if ((off_t)(write_count / 1024) > kbytes_left) {
828             write_count = (ssize_t)kbytes_left * 1024;
829         }
830     }
831     volume_info[fd].amount_written += (off_t)((write_count + 1023) / 1024);
832     if (write_count <= 0) {
833         volume_info[fd].at_bof = 0;
834         volume_info[fd].at_eom = 1;
835         errno = ENOSPC;
836         return -1;
837     }
838
839     /*
840      * Do the write and truncate the file, if needed.  Checking for
841      * last_operation_write is an optimization so we only truncate
842      * once.
843      */
844     if (! volume_info[fd].last_operation_write) {
845         off_t curpos;
846
847         if ((curpos = lseek(file_fd, (off_t)0, SEEK_CUR)) < 0) {
848             dbprintf((": Can not determine current file position <%s>",
849                 strerror(errno)));
850             return -1;
851         }
852         if (ftruncate(file_fd, curpos) != 0) {
853             dbprintf(("ftruncate failed; Can not trim output file <%s>",
854                 strerror(errno)));
855             return -1;
856         }
857         volume_info[fd].at_bof = 0;
858         volume_info[fd].at_eom = 1;
859     }
860     result = fullwrite(file_fd, buffer, (size_t)write_count);
861     if (result >= 0) {
862         volume_info[fd].last_operation_write = 1;
863         pos = volume_info[fd].file_current;
864         put_record_size(&volume_info[fd].fi[pos],
865                         volume_info[fd].record_current,
866                         (size_t)result);
867         volume_info[fd].record_current += (off_t)1;
868     }
869
870     return result;
871 }
872
873 int
874 file_tapefd_close(
875     int fd)
876 {
877     off_t pos;
878     int save_errno;
879     char *line;
880     size_t len;
881     char number[NUM_STR_SIZE];
882     ssize_t result;
883     struct file_info **fi_p;
884     struct record_info **ri_p;
885
886     /*
887      * If our last operation was a write, write a tapemark.
888      */
889     if (volume_info[fd].last_operation_write) {
890         if ((result = (ssize_t)file_tapefd_weof(fd, (off_t)1)) != 0) {
891             return (int)result;
892         }
893     }
894
895     /*
896      * If we are not at BOF, fsf to the next file unless we
897      * are already at end of tape.
898      */
899     if (! volume_info[fd].at_bof && ! volume_info[fd].at_eom) {
900         if ((result = (ssize_t)file_tapefd_fsf(fd, (off_t)1)) != 0) {
901             return (int)result;
902         }
903     }
904
905     /*
906      * Close the file if it is still open.
907      */
908     file_close(fd);
909
910     /*
911      * Release the info structure areas.
912      */
913     for (pos = 0; pos < (off_t)volume_info[fd].fi_limit; pos++) {
914         amfree(volume_info[fd].fi[pos].name);
915         ri_p = &volume_info[fd].fi[pos].ri;
916         amtable_free((void **)ri_p,
917                      &volume_info[fd].fi[pos].ri_limit);
918         volume_info[fd].fi[pos].ri_count = 0;
919     }
920     fi_p = &volume_info[fd].fi;
921     amtable_free((void **)fi_p, &volume_info[fd].fi_limit);
922     volume_info[fd].file_count = 0;
923     amfree(volume_info[fd].basename);
924
925     /*
926      * Update the status file if we were online.
927      */
928     if (volume_info[fd].is_online) {
929         if (lseek(fd, (off_t)0, SEEK_SET) != (off_t)0) {
930             save_errno = errno;
931             aclose(fd);
932             errno = save_errno;
933             return -1;
934         }
935         if (ftruncate(fd, (off_t)0) != 0) {
936             save_errno = errno;
937             aclose(fd);
938             errno = save_errno;
939             return -1;
940         }
941         snprintf(number, SIZEOF(number), "%05" OFF_T_RFMT,
942                  (OFF_T_FMT_TYPE)volume_info[fd].file_current);
943         line = vstralloc("position ", number, "\n", NULL);
944         len = strlen(line);
945         result = write(fd, line, len);
946         amfree(line);
947         if (result != (ssize_t)len) {
948             if (result >= 0) {
949                 errno = ENOSPC;
950             }
951             save_errno = errno;
952             aclose(fd);
953             errno = save_errno;
954             return -1;
955         }
956     }
957
958     areads_relbuf(fd);
959     return close(fd);
960 }
961
962 void
963 file_tapefd_resetofs(
964     int fd)
965 {
966     (void)fd;   /* Quiet unused parameter warning */
967 }
968
969 int
970 file_tapefd_status(
971     int                  fd,
972     struct am_mt_status *stat)
973 {
974     int result;
975
976     /*
977      * See if we are online.
978      */
979     if ((result = check_online(fd)) != 0) {
980         return result;
981     }
982     memset((void *)stat, 0, SIZEOF(*stat));
983     stat->online_valid = 1;
984     stat->online = (char)volume_info[fd].is_online;
985     return 0;
986 }
987
988 int
989 file_tape_stat(
990      char *             filename,
991      struct stat *      buf)
992 {
993      return stat(filename, buf);
994 }
995
996 int
997 file_tape_access(
998      char *     filename,
999      int        mode)
1000 {
1001      return access(filename, mode);
1002 }
1003
1004 int
1005 file_tapefd_rewind(
1006     int fd)
1007 {
1008     int result;
1009
1010     /*
1011      * Make sure we are online.
1012      */
1013     if ((result = check_online(fd)) != 0) {
1014         return result;
1015     }
1016     if (! volume_info[fd].is_online) {
1017         errno = EIO;
1018         return -1;
1019     }
1020
1021     /*
1022      * If our last operation was a write, write a tapemark.
1023      */
1024     if (volume_info[fd].last_operation_write) {
1025         if ((result = file_tapefd_weof(fd, (off_t)1)) != 0) {
1026             return result;
1027         }
1028     }
1029
1030     /*
1031      * Close the file if it is still open.
1032      */
1033     file_close(fd);
1034
1035     /*
1036      * Adjust the position and reset the flags.
1037      */
1038     volume_info[fd].file_current = 0;
1039     volume_info[fd].record_current = (off_t)0;
1040
1041     volume_info[fd].at_bof = 1;
1042     volume_info[fd].at_eof = 0;
1043     volume_info[fd].at_eom
1044       = (volume_info[fd].file_current >= volume_info[fd].file_count);
1045     volume_info[fd].last_operation_write = 0;
1046     volume_info[fd].amount_written = (off_t)0;
1047
1048     return result;
1049 }
1050
1051 int
1052 file_tapefd_unload(
1053     int fd)
1054 {
1055     int result;
1056
1057     /*
1058      * Make sure we are online.
1059      */
1060     if ((result = check_online(fd)) != 0) {
1061         return result;
1062     }
1063     if (! volume_info[fd].is_online) {
1064         errno = EIO;
1065         return -1;
1066     }
1067
1068     (void)file_tapefd_rewind(fd);
1069     return 0;
1070 }
1071
1072 int
1073 file_tapefd_fsf(
1074     int         fd,
1075     off_t       count)
1076 {
1077     int result;
1078
1079     /*
1080      * Make sure we are online.
1081      */
1082     if ((result = check_online(fd)) != 0) {
1083         return result;
1084     }
1085     if (! volume_info[fd].is_online) {
1086         errno = EIO;
1087         return -1;
1088     }
1089
1090     /*
1091      * If our last operation was a write and we are going to move
1092      * backward, write a tapemark.
1093      */
1094     if (volume_info[fd].last_operation_write && count < 0) {
1095         if ((result = file_tapefd_weof(fd, (off_t)1)) != 0) {
1096             errno = EIO;
1097             return -1;
1098         }
1099     }
1100
1101     /*
1102      * Close the file if it is still open.
1103      */
1104     file_close(fd);
1105
1106     /*
1107      * If we are at EOM and moving backward, adjust the count to go
1108      * one more file.
1109      */
1110     if (volume_info[fd].at_eom && count < 0) {
1111         count--;
1112     }
1113
1114     /*
1115      * Adjust the position and return an error if we go beyond either
1116      * end of the tape.
1117      */
1118     volume_info[fd].file_current += count;
1119     if (volume_info[fd].file_current > volume_info[fd].file_count) {
1120         volume_info[fd].file_current = volume_info[fd].file_count;
1121         errno = EIO;
1122         result = -1;
1123     } else if (volume_info[fd].file_current < 0) {
1124         volume_info[fd].file_current = 0;
1125         errno = EIO;
1126         result = -1;
1127     }
1128     volume_info[fd].record_current = (off_t)0;
1129
1130     /*
1131      * Set BOF to true so we can write.  Set to EOF to false if the
1132      * fsf succeeded or if it failed but we were moving backward (and
1133      * thus we are at beginning of tape), otherwise set it to true so
1134      * a subsequent read will fail.  Set EOM to whatever is right.
1135      * Reset amount_written if we ended up back at BOM.
1136      */
1137     volume_info[fd].at_bof = 1;
1138     if (result == 0 || count < 0) {
1139         volume_info[fd].at_eof = 0;
1140     } else {
1141         volume_info[fd].at_eof = 1;
1142     }
1143     volume_info[fd].at_eom
1144       = (volume_info[fd].file_current >= volume_info[fd].file_count);
1145     volume_info[fd].last_operation_write = 0;
1146     if (volume_info[fd].file_current == 0) {
1147         volume_info[fd].amount_written = (off_t)0;
1148     }
1149
1150     return result;
1151 }
1152
1153 int
1154 file_tapefd_weof(
1155     int         fd,
1156     off_t       count)
1157 {
1158     int file_fd;
1159     int result;
1160     char *save_host;
1161     char *save_disk;
1162     int save_level;
1163     int save_errno;
1164
1165     /*
1166      * Make sure we are online.
1167      */
1168     if ((result = check_online(fd)) != 0) {
1169         return result;
1170     }
1171     if (! volume_info[fd].is_online) {
1172         errno = EIO;
1173         return -1;
1174     }
1175
1176     /*
1177      * Check for write access first.
1178      */
1179     if ((volume_info[fd].flags & 3) == O_RDONLY) {
1180         errno = EACCES;
1181         return -1;
1182     }
1183
1184     /*
1185      * Special case: allow a zero count.
1186      */
1187     if (count == 0) {
1188         return 0;                               /* special case */
1189     }
1190
1191     /*
1192      * Disallow negative count.
1193      */
1194     if (count < 0) {
1195         errno = EINVAL;
1196         return -1;
1197     }
1198
1199     /*
1200      * Close out the current file if open.
1201      */
1202     if ((file_fd = volume_info[fd].fd) >= 0) {
1203         off_t curpos;
1204
1205         if ((curpos = lseek(file_fd, (off_t)0, SEEK_CUR)) < 0) {
1206             save_errno = errno;
1207             dbprintf((": Can not determine current file position <%s>",
1208                 strerror(errno)));
1209             file_close(fd);
1210             errno = save_errno;
1211             return -1;
1212         }
1213         if (ftruncate(file_fd, curpos) != 0) {
1214             save_errno = errno;
1215             dbprintf(("ftruncate failed; Can not trim output file <%s>",
1216                 strerror(errno)));
1217             file_close(fd);
1218             errno = save_errno;
1219             return -1;
1220         }
1221         
1222         file_close(fd);
1223         volume_info[fd].file_current++;
1224         volume_info[fd].record_current = (off_t)0;
1225         volume_info[fd].at_bof = 1;
1226         volume_info[fd].at_eof = 0;
1227         volume_info[fd].at_eom = 1;
1228         volume_info[fd].last_operation_write = 0;
1229         count--;
1230     }
1231
1232     /*
1233      * Release any data files from current through the end.
1234      */
1235     file_release(fd);
1236
1237     /*
1238      * Save any labelling information in case we clobber it.
1239      */
1240     if ((save_host = tapefd_getinfo_host(fd)) != NULL) {
1241         save_host = stralloc(save_host);
1242     }
1243     if ((save_disk = tapefd_getinfo_disk(fd)) != NULL) {
1244         save_disk = stralloc(save_disk);
1245     }
1246     save_level = tapefd_getinfo_level(fd);
1247
1248     /*
1249      * Add more tapemarks.
1250      */
1251     while (--count >= 0) {
1252         if (file_open(fd) < 0) {
1253             break;
1254         }
1255         file_close(fd);
1256         volume_info[fd].file_current++;
1257         volume_info[fd].file_count = volume_info[fd].file_current;
1258         volume_info[fd].record_current = (off_t)0;
1259         volume_info[fd].at_bof = 1;
1260         volume_info[fd].at_eof = 0;
1261         volume_info[fd].at_eom = 1;
1262         volume_info[fd].last_operation_write = 0;
1263
1264         /*
1265          * Only the first "file" terminated by an EOF gets the naming
1266          * information from the caller.
1267          */
1268         tapefd_setinfo_host(fd, NULL);
1269         tapefd_setinfo_disk(fd, NULL);
1270         tapefd_setinfo_level(fd, -1);
1271     }
1272
1273     /*
1274      * Restore the labelling information.
1275      */
1276     save_errno = errno;
1277     tapefd_setinfo_host(fd, save_host);
1278     amfree(save_host);
1279     tapefd_setinfo_disk(fd, save_disk);
1280     amfree(save_disk);
1281     tapefd_setinfo_level(fd, save_level);
1282     errno = save_errno;
1283
1284     return result;
1285 }
1286
1287 int
1288 file_tapefd_can_fork(
1289     int fd)
1290 {
1291     (void)fd;   /* Quiet unused parameter warning */
1292     return 0;
1293 }