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