e6b71ab03e33ca3c47817aba22a92c067e8a5d30
[openafs.git] / src / butm / file_tm.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #ifdef AFS_NT40_ENV
17 #include <winsock2.h>
18 #else
19 #include <netinet/in.h>
20 #endif
21 #include <sys/types.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <sys/stat.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <lwp.h>
29 #include <afs/com_err.h>
30 #include <afs/butm.h>
31 #include <afs/usd.h>
32 #include "error_macros.h"
33
34 #ifdef O_LARGEFILE
35 typedef off64_t osi_lloff_t;
36 #else /* O_LARGEFILE */
37 #ifdef AFS_HAVE_LLSEEK
38 typedef offset_t osi_lloff_t;
39 #else /* AFS_HAVE_LLSEEK */
40 typedef off_t osi_lloff_t;
41 #endif /* AFS_HAVE_LLSEEK */
42 #endif /* O_LARGEFILE */
43
44 extern int isafile;
45
46 #define FILE_MAGIC  1000000007  /* s/w file mark */
47 #define FILE_BEGIN  0           /* byte field in file mark */
48 #define FILE_FMEND    1         /* byte field in file mark */
49 #define FILE_EOD    -1          /* byte field in file mark */
50 #define TAPE_MAGIC  1100000009  /* tape label block */
51 #define BLOCK_MAGIC 1100000005  /* file data block */
52 #ifdef AFS_PTHREAD_ENV
53 #define POLL()
54 #define SLEEP(s) sleep(s)
55 #else
56 #define POLL()   IOMGR_Poll()
57 #define SLEEP(s) IOMGR_Sleep(s)
58 #endif
59
60 /* Notes: (PA)
61  *
62  * 1) filemarks and block marks have the magic,bytes fields reversed. This
63  *      is undoubtedly a bug. Also note that the two structures have
64  *      inconsistent types, overlaying int and afs_int32.
65  * 2) When a volume is dumped, if the volume is locked, the dump will produce
66  *      an anomalous tape format of the form:
67  *              s/w file begin mark
68  *              volume header
69  *              s/w file end mark
70  *      The design of the original butm code means that this cannot be
71  *      handled correctly. The code is modified so that ReadFileData
72  *      returns BUTM_ENDVOLUME if it encounters the s/w filemark.
73  */
74
75 /* data organization on tape:
76  * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
77  * blockMark contains a magic number and counts of real data bytes
78  * written out in the block.
79  *
80  * each file is preceeded by a fileMark, which acts as the file 
81  * delimiter. A file consists of contigous data blocks. TM does
82  * understand or interrpet the data in data blocks. 
83  *
84  * The tape begins with a tape label and ends with EOF file markers
85  * in succession (2 or 4 of them ).
86  */
87
88
89 struct fileMark {               /* in network byte order */
90     afs_int32 magic;
91     afs_uint32 nBytes;
92 };
93
94 struct tapeLabel {
95     afs_int32 magic;
96     struct butm_tapeLabel label;
97 };
98
99 struct progress {
100     usd_handle_t fid;           /* file id of simulated tape */
101     afs_int32 mountId;          /* current mountId */
102     afs_int32 reading;          /* read file operation in progress */
103     afs_int32 writing;          /* write file operation in progress */
104 };
105
106 static struct configuration {
107     char tapedir[64];           /* directory to create "tapes" */
108     afs_int32 mountId;          /* for detecting simultaneous mounts */
109     afs_uint32 tapeSize;        /* size of simulated tapes */
110     afs_uint32 fileMarkSize;    /* size of file mark, bytes */
111     afs_int32 portOffset;       /* port + portOffset is used by TC to listen */
112 } config;
113
114 static char *whoami = "file_tm";
115 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
116
117 #define BLOCK_LABEL      0      /* read/write a  tape label     */
118 #define BLOCK_FMBEGIN    1      /* read/write a  begin FileMark */
119 #define BLOCK_DATA       2      /* read/write a  data block     */
120 #define BLOCK_FMEND      3      /* read/write an end FileMark   */
121 #define BLOCK_EOD        4      /* read/write an EOD FileMark   */
122 #define BLOCK_EOF        5      /* tape block is a HW EOF mark (usually error) */
123 #define BLOCK_UNKNOWN    6      /* tape block is unknwon (error) */
124
125 #define WRITE_OP 1
126 #define READ_OP  0
127
128 #define READS  (((struct progress *)(info->tmRock))->reading)
129 #define WRITES (((struct progress *)(info->tmRock))->writing)
130
131 /* ----------------------------------------------------------------------
132  * These routines use the usd library to perform tape operations.
133  * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF, 
134  * PrepareAccess(nt), ShutdownAccess(nt)
135  * 
136  * Return Values: USD functions return 0 if successful or errno if failed.
137  */
138 /* ------------------ USD Interface Functions Begin ------------------------ */
139
140 /*
141  * On Unix, fork a child process to perform an IOCTL call. This avoids
142  * blocking the entire process during a tape operation
143  */
144
145 #ifndef AFS_NT40_ENV
146 /* Unix version of function */
147
148 static int
149 ForkIoctl(usd_handle_t fd, int op, int count)
150 {
151     int rc;                     /* return code from system calls */
152     int i;                      /* loop index */
153     int pid;                    /* process ID of child process */
154     int status;                 /* exit status of child process */
155     int ioctl_rc;               /* return code from ioctl call */
156     int pipefd[2];              /* pipe for child return status */
157     int forkflag;               /* flag set when ready to fork */
158     usd_tapeop_t tapeop;        /* tape operation specification */
159     int unixfd;
160
161 #ifdef AFS_PTHREAD_ENV
162     forkflag = 0;               /* No need to fork if using pthreads */
163 #else
164     forkflag = 1;
165 #endif
166
167     /* initialize tape command structure */
168     tapeop.tp_op = op;
169     tapeop.tp_count = count;
170
171     /* create pipe for getting return code from child */
172     if (forkflag) {
173         rc = pipe(pipefd);
174         if (rc < 0) {
175             printf("butm:  Can't open pipe for IOCTL process. Error %d\n",
176                    errno);
177             forkflag = 0;
178         }
179     }
180
181     if (forkflag) {
182         pid = fork();
183         if (pid < 0) {
184             close(pipefd[0]);
185             close(pipefd[1]);
186             printf("butm:  Can't fork IOCTL process. Error %d\n", errno);
187             forkflag = 0;
188
189         }
190     }
191
192     if (!forkflag) {            /* problem starting child process */
193         /* do the ioctl anyway, it will probably work */
194         ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
195     } else if (pid == 0) {      /* child process */
196         /* close all unneccessary file descriptors */
197         /* note: as painful as it is, we have to reach under the covers of
198          *       the usd package to implement this functionality.
199          */
200         unixfd = (int)(fd->handle);
201
202         for (i = 3; i < _POSIX_OPEN_MAX; i++) {
203             if (i != unixfd && i != pipefd[1]) {
204                 close(i);
205             }
206         }
207
208         /* do the ioctl call */
209         ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
210
211         /* send the return code back to the parent */
212         write(pipefd[1], &ioctl_rc, sizeof(int));
213
214         exit(0);
215     } else {                    /* parent process */
216
217         close(pipefd[1]);
218         POLL();
219         /* read the result from the child process */
220         rc = read(pipefd[0], &ioctl_rc, sizeof(int));
221         if (rc != sizeof(int)) {
222             /* tape is now in unknown state */
223             printf("butm:  Can't determine IOCTL child status. Error %d\n",
224                    errno);
225             ioctl_rc = EFAULT;
226         }
227
228         close(pipefd[0]);
229
230         /* get the completion status from the child process */
231         rc = waitpid(pid, &status, 0);
232         while (rc < 0 && errno == EINTR) {
233             rc = waitpid(pid, &status, 0);
234         }
235         if (rc < 0) {
236             printf("butm:  Can't determine IOCTL child status. Error %d\n",
237                    errno);
238         } else if (status != 0) {
239             printf
240                 ("butm: Unexpected IOCTL process status 0x%04x . Error %d\n",
241                  status, errno);
242         }
243         SLEEP(1);
244     }
245
246     return (ioctl_rc);
247 }
248 #else
249 /* NT version of function */
250
251 static int
252 ForkIoctl(usd_handle_t fd, int op, int count)
253 {
254     usd_tapeop_t tapeop;
255
256     /* Issue requested tape control */
257     tapeop.tp_op = op;
258     tapeop.tp_count = count;
259
260     return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
261 }
262 #endif /* !AFS_NT40_ENV */
263
264
265 /*
266  * On Unix, fork a child process to attempt to open the drive. We want to make
267  * certain there is tape in the drive before trying to open the device
268  * in the main process
269  */
270
271 #ifndef AFS_NT40_ENV
272 /* Unix version of function. */
273
274 static int
275 ForkOpen(char *device)
276 {
277     int rc;                     /* return code from system calls */
278     int i;                      /* loop index */
279     int pid;                    /* process ID of child process */
280     int status;                 /* exit status of child process */
281     int open_rc;                /* return code from open */
282     int pipefd[2];              /* pipe for child return status */
283     int forkflag;               /* flag set when ready to fork */
284     usd_handle_t fd;            /* handle returned from open */
285
286 #ifdef AFS_PTHREAD_ENV
287     forkflag = 0;               /* No need to fork if using pthreads */
288 #else
289     forkflag = 1;
290 #endif
291
292     /* create pipe for getting return code from child */
293     if (forkflag) {
294         rc = pipe(pipefd);
295         if (rc < 0) {
296             printf("butm: Cannot create pipe for OPEN process. Error %d\n",
297                    errno);
298             forkflag = 0;
299         }
300     }
301
302     if (forkflag) {
303         pid = fork();
304         if (pid < 0) {
305             close(pipefd[0]);
306             close(pipefd[1]);
307             printf("butm: Cannot create child process for OPEN. Error %d\n",
308                    errno);
309             forkflag = 0;
310         }
311     }
312
313     if (!forkflag) {            /* problem starting child process */
314         /*
315          *return success, the caller will discover any problems
316          * when it opens the device.
317          */
318         open_rc = 0;
319     } else if (pid == 0) {      /* child process */
320         /* close all unneccessary file descriptors */
321         for (i = 3; i < _POSIX_OPEN_MAX; i++) {
322             if (i != pipefd[1]) {
323                 close(i);
324             }
325         }
326
327         /* try the open */
328         open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
329
330         if (open_rc == 0) {
331             USD_CLOSE(fd);
332         }
333
334         /* send the return code back to the parent */
335         write(pipefd[1], &open_rc, sizeof(open_rc));
336
337         exit(0);
338     } else {                    /* parent process */
339
340         close(pipefd[1]);
341
342         POLL();
343         /* read the result from the child process */
344         rc = read(pipefd[0], &open_rc, sizeof(open_rc));
345         if (rc != sizeof(open_rc)) {
346             /* this is not a problem since we will reopen the device anyway */
347             printf("butm: No response from  OPEN process. Error %d\n", errno);
348             open_rc = 0;
349         }
350
351         close(pipefd[0]);
352
353         /* get the completion status from the child process */
354         rc = waitpid(pid, &status, 0);
355         while (rc < 0 && errno == EINTR) {
356             rc = waitpid(pid, &status, 0);
357         }
358         if (rc < 0) {
359             printf("butm: Cannot get status of OPEN process. Error %d\n",
360                    errno);
361         } else if (status != 0) {
362             printf
363                 ("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n",
364                  status, errno);
365         }
366         SLEEP(1);
367     }
368
369     return (open_rc);
370 }
371 #else
372 /* NT version of function. */
373
374 static int
375 ForkOpen(char *device)
376 {
377     return (0);
378 }
379 #endif /* AFS_NT40_ENV */
380
381 /*
382  * On Unix, fork a child process to close the drive. If the drive rewinds
383  * on close it could cause the process to block.
384  */
385
386 #ifndef AFS_NT40_ENV
387 /* Unix version of function */
388
389 static int
390 ForkClose(usd_handle_t fd)
391 {
392     int rc;                     /* return code from system calls */
393     int i;                      /* loop index */
394     int pid;                    /* process ID of child process */
395     int status;                 /* exit status of child process */
396     int close_rc, parent_close_rc;      /* return codes from close */
397     int pipefd[2];              /* pipe for child return status */
398     int ctlpipe[2];             /* pipe for message to child */
399     int forkflag;               /* flag set when ready to fork */
400     int unixfd;
401
402 #ifdef AFS_PTHREAD_ENV
403     forkflag = 0;               /* No need to fork if using pthreads */
404 #else
405     forkflag = 1;
406 #endif
407
408     /* create pipe for getting return code from child */
409     if (forkflag) {
410         rc = pipe(pipefd);
411         if (rc < 0) {
412             printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
413                    errno);
414             forkflag = 0;
415         }
416     }
417
418     /* create pipe for notifying child when to close */
419     if (forkflag) {
420         rc = pipe(ctlpipe);
421         if (rc < 0) {
422             close(pipefd[0]);
423             close(pipefd[1]);
424             printf("butm: Cannot create pipe for CLOSE  process. Error %d\n",
425                    errno);
426             forkflag = 0;
427         }
428     }
429
430     if (forkflag) {
431         pid = fork();
432         if (pid < 0) {
433             close(pipefd[0]);
434             close(pipefd[1]);
435             close(ctlpipe[0]);
436             close(ctlpipe[1]);
437             printf("butm: Cannot create CLOSE child process. Error %d\n",
438                    errno);
439             forkflag = 0;
440         }
441     }
442
443     if (!forkflag) {            /* problem starting child process */
444         close_rc = USD_CLOSE(fd);
445         parent_close_rc = close_rc;
446     } else if (pid == 0) {      /* child process */
447         /* close all unneccessary file descriptors */
448         /* note: as painful as it is, we have to reach under the covers of
449          *       the usd package to implement this functionality.
450          */
451         unixfd = (int)(fd->handle);
452
453         for (i = 3; i < _POSIX_OPEN_MAX; i++) {
454             if (i != unixfd && i != ctlpipe[0] && i != pipefd[1]) {
455                 close(i);
456             }
457         }
458
459         /* the parent writes the control pipe after it closes the device */
460         read(ctlpipe[0], &close_rc, sizeof(int));
461         close(ctlpipe[0]);
462
463         /* do the close */
464         close_rc = USD_CLOSE(fd);
465
466         /* send the return code back to the parent */
467         write(pipefd[1], &close_rc, sizeof(int));
468
469         exit(0);
470     } else {                    /* parent process */
471
472         close(pipefd[1]);
473         close(ctlpipe[0]);
474
475         POLL();
476         /*
477          * close the device, this should have no effect as long as the
478          * child has not closed
479          */
480
481         parent_close_rc = USD_CLOSE(fd);
482
483         /* notify the child to do its close */
484         rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
485         if (rc != sizeof(int)) {
486             printf("butm: Error communicating with CLOSE process. Error %d\n",
487                    errno);
488         }
489         close(ctlpipe[1]);
490
491         /* read the result from the child process */
492         rc = read(pipefd[0], &close_rc, sizeof(int));
493         if (rc != sizeof(int)) {
494             /* logging is enough, since we wrote a file mark the */
495             /* return code from the close doesn't really matter  */
496             printf("butm: No response from  CLOSE  process. Error %d\n",
497                    errno);
498             close_rc = 0;
499         }
500
501         close(pipefd[0]);
502
503         /* get the completion status from the child process */
504         rc = waitpid(pid, &status, 0);
505         while (rc < 0 && errno == EINTR) {
506             rc = waitpid(pid, &status, 0);
507         }
508         if (rc < 0) {
509             printf("butm: Cannot get status of CLOSE  process. Error %d\n",
510                    errno);
511         } else if (status != 0) {
512             printf
513                 ("butm: Unexpected exit status 0x04x from CLOSE  process. Error %d\n",
514                  status, errno);
515         }
516
517         /* if either process received an error, then return an error */
518         if (parent_close_rc < 0) {
519             close_rc = parent_close_rc;
520         }
521         SLEEP(1);
522     }
523
524     return (close_rc);
525 }
526 #else
527 /* NT version of function */
528
529 static int
530 ForkClose(usd_handle_t fd)
531 {
532     return (USD_CLOSE(fd));
533 }
534 #endif /* AFS_NT40_ENV */
535
536 /* Forward space file */
537 static int
538 ForwardSpace(usd_handle_t fid, int count)
539 {
540     POLL();
541
542     if (isafile) {
543         return (0);
544     } else {
545         return (ForkIoctl(fid, USDTAPE_FSF, count));
546     }
547 }
548
549 /* Backward space file */
550 static int
551 BackwardSpace(usd_handle_t fid, int count)
552 {
553     POLL();
554
555     if (isafile) {
556         return (0);
557     } else {
558         return (ForkIoctl(fid, USDTAPE_BSF, count));
559     }
560 }
561
562 /* write end of file mark */
563 static
564 WriteEOF(usd_handle_t fid, int count)
565 {
566     POLL();
567
568     if (isafile) {
569         return (0);
570     } else {
571         return (ForkIoctl(fid, USDTAPE_WEOF, count));
572     }
573 }
574
575 /* rewind tape */
576 static int
577 Rewind(usd_handle_t fid)
578 {
579     if (isafile) {
580         afs_hyper_t startOff, stopOff;
581         hzero(startOff);
582
583         return (USD_SEEK(fid, startOff, SEEK_SET, &stopOff));
584     } else {
585         return (ForkIoctl(fid, USDTAPE_REW, 0));
586     };
587 }
588
589 /* prepare tape drive for access */
590 static int
591 PrepareAccess(usd_handle_t fid)
592 {
593     int code = 0;
594 #ifdef AFS_NT40_ENV
595     if (!isafile) {
596         (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
597     }
598     /* NT won't rewind tape when it is opened */
599     code = Rewind(fid);
600 #endif /* AFS_NT40_ENV */
601     return code;
602 }
603
604 /* decommission tape drive after all accesses complete */
605 static int
606 ShutdownAccess(usd_handle_t fid)
607 {
608 #ifdef AFS_NT40_ENV
609     if (!isafile) {
610         (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
611     }
612 #endif /* AFS_NT40_ENV */
613     return 0;
614 }
615
616 /* -------------------- USD Interface Functions End ----------------------- */
617
618 /* =====================================================================
619  * Support routines
620  * ===================================================================== */
621
622 /* incSize
623  *      add the supplied no. of bytes to the byte count of information placed
624  *      on the tape.
625  * entry:
626  *      dataSize - bytes used on the tape
627  */
628
629 incSize(info, dataSize)
630      struct butm_tapeInfo *info;
631      afs_uint32 dataSize;
632 {
633     info->nBytes += dataSize;
634     info->kBytes += (info->nBytes / 1024);
635     info->nBytes %= 1024;
636 }
637
638 /* incPosition
639  *      IF YOU ADD/CHANGE THE ifdefs, BE SURE
640  *      TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
641  *
642  *      add the supplied no. of bytes to the byte count of data placed
643  *      on the tape. Also check for reaching 2GB limit and reset the 
644  *      pointer if necessary.  This allows us to use >2GB tapes.
645  * entry:
646  *      fid      - file id for the tape.
647  *      dataSize - bytes used on the tape
648  */
649
650 incPosition(info, fid, dataSize)
651      struct butm_tapeInfo *info;
652      usd_handle_t fid;
653      afs_uint32 dataSize;
654 {
655     /* Add this to the amount of data written to the tape */
656     incSize(info, dataSize);
657
658     info->posCount += dataSize;
659
660     if (info->posCount >= 2147467264) { /* 2GB - 16K */
661         info->posCount = 0;
662 #if (defined(AFS_SUN_ENV) || defined(AFS_LINUX24_ENV))
663         if (!isafile) {
664         afs_hyper_t off;
665             hset64(off, 0, 0);
666             USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
667         }
668 #endif
669     }
670 }
671
672 /*
673  * This accounts for tape drives with a block size different from variable or 16K 
674  * blocks and only reads that block size.
675  */
676 afs_int32 TapeBlockSize;
677 afs_int32
678 readData(fid, data, totalSize, errorP)
679      usd_handle_t fid;
680      char *data;
681      afs_int32 totalSize;
682      afs_int32 *errorP;
683 {
684     afs_int32 toread;           /* Number of bytes to read */
685     afs_int32 rSize;            /* Total bytes read so far */
686     afs_int32 tSize;            /* Temporary size */
687     afs_int32 rc;               /* return code */
688
689     toread = totalSize;         /* First, try to read all the data */
690
691     rc = USD_READ(fid, &data[0], toread, &rSize);
692     if (rc != 0) {
693         *errorP = rc;
694         return -1;
695     }
696     if (rSize == 0)             /* reached EOF */
697         return rSize;
698
699     if (rSize != TapeBlockSize) {       /* Tape block size has changed */
700         TapeBlockSize = rSize;
701         printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
702     }
703
704     /* Read the rest of the data in */
705     while (rSize < totalSize) {
706         toread =
707             ((totalSize - rSize) <
708              TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
709         rc = USD_READ(fid, &data[rSize], toread, &tSize);
710         if (rc)
711             *errorP = rc;
712         else
713             rSize += tSize;
714         if (tSize != toread)
715             break;
716     }
717
718     if (rSize > totalSize)
719         printf("readData - Read > 16K data block - continuing.\n");
720
721     return (rSize);
722 }
723
724 afs_int32
725 SeekFile(info, count)
726      struct butm_tapeInfo *info;
727      int count;
728 {
729     afs_int32 code = 0;
730     struct progress *p;
731     afs_int32 error = 0;
732
733     if (count == 0)
734         ERROR_EXIT(0);
735     p = (struct progress *)info->tmRock;
736
737     if (isafile) {              /* no reason for seeking through a file */
738         p->reading = p->writing = 0;
739         return (0);
740     }
741
742     POLL();
743
744     if (count > 0)
745         error = ForwardSpace(p->fid, count);
746     else
747         error = BackwardSpace(p->fid, -count);
748
749     POLL();
750
751     if (error) {
752         info->status |= BUTM_STATUS_SEEKERROR;
753         ERROR_EXIT(BUTM_IOCTL);
754     }
755
756     info->position += count;
757     incSize(info, (count * config.fileMarkSize));
758     p = (struct progress *)info->tmRock;
759     p->reading = p->writing = 0;
760
761   error_exit:
762     if (error)
763         info->error = error;
764     return (code);
765 }
766
767 /* Step to the next filemark if we are not at one already */
768 afs_int32
769 NextFile(info)
770      struct butm_tapeInfo *info;
771 {
772     afs_int32 code;
773
774     if (!READS && !WRITES)
775         return 0;
776
777     code = SeekFile(info, 1);
778     return (code);
779 }
780
781 static afs_int32
782 WriteTapeBlock(info, buffer, length, blockType)
783      struct butm_tapeInfo *info;
784      char *buffer;              /* assumed to be 16384 bytes with data in it */
785      afs_int32 length;          /* amount data in buffer */
786      afs_int32 blockType;
787 {
788     afs_int32 code = 0, rc = 0;
789     afs_int32 wsize;
790     struct tapeLabel *label;
791     struct fileMark *fmark;
792     struct blockMark *bmark;
793     struct progress *p;
794     afs_int32 error = 0;
795
796     p = (struct progress *)info->tmRock;
797
798     if (blockType == BLOCK_DATA) {      /* Data Block */
799         if (length == 0)
800             ERROR_EXIT(0);
801         bmark = (struct blockMark *)buffer;
802         memset(bmark, 0, sizeof(struct blockMark));
803         bmark->magic = htonl(BLOCK_MAGIC);
804         bmark->count = htonl(length);
805     } else if (blockType == BLOCK_FMBEGIN) {    /* Filemark - begin */
806         fmark = (struct fileMark *)buffer;
807         fmark->magic = htonl(FILE_MAGIC);
808         fmark->nBytes = htonl(FILE_BEGIN);
809     } else if (blockType == BLOCK_FMEND) {      /* Filemark - end */
810         fmark = (struct fileMark *)buffer;
811         fmark->magic = htonl(FILE_MAGIC);
812         fmark->nBytes = htonl(FILE_FMEND);
813     } else if (blockType == BLOCK_LABEL) {      /* Label */
814         label = (struct tapeLabel *)buffer;
815         label->magic = htonl(TAPE_MAGIC);
816     } else if (blockType == BLOCK_EOD) {        /* Filemark - EOD mark */
817         fmark = (struct fileMark *)buffer;
818         fmark->magic = htonl(FILE_MAGIC);
819         fmark->nBytes = htonl(FILE_EOD);
820     }
821
822     /* Write the tape block */
823     /* -------------------- */
824     rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
825     if ((rc == 0) && (wsize > 0)) {
826         incPosition(info, p->fid, wsize);       /* record whats written */
827         p->writing++;
828     }
829
830     if (wsize != BUTM_BLOCKSIZE) {
831         info->status |= BUTM_STATUS_WRITEERROR;
832         if (rc != 0) {
833             error = rc;
834             ERROR_EXIT(BUTM_IO);
835         } else
836             ERROR_EXIT(BUTM_EOT);
837     }
838     if (isafile)
839         info->position++;
840
841     /* Write trailing EOF marker for some block types */
842     /* ---------------------------------------------- */
843     if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
844         || (blockType == BLOCK_EOD)) {
845
846         POLL();
847         error = WriteEOF(p->fid, 1);
848         POLL();
849         if (error) {
850             info->status |= BUTM_STATUS_WRITEERROR;
851             ERROR_EXIT(BUTM_IOCTL);
852         }
853
854         incSize(info, config.fileMarkSize);
855         if (!isafile)
856             info->position++;
857         p->writing = 0;
858     }
859
860   error_exit:
861     if (error)
862         info->error = error;
863     return (code);
864 }
865
866 static afs_int32
867 ReadTapeBlock(info, buffer, blockType)
868      struct butm_tapeInfo *info;
869      char *buffer;              /* assumed to be 16384 bytes */
870      afs_int32 *blockType;
871 {
872     afs_int32 code = 0;
873     afs_int32 rsize, fmtype;
874     struct tapeLabel *label;
875     struct fileMark *fmark;
876     struct blockMark *bmark;
877     struct progress *p;
878
879     *blockType = BLOCK_UNKNOWN;
880
881     p = (struct progress *)info->tmRock;
882
883     memset(buffer, 0, BUTM_BLOCKSIZE);
884     label = (struct tapeLabel *)buffer;
885     fmark = (struct fileMark *)buffer;
886     bmark = (struct blockMark *)buffer;
887
888     rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
889     if (rsize > 0) {
890         incPosition(info, p->fid, rsize);
891         p->reading++;
892     }
893
894     if (rsize == 0) {           /* Read a HW EOF Marker? OK */
895         *blockType = BLOCK_EOF;
896         incSize(info, config.fileMarkSize);     /* Size of filemark */
897         if (!isafile)
898             info->position++;   /* bump position */
899         p->reading = 0;         /* No reads since EOF */
900     }
901
902     else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
903         info->status |= BUTM_STATUS_READERROR;
904         ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
905     }
906
907     else if (ntohl(bmark->magic) == BLOCK_MAGIC) {      /* Data block? */
908         *blockType = BLOCK_DATA;
909     }
910
911     else if (ntohl(fmark->magic) == FILE_MAGIC) {       /* Read a filemark? */
912         fmtype = ntohl(fmark->nBytes);
913
914         if (fmtype == FILE_BEGIN) {     /* filemark begin */
915             *blockType = BLOCK_FMBEGIN;
916         } else if (fmtype == FILE_FMEND) {      /* filemark end */
917             *blockType = BLOCK_FMEND;
918             code = SeekFile(info, 1);
919         } else if (fmtype == FILE_EOD) {        /* EOD mark */
920             *blockType = BLOCK_EOD;
921             info->status |= BUTM_STATUS_EOD;
922             code = SeekFile(info, 1);
923         }
924     }
925
926     else if (ntohl(label->magic) == TAPE_MAGIC) {       /* Read a tape label? */
927         *blockType = BLOCK_LABEL;
928         code = SeekFile(info, 1);
929     }
930
931     if (isafile)
932         info->position++;
933
934   error_exit:
935     return (code);
936 }
937
938 /* check
939  *      check version numbers and permissions in the info structure
940  */
941
942 static afs_int32
943 check(info, write)
944      struct butm_tapeInfo *info;
945      int write;                 /* write operation requested */
946 {
947     struct progress *p;
948
949     if (!info)
950         return (BUTM_BADARGUMENT);
951
952     /* Check version number in info structure */
953     if (info->structVersion != BUTM_MAJORVERSION)
954         return BUTM_OLDINTERFACE;
955
956     /* Check if a tape is mounted */
957     if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
958         return BUTM_NOMOUNT;
959
960     /* If writing check if there is write access */
961     if (write && (info->flags & BUTM_FLAGS_READONLY))
962         return BUTM_READONLY;
963
964     return 0;
965 }
966
967 static afs_int32
968 rewindFile(info)
969      struct butm_tapeInfo *info;
970 {
971     struct progress *p;
972     afs_int32 code = 0;
973     afs_int32 error;
974
975     p = (struct progress *)info->tmRock;
976
977     POLL();
978
979     error = Rewind(p->fid);
980
981     POLL();
982
983     if (error) {
984         info->status |= BUTM_STATUS_SEEKERROR;
985         ERROR_EXIT(BUTM_IOCTL);
986     }
987
988     info->position = (isafile ? 0 : 1);
989     info->kBytes = info->nBytes = 0;
990     info->nFiles = info->nRecords = 0;
991     p->reading = p->writing = 0;
992
993   error_exit:
994     if (error)
995         info->error = error;
996     return (code);
997 }
998
999 /* =====================================================================
1000  * butm routines
1001  * ===================================================================== */
1002
1003 static afs_int32
1004 file_Mount(info, tape)
1005      struct butm_tapeInfo *info;
1006      char *tape;
1007 {
1008     struct progress *p;
1009     char filename[64];
1010     usd_handle_t fid;
1011     int xflags;
1012     afs_int32 code = 0, error = 0, rc = 0;
1013
1014     if (info->debug)
1015         printf("butm: Mount tape drive\n");
1016
1017     POLL();
1018     info->error = 0;
1019
1020     if (!info || !tape)
1021         ERROR_EXIT(BUTM_BADARGUMENT);
1022     if (info->structVersion != BUTM_MAJORVERSION)
1023         ERROR_EXIT(BUTM_OLDINTERFACE);
1024     if (info->tmRock)
1025         ERROR_EXIT(BUTM_PARALLELMOUNTS);
1026     if (strlen(tape) >= sizeof(info->name))
1027         ERROR_EXIT(BUTM_BADARGUMENT);
1028
1029     strcpy(info->name, tape);
1030
1031     strcpy(filename, config.tapedir);   /* the name of the tape device */
1032     info->position = (isafile ? 0 : 1);
1033     info->kBytes = info->nBytes = 0;
1034     info->nRecords = info->nFiles = 0;
1035     info->recordSize = 0;
1036     info->tapeSize = config.tapeSize;
1037     info->coefBytes = 1;
1038     info->coefRecords = 0;
1039     info->coefFiles = sizeof(struct fileMark);
1040     info->simultaneousTapes = 1;
1041     info->status = 0;
1042     info->error = 0;
1043     info->flags = BUTM_FLAGS_SEQUENTIAL;
1044
1045     xflags = 0;
1046     if (isafile) {
1047         xflags |= USD_OPEN_CREATE;
1048     } else {
1049         /*
1050          * try to open in a child process first so nothing will
1051          * time out should the process block because the device
1052          * isn't ready.
1053          */
1054
1055         if (ForkOpen(filename)) {
1056             ERROR_EXIT(BUTM_MOUNTFAIL);
1057         }
1058     }
1059
1060     /* Now go ahead and open the tape drive for real */
1061     rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1062                   &fid);
1063     if (rc != 0) {              /* try for lesser access */
1064         rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1065
1066         if (rc) {
1067             error = rc;
1068             ERROR_EXIT(BUTM_MOUNTFAIL);
1069         }
1070         info->flags |= BUTM_FLAGS_READONLY;
1071     }
1072
1073     (void)PrepareAccess(fid);   /* for NT */
1074
1075     p = (struct progress *)malloc(sizeof(*p));
1076     info->tmRock = (char *)p;
1077     p->fid = fid;
1078     p->mountId = config.mountId = time(0);
1079     p->reading = p->writing = 0;
1080
1081     TapeBlockSize = BUTM_BLOCKSIZE;     /* Initialize */
1082
1083   error_exit:
1084     if (error)
1085         info->error = error;
1086     return (code);
1087 }
1088
1089 static afs_int32
1090 file_Dismount(info)
1091      struct butm_tapeInfo *info;
1092 {
1093     struct progress *p;
1094     afs_int32 code = 0, error = 0;
1095
1096     if (info->debug)
1097         printf("butm: Unmount tape drive\n");
1098
1099     POLL();
1100     info->error = 0;
1101
1102     code = check(info, READ_OP);
1103     if (code)
1104         ERROR_EXIT(code);
1105
1106     p = (struct progress *)info->tmRock;
1107
1108     (void)ShutdownAccess(p->fid);       /* for NT */
1109
1110     /* close the device */
1111     if (error = ForkClose(p->fid)) {
1112         printf("butm: Tape close failed. Error %d\n", errno);
1113     }
1114
1115     POLL();
1116
1117     if (error) {
1118         code = BUTM_DISMOUNTFAIL;
1119         info->status |= BUTM_STATUS_TAPEERROR;
1120     }
1121
1122     config.mountId = 0;
1123     info->tmRock = 0;           /* mark it as closed - even if error on close */
1124     if (p)
1125         free(p);
1126
1127   error_exit:
1128     if (error)
1129         info->error = error;
1130     return (code);
1131 }
1132
1133 /* file_WriteLabel
1134  *      write the header on a tape
1135  * entry:
1136  *      info - handle on tape unit
1137  *      label - label information. This label is not copied onto the tape.
1138  *              If supplied, various fields are copied from this label to
1139  *              the actual tape label written on the tape.
1140  */
1141
1142 static afs_int32
1143 file_WriteLabel(info, label, rewind)
1144      struct butm_tapeInfo *info;
1145      struct butm_tapeLabel *label;
1146      afs_int32 rewind;
1147 {
1148     afs_int32 code = 0;
1149     afs_int32 fcode;
1150     struct tapeLabel *tlabel;
1151     struct progress *p;
1152     afs_hyper_t off;            /* offset */
1153
1154     if (info->debug)
1155         printf("butm: Write tape label\n");
1156
1157     POLL();
1158     info->error = 0;
1159
1160     code = check(info, WRITE_OP);
1161     if (code)
1162         ERROR_EXIT(code);
1163     if (!label)
1164         ERROR_EXIT(BUTM_BADARGUMENT);
1165     if (label->structVersion != CUR_TAPE_VERSION)
1166         ERROR_EXIT(BUTM_OLDINTERFACE);
1167
1168     if (rewind) {               /* Not appending, so rewind */
1169         code = rewindFile(info);
1170         if (code)
1171             ERROR_EXIT(code);
1172
1173         if (isafile) {
1174             p = (struct progress *)info->tmRock;
1175             hset64(off, 0, 0);
1176             code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1177             if (code)
1178                 ERROR_EXIT(BUTM_POSITION);
1179         }
1180     } else {
1181         if (READS || WRITES)
1182             ERROR_EXIT(BUTM_BADOP);
1183     }
1184
1185     /* Copy the label into the tape block
1186      * ---------------------------------- */
1187     memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1188
1189     if (!label->creationTime)
1190         label->creationTime = time(0);
1191
1192     tlabel = (struct tapeLabel *)tapeBlock;
1193     memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1194     tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1195     tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1196     tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1197     tlabel->label.size = htonl(tlabel->label.size);
1198     tlabel->label.useCount = htonl(tlabel->label.useCount);
1199     tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1200
1201     /* 
1202      * write the tape label - For appends, the write may need to skip 
1203      * over 1 or 2 EOF marks that were written when tape was closed after 
1204      * the last dump. Plus, some AIX tape drives require we try forwarding
1205      * over the last EOF and take an error before we can write the new label.
1206      * ---------------------------------------------------------------------- */
1207     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1208     if (!isafile && !rewind && (code == BUTM_IO))
1209         do {                    /* do if write failed */
1210             fcode = SeekFile(info, 1);  /* skip over the EOF */
1211             if (fcode)
1212                 break;          /* leave if error */
1213
1214             code =
1215                 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1216             if (code != BUTM_IO)
1217                 break;          /* continue if write failed */
1218
1219             fcode = SeekFile(info, 1);  /* skip over the EOF */
1220             if (fcode) {        /* retry 1 write if couldn't skip */
1221                 code =
1222                     WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1223                                    BLOCK_LABEL);
1224                 break;
1225             }
1226
1227             code =
1228                 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1229             if (code != BUTM_IO)
1230                 break;          /* continue if write failed */
1231
1232             fcode = SeekFile(info, 1);  /* skip over the EOF */
1233             if (fcode) {        /* retry 1 write if couldn't skip */
1234                 code =
1235                     WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1236                                    BLOCK_LABEL);
1237                 break;
1238             }
1239             break;
1240         } while (0);
1241
1242     /* clear the write error status a failed WriteTapeBlock may have produced */
1243     if (!code)
1244         info->status &= ~BUTM_STATUS_WRITEERROR;
1245
1246   error_exit:
1247     return code;
1248 }
1249
1250 static afs_int32
1251 file_ReadLabel(info, label, rewind)
1252      struct butm_tapeInfo *info;
1253      struct butm_tapeLabel *label;
1254      afs_int32 rewind;
1255 {
1256     struct tapeLabel *tlabel;
1257     afs_int32 code = 0;
1258     afs_int32 blockType;
1259
1260     if (info->debug)
1261         printf("butm: Read tape label\n");
1262
1263     POLL();
1264     info->error = 0;
1265
1266     code = check(info, READ_OP);
1267     if (code)
1268         ERROR_EXIT(code);
1269     if (READS || WRITES)
1270         ERROR_EXIT(BUTM_BADOP);
1271
1272     if (rewind) {
1273         code = rewindFile(info);
1274         if (code)
1275             ERROR_EXIT(code);   /* status is set so return */
1276     }
1277
1278     /*
1279      * When appended labels were written, either 1 or 2 EOF marks may
1280      * have had to be skipped.  When reading a label, these EOF marks 
1281      * must also be skipped. When an EOF is read, 0 bytes are returned
1282      * (refer to the write calls in file_WriteLabel routine).
1283      * ---------------------------------------------------------------- */
1284     code = ReadTapeBlock(info, tapeBlock, &blockType);
1285     if (!isafile && !rewind && (blockType == BLOCK_EOF))
1286         do {
1287             code = ReadTapeBlock(info, tapeBlock, &blockType);
1288             if (blockType != BLOCK_EOF)
1289                 break;          /* didn't read an EOF */
1290
1291             code = ReadTapeBlock(info, tapeBlock, &blockType);
1292         } while (0);
1293
1294     if (blockType == BLOCK_UNKNOWN)
1295         ERROR_EXIT(BUTM_NOLABEL);
1296     if (code)
1297         ERROR_EXIT(code);
1298     if (blockType != BLOCK_LABEL)
1299         ERROR_EXIT(BUTM_BADBLOCK);
1300
1301     /* Copy label out
1302      * -------------- */
1303     if (label) {
1304         tlabel = (struct tapeLabel *)tapeBlock;
1305         memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1306         label->structVersion = ntohl(label->structVersion);
1307         label->creationTime = ntohl(label->creationTime);
1308         label->expirationDate = ntohl(label->expirationDate);
1309         label->size = ntohl(label->size);
1310         label->dumpid = ntohl(label->dumpid);
1311         label->useCount = ntohl(label->useCount);
1312
1313         info->tapeSize = label->size;   /* use size from label */
1314     }
1315
1316   error_exit:
1317     return (code);
1318 }
1319
1320 static afs_int32
1321 file_WriteFileBegin(info)
1322      struct butm_tapeInfo *info;
1323 {
1324     afs_int32 code = 0;
1325     afs_int32 error = 0;
1326
1327     if (info->debug)
1328         printf("butm: Write filemark begin\n");
1329
1330     POLL();
1331
1332     code = check(info, WRITE_OP);
1333     if (code)
1334         ERROR_EXIT(code);
1335
1336     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1337     if (code)
1338         ERROR_EXIT(code);
1339
1340     info->nFiles++;
1341
1342   error_exit:
1343     return (code);
1344 }
1345
1346 static afs_int32
1347 file_ReadFileBegin(info)
1348      struct butm_tapeInfo *info;
1349 {
1350     afs_int32 code = 0;
1351     afs_int32 blockType;
1352
1353     if (info->debug)
1354         printf("butm: Read filemark begin\n");
1355
1356     POLL();
1357     info->error = 0;
1358
1359     code = check(info, READ_OP);
1360     if (code)
1361         ERROR_EXIT(code);
1362     if (READS || WRITES)
1363         ERROR_EXIT(BUTM_BADOP);
1364
1365     code = ReadTapeBlock(info, tapeBlock, &blockType);
1366     if (code)
1367         ERROR_EXIT(code);
1368
1369     if (blockType != BLOCK_FMBEGIN) {
1370         if (blockType == BLOCK_EOD)
1371             ERROR_EXIT(BUTM_EOD);       /* EODump label */
1372         if (blockType == BLOCK_LABEL)
1373             ERROR_EXIT(BUTM_LABEL);     /* Tape label */
1374         ERROR_EXIT(BUTM_BADBLOCK);      /* Other */
1375     }
1376
1377   error_exit:
1378     return (code);
1379 }
1380
1381 /* Writes data out in block sizes of 16KB. Does destroy the data.
1382  * Assumes the data buffer has a space reserved at beginning for a blockMark.
1383  */
1384 static afs_int32
1385 file_WriteFileData(info, data, blocks, len)
1386      struct butm_tapeInfo *info;
1387      char *data;
1388      afs_int32 blocks;
1389      afs_int32 len;
1390 {
1391     afs_int32 code = 0;
1392     int length;
1393     afs_int32 b;
1394     char *bstart;               /* Where block starts for a 16K block */
1395     char *dstart;               /* Where data  starts for a 16K block */
1396
1397     if (info->debug)
1398         printf("butm: Write tape data - %u bytes\n", len);
1399
1400     POLL();
1401     info->error = 0;
1402
1403     code = check(info, WRITE_OP);
1404     if (code)
1405         ERROR_EXIT(code);
1406     if (!data || (len < 0))
1407         ERROR_EXIT(BUTM_BADARGUMENT);
1408     if (READS || !WRITES)
1409         ERROR_EXIT(BUTM_BADOP);
1410
1411     b = 0;                      /* start at block 0 */
1412     while (len > 0) {
1413         dstart = &data[b * BUTM_BLKSIZE];
1414         bstart = dstart - sizeof(struct blockMark);
1415
1416         if (len < BUTM_BLKSIZE) {
1417             memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1418             length = len;
1419         } else {
1420             length = BUTM_BLKSIZE;
1421         }
1422
1423         code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1424
1425         len -= length;
1426
1427         /* If there are more blocks, step to next block */
1428         /* Otherwise, copy the data to beginning of last block */
1429
1430         if (b < (blocks - 1))
1431             b++;
1432         else if (len)
1433             memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1434     }
1435
1436   error_exit:
1437     return (code);
1438 }
1439
1440 /* file_ReadFileData
1441  *      Read a data block from tape.
1442  * entry:
1443  *      info - tape info structure, c.f. fid
1444  *      data - ptr to buffer for data
1445  *      len - size of data buffer
1446  * exit:
1447  *      nBytes - no. of data bytes read.
1448  */
1449
1450 static afs_int32
1451 file_ReadFileData(info, data, len, nBytes)
1452      struct butm_tapeInfo *info;
1453      char *data;
1454      int len;
1455      int *nBytes;
1456 {
1457     struct blockMark *bmark;
1458     afs_int32 code = 0;
1459     afs_int32 blockType;
1460
1461     if (info->debug)
1462         printf("butm: Read tape data - %u bytes\n", len);
1463
1464     POLL();
1465     info->error = 0;
1466
1467     if (!nBytes)
1468         ERROR_EXIT(BUTM_BADARGUMENT);
1469     *nBytes = 0;
1470
1471     code = check(info, READ_OP);
1472     if (code)
1473         ERROR_EXIT(code);
1474     if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1475         ERROR_EXIT(BUTM_BADARGUMENT);
1476     if (!READS || WRITES)
1477         ERROR_EXIT(BUTM_BADOP);
1478
1479     data -= sizeof(struct blockMark);
1480     code = ReadTapeBlock(info, data, &blockType);
1481     if (code)
1482         ERROR_EXIT(code);
1483
1484     if (blockType != BLOCK_DATA) {
1485         if (blockType == BLOCK_EOF)
1486             ERROR_EXIT(BUTM_EOF);
1487         if (blockType == BLOCK_FMEND)
1488             ERROR_EXIT(BUTM_ENDVOLUME);
1489         ERROR_EXIT(BUTM_BADBLOCK);
1490     }
1491
1492     bmark = (struct blockMark *)data;
1493     *nBytes = ntohl(bmark->count);      /* Size of data in buf */
1494
1495   error_exit:
1496     return (code);
1497 }
1498
1499 static afs_int32
1500 file_WriteFileEnd(info)
1501      struct butm_tapeInfo *info;
1502 {
1503     afs_int32 code = 0;
1504
1505     if (info->debug)
1506         printf("butm: Write filemark end\n");
1507
1508     POLL();
1509     info->error = 0;
1510
1511     code = check(info, WRITE_OP);
1512     if (code)
1513         ERROR_EXIT(code);
1514     if (READS || !WRITES)
1515         ERROR_EXIT(BUTM_BADOP);
1516
1517     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1518
1519   error_exit:
1520     return (code);
1521 }
1522
1523 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next 
1524  * HW filemark. If the read of the SW filemark shows it's an EOF, then 
1525  * ignore that the SW filemark is not there and return 0 (found the SW filemark
1526  * missing with some 3.1 dumps).
1527  */
1528 static afs_int32
1529 file_ReadFileEnd(info)
1530      struct butm_tapeInfo *info;
1531 {
1532     afs_int32 code = 0;
1533     afs_int32 blockType;
1534
1535     if (info->debug)
1536         printf("butm: Read filemark end\n");
1537
1538     POLL();
1539     info->error = 0;
1540
1541     code = check(info, READ_OP);
1542     if (code)
1543         ERROR_EXIT(code);
1544     if (!READS || WRITES)
1545         ERROR_EXIT(BUTM_BADOP);
1546
1547     info->status &= ~BUTM_STATUS_EOF;
1548
1549     code = ReadTapeBlock(info, tapeBlock, &blockType);
1550     if (code)
1551         ERROR_EXIT(code);
1552
1553     if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1554         ERROR_EXIT(BUTM_BADBLOCK);
1555
1556   error_exit:
1557     return code;
1558 }
1559
1560 /*
1561  * Write the end-of-dump marker.
1562  */
1563 static afs_int32
1564 file_WriteEODump(info)
1565      struct butm_tapeInfo *info;
1566 {
1567     afs_int32 code = 0;
1568
1569     if (info->debug)
1570         printf("butm: Write filemark EOD\n");
1571
1572     POLL();
1573     info->error = 0;
1574
1575     code = check(info, WRITE_OP);
1576     if (code)
1577         ERROR_EXIT(code);
1578     if (READS || WRITES)
1579         ERROR_EXIT(BUTM_BADOP);
1580
1581     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1582     if (code)
1583         ERROR_EXIT(code);
1584
1585     info->status |= BUTM_STATUS_EOD;
1586
1587   error_exit:
1588     return (code);
1589 }
1590
1591 static afs_int32
1592 file_Seek(info, position)
1593      struct butm_tapeInfo *info;
1594      afs_int32 position;
1595 {
1596     afs_int32 code = 0;
1597     afs_int32 w;
1598     osi_lloff_t posit;
1599     afs_uint32 c, d;
1600     struct progress *p;
1601     afs_hyper_t startOff, stopOff;      /* for normal file(non-tape)  seeks  */
1602
1603     if (info->debug)
1604         printf("butm: Seek to the tape position %d\n", position);
1605
1606     info->error = 0;
1607
1608     code = check(info, READ_OP);
1609     if (code)
1610         ERROR_EXIT(code);
1611
1612     if (isafile) {
1613         p = (struct progress *)info->tmRock;
1614         posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1615
1616         /* Not really necessary to do it this way, should be fixed */
1617 #ifdef O_LARGEFILE
1618         c = (posit >> 32);
1619         d = (posit & 0xffffffff);
1620 #else
1621         c = 0;
1622         d = posit;
1623 #endif
1624         hset64(startOff, c, d);
1625
1626         w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1627         if (w)
1628             info->error = w;
1629         if (hcmp(startOff, stopOff) != 0)
1630             ERROR_EXIT(BUTM_POSITION);
1631
1632         p->reading = p->writing = 0;
1633         info->position = position;
1634     } else {
1635         /* Don't position backwards if we are in-between FMs */
1636         if ((READS || WRITES) && ((position - info->position) <= 0))
1637             ERROR_EXIT(BUTM_BADOP);
1638
1639         code = SeekFile(info, (position - info->position));
1640         if (code)
1641             ERROR_EXIT(code);
1642     }
1643
1644   error_exit:
1645     return (code);
1646 }
1647
1648 /*
1649  * Seek to the EODump (end-of-dump) after the given position. This is 
1650  * the position after the EOF filemark immediately after the EODump mark. 
1651  * This is for tapes of version 4 or greater.
1652  */
1653 static afs_int32
1654 file_SeekEODump(info, position)
1655      struct butm_tapeInfo *info;
1656      afs_int32 position;
1657 {
1658     afs_int32 code = 0;
1659     afs_int32 blockType;
1660     afs_int32 w;
1661     struct progress *p;
1662     afs_hyper_t startOff, stopOff;      /* file seek offsets */
1663
1664     if (info->debug)
1665         printf("butm: Seek to end-of-dump\n");
1666     info->error = 0;
1667
1668     code = check(info, READ_OP);
1669     if (code)
1670         ERROR_EXIT(code);
1671     if (READS || WRITES)
1672         ERROR_EXIT(BUTM_BADOP);
1673
1674     if (isafile) {
1675         p = (struct progress *)info->tmRock;
1676         hset64(startOff, 0, 0);
1677         w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1678         if (w) {
1679             info->error = w;
1680             ERROR_EXIT(BUTM_POSITION);
1681         }
1682
1683         if (hgetlo(stopOff) % BUTM_BLOCKSIZE)
1684             ERROR_EXIT(BUTM_POSITION);
1685         info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1686     } else {
1687         /* Seek to the desired position */
1688         code = SeekFile(info, (position - info->position) + 1);
1689         if (code)
1690             ERROR_EXIT(code);
1691
1692         /*
1693          * Search until the filemark is an EODump filemark.
1694          * Skip over volumes only.
1695          */
1696         while (1) {
1697             code = ReadTapeBlock(info, tapeBlock, &blockType);
1698             if (code)
1699                 ERROR_EXIT(code);
1700
1701             if (blockType == BLOCK_EOD)
1702                 break;
1703             if (blockType != BLOCK_FMBEGIN)
1704                 ERROR_EXIT(BUTM_BADBLOCK);
1705
1706             code = SeekFile(info, 1);   /* Step forward to next volume */
1707             if (code)
1708                 ERROR_EXIT(code);
1709         }
1710         code = 0;
1711     }
1712
1713   error_exit:
1714     return (code);
1715 }
1716
1717 static afs_int32
1718 file_SetSize(info, size)
1719      struct butm_tapeInfo *info;
1720      afs_uint32 size;
1721 {
1722     if (info->debug)
1723         printf("butm: Set size of tape\n");
1724     info->error = 0;
1725
1726     if (size <= 0)
1727         info->tapeSize = config.tapeSize;
1728     else
1729         info->tapeSize = size;
1730     return 0;
1731 }
1732
1733 static afs_int32
1734 file_GetSize(info, size)
1735      struct butm_tapeInfo *info;
1736      afs_uint32 *size;
1737 {
1738     if (info->debug)
1739         printf("butm: Get size of tape\n");
1740     info->error = 0;
1741
1742     *size = info->tapeSize;
1743     return 0;
1744 }
1745
1746 /* =====================================================================
1747  * Startup/configuration routines.
1748  * ===================================================================== */
1749
1750 static afs_int32
1751 file_Configure(file)
1752      struct tapeConfig *file;
1753 {
1754     if (!file) {
1755         afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1756         return BUTM_BADCONFIG;
1757     }
1758
1759     config.tapeSize = file->capacity;
1760     config.fileMarkSize = file->fileMarkSize;
1761     config.portOffset = file->portOffset;
1762     strcpy(config.tapedir, file->device);
1763
1764     /* Tape must be large enough to at least fit a label */
1765     if (config.tapeSize <= 0) {
1766         afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1767                 config.tapeSize);
1768         return BUTM_BADCONFIG;
1769     }
1770
1771     if (strlen(config.tapedir) == 0) {
1772         afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1773         return BUTM_BADCONFIG;
1774     }
1775
1776     config.mountId = 0;
1777     return 0;
1778 }
1779
1780 /* This procedure instantiates a tape module of type file_tm. */
1781 afs_int32
1782 butm_file_Instantiate(info, file)
1783      struct butm_tapeInfo *info;
1784      struct tapeConfig *file;
1785 {
1786     extern int debugLevel;
1787     afs_int32 code = 0;
1788
1789     if (debugLevel > 98)
1790         printf("butm: Instantiate butc\n");
1791
1792     if (!info)
1793         ERROR_EXIT(BUTM_BADARGUMENT);
1794     if (info->structVersion != BUTM_MAJORVERSION)
1795         ERROR_EXIT(BUTM_OLDINTERFACE);
1796
1797     memset(info, 0, sizeof(struct butm_tapeInfo));
1798     info->structVersion = BUTM_MAJORVERSION;
1799     info->ops.mount = file_Mount;
1800     info->ops.dismount = file_Dismount;
1801     info->ops.create = file_WriteLabel;
1802     info->ops.readLabel = file_ReadLabel;
1803     info->ops.seek = file_Seek;
1804     info->ops.seekEODump = file_SeekEODump;
1805     info->ops.readFileBegin = file_ReadFileBegin;
1806     info->ops.readFileData = file_ReadFileData;
1807     info->ops.readFileEnd = file_ReadFileEnd;
1808     info->ops.writeFileBegin = file_WriteFileBegin;
1809     info->ops.writeFileData = file_WriteFileData;
1810     info->ops.writeFileEnd = file_WriteFileEnd;
1811     info->ops.writeEOT = file_WriteEODump;
1812     info->ops.setSize = file_SetSize;
1813     info->ops.getSize = file_GetSize;
1814     info->debug = ((debugLevel > 98) ? 1 : 0);
1815
1816     code = file_Configure(file);
1817
1818   error_exit:
1819     return code;
1820 }