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;
657 /* Add this to the amount of data written to the tape */
658 incSize(info, dataSize);
660 info->posCount += dataSize;
662 if (info->posCount >= 2147467264) { /* 2GB - 16K */
664 #if (defined(AFS_SUN_ENV) || defined(AFS_DEC_ENV) || defined(AFS_LINUX24_ENV))
667 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
674 * This accounts for tape drives with a block size different from variable or 16K
675 * blocks and only reads that block size.
677 afs_int32 TapeBlockSize;
679 readData(fid, data, totalSize, errorP)
685 afs_int32 toread; /* Number of bytes to read */
686 afs_int32 rSize; /* Total bytes read so far */
687 afs_int32 tSize; /* Temporary size */
688 afs_int32 rc; /* return code */
690 toread = totalSize; /* First, try to read all the data */
692 rc = USD_READ(fid, &data[0], toread, &rSize);
697 if (rSize == 0) /* reached EOF */
700 if (rSize != TapeBlockSize) { /* Tape block size has changed */
701 TapeBlockSize = rSize;
702 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
705 /* Read the rest of the data in */
706 while (rSize < totalSize) {
708 ((totalSize - rSize) <
709 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
710 rc = USD_READ(fid, &data[rSize], toread, &tSize);
719 if (rSize > totalSize)
720 printf("readData - Read > 16K data block - continuing.\n");
726 SeekFile(info, count)
727 struct butm_tapeInfo *info;
732 int cpid, status, rcpid, stat;
738 p = (struct progress *)info->tmRock;
740 if (isafile) { /* no reason for seeking through a file */
741 p->reading = p->writing = 0;
748 error = ForwardSpace(p->fid, count);
750 error = BackwardSpace(p->fid, -count);
755 info->status |= BUTM_STATUS_SEEKERROR;
756 ERROR_EXIT(BUTM_IOCTL);
759 info->position += count;
760 incSize(info, (count * config.fileMarkSize));
761 p = (struct progress *)info->tmRock;
762 p->reading = p->writing = 0;
770 /* Step to the next filemark if we are not at one already */
773 struct butm_tapeInfo *info;
777 if (!READS && !WRITES)
780 code = SeekFile(info, 1);
785 WriteTapeBlock(info, buffer, length, blockType)
786 struct butm_tapeInfo *info;
787 char *buffer; /* assumed to be 16384 bytes with data in it */
788 afs_int32 length; /* amount data in buffer */
791 afs_int32 code = 0, rc = 0;
793 struct tapeLabel *label;
794 struct fileMark *fmark;
795 struct blockMark *bmark;
799 p = (struct progress *)info->tmRock;
801 if (blockType == BLOCK_DATA) { /* Data Block */
804 bmark = (struct blockMark *)buffer;
805 memset(bmark, 0, sizeof(struct blockMark));
806 bmark->magic = htonl(BLOCK_MAGIC);
807 bmark->count = htonl(length);
808 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
809 fmark = (struct fileMark *)buffer;
810 fmark->magic = htonl(FILE_MAGIC);
811 fmark->nBytes = htonl(FILE_BEGIN);
812 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
813 fmark = (struct fileMark *)buffer;
814 fmark->magic = htonl(FILE_MAGIC);
815 fmark->nBytes = htonl(FILE_FMEND);
816 } else if (blockType == BLOCK_LABEL) { /* Label */
817 label = (struct tapeLabel *)buffer;
818 label->magic = htonl(TAPE_MAGIC);
819 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
820 fmark = (struct fileMark *)buffer;
821 fmark->magic = htonl(FILE_MAGIC);
822 fmark->nBytes = htonl(FILE_EOD);
825 /* Write the tape block */
826 /* -------------------- */
827 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
828 if ((rc == 0) && (wsize > 0)) {
829 incPosition(info, p->fid, wsize); /* record whats written */
833 if (wsize != BUTM_BLOCKSIZE) {
834 info->status |= BUTM_STATUS_WRITEERROR;
839 ERROR_EXIT(BUTM_EOT);
844 /* Write trailing EOF marker for some block types */
845 /* ---------------------------------------------- */
846 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
847 || (blockType == BLOCK_EOD)) {
850 error = WriteEOF(p->fid, 1);
853 info->status |= BUTM_STATUS_WRITEERROR;
854 ERROR_EXIT(BUTM_IOCTL);
857 incSize(info, config.fileMarkSize);
870 ReadTapeBlock(info, buffer, blockType)
871 struct butm_tapeInfo *info;
872 char *buffer; /* assumed to be 16384 bytes */
873 afs_int32 *blockType;
876 afs_int32 rsize, fmtype;
877 struct tapeLabel *label;
878 struct fileMark *fmark;
879 struct blockMark *bmark;
882 *blockType = BLOCK_UNKNOWN;
884 p = (struct progress *)info->tmRock;
886 memset(buffer, 0, BUTM_BLOCKSIZE);
887 label = (struct tapeLabel *)buffer;
888 fmark = (struct fileMark *)buffer;
889 bmark = (struct blockMark *)buffer;
891 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
893 incPosition(info, p->fid, rsize);
897 if (rsize == 0) { /* Read a HW EOF Marker? OK */
898 *blockType = BLOCK_EOF;
899 incSize(info, config.fileMarkSize); /* Size of filemark */
901 info->position++; /* bump position */
902 p->reading = 0; /* No reads since EOF */
905 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
906 info->status |= BUTM_STATUS_READERROR;
907 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
910 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
911 *blockType = BLOCK_DATA;
914 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
915 fmtype = ntohl(fmark->nBytes);
917 if (fmtype == FILE_BEGIN) { /* filemark begin */
918 *blockType = BLOCK_FMBEGIN;
919 } else if (fmtype == FILE_FMEND) { /* filemark end */
920 *blockType = BLOCK_FMEND;
921 code = SeekFile(info, 1);
922 } else if (fmtype == FILE_EOD) { /* EOD mark */
923 *blockType = BLOCK_EOD;
924 info->status |= BUTM_STATUS_EOD;
925 code = SeekFile(info, 1);
929 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
930 *blockType = BLOCK_LABEL;
931 code = SeekFile(info, 1);
942 * check version numbers and permissions in the info structure
947 struct butm_tapeInfo *info;
948 int write; /* write operation requested */
953 return (BUTM_BADARGUMENT);
955 /* Check version number in info structure */
956 if (info->structVersion != BUTM_MAJORVERSION)
957 return BUTM_OLDINTERFACE;
959 /* Check if a tape is mounted */
960 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
963 /* If writing check if there is write access */
964 if (write && (info->flags & BUTM_FLAGS_READONLY))
965 return BUTM_READONLY;
972 struct butm_tapeInfo *info;
975 int cpid, status, rcpid, stat;
980 p = (struct progress *)info->tmRock;
984 error = Rewind(p->fid);
989 info->status |= BUTM_STATUS_SEEKERROR;
990 ERROR_EXIT(BUTM_IOCTL);
993 info->position = (isafile ? 0 : 1);
994 info->kBytes = info->nBytes = 0;
995 info->nFiles = info->nRecords = 0;
996 p->reading = p->writing = 0;
1000 info->error = error;
1004 /* =====================================================================
1006 * ===================================================================== */
1009 file_Mount(info, tape)
1010 struct butm_tapeInfo *info;
1016 int cpid, status, rcpid, xflags;
1017 afs_int32 code = 0, error = 0, rc = 0;
1020 printf("butm: Mount tape drive\n");
1026 ERROR_EXIT(BUTM_BADARGUMENT);
1027 if (info->structVersion != BUTM_MAJORVERSION)
1028 ERROR_EXIT(BUTM_OLDINTERFACE);
1030 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1031 if (strlen(tape) >= sizeof(info->name))
1032 ERROR_EXIT(BUTM_BADARGUMENT);
1034 strcpy(info->name, tape);
1036 strcpy(filename, config.tapedir); /* the name of the tape device */
1037 info->position = (isafile ? 0 : 1);
1038 info->kBytes = info->nBytes = 0;
1039 info->nRecords = info->nFiles = 0;
1040 info->recordSize = 0;
1041 info->tapeSize = config.tapeSize;
1042 info->coefBytes = 1;
1043 info->coefRecords = 0;
1044 info->coefFiles = sizeof(struct fileMark);
1045 info->simultaneousTapes = 1;
1048 info->flags = BUTM_FLAGS_SEQUENTIAL;
1052 xflags |= USD_OPEN_CREATE;
1055 * try to open in a child process first so nothing will
1056 * time out should the process block because the device
1060 if (ForkOpen(filename)) {
1061 ERROR_EXIT(BUTM_MOUNTFAIL);
1065 /* Now go ahead and open the tape drive for real */
1066 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1068 if (rc != 0) { /* try for lesser access */
1069 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1073 ERROR_EXIT(BUTM_MOUNTFAIL);
1075 info->flags |= BUTM_FLAGS_READONLY;
1078 (void)PrepareAccess(fid); /* for NT */
1080 p = (struct progress *)malloc(sizeof(*p));
1081 info->tmRock = (char *)p;
1083 p->mountId = config.mountId = time(0);
1084 p->reading = p->writing = 0;
1086 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1090 info->error = error;
1096 struct butm_tapeInfo *info;
1099 int cpid, status, rcpid, stat;
1100 afs_int32 code = 0, error = 0, cd;
1106 printf("butm: Unmount tape drive\n");
1111 code = check(info, READ_OP);
1115 p = (struct progress *)info->tmRock;
1117 (void)ShutdownAccess(p->fid); /* for NT */
1119 /* close the device */
1120 if (error = ForkClose(p->fid)) {
1121 printf("butm: Tape close failed. Error %d\n", errno);
1127 code = BUTM_DISMOUNTFAIL;
1128 info->status |= BUTM_STATUS_TAPEERROR;
1132 info->tmRock = 0; /* mark it as closed - even if error on close */
1138 info->error = error;
1143 * write the header on a tape
1145 * info - handle on tape unit
1146 * label - label information. This label is not copied onto the tape.
1147 * If supplied, various fields are copied from this label to
1148 * the actual tape label written on the tape.
1152 file_WriteLabel(info, label, rewind)
1153 struct butm_tapeInfo *info;
1154 struct butm_tapeLabel *label;
1159 struct tapeLabel *tlabel;
1161 afs_hyper_t off; /* offset */
1164 printf("butm: Write tape label\n");
1169 code = check(info, WRITE_OP);
1173 ERROR_EXIT(BUTM_BADARGUMENT);
1174 if (label->structVersion != CUR_TAPE_VERSION)
1175 ERROR_EXIT(BUTM_OLDINTERFACE);
1177 if (rewind) { /* Not appending, so rewind */
1178 code = rewindFile(info);
1183 p = (struct progress *)info->tmRock;
1185 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1187 ERROR_EXIT(BUTM_POSITION);
1190 if (READS || WRITES)
1191 ERROR_EXIT(BUTM_BADOP);
1194 /* Copy the label into the tape block
1195 * ---------------------------------- */
1196 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1198 if (!label->creationTime)
1199 label->creationTime = time(0);
1201 tlabel = (struct tapeLabel *)tapeBlock;
1202 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1203 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1204 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1205 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1206 tlabel->label.size = htonl(tlabel->label.size);
1207 tlabel->label.useCount = htonl(tlabel->label.useCount);
1208 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1211 * write the tape label - For appends, the write may need to skip
1212 * over 1 or 2 EOF marks that were written when tape was closed after
1213 * the last dump. Plus, some AIX tape drives require we try forwarding
1214 * over the last EOF and take an error before we can write the new label.
1215 * ---------------------------------------------------------------------- */
1216 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1217 if (!isafile && !rewind && (code == BUTM_IO))
1218 do { /* do if write failed */
1219 fcode = SeekFile(info, 1); /* skip over the EOF */
1221 break; /* leave if error */
1224 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1225 if (code != BUTM_IO)
1226 break; /* continue if write failed */
1228 fcode = SeekFile(info, 1); /* skip over the EOF */
1229 if (fcode) { /* retry 1 write if couldn't skip */
1231 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1237 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1238 if (code != BUTM_IO)
1239 break; /* continue if write failed */
1241 fcode = SeekFile(info, 1); /* skip over the EOF */
1242 if (fcode) { /* retry 1 write if couldn't skip */
1244 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1251 /* clear the write error status a failed WriteTapeBlock may have produced */
1253 info->status &= ~BUTM_STATUS_WRITEERROR;
1260 file_ReadLabel(info, label, rewind)
1261 struct butm_tapeInfo *info;
1262 struct butm_tapeLabel *label;
1265 struct tapeLabel *tlabel;
1267 afs_int32 blockType;
1270 printf("butm: Read tape label\n");
1275 code = check(info, READ_OP);
1278 if (READS || WRITES)
1279 ERROR_EXIT(BUTM_BADOP);
1282 code = rewindFile(info);
1284 ERROR_EXIT(code); /* status is set so return */
1288 * When appended labels were written, either 1 or 2 EOF marks may
1289 * have had to be skipped. When reading a label, these EOF marks
1290 * must also be skipped. When an EOF is read, 0 bytes are returned
1291 * (refer to the write calls in file_WriteLabel routine).
1292 * ---------------------------------------------------------------- */
1293 code = ReadTapeBlock(info, tapeBlock, &blockType);
1294 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1296 code = ReadTapeBlock(info, tapeBlock, &blockType);
1297 if (blockType != BLOCK_EOF)
1298 break; /* didn't read an EOF */
1300 code = ReadTapeBlock(info, tapeBlock, &blockType);
1303 if (blockType == BLOCK_UNKNOWN)
1304 ERROR_EXIT(BUTM_NOLABEL);
1307 if (blockType != BLOCK_LABEL)
1308 ERROR_EXIT(BUTM_BADBLOCK);
1313 tlabel = (struct tapeLabel *)tapeBlock;
1314 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1315 label->structVersion = ntohl(label->structVersion);
1316 label->creationTime = ntohl(label->creationTime);
1317 label->expirationDate = ntohl(label->expirationDate);
1318 label->size = ntohl(label->size);
1319 label->dumpid = ntohl(label->dumpid);
1320 label->useCount = ntohl(label->useCount);
1322 info->tapeSize = label->size; /* use size from label */
1330 file_WriteFileBegin(info)
1331 struct butm_tapeInfo *info;
1334 afs_int32 error = 0;
1337 printf("butm: Write filemark begin\n");
1341 code = check(info, WRITE_OP);
1345 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1356 file_ReadFileBegin(info)
1357 struct butm_tapeInfo *info;
1359 struct fileMark mark;
1361 afs_int32 blockType;
1364 printf("butm: Read filemark begin\n");
1369 code = check(info, READ_OP);
1372 if (READS || WRITES)
1373 ERROR_EXIT(BUTM_BADOP);
1375 code = ReadTapeBlock(info, tapeBlock, &blockType);
1379 if (blockType != BLOCK_FMBEGIN) {
1380 if (blockType == BLOCK_EOD)
1381 ERROR_EXIT(BUTM_EOD); /* EODump label */
1382 if (blockType == BLOCK_LABEL)
1383 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1384 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1391 /* Writes data out in block sizes of 16KB. Does destroy the data.
1392 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1395 file_WriteFileData(info, data, blocks, len)
1396 struct butm_tapeInfo *info;
1404 char *bstart; /* Where block starts for a 16K block */
1405 char *dstart; /* Where data starts for a 16K block */
1408 printf("butm: Write tape data - %u bytes\n", len);
1413 code = check(info, WRITE_OP);
1416 if (!data || (len < 0))
1417 ERROR_EXIT(BUTM_BADARGUMENT);
1418 if (READS || !WRITES)
1419 ERROR_EXIT(BUTM_BADOP);
1421 b = 0; /* start at block 0 */
1423 dstart = &data[b * BUTM_BLKSIZE];
1424 bstart = dstart - sizeof(struct blockMark);
1426 if (len < BUTM_BLKSIZE) {
1427 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1430 length = BUTM_BLKSIZE;
1433 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1437 /* If there are more blocks, step to next block */
1438 /* Otherwise, copy the data to beginning of last block */
1440 if (b < (blocks - 1))
1443 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1450 /* file_ReadFileData
1451 * Read a data block from tape.
1453 * info - tape info structure, c.f. fid
1454 * data - ptr to buffer for data
1455 * len - size of data buffer
1457 * nBytes - no. of data bytes read.
1461 file_ReadFileData(info, data, len, nBytes)
1462 struct butm_tapeInfo *info;
1467 struct blockMark *bmark;
1469 afs_int32 blockType;
1472 printf("butm: Read tape data - %u bytes\n", len);
1478 ERROR_EXIT(BUTM_BADARGUMENT);
1481 code = check(info, READ_OP);
1484 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1485 ERROR_EXIT(BUTM_BADARGUMENT);
1486 if (!READS || WRITES)
1487 ERROR_EXIT(BUTM_BADOP);
1489 data -= sizeof(struct blockMark);
1490 code = ReadTapeBlock(info, data, &blockType);
1494 if (blockType != BLOCK_DATA) {
1495 if (blockType == BLOCK_EOF)
1496 ERROR_EXIT(BUTM_EOF);
1497 if (blockType == BLOCK_FMEND)
1498 ERROR_EXIT(BUTM_ENDVOLUME);
1499 ERROR_EXIT(BUTM_BADBLOCK);
1502 bmark = (struct blockMark *)data;
1503 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1510 file_WriteFileEnd(info)
1511 struct butm_tapeInfo *info;
1516 printf("butm: Write filemark end\n");
1521 code = check(info, WRITE_OP);
1524 if (READS || !WRITES)
1525 ERROR_EXIT(BUTM_BADOP);
1527 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1533 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1534 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1535 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1536 * missing with some 3.1 dumps).
1539 file_ReadFileEnd(info)
1540 struct butm_tapeInfo *info;
1542 struct fileMark mark;
1544 afs_int32 blockType;
1547 printf("butm: Read filemark end\n");
1552 code = check(info, READ_OP);
1555 if (!READS || WRITES)
1556 ERROR_EXIT(BUTM_BADOP);
1558 info->status &= ~BUTM_STATUS_EOF;
1560 code = ReadTapeBlock(info, tapeBlock, &blockType);
1564 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1565 ERROR_EXIT(BUTM_BADBLOCK);
1572 * Write the end-of-dump marker.
1575 file_WriteEODump(info)
1576 struct butm_tapeInfo *info;
1581 printf("butm: Write filemark EOD\n");
1586 code = check(info, WRITE_OP);
1589 if (READS || WRITES)
1590 ERROR_EXIT(BUTM_BADOP);
1592 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1596 info->status |= BUTM_STATUS_EOD;
1603 file_Seek(info, position)
1604 struct butm_tapeInfo *info;
1612 afs_hyper_t startOff, stopOff; /* for normal file(non-tape) seeks */
1615 printf("butm: Seek to the tape position %d\n", position);
1619 code = check(info, READ_OP);
1624 p = (struct progress *)info->tmRock;
1625 posit = (osi_lloff_t)position * (osi_lloff_t)BUTM_BLOCKSIZE;
1627 /* Not really necessary to do it this way, should be fixed */
1630 d = (posit & 0xffffffff);
1635 hset64(startOff, c, d);
1637 w = USD_SEEK(p->fid, startOff, SEEK_SET, &stopOff);
1640 if (hcmp(startOff, stopOff) != 0)
1641 ERROR_EXIT(BUTM_POSITION);
1643 p->reading = p->writing = 0;
1644 info->position = position;
1646 /* Don't position backwards if we are in-between FMs */
1647 if ((READS || WRITES) && ((position - info->position) <= 0))
1648 ERROR_EXIT(BUTM_BADOP);
1650 code = SeekFile(info, (position - info->position));
1660 * Seek to the EODump (end-of-dump) after the given position. This is
1661 * the position after the EOF filemark immediately after the EODump mark.
1662 * This is for tapes of version 4 or greater.
1665 file_SeekEODump(info, position)
1666 struct butm_tapeInfo *info;
1669 struct fileMark mark;
1671 afs_int32 blockType;
1674 afs_hyper_t startOff, stopOff; /* file seek offsets */
1677 printf("butm: Seek to end-of-dump\n");
1680 code = check(info, READ_OP);
1683 if (READS || WRITES)
1684 ERROR_EXIT(BUTM_BADOP);
1687 p = (struct progress *)info->tmRock;
1688 hset64(startOff, 0, 0);
1689 w = USD_SEEK(p->fid, startOff, SEEK_END, &stopOff);
1693 ERROR_EXIT(BUTM_POSITION);
1695 if (hgetlo(stopOff) % BUTM_BLOCKSIZE)
1696 ERROR_EXIT(BUTM_POSITION);
1697 info->position = (hgetlo(stopOff) / BUTM_BLOCKSIZE);
1699 /* Seek to the desired position */
1700 code = SeekFile(info, (position - info->position) + 1);
1705 * Search until the filemark is an EODump filemark.
1706 * Skip over volumes only.
1709 code = ReadTapeBlock(info, tapeBlock, &blockType);
1713 if (blockType == BLOCK_EOD)
1715 if (blockType != BLOCK_FMBEGIN)
1716 ERROR_EXIT(BUTM_BADBLOCK);
1718 code = SeekFile(info, 1); /* Step forward to next volume */
1730 file_SetSize(info, size)
1731 struct butm_tapeInfo *info;
1735 printf("butm: Set size of tape\n");
1739 info->tapeSize = config.tapeSize;
1741 info->tapeSize = size;
1746 file_GetSize(info, size)
1747 struct butm_tapeInfo *info;
1751 printf("butm: Get size of tape\n");
1754 *size = info->tapeSize;
1758 /* =====================================================================
1759 * Startup/configuration routines.
1760 * ===================================================================== */
1763 file_Configure(file)
1764 struct tapeConfig *file;
1767 com_err(whoami, BUTM_BADCONFIG, "device not specified");
1768 return BUTM_BADCONFIG;
1771 config.tapeSize = file->capacity;
1772 config.fileMarkSize = file->fileMarkSize;
1773 config.portOffset = file->portOffset;
1774 strcpy(config.tapedir, file->device);
1776 /* Tape must be large enough to at least fit a label */
1777 if (config.tapeSize <= 0) {
1778 com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1780 return BUTM_BADCONFIG;
1783 if (strlen(config.tapedir) == 0) {
1784 com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1785 return BUTM_BADCONFIG;
1792 /* This procedure instantiates a tape module of type file_tm. */
1794 butm_file_Instantiate(info, file)
1795 struct butm_tapeInfo *info;
1796 struct tapeConfig *file;
1798 extern int debugLevel;
1801 if (debugLevel > 98)
1802 printf("butm: Instantiate butc\n");
1805 ERROR_EXIT(BUTM_BADARGUMENT);
1806 if (info->structVersion != BUTM_MAJORVERSION)
1807 ERROR_EXIT(BUTM_OLDINTERFACE);
1809 memset(info, 0, sizeof(struct butm_tapeInfo));
1810 info->structVersion = BUTM_MAJORVERSION;
1811 info->ops.mount = file_Mount;
1812 info->ops.dismount = file_Dismount;
1813 info->ops.create = file_WriteLabel;
1814 info->ops.readLabel = file_ReadLabel;
1815 info->ops.seek = file_Seek;
1816 info->ops.seekEODump = file_SeekEODump;
1817 info->ops.readFileBegin = file_ReadFileBegin;
1818 info->ops.readFileData = file_ReadFileData;
1819 info->ops.readFileEnd = file_ReadFileEnd;
1820 info->ops.writeFileBegin = file_WriteFileBegin;
1821 info->ops.writeFileData = file_WriteFileData;
1822 info->ops.writeFileEnd = file_WriteFileEnd;
1823 info->ops.writeEOT = file_WriteEODump;
1824 info->ops.setSize = file_SetSize;
1825 info->ops.getSize = file_GetSize;
1826 info->debug = ((debugLevel > 98) ? 1 : 0);
1828 code = file_Configure(file);