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>
31 #include <afs/com_err.h>
34 #include "error_macros.h"
35 #include "butm_prototypes.h"
38 typedef off64_t osi_lloff_t;
39 #else /* O_LARGEFILE */
40 #ifdef AFS_HAVE_LLSEEK
41 typedef offset_t osi_lloff_t;
42 #else /* AFS_HAVE_LLSEEK */
43 typedef off_t osi_lloff_t;
44 #endif /* AFS_HAVE_LLSEEK */
45 #endif /* O_LARGEFILE */
49 #define FILE_MAGIC 1000000007 /* s/w file mark */
50 #define FILE_BEGIN 0 /* byte field in file mark */
51 #define FILE_FMEND 1 /* byte field in file mark */
52 #define FILE_EOD -1 /* byte field in file mark */
53 #define TAPE_MAGIC 1100000009 /* tape label block */
54 #define BLOCK_MAGIC 1100000005 /* file data block */
55 #ifdef AFS_PTHREAD_ENV
57 #define SLEEP(s) sleep(s)
59 #define POLL() IOMGR_Poll()
60 #define SLEEP(s) IOMGR_Sleep(s)
65 * 1) filemarks and block marks have the magic,bytes fields reversed. This
66 * is undoubtedly a bug. Also note that the two structures have
67 * inconsistent types, overlaying int and afs_int32.
68 * 2) When a volume is dumped, if the volume is locked, the dump will produce
69 * an anomalous tape format of the form:
73 * The design of the original butm code means that this cannot be
74 * handled correctly. The code is modified so that ReadFileData
75 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
78 /* data organization on tape:
79 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
80 * blockMark contains a magic number and counts of real data bytes
81 * written out in the block.
83 * each file is preceeded by a fileMark, which acts as the file
84 * delimiter. A file consists of contigous data blocks. TM does
85 * understand or interrpet the data in data blocks.
87 * The tape begins with a tape label and ends with EOF file markers
88 * in succession (2 or 4 of them ).
92 struct fileMark { /* in network byte order */
99 struct butm_tapeLabel label;
103 usd_handle_t fid; /* file id of simulated tape */
104 afs_int32 mountId; /* current mountId */
105 afs_int32 reading; /* read file operation in progress */
106 afs_int32 writing; /* write file operation in progress */
109 static struct configuration {
110 char tapedir[64]; /* directory to create "tapes" */
111 afs_int32 mountId; /* for detecting simultaneous mounts */
112 afs_uint32 tapeSize; /* size of simulated tapes */
113 afs_uint32 fileMarkSize; /* size of file mark, bytes */
114 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
117 static char *whoami = "file_tm";
118 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
120 #define BLOCK_LABEL 0 /* read/write a tape label */
121 #define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
122 #define BLOCK_DATA 2 /* read/write a data block */
123 #define BLOCK_FMEND 3 /* read/write an end FileMark */
124 #define BLOCK_EOD 4 /* read/write an EOD FileMark */
125 #define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
126 #define BLOCK_UNKNOWN 6 /* tape block is unknwon (error) */
131 #define READS (((struct progress *)(info->tmRock))->reading)
132 #define WRITES (((struct progress *)(info->tmRock))->writing)
134 /* ----------------------------------------------------------------------
135 * These routines use the usd library to perform tape operations.
136 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
137 * PrepareAccess(nt), ShutdownAccess(nt)
139 * Return Values: USD functions return 0 if successful or errno if failed.
141 /* ------------------ USD Interface Functions Begin ------------------------ */
144 * On Unix, fork a child process to perform an IOCTL call. This avoids
145 * blocking the entire process during a tape operation
149 /* Unix version of function */
152 ForkIoctl(usd_handle_t fd, int op, int count)
154 int rc; /* return code from system calls */
155 int i; /* loop index */
156 int pid; /* process ID of child process */
157 int status; /* exit status of child process */
158 int ioctl_rc; /* return code from ioctl call */
159 int pipefd[2]; /* pipe for child return status */
160 int forkflag; /* flag set when ready to fork */
161 usd_tapeop_t tapeop; /* tape operation specification */
164 #ifdef AFS_PTHREAD_ENV
165 forkflag = 0; /* No need to fork if using pthreads */
170 /* initialize tape command structure */
172 tapeop.tp_count = count;
174 /* create pipe for getting return code from child */
178 printf("butm: Can't open pipe for IOCTL process. Error %d\n",
189 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
195 if (!forkflag) { /* problem starting child process */
196 /* do the ioctl anyway, it will probably work */
197 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
198 } else if (pid == 0) { /* child process */
199 /* close all unneccessary file descriptors */
200 /* note: as painful as it is, we have to reach under the covers of
201 * the usd package to implement this functionality.
203 unixfd = (intptr_t)(fd->handle);
205 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
206 if (i != unixfd && i != pipefd[1]) {
211 /* do the ioctl call */
212 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
214 /* send the return code back to the parent */
215 write(pipefd[1], &ioctl_rc, sizeof(int));
218 } else { /* parent process */
222 /* read the result from the child process */
223 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
224 if (rc != sizeof(int)) {
225 /* tape is now in unknown state */
226 printf("butm: Can't determine IOCTL child status. Error %d\n",
233 /* get the completion status from the child process */
234 rc = waitpid(pid, &status, 0);
235 while (rc < 0 && errno == EINTR) {
236 rc = waitpid(pid, &status, 0);
239 printf("butm: Can't determine IOCTL child status. Error %d\n",
241 } else if (status != 0) {
243 ("butm: Unexpected IOCTL process status 0x%04x . Error %d\n",
252 /* NT version of function */
255 ForkIoctl(usd_handle_t fd, int op, int count)
259 /* Issue requested tape control */
261 tapeop.tp_count = count;
263 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
265 #endif /* !AFS_NT40_ENV */
269 * On Unix, fork a child process to attempt to open the drive. We want to make
270 * certain there is tape in the drive before trying to open the device
271 * in the main process
275 /* Unix version of function. */
278 ForkOpen(char *device)
280 int rc; /* return code from system calls */
281 int i; /* loop index */
282 int pid; /* process ID of child process */
283 int status; /* exit status of child process */
284 int open_rc; /* return code from open */
285 int pipefd[2]; /* pipe for child return status */
286 int forkflag; /* flag set when ready to fork */
287 usd_handle_t fd; /* handle returned from open */
289 #ifdef AFS_PTHREAD_ENV
290 forkflag = 0; /* No need to fork if using pthreads */
295 /* create pipe for getting return code from child */
299 printf("butm: Cannot create pipe for OPEN process. Error %d\n",
310 printf("butm: Cannot create child process for OPEN. Error %d\n",
316 if (!forkflag) { /* problem starting child process */
318 *return success, the caller will discover any problems
319 * when it opens the device.
322 } else if (pid == 0) { /* child process */
323 /* close all unneccessary file descriptors */
324 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
325 if (i != pipefd[1]) {
331 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
337 /* send the return code back to the parent */
338 write(pipefd[1], &open_rc, sizeof(open_rc));
341 } else { /* parent process */
346 /* read the result from the child process */
347 rc = read(pipefd[0], &open_rc, sizeof(open_rc));
348 if (rc != sizeof(open_rc)) {
349 /* this is not a problem since we will reopen the device anyway */
350 printf("butm: No response from OPEN process. Error %d\n", errno);
356 /* get the completion status from the child process */
357 rc = waitpid(pid, &status, 0);
358 while (rc < 0 && errno == EINTR) {
359 rc = waitpid(pid, &status, 0);
362 printf("butm: Cannot get status of OPEN process. Error %d\n",
364 } else if (status != 0) {
366 ("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n",
375 /* NT version of function. */
378 ForkOpen(char *device)
382 #endif /* AFS_NT40_ENV */
385 * On Unix, fork a child process to close the drive. If the drive rewinds
386 * on close it could cause the process to block.
390 /* Unix version of function */
393 ForkClose(usd_handle_t fd)
395 int rc; /* return code from system calls */
396 int i; /* loop index */
397 int pid; /* process ID of child process */
398 int status; /* exit status of child process */
399 int close_rc, parent_close_rc; /* return codes from close */
400 int pipefd[2]; /* pipe for child return status */
401 int ctlpipe[2]; /* pipe for message to child */
402 int forkflag; /* flag set when ready to fork */
405 #ifdef AFS_PTHREAD_ENV
406 forkflag = 0; /* No need to fork if using pthreads */
411 /* create pipe for getting return code from child */
415 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
421 /* create pipe for notifying child when to close */
427 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
440 printf("butm: Cannot create CLOSE child process. Error %d\n",
446 if (!forkflag) { /* problem starting child process */
447 close_rc = USD_CLOSE(fd);
448 parent_close_rc = close_rc;
449 } else if (pid == 0) { /* child process */
450 /* close all unneccessary file descriptors */
451 /* note: as painful as it is, we have to reach under the covers of
452 * the usd package to implement this functionality.
454 unixfd = (intptr_t)(fd->handle);
456 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
457 if (i != unixfd && i != ctlpipe[0] && i != pipefd[1]) {
462 /* the parent writes the control pipe after it closes the device */
463 read(ctlpipe[0], &close_rc, sizeof(int));
467 close_rc = USD_CLOSE(fd);
469 /* send the return code back to the parent */
470 write(pipefd[1], &close_rc, sizeof(int));
473 } else { /* parent process */
480 * close the device, this should have no effect as long as the
481 * child has not closed
484 parent_close_rc = USD_CLOSE(fd);
486 /* notify the child to do its close */
487 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
488 if (rc != sizeof(int)) {
489 printf("butm: Error communicating with CLOSE process. Error %d\n",
494 /* read the result from the child process */
495 rc = read(pipefd[0], &close_rc, sizeof(int));
496 if (rc != sizeof(int)) {
497 /* logging is enough, since we wrote a file mark the */
498 /* return code from the close doesn't really matter */
499 printf("butm: No response from CLOSE process. Error %d\n",
506 /* get the completion status from the child process */
507 rc = waitpid(pid, &status, 0);
508 while (rc < 0 && errno == EINTR) {
509 rc = waitpid(pid, &status, 0);
512 printf("butm: Cannot get status of CLOSE process. Error %d\n",
514 } else if (status != 0) {
516 ("butm: Unexpected exit status 0x%04x from CLOSE process. Error %d\n",
520 /* if either process received an error, then return an error */
521 if (parent_close_rc < 0) {
522 close_rc = parent_close_rc;
530 /* NT version of function */
533 ForkClose(usd_handle_t fd)
535 return (USD_CLOSE(fd));
537 #endif /* AFS_NT40_ENV */
539 /* Forward space file */
541 ForwardSpace(usd_handle_t fid, int count)
548 return (ForkIoctl(fid, USDTAPE_FSF, count));
552 /* Backward space file */
554 BackwardSpace(usd_handle_t fid, int count)
561 return (ForkIoctl(fid, USDTAPE_BSF, count));
565 /* write end of file mark */
567 WriteEOF(usd_handle_t fid, int count)
574 return (ForkIoctl(fid, USDTAPE_WEOF, count));
580 Rewind(usd_handle_t fid)
583 afs_hyper_t startOff, stopOff;
586 return (USD_SEEK(fid, startOff, SEEK_SET, &stopOff));
588 return (ForkIoctl(fid, USDTAPE_REW, 0));
592 /* prepare tape drive for access */
594 PrepareAccess(usd_handle_t fid)
599 (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
601 /* NT won't rewind tape when it is opened */
603 #endif /* AFS_NT40_ENV */
607 /* decommission tape drive after all accesses complete */
609 ShutdownAccess(usd_handle_t fid)
613 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
615 #endif /* AFS_NT40_ENV */
619 /* -------------------- USD Interface Functions End ----------------------- */
621 /* =====================================================================
623 * ===================================================================== */
626 * add the supplied no. of bytes to the byte count of information placed
629 * dataSize - bytes used on the tape
633 incSize(struct butm_tapeInfo *info, afs_uint32 dataSize)
635 info->nBytes += dataSize;
636 info->kBytes += (info->nBytes / 1024);
637 info->nBytes %= 1024;
641 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
642 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
644 * add the supplied no. of bytes to the byte count of data placed
645 * on the tape. Also check for reaching 2GB limit and reset the
646 * pointer if necessary. This allows us to use >2GB tapes.
648 * fid - file id for the tape.
649 * dataSize - bytes used on the tape
653 incPosition(struct butm_tapeInfo *info, usd_handle_t fid, afs_uint32 dataSize)
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(usd_handle_t fid, char *data, afs_uint32 totalSize, afs_int32 *errorP)
680 afs_int32 toread; /* Number of bytes to read */
681 afs_uint32 rSize; /* Total bytes read so far */
682 afs_uint32 tSize; /* Temporary size */
683 afs_int32 rc; /* return code */
685 toread = totalSize; /* First, try to read all the data */
687 rc = USD_READ(fid, &data[0], toread, &rSize);
692 if (rSize == 0) /* reached EOF */
695 if (rSize != TapeBlockSize) { /* Tape block size has changed */
696 TapeBlockSize = rSize;
697 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
700 /* Read the rest of the data in */
701 while (rSize < totalSize) {
703 ((totalSize - rSize) <
704 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
705 rc = USD_READ(fid, &data[rSize], toread, &tSize);
714 if (rSize > totalSize)
715 printf("readData - Read > 16K data block - continuing.\n");
721 SeekFile(struct butm_tapeInfo *info, int count)
729 p = (struct progress *)info->tmRock;
731 if (isafile) { /* no reason for seeking through a file */
732 p->reading = p->writing = 0;
739 error = ForwardSpace(p->fid, count);
741 error = BackwardSpace(p->fid, -count);
746 info->status |= BUTM_STATUS_SEEKERROR;
747 ERROR_EXIT(BUTM_IOCTL);
750 info->position += count;
751 incSize(info, (count * config.fileMarkSize));
752 p = (struct progress *)info->tmRock;
753 p->reading = p->writing = 0;
761 /* Step to the next filemark if we are not at one already */
763 NextFile(struct butm_tapeInfo *info)
767 if (!READS && !WRITES)
770 code = SeekFile(info, 1);
775 WriteTapeBlock(struct butm_tapeInfo *info,
776 char *buffer, /* assumed to be 16384 bytes with data in it */
777 afs_int32 length, /* amount data in buffer */
780 afs_int32 code = 0, rc = 0;
782 struct tapeLabel *label;
783 struct fileMark *fmark;
784 struct blockMark *bmark;
788 p = (struct progress *)info->tmRock;
790 if (blockType == BLOCK_DATA) { /* Data Block */
793 bmark = (struct blockMark *)buffer;
794 memset(bmark, 0, sizeof(struct blockMark));
795 bmark->magic = htonl(BLOCK_MAGIC);
796 bmark->count = htonl(length);
797 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
798 fmark = (struct fileMark *)buffer;
799 fmark->magic = htonl(FILE_MAGIC);
800 fmark->nBytes = htonl(FILE_BEGIN);
801 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
802 fmark = (struct fileMark *)buffer;
803 fmark->magic = htonl(FILE_MAGIC);
804 fmark->nBytes = htonl(FILE_FMEND);
805 } else if (blockType == BLOCK_LABEL) { /* Label */
806 label = (struct tapeLabel *)buffer;
807 label->magic = htonl(TAPE_MAGIC);
808 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
809 fmark = (struct fileMark *)buffer;
810 fmark->magic = htonl(FILE_MAGIC);
811 fmark->nBytes = htonl(FILE_EOD);
814 /* Write the tape block */
815 /* -------------------- */
816 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
817 if ((rc == 0) && (wsize > 0)) {
818 incPosition(info, p->fid, wsize); /* record whats written */
822 if (wsize != BUTM_BLOCKSIZE) {
823 info->status |= BUTM_STATUS_WRITEERROR;
828 ERROR_EXIT(BUTM_EOT);
833 /* Write trailing EOF marker for some block types */
834 /* ---------------------------------------------- */
835 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
836 || (blockType == BLOCK_EOD)) {
839 error = WriteEOF(p->fid, 1);
842 info->status |= BUTM_STATUS_WRITEERROR;
843 ERROR_EXIT(BUTM_IOCTL);
846 incSize(info, config.fileMarkSize);
859 ReadTapeBlock(struct butm_tapeInfo *info,
860 char *buffer, /* assumed to be 16384 bytes */
861 afs_int32 *blockType)
864 afs_int32 rsize, fmtype;
865 struct tapeLabel *label;
866 struct fileMark *fmark;
867 struct blockMark *bmark;
870 *blockType = BLOCK_UNKNOWN;
872 p = (struct progress *)info->tmRock;
874 memset(buffer, 0, BUTM_BLOCKSIZE);
875 label = (struct tapeLabel *)buffer;
876 fmark = (struct fileMark *)buffer;
877 bmark = (struct blockMark *)buffer;
879 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
881 incPosition(info, p->fid, rsize);
885 if (rsize == 0) { /* Read a HW EOF Marker? OK */
886 *blockType = BLOCK_EOF;
887 incSize(info, config.fileMarkSize); /* Size of filemark */
889 info->position++; /* bump position */
890 p->reading = 0; /* No reads since EOF */
893 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
894 info->status |= BUTM_STATUS_READERROR;
895 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
898 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
899 *blockType = BLOCK_DATA;
902 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
903 fmtype = ntohl(fmark->nBytes);
905 if (fmtype == FILE_BEGIN) { /* filemark begin */
906 *blockType = BLOCK_FMBEGIN;
907 } else if (fmtype == FILE_FMEND) { /* filemark end */
908 *blockType = BLOCK_FMEND;
909 code = SeekFile(info, 1);
910 } else if (fmtype == FILE_EOD) { /* EOD mark */
911 *blockType = BLOCK_EOD;
912 info->status |= BUTM_STATUS_EOD;
913 code = SeekFile(info, 1);
917 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
918 *blockType = BLOCK_LABEL;
919 code = SeekFile(info, 1);
930 * check version numbers and permissions in the info structure
934 check(struct butm_tapeInfo *info,
935 int write) /* write operation requested */
940 return (BUTM_BADARGUMENT);
942 /* Check version number in info structure */
943 if (info->structVersion != BUTM_MAJORVERSION)
944 return BUTM_OLDINTERFACE;
946 /* Check if a tape is mounted */
947 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
950 /* If writing check if there is write access */
951 if (write && (info->flags & BUTM_FLAGS_READONLY))
952 return BUTM_READONLY;
958 rewindFile(struct butm_tapeInfo *info)
964 p = (struct progress *)info->tmRock;
968 error = Rewind(p->fid);
973 info->status |= BUTM_STATUS_SEEKERROR;
974 ERROR_EXIT(BUTM_IOCTL);
977 info->position = (isafile ? 0 : 1);
978 info->kBytes = info->nBytes = 0;
979 info->nFiles = info->nRecords = 0;
980 p->reading = p->writing = 0;
988 /* =====================================================================
990 * ===================================================================== */
993 file_Mount(struct butm_tapeInfo *info, char *tape)
999 afs_int32 code = 0, error = 0, rc = 0;
1002 printf("butm: Mount tape drive\n");
1008 ERROR_EXIT(BUTM_BADARGUMENT);
1009 if (info->structVersion != BUTM_MAJORVERSION)
1010 ERROR_EXIT(BUTM_OLDINTERFACE);
1012 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1013 if (strlen(tape) >= sizeof(info->name))
1014 ERROR_EXIT(BUTM_BADARGUMENT);
1016 strcpy(info->name, tape);
1018 strcpy(filename, config.tapedir); /* the name of the tape device */
1019 info->position = (isafile ? 0 : 1);
1020 info->kBytes = info->nBytes = 0;
1021 info->nRecords = info->nFiles = 0;
1022 info->recordSize = 0;
1023 info->tapeSize = config.tapeSize;
1024 info->coefBytes = 1;
1025 info->coefRecords = 0;
1026 info->coefFiles = sizeof(struct fileMark);
1027 info->simultaneousTapes = 1;
1030 info->flags = BUTM_FLAGS_SEQUENTIAL;
1034 xflags |= USD_OPEN_CREATE;
1037 * try to open in a child process first so nothing will
1038 * time out should the process block because the device
1042 if (ForkOpen(filename)) {
1043 ERROR_EXIT(BUTM_MOUNTFAIL);
1047 /* Now go ahead and open the tape drive for real */
1048 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1050 if (rc != 0) { /* try for lesser access */
1051 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1055 ERROR_EXIT(BUTM_MOUNTFAIL);
1057 info->flags |= BUTM_FLAGS_READONLY;
1060 (void)PrepareAccess(fid); /* for NT */
1062 p = (struct progress *)malloc(sizeof(*p));
1063 info->tmRock = (char *)p;
1065 p->mountId = config.mountId = time(0);
1066 p->reading = p->writing = 0;
1068 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1072 info->error = error;
1077 file_Dismount(struct butm_tapeInfo *info)
1080 afs_int32 code = 0, error = 0;
1083 printf("butm: Unmount tape drive\n");
1088 code = check(info, READ_OP);
1092 p = (struct progress *)info->tmRock;
1094 (void)ShutdownAccess(p->fid); /* for NT */
1096 /* close the device */
1097 if ((error = ForkClose(p->fid))) {
1098 printf("butm: Tape close failed. Error %d\n", errno);
1104 code = BUTM_DISMOUNTFAIL;
1105 info->status |= BUTM_STATUS_TAPEERROR;
1109 info->tmRock = 0; /* mark it as closed - even if error on close */
1115 info->error = error;
1120 * write the header on a tape
1122 * info - handle on tape unit
1123 * label - label information. This label is not copied onto the tape.
1124 * If supplied, various fields are copied from this label to
1125 * the actual tape label written on the tape.
1129 file_WriteLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1134 struct tapeLabel *tlabel;
1136 afs_hyper_t off; /* offset */
1139 printf("butm: Write tape label\n");
1144 code = check(info, WRITE_OP);
1148 ERROR_EXIT(BUTM_BADARGUMENT);
1149 if (label->structVersion != CUR_TAPE_VERSION)
1150 ERROR_EXIT(BUTM_OLDINTERFACE);
1152 if (rewind) { /* Not appending, so rewind */
1153 code = rewindFile(info);
1158 p = (struct progress *)info->tmRock;
1160 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1162 ERROR_EXIT(BUTM_POSITION);
1165 if (READS || WRITES)
1166 ERROR_EXIT(BUTM_BADOP);
1169 /* Copy the label into the tape block
1170 * ---------------------------------- */
1171 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1173 if (!label->creationTime)
1174 label->creationTime = time(0);
1176 tlabel = (struct tapeLabel *)tapeBlock;
1177 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1178 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1179 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1180 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1181 tlabel->label.size = htonl(tlabel->label.size);
1182 tlabel->label.useCount = htonl(tlabel->label.useCount);
1183 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1186 * write the tape label - For appends, the write may need to skip
1187 * over 1 or 2 EOF marks that were written when tape was closed after
1188 * the last dump. Plus, some AIX tape drives require we try forwarding
1189 * over the last EOF and take an error before we can write the new label.
1190 * ---------------------------------------------------------------------- */
1191 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1192 if (!isafile && !rewind && (code == BUTM_IO))
1193 do { /* do if write failed */
1194 fcode = SeekFile(info, 1); /* skip over the EOF */
1196 break; /* leave if error */
1199 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1200 if (code != BUTM_IO)
1201 break; /* continue if write failed */
1203 fcode = SeekFile(info, 1); /* skip over the EOF */
1204 if (fcode) { /* retry 1 write if couldn't skip */
1206 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1212 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1213 if (code != BUTM_IO)
1214 break; /* continue if write failed */
1216 fcode = SeekFile(info, 1); /* skip over the EOF */
1217 if (fcode) { /* retry 1 write if couldn't skip */
1219 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1226 /* clear the write error status a failed WriteTapeBlock may have produced */
1228 info->status &= ~BUTM_STATUS_WRITEERROR;
1235 file_ReadLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1238 struct tapeLabel *tlabel;
1240 afs_int32 blockType;
1243 printf("butm: Read tape label\n");
1248 code = check(info, READ_OP);
1251 if (READS || WRITES)
1252 ERROR_EXIT(BUTM_BADOP);
1255 code = rewindFile(info);
1257 ERROR_EXIT(code); /* status is set so return */
1261 * When appended labels were written, either 1 or 2 EOF marks may
1262 * have had to be skipped. When reading a label, these EOF marks
1263 * must also be skipped. When an EOF is read, 0 bytes are returned
1264 * (refer to the write calls in file_WriteLabel routine).
1265 * ---------------------------------------------------------------- */
1266 code = ReadTapeBlock(info, tapeBlock, &blockType);
1267 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1269 code = ReadTapeBlock(info, tapeBlock, &blockType);
1270 if (blockType != BLOCK_EOF)
1271 break; /* didn't read an EOF */
1273 code = ReadTapeBlock(info, tapeBlock, &blockType);
1276 if (blockType == BLOCK_UNKNOWN)
1277 ERROR_EXIT(BUTM_NOLABEL);
1280 if (blockType != BLOCK_LABEL)
1281 ERROR_EXIT(BUTM_BADBLOCK);
1286 tlabel = (struct tapeLabel *)tapeBlock;
1287 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1288 label->structVersion = ntohl(label->structVersion);
1289 label->creationTime = ntohl(label->creationTime);
1290 label->expirationDate = ntohl(label->expirationDate);
1291 label->size = ntohl(label->size);
1292 label->dumpid = ntohl(label->dumpid);
1293 label->useCount = ntohl(label->useCount);
1295 info->tapeSize = label->size; /* use size from label */
1303 file_WriteFileBegin(struct butm_tapeInfo *info)
1308 printf("butm: Write filemark begin\n");
1312 code = check(info, WRITE_OP);
1316 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1327 file_ReadFileBegin(struct butm_tapeInfo *info)
1330 afs_int32 blockType;
1333 printf("butm: Read filemark begin\n");
1338 code = check(info, READ_OP);
1341 if (READS || WRITES)
1342 ERROR_EXIT(BUTM_BADOP);
1344 code = ReadTapeBlock(info, tapeBlock, &blockType);
1348 if (blockType != BLOCK_FMBEGIN) {
1349 if (blockType == BLOCK_EOD)
1350 ERROR_EXIT(BUTM_EOD); /* EODump label */
1351 if (blockType == BLOCK_LABEL)
1352 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1353 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1360 /* Writes data out in block sizes of 16KB. Does destroy the data.
1361 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1364 file_WriteFileData(struct butm_tapeInfo *info, char *data, afs_int32 blocks, afs_int32 len)
1369 char *bstart; /* Where block starts for a 16K block */
1370 char *dstart; /* Where data starts for a 16K block */
1373 printf("butm: Write tape data - %u bytes\n", len);
1378 code = check(info, WRITE_OP);
1381 if (!data || (len < 0))
1382 ERROR_EXIT(BUTM_BADARGUMENT);
1383 if (READS || !WRITES)
1384 ERROR_EXIT(BUTM_BADOP);
1386 b = 0; /* start at block 0 */
1388 dstart = &data[b * BUTM_BLKSIZE];
1389 bstart = dstart - sizeof(struct blockMark);
1391 if (len < BUTM_BLKSIZE) {
1392 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1395 length = BUTM_BLKSIZE;
1398 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1402 /* If there are more blocks, step to next block */
1403 /* Otherwise, copy the data to beginning of last block */
1405 if (b < (blocks - 1))
1408 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1415 /* file_ReadFileData
1416 * Read a data block from tape.
1418 * info - tape info structure, c.f. fid
1419 * data - ptr to buffer for data
1420 * len - size of data buffer
1422 * nBytes - no. of data bytes read.
1426 file_ReadFileData(struct butm_tapeInfo *info, char *data, int len, int *nBytes)
1428 struct blockMark *bmark;
1430 afs_int32 blockType;
1433 printf("butm: Read tape data - %u bytes\n", len);
1439 ERROR_EXIT(BUTM_BADARGUMENT);
1442 code = check(info, READ_OP);
1445 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1446 ERROR_EXIT(BUTM_BADARGUMENT);
1447 if (!READS || WRITES)
1448 ERROR_EXIT(BUTM_BADOP);
1450 data -= sizeof(struct blockMark);
1451 code = ReadTapeBlock(info, data, &blockType);
1455 if (blockType != BLOCK_DATA) {
1456 if (blockType == BLOCK_EOF)
1457 ERROR_EXIT(BUTM_EOF);
1458 if (blockType == BLOCK_FMEND)
1459 ERROR_EXIT(BUTM_ENDVOLUME);
1460 ERROR_EXIT(BUTM_BADBLOCK);
1463 bmark = (struct blockMark *)data;
1464 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1471 file_WriteFileEnd(struct butm_tapeInfo *info)
1476 printf("butm: Write filemark end\n");
1481 code = check(info, WRITE_OP);
1484 if (READS || !WRITES)
1485 ERROR_EXIT(BUTM_BADOP);
1487 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1493 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1494 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1495 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1496 * missing with some 3.1 dumps).
1499 file_ReadFileEnd(struct butm_tapeInfo *info)
1502 afs_int32 blockType;
1505 printf("butm: Read filemark end\n");
1510 code = check(info, READ_OP);
1513 if (!READS || WRITES)
1514 ERROR_EXIT(BUTM_BADOP);
1516 info->status &= ~BUTM_STATUS_EOF;
1518 code = ReadTapeBlock(info, tapeBlock, &blockType);
1522 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1523 ERROR_EXIT(BUTM_BADBLOCK);
1530 * Write the end-of-dump marker.
1533 file_WriteEODump(struct butm_tapeInfo *info)
1538 printf("butm: Write filemark EOD\n");
1543 code = check(info, WRITE_OP);
1546 if (READS || WRITES)
1547 ERROR_EXIT(BUTM_BADOP);
1549 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1553 info->status |= BUTM_STATUS_EOD;
1560 file_Seek(struct butm_tapeInfo *info, afs_int32 position)
1567 afs_hyper_t startOff, stopOff; /* for normal file(non-tape) seeks */
1570 printf("butm: Seek to the tape position %d\n", position);
1574 code = check(info, READ_OP);
1579 p = (struct progress *)info->tmRock;
1580 posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1582 /* Not really necessary to do it this way, should be fixed */
1585 d = (posit & 0xffffffff);
1590 hset64(startOff, c, d);
1592 w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1595 if (hcmp(startOff, stopOff) != 0)
1596 ERROR_EXIT(BUTM_POSITION);
1598 p->reading = p->writing = 0;
1599 info->position = position;
1601 /* Don't position backwards if we are in-between FMs */
1602 if ((READS || WRITES) && ((position - info->position) <= 0))
1603 ERROR_EXIT(BUTM_BADOP);
1605 code = SeekFile(info, (position - info->position));
1615 * Seek to the EODump (end-of-dump) after the given position. This is
1616 * the position after the EOF filemark immediately after the EODump mark.
1617 * This is for tapes of version 4 or greater.
1620 file_SeekEODump(struct butm_tapeInfo *info, afs_int32 position)
1623 afs_int32 blockType;
1626 afs_hyper_t startOff, stopOff; /* file seek offsets */
1629 printf("butm: Seek to end-of-dump\n");
1632 code = check(info, READ_OP);
1635 if (READS || WRITES)
1636 ERROR_EXIT(BUTM_BADOP);
1639 p = (struct progress *)info->tmRock;
1640 hset64(startOff, 0, 0);
1641 w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1644 ERROR_EXIT(BUTM_POSITION);
1647 if (hgetlo(stopOff) % BUTM_BLOCKSIZE)
1648 ERROR_EXIT(BUTM_POSITION);
1649 info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1651 /* Seek to the desired position */
1652 code = SeekFile(info, (position - info->position) + 1);
1657 * Search until the filemark is an EODump filemark.
1658 * Skip over volumes only.
1661 code = ReadTapeBlock(info, tapeBlock, &blockType);
1665 if (blockType == BLOCK_EOD)
1667 if (blockType != BLOCK_FMBEGIN)
1668 ERROR_EXIT(BUTM_BADBLOCK);
1670 code = SeekFile(info, 1); /* Step forward to next volume */
1682 file_SetSize(struct butm_tapeInfo *info, afs_uint32 size)
1685 printf("butm: Set size of tape\n");
1689 info->tapeSize = config.tapeSize;
1691 info->tapeSize = size;
1696 file_GetSize(struct butm_tapeInfo *info, afs_uint32 *size)
1699 printf("butm: Get size of tape\n");
1702 *size = info->tapeSize;
1706 /* =====================================================================
1707 * Startup/configuration routines.
1708 * ===================================================================== */
1711 file_Configure(struct tapeConfig *file)
1714 afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1715 return BUTM_BADCONFIG;
1718 config.tapeSize = file->capacity;
1719 config.fileMarkSize = file->fileMarkSize;
1720 config.portOffset = file->portOffset;
1721 strcpy(config.tapedir, file->device);
1723 /* Tape must be large enough to at least fit a label */
1724 if (config.tapeSize <= 0) {
1725 afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1727 return BUTM_BADCONFIG;
1730 if (strlen(config.tapedir) == 0) {
1731 afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1732 return BUTM_BADCONFIG;
1739 /* This procedure instantiates a tape module of type file_tm. */
1741 butm_file_Instantiate(struct butm_tapeInfo *info, struct tapeConfig *file)
1743 extern int debugLevel;
1746 if (debugLevel > 98)
1747 printf("butm: Instantiate butc\n");
1750 ERROR_EXIT(BUTM_BADARGUMENT);
1751 if (info->structVersion != BUTM_MAJORVERSION)
1752 ERROR_EXIT(BUTM_OLDINTERFACE);
1754 memset(info, 0, sizeof(struct butm_tapeInfo));
1755 info->structVersion = BUTM_MAJORVERSION;
1756 info->ops.mount = file_Mount;
1757 info->ops.dismount = file_Dismount;
1758 info->ops.create = file_WriteLabel;
1759 info->ops.readLabel = file_ReadLabel;
1760 info->ops.seek = file_Seek;
1761 info->ops.seekEODump = file_SeekEODump;
1762 info->ops.readFileBegin = file_ReadFileBegin;
1763 info->ops.readFileData = file_ReadFileData;
1764 info->ops.readFileEnd = file_ReadFileEnd;
1765 info->ops.writeFileBegin = file_WriteFileBegin;
1766 info->ops.writeFileData = file_WriteFileData;
1767 info->ops.writeFileEnd = file_WriteFileEnd;
1768 info->ops.writeEOT = file_WriteEODump;
1769 info->ops.setSize = file_SetSize;
1770 info->ops.getSize = file_GetSize;
1771 info->debug = ((debugLevel > 98) ? 1 : 0);
1773 code = file_Configure(file);