Bug Summary

File:src/sbin/dump/tape.c
Warning:line 818, column 7
Although the value stored to 'nread' is used in the enclosing expression, the value is never actually read from 'nread'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name tape.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sbin/dump/obj -resource-dir /usr/local/lib/clang/13.0.0 -D RDUMP -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/sbin/dump/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/sbin/dump/tape.c
1/* $OpenBSD: tape.c,v 1.47 2021/01/21 00:16:36 mortimer Exp $ */
2/* $NetBSD: tape.c,v 1.11 1997/06/05 11:13:26 lukem Exp $ */
3
4/*-
5 * Copyright (c) 1980, 1991, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/param.h> /* MAXBSIZE DEV_BSIZE */
34#include <sys/socket.h>
35#include <sys/time.h>
36#include <sys/wait.h>
37#include <sys/stat.h>
38#include <ufs/ffs/fs.h>
39#include <ufs/ufs/dinode.h>
40
41#include <protocols/dumprestore.h>
42
43#include <errno(*__errno()).h>
44#include <fcntl.h>
45#include <signal.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <time.h>
50#include <unistd.h>
51#include <limits.h>
52
53#include "dump.h"
54#include "pathnames.h"
55
56#define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
57
58ino_t curino; /* current inumber; used globally */
59int newtape; /* new tape flag */
60union u_spcl u_spcl; /* mapping of variables in a control block */
61
62static int tapefd; /* tape file descriptor */
63static int64_t asize; /* number of 0.1" units written on cur tape */
64static int writesize; /* size of malloc()ed buffer for tape */
65static int64_t lastspclrec = -1; /* tape block number of last written header */
66static int trecno = 0; /* next record to write in current block */
67static int64_t blocksthisvol; /* number of blocks on current output file */
68static char *nexttape;
69
70static ssize_t atomic(ssize_t (*)(int, void *, size_t), int, char *, int);
71static void doslave(int, int);
72static void enslave(void);
73static void flushtape(void);
74static void killall(void);
75static void rollforward(void);
76
77void tperror(int signo);
78void sigpipe(int signo);
79void proceed(int signo);
80
81/*
82 * Concurrent dump mods (Caltech) - disk block reading and tape writing
83 * are exported to several slave processes. While one slave writes the
84 * tape, the others read disk blocks; they pass control of the tape in
85 * a ring via signals. The parent process traverses the filesystem and
86 * sends writeheader()'s and lists of daddr's to the slaves via pipes.
87 * The following structure defines the instruction packets sent to slaves.
88 */
89struct req {
90 daddr_t dblk;
91 int count;
92};
93static int reqsiz;
94
95#define SLAVES3 3 /* 1 slave writing, 1 reading, 1 for slack */
96static struct slave {
97 int64_t tapea; /* header number at start of this chunk */
98 int64_t firstrec; /* record number of this block */
99 int count; /* count to next header (used for TS_TAPE */
100 /* after EOT) */
101 int inode; /* inode that we are currently dealing with */
102 int fd; /* FD for this slave */
103 pid_t pid; /* PID for this slave */
104 int sent; /* 1 == we've sent this slave requests */
105 char (*tblock)[TP_BSIZE1024]; /* buffer for data blocks */
106 struct req *req; /* buffer for requests */
107} slaves[SLAVES3+1];
108static struct slave *slp;
109
110static char (*nextblock)[TP_BSIZE1024];
111
112static time_t tstart_volume; /* time of volume start */
113static int64_t tapea_volume; /* value of spcl.c_tapea at volume start */
114
115static pid_t master; /* pid of master, for sending error signals */
116static int tenths; /* length of tape used per block written */
117static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */
118
119int
120alloctape(void)
121{
122 int pgoff = getpagesize() - 1;
123 char *buf;
124 int i;
125
126 writesize = ntrec * TP_BSIZE1024;
127 reqsiz = (ntrec + 1) * sizeof(struct req);
128 /*
129 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
130 * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
131 * repositioning after stopping, i.e, streaming mode, where the gap is
132 * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
133 */
134 if (blocksperfile == 0 && !unlimited)
135 tenths = writesize / density +
136 (cartridge ? 16 : density == 625 ? 5 : 8);
137 /*
138 * Allocate tape buffer contiguous with the array of instruction
139 * packets, so flushtape() can write them together with one write().
140 * Align tape buffer on page boundary to speed up tape write().
141 */
142 for (i = 0; i <= SLAVES3; i++) {
143 buf = malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE1024));
144 if (buf == NULL((void *)0))
145 return(0);
146 slaves[i].tblock = (char (*)[TP_BSIZE1024])
147 (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
148 slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
149 }
150 slp = &slaves[0];
151 slp->count = 1;
152 slp->tapea = 0;
153 slp->firstrec = 0;
154 nextblock = slp->tblock;
155 return(1);
156}
157
158void
159writerec(char *dp, int isspcl)
160{
161
162 slp->req[trecno].dblk = 0;
163 slp->req[trecno].count = 1;
164 *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp;
165 if (isspcl)
166 lastspclrec = spclu_spcl.s_spcl.c_tapea;
167 trecno++;
168 spclu_spcl.s_spcl.c_tapea++;
169 if (trecno >= ntrec)
170 flushtape();
171}
172
173void
174dumpblock(daddr_t blkno, int size)
175{
176 int avail, tpblks;
177 daddr_t dblkno;
178
179 dblkno = fsbtodb(sblock, blkno)((blkno) << (sblock)->fs_fsbtodb);
180 tpblks = size >> tp_bshift;
181 while ((avail = MINIMUM(tpblks, ntrec - trecno)(((tpblks) < (ntrec - trecno)) ? (tpblks) : (ntrec - trecno
))
) > 0) {
182 slp->req[trecno].dblk = dblkno;
183 slp->req[trecno].count = avail;
184 trecno += avail;
185 spclu_spcl.s_spcl.c_tapea += avail;
186 if (trecno >= ntrec)
187 flushtape();
188 dblkno += avail << (tp_bshift - (ffs(DEV_BSIZE(1 << 9)) - 1));
189 tpblks -= avail;
190 }
191}
192
193int nogripe = 0;
194
195/* ARGSUSED */
196void
197tperror(int signo)
198{
199 /* XXX - signal races */
200
201 if (pipeout) {
202 msg("write error on %s\n", tape);
203 quit("Cannot recover\n");
204 /* NOTREACHED */
205 }
206 msg("write error %lld blocks into volume %d\n",
207 (long long)blocksthisvol, tapeno);
208 broadcast("DUMP WRITE ERROR!\n");
209 if (!query("Do you want to restart?"))
210 dumpabort(0);
211 msg("Closing this volume. Prepare to restart with new media;\n");
212 msg("this dump volume will be rewritten.\n");
213 killall();
214 nogripe = 1;
215 close_rewind();
216 Exit(X_REWRITE2);
217}
218
219/* ARGSUSED */
220void
221sigpipe(int signo)
222{
223
224 quit("Broken pipe\n");
225}
226
227/*
228 * do_stats --
229 * Update xferrate stats
230 */
231time_t
232do_stats(void)
233{
234 time_t tnow, ttaken;
235 int64_t blocks;
236
237 (void)time(&tnow);
238 ttaken = tnow - tstart_volume;
239 blocks = spclu_spcl.s_spcl.c_tapea - tapea_volume;
240 msg("Volume %d completed at: %s", tapeno, ctime(&tnow));
241 if (ttaken > 0) {
242 msg("Volume %d took %lld:%02lld:%02lld\n", tapeno,
243 (long long)ttaken / 3600, ((long long)ttaken % 3600) / 60,
244 (long long)ttaken % 60);
245 blocks /= ttaken;
246 msg("Volume %d transfer rate: %lld KB/s\n", tapeno, blocks);
247 xferrate += blocks;
248 }
249 return(tnow);
250}
251
252/*
253 * statussig --
254 * information message upon receipt of SIGINFO
255 * (derived from optr.c::timeest())
256 * XXX not safe
257 */
258/* ARGSUSED */
259void
260statussig(int signo)
261{
262 time_t tnow, deltat;
263 int save_errno = errno(*__errno());
264
265 if (blockswritten < 500)
266 return;
267 (void) time(&tnow);
268 deltat = tstart_writing - tnow + (1.0 * (tnow - tstart_writing))
269 / blockswritten * tapesize;
270 /* XXX not safe due to floating point printf */
271 dprintf(STDERR_FILENO2,
272 "dump: %s %3.2f%% done at %lld KB/s, finished in %d:%02d\n",
273 tape, (blockswritten * 100.0) / tapesize,
274 (spclu_spcl.s_spcl.c_tapea - tapea_volume) / (tnow - tstart_volume),
275 (int)(deltat / 3600), (int)((deltat % 3600) / 60));
276 errno(*__errno()) = save_errno;
277}
278
279static void
280flushtape(void)
281{
282 int i, blks, got;
283 int64_t lastfirstrec;
284
285 int siz = (char *)nextblock - (char *)slp->req;
286
287 slp->req[trecno].count = 0; /* Sentinel */
288
289 if (atomic((ssize_t (*)(int, void *, size_t))write, slp->fd,
290 (char *)slp->req, siz) != siz)
291 quit("error writing command pipe: %s\n", strerror(errno(*__errno())));
292 slp->sent = 1; /* we sent a request, read the response later */
293
294 lastfirstrec = slp->firstrec;
295
296 if (++slp >= &slaves[SLAVES3])
297 slp = &slaves[0];
298
299 /* Read results back from next slave */
300 if (slp->sent) {
301 if (atomic(read, slp->fd, (char *)&got, sizeof(got))
302 != sizeof(got)) {
303 perror(" DUMP: error reading command pipe in master");
304 dumpabort(0);
305 }
306 slp->sent = 0;
307
308 /* Check for end of tape */
309 if (got < writesize) {
310 msg("End of tape detected\n");
311
312 /*
313 * Drain the results, don't care what the values were.
314 * If we read them here then trewind won't...
315 */
316 for (i = 0; i < SLAVES3; i++) {
317 if (slaves[i].sent) {
318 if (atomic(read, slaves[i].fd,
319 (char *)&got, sizeof(got))
320 != sizeof(got)) {
321 perror(" DUMP: error reading command pipe in master");
322 dumpabort(0);
323 }
324 slaves[i].sent = 0;
325 }
326 }
327
328 close_rewind();
329 rollforward();
330 return;
331 }
332 }
333
334 blks = 0;
335 if (spclu_spcl.s_spcl.c_type != TS_END5 && spclu_spcl.s_spcl.c_type != TS_CLRI6 &&
336 spclu_spcl.s_spcl.c_type != TS_BITS3) {
337 if (spclu_spcl.s_spcl.c_count > TP_NINDIR(1024/2))
338 quit("c_count too large\n");
339 for (i = 0; i < spclu_spcl.s_spcl.c_count; i++)
340 if (spclu_spcl.s_spcl.c_addr[i] != 0)
341 blks++;
342 }
343 slp->count = lastspclrec + blks + 1 - spclu_spcl.s_spcl.c_tapea;
344 slp->tapea = spclu_spcl.s_spcl.c_tapea;
345 slp->firstrec = lastfirstrec + ntrec;
346 slp->inode = curino;
347 nextblock = slp->tblock;
348 trecno = 0;
349 asize += tenths;
350 blockswritten += ntrec;
351 blocksthisvol += ntrec;
352 if (!pipeout && !unlimited && (blocksperfile ?
353 (blocksthisvol >= blocksperfile) : (asize > tsize))) {
354 close_rewind();
355 startnewtape(0);
356 }
357 timeest();
358}
359
360void
361trewind(void)
362{
363 struct stat sb;
364 int f, got;
365
366 for (f = 0; f < SLAVES3; f++) {
367 /*
368 * Drain the results, but unlike EOT we DO (or should) care
369 * what the return values were, since if we detect EOT after
370 * we think we've written the last blocks to the tape anyway,
371 * we have to replay those blocks with rollforward.
372 *
373 * fixme: punt for now.
374 */
375 if (slaves[f].sent) {
376 if (atomic(read, slaves[f].fd, (char *)&got, sizeof(got))
377 != sizeof(got)) {
378 perror(" DUMP: error reading command pipe in master");
379 dumpabort(0);
380 }
381 slaves[f].sent = 0;
382 if (got != writesize) {
383 msg("EOT detected in last 2 tape records!\n");
384 msg("Use a longer tape, decrease the size estimate\n");
385 quit("or use no size estimate at all.\n");
386 }
387 }
388 (void) close(slaves[f].fd);
389 }
390 while (wait((int *)NULL((void *)0)) >= 0) /* wait for any signals from slaves */
391 /* void */;
392
393 if (pipeout)
394 return;
395
396 msg("Closing %s\n", tape);
397
398#ifdef RDUMP1
399 if (host) {
400 rmtclose();
401 while (rmtopen(tape, O_RDONLY0x0000) < 0)
402 sleep(10);
403 rmtclose();
404 return;
405 }
406#endif
407 /*
408 * st(4) says: "Bit 1 of the minor number specifies whether an eject is
409 * attempted when the device is closed. When it is set, the device
410 * will attempt to eject its media on close ...".
411 *
412 * If the tape has been ejected, looping on open() will generate 'Media
413 * not present' errors until a tape is loaded. Once loaded the tape
414 * will be immediately ejected as a result of the second close().
415 *
416 * So if the tape will be ejected, just close and return.
417 */
418 if ((fstat(tapefd, &sb) == 0) && (minor(sb.st_rdev)((unsigned)((sb.st_rdev) & 0xff) | (((sb.st_rdev) & 0xffff0000
) >> 8))
& 0x02)) {
419 (void) close(tapefd);
420 return;
421 }
422
423 (void) close(tapefd);
424 while ((f = open(tape, O_RDONLY0x0000)) == -1)
425 sleep (10);
426 (void) close(f);
427}
428
429void
430close_rewind(void)
431{
432 trewind();
433 (void)do_stats();
434 if (nexttape)
435 return;
436 if (!nogripe) {
437 msg("Change Volumes: Mount volume #%d\n", tapeno+1);
438 broadcast("CHANGE DUMP VOLUMES!\7\7\n");
439 }
440 while (!query("Is the new volume mounted and ready to go?"))
441 if (query("Do you want to abort?")) {
442 dumpabort(0);
443 /*NOTREACHED*/
444 }
445}
446
447void
448rollforward(void)
449{
450 struct req *p, *q, *prev;
451 struct slave *tslp;
452 int i, size, got;
453 int64_t savedtapea;
454 union u_spcl *ntb, *otb;
455 tslp = &slaves[SLAVES3];
456 ntb = (union u_spcl *)tslp->tblock[1];
457
458 /*
459 * Each of the N slaves should have requests that need to
460 * be replayed on the next tape. Use the extra slave buffers
461 * (slaves[SLAVES]) to construct request lists to be sent to
462 * each slave in turn.
463 */
464 for (i = 0; i < SLAVES3; i++) {
465 q = &tslp->req[1];
466 otb = (union u_spcl *)slp->tblock;
467
468 /*
469 * For each request in the current slave, copy it to tslp.
470 */
471
472 prev = NULL((void *)0);
473 for (p = slp->req; p->count > 0; p += p->count) {
474 *q = *p;
475 if (p->dblk == 0)
476 *ntb++ = *otb++; /* copy the datablock also */
477 prev = q;
478 q += q->count;
479 }
480 if (prev == NULL((void *)0))
481 quit("rollforward: protocol botch\n");
482 if (prev->dblk != 0)
483 prev->count -= 1;
484 else
485 ntb--;
486 q -= 1;
487 q->count = 0;
488 q = &tslp->req[0];
489 if (i == 0) {
490 q->dblk = 0;
491 q->count = 1;
492 trecno = 0;
493 nextblock = tslp->tblock;
494 savedtapea = spclu_spcl.s_spcl.c_tapea;
495 spclu_spcl.s_spcl.c_tapea = slp->tapea;
496 startnewtape(0);
497 spclu_spcl.s_spcl.c_tapea = savedtapea;
498 lastspclrec = savedtapea - 1;
499 }
500 size = (char *)ntb - (char *)q;
501 if (atomic((ssize_t (*)(int, void *, size_t))write,
502 slp->fd, (char *)q, size) != size) {
503 perror(" DUMP: error writing command pipe");
504 dumpabort(0);
505 }
506 slp->sent = 1;
507 if (++slp >= &slaves[SLAVES3])
508 slp = &slaves[0];
509
510 q->count = 1;
511
512 if (prev->dblk != 0) {
513 /*
514 * If the last one was a disk block, make the
515 * first of this one be the last bit of that disk
516 * block...
517 */
518 q->dblk = prev->dblk +
519 prev->count * (TP_BSIZE1024 / DEV_BSIZE(1 << 9));
520 ntb = (union u_spcl *)tslp->tblock;
521 } else {
522 /*
523 * It wasn't a disk block. Copy the data to its
524 * new location in the buffer.
525 */
526 q->dblk = 0;
527 *((union u_spcl *)tslp->tblock) = *ntb;
528 ntb = (union u_spcl *)tslp->tblock[1];
529 }
530 }
531 slp->req[0] = *q;
532 nextblock = slp->tblock;
533 if (q->dblk == 0)
534 nextblock++;
535 trecno = 1;
536
537 /*
538 * Clear the first slaves' response. One hopes that it
539 * worked ok, otherwise the tape is much too short!
540 */
541 if (slp->sent) {
542 if (atomic(read, slp->fd, (char *)&got, sizeof(got))
543 != sizeof(got)) {
544 perror(" DUMP: error reading command pipe in master");
545 dumpabort(0);
546 }
547 slp->sent = 0;
548
549 if (got != writesize) {
550 quit("EOT detected at start of the tape!\n");
551 }
552 }
553}
554
555/*
556 * We implement taking and restoring checkpoints on the tape level.
557 * When each tape is opened, a new process is created by forking; this
558 * saves all of the necessary context in the parent. The child
559 * continues the dump; the parent waits around, saving the context.
560 * If the child returns X_REWRITE, then it had problems writing that tape;
561 * this causes the parent to fork again, duplicating the context, and
562 * everything continues as if nothing had happened.
563 */
564void
565startnewtape(int top)
566{
567 pid_t parentpid;
568 pid_t childpid;
569 int status;
570 pid_t waitingpid;
571 char *p;
572 sig_t interrupt_save;
573
574 interrupt_save = signal(SIGINT2, SIG_IGN(void (*)(int))1);
575 parentpid = getpid();
576 tapea_volume = spclu_spcl.s_spcl.c_tapea;
577 (void)time(&tstart_volume);
578
579restore_check_point:
580 (void)signal(SIGINT2, interrupt_save);
581 /*
582 * All signals are inherited...
583 */
584 childpid = fork();
585 if (childpid == -1) {
586 msg("Context save fork fails in parent %d\n", parentpid);
587 Exit(X_ABORT3);
588 }
589 if (childpid != 0) {
590 /*
591 * PARENT:
592 * save the context by waiting
593 * until the child doing all of the work returns.
594 * don't catch the interrupt
595 */
596 signal(SIGINT2, SIG_IGN(void (*)(int))1);
597#ifdef TDEBUG
598 msg("Tape: %d; parent process: %d child process %d\n",
599 tapeno+1, parentpid, childpid);
600#endif /* TDEBUG */
601 while ((waitingpid = wait(&status)) != childpid)
602 msg("Parent %d waiting for child %d has another child %d return\n",
603 parentpid, childpid, waitingpid);
604 if (status & 0xFF) {
605 msg("Child %d returns LOB status %o\n",
606 childpid, status&0xFF);
607 }
608 status = (status >> 8) & 0xFF;
609#ifdef TDEBUG
610 switch(status) {
611 case X_FINOK0:
612 msg("Child %d finishes X_FINOK\n", childpid);
613 break;
614 case X_ABORT3:
615 msg("Child %d finishes X_ABORT\n", childpid);
616 break;
617 case X_REWRITE2:
618 msg("Child %d finishes X_REWRITE\n", childpid);
619 break;
620 default:
621 msg("Child %d finishes unknown %d\n",
622 childpid, status);
623 break;
624 }
625#endif /* TDEBUG */
626 switch(status) {
627 case X_FINOK0:
628 Exit(X_FINOK0);
629 break;
630 case X_ABORT3:
631 Exit(X_ABORT3);
632 break;
633 case X_REWRITE2:
634 goto restore_check_point;
635 default:
636 msg("Bad return code from dump: %d\n", status);
637 Exit(X_ABORT3);
638 }
639 /*NOTREACHED*/
640 } else { /* we are the child; just continue */
641#ifdef TDEBUG
642 sleep(4); /* allow time for parent's message to get out */
643 msg("Child on Tape %d has parent %d, my pid = %d\n",
644 tapeno+1, parentpid, getpid());
645#endif /* TDEBUG */
646 /*
647 * If we have a name like "/dev/rst0,/dev/rst1",
648 * use the name before the comma first, and save
649 * the remaining names for subsequent volumes.
650 */
651 tapeno++; /* current tape sequence */
652 if (nexttape || strchr(tape, ',')) {
653 if (nexttape && *nexttape)
654 tape = nexttape;
655 if ((p = strchr(tape, ',')) != NULL((void *)0)) {
656 *p = '\0';
657 nexttape = p + 1;
658 } else
659 nexttape = NULL((void *)0);
660 msg("Dumping volume %d on %s\n", tapeno, tape);
661 }
662#ifdef RDUMP1
663 while ((tapefd = (host ? rmtopen(tape, O_WRONLY0x0001|O_CREAT0x0200) :
664 pipeout ? 1 : open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1)
665#else
666 while ((tapefd = (pipeout ? 1 :
667 open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1)
668#endif
669 {
670 msg("Cannot open output \"%s\".\n", tape);
671 if (!query("Do you want to retry the open?"))
672 dumpabort(0);
673 }
674
675 enslave(); /* Share open tape file descriptor with slaves */
676
677 asize = 0;
678 blocksthisvol = 0;
679 if (top)
680 newtape++; /* new tape signal */
681 spclu_spcl.s_spcl.c_count = slp->count;
682 /*
683 * measure firstrec in TP_BSIZE units since restore doesn't
684 * know the correct ntrec value...
685 */
686 spclu_spcl.s_spcl.c_firstrec = slp->firstrec;
687 spclu_spcl.s_spcl.c_volume++;
688 spclu_spcl.s_spcl.c_type = TS_TAPE1;
689 if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119)
690 spclu_spcl.s_spcl.c_flags |= DR_NEWHEADER0x0001;
691 writeheader((ino_t)slp->inode);
692 if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119)
693 spclu_spcl.s_spcl.c_flags &=~ DR_NEWHEADER0x0001;
694 msg("Volume %d started at: %s", tapeno, ctime(&tstart_volume));
695 if (tapeno > 1)
696 msg("Volume %d begins with blocks from inode %llu\n",
697 tapeno, (unsigned long long)slp->inode);
698 }
699}
700
701/* ARGSUSED */
702void
703dumpabort(int signo)
704{
705
706 if (master != 0 && master != getpid())
707 /* Signals master to call dumpabort */
708 (void) kill(master, SIGTERM15);
709 else {
710 killall();
711 msg("The ENTIRE dump is aborted.\n");
712 }
713#ifdef RDUMP1
714 rmtclose();
715#endif
716 Exit(X_ABORT3);
717}
718
719__dead__attribute__((__noreturn__)) void
720Exit(int status)
721{
722
723#ifdef TDEBUG
724 msg("pid = %d exits with status %d\n", getpid(), status);
725#endif /* TDEBUG */
726 exit(status);
727}
728
729/*
730 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
731 */
732/* ARGSUSED */
733void
734proceed(int signo)
735{
736 caught++;
737}
738
739void
740enslave(void)
741{
742 int cmd[2];
743 int i, j;
744
745 master = getpid();
746
747 signal(SIGTERM15, dumpabort); /* Slave sends SIGTERM on dumpabort() */
748 signal(SIGPIPE13, sigpipe);
749 signal(SIGUSR130, tperror); /* Slave sends SIGUSR1 on tape errors */
750 signal(SIGUSR231, proceed); /* Slave sends SIGUSR2 to next slave */
751
752 for (i = 0; i < SLAVES3; i++) {
753 if (i == slp - &slaves[0]) {
754 caught = 1;
755 } else {
756 caught = 0;
757 }
758
759 if (socketpair(AF_UNIX1, SOCK_STREAM1, 0, cmd) == -1 ||
760 (slaves[i].pid = fork()) == -1)
761 quit("too many slaves, %d (recompile smaller): %s\n",
762 i, strerror(errno(*__errno())));
763
764 slaves[i].fd = cmd[1];
765 slaves[i].sent = 0;
766 if (slaves[i].pid == 0) { /* Slave starts up here */
767 for (j = 0; j <= i; j++)
768 (void) close(slaves[j].fd);
769 signal(SIGINT2, SIG_IGN(void (*)(int))1); /* Master handles this */
770 signal(SIGINFO29, SIG_IGN(void (*)(int))1);
771 doslave(cmd[0], i);
772 Exit(X_FINOK0);
773 }
774 }
775
776 for (i = 0; i < SLAVES3; i++)
777 (void) atomic((ssize_t (*)(int, void *, size_t))write,
778 slaves[i].fd, (char *) &slaves[(i + 1) % SLAVES3].pid,
779 sizeof(slaves[0].pid));
780 master = 0;
781}
782
783void
784killall(void)
785{
786 int i;
787
788 for (i = 0; i < SLAVES3; i++)
789 if (slaves[i].pid > 0) {
790 (void) kill(slaves[i].pid, SIGKILL9);
791 slaves[i].pid = 0;
792 }
793}
794
795/*
796 * Synchronization - each process has a lockfile, and shares file
797 * descriptors to the following process's lockfile. When our write
798 * completes, we release our lock on the following process's lock-
799 * file, allowing the following process to lock it and proceed. We
800 * get the lock back for the next cycle by swapping descriptors.
801 */
802static void
803doslave(int cmd, int slave_number)
804{
805 int nread, nextslave, size, wrote = 0, eot_count;
806 sigset_t nsigset, osigset;
807
808 /*
809 * Need our own seek pointer.
810 */
811 (void) close(diskfd);
812 if ((diskfd = open(disk, O_RDONLY0x0000)) == -1)
813 quit("slave couldn't reopen disk: %s\n", strerror(errno(*__errno())));
814
815 /*
816 * Need the pid of the next slave in the loop...
817 */
818 if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof(nextslave)))
Although the value stored to 'nread' is used in the enclosing expression, the value is never actually read from 'nread'
819 != sizeof(nextslave)) {
820 quit("master/slave protocol botched - didn't get pid of next slave.\n");
821 }
822
823 /*
824 * Get list of blocks to dump, read the blocks into tape buffer
825 */
826 while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
827 struct req *p = slp->req;
828
829 for (trecno = 0; trecno < ntrec;
830 trecno += p->count, p += p->count) {
831 if (p->dblk) {
832 bread(p->dblk, slp->tblock[trecno],
833 p->count * TP_BSIZE1024);
834 } else {
835 if (p->count != 1 || atomic(read, cmd,
836 (char *)slp->tblock[trecno],
837 TP_BSIZE1024) != TP_BSIZE1024)
838 quit("master/slave protocol botched.\n");
839 }
840 }
841
842 sigemptyset(&nsigset);
843 sigaddset(&nsigset, SIGUSR231);
844 sigprocmask(SIG_BLOCK1, &nsigset, &osigset);
845 while (!caught)
846 sigsuspend(&osigset);
847 caught = 0;
848 sigprocmask(SIG_SETMASK3, &osigset, NULL((void *)0));
849
850 /* Try to write the data... */
851 eot_count = 0;
852 size = 0;
853
854 while (eot_count < 10 && size < writesize) {
855#ifdef RDUMP1
856 if (host)
857 wrote = rmtwrite(slp->tblock[0]+size,
858 writesize-size);
859 else
860#endif
861 wrote = write(tapefd, slp->tblock[0]+size,
862 writesize-size);
863#ifdef WRITEDEBUG
864 printf("slave %d wrote %d\n", slave_number, wrote);
865#endif
866 if (wrote < 0)
867 break;
868 if (wrote == 0)
869 eot_count++;
870 size += wrote;
871 }
872
873#ifdef WRITEDEBUG
874 if (size != writesize)
875 printf("slave %d only wrote %d out of %d bytes and gave up.\n",
876 slave_number, size, writesize);
877#endif
878
879 if (eot_count > 0)
880 size = 0;
881
882 /*
883 * Handle ENOSPC as an EOT condition
884 */
885 if (wrote < 0 && errno(*__errno()) == ENOSPC28) {
886 wrote = 0;
887 eot_count++;
888 }
889
890 if (size < 0) {
891 (void) kill(master, SIGUSR130);
892 sigemptyset(&nsigset);
893 for (;;)
894 sigsuspend(&nsigset);
895 } else {
896 /*
897 * pass size of write back to master
898 * (for EOT handling)
899 */
900 (void) atomic((ssize_t (*)(int, void *, size_t))write,
901 cmd, (char *)&size, sizeof(size));
902 }
903
904 /*
905 * If partial write, don't want next slave to go.
906 * Also jolts him awake.
907 */
908 (void) kill(nextslave, SIGUSR231);
909 }
910 if (nread != 0)
911 quit("error reading command pipe: %s\n", strerror(errno(*__errno())));
912}
913
914/*
915 * Since a read from a pipe may not return all we asked for,
916 * or a write may not write all we ask if we get a signal,
917 * loop until the count is satisfied (or error).
918 */
919static ssize_t
920atomic(ssize_t (*func)(int, void *, size_t), int fd, char *buf, int count)
921{
922 ssize_t got, need = count;
923
924 while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
925 buf += got;
926 return (got < 0 ? got : count - need);
927}