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