Deleted Added
sdiff udiff text old ( 18286 ) new ( 22192 )
full compact
1/*-
2 * Copyright (c) 1980, 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)tape.c 8.2 (Berkeley) 3/17/94";
36#endif /* not lint */
37
38#include <sys/param.h>
39#include <sys/socket.h>
40#include <sys/time.h>
41#include <sys/wait.h>
42#ifdef sunos
43#include <sys/vnode.h>
44
45#include <ufs/fs.h>
46#include <ufs/inode.h>
47#else
48#include <ufs/ffs/fs.h>
49#include <ufs/ufs/dinode.h>
50#endif
51
52#include <protocols/dumprestore.h>
53
54#include <errno.h>
55#include <fcntl.h>
56#include <setjmp.h>
57#include <signal.h>
58#include <stdio.h>
59#ifdef __STDC__
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63#else
64int write(), read();
65#endif
66
67#include "dump.h"
68#include "pathnames.h"
69
70int writesize; /* size of malloc()ed buffer for tape */
71long lastspclrec = -1; /* tape block number of last written header */
72int trecno = 0; /* next record to write in current block */
73extern long blocksperfile; /* number of blocks per output file */
74long blocksthisvol; /* number of blocks on current output file */
75extern int ntrec; /* blocking factor on tape */
76extern int cartridge;
77extern char *host;
78char *nexttape;
79
80static int atomic __P((int (*)(), int, char *, int));
81static void doslave __P((int, int));
82static void enslave __P((void));
83static void flushtape __P((void));
84static void killall __P((void));
85static void rollforward __P((void));
86
87/*
88 * Concurrent dump mods (Caltech) - disk block reading and tape writing
89 * are exported to several slave processes. While one slave writes the
90 * tape, the others read disk blocks; they pass control of the tape in
91 * a ring via signals. The parent process traverses the filesystem and
92 * sends writeheader()'s and lists of daddr's to the slaves via pipes.
93 * The following structure defines the instruction packets sent to slaves.
94 */
95struct req {
96 daddr_t dblk;
97 int count;
98};
99int reqsiz;
100
101#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
102struct slave {
103 int tapea; /* header number at start of this chunk */
104 int count; /* count to next header (used for TS_TAPE */
105 /* after EOT) */
106 int inode; /* inode that we are currently dealing with */
107 int fd; /* FD for this slave */
108 int pid; /* PID for this slave */
109 int sent; /* 1 == we've sent this slave requests */
110 int firstrec; /* record number of this block */
111 char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
112 struct req *req; /* buffer for requests */
113} slaves[SLAVES+1];
114struct slave *slp;
115
116char (*nextblock)[TP_BSIZE];
117
118int master; /* pid of master, for sending error signals */
119int tenths; /* length of tape used per block written */
120static int caught; /* have we caught the signal to proceed? */
121static int ready; /* have we reached the lock point without having */
122 /* received the SIGUSR2 signal from the prev slave? */
123static jmp_buf jmpbuf; /* where to jump to if we are ready when the */
124 /* SIGUSR2 arrives from the previous slave */
125
126int
127alloctape()
128{
129 int pgoff = getpagesize() - 1;
130 char *buf;
131 int i;
132
133 writesize = ntrec * TP_BSIZE;
134 reqsiz = (ntrec + 1) * sizeof(struct req);
135 /*
136 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
137 * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
138 * repositioning after stopping, i.e, streaming mode, where the gap is
139 * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
140 */
141 if (blocksperfile == 0 && !unlimited)
142 tenths = writesize / density +
143 (cartridge ? 16 : density == 625 ? 5 : 8);
144 /*
145 * Allocate tape buffer contiguous with the array of instruction
146 * packets, so flushtape() can write them together with one write().
147 * Align tape buffer on page boundary to speed up tape write().
148 */
149 for (i = 0; i <= SLAVES; i++) {
150 buf = (char *)
151 malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
152 if (buf == NULL)
153 return(0);
154 slaves[i].tblock = (char (*)[TP_BSIZE])
155 (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
156 slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
157 }
158 slp = &slaves[0];
159 slp->count = 1;
160 slp->tapea = 0;
161 slp->firstrec = 0;
162 nextblock = slp->tblock;
163 return(1);
164}
165
166void
167writerec(dp, isspcl)
168 char *dp;
169 int isspcl;
170{
171
172 slp->req[trecno].dblk = (daddr_t)0;
173 slp->req[trecno].count = 1;
174 *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp;
175 if (isspcl)
176 lastspclrec = spcl.c_tapea;
177 trecno++;
178 spcl.c_tapea++;
179 if (trecno >= ntrec)
180 flushtape();
181}
182
183void
184dumpblock(blkno, size)
185 daddr_t blkno;
186 int size;
187{
188 int avail, tpblks, dblkno;
189
190 dblkno = fsbtodb(sblock, blkno);
191 tpblks = size >> tp_bshift;
192 while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
193 slp->req[trecno].dblk = dblkno;
194 slp->req[trecno].count = avail;
195 trecno += avail;
196 spcl.c_tapea += avail;
197 if (trecno >= ntrec)
198 flushtape();
199 dblkno += avail << (tp_bshift - dev_bshift);
200 tpblks -= avail;
201 }
202}
203
204int nogripe = 0;
205
206void
207tperror(signo)
208 int signo;
209{
210
211 if (pipeout) {
212 msg("write error on %s\n", tape);
213 quit("Cannot recover\n");
214 /* NOTREACHED */
215 }
216 msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno);
217 broadcast("DUMP WRITE ERROR!\n");
218 if (!query("Do you want to restart?"))
219 dumpabort(0);
220 msg("Closing this volume. Prepare to restart with new media;\n");
221 msg("this dump volume will be rewritten.\n");
222 killall();
223 nogripe = 1;
224 close_rewind();
225 Exit(X_REWRITE);
226}
227
228void
229sigpipe(signo)
230 int signo;
231{
232
233 quit("Broken pipe\n");
234}
235
236static void
237flushtape()
238{
239 int i, blks, got;
240 long lastfirstrec;
241
242 int siz = (char *)nextblock - (char *)slp->req;
243
244 slp->req[trecno].count = 0; /* Sentinel */
245
246 if (atomic(write, slp->fd, (char *)slp->req, siz) != siz)
247 quit("error writing command pipe: %s\n", strerror(errno));
248 slp->sent = 1; /* we sent a request, read the response later */
249
250 lastfirstrec = slp->firstrec;
251
252 if (++slp >= &slaves[SLAVES])
253 slp = &slaves[0];
254
255 /* Read results back from next slave */
256 if (slp->sent) {
257 if (atomic(read, slp->fd, (char *)&got, sizeof got)
258 != sizeof got) {
259 perror(" DUMP: error reading command pipe in master");
260 dumpabort(0);
261 }
262 slp->sent = 0;
263
264 /* Check for end of tape */
265 if (got < writesize) {
266 msg("End of tape detected\n");
267
268 /*
269 * Drain the results, don't care what the values were.
270 * If we read them here then trewind won't...
271 */
272 for (i = 0; i < SLAVES; i++) {
273 if (slaves[i].sent) {
274 if (atomic(read, slaves[i].fd,
275 (char *)&got, sizeof got)
276 != sizeof got) {
277 perror(" DUMP: error reading command pipe in master");
278 dumpabort(0);
279 }
280 slaves[i].sent = 0;
281 }
282 }
283
284 close_rewind();
285 rollforward();
286 return;
287 }
288 }
289
290 blks = 0;
291 if (spcl.c_type != TS_END) {
292 for (i = 0; i < spcl.c_count; i++)
293 if (spcl.c_addr[i] != 0)
294 blks++;
295 }
296 slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
297 slp->tapea = spcl.c_tapea;
298 slp->firstrec = lastfirstrec + ntrec;
299 slp->inode = curino;
300 nextblock = slp->tblock;
301 trecno = 0;
302 asize += tenths;
303 blockswritten += ntrec;
304 blocksthisvol += ntrec;
305 if (!pipeout && !unlimited && (blocksperfile ?
306 (blocksthisvol >= blocksperfile) : (asize > tsize))) {
307 close_rewind();
308 startnewtape(0);
309 }
310 timeest();
311}
312
313void
314trewind()
315{
316 int f;
317 int got;
318
319 for (f = 0; f < SLAVES; f++) {
320 /*
321 * Drain the results, but unlike EOT we DO (or should) care
322 * what the return values were, since if we detect EOT after
323 * we think we've written the last blocks to the tape anyway,
324 * we have to replay those blocks with rollforward.
325 *
326 * fixme: punt for now.
327 */
328 if (slaves[f].sent) {
329 if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
330 != sizeof got) {
331 perror(" DUMP: error reading command pipe in master");
332 dumpabort(0);
333 }
334 slaves[f].sent = 0;
335 if (got != writesize) {
336 msg("EOT detected in last 2 tape records!\n");
337 msg("Use a longer tape, decrease the size estimate\n");
338 quit("or use no size estimate at all.\n");
339 }
340 }
341 (void) close(slaves[f].fd);
342 }
343 while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */
344 /* void */;
345
346 if (pipeout)
347 return;
348
349 msg("Closing %s\n", tape);
350
351#ifdef RDUMP
352 if (host) {
353 rmtclose();
354 while (rmtopen(tape, 0) < 0)
355 sleep(10);
356 rmtclose();
357 return;
358 }
359#endif
360 (void) close(tapefd);
361 while ((f = open(tape, 0)) < 0)
362 sleep (10);
363 (void) close(f);
364}
365
366void
367close_rewind()
368{
369 trewind();
370 if (nexttape)
371 return;
372 if (!nogripe) {
373 msg("Change Volumes: Mount volume #%d\n", tapeno+1);
374 broadcast("CHANGE DUMP VOLUMES!\7\7\n");
375 }
376 while (!query("Is the new volume mounted and ready to go?"))
377 if (query("Do you want to abort?")) {
378 dumpabort(0);
379 /*NOTREACHED*/
380 }
381}
382
383void
384rollforward()
385{
386 register struct req *p, *q, *prev;
387 register struct slave *tslp;
388 int i, size, savedtapea, got;
389 union u_spcl *ntb, *otb;
390 tslp = &slaves[SLAVES];
391 ntb = (union u_spcl *)tslp->tblock[1];
392
393 /*
394 * Each of the N slaves should have requests that need to
395 * be replayed on the next tape. Use the extra slave buffers
396 * (slaves[SLAVES]) to construct request lists to be sent to
397 * each slave in turn.
398 */
399 for (i = 0; i < SLAVES; i++) {
400 q = &tslp->req[1];
401 otb = (union u_spcl *)slp->tblock;
402
403 /*
404 * For each request in the current slave, copy it to tslp.
405 */
406
407 prev = NULL;
408 for (p = slp->req; p->count > 0; p += p->count) {
409 *q = *p;
410 if (p->dblk == 0)
411 *ntb++ = *otb++; /* copy the datablock also */
412 prev = q;
413 q += q->count;
414 }
415 if (prev == NULL)
416 quit("rollforward: protocol botch");
417 if (prev->dblk != 0)
418 prev->count -= 1;
419 else
420 ntb--;
421 q -= 1;
422 q->count = 0;
423 q = &tslp->req[0];
424 if (i == 0) {
425 q->dblk = 0;
426 q->count = 1;
427 trecno = 0;
428 nextblock = tslp->tblock;
429 savedtapea = spcl.c_tapea;
430 spcl.c_tapea = slp->tapea;
431 startnewtape(0);
432 spcl.c_tapea = savedtapea;
433 lastspclrec = savedtapea - 1;
434 }
435 size = (char *)ntb - (char *)q;
436 if (atomic(write, slp->fd, (char *)q, size) != size) {
437 perror(" DUMP: error writing command pipe");
438 dumpabort(0);
439 }
440 slp->sent = 1;
441 if (++slp >= &slaves[SLAVES])
442 slp = &slaves[0];
443
444 q->count = 1;
445
446 if (prev->dblk != 0) {
447 /*
448 * If the last one was a disk block, make the
449 * first of this one be the last bit of that disk
450 * block...
451 */
452 q->dblk = prev->dblk +
453 prev->count * (TP_BSIZE / DEV_BSIZE);
454 ntb = (union u_spcl *)tslp->tblock;
455 } else {
456 /*
457 * It wasn't a disk block. Copy the data to its
458 * new location in the buffer.
459 */
460 q->dblk = 0;
461 *((union u_spcl *)tslp->tblock) = *ntb;
462 ntb = (union u_spcl *)tslp->tblock[1];
463 }
464 }
465 slp->req[0] = *q;
466 nextblock = slp->tblock;
467 if (q->dblk == 0)
468 nextblock++;
469 trecno = 1;
470
471 /*
472 * Clear the first slaves' response. One hopes that it
473 * worked ok, otherwise the tape is much too short!
474 */
475 if (slp->sent) {
476 if (atomic(read, slp->fd, (char *)&got, sizeof got)
477 != sizeof got) {
478 perror(" DUMP: error reading command pipe in master");
479 dumpabort(0);
480 }
481 slp->sent = 0;
482
483 if (got != writesize) {
484 quit("EOT detected at start of the tape!\n");
485 }
486 }
487}
488
489/*
490 * We implement taking and restoring checkpoints on the tape level.
491 * When each tape is opened, a new process is created by forking; this
492 * saves all of the necessary context in the parent. The child
493 * continues the dump; the parent waits around, saving the context.
494 * If the child returns X_REWRITE, then it had problems writing that tape;
495 * this causes the parent to fork again, duplicating the context, and
496 * everything continues as if nothing had happened.
497 */
498void
499startnewtape(top)
500 int top;
501{
502 int parentpid;
503 int childpid;
504 int status;
505 int waitpid;
506 char *p;
507#ifdef sunos
508 void (*interrupt_save)();
509#else
510 sig_t interrupt_save;
511#endif
512
513 interrupt_save = signal(SIGINT, SIG_IGN);
514 parentpid = getpid();
515
516restore_check_point:
517 (void)signal(SIGINT, interrupt_save);
518 /*
519 * All signals are inherited...
520 */
521 childpid = fork();
522 if (childpid < 0) {
523 msg("Context save fork fails in parent %d\n", parentpid);
524 Exit(X_ABORT);
525 }
526 if (childpid != 0) {
527 /*
528 * PARENT:
529 * save the context by waiting
530 * until the child doing all of the work returns.
531 * don't catch the interrupt
532 */
533 signal(SIGINT, SIG_IGN);
534#ifdef TDEBUG
535 msg("Tape: %d; parent process: %d child process %d\n",
536 tapeno+1, parentpid, childpid);
537#endif /* TDEBUG */
538 while ((waitpid = wait(&status)) != childpid)
539 msg("Parent %d waiting for child %d has another child %d return\n",
540 parentpid, childpid, waitpid);
541 if (status & 0xFF) {
542 msg("Child %d returns LOB status %o\n",
543 childpid, status&0xFF);
544 }
545 status = (status >> 8) & 0xFF;
546#ifdef TDEBUG
547 switch(status) {
548 case X_FINOK:
549 msg("Child %d finishes X_FINOK\n", childpid);
550 break;
551 case X_ABORT:
552 msg("Child %d finishes X_ABORT\n", childpid);
553 break;
554 case X_REWRITE:
555 msg("Child %d finishes X_REWRITE\n", childpid);
556 break;
557 default:
558 msg("Child %d finishes unknown %d\n",
559 childpid, status);
560 break;
561 }
562#endif /* TDEBUG */
563 switch(status) {
564 case X_FINOK:
565 Exit(X_FINOK);
566 case X_ABORT:
567 Exit(X_ABORT);
568 case X_REWRITE:
569 goto restore_check_point;
570 default:
571 msg("Bad return code from dump: %d\n", status);
572 Exit(X_ABORT);
573 }
574 /*NOTREACHED*/
575 } else { /* we are the child; just continue */
576#ifdef TDEBUG
577 sleep(4); /* allow time for parent's message to get out */
578 msg("Child on Tape %d has parent %d, my pid = %d\n",
579 tapeno+1, parentpid, getpid());
580#endif /* TDEBUG */
581 /*
582 * If we have a name like "/dev/rmt0,/dev/rmt1",
583 * use the name before the comma first, and save
584 * the remaining names for subsequent volumes.
585 */
586 tapeno++; /* current tape sequence */
587 if (nexttape || index(tape, ',')) {
588 if (nexttape && *nexttape)
589 tape = nexttape;
590 if ((p = index(tape, ',')) != NULL) {
591 *p = '\0';
592 nexttape = p + 1;
593 } else
594 nexttape = NULL;
595 msg("Dumping volume %d on %s\n", tapeno, tape);
596 }
597#ifdef RDUMP
598 while ((tapefd = (host ? rmtopen(tape, 2) :
599 pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
600#else
601 while ((tapefd = (pipeout ? 1 :
602 open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
603#endif
604 {
605 msg("Cannot open output \"%s\".\n", tape);
606 if (!query("Do you want to retry the open?"))
607 dumpabort(0);
608 }
609
610 enslave(); /* Share open tape file descriptor with slaves */
611
612 asize = 0;
613 blocksthisvol = 0;
614 if (top)
615 newtape++; /* new tape signal */
616 spcl.c_count = slp->count;
617 /*
618 * measure firstrec in TP_BSIZE units since restore doesn't
619 * know the correct ntrec value...
620 */
621 spcl.c_firstrec = slp->firstrec;
622 spcl.c_volume++;
623 spcl.c_type = TS_TAPE;
624 spcl.c_flags |= DR_NEWHEADER;
625 writeheader((ino_t)slp->inode);
626 spcl.c_flags &=~ DR_NEWHEADER;
627 if (tapeno > 1)
628 msg("Volume %d begins with blocks from inode %d\n",
629 tapeno, slp->inode);
630 }
631}
632
633void
634dumpabort(signo)
635 int signo;
636{
637
638 if (master != 0 && master != getpid())
639 /* Signals master to call dumpabort */
640 (void) kill(master, SIGTERM);
641 else {
642 killall();
643 msg("The ENTIRE dump is aborted.\n");
644 }
645#ifdef RDUMP
646 rmtclose();
647#endif
648 Exit(X_ABORT);
649}
650
651void
652Exit(status)
653 int status;
654{
655
656#ifdef TDEBUG
657 msg("pid = %d exits with status %d\n", getpid(), status);
658#endif /* TDEBUG */
659 exit(status);
660}
661
662/*
663 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
664 */
665void
666proceed(signo)
667 int signo;
668{
669
670 if (ready)
671 longjmp(jmpbuf, 1);
672 caught++;
673}
674
675void
676enslave()
677{
678 int cmd[2];
679 register int i, j;
680
681 master = getpid();
682
683 signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
684 signal(SIGPIPE, sigpipe);
685 signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
686 signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */
687
688 for (i = 0; i < SLAVES; i++) {
689 if (i == slp - &slaves[0]) {
690 caught = 1;
691 } else {
692 caught = 0;
693 }
694
695 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
696 (slaves[i].pid = fork()) < 0)
697 quit("too many slaves, %d (recompile smaller): %s\n",
698 i, strerror(errno));
699
700 slaves[i].fd = cmd[1];
701 slaves[i].sent = 0;
702 if (slaves[i].pid == 0) { /* Slave starts up here */
703 for (j = 0; j <= i; j++)
704 (void) close(slaves[j].fd);
705 signal(SIGINT, SIG_IGN); /* Master handles this */
706 doslave(cmd[0], i);
707 Exit(X_FINOK);
708 }
709 }
710
711 for (i = 0; i < SLAVES; i++)
712 (void) atomic(write, slaves[i].fd,
713 (char *) &slaves[(i + 1) % SLAVES].pid,
714 sizeof slaves[0].pid);
715
716 master = 0;
717}
718
719void
720killall()
721{
722 register int i;
723
724 for (i = 0; i < SLAVES; i++)
725 if (slaves[i].pid > 0)
726 (void) kill(slaves[i].pid, SIGKILL);
727}
728
729/*
730 * Synchronization - each process has a lockfile, and shares file
731 * descriptors to the following process's lockfile. When our write
732 * completes, we release our lock on the following process's lock-
733 * file, allowing the following process to lock it and proceed. We
734 * get the lock back for the next cycle by swapping descriptors.
735 */
736static void
737doslave(cmd, slave_number)
738 register int cmd;
739 int slave_number;
740{
741 register int nread;
742 int nextslave, size, wrote, eot_count;
743
744 /*
745 * Need our own seek pointer.
746 */
747 (void) close(diskfd);
748 if ((diskfd = open(disk, O_RDONLY)) < 0)
749 quit("slave couldn't reopen disk: %s\n", strerror(errno));
750
751 /*
752 * Need the pid of the next slave in the loop...
753 */
754 if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave))
755 != sizeof nextslave) {
756 quit("master/slave protocol botched - didn't get pid of next slave.\n");
757 }
758
759 /*
760 * Get list of blocks to dump, read the blocks into tape buffer
761 */
762 while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
763 register struct req *p = slp->req;
764
765 for (trecno = 0; trecno < ntrec;
766 trecno += p->count, p += p->count) {
767 if (p->dblk) {
768 bread(p->dblk, slp->tblock[trecno],
769 p->count * TP_BSIZE);
770 } else {
771 if (p->count != 1 || atomic(read, cmd,
772 (char *)slp->tblock[trecno],
773 TP_BSIZE) != TP_BSIZE)
774 quit("master/slave protocol botched.\n");
775 }
776 }
777 if (setjmp(jmpbuf) == 0) {
778 ready = 1;
779 if (!caught)
780 (void) pause();
781 }
782 ready = 0;
783 caught = 0;
784
785 /* Try to write the data... */
786 eot_count = 0;
787 size = 0;
788
789 while (eot_count < 10 && size < writesize) {
790#ifdef RDUMP
791 if (host)
792 wrote = rmtwrite(slp->tblock[0]+size,
793 writesize-size);
794 else
795#endif
796 wrote = write(tapefd, slp->tblock[0]+size,
797 writesize-size);
798#ifdef WRITEDEBUG
799 printf("slave %d wrote %d\n", slave_number, wrote);
800#endif
801 if (wrote < 0)
802 break;
803 if (wrote == 0)
804 eot_count++;
805 size += wrote;
806 }
807
808#ifdef WRITEDEBUG
809 if (size != writesize)
810 printf("slave %d only wrote %d out of %d bytes and gave up.\n",
811 slave_number, size, writesize);
812#endif
813
814 if (eot_count > 0)
815 size = 0;
816
817 /*
818 * fixme: Pyramids running OSx return ENOSPC
819 * at EOT on 1/2 inch drives.
820 */
821 if (size < 0) {
822 (void) kill(master, SIGUSR1);
823 for (;;)
824 (void) sigpause(0);
825 } else {
826 /*
827 * pass size of write back to master
828 * (for EOT handling)
829 */
830 (void) atomic(write, cmd, (char *)&size, sizeof size);
831 }
832
833 /*
834 * If partial write, don't want next slave to go.
835 * Also jolts him awake.
836 */
837 (void) kill(nextslave, SIGUSR2);
838 }
839 if (nread != 0)
840 quit("error reading command pipe: %s\n", strerror(errno));
841}
842
843/*
844 * Since a read from a pipe may not return all we asked for,
845 * or a write may not write all we ask if we get a signal,
846 * loop until the count is satisfied (or error).
847 */
848static int
849atomic(func, fd, buf, count)
850 int (*func)(), fd;
851 char *buf;
852 int count;
853{
854 int got, need = count;
855
856 while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
857 buf += got;
858 return (got < 0 ? got : count - need);
859}