| 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' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | |
| 58 | ino_t curino; /* current inumber; used globally */ |
| 59 | int newtape; /* new tape flag */ |
| 60 | union u_spcl u_spcl; /* mapping of variables in a control block */ |
| 61 | |
| 62 | static int tapefd; /* tape file descriptor */ |
| 63 | static int64_t asize; /* number of 0.1" units written on cur tape */ |
| 64 | static int writesize; /* size of malloc()ed buffer for tape */ |
| 65 | static int64_t lastspclrec = -1; /* tape block number of last written header */ |
| 66 | static int trecno = 0; /* next record to write in current block */ |
| 67 | static int64_t blocksthisvol; /* number of blocks on current output file */ |
| 68 | static char *nexttape; |
| 69 | |
| 70 | static ssize_t atomic(ssize_t (*)(int, void *, size_t), int, char *, int); |
| 71 | static void doslave(int, int); |
| 72 | static void enslave(void); |
| 73 | static void flushtape(void); |
| 74 | static void killall(void); |
| 75 | static void rollforward(void); |
| 76 | |
| 77 | void tperror(int signo); |
| 78 | void sigpipe(int signo); |
| 79 | void 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 | */ |
| 89 | struct req { |
| 90 | daddr_t dblk; |
| 91 | int count; |
| 92 | }; |
| 93 | static int reqsiz; |
| 94 | |
| 95 | #define SLAVES3 3 /* 1 slave writing, 1 reading, 1 for slack */ |
| 96 | static 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]; |
| 108 | static struct slave *slp; |
| 109 | |
| 110 | static char (*nextblock)[TP_BSIZE1024]; |
| 111 | |
| 112 | static time_t tstart_volume; /* time of volume start */ |
| 113 | static int64_t tapea_volume; /* value of spcl.c_tapea at volume start */ |
| 114 | |
| 115 | static pid_t master; /* pid of master, for sending error signals */ |
| 116 | static int tenths; /* length of tape used per block written */ |
| 117 | static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ |
| 118 | |
| 119 | int |
| 120 | alloctape(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 | |
| 158 | void |
| 159 | writerec(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 | |
| 173 | void |
| 174 | dumpblock(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 | |
| 193 | int nogripe = 0; |
| 194 | |
| 195 | /* ARGSUSED */ |
| 196 | void |
| 197 | tperror(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 */ |
| 220 | void |
| 221 | sigpipe(int signo) |
| 222 | { |
| 223 | |
| 224 | quit("Broken pipe\n"); |
| 225 | } |
| 226 | |
| 227 | /* |
| 228 | * do_stats -- |
| 229 | * Update xferrate stats |
| 230 | */ |
| 231 | time_t |
| 232 | do_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 */ |
| 259 | void |
| 260 | statussig(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 | |
| 279 | static void |
| 280 | flushtape(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 | |
| 360 | void |
| 361 | trewind(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 | |
| 429 | void |
| 430 | close_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 | |
| 447 | void |
| 448 | rollforward(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 | */ |
| 564 | void |
| 565 | startnewtape(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 | |
| 579 | restore_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 */ |
| 702 | void |
| 703 | dumpabort(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 |
| 720 | Exit(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 */ |
| 733 | void |
| 734 | proceed(int signo) |
| 735 | { |
| 736 | caught++; |
| 737 | } |
| 738 | |
| 739 | void |
| 740 | enslave(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 | |
| 783 | void |
| 784 | killall(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 | */ |
| 802 | static void |
| 803 | doslave(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 | */ |
| 919 | static ssize_t |
| 920 | atomic(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 | } |