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 #ifdef HAVE_SYS_WAIT_H
23 #include <afs/com_err.h>
27 #include "error_macros.h"
28 #include "butm_prototypes.h"
31 typedef off64_t osi_lloff_t;
32 #else /* O_LARGEFILE */
33 #ifdef AFS_HAVE_LLSEEK
34 typedef offset_t osi_lloff_t;
35 #else /* AFS_HAVE_LLSEEK */
36 typedef off_t osi_lloff_t;
37 #endif /* AFS_HAVE_LLSEEK */
38 #endif /* O_LARGEFILE */
42 #define FILE_MAGIC 1000000007 /* s/w file mark */
43 #define FILE_BEGIN 0 /* byte field in file mark */
44 #define FILE_FMEND 1 /* byte field in file mark */
45 #define FILE_EOD -1 /* byte field in file mark */
46 #define TAPE_MAGIC 1100000009 /* tape label block */
47 #define BLOCK_MAGIC 1100000005 /* file data block */
48 #ifdef AFS_PTHREAD_ENV
50 #define SLEEP(s) sleep(s)
52 #define POLL() IOMGR_Poll()
53 #define SLEEP(s) IOMGR_Sleep(s)
58 * 1) filemarks and block marks have the magic,bytes fields reversed. This
59 * is undoubtedly a bug. Also note that the two structures have
60 * inconsistent types, overlaying int and afs_int32.
61 * 2) When a volume is dumped, if the volume is locked, the dump will produce
62 * an anomalous tape format of the form:
66 * The design of the original butm code means that this cannot be
67 * handled correctly. The code is modified so that ReadFileData
68 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
71 /* data organization on tape:
72 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
73 * blockMark contains a magic number and counts of real data bytes
74 * written out in the block.
76 * each file is preceeded by a fileMark, which acts as the file
77 * delimiter. A file consists of contigous data blocks. TM does
78 * understand or interrpet the data in data blocks.
80 * The tape begins with a tape label and ends with EOF file markers
81 * in succession (2 or 4 of them ).
85 struct fileMark { /* in network byte order */
92 struct butm_tapeLabel label;
96 usd_handle_t fid; /* file id of simulated tape */
97 afs_int32 mountId; /* current mountId */
98 afs_int32 reading; /* read file operation in progress */
99 afs_int32 writing; /* write file operation in progress */
102 static struct configuration {
103 char tapedir[64]; /* directory to create "tapes" */
104 afs_int32 mountId; /* for detecting simultaneous mounts */
105 afs_uint32 tapeSize; /* size of simulated tapes */
106 afs_uint32 fileMarkSize; /* size of file mark, bytes */
107 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
110 static char *whoami = "file_tm";
111 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
113 #define BLOCK_LABEL 0 /* read/write a tape label */
114 #define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
115 #define BLOCK_DATA 2 /* read/write a data block */
116 #define BLOCK_FMEND 3 /* read/write an end FileMark */
117 #define BLOCK_EOD 4 /* read/write an EOD FileMark */
118 #define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
119 #define BLOCK_UNKNOWN 6 /* tape block is unknwon (error) */
124 #define READS (((struct progress *)(info->tmRock))->reading)
125 #define WRITES (((struct progress *)(info->tmRock))->writing)
127 /* ----------------------------------------------------------------------
128 * These routines use the usd library to perform tape operations.
129 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
130 * PrepareAccess(nt), ShutdownAccess(nt)
132 * Return Values: USD functions return 0 if successful or errno if failed.
134 /* ------------------ USD Interface Functions Begin ------------------------ */
137 * On Unix, fork a child process to perform an IOCTL call. This avoids
138 * blocking the entire process during a tape operation
142 /* Unix version of function */
145 ForkIoctl(usd_handle_t fd, int op, int count)
147 int rc; /* return code from system calls */
148 int i; /* loop index */
149 int pid; /* process ID of child process */
150 int status; /* exit status of child process */
151 int ioctl_rc; /* return code from ioctl call */
152 int pipefd[2]; /* pipe for child return status */
153 int forkflag; /* flag set when ready to fork */
154 usd_tapeop_t tapeop; /* tape operation specification */
157 #ifdef AFS_PTHREAD_ENV
158 forkflag = 0; /* No need to fork if using pthreads */
163 /* initialize tape command structure */
165 tapeop.tp_count = count;
167 /* create pipe for getting return code from child */
171 printf("butm: Can't open pipe for IOCTL process. Error %d\n",
182 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
188 if (!forkflag) { /* problem starting child process */
189 /* do the ioctl anyway, it will probably work */
190 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
191 } else if (pid == 0) { /* child process */
192 /* close all unneccessary file descriptors */
193 /* note: as painful as it is, we have to reach under the covers of
194 * the usd package to implement this functionality.
196 unixfd = (intptr_t)(fd->handle);
198 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
199 if (i != unixfd && i != pipefd[1]) {
204 /* do the ioctl call */
205 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
207 /* send the return code back to the parent */
208 write(pipefd[1], &ioctl_rc, sizeof(int));
211 } else { /* parent process */
215 /* read the result from the child process */
216 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
217 if (rc != sizeof(int)) {
218 /* tape is now in unknown state */
219 printf("butm: Can't determine IOCTL child status. Error %d\n",
226 /* get the completion status from the child process */
227 rc = waitpid(pid, &status, 0);
228 while (rc < 0 && errno == EINTR) {
229 rc = waitpid(pid, &status, 0);
232 printf("butm: Can't determine IOCTL child status. Error %d\n",
234 } else if (status != 0) {
236 ("butm: Unexpected IOCTL process status 0x%04x . Error %d\n",
245 /* NT version of function */
248 ForkIoctl(usd_handle_t fd, int op, int count)
252 /* Issue requested tape control */
254 tapeop.tp_count = count;
256 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
258 #endif /* !AFS_NT40_ENV */
262 * On Unix, fork a child process to attempt to open the drive. We want to make
263 * certain there is tape in the drive before trying to open the device
264 * in the main process
268 /* Unix version of function. */
271 ForkOpen(char *device)
273 int rc; /* return code from system calls */
274 int i; /* loop index */
275 int pid; /* process ID of child process */
276 int status; /* exit status of child process */
277 int open_rc; /* return code from open */
278 int pipefd[2]; /* pipe for child return status */
279 int forkflag; /* flag set when ready to fork */
280 usd_handle_t fd; /* handle returned from open */
282 #ifdef AFS_PTHREAD_ENV
283 forkflag = 0; /* No need to fork if using pthreads */
288 /* create pipe for getting return code from child */
292 printf("butm: Cannot create pipe for OPEN process. Error %d\n",
303 printf("butm: Cannot create child process for OPEN. Error %d\n",
309 if (!forkflag) { /* problem starting child process */
311 *return success, the caller will discover any problems
312 * when it opens the device.
315 } else if (pid == 0) { /* child process */
316 /* close all unneccessary file descriptors */
317 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
318 if (i != pipefd[1]) {
324 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
330 /* send the return code back to the parent */
331 write(pipefd[1], &open_rc, sizeof(open_rc));
334 } else { /* parent process */
339 /* read the result from the child process */
340 rc = read(pipefd[0], &open_rc, sizeof(open_rc));
341 if (rc != sizeof(open_rc)) {
342 /* this is not a problem since we will reopen the device anyway */
343 printf("butm: No response from OPEN process. Error %d\n", errno);
349 /* get the completion status from the child process */
350 rc = waitpid(pid, &status, 0);
351 while (rc < 0 && errno == EINTR) {
352 rc = waitpid(pid, &status, 0);
355 printf("butm: Cannot get status of OPEN process. Error %d\n",
357 } else if (status != 0) {
359 ("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n",
368 /* NT version of function. */
371 ForkOpen(char *device)
375 #endif /* AFS_NT40_ENV */
378 * On Unix, fork a child process to close the drive. If the drive rewinds
379 * on close it could cause the process to block.
383 /* Unix version of function */
386 ForkClose(usd_handle_t fd)
388 int rc; /* return code from system calls */
389 int i; /* loop index */
390 int pid; /* process ID of child process */
391 int status; /* exit status of child process */
392 int close_rc, parent_close_rc; /* return codes from close */
393 int pipefd[2]; /* pipe for child return status */
394 int ctlpipe[2]; /* pipe for message to child */
395 int forkflag; /* flag set when ready to fork */
398 #ifdef AFS_PTHREAD_ENV
399 forkflag = 0; /* No need to fork if using pthreads */
404 /* create pipe for getting return code from child */
408 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
414 /* create pipe for notifying child when to close */
420 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
433 printf("butm: Cannot create CLOSE child process. Error %d\n",
439 if (!forkflag) { /* problem starting child process */
440 close_rc = USD_CLOSE(fd);
441 parent_close_rc = close_rc;
442 } else if (pid == 0) { /* child process */
443 /* close all unneccessary file descriptors */
444 /* note: as painful as it is, we have to reach under the covers of
445 * the usd package to implement this functionality.
447 unixfd = (intptr_t)(fd->handle);
449 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
450 if (i != unixfd && i != ctlpipe[0] && i != pipefd[1]) {
455 /* the parent writes the control pipe after it closes the device */
456 read(ctlpipe[0], &close_rc, sizeof(int));
460 close_rc = USD_CLOSE(fd);
462 /* send the return code back to the parent */
463 write(pipefd[1], &close_rc, sizeof(int));
466 } else { /* parent process */
473 * close the device, this should have no effect as long as the
474 * child has not closed
477 parent_close_rc = USD_CLOSE(fd);
479 /* notify the child to do its close */
480 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
481 if (rc != sizeof(int)) {
482 printf("butm: Error communicating with CLOSE process. Error %d\n",
487 /* read the result from the child process */
488 rc = read(pipefd[0], &close_rc, sizeof(int));
489 if (rc != sizeof(int)) {
490 /* logging is enough, since we wrote a file mark the */
491 /* return code from the close doesn't really matter */
492 printf("butm: No response from CLOSE process. Error %d\n",
499 /* get the completion status from the child process */
500 rc = waitpid(pid, &status, 0);
501 while (rc < 0 && errno == EINTR) {
502 rc = waitpid(pid, &status, 0);
505 printf("butm: Cannot get status of CLOSE process. Error %d\n",
507 } else if (status != 0) {
509 ("butm: Unexpected exit status 0x%04x from CLOSE process. Error %d\n",
513 /* if either process received an error, then return an error */
514 if (parent_close_rc < 0) {
515 close_rc = parent_close_rc;
523 /* NT version of function */
526 ForkClose(usd_handle_t fd)
528 return (USD_CLOSE(fd));
530 #endif /* AFS_NT40_ENV */
532 /* Forward space file */
534 ForwardSpace(usd_handle_t fid, int count)
541 return (ForkIoctl(fid, USDTAPE_FSF, count));
545 /* Backward space file */
547 BackwardSpace(usd_handle_t fid, int count)
554 return (ForkIoctl(fid, USDTAPE_BSF, count));
558 /* write end of file mark */
560 WriteEOF(usd_handle_t fid, int count)
567 return (ForkIoctl(fid, USDTAPE_WEOF, count));
573 Rewind(usd_handle_t fid)
578 return (USD_SEEK(fid, 0, SEEK_SET, &stopOff));
580 return (ForkIoctl(fid, USDTAPE_REW, 0));
584 /* prepare tape drive for access */
586 PrepareAccess(usd_handle_t fid)
591 (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
593 /* NT won't rewind tape when it is opened */
595 #endif /* AFS_NT40_ENV */
599 /* decommission tape drive after all accesses complete */
601 ShutdownAccess(usd_handle_t fid)
605 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
607 #endif /* AFS_NT40_ENV */
611 /* -------------------- USD Interface Functions End ----------------------- */
613 /* =====================================================================
615 * ===================================================================== */
618 * add the supplied no. of bytes to the byte count of information placed
621 * dataSize - bytes used on the tape
625 incSize(struct butm_tapeInfo *info, afs_uint32 dataSize)
627 info->nBytes += dataSize;
628 info->kBytes += (info->nBytes / 1024);
629 info->nBytes %= 1024;
633 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
634 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
636 * add the supplied no. of bytes to the byte count of data placed
637 * on the tape. Also check for reaching 2GB limit and reset the
638 * pointer if necessary. This allows us to use >2GB tapes.
640 * fid - file id for the tape.
641 * dataSize - bytes used on the tape
645 incPosition(struct butm_tapeInfo *info, usd_handle_t fid, afs_uint32 dataSize)
647 /* Add this to the amount of data written to the tape */
648 incSize(info, dataSize);
650 info->posCount += dataSize;
652 if (info->posCount >= 2147467264) { /* 2GB - 16K */
654 #if (defined(AFS_SUN_ENV) || defined(AFS_LINUX24_ENV))
659 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
666 * This accounts for tape drives with a block size different from variable or 16K
667 * blocks and only reads that block size.
669 afs_int32 TapeBlockSize;
671 readData(usd_handle_t fid, char *data, afs_uint32 totalSize, afs_int32 *errorP)
673 afs_int32 toread; /* Number of bytes to read */
674 afs_uint32 rSize; /* Total bytes read so far */
675 afs_uint32 tSize; /* Temporary size */
676 afs_int32 rc; /* return code */
678 toread = totalSize; /* First, try to read all the data */
680 rc = USD_READ(fid, &data[0], toread, &rSize);
685 if (rSize == 0) /* reached EOF */
688 if (rSize != TapeBlockSize) { /* Tape block size has changed */
689 TapeBlockSize = rSize;
690 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
693 /* Read the rest of the data in */
694 while (rSize < totalSize) {
696 ((totalSize - rSize) <
697 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
698 rc = USD_READ(fid, &data[rSize], toread, &tSize);
707 if (rSize > totalSize)
708 printf("readData - Read > 16K data block - continuing.\n");
714 SeekFile(struct butm_tapeInfo *info, int count)
722 p = (struct progress *)info->tmRock;
724 if (isafile) { /* no reason for seeking through a file */
725 p->reading = p->writing = 0;
732 error = ForwardSpace(p->fid, count);
734 error = BackwardSpace(p->fid, -count);
739 info->status |= BUTM_STATUS_SEEKERROR;
740 ERROR_EXIT(BUTM_IOCTL);
743 info->position += count;
744 incSize(info, (count * config.fileMarkSize));
745 p = (struct progress *)info->tmRock;
746 p->reading = p->writing = 0;
754 /* Step to the next filemark if we are not at one already */
756 NextFile(struct butm_tapeInfo *info)
760 if (!READS && !WRITES)
763 code = SeekFile(info, 1);
768 WriteTapeBlock(struct butm_tapeInfo *info,
769 char *buffer, /* assumed to be 16384 bytes with data in it */
770 afs_int32 length, /* amount data in buffer */
773 afs_int32 code = 0, rc = 0;
775 struct tapeLabel *label;
776 struct fileMark *fmark;
777 struct blockMark *bmark;
781 p = (struct progress *)info->tmRock;
783 if (blockType == BLOCK_DATA) { /* Data Block */
786 bmark = (struct blockMark *)buffer;
787 memset(bmark, 0, sizeof(struct blockMark));
788 bmark->magic = htonl(BLOCK_MAGIC);
789 bmark->count = htonl(length);
790 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
791 fmark = (struct fileMark *)buffer;
792 fmark->magic = htonl(FILE_MAGIC);
793 fmark->nBytes = htonl(FILE_BEGIN);
794 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
795 fmark = (struct fileMark *)buffer;
796 fmark->magic = htonl(FILE_MAGIC);
797 fmark->nBytes = htonl(FILE_FMEND);
798 } else if (blockType == BLOCK_LABEL) { /* Label */
799 label = (struct tapeLabel *)buffer;
800 label->magic = htonl(TAPE_MAGIC);
801 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
802 fmark = (struct fileMark *)buffer;
803 fmark->magic = htonl(FILE_MAGIC);
804 fmark->nBytes = htonl(FILE_EOD);
807 /* Write the tape block */
808 /* -------------------- */
809 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
810 if ((rc == 0) && (wsize > 0)) {
811 incPosition(info, p->fid, wsize); /* record whats written */
815 if (wsize != BUTM_BLOCKSIZE) {
816 info->status |= BUTM_STATUS_WRITEERROR;
821 ERROR_EXIT(BUTM_EOT);
826 /* Write trailing EOF marker for some block types */
827 /* ---------------------------------------------- */
828 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
829 || (blockType == BLOCK_EOD)) {
832 error = WriteEOF(p->fid, 1);
835 info->status |= BUTM_STATUS_WRITEERROR;
836 ERROR_EXIT(BUTM_IOCTL);
839 incSize(info, config.fileMarkSize);
852 ReadTapeBlock(struct butm_tapeInfo *info,
853 char *buffer, /* assumed to be 16384 bytes */
854 afs_int32 *blockType)
857 afs_int32 rsize, fmtype;
858 struct tapeLabel *label;
859 struct fileMark *fmark;
860 struct blockMark *bmark;
863 *blockType = BLOCK_UNKNOWN;
865 p = (struct progress *)info->tmRock;
867 memset(buffer, 0, BUTM_BLOCKSIZE);
868 label = (struct tapeLabel *)buffer;
869 fmark = (struct fileMark *)buffer;
870 bmark = (struct blockMark *)buffer;
872 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
874 incPosition(info, p->fid, rsize);
878 if (rsize == 0) { /* Read a HW EOF Marker? OK */
879 *blockType = BLOCK_EOF;
880 incSize(info, config.fileMarkSize); /* Size of filemark */
882 info->position++; /* bump position */
883 p->reading = 0; /* No reads since EOF */
886 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
887 info->status |= BUTM_STATUS_READERROR;
888 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
891 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
892 *blockType = BLOCK_DATA;
895 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
896 fmtype = ntohl(fmark->nBytes);
898 if (fmtype == FILE_BEGIN) { /* filemark begin */
899 *blockType = BLOCK_FMBEGIN;
900 } else if (fmtype == FILE_FMEND) { /* filemark end */
901 *blockType = BLOCK_FMEND;
902 code = SeekFile(info, 1);
903 } else if (fmtype == FILE_EOD) { /* EOD mark */
904 *blockType = BLOCK_EOD;
905 info->status |= BUTM_STATUS_EOD;
906 code = SeekFile(info, 1);
910 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
911 *blockType = BLOCK_LABEL;
912 code = SeekFile(info, 1);
923 * check version numbers and permissions in the info structure
927 check(struct butm_tapeInfo *info,
928 int write) /* write operation requested */
933 return (BUTM_BADARGUMENT);
935 /* Check version number in info structure */
936 if (info->structVersion != BUTM_MAJORVERSION)
937 return BUTM_OLDINTERFACE;
939 /* Check if a tape is mounted */
940 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
943 /* If writing check if there is write access */
944 if (write && (info->flags & BUTM_FLAGS_READONLY))
945 return BUTM_READONLY;
951 rewindFile(struct butm_tapeInfo *info)
957 p = (struct progress *)info->tmRock;
961 error = Rewind(p->fid);
966 info->status |= BUTM_STATUS_SEEKERROR;
967 ERROR_EXIT(BUTM_IOCTL);
970 info->position = (isafile ? 0 : 1);
971 info->kBytes = info->nBytes = 0;
972 info->nFiles = info->nRecords = 0;
973 p->reading = p->writing = 0;
981 /* =====================================================================
983 * ===================================================================== */
986 file_Mount(struct butm_tapeInfo *info, char *tape)
992 afs_int32 code = 0, error = 0, rc = 0;
995 printf("butm: Mount tape drive\n");
1001 ERROR_EXIT(BUTM_BADARGUMENT);
1002 if (info->structVersion != BUTM_MAJORVERSION)
1003 ERROR_EXIT(BUTM_OLDINTERFACE);
1005 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1006 if (strlen(tape) >= sizeof(info->name))
1007 ERROR_EXIT(BUTM_BADARGUMENT);
1009 strcpy(info->name, tape);
1011 strcpy(filename, config.tapedir); /* the name of the tape device */
1012 info->position = (isafile ? 0 : 1);
1013 info->kBytes = info->nBytes = 0;
1014 info->nRecords = info->nFiles = 0;
1015 info->recordSize = 0;
1016 info->tapeSize = config.tapeSize;
1017 info->coefBytes = 1;
1018 info->coefRecords = 0;
1019 info->coefFiles = sizeof(struct fileMark);
1020 info->simultaneousTapes = 1;
1023 info->flags = BUTM_FLAGS_SEQUENTIAL;
1027 xflags |= USD_OPEN_CREATE;
1030 * try to open in a child process first so nothing will
1031 * time out should the process block because the device
1035 if (ForkOpen(filename)) {
1036 ERROR_EXIT(BUTM_MOUNTFAIL);
1040 /* Now go ahead and open the tape drive for real */
1041 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1043 if (rc != 0) { /* try for lesser access */
1044 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1048 ERROR_EXIT(BUTM_MOUNTFAIL);
1050 info->flags |= BUTM_FLAGS_READONLY;
1053 (void)PrepareAccess(fid); /* for NT */
1055 p = (struct progress *)malloc(sizeof(*p));
1056 info->tmRock = (char *)p;
1058 p->mountId = config.mountId = time(0);
1059 p->reading = p->writing = 0;
1061 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1065 info->error = error;
1070 file_Dismount(struct butm_tapeInfo *info)
1073 afs_int32 code = 0, error = 0;
1076 printf("butm: Unmount tape drive\n");
1081 code = check(info, READ_OP);
1085 p = (struct progress *)info->tmRock;
1087 (void)ShutdownAccess(p->fid); /* for NT */
1089 /* close the device */
1090 if ((error = ForkClose(p->fid))) {
1091 printf("butm: Tape close failed. Error %d\n", errno);
1097 code = BUTM_DISMOUNTFAIL;
1098 info->status |= BUTM_STATUS_TAPEERROR;
1102 info->tmRock = 0; /* mark it as closed - even if error on close */
1108 info->error = error;
1113 * write the header on a tape
1115 * info - handle on tape unit
1116 * label - label information. This label is not copied onto the tape.
1117 * If supplied, various fields are copied from this label to
1118 * the actual tape label written on the tape.
1122 file_WriteLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1127 struct tapeLabel *tlabel;
1129 afs_int64 off; /* offset */
1132 printf("butm: Write tape label\n");
1137 code = check(info, WRITE_OP);
1141 ERROR_EXIT(BUTM_BADARGUMENT);
1142 if (label->structVersion != CUR_TAPE_VERSION)
1143 ERROR_EXIT(BUTM_OLDINTERFACE);
1145 if (rewind) { /* Not appending, so rewind */
1146 code = rewindFile(info);
1151 p = (struct progress *)info->tmRock;
1153 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1155 ERROR_EXIT(BUTM_POSITION);
1158 if (READS || WRITES)
1159 ERROR_EXIT(BUTM_BADOP);
1162 /* Copy the label into the tape block
1163 * ---------------------------------- */
1164 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1166 if (!label->creationTime)
1167 label->creationTime = time(0);
1169 tlabel = (struct tapeLabel *)tapeBlock;
1170 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1171 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1172 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1173 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1174 tlabel->label.size = htonl(tlabel->label.size);
1175 tlabel->label.useCount = htonl(tlabel->label.useCount);
1176 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1179 * write the tape label - For appends, the write may need to skip
1180 * over 1 or 2 EOF marks that were written when tape was closed after
1181 * the last dump. Plus, some AIX tape drives require we try forwarding
1182 * over the last EOF and take an error before we can write the new label.
1183 * ---------------------------------------------------------------------- */
1184 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1185 if (!isafile && !rewind && (code == BUTM_IO))
1186 do { /* do if write failed */
1187 fcode = SeekFile(info, 1); /* skip over the EOF */
1189 break; /* leave if error */
1192 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1193 if (code != BUTM_IO)
1194 break; /* continue if write failed */
1196 fcode = SeekFile(info, 1); /* skip over the EOF */
1197 if (fcode) { /* retry 1 write if couldn't skip */
1199 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1205 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1206 if (code != BUTM_IO)
1207 break; /* continue if write failed */
1209 fcode = SeekFile(info, 1); /* skip over the EOF */
1210 if (fcode) { /* retry 1 write if couldn't skip */
1212 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1219 /* clear the write error status a failed WriteTapeBlock may have produced */
1221 info->status &= ~BUTM_STATUS_WRITEERROR;
1228 file_ReadLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1231 struct tapeLabel *tlabel;
1233 afs_int32 blockType;
1236 printf("butm: Read tape label\n");
1241 code = check(info, READ_OP);
1244 if (READS || WRITES)
1245 ERROR_EXIT(BUTM_BADOP);
1248 code = rewindFile(info);
1250 ERROR_EXIT(code); /* status is set so return */
1254 * When appended labels were written, either 1 or 2 EOF marks may
1255 * have had to be skipped. When reading a label, these EOF marks
1256 * must also be skipped. When an EOF is read, 0 bytes are returned
1257 * (refer to the write calls in file_WriteLabel routine).
1258 * ---------------------------------------------------------------- */
1259 code = ReadTapeBlock(info, tapeBlock, &blockType);
1260 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1262 code = ReadTapeBlock(info, tapeBlock, &blockType);
1263 if (blockType != BLOCK_EOF)
1264 break; /* didn't read an EOF */
1266 code = ReadTapeBlock(info, tapeBlock, &blockType);
1269 if (blockType == BLOCK_UNKNOWN)
1270 ERROR_EXIT(BUTM_NOLABEL);
1273 if (blockType != BLOCK_LABEL)
1274 ERROR_EXIT(BUTM_BADBLOCK);
1279 tlabel = (struct tapeLabel *)tapeBlock;
1280 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1281 label->structVersion = ntohl(label->structVersion);
1282 label->creationTime = ntohl(label->creationTime);
1283 label->expirationDate = ntohl(label->expirationDate);
1284 label->size = ntohl(label->size);
1285 label->dumpid = ntohl(label->dumpid);
1286 label->useCount = ntohl(label->useCount);
1288 info->tapeSize = label->size; /* use size from label */
1296 file_WriteFileBegin(struct butm_tapeInfo *info)
1301 printf("butm: Write filemark begin\n");
1305 code = check(info, WRITE_OP);
1309 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1320 file_ReadFileBegin(struct butm_tapeInfo *info)
1323 afs_int32 blockType;
1326 printf("butm: Read filemark begin\n");
1331 code = check(info, READ_OP);
1334 if (READS || WRITES)
1335 ERROR_EXIT(BUTM_BADOP);
1337 code = ReadTapeBlock(info, tapeBlock, &blockType);
1341 if (blockType != BLOCK_FMBEGIN) {
1342 if (blockType == BLOCK_EOD)
1343 ERROR_EXIT(BUTM_EOD); /* EODump label */
1344 if (blockType == BLOCK_LABEL)
1345 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1346 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1353 /* Writes data out in block sizes of 16KB. Does destroy the data.
1354 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1357 file_WriteFileData(struct butm_tapeInfo *info, char *data, afs_int32 blocks, afs_int32 len)
1362 char *bstart; /* Where block starts for a 16K block */
1363 char *dstart; /* Where data starts for a 16K block */
1366 printf("butm: Write tape data - %u bytes\n", len);
1371 code = check(info, WRITE_OP);
1374 if (!data || (len < 0))
1375 ERROR_EXIT(BUTM_BADARGUMENT);
1376 if (READS || !WRITES)
1377 ERROR_EXIT(BUTM_BADOP);
1379 b = 0; /* start at block 0 */
1381 dstart = &data[b * BUTM_BLKSIZE];
1382 bstart = dstart - sizeof(struct blockMark);
1384 if (len < BUTM_BLKSIZE) {
1385 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1388 length = BUTM_BLKSIZE;
1391 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1395 /* If there are more blocks, step to next block */
1396 /* Otherwise, copy the data to beginning of last block */
1398 if (b < (blocks - 1))
1401 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1408 /* file_ReadFileData
1409 * Read a data block from tape.
1411 * info - tape info structure, c.f. fid
1412 * data - ptr to buffer for data
1413 * len - size of data buffer
1415 * nBytes - no. of data bytes read.
1419 file_ReadFileData(struct butm_tapeInfo *info, char *data, int len, int *nBytes)
1421 struct blockMark *bmark;
1423 afs_int32 blockType;
1426 printf("butm: Read tape data - %u bytes\n", len);
1432 ERROR_EXIT(BUTM_BADARGUMENT);
1435 code = check(info, READ_OP);
1438 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1439 ERROR_EXIT(BUTM_BADARGUMENT);
1440 if (!READS || WRITES)
1441 ERROR_EXIT(BUTM_BADOP);
1443 data -= sizeof(struct blockMark);
1444 code = ReadTapeBlock(info, data, &blockType);
1448 if (blockType != BLOCK_DATA) {
1449 if (blockType == BLOCK_EOF)
1450 ERROR_EXIT(BUTM_EOF);
1451 if (blockType == BLOCK_FMEND)
1452 ERROR_EXIT(BUTM_ENDVOLUME);
1453 ERROR_EXIT(BUTM_BADBLOCK);
1456 bmark = (struct blockMark *)data;
1457 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1464 file_WriteFileEnd(struct butm_tapeInfo *info)
1469 printf("butm: Write filemark end\n");
1474 code = check(info, WRITE_OP);
1477 if (READS || !WRITES)
1478 ERROR_EXIT(BUTM_BADOP);
1480 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1486 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1487 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1488 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1489 * missing with some 3.1 dumps).
1492 file_ReadFileEnd(struct butm_tapeInfo *info)
1495 afs_int32 blockType;
1498 printf("butm: Read filemark end\n");
1503 code = check(info, READ_OP);
1506 if (!READS || WRITES)
1507 ERROR_EXIT(BUTM_BADOP);
1509 info->status &= ~BUTM_STATUS_EOF;
1511 code = ReadTapeBlock(info, tapeBlock, &blockType);
1515 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1516 ERROR_EXIT(BUTM_BADBLOCK);
1523 * Write the end-of-dump marker.
1526 file_WriteEODump(struct butm_tapeInfo *info)
1531 printf("butm: Write filemark EOD\n");
1536 code = check(info, WRITE_OP);
1539 if (READS || WRITES)
1540 ERROR_EXIT(BUTM_BADOP);
1542 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1546 info->status |= BUTM_STATUS_EOD;
1553 file_Seek(struct butm_tapeInfo *info, afs_int32 position)
1559 afs_int64 stopOff; /* for normal file(non-tape) seeks */
1562 printf("butm: Seek to the tape position %d\n", position);
1566 code = check(info, READ_OP);
1571 p = (struct progress *)info->tmRock;
1572 posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1574 w = USD_SEEK(p->fid, posit, SEEK_SET, &stopOff);
1577 if (posit != stopOff)
1578 ERROR_EXIT(BUTM_POSITION);
1580 p->reading = p->writing = 0;
1581 info->position = position;
1583 /* Don't position backwards if we are in-between FMs */
1584 if ((READS || WRITES) && ((position - info->position) <= 0))
1585 ERROR_EXIT(BUTM_BADOP);
1587 code = SeekFile(info, (position - info->position));
1597 * Seek to the EODump (end-of-dump) after the given position. This is
1598 * the position after the EOF filemark immediately after the EODump mark.
1599 * This is for tapes of version 4 or greater.
1602 file_SeekEODump(struct butm_tapeInfo *info, afs_int32 position)
1605 afs_int32 blockType;
1608 afs_int64 stopOff; /* file seek offsets */
1611 printf("butm: Seek to end-of-dump\n");
1614 code = check(info, READ_OP);
1617 if (READS || WRITES)
1618 ERROR_EXIT(BUTM_BADOP);
1621 p = (struct progress *)info->tmRock;
1622 w = USD_SEEK(p->fid, 0, SEEK_END, &stopOff);
1625 ERROR_EXIT(BUTM_POSITION);
1628 if (stopOff % BUTM_BLOCKSIZE)
1629 ERROR_EXIT(BUTM_POSITION);
1630 info->position = (stopOff / BUTM_BLOCKSIZE);
1632 /* Seek to the desired position */
1633 code = SeekFile(info, (position - info->position) + 1);
1638 * Search until the filemark is an EODump filemark.
1639 * Skip over volumes only.
1642 code = ReadTapeBlock(info, tapeBlock, &blockType);
1646 if (blockType == BLOCK_EOD)
1648 if (blockType != BLOCK_FMBEGIN)
1649 ERROR_EXIT(BUTM_BADBLOCK);
1651 code = SeekFile(info, 1); /* Step forward to next volume */
1663 file_SetSize(struct butm_tapeInfo *info, afs_uint32 size)
1666 printf("butm: Set size of tape\n");
1670 info->tapeSize = config.tapeSize;
1672 info->tapeSize = size;
1677 file_GetSize(struct butm_tapeInfo *info, afs_uint32 *size)
1680 printf("butm: Get size of tape\n");
1683 *size = info->tapeSize;
1687 /* =====================================================================
1688 * Startup/configuration routines.
1689 * ===================================================================== */
1692 file_Configure(struct tapeConfig *file)
1695 afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1696 return BUTM_BADCONFIG;
1699 config.tapeSize = file->capacity;
1700 config.fileMarkSize = file->fileMarkSize;
1701 config.portOffset = file->portOffset;
1702 strcpy(config.tapedir, file->device);
1704 /* Tape must be large enough to at least fit a label */
1705 if (config.tapeSize <= 0) {
1706 afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1708 return BUTM_BADCONFIG;
1711 if (strlen(config.tapedir) == 0) {
1712 afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1713 return BUTM_BADCONFIG;
1720 /* This procedure instantiates a tape module of type file_tm. */
1722 butm_file_Instantiate(struct butm_tapeInfo *info, struct tapeConfig *file)
1724 extern int debugLevel;
1727 if (debugLevel > 98)
1728 printf("butm: Instantiate butc\n");
1731 ERROR_EXIT(BUTM_BADARGUMENT);
1732 if (info->structVersion != BUTM_MAJORVERSION)
1733 ERROR_EXIT(BUTM_OLDINTERFACE);
1735 memset(info, 0, sizeof(struct butm_tapeInfo));
1736 info->structVersion = BUTM_MAJORVERSION;
1737 info->ops.mount = file_Mount;
1738 info->ops.dismount = file_Dismount;
1739 info->ops.create = file_WriteLabel;
1740 info->ops.readLabel = file_ReadLabel;
1741 info->ops.seek = file_Seek;
1742 info->ops.seekEODump = file_SeekEODump;
1743 info->ops.readFileBegin = file_ReadFileBegin;
1744 info->ops.readFileData = file_ReadFileData;
1745 info->ops.readFileEnd = file_ReadFileEnd;
1746 info->ops.writeFileBegin = file_WriteFileBegin;
1747 info->ops.writeFileData = file_WriteFileData;
1748 info->ops.writeFileEnd = file_WriteFileEnd;
1749 info->ops.writeEOT = file_WriteEODump;
1750 info->ops.setSize = file_SetSize;
1751 info->ops.getSize = file_GetSize;
1752 info->debug = ((debugLevel > 98) ? 1 : 0);
1754 code = file_Configure(file);