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