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