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