Imported Upstream version 2.5.1
[debian/amanda] / tape-src / output-rait.c
1 #ifdef NO_AMANDA
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/wait.h>
10 #include <sys/ioctl.h>
11 #else
12 #include "amanda.h"
13 #include "tapeio.h"
14 #endif
15
16 #include "output-rait.h"
17 #include "output-tape.h"
18
19 #ifdef NO_AMANDA
20 #define amfree(x)       do {                                            \
21         if (x) {                                                        \
22             int save_errno = errno;                                     \
23             free(x);                                                    \
24             (x) = NULL;                                                 \
25             errno = save_errno;                                         \
26         }
27 } while(0)
28 #define tape_open       open
29 #define tapefd_read     read
30 #define tapefd_write    write
31 #define tapefd_close    close
32 #define tape_access     access
33 #define tape_stat       stat
34 #define tapefd_fsf      tape_tapefd_fsf
35 #define tapefd_rewind   tape_tapefd_rewind
36 #define tapefd_status   tape_tapefd_status
37 #define tapefd_unload   tape_tapefd_unload
38 #define tapefd_weof     tape_tapefd_weof
39
40 int tapeio_init_devname (char * dev,
41                          char **dev_left,
42                          char **dev_right,
43                          char **dev_next);
44 char *tapeio_next_devname (char * dev_left,
45                            char * dev_right,
46                            char **dev_next);
47 #endif
48
49 /*
50 ** RAIT -- redundant array of (inexpensive?) tapes
51 **
52 ** Author: Marc Mengel <mengel@fnal.gov>
53 **
54 ** This package provides for striping input/output across
55 ** multiple tape drives.
56 **
57                  Table of Contents
58
59   rait.c..................................................1
60         MAX_RAITS.........................................2
61         rait_table........................................2
62         rait_open(char *dev, int flags, mode_t mode)......2
63         rait_close(int fd)................................3
64         rait_lseek(int fd, long pos, int whence)..........4
65         rait_write(int fd, const char *buf, size_t len) ..5
66         rait_read(int fd, char *buf, size_t len)..........6
67         rait_ioctl(int fd, int op, void *p)...............8
68         rait_access(devname, R_OK|W_OK)...................8
69         rait_stat(devname, struct statbuf*)...............8
70         rait_copy(char *f1, char *f2).....................9
71         ifndef NO_AMANDA
72             rait_tapefd_fsf(rait_tapefd, count)..........10
73             rait_tapefd_rewind(rait_tapefd)..............10
74             rait_tapefd_resetofs(rait_tapefd)............10
75             rait_tapefd_unload(rait_tapefd)..............10
76             rait_tapefd_status(rait_tapefd, stat)........10
77             rait_tapefd_weof(rait_tapefd, count).........10
78
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
87 */
88
89 /*\f*/
90
91 /*
92 ** rait_open takes a string like:
93 ** "/dev/rmt/tps0d{3,5,7,19}nrnsv"
94 ** and opens
95 ** "/dev/rmt/tps0d3nrnsv"
96 ** "/dev/rmt/tps0d5nrnsv"
97 ** "/dev/rmt/tps0d7nrnsv"
98 ** "/dev/rmt/tps0d19nrnsv"
99 ** as a RAIT.
100 **
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.
103 */
104
105 #ifdef RAIT_DEBUG
106 #define rait_debug(p) do {                                              \
107   int save_errno = errno;                                               \
108                                                                         \
109   if (0!=getenv("RAIT_DEBUG")) {                                        \
110     fprintf p;                                                          \
111   }                                                                     \
112   errno = save_errno;                                                   \
113 } while (0)
114 #else
115 #define rait_debug(p)
116 #endif
117
118 static RAIT *rait_table = 0;            /* table to keep track of RAITS */
119 static size_t rait_table_count;
120
121 #ifdef NO_AMANDA
122 /*
123  * amtable_alloc -- (re)allocate enough space for some number of elements.
124  *
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
132  */
133
134 static int
135 amtable_alloc(
136     void **     table,
137     int *       current,
138     size_t      elsize,
139     int         count,
140     int         bump,
141     void *      dummy)
142 {
143     void *table_new;
144     int table_count_new;
145
146     if (count >= *current) {
147         table_count_new = ((count + bump) / bump) * bump;
148         table_new = alloc(table_count_new * elsize);
149         if (0 != *table) {
150             memcpy(table_new, *table, *current * elsize);
151             amfree(*table);
152         }
153         *table = table_new;
154         memset(((char *)*table) + *current * elsize,
155                0,
156                (table_count_new - *current) * elsize);
157         *current = table_count_new;
158     }
159     return 0;
160 }
161
162 /*
163  * amtable_free -- release a table.
164  *
165  * input:       table -- pointer to pointer to table
166  *              current -- pointer to current number of elements
167  * output:      table -- possibly adjusted to point to new table area
168  *              current -- possibly adjusted to new number of elements
169  */
170
171 void
172 amtable_free(
173     void **     table,
174     int *       current)
175 {
176     amfree(*table);
177     *current = 0;
178 }
179 #endif
180
181 #define rait_table_alloc(fd)    amtable_alloc((void **)rait_table_p,         \
182                                               &rait_table_count,             \
183                                               SIZEOF(*rait_table),   \
184                                               (size_t)(fd),                  \
185                                               10,                            \
186                                               NULL)
187
188 int
189 rait_open(
190     char *      dev,
191     int         flags,
192     mode_t      mask)
193 {
194     int fd;                     /* the file descriptor number to return */
195     RAIT *res;                  /* resulting RAIT structure */
196     char *dev_left;             /* string before { */
197     char *dev_right;            /* string after } */
198     char *dev_next;             /* string inside {} */
199     char *dev_real;             /* parsed device name */
200     int rait_flag;              /* true if RAIT syntax in dev */
201     int save_errno;
202     int r;
203     RAIT **rait_table_p = &rait_table;
204     int **fds_p;
205
206     rait_debug((stderr,"rait_open( %s, %d, %d )\n", dev, flags, mask));
207
208     rait_flag = (0 != strchr(dev, '{'));
209
210     if (rait_flag) {
211
212         /*
213         ** we have to return a valid file descriptor, so use
214         ** a dummy one to /dev/null
215         */
216         fd = open("/dev/null",flags,mask);
217     } else {
218
219         /*
220         ** call the normal tape_open function if we are not
221         ** going to do RAIT
222         */
223         fd = tape_open(dev,flags,mask);
224     }
225     if(-1 == fd) {
226         rait_debug((stderr, "rait_open:returning %d: %s\n",
227                             fd,
228                             strerror(errno)));
229         return fd;
230     }
231
232     if(0 != rait_table_alloc(fd + 1)) {
233         save_errno = errno;
234         (void)tapefd_close(fd);
235         errno = save_errno;
236         rait_debug((stderr, "rait_open:returning %d: %s\n",
237                             -1,
238                             strerror(errno)));
239         return -1;
240     }
241
242     res = &rait_table[fd];
243
244     memset(res, 0, SIZEOF(*res));
245     res->nopen = 1;
246
247     res->fd_count = 0;
248     if (rait_flag) {
249
250         /* copy and parse the dev string so we can scribble on it */
251         dev = stralloc(dev);
252         if (0 == dev) {
253             rait_debug((stderr, "rait_open:returning %d: %s\n",
254                                 -1,
255                                 "out of stralloc memory"));
256             return -1;
257         }
258         if (0 != tapeio_init_devname(dev, &dev_left, &dev_right, &dev_next)) {
259             rait_debug((stderr, "rait_open:returning %d: %s\n",
260                                 -1,
261                                 strerror(errno)));
262             return -1;
263         }
264
265         while (0 != (dev_real = tapeio_next_devname(dev_left, dev_right, &dev_next))) {
266             fds_p = &(res->fds);
267             r = amtable_alloc((void **)fds_p,
268                             &res->fd_count,
269                             SIZEOF(*res->fds),
270                             (size_t)res->nfds + 1,
271                             10,
272                             NULL);
273             if (0 != r) {
274                 (void)rait_close(fd);
275                 fd = -1;
276                 amfree(dev_real);
277                 break;
278             }
279             res->fds[ res->nfds ] = tape_open(dev_real,flags,mask);
280             rait_debug((stderr,"rait_open:opening %s yields %d\n",
281                         dev_real, res->fds[res->nfds] ));
282             if ( res->fds[res->nfds] < 0 ) {
283                 save_errno = errno;
284                 (void)rait_close(fd);
285                 amfree(dev_real);
286                 errno = save_errno;
287                 fd = -1;
288                 break;
289             }
290             tapefd_set_master_fd(res->fds[res->nfds], fd);
291             amfree(dev_real);
292             res->nfds++;
293         }
294
295         /* clean up our copied string */
296         amfree(dev);
297
298     } else {
299
300         /*
301         ** set things up to treat this as a normal tape if we ever
302         ** come in here again
303         */
304
305         res->nfds = 0;
306         fds_p = &(res->fds);
307         r = amtable_alloc((void **)fds_p,
308                           &res->fd_count,
309                           SIZEOF(*res->fds),
310                           (size_t)res->nfds + 1,
311                           1,
312                           NULL);
313         if (0 != r) {
314             (void)tapefd_close(fd);
315             memset(res, 0, SIZEOF(*res));
316             errno = ENOMEM;
317             fd = -1;
318         } else {
319             res->fds[res->nfds] = fd;
320             res->nfds++;
321         }
322     }
323
324     if (fd >= 0 && res->nfds > 0) {
325         res->readres = alloc(res->nfds * SIZEOF(*res->readres));
326         memset(res->readres, 0, res->nfds * SIZEOF(*res->readres));
327     }
328
329     rait_debug((stderr, "rait_open:returning %d%s%s\n",
330                         fd,
331                         (fd < 0) ? ": " : "",
332                         (fd < 0) ? strerror(errno) : ""));
333
334     return fd;
335 }
336
337 #ifdef NO_AMANDA
338 int
339 tapeio_init_devname(
340     char *      dev,
341     char **     dev_left,
342     char **     dev_right,
343     char **     dev_next)
344 {
345     /*
346     ** find the first { and then the first } that follows it
347     */
348     if ( 0 == (*dev_next = strchr(dev, '{'))
349          || 0 == (*dev_right = strchr(*dev_next + 1, '}')) ) {
350         /* we dont have a {} pair */
351         amfree(dev);
352         errno = EINVAL;
353         return -1;
354     }
355
356     *dev_left = dev;                            /* before the { */
357     **dev_next = 0;                             /* zap the { */
358     (*dev_next)++;
359     (*dev_right)++;                             /* after the } */
360     return 0;
361 }
362
363 char *
364 tapeio_next_devname(
365     char *      dev_left,
366     char *      dev_right,
367     char **     dev_next)
368 {
369     char *dev_real = 0;
370     char *next;
371     int len;
372
373     next = *dev_next;
374     if (0 != (*dev_next = strchr(next, ','))
375         || 0 != (*dev_next = strchr(next, '}'))){
376
377         **dev_next = 0;                         /* zap the terminator */
378         (*dev_next)++;
379
380         /*
381         ** we have one string picked out, build it into the buffer
382         */
383         len = strlen(dev_left) + strlen(next) + strlen(dev_right) + 1;
384         dev_real = alloc(len);
385         strcpy(dev_real, dev_left);             /* safe */
386         strcat(dev_real, next);         /* safe */
387         strcat(dev_real, dev_right);    /* safe */
388     }
389     return dev_real;
390 }
391 #endif
392
393 /*
394 ** close everything we opened and free our memory.
395 */
396 int
397 rait_close(
398     int fd)
399 {
400     int i;                      /* index into RAIT drives */
401     int j;                      /* individual tapefd_close result */
402     int res;                    /* result from close */
403     RAIT *pr;                   /* RAIT entry from table */
404     int save_errno = errno;
405     pid_t kid;
406     int **fds_p;
407
408     rait_debug((stderr,"rait_close( %d )\n", fd));
409
410     if ((fd < 0) || ((size_t)fd >= rait_table_count)) {
411         errno = EBADF;
412         rait_debug((stderr, "rait_close:returning %d: %s\n",
413                             -1,
414                             strerror(errno)));
415         return -1;
416     }
417
418     pr = &rait_table[fd];
419     if (0 == pr->nopen) {
420         errno = EBADF;
421         rait_debug((stderr, "rait_close:returning %d: %s\n",
422                             -1,
423                             strerror(errno)));
424         return -1;
425     }
426
427     if (0 == pr->readres && 0 < pr->nfds) {
428         pr->readres = alloc(pr->nfds * SIZEOF(*pr->readres));
429         memset(pr->readres, 0, pr->nfds * SIZEOF(*pr->readres));
430     }
431
432     res = 0;
433     /*
434     ** this looks strange, but we start kids who are going to close the
435     ** drives in parallel just after the parent has closed their copy of
436     ** the descriptor. ('cause closing tape devices usually causes slow
437     ** activities like filemark writes, etc.)
438     */
439     for( i = 0; i < pr->nfds; i++ ) {
440         if(tapefd_can_fork(pr->fds[i])) {
441             if ((kid = fork()) == 0) {
442                 /* we are the child process */
443                 sleep(0);
444                 j = tapefd_close(pr->fds[i]);
445                 exit(j);
446             } else {
447                 /* remember who the child is or that an error happened */
448                 pr->readres[i] = (ssize_t)kid;
449             }
450         }
451         else {
452             j = tapefd_close(pr->fds[i]);
453             if ( j != 0 )
454                 res = j;
455             pr->readres[i] = -1;
456         }
457     }
458
459     for( i = 0; i < pr->nfds; i++ ) {
460         j = tapefd_close(pr->fds[i]);
461         if ( j != 0 )
462            res = j;
463     }
464
465     for( i = 0; i < pr->nfds; i++ ) {
466         int stat;
467         if(pr->readres[i] != -1) {
468             waitpid((pid_t)pr->readres[i], &stat, 0);
469             if( WEXITSTATUS(stat) != 0 ) {
470                 res = WEXITSTATUS(stat);
471                 if( res == 255 )
472                     res = -1;
473             }
474         }
475     }
476     if (pr->nfds > 1) {
477         (void)close(fd);        /* close the dummy /dev/null descriptor */
478     }
479     if (0 != pr->fds) {
480         fds_p = &pr->fds;
481         amtable_free((void **)fds_p, &pr->fd_count);
482     }
483     if (0 != pr->readres) {
484         amfree(pr->readres);
485     }
486     if (0 != pr->xorbuf) {
487         amfree(pr->xorbuf);
488     }
489     pr->nopen = 0;
490     errno = save_errno;
491     rait_debug((stderr, "rait_close:returning %d%s%s\n",
492                         res,
493                         (res < 0) ? ": " : "",
494                         (res < 0) ? strerror(errno) : ""));
495     return res;
496 }
497
498 /*\f*/
499
500 /*
501 ** seek out to the nth byte on the RAIT set.
502 ** this is assumed to be evenly divided across all the stripes
503 */
504 off_t
505 rait_lseek(
506     int         fd,
507     off_t       pos,
508     int         whence)
509 {
510     int i;                      /* drive number in RAIT */
511     off_t res,                  /* result of lseeks */
512          total;                 /* total of results */
513     RAIT *pr;                   /* RAIT slot in table */
514
515     rait_debug((stderr, "rait_lseek(%d," OFF_T_FMT ",%d)\n",
516                 fd, (OFF_T_FMT_TYPE)pos, whence));
517
518     if ((fd < 0) || ((size_t)fd >= rait_table_count)) {
519         errno = EBADF;
520         rait_debug((stderr, "rait_lseek:returning %d: %s\n",
521                             -1,
522                             strerror(errno)));
523         return (off_t)-1;
524     }
525
526     pr = &rait_table[fd];
527     if (0 == pr->nopen) {
528         errno = EBADF;
529         rait_debug((stderr, "rait_lseek:returning %d: %s\n",
530                             -1,
531                             strerror(errno)));
532         return (off_t)-1;
533     }
534
535     if ((pr->nfds > 1) && ((pos % (off_t)(pr->nfds-1)) != (off_t)0)) {
536         errno = EDOM;
537         total = (off_t)-1;
538     } else {
539         total = (off_t)0;
540         pos = pos / (off_t)pr->nfds;
541         for( i = 0; i < pr->nfds; i++ ) {
542             if ((off_t)0 >= (res = lseek(pr->fds[i], pos, whence))) {
543                 total = res;
544                 break;
545             }
546             total += res;
547         }
548     }
549     rait_debug((stderr, "rait_lseek:returning %ld%s%s\n",
550                         total,
551                         (total < 0) ? ": " : "",
552                         (total < 0) ? strerror(errno) : ""));
553     return total;
554 }
555
556 /*\f*/
557
558 /*
559 ** if we only have one stream, just do a write,
560 ** otherwise compute an xor sum, and do several
561 ** writes...
562 */
563 ssize_t
564 rait_write(
565     int         fd,
566     const void *bufptr,
567     size_t      len)
568 {
569     const char *buf = bufptr;
570     int i;      /* drive number */
571     size_t j;   /* byte offset */
572     RAIT *pr;   /* RAIT structure for this RAIT */
573     ssize_t res;
574     ssize_t total = 0;
575     int data_fds;       /* number of data stream file descriptors */
576
577     rait_debug((stderr, "rait_write(%d,%lx,%d)\n",fd,(unsigned long)buf,len));
578
579     if ((fd < 0) || ((size_t)fd >= rait_table_count)) {
580         errno = EBADF;
581         rait_debug((stderr, "rait_write:returning %d: %s\n",
582                             -1,
583                             strerror(errno)));
584         return -1;
585     }
586
587     pr = &rait_table[fd];
588     if (0 == pr->nopen) {
589         errno = EBADF;
590         rait_debug((stderr, "rait_write:returning %d: %s\n",
591                             -1,
592                             strerror(errno)));
593         return -1;
594     }
595
596     /* need to be able to slice it up evenly... */
597     if (pr->nfds > 1) {
598         data_fds = pr->nfds - 1;
599         if (0 != len % data_fds) {
600             errno = EDOM;
601             rait_debug((stderr, "rait_write:returning %d: %s\n",
602                                 -1,
603                                 strerror(errno)));
604             return -1;
605         }
606         /* each slice gets an even portion */
607         len = len / data_fds;
608
609         /* make sure we have enough buffer space */
610         if (len > (size_t)pr->xorbuflen) {
611             if (0 != pr->xorbuf) {
612                 amfree(pr->xorbuf);
613             }
614             pr->xorbuf = alloc(len);
615             pr->xorbuflen = len;
616         }
617
618         /* compute the sum */
619         memcpy(pr->xorbuf, buf, len);
620         for( i = 1; i < data_fds; i++ ) {
621             for( j = 0; j < len; j++ ) {
622                 pr->xorbuf[j] ^= buf[len * i + j];
623             }
624         }
625     } else {
626         data_fds = pr->nfds;
627     }
628
629     /* write the chunks in the main buffer */
630     for( i = 0; i < data_fds; i++ ) {
631         res = tapefd_write(pr->fds[i], buf + len*i , len);
632         rait_debug((stderr, "rait_write: write(%d,%lx,%d) returns %d%s%s\n",
633                         pr->fds[i],
634                         (unsigned long)(buf + len*i),
635                         len,
636                         res,
637                         (res < 0) ? ": " : "",
638                         (res < 0) ? strerror(errno) : ""));
639         if (res < 0) {
640             total = res;
641             break;
642         }
643         total += res;
644     }
645     if (total >= 0 && pr->nfds > 1) {
646         /* write the sum, don't include it in the total bytes written */
647         res = tapefd_write(pr->fds[i], pr->xorbuf, len);
648         rait_debug((stderr, "rait_write: write(%d,%lx,%d) returns %d%s%s\n",
649                     pr->fds[i],
650                     (unsigned long)pr->xorbuf,
651                     len,
652                     res,
653                     (res < 0) ? ": " : "",
654                     (res < 0) ? strerror(errno) : ""));
655         if (res < 0) {
656             total = res;
657         }
658     }
659
660     rait_debug((stderr, "rait_write:returning %d%s%s\n",
661                         total,
662                         (total < 0) ? ": " : "",
663                         (total < 0) ? strerror(errno) : ""));
664
665     return total;
666 }
667
668 /*\f*/
669
670 /*
671 ** once again, if there is one data stream do a read, otherwise
672 ** do all n reads, and if any of the first n - 1 fail, compute
673 ** the missing block from the other three, then return the data.
674 ** there's some silliness here for reading tape with bigger buffers
675 ** than we wrote with, (thus the extra bcopys down below).  On disk if
676 ** you read with a bigger buffer size than you wrote with, you just
677 ** garble the data...
678 */
679 ssize_t
680 rait_read(
681     int         fd,
682     void *      bufptr,
683     size_t      len)
684 {
685     char *buf = bufptr;
686     int nerrors, neofs, errorblock;
687     ssize_t    total;
688     int i;
689     size_t j;
690     RAIT *pr;
691     int data_fds;
692     int save_errno = errno;
693     ssize_t maxreadres = 0;
694     int sum_mismatch = 0;
695
696     rait_debug((stderr, "rait_read(%d,%lx,%d)\n",fd,(unsigned long)buf,len));
697
698     if ((fd < 0) || ((size_t)fd >= rait_table_count)) {
699         errno = EBADF;
700         rait_debug((stderr, "rait_read:returning %d: %s\n",
701                             -1,
702                             strerror(errno)));
703         return -1;
704     }
705
706     pr = &rait_table[fd];
707     if (0 == pr->nopen) {
708         errno = EBADF;
709         rait_debug((stderr, "rait_read:returning %d: %s\n",
710                             -1,
711                             strerror(errno)));
712         return -1;
713     }
714
715     nerrors = 0;
716     neofs = 0;
717     errorblock = -1;
718     /* once again , we slice it evenly... */
719     if (pr->nfds > 1) {
720         data_fds = pr->nfds - 1;
721         if (0 != len % data_fds) {
722             errno = EDOM;
723             rait_debug((stderr, "rait_read:returning %d: %s\n",
724                                 -1,
725                                 strerror(errno)));
726             return -1;
727         }
728         len = len / data_fds;
729     } else {
730         data_fds = 1;
731     }
732
733     /* try all the reads, save the result codes */
734     /* count the eof/errors */
735     for( i = 0; i < data_fds; i++ ) {
736         pr->readres[i] = tapefd_read(pr->fds[i], buf + len*i , len);
737         rait_debug((stderr, "rait_read: read on fd %d returns %d%s%s\n",
738                     pr->fds[i],
739                     pr->readres[i],
740                     (pr->readres[i] < 0) ? ": " : "",
741                     (pr->readres[i] < 0) ? strerror(errno) : ""));
742         if ( pr->readres[i] <= 0 ) {
743             if ( pr->readres[i] == 0 ) {
744                 neofs++;
745             } else {
746                 if (0 == nerrors) {
747                     save_errno = errno;
748                 }
749                 nerrors++;
750             }
751             errorblock = i;
752         } else if (pr->readres[i] > maxreadres) {
753             maxreadres = pr->readres[i];
754         }
755     }
756     if (pr->nfds > 1) {
757         /* make sure we have enough buffer space */
758         if (len > (size_t)pr->xorbuflen) {
759             if (0 != pr->xorbuf) {
760                 amfree(pr->xorbuf);
761             }
762             pr->xorbuf = alloc(len);
763             pr->xorbuflen = len;
764         }
765         pr->readres[i] = tapefd_read(pr->fds[i], pr->xorbuf , len);
766         rait_debug((stderr, "rait_read: read on fd %d returns %d%s%s\n",
767                     pr->fds[i],
768                     pr->readres[i],
769                     (pr->readres[i] < 0) ? ": " : "",
770                     (pr->readres[i] < 0) ? strerror(errno) : ""));
771     }
772
773     /*
774      * Make sure all the reads were the same length
775      */
776     for (j = 0; j < (size_t)pr->nfds; j++) {
777         if (pr->readres[j] != maxreadres) {
778             nerrors++;
779             errorblock = (int)j;
780         }
781     }
782
783     /*
784      * If no errors, check that the xor sum matches
785      */
786     if ( nerrors == 0 && pr->nfds > 1  ) {
787        for(i = 0; i < (int)maxreadres; i++ ) {
788            int sum = 0;
789            for(j = 0; (j + 1) < (size_t)pr->nfds; j++) {
790                sum ^= (buf + len * j)[i];
791            }
792            if (sum != pr->xorbuf[i]) {
793               sum_mismatch = 1;
794            }
795        }
796     }
797
798     /*
799     ** now decide what "really" happened --
800     ** all n getting eof is a "real" eof
801     ** just one getting an error/eof is recoverable if we are doing RAIT
802     ** anything else fails
803     */
804
805     if (neofs == pr->nfds) {
806         rait_debug((stderr, "rait_read:returning 0\n"));
807         return 0;
808     }
809
810     if (sum_mismatch) {
811         errno = EDOM;
812         rait_debug((stderr, "rait_read:returning %d: %s\n",
813                             -1,
814                             "XOR block mismatch"));
815         return -1;
816     }
817
818     if (nerrors > 1 || (pr->nfds <= 1 && nerrors > 0)) {
819         errno = save_errno;
820         rait_debug((stderr, "rait_read:returning %d: %s\n",
821                             -1,
822                             strerror(errno)));
823         return -1;
824     }
825
826     /*
827     ** so now if we failed on a data block, we need to do a recovery
828     ** if we failed on the xor block -- who cares?
829     */
830     if (nerrors == 1 && pr->nfds > 1 && errorblock != pr->nfds-1) {
831
832         rait_debug((stderr, "rait_read: fixing data from fd %d\n",
833                             pr->fds[errorblock]));
834
835         /* the reads were all *supposed* to be the same size, so... */
836         pr->readres[errorblock] = maxreadres;
837
838         /* fill it in first with the xor sum */
839         memcpy(buf + len * errorblock, pr->xorbuf, len);
840
841         /* xor back out the other blocks */
842         for( i = 0; i < data_fds; i++ ) {
843             if( i != errorblock ) {
844                 for( j = 0; j < len ; j++ ) {
845                     buf[j + len * errorblock] ^= buf[j + len * i];
846                 }
847             }
848         }
849         /* there, now the block is back as if it never failed... */
850     }
851
852     /* pack together partial reads... */
853     total = pr->readres[0];
854     for( i = 1; i < data_fds; i++ ) {
855         if (total != (ssize_t)(len * i)) {
856             memmove(buf + total, buf + len*i, (size_t)pr->readres[i]);
857         }
858         total += pr->readres[i];
859     }
860
861     rait_debug((stderr, "rait_read:returning %d%s%s\n",
862                         total,
863                         (total < 0) ? ": " : "",
864                         (total < 0) ? strerror(errno) : ""));
865
866     return total;
867 }
868
869 /*\f*/
870
871 int
872 rait_ioctl(
873     int         fd,
874     int         op,
875     void *      p)
876 {
877     int i, res = 0;
878     RAIT *pr;
879     int errors = 0;
880
881     rait_debug((stderr, "rait_ioctl(%d,%d)\n",fd,op));
882
883     if ((fd < 0) || ((size_t)fd >= rait_table_count)) {
884         errno = EBADF;
885         rait_debug((stderr, "rait_ioctl:returning %d: %s\n",
886                             -1,
887                             strerror(errno)));
888         return -1;
889     }
890
891     pr = &rait_table[fd];
892     if (0 == pr->nopen) {
893         errno = EBADF;
894         rait_debug((stderr, "rait_ioctl:returning %d: %s\n",
895                             -1,
896                             strerror(errno)));
897         return -1;
898     }
899
900     for( i = 0; i < pr->nfds ; i++ ) {
901         /*@ignore@*/
902         res = ioctl(pr->fds[i], op, p);
903         /*@end@*/
904         if ( res != 0 ) {
905             errors++;
906             if (errors > 1) {
907                 break;
908             }
909             res = 0;
910         }
911     }
912
913     rait_debug((stderr, "rait_ioctl: returning %d%s%s\n",
914                         res,
915                         (res < 0) ? ": " : "",
916                         (res < 0) ? strerror(errno) : ""));
917
918     return res;
919 }
920
921 /*
922 ** access() all the devices, returning if any fail
923 */
924 int
925 rait_access(
926     char *      devname,
927     int         flags)
928 {
929     int res = 0;
930     char *dev_left;             /* string before { */
931     char *dev_right;            /* string after } */
932     char *dev_next;             /* string inside {} */
933     char *dev_real;             /* parsed device name */
934
935     /* copy and parse the dev string so we can scribble on it */
936     devname = stralloc(devname);
937     if (0 == devname) {
938         rait_debug((stderr, "rait_access:returning %d: %s\n",
939                             -1,
940                             "out of stralloc memory"));
941         return -1;
942     }
943     if ( 0 != tapeio_init_devname(devname, &dev_left, &dev_right, &dev_next)) {
944         rait_debug((stderr, "rait_access:returning %d: %s\n",
945                             -1,
946                             strerror(errno)));
947         return -1;
948     }
949
950     while( 0 != (dev_real = tapeio_next_devname(dev_left, dev_right, &dev_next))) {
951         res = tape_access(dev_real, flags);
952         rait_debug((stderr,"rait_access:access( %s, %d ) yields %d\n",
953                 dev_real, flags, res ));
954         amfree(dev_real);
955         if (res < 0) {
956             break;
957         }
958     }
959     amfree(devname);
960
961     rait_debug((stderr, "rait_access: returning %d%s%s\n",
962                         res,
963                         (res < 0) ? ": " : "",
964                         (res < 0) ? strerror(errno) : ""));
965
966     return res;
967 }
968
969 /*
970 ** stat all the devices, returning the last one unless one fails
971 */
972 int
973 rait_stat(
974     char *       devname,
975     struct stat *buf)
976 {
977     int res = 0;
978     char *dev_left;             /* string before { */
979     char *dev_right;            /* string after } */
980     char *dev_next;             /* string inside {} */
981     char *dev_real;             /* parsed device name */
982
983     /* copy and parse the dev string so we can scribble on it */
984     devname = stralloc(devname);
985     if (0 == devname) {
986         rait_debug((stderr, "rait_access:returning %d: %s\n",
987                             -1,
988                             "out of stralloc memory"));
989         return -1;
990     }
991     if ( 0 != tapeio_init_devname(devname, &dev_left, &dev_right, &dev_next)) {
992         rait_debug((stderr, "rait_access:returning %d: %s\n",
993                             -1,
994                             strerror(errno)));
995         return -1;
996     }
997
998     while( 0 != (dev_real = tapeio_next_devname(dev_left, dev_right, &dev_next))) {
999         res = tape_stat(dev_real, buf);
1000         rait_debug((stderr,"rait_stat:stat( %s ) yields %d (%s)\n",
1001                 dev_real, res, (res != 0) ? strerror(errno) : "no error" ));
1002         amfree(dev_real);
1003         if (res != 0) {
1004             break;
1005         }
1006     }
1007     amfree(devname);
1008
1009     rait_debug((stderr, "rait_access: returning %d%s%s\n",
1010                         res,
1011                         (res < 0) ? ": " : "",
1012                         (res < 0) ? strerror(errno) : ""));
1013
1014     return res;
1015 }
1016
1017 /*\f*/
1018
1019 int
1020 rait_copy(
1021     char *      f1,
1022     char *      f2,
1023     size_t      buflen)
1024 {
1025     int t1, t2;
1026     ssize_t len;
1027     ssize_t wres;
1028     char *buf;
1029     int save_errno;
1030
1031     t1 = rait_open(f1,O_RDONLY,0644);
1032     if (t1 < 0) {
1033         return t1;
1034     }
1035     t2 = rait_open(f2,O_CREAT|O_RDWR,0644);
1036     if (t2 < 0) {
1037         save_errno = errno;
1038         (void)rait_close(t1);
1039         errno = save_errno;
1040         return -1;
1041     }
1042     buf = alloc(buflen);
1043     do {
1044         len = rait_read(t1,buf,buflen);
1045         if (len > 0 ) {
1046             wres = rait_write(t2, buf, (size_t)len);
1047             if (wres < 0) {
1048                 len = -1;
1049                 break;
1050             }
1051         }
1052     } while( len > 0 );
1053     save_errno = errno;
1054     amfree(buf);
1055     (void)rait_close(t1);
1056     (void)rait_close(t2);
1057     errno = save_errno;
1058     return (len < 0) ? -1 : 0;
1059 }
1060
1061 /*\f*/
1062
1063 /*
1064 ** Amanda Tape API routines:
1065 */
1066
1067 static int
1068 rait_tapefd_ioctl(
1069     int         (*func0)(int),
1070     int         (*func1)(int, off_t),
1071     int         fd,
1072     off_t       count)
1073 {
1074     int i, j, res = 0;
1075     RAIT *pr;
1076     int errors = 0;
1077     pid_t kid;
1078     int status = 0;
1079
1080     rait_debug((stderr, "rait_tapefd_ioctl(%d,%d)\n",fd,count));
1081
1082     if ((fd < 0) || ((size_t)fd >= rait_table_count)) {
1083         errno = EBADF;
1084         rait_debug((stderr, "rait_tapefd_ioctl:returning %d: %s\n",
1085                             -1,
1086                             strerror(errno)));
1087         return -1;
1088     }
1089
1090     pr = &rait_table[fd];
1091     if (0 == pr->nopen) {
1092         errno = EBADF;
1093         rait_debug((stderr, "rait_tapefd_ioctl:returning %d: %s\n",
1094                             -1,
1095                             strerror(errno)));
1096         return -1;
1097     }
1098
1099     if (0 == pr->readres && 0 < pr->nfds) {
1100         pr->readres = alloc(pr->nfds * SIZEOF(*pr->readres));
1101         memset(pr->readres, 0, pr->nfds * SIZEOF(*pr->readres));
1102     }
1103
1104     for( i = 0; i < pr->nfds ; i++ ) {
1105         if(tapefd_can_fork(pr->fds[i])) {
1106             if ((kid = fork()) < 1) {
1107                 rait_debug((stderr, "in kid, fork returned %d\n", kid));
1108                 /* if we are the kid, or fork failed do the action */
1109                 if (func0 != NULL) {
1110                     res = (*func0)(pr->fds[i]);
1111                 } else {
1112                     res = (*func1)(pr->fds[i], count);
1113                 }
1114                 rait_debug((stderr, "in kid, func (%d) returned %d errno %s\n",
1115                                 pr->fds[i], res, strerror(errno)));
1116                 if (kid == 0)
1117                     exit(res);
1118             } else {
1119                 rait_debug((stderr, "in parent, fork returned %d\n", kid));
1120                 pr->readres[i] = (ssize_t)kid;
1121             }
1122         }
1123         else {
1124             if(func0 != NULL) {
1125                 j = (*func0)(pr->fds[i]);
1126             } else {
1127                 j = (*func1)(pr->fds[i], count);
1128             }
1129             if( j != 0) {
1130                 errors++;
1131             }
1132             pr->readres[i] = -1;
1133         }
1134     }
1135     for( i = 0; i < pr->nfds ; i++ ) {
1136         if(tapefd_can_fork(pr->fds[i])) {
1137             rait_debug((stderr, "in parent, waiting for %d\n", pr->readres[i]));
1138             waitpid((pid_t)pr->readres[i], &status, 0);
1139             if( WEXITSTATUS(status) != 0 ) {
1140                 res = WEXITSTATUS(status);
1141                 if( res == 255 )
1142                     res = -1;
1143             }
1144             rait_debug((stderr, "in parent, return code was %d\n", res));
1145             if ( res != 0 ) {
1146                 errors++;
1147                 res = 0;
1148             }
1149         }
1150     }
1151     if (errors > 0) {
1152         res = -1;
1153     }
1154
1155     rait_debug((stderr, "rait_tapefd_ioctl: returning %d%s%s\n",
1156                         res,
1157                         (res < 0) ? ": " : "",
1158                         (res < 0) ? strerror(errno) : ""));
1159
1160     return res;
1161 }
1162
1163 int
1164 rait_tapefd_fsf(
1165     int         fd,
1166     off_t       count)
1167 {
1168     return rait_tapefd_ioctl(NULL, tapefd_fsf, fd, count);
1169 }
1170
1171 int
1172 rait_tapefd_rewind(
1173     int         fd)
1174 {
1175     return rait_tapefd_ioctl(tapefd_rewind, NULL, fd, (off_t)-1);
1176 }
1177
1178 int
1179 rait_tapefd_unload(
1180     int         fd)
1181 {
1182     return rait_tapefd_ioctl(tapefd_unload, NULL, fd, (off_t)-1);
1183 }
1184
1185 int
1186 rait_tapefd_weof(
1187     int         fd,
1188     off_t       count)
1189 {
1190     return rait_tapefd_ioctl(NULL, tapefd_weof, fd, count);
1191 }
1192
1193 int
1194 rait_tape_open(
1195     char *      name,
1196     int         flags,
1197     mode_t      mask)
1198 {
1199     return rait_open(name, flags, mask);
1200 }
1201
1202 int
1203 rait_tapefd_status(
1204     int                  fd,
1205     struct am_mt_status *stat)
1206 {
1207     int i;
1208     RAIT *pr;
1209     int res = 0;
1210     int errors = 0;
1211
1212     rait_debug((stderr, "rait_tapefd_status(%d)\n",fd));
1213
1214     if ((fd < 0) || ((size_t)fd >= rait_table_count)) {
1215         errno = EBADF;
1216         rait_debug((stderr, "rait_tapefd_status:returning %d: %s\n",
1217                             -1,
1218                             strerror(errno)));
1219         return -1;
1220     }
1221
1222     pr = &rait_table[fd];
1223     if (0 == pr->nopen) {
1224         errno = EBADF;
1225         rait_debug((stderr, "rait_tapefd_status:returning %d: %s\n",
1226                             -1,
1227                             strerror(errno)));
1228         return -1;
1229     }
1230
1231     for( i = 0; i < pr->nfds ; i++ ) {
1232         res = tapefd_status(pr->fds[i], stat);
1233         if(res != 0) {
1234             errors++;
1235         }
1236     }
1237     if (errors > 0) {
1238         res = -1;
1239     }
1240     return res;
1241 }
1242
1243 void
1244 rait_tapefd_resetofs(
1245     int         fd)
1246 {
1247     (void)rait_lseek(fd, (off_t)0, SEEK_SET);
1248 }
1249
1250 int
1251 rait_tapefd_can_fork(
1252     int         fd)
1253 {
1254     (void)fd;   /* Quiet unused parameter warning */
1255
1256     return 0;
1257 }
1258