reindent-again-20030808
[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     afs_hyper_t off;
656
657     /* Add this to the amount of data written to the tape */
658     incSize(info, dataSize);
659
660     info->posCount += dataSize;
661
662     if (info->posCount >= 2147467264) { /* 2GB - 16K */
663         info->posCount = 0;
664 #if (defined(AFS_SUN_ENV) || defined(AFS_DEC_ENV) || defined(AFS_LINUX24_ENV))
665         if (!isafile) {
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     afs_int32 fcode;
732     int cpid, status, rcpid, stat;
733     struct progress *p;
734     afs_int32 error = 0;
735
736     if (count == 0)
737         ERROR_EXIT(0);
738     p = (struct progress *)info->tmRock;
739
740     if (isafile) {              /* no reason for seeking through a file */
741         p->reading = p->writing = 0;
742         return (0);
743     }
744
745     POLL();
746
747     if (count > 0)
748         error = ForwardSpace(p->fid, count);
749     else
750         error = BackwardSpace(p->fid, -count);
751
752     POLL();
753
754     if (error) {
755         info->status |= BUTM_STATUS_SEEKERROR;
756         ERROR_EXIT(BUTM_IOCTL);
757     }
758
759     info->position += count;
760     incSize(info, (count * config.fileMarkSize));
761     p = (struct progress *)info->tmRock;
762     p->reading = p->writing = 0;
763
764   error_exit:
765     if (error)
766         info->error = error;
767     return (code);
768 }
769
770 /* Step to the next filemark if we are not at one already */
771 afs_int32
772 NextFile(info)
773      struct butm_tapeInfo *info;
774 {
775     afs_int32 code;
776
777     if (!READS && !WRITES)
778         return 0;
779
780     code = SeekFile(info, 1);
781     return (code);
782 }
783
784 static afs_int32
785 WriteTapeBlock(info, buffer, length, blockType)
786      struct butm_tapeInfo *info;
787      char *buffer;              /* assumed to be 16384 bytes with data in it */
788      afs_int32 length;          /* amount data in buffer */
789      afs_int32 blockType;
790 {
791     afs_int32 code = 0, rc = 0;
792     afs_int32 wsize;
793     struct tapeLabel *label;
794     struct fileMark *fmark;
795     struct blockMark *bmark;
796     struct progress *p;
797     afs_int32 error = 0;
798
799     p = (struct progress *)info->tmRock;
800
801     if (blockType == BLOCK_DATA) {      /* Data Block */
802         if (length == 0)
803             ERROR_EXIT(0);
804         bmark = (struct blockMark *)buffer;
805         memset(bmark, 0, sizeof(struct blockMark));
806         bmark->magic = htonl(BLOCK_MAGIC);
807         bmark->count = htonl(length);
808     } else if (blockType == BLOCK_FMBEGIN) {    /* Filemark - begin */
809         fmark = (struct fileMark *)buffer;
810         fmark->magic = htonl(FILE_MAGIC);
811         fmark->nBytes = htonl(FILE_BEGIN);
812     } else if (blockType == BLOCK_FMEND) {      /* Filemark - end */
813         fmark = (struct fileMark *)buffer;
814         fmark->magic = htonl(FILE_MAGIC);
815         fmark->nBytes = htonl(FILE_FMEND);
816     } else if (blockType == BLOCK_LABEL) {      /* Label */
817         label = (struct tapeLabel *)buffer;
818         label->magic = htonl(TAPE_MAGIC);
819     } else if (blockType == BLOCK_EOD) {        /* Filemark - EOD mark */
820         fmark = (struct fileMark *)buffer;
821         fmark->magic = htonl(FILE_MAGIC);
822         fmark->nBytes = htonl(FILE_EOD);
823     }
824
825     /* Write the tape block */
826     /* -------------------- */
827     rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
828     if ((rc == 0) && (wsize > 0)) {
829         incPosition(info, p->fid, wsize);       /* record whats written */
830         p->writing++;
831     }
832
833     if (wsize != BUTM_BLOCKSIZE) {
834         info->status |= BUTM_STATUS_WRITEERROR;
835         if (rc != 0) {
836             error = rc;
837             ERROR_EXIT(BUTM_IO);
838         } else
839             ERROR_EXIT(BUTM_EOT);
840     }
841     if (isafile)
842         info->position++;
843
844     /* Write trailing EOF marker for some block types */
845     /* ---------------------------------------------- */
846     if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
847         || (blockType == BLOCK_EOD)) {
848
849         POLL();
850         error = WriteEOF(p->fid, 1);
851         POLL();
852         if (error) {
853             info->status |= BUTM_STATUS_WRITEERROR;
854             ERROR_EXIT(BUTM_IOCTL);
855         }
856
857         incSize(info, config.fileMarkSize);
858         if (!isafile)
859             info->position++;
860         p->writing = 0;
861     }
862
863   error_exit:
864     if (error)
865         info->error = error;
866     return (code);
867 }
868
869 static afs_int32
870 ReadTapeBlock(info, buffer, blockType)
871      struct butm_tapeInfo *info;
872      char *buffer;              /* assumed to be 16384 bytes */
873      afs_int32 *blockType;
874 {
875     afs_int32 code = 0;
876     afs_int32 rsize, fmtype;
877     struct tapeLabel *label;
878     struct fileMark *fmark;
879     struct blockMark *bmark;
880     struct progress *p;
881
882     *blockType = BLOCK_UNKNOWN;
883
884     p = (struct progress *)info->tmRock;
885
886     memset(buffer, 0, BUTM_BLOCKSIZE);
887     label = (struct tapeLabel *)buffer;
888     fmark = (struct fileMark *)buffer;
889     bmark = (struct blockMark *)buffer;
890
891     rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
892     if (rsize > 0) {
893         incPosition(info, p->fid, rsize);
894         p->reading++;
895     }
896
897     if (rsize == 0) {           /* Read a HW EOF Marker? OK */
898         *blockType = BLOCK_EOF;
899         incSize(info, config.fileMarkSize);     /* Size of filemark */
900         if (!isafile)
901             info->position++;   /* bump position */
902         p->reading = 0;         /* No reads since EOF */
903     }
904
905     else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
906         info->status |= BUTM_STATUS_READERROR;
907         ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
908     }
909
910     else if (ntohl(bmark->magic) == BLOCK_MAGIC) {      /* Data block? */
911         *blockType = BLOCK_DATA;
912     }
913
914     else if (ntohl(fmark->magic) == FILE_MAGIC) {       /* Read a filemark? */
915         fmtype = ntohl(fmark->nBytes);
916
917         if (fmtype == FILE_BEGIN) {     /* filemark begin */
918             *blockType = BLOCK_FMBEGIN;
919         } else if (fmtype == FILE_FMEND) {      /* filemark end */
920             *blockType = BLOCK_FMEND;
921             code = SeekFile(info, 1);
922         } else if (fmtype == FILE_EOD) {        /* EOD mark */
923             *blockType = BLOCK_EOD;
924             info->status |= BUTM_STATUS_EOD;
925             code = SeekFile(info, 1);
926         }
927     }
928
929     else if (ntohl(label->magic) == TAPE_MAGIC) {       /* Read a tape label? */
930         *blockType = BLOCK_LABEL;
931         code = SeekFile(info, 1);
932     }
933
934     if (isafile)
935         info->position++;
936
937   error_exit:
938     return (code);
939 }
940
941 /* check
942  *      check version numbers and permissions in the info structure
943  */
944
945 static afs_int32
946 check(info, write)
947      struct butm_tapeInfo *info;
948      int write;                 /* write operation requested */
949 {
950     struct progress *p;
951
952     if (!info)
953         return (BUTM_BADARGUMENT);
954
955     /* Check version number in info structure */
956     if (info->structVersion != BUTM_MAJORVERSION)
957         return BUTM_OLDINTERFACE;
958
959     /* Check if a tape is mounted */
960     if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
961         return BUTM_NOMOUNT;
962
963     /* If writing check if there is write access */
964     if (write && (info->flags & BUTM_FLAGS_READONLY))
965         return BUTM_READONLY;
966
967     return 0;
968 }
969
970 static afs_int32
971 rewindFile(info)
972      struct butm_tapeInfo *info;
973 {
974     struct progress *p;
975     int cpid, status, rcpid, stat;
976     afs_int32 code = 0;
977     afs_int32 rwcode;
978     afs_int32 error;
979
980     p = (struct progress *)info->tmRock;
981
982     POLL();
983
984     error = Rewind(p->fid);
985
986     POLL();
987
988     if (error) {
989         info->status |= BUTM_STATUS_SEEKERROR;
990         ERROR_EXIT(BUTM_IOCTL);
991     }
992
993     info->position = (isafile ? 0 : 1);
994     info->kBytes = info->nBytes = 0;
995     info->nFiles = info->nRecords = 0;
996     p->reading = p->writing = 0;
997
998   error_exit:
999     if (error)
1000         info->error = error;
1001     return (code);
1002 }
1003
1004 /* =====================================================================
1005  * butm routines
1006  * ===================================================================== */
1007
1008 static afs_int32
1009 file_Mount(info, tape)
1010      struct butm_tapeInfo *info;
1011      char *tape;
1012 {
1013     struct progress *p;
1014     char filename[64];
1015     usd_handle_t fid;
1016     int cpid, status, rcpid, xflags;
1017     afs_int32 code = 0, error = 0, rc = 0;
1018
1019     if (info->debug)
1020         printf("butm: Mount tape drive\n");
1021
1022     POLL();
1023     info->error = 0;
1024
1025     if (!info || !tape)
1026         ERROR_EXIT(BUTM_BADARGUMENT);
1027     if (info->structVersion != BUTM_MAJORVERSION)
1028         ERROR_EXIT(BUTM_OLDINTERFACE);
1029     if (info->tmRock)
1030         ERROR_EXIT(BUTM_PARALLELMOUNTS);
1031     if (strlen(tape) >= sizeof(info->name))
1032         ERROR_EXIT(BUTM_BADARGUMENT);
1033
1034     strcpy(info->name, tape);
1035
1036     strcpy(filename, config.tapedir);   /* the name of the tape device */
1037     info->position = (isafile ? 0 : 1);
1038     info->kBytes = info->nBytes = 0;
1039     info->nRecords = info->nFiles = 0;
1040     info->recordSize = 0;
1041     info->tapeSize = config.tapeSize;
1042     info->coefBytes = 1;
1043     info->coefRecords = 0;
1044     info->coefFiles = sizeof(struct fileMark);
1045     info->simultaneousTapes = 1;
1046     info->status = 0;
1047     info->error = 0;
1048     info->flags = BUTM_FLAGS_SEQUENTIAL;
1049
1050     xflags = 0;
1051     if (isafile) {
1052         xflags |= USD_OPEN_CREATE;
1053     } else {
1054         /*
1055          * try to open in a child process first so nothing will
1056          * time out should the process block because the device
1057          * isn't ready.
1058          */
1059
1060         if (ForkOpen(filename)) {
1061             ERROR_EXIT(BUTM_MOUNTFAIL);
1062         }
1063     }
1064
1065     /* Now go ahead and open the tape drive for real */
1066     rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1067                   &fid);
1068     if (rc != 0) {              /* try for lesser access */
1069         rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1070
1071         if (rc) {
1072             error = rc;
1073             ERROR_EXIT(BUTM_MOUNTFAIL);
1074         }
1075         info->flags |= BUTM_FLAGS_READONLY;
1076     }
1077
1078     (void)PrepareAccess(fid);   /* for NT */
1079
1080     p = (struct progress *)malloc(sizeof(*p));
1081     info->tmRock = (char *)p;
1082     p->fid = fid;
1083     p->mountId = config.mountId = time(0);
1084     p->reading = p->writing = 0;
1085
1086     TapeBlockSize = BUTM_BLOCKSIZE;     /* Initialize */
1087
1088   error_exit:
1089     if (error)
1090         info->error = error;
1091     return (code);
1092 }
1093
1094 static afs_int32
1095 file_Dismount(info)
1096      struct butm_tapeInfo *info;
1097 {
1098     struct progress *p;
1099     int cpid, status, rcpid, stat;
1100     afs_int32 code = 0, error = 0, cd;
1101     afs_int32 clcode;
1102     int fd[2];
1103     char c;
1104
1105     if (info->debug)
1106         printf("butm: Unmount tape drive\n");
1107
1108     POLL();
1109     info->error = 0;
1110
1111     code = check(info, READ_OP);
1112     if (code)
1113         ERROR_EXIT(code);
1114
1115     p = (struct progress *)info->tmRock;
1116
1117     (void)ShutdownAccess(p->fid);       /* for NT */
1118
1119     /* close the device */
1120     if (error = ForkClose(p->fid)) {
1121         printf("butm: Tape close failed. Error %d\n", errno);
1122     }
1123
1124     POLL();
1125
1126     if (error) {
1127         code = BUTM_DISMOUNTFAIL;
1128         info->status |= BUTM_STATUS_TAPEERROR;
1129     }
1130
1131     config.mountId = 0;
1132     info->tmRock = 0;           /* mark it as closed - even if error on close */
1133     if (p)
1134         free(p);
1135
1136   error_exit:
1137     if (error)
1138         info->error = error;
1139     return (code);
1140 }
1141
1142 /* file_WriteLabel
1143  *      write the header on a tape
1144  * entry:
1145  *      info - handle on tape unit
1146  *      label - label information. This label is not copied onto the tape.
1147  *              If supplied, various fields are copied from this label to
1148  *              the actual tape label written on the tape.
1149  */
1150
1151 static afs_int32
1152 file_WriteLabel(info, label, rewind)
1153      struct butm_tapeInfo *info;
1154      struct butm_tapeLabel *label;
1155      afs_int32 rewind;
1156 {
1157     afs_int32 code = 0;
1158     afs_int32 fcode;
1159     struct tapeLabel *tlabel;
1160     struct progress *p;
1161     afs_hyper_t off;            /* offset */
1162
1163     if (info->debug)
1164         printf("butm: Write tape label\n");
1165
1166     POLL();
1167     info->error = 0;
1168
1169     code = check(info, WRITE_OP);
1170     if (code)
1171         ERROR_EXIT(code);
1172     if (!label)
1173         ERROR_EXIT(BUTM_BADARGUMENT);
1174     if (label->structVersion != CUR_TAPE_VERSION)
1175         ERROR_EXIT(BUTM_OLDINTERFACE);
1176
1177     if (rewind) {               /* Not appending, so rewind */
1178         code = rewindFile(info);
1179         if (code)
1180             ERROR_EXIT(code);
1181
1182         if (isafile) {
1183             p = (struct progress *)info->tmRock;
1184             hset64(off, 0, 0);
1185             code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1186             if (code)
1187                 ERROR_EXIT(BUTM_POSITION);
1188         }
1189     } else {
1190         if (READS || WRITES)
1191             ERROR_EXIT(BUTM_BADOP);
1192     }
1193
1194     /* Copy the label into the tape block
1195      * ---------------------------------- */
1196     memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1197
1198     if (!label->creationTime)
1199         label->creationTime = time(0);
1200
1201     tlabel = (struct tapeLabel *)tapeBlock;
1202     memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1203     tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1204     tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1205     tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1206     tlabel->label.size = htonl(tlabel->label.size);
1207     tlabel->label.useCount = htonl(tlabel->label.useCount);
1208     tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1209
1210     /* 
1211      * write the tape label - For appends, the write may need to skip 
1212      * over 1 or 2 EOF marks that were written when tape was closed after 
1213      * the last dump. Plus, some AIX tape drives require we try forwarding
1214      * over the last EOF and take an error before we can write the new label.
1215      * ---------------------------------------------------------------------- */
1216     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1217     if (!isafile && !rewind && (code == BUTM_IO))
1218         do {                    /* do if write failed */
1219             fcode = SeekFile(info, 1);  /* skip over the EOF */
1220             if (fcode)
1221                 break;          /* leave if error */
1222
1223             code =
1224                 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1225             if (code != BUTM_IO)
1226                 break;          /* continue if write failed */
1227
1228             fcode = SeekFile(info, 1);  /* skip over the EOF */
1229             if (fcode) {        /* retry 1 write if couldn't skip */
1230                 code =
1231                     WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1232                                    BLOCK_LABEL);
1233                 break;
1234             }
1235
1236             code =
1237                 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1238             if (code != BUTM_IO)
1239                 break;          /* continue if write failed */
1240
1241             fcode = SeekFile(info, 1);  /* skip over the EOF */
1242             if (fcode) {        /* retry 1 write if couldn't skip */
1243                 code =
1244                     WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1245                                    BLOCK_LABEL);
1246                 break;
1247             }
1248             break;
1249         } while (0);
1250
1251     /* clear the write error status a failed WriteTapeBlock may have produced */
1252     if (!code)
1253         info->status &= ~BUTM_STATUS_WRITEERROR;
1254
1255   error_exit:
1256     return code;
1257 }
1258
1259 static afs_int32
1260 file_ReadLabel(info, label, rewind)
1261      struct butm_tapeInfo *info;
1262      struct butm_tapeLabel *label;
1263      afs_int32 rewind;
1264 {
1265     struct tapeLabel *tlabel;
1266     afs_int32 code = 0;
1267     afs_int32 blockType;
1268
1269     if (info->debug)
1270         printf("butm: Read tape label\n");
1271
1272     POLL();
1273     info->error = 0;
1274
1275     code = check(info, READ_OP);
1276     if (code)
1277         ERROR_EXIT(code);
1278     if (READS || WRITES)
1279         ERROR_EXIT(BUTM_BADOP);
1280
1281     if (rewind) {
1282         code = rewindFile(info);
1283         if (code)
1284             ERROR_EXIT(code);   /* status is set so return */
1285     }
1286
1287     /*
1288      * When appended labels were written, either 1 or 2 EOF marks may
1289      * have had to be skipped.  When reading a label, these EOF marks 
1290      * must also be skipped. When an EOF is read, 0 bytes are returned
1291      * (refer to the write calls in file_WriteLabel routine).
1292      * ---------------------------------------------------------------- */
1293     code = ReadTapeBlock(info, tapeBlock, &blockType);
1294     if (!isafile && !rewind && (blockType == BLOCK_EOF))
1295         do {
1296             code = ReadTapeBlock(info, tapeBlock, &blockType);
1297             if (blockType != BLOCK_EOF)
1298                 break;          /* didn't read an EOF */
1299
1300             code = ReadTapeBlock(info, tapeBlock, &blockType);
1301         } while (0);
1302
1303     if (blockType == BLOCK_UNKNOWN)
1304         ERROR_EXIT(BUTM_NOLABEL);
1305     if (code)
1306         ERROR_EXIT(code);
1307     if (blockType != BLOCK_LABEL)
1308         ERROR_EXIT(BUTM_BADBLOCK);
1309
1310     /* Copy label out
1311      * -------------- */
1312     if (label) {
1313         tlabel = (struct tapeLabel *)tapeBlock;
1314         memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1315         label->structVersion = ntohl(label->structVersion);
1316         label->creationTime = ntohl(label->creationTime);
1317         label->expirationDate = ntohl(label->expirationDate);
1318         label->size = ntohl(label->size);
1319         label->dumpid = ntohl(label->dumpid);
1320         label->useCount = ntohl(label->useCount);
1321
1322         info->tapeSize = label->size;   /* use size from label */
1323     }
1324
1325   error_exit:
1326     return (code);
1327 }
1328
1329 static afs_int32
1330 file_WriteFileBegin(info)
1331      struct butm_tapeInfo *info;
1332 {
1333     afs_int32 code = 0;
1334     afs_int32 error = 0;
1335
1336     if (info->debug)
1337         printf("butm: Write filemark begin\n");
1338
1339     POLL();
1340
1341     code = check(info, WRITE_OP);
1342     if (code)
1343         ERROR_EXIT(code);
1344
1345     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1346     if (code)
1347         ERROR_EXIT(code);
1348
1349     info->nFiles++;
1350
1351   error_exit:
1352     return (code);
1353 }
1354
1355 static afs_int32
1356 file_ReadFileBegin(info)
1357      struct butm_tapeInfo *info;
1358 {
1359     struct fileMark mark;
1360     afs_int32 code = 0;
1361     afs_int32 blockType;
1362
1363     if (info->debug)
1364         printf("butm: Read filemark begin\n");
1365
1366     POLL();
1367     info->error = 0;
1368
1369     code = check(info, READ_OP);
1370     if (code)
1371         ERROR_EXIT(code);
1372     if (READS || WRITES)
1373         ERROR_EXIT(BUTM_BADOP);
1374
1375     code = ReadTapeBlock(info, tapeBlock, &blockType);
1376     if (code)
1377         ERROR_EXIT(code);
1378
1379     if (blockType != BLOCK_FMBEGIN) {
1380         if (blockType == BLOCK_EOD)
1381             ERROR_EXIT(BUTM_EOD);       /* EODump label */
1382         if (blockType == BLOCK_LABEL)
1383             ERROR_EXIT(BUTM_LABEL);     /* Tape label */
1384         ERROR_EXIT(BUTM_BADBLOCK);      /* Other */
1385     }
1386
1387   error_exit:
1388     return (code);
1389 }
1390
1391 /* Writes data out in block sizes of 16KB. Does destroy the data.
1392  * Assumes the data buffer has a space reserved at beginning for a blockMark.
1393  */
1394 static afs_int32
1395 file_WriteFileData(info, data, blocks, len)
1396      struct butm_tapeInfo *info;
1397      char *data;
1398      afs_int32 blocks;
1399      afs_int32 len;
1400 {
1401     afs_int32 code = 0;
1402     int length;
1403     afs_int32 b;
1404     char *bstart;               /* Where block starts for a 16K block */
1405     char *dstart;               /* Where data  starts for a 16K block */
1406
1407     if (info->debug)
1408         printf("butm: Write tape data - %u bytes\n", len);
1409
1410     POLL();
1411     info->error = 0;
1412
1413     code = check(info, WRITE_OP);
1414     if (code)
1415         ERROR_EXIT(code);
1416     if (!data || (len < 0))
1417         ERROR_EXIT(BUTM_BADARGUMENT);
1418     if (READS || !WRITES)
1419         ERROR_EXIT(BUTM_BADOP);
1420
1421     b = 0;                      /* start at block 0 */
1422     while (len > 0) {
1423         dstart = &data[b * BUTM_BLKSIZE];
1424         bstart = dstart - sizeof(struct blockMark);
1425
1426         if (len < BUTM_BLKSIZE) {
1427             memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1428             length = len;
1429         } else {
1430             length = BUTM_BLKSIZE;
1431         }
1432
1433         code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1434
1435         len -= length;
1436
1437         /* If there are more blocks, step to next block */
1438         /* Otherwise, copy the data to beginning of last block */
1439
1440         if (b < (blocks - 1))
1441             b++;
1442         else if (len)
1443             memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1444     }
1445
1446   error_exit:
1447     return (code);
1448 }
1449
1450 /* file_ReadFileData
1451  *      Read a data block from tape.
1452  * entry:
1453  *      info - tape info structure, c.f. fid
1454  *      data - ptr to buffer for data
1455  *      len - size of data buffer
1456  * exit:
1457  *      nBytes - no. of data bytes read.
1458  */
1459
1460 static afs_int32
1461 file_ReadFileData(info, data, len, nBytes)
1462      struct butm_tapeInfo *info;
1463      char *data;
1464      int len;
1465      int *nBytes;
1466 {
1467     struct blockMark *bmark;
1468     afs_int32 code = 0;
1469     afs_int32 blockType;
1470
1471     if (info->debug)
1472         printf("butm: Read tape data - %u bytes\n", len);
1473
1474     POLL();
1475     info->error = 0;
1476
1477     if (!nBytes)
1478         ERROR_EXIT(BUTM_BADARGUMENT);
1479     *nBytes = 0;
1480
1481     code = check(info, READ_OP);
1482     if (code)
1483         ERROR_EXIT(code);
1484     if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1485         ERROR_EXIT(BUTM_BADARGUMENT);
1486     if (!READS || WRITES)
1487         ERROR_EXIT(BUTM_BADOP);
1488
1489     data -= sizeof(struct blockMark);
1490     code = ReadTapeBlock(info, data, &blockType);
1491     if (code)
1492         ERROR_EXIT(code);
1493
1494     if (blockType != BLOCK_DATA) {
1495         if (blockType == BLOCK_EOF)
1496             ERROR_EXIT(BUTM_EOF);
1497         if (blockType == BLOCK_FMEND)
1498             ERROR_EXIT(BUTM_ENDVOLUME);
1499         ERROR_EXIT(BUTM_BADBLOCK);
1500     }
1501
1502     bmark = (struct blockMark *)data;
1503     *nBytes = ntohl(bmark->count);      /* Size of data in buf */
1504
1505   error_exit:
1506     return (code);
1507 }
1508
1509 static afs_int32
1510 file_WriteFileEnd(info)
1511      struct butm_tapeInfo *info;
1512 {
1513     afs_int32 code = 0;
1514
1515     if (info->debug)
1516         printf("butm: Write filemark end\n");
1517
1518     POLL();
1519     info->error = 0;
1520
1521     code = check(info, WRITE_OP);
1522     if (code)
1523         ERROR_EXIT(code);
1524     if (READS || !WRITES)
1525         ERROR_EXIT(BUTM_BADOP);
1526
1527     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1528
1529   error_exit:
1530     return (code);
1531 }
1532
1533 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next 
1534  * HW filemark. If the read of the SW filemark shows it's an EOF, then 
1535  * ignore that the SW filemark is not there and return 0 (found the SW filemark
1536  * missing with some 3.1 dumps).
1537  */
1538 static afs_int32
1539 file_ReadFileEnd(info)
1540      struct butm_tapeInfo *info;
1541 {
1542     struct fileMark mark;
1543     afs_int32 code = 0;
1544     afs_int32 blockType;
1545
1546     if (info->debug)
1547         printf("butm: Read filemark end\n");
1548
1549     POLL();
1550     info->error = 0;
1551
1552     code = check(info, READ_OP);
1553     if (code)
1554         ERROR_EXIT(code);
1555     if (!READS || WRITES)
1556         ERROR_EXIT(BUTM_BADOP);
1557
1558     info->status &= ~BUTM_STATUS_EOF;
1559
1560     code = ReadTapeBlock(info, tapeBlock, &blockType);
1561     if (code)
1562         ERROR_EXIT(code);
1563
1564     if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1565         ERROR_EXIT(BUTM_BADBLOCK);
1566
1567   error_exit:
1568     return code;
1569 }
1570
1571 /*
1572  * Write the end-of-dump marker.
1573  */
1574 static afs_int32
1575 file_WriteEODump(info)
1576      struct butm_tapeInfo *info;
1577 {
1578     afs_int32 code = 0;
1579
1580     if (info->debug)
1581         printf("butm: Write filemark EOD\n");
1582
1583     POLL();
1584     info->error = 0;
1585
1586     code = check(info, WRITE_OP);
1587     if (code)
1588         ERROR_EXIT(code);
1589     if (READS || WRITES)
1590         ERROR_EXIT(BUTM_BADOP);
1591
1592     code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1593     if (code)
1594         ERROR_EXIT(code);
1595
1596     info->status |= BUTM_STATUS_EOD;
1597
1598   error_exit:
1599     return (code);
1600 }
1601
1602 static afs_int32
1603 file_Seek(info, position)
1604      struct butm_tapeInfo *info;
1605      afs_int32 position;
1606 {
1607     afs_int32 code = 0;
1608     afs_int32 w;
1609     osi_lloff_t posit;
1610     afs_uint32 c, d;
1611     struct progress *p;
1612     afs_hyper_t startOff, stopOff;      /* for normal file(non-tape)  seeks  */
1613
1614     if (info->debug)
1615         printf("butm: Seek to the tape position %d\n", position);
1616
1617     info->error = 0;
1618
1619     code = check(info, READ_OP);
1620     if (code)
1621         ERROR_EXIT(code);
1622
1623     if (isafile) {
1624         p = (struct progress *)info->tmRock;
1625         posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1626
1627         /* Not really necessary to do it this way, should be fixed */
1628 #ifdef O_LARGEFILE
1629         c = (posit >> 32);
1630         d = (posit & 0xffffffff);
1631 #else
1632         c = 0;
1633         d = posit;
1634 #endif
1635         hset64(startOff, c, d);
1636
1637         w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1638         if (w)
1639             info->error == w;
1640         if (hcmp(startOff, stopOff) != 0)
1641             ERROR_EXIT(BUTM_POSITION);
1642
1643         p->reading = p->writing = 0;
1644         info->position = position;
1645     } else {
1646         /* Don't position backwards if we are in-between FMs */
1647         if ((READS || WRITES) && ((position - info->position) <= 0))
1648             ERROR_EXIT(BUTM_BADOP);
1649
1650         code = SeekFile(info, (position - info->position));
1651         if (code)
1652             ERROR_EXIT(code);
1653     }
1654
1655   error_exit:
1656     return (code);
1657 }
1658
1659 /*
1660  * Seek to the EODump (end-of-dump) after the given position. This is 
1661  * the position after the EOF filemark immediately after the EODump mark. 
1662  * This is for tapes of version 4 or greater.
1663  */
1664 static afs_int32
1665 file_SeekEODump(info, position)
1666      struct butm_tapeInfo *info;
1667      afs_int32 position;
1668 {
1669     struct fileMark mark;
1670     afs_int32 code = 0;
1671     afs_int32 blockType;
1672     afs_int32 w;
1673     struct progress *p;
1674     afs_hyper_t startOff, stopOff;      /* file seek offsets */
1675
1676     if (info->debug)
1677         printf("butm: Seek to end-of-dump\n");
1678     info->error = 0;
1679
1680     code = check(info, READ_OP);
1681     if (code)
1682         ERROR_EXIT(code);
1683     if (READS || WRITES)
1684         ERROR_EXIT(BUTM_BADOP);
1685
1686     if (isafile) {
1687         p = (struct progress *)info->tmRock;
1688         hset64(startOff, 0, 0);
1689         w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1690         if (w)
1691             info->error == w;
1692         if (w)
1693             ERROR_EXIT(BUTM_POSITION);
1694
1695         if (hgetlo(stopOff) % BUTM_BLOCKSIZE)
1696             ERROR_EXIT(BUTM_POSITION);
1697         info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1698     } else {
1699         /* Seek to the desired position */
1700         code = SeekFile(info, (position - info->position) + 1);
1701         if (code)
1702             ERROR_EXIT(code);
1703
1704         /*
1705          * Search until the filemark is an EODump filemark.
1706          * Skip over volumes only.
1707          */
1708         while (1) {
1709             code = ReadTapeBlock(info, tapeBlock, &blockType);
1710             if (code)
1711                 ERROR_EXIT(code);
1712
1713             if (blockType == BLOCK_EOD)
1714                 break;
1715             if (blockType != BLOCK_FMBEGIN)
1716                 ERROR_EXIT(BUTM_BADBLOCK);
1717
1718             code = SeekFile(info, 1);   /* Step forward to next volume */
1719             if (code)
1720                 ERROR_EXIT(code);
1721         }
1722         code = 0;
1723     }
1724
1725   error_exit:
1726     return (code);
1727 }
1728
1729 static afs_int32
1730 file_SetSize(info, size)
1731      struct butm_tapeInfo *info;
1732      afs_uint32 size;
1733 {
1734     if (info->debug)
1735         printf("butm: Set size of tape\n");
1736     info->error = 0;
1737
1738     if (size <= 0)
1739         info->tapeSize = config.tapeSize;
1740     else
1741         info->tapeSize = size;
1742     return 0;
1743 }
1744
1745 static afs_int32
1746 file_GetSize(info, size)
1747      struct butm_tapeInfo *info;
1748      afs_uint32 *size;
1749 {
1750     if (info->debug)
1751         printf("butm: Get size of tape\n");
1752     info->error = 0;
1753
1754     *size = info->tapeSize;
1755     return 0;
1756 }
1757
1758 /* =====================================================================
1759  * Startup/configuration routines.
1760  * ===================================================================== */
1761
1762 static afs_int32
1763 file_Configure(file)
1764      struct tapeConfig *file;
1765 {
1766     if (!file) {
1767         com_err(whoami, BUTM_BADCONFIG, "device not specified");
1768         return BUTM_BADCONFIG;
1769     }
1770
1771     config.tapeSize = file->capacity;
1772     config.fileMarkSize = file->fileMarkSize;
1773     config.portOffset = file->portOffset;
1774     strcpy(config.tapedir, file->device);
1775
1776     /* Tape must be large enough to at least fit a label */
1777     if (config.tapeSize <= 0) {
1778         com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1779                 config.tapeSize);
1780         return BUTM_BADCONFIG;
1781     }
1782
1783     if (strlen(config.tapedir) == 0) {
1784         com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1785         return BUTM_BADCONFIG;
1786     }
1787
1788     config.mountId = 0;
1789     return 0;
1790 }
1791
1792 /* This procedure instantiates a tape module of type file_tm. */
1793 afs_int32
1794 butm_file_Instantiate(info, file)
1795      struct butm_tapeInfo *info;
1796      struct tapeConfig *file;
1797 {
1798     extern int debugLevel;
1799     afs_int32 code = 0;
1800
1801     if (debugLevel > 98)
1802         printf("butm: Instantiate butc\n");
1803
1804     if (!info)
1805         ERROR_EXIT(BUTM_BADARGUMENT);
1806     if (info->structVersion != BUTM_MAJORVERSION)
1807         ERROR_EXIT(BUTM_OLDINTERFACE);
1808
1809     memset(info, 0, sizeof(struct butm_tapeInfo));
1810     info->structVersion = BUTM_MAJORVERSION;
1811     info->ops.mount = file_Mount;
1812     info->ops.dismount = file_Dismount;
1813     info->ops.create = file_WriteLabel;
1814     info->ops.readLabel = file_ReadLabel;
1815     info->ops.seek = file_Seek;
1816     info->ops.seekEODump = file_SeekEODump;
1817     info->ops.readFileBegin = file_ReadFileBegin;
1818     info->ops.readFileData = file_ReadFileData;
1819     info->ops.readFileEnd = file_ReadFileEnd;
1820     info->ops.writeFileBegin = file_WriteFileBegin;
1821     info->ops.writeFileData = file_WriteFileData;
1822     info->ops.writeFileEnd = file_WriteFileEnd;
1823     info->ops.writeEOT = file_WriteEODump;
1824     info->ops.setSize = file_SetSize;
1825     info->ops.getSize = file_GetSize;
1826     info->debug = ((debugLevel > 98) ? 1 : 0);
1827
1828     code = file_Configure(file);
1829
1830   error_exit:
1831     return code;
1832 }