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