summaryrefslogtreecommitdiff
path: root/sbin/dump
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sbin/dump
initial import of NetBSD tree
Diffstat (limited to 'sbin/dump')
-rw-r--r--sbin/dump/Makefile26
-rw-r--r--sbin/dump/dump.8355
-rw-r--r--sbin/dump/dump.h215
-rw-r--r--sbin/dump/dumprmt.c380
-rw-r--r--sbin/dump/itime.c277
-rw-r--r--sbin/dump/main.c592
-rw-r--r--sbin/dump/optr.c544
-rw-r--r--sbin/dump/pathnames.h44
-rw-r--r--sbin/dump/tape.c868
-rw-r--r--sbin/dump/traverse.c616
-rw-r--r--sbin/dump/unctime.c112
11 files changed, 4029 insertions, 0 deletions
diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile
new file mode 100644
index 00000000000..d49fef2b75a
--- /dev/null
+++ b/sbin/dump/Makefile
@@ -0,0 +1,26 @@
+# $NetBSD: Makefile,v 1.16 1995/03/18 14:54:53 cgd Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+# dump.h header file
+# itime.c reads /etc/dumpdates
+# main.c driver
+# optr.c operator interface
+# dumprmt.c handles remote tape via rmt(8)
+# tape.c handles the mag tape and opening/closing
+# traverse.c traverses the file system
+# unctime.c undo ctime
+#
+# DEBUG use local directory to find ddate and dumpdates
+# TDEBUG trace out the process forking
+
+PROG= dump
+LINKS= ${BINDIR}/dump ${BINDIR}/rdump
+CFLAGS+=-DRDUMP
+SRCS= itime.c main.c optr.c dumprmt.c tape.c traverse.c unctime.c
+BINOWN= root
+BINGRP= tty
+BINMODE=6555
+MAN= dump.8
+MLINKS+=dump.8 rdump.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8
new file mode 100644
index 00000000000..4f0d22e6209
--- /dev/null
+++ b/sbin/dump/dump.8
@@ -0,0 +1,355 @@
+.\" $NetBSD: dump.8,v 1.12 1995/03/18 14:54:55 cgd Exp $
+.\"
+.\" Copyright (c) 1980, 1991, 1993
+.\" Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)dump.8 8.1 (Berkeley) 6/16/93
+.\"
+.Dd June 16, 1993
+.Dt DUMP 8
+.Os BSD 4
+.Sh NAME
+.Nm dump
+.Nd filesystem backup
+.Sh SYNOPSIS
+.Nm dump
+.Op Fl 0123456789cnu
+.Op Fl B Ar records
+.Op Fl b Ar blocksize
+.Op Fl d Ar density
+.Op Fl f Ar file
+.Op Fl h Ar level
+.Op Fl s Ar feet
+.Op Fl T Ar date
+.Ar filesystem
+.Nm dump
+.Op Fl W Li \&| Fl w
+.Pp
+.in -\\n(iSu
+(The
+.Bx 4.3
+option syntax is implemented for backward compatibility, but
+is not documented here.)
+.Sh DESCRIPTION
+.Nm Dump
+examines files
+on a filesystem
+and determines which files
+need to be backed up. These files
+are copied to the given disk, tape or other
+storage medium for safe keeping (see the
+.Fl f
+option below for doing remote backups).
+A dump that is larger than the output medium is broken into
+multiple volumes.
+On most media the size is determined by writing until an
+end-of-media indication is returned.
+On media that cannot reliably return an end-of-media indication
+(such as some cartridge tape drives)
+each volume is of a fixed size;
+the actual size is determined by the tape size and density and/or
+block count options below.
+By default, the same output file name is used for each volume
+after prompting the operator to change media.
+.Pp
+The following options are supported by
+.Nm dump :
+.Bl -tag -width Ds
+.It Fl 0\-9
+Dump levels.
+A level 0, full backup,
+guarantees the entire file system is copied
+(but see also the
+.Fl h
+option below).
+A level number above 0,
+incremental backup,
+tells dump to
+copy all files new or modified since the
+last dump of the same or lower level.
+The default level is 9.
+.It Fl B Ar records
+The number of dump records per volume.
+This option overrides the calculation of tape size
+based on length and density.
+.It Fl b Ar blocksize
+The number of kilobytes per dump record.
+.It Fl c
+Modify the calculation of the default density and tape size to be more
+appropriate for cartridge tapes.
+.It Fl d Ar density
+Set tape density to
+.Ar density .
+The default is 1600BPI.
+.It Fl f Ar file
+Write the backup to
+.Ar file ;
+.Ar file
+may be a special device file
+like
+.Pa /dev/rmt12
+(a tape drive),
+.Pa /dev/rsd1c
+(a disk drive),
+an ordinary file,
+or
+.Ql Fl
+(the standard output).
+Multiple file names may be given as a single argument separated by commas.
+Each file will be used for one dump volume in the order listed;
+if the dump requires more volumes than the number of names given,
+the last file name will used for all remaining volumes after prompting
+for media changes.
+If the name of the file is of the form
+.Dq host:file ,
+or
+.Dq user@host:file ,
+.Nm dump
+writes to the named file on the remote host using
+.Xr rmt 8 .
+.It Fl h Ar level
+Honor the user
+.Dq nodump
+flag
+.Dp Dv UF_NODUMP
+only for dumps at or above the given
+.Ar level .
+The default honor level is 1,
+so that incremental backups omit such files
+but full backups retain them.
+.It Fl n
+Whenever
+.Nm dump
+requires operator attention,
+notify all operators in the group
+.Dq operator
+by means similar to a
+.Xr wall 1 .
+.It Fl s Ar feet
+Attempt to calculate the amount of tape needed
+at a particular density.
+If this amount is exceeded,
+.Nm dump
+prompts for a new tape.
+It is recommended to be a bit conservative on this option.
+The default tape length is 2300 feet.
+.It Fl T Ar date
+Use the specified date as the starting time for the dump
+instead of the time determined from looking in
+.Pa /etc/dumpdates .
+The format of date is the same as that of
+.Xr ctime 3 .
+This option is useful for automated dump scripts that wish to
+dump over a specific period of time.
+The
+.Fl T
+flag is mutually exclusive from the
+.Fl u
+flag.
+.It Fl u
+Update the file
+.Pa /etc/dumpdates
+after a successful dump.
+The format of
+.Pa /etc/dumpdates
+is readable by people, consisting of one
+free format record per line:
+filesystem name,
+increment level
+and
+.Xr ctime 3
+format dump date.
+There may be only one entry per filesystem at each level.
+The file
+.Pa /etc/dumpdates
+may be edited to change any of the fields,
+if necessary.
+.It Fl W
+.Nm Dump
+tells the operator what file systems need to be dumped.
+This information is gleaned from the files
+.Pa /etc/dumpdates
+and
+.Pa /etc/fstab .
+The
+.Fl W
+flag causes
+.Nm dump
+to print out, for each file system in
+.Pa /etc/dumpdates
+the most recent dump date and level,
+and highlights those file systems that should be dumped.
+If the
+.Fl W
+flag is set, all other options are ignored, and
+.Nm dump
+exits immediately.
+.It Fl w
+Is like W, but prints only those filesystems which need to be dumped.
+.El
+.Pp
+.Nm Dump
+requires operator intervention on these conditions:
+end of tape,
+end of dump,
+tape write error,
+tape open error or
+disk read error (if there are more than a threshold of 32).
+In addition to alerting all operators implied by the
+.Fl n
+flag,
+.Nm dump
+interacts with the operator on
+.Nm dump Ns 's
+control terminal at times when
+.Nm dump
+can no longer proceed,
+or if something is grossly wrong.
+All questions
+.Nm dump
+poses
+.Em must
+be answered by typing
+.Dq yes
+or
+.Dq no ,
+appropriately.
+.Pp
+Since making a dump involves a lot of time and effort for full dumps,
+.Nm dump
+checkpoints itself at the start of each tape volume.
+If writing that volume fails for some reason,
+.Nm dump
+will,
+with operator permission,
+restart itself from the checkpoint
+after the old tape has been rewound and removed,
+and a new tape has been mounted.
+.Pp
+.Nm Dump
+tells the operator what is going on at periodic intervals,
+including usually low estimates of the number of blocks to write,
+the number of tapes it will take, the time to completion, and
+the time to the tape change.
+The output is verbose,
+so that others know that the terminal
+controlling
+.Nm dump
+is busy,
+and will be for some time.
+.Pp
+In the event of a catastrophic disk event, the time required
+to restore all the necessary backup tapes or files to disk
+can be kept to a minimum by staggering the incremental dumps.
+An efficient method of staggering incremental dumps
+to minimize the number of tapes follows:
+.Bl -bullet -offset indent
+.It
+Always start with a level 0 backup, for example:
+.Bd -literal -offset indent
+/sbin/dump -0u -f /dev/nrst1 /usr/src
+.Ed
+.Pp
+This should be done at set intervals, say once a month or once every two months,
+and on a set of fresh tapes that is saved forever.
+.It
+After a level 0, dumps of active file
+systems are taken on a daily basis,
+using a modified Tower of Hanoi algorithm,
+with this sequence of dump levels:
+.Bd -literal -offset indent
+3 2 5 4 7 6 9 8 9 9 ...
+.Ed
+.Pp
+For the daily dumps, it should be possible to use a fixed number of tapes
+for each day, used on a weekly basis.
+Each week, a level 1 dump is taken, and
+the daily Hanoi sequence repeats beginning with 3.
+For weekly dumps, another fixed set of tapes per dumped file system is
+used, also on a cyclical basis.
+.El
+.Pp
+After several months or so, the daily and weekly tapes should get
+rotated out of the dump cycle and fresh tapes brought in.
+.Sh FILES
+.Bl -tag -width /etc/dumpdates -compact
+.It Pa /dev/rmt8
+default tape unit to dump to
+.It Pa /etc/dumpdates
+dump date records
+.It Pa /etc/fstab
+dump table: file systems and frequency
+.It Pa /etc/group
+to find group
+.Em operator
+.El
+.Sh SEE ALSO
+.Xr restore 8 ,
+.Xr rmt 8 ,
+.Xr dump 5 ,
+.Xr fstab 5
+.Sh DIAGNOSTICS
+Many, and verbose.
+.Pp
+Dump exits with zero status on success.
+Startup errors are indicated with an exit code of 1;
+abnormal termination is indicated with an exit code of 3.
+.Sh BUGS
+Fewer than 32 read errors on the filesystem are ignored.
+.Pp
+Each reel requires a new process, so parent processes for
+reels already written just hang around until the entire tape
+is written.
+.Pp
+.Nm Dump
+with the
+.Fl W
+or
+.Fl w
+flags does not report filesystems that have never been recorded
+in
+.Pa /etc/dumpdates ,
+even if listed in
+.Pa /etc/fstab .
+.Pp
+It would be nice if
+.Nm dump
+knew about the dump sequence,
+kept track of the tapes scribbled on,
+told the operator which tape to mount when,
+and provided more assistance
+for the operator running
+.Xr restore .
+.Sh HISTORY
+A
+.Nm dump
+command appeared in Version 6 AT&T UNIX.
diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h
new file mode 100644
index 00000000000..e5a8224a58a
--- /dev/null
+++ b/sbin/dump/dump.h
@@ -0,0 +1,215 @@
+/* $NetBSD: dump.h,v 1.9 1995/03/18 14:54:57 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dump.h 8.1 (Berkeley) 6/5/93
+ */
+
+#define MAXINOPB (MAXBSIZE / sizeof(struct dinode))
+#define MAXNINDIR (MAXBSIZE / sizeof(daddr_t))
+
+/*
+ * Dump maps used to describe what is to be dumped.
+ */
+int mapsize; /* size of the state maps */
+char *usedinomap; /* map of allocated inodes */
+char *dumpdirmap; /* map of directories to be dumped */
+char *dumpinomap; /* map of files to be dumped */
+/*
+ * Map manipulation macros.
+ */
+#define SETINO(ino, map) \
+ map[(u_int)((ino) - 1) / NBBY] |= 1 << ((u_int)((ino) - 1) % NBBY)
+#define CLRINO(ino, map) \
+ map[(u_int)((ino) - 1) / NBBY] &= ~(1 << ((u_int)((ino) - 1) % NBBY))
+#define TSTINO(ino, map) \
+ (map[(u_int)((ino) - 1) / NBBY] & (1 << ((u_int)((ino) - 1) % NBBY)))
+
+/*
+ * All calculations done in 0.1" units!
+ */
+char *disk; /* name of the disk file */
+char *tape; /* name of the tape file */
+char *dumpdates; /* name of the file containing dump date information*/
+char *temp; /* name of the file for doing rewrite of dumpdates */
+char lastlevel; /* dump level of previous dump */
+char level; /* dump level of this dump */
+int uflag; /* update flag */
+int diskfd; /* disk file descriptor */
+int tapefd; /* tape file descriptor */
+int pipeout; /* true => output to standard output */
+ino_t curino; /* current inumber; used globally */
+int newtape; /* new tape flag */
+int density; /* density in 0.1" units */
+long tapesize; /* estimated tape size, blocks */
+long tsize; /* tape size in 0.1" units */
+long asize; /* number of 0.1" units written on current tape */
+int etapes; /* estimated number of tapes */
+int nonodump; /* if set, do not honor UF_NODUMP user flags */
+
+int notify; /* notify operator flag */
+int blockswritten; /* number of blocks written on current tape */
+int tapeno; /* current tape number */
+time_t tstart_writing; /* when started writing the first tape block */
+struct fs *sblock; /* the file system super block */
+char sblock_buf[MAXBSIZE];
+long dev_bsize; /* block size of underlying disk device */
+int dev_bshift; /* log2(dev_bsize) */
+int tp_bshift; /* log2(TP_BSIZE) */
+
+#ifndef __P
+#include <sys/cdefs.h>
+#endif
+
+/* operator interface functions */
+void broadcast __P((char *message));
+void lastdump __P((int arg)); /* int should be char */
+void msg __P((const char *fmt, ...));
+void msgtail __P((const char *fmt, ...));
+int query __P((char *question));
+void quit __P((const char *fmt, ...));
+void set_operators __P((void));
+void timeest __P((void));
+time_t unctime __P((char *str));
+
+/* mapping rouintes */
+struct dinode;
+long blockest __P((struct dinode *dp));
+int mapfiles __P((ino_t maxino, long *tapesize));
+int mapdirs __P((ino_t maxino, long *tapesize));
+
+/* file dumping routines */
+void blksout __P((daddr_t *blkp, int frags, ino_t ino));
+void bread __P((daddr_t blkno, char *buf, int size));
+void dumpino __P((struct dinode *dp, ino_t ino));
+void dumpmap __P((char *map, int type, ino_t ino));
+void writeheader __P((ino_t ino));
+
+/* tape writing routines */
+int alloctape __P((void));
+void close_rewind __P((void));
+void dumpblock __P((daddr_t blkno, int size));
+void startnewtape __P((int top));
+void trewind __P((void));
+void writerec __P((char *dp, int isspcl));
+
+__dead void Exit __P((int status));
+void dumpabort __P((int signo));
+void getfstab __P((void));
+
+char *rawname __P((char *cp));
+struct dinode *getino __P((ino_t inum));
+
+/* rdump routines */
+#ifdef RDUMP
+void rmtclose __P((void));
+int rmthost __P((char *host));
+int rmtopen __P((char *tape, int mode));
+int rmtwrite __P((char *buf, int count));
+#endif /* RDUMP */
+
+void interrupt __P((int signo)); /* in case operator bangs on console */
+
+/*
+ * Exit status codes
+ */
+#define X_FINOK 0 /* normal exit */
+#define X_REWRITE 2 /* restart writing from the check point */
+#define X_ABORT 3 /* abort dump; don't attempt checkpointing */
+
+#define OPGRENT "operator" /* group entry to notify */
+#define DIALUP "ttyd" /* prefix for dialups */
+
+struct fstab *fstabsearch __P((char *key)); /* search fs_file and fs_spec */
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+/*
+ * The contents of the file _PATH_DUMPDATES is maintained both on
+ * a linked list, and then (eventually) arrayified.
+ */
+struct dumpdates {
+ char dd_name[NAME_MAX+3];
+ char dd_level;
+ time_t dd_ddate;
+};
+struct dumptime {
+ struct dumpdates dt_value;
+ struct dumptime *dt_next;
+};
+struct dumptime *dthead; /* head of the list version */
+int nddates; /* number of records (might be zero) */
+int ddates_in; /* we have read the increment file */
+struct dumpdates **ddatev; /* the arrayfied version */
+void initdumptimes __P((void));
+void getdumptime __P((void));
+void putdumptime __P((void));
+#define ITITERATE(i, ddp) \
+ for (ddp = ddatev[i = 0]; i < nddates; ddp = ddatev[++i])
+
+void sig __P((int signo));
+
+/*
+ * Compatibility with old systems.
+ */
+#ifdef COMPAT
+#include <sys/file.h>
+#define strchr(a,b) index(a,b)
+#define strrchr(a,b) rindex(a,b)
+extern char *strdup(), *ctime();
+extern int read(), write();
+extern int errno;
+#endif
+
+#ifndef _PATH_UTMP
+#define _PATH_UTMP "/etc/utmp"
+#endif
+#ifndef _PATH_FSTAB
+#define _PATH_FSTAB "/etc/fstab"
+#endif
+
+#ifdef sunos
+extern char *calloc();
+extern char *malloc();
+extern long atol();
+extern char *strcpy();
+extern char *strncpy();
+extern char *strcat();
+extern time_t time();
+extern void endgrent();
+extern __dead void exit();
+extern off_t lseek();
+extern const char *strerror();
+#endif
diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c
new file mode 100644
index 00000000000..75841fe584f
--- /dev/null
+++ b/sbin/dump/dumprmt.c
@@ -0,0 +1,380 @@
+/* $NetBSD: dumprmt.c,v 1.9 1995/03/18 14:54:59 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dumprmt.c 8.1 (Berkeley) 6/5/93";
+#else
+static char rcsid[] = "$NetBSD: dumprmt.c,v 1.9 1995/03/18 14:54:59 cgd Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mtio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "pathnames.h"
+#include "dump.h"
+
+#define TS_CLOSED 0
+#define TS_OPEN 1
+
+static int rmtstate = TS_CLOSED;
+static int rmtape;
+static char *rmtpeer;
+
+static int okname __P((char *));
+static int rmtcall __P((char *, char *));
+static void rmtconnaborted __P((/* int, int */));
+static int rmtgetb __P((void));
+static void rmtgetconn __P((void));
+static void rmtgets __P((char *, int));
+static int rmtreply __P((char *));
+
+extern int ntrec; /* blocking factor on tape */
+
+int
+rmthost(host)
+ char *host;
+{
+
+ rmtpeer = malloc(strlen(host) + 1);
+ if (rmtpeer)
+ strcpy(rmtpeer, host);
+ else
+ rmtpeer = host;
+ signal(SIGPIPE, rmtconnaborted);
+ rmtgetconn();
+ if (rmtape < 0)
+ return (0);
+ return (1);
+}
+
+static void
+rmtconnaborted()
+{
+
+ errx(1, "Lost connection to remote host.");
+}
+
+void
+rmtgetconn()
+{
+ register char *cp;
+ static struct servent *sp = NULL;
+ static struct passwd *pwd = NULL;
+#ifdef notdef
+ static int on = 1;
+#endif
+ char *tuser;
+ int size;
+ int maxseg;
+
+ if (sp == NULL) {
+ sp = getservbyname("shell", "tcp");
+ if (sp == NULL)
+ errx(1, "shell/tcp: unknown service");
+ pwd = getpwuid(getuid());
+ if (pwd == NULL)
+ errx(1, "who are you?");
+ }
+ if ((cp = strchr(rmtpeer, '@')) != NULL) {
+ tuser = rmtpeer;
+ *cp = '\0';
+ if (!okname(tuser))
+ exit(1);
+ rmtpeer = ++cp;
+ } else
+ tuser = pwd->pw_name;
+ rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser,
+ _PATH_RMT, (int *)0);
+ size = ntrec * TP_BSIZE;
+ if (size > 60 * 1024) /* XXX */
+ size = 60 * 1024;
+ /* Leave some space for rmt request/response protocol */
+ size += 2 * 1024;
+ while (size > TP_BSIZE &&
+ setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
+ size -= TP_BSIZE;
+ (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
+ maxseg = 1024;
+ if (setsockopt(rmtape, IPPROTO_TCP, TCP_MAXSEG,
+ &maxseg, sizeof (maxseg)) < 0)
+ perror("TCP_MAXSEG setsockopt");
+
+#ifdef notdef
+ if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
+ perror("TCP_NODELAY setsockopt");
+#endif
+}
+
+static int
+okname(cp0)
+ char *cp0;
+{
+ register char *cp;
+ register int c;
+
+ for (cp = cp0; *cp; cp++) {
+ c = *cp;
+ if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
+ warnx("invalid user name: %s", cp0);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int
+rmtopen(tape, mode)
+ char *tape;
+ int mode;
+{
+ char buf[256];
+
+ (void)sprintf(buf, "O%s\n%d\n", tape, mode);
+ rmtstate = TS_OPEN;
+ return (rmtcall(tape, buf));
+}
+
+void
+rmtclose()
+{
+
+ if (rmtstate != TS_OPEN)
+ return;
+ rmtcall("close", "C\n");
+ rmtstate = TS_CLOSED;
+}
+
+int
+rmtread(buf, count)
+ char *buf;
+ int count;
+{
+ char line[30];
+ int n, i, cc;
+ extern errno;
+
+ (void)sprintf(line, "R%d\n", count);
+ n = rmtcall("read", line);
+ if (n < 0) {
+ errno = n;
+ return (-1);
+ }
+ for (i = 0; i < n; i += cc) {
+ cc = read(rmtape, buf+i, n - i);
+ if (cc <= 0) {
+ rmtconnaborted();
+ }
+ }
+ return (n);
+}
+
+int
+rmtwrite(buf, count)
+ char *buf;
+ int count;
+{
+ char line[30];
+
+ (void)sprintf(line, "W%d\n", count);
+ write(rmtape, line, strlen(line));
+ write(rmtape, buf, count);
+ return (rmtreply("write"));
+}
+
+void
+rmtwrite0(count)
+ int count;
+{
+ char line[30];
+
+ (void)sprintf(line, "W%d\n", count);
+ write(rmtape, line, strlen(line));
+}
+
+void
+rmtwrite1(buf, count)
+ char *buf;
+ int count;
+{
+
+ write(rmtape, buf, count);
+}
+
+int
+rmtwrite2()
+{
+
+ return (rmtreply("write"));
+}
+
+int
+rmtseek(offset, pos)
+ int offset, pos;
+{
+ char line[80];
+
+ (void)sprintf(line, "L%d\n%d\n", offset, pos);
+ return (rmtcall("seek", line));
+}
+
+struct mtget mts;
+
+struct mtget *
+rmtstatus()
+{
+ register int i;
+ register char *cp;
+
+ if (rmtstate != TS_OPEN)
+ return (NULL);
+ rmtcall("status", "S\n");
+ for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++)
+ *cp++ = rmtgetb();
+ return (&mts);
+}
+
+int
+rmtioctl(cmd, count)
+ int cmd, count;
+{
+ char buf[256];
+
+ if (count < 0)
+ return (-1);
+ (void)sprintf(buf, "I%d\n%d\n", cmd, count);
+ return (rmtcall("ioctl", buf));
+}
+
+static int
+rmtcall(cmd, buf)
+ char *cmd, *buf;
+{
+
+ if (write(rmtape, buf, strlen(buf)) != strlen(buf))
+ rmtconnaborted();
+ return (rmtreply(cmd));
+}
+
+static int
+rmtreply(cmd)
+ char *cmd;
+{
+ register char *cp;
+ char code[30], emsg[BUFSIZ];
+
+ rmtgets(code, sizeof (code));
+ if (*code == 'E' || *code == 'F') {
+ rmtgets(emsg, sizeof (emsg));
+ msg("%s: %s", cmd, emsg);
+ if (*code == 'F') {
+ rmtstate = TS_CLOSED;
+ return (-1);
+ }
+ return (-1);
+ }
+ if (*code != 'A') {
+ /* Kill trailing newline */
+ cp = code + strlen(code);
+ if (cp > code && *--cp == '\n')
+ *cp = '\0';
+
+ msg("Protocol to remote tape server botched (code \"%s\").\n",
+ code);
+ rmtconnaborted();
+ }
+ return (atoi(code + 1));
+}
+
+int
+rmtgetb()
+{
+ char c;
+
+ if (read(rmtape, &c, 1) != 1)
+ rmtconnaborted();
+ return (c);
+}
+
+/* Get a line (guaranteed to have a trailing newline). */
+void
+rmtgets(line, len)
+ char *line;
+ int len;
+{
+ register char *cp = line;
+
+ while (len > 1) {
+ *cp = rmtgetb();
+ if (*cp == '\n') {
+ cp[1] = '\0';
+ return;
+ }
+ cp++;
+ len--;
+ }
+ *cp = '\0';
+ msg("Protocol to remote tape server botched.\n");
+ msg("(rmtgets got \"%s\").\n", line);
+ rmtconnaborted();
+}
diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c
new file mode 100644
index 00000000000..6972d956f91
--- /dev/null
+++ b/sbin/dump/itime.c
@@ -0,0 +1,277 @@
+/* $NetBSD: itime.c,v 1.3 1995/03/18 14:55:01 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93";
+#else
+static char rcsid[] = "$NetBSD: itime.c,v 1.3 1995/03/18 14:55:01 cgd Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fsdir.h>
+#include <ufs/inode.h>
+#include <ufs/fs.h>
+#else
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "dump.h"
+
+struct dumpdates **ddatev = 0;
+int nddates = 0;
+int ddates_in = 0;
+struct dumptime *dthead = 0;
+
+static void dumprecout __P((FILE *, struct dumpdates *));
+static int getrecord __P((FILE *, struct dumpdates *));
+static int makedumpdate __P((struct dumpdates *, char *));
+static void readdumptimes __P((FILE *));
+
+void
+initdumptimes()
+{
+ FILE *df;
+
+ if ((df = fopen(dumpdates, "r")) == NULL) {
+ if (errno != ENOENT) {
+ quit("cannot read %s: %s\n", dumpdates,
+ strerror(errno));
+ /* NOTREACHED */
+ }
+ /*
+ * Dumpdates does not exist, make an empty one.
+ */
+ msg("WARNING: no file `%s', making an empty one\n", dumpdates);
+ if ((df = fopen(dumpdates, "w")) == NULL) {
+ quit("cannot create %s: %s\n", dumpdates,
+ strerror(errno));
+ /* NOTREACHED */
+ }
+ (void) fclose(df);
+ if ((df = fopen(dumpdates, "r")) == NULL) {
+ quit("cannot read %s even after creating it: %s\n",
+ dumpdates, strerror(errno));
+ /* NOTREACHED */
+ }
+ }
+ (void) flock(fileno(df), LOCK_SH);
+ readdumptimes(df);
+ (void) fclose(df);
+}
+
+static void
+readdumptimes(df)
+ FILE *df;
+{
+ register int i;
+ register struct dumptime *dtwalk;
+
+ for (;;) {
+ dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime));
+ if (getrecord(df, &(dtwalk->dt_value)) < 0)
+ break;
+ nddates++;
+ dtwalk->dt_next = dthead;
+ dthead = dtwalk;
+ }
+
+ ddates_in = 1;
+ /*
+ * arrayify the list, leaving enough room for the additional
+ * record that we may have to add to the ddate structure
+ */
+ ddatev = (struct dumpdates **)
+ calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *));
+ dtwalk = dthead;
+ for (i = nddates - 1; i >= 0; i--, dtwalk = dtwalk->dt_next)
+ ddatev[i] = &dtwalk->dt_value;
+}
+
+void
+getdumptime()
+{
+ register struct dumpdates *ddp;
+ register int i;
+ char *fname;
+
+ fname = disk;
+#ifdef FDEBUG
+ msg("Looking for name %s in dumpdates = %s for level = %c\n",
+ fname, dumpdates, level);
+#endif
+ spcl.c_ddate = 0;
+ lastlevel = '0';
+
+ initdumptimes();
+ /*
+ * Go find the entry with the same name for a lower increment
+ * and older date
+ */
+ ITITERATE(i, ddp) {
+ if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0)
+ continue;
+ if (ddp->dd_level >= level)
+ continue;
+ if (ddp->dd_ddate <= spcl.c_ddate)
+ continue;
+ spcl.c_ddate = ddp->dd_ddate;
+ lastlevel = ddp->dd_level;
+ }
+}
+
+void
+putdumptime()
+{
+ FILE *df;
+ register struct dumpdates *dtwalk;
+ register int i;
+ int fd;
+ char *fname;
+
+ if(uflag == 0)
+ return;
+ if ((df = fopen(dumpdates, "r+")) == NULL)
+ quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno));
+ fd = fileno(df);
+ (void) flock(fd, LOCK_EX);
+ fname = disk;
+ free((char *)ddatev);
+ ddatev = 0;
+ nddates = 0;
+ dthead = 0;
+ ddates_in = 0;
+ readdumptimes(df);
+ if (fseek(df, 0L, 0) < 0)
+ quit("fseek: %s\n", strerror(errno));
+ spcl.c_ddate = 0;
+ ITITERATE(i, dtwalk) {
+ if (strncmp(fname, dtwalk->dd_name,
+ sizeof (dtwalk->dd_name)) != 0)
+ continue;
+ if (dtwalk->dd_level != level)
+ continue;
+ goto found;
+ }
+ /*
+ * construct the new upper bound;
+ * Enough room has been allocated.
+ */
+ dtwalk = ddatev[nddates] =
+ (struct dumpdates *)calloc(1, sizeof (struct dumpdates));
+ nddates += 1;
+ found:
+ (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name));
+ dtwalk->dd_level = level;
+ dtwalk->dd_ddate = spcl.c_date;
+
+ ITITERATE(i, dtwalk) {
+ dumprecout(df, dtwalk);
+ }
+ if (fflush(df))
+ quit("%s: %s\n", dumpdates, strerror(errno));
+ if (ftruncate(fd, ftell(df)))
+ quit("ftruncate (%s): %s\n", dumpdates, strerror(errno));
+ (void) fclose(df);
+ msg("level %c dump on %s", level,
+ spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+}
+
+static void
+dumprecout(file, what)
+ FILE *file;
+ struct dumpdates *what;
+{
+
+ if (fprintf(file, DUMPOUTFMT,
+ what->dd_name,
+ what->dd_level,
+ ctime(&what->dd_ddate)) < 0)
+ quit("%s: %s\n", dumpdates, strerror(errno));
+}
+
+int recno;
+
+static int
+getrecord(df, ddatep)
+ FILE *df;
+ struct dumpdates *ddatep;
+{
+ char tbuf[BUFSIZ];
+
+ recno = 0;
+ if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf)
+ return(-1);
+ recno++;
+ if (makedumpdate(ddatep, tbuf) < 0)
+ msg("Unknown intermediate format in %s, line %d\n",
+ dumpdates, recno);
+
+#ifdef FDEBUG
+ msg("getrecord: %s %c %s", ddatep->dd_name, ddatep->dd_level,
+ ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate));
+#endif
+ return(0);
+}
+
+static int
+makedumpdate(ddp, tbuf)
+ struct dumpdates *ddp;
+ char *tbuf;
+{
+ char un_buf[128];
+
+ (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf);
+ ddp->dd_ddate = unctime(un_buf);
+ if (ddp->dd_ddate < 0)
+ return(-1);
+ return(0);
+}
diff --git a/sbin/dump/main.c b/sbin/dump/main.c
new file mode 100644
index 00000000000..42d21cceda4
--- /dev/null
+++ b/sbin/dump/main.c
@@ -0,0 +1,592 @@
+/* $NetBSD: main.c,v 1.6 1995/03/18 14:55:02 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 4/15/94";
+#else
+static char rcsid[] = "$NetBSD: main.c,v 1.6 1995/03/18 14:55:02 cgd Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/inode.h>
+#include <ufs/fs.h>
+#else
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dump.h"
+#include "pathnames.h"
+
+#ifndef SBOFF
+#define SBOFF (SBLOCK * DEV_BSIZE)
+#endif
+
+int notify = 0; /* notify operator flag */
+int blockswritten = 0; /* number of blocks written on current tape */
+int tapeno = 0; /* current tape number */
+int density = 0; /* density in bytes/0.1" */
+int ntrec = NTREC; /* # tape blocks in each tape record */
+int cartridge = 0; /* Assume non-cartridge tape */
+long dev_bsize = 1; /* recalculated below */
+long blocksperfile; /* output blocks per file */
+char *host = NULL; /* remote host (if any) */
+
+static long numarg __P((char *, long, long));
+static void obsolete __P((int *, char **[]));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register ino_t ino;
+ register int dirty;
+ register struct dinode *dp;
+ register struct fstab *dt;
+ register char *map;
+ register int ch;
+ int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1;
+ ino_t maxino;
+
+ spcl.c_date = 0;
+ (void)time((time_t *)&spcl.c_date);
+
+ tsize = 0; /* Default later, based on 'c' option for cart tapes */
+ tape = _PATH_DEFTAPE;
+ dumpdates = _PATH_DUMPDATES;
+ temp = _PATH_DTMP;
+ if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
+ quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
+ level = '0';
+
+ if (argc < 2)
+ usage();
+
+ obsolete(&argc, &argv);
+ while ((ch = getopt(argc, argv, "0123456789B:b:cd:f:h:ns:T:uWw")) != -1)
+ switch (ch) {
+ /* dump level */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ level = ch;
+ break;
+
+ case 'B': /* blocks per output file */
+ blocksperfile = numarg("blocks per file", 1L, 0L);
+ break;
+
+ case 'b': /* blocks per tape write */
+ ntrec = numarg("blocks per write", 1L, 1000L);
+ break;
+
+ case 'c': /* Tape is cart. not 9-track */
+ cartridge = 1;
+ break;
+
+ case 'd': /* density, in bits per inch */
+ density = numarg("density", 10L, 327670L) / 10;
+ if (density >= 625 && !bflag)
+ ntrec = HIGHDENSITYTREC;
+ break;
+
+ case 'f': /* output file */
+ tape = optarg;
+ break;
+
+ case 'h':
+ honorlevel = numarg("honor level", 0L, 10L);
+ break;
+
+ case 'n': /* notify operators */
+ notify = 1;
+ break;
+
+ case 's': /* tape size, feet */
+ tsize = numarg("tape size", 1L, 0L) * 12 * 10;
+ break;
+
+ case 'T': /* time of last dump */
+ spcl.c_ddate = unctime(optarg);
+ if (spcl.c_ddate < 0) {
+ (void)fprintf(stderr, "bad time \"%s\"\n",
+ optarg);
+ exit(X_ABORT);
+ }
+ Tflag = 1;
+ lastlevel = '?';
+ break;
+
+ case 'u': /* update /etc/dumpdates */
+ uflag = 1;
+ break;
+
+ case 'W': /* what to do */
+ case 'w':
+ lastdump(ch);
+ exit(0); /* do nothing else */
+
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ (void)fprintf(stderr, "Must specify disk or filesystem\n");
+ exit(X_ABORT);
+ }
+ disk = *argv++;
+ argc--;
+ if (argc >= 1) {
+ (void)fprintf(stderr, "Unknown arguments to dump:");
+ while (argc--)
+ (void)fprintf(stderr, " %s", *argv++);
+ (void)fprintf(stderr, "\n");
+ exit(X_ABORT);
+ }
+ if (Tflag && uflag) {
+ (void)fprintf(stderr,
+ "You cannot use the T and u flags together.\n");
+ exit(X_ABORT);
+ }
+ if (strcmp(tape, "-") == 0) {
+ pipeout++;
+ tape = "standard output";
+ }
+
+ if (blocksperfile)
+ blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
+ else {
+ /*
+ * Determine how to default tape size and density
+ *
+ * density tape size
+ * 9-track 1600 bpi (160 bytes/.1") 2300 ft.
+ * 9-track 6250 bpi (625 bytes/.1") 2300 ft.
+ * cartridge 8000 bpi (100 bytes/.1") 1700 ft.
+ * (450*4 - slop)
+ */
+ if (density == 0)
+ density = cartridge ? 100 : 160;
+ if (tsize == 0)
+ tsize = cartridge ? 1700L*120L : 2300L*120L;
+ }
+
+ if (strchr(tape, ':')) {
+ host = tape;
+ tape = strchr(host, ':');
+ *tape++ = '\0';
+#ifdef RDUMP
+ if (rmthost(host) == 0)
+ exit(X_ABORT);
+#else
+ (void)fprintf(stderr, "remote dump not enabled\n");
+ exit(X_ABORT);
+#endif
+ }
+ (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */
+
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, sig);
+ if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
+ signal(SIGTRAP, sig);
+ if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
+ signal(SIGFPE, sig);
+ if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
+ signal(SIGBUS, sig);
+ if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
+ signal(SIGSEGV, sig);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, sig);
+ if (signal(SIGINT, interrupt) == SIG_IGN)
+ signal(SIGINT, SIG_IGN);
+
+ set_operators(); /* /etc/group snarfed */
+ getfstab(); /* /etc/fstab snarfed */
+ /*
+ * disk can be either the full special file name,
+ * the suffix of the special file name,
+ * the special name missing the leading '/',
+ * the file system name with or without the leading '/'.
+ */
+ dt = fstabsearch(disk);
+ if (dt != NULL) {
+ disk = rawname(dt->fs_spec);
+ (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN);
+ (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN);
+ } else {
+ (void)strncpy(spcl.c_dev, disk, NAMELEN);
+ (void)strncpy(spcl.c_filesys, "an unlisted file system",
+ NAMELEN);
+ }
+ (void)strcpy(spcl.c_label, "none");
+ (void)gethostname(spcl.c_host, NAMELEN);
+ spcl.c_level = level - '0';
+ spcl.c_type = TS_TAPE;
+ if (!Tflag)
+ getdumptime(); /* /etc/dumpdates snarfed */
+
+ msg("Date of this level %c dump: %s", level,
+ spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+ msg("Date of last level %c dump: %s", lastlevel,
+ spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate));
+ msg("Dumping %s ", disk);
+ if (dt != NULL)
+ msgtail("(%s) ", dt->fs_file);
+ if (host)
+ msgtail("to %s on host %s\n", tape, host);
+ else
+ msgtail("to %s\n", tape);
+
+ if ((diskfd = open(disk, O_RDONLY)) < 0) {
+ msg("Cannot open %s\n", disk);
+ exit(X_ABORT);
+ }
+ sync();
+ sblock = (struct fs *)sblock_buf;
+ bread(SBOFF, (char *) sblock, SBSIZE);
+ if (sblock->fs_magic != FS_MAGIC)
+ quit("bad sblock magic number\n");
+ dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1);
+ dev_bshift = ffs(dev_bsize) - 1;
+ if (dev_bsize != (1 << dev_bshift))
+ quit("dev_bsize (%d) is not a power of 2", dev_bsize);
+ tp_bshift = ffs(TP_BSIZE) - 1;
+ if (TP_BSIZE != (1 << tp_bshift))
+ quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
+#ifdef FS_44INODEFMT
+ if (sblock->fs_inodefmt >= FS_44INODEFMT)
+ spcl.c_flags |= DR_NEWINODEFMT;
+#endif
+ maxino = sblock->fs_ipg * sblock->fs_ncg;
+ mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE);
+ usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
+
+ nonodump = spcl.c_level < honorlevel;
+
+ msg("mapping (Pass I) [regular files]\n");
+ anydirskipped = mapfiles(maxino, &tapesize);
+
+ msg("mapping (Pass II) [directories]\n");
+ while (anydirskipped) {
+ anydirskipped = mapdirs(maxino, &tapesize);
+ }
+
+ if (pipeout) {
+ tapesize += 10; /* 10 trailer blocks */
+ msg("estimated %ld tape blocks.\n", tapesize);
+ } else {
+ double fetapes;
+
+ if (blocksperfile)
+ fetapes = (double) tapesize / blocksperfile;
+ else if (cartridge) {
+ /* Estimate number of tapes, assuming streaming stops at
+ the end of each block written, and not in mid-block.
+ Assume no erroneous blocks; this can be compensated
+ for with an artificially low tape size. */
+ fetapes =
+ ( tapesize /* blocks */
+ * TP_BSIZE /* bytes/block */
+ * (1.0/density) /* 0.1" / byte */
+ +
+ tapesize /* blocks */
+ * (1.0/ntrec) /* streaming-stops per block */
+ * 15.48 /* 0.1" / streaming-stop */
+ ) * (1.0 / tsize ); /* tape / 0.1" */
+ } else {
+ /* Estimate number of tapes, for old fashioned 9-track
+ tape */
+ int tenthsperirg = (density == 625) ? 3 : 7;
+ fetapes =
+ ( tapesize /* blocks */
+ * TP_BSIZE /* bytes / block */
+ * (1.0/density) /* 0.1" / byte */
+ +
+ tapesize /* blocks */
+ * (1.0/ntrec) /* IRG's / block */
+ * tenthsperirg /* 0.1" / IRG */
+ ) * (1.0 / tsize ); /* tape / 0.1" */
+ }
+ etapes = fetapes; /* truncating assignment */
+ etapes++;
+ /* count the dumped inodes map on each additional tape */
+ tapesize += (etapes - 1) *
+ (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
+ tapesize += etapes + 10; /* headers + 10 trailer blks */
+ msg("estimated %ld tape blocks on %3.2f tape(s).\n",
+ tapesize, fetapes);
+ }
+
+ /*
+ * Allocate tape buffer.
+ */
+ if (!alloctape())
+ quit("can't allocate tape buffers - try a smaller blocking factor.\n");
+
+ startnewtape(1);
+ (void)time((time_t *)&(tstart_writing));
+ dumpmap(usedinomap, TS_CLRI, maxino - 1);
+
+ msg("dumping (Pass III) [directories]\n");
+ dirty = 0; /* XXX just to get gcc to shut up */
+ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */
+ dirty = *map++;
+ else
+ dirty >>= 1;
+ if ((dirty & 1) == 0)
+ continue;
+ /*
+ * Skip directory inodes deleted and maybe reallocated
+ */
+ dp = getino(ino);
+ if ((dp->di_mode & IFMT) != IFDIR)
+ continue;
+ (void)dumpino(dp, ino);
+ }
+
+ msg("dumping (Pass IV) [regular files]\n");
+ for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
+ int mode;
+
+ if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */
+ dirty = *map++;
+ else
+ dirty >>= 1;
+ if ((dirty & 1) == 0)
+ continue;
+ /*
+ * Skip inodes deleted and reallocated as directories.
+ */
+ dp = getino(ino);
+ mode = dp->di_mode & IFMT;
+ if (mode == IFDIR)
+ continue;
+ (void)dumpino(dp, ino);
+ }
+
+ spcl.c_type = TS_END;
+ for (i = 0; i < ntrec; i++)
+ writeheader(maxino - 1);
+ if (pipeout)
+ msg("DUMP: %ld tape blocks\n",spcl.c_tapea);
+ else
+ msg("DUMP: %ld tape blocks on %d volumes(s)\n",
+ spcl.c_tapea, spcl.c_volume);
+ putdumptime();
+ trewind();
+ broadcast("DUMP IS DONE!\7\7\n");
+ msg("DUMP IS DONE\n");
+ Exit(X_FINOK);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: dump [-0123456789cnu] [-B records] [-b blocksize] [-d density] [-f file]\n [-h level] [-s feet] [-T date] filesystem\n");
+ (void)fprintf(stderr, " dump [-W | -w]\n");
+ exit(1);
+}
+
+/*
+ * Pick up a numeric argument. It must be nonnegative and in the given
+ * range (except that a vmax of 0 means unlimited).
+ */
+static long
+numarg(meaning, vmin, vmax)
+ char *meaning;
+ long vmin, vmax;
+{
+ char *p;
+ long val;
+
+ val = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal %s -- %s", meaning, optarg);
+ if (val < vmin || (vmax && val > vmax))
+ errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax);
+ return (val);
+}
+
+void
+sig(signo)
+ int signo;
+{
+ switch(signo) {
+ case SIGALRM:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGHUP:
+ case SIGTERM:
+ case SIGTRAP:
+ if (pipeout)
+ quit("Signal on pipe: cannot recover\n");
+ msg("Rewriting attempted as response to unknown signal.\n");
+ (void)fflush(stderr);
+ (void)fflush(stdout);
+ close_rewind();
+ exit(X_REWRITE);
+ /* NOTREACHED */
+ case SIGSEGV:
+ msg("SIGSEGV: ABORTING!\n");
+ (void)signal(SIGSEGV, SIG_DFL);
+ (void)kill(0, SIGSEGV);
+ /* NOTREACHED */
+ }
+}
+
+char *
+rawname(cp)
+ char *cp;
+{
+ static char rawbuf[MAXPATHLEN];
+ char *dp = strrchr(cp, '/');
+
+ if (dp == NULL)
+ return (NULL);
+ *dp = '\0';
+ (void)strcpy(rawbuf, cp);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, dp + 1);
+ return (rawbuf);
+}
+
+/*
+ * obsolete --
+ * Change set of key letters and ordered arguments into something
+ * getopt(3) will like.
+ */
+static void
+obsolete(argcp, argvp)
+ int *argcp;
+ char **argvp[];
+{
+ int argc, flags;
+ char *ap, **argv, *flagsp, **nargv, *p;
+
+ /* Setup. */
+ argv = *argvp;
+ argc = *argcp;
+
+ /* Return if no arguments or first argument has leading dash. */
+ ap = argv[1];
+ if (argc == 1 || *ap == '-')
+ return;
+
+ /* Allocate space for new arguments. */
+ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
+ (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
+ err(1, NULL);
+
+ *nargv++ = *argv;
+ argv += 2;
+
+ for (flags = 0; *ap; ++ap) {
+ switch (*ap) {
+ case 'B':
+ case 'b':
+ case 'd':
+ case 'f':
+ case 'h':
+ case 's':
+ case 'T':
+ if (*argv == NULL) {
+ warnx("option requires an argument -- %c", *ap);
+ usage();
+ }
+ if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
+ err(1, NULL);
+ nargv[0][0] = '-';
+ nargv[0][1] = *ap;
+ (void)strcpy(&nargv[0][2], *argv);
+ ++argv;
+ ++nargv;
+ break;
+ default:
+ if (!flags) {
+ *p++ = '-';
+ flags = 1;
+ }
+ *p++ = *ap;
+ break;
+ }
+ }
+
+ /* Terminate flags. */
+ if (flags) {
+ *p = '\0';
+ *nargv++ = flagsp;
+ }
+
+ /* Copy remaining arguments. */
+ while (*nargv++ = *argv++);
+
+ /* Update argument count. */
+ *argcp = nargv - *argvp - 1;
+}
diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c
new file mode 100644
index 00000000000..52755a57fef
--- /dev/null
+++ b/sbin/dump/optr.c
@@ -0,0 +1,544 @@
+/* $NetBSD: optr.c,v 1.3 1995/03/18 14:55:04 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94";
+#else
+static char rcsid[] = "$NetBSD: optr.c,v 1.3 1995/03/18 14:55:04 cgd Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#endif
+#include <tzfile.h>
+#ifdef __STDC__
+#include <unistd.h>
+#endif
+#include <utmp.h>
+#ifndef __STDC__
+#include <varargs.h>
+#endif
+
+#include "dump.h"
+#include "pathnames.h"
+
+void alarmcatch __P((/* int, int */));
+int datesort __P((const void *, const void *));
+static void sendmes __P((char *, char *));
+
+/*
+ * Query the operator; This previously-fascist piece of code
+ * no longer requires an exact response.
+ * It is intended to protect dump aborting by inquisitive
+ * people banging on the console terminal to see what is
+ * happening which might cause dump to croak, destroying
+ * a large number of hours of work.
+ *
+ * Every 2 minutes we reprint the message, alerting others
+ * that dump needs attention.
+ */
+static int timeout;
+static char *attnmessage; /* attention message */
+
+int
+query(question)
+ char *question;
+{
+ char replybuffer[64];
+ int back, errcount;
+ FILE *mytty;
+
+ if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
+ quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
+ attnmessage = question;
+ timeout = 0;
+ alarmcatch();
+ back = -1;
+ errcount = 0;
+ do {
+ if (fgets(replybuffer, 63, mytty) == NULL) {
+ clearerr(mytty);
+ if (++errcount > 30) /* XXX ugly */
+ quit("excessive operator query failures\n");
+ } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
+ back = 1;
+ } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
+ back = 0;
+ } else {
+ (void) fprintf(stderr,
+ " DUMP: \"Yes\" or \"No\"?\n");
+ (void) fprintf(stderr,
+ " DUMP: %s: (\"yes\" or \"no\") ", question);
+ }
+ } while (back < 0);
+
+ /*
+ * Turn off the alarm, and reset the signal to trap out..
+ */
+ (void) alarm(0);
+ if (signal(SIGALRM, sig) == SIG_IGN)
+ signal(SIGALRM, SIG_IGN);
+ (void) fclose(mytty);
+ return(back);
+}
+
+char lastmsg[100];
+
+/*
+ * Alert the console operator, and enable the alarm clock to
+ * sleep for 2 minutes in case nobody comes to satisfy dump
+ */
+void
+alarmcatch()
+{
+ if (notify == 0) {
+ if (timeout == 0)
+ (void) fprintf(stderr,
+ " DUMP: %s: (\"yes\" or \"no\") ",
+ attnmessage);
+ else
+ msgtail("\7\7");
+ } else {
+ if (timeout) {
+ msgtail("\n");
+ broadcast(""); /* just print last msg */
+ }
+ (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ",
+ attnmessage);
+ }
+ signal(SIGALRM, alarmcatch);
+ (void) alarm(120);
+ timeout = 1;
+}
+
+/*
+ * Here if an inquisitive operator interrupts the dump program
+ */
+void
+interrupt(signo)
+ int signo;
+{
+ msg("Interrupt received.\n");
+ if (query("Do you want to abort dump?"))
+ dumpabort(0);
+}
+
+/*
+ * The following variables and routines manage alerting
+ * operators to the status of dump.
+ * This works much like wall(1) does.
+ */
+struct group *gp;
+
+/*
+ * Get the names from the group entry "operator" to notify.
+ */
+void
+set_operators()
+{
+ if (!notify) /*not going to notify*/
+ return;
+ gp = getgrnam(OPGRENT);
+ (void) endgrent();
+ if (gp == NULL) {
+ msg("No group entry for %s.\n", OPGRENT);
+ notify = 0;
+ return;
+ }
+}
+
+struct tm *localclock;
+
+/*
+ * We fork a child to do the actual broadcasting, so
+ * that the process control groups are not messed up
+ */
+void
+broadcast(message)
+ char *message;
+{
+ time_t clock;
+ FILE *f_utmp;
+ struct utmp utmp;
+ char **np;
+ int pid, s;
+
+ if (!notify || gp == NULL)
+ return;
+
+ switch (pid = fork()) {
+ case -1:
+ return;
+ case 0:
+ break;
+ default:
+ while (wait(&s) != pid)
+ continue;
+ return;
+ }
+
+ clock = time((time_t *)0);
+ localclock = localtime(&clock);
+
+ if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
+ msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno));
+ return;
+ }
+
+ while (!feof(f_utmp)) {
+ if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1)
+ break;
+ if (utmp.ut_name[0] == 0)
+ continue;
+ for (np = gp->gr_mem; *np; np++) {
+ if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
+ continue;
+ /*
+ * Do not send messages to operators on dialups
+ */
+ if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
+ continue;
+#ifdef DEBUG
+ msg("Message to %s at %s\n", *np, utmp.ut_line);
+#endif
+ sendmes(utmp.ut_line, message);
+ }
+ }
+ (void) fclose(f_utmp);
+ Exit(0); /* the wait in this same routine will catch this */
+ /* NOTREACHED */
+}
+
+static void
+sendmes(tty, message)
+ char *tty, *message;
+{
+ char t[50], buf[BUFSIZ];
+ register char *cp;
+ int lmsg = 1;
+ FILE *f_tty;
+
+ (void) strcpy(t, _PATH_DEV);
+ (void) strcat(t, tty);
+
+ if ((f_tty = fopen(t, "w")) != NULL) {
+ setbuf(f_tty, buf);
+ (void) fprintf(f_tty,
+ "\n\
+\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\
+DUMP: NEEDS ATTENTION: ",
+ localclock->tm_hour, localclock->tm_min);
+ for (cp = lastmsg; ; cp++) {
+ if (*cp == '\0') {
+ if (lmsg) {
+ cp = message;
+ if (*cp == '\0')
+ break;
+ lmsg = 0;
+ } else
+ break;
+ }
+ if (*cp == '\n')
+ (void) putc('\r', f_tty);
+ (void) putc(*cp, f_tty);
+ }
+ (void) fclose(f_tty);
+ }
+}
+
+/*
+ * print out an estimate of the amount of time left to do the dump
+ */
+
+time_t tschedule = 0;
+
+void
+timeest()
+{
+ time_t tnow, deltat;
+
+ (void) time((time_t *) &tnow);
+ if (tnow >= tschedule) {
+ tschedule = tnow + 300;
+ if (blockswritten < 500)
+ return;
+ deltat = tstart_writing - tnow +
+ (1.0 * (tnow - tstart_writing))
+ / blockswritten * tapesize;
+ msg("%3.2f%% done, finished in %d:%02d\n",
+ (blockswritten * 100.0) / tapesize,
+ deltat / 3600, (deltat % 3600) / 60);
+ }
+}
+
+void
+#if __STDC__
+msg(const char *fmt, ...)
+#else
+msg(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void) fprintf(stderr," DUMP: ");
+#ifdef TDEBUG
+ (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ (void) vsprintf(lastmsg, fmt, ap);
+ va_end(ap);
+}
+
+void
+#if __STDC__
+msgtail(const char *fmt, ...)
+#else
+msgtail(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+void
+#if __STDC__
+quit(const char *fmt, ...)
+#else
+quit(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void) fprintf(stderr," DUMP: ");
+#ifdef TDEBUG
+ (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ dumpabort(0);
+}
+
+/*
+ * Tell the operator what has to be done;
+ * we don't actually do it
+ */
+
+struct fstab *
+allocfsent(fs)
+ register struct fstab *fs;
+{
+ register struct fstab *new;
+
+ new = (struct fstab *)malloc(sizeof (*fs));
+ if (new == NULL ||
+ (new->fs_file = strdup(fs->fs_file)) == NULL ||
+ (new->fs_type = strdup(fs->fs_type)) == NULL ||
+ (new->fs_spec = strdup(fs->fs_spec)) == NULL)
+ quit("%s\n", strerror(errno));
+ new->fs_passno = fs->fs_passno;
+ new->fs_freq = fs->fs_freq;
+ return (new);
+}
+
+struct pfstab {
+ struct pfstab *pf_next;
+ struct fstab *pf_fstab;
+};
+
+static struct pfstab *table;
+
+void
+getfstab()
+{
+ register struct fstab *fs;
+ register struct pfstab *pf;
+
+ if (setfsent() == 0) {
+ msg("Can't open %s for dump table information: %s\n",
+ _PATH_FSTAB, strerror(errno));
+ return;
+ }
+ while ((fs = getfsent()) != NULL) {
+ if (strcmp(fs->fs_type, FSTAB_RW) &&
+ strcmp(fs->fs_type, FSTAB_RO) &&
+ strcmp(fs->fs_type, FSTAB_RQ))
+ continue;
+ fs = allocfsent(fs);
+ if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
+ quit("%s\n", strerror(errno));
+ pf->pf_fstab = fs;
+ pf->pf_next = table;
+ table = pf;
+ }
+ (void) endfsent();
+}
+
+/*
+ * Search in the fstab for a file name.
+ * This file name can be either the special or the path file name.
+ *
+ * The entries in the fstab are the BLOCK special names, not the
+ * character special names.
+ * The caller of fstabsearch assures that the character device
+ * is dumped (that is much faster)
+ *
+ * The file name can omit the leading '/'.
+ */
+struct fstab *
+fstabsearch(key)
+ char *key;
+{
+ register struct pfstab *pf;
+ register struct fstab *fs;
+ char *rn;
+
+ for (pf = table; pf != NULL; pf = pf->pf_next) {
+ fs = pf->pf_fstab;
+ if (strcmp(fs->fs_file, key) == 0 ||
+ strcmp(fs->fs_spec, key) == 0)
+ return (fs);
+ rn = rawname(fs->fs_spec);
+ if (rn != NULL && strcmp(rn, key) == 0)
+ return (fs);
+ if (key[0] != '/') {
+ if (*fs->fs_spec == '/' &&
+ strcmp(fs->fs_spec + 1, key) == 0)
+ return (fs);
+ if (*fs->fs_file == '/' &&
+ strcmp(fs->fs_file + 1, key) == 0)
+ return (fs);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Tell the operator what to do
+ */
+void
+lastdump(arg)
+ char arg; /* w ==> just what to do; W ==> most recent dumps */
+{
+ register int i;
+ register struct fstab *dt;
+ register struct dumpdates *dtwalk;
+ char *lastname, *date;
+ int dumpme;
+ time_t tnow;
+
+ (void) time(&tnow);
+ getfstab(); /* /etc/fstab input */
+ initdumptimes(); /* /etc/dumpdates input */
+ qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort);
+
+ if (arg == 'w')
+ (void) printf("Dump these file systems:\n");
+ else
+ (void) printf("Last dump(s) done (Dump '>' file systems):\n");
+ lastname = "??";
+ ITITERATE(i, dtwalk) {
+ if (strncmp(lastname, dtwalk->dd_name,
+ sizeof(dtwalk->dd_name)) == 0)
+ continue;
+ date = (char *)ctime(&dtwalk->dd_ddate);
+ date[16] = '\0'; /* blast away seconds and year */
+ lastname = dtwalk->dd_name;
+ dt = fstabsearch(dtwalk->dd_name);
+ dumpme = (dt != NULL &&
+ dt->fs_freq != 0 &&
+ dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY));
+ if (arg != 'w' || dumpme)
+ (void) printf(
+ "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
+ dumpme && (arg != 'w') ? '>' : ' ',
+ dtwalk->dd_name,
+ dt ? dt->fs_file : "",
+ dtwalk->dd_level,
+ date);
+ }
+}
+
+int
+datesort(a1, a2)
+ const void *a1, *a2;
+{
+ struct dumpdates *d1 = *(struct dumpdates **)a1;
+ struct dumpdates *d2 = *(struct dumpdates **)a2;
+ int diff;
+
+ diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
+ if (diff == 0)
+ return (d2->dd_ddate - d1->dd_ddate);
+ return (diff);
+}
diff --git a/sbin/dump/pathnames.h b/sbin/dump/pathnames.h
new file mode 100644
index 00000000000..1e4f5ceec35
--- /dev/null
+++ b/sbin/dump/pathnames.h
@@ -0,0 +1,44 @@
+/* $NetBSD: pathnames.h,v 1.8 1995/03/18 14:55:06 cgd Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ */
+
+#include <paths.h>
+
+#define _PATH_DEFTAPE "/dev/rmt8"
+#define _PATH_DTMP "/etc/dtmp"
+#define _PATH_DUMPDATES "/etc/dumpdates"
+#define _PATH_LOCK "/tmp/dumplockXXXXXX"
+#define _PATH_RMT "/etc/rmt"
diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c
new file mode 100644
index 00000000000..59ed514f537
--- /dev/null
+++ b/sbin/dump/tape.c
@@ -0,0 +1,868 @@
+/* $NetBSD: tape.c,v 1.7 1995/03/21 18:48:47 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)tape.c 8.2 (Berkeley) 3/17/94";
+#else
+static char rcsid[] = "$NetBSD: tape.c,v 1.7 1995/03/21 18:48:47 mycroft Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#else
+int write(), read();
+#endif
+
+#include "dump.h"
+#include "pathnames.h"
+
+int writesize; /* size of malloc()ed buffer for tape */
+long lastspclrec = -1; /* tape block number of last written header */
+int trecno = 0; /* next record to write in current block */
+extern long blocksperfile; /* number of blocks per output file */
+long blocksthisvol; /* number of blocks on current output file */
+extern int ntrec; /* blocking factor on tape */
+extern int cartridge;
+extern char *host;
+char *nexttape;
+
+static ssize_t atomic __P((ssize_t (*)(), int, char *, int));
+static void doslave __P((int, int));
+static void enslave __P((void));
+static void flushtape __P((void));
+static void killall __P((void));
+static void rollforward __P((void));
+
+/*
+ * Concurrent dump mods (Caltech) - disk block reading and tape writing
+ * are exported to several slave processes. While one slave writes the
+ * tape, the others read disk blocks; they pass control of the tape in
+ * a ring via signals. The parent process traverses the filesystem and
+ * sends writeheader()'s and lists of daddr's to the slaves via pipes.
+ * The following structure defines the instruction packets sent to slaves.
+ */
+struct req {
+ daddr_t dblk;
+ int count;
+};
+int reqsiz;
+
+#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
+struct slave {
+ int tapea; /* header number at start of this chunk */
+ int count; /* count to next header (used for TS_TAPE */
+ /* after EOT) */
+ int inode; /* inode that we are currently dealing with */
+ int fd; /* FD for this slave */
+ int pid; /* PID for this slave */
+ int sent; /* 1 == we've sent this slave requests */
+ int firstrec; /* record number of this block */
+ char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
+ struct req *req; /* buffer for requests */
+} slaves[SLAVES+1];
+struct slave *slp;
+
+char (*nextblock)[TP_BSIZE];
+
+int master; /* pid of master, for sending error signals */
+int tenths; /* length of tape used per block written */
+static int caught; /* have we caught the signal to proceed? */
+static int ready; /* have we reached the lock point without having */
+ /* received the SIGUSR2 signal from the prev slave? */
+static jmp_buf jmpbuf; /* where to jump to if we are ready when the */
+ /* SIGUSR2 arrives from the previous slave */
+
+int
+alloctape()
+{
+ int pgoff = getpagesize() - 1;
+ char *buf;
+ int i;
+
+ writesize = ntrec * TP_BSIZE;
+ reqsiz = (ntrec + 1) * sizeof(struct req);
+ /*
+ * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
+ * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
+ * repositioning after stopping, i.e, streaming mode, where the gap is
+ * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
+ */
+ if (blocksperfile == 0)
+ tenths = writesize / density +
+ (cartridge ? 16 : density == 625 ? 5 : 8);
+ /*
+ * Allocate tape buffer contiguous with the array of instruction
+ * packets, so flushtape() can write them together with one write().
+ * Align tape buffer on page boundary to speed up tape write().
+ */
+ for (i = 0; i <= SLAVES; i++) {
+ buf = (char *)
+ malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
+ if (buf == NULL)
+ return(0);
+ slaves[i].tblock = (char (*)[TP_BSIZE])
+ (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
+ slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
+ }
+ slp = &slaves[0];
+ slp->count = 1;
+ slp->tapea = 0;
+ slp->firstrec = 0;
+ nextblock = slp->tblock;
+ return(1);
+}
+
+void
+writerec(dp, isspcl)
+ char *dp;
+ int isspcl;
+{
+
+ slp->req[trecno].dblk = (daddr_t)0;
+ slp->req[trecno].count = 1;
+ *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp;
+ if (isspcl)
+ lastspclrec = spcl.c_tapea;
+ trecno++;
+ spcl.c_tapea++;
+ if (trecno >= ntrec)
+ flushtape();
+}
+
+void
+dumpblock(blkno, size)
+ daddr_t blkno;
+ int size;
+{
+ int avail, tpblks, dblkno;
+
+ dblkno = fsbtodb(sblock, blkno);
+ tpblks = size >> tp_bshift;
+ while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
+ slp->req[trecno].dblk = dblkno;
+ slp->req[trecno].count = avail;
+ trecno += avail;
+ spcl.c_tapea += avail;
+ if (trecno >= ntrec)
+ flushtape();
+ dblkno += avail << (tp_bshift - dev_bshift);
+ tpblks -= avail;
+ }
+}
+
+int nogripe = 0;
+
+void
+tperror(signo)
+ int signo;
+{
+
+ if (pipeout) {
+ msg("write error on %s\n", tape);
+ quit("Cannot recover\n");
+ /* NOTREACHED */
+ }
+ msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno);
+ broadcast("DUMP WRITE ERROR!\n");
+ if (!query("Do you want to restart?"))
+ dumpabort(0);
+ msg("Closing this volume. Prepare to restart with new media;\n");
+ msg("this dump volume will be rewritten.\n");
+ killall();
+ nogripe = 1;
+ close_rewind();
+ Exit(X_REWRITE);
+}
+
+void
+sigpipe(signo)
+ int signo;
+{
+
+ quit("Broken pipe\n");
+}
+
+static void
+flushtape()
+{
+ int i, blks, got;
+ long lastfirstrec;
+
+ int siz = (char *)nextblock - (char *)slp->req;
+
+ slp->req[trecno].count = 0; /* Sentinel */
+
+ if (atomic(write, slp->fd, (char *)slp->req, siz) != siz)
+ quit("error writing command pipe: %s\n", strerror(errno));
+ slp->sent = 1; /* we sent a request, read the response later */
+
+ lastfirstrec = slp->firstrec;
+
+ if (++slp >= &slaves[SLAVES])
+ slp = &slaves[0];
+
+ /* Read results back from next slave */
+ if (slp->sent) {
+ if (atomic(read, slp->fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slp->sent = 0;
+
+ /* Check for end of tape */
+ if (got < writesize) {
+ msg("End of tape detected\n");
+
+ /*
+ * Drain the results, don't care what the values were.
+ * If we read them here then trewind won't...
+ */
+ for (i = 0; i < SLAVES; i++) {
+ if (slaves[i].sent) {
+ if (atomic(read, slaves[i].fd,
+ (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slaves[i].sent = 0;
+ }
+ }
+
+ close_rewind();
+ rollforward();
+ return;
+ }
+ }
+
+ blks = 0;
+ if (spcl.c_type != TS_END) {
+ for (i = 0; i < spcl.c_count; i++)
+ if (spcl.c_addr[i] != 0)
+ blks++;
+ }
+ slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
+ slp->tapea = spcl.c_tapea;
+ slp->firstrec = lastfirstrec + ntrec;
+ slp->inode = curino;
+ nextblock = slp->tblock;
+ trecno = 0;
+ asize += tenths;
+ blockswritten += ntrec;
+ blocksthisvol += ntrec;
+ if (!pipeout && (blocksperfile ?
+ (blocksthisvol >= blocksperfile) : (asize > tsize))) {
+ close_rewind();
+ startnewtape(0);
+ }
+ timeest();
+}
+
+void
+trewind()
+{
+ int f;
+ int got;
+
+ for (f = 0; f < SLAVES; f++) {
+ /*
+ * Drain the results, but unlike EOT we DO (or should) care
+ * what the return values were, since if we detect EOT after
+ * we think we've written the last blocks to the tape anyway,
+ * we have to replay those blocks with rollforward.
+ *
+ * fixme: punt for now.
+ */
+ if (slaves[f].sent) {
+ if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slaves[f].sent = 0;
+ if (got != writesize) {
+ msg("EOT detected in last 2 tape records!\n");
+ msg("Use a longer tape, decrease the size estimate\n");
+ quit("or use no size estimate at all.\n");
+ }
+ }
+ (void) close(slaves[f].fd);
+ }
+ while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */
+ /* void */;
+
+ if (pipeout)
+ return;
+
+ msg("Closing %s\n", tape);
+
+#ifdef RDUMP
+ if (host) {
+ rmtclose();
+ while (rmtopen(tape, 0) < 0)
+ sleep(10);
+ rmtclose();
+ return;
+ }
+#endif
+ (void) close(tapefd);
+ while ((f = open(tape, 0)) < 0)
+ sleep (10);
+ (void) close(f);
+}
+
+void
+close_rewind()
+{
+ trewind();
+ if (nexttape)
+ return;
+ if (!nogripe) {
+ msg("Change Volumes: Mount volume #%d\n", tapeno+1);
+ broadcast("CHANGE DUMP VOLUMES!\7\7\n");
+ }
+ while (!query("Is the new volume mounted and ready to go?"))
+ if (query("Do you want to abort?")) {
+ dumpabort(0);
+ /*NOTREACHED*/
+ }
+}
+
+void
+rollforward()
+{
+ register struct req *p, *q, *prev;
+ register struct slave *tslp;
+ int i, size, savedtapea, got;
+ union u_spcl *ntb, *otb;
+ tslp = &slaves[SLAVES];
+ ntb = (union u_spcl *)tslp->tblock[1];
+
+ /*
+ * Each of the N slaves should have requests that need to
+ * be replayed on the next tape. Use the extra slave buffers
+ * (slaves[SLAVES]) to construct request lists to be sent to
+ * each slave in turn.
+ */
+ for (i = 0; i < SLAVES; i++) {
+ q = &tslp->req[1];
+ otb = (union u_spcl *)slp->tblock;
+
+ /*
+ * For each request in the current slave, copy it to tslp.
+ */
+
+ prev = NULL;
+ for (p = slp->req; p->count > 0; p += p->count) {
+ *q = *p;
+ if (p->dblk == 0)
+ *ntb++ = *otb++; /* copy the datablock also */
+ prev = q;
+ q += q->count;
+ }
+ if (prev == NULL)
+ quit("rollforward: protocol botch");
+ if (prev->dblk != 0)
+ prev->count -= 1;
+ else
+ ntb--;
+ q -= 1;
+ q->count = 0;
+ q = &tslp->req[0];
+ if (i == 0) {
+ q->dblk = 0;
+ q->count = 1;
+ trecno = 0;
+ nextblock = tslp->tblock;
+ savedtapea = spcl.c_tapea;
+ spcl.c_tapea = slp->tapea;
+ startnewtape(0);
+ spcl.c_tapea = savedtapea;
+ lastspclrec = savedtapea - 1;
+ }
+ size = (char *)ntb - (char *)q;
+ if (atomic(write, slp->fd, (char *)q, size) != size) {
+ perror(" DUMP: error writing command pipe");
+ dumpabort(0);
+ }
+ slp->sent = 1;
+ if (++slp >= &slaves[SLAVES])
+ slp = &slaves[0];
+
+ q->count = 1;
+
+ if (prev->dblk != 0) {
+ /*
+ * If the last one was a disk block, make the
+ * first of this one be the last bit of that disk
+ * block...
+ */
+ q->dblk = prev->dblk +
+ prev->count * (TP_BSIZE / DEV_BSIZE);
+ ntb = (union u_spcl *)tslp->tblock;
+ } else {
+ /*
+ * It wasn't a disk block. Copy the data to its
+ * new location in the buffer.
+ */
+ q->dblk = 0;
+ *((union u_spcl *)tslp->tblock) = *ntb;
+ ntb = (union u_spcl *)tslp->tblock[1];
+ }
+ }
+ slp->req[0] = *q;
+ nextblock = slp->tblock;
+ if (q->dblk == 0)
+ nextblock++;
+ trecno = 1;
+
+ /*
+ * Clear the first slaves' response. One hopes that it
+ * worked ok, otherwise the tape is much too short!
+ */
+ if (slp->sent) {
+ if (atomic(read, slp->fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slp->sent = 0;
+
+ if (got != writesize) {
+ quit("EOT detected at start of the tape!\n");
+ }
+ }
+}
+
+/*
+ * 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
+startnewtape(top)
+ int top;
+{
+ int parentpid;
+ int childpid;
+ int status;
+ int waitpid;
+ char *p;
+#ifdef sunos
+ void (*interrupt_save)();
+#else
+ sig_t interrupt_save;
+#endif
+
+ interrupt_save = signal(SIGINT, SIG_IGN);
+ parentpid = getpid();
+
+restore_check_point:
+ (void)signal(SIGINT, interrupt_save);
+ /*
+ * All signals are inherited...
+ */
+ childpid = fork();
+ if (childpid < 0) {
+ msg("Context save fork fails in parent %d\n", parentpid);
+ Exit(X_ABORT);
+ }
+ if (childpid != 0) {
+ /*
+ * PARENT:
+ * save the context by waiting
+ * until the child doing all of the work returns.
+ * don't catch the interrupt
+ */
+ signal(SIGINT, SIG_IGN);
+#ifdef TDEBUG
+ msg("Tape: %d; parent process: %d child process %d\n",
+ tapeno+1, parentpid, childpid);
+#endif /* TDEBUG */
+ while ((waitpid = wait(&status)) != childpid)
+ msg("Parent %d waiting for child %d has another child %d return\n",
+ parentpid, childpid, waitpid);
+ if (status & 0xFF) {
+ msg("Child %d returns LOB status %o\n",
+ childpid, status&0xFF);
+ }
+ status = (status >> 8) & 0xFF;
+#ifdef TDEBUG
+ switch(status) {
+ case X_FINOK:
+ msg("Child %d finishes X_FINOK\n", childpid);
+ break;
+ case X_ABORT:
+ msg("Child %d finishes X_ABORT\n", childpid);
+ break;
+ case X_REWRITE:
+ msg("Child %d finishes X_REWRITE\n", childpid);
+ break;
+ default:
+ msg("Child %d finishes unknown %d\n",
+ childpid, status);
+ break;
+ }
+#endif /* TDEBUG */
+ switch(status) {
+ case X_FINOK:
+ Exit(X_FINOK);
+ case X_ABORT:
+ Exit(X_ABORT);
+ case X_REWRITE:
+ goto restore_check_point;
+ default:
+ msg("Bad return code from dump: %d\n", status);
+ Exit(X_ABORT);
+ }
+ /*NOTREACHED*/
+ } else { /* we are the child; just continue */
+#ifdef TDEBUG
+ sleep(4); /* allow time for parent's message to get out */
+ msg("Child on Tape %d has parent %d, my pid = %d\n",
+ tapeno+1, parentpid, getpid());
+#endif /* TDEBUG */
+ /*
+ * If we have a name like "/dev/rmt0,/dev/rmt1",
+ * use the name before the comma first, and save
+ * the remaining names for subsequent volumes.
+ */
+ tapeno++; /* current tape sequence */
+ if (nexttape || strchr(tape, ',')) {
+ if (nexttape && *nexttape)
+ tape = nexttape;
+ if ((p = strchr(tape, ',')) != NULL) {
+ *p = '\0';
+ nexttape = p + 1;
+ } else
+ nexttape = NULL;
+ msg("Dumping volume %d on %s\n", tapeno, tape);
+ }
+#ifdef RDUMP
+ while ((tapefd = (host ? rmtopen(tape, 2) :
+ pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#else
+ while ((tapefd = (pipeout ? 1 :
+ open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#endif
+ {
+ msg("Cannot open output \"%s\".\n", tape);
+ if (!query("Do you want to retry the open?"))
+ dumpabort(0);
+ }
+
+ enslave(); /* Share open tape file descriptor with slaves */
+
+ asize = 0;
+ blocksthisvol = 0;
+ if (top)
+ newtape++; /* new tape signal */
+ spcl.c_count = slp->count;
+ /*
+ * measure firstrec in TP_BSIZE units since restore doesn't
+ * know the correct ntrec value...
+ */
+ spcl.c_firstrec = slp->firstrec;
+ spcl.c_volume++;
+ spcl.c_type = TS_TAPE;
+ spcl.c_flags |= DR_NEWHEADER;
+ writeheader((ino_t)slp->inode);
+ spcl.c_flags &=~ DR_NEWHEADER;
+ if (tapeno > 1)
+ msg("Volume %d begins with blocks from inode %d\n",
+ tapeno, slp->inode);
+ }
+}
+
+void
+dumpabort(signo)
+ int signo;
+{
+
+ if (master != 0 && master != getpid())
+ /* Signals master to call dumpabort */
+ (void) kill(master, SIGTERM);
+ else {
+ killall();
+ msg("The ENTIRE dump is aborted.\n");
+ }
+#ifdef RDUMP
+ rmtclose();
+#endif
+ Exit(X_ABORT);
+}
+
+__dead void
+Exit(status)
+ int status;
+{
+
+#ifdef TDEBUG
+ msg("pid = %d exits with status %d\n", getpid(), status);
+#endif /* TDEBUG */
+ exit(status);
+}
+
+/*
+ * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
+ */
+void
+proceed(signo)
+ int signo;
+{
+
+ if (ready)
+ longjmp(jmpbuf, 1);
+ caught++;
+}
+
+void
+enslave()
+{
+ int cmd[2];
+ register int i, j;
+
+ master = getpid();
+
+ signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
+ signal(SIGPIPE, sigpipe);
+ signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
+ signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */
+
+ for (i = 0; i < SLAVES; i++) {
+ if (i == slp - &slaves[0]) {
+ caught = 1;
+ } else {
+ caught = 0;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
+ (slaves[i].pid = fork()) < 0)
+ quit("too many slaves, %d (recompile smaller): %s\n",
+ i, strerror(errno));
+
+ slaves[i].fd = cmd[1];
+ slaves[i].sent = 0;
+ if (slaves[i].pid == 0) { /* Slave starts up here */
+ for (j = 0; j <= i; j++)
+ (void) close(slaves[j].fd);
+ signal(SIGINT, SIG_IGN); /* Master handles this */
+ doslave(cmd[0], i);
+ Exit(X_FINOK);
+ }
+ }
+
+ for (i = 0; i < SLAVES; i++)
+ (void) atomic(write, slaves[i].fd,
+ (char *) &slaves[(i + 1) % SLAVES].pid,
+ sizeof slaves[0].pid);
+
+ master = 0;
+}
+
+void
+killall()
+{
+ register int i;
+
+ for (i = 0; i < SLAVES; i++)
+ if (slaves[i].pid > 0)
+ (void) kill(slaves[i].pid, SIGKILL);
+}
+
+/*
+ * Synchronization - each process has a lockfile, and shares file
+ * descriptors to the following process's lockfile. When our write
+ * completes, we release our lock on the following process's lock-
+ * file, allowing the following process to lock it and proceed. We
+ * get the lock back for the next cycle by swapping descriptors.
+ */
+static void
+doslave(cmd, slave_number)
+ register int cmd;
+ int slave_number;
+{
+ register int nread;
+ int nextslave, size, wrote, eot_count;
+ sigset_t sigset;
+
+ /*
+ * Need our own seek pointer.
+ */
+ (void) close(diskfd);
+ if ((diskfd = open(disk, O_RDONLY)) < 0)
+ quit("slave couldn't reopen disk: %s\n", strerror(errno));
+
+ /*
+ * Need the pid of the next slave in the loop...
+ */
+ if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave))
+ != sizeof nextslave) {
+ quit("master/slave protocol botched - didn't get pid of next slave.\n");
+ }
+
+ /*
+ * Get list of blocks to dump, read the blocks into tape buffer
+ */
+ while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
+ register struct req *p = slp->req;
+
+ for (trecno = 0; trecno < ntrec;
+ trecno += p->count, p += p->count) {
+ if (p->dblk) {
+ bread(p->dblk, slp->tblock[trecno],
+ p->count * TP_BSIZE);
+ } else {
+ if (p->count != 1 || atomic(read, cmd,
+ (char *)slp->tblock[trecno],
+ TP_BSIZE) != TP_BSIZE)
+ quit("master/slave protocol botched.\n");
+ }
+ }
+ if (setjmp(jmpbuf) == 0) {
+ ready = 1;
+ if (!caught)
+ (void) pause();
+ }
+ ready = 0;
+ caught = 0;
+
+ /* Try to write the data... */
+ eot_count = 0;
+ size = 0;
+
+ while (eot_count < 10 && size < writesize) {
+#ifdef RDUMP
+ if (host)
+ wrote = rmtwrite(slp->tblock[0]+size,
+ writesize-size);
+ else
+#endif
+ wrote = write(tapefd, slp->tblock[0]+size,
+ writesize-size);
+#ifdef WRITEDEBUG
+ printf("slave %d wrote %d\n", slave_number, wrote);
+#endif
+ if (wrote < 0)
+ break;
+ if (wrote == 0)
+ eot_count++;
+ size += wrote;
+ }
+
+#ifdef WRITEDEBUG
+ if (size != writesize)
+ printf("slave %d only wrote %d out of %d bytes and gave up.\n",
+ slave_number, size, writesize);
+#endif
+
+ if (eot_count > 0)
+ size = 0;
+
+ /*
+ * fixme: Pyramids running OSx return ENOSPC
+ * at EOT on 1/2 inch drives.
+ */
+ if (size < 0) {
+ (void) kill(master, SIGUSR1);
+ sigemptyset(&sigset);
+ for (;;)
+ sigsuspend(&sigset);
+ } else {
+ /*
+ * pass size of write back to master
+ * (for EOT handling)
+ */
+ (void) atomic(write, cmd, (char *)&size, sizeof size);
+ }
+
+ /*
+ * If partial write, don't want next slave to go.
+ * Also jolts him awake.
+ */
+ (void) kill(nextslave, SIGUSR2);
+ }
+ if (nread != 0)
+ quit("error reading command pipe: %s\n", strerror(errno));
+}
+
+/*
+ * 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)
+ ssize_t (*func)();
+ int fd;
+ char *buf;
+ int count;
+{
+ ssize_t got, need = count;
+
+ while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
+ buf += got;
+ return (got < 0 ? got : count - need);
+}
diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c
new file mode 100644
index 00000000000..5789e6d9ffd
--- /dev/null
+++ b/sbin/dump/traverse.c
@@ -0,0 +1,616 @@
+/* $NetBSD: traverse.c,v 1.14 1995/06/18 21:35:33 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)traverse.c 8.2 (Berkeley) 9/23/93";
+#else
+static char rcsid[] = "$NetBSD: traverse.c,v 1.14 1995/06/18 21:35:33 cgd Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/fsdir.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "dump.h"
+
+#define HASDUMPEDFILE 0x1
+#define HASSUBDIRS 0x2
+
+#ifdef FS_44INODEFMT
+typedef quad_t fsizeT;
+#else
+typedef long fsizeT;
+#endif
+
+static int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size));
+static void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size));
+static int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize));
+
+/*
+ * This is an estimation of the number of TP_BSIZE blocks in the file.
+ * It estimates the number of blocks in files with holes by assuming
+ * that all of the blocks accounted for by di_blocks are data blocks
+ * (when some of the blocks are usually used for indirect pointers);
+ * hence the estimate may be high.
+ */
+long
+blockest(dp)
+ register struct dinode *dp;
+{
+ long blkest, sizeest;
+
+ /*
+ * dp->di_size is the size of the file in bytes.
+ * dp->di_blocks stores the number of sectors actually in the file.
+ * If there are more sectors than the size would indicate, this just
+ * means that there are indirect blocks in the file or unused
+ * sectors in the last file block; we can safely ignore these
+ * (blkest = sizeest below).
+ * If the file is bigger than the number of sectors would indicate,
+ * then the file has holes in it. In this case we must use the
+ * block count to estimate the number of data blocks used, but
+ * we use the actual size for estimating the number of indirect
+ * dump blocks (sizeest vs. blkest in the indirect block
+ * calculation).
+ */
+ blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE);
+ sizeest = howmany(dp->di_size, TP_BSIZE);
+ if (blkest > sizeest)
+ blkest = sizeest;
+ if (dp->di_size > sblock->fs_bsize * NDADDR) {
+ /* calculate the number of indirect blocks on the dump tape */
+ blkest +=
+ howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
+ TP_NINDIR);
+ }
+ return (blkest + 1);
+}
+
+/* Auxiliary macro to pick up files changed since previous dump. */
+#define CHANGEDSINCE(dp, t) \
+ ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t))
+
+/* The WANTTODUMP macro decides whether a file should be dumped. */
+#ifdef UF_NODUMP
+#define WANTTODUMP(dp) \
+ (CHANGEDSINCE(dp, spcl.c_ddate) && \
+ (nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP))
+#else
+#define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
+#endif
+
+/*
+ * Dump pass 1.
+ *
+ * Walk the inode list for a filesystem to find all allocated inodes
+ * that have been modified since the previous dump time. Also, find all
+ * the directories in the filesystem.
+ */
+int
+mapfiles(maxino, tapesize)
+ ino_t maxino;
+ long *tapesize;
+{
+ register int mode;
+ register ino_t ino;
+ register struct dinode *dp;
+ int anydirskipped = 0;
+
+ for (ino = ROOTINO; ino < maxino; ino++) {
+ dp = getino(ino);
+ if ((mode = (dp->di_mode & IFMT)) == 0)
+ continue;
+ SETINO(ino, usedinomap);
+ if (mode == IFDIR)
+ SETINO(ino, dumpdirmap);
+ if (WANTTODUMP(dp)) {
+ SETINO(ino, dumpinomap);
+ if (mode != IFREG && mode != IFDIR && mode != IFLNK)
+ *tapesize += 1;
+ else
+ *tapesize += blockest(dp);
+ continue;
+ }
+ if (mode == IFDIR)
+ anydirskipped = 1;
+ }
+ /*
+ * Restore gets very upset if the root is not dumped,
+ * so ensure that it always is dumped.
+ */
+ SETINO(ROOTINO, dumpinomap);
+ return (anydirskipped);
+}
+
+/*
+ * Dump pass 2.
+ *
+ * Scan each directory on the filesystem to see if it has any modified
+ * files in it. If it does, and has not already been added to the dump
+ * list (because it was itself modified), then add it. If a directory
+ * has not been modified itself, contains no modified files and has no
+ * subdirectories, then it can be deleted from the dump list and from
+ * the list of directories. By deleting it from the list of directories,
+ * its parent may now qualify for the same treatment on this or a later
+ * pass using this algorithm.
+ */
+int
+mapdirs(maxino, tapesize)
+ ino_t maxino;
+ long *tapesize;
+{
+ register struct dinode *dp;
+ register int i, isdir;
+ register char *map;
+ register ino_t ino;
+ long filesize;
+ int ret, change = 0;
+
+ isdir = 0; /* XXX just to get gcc to shut up */
+ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */
+ isdir = *map++;
+ else
+ isdir >>= 1;
+ if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap))
+ continue;
+ dp = getino(ino);
+ filesize = dp->di_size;
+ for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
+ if (dp->di_db[i] != 0)
+ ret |= searchdir(ino, dp->di_db[i],
+ (long)dblksize(sblock, dp, i),
+ filesize);
+ if (ret & HASDUMPEDFILE)
+ filesize = 0;
+ else
+ filesize -= sblock->fs_bsize;
+ }
+ for (i = 0; filesize > 0 && i < NIADDR; i++) {
+ if (dp->di_ib[i] == 0)
+ continue;
+ ret |= dirindir(ino, dp->di_ib[i], i, &filesize);
+ }
+ if (ret & HASDUMPEDFILE) {
+ SETINO(ino, dumpinomap);
+ *tapesize += blockest(dp);
+ change = 1;
+ continue;
+ }
+ if ((ret & HASSUBDIRS) == 0) {
+ if (!TSTINO(ino, dumpinomap)) {
+ CLRINO(ino, dumpdirmap);
+ change = 1;
+ }
+ }
+ }
+ return (change);
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be searched
+ * as directories. Quit as soon as any entry is found that will
+ * require the directory to be dumped.
+ */
+static int
+dirindir(ino, blkno, ind_level, filesize)
+ ino_t ino;
+ daddr_t blkno;
+ int ind_level;
+ long *filesize;
+{
+ int ret = 0;
+ register int i;
+ daddr_t idblk[MAXNINDIR];
+
+ bread(fsbtodb(sblock, blkno), (char *)idblk, (int)sblock->fs_bsize);
+ if (ind_level <= 0) {
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ blkno = idblk[i];
+ if (blkno != 0)
+ ret |= searchdir(ino, blkno, sblock->fs_bsize,
+ *filesize);
+ if (ret & HASDUMPEDFILE)
+ *filesize = 0;
+ else
+ *filesize -= sblock->fs_bsize;
+ }
+ return (ret);
+ }
+ ind_level--;
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ blkno = idblk[i];
+ if (blkno != 0)
+ ret |= dirindir(ino, blkno, ind_level, filesize);
+ }
+ return (ret);
+}
+
+/*
+ * Scan a disk block containing directory information looking to see if
+ * any of the entries are on the dump list and to see if the directory
+ * contains any subdirectories.
+ */
+static int
+searchdir(ino, blkno, size, filesize)
+ ino_t ino;
+ daddr_t blkno;
+ register long size;
+ long filesize;
+{
+ register struct direct *dp;
+ register long loc, ret = 0;
+ char dblk[MAXBSIZE];
+
+ bread(fsbtodb(sblock, blkno), dblk, (int)size);
+ if (filesize < size)
+ size = filesize;
+ for (loc = 0; loc < size; ) {
+ dp = (struct direct *)(dblk + loc);
+ if (dp->d_reclen == 0) {
+ msg("corrupted directory, inumber %d\n", ino);
+ break;
+ }
+ loc += dp->d_reclen;
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_name[0] == '.') {
+ if (dp->d_name[1] == '\0')
+ continue;
+ if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
+ continue;
+ }
+ if (TSTINO(dp->d_ino, dumpinomap)) {
+ ret |= HASDUMPEDFILE;
+ if (ret & HASSUBDIRS)
+ break;
+ }
+ if (TSTINO(dp->d_ino, dumpdirmap)) {
+ ret |= HASSUBDIRS;
+ if (ret & HASDUMPEDFILE)
+ break;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Dump passes 3 and 4.
+ *
+ * Dump the contents of an inode to tape.
+ */
+void
+dumpino(dp, ino)
+ register struct dinode *dp;
+ ino_t ino;
+{
+ int ind_level, cnt;
+ fsizeT size;
+ char buf[TP_BSIZE];
+
+ if (newtape) {
+ newtape = 0;
+ dumpmap(dumpinomap, TS_BITS, ino);
+ }
+ CLRINO(ino, dumpinomap);
+ spcl.c_dinode = *dp;
+ spcl.c_type = TS_INODE;
+ spcl.c_count = 0;
+ switch (dp->di_mode & IFMT) {
+
+ case 0:
+ /*
+ * Freed inode.
+ */
+ return;
+
+ case IFLNK:
+ /*
+ * Check for short symbolic link.
+ */
+ if (dp->di_size > 0 &&
+#ifdef FS_44INODEFMT
+ (dp->di_size < sblock->fs_maxsymlinklen ||
+ (sblock->fs_maxsymlinklen == 0 && dp->di_blocks == 0))) {
+#else
+ dp->di_blocks == 0) {
+#endif
+ spcl.c_addr[0] = 1;
+ spcl.c_count = 1;
+ writeheader(ino);
+ memcpy(buf, dp->di_shortlink, (u_long)dp->di_size);
+ buf[dp->di_size] = '\0';
+ writerec(buf, 0);
+ return;
+ }
+ /* fall through */
+
+ case IFDIR:
+ case IFREG:
+ if (dp->di_size > 0)
+ break;
+ /* fall through */
+
+ case IFIFO:
+ case IFSOCK:
+ case IFCHR:
+ case IFBLK:
+ writeheader(ino);
+ return;
+
+ default:
+ msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT);
+ return;
+ }
+ if (dp->di_size > NDADDR * sblock->fs_bsize)
+ cnt = NDADDR * sblock->fs_frag;
+ else
+ cnt = howmany(dp->di_size, sblock->fs_fsize);
+ blksout(&dp->di_db[0], cnt, ino);
+ if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0)
+ return;
+ for (ind_level = 0; ind_level < NIADDR; ind_level++) {
+ dmpindir(ino, dp->di_ib[ind_level], ind_level, &size);
+ if (size <= 0)
+ return;
+ }
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be dumped.
+ */
+static void
+dmpindir(ino, blk, ind_level, size)
+ ino_t ino;
+ daddr_t blk;
+ int ind_level;
+ fsizeT *size;
+{
+ int i, cnt;
+ daddr_t idblk[MAXNINDIR];
+
+ if (blk != 0)
+ bread(fsbtodb(sblock, blk), (char *)idblk, (int) sblock->fs_bsize);
+ else
+ memset(idblk, 0, (int)sblock->fs_bsize);
+ if (ind_level <= 0) {
+ if (*size < NINDIR(sblock) * sblock->fs_bsize)
+ cnt = howmany(*size, sblock->fs_fsize);
+ else
+ cnt = NINDIR(sblock) * sblock->fs_frag;
+ *size -= NINDIR(sblock) * sblock->fs_bsize;
+ blksout(&idblk[0], cnt, ino);
+ return;
+ }
+ ind_level--;
+ for (i = 0; i < NINDIR(sblock); i++) {
+ dmpindir(ino, idblk[i], ind_level, size);
+ if (*size <= 0)
+ return;
+ }
+}
+
+/*
+ * Collect up the data into tape record sized buffers and output them.
+ */
+void
+blksout(blkp, frags, ino)
+ daddr_t *blkp;
+ int frags;
+ ino_t ino;
+{
+ register daddr_t *bp;
+ int i, j, count, blks, tbperdb;
+
+ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
+ tbperdb = sblock->fs_bsize >> tp_bshift;
+ for (i = 0; i < blks; i += TP_NINDIR) {
+ if (i + TP_NINDIR > blks)
+ count = blks;
+ else
+ count = i + TP_NINDIR;
+ for (j = i; j < count; j++)
+ if (blkp[j / tbperdb] != 0)
+ spcl.c_addr[j - i] = 1;
+ else
+ spcl.c_addr[j - i] = 0;
+ spcl.c_count = count - i;
+ writeheader(ino);
+ bp = &blkp[i / tbperdb];
+ for (j = i; j < count; j += tbperdb, bp++)
+ if (*bp != 0)
+ if (j + tbperdb <= count)
+ dumpblock(*bp, (int)sblock->fs_bsize);
+ else
+ dumpblock(*bp, (count - j) * TP_BSIZE);
+ spcl.c_type = TS_ADDR;
+ }
+}
+
+/*
+ * Dump a map to the tape.
+ */
+void
+dumpmap(map, type, ino)
+ char *map;
+ int type;
+ ino_t ino;
+{
+ register int i;
+ char *cp;
+
+ spcl.c_type = type;
+ spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
+ writeheader(ino);
+ for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
+ writerec(cp, 0);
+}
+
+/*
+ * Write a header record to the dump tape.
+ */
+void
+writeheader(ino)
+ ino_t ino;
+{
+ register long sum, cnt, *lp;
+
+ spcl.c_inumber = ino;
+ spcl.c_magic = NFS_MAGIC;
+ spcl.c_checksum = 0;
+ lp = (long *)&spcl;
+ sum = 0;
+ cnt = sizeof(union u_spcl) / (4 * sizeof(long));
+ while (--cnt >= 0) {
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ }
+ spcl.c_checksum = CHECKSUM - sum;
+ writerec((char *)&spcl, 1);
+}
+
+struct dinode *
+getino(inum)
+ ino_t inum;
+{
+ static daddr_t minino, maxino;
+ static struct dinode inoblock[MAXINOPB];
+
+ curino = inum;
+ if (inum >= minino && inum < maxino)
+ return (&inoblock[inum - minino]);
+ bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock,
+ (int)sblock->fs_bsize);
+ minino = inum - (inum % INOPB(sblock));
+ maxino = minino + INOPB(sblock);
+ return (&inoblock[inum - minino]);
+}
+
+/*
+ * Read a chunk of data from the disk.
+ * Try to recover from hard errors by reading in sector sized pieces.
+ * Error recovery is attempted at most BREADEMAX times before seeking
+ * consent from the operator to continue.
+ */
+int breaderrors = 0;
+#define BREADEMAX 32
+
+void
+bread(blkno, buf, size)
+ daddr_t blkno;
+ char *buf;
+ int size;
+{
+ int cnt, i;
+ extern int errno;
+
+loop:
+ if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
+ msg("bread: lseek fails\n");
+ if ((cnt = read(diskfd, buf, size)) == size)
+ return;
+ if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
+ /*
+ * Trying to read the final fragment.
+ *
+ * NB - dump only works in TP_BSIZE blocks, hence
+ * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
+ * It should be smarter about not actually trying to
+ * read more than it can get, but for the time being
+ * we punt and scale back the read only when it gets
+ * us into trouble. (mkm 9/25/83)
+ */
+ size -= dev_bsize;
+ goto loop;
+ }
+ if (cnt == -1)
+ msg("read error from %s: %s: [block %d]: count=%d\n",
+ disk, strerror(errno), blkno, size);
+ else
+ msg("short read error from %s: [block %d]: count=%d, got=%d\n",
+ disk, blkno, size, cnt);
+ if (++breaderrors > BREADEMAX) {
+ msg("More than %d block read errors from %d\n",
+ BREADEMAX, disk);
+ broadcast("DUMP IS AILING!\n");
+ msg("This is an unrecoverable error.\n");
+ if (!query("Do you want to attempt to continue?")){
+ dumpabort(0);
+ /*NOTREACHED*/
+ } else
+ breaderrors = 0;
+ }
+ /*
+ * Zero buffer, then try to read each sector of buffer separately.
+ */
+ memset(buf, 0, size);
+ for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
+ if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
+ msg("bread: lseek2 fails!\n");
+ if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize)
+ continue;
+ if (cnt == -1) {
+ msg("read error from %s: %s: [sector %d]: count=%d\n",
+ disk, strerror(errno), blkno, dev_bsize);
+ continue;
+ }
+ msg("short read error from %s: [sector %d]: count=%d, got=%d\n",
+ disk, blkno, dev_bsize, cnt);
+ }
+}
diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c
new file mode 100644
index 00000000000..0613246bddb
--- /dev/null
+++ b/sbin/dump/unctime.c
@@ -0,0 +1,112 @@
+/* $NetBSD: unctime.c,v 1.9 1995/03/18 14:55:11 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)unctime.c 8.2 (Berkeley) 6/14/94";
+#else
+static char rcsid[] = "$NetBSD: unctime.c,v 1.9 1995/03/18 14:55:11 cgd Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <time.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#ifndef __P
+#include <sys/cdefs.h>
+#endif
+
+/*
+ * Convert a ctime(3) format string into a system format date.
+ * Return the date thus calculated.
+ *
+ * Return -1 if the string is not in ctime format.
+ */
+
+/*
+ * Offsets into the ctime string to various parts.
+ */
+
+#define E_MONTH 4
+#define E_DAY 8
+#define E_HOUR 11
+#define E_MINUTE 14
+#define E_SECOND 17
+#define E_YEAR 20
+
+static int lookup __P((char *));
+
+
+time_t
+unctime(str)
+ char *str;
+{
+ struct tm then;
+ char dbuf[26];
+
+ (void) strncpy(dbuf, str, sizeof(dbuf) - 1);
+ dbuf[sizeof(dbuf) - 1] = '\0';
+ dbuf[E_MONTH+3] = '\0';
+ if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0)
+ return (-1);
+ then.tm_mday = atoi(&dbuf[E_DAY]);
+ then.tm_hour = atoi(&dbuf[E_HOUR]);
+ then.tm_min = atoi(&dbuf[E_MINUTE]);
+ then.tm_sec = atoi(&dbuf[E_SECOND]);
+ then.tm_year = atoi(&dbuf[E_YEAR]) - 1900;
+ then.tm_isdst = -1;
+ return(mktime(&then));
+}
+
+static char months[] =
+ "JanFebMarAprMayJunJulAugSepOctNovDec";
+
+static int
+lookup(str)
+ char *str;
+{
+ register char *cp, *cp2;
+
+ for (cp = months, cp2 = str; *cp != '\0'; cp += 3)
+ if (strncmp(cp, cp2, 3) == 0)
+ return((cp-months) / 3);
+ return(-1);
+}