Bug Summary

File:src/sbin/dump/tape.c
Warning:line 813, 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.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tape.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -D RDUMP -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/sbin/dump/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/sbin/dump/tape.c
1/* $OpenBSD: tape.c,v 1.48 2023/03/08 04:43:06 guenther 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
195void
196tperror(int signo)
197{
198 /* XXX - signal races */
199
200 if (pipeout) {
201 msg("write error on %s\n", tape);
202 quit("Cannot recover\n");
203 /* NOTREACHED */
204 }
205 msg("write error %lld blocks into volume %d\n",
206 (long long)blocksthisvol, tapeno);
207 broadcast("DUMP WRITE ERROR!\n");
208 if (!query("Do you want to restart?"))
209 dumpabort(0);
210 msg("Closing this volume. Prepare to restart with new media;\n");
211 msg("this dump volume will be rewritten.\n");
212 killall();
213 nogripe = 1;
214 close_rewind();
215 Exit(X_REWRITE2);
216}
217
218void
219sigpipe(int signo)
220{
221
222 quit("Broken pipe\n");
223}
224
225/*
226 * do_stats --
227 * Update xferrate stats
228 */
229time_t
230do_stats(void)
231{
232 time_t tnow, ttaken;
233 int64_t blocks;
234
235 (void)time(&tnow);
236 ttaken = tnow - tstart_volume;
237 blocks = spclu_spcl.s_spcl.c_tapea - tapea_volume;
238 msg("Volume %d completed at: %s", tapeno, ctime(&tnow));
239 if (ttaken > 0) {
240 msg("Volume %d took %lld:%02lld:%02lld\n", tapeno,
241 (long long)ttaken / 3600, ((long long)ttaken % 3600) / 60,
242 (long long)ttaken % 60);
243 blocks /= ttaken;
244 msg("Volume %d transfer rate: %lld KB/s\n", tapeno, blocks);
245 xferrate += blocks;
246 }
247 return(tnow);
248}
249
250/*
251 * statussig --
252 * information message upon receipt of SIGINFO
253 * (derived from optr.c::timeest())
254 * XXX not safe
255 */
256void
257statussig(int signo)
258{
259 time_t tnow, deltat;
260 int save_errno = errno(*__errno());
261
262 if (blockswritten < 500)
263 return;
264 (void) time(&tnow);
265 deltat = tstart_writing - tnow + (1.0 * (tnow - tstart_writing))
266 / blockswritten * tapesize;
267 /* XXX not safe due to floating point printf */
268 dprintf(STDERR_FILENO2,
269 "dump: %s %3.2f%% done at %lld KB/s, finished in %d:%02d\n",
270 tape, (blockswritten * 100.0) / tapesize,
271 (spclu_spcl.s_spcl.c_tapea - tapea_volume) / (tnow - tstart_volume),
272 (int)(deltat / 3600), (int)((deltat % 3600) / 60));
273 errno(*__errno()) = save_errno;
274}
275
276static void
277flushtape(void)
278{
279 int i, blks, got;
280 int64_t lastfirstrec;
281
282 int siz = (char *)nextblock - (char *)slp->req;
283
284 slp->req[trecno].count = 0; /* Sentinel */
285
286 if (atomic((ssize_t (*)(int, void *, size_t))write, slp->fd,
287 (char *)slp->req, siz) != siz)
288 quit("error writing command pipe: %s\n", strerror(errno(*__errno())));
289 slp->sent = 1; /* we sent a request, read the response later */
290
291 lastfirstrec = slp->firstrec;
292
293 if (++slp >= &slaves[SLAVES3])
294 slp = &slaves[0];
295
296 /* Read results back from next slave */
297 if (slp->sent) {
298 if (atomic(read, slp->fd, (char *)&got, sizeof(got))
299 != sizeof(got)) {
300 perror(" DUMP: error reading command pipe in master");
301 dumpabort(0);
302 }
303 slp->sent = 0;
304
305 /* Check for end of tape */
306 if (got < writesize) {
307 msg("End of tape detected\n");
308
309 /*
310 * Drain the results, don't care what the values were.
311 * If we read them here then trewind won't...
312 */
313 for (i = 0; i < SLAVES3; i++) {
314 if (slaves[i].sent) {
315 if (atomic(read, slaves[i].fd,
316 (char *)&got, sizeof(got))
317 != sizeof(got)) {
318 perror(" DUMP: error reading command pipe in master");
319 dumpabort(0);
320 }
321 slaves[i].sent = 0;
322 }
323 }
324
325 close_rewind();
326 rollforward();
327 return;
328 }
329 }
330
331 blks = 0;
332 if (spclu_spcl.s_spcl.c_type != TS_END5 && spclu_spcl.s_spcl.c_type != TS_CLRI6 &&
333 spclu_spcl.s_spcl.c_type != TS_BITS3) {
334 if (spclu_spcl.s_spcl.c_count > TP_NINDIR(1024/2))
335 quit("c_count too large\n");
336 for (i = 0; i < spclu_spcl.s_spcl.c_count; i++)
337 if (spclu_spcl.s_spcl.c_addr[i] != 0)
338 blks++;
339 }
340 slp->count = lastspclrec + blks + 1 - spclu_spcl.s_spcl.c_tapea;
341 slp->tapea = spclu_spcl.s_spcl.c_tapea;
342 slp->firstrec = lastfirstrec + ntrec;
343 slp->inode = curino;
344 nextblock = slp->tblock;
345 trecno = 0;
346 asize += tenths;
347 blockswritten += ntrec;
348 blocksthisvol += ntrec;
349 if (!pipeout && !unlimited && (blocksperfile ?
350 (blocksthisvol >= blocksperfile) : (asize > tsize))) {
351 close_rewind();
352 startnewtape(0);
353 }
354 timeest();
355}
356
357void
358trewind(void)
359{
360 struct stat sb;
361 int f, got;
362
363 for (f = 0; f < SLAVES3; f++) {
364 /*
365 * Drain the results, but unlike EOT we DO (or should) care
366 * what the return values were, since if we detect EOT after
367 * we think we've written the last blocks to the tape anyway,
368 * we have to replay those blocks with rollforward.
369 *
370 * fixme: punt for now.
371 */
372 if (slaves[f].sent) {
373 if (atomic(read, slaves[f].fd, (char *)&got, sizeof(got))
374 != sizeof(got)) {
375 perror(" DUMP: error reading command pipe in master");
376 dumpabort(0);
377 }
378 slaves[f].sent = 0;
379 if (got != writesize) {
380 msg("EOT detected in last 2 tape records!\n");
381 msg("Use a longer tape, decrease the size estimate\n");
382 quit("or use no size estimate at all.\n");
383 }
384 }
385 (void) close(slaves[f].fd);
386 }
387 while (wait((int *)NULL((void *)0)) >= 0) /* wait for any signals from slaves */
388 /* void */;
389
390 if (pipeout)
391 return;
392
393 msg("Closing %s\n", tape);
394
395#ifdef RDUMP1
396 if (host) {
397 rmtclose();
398 while (rmtopen(tape, O_RDONLY0x0000) < 0)
399 sleep(10);
400 rmtclose();
401 return;
402 }
403#endif
404 /*
405 * st(4) says: "Bit 1 of the minor number specifies whether an eject is
406 * attempted when the device is closed. When it is set, the device
407 * will attempt to eject its media on close ...".
408 *
409 * If the tape has been ejected, looping on open() will generate 'Media
410 * not present' errors until a tape is loaded. Once loaded the tape
411 * will be immediately ejected as a result of the second close().
412 *
413 * So if the tape will be ejected, just close and return.
414 */
415 if ((fstat(tapefd, &sb) == 0) && (minor(sb.st_rdev)((unsigned)((sb.st_rdev) & 0xff) | (((sb.st_rdev) & 0xffff0000
) >> 8))
& 0x02)) {
416 (void) close(tapefd);
417 return;
418 }
419
420 (void) close(tapefd);
421 while ((f = open(tape, O_RDONLY0x0000)) == -1)
422 sleep (10);
423 (void) close(f);
424}
425
426void
427close_rewind(void)
428{
429 trewind();
430 (void)do_stats();
431 if (nexttape)
432 return;
433 if (!nogripe) {
434 msg("Change Volumes: Mount volume #%d\n", tapeno+1);
435 broadcast("CHANGE DUMP VOLUMES!\7\7\n");
436 }
437 while (!query("Is the new volume mounted and ready to go?"))
438 if (query("Do you want to abort?")) {
439 dumpabort(0);
440 /*NOTREACHED*/
441 }
442}
443
444void
445rollforward(void)
446{
447 struct req *p, *q, *prev;
448 struct slave *tslp;
449 int i, size, got;
450 int64_t savedtapea;
451 union u_spcl *ntb, *otb;
452 tslp = &slaves[SLAVES3];
453 ntb = (union u_spcl *)tslp->tblock[1];
454
455 /*
456 * Each of the N slaves should have requests that need to
457 * be replayed on the next tape. Use the extra slave buffers
458 * (slaves[SLAVES]) to construct request lists to be sent to
459 * each slave in turn.
460 */
461 for (i = 0; i < SLAVES3; i++) {
462 q = &tslp->req[1];
463 otb = (union u_spcl *)slp->tblock;
464
465 /*
466 * For each request in the current slave, copy it to tslp.
467 */
468
469 prev = NULL((void *)0);
470 for (p = slp->req; p->count > 0; p += p->count) {
471 *q = *p;
472 if (p->dblk == 0)
473 *ntb++ = *otb++; /* copy the datablock also */
474 prev = q;
475 q += q->count;
476 }
477 if (prev == NULL((void *)0))
478 quit("rollforward: protocol botch\n");
479 if (prev->dblk != 0)
480 prev->count -= 1;
481 else
482 ntb--;
483 q -= 1;
484 q->count = 0;
485 q = &tslp->req[0];
486 if (i == 0) {
487 q->dblk = 0;
488 q->count = 1;
489 trecno = 0;
490 nextblock = tslp->tblock;
491 savedtapea = spclu_spcl.s_spcl.c_tapea;
492 spclu_spcl.s_spcl.c_tapea = slp->tapea;
493 startnewtape(0);
494 spclu_spcl.s_spcl.c_tapea = savedtapea;
495 lastspclrec = savedtapea - 1;
496 }
497 size = (char *)ntb - (char *)q;
498 if (atomic((ssize_t (*)(int, void *, size_t))write,
499 slp->fd, (char *)q, size) != size) {
500 perror(" DUMP: error writing command pipe");
501 dumpabort(0);
502 }
503 slp->sent = 1;
504 if (++slp >= &slaves[SLAVES3])
505 slp = &slaves[0];
506
507 q->count = 1;
508
509 if (prev->dblk != 0) {
510 /*
511 * If the last one was a disk block, make the
512 * first of this one be the last bit of that disk
513 * block...
514 */
515 q->dblk = prev->dblk +
516 prev->count * (TP_BSIZE1024 / DEV_BSIZE(1 << 9));
517 ntb = (union u_spcl *)tslp->tblock;
518 } else {
519 /*
520 * It wasn't a disk block. Copy the data to its
521 * new location in the buffer.
522 */
523 q->dblk = 0;
524 *((union u_spcl *)tslp->tblock) = *ntb;
525 ntb = (union u_spcl *)tslp->tblock[1];
526 }
527 }
528 slp->req[0] = *q;
529 nextblock = slp->tblock;
530 if (q->dblk == 0)
531 nextblock++;
532 trecno = 1;
533
534 /*
535 * Clear the first slaves' response. One hopes that it
536 * worked ok, otherwise the tape is much too short!
537 */
538 if (slp->sent) {
539 if (atomic(read, slp->fd, (char *)&got, sizeof(got))
540 != sizeof(got)) {
541 perror(" DUMP: error reading command pipe in master");
542 dumpabort(0);
543 }
544 slp->sent = 0;
545
546 if (got != writesize) {
547 quit("EOT detected at start of the tape!\n");
548 }
549 }
550}
551
552/*
553 * We implement taking and restoring checkpoints on the tape level.
554 * When each tape is opened, a new process is created by forking; this
555 * saves all of the necessary context in the parent. The child
556 * continues the dump; the parent waits around, saving the context.
557 * If the child returns X_REWRITE, then it had problems writing that tape;
558 * this causes the parent to fork again, duplicating the context, and
559 * everything continues as if nothing had happened.
560 */
561void
562startnewtape(int top)
563{
564 pid_t parentpid;
565 pid_t childpid;
566 int status;
567 pid_t waitingpid;
568 char *p;
569 sig_t interrupt_save;
570
571 interrupt_save = signal(SIGINT2, SIG_IGN(void (*)(int))1);
572 parentpid = getpid();
573 tapea_volume = spclu_spcl.s_spcl.c_tapea;
574 (void)time(&tstart_volume);
575
576restore_check_point:
577 (void)signal(SIGINT2, interrupt_save);
578 /*
579 * All signals are inherited...
580 */
581 childpid = fork();
582 if (childpid == -1) {
583 msg("Context save fork fails in parent %d\n", parentpid);
584 Exit(X_ABORT3);
585 }
586 if (childpid != 0) {
587 /*
588 * PARENT:
589 * save the context by waiting
590 * until the child doing all of the work returns.
591 * don't catch the interrupt
592 */
593 signal(SIGINT2, SIG_IGN(void (*)(int))1);
594#ifdef TDEBUG
595 msg("Tape: %d; parent process: %d child process %d\n",
596 tapeno+1, parentpid, childpid);
597#endif /* TDEBUG */
598 while ((waitingpid = wait(&status)) != childpid)
599 msg("Parent %d waiting for child %d has another child %d return\n",
600 parentpid, childpid, waitingpid);
601 if (status & 0xFF) {
602 msg("Child %d returns LOB status %o\n",
603 childpid, status&0xFF);
604 }
605 status = (status >> 8) & 0xFF;
606#ifdef TDEBUG
607 switch(status) {
608 case X_FINOK0:
609 msg("Child %d finishes X_FINOK\n", childpid);
610 break;
611 case X_ABORT3:
612 msg("Child %d finishes X_ABORT\n", childpid);
613 break;
614 case X_REWRITE2:
615 msg("Child %d finishes X_REWRITE\n", childpid);
616 break;
617 default:
618 msg("Child %d finishes unknown %d\n",
619 childpid, status);
620 break;
621 }
622#endif /* TDEBUG */
623 switch(status) {
624 case X_FINOK0:
625 Exit(X_FINOK0);
626 break;
627 case X_ABORT3:
628 Exit(X_ABORT3);
629 break;
630 case X_REWRITE2:
631 goto restore_check_point;
632 default:
633 msg("Bad return code from dump: %d\n", status);
634 Exit(X_ABORT3);
635 }
636 /*NOTREACHED*/
637 } else { /* we are the child; just continue */
638#ifdef TDEBUG
639 sleep(4); /* allow time for parent's message to get out */
640 msg("Child on Tape %d has parent %d, my pid = %d\n",
641 tapeno+1, parentpid, getpid());
642#endif /* TDEBUG */
643 /*
644 * If we have a name like "/dev/rst0,/dev/rst1",
645 * use the name before the comma first, and save
646 * the remaining names for subsequent volumes.
647 */
648 tapeno++; /* current tape sequence */
649 if (nexttape || strchr(tape, ',')) {
650 if (nexttape && *nexttape)
651 tape = nexttape;
652 if ((p = strchr(tape, ',')) != NULL((void *)0)) {
653 *p = '\0';
654 nexttape = p + 1;
655 } else
656 nexttape = NULL((void *)0);
657 msg("Dumping volume %d on %s\n", tapeno, tape);
658 }
659#ifdef RDUMP1
660 while ((tapefd = (host ? rmtopen(tape, O_WRONLY0x0001|O_CREAT0x0200) :
661 pipeout ? 1 : open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1)
662#else
663 while ((tapefd = (pipeout ? 1 :
664 open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1)
665#endif
666 {
667 msg("Cannot open output \"%s\".\n", tape);
668 if (!query("Do you want to retry the open?"))
669 dumpabort(0);
670 }
671
672 enslave(); /* Share open tape file descriptor with slaves */
673
674 asize = 0;
675 blocksthisvol = 0;
676 if (top)
677 newtape++; /* new tape signal */
678 spclu_spcl.s_spcl.c_count = slp->count;
679 /*
680 * measure firstrec in TP_BSIZE units since restore doesn't
681 * know the correct ntrec value...
682 */
683 spclu_spcl.s_spcl.c_firstrec = slp->firstrec;
684 spclu_spcl.s_spcl.c_volume++;
685 spclu_spcl.s_spcl.c_type = TS_TAPE1;
686 if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119)
687 spclu_spcl.s_spcl.c_flags |= DR_NEWHEADER0x0001;
688 writeheader((ino_t)slp->inode);
689 if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119)
690 spclu_spcl.s_spcl.c_flags &=~ DR_NEWHEADER0x0001;
691 msg("Volume %d started at: %s", tapeno, ctime(&tstart_volume));
692 if (tapeno > 1)
693 msg("Volume %d begins with blocks from inode %llu\n",
694 tapeno, (unsigned long long)slp->inode);
695 }
696}
697
698void
699dumpabort(int signo)
700{
701
702 if (master != 0 && master != getpid())
703 /* Signals master to call dumpabort */
704 (void) kill(master, SIGTERM15);
705 else {
706 killall();
707 msg("The ENTIRE dump is aborted.\n");
708 }
709#ifdef RDUMP1
710 rmtclose();
711#endif
712 Exit(X_ABORT3);
713}
714
715__dead__attribute__((__noreturn__)) void
716Exit(int status)
717{
718
719#ifdef TDEBUG
720 msg("pid = %d exits with status %d\n", getpid(), status);
721#endif /* TDEBUG */
722 exit(status);
723}
724
725/*
726 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
727 */
728void
729proceed(int signo)
730{
731 caught++;
732}
733
734void
735enslave(void)
736{
737 int cmd[2];
738 int i, j;
739
740 master = getpid();
741
742 signal(SIGTERM15, dumpabort); /* Slave sends SIGTERM on dumpabort() */
743 signal(SIGPIPE13, sigpipe);
744 signal(SIGUSR130, tperror); /* Slave sends SIGUSR1 on tape errors */
745 signal(SIGUSR231, proceed); /* Slave sends SIGUSR2 to next slave */
746
747 for (i = 0; i < SLAVES3; i++) {
748 if (i == slp - &slaves[0]) {
749 caught = 1;
750 } else {
751 caught = 0;
752 }
753
754 if (socketpair(AF_UNIX1, SOCK_STREAM1, 0, cmd) == -1 ||
755 (slaves[i].pid = fork()) == -1)
756 quit("too many slaves, %d (recompile smaller): %s\n",
757 i, strerror(errno(*__errno())));
758
759 slaves[i].fd = cmd[1];
760 slaves[i].sent = 0;
761 if (slaves[i].pid == 0) { /* Slave starts up here */
762 for (j = 0; j <= i; j++)
763 (void) close(slaves[j].fd);
764 signal(SIGINT2, SIG_IGN(void (*)(int))1); /* Master handles this */
765 signal(SIGINFO29, SIG_IGN(void (*)(int))1);
766 doslave(cmd[0], i);
767 Exit(X_FINOK0);
768 }
769 }
770
771 for (i = 0; i < SLAVES3; i++)
772 (void) atomic((ssize_t (*)(int, void *, size_t))write,
773 slaves[i].fd, (char *) &slaves[(i + 1) % SLAVES3].pid,
774 sizeof(slaves[0].pid));
775 master = 0;
776}
777
778void
779killall(void)
780{
781 int i;
782
783 for (i = 0; i < SLAVES3; i++)
784 if (slaves[i].pid > 0) {
785 (void) kill(slaves[i].pid, SIGKILL9);
786 slaves[i].pid = 0;
787 }
788}
789
790/*
791 * Synchronization - each process has a lockfile, and shares file
792 * descriptors to the following process's lockfile. When our write
793 * completes, we release our lock on the following process's lock-
794 * file, allowing the following process to lock it and proceed. We
795 * get the lock back for the next cycle by swapping descriptors.
796 */
797static void
798doslave(int cmd, int slave_number)
799{
800 int nread, nextslave, size, wrote = 0, eot_count;
801 sigset_t nsigset, osigset;
802
803 /*
804 * Need our own seek pointer.
805 */
806 (void) close(diskfd);
807 if ((diskfd = open(disk, O_RDONLY0x0000)) == -1)
808 quit("slave couldn't reopen disk: %s\n", strerror(errno(*__errno())));
809
810 /*
811 * Need the pid of the next slave in the loop...
812 */
813 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'
814 != sizeof(nextslave)) {
815 quit("master/slave protocol botched - didn't get pid of next slave.\n");
816 }
817
818 /*
819 * Get list of blocks to dump, read the blocks into tape buffer
820 */
821 while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
822 struct req *p = slp->req;
823
824 for (trecno = 0; trecno < ntrec;
825 trecno += p->count, p += p->count) {
826 if (p->dblk) {
827 bread(p->dblk, slp->tblock[trecno],
828 p->count * TP_BSIZE1024);
829 } else {
830 if (p->count != 1 || atomic(read, cmd,
831 (char *)slp->tblock[trecno],
832 TP_BSIZE1024) != TP_BSIZE1024)
833 quit("master/slave protocol botched.\n");
834 }
835 }
836
837 sigemptyset(&nsigset);
838 sigaddset(&nsigset, SIGUSR231);
839 sigprocmask(SIG_BLOCK1, &nsigset, &osigset);
840 while (!caught)
841 sigsuspend(&osigset);
842 caught = 0;
843 sigprocmask(SIG_SETMASK3, &osigset, NULL((void *)0));
844
845 /* Try to write the data... */
846 eot_count = 0;
847 size = 0;
848
849 while (eot_count < 10 && size < writesize) {
850#ifdef RDUMP1
851 if (host)
852 wrote = rmtwrite(slp->tblock[0]+size,
853 writesize-size);
854 else
855#endif
856 wrote = write(tapefd, slp->tblock[0]+size,
857 writesize-size);
858#ifdef WRITEDEBUG
859 printf("slave %d wrote %d\n", slave_number, wrote);
860#endif
861 if (wrote < 0)
862 break;
863 if (wrote == 0)
864 eot_count++;
865 size += wrote;
866 }
867
868#ifdef WRITEDEBUG
869 if (size != writesize)
870 printf("slave %d only wrote %d out of %d bytes and gave up.\n",
871 slave_number, size, writesize);
872#endif
873
874 if (eot_count > 0)
875 size = 0;
876
877 /*
878 * Handle ENOSPC as an EOT condition
879 */
880 if (wrote < 0 && errno(*__errno()) == ENOSPC28) {
881 wrote = 0;
882 eot_count++;
883 }
884
885 if (size < 0) {
886 (void) kill(master, SIGUSR130);
887 sigemptyset(&nsigset);
888 for (;;)
889 sigsuspend(&nsigset);
890 } else {
891 /*
892 * pass size of write back to master
893 * (for EOT handling)
894 */
895 (void) atomic((ssize_t (*)(int, void *, size_t))write,
896 cmd, (char *)&size, sizeof(size));
897 }
898
899 /*
900 * If partial write, don't want next slave to go.
901 * Also jolts him awake.
902 */
903 (void) kill(nextslave, SIGUSR231);
904 }
905 if (nread != 0)
906 quit("error reading command pipe: %s\n", strerror(errno(*__errno())));
907}
908
909/*
910 * Since a read from a pipe may not return all we asked for,
911 * or a write may not write all we ask if we get a signal,
912 * loop until the count is satisfied (or error).
913 */
914static ssize_t
915atomic(ssize_t (*func)(int, void *, size_t), int fd, char *buf, int count)
916{
917 ssize_t got, need = count;
918
919 while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
920 buf += got;
921 return (got < 0 ? got : count - need);
922}