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>
15 #include <sys/types.h>
19 #include <netinet/in.h>
33 #include <afs/com_err.h>
36 #include "error_macros.h"
37 #include "butm_prototypes.h"
40 typedef off64_t osi_lloff_t;
41 #else /* O_LARGEFILE */
42 #ifdef AFS_HAVE_LLSEEK
43 typedef offset_t osi_lloff_t;
44 #else /* AFS_HAVE_LLSEEK */
45 typedef off_t osi_lloff_t;
46 #endif /* AFS_HAVE_LLSEEK */
47 #endif /* O_LARGEFILE */
51 #define FILE_MAGIC 1000000007 /* s/w file mark */
52 #define FILE_BEGIN 0 /* byte field in file mark */
53 #define FILE_FMEND 1 /* byte field in file mark */
54 #define FILE_EOD -1 /* byte field in file mark */
55 #define TAPE_MAGIC 1100000009 /* tape label block */
56 #define BLOCK_MAGIC 1100000005 /* file data block */
57 #ifdef AFS_PTHREAD_ENV
59 #define SLEEP(s) sleep(s)
61 #define POLL() IOMGR_Poll()
62 #define SLEEP(s) IOMGR_Sleep(s)
67 * 1) filemarks and block marks have the magic,bytes fields reversed. This
68 * is undoubtedly a bug. Also note that the two structures have
69 * inconsistent types, overlaying int and afs_int32.
70 * 2) When a volume is dumped, if the volume is locked, the dump will produce
71 * an anomalous tape format of the form:
75 * The design of the original butm code means that this cannot be
76 * handled correctly. The code is modified so that ReadFileData
77 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
80 /* data organization on tape:
81 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
82 * blockMark contains a magic number and counts of real data bytes
83 * written out in the block.
85 * each file is preceeded by a fileMark, which acts as the file
86 * delimiter. A file consists of contigous data blocks. TM does
87 * understand or interrpet the data in data blocks.
89 * The tape begins with a tape label and ends with EOF file markers
90 * in succession (2 or 4 of them ).
94 struct fileMark { /* in network byte order */
101 struct butm_tapeLabel label;
105 usd_handle_t fid; /* file id of simulated tape */
106 afs_int32 mountId; /* current mountId */
107 afs_int32 reading; /* read file operation in progress */
108 afs_int32 writing; /* write file operation in progress */
111 static struct configuration {
112 char tapedir[64]; /* directory to create "tapes" */
113 afs_int32 mountId; /* for detecting simultaneous mounts */
114 afs_uint32 tapeSize; /* size of simulated tapes */
115 afs_uint32 fileMarkSize; /* size of file mark, bytes */
116 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
119 static char *whoami = "file_tm";
120 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
122 #define BLOCK_LABEL 0 /* read/write a tape label */
123 #define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
124 #define BLOCK_DATA 2 /* read/write a data block */
125 #define BLOCK_FMEND 3 /* read/write an end FileMark */
126 #define BLOCK_EOD 4 /* read/write an EOD FileMark */
127 #define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
128 #define BLOCK_UNKNOWN 6 /* tape block is unknwon (error) */
133 #define READS (((struct progress *)(info->tmRock))->reading)
134 #define WRITES (((struct progress *)(info->tmRock))->writing)
136 /* ----------------------------------------------------------------------
137 * These routines use the usd library to perform tape operations.
138 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
139 * PrepareAccess(nt), ShutdownAccess(nt)
141 * Return Values: USD functions return 0 if successful or errno if failed.
143 /* ------------------ USD Interface Functions Begin ------------------------ */
146 * On Unix, fork a child process to perform an IOCTL call. This avoids
147 * blocking the entire process during a tape operation
151 /* Unix version of function */
154 ForkIoctl(usd_handle_t fd, int op, int count)
156 int rc; /* return code from system calls */
157 int i; /* loop index */
158 int pid; /* process ID of child process */
159 int status; /* exit status of child process */
160 int ioctl_rc; /* return code from ioctl call */
161 int pipefd[2]; /* pipe for child return status */
162 int forkflag; /* flag set when ready to fork */
163 usd_tapeop_t tapeop; /* tape operation specification */
166 #ifdef AFS_PTHREAD_ENV
167 forkflag = 0; /* No need to fork if using pthreads */
172 /* initialize tape command structure */
174 tapeop.tp_count = count;
176 /* create pipe for getting return code from child */
180 printf("butm: Can't open pipe for IOCTL process. Error %d\n",
191 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
197 if (!forkflag) { /* problem starting child process */
198 /* do the ioctl anyway, it will probably work */
199 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
200 } else if (pid == 0) { /* child process */
201 /* close all unneccessary file descriptors */
202 /* note: as painful as it is, we have to reach under the covers of
203 * the usd package to implement this functionality.
205 unixfd = (intptr_t)(fd->handle);
207 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
208 if (i != unixfd && i != pipefd[1]) {
213 /* do the ioctl call */
214 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
216 /* send the return code back to the parent */
217 write(pipefd[1], &ioctl_rc, sizeof(int));
220 } else { /* parent process */
224 /* read the result from the child process */
225 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
226 if (rc != sizeof(int)) {
227 /* tape is now in unknown state */
228 printf("butm: Can't determine IOCTL child status. Error %d\n",
235 /* get the completion status from the child process */
236 rc = waitpid(pid, &status, 0);
237 while (rc < 0 && errno == EINTR) {
238 rc = waitpid(pid, &status, 0);
241 printf("butm: Can't determine IOCTL child status. Error %d\n",
243 } else if (status != 0) {
245 ("butm: Unexpected IOCTL process status 0x%04x . Error %d\n",
254 /* NT version of function */
257 ForkIoctl(usd_handle_t fd, int op, int count)
261 /* Issue requested tape control */
263 tapeop.tp_count = count;
265 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
267 #endif /* !AFS_NT40_ENV */
271 * On Unix, fork a child process to attempt to open the drive. We want to make
272 * certain there is tape in the drive before trying to open the device
273 * in the main process
277 /* Unix version of function. */
280 ForkOpen(char *device)
282 int rc; /* return code from system calls */
283 int i; /* loop index */
284 int pid; /* process ID of child process */
285 int status; /* exit status of child process */
286 int open_rc; /* return code from open */
287 int pipefd[2]; /* pipe for child return status */
288 int forkflag; /* flag set when ready to fork */
289 usd_handle_t fd; /* handle returned from open */
291 #ifdef AFS_PTHREAD_ENV
292 forkflag = 0; /* No need to fork if using pthreads */
297 /* create pipe for getting return code from child */
301 printf("butm: Cannot create pipe for OPEN process. Error %d\n",
312 printf("butm: Cannot create child process for OPEN. Error %d\n",
318 if (!forkflag) { /* problem starting child process */
320 *return success, the caller will discover any problems
321 * when it opens the device.
324 } else if (pid == 0) { /* child process */
325 /* close all unneccessary file descriptors */
326 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
327 if (i != pipefd[1]) {
333 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
339 /* send the return code back to the parent */
340 write(pipefd[1], &open_rc, sizeof(open_rc));
343 } else { /* parent process */
348 /* read the result from the child process */
349 rc = read(pipefd[0], &open_rc, sizeof(open_rc));
350 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) {
361 rc = waitpid(pid, &status, 0);
364 printf("butm: Cannot get status of OPEN process. Error %d\n",
366 } else if (status != 0) {
368 ("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n",
377 /* NT version of function. */
380 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 */
395 ForkClose(usd_handle_t fd)
397 int rc; /* return code from system calls */
398 int i; /* loop index */
399 int pid; /* process ID of child process */
400 int status; /* exit status of child process */
401 int close_rc, parent_close_rc; /* return codes from close */
402 int pipefd[2]; /* pipe for child return status */
403 int ctlpipe[2]; /* pipe for message to child */
404 int forkflag; /* flag set when ready to fork */
407 #ifdef AFS_PTHREAD_ENV
408 forkflag = 0; /* No need to fork if using pthreads */
413 /* create pipe for getting return code from child */
417 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
423 /* create pipe for notifying child when to close */
429 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
442 printf("butm: Cannot create CLOSE child process. Error %d\n",
448 if (!forkflag) { /* problem starting child process */
449 close_rc = USD_CLOSE(fd);
450 parent_close_rc = close_rc;
451 } else if (pid == 0) { /* child process */
452 /* close all unneccessary file descriptors */
453 /* note: as painful as it is, we have to reach under the covers of
454 * the usd package to implement this functionality.
456 unixfd = (intptr_t)(fd->handle);
458 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
459 if (i != unixfd && i != ctlpipe[0] && i != pipefd[1]) {
464 /* the parent writes the control pipe after it closes the device */
465 read(ctlpipe[0], &close_rc, sizeof(int));
469 close_rc = USD_CLOSE(fd);
471 /* send the return code back to the parent */
472 write(pipefd[1], &close_rc, sizeof(int));
475 } else { /* parent process */
482 * close the device, this should have no effect as long as the
483 * child has not closed
486 parent_close_rc = USD_CLOSE(fd);
488 /* notify the child to do its close */
489 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
490 if (rc != sizeof(int)) {
491 printf("butm: Error communicating with CLOSE process. Error %d\n",
496 /* read the result from the child process */
497 rc = read(pipefd[0], &close_rc, sizeof(int));
498 if (rc != sizeof(int)) {
499 /* logging is enough, since we wrote a file mark the */
500 /* return code from the close doesn't really matter */
501 printf("butm: No response from CLOSE process. Error %d\n",
508 /* get the completion status from the child process */
509 rc = waitpid(pid, &status, 0);
510 while (rc < 0 && errno == EINTR) {
511 rc = waitpid(pid, &status, 0);
514 printf("butm: Cannot get status of CLOSE process. Error %d\n",
516 } else if (status != 0) {
518 ("butm: Unexpected exit status 0x%04x from CLOSE process. Error %d\n",
522 /* if either process received an error, then return an error */
523 if (parent_close_rc < 0) {
524 close_rc = parent_close_rc;
532 /* NT version of function */
535 ForkClose(usd_handle_t fd)
537 return (USD_CLOSE(fd));
539 #endif /* AFS_NT40_ENV */
541 /* Forward space file */
543 ForwardSpace(usd_handle_t fid, int count)
550 return (ForkIoctl(fid, USDTAPE_FSF, count));
554 /* Backward space file */
556 BackwardSpace(usd_handle_t fid, int count)
563 return (ForkIoctl(fid, USDTAPE_BSF, count));
567 /* write end of file mark */
569 WriteEOF(usd_handle_t fid, int count)
576 return (ForkIoctl(fid, USDTAPE_WEOF, count));
582 Rewind(usd_handle_t fid)
585 afs_hyper_t startOff, stopOff;
588 return (USD_SEEK(fid, startOff, SEEK_SET, &stopOff));
590 return (ForkIoctl(fid, USDTAPE_REW, 0));
594 /* prepare tape drive for access */
596 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 */
611 ShutdownAccess(usd_handle_t fid)
615 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
617 #endif /* AFS_NT40_ENV */
621 /* -------------------- USD Interface Functions End ----------------------- */
623 /* =====================================================================
625 * ===================================================================== */
628 * add the supplied no. of bytes to the byte count of information placed
631 * dataSize - bytes used on the tape
635 incSize(struct butm_tapeInfo *info, afs_uint32 dataSize)
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
655 incPosition(struct butm_tapeInfo *info, usd_handle_t fid, afs_uint32 dataSize)
657 /* Add this to the amount of data written to the tape */
658 incSize(info, dataSize);
660 info->posCount += dataSize;
662 if (info->posCount >= 2147467264) { /* 2GB - 16K */
664 #if (defined(AFS_SUN_ENV) || defined(AFS_LINUX24_ENV))
668 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
675 * This accounts for tape drives with a block size different from variable or 16K
676 * blocks and only reads that block size.
678 afs_int32 TapeBlockSize;
680 readData(usd_handle_t fid, char *data, afs_uint32 totalSize, afs_int32 *errorP)
682 afs_int32 toread; /* Number of bytes to read */
683 afs_uint32 rSize; /* Total bytes read so far */
684 afs_uint32 tSize; /* Temporary size */
685 afs_int32 rc; /* return code */
687 toread = totalSize; /* First, try to read all the data */
689 rc = USD_READ(fid, &data[0], toread, &rSize);
694 if (rSize == 0) /* reached EOF */
697 if (rSize != TapeBlockSize) { /* Tape block size has changed */
698 TapeBlockSize = rSize;
699 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
702 /* Read the rest of the data in */
703 while (rSize < totalSize) {
705 ((totalSize - rSize) <
706 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
707 rc = USD_READ(fid, &data[rSize], toread, &tSize);
716 if (rSize > totalSize)
717 printf("readData - Read > 16K data block - continuing.\n");
723 SeekFile(struct butm_tapeInfo *info, int count)
731 p = (struct progress *)info->tmRock;
733 if (isafile) { /* no reason for seeking through a file */
734 p->reading = p->writing = 0;
741 error = ForwardSpace(p->fid, count);
743 error = BackwardSpace(p->fid, -count);
748 info->status |= BUTM_STATUS_SEEKERROR;
749 ERROR_EXIT(BUTM_IOCTL);
752 info->position += count;
753 incSize(info, (count * config.fileMarkSize));
754 p = (struct progress *)info->tmRock;
755 p->reading = p->writing = 0;
763 /* Step to the next filemark if we are not at one already */
765 NextFile(struct butm_tapeInfo *info)
769 if (!READS && !WRITES)
772 code = SeekFile(info, 1);
777 WriteTapeBlock(struct butm_tapeInfo *info,
778 char *buffer, /* assumed to be 16384 bytes with data in it */
779 afs_int32 length, /* amount data in buffer */
782 afs_int32 code = 0, rc = 0;
784 struct tapeLabel *label;
785 struct fileMark *fmark;
786 struct blockMark *bmark;
790 p = (struct progress *)info->tmRock;
792 if (blockType == BLOCK_DATA) { /* Data Block */
795 bmark = (struct blockMark *)buffer;
796 memset(bmark, 0, sizeof(struct blockMark));
797 bmark->magic = htonl(BLOCK_MAGIC);
798 bmark->count = htonl(length);
799 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
800 fmark = (struct fileMark *)buffer;
801 fmark->magic = htonl(FILE_MAGIC);
802 fmark->nBytes = htonl(FILE_BEGIN);
803 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
804 fmark = (struct fileMark *)buffer;
805 fmark->magic = htonl(FILE_MAGIC);
806 fmark->nBytes = htonl(FILE_FMEND);
807 } else if (blockType == BLOCK_LABEL) { /* Label */
808 label = (struct tapeLabel *)buffer;
809 label->magic = htonl(TAPE_MAGIC);
810 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
811 fmark = (struct fileMark *)buffer;
812 fmark->magic = htonl(FILE_MAGIC);
813 fmark->nBytes = htonl(FILE_EOD);
816 /* Write the tape block */
817 /* -------------------- */
818 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
819 if ((rc == 0) && (wsize > 0)) {
820 incPosition(info, p->fid, wsize); /* record whats written */
824 if (wsize != BUTM_BLOCKSIZE) {
825 info->status |= BUTM_STATUS_WRITEERROR;
830 ERROR_EXIT(BUTM_EOT);
835 /* Write trailing EOF marker for some block types */
836 /* ---------------------------------------------- */
837 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
838 || (blockType == BLOCK_EOD)) {
841 error = WriteEOF(p->fid, 1);
844 info->status |= BUTM_STATUS_WRITEERROR;
845 ERROR_EXIT(BUTM_IOCTL);
848 incSize(info, config.fileMarkSize);
861 ReadTapeBlock(struct butm_tapeInfo *info,
862 char *buffer, /* assumed to be 16384 bytes */
863 afs_int32 *blockType)
866 afs_int32 rsize, fmtype;
867 struct tapeLabel *label;
868 struct fileMark *fmark;
869 struct blockMark *bmark;
872 *blockType = BLOCK_UNKNOWN;
874 p = (struct progress *)info->tmRock;
876 memset(buffer, 0, BUTM_BLOCKSIZE);
877 label = (struct tapeLabel *)buffer;
878 fmark = (struct fileMark *)buffer;
879 bmark = (struct blockMark *)buffer;
881 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
883 incPosition(info, p->fid, rsize);
887 if (rsize == 0) { /* Read a HW EOF Marker? OK */
888 *blockType = BLOCK_EOF;
889 incSize(info, config.fileMarkSize); /* Size of filemark */
891 info->position++; /* bump position */
892 p->reading = 0; /* No reads since EOF */
895 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
896 info->status |= BUTM_STATUS_READERROR;
897 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
900 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
901 *blockType = BLOCK_DATA;
904 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
905 fmtype = ntohl(fmark->nBytes);
907 if (fmtype == FILE_BEGIN) { /* filemark begin */
908 *blockType = BLOCK_FMBEGIN;
909 } else if (fmtype == FILE_FMEND) { /* filemark end */
910 *blockType = BLOCK_FMEND;
911 code = SeekFile(info, 1);
912 } else if (fmtype == FILE_EOD) { /* EOD mark */
913 *blockType = BLOCK_EOD;
914 info->status |= BUTM_STATUS_EOD;
915 code = SeekFile(info, 1);
919 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
920 *blockType = BLOCK_LABEL;
921 code = SeekFile(info, 1);
932 * check version numbers and permissions in the info structure
936 check(struct butm_tapeInfo *info,
937 int write) /* write operation requested */
942 return (BUTM_BADARGUMENT);
944 /* Check version number in info structure */
945 if (info->structVersion != BUTM_MAJORVERSION)
946 return BUTM_OLDINTERFACE;
948 /* Check if a tape is mounted */
949 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
952 /* If writing check if there is write access */
953 if (write && (info->flags & BUTM_FLAGS_READONLY))
954 return BUTM_READONLY;
960 rewindFile(struct butm_tapeInfo *info)
966 p = (struct progress *)info->tmRock;
970 error = Rewind(p->fid);
975 info->status |= BUTM_STATUS_SEEKERROR;
976 ERROR_EXIT(BUTM_IOCTL);
979 info->position = (isafile ? 0 : 1);
980 info->kBytes = info->nBytes = 0;
981 info->nFiles = info->nRecords = 0;
982 p->reading = p->writing = 0;
990 /* =====================================================================
992 * ===================================================================== */
995 file_Mount(struct butm_tapeInfo *info, char *tape)
1001 afs_int32 code = 0, error = 0, rc = 0;
1004 printf("butm: Mount tape drive\n");
1010 ERROR_EXIT(BUTM_BADARGUMENT);
1011 if (info->structVersion != BUTM_MAJORVERSION)
1012 ERROR_EXIT(BUTM_OLDINTERFACE);
1014 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1015 if (strlen(tape) >= sizeof(info->name))
1016 ERROR_EXIT(BUTM_BADARGUMENT);
1018 strcpy(info->name, tape);
1020 strcpy(filename, config.tapedir); /* the name of the tape device */
1021 info->position = (isafile ? 0 : 1);
1022 info->kBytes = info->nBytes = 0;
1023 info->nRecords = info->nFiles = 0;
1024 info->recordSize = 0;
1025 info->tapeSize = config.tapeSize;
1026 info->coefBytes = 1;
1027 info->coefRecords = 0;
1028 info->coefFiles = sizeof(struct fileMark);
1029 info->simultaneousTapes = 1;
1032 info->flags = BUTM_FLAGS_SEQUENTIAL;
1036 xflags |= USD_OPEN_CREATE;
1039 * try to open in a child process first so nothing will
1040 * time out should the process block because the device
1044 if (ForkOpen(filename)) {
1045 ERROR_EXIT(BUTM_MOUNTFAIL);
1049 /* Now go ahead and open the tape drive for real */
1050 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1052 if (rc != 0) { /* try for lesser access */
1053 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1057 ERROR_EXIT(BUTM_MOUNTFAIL);
1059 info->flags |= BUTM_FLAGS_READONLY;
1062 (void)PrepareAccess(fid); /* for NT */
1064 p = (struct progress *)malloc(sizeof(*p));
1065 info->tmRock = (char *)p;
1067 p->mountId = config.mountId = time(0);
1068 p->reading = p->writing = 0;
1070 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1074 info->error = error;
1079 file_Dismount(struct butm_tapeInfo *info)
1082 afs_int32 code = 0, error = 0;
1085 printf("butm: Unmount tape drive\n");
1090 code = check(info, READ_OP);
1094 p = (struct progress *)info->tmRock;
1096 (void)ShutdownAccess(p->fid); /* for NT */
1098 /* close the device */
1099 if ((error = ForkClose(p->fid))) {
1100 printf("butm: Tape close failed. Error %d\n", errno);
1106 code = BUTM_DISMOUNTFAIL;
1107 info->status |= BUTM_STATUS_TAPEERROR;
1111 info->tmRock = 0; /* mark it as closed - even if error on close */
1117 info->error = error;
1122 * write the header on a tape
1124 * info - handle on tape unit
1125 * label - label information. This label is not copied onto the tape.
1126 * If supplied, various fields are copied from this label to
1127 * the actual tape label written on the tape.
1131 file_WriteLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1136 struct tapeLabel *tlabel;
1138 afs_hyper_t off; /* offset */
1141 printf("butm: Write tape label\n");
1146 code = check(info, WRITE_OP);
1150 ERROR_EXIT(BUTM_BADARGUMENT);
1151 if (label->structVersion != CUR_TAPE_VERSION)
1152 ERROR_EXIT(BUTM_OLDINTERFACE);
1154 if (rewind) { /* Not appending, so rewind */
1155 code = rewindFile(info);
1160 p = (struct progress *)info->tmRock;
1162 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1164 ERROR_EXIT(BUTM_POSITION);
1167 if (READS || WRITES)
1168 ERROR_EXIT(BUTM_BADOP);
1171 /* Copy the label into the tape block
1172 * ---------------------------------- */
1173 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1175 if (!label->creationTime)
1176 label->creationTime = time(0);
1178 tlabel = (struct tapeLabel *)tapeBlock;
1179 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1180 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1181 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1182 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1183 tlabel->label.size = htonl(tlabel->label.size);
1184 tlabel->label.useCount = htonl(tlabel->label.useCount);
1185 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1188 * write the tape label - For appends, the write may need to skip
1189 * over 1 or 2 EOF marks that were written when tape was closed after
1190 * the last dump. Plus, some AIX tape drives require we try forwarding
1191 * over the last EOF and take an error before we can write the new label.
1192 * ---------------------------------------------------------------------- */
1193 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1194 if (!isafile && !rewind && (code == BUTM_IO))
1195 do { /* do if write failed */
1196 fcode = SeekFile(info, 1); /* skip over the EOF */
1198 break; /* leave if error */
1201 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1202 if (code != BUTM_IO)
1203 break; /* continue if write failed */
1205 fcode = SeekFile(info, 1); /* skip over the EOF */
1206 if (fcode) { /* retry 1 write if couldn't skip */
1208 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1214 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1215 if (code != BUTM_IO)
1216 break; /* continue if write failed */
1218 fcode = SeekFile(info, 1); /* skip over the EOF */
1219 if (fcode) { /* retry 1 write if couldn't skip */
1221 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1228 /* clear the write error status a failed WriteTapeBlock may have produced */
1230 info->status &= ~BUTM_STATUS_WRITEERROR;
1237 file_ReadLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1240 struct tapeLabel *tlabel;
1242 afs_int32 blockType;
1245 printf("butm: Read tape label\n");
1250 code = check(info, READ_OP);
1253 if (READS || WRITES)
1254 ERROR_EXIT(BUTM_BADOP);
1257 code = rewindFile(info);
1259 ERROR_EXIT(code); /* status is set so return */
1263 * When appended labels were written, either 1 or 2 EOF marks may
1264 * have had to be skipped. When reading a label, these EOF marks
1265 * must also be skipped. When an EOF is read, 0 bytes are returned
1266 * (refer to the write calls in file_WriteLabel routine).
1267 * ---------------------------------------------------------------- */
1268 code = ReadTapeBlock(info, tapeBlock, &blockType);
1269 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1271 code = ReadTapeBlock(info, tapeBlock, &blockType);
1272 if (blockType != BLOCK_EOF)
1273 break; /* didn't read an EOF */
1275 code = ReadTapeBlock(info, tapeBlock, &blockType);
1278 if (blockType == BLOCK_UNKNOWN)
1279 ERROR_EXIT(BUTM_NOLABEL);
1282 if (blockType != BLOCK_LABEL)
1283 ERROR_EXIT(BUTM_BADBLOCK);
1288 tlabel = (struct tapeLabel *)tapeBlock;
1289 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1290 label->structVersion = ntohl(label->structVersion);
1291 label->creationTime = ntohl(label->creationTime);
1292 label->expirationDate = ntohl(label->expirationDate);
1293 label->size = ntohl(label->size);
1294 label->dumpid = ntohl(label->dumpid);
1295 label->useCount = ntohl(label->useCount);
1297 info->tapeSize = label->size; /* use size from label */
1305 file_WriteFileBegin(struct butm_tapeInfo *info)
1310 printf("butm: Write filemark begin\n");
1314 code = check(info, WRITE_OP);
1318 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1329 file_ReadFileBegin(struct butm_tapeInfo *info)
1332 afs_int32 blockType;
1335 printf("butm: Read filemark begin\n");
1340 code = check(info, READ_OP);
1343 if (READS || WRITES)
1344 ERROR_EXIT(BUTM_BADOP);
1346 code = ReadTapeBlock(info, tapeBlock, &blockType);
1350 if (blockType != BLOCK_FMBEGIN) {
1351 if (blockType == BLOCK_EOD)
1352 ERROR_EXIT(BUTM_EOD); /* EODump label */
1353 if (blockType == BLOCK_LABEL)
1354 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1355 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1362 /* Writes data out in block sizes of 16KB. Does destroy the data.
1363 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1366 file_WriteFileData(struct butm_tapeInfo *info, char *data, afs_int32 blocks, afs_int32 len)
1371 char *bstart; /* Where block starts for a 16K block */
1372 char *dstart; /* Where data starts for a 16K block */
1375 printf("butm: Write tape data - %u bytes\n", len);
1380 code = check(info, WRITE_OP);
1383 if (!data || (len < 0))
1384 ERROR_EXIT(BUTM_BADARGUMENT);
1385 if (READS || !WRITES)
1386 ERROR_EXIT(BUTM_BADOP);
1388 b = 0; /* start at block 0 */
1390 dstart = &data[b * BUTM_BLKSIZE];
1391 bstart = dstart - sizeof(struct blockMark);
1393 if (len < BUTM_BLKSIZE) {
1394 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1397 length = BUTM_BLKSIZE;
1400 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1404 /* If there are more blocks, step to next block */
1405 /* Otherwise, copy the data to beginning of last block */
1407 if (b < (blocks - 1))
1410 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1417 /* file_ReadFileData
1418 * Read a data block from tape.
1420 * info - tape info structure, c.f. fid
1421 * data - ptr to buffer for data
1422 * len - size of data buffer
1424 * nBytes - no. of data bytes read.
1428 file_ReadFileData(struct butm_tapeInfo *info, char *data, int len, int *nBytes)
1430 struct blockMark *bmark;
1432 afs_int32 blockType;
1435 printf("butm: Read tape data - %u bytes\n", len);
1441 ERROR_EXIT(BUTM_BADARGUMENT);
1444 code = check(info, READ_OP);
1447 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1448 ERROR_EXIT(BUTM_BADARGUMENT);
1449 if (!READS || WRITES)
1450 ERROR_EXIT(BUTM_BADOP);
1452 data -= sizeof(struct blockMark);
1453 code = ReadTapeBlock(info, data, &blockType);
1457 if (blockType != BLOCK_DATA) {
1458 if (blockType == BLOCK_EOF)
1459 ERROR_EXIT(BUTM_EOF);
1460 if (blockType == BLOCK_FMEND)
1461 ERROR_EXIT(BUTM_ENDVOLUME);
1462 ERROR_EXIT(BUTM_BADBLOCK);
1465 bmark = (struct blockMark *)data;
1466 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1473 file_WriteFileEnd(struct butm_tapeInfo *info)
1478 printf("butm: Write filemark end\n");
1483 code = check(info, WRITE_OP);
1486 if (READS || !WRITES)
1487 ERROR_EXIT(BUTM_BADOP);
1489 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1495 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1496 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1497 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1498 * missing with some 3.1 dumps).
1501 file_ReadFileEnd(struct butm_tapeInfo *info)
1504 afs_int32 blockType;
1507 printf("butm: Read filemark end\n");
1512 code = check(info, READ_OP);
1515 if (!READS || WRITES)
1516 ERROR_EXIT(BUTM_BADOP);
1518 info->status &= ~BUTM_STATUS_EOF;
1520 code = ReadTapeBlock(info, tapeBlock, &blockType);
1524 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1525 ERROR_EXIT(BUTM_BADBLOCK);
1532 * Write the end-of-dump marker.
1535 file_WriteEODump(struct butm_tapeInfo *info)
1540 printf("butm: Write filemark EOD\n");
1545 code = check(info, WRITE_OP);
1548 if (READS || WRITES)
1549 ERROR_EXIT(BUTM_BADOP);
1551 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1555 info->status |= BUTM_STATUS_EOD;
1562 file_Seek(struct butm_tapeInfo *info, afs_int32 position)
1569 afs_hyper_t startOff, stopOff; /* for normal file(non-tape) seeks */
1572 printf("butm: Seek to the tape position %d\n", position);
1576 code = check(info, READ_OP);
1581 p = (struct progress *)info->tmRock;
1582 posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1584 /* Not really necessary to do it this way, should be fixed */
1587 d = (posit & 0xffffffff);
1592 hset64(startOff, c, d);
1594 w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1597 if (hcmp(startOff, stopOff) != 0)
1598 ERROR_EXIT(BUTM_POSITION);
1600 p->reading = p->writing = 0;
1601 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));
1617 * Seek to the EODump (end-of-dump) after the given position. This is
1618 * the position after the EOF filemark immediately after the EODump mark.
1619 * This is for tapes of version 4 or greater.
1622 file_SeekEODump(struct butm_tapeInfo *info, afs_int32 position)
1625 afs_int32 blockType;
1628 afs_hyper_t startOff, stopOff; /* file seek offsets */
1631 printf("butm: Seek to end-of-dump\n");
1634 code = check(info, READ_OP);
1637 if (READS || WRITES)
1638 ERROR_EXIT(BUTM_BADOP);
1641 p = (struct progress *)info->tmRock;
1642 hset64(startOff, 0, 0);
1643 w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1646 ERROR_EXIT(BUTM_POSITION);
1649 if (hgetlo(stopOff) % BUTM_BLOCKSIZE)
1650 ERROR_EXIT(BUTM_POSITION);
1651 info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1653 /* Seek to the desired position */
1654 code = SeekFile(info, (position - info->position) + 1);
1659 * Search until the filemark is an EODump filemark.
1660 * Skip over volumes only.
1663 code = ReadTapeBlock(info, tapeBlock, &blockType);
1667 if (blockType == BLOCK_EOD)
1669 if (blockType != BLOCK_FMBEGIN)
1670 ERROR_EXIT(BUTM_BADBLOCK);
1672 code = SeekFile(info, 1); /* Step forward to next volume */
1684 file_SetSize(struct butm_tapeInfo *info, afs_uint32 size)
1687 printf("butm: Set size of tape\n");
1691 info->tapeSize = config.tapeSize;
1693 info->tapeSize = size;
1698 file_GetSize(struct butm_tapeInfo *info, afs_uint32 *size)
1701 printf("butm: Get size of tape\n");
1704 *size = info->tapeSize;
1708 /* =====================================================================
1709 * Startup/configuration routines.
1710 * ===================================================================== */
1713 file_Configure(struct tapeConfig *file)
1716 afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1717 return BUTM_BADCONFIG;
1720 config.tapeSize = file->capacity;
1721 config.fileMarkSize = file->fileMarkSize;
1722 config.portOffset = file->portOffset;
1723 strcpy(config.tapedir, file->device);
1725 /* Tape must be large enough to at least fit a label */
1726 if (config.tapeSize <= 0) {
1727 afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1729 return BUTM_BADCONFIG;
1732 if (strlen(config.tapedir) == 0) {
1733 afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1734 return BUTM_BADCONFIG;
1741 /* This procedure instantiates a tape module of type file_tm. */
1743 butm_file_Instantiate(struct butm_tapeInfo *info, struct tapeConfig *file)
1745 extern int debugLevel;
1748 if (debugLevel > 98)
1749 printf("butm: Instantiate butc\n");
1752 ERROR_EXIT(BUTM_BADARGUMENT);
1753 if (info->structVersion != BUTM_MAJORVERSION)
1754 ERROR_EXIT(BUTM_OLDINTERFACE);
1756 memset(info, 0, sizeof(struct butm_tapeInfo));
1757 info->structVersion = BUTM_MAJORVERSION;
1758 info->ops.mount = file_Mount;
1759 info->ops.dismount = file_Dismount;
1760 info->ops.create = file_WriteLabel;
1761 info->ops.readLabel = file_ReadLabel;
1762 info->ops.seek = file_Seek;
1763 info->ops.seekEODump = file_SeekEODump;
1764 info->ops.readFileBegin = file_ReadFileBegin;
1765 info->ops.readFileData = file_ReadFileData;
1766 info->ops.readFileEnd = file_ReadFileEnd;
1767 info->ops.writeFileBegin = file_WriteFileBegin;
1768 info->ops.writeFileData = file_WriteFileData;
1769 info->ops.writeFileEnd = file_WriteFileEnd;
1770 info->ops.writeEOT = file_WriteEODump;
1771 info->ops.setSize = file_SetSize;
1772 info->ops.getSize = file_GetSize;
1773 info->debug = ((debugLevel > 98) ? 1 : 0);
1775 code = file_Configure(file);