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