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>
13 #include <sys/types.h>
17 #include <netinet/in.h>
28 #include <afs/com_err.h>
31 #include "error_macros.h"
32 #include "butm_prototypes.h"
35 typedef off64_t osi_lloff_t;
36 #else /* O_LARGEFILE */
37 #ifdef AFS_HAVE_LLSEEK
38 typedef offset_t osi_lloff_t;
39 #else /* AFS_HAVE_LLSEEK */
40 typedef off_t osi_lloff_t;
41 #endif /* AFS_HAVE_LLSEEK */
42 #endif /* O_LARGEFILE */
46 #define FILE_MAGIC 1000000007 /* s/w file mark */
47 #define FILE_BEGIN 0 /* byte field in file mark */
48 #define FILE_FMEND 1 /* byte field in file mark */
49 #define FILE_EOD -1 /* byte field in file mark */
50 #define TAPE_MAGIC 1100000009 /* tape label block */
51 #define BLOCK_MAGIC 1100000005 /* file data block */
52 #ifdef AFS_PTHREAD_ENV
54 #define SLEEP(s) sleep(s)
56 #define POLL() IOMGR_Poll()
57 #define SLEEP(s) IOMGR_Sleep(s)
62 * 1) filemarks and block marks have the magic,bytes fields reversed. This
63 * is undoubtedly a bug. Also note that the two structures have
64 * inconsistent types, overlaying int and afs_int32.
65 * 2) When a volume is dumped, if the volume is locked, the dump will produce
66 * an anomalous tape format of the form:
70 * The design of the original butm code means that this cannot be
71 * handled correctly. The code is modified so that ReadFileData
72 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
75 /* data organization on tape:
76 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
77 * blockMark contains a magic number and counts of real data bytes
78 * written out in the block.
80 * each file is preceeded by a fileMark, which acts as the file
81 * delimiter. A file consists of contigous data blocks. TM does
82 * understand or interrpet the data in data blocks.
84 * The tape begins with a tape label and ends with EOF file markers
85 * in succession (2 or 4 of them ).
89 struct fileMark { /* in network byte order */
96 struct butm_tapeLabel label;
100 usd_handle_t fid; /* file id of simulated tape */
101 afs_int32 mountId; /* current mountId */
102 afs_int32 reading; /* read file operation in progress */
103 afs_int32 writing; /* write file operation in progress */
106 static struct configuration {
107 char tapedir[64]; /* directory to create "tapes" */
108 afs_int32 mountId; /* for detecting simultaneous mounts */
109 afs_uint32 tapeSize; /* size of simulated tapes */
110 afs_uint32 fileMarkSize; /* size of file mark, bytes */
111 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
114 static char *whoami = "file_tm";
115 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
117 #define BLOCK_LABEL 0 /* read/write a tape label */
118 #define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
119 #define BLOCK_DATA 2 /* read/write a data block */
120 #define BLOCK_FMEND 3 /* read/write an end FileMark */
121 #define BLOCK_EOD 4 /* read/write an EOD FileMark */
122 #define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
123 #define BLOCK_UNKNOWN 6 /* tape block is unknwon (error) */
128 #define READS (((struct progress *)(info->tmRock))->reading)
129 #define WRITES (((struct progress *)(info->tmRock))->writing)
131 /* ----------------------------------------------------------------------
132 * These routines use the usd library to perform tape operations.
133 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
134 * PrepareAccess(nt), ShutdownAccess(nt)
136 * Return Values: USD functions return 0 if successful or errno if failed.
138 /* ------------------ USD Interface Functions Begin ------------------------ */
141 * On Unix, fork a child process to perform an IOCTL call. This avoids
142 * blocking the entire process during a tape operation
146 /* Unix version of function */
149 ForkIoctl(usd_handle_t fd, int op, int count)
151 int rc; /* return code from system calls */
152 int i; /* loop index */
153 int pid; /* process ID of child process */
154 int status; /* exit status of child process */
155 int ioctl_rc; /* return code from ioctl call */
156 int pipefd[2]; /* pipe for child return status */
157 int forkflag; /* flag set when ready to fork */
158 usd_tapeop_t tapeop; /* tape operation specification */
161 #ifdef AFS_PTHREAD_ENV
162 forkflag = 0; /* No need to fork if using pthreads */
167 /* initialize tape command structure */
169 tapeop.tp_count = count;
171 /* create pipe for getting return code from child */
175 printf("butm: Can't open pipe for IOCTL process. Error %d\n",
186 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
192 if (!forkflag) { /* problem starting child process */
193 /* do the ioctl anyway, it will probably work */
194 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
195 } else if (pid == 0) { /* child process */
196 /* close all unneccessary file descriptors */
197 /* note: as painful as it is, we have to reach under the covers of
198 * the usd package to implement this functionality.
200 unixfd = (intptr_t)(fd->handle);
202 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
203 if (i != unixfd && i != pipefd[1]) {
208 /* do the ioctl call */
209 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
211 /* send the return code back to the parent */
212 write(pipefd[1], &ioctl_rc, sizeof(int));
215 } else { /* parent process */
219 /* read the result from the child process */
220 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
221 if (rc != sizeof(int)) {
222 /* tape is now in unknown state */
223 printf("butm: Can't determine IOCTL child status. Error %d\n",
230 /* get the completion status from the child process */
231 rc = waitpid(pid, &status, 0);
232 while (rc < 0 && errno == EINTR) {
233 rc = waitpid(pid, &status, 0);
236 printf("butm: Can't determine IOCTL child status. Error %d\n",
238 } else if (status != 0) {
240 ("butm: Unexpected IOCTL process status 0x%04x . Error %d\n",
249 /* NT version of function */
252 ForkIoctl(usd_handle_t fd, int op, int count)
256 /* Issue requested tape control */
258 tapeop.tp_count = count;
260 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
262 #endif /* !AFS_NT40_ENV */
266 * On Unix, fork a child process to attempt to open the drive. We want to make
267 * certain there is tape in the drive before trying to open the device
268 * in the main process
272 /* Unix version of function. */
275 ForkOpen(char *device)
277 int rc; /* return code from system calls */
278 int i; /* loop index */
279 int pid; /* process ID of child process */
280 int status; /* exit status of child process */
281 int open_rc; /* return code from open */
282 int pipefd[2]; /* pipe for child return status */
283 int forkflag; /* flag set when ready to fork */
284 usd_handle_t fd; /* handle returned from open */
286 #ifdef AFS_PTHREAD_ENV
287 forkflag = 0; /* No need to fork if using pthreads */
292 /* create pipe for getting return code from child */
296 printf("butm: Cannot create pipe for OPEN process. Error %d\n",
307 printf("butm: Cannot create child process for OPEN. Error %d\n",
313 if (!forkflag) { /* problem starting child process */
315 *return success, the caller will discover any problems
316 * when it opens the device.
319 } else if (pid == 0) { /* child process */
320 /* close all unneccessary file descriptors */
321 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
322 if (i != pipefd[1]) {
328 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
334 /* send the return code back to the parent */
335 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)) {
346 /* this is not a problem since we will reopen the device anyway */
347 printf("butm: No response from OPEN process. Error %d\n", errno);
353 /* get the completion status from the child process */
354 rc = waitpid(pid, &status, 0);
355 while (rc < 0 && errno == EINTR) {
356 rc = waitpid(pid, &status, 0);
359 printf("butm: Cannot get status of OPEN process. Error %d\n",
361 } else if (status != 0) {
363 ("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n",
372 /* NT version of function. */
375 ForkOpen(char *device)
379 #endif /* AFS_NT40_ENV */
382 * On Unix, fork a child process to close the drive. If the drive rewinds
383 * on close it could cause the process to block.
387 /* Unix version of function */
390 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 */
412 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
418 /* create pipe for notifying child when to close */
424 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
437 printf("butm: Cannot create CLOSE child process. Error %d\n",
443 if (!forkflag) { /* problem starting child process */
444 close_rc = USD_CLOSE(fd);
445 parent_close_rc = close_rc;
446 } else if (pid == 0) { /* child process */
447 /* close all unneccessary file descriptors */
448 /* note: as painful as it is, we have to reach under the covers of
449 * the usd package to implement this functionality.
451 unixfd = (intptr_t)(fd->handle);
453 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
454 if (i != unixfd && i != ctlpipe[0] && i != pipefd[1]) {
459 /* the parent writes the control pipe after it closes the device */
460 read(ctlpipe[0], &close_rc, sizeof(int));
464 close_rc = USD_CLOSE(fd);
466 /* send the return code back to the parent */
467 write(pipefd[1], &close_rc, sizeof(int));
470 } else { /* parent process */
477 * close the device, this should have no effect as long as the
478 * child has not closed
481 parent_close_rc = USD_CLOSE(fd);
483 /* notify the child to do its close */
484 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
485 if (rc != sizeof(int)) {
486 printf("butm: Error communicating with CLOSE process. Error %d\n",
491 /* read the result from the child process */
492 rc = read(pipefd[0], &close_rc, sizeof(int));
493 if (rc != sizeof(int)) {
494 /* logging is enough, since we wrote a file mark the */
495 /* return code from the close doesn't really matter */
496 printf("butm: No response from CLOSE process. Error %d\n",
503 /* get the completion status from the child process */
504 rc = waitpid(pid, &status, 0);
505 while (rc < 0 && errno == EINTR) {
506 rc = waitpid(pid, &status, 0);
509 printf("butm: Cannot get status of CLOSE process. Error %d\n",
511 } else if (status != 0) {
513 ("butm: Unexpected exit status 0x%04x from CLOSE process. Error %d\n",
517 /* if either process received an error, then return an error */
518 if (parent_close_rc < 0) {
519 close_rc = parent_close_rc;
527 /* NT version of function */
530 ForkClose(usd_handle_t fd)
532 return (USD_CLOSE(fd));
534 #endif /* AFS_NT40_ENV */
536 /* Forward space file */
538 ForwardSpace(usd_handle_t fid, int count)
545 return (ForkIoctl(fid, USDTAPE_FSF, count));
549 /* Backward space file */
551 BackwardSpace(usd_handle_t fid, int count)
558 return (ForkIoctl(fid, USDTAPE_BSF, count));
562 /* write end of file mark */
564 WriteEOF(usd_handle_t fid, int count)
571 return (ForkIoctl(fid, USDTAPE_WEOF, count));
577 Rewind(usd_handle_t fid)
580 afs_hyper_t startOff, stopOff;
583 return (USD_SEEK(fid, startOff, SEEK_SET, &stopOff));
585 return (ForkIoctl(fid, USDTAPE_REW, 0));
589 /* prepare tape drive for access */
591 PrepareAccess(usd_handle_t fid)
596 (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
598 /* NT won't rewind tape when it is opened */
600 #endif /* AFS_NT40_ENV */
604 /* decommission tape drive after all accesses complete */
606 ShutdownAccess(usd_handle_t fid)
610 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
612 #endif /* AFS_NT40_ENV */
616 /* -------------------- USD Interface Functions End ----------------------- */
618 /* =====================================================================
620 * ===================================================================== */
623 * add the supplied no. of bytes to the byte count of information placed
626 * dataSize - bytes used on the tape
630 incSize(struct butm_tapeInfo *info, afs_uint32 dataSize)
632 info->nBytes += dataSize;
633 info->kBytes += (info->nBytes / 1024);
634 info->nBytes %= 1024;
638 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
639 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
641 * add the supplied no. of bytes to the byte count of data placed
642 * on the tape. Also check for reaching 2GB limit and reset the
643 * pointer if necessary. This allows us to use >2GB tapes.
645 * fid - file id for the tape.
646 * dataSize - bytes used on the tape
650 incPosition(struct butm_tapeInfo *info, usd_handle_t fid, afs_uint32 dataSize)
652 /* Add this to the amount of data written to the tape */
653 incSize(info, dataSize);
655 info->posCount += dataSize;
657 if (info->posCount >= 2147467264) { /* 2GB - 16K */
659 #if (defined(AFS_SUN_ENV) || defined(AFS_LINUX24_ENV))
663 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
670 * This accounts for tape drives with a block size different from variable or 16K
671 * blocks and only reads that block size.
673 afs_int32 TapeBlockSize;
675 readData(usd_handle_t fid, char *data, afs_uint32 totalSize, afs_int32 *errorP)
677 afs_int32 toread; /* Number of bytes to read */
678 afs_uint32 rSize; /* Total bytes read so far */
679 afs_uint32 tSize; /* Temporary size */
680 afs_int32 rc; /* return code */
682 toread = totalSize; /* First, try to read all the data */
684 rc = USD_READ(fid, &data[0], toread, &rSize);
689 if (rSize == 0) /* reached EOF */
692 if (rSize != TapeBlockSize) { /* Tape block size has changed */
693 TapeBlockSize = rSize;
694 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
697 /* Read the rest of the data in */
698 while (rSize < totalSize) {
700 ((totalSize - rSize) <
701 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
702 rc = USD_READ(fid, &data[rSize], toread, &tSize);
711 if (rSize > totalSize)
712 printf("readData - Read > 16K data block - continuing.\n");
718 SeekFile(struct butm_tapeInfo *info, int count)
726 p = (struct progress *)info->tmRock;
728 if (isafile) { /* no reason for seeking through a file */
729 p->reading = p->writing = 0;
736 error = ForwardSpace(p->fid, count);
738 error = BackwardSpace(p->fid, -count);
743 info->status |= BUTM_STATUS_SEEKERROR;
744 ERROR_EXIT(BUTM_IOCTL);
747 info->position += count;
748 incSize(info, (count * config.fileMarkSize));
749 p = (struct progress *)info->tmRock;
750 p->reading = p->writing = 0;
758 /* Step to the next filemark if we are not at one already */
760 NextFile(struct butm_tapeInfo *info)
764 if (!READS && !WRITES)
767 code = SeekFile(info, 1);
772 WriteTapeBlock(struct butm_tapeInfo *info,
773 char *buffer, /* assumed to be 16384 bytes with data in it */
774 afs_int32 length, /* amount data in buffer */
777 afs_int32 code = 0, rc = 0;
779 struct tapeLabel *label;
780 struct fileMark *fmark;
781 struct blockMark *bmark;
785 p = (struct progress *)info->tmRock;
787 if (blockType == BLOCK_DATA) { /* Data Block */
790 bmark = (struct blockMark *)buffer;
791 memset(bmark, 0, sizeof(struct blockMark));
792 bmark->magic = htonl(BLOCK_MAGIC);
793 bmark->count = htonl(length);
794 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
795 fmark = (struct fileMark *)buffer;
796 fmark->magic = htonl(FILE_MAGIC);
797 fmark->nBytes = htonl(FILE_BEGIN);
798 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
799 fmark = (struct fileMark *)buffer;
800 fmark->magic = htonl(FILE_MAGIC);
801 fmark->nBytes = htonl(FILE_FMEND);
802 } else if (blockType == BLOCK_LABEL) { /* Label */
803 label = (struct tapeLabel *)buffer;
804 label->magic = htonl(TAPE_MAGIC);
805 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
806 fmark = (struct fileMark *)buffer;
807 fmark->magic = htonl(FILE_MAGIC);
808 fmark->nBytes = htonl(FILE_EOD);
811 /* Write the tape block */
812 /* -------------------- */
813 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
814 if ((rc == 0) && (wsize > 0)) {
815 incPosition(info, p->fid, wsize); /* record whats written */
819 if (wsize != BUTM_BLOCKSIZE) {
820 info->status |= BUTM_STATUS_WRITEERROR;
825 ERROR_EXIT(BUTM_EOT);
830 /* Write trailing EOF marker for some block types */
831 /* ---------------------------------------------- */
832 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
833 || (blockType == BLOCK_EOD)) {
836 error = WriteEOF(p->fid, 1);
839 info->status |= BUTM_STATUS_WRITEERROR;
840 ERROR_EXIT(BUTM_IOCTL);
843 incSize(info, config.fileMarkSize);
856 ReadTapeBlock(struct butm_tapeInfo *info,
857 char *buffer, /* assumed to be 16384 bytes */
858 afs_int32 *blockType)
861 afs_int32 rsize, fmtype;
862 struct tapeLabel *label;
863 struct fileMark *fmark;
864 struct blockMark *bmark;
867 *blockType = BLOCK_UNKNOWN;
869 p = (struct progress *)info->tmRock;
871 memset(buffer, 0, BUTM_BLOCKSIZE);
872 label = (struct tapeLabel *)buffer;
873 fmark = (struct fileMark *)buffer;
874 bmark = (struct blockMark *)buffer;
876 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
878 incPosition(info, p->fid, rsize);
882 if (rsize == 0) { /* Read a HW EOF Marker? OK */
883 *blockType = BLOCK_EOF;
884 incSize(info, config.fileMarkSize); /* Size of filemark */
886 info->position++; /* bump position */
887 p->reading = 0; /* No reads since EOF */
890 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
891 info->status |= BUTM_STATUS_READERROR;
892 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
895 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
896 *blockType = BLOCK_DATA;
899 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
900 fmtype = ntohl(fmark->nBytes);
902 if (fmtype == FILE_BEGIN) { /* filemark begin */
903 *blockType = BLOCK_FMBEGIN;
904 } else if (fmtype == FILE_FMEND) { /* filemark end */
905 *blockType = BLOCK_FMEND;
906 code = SeekFile(info, 1);
907 } else if (fmtype == FILE_EOD) { /* EOD mark */
908 *blockType = BLOCK_EOD;
909 info->status |= BUTM_STATUS_EOD;
910 code = SeekFile(info, 1);
914 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
915 *blockType = BLOCK_LABEL;
916 code = SeekFile(info, 1);
927 * check version numbers and permissions in the info structure
931 check(struct butm_tapeInfo *info,
932 int write) /* write operation requested */
937 return (BUTM_BADARGUMENT);
939 /* Check version number in info structure */
940 if (info->structVersion != BUTM_MAJORVERSION)
941 return BUTM_OLDINTERFACE;
943 /* Check if a tape is mounted */
944 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
947 /* If writing check if there is write access */
948 if (write && (info->flags & BUTM_FLAGS_READONLY))
949 return BUTM_READONLY;
955 rewindFile(struct butm_tapeInfo *info)
961 p = (struct progress *)info->tmRock;
965 error = Rewind(p->fid);
970 info->status |= BUTM_STATUS_SEEKERROR;
971 ERROR_EXIT(BUTM_IOCTL);
974 info->position = (isafile ? 0 : 1);
975 info->kBytes = info->nBytes = 0;
976 info->nFiles = info->nRecords = 0;
977 p->reading = p->writing = 0;
985 /* =====================================================================
987 * ===================================================================== */
990 file_Mount(struct butm_tapeInfo *info, char *tape)
996 afs_int32 code = 0, error = 0, rc = 0;
999 printf("butm: Mount tape drive\n");
1005 ERROR_EXIT(BUTM_BADARGUMENT);
1006 if (info->structVersion != BUTM_MAJORVERSION)
1007 ERROR_EXIT(BUTM_OLDINTERFACE);
1009 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1010 if (strlen(tape) >= sizeof(info->name))
1011 ERROR_EXIT(BUTM_BADARGUMENT);
1013 strcpy(info->name, tape);
1015 strcpy(filename, config.tapedir); /* the name of the tape device */
1016 info->position = (isafile ? 0 : 1);
1017 info->kBytes = info->nBytes = 0;
1018 info->nRecords = info->nFiles = 0;
1019 info->recordSize = 0;
1020 info->tapeSize = config.tapeSize;
1021 info->coefBytes = 1;
1022 info->coefRecords = 0;
1023 info->coefFiles = sizeof(struct fileMark);
1024 info->simultaneousTapes = 1;
1027 info->flags = BUTM_FLAGS_SEQUENTIAL;
1031 xflags |= USD_OPEN_CREATE;
1034 * try to open in a child process first so nothing will
1035 * time out should the process block because the device
1039 if (ForkOpen(filename)) {
1040 ERROR_EXIT(BUTM_MOUNTFAIL);
1044 /* Now go ahead and open the tape drive for real */
1045 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1047 if (rc != 0) { /* try for lesser access */
1048 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1052 ERROR_EXIT(BUTM_MOUNTFAIL);
1054 info->flags |= BUTM_FLAGS_READONLY;
1057 (void)PrepareAccess(fid); /* for NT */
1059 p = (struct progress *)malloc(sizeof(*p));
1060 info->tmRock = (char *)p;
1062 p->mountId = config.mountId = time(0);
1063 p->reading = p->writing = 0;
1065 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1069 info->error = error;
1074 file_Dismount(struct butm_tapeInfo *info)
1077 afs_int32 code = 0, error = 0;
1080 printf("butm: Unmount tape drive\n");
1085 code = check(info, READ_OP);
1089 p = (struct progress *)info->tmRock;
1091 (void)ShutdownAccess(p->fid); /* for NT */
1093 /* close the device */
1094 if ((error = ForkClose(p->fid))) {
1095 printf("butm: Tape close failed. Error %d\n", errno);
1101 code = BUTM_DISMOUNTFAIL;
1102 info->status |= BUTM_STATUS_TAPEERROR;
1106 info->tmRock = 0; /* mark it as closed - even if error on close */
1112 info->error = error;
1117 * write the header on a tape
1119 * info - handle on tape unit
1120 * label - label information. This label is not copied onto the tape.
1121 * If supplied, various fields are copied from this label to
1122 * the actual tape label written on the tape.
1126 file_WriteLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1131 struct tapeLabel *tlabel;
1133 afs_hyper_t off; /* offset */
1136 printf("butm: Write tape label\n");
1141 code = check(info, WRITE_OP);
1145 ERROR_EXIT(BUTM_BADARGUMENT);
1146 if (label->structVersion != CUR_TAPE_VERSION)
1147 ERROR_EXIT(BUTM_OLDINTERFACE);
1149 if (rewind) { /* Not appending, so rewind */
1150 code = rewindFile(info);
1155 p = (struct progress *)info->tmRock;
1157 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1159 ERROR_EXIT(BUTM_POSITION);
1162 if (READS || WRITES)
1163 ERROR_EXIT(BUTM_BADOP);
1166 /* Copy the label into the tape block
1167 * ---------------------------------- */
1168 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1170 if (!label->creationTime)
1171 label->creationTime = time(0);
1173 tlabel = (struct tapeLabel *)tapeBlock;
1174 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1175 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1176 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1177 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1178 tlabel->label.size = htonl(tlabel->label.size);
1179 tlabel->label.useCount = htonl(tlabel->label.useCount);
1180 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1183 * write the tape label - For appends, the write may need to skip
1184 * over 1 or 2 EOF marks that were written when tape was closed after
1185 * the last dump. Plus, some AIX tape drives require we try forwarding
1186 * over the last EOF and take an error before we can write the new label.
1187 * ---------------------------------------------------------------------- */
1188 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1189 if (!isafile && !rewind && (code == BUTM_IO))
1190 do { /* do if write failed */
1191 fcode = SeekFile(info, 1); /* skip over the EOF */
1193 break; /* leave if error */
1196 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1197 if (code != BUTM_IO)
1198 break; /* continue if write failed */
1200 fcode = SeekFile(info, 1); /* skip over the EOF */
1201 if (fcode) { /* retry 1 write if couldn't skip */
1203 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1209 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1210 if (code != BUTM_IO)
1211 break; /* continue if write failed */
1213 fcode = SeekFile(info, 1); /* skip over the EOF */
1214 if (fcode) { /* retry 1 write if couldn't skip */
1216 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1223 /* clear the write error status a failed WriteTapeBlock may have produced */
1225 info->status &= ~BUTM_STATUS_WRITEERROR;
1232 file_ReadLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1235 struct tapeLabel *tlabel;
1237 afs_int32 blockType;
1240 printf("butm: Read tape label\n");
1245 code = check(info, READ_OP);
1248 if (READS || WRITES)
1249 ERROR_EXIT(BUTM_BADOP);
1252 code = rewindFile(info);
1254 ERROR_EXIT(code); /* status is set so return */
1258 * When appended labels were written, either 1 or 2 EOF marks may
1259 * have had to be skipped. When reading a label, these EOF marks
1260 * must also be skipped. When an EOF is read, 0 bytes are returned
1261 * (refer to the write calls in file_WriteLabel routine).
1262 * ---------------------------------------------------------------- */
1263 code = ReadTapeBlock(info, tapeBlock, &blockType);
1264 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1266 code = ReadTapeBlock(info, tapeBlock, &blockType);
1267 if (blockType != BLOCK_EOF)
1268 break; /* didn't read an EOF */
1270 code = ReadTapeBlock(info, tapeBlock, &blockType);
1273 if (blockType == BLOCK_UNKNOWN)
1274 ERROR_EXIT(BUTM_NOLABEL);
1277 if (blockType != BLOCK_LABEL)
1278 ERROR_EXIT(BUTM_BADBLOCK);
1283 tlabel = (struct tapeLabel *)tapeBlock;
1284 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1285 label->structVersion = ntohl(label->structVersion);
1286 label->creationTime = ntohl(label->creationTime);
1287 label->expirationDate = ntohl(label->expirationDate);
1288 label->size = ntohl(label->size);
1289 label->dumpid = ntohl(label->dumpid);
1290 label->useCount = ntohl(label->useCount);
1292 info->tapeSize = label->size; /* use size from label */
1300 file_WriteFileBegin(struct butm_tapeInfo *info)
1305 printf("butm: Write filemark begin\n");
1309 code = check(info, WRITE_OP);
1313 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1324 file_ReadFileBegin(struct butm_tapeInfo *info)
1327 afs_int32 blockType;
1330 printf("butm: Read filemark begin\n");
1335 code = check(info, READ_OP);
1338 if (READS || WRITES)
1339 ERROR_EXIT(BUTM_BADOP);
1341 code = ReadTapeBlock(info, tapeBlock, &blockType);
1345 if (blockType != BLOCK_FMBEGIN) {
1346 if (blockType == BLOCK_EOD)
1347 ERROR_EXIT(BUTM_EOD); /* EODump label */
1348 if (blockType == BLOCK_LABEL)
1349 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1350 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1357 /* Writes data out in block sizes of 16KB. Does destroy the data.
1358 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1361 file_WriteFileData(struct butm_tapeInfo *info, char *data, afs_int32 blocks, afs_int32 len)
1366 char *bstart; /* Where block starts for a 16K block */
1367 char *dstart; /* Where data starts for a 16K block */
1370 printf("butm: Write tape data - %u bytes\n", len);
1375 code = check(info, WRITE_OP);
1378 if (!data || (len < 0))
1379 ERROR_EXIT(BUTM_BADARGUMENT);
1380 if (READS || !WRITES)
1381 ERROR_EXIT(BUTM_BADOP);
1383 b = 0; /* start at block 0 */
1385 dstart = &data[b * BUTM_BLKSIZE];
1386 bstart = dstart - sizeof(struct blockMark);
1388 if (len < BUTM_BLKSIZE) {
1389 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1392 length = BUTM_BLKSIZE;
1395 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1399 /* If there are more blocks, step to next block */
1400 /* Otherwise, copy the data to beginning of last block */
1402 if (b < (blocks - 1))
1405 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1412 /* file_ReadFileData
1413 * Read a data block from tape.
1415 * info - tape info structure, c.f. fid
1416 * data - ptr to buffer for data
1417 * len - size of data buffer
1419 * nBytes - no. of data bytes read.
1423 file_ReadFileData(struct butm_tapeInfo *info, char *data, int len, int *nBytes)
1425 struct blockMark *bmark;
1427 afs_int32 blockType;
1430 printf("butm: Read tape data - %u bytes\n", len);
1436 ERROR_EXIT(BUTM_BADARGUMENT);
1439 code = check(info, READ_OP);
1442 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1443 ERROR_EXIT(BUTM_BADARGUMENT);
1444 if (!READS || WRITES)
1445 ERROR_EXIT(BUTM_BADOP);
1447 data -= sizeof(struct blockMark);
1448 code = ReadTapeBlock(info, data, &blockType);
1452 if (blockType != BLOCK_DATA) {
1453 if (blockType == BLOCK_EOF)
1454 ERROR_EXIT(BUTM_EOF);
1455 if (blockType == BLOCK_FMEND)
1456 ERROR_EXIT(BUTM_ENDVOLUME);
1457 ERROR_EXIT(BUTM_BADBLOCK);
1460 bmark = (struct blockMark *)data;
1461 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1468 file_WriteFileEnd(struct butm_tapeInfo *info)
1473 printf("butm: Write filemark end\n");
1478 code = check(info, WRITE_OP);
1481 if (READS || !WRITES)
1482 ERROR_EXIT(BUTM_BADOP);
1484 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1490 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1491 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1492 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1493 * missing with some 3.1 dumps).
1496 file_ReadFileEnd(struct butm_tapeInfo *info)
1499 afs_int32 blockType;
1502 printf("butm: Read filemark end\n");
1507 code = check(info, READ_OP);
1510 if (!READS || WRITES)
1511 ERROR_EXIT(BUTM_BADOP);
1513 info->status &= ~BUTM_STATUS_EOF;
1515 code = ReadTapeBlock(info, tapeBlock, &blockType);
1519 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1520 ERROR_EXIT(BUTM_BADBLOCK);
1527 * Write the end-of-dump marker.
1530 file_WriteEODump(struct butm_tapeInfo *info)
1535 printf("butm: Write filemark EOD\n");
1540 code = check(info, WRITE_OP);
1543 if (READS || WRITES)
1544 ERROR_EXIT(BUTM_BADOP);
1546 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1550 info->status |= BUTM_STATUS_EOD;
1557 file_Seek(struct butm_tapeInfo *info, afs_int32 position)
1564 afs_hyper_t startOff, stopOff; /* for normal file(non-tape) seeks */
1567 printf("butm: Seek to the tape position %d\n", position);
1571 code = check(info, READ_OP);
1576 p = (struct progress *)info->tmRock;
1577 posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1579 /* Not really necessary to do it this way, should be fixed */
1582 d = (posit & 0xffffffff);
1587 hset64(startOff, c, d);
1589 w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1592 if (hcmp(startOff, stopOff) != 0)
1593 ERROR_EXIT(BUTM_POSITION);
1595 p->reading = p->writing = 0;
1596 info->position = position;
1598 /* Don't position backwards if we are in-between FMs */
1599 if ((READS || WRITES) && ((position - info->position) <= 0))
1600 ERROR_EXIT(BUTM_BADOP);
1602 code = SeekFile(info, (position - info->position));
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.
1617 file_SeekEODump(struct butm_tapeInfo *info, afs_int32 position)
1620 afs_int32 blockType;
1623 afs_hyper_t startOff, stopOff; /* file seek offsets */
1626 printf("butm: Seek to end-of-dump\n");
1629 code = check(info, READ_OP);
1632 if (READS || WRITES)
1633 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);
1641 ERROR_EXIT(BUTM_POSITION);
1644 if (hgetlo(stopOff) % BUTM_BLOCKSIZE)
1645 ERROR_EXIT(BUTM_POSITION);
1646 info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1648 /* Seek to the desired position */
1649 code = SeekFile(info, (position - info->position) + 1);
1654 * Search until the filemark is an EODump filemark.
1655 * Skip over volumes only.
1658 code = ReadTapeBlock(info, tapeBlock, &blockType);
1662 if (blockType == BLOCK_EOD)
1664 if (blockType != BLOCK_FMBEGIN)
1665 ERROR_EXIT(BUTM_BADBLOCK);
1667 code = SeekFile(info, 1); /* Step forward to next volume */
1679 file_SetSize(struct butm_tapeInfo *info, afs_uint32 size)
1682 printf("butm: Set size of tape\n");
1686 info->tapeSize = config.tapeSize;
1688 info->tapeSize = size;
1693 file_GetSize(struct butm_tapeInfo *info, afs_uint32 *size)
1696 printf("butm: Get size of tape\n");
1699 *size = info->tapeSize;
1703 /* =====================================================================
1704 * Startup/configuration routines.
1705 * ===================================================================== */
1708 file_Configure(struct tapeConfig *file)
1711 afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1712 return BUTM_BADCONFIG;
1715 config.tapeSize = file->capacity;
1716 config.fileMarkSize = file->fileMarkSize;
1717 config.portOffset = file->portOffset;
1718 strcpy(config.tapedir, file->device);
1720 /* Tape must be large enough to at least fit a label */
1721 if (config.tapeSize <= 0) {
1722 afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1724 return BUTM_BADCONFIG;
1727 if (strlen(config.tapedir) == 0) {
1728 afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1729 return BUTM_BADCONFIG;
1736 /* This procedure instantiates a tape module of type file_tm. */
1738 butm_file_Instantiate(struct butm_tapeInfo *info, struct tapeConfig *file)
1740 extern int debugLevel;
1743 if (debugLevel > 98)
1744 printf("butm: Instantiate butc\n");
1747 ERROR_EXIT(BUTM_BADARGUMENT);
1748 if (info->structVersion != BUTM_MAJORVERSION)
1749 ERROR_EXIT(BUTM_OLDINTERFACE);
1751 memset(info, 0, sizeof(struct butm_tapeInfo));
1752 info->structVersion = BUTM_MAJORVERSION;
1753 info->ops.mount = file_Mount;
1754 info->ops.dismount = file_Dismount;
1755 info->ops.create = file_WriteLabel;
1756 info->ops.readLabel = file_ReadLabel;
1757 info->ops.seek = file_Seek;
1758 info->ops.seekEODump = file_SeekEODump;
1759 info->ops.readFileBegin = file_ReadFileBegin;
1760 info->ops.readFileData = file_ReadFileData;
1761 info->ops.readFileEnd = file_ReadFileEnd;
1762 info->ops.writeFileBegin = file_WriteFileBegin;
1763 info->ops.writeFileData = file_WriteFileData;
1764 info->ops.writeFileEnd = file_WriteFileEnd;
1765 info->ops.writeEOT = file_WriteEODump;
1766 info->ops.setSize = file_SetSize;
1767 info->ops.getSize = file_GetSize;
1768 info->debug = ((debugLevel > 98) ? 1 : 0);
1770 code = file_Configure(file);