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