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 <afsconfig.h>
11 #include <afs/param.h>
18 #include <netinet/in.h>
20 #include <sys/types.h>
28 #include <afs/com_err.h>
31 #include "error_macros.h"
36 #define FILE_MAGIC 1000000007 /* s/w file mark */
37 #define FILE_BEGIN 0 /* byte field in file mark */
38 #define FILE_FMEND 1 /* byte field in file mark */
39 #define FILE_EOD -1 /* byte field in file mark */
40 #define TAPE_MAGIC 1100000009 /* tape label block */
41 #define BLOCK_MAGIC 1100000005 /* file data block */
42 #ifdef AFS_PTHREAD_ENV
44 #define SLEEP(s) sleep(s)
46 #define POLL() IOMGR_Poll()
47 #define SLEEP(s) IOMGR_Sleep(s)
52 * 1) filemarks and block marks have the magic,bytes fields reversed. This
53 * is undoubtedly a bug. Also note that the two structures have
54 * inconsistent types, overlaying int and afs_int32.
55 * 2) When a volume is dumped, if the volume is locked, the dump will produce
56 * an anomalous tape format of the form:
60 * The design of the original butm code means that this cannot be
61 * handled correctly. The code is modified so that ReadFileData
62 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
65 /* data organization on tape:
66 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
67 * blockMark contains a magic number and counts of real data bytes
68 * written out in the block.
70 * each file is preceeded by a fileMark, which acts as the file
71 * delimiter. A file consists of contigous data blocks. TM does
72 * understand or interrpet the data in data blocks.
74 * The tape begins with a tape label and ends with EOF file markers
75 * in succession (2 or 4 of them ).
79 struct fileMark { /* in network byte order */
86 struct butm_tapeLabel label;
90 usd_handle_t fid; /* file id of simulated tape */
91 afs_int32 mountId; /* current mountId */
92 afs_int32 reading; /* read file operation in progress */
93 afs_int32 writing; /* write file operation in progress */
96 static struct configuration {
97 char tapedir[64]; /* directory to create "tapes" */
98 afs_int32 mountId; /* for detecting simultaneous mounts */
99 afs_uint32 tapeSize; /* size of simulated tapes */
100 afs_uint32 fileMarkSize; /* size of file mark, bytes */
101 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
104 static char *whoami = "file_tm";
105 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
107 #define BLOCK_LABEL 0 /* read/write a tape label */
108 #define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
109 #define BLOCK_DATA 2 /* read/write a data block */
110 #define BLOCK_FMEND 3 /* read/write an end FileMark */
111 #define BLOCK_EOD 4 /* read/write an EOD FileMark */
112 #define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
113 #define BLOCK_UNKNOWN 6 /* tape block is unknwon (error)*/
118 #define READS (((struct progress *)(info->tmRock))->reading)
119 #define WRITES (((struct progress *)(info->tmRock))->writing)
121 /* ----------------------------------------------------------------------
122 * These routines use the usd library to perform tape operations.
123 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
124 * PrepareAccess(nt), ShutdownAccess(nt)
126 * Return Values: USD functions return 0 if successful or errno if failed.
128 /* ------------------ USD Interface Functions Begin ------------------------ */
131 * On Unix, fork a child process to perform an IOCTL call. This avoids
132 * blocking the entire process during a tape operation
136 /* Unix version of function */
138 static int ForkIoctl(usd_handle_t fd, int op, int count)
140 int rc; /* return code from system calls */
141 int i; /* loop index */
142 int pid; /* process ID of child process */
143 int status; /* exit status of child process */
144 int ioctl_rc; /* return code from ioctl call */
145 int pipefd[2]; /* pipe for child return status */
146 int forkflag; /* flag set when ready to fork */
147 usd_tapeop_t tapeop; /* tape operation specification */
150 #ifdef AFS_PTHREAD_ENV
151 forkflag = 0; /* No need to fork if using pthreads */
156 /* initialize tape command structure */
158 tapeop.tp_count = count;
160 /* create pipe for getting return code from child */
166 printf("butm: Can't open pipe for IOCTL process. Error %d\n", errno);
178 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
184 if ( !forkflag ) /* problem starting child process */
186 /* do the ioctl anyway, it will probably work */
187 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
189 else if ( pid == 0 ) /* child process */
191 /* close all unneccessary file descriptors */
192 /* note: as painful as it is, we have to reach under the covers of
193 * the usd package to implement this functionality.
195 unixfd = (int)(fd->handle);
197 for ( i = 3 ; i < _POSIX_OPEN_MAX ; i++ )
199 if ( i != unixfd && i != pipefd[1] )
205 /* do the ioctl call */
206 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
208 /* send the return code back to the parent */
209 write(pipefd[1], &ioctl_rc, sizeof(int));
213 else /* parent process */
217 /* read the result from the child process */
218 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
219 if ( rc != sizeof(int) )
221 /* tape is now in unknown state */
222 printf("butm: Can't determine IOCTL child status. Error %d\n", errno);
228 /* get the completion status from the child process */
229 rc = waitpid(pid,&status,0);
230 while (rc < 0 && errno == EINTR )
232 rc = waitpid(pid,&status,0);
236 printf("butm: Can't determine IOCTL child status. Error %d\n", errno);
238 else if ( status != 0 )
240 printf("butm: Unexpected IOCTL process status 0x%04x . Error %d\n", status, errno);
248 /* NT version of function */
250 static int ForkIoctl(usd_handle_t fd, int op, int count)
254 /* Issue requested tape control */
256 tapeop.tp_count = count;
258 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
260 #endif /* !AFS_NT40_ENV */
264 * On Unix, fork a child process to attempt to open the drive. We want to make
265 * certain there is tape in the drive before trying to open the device
266 * in the main process
270 /* Unix version of function. */
272 static int ForkOpen(char *device)
274 int rc; /* return code from system calls */
275 int i; /* loop index */
276 int pid; /* process ID of child process */
277 int status; /* exit status of child process */
278 int open_rc; /* return code from open */
279 int pipefd[2]; /* pipe for child return status */
280 int forkflag; /* flag set when ready to fork */
281 usd_handle_t fd; /* handle returned from open */
283 #ifdef AFS_PTHREAD_ENV
284 forkflag = 0; /* No need to fork if using pthreads */
289 /* create pipe for getting return code from child */
294 printf("butm: Cannot create pipe for OPEN process. Error %d\n", errno);
306 printf("butm: Cannot create child process for OPEN. Error %d\n", errno);
311 if ( !forkflag ) /* problem starting child process */
314 *return success, the caller will discover any problems
315 * when it opens the device.
319 else if ( pid == 0 ) /* child process */
321 /* close all unneccessary file descriptors */
322 for ( i = 3 ; i < _POSIX_OPEN_MAX ; i++ )
324 if ( i != pipefd[1] )
331 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
337 /* send the return code back to the parent */
338 write(pipefd[1], &open_rc, sizeof(open_rc));
342 else /* parent process */
347 /* read the result from the child process */
348 rc = read(pipefd[0], &open_rc, sizeof(open_rc));
349 if ( rc != sizeof(open_rc) )
351 /* this is not a problem since we will reopen the device anyway */
352 printf("butm: No response from OPEN process. Error %d\n", errno);
358 /* get the completion status from the child process */
359 rc = waitpid(pid,&status,0);
360 while (rc < 0 && errno == EINTR )
362 rc = waitpid(pid,&status,0);
366 printf("butm: Cannot get status of OPEN process. Error %d\n", errno);
368 else if ( status != 0 )
370 printf("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n", status, errno);
378 /* NT version of function. */
380 static int ForkOpen(char *device)
384 #endif /* AFS_NT40_ENV */
387 * On Unix, fork a child process to close the drive. If the drive rewinds
388 * on close it could cause the process to block.
392 /* Unix version of function */
394 static int ForkClose(usd_handle_t fd)
396 int rc; /* return code from system calls */
397 int i; /* loop index */
398 int pid; /* process ID of child process */
399 int status; /* exit status of child process */
400 int close_rc, parent_close_rc; /* return codes from close */
401 int pipefd[2]; /* pipe for child return status */
402 int ctlpipe[2]; /* pipe for message to child */
403 int forkflag; /* flag set when ready to fork */
406 #ifdef AFS_PTHREAD_ENV
407 forkflag = 0; /* No need to fork if using pthreads */
412 /* create pipe for getting return code from child */
418 printf("butm: Cannot create pipe for CLOSE process. Error %d\n", errno);
423 /* create pipe for notifying child when to close */
431 printf("butm: Cannot create pipe for CLOSE process. Error %d\n", errno);
445 printf("butm: Cannot create CLOSE child process. Error %d\n", errno);
450 if ( !forkflag ) /* problem starting child process */
452 close_rc = USD_CLOSE(fd);
453 parent_close_rc = close_rc;
455 else if ( pid == 0 ) /* child process */
457 /* close all unneccessary file descriptors */
458 /* note: as painful as it is, we have to reach under the covers of
459 * the usd package to implement this functionality.
461 unixfd = (int)(fd->handle);
463 for ( i = 3 ; i < _POSIX_OPEN_MAX ; i++ )
465 if ( i != unixfd && i != ctlpipe[0] && i != pipefd[1] )
471 /* the parent writes the control pipe after it closes the device */
472 read(ctlpipe[0], &close_rc, sizeof(int));
476 close_rc = USD_CLOSE(fd);
478 /* send the return code back to the parent */
479 write(pipefd[1], &close_rc, sizeof(int));
483 else /* parent process */
490 * close the device, this should have no effect as long as the
491 * child has not closed
494 parent_close_rc = USD_CLOSE(fd);
496 /* notify the child to do its close */
497 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
498 if ( rc != sizeof(int) )
500 printf("butm: Error communicating with CLOSE process. Error %d\n", errno);
504 /* read the result from the child process */
505 rc = read(pipefd[0], &close_rc, sizeof(int));
506 if ( rc != sizeof(int) )
508 /* logging is enough, since we wrote a file mark the */
509 /* return code from the close doesn't really matter */
510 printf("butm: No response from CLOSE process. Error %d\n", errno);
516 /* get the completion status from the child process */
517 rc = waitpid(pid,&status,0);
518 while (rc < 0 && errno == EINTR )
520 rc = waitpid(pid,&status,0);
524 printf("butm: Cannot get status of CLOSE process. Error %d\n", errno);
526 else if ( status != 0 )
528 printf("butm: Unexpected exit status 0x04x from CLOSE process. Error %d\n", status, errno);
531 /* if either process received an error, then return an error */
532 if ( parent_close_rc < 0 )
534 close_rc = parent_close_rc;
542 /* NT version of function */
544 static int ForkClose(usd_handle_t fd)
546 return(USD_CLOSE(fd));
548 #endif /* AFS_NT40_ENV */
550 /* Forward space file */
551 static int ForwardSpace(usd_handle_t fid, int count)
558 return (ForkIoctl(fid, USDTAPE_FSF, count));
562 /* Backward space file */
563 static int BackwardSpace(usd_handle_t fid, int count)
570 return (ForkIoctl(fid, USDTAPE_BSF, count));
574 /* write end of file mark */
575 static WriteEOF(usd_handle_t fid, int count)
582 return (ForkIoctl(fid, USDTAPE_WEOF,count));
587 static int Rewind(usd_handle_t fid)
590 afs_hyper_t startOff, stopOff;
593 return (USD_SEEK(fid, startOff, SEEK_SET, &stopOff));
595 return (ForkIoctl(fid, USDTAPE_REW, 0));
599 /* prepare tape drive for access */
600 static int PrepareAccess(usd_handle_t fid)
605 (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
607 /* NT won't rewind tape when it is opened */
609 #endif /* AFS_NT40_ENV */
613 /* decommission tape drive after all accesses complete */
614 static int ShutdownAccess(usd_handle_t fid)
618 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
620 #endif /* AFS_NT40_ENV */
624 /* -------------------- USD Interface Functions End ----------------------- */
626 /* =====================================================================
628 * ===================================================================== */
631 * add the supplied no. of bytes to the byte count of information placed
634 * dataSize - bytes used on the tape
637 incSize(info, dataSize)
638 struct butm_tapeInfo *info;
641 info->nBytes += dataSize;
642 info->kBytes += (info->nBytes/1024);
643 info->nBytes %= 1024;
647 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
648 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
650 * add the supplied no. of bytes to the byte count of data placed
651 * on the tape. Also check for reaching 2GB limit and reset the
652 * pointer if necessary. This allows us to use >2GB tapes.
654 * fid - file id for the tape.
655 * dataSize - bytes used on the tape
658 incPosition(info, fid, dataSize)
659 struct butm_tapeInfo *info;
665 /* Add this to the amount of data written to the tape */
666 incSize(info, dataSize);
668 info->posCount += dataSize;
670 if ( info->posCount >= 2147467264 ) /* 2GB - 16K */
673 #if (defined(AFS_SUN_ENV) || defined(AFS_DEC_ENV))
677 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
684 * This accounts for tape drives with a block size different from variable or 16K
685 * blocks and only reads that block size.
687 afs_int32 TapeBlockSize;
688 afs_int32 readData(fid, data, totalSize, errorP)
694 afs_int32 toread; /* Number of bytes to read */
695 afs_int32 rSize; /* Total bytes read so far */
696 afs_int32 tSize; /* Temporary size */
697 afs_int32 rc; /* return code */
699 toread = totalSize; /* First, try to read all the data */
701 rc = USD_READ(fid, &data[0], toread, &rSize);
707 if ( rSize == 0 ) /* reached EOF */
710 if ( rSize != TapeBlockSize ) /* Tape block size has changed */
712 TapeBlockSize = rSize;
713 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
716 /* Read the rest of the data in */
717 while (rSize < totalSize)
719 toread = ( (totalSize-rSize)<TapeBlockSize ? (totalSize-rSize) : TapeBlockSize);
720 rc = USD_READ(fid, &data[rSize], toread, &tSize);
721 if (rc) *errorP = rc;
723 if (tSize != toread) break;
726 if (rSize > totalSize)
727 printf("readData - Read > 16K data block - continuing.\n");
733 SeekFile(info, count)
734 struct butm_tapeInfo *info;
739 int cpid, status, rcpid, stat;
743 if (count == 0) ERROR_EXIT(0);
744 p = (struct progress *)info->tmRock;
746 if (isafile) /* no reason for seeking through a file */
748 p->reading = p->writing = 0;
755 error = ForwardSpace (p->fid, count);
757 error = BackwardSpace(p->fid, -count);
763 info->status |= BUTM_STATUS_SEEKERROR;
764 ERROR_EXIT(BUTM_IOCTL);
767 info->position += count;
768 incSize(info, (count * config.fileMarkSize));
769 p = (struct progress *)info->tmRock;
770 p->reading = p->writing = 0;
773 if (error) info->error = error;
777 /* Step to the next filemark if we are not at one already */
778 afs_int32 NextFile(info)
779 struct butm_tapeInfo *info;
783 if (!READS && !WRITES)
786 code = SeekFile(info, 1);
791 WriteTapeBlock(info, buffer, length, blockType)
792 struct butm_tapeInfo *info;
793 char *buffer; /* assumed to be 16384 bytes with data in it */
794 afs_int32 length; /* amount data in buffer */
797 afs_int32 code = 0, rc = 0;
799 struct tapeLabel *label;
800 struct fileMark *fmark;
801 struct blockMark *bmark;
805 p = (struct progress *)info->tmRock;
807 if (blockType == BLOCK_DATA) /* Data Block */
809 if (length == 0) ERROR_EXIT(0);
810 bmark = (struct blockMark *)buffer;
811 memset(bmark, 0, sizeof(struct blockMark));
812 bmark->magic = htonl(BLOCK_MAGIC);
813 bmark->count = htonl(length);
815 else if (blockType == BLOCK_FMBEGIN) /* Filemark - begin */
817 fmark = (struct fileMark *)buffer;
818 fmark->magic = htonl(FILE_MAGIC);
819 fmark->nBytes = htonl(FILE_BEGIN);
821 else if (blockType == BLOCK_FMEND) /* Filemark - end */
823 fmark = (struct fileMark *)buffer;
824 fmark->magic = htonl(FILE_MAGIC);
825 fmark->nBytes = htonl(FILE_FMEND);
827 else if (blockType == BLOCK_LABEL) /* Label */
829 label = (struct tapeLabel *)buffer;
830 label->magic = htonl(TAPE_MAGIC);
832 else if (blockType == BLOCK_EOD) /* Filemark - EOD mark */
834 fmark = (struct fileMark *)buffer;
835 fmark->magic = htonl(FILE_MAGIC);
836 fmark->nBytes = htonl(FILE_EOD);
839 /* Write the tape block */
840 /* -------------------- */
841 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
842 if ( (rc == 0) && (wsize > 0) )
844 incPosition(info, p->fid, wsize); /* record whats written */
848 if (wsize != BUTM_BLOCKSIZE)
850 info->status |= BUTM_STATUS_WRITEERROR;
857 ERROR_EXIT(BUTM_EOT);
859 if (isafile) info->position++;
861 /* Write trailing EOF marker for some block types */
862 /* ---------------------------------------------- */
863 if ( (blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL) || (blockType == BLOCK_EOD) )
867 error = WriteEOF(p->fid, 1);
871 info->status |= BUTM_STATUS_WRITEERROR;
872 ERROR_EXIT(BUTM_IOCTL);
875 incSize(info, config.fileMarkSize);
876 if (!isafile) info->position++;
881 if (error) info->error = error;
886 ReadTapeBlock(info, buffer, blockType)
887 struct butm_tapeInfo *info;
888 char *buffer; /* assumed to be 16384 bytes */
889 afs_int32 *blockType;
892 afs_int32 rsize, fmtype;
893 struct tapeLabel *label;
894 struct fileMark *fmark;
895 struct blockMark *bmark;
898 *blockType = BLOCK_UNKNOWN;
900 p = (struct progress *)info->tmRock;
902 memset(buffer, 0, BUTM_BLOCKSIZE);
903 label = (struct tapeLabel *)buffer;
904 fmark = (struct fileMark *)buffer;
905 bmark = (struct blockMark *)buffer;
907 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
910 incPosition(info, p->fid, rsize);
914 if (rsize == 0) /* Read a HW EOF Marker? OK */
916 *blockType = BLOCK_EOF;
917 incSize(info, config.fileMarkSize); /* Size of filemark */
918 if (!isafile) info->position++; /* bump position */
919 p->reading = 0; /* No reads since EOF */
922 else if (rsize != BUTM_BLOCKSIZE) /* Didn't Read a full block */
924 info->status |= BUTM_STATUS_READERROR;
925 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
928 else if (ntohl(bmark->magic) == BLOCK_MAGIC) /* Data block? */
930 *blockType = BLOCK_DATA;
933 else if (ntohl(fmark->magic) == FILE_MAGIC) /* Read a filemark? */
935 fmtype = ntohl(fmark->nBytes);
937 if (fmtype == FILE_BEGIN) /* filemark begin */
939 *blockType = BLOCK_FMBEGIN;
941 else if (fmtype == FILE_FMEND) /* filemark end */
943 *blockType = BLOCK_FMEND;
944 code = SeekFile(info, 1);
946 else if (fmtype == FILE_EOD) /* EOD mark */
948 *blockType = BLOCK_EOD;
949 info->status |= BUTM_STATUS_EOD;
950 code = SeekFile(info, 1);
954 else if (ntohl(label->magic) == TAPE_MAGIC) /* Read a tape label? */
956 *blockType = BLOCK_LABEL;
957 code = SeekFile(info, 1);
960 if (isafile) info->position++;
967 * check version numbers and permissions in the info structure
970 static afs_int32 check (info, write)
971 struct butm_tapeInfo *info;
972 int write; /* write operation requested */
977 return(BUTM_BADARGUMENT);
979 /* Check version number in info structure */
980 if (info->structVersion != BUTM_MAJORVERSION)
981 return BUTM_OLDINTERFACE;
983 /* Check if a tape is mounted */
984 if (((p = (struct progress *)info->tmRock) == 0) || ( p->fid == 0))
987 /* If writing check if there is write access */
988 if ( write && (info->flags & BUTM_FLAGS_READONLY) )
989 return BUTM_READONLY;
994 static afs_int32 rewindFile (info)
995 struct butm_tapeInfo *info;
998 int cpid, status, rcpid, stat;
1003 p = (struct progress *)info->tmRock;
1007 error = Rewind(p->fid);
1013 info->status |= BUTM_STATUS_SEEKERROR;
1014 ERROR_EXIT(BUTM_IOCTL);
1017 info->position = (isafile ? 0 : 1);
1018 info->kBytes = info->nBytes = 0;
1019 info->nFiles = info->nRecords = 0;
1020 p->reading = p->writing = 0;
1023 if (error) info->error = error;
1027 /* =====================================================================
1029 * ===================================================================== */
1032 file_Mount (info, tape)
1033 struct butm_tapeInfo *info;
1039 int cpid, status, rcpid, xflags;
1040 afs_int32 code = 0, error = 0, rc = 0;
1042 if (info->debug) printf("butm: Mount tape drive\n");
1047 if (!info || !tape) ERROR_EXIT(BUTM_BADARGUMENT);
1048 if (info->structVersion != BUTM_MAJORVERSION) ERROR_EXIT(BUTM_OLDINTERFACE);
1049 if (info->tmRock) ERROR_EXIT(BUTM_PARALLELMOUNTS);
1050 if (strlen(tape) >= sizeof(info->name)) ERROR_EXIT(BUTM_BADARGUMENT);
1052 strcpy (info->name, tape);
1054 strcpy (filename, config.tapedir); /* the name of the tape device*/
1055 info->position = (isafile ? 0 : 1);
1056 info->kBytes = info->nBytes = 0;
1057 info->nRecords = info->nFiles = 0;
1058 info->recordSize = 0;
1059 info->tapeSize = config.tapeSize;
1060 info->coefBytes = 1;
1061 info->coefRecords = 0;
1062 info->coefFiles = sizeof(struct fileMark);
1063 info->simultaneousTapes = 1;
1066 info->flags = BUTM_FLAGS_SEQUENTIAL;
1071 xflags |= USD_OPEN_CREATE;
1076 * try to open in a child process first so nothing will
1077 * time out should the process block because the device
1081 if (ForkOpen(filename)) {
1082 ERROR_EXIT(BUTM_MOUNTFAIL);
1086 /* Now go ahead and open the tape drive for real */
1087 rc = usd_Open(filename,
1088 (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777, &fid);
1090 { /* try for lesser access */
1091 rc = usd_Open(filename,
1092 (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1097 ERROR_EXIT(BUTM_MOUNTFAIL);
1099 info->flags |= BUTM_FLAGS_READONLY;
1102 (void)PrepareAccess(fid); /* for NT */
1104 p = (struct progress *) malloc (sizeof(*p));
1105 info->tmRock = (char *)p;
1107 p->mountId = config.mountId = time(0);
1108 p->reading = p->writing = 0;
1110 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1113 if (error) info->error = error;
1117 static afs_int32 file_Dismount (info)
1118 struct butm_tapeInfo *info;
1121 int cpid, status, rcpid, stat;
1122 afs_int32 code = 0, error = 0, cd;
1127 if (info->debug) printf("butm: Unmount tape drive\n");
1132 code = check (info, READ_OP);
1133 if (code) ERROR_EXIT(code);
1135 p = (struct progress *)info->tmRock;
1137 (void)ShutdownAccess(p->fid); /* for NT */
1139 /* close the device */
1140 if (error = ForkClose(p->fid))
1142 printf("butm: Tape close failed. Error %d\n", errno);
1149 code = BUTM_DISMOUNTFAIL;
1150 info->status |= BUTM_STATUS_TAPEERROR;
1154 info->tmRock = 0; /* mark it as closed - even if error on close */
1158 if (error) info->error = error;
1163 * write the header on a tape
1165 * info - handle on tape unit
1166 * label - label information. This label is not copied onto the tape.
1167 * If supplied, various fields are copied from this label to
1168 * the actual tape label written on the tape.
1171 static afs_int32 file_WriteLabel (info, label, rewind)
1172 struct butm_tapeInfo *info;
1173 struct butm_tapeLabel *label;
1178 struct tapeLabel *tlabel;
1180 afs_hyper_t off; /* offset */
1182 if (info->debug) printf("butm: Write tape label\n");
1187 code = check(info, WRITE_OP);
1188 if (code) ERROR_EXIT(code);
1189 if (!label) ERROR_EXIT(BUTM_BADARGUMENT);
1190 if (label->structVersion != CUR_TAPE_VERSION) ERROR_EXIT(BUTM_OLDINTERFACE);
1192 if (rewind) /* Not appending, so rewind */
1194 code = rewindFile(info);
1195 if (code) ERROR_EXIT(code);
1199 p = (struct progress *)info->tmRock;
1201 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1202 if (code) ERROR_EXIT(BUTM_POSITION);
1207 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1210 /* Copy the label into the tape block
1211 * ---------------------------------- */
1212 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1214 if (!label->creationTime) label->creationTime = time(0);
1216 tlabel = (struct tapeLabel *)tapeBlock;
1217 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1218 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1219 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1220 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1221 tlabel->label.size = htonl(tlabel->label.size);
1222 tlabel->label.useCount = htonl(tlabel->label.useCount);
1223 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1226 * write the tape label - For appends, the write may need to skip
1227 * over 1 or 2 EOF marks that were written when tape was closed after
1228 * the last dump. Plus, some AIX tape drives require we try forwarding
1229 * over the last EOF and take an error before we can write the new label.
1230 * ---------------------------------------------------------------------- */
1231 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1232 if ( !isafile && !rewind && (code == BUTM_IO) ) do /* do if write failed */
1234 fcode = SeekFile(info, 1); /* skip over the EOF */
1235 if (fcode) break; /* leave if error */
1237 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1238 if (code != BUTM_IO) break; /* continue if write failed */
1240 fcode = SeekFile(info, 1); /* skip over the EOF */
1241 if (fcode) /* retry 1 write if couldn't skip */
1243 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1247 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1248 if (code != BUTM_IO) break; /* continue if write failed */
1250 fcode = SeekFile(info, 1); /* skip over the EOF */
1251 if (fcode) /* retry 1 write if couldn't skip */
1253 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1259 /* clear the write error status a failed WriteTapeBlock may have produced */
1260 if (!code) info->status &= ~BUTM_STATUS_WRITEERROR;
1267 file_ReadLabel (info, label, rewind)
1268 struct butm_tapeInfo *info;
1269 struct butm_tapeLabel *label;
1272 struct tapeLabel *tlabel;
1274 afs_int32 blockType;
1276 if (info->debug) printf("butm: Read tape label\n");
1281 code = check(info, READ_OP);
1282 if (code) ERROR_EXIT(code);
1283 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1287 code = rewindFile(info);
1288 if (code) ERROR_EXIT(code); /* status is set so return */
1292 * When appended labels were written, either 1 or 2 EOF marks may
1293 * have had to be skipped. When reading a label, these EOF marks
1294 * must also be skipped. When an EOF is read, 0 bytes are returned
1295 * (refer to the write calls in file_WriteLabel routine).
1296 * ---------------------------------------------------------------- */
1297 code = ReadTapeBlock(info, tapeBlock, &blockType);
1298 if ( !isafile && !rewind && (blockType == BLOCK_EOF) ) do
1300 code = ReadTapeBlock(info, tapeBlock, &blockType);
1301 if (blockType != BLOCK_EOF) break; /* didn't read an EOF */
1303 code = ReadTapeBlock(info, tapeBlock, &blockType);
1306 if (blockType == BLOCK_UNKNOWN) ERROR_EXIT(BUTM_NOLABEL);
1307 if (code) ERROR_EXIT(code);
1308 if (blockType != BLOCK_LABEL) ERROR_EXIT(BUTM_BADBLOCK);
1314 tlabel = (struct tapeLabel *) tapeBlock;
1315 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1316 label->structVersion = ntohl(label->structVersion);
1317 label->creationTime = ntohl(label->creationTime);
1318 label->expirationDate = ntohl(label->expirationDate);
1319 label->size = ntohl(label->size);
1320 label->dumpid = ntohl(label->dumpid);
1321 label->useCount = ntohl(label->useCount);
1323 info->tapeSize = label->size; /* use size from label */
1330 static afs_int32 file_WriteFileBegin (info)
1331 struct butm_tapeInfo *info;
1334 afs_int32 error = 0;
1336 if (info->debug) printf("butm: Write filemark begin\n");
1340 code = check(info, WRITE_OP);
1341 if (code) ERROR_EXIT(code);
1343 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1344 if (code) ERROR_EXIT(code);
1352 static afs_int32 file_ReadFileBegin (info)
1353 struct butm_tapeInfo *info;
1355 struct fileMark mark;
1357 afs_int32 blockType;
1359 if (info->debug) printf("butm: Read filemark begin\n");
1364 code = check(info, READ_OP);
1365 if (code) ERROR_EXIT(code);
1366 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1368 code = ReadTapeBlock(info, tapeBlock, &blockType);
1369 if (code) ERROR_EXIT(code);
1371 if (blockType != BLOCK_FMBEGIN)
1373 if (blockType == BLOCK_EOD) ERROR_EXIT(BUTM_EOD); /* EODump label */
1374 if (blockType == BLOCK_LABEL) ERROR_EXIT(BUTM_LABEL); /* Tape label */
1375 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1382 /* Writes data out in block sizes of 16KB. Does destroy the data.
1383 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1386 file_WriteFileData (info, data, blocks, len)
1387 struct butm_tapeInfo *info;
1395 char *bstart; /* Where block starts for a 16K block */
1396 char *dstart; /* Where data starts for a 16K block */
1398 if (info->debug) printf("butm: Write tape data - %u bytes\n", len);
1403 code = check(info, WRITE_OP);
1404 if (code) ERROR_EXIT(code);
1405 if (!data || (len < 0)) ERROR_EXIT(BUTM_BADARGUMENT);
1406 if (READS || !WRITES) ERROR_EXIT(BUTM_BADOP);
1408 b = 0; /* start at block 0 */
1411 dstart = &data[b*BUTM_BLKSIZE];
1412 bstart = dstart - sizeof(struct blockMark);
1414 if (len < BUTM_BLKSIZE)
1416 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1421 length = BUTM_BLKSIZE;
1424 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1428 /* If there are more blocks, step to next block */
1429 /* Otherwise, copy the data to beginning of last block */
1431 if (b < (blocks-1)) b++;
1433 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1440 /* file_ReadFileData
1441 * Read a data block from tape.
1443 * info - tape info structure, c.f. fid
1444 * data - ptr to buffer for data
1445 * len - size of data buffer
1447 * nBytes - no. of data bytes read.
1451 file_ReadFileData (info, data, len, nBytes)
1452 struct butm_tapeInfo *info;
1457 struct blockMark *bmark;
1459 afs_int32 blockType;
1461 if (info->debug) printf("butm: Read tape data - %u bytes\n", len);
1466 if (!nBytes) ERROR_EXIT(BUTM_BADARGUMENT);
1469 code = check(info, READ_OP);
1470 if (code) ERROR_EXIT(code);
1471 if ( !data || (len < 0) || (len > BUTM_BLKSIZE) )
1472 ERROR_EXIT(BUTM_BADARGUMENT);
1473 if (!READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1475 data -= sizeof(struct blockMark);
1476 code = ReadTapeBlock(info, data, &blockType);
1477 if (code) ERROR_EXIT(code);
1479 if (blockType != BLOCK_DATA)
1481 if (blockType == BLOCK_EOF) ERROR_EXIT(BUTM_EOF);
1482 if (blockType == BLOCK_FMEND) ERROR_EXIT(BUTM_ENDVOLUME);
1483 ERROR_EXIT(BUTM_BADBLOCK);
1486 bmark = (struct blockMark *)data;
1487 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1493 static afs_int32 file_WriteFileEnd (info)
1494 struct butm_tapeInfo *info;
1498 if (info->debug) printf("butm: Write filemark end\n");
1503 code = check(info, WRITE_OP);
1504 if (code) ERROR_EXIT(code);
1505 if (READS || !WRITES) ERROR_EXIT(BUTM_BADOP);
1507 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1513 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1514 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1515 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1516 * missing with some 3.1 dumps).
1518 static afs_int32 file_ReadFileEnd (info)
1519 struct butm_tapeInfo *info;
1521 struct fileMark mark;
1523 afs_int32 blockType;
1525 if (info->debug) printf("butm: Read filemark end\n");
1530 code = check(info, READ_OP);
1531 if (code) ERROR_EXIT(code);
1532 if (!READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1534 info->status &= ~BUTM_STATUS_EOF;
1536 code = ReadTapeBlock(info, tapeBlock, &blockType);
1537 if (code) ERROR_EXIT(code);
1539 if ( (blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF) )
1540 ERROR_EXIT(BUTM_BADBLOCK);
1547 * Write the end-of-dump marker.
1549 static afs_int32 file_WriteEODump (info)
1550 struct butm_tapeInfo *info;
1554 if (info->debug) printf("butm: Write filemark EOD\n");
1559 code = check(info, WRITE_OP);
1560 if (code) ERROR_EXIT(code);
1561 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1563 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1564 if (code) ERROR_EXIT(code);
1566 info->status |= BUTM_STATUS_EOD;
1572 static afs_int32 file_Seek (info, position)
1573 struct butm_tapeInfo *info;
1579 afs_hyper_t startOff, stopOff; /* for normal file(non-tape) seeks */
1581 if (info->debug) printf("butm: Seek to the tape position %d\n", position);
1585 code = check(info, READ_OP);
1586 if (code) ERROR_EXIT(code);
1590 p = (struct progress *)info->tmRock;
1591 posit = (position * BUTM_BLOCKSIZE);
1593 hset64(startOff, 0, posit);
1594 w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1595 if (w) info->error == w;
1596 if (hcmp(startOff, stopOff) != 0) ERROR_EXIT(BUTM_POSITION);
1598 p->reading = p->writing = 0;
1599 info->position = position;
1603 /* Don't position backwards if we are in-between FMs */
1604 if ((READS || WRITES) && ((position-info->position) <= 0))
1605 ERROR_EXIT(BUTM_BADOP);
1607 code = SeekFile(info, (position - info->position));
1608 if (code) ERROR_EXIT(code);
1616 * Seek to the EODump (end-of-dump) after the given position. This is
1617 * the position after the EOF filemark immediately after the EODump mark.
1618 * This is for tapes of version 4 or greater.
1620 static afs_int32 file_SeekEODump (info, position)
1621 struct butm_tapeInfo *info;
1624 struct fileMark mark;
1626 afs_int32 blockType;
1629 afs_hyper_t startOff, stopOff; /* file seek offsets */
1631 if (info->debug) printf("butm: Seek to end-of-dump\n");
1634 code = check(info, READ_OP);
1635 if ( code ) ERROR_EXIT(code);
1636 if (READS || WRITES) ERROR_EXIT(BUTM_BADOP);
1640 p = (struct progress *)info->tmRock;
1641 hset64(startOff, 0, 0);
1642 w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1643 if (w) info->error == w;
1644 if (w) ERROR_EXIT(BUTM_POSITION);
1646 if (hgetlo(stopOff) % BUTM_BLOCKSIZE) ERROR_EXIT(BUTM_POSITION);
1647 info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1651 /* Seek to the desired position */
1652 code = SeekFile(info, (position - info->position) + 1);
1653 if (code) ERROR_EXIT(code);
1656 * Search until the filemark is an EODump filemark.
1657 * Skip over volumes only.
1661 code = ReadTapeBlock(info, tapeBlock, &blockType);
1662 if (code) ERROR_EXIT(code);
1664 if (blockType == BLOCK_EOD) break;
1665 if (blockType != BLOCK_FMBEGIN) ERROR_EXIT(BUTM_BADBLOCK);
1667 code = SeekFile(info, 1); /* Step forward to next volume */
1668 if (code) ERROR_EXIT(code);
1677 static afs_int32 file_SetSize (info, size)
1678 struct butm_tapeInfo *info;
1681 if (info->debug) printf("butm: Set size of tape\n");
1684 if (size <= 0 ) info->tapeSize = config.tapeSize;
1685 else info->tapeSize = size;
1689 static afs_int32 file_GetSize (info, size)
1690 struct butm_tapeInfo *info;
1693 if (info->debug) printf("butm: Get size of tape\n");
1696 *size = info->tapeSize;
1700 /* =====================================================================
1701 * Startup/configuration routines.
1702 * ===================================================================== */
1704 static afs_int32 file_Configure (file)
1705 struct tapeConfig *file;
1709 com_err(whoami, BUTM_BADCONFIG, "device not specified");
1710 return BUTM_BADCONFIG;
1713 config.tapeSize = file->capacity;
1714 config.fileMarkSize = file->fileMarkSize;
1715 config.portOffset = file->portOffset;
1716 strcpy(config.tapedir, file->device);
1718 /* Tape must be large enough to at least fit a label */
1719 if (config.tapeSize <= 0)
1721 com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes", config.tapeSize);
1722 return BUTM_BADCONFIG;
1725 if (strlen(config.tapedir) == 0)
1727 com_err (whoami, BUTM_BADCONFIG, "no tape device specified");
1728 return BUTM_BADCONFIG;
1735 /* This procedure instantiates a tape module of type file_tm. */
1736 afs_int32 butm_file_Instantiate (info, file)
1737 struct butm_tapeInfo *info;
1738 struct tapeConfig *file;
1740 extern int debugLevel;
1743 if (debugLevel > 98) printf("butm: Instantiate butc\n");
1745 if (!info) ERROR_EXIT(BUTM_BADARGUMENT);
1746 if (info->structVersion != BUTM_MAJORVERSION) ERROR_EXIT(BUTM_OLDINTERFACE);
1748 memset(info, 0, sizeof(struct butm_tapeInfo));
1749 info->structVersion = BUTM_MAJORVERSION;
1750 info->ops.mount = file_Mount;
1751 info->ops.dismount = file_Dismount;
1752 info->ops.create = file_WriteLabel;
1753 info->ops.readLabel = file_ReadLabel;
1754 info->ops.seek = file_Seek;
1755 info->ops.seekEODump = file_SeekEODump;
1756 info->ops.readFileBegin = file_ReadFileBegin;
1757 info->ops.readFileData = file_ReadFileData;
1758 info->ops.readFileEnd = file_ReadFileEnd;
1759 info->ops.writeFileBegin = file_WriteFileBegin;
1760 info->ops.writeFileData = file_WriteFileData;
1761 info->ops.writeFileEnd = file_WriteFileEnd;
1762 info->ops.writeEOT = file_WriteEODump;
1763 info->ops.setSize = file_SetSize;
1764 info->ops.getSize = file_GetSize;
1765 info->debug = ((debugLevel > 98) ? 1 : 0);
1767 code = file_Configure (file);