dumptape.c revision fe0e7ec4d916b05b52d8c7cc8a3e6a1b28e77b6f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2005 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 <assert.h>
#include <limits.h>
#define SLEEPMS 50
/*
* 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 {
long igen; /* inode generation number */
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 {
};
struct req {
short aflag; /* write data to archive process as well */
short tflag; /* begin new tape */
union reqdata {
} data;
};
static int reqsiz = 0; /* alloctape will initialize */
#define SLAVES 3
struct slaves {
int sl_slavefd; /* pipe from master to slave */
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 bdesc {
char *b_data; /* pointer to buffer data */
int b_flags; /* flags (see below) */
};
/*
* The following variables are in shared memory, and must be
*/
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
*/
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 int arch; /* fd of output archiver */
static int archivefd; /* fd of archive file (proper) */
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);
#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();
#endif
/*
* Allocate buffers and shared memory variables. Tape buffers are
* allocated on page boundaries for tape write() efficiency.
*/
void
#ifdef __STDC__
#else
#endif
alloctape(void)
{
int mapfd;
char *obuf;
int saverr;
int i, j;
if (!printsize)
/*
* set up shared memory seg for here and child
*/
if (mapfd == -1) {
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
*/
tapesize =
/* LINTED: pgoff fits into a size_t */
/* buffer descriptors */
#ifdef INSTRUMENT
#endif
/* shared variables */
+ (size_t)sizeof (int *)
dumpabort();
/*NOTREACHED*/
}
/*
* Buffers and buffer headers
*/
/* LINTED obuf and writesize are aligned */
/*
* Shared memory variables
*/
/* LINTED pointer alignment ok */
#ifdef INSTRUMENT
/*
* Debugging and instrumentation variables
*/
#endif
}
}
static void
#ifdef __STDC__
freetape(void)
#else
freetape()
#endif
{
return;
}
/*
* 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
tapeno = 0;
}
static void
#ifdef __STDC__
bufclear(void)
#else
bufclear()
#endif
{
int i;
"bufclear: current pointer out of range of shared memory\n"));
dumpabort();
/*NOTREACHED*/
}
/* ANSI string catenation, to shut cstyle up */
"out of range of buffer\naddresses (0x%x - 0x%x)\n"),
dumpabort();
/*NOTREACHED*/
}
}
/*
* 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
setuparchive(void)
{
int cmd[2];
char *data;
char *errmsg;
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) {
if (archivefd < 0) {
dumpabort();
/*NOTREACHED*/
}
"Cannot position archive file `%s' : %s\n"),
dumpabort();
/*NOTREACHED*/
}
"Cannot truncate archive file `%s' : %s\n"),
dumpabort();
/*NOTREACHED*/
}
}
return (0);
}
return (0);
}
if (pid > 0) {
/* parent process */
return (pid);
}
/*
* child process
*/
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
#endif
freeino(); /* release unneeded resources */
freetape();
}
}
for (;;) {
sizeof (flags));
break;
!= tp_bsize) {
if (size != -1) {
"Output truncated"));
errmsg = "";
} else {
}
/* cast to keep lint&printf happy */
"Cannot write archive file `%s' at offset %lld: %s\n"),
errmsg);
"Archive file will be deleted, dump will continue\n"));
punt++;
}
}
} else {
break;
}
}
if (archive) {
archivefd = -1;
}
if (punt) {
(void) unlink(archivefile);
}
/* NOTREACHED */
return (0);
}
/*
* Start a process to read the output buffers and write the data
* to the output device.
*/
static pid_t
setupwriter(void)
{
int cmd[2];
int saverr;
caught = 0;
return (0);
}
return (0);
}
if (pid > 0) {
/*
* Parent process
*/
return (pid);
}
/*
* Child (writer) process
*/
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
#endif
child_chdir();
freeino(); /* release unneeded resources */
}
}
fi = -1;
if (arch >= 0) {
arch = -1;
}
/* NOTREACHED */
return (0);
}
void
#ifdef __STDC__
spclrec(void)
#else
spclrec()
#endif
{
int s, i;
int flags = BUF_SPCLREC;
/* LINTED: result fits in a short */
/* LINTED: result fits in a short */
}
/*
* Only TS_INODEs should have short metadata, if this
* isn't such a spclrec, clear the metadata flag and
* the c_shadow contents.
*/
}
if (doingactive)
flags = BUF_SPCLREC;
/* LINTED for now, max inode # is 2**31 (ufs max size is 4TB) */
spcl.c_checksum = 0;
s = CHECKSUM;
assert((i%8) == 0);
i /= 8;
do {
} while (--i > 0);
spcl.c_checksum = s;
}
/*
* Fill appropriate buffer
*/
void
{
"taprec: Unexpected buffer size, expected %d, got %d.\n"),
dumpabort();
/*NOTREACHED*/
}
nap(10);
}
if (dumptoarchive)
flags |= BUF_ARCHIVE;
/* no locking as we assume only one reader and one writer active */
(*tapea)++;
}
void
{
if (dumptoarchive) {
/* LINTED: result fits in a short */
}
}
/*ARGSUSED*/
static void
{
char buf[3000];
if (pipeout) {
dumpabort();
/* NOTREACHED */
}
if (!doingverify) {
gettext("Do you want to restart?: (\"yes\" or \"no\") "));
dumpabort();
/*NOTREACHED*/
}
/* ANSI string catenation, to shut cstyle up */
"it is rewound,\nreplace the faulty tape "
"with a new one;\nthis dump volume will "
"be rewritten.\n"));
}
} else {
"Do you want to rewrite?: (\"yes\" or \"no\") "));
dumpabort();
/*NOTREACHED*/
}
"This tape will be rewritten and then verified\n"));
}
killall();
trewind();
}
/*
* 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
{
int wasactive;
sizeof (wasactive)) {
cmdrderr();
dumpabort();
/*NOTREACHED*/
}
if (wasactive) {
active++;
"The file at inode `%lu' was active and will be recopied\n"),
/* LINTED: 32-bit to 8-bit assignment ok */
}
}
if (dumptoarchive) {
/* LINTED: result fits in a short */
}
if (fn)
cmdwrterr();
dumpabort();
/*NOTREACHED*/
}
++recsout;
nextslave();
}
void
{
/* LINTED for now, max inode # is 2**31 (ufs max size is 1TB) */
}
static void
#ifdef __STDC__
nextslave(void)
#else
#endif
{
rotor = 0;
}
}
void
#ifdef __STDC__
flushcmds(void)
#else
#endif
{
int i;
int wasactive;
/*
* Retrieve all slave status
*/
rotor = 0;
}
sizeof (wasactive)) {
cmdrderr();
dumpabort();
/*NOTREACHED*/
}
if (wasactive) {
active++;
"inode %d was active and will be recopied\n"),
/* LINTED: 32-bit to 8-bit assignment ok */
}
nextslave();
}
}
void
#ifdef __STDC__
flusht(void)
#else
flusht()
#endif
{
(void) sigemptyset(&block_set);
/*NOTREACHED*/
}
/*
* 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(int sig)
{
int status;
/*
* 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.
*/
cmdrderr();
dumpabort();
/*NOTREACHED*/
}
TP_BSIZE_MIN) != TP_BSIZE_MIN) {
cmdrderr();
dumpabort();
/*NOTREACHED*/
}
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.
*/
}
lf_archoffset = 0LL;
/*NOTREACHED*/
}
if (leftover) {
}
if (writepid) {
writer = -1;
}
if (archivepid) {
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
(long)archivepid, status);
#endif
archivepid = 0;
}
/*
* Checkpoint archive file
*/
if (!doingverify && archive) {
if (lf_archoffset < 0) {
dumpabort();
/*NOTREACHED*/
}
archivefd = -1;
}
"Tape too short: changing volumes and restarting\n"));
reset();
}
if (!pipeout) {
if (verify && !doingverify)
trewind();
else {
close_rewind();
changevol();
}
}
otape(0);
/*NOTREACHED*/
}
static void
{
}
/*ARGSUSED*/
static void
{
/*NOTREACHED*/
}
void
#ifdef __STDC__
nextdevice(void)
#else
#endif
{
char *cp;
return;
if (diskette) {
}
*tape++ = 0;
if (cp != (char *)0)
cp++;
else
} else
/*
* 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.
*/
/* LINTED: dumpdev is not NULL */
}
/*LINTED [cast to smaller integer]*/
/* LINTED unsigned -> signed cast ok */
else
}
/*
* 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(int f)
{
char *c;
int unit;
int rewind;
if (host) {
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.
*/
rewind = 0;
(unit & MT_NOREWIND))
rewind = 0;
else
rewind = 1;
} else {
"Cannot obtain status of output device `%s'\n"),
tape);
dumpabort();
/*NOTREACHED*/
}
}
return (rewind);
}
static void
#ifdef __STDC__
just_rewind(void)
#else
#endif
{
if (slavep->sl_slavefd >= 0) {
}
}
/* 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) {
} else {
}
}
/*
* Guess whether the tape is rewinding so we can tell
* the operator if it's going to take a long time.
*/
/* tape is probably rewinding */
}
}
void
#ifdef __STDC__
trewind(void)
#else
trewind()
#endif
{
close_rewind();
} else {
just_rewind();
if (host)
rmtclose();
else {
to = -1;
}
}
}
void
#ifdef __STDC__
close_rewind(void)
#else
#endif
{
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).
*/
/* tape is probably rewinding */
}
if (host) {
rmtclose();
} else {
if (diskette)
}
to = -1;
}
}
void
#ifdef __STDC__
changevol(void)
#else
#endif
{
/*CONSTANTCONDITION*/
filenum = 1;
nextdevice();
if (host) {
if (cp == (char *)0)
else
cp++;
dumpabort();
/*NOTREACHED*/
}
}
/*
* 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.
*/
}
/*
* 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).
*/
if (host) {
rmtclose();
return;
}
} else {
int f, m;
if ((f = doingverify ?
>= 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);
}
} else
"Is the new volume (%s) mounted on `%s' and ready to go?: %s"),
"Do you want to abort dump?: (\"yes\" or \"no\") "));
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
{
char buf[3000];
int status;
if (verify) {
if (doingverify)
doingverify = 0;
else
}
/*
* All signals are inherited...
*/
if (childpid < 0) {
"Context-saving fork failed in parent %ld\n"),
(long)parentpid);
}
if (childpid != 0) {
/*
* PARENT:
* save the context by waiting
* until the child doing all of the work returns.
* let the child catch user interrupts
*/
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
"Volume: %d; parent process: %ld child process %ld\n"),
#endif /* TDEBUG */
for (;;) {
break;
"Parent %ld waiting for child %ld had another child %ld return\n"),
}
if (WIFSIGNALED(status)) {
} else
#ifdef TDEBUG
switch (status) {
case X_FINOK:
/* XGETTEXT: #ifdef TDEBUG only */
"Child %ld finishes X_FINOK\n"), (long)childpid);
break;
case X_ABORT:
/* XGETTEXT: #ifdef TDEBUG only */
"Child %ld finishes X_ABORT\n"), (long)childpid);
break;
case X_REWRITE:
/* XGETTEXT: #ifdef TDEBUG only */
"Child %ld finishes X_REWRITE\n"), (long)childpid);
break;
case X_RESTART:
/* XGETTEXT: #ifdef TDEBUG only */
"Child %ld finishes X_RESTART\n"), (long)childpid);
break;
case X_VERIFY:
/* XGETTEXT: #ifdef TDEBUG only */
"Child %ld finishes X_VERIFY\n"), (long)childpid);
break;
default:
/* XGETTEXT: #ifdef TDEBUG only */
break;
}
#endif /* TDEBUG */
switch (status) {
case X_FINOK:
/* wait for children */
while (waitpid(0, (int *)0, 0) >= 0)
/*LINTED [empty body]*/
continue;
/*NOTREACHED*/
case 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) {
}
if (!offline)
autoload = 0;
changevol();
return;
/* NOTREACHED */
default:
/*NOTREACHED*/
}
/*NOTREACHED*/
} else { /* we are the child; just continue */
child_chdir();
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
"Child on Volume %d has parent %ld, my pid = %ld\n"),
#endif
"Cannot open `%s'. Do you want to retry the open?: (\"yes\" or \"no\") "),
dumpdev);
if (doingverify) {
/* 1 for stdout */
pipeout ? 1 :
if (autoload) {
dumpabort();
/*NOTREACHED*/
}
} else {
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) {
else
} else {
else
}
} 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.
*/
/*
* The tape is rewinding;
* we're screwed.
*/
"Cannot position tape using rewind device!\n"));
dumpabort();
/*NOTREACHED*/
} else {
(void) alarm(15);
}
(void) sleep(10);
(void) alarm(0);
(struct sigvec *)0);
} else {
int m;
/*
* Only verify the tape label if label
* verification is on and we are at BOT
*/
if (pipeout)
to = 1;
< 0)
dumpabort();
/*NOTREACHED*/
}
}
}
if (!pipeout) {
/*
* Make sure the tape is positioned
* where it is supposed to be
*/
"Warning - tape positioning error!\n\
\t%s current file %ld, should be %ld\n"),
dumpailing();
}
}
tapeno++; /* current tape sequence */
enslave(); /* Share tape buffers with slaves */
#ifdef DEBUG
if (xflag) {
/* XGETTEXT: #ifdef DEBUG only */
}
#endif
if (leftover == 0) {
spclrec();
newtape = 0;
} else
newtape++; /* new volume indication */
if (doingverify) {
} else if (tapeno > 1) {
"Volume %d begins with blocks from inode %lu\n"),
}
(void) time(tstart_writing);
}
}
void
#ifdef __STDC__
dumpabort(void)
#else
#endif
{
/*
* signal master to call dumpabort
*/
else {
killall();
if (archivefile)
(void) unlink(archivefile);
}
}
void
dumpailing(void)
{
"Do you want to attempt to continue? (\"yes\" or \"no\") "))) {
dumpabort();
/*NOTREACHED*/
}
}
void
{
/*
* Clean up message system
*/
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
#endif /* TDEBUG */
}
static void
#ifdef __STDC__
killall(void)
#else
killall()
#endif
{
if (slavep->sl_slavepid > 0) {
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
(long)slavep->sl_slavepid);
#endif
}
if (writepid) {
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
#endif
}
if (archivepid) {
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
#endif
}
}
/*ARGSUSED*/
static void
{
caught++;
}
/*ARGSUSED*/
static void
{
}
static void
#ifdef __STDC__
enslave(void)
#else
enslave()
#endif
{
int i;
int saverr;
/*
* slave sends SIGTERM on dumpabort
*/
totalrecsout += recsout;
caught = 0;
recsout = 0;
rotor = 0;
bufclear();
for (i = 0; i < SLAVES; i++) {
"Cannot create pipe for slave process: %s\n"),
dumpabort();
/*NOTREACHED*/
}
dumpabort();
/*NOTREACHED*/
}
child_chdir();
freeino(); /* release unneeded resources */
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
#endif
/* Closes cmd[1] as a side-effect */
slavep++)
if (slavep->sl_slavefd >= 0) {
}
to = -1;
if (fi < 0) {
"Cannot open dump device `%s': %s\n"),
dumpabort();
/*NOTREACHED*/
}
cmdrderr();
dumpabort();
/*NOTREACHED*/
}
}
/* Parent continues here */
}
if (archive) {
archivepid = setuparchive();
if (!archivepid) {
dumpabort();
/*NOTREACHED*/
}
}
writepid = setupwriter();
if (!writepid) {
dumpabort();
/*NOTREACHED*/
}
if (arch >= 0) {
arch = -1;
}
/* Tell each slave who follows it */
for (i = 0; i < SLAVES; i++) {
sizeof (int)) != sizeof (int)) {
cmdwrterr();
dumpabort();
/*NOTREACHED*/
}
}
master = 0;
}
static void
#ifdef __STDC__
wait_our_turn(void)
#else
#endif
{
if (!caught) {
#ifdef INSTRUMENT
(*idle)++;
#endif
}
caught = 0;
}
static void
{
ulong_t i;
int notactive = 0;
/*CONSTANTCONDITION*/
if (p->br_dblk) {
} else {
sizeof (spcl));
}
if (p->br_dblk) {
i > 0;
/* LINTED character pointers aren't signed */
/* LINTED unsigned to signed conversion ok */
}
} else
spclrec();
/*
* Note that we lie about file activity since we don't
* check for it.
*/
cmdwrterr();
dumpabort();
/*NOTREACHED*/
}
}
}
static int count; /* tape blocks written since last spclrec */
/*ARGSUSED*/
static void
{
tapeno);
}
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
{
int siz; /* bytes written (block) */
int trecs; /* records written (block) */
long asize = 0; /* number of 0.1" units... */
/* ...written on current tape */
char *endmp; /* end of valid map data */
char *mp; /* current map entry */
count = 0;
if (doingverify) {
if (rbuf == 0) {
/* Restart from checkpoint */
}
}
for (;;) {
/* START: wait until all buffers in tape block are full */
if (caught) { /* master signalled flush */
caught = 0;
/* signal ready */
break;
}
#ifdef INSTRUMENT
(*readmissp)++;
#endif
nap(50);
continue;
}
bp++;
continue;
}
/* END: wait until all buffers in tape block are full */
if (host) {
if (!doingverify)
siz = -1;
} else {
if (!doingverify)
siz = -1;
siz = 0; /* really EOF */
}
if (siz < 0 ||
char buf[3000];
/*
* Isn't i18n wonderful?
*/
if (doingverify) {
if (diskette)
"Verification error %ld blocks into diskette %d\n"),
else if (tapeout)
"Verification error %ld feet into tape %d\n"),
asize)/120L,
tapeno);
else
"Verification error %ld blocks into volume %d\n"),
} else {
if (diskette)
"Write error %ld blocks into diskette %d\n"),
else if (tapeout)
"Write error %ld feet into tape %d\n"),
else
"Write error %ld blocks into volume %d\n"),
}
/* Restart from checkpoint */
#ifdef TDEBUG
/* XGETTEXT: #ifdef TDEBUG only */
#endif
}
if (diskette)
else
if (trecs)
chkpt.sl_firstrec++;
cmdwrterr();
dumpabort();
/*NOTREACHED*/
}
cmdwrterr();
dumpabort();
/*NOTREACHED*/
}
}
/*LINTED [bp->b_data is aligned]*/
}
count = 0;
} else {
count++;
mp++;
}
/*
* Adjust for contiguous hole
*/
if (*mp)
break;
}
}
/*
* Check for end of tape
*/
if (tapeout)
else
caught = 0;
break;
}
} else
}
}
/*
* 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
{
/*
* 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.
*/
lastnonaddr == TS_INODE) {
/*LINTED [bp->b_data is aligned]*/
}
} else {
/*
* If not, use what we have.
*/
}
switch (type) { /* set output state */
case TS_ADDR:
switch (lastnonaddr) {
case TS_INODE:
case TS_TAPE:
else
break;
case TS_CLRI:
break;
case TS_BITS:
break;
}
break;
case TS_INODE:
else
break;
case 0: /* EOT on 1st record */
case TS_TAPE:
ino = UFSROOTINO;
break;
case TS_CLRI:
break;
case TS_BITS:
break;
case TS_END:
else
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)
*/
cmdwrterr();
dumpabort();
/*NOTREACHED*/
}
cmdwrterr();
dumpabort();
/*NOTREACHED*/
}
#ifdef DEBUG
if (xflag) {
/* XGETTEXT: #ifdef DEBUG only */
}
#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
{
/* don't inherit random value if immediately get zero back from func */
errno = 0;
while (need > 0) {
continue;
if (got <= 0)
break;
}
/* if we got what was asked for, return count, else failure (got) */
}
void
#ifdef __STDC__
positiontape(char *msgbuf)
#else
positiontape(char *msgbuf)
#endif
{
/* Static as never change, no need to waste stack space */
int m;
/* gettext()'s return value is volatile, hence the strdup()s */
/*
* To avoid writing tape marks at inappropriate places, we open the
* device read-only, position it, close it, and reopen it for writing.
*/
if (autoload) {
dumpabort();
/*NOTREACHED*/
}
} else {
dumpabort();
/*NOTREACHED*/
}
}
}
if (host) {
filenum > 1) {
dumpabort();
/*NOTREACHED*/
}
}
rmtclose();
} else {
filenum > 1) {
dumpabort();
/*NOTREACHED*/
}
}
to = -1;
}
}
static void
#ifdef __STDC__
cmdwrterr(void)
#else
#endif
{
}
static void
#ifdef __STDC__
cmdrderr(void)
#else
cmdrderr()
#endif
{
}