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