summaryrefslogtreecommitdiff
path: root/usr/src/cmd/backup/dump/dumptape.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/backup/dump/dumptape.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/backup/dump/dumptape.c')
-rw-r--r--usr/src/cmd/backup/dump/dumptape.c2511
1 files changed, 2511 insertions, 0 deletions
diff --git a/usr/src/cmd/backup/dump/dumptape.c b/usr/src/cmd/backup/dump/dumptape.c
new file mode 100644
index 0000000000..0d3020fb50
--- /dev/null
+++ b/usr/src/cmd/backup/dump/dumptape.c
@@ -0,0 +1,2511 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "dump.h"
+#include <rmt.h>
+#include <setjmp.h>
+#include <sys/fdio.h>
+#include <sys/mkdev.h>
+#include <assert.h>
+#include <limits.h>
+
+#define SLEEPMS 50
+
+static uint_t writesize; /* size of malloc()ed buffer for tape */
+static ino_t inos[TP_NINOS]; /* starting inodes on each tape */
+
+/*
+ * The req structure is used to pass commands from the parent
+ * process through the pipes to the slave processes. It comes
+ * in two flavors, depending on which mode dump is operating under:
+ * an inode request (on-line mode) and a disk block request ("old" mode).
+ */
+/*
+ * The inode request structure is used during on-line mode.
+ * The master passes inode numbers and starting offsets to
+ * the slaves. The tape writer passes out the current inode,
+ * offset, and number of tape records written after completing a volume.
+ */
+struct ireq {
+ ino_t inumber; /* inode number to open/dump */
+ long igen; /* inode generation number */
+ off_t offset; /* starting offset in inode */
+ int count; /* count for 1st spclrec */
+};
+/*
+ * The block request structure is used in off-line mode to pass
+ * commands to dump disk blocks from the parent process through
+ * the pipes to the slave processes.
+ */
+struct breq {
+ diskaddr_t dblk; /* disk address to read */
+ size_t size; /* number of bytes to read from disk */
+ ulong_t spclrec[1]; /* actually longer */
+};
+
+struct req {
+ short aflag; /* write data to archive process as well */
+ short tflag; /* begin new tape */
+ union reqdata {
+ struct ireq ino; /* used for on-line mode */
+ struct breq blks; /* used for off-line mode */
+ } data;
+};
+
+#define ir_inumber data.ino.inumber
+#define ir_igen data.ino.igen
+#define ir_offset data.ino.offset
+#define ir_count data.ino.count
+
+#define br_dblk data.blks.dblk
+#define br_size data.blks.size
+#define br_spcl data.blks.spclrec
+
+static int reqsiz = 0; /* alloctape will initialize */
+
+#define SLAVES 3
+struct slaves {
+ int sl_slavefd; /* pipe from master to slave */
+ pid_t sl_slavepid; /* slave pid; used by killall() */
+ ino_t sl_inos; /* inos, if this record starts tape */
+ int sl_offset; /* logical blocks written for object */
+ int sl_count; /* logical blocks left in spclrec */
+ int sl_tapea; /* header number, if starting tape */
+ int sl_firstrec; /* number of first block on tape */
+ int sl_state; /* dump output state */
+ struct req *sl_req; /* instruction packet to slave */
+};
+static struct slaves slaves[SLAVES]; /* one per slave */
+static struct slaves *slp; /* pointer to current slave */
+static struct slaves chkpt; /* checkpointed data */
+
+struct bdesc {
+ char *b_data; /* pointer to buffer data */
+ int b_flags; /* flags (see below) */
+};
+
+/*
+ * The following variables are in shared memory, and must be
+ * explicitly checkpointed and/or reset.
+ */
+static caddr_t shared; /* pointer to block of shared memory */
+static struct bdesc *bufp; /* buffer descriptors */
+static struct bdesc **current; /* output buffer to fill */
+static int *tapea; /* logical record count */
+
+#ifdef INSTRUMENT
+static int *readmissp; /* number of times writer was idle */
+static int *idle; /* number of times slaves were idle */
+#endif /* INSTRUMENT */
+
+/*
+ * Buffer flags
+ */
+#define BUF_EMPTY 0x0 /* nothing in buffer */
+#define BUF_FULL 0x1 /* data in buffer */
+#define BUF_SPCLREC 0x2 /* contains special record */
+#define BUF_ARCHIVE 0x4 /* dump to archive */
+
+static int recsout; /* number of req's sent to slaves */
+static int totalrecsout; /* total number of req's sent to slaves */
+static int rotor; /* next slave to be instructed */
+static pid_t master; /* pid of master, for sending error signals */
+static int writer = -1; /* fd of tape writer */
+static pid_t writepid; /* pid of tape writer */
+static int arch; /* fd of output archiver */
+static pid_t archivepid; /* pid of output archiver */
+static int archivefd; /* fd of archive file (proper) */
+static offset_t lf_archoffset; /* checkpointed offset into archive file */
+
+int caught; /* caught signal -- imported by mapfile() */
+
+#ifdef DEBUG
+extern int xflag;
+#endif
+
+#ifdef __STDC__
+static void cmdwrterr(void);
+static void cmdrderr(void);
+static void freetape(void);
+static void bufclear(void);
+static pid_t setuparchive(void);
+static pid_t setupwriter(void);
+static void nextslave(void);
+static void tperror(int);
+static void rollforward(int);
+static void nap(int);
+static void alrm(int);
+static void just_rewind(void);
+static void killall(void);
+static void proceed(int);
+static void die(int);
+static void enslave(void);
+static void wait_our_turn(void);
+static void dumpoffline(int, pid_t, int);
+static void onxfsz(int);
+static void dowrite(int);
+static void checkpoint(struct bdesc *, int);
+static ssize_t atomic(int (*)(), int, char *, int);
+#else
+static void cmdwrterr();
+static void cmdrderr();
+static void freetape();
+static void bufclear();
+static pid_t setuparchive();
+static pid_t setupwriter();
+static void nextslave();
+static void tperror();
+static void rollforward();
+static void nap();
+static void alrm();
+static void just_rewind();
+static void killall();
+static void proceed();
+static void die();
+static void enslave();
+static void wait_our_turn();
+static void dumpoffline();
+static void onxfsz();
+static void dowrite();
+static void checkpoint();
+static ssize_t atomic();
+#endif
+
+static size_t tapesize;
+
+/*
+ * Allocate buffers and shared memory variables. Tape buffers are
+ * allocated on page boundaries for tape write() efficiency.
+ */
+void
+#ifdef __STDC__
+#else
+#endif
+alloctape(void)
+{
+ struct slaves *slavep;
+ ulong_t pgoff = (unsigned)(getpagesize() - 1); /* 2**n - 1 */
+ int mapfd;
+ char *obuf;
+ int saverr;
+ int i, j;
+
+ writesize = ntrec * tp_bsize;
+ if (!printsize)
+ msg(gettext("Writing %d Kilobyte records\n"),
+ writesize / TP_BSIZE_MIN);
+
+ /*
+ * set up shared memory seg for here and child
+ */
+ mapfd = open("/dev/zero", O_RDWR);
+ if (mapfd == -1) {
+ saverr = errno;
+ msg(gettext("Cannot open `%s': %s\n"),
+ "/dev/zero", strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ /*
+ * Allocate space such that buffers are page-aligned and
+ * pointers are aligned on 4-byte boundaries (for SPARC).
+ * This code assumes that (NBUF * writesize) is a multiple
+ * of the page size and that pages are aligned on 4-byte
+ * boundaries. Space is allocated as follows:
+ *
+ * (NBUF * writesize) for the actual buffers
+ * (pagesize - 1) for padding so the buffers are page-aligned
+ * (NBUF * ntrec * sizeof (struct bdesc)) for each buffer
+ * (n * sizeof (int)) for [n] debugging variables/pointers
+ * (n * sizeof (int)) for [n] miscellaneous variables/pointers
+ */
+ tapesize =
+ (NBUF * writesize) /* output buffers */
+ /* LINTED: pgoff fits into a size_t */
+ + (size_t)pgoff /* page alignment */
+ /* buffer descriptors */
+ + (((size_t)sizeof (struct bdesc)) * NBUF * ntrec)
+#ifdef INSTRUMENT
+ + (2 * (size_t)sizeof (int *)) /* instrumentation */
+#endif
+ /* shared variables */
+ + (size_t)sizeof (struct bdesc **)
+ + (size_t)sizeof (int *)
+ + (3 * (size_t)sizeof (time_t));
+
+ shared = mmap((char *)0, tapesize, PROT_READ|PROT_WRITE,
+ MAP_SHARED, mapfd, (off_t)0);
+ if (shared == (caddr_t)-1) {
+ saverr = errno;
+ msg(gettext("Cannot memory map output buffers: %s\n"),
+ strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ (void) close(mapfd);
+
+ /*
+ * Buffers and buffer headers
+ */
+ obuf = (char *)(((ulong_t)shared + pgoff) & ~pgoff);
+ /* LINTED obuf and writesize are aligned */
+ bufp = (struct bdesc *)(obuf + NBUF*writesize);
+ /*
+ * Shared memory variables
+ */
+ current = (struct bdesc **)&bufp[NBUF*ntrec];
+ tapea = (int *)(current + 1);
+ /* LINTED pointer alignment ok */
+ telapsed = (time_t *)(tapea + 1);
+ tstart_writing = telapsed + 1;
+ tschedule = tstart_writing + 1;
+#ifdef INSTRUMENT
+ /*
+ * Debugging and instrumentation variables
+ */
+ readmissp = (int *)(tschedule + 1);
+ idle = readmissp + 1;
+#endif
+ for (i = 0, j = 0; i < NBUF * ntrec; i++, j += tp_bsize) {
+ bufp[i].b_data = &obuf[j];
+ }
+
+ reqsiz = sizeof (struct req) + tp_bsize - sizeof (long);
+ for (slavep = slaves; slavep < &slaves[SLAVES]; slavep++)
+ slavep->sl_req = (struct req *)xmalloc(reqsiz);
+
+ chkpt.sl_offset = 0; /* start at offset 0 */
+ chkpt.sl_count = 0;
+ chkpt.sl_inos = UFSROOTINO; /* in root inode */
+ chkpt.sl_firstrec = 1;
+ chkpt.sl_tapea = 0;
+}
+
+static void
+#ifdef __STDC__
+freetape(void)
+#else
+freetape()
+#endif
+{
+ if (shared == NULL)
+ return;
+ (void) timeclock((time_t)0);
+ (void) munmap(shared, tapesize);
+ shared = NULL;
+}
+
+/*
+ * Reset tape state variables -- called
+ * before a pass to dump active files.
+ */
+void
+#ifdef __STDC__
+reset(void)
+#else
+reset()
+#endif
+{
+ bufclear();
+
+#ifdef INSTRUMENT
+ (*readmissp) = 0;
+ (*idle) = 0;
+#endif
+
+ spcl.c_flags = 0;
+ spcl.c_volume = 0;
+ tapeno = 0;
+
+ chkpt.sl_offset = 0; /* start at offset 0 */
+ chkpt.sl_count = 0;
+ chkpt.sl_inos = UFSROOTINO; /* in root inode */
+ chkpt.sl_firstrec = 1;
+ chkpt.sl_tapea = 0;
+}
+
+static void
+#ifdef __STDC__
+bufclear(void)
+#else
+bufclear()
+#endif
+{
+ struct bdesc *bp;
+ int i;
+
+ for (i = 0, bp = bufp; i < NBUF * ntrec; i++, bp++)
+ bp->b_flags = BUF_EMPTY;
+ if ((caddr_t)current < shared ||
+ (caddr_t)current > (shared + tapesize)) {
+ msg(gettext(
+ "bufclear: current pointer out of range of shared memory\n"));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if ((*current != NULL) &&
+ (*current < &bufp[0] || *current > &bufp[NBUF*ntrec])) {
+ /* ANSI string catenation, to shut cstyle up */
+ msg(gettext("bufclear: current buffer pointer (0x%x) "
+ "out of range of buffer\naddresses (0x%x - 0x%x)\n"),
+ *current, &bufp[0], &bufp[NBUF*ntrec]);
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ *current = bufp;
+}
+
+/*
+ * Start a process to collect information describing the dump.
+ * This data takes two forms:
+ * the bitmap and directory information being written to
+ * the front of the tape (the "archive" file)
+ * information describing each directory and inode (to
+ * be included in the database tmp file)
+ * Write the data to the files as it is received so huge file
+ * systems don't cause dump to consume large amounts of memory.
+ */
+static pid_t
+#ifdef __STDC__
+setuparchive(void)
+#else
+setuparchive()
+#endif
+{
+ struct slaves *slavep;
+ int cmd[2];
+ pid_t pid;
+ ssize_t size;
+ char *data;
+ char *errmsg;
+ int flags, saverr;
+ int punt = 0;
+
+ /*
+ * Both the archive and database tmp files are
+ * checkpointed by taking their current offsets
+ * (sizes) after completing each volume. Restoring
+ * from a checkpoint involves truncating to the
+ * checkpointed size.
+ */
+ if (archive && !doingactive) {
+ /* It's allowed/expected to exist, so can't use O_EXCL */
+ archivefd = safe_file_open(archivefile, O_WRONLY, 0600);
+ if (archivefd < 0) {
+ saverr = errno;
+ msg(gettext("Cannot open archive file `%s': %s\n"),
+ archivefile, strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+
+ if (lseek64(archivefd, lf_archoffset, 0) < 0) {
+ saverr = errno;
+ msg(gettext(
+ "Cannot position archive file `%s' : %s\n"),
+ archivefile, strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if (ftruncate64(archivefd, lf_archoffset) < 0) {
+ saverr = errno;
+ msg(gettext(
+ "Cannot truncate archive file `%s' : %s\n"),
+ archivefile, strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+
+ if (pipe(cmd) < 0) {
+ saverr = errno;
+ msg(gettext("%s: %s error: %s\n"),
+ "setuparchive", "pipe", strerror(saverr));
+ return (0);
+ }
+ sighold(SIGINT);
+ if ((pid = fork()) < 0) {
+ saverr = errno;
+ msg(gettext("%s: %s error: %s\n"),
+ "setuparchive", "fork", strerror(saverr));
+ return (0);
+ }
+ if (pid > 0) {
+ sigrelse(SIGINT);
+ /* parent process */
+ (void) close(cmd[0]);
+ arch = cmd[1];
+ return (pid);
+ }
+ /*
+ * child process
+ */
+ (void) signal(SIGINT, SIG_IGN); /* master handles this */
+#ifdef TDEBUG
+ (void) sleep(4); /* allow time for parent's message to get out */
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Archiver has pid = %ld\n"), (long)getpid());
+#endif
+ freeino(); /* release unneeded resources */
+ freetape();
+ for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) {
+ if (slavep->sl_slavefd != -1) {
+ (void) close(slavep->sl_slavefd);
+ slavep->sl_slavefd = -1;
+ }
+ }
+ (void) close(to);
+ (void) close(fi);
+ to = fi = -1;
+ (void) close(cmd[1]);
+ data = xmalloc(tp_bsize);
+ for (;;) {
+ size = atomic((int(*)())read, cmd[0], (char *)&flags,
+ sizeof (flags));
+ if ((unsigned)size != sizeof (flags))
+ break;
+ size = atomic((int(*)())read, cmd[0], data, tp_bsize);
+ if (size == tp_bsize) {
+ if (archive && flags & BUF_ARCHIVE && !punt &&
+ (size = write(archivefd, data, tp_bsize))
+ != tp_bsize) {
+ struct stat64 stats;
+
+ if (size != -1) {
+ errmsg = strdup(gettext(
+ "Output truncated"));
+ if (errmsg == NULL)
+ errmsg = "";
+ } else {
+ errmsg = strerror(errno);
+ }
+
+ if (fstat64(archivefd, &stats) < 0)
+ stats.st_size = -1;
+
+ /* cast to keep lint&printf happy */
+ msg(gettext(
+ "Cannot write archive file `%s' at offset %lld: %s\n"),
+ archivefile, (longlong_t)stats.st_size,
+ errmsg);
+ msg(gettext(
+ "Archive file will be deleted, dump will continue\n"));
+ punt++;
+ if ((size != -1) && (*errmsg != '\0')) {
+ free(errmsg);
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ (void) close(cmd[0]);
+ if (archive) {
+ (void) close(archivefd);
+ archivefd = -1;
+ }
+ if (punt) {
+ (void) unlink(archivefile);
+ Exit(X_ABORT);
+ }
+ Exit(X_FINOK);
+ /* NOTREACHED */
+}
+
+/*
+ * Start a process to read the output buffers and write the data
+ * to the output device.
+ */
+static pid_t
+#ifdef __STDC__
+setupwriter(void)
+#else
+setupwriter()
+#endif
+{
+ struct slaves *slavep;
+ int cmd[2];
+ pid_t pid;
+ int saverr;
+
+ caught = 0;
+ if (pipe(cmd) < 0) {
+ saverr = errno;
+ msg(gettext("%s: %s error: %s\n"),
+ "setupwriter", "pipe", strerror(saverr));
+ return (0);
+ }
+ sighold(SIGINT);
+ if ((pid = fork()) < 0) {
+ saverr = errno;
+ msg(gettext("%s: %s error: %s\n"),
+ "setupwriter", "fork", strerror(saverr));
+ return (0);
+ }
+ if (pid > 0) {
+ /*
+ * Parent process
+ */
+ sigrelse(SIGINT);
+ (void) close(cmd[0]);
+ writer = cmd[1];
+ return (pid);
+ }
+ /*
+ * Child (writer) process
+ */
+ (void) signal(SIGINT, SIG_IGN); /* master handles this */
+#ifdef TDEBUG
+ (void) sleep(4); /* allow time for parent's message to get out */
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Writer has pid = %ld\n"), (long)getpid());
+#endif
+ child_chdir();
+ freeino(); /* release unneeded resources */
+ for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) {
+ if (slavep->sl_slavefd != -1) {
+ (void) close(slavep->sl_slavefd);
+ slavep->sl_slavefd = -1;
+ }
+ }
+ (void) close(fi);
+ fi = -1;
+ (void) close(cmd[1]);
+ dowrite(cmd[0]);
+ if (arch >= 0) {
+ (void) close(arch);
+ arch = -1;
+ }
+ (void) close(cmd[0]);
+ Exit(X_FINOK);
+ /* NOTREACHED */
+}
+
+void
+#ifdef __STDC__
+spclrec(void)
+#else
+spclrec()
+#endif
+{
+ int s, i;
+ int32_t *ip;
+ int flags = BUF_SPCLREC;
+
+ if ((BIT(ino, shamap)) && (spcl.c_type == TS_INODE)) {
+ spcl.c_type = TS_ADDR;
+ /* LINTED: result fits in a short */
+ spcl.c_dinode.di_mode &= ~S_IFMT;
+ /* LINTED: result fits in a short */
+ spcl.c_dinode.di_mode |= IFSHAD;
+ }
+
+ /*
+ * Only TS_INODEs should have short metadata, if this
+ * isn't such a spclrec, clear the metadata flag and
+ * the c_shadow contents.
+ */
+ if (!(spcl.c_type == TS_INODE && (spcl.c_flags & DR_HASMETA))) {
+ spcl.c_flags &= ~DR_HASMETA;
+ bcopy(c_shadow_save, &(spcl.c_shadow),
+ sizeof (spcl.c_shadow));
+ }
+
+ if (spcl.c_type == TS_END) {
+ spcl.c_count = 1;
+ spcl.c_flags |= DR_INODEINFO;
+ bcopy((char *)inos, (char *)spcl.c_inos, sizeof (inos));
+ } else if (spcl.c_type == TS_TAPE) {
+ spcl.c_flags |= DR_NEWHEADER;
+ if (doingactive)
+ spcl.c_flags |= DR_REDUMP;
+ } else if (spcl.c_type != TS_INODE)
+ flags = BUF_SPCLREC;
+ spcl.c_tapea = *tapea;
+ /* LINTED for now, max inode # is 2**31 (ufs max size is 4TB) */
+ spcl.c_inumber = (ino32_t)ino;
+ spcl.c_magic = (tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC;
+ spcl.c_checksum = 0;
+ ip = (int32_t *)&spcl;
+ s = CHECKSUM;
+ assert((tp_bsize % sizeof (*ip)) == 0);
+ i = tp_bsize / sizeof (*ip);
+ assert((i%8) == 0);
+ i /= 8;
+ do {
+ s -= *ip++; s -= *ip++; s -= *ip++; s -= *ip++;
+ s -= *ip++; s -= *ip++; s -= *ip++; s -= *ip++;
+ } while (--i > 0);
+ spcl.c_checksum = s;
+ taprec((uchar_t *)&spcl, flags, sizeof (spcl));
+ if (spcl.c_type == TS_END)
+ spcl.c_flags &= ~DR_INODEINFO;
+ else if (spcl.c_type == TS_TAPE)
+ spcl.c_flags &= ~(DR_NEWHEADER|DR_REDUMP|DR_TRUEINC);
+}
+
+/*
+ * Fill appropriate buffer
+ */
+void
+taprec(dp, flags, size)
+ uchar_t *dp;
+ int flags;
+ int size;
+{
+ if (size > tp_bsize) {
+ msg(gettext(
+ "taprec: Unexpected buffer size, expected %d, got %d.\n"),
+ tp_bsize, size);
+ dumpabort();
+ /*NOTREACHED*/
+ }
+
+ while ((*current)->b_flags & BUF_FULL)
+ nap(10);
+
+ bcopy(dp, (*current)->b_data, (size_t)size);
+ if (size < tp_bsize) {
+ bzero((*current)->b_data + size, tp_bsize - size);
+ }
+
+ if (dumptoarchive)
+ flags |= BUF_ARCHIVE;
+
+ /* no locking as we assume only one reader and one writer active */
+ (*current)->b_flags = (flags | BUF_FULL);
+ if (++*current >= &bufp[NBUF*ntrec])
+ (*current) = &bufp[0];
+ (*tapea)++;
+}
+
+void
+dmpblk(blkno, size, offset)
+ daddr32_t blkno;
+ size_t size;
+ off_t offset;
+{
+ diskaddr_t dblkno;
+
+ assert((offset >> DEV_BSHIFT) <= INT32_MAX);
+ dblkno = fsbtodb(sblock, blkno) + (offset >> DEV_BSHIFT);
+ size = (size + DEV_BSIZE-1) & ~(DEV_BSIZE-1);
+ slp->sl_req->br_dblk = dblkno;
+ slp->sl_req->br_size = size;
+ if (dumptoarchive) {
+ /* LINTED: result fits in a short */
+ slp->sl_req->aflag |= BUF_ARCHIVE;
+ }
+ toslave((void(*)())0, ino);
+}
+
+/*ARGSUSED*/
+static void
+tperror(sig)
+ int sig;
+{
+ char buf[3000];
+
+ if (pipeout) {
+ msg(gettext("Write error on %s\n"), tape);
+ msg(gettext("Cannot recover\n"));
+ dumpabort();
+ /* NOTREACHED */
+ }
+ if (!doingverify) {
+ broadcast(gettext("WRITE ERROR!\n"));
+ (void) snprintf(buf, sizeof (buf),
+ gettext("Do you want to restart?: (\"yes\" or \"no\") "));
+ if (!query(buf)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if (tapeout && (isrewind(to) || offline)) {
+ /* ANSI string catenation, to shut cstyle up */
+ msg(gettext("This tape will rewind. After "
+ "it is rewound,\nreplace the faulty tape "
+ "with a new one;\nthis dump volume will "
+ "be rewritten.\n"));
+ }
+ } else {
+ broadcast(gettext("TAPE VERIFICATION ERROR!\n"));
+ (void) snprintf(buf, sizeof (buf), gettext(
+ "Do you want to rewrite?: (\"yes\" or \"no\") "));
+ if (!query(buf)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ msg(gettext(
+ "This tape will be rewritten and then verified\n"));
+ }
+ killall();
+ trewind();
+ Exit(X_REWRITE);
+}
+
+/*
+ * Called by master from pass() to send a request to dump files/blocks
+ * to one of the slaves. Slaves return whether the file was active
+ * when it was being dumped. The tape writer process sends checkpoint
+ * info when it completes a volume.
+ */
+void
+toslave(fn, inumber)
+ void (*fn)();
+ ino_t inumber;
+{
+ int wasactive;
+
+ if (recsout >= SLAVES) {
+ if ((unsigned)atomic((int(*)())read, slp->sl_slavefd,
+ (char *)&wasactive, sizeof (wasactive)) !=
+ sizeof (wasactive)) {
+ cmdrderr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if (wasactive) {
+ active++;
+ msg(gettext(
+ "The file at inode `%lu' was active and will be recopied\n"),
+ slp->sl_req->ir_inumber);
+ /* LINTED: 32-bit to 8-bit assignment ok */
+ BIS(slp->sl_req->ir_inumber, activemap);
+ }
+ }
+ slp->sl_req->aflag = 0;
+ if (dumptoarchive) {
+ /* LINTED: result fits in a short */
+ slp->sl_req->aflag |= BUF_ARCHIVE;
+ }
+ if (fn)
+ (*fn)(inumber);
+
+ if (atomic((int(*)())write, slp->sl_slavefd, (char *)slp->sl_req,
+ reqsiz) != reqsiz) {
+ cmdwrterr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ ++recsout;
+ nextslave();
+}
+
+void
+dospcl(inumber)
+ ino_t inumber;
+{
+ /* LINTED for now, max inode # is 2**31 (ufs max size is 1TB) */
+ spcl.c_inumber = (ino32_t)inumber;
+ slp->sl_req->br_dblk = 0;
+ bcopy((char *)&spcl, (char *)slp->sl_req->br_spcl, tp_bsize);
+}
+
+static void
+#ifdef __STDC__
+nextslave(void)
+#else
+nextslave()
+#endif
+{
+ if (++rotor >= SLAVES) {
+ rotor = 0;
+ }
+ slp = &slaves[rotor];
+}
+
+void
+#ifdef __STDC__
+flushcmds(void)
+#else
+flushcmds()
+#endif
+{
+ int i;
+ int wasactive;
+
+ /*
+ * Retrieve all slave status
+ */
+ if (recsout < SLAVES) {
+ slp = slaves;
+ rotor = 0;
+ }
+ for (i = 0; i < (recsout < SLAVES ? recsout : SLAVES); i++) {
+ if ((unsigned)atomic((int(*)())read, slp->sl_slavefd,
+ (char *)&wasactive, sizeof (wasactive)) !=
+ sizeof (wasactive)) {
+ cmdrderr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if (wasactive) {
+ active++;
+ msg(gettext(
+ "inode %d was active and will be recopied\n"),
+ slp->sl_req->ir_inumber);
+ /* LINTED: 32-bit to 8-bit assignment ok */
+ BIS(slp->sl_req->ir_inumber, activemap);
+ }
+ nextslave();
+ }
+}
+
+void
+#ifdef __STDC__
+flusht(void)
+#else
+flusht()
+#endif
+{
+ sigset_t block_set, oset; /* hold SIGUSR1 and atomically sleep */
+
+ (void) sigemptyset(&block_set);
+ (void) sigaddset(&block_set, SIGUSR1);
+ (void) sigprocmask(SIG_BLOCK, &block_set, &oset);
+ (void) kill(writepid, SIGUSR1); /* tell writer to flush */
+ (void) sigpause(SIGUSR1); /* wait for SIGUSR1 from writer */
+ /*NOTREACHED*/
+}
+
+jmp_buf checkpoint_buf;
+
+/*
+ * Roll forward to the next volume after receiving
+ * an EOT signal from writer. Get checkpoint data
+ * from writer and return if done, otherwise fork
+ * a new process and jump back to main state loop
+ * to begin the next volume. Installed as the master's
+ * signal handler for SIGUSR1.
+ */
+/*ARGSUSED*/
+static void
+rollforward(sig)
+ int sig;
+{
+ int status;
+ (void) sighold(SIGUSR1);
+
+ /*
+ * Writer sends us checkpoint information after
+ * each volume. A returned state of DS_DONE with no
+ * unwritten (left-over) records differentiates a
+ * clean flush from one in which EOT was encountered.
+ */
+ if ((unsigned)atomic((int(*)())read, writer, (char *)&chkpt,
+ sizeof (struct slaves)) != sizeof (struct slaves)) {
+ cmdrderr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if (atomic((int(*)())read, writer, (char *)&spcl,
+ TP_BSIZE_MIN) != TP_BSIZE_MIN) {
+ cmdrderr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ ino = chkpt.sl_inos - 1;
+ pos = chkpt.sl_offset;
+ leftover = chkpt.sl_count;
+ dumpstate = chkpt.sl_state;
+ blockswritten = ++chkpt.sl_tapea;
+
+ if (dumpstate == DS_DONE) {
+ if (archivepid) {
+ /*
+ * If archiving (either archive or
+ * database), signal the archiver
+ * to finish up. This must happen
+ * before the writer exits in order
+ * to avoid a race.
+ */
+ (void) kill(archivepid, SIGUSR1);
+ }
+ (void) signal(SIGUSR1, SIG_IGN);
+ (void) sigrelse(SIGUSR1);
+ (void) kill(writepid, SIGUSR1); /* tell writer to exit */
+
+ lf_archoffset = 0LL;
+ longjmp(checkpoint_buf, 1);
+ /*NOTREACHED*/
+ }
+
+ if (leftover) {
+ (void) memmove(spcl.c_addr,
+ &spcl.c_addr[spcl.c_count-leftover], leftover);
+ bzero(&spcl.c_addr[leftover], TP_NINDIR-leftover);
+ }
+ if (writepid) {
+ (void) kill(writepid, SIGUSR1); /* tell writer to exit */
+ (void) close(writer);
+ writer = -1;
+ }
+ if (archivepid) {
+ (void) waitpid(archivepid, &status, 0); /* wait for archiver */
+#ifdef TDEBUG
+
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Archiver %ld returns with status %d\n"),
+ (long)archivepid, status);
+#endif
+ archivepid = 0;
+ }
+ /*
+ * Checkpoint archive file
+ */
+ if (!doingverify && archive) {
+ lf_archoffset = lseek64(archivefd, (off64_t)0, 2);
+ if (lf_archoffset < 0) {
+ int saverr = errno;
+ msg(gettext("Cannot position archive file `%s': %s\n"),
+ archivefile, strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ (void) close(archivefd);
+ archivefd = -1;
+ }
+ resetino(ino);
+
+ if (dumpstate == DS_START) {
+ msg(gettext(
+ "Tape too short: changing volumes and restarting\n"));
+ reset();
+ }
+
+ if (!pipeout) {
+ if (verify && !doingverify)
+ trewind();
+ else {
+ close_rewind();
+ changevol();
+ }
+ }
+
+ (void) sigrelse(SIGUSR1);
+ otape(0);
+ longjmp(checkpoint_buf, 1);
+ /*NOTREACHED*/
+}
+
+static void
+nap(ms)
+ int ms;
+{
+ struct timeval tv;
+
+ tv.tv_sec = ms / 1000;
+ tv.tv_usec = (ms - tv.tv_sec * 1000) * 1000;
+ (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv);
+}
+
+static jmp_buf alrm_buf;
+
+/*ARGSUSED*/
+static void
+alrm(sig)
+ int sig;
+{
+ longjmp(alrm_buf, 1);
+ /*NOTREACHED*/
+}
+
+void
+#ifdef __STDC__
+nextdevice(void)
+#else
+nextdevice()
+#endif
+{
+ char *cp;
+
+ if (host != NULL) /* we set the host only once in ufsdump */
+ return;
+
+ host = NULL;
+ if (strchr(tape, ':')) {
+ if (diskette) {
+ msg(gettext("Cannot do remote dump to diskette\n"));
+ Exit(X_ABORT);
+ }
+ host = tape;
+ tape = strchr(host, ':');
+ *tape++ = 0;
+ cp = strchr(host, '@'); /* user@host? */
+ if (cp != (char *)0)
+ cp++;
+ else
+ cp = host;
+ } else
+ cp = spcl.c_host;
+ /*
+ * dumpdev is provided for use in prompts and is of
+ * the form:
+ * hostname:device
+ * sdumpdev is of the form:
+ * hostname:device
+ * for remote devices, and simply:
+ * device
+ * for local devices.
+ */
+ if (dumpdev != (char *)NULL) {
+ /* LINTED: dumpdev is not NULL */
+ free(dumpdev);
+ }
+ /*LINTED [cast to smaller integer]*/
+ dumpdev = xmalloc((size_t)((sizeof (spcl.c_host) + strlen(tape) + 2)));
+ /* LINTED unsigned -> signed cast ok */
+ (void) sprintf(dumpdev, "%.*s:%s", (int)sizeof (spcl.c_host), cp, tape);
+ if (cp == spcl.c_host)
+ sdumpdev = strchr(dumpdev, ':') + 1;
+ else
+ sdumpdev = dumpdev;
+}
+
+/*
+ * Gross hack due to misfeature of mt tape driver that causes
+ * the device to rewind if we generate any signals. Guess
+ * whether tape is rewind device or not -- for local devices
+ * we can just look at the minor number. For rmt devices,
+ * make an educated guess.
+ */
+int
+isrewind(f)
+ int f; /* fd, if local device */
+{
+ struct stat64 sbuf;
+ char *c;
+ int unit;
+ int rewind;
+
+ if (host) {
+ c = strrchr(tape, '/');
+ if (c == NULL)
+ c = tape;
+ else
+ c++;
+ /*
+ * If the last component begins or ends with an 'n', it is
+ * assumed to be a non-rewind device.
+ */
+ if (c[0] == 'n' || c[strlen(c)-1] == 'n')
+ rewind = 0;
+ else if ((strstr(tape, "mt") || strstr(tape, "st")) &&
+ sscanf(tape, "%*[a-zA-Z/]%d", &unit) == 1 &&
+ (unit & MT_NOREWIND))
+ rewind = 0;
+ else
+ rewind = 1;
+ } else {
+ if (fstat64(f, &sbuf) < 0) {
+ msg(gettext(
+ "Cannot obtain status of output device `%s'\n"),
+ tape);
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ rewind = minor(sbuf.st_rdev) & MT_NOREWIND ? 0 : 1;
+ }
+ return (rewind);
+}
+
+static void
+#ifdef __STDC__
+just_rewind(void)
+#else
+just_rewind()
+#endif
+{
+ struct slaves *slavep;
+ char *rewinding = gettext("Tape rewinding\n");
+
+ for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) {
+ if (slavep->sl_slavepid > 0) /* signal normal exit */
+ (void) kill(slavep->sl_slavepid, SIGTERM);
+ if (slavep->sl_slavefd >= 0) {
+ (void) close(slavep->sl_slavefd);
+ slavep->sl_slavefd = -1;
+ }
+ }
+
+ /* wait for any signals from slaves */
+ while (waitpid(0, (int *)0, 0) >= 0)
+ /*LINTED [empty body]*/
+ continue;
+
+ if (pipeout)
+ return;
+
+ if (doingverify) {
+ /*
+ * Space to the end of the tape.
+ * Backup first in case we already read the EOF.
+ */
+ if (host) {
+ (void) rmtioctl(MTBSR, 1);
+ if (rmtioctl(MTEOM, 1) < 0)
+ (void) rmtioctl(MTFSF, 1);
+ } else {
+ static struct mtop bsr = { MTBSR, 1 };
+ static struct mtop eom = { MTEOM, 1 };
+ static struct mtop fsf = { MTFSF, 1 };
+
+ (void) ioctl(to, MTIOCTOP, &bsr);
+ if (ioctl(to, MTIOCTOP, &eom) < 0)
+ (void) ioctl(to, MTIOCTOP, &fsf);
+ }
+ }
+
+ /*
+ * Guess whether the tape is rewinding so we can tell
+ * the operator if it's going to take a long time.
+ */
+ if (tapeout && isrewind(to)) {
+ /* tape is probably rewinding */
+ msg(rewinding);
+ }
+}
+
+void
+#ifdef __STDC__
+trewind(void)
+#else
+trewind()
+#endif
+{
+ (void) timeclock((time_t)0);
+ if (offline && (!verify || doingverify)) {
+ close_rewind();
+ } else {
+ just_rewind();
+ if (host)
+ rmtclose();
+ else {
+ (void) close(to);
+ to = -1;
+ }
+ }
+}
+
+void
+#ifdef __STDC__
+close_rewind(void)
+#else
+close_rewind()
+#endif
+{
+ char *rewinding = gettext("Tape rewinding\n");
+
+ (void) timeclock((time_t)0);
+ just_rewind();
+ /*
+ * The check in just_rewind won't catch the case in
+ * which the current volume is being taken off-line
+ * and is not mounted on a no-rewind device (and is
+ * not the last volume, which is not taken off-line).
+ */
+ if (tapeout && !isrewind(to) && offline) {
+ /* tape is probably rewinding */
+ msg(rewinding);
+ }
+ if (host) {
+ if (offline || autoload)
+ (void) rmtioctl(MTOFFL, 0);
+ rmtclose();
+ } else {
+ if (offline || autoload) {
+ static struct mtop offl = { MTOFFL, 0 };
+
+ (void) ioctl(to, MTIOCTOP, &offl);
+ if (diskette)
+ (void) ioctl(to, FDEJECT, 0);
+ }
+ (void) close(to);
+ to = -1;
+ }
+}
+
+void
+#ifdef __STDC__
+changevol(void)
+#else
+changevol()
+#endif
+{
+ char buf1[3000], buf2[3000];
+ char volname[LBLSIZE+1];
+
+ /*CONSTANTCONDITION*/
+ assert(sizeof (spcl.c_label) < sizeof (volname));
+
+ filenum = 1;
+ nextdevice();
+ (void) strcpy(spcl.c_label, tlabel);
+ if (host) {
+ char *rhost = host;
+ char *cp = strchr(host, '@');
+ if (cp == (char *)0)
+ cp = host;
+ else
+ cp++;
+
+ if (rmthost(rhost, ntrec) == 0) {
+ msg(gettext("Cannot connect to tape host `%s'\n"), cp);
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if (rhost != host)
+ free(rhost);
+ }
+
+ /*
+ * Make volume switching as automatic as possible
+ * while avoiding overwriting volumes. We will
+ * switch automatically under the following condition:
+ * 1) The user specified autoloading from the
+ * command line.
+ * At one time, we (in the guise of hsmdump) had the
+ * concept of a sequence of devices to rotate through,
+ * but that's never been a ufsdump feature.
+ */
+ if (autoload) {
+ int tries;
+
+ /*
+ * Stop the clock for throughput calculations.
+ */
+ if ((telapsed != NULL) && (tstart_writing != NULL)) {
+ *telapsed += time((time_t *)NULL) - *tstart_writing;
+ }
+
+ (void) snprintf(volname, sizeof (volname), "#%d", tapeno+1);
+ (void) snprintf(buf1, sizeof (buf1), gettext(
+ "Mounting volume %s on %s\n"), volname, dumpdev);
+ msg(buf1);
+ broadcast(buf1);
+
+ /*
+ * Wait for the tape to autoload. Note that the delay
+ * period doesn't take into account however long it takes
+ * for the open to fail (measured at 21 seconds for an
+ * Exabyte 8200 under 2.7 on an Ultra 2).
+ */
+ for (tries = 0; tries < autoload_tries; tries++) {
+ if (host) {
+ if (rmtopen(tape, O_RDONLY) >= 0) {
+ rmtclose();
+ return;
+ }
+ } else {
+ int f, m;
+
+ m = (access(tape, F_OK) == 0) ? 0 : O_CREAT;
+ if ((f = doingverify ?
+ safe_device_open(tape, O_RDONLY, 0600) :
+ safe_device_open(tape, O_RDONLY|m, 0600))
+ >= 0) {
+ (void) close(f);
+ return;
+ }
+ }
+ (void) sleep(autoload_period);
+ }
+ /*
+ * Autoload timed out, ask the operator to do it.
+ * Note that query() will update *telapsed, and we
+ * shouldn't charge for the autoload time. So, since
+ * we updated *telapsed ourselves above, we just set
+ * tstart_writing to the current time, and query()
+ * will end up making a null-effect change. This,
+ * of course, assumes that our caller will be resetting
+ * *tstart_writing. This is currently the case.
+ * If tstart_writing is NULL (should never happen),
+ * we're ok, since time(2) will accept a NULL pointer.
+ */
+ (void) time(tstart_writing);
+ }
+
+ if (strncmp(spcl.c_label, "none", 5)) {
+ (void) strncpy(volname, spcl.c_label, sizeof (spcl.c_label));
+ volname[sizeof (spcl.c_label)] = '\0';
+ } else
+ (void) snprintf(volname, sizeof (volname), "#%d", tapeno+1);
+
+ timeest(1, spcl.c_tapea);
+ (void) snprintf(buf1, sizeof (buf1), gettext(
+ "Change Volumes: Mount volume `%s' on `%s'\n"), volname, dumpdev);
+ msg(buf1);
+ broadcast(gettext("CHANGE VOLUMES!\7\7\n"));
+ (void) snprintf(buf1, sizeof (buf1), gettext(
+ "Is the new volume (%s) mounted on `%s' and ready to go?: %s"),
+ volname, dumpdev, gettext("(\"yes\" or \"no\") "));
+ while (!query(buf1)) {
+ (void) snprintf(buf2, sizeof (buf2), gettext(
+ "Do you want to abort dump?: (\"yes\" or \"no\") "));
+ if (query(buf2)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+}
+
+/*
+ * We implement taking and restoring checkpoints on the tape level.
+ * When each tape is opened, a new process is created by forking; this
+ * saves all of the necessary context in the parent. The child
+ * continues the dump; the parent waits around, saving the context.
+ * If the child returns X_REWRITE, then it had problems writing that tape;
+ * this causes the parent to fork again, duplicating the context, and
+ * everything continues as if nothing had happened.
+ */
+
+void
+otape(top)
+ int top;
+{
+ static struct mtget mt;
+ char buf[3000];
+ pid_t parentpid;
+ pid_t childpid;
+ pid_t waitproc;
+ int status;
+ struct sigvec sv, osv;
+
+ sv.sv_flags = SA_RESTART;
+ (void) sigemptyset(&sv.sa_mask);
+ sv.sv_handler = SIG_IGN;
+ (void) sigvec(SIGINT, &sv, (struct sigvec *)0);
+
+ parentpid = getpid();
+
+ if (verify) {
+ if (doingverify)
+ doingverify = 0;
+ else
+ Exit(X_VERIFY);
+ }
+restore_check_point:
+
+ sv.sv_handler = interrupt;
+ (void) sigvec(SIGINT, &sv, (struct sigvec *)0);
+ (void) fflush(stderr);
+ /*
+ * All signals are inherited...
+ */
+ sighold(SIGINT);
+ childpid = fork();
+ if (childpid < 0) {
+ msg(gettext(
+ "Context-saving fork failed in parent %ld\n"),
+ (long)parentpid);
+ Exit(X_ABORT);
+ }
+ if (childpid != 0) {
+ /*
+ * PARENT:
+ * save the context by waiting
+ * until the child doing all of the work returns.
+ * let the child catch user interrupts
+ */
+ sv.sv_handler = SIG_IGN;
+ (void) sigvec(SIGINT, &sv, (struct sigvec *)0);
+ sigrelse(SIGINT);
+#ifdef TDEBUG
+
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext(
+ "Volume: %d; parent process: %ld child process %ld\n"),
+ tapeno+1, (long)parentpid, (long)childpid);
+#endif /* TDEBUG */
+ for (;;) {
+ waitproc = waitpid(0, &status, 0);
+ if (waitproc == childpid)
+ break;
+ msg(gettext(
+ "Parent %ld waiting for child %ld had another child %ld return\n"),
+ (long)parentpid, (long)childpid, (long)waitproc);
+ }
+ if (WIFSIGNALED(status)) {
+ msg(gettext("Process %ld killed by signal %d: %s\n"),
+ (long)childpid, WTERMSIG(status),
+ strsignal(WTERMSIG(status)));
+ status = X_ABORT;
+ } else
+ status = WEXITSTATUS(status);
+#ifdef TDEBUG
+ switch (status) {
+ case X_FINOK:
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext(
+ "Child %ld finishes X_FINOK\n"), (long)childpid);
+ break;
+ case X_ABORT:
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext(
+ "Child %ld finishes X_ABORT\n"), (long)childpid);
+ break;
+ case X_REWRITE:
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext(
+ "Child %ld finishes X_REWRITE\n"), (long)childpid);
+ break;
+ case X_RESTART:
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext(
+ "Child %ld finishes X_RESTART\n"), (long)childpid);
+ break;
+ case X_VERIFY:
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext(
+ "Child %ld finishes X_VERIFY\n"), (long)childpid);
+ break;
+ default:
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Child %ld finishes unknown %d\n"),
+ (long)childpid, status);
+ break;
+ }
+#endif /* TDEBUG */
+ switch (status) {
+ case X_FINOK:
+ /* wait for children */
+ while (waitpid(0, (int *)0, 0) >= 0)
+ /*LINTED [empty body]*/
+ continue;
+ Exit(X_FINOK);
+ /*NOTREACHED*/
+ case X_ABORT:
+ Exit(X_ABORT);
+ /*NOTREACHED*/
+ case X_VERIFY:
+ doingverify++;
+ goto restore_check_point;
+ /*NOTREACHED*/
+ case X_REWRITE:
+ doingverify = 0;
+ changevol();
+ goto restore_check_point;
+ /* NOTREACHED */
+ case X_RESTART:
+ doingverify = 0;
+ if (!top) {
+ Exit(X_RESTART);
+ }
+ if (!offline)
+ autoload = 0;
+ changevol();
+ sv.sv_handler = interrupt;
+ (void) sigvec(SIGINT, &sv, (struct sigvec *)0);
+ return;
+ /* NOTREACHED */
+ default:
+ msg(gettext("Bad return code from dump: %d\n"), status);
+ Exit(X_ABORT);
+ /*NOTREACHED*/
+ }
+ /*NOTREACHED*/
+ } else { /* we are the child; just continue */
+ child_chdir();
+ sigrelse(SIGINT);
+#ifdef TDEBUG
+ (void) sleep(4); /* time for parent's message to get out */
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext(
+ "Child on Volume %d has parent %ld, my pid = %ld\n"),
+ tapeno+1, (long)parentpid, (long)getpid());
+#endif
+ (void) snprintf(buf, sizeof (buf), gettext(
+"Cannot open `%s'. Do you want to retry the open?: (\"yes\" or \"no\") "),
+ dumpdev);
+ if (doingverify) {
+ /* 1 for stdout */
+ while ((to = host ? rmtopen(tape, O_RDONLY) :
+ pipeout ? 1 :
+ safe_device_open(tape, O_RDONLY, 0600)) < 0) {
+ perror(tape);
+ if (autoload) {
+ if (!query_once(buf, 1)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ } else {
+ if (!query(buf)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+ }
+
+ /*
+ * If we're using the non-rewinding tape device,
+ * the tape will be left positioned after the
+ * EOF mark. We need to back up to the beginning
+ * of this tape file (cross two tape marks in the
+ * reverse direction and one in the forward
+ * direction) before the verify pass.
+ */
+ if (host) {
+ if (rmtioctl(MTBSF, 2) >= 0)
+ (void) rmtioctl(MTFSF, 1);
+ else
+ (void) rmtioctl(MTNBSF, 1);
+ } else {
+ static struct mtop bsf = { MTBSF, 2 };
+ static struct mtop fsf = { MTFSF, 1 };
+ static struct mtop nbsf = { MTNBSF, 1 };
+
+ if (ioctl(to, MTIOCTOP, &bsf) >= 0)
+ (void) ioctl(to, MTIOCTOP, &fsf);
+ else
+ (void) ioctl(to, MTIOCTOP, &nbsf);
+ }
+ } else {
+ /*
+ * XXX Add logic to test for "tape" being a
+ * XXX device or a non-existent file.
+ * Current behaviour is that it must exist,
+ * and we over-write whatever's there.
+ * This can be bad if tape == "/etc/passwd".
+ */
+ if (!pipeout && doposition && (tapeno == 0)) {
+ positiontape(buf);
+ if (setjmp(alrm_buf)) {
+ /*
+ * The tape is rewinding;
+ * we're screwed.
+ */
+ msg(gettext(
+ "Cannot position tape using rewind device!\n"));
+ dumpabort();
+ /*NOTREACHED*/
+ } else {
+ sv.sv_handler = alrm;
+ (void) sigvec(SIGALRM, &sv, &osv);
+ (void) alarm(15);
+ }
+ while ((to = host ? rmtopen(tape, O_WRONLY) :
+ safe_device_open(tape, O_WRONLY, 0600)) < 0)
+ (void) sleep(10);
+ (void) alarm(0);
+ (void) sigvec(SIGALRM, &osv,
+ (struct sigvec *)0);
+ } else {
+ int m;
+ m = (access(tape, F_OK) == 0) ? 0 : O_CREAT;
+ /*
+ * Only verify the tape label if label
+ * verification is on and we are at BOT
+ */
+ if (pipeout)
+ to = 1;
+ else while ((to = host ?
+ rmtopen(tape, O_WRONLY) :
+ safe_device_open(tape, O_WRONLY|m, 0600))
+ < 0)
+ if (!query_once(buf, 1)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+ }
+ if (!pipeout) {
+ tapeout = host ? rmtstatus(&mt) >= 0 :
+ ioctl(to, MTIOCGET, &mt) >= 0; /* set state */
+ /*
+ * Make sure the tape is positioned
+ * where it is supposed to be
+ */
+ if (tapeout && (tapeno > 0) &&
+ (mt.mt_fileno != (filenum-1))) {
+ (void) snprintf(buf, sizeof (buf), gettext(
+ "Warning - tape positioning error!\n\
+\t%s current file %ld, should be %ld\n"),
+ tape, mt.mt_fileno+1, filenum);
+ msg(buf);
+ dumpailing();
+ }
+ }
+ tapeno++; /* current tape sequence */
+ if (tapeno < TP_NINOS)
+ inos[tapeno] = chkpt.sl_inos;
+ spcl.c_firstrec = chkpt.sl_firstrec;
+ spcl.c_tapea = (*tapea) = chkpt.sl_tapea;
+ spcl.c_volume++;
+
+ enslave(); /* Share tape buffers with slaves */
+
+#ifdef DEBUG
+ if (xflag) {
+ /* XGETTEXT: #ifdef DEBUG only */
+ msg(gettext("Checkpoint state:\n"));
+ msg(" blockswritten %u\n", blockswritten);
+ msg(" ino %u\n", ino);
+ msg(" pos %u\n", pos);
+ msg(" left %u\n", leftover);
+ msg(" tapea %u\n", (*tapea));
+ msg(" state %d\n", dumpstate);
+ }
+#endif
+ spcl.c_type = TS_TAPE;
+ spcl.c_tpbsize = tp_bsize;
+ if (leftover == 0) {
+ spcl.c_count = 0;
+ spclrec();
+ newtape = 0;
+ } else
+ newtape++; /* new volume indication */
+ if (doingverify) {
+ msg(gettext("Starting verify pass\n"));
+ } else if (tapeno > 1) {
+ msg(gettext(
+ "Volume %d begins with blocks from inode %lu\n"),
+ tapeno, chkpt.sl_inos);
+ }
+ (void) timeclock((time_t)1);
+ (void) time(tstart_writing);
+ timeest(0, spcl.c_tapea);
+ }
+}
+
+void
+#ifdef __STDC__
+dumpabort(void)
+#else
+dumpabort()
+#endif
+{
+
+ if (master && master != getpid())
+ /*
+ * signal master to call dumpabort
+ */
+ (void) kill(master, SIGTERM);
+ else {
+ killall();
+
+ if (archivefile)
+ (void) unlink(archivefile);
+ msg(gettext("The ENTIRE dump is aborted.\n"));
+ }
+ Exit(X_ABORT);
+}
+
+void
+dumpailing(void)
+{
+
+ broadcast(gettext("DUMP IS AILING!\n"));
+ if (!query(gettext(
+ "Do you want to attempt to continue? (\"yes\" or \"no\") "))) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+}
+
+void
+Exit(status)
+{
+ /*
+ * Clean up message system
+ */
+#ifdef TDEBUG
+
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("pid = %ld exits with status %d\n"),
+ (long)getpid(), status);
+#endif /* TDEBUG */
+ exit(status);
+}
+
+static void
+#ifdef __STDC__
+killall(void)
+#else
+killall()
+#endif
+{
+ struct slaves *slavep;
+
+ for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++)
+ if (slavep->sl_slavepid > 0) {
+ (void) kill(slavep->sl_slavepid, SIGKILL);
+#ifdef TDEBUG
+
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Slave child %ld killed\n"),
+ (long)slavep->sl_slavepid);
+#endif
+ }
+ if (writepid) {
+ (void) kill(writepid, SIGKILL);
+#ifdef TDEBUG
+
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Writer child %ld killed\n"), (long)writepid);
+#endif
+ }
+ if (archivepid) {
+ (void) kill(archivepid, SIGKILL);
+#ifdef TDEBUG
+
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Archiver child %ld killed\n"), (long)archivepid);
+#endif
+ }
+}
+
+/*ARGSUSED*/
+static void
+proceed(sig)
+ int sig;
+{
+ caught++;
+}
+
+/*ARGSUSED*/
+static void
+die(sig)
+ int sig;
+{
+ Exit(X_FINOK);
+}
+
+static void
+#ifdef __STDC__
+enslave(void)
+#else
+enslave()
+#endif
+{
+ int cmd[2]; /* file descriptors */
+ int i;
+ struct sigvec sv;
+ struct slaves *slavep;
+ int saverr;
+
+ sv.sv_flags = SA_RESTART;
+ (void) sigemptyset(&sv.sa_mask);
+ master = getpid();
+ /*
+ * slave sends SIGTERM on dumpabort
+ */
+ sv.sv_handler = (void(*)(int))dumpabort;
+ (void) sigvec(SIGTERM, &sv, (struct sigvec *)0);
+ sv.sv_handler = tperror;
+ (void) sigvec(SIGUSR2, &sv, (struct sigvec *)0);
+ sv.sv_handler = proceed;
+ (void) sigvec(SIGUSR1, &sv, (struct sigvec *)0);
+ totalrecsout += recsout;
+ caught = 0;
+ recsout = 0;
+ rotor = 0;
+ bufclear();
+ for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++)
+ slavep->sl_slavefd = -1;
+ archivefd = arch = writer = -1;
+ for (i = 0; i < SLAVES; i++) {
+ if (pipe(cmd) < 0) {
+ saverr = errno;
+ msg(gettext(
+ "Cannot create pipe for slave process: %s\n"),
+ strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ sighold(SIGUSR2);
+ sighold(SIGINT);
+ sighold(SIGTERM);
+ if ((slaves[i].sl_slavepid = fork()) < 0) {
+ saverr = errno;
+ msg(gettext("Cannot create slave process: %s\n"),
+ strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ slaves[i].sl_slavefd = cmd[1];
+ if (slaves[i].sl_slavepid == 0) { /* Slave starts up here */
+ pid_t next; /* pid of neighbor */
+
+ sv.sv_handler = SIG_DFL;
+ (void) sigvec(SIGUSR2, &sv, (struct sigvec *)0);
+ sv.sv_handler = SIG_IGN; /* master handler INT */
+ (void) sigvec(SIGINT, &sv, (struct sigvec *)0);
+ sv.sv_handler = die; /* normal slave exit */
+ (void) sigvec(SIGTERM, &sv, (struct sigvec *)0);
+
+ child_chdir();
+ sigrelse(SIGUSR2);
+ sigrelse(SIGINT);
+ sigrelse(SIGTERM);
+
+ freeino(); /* release unneeded resources */
+#ifdef TDEBUG
+ (void) sleep(4); /* time for parent's message to get out */
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("Neighbor has pid = %ld\n"), (long)getpid());
+#endif
+ /* Closes cmd[1] as a side-effect */
+ for (slavep = &slaves[0];
+ slavep < &slaves[SLAVES];
+ slavep++)
+ if (slavep->sl_slavefd >= 0) {
+ (void) close(slavep->sl_slavefd);
+ slavep->sl_slavefd = -1;
+ }
+ (void) close(to);
+ (void) close(fi); /* Need our own seek ptr */
+ to = -1;
+
+ fi = open(disk, O_RDONLY);
+
+ if (fi < 0) {
+ saverr = errno;
+ msg(gettext(
+ "Cannot open dump device `%s': %s\n"),
+ disk, strerror(saverr));
+ dumpabort();
+ /*NOTREACHED*/
+ }
+
+ if ((unsigned)atomic((int(*)())read, cmd[0],
+ (char *)&next, sizeof (next)) != sizeof (next)) {
+ cmdrderr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ dumpoffline(cmd[0], next, i);
+ Exit(X_FINOK);
+ }
+ /* Parent continues here */
+ sigrelse(SIGUSR2);
+ sigrelse(SIGINT);
+ sigrelse(SIGTERM);
+ (void) close(cmd[0]);
+ }
+
+ if (archive) {
+ archivepid = setuparchive();
+ if (!archivepid) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+
+ writepid = setupwriter();
+ if (!writepid) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+
+ if (arch >= 0) {
+ (void) close(arch); /* only writer has this open */
+ arch = -1;
+ }
+
+ /* Tell each slave who follows it */
+ for (i = 0; i < SLAVES; i++) {
+ if ((unsigned)atomic((int(*)())write, slaves[i].sl_slavefd,
+ (char *)&(slaves[(i + 1) % SLAVES].sl_slavepid),
+ sizeof (int)) != sizeof (int)) {
+ cmdwrterr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+ sv.sv_handler = rollforward; /* rcvd from writer on EOT */
+ (void) sigvec(SIGUSR1, &sv, (struct sigvec *)0);
+ slp = slaves;
+ (void) kill(slp->sl_slavepid, SIGUSR1);
+ master = 0;
+}
+
+static void
+#ifdef __STDC__
+wait_our_turn(void)
+#else
+wait_our_turn()
+#endif
+{
+ (void) sighold(SIGUSR1);
+
+ if (!caught) {
+#ifdef INSTRUMENT
+ (*idle)++;
+#endif
+ (void) sigpause(SIGUSR1);
+ }
+ caught = 0;
+ (void) sigrelse(SIGUSR1);
+}
+
+static void
+dumpoffline(cmd, next, mynum)
+ int cmd;
+ pid_t next;
+ int mynum;
+{
+ struct req *p = slaves[mynum].sl_req;
+ ulong_t i;
+ uchar_t *cp;
+ uchar_t *blkbuf;
+ int notactive = 0;
+
+ blkbuf = xmalloc(sblock->fs_bsize);
+
+ /*CONSTANTCONDITION*/
+ assert(sizeof (spcl) == TP_BSIZE_MIN);
+
+ while (atomic((int(*)())read, cmd, (char *)p, reqsiz) == reqsiz) {
+ if (p->br_dblk) {
+ bread(p->br_dblk, (uchar_t *)blkbuf, p->br_size);
+ } else {
+ bcopy((char *)p->br_spcl, (char *)&spcl,
+ sizeof (spcl));
+ ino = spcl.c_inumber;
+ }
+ dumptoarchive = p->aflag & BUF_ARCHIVE;
+ wait_our_turn();
+ if (p->br_dblk) {
+ for (i = p->br_size, cp = blkbuf;
+ i > 0;
+ /* LINTED character pointers aren't signed */
+ cp += i > tp_bsize ? tp_bsize : i,
+ i -= i > tp_bsize ? tp_bsize : i) {
+ /* LINTED unsigned to signed conversion ok */
+ taprec(cp, 0, i > tp_bsize ? tp_bsize : (int)i);
+ }
+ } else
+ spclrec();
+ (void) kill(next, SIGUSR1); /* Next slave's turn */
+ /*
+ * Note that we lie about file activity since we don't
+ * check for it.
+ */
+ if ((unsigned)atomic((int(*)())write, cmd, (char *)&notactive,
+ sizeof (notactive)) != sizeof (notactive)) {
+ cmdwrterr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+
+ free(blkbuf);
+}
+
+static int count; /* tape blocks written since last spclrec */
+
+/*ARGSUSED*/
+static void
+onxfsz(sig)
+ int sig;
+{
+ msg(gettext("File size limit exceeded writing output volume %d\n"),
+ tapeno);
+ (void) kill(master, SIGUSR2);
+ Exit(X_REWRITE);
+}
+
+static long lastnonaddr; /* last DS_{INODE,CLRI,BITS} written */
+static long lastnonaddrm; /* and the mode thereof */
+/*
+ * dowrite -- the main body of the output writer process
+ */
+static void
+dowrite(cmd)
+ int cmd;
+{
+ struct bdesc *last =
+ &bufp[(NBUF*ntrec)-1]; /* last buffer in pool */
+ struct bdesc *bp = bufp; /* current buf in tape block */
+ struct bdesc *begin = bufp; /* first buf of tape block */
+ struct bdesc *end = bufp + (ntrec-1); /* last buf of tape block */
+ int siz; /* bytes written (block) */
+ int trecs; /* records written (block) */
+ long asize = 0; /* number of 0.1" units... */
+ /* ...written on current tape */
+ char *tp, *rbuf = NULL;
+ char *recmap = spcl.c_addr; /* current tape record map */
+ char *endmp; /* end of valid map data */
+ char *mp; /* current map entry */
+ union u_spcl *sp;
+
+ (void) signal(SIGXFSZ, onxfsz);
+
+ bzero((char *)&spcl, sizeof (spcl));
+ count = 0;
+
+ if (doingverify) {
+ rbuf = (char *)malloc((uint_t)writesize);
+ if (rbuf == 0) {
+ /* Restart from checkpoint */
+ (void) kill(master, SIGUSR2);
+ Exit(X_REWRITE);
+ }
+ }
+
+ for (;;) {
+ /* START: wait until all buffers in tape block are full */
+ if ((bp->b_flags & BUF_FULL) == 0) {
+ if (caught) { /* master signalled flush */
+ (void) sighold(SIGUSR1);
+ caught = 0;
+ /* signal ready */
+ (void) kill(master, SIGUSR1);
+ chkpt.sl_count = 0; /* signal not at EOT */
+ checkpoint(bp-1, cmd); /* send data */
+ (void) sigpause(SIGUSR1);
+ break;
+ }
+#ifdef INSTRUMENT
+ (*readmissp)++;
+#endif
+ nap(50);
+ continue;
+ }
+ if (bp < end) {
+ bp++;
+ continue;
+ }
+ /* END: wait until all buffers in tape block are full */
+
+ tp = begin->b_data;
+ (void) sighold(SIGUSR1);
+ if (host) {
+ if (!doingverify)
+ siz = rmtwrite(tp, writesize);
+ else if ((siz = rmtread(rbuf, writesize)) ==
+ writesize && bcmp(rbuf, tp, writesize))
+ siz = -1;
+ } else {
+ if (!doingverify)
+ siz = write(to, tp, writesize);
+ else if ((siz = read(to, rbuf, writesize)) ==
+ writesize && bcmp(rbuf, tp, writesize))
+ siz = -1;
+ if (siz < 0 && diskette && errno == ENOSPC)
+ siz = 0; /* really EOF */
+ }
+ (void) sigrelse(SIGUSR1);
+ if (siz < 0 ||
+ (pipeout && siz != writesize)) {
+ char buf[3000];
+
+ /*
+ * Isn't i18n wonderful?
+ */
+ if (doingverify) {
+ if (diskette)
+ (void) snprintf(buf, sizeof (buf),
+ gettext(
+ "Verification error %ld blocks into diskette %d\n"),
+ asize * 2, tapeno);
+ else if (tapeout)
+ (void) snprintf(buf, sizeof (buf),
+ gettext(
+ "Verification error %ld feet into tape %d\n"),
+ (cartridge ? asize/tracks :
+ asize)/120L,
+ tapeno);
+ else
+ (void) snprintf(buf, sizeof (buf),
+ gettext(
+ "Verification error %ld blocks into volume %d\n"),
+ asize * 2, tapeno);
+
+ } else {
+ if (diskette)
+ (void) snprintf(buf, sizeof (buf),
+ gettext(
+ "Write error %ld blocks into diskette %d\n"),
+ asize * 2, tapeno);
+ else if (tapeout)
+ (void) snprintf(buf, sizeof (buf),
+ gettext(
+ "Write error %ld feet into tape %d\n"),
+ (cartridge ? asize/tracks :
+ asize)/120L, tapeno);
+ else
+ (void) snprintf(buf, sizeof (buf),
+ gettext(
+ "Write error %ld blocks into volume %d\n"),
+ asize * 2, tapeno);
+ }
+
+ msg(buf);
+ /* Restart from checkpoint */
+#ifdef TDEBUG
+
+ /* XGETTEXT: #ifdef TDEBUG only */
+ msg(gettext("sending SIGUSR2 to pid %ld\n"), master);
+#endif
+ (void) kill(master, SIGUSR2);
+ Exit(X_REWRITE);
+ }
+ trecs = siz / tp_bsize;
+ if (diskette)
+ asize += trecs; /* asize == blocks written */
+ else
+ asize += (siz/density + tenthsperirg);
+ if (trecs)
+ chkpt.sl_firstrec++;
+ for (bp = begin; bp < begin + trecs; bp++) {
+ if ((arch >= 0) && (bp->b_flags & BUF_ARCHIVE)) {
+ if ((unsigned)atomic((int(*)())write, arch,
+ (char *)&bp->b_flags, sizeof (bp->b_flags))
+ != sizeof (bp->b_flags)) {
+ cmdwrterr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if (atomic((int(*)())write, arch, bp->b_data,
+ tp_bsize) != tp_bsize) {
+ cmdwrterr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+ if (bp->b_flags & BUF_SPCLREC) {
+ /*LINTED [bp->b_data is aligned]*/
+ sp = (union u_spcl *)bp->b_data;
+ if (sp->s_spcl.c_type != TS_ADDR) {
+ lastnonaddr = sp->s_spcl.c_type;
+ lastnonaddrm =
+ sp->s_spcl.c_dinode.di_mode;
+ if (sp->s_spcl.c_type != TS_TAPE)
+ chkpt.sl_offset = 0;
+ }
+ chkpt.sl_count = sp->s_spcl.c_count;
+ bcopy((char *)sp,
+ (char *)&spcl, sizeof (spcl));
+ mp = recmap;
+ endmp = &recmap[spcl.c_count];
+ count = 0;
+ } else {
+ chkpt.sl_offset++;
+ chkpt.sl_count--;
+ count++;
+ mp++;
+ }
+ /*
+ * Adjust for contiguous hole
+ */
+ for (; mp < endmp; mp++) {
+ if (*mp)
+ break;
+ chkpt.sl_offset++;
+ chkpt.sl_count--;
+ }
+ }
+ /*
+ * Check for end of tape
+ */
+ if (trecs < ntrec ||
+ (!pipeout && tsize > 0 && asize > tsize)) {
+ if (tapeout)
+ msg(gettext("End-of-tape detected\n"));
+ else
+ msg(gettext("End-of-file detected\n"));
+ (void) sighold(SIGUSR1);
+ caught = 0;
+ (void) kill(master, SIGUSR1); /* signal EOT */
+ checkpoint(--bp, cmd); /* send checkpoint data */
+ (void) sigpause(SIGUSR1);
+ break;
+ }
+ for (bp = begin; bp <= end; bp++)
+ bp->b_flags = BUF_EMPTY;
+ if (end + ntrec > last) {
+ bp = begin = bufp;
+ timeest(0, spcl.c_tapea);
+ } else
+ bp = begin = end+1;
+ end = begin + (ntrec-1);
+ }
+
+ if (rbuf != NULL)
+ free(rbuf);
+}
+
+/*
+ * Send checkpoint info back to master. This information
+ * consists of the current inode number, number of logical
+ * blocks written for that inode (or bitmap), the last logical
+ * block number written, the number of logical blocks written
+ * to this volume, the current dump state, and the current
+ * special record map.
+ */
+static void
+checkpoint(bp, cmd)
+ struct bdesc *bp;
+ int cmd;
+{
+ int state, type;
+ ino_t ino;
+
+ if (++bp >= &bufp[NBUF*ntrec])
+ bp = bufp;
+
+ /*
+ * If we are dumping files and the record following
+ * the last written to tape is a special record, use
+ * it to get an accurate indication of current state.
+ */
+ if ((bp->b_flags & BUF_SPCLREC) && (bp->b_flags & BUF_FULL) &&
+ lastnonaddr == TS_INODE) {
+ /*LINTED [bp->b_data is aligned]*/
+ union u_spcl *nextspcl = (union u_spcl *)bp->b_data;
+
+ if (nextspcl->s_spcl.c_type == TS_INODE) {
+ chkpt.sl_offset = 0;
+ chkpt.sl_count = 0;
+ } else if (nextspcl->s_spcl.c_type == TS_END) {
+ chkpt.sl_offset = 0;
+ chkpt.sl_count = 1; /* EOT indicator */
+ }
+ ino = nextspcl->s_spcl.c_inumber;
+ type = nextspcl->s_spcl.c_type;
+ } else {
+ /*
+ * If not, use what we have.
+ */
+ ino = spcl.c_inumber;
+ type = spcl.c_type;
+ }
+
+ switch (type) { /* set output state */
+ case TS_ADDR:
+ switch (lastnonaddr) {
+ case TS_INODE:
+ case TS_TAPE:
+ if ((lastnonaddrm & IFMT) == IFDIR ||
+ (lastnonaddrm & IFMT) == IFATTRDIR)
+ state = DS_DIRS;
+ else
+ state = DS_FILES;
+ break;
+ case TS_CLRI:
+ state = DS_CLRI;
+ break;
+ case TS_BITS:
+ state = DS_BITS;
+ break;
+ }
+ break;
+ case TS_INODE:
+ if ((spcl.c_dinode.di_mode & IFMT) == IFDIR ||
+ (spcl.c_dinode.di_mode & IFMT) == IFATTRDIR)
+ state = DS_DIRS;
+ else
+ state = DS_FILES;
+ break;
+ case 0: /* EOT on 1st record */
+ case TS_TAPE:
+ state = DS_START;
+ ino = UFSROOTINO;
+ break;
+ case TS_CLRI:
+ state = DS_CLRI;
+ break;
+ case TS_BITS:
+ state = DS_BITS;
+ break;
+ case TS_END:
+ if (spcl.c_type == TS_END)
+ state = DS_DONE;
+ else
+ state = DS_END;
+ break;
+ }
+
+ /*
+ * Checkpoint info to be processed by rollforward():
+ * The inode with which the next volume should begin
+ * The last inode number on this volume
+ * The last logical block number on this volume
+ * The current output state
+ * The offset within the current inode (already in sl_offset)
+ * The number of records left from last spclrec (in sl_count)
+ * The physical block the next vol begins with (in sl_firstrec)
+ */
+ chkpt.sl_inos = ino;
+ chkpt.sl_tapea = spcl.c_tapea + count;
+ chkpt.sl_state = state;
+
+ if ((unsigned)atomic((int(*)())write, cmd, (char *)&chkpt,
+ sizeof (chkpt)) != sizeof (chkpt)) {
+ cmdwrterr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ if ((unsigned)atomic((int(*)())write, cmd, (char *)&spcl,
+ sizeof (spcl)) != sizeof (spcl)) {
+ cmdwrterr();
+ dumpabort();
+ /*NOTREACHED*/
+ }
+#ifdef DEBUG
+ if (xflag) {
+ /* XGETTEXT: #ifdef DEBUG only */
+ msg(gettext("sent chkpt to master:\n"));
+ msg(" ino %u\n", chkpt.sl_inos);
+ msg(" 1strec %u\n", chkpt.sl_firstrec);
+ msg(" lastrec %u\n", chkpt.sl_tapea);
+ msg(" written %u\n", chkpt.sl_offset);
+ msg(" left %u\n", chkpt.sl_count);
+ msg(" state %d\n", chkpt.sl_state);
+ }
+#endif
+}
+
+/*
+ * Since a read from a pipe may not return all we asked for,
+ * or a write may not write all we ask if we get a signal,
+ * loop until the count is satisfied (or error).
+ */
+static ssize_t
+atomic(func, fd, buf, count)
+ int (*func)(), fd, count;
+ char *buf;
+{
+ ssize_t got = 0, need = count;
+
+ /* don't inherit random value if immediately get zero back from func */
+ errno = 0;
+ while (need > 0) {
+ got = (*func)(fd, buf, MIN(need, 4096));
+ if (got < 0 && errno == EINTR)
+ continue;
+ if (got <= 0)
+ break;
+ buf += got;
+ need -= got;
+ }
+ /* if we got what was asked for, return count, else failure (got) */
+ return ((need != 0) ? got : count);
+}
+
+void
+#ifdef __STDC__
+positiontape(char *msgbuf)
+#else
+positiontape(msgbuf)
+ char *msgbuf;
+#endif
+{
+ /* Static as never change, no need to waste stack space */
+ static struct mtget mt;
+ static struct mtop rew = { MTREW, 1 };
+ static struct mtop fsf = { MTFSF, 1 };
+ char *info = strdup(gettext("Positioning `%s' to file %ld\n"));
+ char *fail = strdup(gettext("Cannot position tape to file %d\n"));
+ int m;
+
+ /* gettext()'s return value is volatile, hence the strdup()s */
+
+ m = (access(tape, F_OK) == 0) ? 0 : O_CREAT;
+
+ /*
+ * To avoid writing tape marks at inappropriate places, we open the
+ * device read-only, position it, close it, and reopen it for writing.
+ */
+ while ((to = host ? rmtopen(tape, O_RDONLY) :
+ safe_device_open(tape, O_RDONLY|m, 0600)) < 0) {
+ if (autoload) {
+ if (!query_once(msgbuf, 1)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ } else {
+ if (!query(msgbuf)) {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+ }
+
+ if (host) {
+ if (rmtstatus(&mt) >= 0 &&
+ rmtioctl(MTREW, 1) >= 0 &&
+ filenum > 1) {
+ msg(info, dumpdev, filenum);
+ if (rmtioctl(MTFSF, filenum-1) < 0) {
+ msg(fail, filenum);
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+ rmtclose();
+ } else {
+ if (ioctl(to, MTIOCGET, &mt) >= 0 &&
+ ioctl(to, MTIOCTOP, &rew) >= 0 &&
+ filenum > 1) {
+ msg(info, dumpdev, filenum);
+ fsf.mt_count = filenum - 1;
+ if (ioctl(to, MTIOCTOP, &fsf) < 0) {
+ msg(fail, filenum);
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+ (void) close(to);
+ to = -1;
+ }
+
+ free(info);
+ free(fail);
+}
+
+static void
+#ifdef __STDC__
+cmdwrterr(void)
+#else
+cmdwrterr()
+#endif
+{
+ int saverr = errno;
+ msg(gettext("Error writing command pipe: %s\n"), strerror(saverr));
+}
+
+static void
+#ifdef __STDC__
+cmdrderr(void)
+#else
+cmdrderr()
+#endif
+{
+ int saverr = errno;
+ msg(gettext("Error reading command pipe: %s\n"), strerror(saverr));
+}