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
);