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