10 #include <sys/ioctl.h>
16 #include "output-rait.h"
17 #include "output-tape.h"
20 #define amfree(x) do { \
21 int save_errno = errno; \
26 #define tape_open open
27 #define tapefd_read read
28 #define tapefd_write write
29 #define tapefd_close close
30 #define tape_access access
31 #define tape_stat stat
32 #define tapefd_fsf tape_tapefd_fsf
33 #define tapefd_rewind tape_tapefd_rewind
34 #define tapefd_status tape_tapefd_status
35 #define tapefd_unload tape_tapefd_unload
36 #define tapefd_weof tape_tapefd_weof
38 int tapeio_init_devname (char * dev,
42 char *tapeio_next_devname (char * dev_left,
48 ** RAIT -- redundant array of (inexpensive?) tapes
50 ** Author: Marc Mengel <mengel@fnal.gov>
52 ** This package provides for striping input/output across
53 ** multiple tape drives.
57 rait.c..................................................1
58 MAX_RAITS.........................................2
59 rait_table........................................2
60 rait_open(char *dev, int flags, int mode).........2
61 rait_close(int fd)................................3
62 rait_lseek(int fd, long pos, int whence)..........4
63 rait_write(int fd, const char *buf, int len) .....5
64 rait_read(int fd, char *buf, int len).............6
65 rait_ioctl(int fd, int op, void *p)...............8
66 rait_access(devname, R_OK|W_OK)...................8
67 rait_stat(devname, struct statbuf*)...............8
68 rait_copy(char *f1, char *f2).....................9
70 rait_tapefd_fsf(rait_tapefd, count)..........10
71 rait_tapefd_rewind(rait_tapefd)..............10
72 rait_tapefd_resetofs(rait_tapefd)............10
73 rait_tapefd_unload(rait_tapefd)..............10
74 rait_tapefd_status(rait_tapefd, stat)........10
75 rait_tapefd_weof(rait_tapefd, count).........10
79 rait.h.................................................1
80 typedef RAIT......................................1
81 ifdef RAIT_REDIRECT...............................1
82 open.........................................1
83 close........................................1
84 ioctl........................................1
85 read.........................................1
86 write........................................1
92 ** rait_open takes a string like:
93 ** "/dev/rmt/tps0d{3,5,7,19}nrnsv"
95 ** "/dev/rmt/tps0d3nrnsv"
96 ** "/dev/rmt/tps0d5nrnsv"
97 ** "/dev/rmt/tps0d7nrnsv"
98 ** "/dev/rmt/tps0d19nrnsv"
101 ** If it has no curly brace, we treat it as a plain device,
102 ** and do a normal open, and do normal operations on it.
106 #define rait_debug(p) do { \
107 int save_errno = errno; \
109 if (0!=getenv("RAIT_DEBUG")) { \
112 errno = save_errno; \
115 #define rait_debug(p)
118 static RAIT *rait_table = 0; /* table to keep track of RAITS */
119 static int rait_table_count;
123 * amtable_alloc -- (re)allocate enough space for some number of elements.
125 * input: table -- pointer to pointer to table
126 * current -- pointer to current number of elements
127 * elsize -- size of a table element
128 * count -- desired number of elements
129 * bump -- round up factor
130 * output: table -- possibly adjusted to point to new table area
131 * current -- possibly adjusted to new number of elements
135 amtable_alloc(void **table,
144 if (count >= *current) {
145 table_count_new = ((count + bump) / bump) * bump;
146 table_new = malloc(table_count_new * elsize);
147 if (0 == table_new) {
152 memcpy(table_new, *table, *current * elsize);
156 memset(((char *)*table) + *current * elsize,
158 (table_count_new - *current) * elsize);
159 *current = table_count_new;
165 * amtable_free -- release a table.
167 * input: table -- pointer to pointer to table
168 * current -- pointer to current number of elements
169 * output: table -- possibly adjusted to point to new table area
170 * current -- possibly adjusted to new number of elements
174 amtable_free(table, current)
183 #define rait_table_alloc(fd) amtable_alloc((void **)&rait_table, \
185 sizeof(*rait_table), \
191 rait_open(char *dev, int flags, int mask) {
192 int fd; /* the file descriptor number to return */
193 RAIT *res; /* resulting RAIT structure */
194 char *dev_left; /* string before { */
195 char *dev_right; /* string after } */
196 char *dev_next; /* string inside {} */
197 char *dev_real; /* parsed device name */
198 int rait_flag; /* true if RAIT syntax in dev */
202 rait_debug((stderr,"rait_open( %s, %d, %d )\n", dev, flags, mask));
204 rait_flag = (0 != strchr(dev, '{'));
209 ** we have to return a valid file descriptor, so use
210 ** a dummy one to /dev/null
212 fd = open("/dev/null",flags,mask);
216 ** call the normal tape_open function if we are not
219 fd = tape_open(dev,flags,mask);
222 rait_debug((stderr, "rait_open:returning %d: %s\n",
228 if(0 != rait_table_alloc(fd + 1)) {
230 (void)tapefd_close(fd);
232 rait_debug((stderr, "rait_open:returning %d: %s\n",
238 res = &rait_table[fd];
240 memset(res, 0, sizeof(*res));
246 /* copy and parse the dev string so we can scribble on it */
249 rait_debug((stderr, "rait_open:returning %d: %s\n",
251 "out of stralloc memory"));
254 if (0 != tapeio_init_devname(dev, &dev_left, &dev_right, &dev_next)) {
255 rait_debug((stderr, "rait_open:returning %d: %s\n",
261 while (0 != (dev_real = tapeio_next_devname(dev_left, dev_right, &dev_next))) {
262 r = amtable_alloc((void **)&res->fds,
269 (void)rait_close(fd);
274 res->fds[ res->nfds ] = tape_open(dev_real,flags,mask);
275 rait_debug((stderr,"rait_open:opening %s yields %d\n",
276 dev_real, res->fds[res->nfds] ));
277 if ( res->fds[res->nfds] < 0 ) {
279 (void)rait_close(fd);
285 tapefd_set_master_fd(res->fds[res->nfds], fd);
290 /* clean up our copied string */
296 ** set things up to treat this as a normal tape if we ever
297 ** come in here again
301 r = amtable_alloc((void **)&res->fds,
308 (void)tapefd_close(fd);
309 memset(res, 0, sizeof(*res));
313 res->fds[res->nfds] = fd;
318 if (fd >= 0 && res->nfds > 0) {
319 res->readres = (int *) malloc(res->nfds * sizeof(*res->readres));
320 if (0 == res->readres) {
321 (void)rait_close(fd);
325 memset(res->readres, 0, res->nfds * sizeof(*res->readres));
329 rait_debug((stderr, "rait_open:returning %d%s%s\n",
331 (fd < 0) ? ": " : "",
332 (fd < 0) ? strerror(errno) : ""));
339 tapeio_init_devname(char * dev,
344 ** find the first { and then the first } that follows it
346 if ( 0 == (*dev_next = strchr(dev, '{'))
347 || 0 == (*dev_right = strchr(*dev_next + 1, '}')) ) {
348 /* we dont have a {} pair */
354 *dev_left = dev; /* before the { */
355 **dev_next = 0; /* zap the { */
357 (*dev_right)++; /* after the } */
362 tapeio_next_devname(char * dev_left,
370 if (0 != (*dev_next = strchr(next, ','))
371 || 0 != (*dev_next = strchr(next, '}'))){
373 **dev_next = 0; /* zap the terminator */
377 ** we have one string picked out, build it into the buffer
379 len = strlen(dev_left) + strlen(next) + strlen(dev_right) + 1;
380 if ( 0 != (dev_real = malloc(len)) ) {
381 strcpy(dev_real, dev_left); /* safe */
382 strcat(dev_real, next); /* safe */
383 strcat(dev_real, dev_right); /* safe */
391 ** close everything we opened and free our memory.
395 int i; /* index into RAIT drives */
396 int j; /* individual tapefd_close result */
397 int res; /* result from close */
398 RAIT *pr; /* RAIT entry from table */
399 int save_errno = errno;
402 rait_debug((stderr,"rait_close( %d )\n", fd));
404 if (fd < 0 || fd >= rait_table_count) {
406 rait_debug((stderr, "rait_close:returning %d: %s\n",
412 pr = &rait_table[fd];
413 if (0 == pr->nopen) {
415 rait_debug((stderr, "rait_close:returning %d: %s\n",
421 if (0 == pr->readres && 0 < pr->nfds) {
422 pr->readres = (int *) malloc(pr->nfds * sizeof(*pr->readres));
423 if (0 == pr->readres) {
425 rait_debug((stderr, "rait_close:returning %d: %s\n",
430 memset(pr->readres, 0, pr->nfds * sizeof(*pr->readres));
435 ** this looks strange, but we start kids who are going to close the
436 ** drives in parallel just after the parent has closed their copy of
437 ** the descriptor. ('cause closing tape devices usually causes slow
438 ** activities like filemark writes, etc.)
440 for( i = 0; i < pr->nfds; i++ ) {
441 if(tapefd_can_fork(pr->fds[i])) {
442 if ((kid = fork()) == 0) {
443 /* we are the child process */
445 j = tapefd_close(pr->fds[i]);
448 /* remember who the child is or that an error happened */
449 pr->readres[i] = kid;
453 j = tapefd_close(pr->fds[i]);
460 for( i = 0; i < pr->nfds; i++ ) {
461 j = tapefd_close(pr->fds[i]);
466 for( i = 0; i < pr->nfds; i++ ) {
468 if(pr->readres[i] != -1) {
469 waitpid( pr->readres[i], &stat, 0);
470 if( WEXITSTATUS(stat) != 0 ) {
471 res = WEXITSTATUS(stat);
478 (void)close(fd); /* close the dummy /dev/null descriptor */
481 amtable_free((void **)&pr->fds, &pr->fd_count);
483 if (0 != pr->readres) {
486 if (0 != pr->xorbuf) {
491 rait_debug((stderr, "rait_close:returning %d%s%s\n",
493 (res < 0) ? ": " : "",
494 (res < 0) ? strerror(errno) : ""));
501 ** seek out to the nth byte on the RAIT set.
502 ** this is assumed to be evenly divided across all the stripes
505 rait_lseek(int fd, long pos, int whence) {
506 int i; /* drive number in RAIT */
507 long res, /* result of lseeks */
508 total; /* total of results */
509 RAIT *pr; /* RAIT slot in table */
511 rait_debug((stderr, "rait_lseek(%d,%ld,%d)\n",fd,pos,whence));
513 if (fd < 0 || fd >= rait_table_count) {
515 rait_debug((stderr, "rait_lseek:returning %d: %s\n",
521 pr = &rait_table[fd];
522 if (0 == pr->nopen) {
524 rait_debug((stderr, "rait_lseek:returning %d: %s\n",
530 if (pr->nfds > 1 && (pos % (pr->nfds-1)) != 0) {
535 pos = pos / pr->nfds;
536 for( i = 0; i < pr->nfds; i++ ) {
537 if (0 >= (res = lseek(pr->fds[i], pos, whence))) {
544 rait_debug((stderr, "rait_lseek:returning %ld%s%s\n",
546 (total < 0) ? ": " : "",
547 (total < 0) ? strerror(errno) : ""));
554 ** if we only have one stream, just do a write,
555 ** otherwise compute an xor sum, and do several
559 rait_write(int fd, const void *bufptr, size_t len) {
560 const char *buf = bufptr;
561 int i = 0, j; /* drive number, byte offset */
562 RAIT *pr; /* RAIT structure for this RAIT */
564 int data_fds; /* number of data stream file descriptors */
566 rait_debug((stderr, "rait_write(%d,%lx,%d)\n",fd,(unsigned long)buf,len));
568 if (fd < 0 || fd >= rait_table_count) {
570 rait_debug((stderr, "rait_write:returning %d: %s\n",
576 pr = &rait_table[fd];
577 if (0 == pr->nopen) {
579 rait_debug((stderr, "rait_write:returning %d: %s\n",
585 /* need to be able to slice it up evenly... */
587 data_fds = pr->nfds - 1;
588 if (0 != len % data_fds) {
590 rait_debug((stderr, "rait_write:returning %d: %s\n",
595 /* each slice gets an even portion */
596 len = len / data_fds;
598 /* make sure we have enough buffer space */
599 if (len > pr->xorbuflen) {
600 if (0 != pr->xorbuf) {
603 pr->xorbuf = malloc(len);
604 if (0 == pr->xorbuf) {
606 rait_debug((stderr, "rait_write:returning %d: %s\n",
614 /* compute the sum */
615 memcpy(pr->xorbuf, buf, len);
616 for( i = 1; i < data_fds; i++ ) {
617 for( j = 0; j < len; j++ ) {
618 pr->xorbuf[j] ^= buf[len * i + j];
625 /* write the chunks in the main buffer */
627 for( i = 0; i < data_fds; i++ ) {
628 res = tapefd_write(pr->fds[i], buf + len*i , len);
629 rait_debug((stderr, "rait_write: write(%d,%lx,%d) returns %d%s%s\n",
631 (unsigned long)(buf + len*i),
634 (res < 0) ? ": " : "",
635 (res < 0) ? strerror(errno) : ""));
643 if (total >= 0 && pr->nfds > 1) {
644 /* write the sum, don't include it in the total bytes written */
645 res = tapefd_write(pr->fds[i], pr->xorbuf, len);
646 rait_debug((stderr, "rait_write: write(%d,%lx,%d) returns %d%s%s\n",
648 (unsigned long)pr->xorbuf,
651 (res < 0) ? ": " : "",
652 (res < 0) ? strerror(errno) : ""));
658 rait_debug((stderr, "rait_write:returning %d%s%s\n",
660 (total < 0) ? ": " : "",
661 (total < 0) ? strerror(errno) : ""));
669 ** once again, if there is one data stream do a read, otherwise
670 ** do all n reads, and if any of the first n - 1 fail, compute
671 ** the missing block from the other three, then return the data.
672 ** there's some silliness here for reading tape with bigger buffers
673 ** than we wrote with, (thus the extra bcopys down below). On disk if
674 ** you read with a bigger buffer size than you wrote with, you just
675 ** garble the data...
678 rait_read(int fd, void *bufptr, size_t len) {
687 int save_errno = errno;
689 int sum_mismatch = 0;
691 rait_debug((stderr, "rait_read(%d,%lx,%d)\n",fd,(unsigned long)buf,len));
693 if (fd < 0 || fd >= rait_table_count) {
695 rait_debug((stderr, "rait_read:returning %d: %s\n",
701 pr = &rait_table[fd];
702 if (0 == pr->nopen) {
704 rait_debug((stderr, "rait_read:returning %d: %s\n",
714 /* once again , we slice it evenly... */
716 data_fds = pr->nfds - 1;
717 if (0 != len % data_fds) {
719 rait_debug((stderr, "rait_read:returning %d: %s\n",
724 len = len / data_fds;
729 /* try all the reads, save the result codes */
730 /* count the eof/errors */
731 for( i = 0; i < data_fds; i++ ) {
732 pr->readres[i] = tapefd_read(pr->fds[i], buf + len*i , len);
733 rait_debug((stderr, "rait_read: read on fd %d returns %d%s%s\n",
736 (pr->readres[i] < 0) ? ": " : "",
737 (pr->readres[i] < 0) ? strerror(errno) : ""));
738 if ( pr->readres[i] <= 0 ) {
739 if ( pr->readres[i] == 0 ) {
748 } else if (pr->readres[i] > maxreadres) {
749 maxreadres = pr->readres[i];
753 /* make sure we have enough buffer space */
754 if (len > pr->xorbuflen) {
755 if (0 != pr->xorbuf) {
758 pr->xorbuf = malloc(len);
759 if (0 == pr->xorbuf) {
761 rait_debug((stderr, "rait_write:returning %d: %s\n",
768 pr->readres[i] = tapefd_read(pr->fds[i], pr->xorbuf , len);
769 rait_debug((stderr, "rait_read: read on fd %d returns %d%s%s\n",
772 (pr->readres[i] < 0) ? ": " : "",
773 (pr->readres[i] < 0) ? strerror(errno) : ""));
777 * Make sure all the reads were the same length
779 for (j = 0; j < pr->nfds; j++) {
780 if (pr->readres[j] != maxreadres) {
787 * If no errors, check that the xor sum matches
789 if ( nerrors == 0 && pr->nfds > 1 ) {
790 for(i = 0; i < maxreadres; i++ ) {
792 for(j = 0; j < pr->nfds - 1; j++) {
793 sum ^= (buf + len * j)[i];
795 if (sum != pr->xorbuf[i]) {
802 ** now decide what "really" happened --
803 ** all n getting eof is a "real" eof
804 ** just one getting an error/eof is recoverable if we are doing RAIT
805 ** anything else fails
808 if (neofs == pr->nfds) {
809 rait_debug((stderr, "rait_read:returning 0\n"));
815 rait_debug((stderr, "rait_read:returning %d: %s\n",
817 "XOR block mismatch"));
821 if (nerrors > 1 || (pr->nfds <= 1 && nerrors > 0)) {
823 rait_debug((stderr, "rait_read:returning %d: %s\n",
830 ** so now if we failed on a data block, we need to do a recovery
831 ** if we failed on the xor block -- who cares?
833 if (nerrors == 1 && pr->nfds > 1 && errorblock != pr->nfds-1) {
835 rait_debug((stderr, "rait_read: fixing data from fd %d\n",
836 pr->fds[errorblock]));
838 /* the reads were all *supposed* to be the same size, so... */
839 pr->readres[errorblock] = maxreadres;
841 /* fill it in first with the xor sum */
842 memcpy(buf + len * errorblock, pr->xorbuf, len);
844 /* xor back out the other blocks */
845 for( i = 0; i < data_fds; i++ ) {
846 if( i != errorblock ) {
847 for( j = 0; j < len ; j++ ) {
848 buf[j + len * errorblock] ^= buf[j + len * i];
852 /* there, now the block is back as if it never failed... */
855 /* pack together partial reads... */
856 total = pr->readres[0];
857 for( i = 1; i < data_fds; i++ ) {
858 if (total != len * i) {
859 memmove(buf + total, buf + len*i, pr->readres[i]);
861 total += pr->readres[i];
864 rait_debug((stderr, "rait_read:returning %d%s%s\n",
866 (total < 0) ? ": " : "",
867 (total < 0) ? strerror(errno) : ""));
874 int rait_ioctl(int fd, int op, void *p) {
879 rait_debug((stderr, "rait_ioctl(%d,%d)\n",fd,op));
881 if (fd < 0 || fd >= rait_table_count) {
883 rait_debug((stderr, "rait_ioctl:returning %d: %s\n",
889 pr = &rait_table[fd];
890 if (0 == pr->nopen) {
892 rait_debug((stderr, "rait_ioctl:returning %d: %s\n",
898 for( i = 0; i < pr->nfds ; i++ ) {
899 res = ioctl(pr->fds[i], op, p);
909 rait_debug((stderr, "rait_ioctl: returning %d%s%s\n",
911 (res < 0) ? ": " : "",
912 (res < 0) ? strerror(errno) : ""));
918 ** access() all the devices, returning if any fail
920 int rait_access(char *devname, int flags) {
922 char *dev_left; /* string before { */
923 char *dev_right; /* string after } */
924 char *dev_next; /* string inside {} */
925 char *dev_real; /* parsed device name */
927 /* copy and parse the dev string so we can scribble on it */
928 devname = stralloc(devname);
930 rait_debug((stderr, "rait_access:returning %d: %s\n",
932 "out of stralloc memory"));
935 if ( 0 != tapeio_init_devname(devname, &dev_left, &dev_right, &dev_next)) {
936 rait_debug((stderr, "rait_access:returning %d: %s\n",
942 while( 0 != (dev_real = tapeio_next_devname(dev_left, dev_right, &dev_next))) {
943 res = tape_access(dev_real, flags);
944 rait_debug((stderr,"rait_access:access( %s, %d ) yields %d\n",
945 dev_real, flags, res ));
953 rait_debug((stderr, "rait_access: returning %d%s%s\n",
955 (res < 0) ? ": " : "",
956 (res < 0) ? strerror(errno) : ""));
962 ** stat all the devices, returning the last one unless one fails
964 int rait_stat(char *devname, struct stat *buf) {
966 char *dev_left; /* string before { */
967 char *dev_right; /* string after } */
968 char *dev_next; /* string inside {} */
969 char *dev_real; /* parsed device name */
971 /* copy and parse the dev string so we can scribble on it */
972 devname = stralloc(devname);
974 rait_debug((stderr, "rait_access:returning %d: %s\n",
976 "out of stralloc memory"));
979 if ( 0 != tapeio_init_devname(devname, &dev_left, &dev_right, &dev_next)) {
980 rait_debug((stderr, "rait_access:returning %d: %s\n",
986 while( 0 != (dev_real = tapeio_next_devname(dev_left, dev_right, &dev_next))) {
987 res = tape_stat(dev_real, buf);
988 rait_debug((stderr,"rait_stat:stat( %s ) yields %d (%s)\n",
989 dev_real, res, (res != 0) ? strerror(errno) : "no error" ));
997 rait_debug((stderr, "rait_access: returning %d%s%s\n",
999 (res < 0) ? ": " : "",
1000 (res < 0) ? strerror(errno) : ""));
1007 int rait_copy(char *f1, char *f2, int buflen) {
1013 t1 = rait_open(f1,O_RDONLY,0644);
1017 t2 = rait_open(f2,O_CREAT|O_RDWR,0644);
1020 (void)rait_close(t1);
1024 buf = malloc(buflen);
1026 (void)rait_close(t1);
1027 (void)rait_close(t2);
1032 len = rait_read(t1,buf,buflen);
1034 wres = rait_write(t2, buf, len);
1043 (void)rait_close(t1);
1044 (void)rait_close(t2);
1046 return (len < 0) ? -1 : 0;
1052 ** Amanda Tape API routines:
1055 static int rait_tapefd_ioctl(int (*func0)(int),
1056 int (*func1)(int, int),
1065 rait_debug((stderr, "rait_tapefd_ioctl(%d,%d)\n",fd,count));
1067 if (fd < 0 || fd >= rait_table_count) {
1069 rait_debug((stderr, "rait_tapefd_ioctl:returning %d: %s\n",
1075 pr = &rait_table[fd];
1076 if (0 == pr->nopen) {
1078 rait_debug((stderr, "rait_tapefd_ioctl:returning %d: %s\n",
1084 if (0 == pr->readres && 0 < pr->nfds) {
1085 pr->readres = (int *) malloc(pr->nfds * sizeof(*pr->readres));
1086 if (0 == pr->readres) {
1088 rait_debug((stderr, "rait_tapefd_ioctl:returning %d: %s\n",
1093 memset(pr->readres, 0, pr->nfds * sizeof(*pr->readres));
1096 for( i = 0; i < pr->nfds ; i++ ) {
1097 if(tapefd_can_fork(pr->fds[i])) {
1098 if ((kid = fork()) < 1) {
1099 rait_debug((stderr, "in kid, fork returned %d\n", kid));
1100 /* if we are the kid, or fork failed do the action */
1102 res = (*func0)(pr->fds[i]);
1104 res = (*func1)(pr->fds[i], count);
1106 rait_debug((stderr, "in kid, func ( %d ) returned %d errno %d\n", pr->fds[i], res, errno));
1110 rait_debug((stderr, "in parent, fork returned %d\n", kid));
1111 pr->readres[i] = kid;
1116 j = (*func0)(pr->fds[i]);
1118 j = (*func1)(pr->fds[i], count);
1123 pr->readres[i] = -1;
1126 for( i = 0; i < pr->nfds ; i++ ) {
1127 if(tapefd_can_fork(pr->fds[i])) {
1128 rait_debug((stderr, "in parent, waiting for %d\n", pr->readres[i]));
1129 waitpid( pr->readres[i], &stat, 0);
1130 if( WEXITSTATUS(stat) != 0 ) {
1131 res = WEXITSTATUS(stat);
1135 rait_debug((stderr, "in parent, return code was %d\n", res));
1146 rait_debug((stderr, "rait_tapefd_ioctl: returning %d%s%s\n",
1148 (res < 0) ? ": " : "",
1149 (res < 0) ? strerror(errno) : ""));
1154 int rait_tapefd_fsf(int fd, int count) {
1155 return rait_tapefd_ioctl(0, tapefd_fsf, fd, count);
1158 int rait_tapefd_rewind(int fd) {
1159 return rait_tapefd_ioctl(tapefd_rewind, 0, fd, -1);
1162 int rait_tapefd_unload(int fd) {
1163 return rait_tapefd_ioctl(tapefd_unload, 0, fd, -1);
1166 int rait_tapefd_weof(int fd, int count) {
1167 return rait_tapefd_ioctl(0, tapefd_weof, fd, count);
1170 int rait_tape_open(char *name, int flags, int mask) {
1171 return rait_open(name, flags, mask);
1174 int rait_tapefd_status(int fd, struct am_mt_status *stat) {
1180 rait_debug((stderr, "rait_tapefd_status(%d)\n",fd));
1182 if (fd < 0 || fd >= rait_table_count) {
1184 rait_debug((stderr, "rait_tapefd_status:returning %d: %s\n",
1190 pr = &rait_table[fd];
1191 if (0 == pr->nopen) {
1193 rait_debug((stderr, "rait_tapefd_status:returning %d: %s\n",
1199 for( i = 0; i < pr->nfds ; i++ ) {
1200 res = tapefd_status(pr->fds[i], stat);
1211 void rait_tapefd_resetofs(int fd) {
1212 rait_lseek(fd, 0L, SEEK_SET);
1216 rait_tapefd_can_fork(fd)