2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
15 #ifdef HAVE_SYS_WAIT_H
23 #include <afs/com_err.h>
27 #include "error_macros.h"
28 #include "butm_prototypes.h"
31 typedef off64_t osi_lloff_t;
32 #else /* O_LARGEFILE */
33 #ifdef AFS_HAVE_LLSEEK
34 typedef offset_t osi_lloff_t;
35 #else /* AFS_HAVE_LLSEEK */
36 typedef off_t osi_lloff_t;
37 #endif /* AFS_HAVE_LLSEEK */
38 #endif /* O_LARGEFILE */
42 #define FILE_MAGIC 1000000007 /* s/w file mark */
43 #define FILE_BEGIN 0 /* byte field in file mark */
44 #define FILE_FMEND 1 /* byte field in file mark */
45 #define FILE_EOD -1 /* byte field in file mark */
46 #define TAPE_MAGIC 1100000009 /* tape label block */
47 #define BLOCK_MAGIC 1100000005 /* file data block */
48 #ifdef AFS_PTHREAD_ENV
50 #define SLEEP(s) sleep(s)
52 #define POLL() IOMGR_Poll()
53 #define SLEEP(s) IOMGR_Sleep(s)
58 * 1) filemarks and block marks have the magic,bytes fields reversed. This
59 * is undoubtedly a bug. Also note that the two structures have
60 * inconsistent types, overlaying int and afs_int32.
61 * 2) When a volume is dumped, if the volume is locked, the dump will produce
62 * an anomalous tape format of the form:
66 * The design of the original butm code means that this cannot be
67 * handled correctly. The code is modified so that ReadFileData
68 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
71 /* data organization on tape:
72 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
73 * blockMark contains a magic number and counts of real data bytes
74 * written out in the block.
76 * each file is preceeded by a fileMark, which acts as the file
77 * delimiter. A file consists of contigous data blocks. TM does
78 * understand or interrpet the data in data blocks.
80 * The tape begins with a tape label and ends with EOF file markers
81 * in succession (2 or 4 of them ).
85 struct fileMark { /* in network byte order */
92 struct butm_tapeLabel label;
96 usd_handle_t fid; /* file id of simulated tape */
97 afs_int32 mountId; /* current mountId */
98 afs_int32 reading; /* read file operation in progress */
99 afs_int32 writing; /* write file operation in progress */
102 static struct configuration {
103 char tapedir[64]; /* directory to create "tapes" */
104 afs_int32 mountId; /* for detecting simultaneous mounts */
105 afs_uint32 tapeSize; /* size of simulated tapes */
106 afs_uint32 fileMarkSize; /* size of file mark, bytes */
107 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
110 static char *whoami = "file_tm";
111 char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
113 #define BLOCK_LABEL 0 /* read/write a tape label */
114 #define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
115 #define BLOCK_DATA 2 /* read/write a data block */
116 #define BLOCK_FMEND 3 /* read/write an end FileMark */
117 #define BLOCK_EOD 4 /* read/write an EOD FileMark */
118 #define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
119 #define BLOCK_UNKNOWN 6 /* tape block is unknwon (error) */
124 #define READS (((struct progress *)(info->tmRock))->reading)
125 #define WRITES (((struct progress *)(info->tmRock))->writing)
127 /* ----------------------------------------------------------------------
128 * These routines use the usd library to perform tape operations.
129 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
130 * PrepareAccess(nt), ShutdownAccess(nt)
132 * Return Values: USD functions return 0 if successful or errno if failed.
134 /* ------------------ USD Interface Functions Begin ------------------------ */
137 * On Unix, fork a child process to perform an IOCTL call. This avoids
138 * blocking the entire process during a tape operation
142 /* Unix version of function */
145 ForkIoctl(usd_handle_t fd, int op, int count)
147 int rc; /* return code from system calls */
148 int i; /* loop index */
149 int pid; /* process ID of child process */
150 int status; /* exit status of child process */
151 int ioctl_rc; /* return code from ioctl call */
152 int pipefd[2]; /* pipe for child return status */
153 int forkflag; /* flag set when ready to fork */
154 usd_tapeop_t tapeop; /* tape operation specification */
157 #ifdef AFS_PTHREAD_ENV
158 forkflag = 0; /* No need to fork if using pthreads */
163 /* initialize tape command structure */
165 tapeop.tp_count = count;
167 /* create pipe for getting return code from child */
171 printf("butm: Can't open pipe for IOCTL process. Error %d\n",
182 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
188 if (!forkflag) { /* problem starting child process */
189 /* do the ioctl anyway, it will probably work */
190 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
191 } else if (pid == 0) { /* child process */
192 /* close all unneccessary file descriptors */
193 /* note: as painful as it is, we have to reach under the covers of
194 * the usd package to implement this functionality.
196 unixfd = (intptr_t)(fd->handle);
198 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
199 if (i != unixfd && i != pipefd[1]) {
204 /* do the ioctl call */
205 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
208 * Send the return code back to the parent.
209 * If this fails, there's nothing we can do, but we must test
210 * it in order to avoid complier warnings on some platforms.
212 if (write(pipefd[1], &ioctl_rc, sizeof(int)) < 0) {
217 } else { /* parent process */
221 /* read the result from the child process */
222 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
223 if (rc != sizeof(int)) {
224 /* tape is now in unknown state */
225 printf("butm: Can't determine IOCTL child status. Error %d\n",
232 /* get the completion status from the child process */
233 rc = waitpid(pid, &status, 0);
234 while (rc < 0 && errno == EINTR) {
235 rc = waitpid(pid, &status, 0);
238 printf("butm: Can't determine IOCTL child status. Error %d\n",
240 } else if (status != 0) {
242 ("butm: Unexpected IOCTL process status 0x%04x . Error %d\n",
251 /* NT version of function */
254 ForkIoctl(usd_handle_t fd, int op, int count)
258 /* Issue requested tape control */
260 tapeop.tp_count = count;
262 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
264 #endif /* !AFS_NT40_ENV */
268 * On Unix, fork a child process to attempt to open the drive. We want to make
269 * certain there is tape in the drive before trying to open the device
270 * in the main process
274 /* Unix version of function. */
277 ForkOpen(char *device)
279 int rc; /* return code from system calls */
280 int i; /* loop index */
281 int pid; /* process ID of child process */
282 int status; /* exit status of child process */
283 int open_rc; /* return code from open */
284 int pipefd[2]; /* pipe for child return status */
285 int forkflag; /* flag set when ready to fork */
286 usd_handle_t fd; /* handle returned from open */
288 #ifdef AFS_PTHREAD_ENV
289 forkflag = 0; /* No need to fork if using pthreads */
294 /* create pipe for getting return code from child */
298 printf("butm: Cannot create pipe for OPEN process. Error %d\n",
309 printf("butm: Cannot create child process for OPEN. Error %d\n",
315 if (!forkflag) { /* problem starting child process */
317 *return success, the caller will discover any problems
318 * when it opens the device.
321 } else if (pid == 0) { /* child process */
322 /* close all unneccessary file descriptors */
323 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
324 if (i != pipefd[1]) {
330 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
337 * Send the return code back to the parent.
338 * If this fails, there's nothing we can do, but we must test
339 * it in order to avoid complier warnings on some platforms.
341 if (write(pipefd[1], &open_rc, sizeof(open_rc)) < 0) {
346 } else { /* parent process */
351 /* read the result from the child process */
352 rc = read(pipefd[0], &open_rc, sizeof(open_rc));
353 if (rc != sizeof(open_rc)) {
354 /* this is not a problem since we will reopen the device anyway */
355 printf("butm: No response from OPEN process. Error %d\n", errno);
361 /* get the completion status from the child process */
362 rc = waitpid(pid, &status, 0);
363 while (rc < 0 && errno == EINTR) {
364 rc = waitpid(pid, &status, 0);
367 printf("butm: Cannot get status of OPEN process. Error %d\n",
369 } else if (status != 0) {
371 ("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n",
380 /* NT version of function. */
383 ForkOpen(char *device)
387 #endif /* AFS_NT40_ENV */
390 * On Unix, fork a child process to close the drive. If the drive rewinds
391 * on close it could cause the process to block.
395 /* Unix version of function */
398 ForkClose(usd_handle_t fd)
400 int rc; /* return code from system calls */
401 int i; /* loop index */
402 int pid; /* process ID of child process */
403 int status; /* exit status of child process */
404 int close_rc, parent_close_rc; /* return codes from close */
405 int pipefd[2]; /* pipe for child return status */
406 int ctlpipe[2]; /* pipe for message to child */
407 int forkflag; /* flag set when ready to fork */
410 #ifdef AFS_PTHREAD_ENV
411 forkflag = 0; /* No need to fork if using pthreads */
416 /* create pipe for getting return code from child */
420 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
426 /* create pipe for notifying child when to close */
432 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
445 printf("butm: Cannot create CLOSE child process. Error %d\n",
451 if (!forkflag) { /* problem starting child process */
452 close_rc = USD_CLOSE(fd);
453 parent_close_rc = close_rc;
454 } else if (pid == 0) { /* child process */
455 /* close all unneccessary file descriptors */
456 /* note: as painful as it is, we have to reach under the covers of
457 * the usd package to implement this functionality.
459 unixfd = (intptr_t)(fd->handle);
461 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
462 if (i != unixfd && i != ctlpipe[0] && i != pipefd[1]) {
468 * The parent writes the control pipe after it closes the device.
469 * We don't actually care about the read; we're just using it to
470 * block until the parent is ready. But we must do something
471 * with the result, to avoid complier warnings on some platforms.
473 if (read(ctlpipe[0], &close_rc, sizeof(int)) < 0) {
479 close_rc = USD_CLOSE(fd);
482 * Send the return code back to the parent.
483 * If this fails, there's nothing we can do, but we must test
484 * it in order to avoid complier warnings on some platforms.
486 if (write(pipefd[1], &close_rc, sizeof(int)) < 0) {
491 } else { /* parent process */
498 * close the device, this should have no effect as long as the
499 * child has not closed
502 parent_close_rc = USD_CLOSE(fd);
504 /* notify the child to do its close */
505 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
506 if (rc != sizeof(int)) {
507 printf("butm: Error communicating with CLOSE process. Error %d\n",
512 /* read the result from the child process */
513 rc = read(pipefd[0], &close_rc, sizeof(int));
514 if (rc != sizeof(int)) {
515 /* logging is enough, since we wrote a file mark the */
516 /* return code from the close doesn't really matter */
517 printf("butm: No response from CLOSE process. Error %d\n",
524 /* get the completion status from the child process */
525 rc = waitpid(pid, &status, 0);
526 while (rc < 0 && errno == EINTR) {
527 rc = waitpid(pid, &status, 0);
530 printf("butm: Cannot get status of CLOSE process. Error %d\n",
532 } else if (status != 0) {
534 ("butm: Unexpected exit status 0x%04x from CLOSE process. Error %d\n",
538 /* if either process received an error, then return an error */
539 if (parent_close_rc < 0) {
540 close_rc = parent_close_rc;
548 /* NT version of function */
551 ForkClose(usd_handle_t fd)
553 return (USD_CLOSE(fd));
555 #endif /* AFS_NT40_ENV */
557 /* Forward space file */
559 ForwardSpace(usd_handle_t fid, int count)
566 return (ForkIoctl(fid, USDTAPE_FSF, count));
570 /* Backward space file */
572 BackwardSpace(usd_handle_t fid, int count)
579 return (ForkIoctl(fid, USDTAPE_BSF, count));
583 /* write end of file mark */
585 WriteEOF(usd_handle_t fid, int count)
592 return (ForkIoctl(fid, USDTAPE_WEOF, count));
598 Rewind(usd_handle_t fid)
603 return (USD_SEEK(fid, 0, SEEK_SET, &stopOff));
605 return (ForkIoctl(fid, USDTAPE_REW, 0));
609 /* prepare tape drive for access */
611 PrepareAccess(usd_handle_t fid)
616 (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
618 /* NT won't rewind tape when it is opened */
620 #endif /* AFS_NT40_ENV */
624 /* decommission tape drive after all accesses complete */
626 ShutdownAccess(usd_handle_t fid)
630 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
632 #endif /* AFS_NT40_ENV */
636 /* -------------------- USD Interface Functions End ----------------------- */
638 /* =====================================================================
640 * ===================================================================== */
643 * add the supplied no. of bytes to the byte count of information placed
646 * dataSize - bytes used on the tape
650 incSize(struct butm_tapeInfo *info, afs_uint32 dataSize)
652 info->nBytes += dataSize;
653 info->kBytes += (info->nBytes / 1024);
654 info->nBytes %= 1024;
658 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
659 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
661 * add the supplied no. of bytes to the byte count of data placed
662 * on the tape. Also check for reaching 2GB limit and reset the
663 * pointer if necessary. This allows us to use >2GB tapes.
665 * fid - file id for the tape.
666 * dataSize - bytes used on the tape
670 incPosition(struct butm_tapeInfo *info, usd_handle_t fid, afs_uint32 dataSize)
672 /* Add this to the amount of data written to the tape */
673 incSize(info, dataSize);
675 info->posCount += dataSize;
677 if (info->posCount >= 2147467264) { /* 2GB - 16K */
679 #if (defined(AFS_SUN_ENV) || defined(AFS_LINUX24_ENV))
684 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
691 * This accounts for tape drives with a block size different from variable or 16K
692 * blocks and only reads that block size.
694 afs_int32 TapeBlockSize;
696 readData(usd_handle_t fid, char *data, afs_uint32 totalSize, afs_int32 *errorP)
698 afs_int32 toread; /* Number of bytes to read */
699 afs_uint32 rSize; /* Total bytes read so far */
700 afs_uint32 tSize; /* Temporary size */
701 afs_int32 rc; /* return code */
703 toread = totalSize; /* First, try to read all the data */
705 rc = USD_READ(fid, &data[0], toread, &rSize);
710 if (rSize == 0) /* reached EOF */
713 if (rSize != TapeBlockSize) { /* Tape block size has changed */
714 TapeBlockSize = rSize;
715 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
718 /* Read the rest of the data in */
719 while (rSize < totalSize) {
721 ((totalSize - rSize) <
722 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
723 rc = USD_READ(fid, &data[rSize], toread, &tSize);
732 if (rSize > totalSize)
733 printf("readData - Read > 16K data block - continuing.\n");
739 SeekFile(struct butm_tapeInfo *info, int count)
747 p = (struct progress *)info->tmRock;
749 if (isafile) { /* no reason for seeking through a file */
750 p->reading = p->writing = 0;
757 error = ForwardSpace(p->fid, count);
759 error = BackwardSpace(p->fid, -count);
764 info->status |= BUTM_STATUS_SEEKERROR;
765 ERROR_EXIT(BUTM_IOCTL);
768 info->position += count;
769 incSize(info, (count * config.fileMarkSize));
770 p = (struct progress *)info->tmRock;
771 p->reading = p->writing = 0;
779 /* Step to the next filemark if we are not at one already */
781 NextFile(struct butm_tapeInfo *info)
785 if (!READS && !WRITES)
788 code = SeekFile(info, 1);
793 WriteTapeBlock(struct butm_tapeInfo *info,
794 char *buffer, /* assumed to be 16384 bytes with data in it */
795 afs_int32 length, /* amount data in buffer */
798 afs_int32 code = 0, rc = 0;
800 struct tapeLabel *label;
801 struct fileMark *fmark;
802 struct blockMark *bmark;
806 p = (struct progress *)info->tmRock;
808 if (blockType == BLOCK_DATA) { /* Data Block */
811 bmark = (struct blockMark *)buffer;
812 memset(bmark, 0, sizeof(struct blockMark));
813 bmark->magic = htonl(BLOCK_MAGIC);
814 bmark->count = htonl(length);
815 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
816 fmark = (struct fileMark *)buffer;
817 fmark->magic = htonl(FILE_MAGIC);
818 fmark->nBytes = htonl(FILE_BEGIN);
819 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
820 fmark = (struct fileMark *)buffer;
821 fmark->magic = htonl(FILE_MAGIC);
822 fmark->nBytes = htonl(FILE_FMEND);
823 } else if (blockType == BLOCK_LABEL) { /* Label */
824 label = (struct tapeLabel *)buffer;
825 label->magic = htonl(TAPE_MAGIC);
826 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
827 fmark = (struct fileMark *)buffer;
828 fmark->magic = htonl(FILE_MAGIC);
829 fmark->nBytes = htonl(FILE_EOD);
832 /* Write the tape block */
833 /* -------------------- */
834 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
835 if ((rc == 0) && (wsize > 0)) {
836 incPosition(info, p->fid, wsize); /* record whats written */
840 if (wsize != BUTM_BLOCKSIZE) {
841 info->status |= BUTM_STATUS_WRITEERROR;
846 ERROR_EXIT(BUTM_EOT);
851 /* Write trailing EOF marker for some block types */
852 /* ---------------------------------------------- */
853 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
854 || (blockType == BLOCK_EOD)) {
857 error = WriteEOF(p->fid, 1);
860 info->status |= BUTM_STATUS_WRITEERROR;
861 ERROR_EXIT(BUTM_IOCTL);
864 incSize(info, config.fileMarkSize);
877 ReadTapeBlock(struct butm_tapeInfo *info,
878 char *buffer, /* assumed to be 16384 bytes */
879 afs_int32 *blockType)
882 afs_int32 rsize, fmtype;
883 struct tapeLabel *label;
884 struct fileMark *fmark;
885 struct blockMark *bmark;
888 *blockType = BLOCK_UNKNOWN;
890 p = (struct progress *)info->tmRock;
892 memset(buffer, 0, BUTM_BLOCKSIZE);
893 label = (struct tapeLabel *)buffer;
894 fmark = (struct fileMark *)buffer;
895 bmark = (struct blockMark *)buffer;
897 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
899 incPosition(info, p->fid, rsize);
903 if (rsize == 0) { /* Read a HW EOF Marker? OK */
904 *blockType = BLOCK_EOF;
905 incSize(info, config.fileMarkSize); /* Size of filemark */
907 info->position++; /* bump position */
908 p->reading = 0; /* No reads since EOF */
911 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
912 info->status |= BUTM_STATUS_READERROR;
913 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
916 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
917 *blockType = BLOCK_DATA;
920 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
921 fmtype = ntohl(fmark->nBytes);
923 if (fmtype == FILE_BEGIN) { /* filemark begin */
924 *blockType = BLOCK_FMBEGIN;
925 } else if (fmtype == FILE_FMEND) { /* filemark end */
926 *blockType = BLOCK_FMEND;
927 code = SeekFile(info, 1);
928 } else if (fmtype == FILE_EOD) { /* EOD mark */
929 *blockType = BLOCK_EOD;
930 info->status |= BUTM_STATUS_EOD;
931 code = SeekFile(info, 1);
935 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
936 *blockType = BLOCK_LABEL;
937 code = SeekFile(info, 1);
948 * check version numbers and permissions in the info structure
952 check(struct butm_tapeInfo *info,
953 int write) /* write operation requested */
958 return (BUTM_BADARGUMENT);
960 /* Check version number in info structure */
961 if (info->structVersion != BUTM_MAJORVERSION)
962 return BUTM_OLDINTERFACE;
964 /* Check if a tape is mounted */
965 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
968 /* If writing check if there is write access */
969 if (write && (info->flags & BUTM_FLAGS_READONLY))
970 return BUTM_READONLY;
976 rewindFile(struct butm_tapeInfo *info)
982 p = (struct progress *)info->tmRock;
986 error = Rewind(p->fid);
991 info->status |= BUTM_STATUS_SEEKERROR;
992 ERROR_EXIT(BUTM_IOCTL);
995 info->position = (isafile ? 0 : 1);
996 info->kBytes = info->nBytes = 0;
997 info->nFiles = info->nRecords = 0;
998 p->reading = p->writing = 0;
1002 info->error = error;
1006 /* =====================================================================
1008 * ===================================================================== */
1011 file_Mount(struct butm_tapeInfo *info, char *tape)
1017 afs_int32 code = 0, error = 0, rc = 0;
1020 ERROR_EXIT(BUTM_BADARGUMENT);
1023 printf("butm: Mount tape drive\n");
1028 if (info->structVersion != BUTM_MAJORVERSION)
1029 ERROR_EXIT(BUTM_OLDINTERFACE);
1031 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1032 if (strlen(tape) >= sizeof(info->name))
1033 ERROR_EXIT(BUTM_BADARGUMENT);
1035 strcpy(info->name, tape);
1037 strcpy(filename, config.tapedir); /* the name of the tape device */
1038 info->position = (isafile ? 0 : 1);
1039 info->kBytes = info->nBytes = 0;
1040 info->nRecords = info->nFiles = 0;
1041 info->recordSize = 0;
1042 info->tapeSize = config.tapeSize;
1043 info->coefBytes = 1;
1044 info->coefRecords = 0;
1045 info->coefFiles = sizeof(struct fileMark);
1046 info->simultaneousTapes = 1;
1049 info->flags = BUTM_FLAGS_SEQUENTIAL;
1053 xflags |= USD_OPEN_CREATE;
1056 * try to open in a child process first so nothing will
1057 * time out should the process block because the device
1061 if (ForkOpen(filename)) {
1062 ERROR_EXIT(BUTM_MOUNTFAIL);
1066 /* Now go ahead and open the tape drive for real */
1067 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1069 if (rc != 0) { /* try for lesser access */
1070 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1074 ERROR_EXIT(BUTM_MOUNTFAIL);
1076 info->flags |= BUTM_FLAGS_READONLY;
1079 (void)PrepareAccess(fid); /* for NT */
1081 p = malloc(sizeof(*p));
1082 info->tmRock = (char *)p;
1084 p->mountId = config.mountId = time(0);
1085 p->reading = p->writing = 0;
1087 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1091 info->error = error;
1096 file_Dismount(struct butm_tapeInfo *info)
1099 afs_int32 code = 0, error = 0;
1102 printf("butm: Unmount tape drive\n");
1107 code = check(info, READ_OP);
1111 p = (struct progress *)info->tmRock;
1113 (void)ShutdownAccess(p->fid); /* for NT */
1115 /* close the device */
1116 if ((error = ForkClose(p->fid))) {
1117 printf("butm: Tape close failed. Error %d\n", errno);
1123 code = BUTM_DISMOUNTFAIL;
1124 info->status |= BUTM_STATUS_TAPEERROR;
1128 info->tmRock = 0; /* mark it as closed - even if error on close */
1134 info->error = error;
1139 * write the header on a tape
1141 * info - handle on tape unit
1142 * label - label information. This label is not copied onto the tape.
1143 * If supplied, various fields are copied from this label to
1144 * the actual tape label written on the tape.
1148 file_WriteLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1153 struct tapeLabel *tlabel;
1155 afs_int64 off; /* offset */
1158 printf("butm: Write tape label\n");
1163 code = check(info, WRITE_OP);
1167 ERROR_EXIT(BUTM_BADARGUMENT);
1168 if (label->structVersion != CUR_TAPE_VERSION)
1169 ERROR_EXIT(BUTM_OLDINTERFACE);
1171 if (rewind) { /* Not appending, so rewind */
1172 code = rewindFile(info);
1177 p = (struct progress *)info->tmRock;
1179 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1181 ERROR_EXIT(BUTM_POSITION);
1184 if (READS || WRITES)
1185 ERROR_EXIT(BUTM_BADOP);
1188 /* Copy the label into the tape block
1189 * ---------------------------------- */
1190 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1192 if (!label->creationTime)
1193 label->creationTime = time(0);
1195 tlabel = (struct tapeLabel *)tapeBlock;
1196 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1197 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1198 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1199 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1200 tlabel->label.size = htonl(tlabel->label.size);
1201 tlabel->label.useCount = htonl(tlabel->label.useCount);
1202 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1205 * write the tape label - For appends, the write may need to skip
1206 * over 1 or 2 EOF marks that were written when tape was closed after
1207 * the last dump. Plus, some AIX tape drives require we try forwarding
1208 * over the last EOF and take an error before we can write the new label.
1209 * ---------------------------------------------------------------------- */
1210 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1211 if (!isafile && !rewind && (code == BUTM_IO))
1212 do { /* do if write failed */
1213 fcode = SeekFile(info, 1); /* skip over the EOF */
1215 break; /* leave if error */
1218 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1219 if (code != BUTM_IO)
1220 break; /* continue if write failed */
1222 fcode = SeekFile(info, 1); /* skip over the EOF */
1223 if (fcode) { /* retry 1 write if couldn't skip */
1225 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1231 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1232 if (code != BUTM_IO)
1233 break; /* continue if write failed */
1235 fcode = SeekFile(info, 1); /* skip over the EOF */
1236 if (fcode) { /* retry 1 write if couldn't skip */
1238 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1245 /* clear the write error status a failed WriteTapeBlock may have produced */
1247 info->status &= ~BUTM_STATUS_WRITEERROR;
1254 file_ReadLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1257 struct tapeLabel *tlabel;
1259 afs_int32 blockType;
1262 printf("butm: Read tape label\n");
1267 code = check(info, READ_OP);
1270 if (READS || WRITES)
1271 ERROR_EXIT(BUTM_BADOP);
1274 code = rewindFile(info);
1276 ERROR_EXIT(code); /* status is set so return */
1280 * When appended labels were written, either 1 or 2 EOF marks may
1281 * have had to be skipped. When reading a label, these EOF marks
1282 * must also be skipped. When an EOF is read, 0 bytes are returned
1283 * (refer to the write calls in file_WriteLabel routine).
1284 * ---------------------------------------------------------------- */
1285 code = ReadTapeBlock(info, tapeBlock, &blockType);
1286 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1288 code = ReadTapeBlock(info, tapeBlock, &blockType);
1289 if (blockType != BLOCK_EOF)
1290 break; /* didn't read an EOF */
1292 code = ReadTapeBlock(info, tapeBlock, &blockType);
1295 if (blockType == BLOCK_UNKNOWN)
1296 ERROR_EXIT(BUTM_NOLABEL);
1299 if (blockType != BLOCK_LABEL)
1300 ERROR_EXIT(BUTM_BADBLOCK);
1305 tlabel = (struct tapeLabel *)tapeBlock;
1306 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1307 label->structVersion = ntohl(label->structVersion);
1308 label->creationTime = ntohl(label->creationTime);
1309 label->expirationDate = ntohl(label->expirationDate);
1310 label->size = ntohl(label->size);
1311 label->dumpid = ntohl(label->dumpid);
1312 label->useCount = ntohl(label->useCount);
1314 info->tapeSize = label->size; /* use size from label */
1322 file_WriteFileBegin(struct butm_tapeInfo *info)
1327 printf("butm: Write filemark begin\n");
1331 code = check(info, WRITE_OP);
1335 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1346 file_ReadFileBegin(struct butm_tapeInfo *info)
1349 afs_int32 blockType;
1352 printf("butm: Read filemark begin\n");
1357 code = check(info, READ_OP);
1360 if (READS || WRITES)
1361 ERROR_EXIT(BUTM_BADOP);
1363 code = ReadTapeBlock(info, tapeBlock, &blockType);
1367 if (blockType != BLOCK_FMBEGIN) {
1368 if (blockType == BLOCK_EOD)
1369 ERROR_EXIT(BUTM_EOD); /* EODump label */
1370 if (blockType == BLOCK_LABEL)
1371 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1372 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1379 /* Writes data out in block sizes of 16KB. Does destroy the data.
1380 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1383 file_WriteFileData(struct butm_tapeInfo *info, char *data, afs_int32 blocks, afs_int32 len)
1388 char *bstart; /* Where block starts for a 16K block */
1389 char *dstart; /* Where data starts for a 16K block */
1392 printf("butm: Write tape data - %u bytes\n", len);
1397 code = check(info, WRITE_OP);
1400 if (!data || (len < 0))
1401 ERROR_EXIT(BUTM_BADARGUMENT);
1402 if (READS || !WRITES)
1403 ERROR_EXIT(BUTM_BADOP);
1405 b = 0; /* start at block 0 */
1407 dstart = &data[b * BUTM_BLKSIZE];
1408 bstart = dstart - sizeof(struct blockMark);
1410 if (len < BUTM_BLKSIZE) {
1411 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1414 length = BUTM_BLKSIZE;
1417 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1421 /* If there are more blocks, step to next block */
1422 /* Otherwise, copy the data to beginning of last block */
1424 if (b < (blocks - 1))
1427 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1434 /* file_ReadFileData
1435 * Read a data block from tape.
1437 * info - tape info structure, c.f. fid
1438 * data - ptr to buffer for data
1439 * len - size of data buffer
1441 * nBytes - no. of data bytes read.
1445 file_ReadFileData(struct butm_tapeInfo *info, char *data, int len, int *nBytes)
1447 struct blockMark *bmark;
1449 afs_int32 blockType;
1452 printf("butm: Read tape data - %u bytes\n", len);
1458 ERROR_EXIT(BUTM_BADARGUMENT);
1461 code = check(info, READ_OP);
1464 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1465 ERROR_EXIT(BUTM_BADARGUMENT);
1466 if (!READS || WRITES)
1467 ERROR_EXIT(BUTM_BADOP);
1469 data -= sizeof(struct blockMark);
1470 code = ReadTapeBlock(info, data, &blockType);
1474 if (blockType != BLOCK_DATA) {
1475 if (blockType == BLOCK_EOF)
1476 ERROR_EXIT(BUTM_EOF);
1477 if (blockType == BLOCK_FMEND)
1478 ERROR_EXIT(BUTM_ENDVOLUME);
1479 ERROR_EXIT(BUTM_BADBLOCK);
1482 bmark = (struct blockMark *)data;
1483 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1490 file_WriteFileEnd(struct butm_tapeInfo *info)
1495 printf("butm: Write filemark end\n");
1500 code = check(info, WRITE_OP);
1503 if (READS || !WRITES)
1504 ERROR_EXIT(BUTM_BADOP);
1506 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1512 /* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1513 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1514 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1515 * missing with some 3.1 dumps).
1518 file_ReadFileEnd(struct butm_tapeInfo *info)
1521 afs_int32 blockType;
1524 printf("butm: Read filemark end\n");
1529 code = check(info, READ_OP);
1532 if (!READS || WRITES)
1533 ERROR_EXIT(BUTM_BADOP);
1535 info->status &= ~BUTM_STATUS_EOF;
1537 code = ReadTapeBlock(info, tapeBlock, &blockType);
1541 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1542 ERROR_EXIT(BUTM_BADBLOCK);
1549 * Write the end-of-dump marker.
1552 file_WriteEODump(struct butm_tapeInfo *info)
1557 printf("butm: Write filemark EOD\n");
1562 code = check(info, WRITE_OP);
1565 if (READS || WRITES)
1566 ERROR_EXIT(BUTM_BADOP);
1568 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1572 info->status |= BUTM_STATUS_EOD;
1579 file_Seek(struct butm_tapeInfo *info, afs_int32 position)
1585 afs_int64 stopOff; /* for normal file(non-tape) seeks */
1588 printf("butm: Seek to the tape position %d\n", position);
1592 code = check(info, READ_OP);
1597 p = (struct progress *)info->tmRock;
1598 posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1600 w = USD_SEEK(p->fid, posit, SEEK_SET, &stopOff);
1603 if (posit != stopOff)
1604 ERROR_EXIT(BUTM_POSITION);
1606 p->reading = p->writing = 0;
1607 info->position = position;
1609 /* Don't position backwards if we are in-between FMs */
1610 if ((READS || WRITES) && ((position - info->position) <= 0))
1611 ERROR_EXIT(BUTM_BADOP);
1613 code = SeekFile(info, (position - info->position));
1623 * Seek to the EODump (end-of-dump) after the given position. This is
1624 * the position after the EOF filemark immediately after the EODump mark.
1625 * This is for tapes of version 4 or greater.
1628 file_SeekEODump(struct butm_tapeInfo *info, afs_int32 position)
1631 afs_int32 blockType;
1634 afs_int64 stopOff; /* file seek offsets */
1637 printf("butm: Seek to end-of-dump\n");
1640 code = check(info, READ_OP);
1643 if (READS || WRITES)
1644 ERROR_EXIT(BUTM_BADOP);
1647 p = (struct progress *)info->tmRock;
1648 w = USD_SEEK(p->fid, 0, SEEK_END, &stopOff);
1651 ERROR_EXIT(BUTM_POSITION);
1654 if (stopOff % BUTM_BLOCKSIZE)
1655 ERROR_EXIT(BUTM_POSITION);
1656 info->position = (stopOff / BUTM_BLOCKSIZE);
1658 /* Seek to the desired position */
1659 code = SeekFile(info, (position - info->position) + 1);
1664 * Search until the filemark is an EODump filemark.
1665 * Skip over volumes only.
1668 code = ReadTapeBlock(info, tapeBlock, &blockType);
1672 if (blockType == BLOCK_EOD)
1674 if (blockType != BLOCK_FMBEGIN)
1675 ERROR_EXIT(BUTM_BADBLOCK);
1677 code = SeekFile(info, 1); /* Step forward to next volume */
1689 file_SetSize(struct butm_tapeInfo *info, afs_uint32 size)
1692 printf("butm: Set size of tape\n");
1696 info->tapeSize = config.tapeSize;
1698 info->tapeSize = size;
1703 file_GetSize(struct butm_tapeInfo *info, afs_uint32 *size)
1706 printf("butm: Get size of tape\n");
1709 *size = info->tapeSize;
1713 /* =====================================================================
1714 * Startup/configuration routines.
1715 * ===================================================================== */
1718 file_Configure(struct tapeConfig *file)
1721 afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1722 return BUTM_BADCONFIG;
1725 config.tapeSize = file->capacity;
1726 config.fileMarkSize = file->fileMarkSize;
1727 config.portOffset = file->portOffset;
1728 strcpy(config.tapedir, file->device);
1730 /* Tape must be large enough to at least fit a label */
1731 if (config.tapeSize <= 0) {
1732 afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1734 return BUTM_BADCONFIG;
1737 if (strlen(config.tapedir) == 0) {
1738 afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1739 return BUTM_BADCONFIG;
1746 /* This procedure instantiates a tape module of type file_tm. */
1748 butm_file_Instantiate(struct butm_tapeInfo *info, struct tapeConfig *file)
1750 extern int debugLevel;
1753 if (debugLevel > 98)
1754 printf("butm: Instantiate butc\n");
1757 ERROR_EXIT(BUTM_BADARGUMENT);
1758 if (info->structVersion != BUTM_MAJORVERSION)
1759 ERROR_EXIT(BUTM_OLDINTERFACE);
1761 memset(info, 0, sizeof(struct butm_tapeInfo));
1762 info->structVersion = BUTM_MAJORVERSION;
1763 info->ops.mount = file_Mount;
1764 info->ops.dismount = file_Dismount;
1765 info->ops.create = file_WriteLabel;
1766 info->ops.readLabel = file_ReadLabel;
1767 info->ops.seek = file_Seek;
1768 info->ops.seekEODump = file_SeekEODump;
1769 info->ops.readFileBegin = file_ReadFileBegin;
1770 info->ops.readFileData = file_ReadFileData;
1771 info->ops.readFileEnd = file_ReadFileEnd;
1772 info->ops.writeFileBegin = file_WriteFileBegin;
1773 info->ops.writeFileData = file_WriteFileData;
1774 info->ops.writeFileEnd = file_WriteFileEnd;
1775 info->ops.writeEOT = file_WriteEODump;
1776 info->ops.setSize = file_SetSize;
1777 info->ops.getSize = file_GetSize;
1778 info->debug = ((debugLevel > 98) ? 1 : 0);
1780 code = file_Configure(file);