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