2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afs/param.h>
14 #include <netinet/in.h>
16 #include <sys/types.h>
24 #include <afs/com_err.h>
27 #include "error_macros.h"
32 #define FILE_MAGIC 1000000007 /* s/w file mark */
33 #define FILE_BEGIN 0 /* byte field in file mark */
34 #define FILE_FMEND 1 /* byte field in file mark */
35 #define FILE_EOD -1 /* byte field in file mark */
36 #define TAPE_MAGIC 1100000009 /* tape label block */
37 #define BLOCK_MAGIC 1100000005 /* file data block */
38 #ifdef AFS_PTHREAD_ENV
40 #define SLEEP(s) sleep(s)
42 #define POLL() IOMGR_Poll()
43 #define SLEEP(s) IOMGR_Sleep(s)
48 * 1) filemarks and block marks have the magic,bytes fields reversed. This
49 * is undoubtedly a bug. Also note that the two structures have
50 * inconsistent types, overlaying int and afs_int32.
51 * 2) When a volume is dumped, if the volume is locked, the dump will produce
52 * an anomalous tape format of the form:
56 * The design of the original butm code means that this cannot be
57 * handled correctly. The code is modified so that ReadFileData
58 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
61 /* data organization on tape:
62 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
63 * blockMark contains a magic number and counts of real data bytes
64 * written out in the block.
66 * each file is preceeded by a fileMark, which acts as the file
67 * delimiter. A file consists of contigous data blocks. TM does
68 * understand or interrpet the data in data blocks.
70 * The tape begins with a tape label and ends with EOF file markers
71 * in succession (2 or 4 of them ).
75 struct fileMark { /* in network byte order */
82 struct butm_tapeLabel label;
86 usd_handle_t fid; /* file id of simulated tape */
87 afs_int32 mountId; /* current mountId */
88 afs_int32 reading; /* read file operation in progress */
89 afs_int32 writing; /* write file operation in progress */
92 static struct configuration {
93 char tapedir[64]; /* directory to create "tapes" */
94 afs_int32 mountId; /* for detecting simultaneous mounts */
95 afs_uint32 tapeSize; /* size of simulated tapes */
96 afs_uint32 fileMarkSize; /* size of file mark, bytes */
97 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
100 static char *whoami = "file_tm";
101 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
103 #define BLOCK_LABEL 0 /* read/write a tape label */
104 #define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
105 #define BLOCK_DATA 2 /* read/write a data block */
106 #define BLOCK_FMEND 3 /* read/write an end FileMark */
107 #define BLOCK_EOD 4 /* read/write an EOD FileMark */
108 #define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
109 #define BLOCK_UNKNOWN 6 /* tape block is unknwon (error)*/
114 #define READS (((struct progress *)(info->tmRock))->reading)
115 #define WRITES (((struct progress *)(info->tmRock))->writing)
117 /* ----------------------------------------------------------------------
118 * These routines use the usd library to perform tape operations.
119 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
120 * PrepareAccess(nt), ShutdownAccess(nt)
122 * Return Values: USD functions return 0 if successful or errno if failed.
124 /* ------------------ USD Interface Functions Begin ------------------------ */
127 * On Unix, fork a child process to perform an IOCTL call. This avoids
128 * blocking the entire process during a tape operation
132 /* Unix version of function */
134 static int ForkIoctl(usd_handle_t fd, int op, int count)
136 int rc; /* return code from system calls */
137 int i; /* loop index */
138 int pid; /* process ID of child process */
139 int status; /* exit status of child process */
140 int ioctl_rc; /* return code from ioctl call */
141 int pipefd[2]; /* pipe for child return status */
142 int forkflag; /* flag set when ready to fork */
143 usd_tapeop_t tapeop; /* tape operation specification */
146 #ifdef AFS_PTHREAD_ENV
147 forkflag = 0; /* No need to fork if using pthreads */
152 /* initialize tape command structure */
154 tapeop.tp_count = count;
156 /* create pipe for getting return code from child */
162 printf("butm: Can't open pipe for IOCTL process. Error %d\n", errno);
174 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
180 if ( !forkflag ) /* problem starting child process */
182 /* do the ioctl anyway, it will probably work */
183 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
185 else if ( pid == 0 ) /* child process */
187 /* close all unneccessary file descriptors */
188 /* note: as painful as it is, we have to reach under the covers of
189 * the usd package to implement this functionality.
191 unixfd = (int)(fd->handle);
193 for ( i = 3 ; i < _POSIX_OPEN_MAX ; i++ )
195 if ( i != unixfd && i != pipefd[1] )
201 /* do the ioctl call */
202 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
204 /* send the return code back to the parent */
205 write(pipefd[1], &ioctl_rc, sizeof(int));
209 else /* parent process */
213 /* read the result from the child process */
214 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
215 if ( rc != sizeof(int) )
217 /* tape is now in unknown state */
218 printf("butm: Can't determine IOCTL child status. Error %d\n", errno);
224 /* get the completion status from the child process */
225 rc = waitpid(pid,&status,0);
226 while (rc < 0 && errno == EINTR )
228 rc = waitpid(pid,&status,0);
232 printf("butm: Can't determine IOCTL child status. Error %d\n", errno);
234 else if ( status != 0 )
236 printf("butm: Unexpected IOCTL process status 0x%04x . Error %d\n", status, errno);
244 /* NT version of function */
246 static int ForkIoctl(usd_handle_t fd, int op, int count)
250 /* Issue requested tape control */
252 tapeop.tp_count = count;
254 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
256 #endif /* !AFS_NT40_ENV */
260 * On Unix, fork a child process to attempt to open the drive. We want to make
261 * certain there is tape in the drive before trying to open the device
262 * in the main process
266 /* Unix version of function. */
268 static int ForkOpen(char *device)
270 int rc; /* return code from system calls */
271 int i; /* loop index */
272 int pid; /* process ID of child process */
273 int status; /* exit status of child process */
274 int open_rc; /* return code from open */
275 int pipefd[2]; /* pipe for child return status */
276 int forkflag; /* flag set when ready to fork */
277 usd_handle_t fd; /* handle returned from open */
279 #ifdef AFS_PTHREAD_ENV
280 forkflag = 0; /* No need to fork if using pthreads */
285 /* create pipe for getting return code from child */
290 printf("butm: Cannot create pipe for OPEN process. Error %d\n", errno);
302 printf("butm: Cannot create child process for OPEN. Error %d\n", errno);
307 if ( !forkflag ) /* problem starting child process */
310 *return success, the caller will discover any problems
311 * when it opens the device.
315 else if ( pid == 0 ) /* child process */
317 /* close all unneccessary file descriptors */
318 for ( i = 3 ; i < _POSIX_OPEN_MAX ; i++ )
320 if ( i != pipefd[1] )
327 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
333 /* send the return code back to the parent */
334 write(pipefd[1], &open_rc, sizeof(open_rc));
338 else /* parent process */
343 /* read the result from the child process */
344 rc = read(pipefd[0], &open_rc, sizeof(open_rc));
345 if ( rc != sizeof(open_rc) )
347 /* this is not a problem since we will reopen the device anyway */
348 printf("butm: No response from OPEN process. Error %d\n", errno);
354 /* get the completion status from the child process */
355 rc = waitpid(pid,&status,0);
356 while (rc < 0 && errno == EINTR )
358 rc = waitpid(pid,&status,0);
362 printf("butm: Cannot get status of OPEN process. Error %d\n", errno);
364 else if ( status != 0 )
366 printf("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n", status, errno);
374 /* NT version of function. */
376 static int ForkOpen(char *device)
380 #endif /* AFS_NT40_ENV */
383 * On Unix, fork a child process to close the drive. If the drive rewinds
384 * on close it could cause the process to block.
388 /* Unix version of function */
390 static int ForkClose(usd_handle_t fd)
392 int rc; /* return code from system calls */
393 int i; /* loop index */
394 int pid; /* process ID of child process */
395 int status; /* exit status of child process */
396 int close_rc, parent_close_rc; /* return codes from close */
397 int pipefd[2]; /* pipe for child return status */
398 int ctlpipe[2]; /* pipe for message to child */
399 int forkflag; /* flag set when ready to fork */
402 #ifdef AFS_PTHREAD_ENV
403 forkflag = 0; /* No need to fork if using pthreads */
408 /* create pipe for getting return code from child */
414 printf("butm: Cannot create pipe for CLOSE process. Error %d\n", errno);
419 /* create pipe for notifying child when to close */
427 printf("butm: Cannot create pipe for CLOSE process. Error %d\n", errno);
441 printf("butm: Cannot create CLOSE child process. Error %d\n", errno);
446 if ( !forkflag ) /* problem starting child process */
448 close_rc = USD_CLOSE(fd);
449 parent_close_rc = close_rc;
451 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.
457 unixfd = (int)(fd->handle);
459 for ( i = 3 ; i < _POSIX_OPEN_MAX ; i++ )
461 if ( i != unixfd && i != ctlpipe[0] && i != pipefd[1] )
467 /* the parent writes the control pipe after it closes the device */
468 read(ctlpipe[0], &close_rc, sizeof(int));
472 close_rc = USD_CLOSE(fd);
474 /* send the return code back to the parent */
475 write(pipefd[1], &close_rc, sizeof(int));
479 else /* parent process */
486 * close the device, this should have no effect as long as the
487 * child has not closed
490 parent_close_rc = USD_CLOSE(fd);
492 /* notify the child to do its close */
493 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
494 if ( rc != sizeof(int) )
496 printf("butm: Error communicating with CLOSE process. Error %d\n", errno);
500 /* read the result from the child process */
501 rc = read(pipefd[0], &close_rc, sizeof(int));
502 if ( rc != sizeof(int) )
504 /* logging is enough, since we wrote a file mark the */
505 /* return code from the close doesn't really matter */
506 printf("butm: No response from CLOSE process. Error %d\n", errno);
512 /* get the completion status from the child process */
513 rc = waitpid(pid,&status,0);
514 while (rc < 0 && errno == EINTR )
516 rc = waitpid(pid,&status,0);
520 printf("butm: Cannot get status of CLOSE process. Error %d\n", errno);
522 else if ( status != 0 )
524 printf("butm: Unexpected exit status 0x04x from CLOSE process. Error %d\n", status, errno);
527 /* if either process received an error, then return an error */
528 if ( parent_close_rc < 0 )
530 close_rc = parent_close_rc;
538 /* NT version of function */
540 static int ForkClose(usd_handle_t fd)
542 return(USD_CLOSE(fd));
544 #endif /* AFS_NT40_ENV */
546 /* Forward space file */
547 static int ForwardSpace(usd_handle_t fid, int count)
554 return (ForkIoctl(fid, USDTAPE_FSF, count));
558 /* Backward space file */
559 static int BackwardSpace(usd_handle_t fid, int count)
566 return (ForkIoctl(fid, USDTAPE_BSF, count));
570 /* write end of file mark */
571 static WriteEOF(usd_handle_t fid, int count)
578 return (ForkIoctl(fid, USDTAPE_WEOF,count));
583 static int Rewind(usd_handle_t fid)
586 afs_hyper_t startOff, stopOff;
589 return (USD_SEEK(fid, startOff, SEEK_SET, &stopOff));
591 return (ForkIoctl(fid, USDTAPE_REW, 0));
595 /* prepare tape drive for access */
596 static int PrepareAccess(usd_handle_t fid)
601 (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
603 /* NT won't rewind tape when it is opened */
605 #endif /* AFS_NT40_ENV */
609 /* decommission tape drive after all accesses complete */
610 static int ShutdownAccess(usd_handle_t fid)
614 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
616 #endif /* AFS_NT40_ENV */
620 /* -------------------- USD Interface Functions End ----------------------- */
622 /* =====================================================================
624 * ===================================================================== */
627 * add the supplied no. of bytes to the byte count of information placed
630 * dataSize - bytes used on the tape
633 incSize(info, dataSize)
634 struct butm_tapeInfo *info;
637 info->nBytes += dataSize;
638 info->kBytes += (info->nBytes/1024);
639 info->nBytes %= 1024;
643 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
644 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
646 * add the supplied no. of bytes to the byte count of data placed
647 * on the tape. Also check for reaching 2GB limit and reset the
648 * pointer if necessary. This allows us to use >2GB tapes.
650 * fid - file id for the tape.
651 * dataSize - bytes used on the tape
654 incPosition(info, fid, dataSize)
655 struct butm_tapeInfo *info;
661 /* Add this to the amount of data written to the tape */
662 incSize(info, dataSize);
664 info->posCount += dataSize;
666 if ( info->posCount >= 2147467264 ) /* 2GB - 16K */
669 #if (defined(AFS_SUN_ENV) || defined(AFS_DEC_ENV))
673 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
680 * This accounts for tape drives with a block size different from variable or 16K
681 * blocks and only reads that block size.
683 afs_int32 TapeBlockSize;
684 afs_int32 readData(fid, data, totalSize, errorP)
690 afs_int32 toread; /* Number of bytes to read */
691 afs_int32 rSize; /* Total bytes read so far */
692 afs_int32 tSize; /* Temporary size */
693 afs_int32 rc; /* return code */
695 toread = totalSize; /* First, try to read all the data */
697 rc = USD_READ(fid, &data[0], toread, &rSize);
703 if ( rSize == 0 ) /* reached EOF */
706 if ( rSize != TapeBlockSize ) /* Tape block size has changed */
708 TapeBlockSize = rSize;
709 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
712 /* Read the rest of the data in */
713 while (rSize < totalSize)
715 toread = ( (totalSize-rSize)<TapeBlockSize ? (totalSize-rSize) : TapeBlockSize);
716 rc = USD_READ(fid, &data[rSize], toread, &tSize);
717 if (rc) *errorP = rc;
719 if (tSize != toread) break;
722 if (rSize > totalSize)
723 printf("readData - Read > 16K data block - continuing.\n");
729 SeekFile(info, count)
730 struct butm_tapeInfo *info;
735 int cpid, status, rcpid, stat;
739 if (count == 0) ERROR_EXIT(0);
740 p = (struct progress *)info->tmRock;
742 if (isafile) /* no reason for seeking through a file */
744 p->reading = p->writing = 0;
751 error = ForwardSpace (p->fid, count);
753 error = BackwardSpace(p->fid, -count);
759 info->status |= BUTM_STATUS_SEEKERROR;
760 ERROR_EXIT(BUTM_IOCTL);
763 info->position += count;
764 incSize(info, (count * config.fileMarkSize));
765 p = (struct progress *)info->tmRock;
766 p->reading = p->writing = 0;
769 if (error) info->error = error;
773 /* Step to the next filemark if we are not at one already */
774 afs_int32 NextFile(info)
775 struct butm_tapeInfo *info;
779 if (!READS && !WRITES)
782 code = SeekFile(info, 1);
787 WriteTapeBlock(info, buffer, length, blockType)
788 struct butm_tapeInfo *info;
789 char *buffer; /* assumed to be 16384 bytes with data in it */
790 afs_int32 length; /* amount data in buffer */
793 afs_int32 code = 0, rc = 0;
795 struct tapeLabel *label;
796 struct fileMark *fmark;
797 struct blockMark *bmark;
801 p = (struct progress *)info->tmRock;
803 if (blockType == BLOCK_DATA) /* Data Block */
805 if (length == 0) ERROR_EXIT(0);
806 bmark = (struct blockMark *)buffer;
807 bzero(bmark, sizeof(struct blockMark));
808 bmark->magic = htonl(BLOCK_MAGIC);
809 bmark->count = htonl(length);
811 else if (blockType == BLOCK_FMBEGIN) /* Filemark - begin */
813 fmark = (struct fileMark *)buffer;
814 fmark->magic = htonl(FILE_MAGIC);
815 fmark->nBytes = htonl(FILE_BEGIN);
817 else if (blockType == BLOCK_FMEND) /* Filemark - end */
819 fmark = (struct fileMark *)buffer;
820 fmark->magic = htonl(FILE_MAGIC);
821 fmark->nBytes = htonl(FILE_FMEND);
823 else if (blockType == BLOCK_LABEL) /* Label */
825 label = (struct tapeLabel *)buffer;
826 label->magic = htonl(TAPE_MAGIC);
828 else if (blockType == BLOCK_EOD) /* Filemark - EOD mark */
830 fmark = (struct fileMark *)buffer;
831 fmark->magic = htonl(FILE_MAGIC);
832 fmark->nBytes = htonl(FILE_EOD);
835 /* Write the tape block */
836 /* -------------------- */
837 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
838 if ( (rc == 0) && (wsize > 0) )
840 incPosition(info, p->fid, wsize); /* record whats written */
844 if (wsize != BUTM_BLOCKSIZE)
846 info->status |= BUTM_STATUS_WRITEERROR;
853 ERROR_EXIT(BUTM_EOT);
855 if (isafile) info->position++;
857 /* Write trailing EOF marker for some block types */
858 /* ---------------------------------------------- */
859 if ( (blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL) || (blockType == BLOCK_EOD) )
863 error = WriteEOF(p->fid, 1);
867 info->status |= BUTM_STATUS_WRITEERROR;
868 ERROR_EXIT(BUTM_IOCTL);
871 incSize(info, config.fileMarkSize);
872 if (!isafile) info->position++;
877 if (error) info->error = error;
882 ReadTapeBlock(info, buffer, blockType)
883 struct butm_tapeInfo *info;
884 char *buffer; /* assumed to be 16384 bytes */
885 afs_int32 *blockType;
888 afs_int32 rsize, fmtype;
889 struct tapeLabel *label;
890 struct fileMark *fmark;
891 struct blockMark *bmark;
894 *blockType = BLOCK_UNKNOWN;
896 p = (struct progress *)info->tmRock;
898 bzero(buffer, BUTM_BLOCKSIZE);
899 label = (struct tapeLabel *)buffer;
900 fmark = (struct fileMark *)buffer;
901 bmark = (struct blockMark *)buffer;
903 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
906 incPosition(info, p->fid, rsize);
910 if (rsize == 0) /* Read a HW EOF Marker? OK */
912 *blockType = BLOCK_EOF;
913 incSize(info, config.fileMarkSize); /* Size of filemark */
914 if (!isafile) info->position++; /* bump position */
915 p->reading = 0; /* No reads since EOF */
918 else if (rsize != BUTM_BLOCKSIZE) /* Didn't Read a full block */
920 info->status |= BUTM_STATUS_READERROR;
921 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
924 else if (ntohl(bmark->magic) == BLOCK_MAGIC) /* Data block? */
926 *blockType = BLOCK_DATA;
929 else if (ntohl(fmark->magic) == FILE_MAGIC) /* Read a filemark? */
931 fmtype = ntohl(fmark->nBytes);
933 if (fmtype == FILE_BEGIN) /* filemark begin */
935 *blockType = BLOCK_FMBEGIN;
937 else if (fmtype == FILE_FMEND) /* filemark end */
939 *blockType = BLOCK_FMEND;
940 code = SeekFile(info, 1);
942 else if (fmtype == FILE_EOD) /* EOD mark */
944 *blockType = BLOCK_EOD;
945 info->status |= BUTM_STATUS_EOD;
946 code = SeekFile(info, 1);
950 else if (ntohl(label->magic) == TAPE_MAGIC) /* Read a tape label? */
952 *blockType = BLOCK_LABEL;
953 code = SeekFile(info, 1);
956 if (isafile) info->position++;
963 * check version numbers and permissions in the info structure
966 static afs_int32 check (info, write)
967 struct butm_tapeInfo *info;
968 int write; /* write operation requested */
973 return(BUTM_BADARGUMENT);
975 /* Check version number in info structure */
976 if (info->structVersion != BUTM_MAJORVERSION)
977 return BUTM_OLDINTERFACE;
979 /* Check if a tape is mounted */
980 if (((p = (struct progress *)info->tmRock) == 0) || ( p->fid == 0))
983 /* If writing check if there is write access */
984 if ( write && (info->flags & BUTM_FLAGS_READONLY) )
985 return BUTM_READONLY;
990 static afs_int32 rewindFile (info)
991 struct butm_tapeInfo *info;
994 int cpid, status, rcpid, stat;
999 p = (struct progress *)info->tmRock;
1003 error = Rewind(p->fid);
1009 info->status |= BUTM_STATUS_SEEKERROR;
1010 ERROR_EXIT(BUTM_IOCTL);
1013 info->position = (isafile ? 0 : 1);
1014 info->kBytes = info->nBytes = 0;
1015 info->nFiles = info->nRecords = 0;
1016 p->reading = p->writing = 0;
1019 if (error) info->error = error;
1023 /* =====================================================================
1025 * ===================================================================== */
1028 file_Mount (info, tape)
1029 struct butm_tapeInfo *info;
1035 int cpid, status, rcpid, xflags;
1036 afs_int32 code = 0, error = 0, rc = 0;
1038 if (info->debug) printf("butm: Mount tape drive\n");
1043 if (!info || !tape) ERROR_EXIT(BUTM_BADARGUMENT);
1044 if (info->structVersion != BUTM_MAJORVERSION) ERROR_EXIT(BUTM_OLDINTERFACE);
1045 if (info->tmRock) ERROR_EXIT(BUTM_PARALLELMOUNTS);
1046 if (strlen(tape) >= sizeof(info->name)) ERROR_EXIT(BUTM_BADARGUMENT);
1048 strcpy (info->name, tape);
1050 strcpy (filename, config.tapedir); /* the name of the tape device*/
1051 info->position = (isafile ? 0 : 1);
1052 info->kBytes = info->nBytes = 0;
1053 info->nRecords = info->nFiles = 0;
1054 info->recordSize = 0;
1055 info->tapeSize = config.tapeSize;
1056 info->coefBytes = 1;
1057 info->coefRecords = 0;
1058 info->coefFiles = sizeof(struct fileMark);
1059 info->simultaneousTapes = 1;
1062 info->flags = BUTM_FLAGS_SEQUENTIAL;
1067 xflags |= USD_OPEN_CREATE;
1072 * try to open in a child process first so nothing will
1073 * time out should the process block because the device
1077 if (ForkOpen(filename)) {
1078 ERROR_EXIT(BUTM_MOUNTFAIL);
1082 /* Now go ahead and open the tape drive for real */
1083 rc = usd_Open(filename,
1084 (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777, &fid);
1086 { /* try for lesser access */
1087 rc = usd_Open(filename,
1088 (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1093 ERROR_EXIT(BUTM_MOUNTFAIL);
1095 info->flags |= BUTM_FLAGS_READONLY;
1098 (void)PrepareAccess(fid); /* for NT */
1100 p = (struct progress *) malloc (sizeof(*p));
1101 info->tmRock = (char *)p;
1103 p->mountId = config.mountId = time(0);
1104 p->reading = p->writing = 0;
1106 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1109 if (error) info->error = error;
1113 static afs_int32 file_Dismount (info)
1114 struct butm_tapeInfo *info;
1117 int cpid, status, rcpid, stat;
1118 afs_int32 code = 0, error = 0, cd;
1123 if (info->debug) printf("butm: Unmount tape drive\n");
1128 code = check (info, READ_OP);
1129 if (code) ERROR_EXIT(code);
1131 p = (struct progress *)info->tmRock;
1133 (void)ShutdownAccess(p->fid); /* for NT */
1135 /* close the device */
1136 if (error = ForkClose(p->fid))
1138 printf("butm: Tape close failed. Error %d\n", errno);
1145 code = BUTM_DISMOUNTFAIL;
1146 info->status |= BUTM_STATUS_TAPEERROR;
1150 info->tmRock = 0; /* mark it as closed - even if error on close */
1154 if (error) info->error = error;
1159 * write the header on a tape
1161 * info - handle on tape unit
1162 * label - label information. This label is not copied onto the tape.
1163 * If supplied, various fields are copied from this label to
1164 * the actual tape label written on the tape.
1167 static afs_int32 file_WriteLabel (info, label, rewind)
1168 struct butm_tapeInfo *info;
1169 struct butm_tapeLabel *label;
1174 struct tapeLabel *tlabel;
1176 afs_hyper_t off; /* offset */
1178 if (info->debug) printf("butm: Write tape label\n");
1183 code = check(info, WRITE_OP);
1184 if (code) ERROR_EXIT(code);
1185 if (!label) ERROR_EXIT(BUTM_BADARGUMENT);
1186 if (label->structVersion != CUR_TAPE_VERSION) ERROR_EXIT(BUTM_OLDINTERFACE);
1188 if (rewind) /* Not appending, so rewind */
1190 code = rewindFile(info);
1191 if (code) ERROR_EXIT(code);
1195 p = (struct progress *)info->tmRock;
1197 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1198 if (code) ERROR_EXIT(BUTM_POSITION);
1203 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1206 /* Copy the label into the tape block
1207 * ---------------------------------- */
1208 bzero(tapeBlock, BUTM_BLOCKSIZE);
1210 if (!label->creationTime) label->creationTime = time(0);
1212 tlabel = (struct tapeLabel *)tapeBlock;
1213 bcopy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1214 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1215 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1216 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1217 tlabel->label.size = htonl(tlabel->label.size);
1218 tlabel->label.useCount = htonl(tlabel->label.useCount);
1219 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1222 * write the tape label - For appends, the write may need to skip
1223 * over 1 or 2 EOF marks that were written when tape was closed after
1224 * the last dump. Plus, some AIX tape drives require we try forwarding
1225 * over the last EOF and take an error before we can write the new label.
1226 * ---------------------------------------------------------------------- */
1227 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1228 if ( !isafile && !rewind && (code == BUTM_IO) ) do /* do if write failed */
1230 fcode = SeekFile(info, 1); /* skip over the EOF */
1231 if (fcode) break; /* leave if error */
1233 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1234 if (code != BUTM_IO) break; /* continue if write failed */
1236 fcode = SeekFile(info, 1); /* skip over the EOF */
1237 if (fcode) /* retry 1 write if couldn't skip */
1239 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1243 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1244 if (code != BUTM_IO) break; /* continue if write failed */
1246 fcode = SeekFile(info, 1); /* skip over the EOF */
1247 if (fcode) /* retry 1 write if couldn't skip */
1249 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1255 /* clear the write error status a failed WriteTapeBlock may have produced */
1256 if (!code) info->status &= ~BUTM_STATUS_WRITEERROR;
1263 file_ReadLabel (info, label, rewind)
1264 struct butm_tapeInfo *info;
1265 struct butm_tapeLabel *label;
1268 struct tapeLabel *tlabel;
1270 afs_int32 blockType;
1272 if (info->debug) printf("butm: Read tape label\n");
1277 code = check(info, READ_OP);
1278 if (code) ERROR_EXIT(code);
1279 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1283 code = rewindFile(info);
1284 if (code) ERROR_EXIT(code); /* status is set so return */
1288 * When appended labels were written, either 1 or 2 EOF marks may
1289 * have had to be skipped. When reading a label, these EOF marks
1290 * must also be skipped. When an EOF is read, 0 bytes are returned
1291 * (refer to the write calls in file_WriteLabel routine).
1292 * ---------------------------------------------------------------- */
1293 code = ReadTapeBlock(info, tapeBlock, &blockType);
1294 if ( !isafile && !rewind && (blockType == BLOCK_EOF) ) do
1296 code = ReadTapeBlock(info, tapeBlock, &blockType);
1297 if (blockType != BLOCK_EOF) break; /* didn't read an EOF */
1299 code = ReadTapeBlock(info, tapeBlock, &blockType);
1302 if (blockType == BLOCK_UNKNOWN) ERROR_EXIT(BUTM_NOLABEL);
1303 if (code) ERROR_EXIT(code);
1304 if (blockType != BLOCK_LABEL) ERROR_EXIT(BUTM_BADBLOCK);
1310 tlabel = (struct tapeLabel *) tapeBlock;
1311 bcopy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1312 label->structVersion = ntohl(label->structVersion);
1313 label->creationTime = ntohl(label->creationTime);
1314 label->expirationDate = ntohl(label->expirationDate);
1315 label->size = ntohl(label->size);
1316 label->dumpid = ntohl(label->dumpid);
1317 label->useCount = ntohl(label->useCount);
1319 info->tapeSize = label->size; /* use size from label */
1326 static afs_int32 file_WriteFileBegin (info)
1327 struct butm_tapeInfo *info;
1330 afs_int32 error = 0;
1332 if (info->debug) printf("butm: Write filemark begin\n");
1336 code = check(info, WRITE_OP);
1337 if (code) ERROR_EXIT(code);
1339 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1340 if (code) ERROR_EXIT(code);
1348 static afs_int32 file_ReadFileBegin (info)
1349 struct butm_tapeInfo *info;
1351 struct fileMark mark;
1353 afs_int32 blockType;
1355 if (info->debug) printf("butm: Read filemark begin\n");
1360 code = check(info, READ_OP);
1361 if (code) ERROR_EXIT(code);
1362 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1364 code = ReadTapeBlock(info, tapeBlock, &blockType);
1365 if (code) ERROR_EXIT(code);
1367 if (blockType != BLOCK_FMBEGIN)
1369 if (blockType == BLOCK_EOD) ERROR_EXIT(BUTM_EOD); /* EODump label */
1370 if (blockType == BLOCK_LABEL) ERROR_EXIT(BUTM_LABEL); /* Tape label */
1371 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1378 /* Writes data out in block sizes of 16KB. Does destroy the data.
1379 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1382 file_WriteFileData (info, data, blocks, len)
1383 struct butm_tapeInfo *info;
1391 char *bstart; /* Where block starts for a 16K block */
1392 char *dstart; /* Where data starts for a 16K block */
1394 if (info->debug) printf("butm: Write tape data - %u bytes\n", len);
1399 code = check(info, WRITE_OP);
1400 if (code) ERROR_EXIT(code);
1401 if (!data || (len < 0)) ERROR_EXIT(BUTM_BADARGUMENT);
1402 if (READS || !WRITES) ERROR_EXIT(BUTM_BADOP);
1404 b = 0; /* start at block 0 */
1407 dstart = &data[b*BUTM_BLKSIZE];
1408 bstart = dstart - sizeof(struct blockMark);
1410 if (len < BUTM_BLKSIZE)
1412 bzero(&dstart[len], BUTM_BLKSIZE - len);
1417 length = BUTM_BLKSIZE;
1420 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1424 /* If there are more blocks, step to next block */
1425 /* Otherwise, copy the data to beginning of last block */
1427 if (b < (blocks-1)) b++;
1429 bcopy(&dstart[BUTM_BLKSIZE], &dstart[0], len);
1436 /* file_ReadFileData
1437 * Read a data block from tape.
1439 * info - tape info structure, c.f. fid
1440 * data - ptr to buffer for data
1441 * len - size of data buffer
1443 * nBytes - no. of data bytes read.
1447 file_ReadFileData (info, data, len, nBytes)
1448 struct butm_tapeInfo *info;
1453 struct blockMark *bmark;
1455 afs_int32 blockType;
1457 if (info->debug) printf("butm: Read tape data - %u bytes\n", len);
1462 if (!nBytes) ERROR_EXIT(BUTM_BADARGUMENT);
1465 code = check(info, READ_OP);
1466 if (code) ERROR_EXIT(code);
1467 if ( !data || (len < 0) || (len > BUTM_BLKSIZE) )
1468 ERROR_EXIT(BUTM_BADARGUMENT);
1469 if (!READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1471 data -= sizeof(struct blockMark);
1472 code = ReadTapeBlock(info, data, &blockType);
1473 if (code) ERROR_EXIT(code);
1475 if (blockType != BLOCK_DATA)
1477 if (blockType == BLOCK_EOF) ERROR_EXIT(BUTM_EOF);
1478 if (blockType == BLOCK_FMEND) ERROR_EXIT(BUTM_ENDVOLUME);
1479 ERROR_EXIT(BUTM_BADBLOCK);
1482 bmark = (struct blockMark *)data;
1483 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1489 static afs_int32 file_WriteFileEnd (info)
1490 struct butm_tapeInfo *info;
1494 if (info->debug) printf("butm: Write filemark end\n");
1499 code = check(info, WRITE_OP);
1500 if (code) ERROR_EXIT(code);
1501 if (READS || !WRITES) ERROR_EXIT(BUTM_BADOP);
1503 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1509 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1510 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1511 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1512 * missing with some 3.1 dumps).
1514 static afs_int32 file_ReadFileEnd (info)
1515 struct butm_tapeInfo *info;
1517 struct fileMark mark;
1519 afs_int32 blockType;
1521 if (info->debug) printf("butm: Read filemark end\n");
1526 code = check(info, READ_OP);
1527 if (code) ERROR_EXIT(code);
1528 if (!READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1530 info->status &= ~BUTM_STATUS_EOF;
1532 code = ReadTapeBlock(info, tapeBlock, &blockType);
1533 if (code) ERROR_EXIT(code);
1535 if ( (blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF) )
1536 ERROR_EXIT(BUTM_BADBLOCK);
1543 * Write the end-of-dump marker.
1545 static afs_int32 file_WriteEODump (info)
1546 struct butm_tapeInfo *info;
1550 if (info->debug) printf("butm: Write filemark EOD\n");
1555 code = check(info, WRITE_OP);
1556 if (code) ERROR_EXIT(code);
1557 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1559 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1560 if (code) ERROR_EXIT(code);
1562 info->status |= BUTM_STATUS_EOD;
1568 static afs_int32 file_Seek (info, position)
1569 struct butm_tapeInfo *info;
1575 afs_hyper_t startOff, stopOff; /* for normal file(non-tape) seeks */
1577 if (info->debug) printf("butm: Seek to the tape position %d\n", position);
1581 code = check(info, READ_OP);
1582 if (code) ERROR_EXIT(code);
1586 p = (struct progress *)info->tmRock;
1587 posit = (position * BUTM_BLOCKSIZE);
1589 hset64(startOff, 0, posit);
1590 w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1591 if (w) info->error == w;
1592 if (hcmp(startOff, stopOff) != 0) ERROR_EXIT(BUTM_POSITION);
1594 p->reading = p->writing = 0;
1595 info->position = position;
1599 /* Don't position backwards if we are in-between FMs */
1600 if ((READS || WRITES) && ((position-info->position) <= 0))
1601 ERROR_EXIT(BUTM_BADOP);
1603 code = SeekFile(info, (position - info->position));
1604 if (code) ERROR_EXIT(code);
1612 * Seek to the EODump (end-of-dump) after the given position. This is
1613 * the position after the EOF filemark immediately after the EODump mark.
1614 * This is for tapes of version 4 or greater.
1616 static afs_int32 file_SeekEODump (info, position)
1617 struct butm_tapeInfo *info;
1620 struct fileMark mark;
1622 afs_int32 blockType;
1625 afs_hyper_t startOff, stopOff; /* file seek offsets */
1627 if (info->debug) printf("butm: Seek to end-of-dump\n");
1630 code = check(info, READ_OP);
1631 if ( code ) ERROR_EXIT(code);
1632 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1636 p = (struct progress *)info->tmRock;
1637 hset64(startOff, 0, 0);
1638 w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1639 if (w) info->error == w;
1640 if (w) ERROR_EXIT(BUTM_POSITION);
1642 if (hgetlo(stopOff) % BUTM_BLOCKSIZE) ERROR_EXIT(BUTM_POSITION);
1643 info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1647 /* Seek to the desired position */
1648 code = SeekFile(info, (position - info->position) + 1);
1649 if (code) ERROR_EXIT(code);
1652 * Search until the filemark is an EODump filemark.
1653 * Skip over volumes only.
1657 code = ReadTapeBlock(info, tapeBlock, &blockType);
1658 if (code) ERROR_EXIT(code);
1660 if (blockType == BLOCK_EOD) break;
1661 if (blockType != BLOCK_FMBEGIN) ERROR_EXIT(BUTM_BADBLOCK);
1663 code = SeekFile(info, 1); /* Step forward to next volume */
1664 if (code) ERROR_EXIT(code);
1673 static afs_int32 file_SetSize (info, size)
1674 struct butm_tapeInfo *info;
1677 if (info->debug) printf("butm: Set size of tape\n");
1680 if (size <= 0 ) info->tapeSize = config.tapeSize;
1681 else info->tapeSize = size;
1685 static afs_int32 file_GetSize (info, size)
1686 struct butm_tapeInfo *info;
1689 if (info->debug) printf("butm: Get size of tape\n");
1692 *size = info->tapeSize;
1696 /* =====================================================================
1697 * Startup/configuration routines.
1698 * ===================================================================== */
1700 static afs_int32 file_Configure (file)
1701 struct tapeConfig *file;
1705 com_err(whoami, BUTM_BADCONFIG, "device not specified");
1706 return BUTM_BADCONFIG;
1709 config.tapeSize = file->capacity;
1710 config.fileMarkSize = file->fileMarkSize;
1711 config.portOffset = file->portOffset;
1712 strcpy(config.tapedir, file->device);
1714 /* Tape must be large enough to at least fit a label */
1715 if (config.tapeSize <= 0)
1717 com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes", config.tapeSize);
1718 return BUTM_BADCONFIG;
1721 if (strlen(config.tapedir) == 0)
1723 com_err (whoami, BUTM_BADCONFIG, "no tape device specified");
1724 return BUTM_BADCONFIG;
1731 /* This procedure instantiates a tape module of type file_tm. */
1732 afs_int32 butm_file_Instantiate (info, file)
1733 struct butm_tapeInfo *info;
1734 struct tapeConfig *file;
1736 extern int debugLevel;
1739 if (debugLevel > 98) printf("butm: Instantiate butc\n");
1741 if (!info) ERROR_EXIT(BUTM_BADARGUMENT);
1742 if (info->structVersion != BUTM_MAJORVERSION) ERROR_EXIT(BUTM_OLDINTERFACE);
1744 bzero (info, sizeof(struct butm_tapeInfo));
1745 info->structVersion = BUTM_MAJORVERSION;
1746 info->ops.mount = file_Mount;
1747 info->ops.dismount = file_Dismount;
1748 info->ops.create = file_WriteLabel;
1749 info->ops.readLabel = file_ReadLabel;
1750 info->ops.seek = file_Seek;
1751 info->ops.seekEODump = file_SeekEODump;
1752 info->ops.readFileBegin = file_ReadFileBegin;
1753 info->ops.readFileData = file_ReadFileData;
1754 info->ops.readFileEnd = file_ReadFileEnd;
1755 info->ops.writeFileBegin = file_WriteFileBegin;
1756 info->ops.writeFileData = file_WriteFileData;
1757 info->ops.writeFileEnd = file_WriteFileEnd;
1758 info->ops.writeEOT = file_WriteEODump;
1759 info->ops.setSize = file_SetSize;
1760 info->ops.getSize = file_GetSize;
1761 info->debug = ((debugLevel > 98) ? 1 : 0);
1763 code = file_Configure (file);