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>
19 #include <netinet/in.h>
21 #include <sys/types.h>
29 #include <afs/com_err.h>
32 #include "error_macros.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 = (int)(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 = (int)(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 0x04x 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
629 incSize(info, dataSize)
630 struct butm_tapeInfo *info;
633 info->nBytes += dataSize;
634 info->kBytes += (info->nBytes / 1024);
635 info->nBytes %= 1024;
639 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
640 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
642 * add the supplied no. of bytes to the byte count of data placed
643 * on the tape. Also check for reaching 2GB limit and reset the
644 * pointer if necessary. This allows us to use >2GB tapes.
646 * fid - file id for the tape.
647 * dataSize - bytes used on the tape
650 incPosition(info, fid, dataSize)
651 struct butm_tapeInfo *info;
655 /* Add this to the amount of data written to the tape */
656 incSize(info, dataSize);
658 info->posCount += dataSize;
660 if (info->posCount >= 2147467264) { /* 2GB - 16K */
662 #if (defined(AFS_SUN_ENV) || defined(AFS_LINUX24_ENV))
666 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
673 * This accounts for tape drives with a block size different from variable or 16K
674 * blocks and only reads that block size.
676 afs_int32 TapeBlockSize;
678 readData(fid, data, totalSize, errorP)
684 afs_int32 toread; /* Number of bytes to read */
685 afs_int32 rSize; /* Total bytes read so far */
686 afs_int32 tSize; /* Temporary size */
687 afs_int32 rc; /* return code */
689 toread = totalSize; /* First, try to read all the data */
691 rc = USD_READ(fid, &data[0], toread, &rSize);
696 if (rSize == 0) /* reached EOF */
699 if (rSize != TapeBlockSize) { /* Tape block size has changed */
700 TapeBlockSize = rSize;
701 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
704 /* Read the rest of the data in */
705 while (rSize < totalSize) {
707 ((totalSize - rSize) <
708 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
709 rc = USD_READ(fid, &data[rSize], toread, &tSize);
718 if (rSize > totalSize)
719 printf("readData - Read > 16K data block - continuing.\n");
725 SeekFile(info, count)
726 struct butm_tapeInfo *info;
735 p = (struct progress *)info->tmRock;
737 if (isafile) { /* no reason for seeking through a file */
738 p->reading = p->writing = 0;
745 error = ForwardSpace(p->fid, count);
747 error = BackwardSpace(p->fid, -count);
752 info->status |= BUTM_STATUS_SEEKERROR;
753 ERROR_EXIT(BUTM_IOCTL);
756 info->position += count;
757 incSize(info, (count * config.fileMarkSize));
758 p = (struct progress *)info->tmRock;
759 p->reading = p->writing = 0;
767 /* Step to the next filemark if we are not at one already */
770 struct butm_tapeInfo *info;
774 if (!READS && !WRITES)
777 code = SeekFile(info, 1);
782 WriteTapeBlock(info, buffer, length, blockType)
783 struct butm_tapeInfo *info;
784 char *buffer; /* assumed to be 16384 bytes with data in it */
785 afs_int32 length; /* amount data in buffer */
788 afs_int32 code = 0, rc = 0;
790 struct tapeLabel *label;
791 struct fileMark *fmark;
792 struct blockMark *bmark;
796 p = (struct progress *)info->tmRock;
798 if (blockType == BLOCK_DATA) { /* Data Block */
801 bmark = (struct blockMark *)buffer;
802 memset(bmark, 0, sizeof(struct blockMark));
803 bmark->magic = htonl(BLOCK_MAGIC);
804 bmark->count = htonl(length);
805 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
806 fmark = (struct fileMark *)buffer;
807 fmark->magic = htonl(FILE_MAGIC);
808 fmark->nBytes = htonl(FILE_BEGIN);
809 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
810 fmark = (struct fileMark *)buffer;
811 fmark->magic = htonl(FILE_MAGIC);
812 fmark->nBytes = htonl(FILE_FMEND);
813 } else if (blockType == BLOCK_LABEL) { /* Label */
814 label = (struct tapeLabel *)buffer;
815 label->magic = htonl(TAPE_MAGIC);
816 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
817 fmark = (struct fileMark *)buffer;
818 fmark->magic = htonl(FILE_MAGIC);
819 fmark->nBytes = htonl(FILE_EOD);
822 /* Write the tape block */
823 /* -------------------- */
824 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
825 if ((rc == 0) && (wsize > 0)) {
826 incPosition(info, p->fid, wsize); /* record whats written */
830 if (wsize != BUTM_BLOCKSIZE) {
831 info->status |= BUTM_STATUS_WRITEERROR;
836 ERROR_EXIT(BUTM_EOT);
841 /* Write trailing EOF marker for some block types */
842 /* ---------------------------------------------- */
843 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
844 || (blockType == BLOCK_EOD)) {
847 error = WriteEOF(p->fid, 1);
850 info->status |= BUTM_STATUS_WRITEERROR;
851 ERROR_EXIT(BUTM_IOCTL);
854 incSize(info, config.fileMarkSize);
867 ReadTapeBlock(info, buffer, blockType)
868 struct butm_tapeInfo *info;
869 char *buffer; /* assumed to be 16384 bytes */
870 afs_int32 *blockType;
873 afs_int32 rsize, fmtype;
874 struct tapeLabel *label;
875 struct fileMark *fmark;
876 struct blockMark *bmark;
879 *blockType = BLOCK_UNKNOWN;
881 p = (struct progress *)info->tmRock;
883 memset(buffer, 0, BUTM_BLOCKSIZE);
884 label = (struct tapeLabel *)buffer;
885 fmark = (struct fileMark *)buffer;
886 bmark = (struct blockMark *)buffer;
888 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
890 incPosition(info, p->fid, rsize);
894 if (rsize == 0) { /* Read a HW EOF Marker? OK */
895 *blockType = BLOCK_EOF;
896 incSize(info, config.fileMarkSize); /* Size of filemark */
898 info->position++; /* bump position */
899 p->reading = 0; /* No reads since EOF */
902 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
903 info->status |= BUTM_STATUS_READERROR;
904 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
907 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
908 *blockType = BLOCK_DATA;
911 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
912 fmtype = ntohl(fmark->nBytes);
914 if (fmtype == FILE_BEGIN) { /* filemark begin */
915 *blockType = BLOCK_FMBEGIN;
916 } else if (fmtype == FILE_FMEND) { /* filemark end */
917 *blockType = BLOCK_FMEND;
918 code = SeekFile(info, 1);
919 } else if (fmtype == FILE_EOD) { /* EOD mark */
920 *blockType = BLOCK_EOD;
921 info->status |= BUTM_STATUS_EOD;
922 code = SeekFile(info, 1);
926 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
927 *blockType = BLOCK_LABEL;
928 code = SeekFile(info, 1);
939 * check version numbers and permissions in the info structure
944 struct butm_tapeInfo *info;
945 int write; /* write operation requested */
950 return (BUTM_BADARGUMENT);
952 /* Check version number in info structure */
953 if (info->structVersion != BUTM_MAJORVERSION)
954 return BUTM_OLDINTERFACE;
956 /* Check if a tape is mounted */
957 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
960 /* If writing check if there is write access */
961 if (write && (info->flags & BUTM_FLAGS_READONLY))
962 return BUTM_READONLY;
969 struct butm_tapeInfo *info;
975 p = (struct progress *)info->tmRock;
979 error = Rewind(p->fid);
984 info->status |= BUTM_STATUS_SEEKERROR;
985 ERROR_EXIT(BUTM_IOCTL);
988 info->position = (isafile ? 0 : 1);
989 info->kBytes = info->nBytes = 0;
990 info->nFiles = info->nRecords = 0;
991 p->reading = p->writing = 0;
999 /* =====================================================================
1001 * ===================================================================== */
1004 file_Mount(info, tape)
1005 struct butm_tapeInfo *info;
1012 afs_int32 code = 0, error = 0, rc = 0;
1015 printf("butm: Mount tape drive\n");
1021 ERROR_EXIT(BUTM_BADARGUMENT);
1022 if (info->structVersion != BUTM_MAJORVERSION)
1023 ERROR_EXIT(BUTM_OLDINTERFACE);
1025 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1026 if (strlen(tape) >= sizeof(info->name))
1027 ERROR_EXIT(BUTM_BADARGUMENT);
1029 strcpy(info->name, tape);
1031 strcpy(filename, config.tapedir); /* the name of the tape device */
1032 info->position = (isafile ? 0 : 1);
1033 info->kBytes = info->nBytes = 0;
1034 info->nRecords = info->nFiles = 0;
1035 info->recordSize = 0;
1036 info->tapeSize = config.tapeSize;
1037 info->coefBytes = 1;
1038 info->coefRecords = 0;
1039 info->coefFiles = sizeof(struct fileMark);
1040 info->simultaneousTapes = 1;
1043 info->flags = BUTM_FLAGS_SEQUENTIAL;
1047 xflags |= USD_OPEN_CREATE;
1050 * try to open in a child process first so nothing will
1051 * time out should the process block because the device
1055 if (ForkOpen(filename)) {
1056 ERROR_EXIT(BUTM_MOUNTFAIL);
1060 /* Now go ahead and open the tape drive for real */
1061 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1063 if (rc != 0) { /* try for lesser access */
1064 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1068 ERROR_EXIT(BUTM_MOUNTFAIL);
1070 info->flags |= BUTM_FLAGS_READONLY;
1073 (void)PrepareAccess(fid); /* for NT */
1075 p = (struct progress *)malloc(sizeof(*p));
1076 info->tmRock = (char *)p;
1078 p->mountId = config.mountId = time(0);
1079 p->reading = p->writing = 0;
1081 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1085 info->error = error;
1091 struct butm_tapeInfo *info;
1094 afs_int32 code = 0, error = 0;
1097 printf("butm: Unmount tape drive\n");
1102 code = check(info, READ_OP);
1106 p = (struct progress *)info->tmRock;
1108 (void)ShutdownAccess(p->fid); /* for NT */
1110 /* close the device */
1111 if (error = ForkClose(p->fid)) {
1112 printf("butm: Tape close failed. Error %d\n", errno);
1118 code = BUTM_DISMOUNTFAIL;
1119 info->status |= BUTM_STATUS_TAPEERROR;
1123 info->tmRock = 0; /* mark it as closed - even if error on close */
1129 info->error = error;
1134 * write the header on a tape
1136 * info - handle on tape unit
1137 * label - label information. This label is not copied onto the tape.
1138 * If supplied, various fields are copied from this label to
1139 * the actual tape label written on the tape.
1143 file_WriteLabel(info, label, rewind)
1144 struct butm_tapeInfo *info;
1145 struct butm_tapeLabel *label;
1150 struct tapeLabel *tlabel;
1152 afs_hyper_t off; /* offset */
1155 printf("butm: Write tape label\n");
1160 code = check(info, WRITE_OP);
1164 ERROR_EXIT(BUTM_BADARGUMENT);
1165 if (label->structVersion != CUR_TAPE_VERSION)
1166 ERROR_EXIT(BUTM_OLDINTERFACE);
1168 if (rewind) { /* Not appending, so rewind */
1169 code = rewindFile(info);
1174 p = (struct progress *)info->tmRock;
1176 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1178 ERROR_EXIT(BUTM_POSITION);
1181 if (READS || WRITES)
1182 ERROR_EXIT(BUTM_BADOP);
1185 /* Copy the label into the tape block
1186 * ---------------------------------- */
1187 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1189 if (!label->creationTime)
1190 label->creationTime = time(0);
1192 tlabel = (struct tapeLabel *)tapeBlock;
1193 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1194 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1195 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1196 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1197 tlabel->label.size = htonl(tlabel->label.size);
1198 tlabel->label.useCount = htonl(tlabel->label.useCount);
1199 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1202 * write the tape label - For appends, the write may need to skip
1203 * over 1 or 2 EOF marks that were written when tape was closed after
1204 * the last dump. Plus, some AIX tape drives require we try forwarding
1205 * over the last EOF and take an error before we can write the new label.
1206 * ---------------------------------------------------------------------- */
1207 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1208 if (!isafile && !rewind && (code == BUTM_IO))
1209 do { /* do if write failed */
1210 fcode = SeekFile(info, 1); /* skip over the EOF */
1212 break; /* leave if error */
1215 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1216 if (code != BUTM_IO)
1217 break; /* continue if write failed */
1219 fcode = SeekFile(info, 1); /* skip over the EOF */
1220 if (fcode) { /* retry 1 write if couldn't skip */
1222 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1228 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1229 if (code != BUTM_IO)
1230 break; /* continue if write failed */
1232 fcode = SeekFile(info, 1); /* skip over the EOF */
1233 if (fcode) { /* retry 1 write if couldn't skip */
1235 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1242 /* clear the write error status a failed WriteTapeBlock may have produced */
1244 info->status &= ~BUTM_STATUS_WRITEERROR;
1251 file_ReadLabel(info, label, rewind)
1252 struct butm_tapeInfo *info;
1253 struct butm_tapeLabel *label;
1256 struct tapeLabel *tlabel;
1258 afs_int32 blockType;
1261 printf("butm: Read tape label\n");
1266 code = check(info, READ_OP);
1269 if (READS || WRITES)
1270 ERROR_EXIT(BUTM_BADOP);
1273 code = rewindFile(info);
1275 ERROR_EXIT(code); /* status is set so return */
1279 * When appended labels were written, either 1 or 2 EOF marks may
1280 * have had to be skipped. When reading a label, these EOF marks
1281 * must also be skipped. When an EOF is read, 0 bytes are returned
1282 * (refer to the write calls in file_WriteLabel routine).
1283 * ---------------------------------------------------------------- */
1284 code = ReadTapeBlock(info, tapeBlock, &blockType);
1285 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1287 code = ReadTapeBlock(info, tapeBlock, &blockType);
1288 if (blockType != BLOCK_EOF)
1289 break; /* didn't read an EOF */
1291 code = ReadTapeBlock(info, tapeBlock, &blockType);
1294 if (blockType == BLOCK_UNKNOWN)
1295 ERROR_EXIT(BUTM_NOLABEL);
1298 if (blockType != BLOCK_LABEL)
1299 ERROR_EXIT(BUTM_BADBLOCK);
1304 tlabel = (struct tapeLabel *)tapeBlock;
1305 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1306 label->structVersion = ntohl(label->structVersion);
1307 label->creationTime = ntohl(label->creationTime);
1308 label->expirationDate = ntohl(label->expirationDate);
1309 label->size = ntohl(label->size);
1310 label->dumpid = ntohl(label->dumpid);
1311 label->useCount = ntohl(label->useCount);
1313 info->tapeSize = label->size; /* use size from label */
1321 file_WriteFileBegin(info)
1322 struct butm_tapeInfo *info;
1325 afs_int32 error = 0;
1328 printf("butm: Write filemark begin\n");
1332 code = check(info, WRITE_OP);
1336 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1347 file_ReadFileBegin(info)
1348 struct butm_tapeInfo *info;
1351 afs_int32 blockType;
1354 printf("butm: Read filemark begin\n");
1359 code = check(info, READ_OP);
1362 if (READS || WRITES)
1363 ERROR_EXIT(BUTM_BADOP);
1365 code = ReadTapeBlock(info, tapeBlock, &blockType);
1369 if (blockType != BLOCK_FMBEGIN) {
1370 if (blockType == BLOCK_EOD)
1371 ERROR_EXIT(BUTM_EOD); /* EODump label */
1372 if (blockType == BLOCK_LABEL)
1373 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1374 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1381 /* Writes data out in block sizes of 16KB. Does destroy the data.
1382 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1385 file_WriteFileData(info, data, blocks, len)
1386 struct butm_tapeInfo *info;
1394 char *bstart; /* Where block starts for a 16K block */
1395 char *dstart; /* Where data starts for a 16K block */
1398 printf("butm: Write tape data - %u bytes\n", len);
1403 code = check(info, WRITE_OP);
1406 if (!data || (len < 0))
1407 ERROR_EXIT(BUTM_BADARGUMENT);
1408 if (READS || !WRITES)
1409 ERROR_EXIT(BUTM_BADOP);
1411 b = 0; /* start at block 0 */
1413 dstart = &data[b * BUTM_BLKSIZE];
1414 bstart = dstart - sizeof(struct blockMark);
1416 if (len < BUTM_BLKSIZE) {
1417 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1420 length = BUTM_BLKSIZE;
1423 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1427 /* If there are more blocks, step to next block */
1428 /* Otherwise, copy the data to beginning of last block */
1430 if (b < (blocks - 1))
1433 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1440 /* file_ReadFileData
1441 * Read a data block from tape.
1443 * info - tape info structure, c.f. fid
1444 * data - ptr to buffer for data
1445 * len - size of data buffer
1447 * nBytes - no. of data bytes read.
1451 file_ReadFileData(info, data, len, nBytes)
1452 struct butm_tapeInfo *info;
1457 struct blockMark *bmark;
1459 afs_int32 blockType;
1462 printf("butm: Read tape data - %u bytes\n", len);
1468 ERROR_EXIT(BUTM_BADARGUMENT);
1471 code = check(info, READ_OP);
1474 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1475 ERROR_EXIT(BUTM_BADARGUMENT);
1476 if (!READS || WRITES)
1477 ERROR_EXIT(BUTM_BADOP);
1479 data -= sizeof(struct blockMark);
1480 code = ReadTapeBlock(info, data, &blockType);
1484 if (blockType != BLOCK_DATA) {
1485 if (blockType == BLOCK_EOF)
1486 ERROR_EXIT(BUTM_EOF);
1487 if (blockType == BLOCK_FMEND)
1488 ERROR_EXIT(BUTM_ENDVOLUME);
1489 ERROR_EXIT(BUTM_BADBLOCK);
1492 bmark = (struct blockMark *)data;
1493 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1500 file_WriteFileEnd(info)
1501 struct butm_tapeInfo *info;
1506 printf("butm: Write filemark end\n");
1511 code = check(info, WRITE_OP);
1514 if (READS || !WRITES)
1515 ERROR_EXIT(BUTM_BADOP);
1517 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1523 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1524 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1525 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1526 * missing with some 3.1 dumps).
1529 file_ReadFileEnd(info)
1530 struct butm_tapeInfo *info;
1533 afs_int32 blockType;
1536 printf("butm: Read filemark end\n");
1541 code = check(info, READ_OP);
1544 if (!READS || WRITES)
1545 ERROR_EXIT(BUTM_BADOP);
1547 info->status &= ~BUTM_STATUS_EOF;
1549 code = ReadTapeBlock(info, tapeBlock, &blockType);
1553 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1554 ERROR_EXIT(BUTM_BADBLOCK);
1561 * Write the end-of-dump marker.
1564 file_WriteEODump(info)
1565 struct butm_tapeInfo *info;
1570 printf("butm: Write filemark EOD\n");
1575 code = check(info, WRITE_OP);
1578 if (READS || WRITES)
1579 ERROR_EXIT(BUTM_BADOP);
1581 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1585 info->status |= BUTM_STATUS_EOD;
1592 file_Seek(info, position)
1593 struct butm_tapeInfo *info;
1601 afs_hyper_t startOff, stopOff; /* for normal file(non-tape) seeks */
1604 printf("butm: Seek to the tape position %d\n", position);
1608 code = check(info, READ_OP);
1613 p = (struct progress *)info->tmRock;
1614 posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1616 /* Not really necessary to do it this way, should be fixed */
1619 d = (posit & 0xffffffff);
1624 hset64(startOff, c, d);
1626 w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1629 if (hcmp(startOff, stopOff) != 0)
1630 ERROR_EXIT(BUTM_POSITION);
1632 p->reading = p->writing = 0;
1633 info->position = position;
1635 /* Don't position backwards if we are in-between FMs */
1636 if ((READS || WRITES) && ((position - info->position) <= 0))
1637 ERROR_EXIT(BUTM_BADOP);
1639 code = SeekFile(info, (position - info->position));
1649 * Seek to the EODump (end-of-dump) after the given position. This is
1650 * the position after the EOF filemark immediately after the EODump mark.
1651 * This is for tapes of version 4 or greater.
1654 file_SeekEODump(info, position)
1655 struct butm_tapeInfo *info;
1659 afs_int32 blockType;
1662 afs_hyper_t startOff, stopOff; /* file seek offsets */
1665 printf("butm: Seek to end-of-dump\n");
1668 code = check(info, READ_OP);
1671 if (READS || WRITES)
1672 ERROR_EXIT(BUTM_BADOP);
1675 p = (struct progress *)info->tmRock;
1676 hset64(startOff, 0, 0);
1677 w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1680 ERROR_EXIT(BUTM_POSITION);
1683 if (hgetlo(stopOff) % BUTM_BLOCKSIZE)
1684 ERROR_EXIT(BUTM_POSITION);
1685 info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1687 /* Seek to the desired position */
1688 code = SeekFile(info, (position - info->position) + 1);
1693 * Search until the filemark is an EODump filemark.
1694 * Skip over volumes only.
1697 code = ReadTapeBlock(info, tapeBlock, &blockType);
1701 if (blockType == BLOCK_EOD)
1703 if (blockType != BLOCK_FMBEGIN)
1704 ERROR_EXIT(BUTM_BADBLOCK);
1706 code = SeekFile(info, 1); /* Step forward to next volume */
1718 file_SetSize(info, size)
1719 struct butm_tapeInfo *info;
1723 printf("butm: Set size of tape\n");
1727 info->tapeSize = config.tapeSize;
1729 info->tapeSize = size;
1734 file_GetSize(info, size)
1735 struct butm_tapeInfo *info;
1739 printf("butm: Get size of tape\n");
1742 *size = info->tapeSize;
1746 /* =====================================================================
1747 * Startup/configuration routines.
1748 * ===================================================================== */
1751 file_Configure(file)
1752 struct tapeConfig *file;
1755 afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1756 return BUTM_BADCONFIG;
1759 config.tapeSize = file->capacity;
1760 config.fileMarkSize = file->fileMarkSize;
1761 config.portOffset = file->portOffset;
1762 strcpy(config.tapedir, file->device);
1764 /* Tape must be large enough to at least fit a label */
1765 if (config.tapeSize <= 0) {
1766 afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1768 return BUTM_BADCONFIG;
1771 if (strlen(config.tapedir) == 0) {
1772 afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1773 return BUTM_BADCONFIG;
1780 /* This procedure instantiates a tape module of type file_tm. */
1782 butm_file_Instantiate(info, file)
1783 struct butm_tapeInfo *info;
1784 struct tapeConfig *file;
1786 extern int debugLevel;
1789 if (debugLevel > 98)
1790 printf("butm: Instantiate butc\n");
1793 ERROR_EXIT(BUTM_BADARGUMENT);
1794 if (info->structVersion != BUTM_MAJORVERSION)
1795 ERROR_EXIT(BUTM_OLDINTERFACE);
1797 memset(info, 0, sizeof(struct butm_tapeInfo));
1798 info->structVersion = BUTM_MAJORVERSION;
1799 info->ops.mount = file_Mount;
1800 info->ops.dismount = file_Dismount;
1801 info->ops.create = file_WriteLabel;
1802 info->ops.readLabel = file_ReadLabel;
1803 info->ops.seek = file_Seek;
1804 info->ops.seekEODump = file_SeekEODump;
1805 info->ops.readFileBegin = file_ReadFileBegin;
1806 info->ops.readFileData = file_ReadFileData;
1807 info->ops.readFileEnd = file_ReadFileEnd;
1808 info->ops.writeFileBegin = file_WriteFileBegin;
1809 info->ops.writeFileData = file_WriteFileData;
1810 info->ops.writeFileEnd = file_WriteFileEnd;
1811 info->ops.writeEOT = file_WriteEODump;
1812 info->ops.setSize = file_SetSize;
1813 info->ops.getSize = file_GetSize;
1814 info->debug = ((debugLevel > 98) ? 1 : 0);
1816 code = file_Configure(file);