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