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