]> git.wh0rd.org - dump.git/commitdiff
Initial revision
authorstelian <stelian>
Mon, 11 Oct 1999 12:20:19 +0000 (12:20 +0000)
committerstelian <stelian>
Mon, 11 Oct 1999 12:20:19 +0000 (12:20 +0000)
64 files changed:
CHANGES [new file with mode: 0644]
COPYRIGHT [new file with mode: 0644]
INSTALL [new file with mode: 0644]
KNOWNBUGS [new file with mode: 0644]
MCONFIG.in [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
THANKS [new file with mode: 0644]
TODO [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
common/Makefile.in [new file with mode: 0644]
common/dumprmt.c [new file with mode: 0644]
compat/include/bsdcompat.h [new file with mode: 0644]
compat/include/err.h [new file with mode: 0644]
compat/include/fstab.h [new file with mode: 0644]
compat/include/glob.h [new file with mode: 0644]
compat/include/pathnames.h [new file with mode: 0644]
compat/include/protocols/dumprestore.h [new file with mode: 0644]
compat/include/tzfile.h [new file with mode: 0644]
compat/lib/Makefile.in [new file with mode: 0644]
compat/lib/err.c [new file with mode: 0644]
compat/lib/fstab.c [new file with mode: 0644]
compat/lib/glob.c [new file with mode: 0644]
config.guess [new file with mode: 0755]
config.h.in [new file with mode: 0644]
config.sub [new file with mode: 0755]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
debian-patch [new file with mode: 0644]
depfix.sed [new file with mode: 0644]
dump-0.2.announce [new file with mode: 0644]
dump-0.2a.announce [new file with mode: 0644]
dump-0.2b.announce [new file with mode: 0644]
dump-0.2c.announce [new file with mode: 0644]
dump-0.2d.announce [new file with mode: 0644]
dump-0.2e.announce [new file with mode: 0644]
dump-0.3.announce [new file with mode: 0644]
dump-0.4b1.announce [new file with mode: 0644]
dump.lsm [new file with mode: 0644]
dump/Makefile.in [new file with mode: 0644]
dump/dump.8 [new file with mode: 0644]
dump/dump.h [new file with mode: 0644]
dump/itime.c [new file with mode: 0644]
dump/main.c [new file with mode: 0644]
dump/optr.c [new file with mode: 0644]
dump/tape.c [new file with mode: 0644]
dump/traverse.c [new file with mode: 0644]
dump/unctime.c [new file with mode: 0644]
install-sh [new file with mode: 0755]
linux-1.2.x.patch [new file with mode: 0644]
restore/Makefile.in [new file with mode: 0644]
restore/dirs.c [new file with mode: 0644]
restore/extern.h [new file with mode: 0644]
restore/interactive.c [new file with mode: 0644]
restore/main.c [new file with mode: 0644]
restore/restore.8 [new file with mode: 0644]
restore/restore.c [new file with mode: 0644]
restore/restore.h [new file with mode: 0644]
restore/symtab.c [new file with mode: 0644]
restore/tape.c [new file with mode: 0644]
restore/utilities.c [new file with mode: 0644]
rmt/Makefile.in [new file with mode: 0644]
rmt/rmt.8 [new file with mode: 0644]
rmt/rmt.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..301bf72
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,119 @@
+
+Changes between versions 0.4b3 and 0.4b4
+========================================
+
+1.     Dump now runs correctly on kernels 2.1.x
+       Fix made by Gerald Peters <gapeters@worldnet.att.net>
+
+Changes between versions 0.4b2 and 0.4b3
+========================================
+
+1.     Use realpath() if available
+
+2.     Report statistics
+
+Changes between versions 0.4b1 and 0.4b2
+========================================
+
+1.     Fixed the bug fix from Greg Lutz (I had made a mistake when integrating
+       the patch)
+
+2.     Fixed restore to make it able to read FreeBSD 2.x dumps again
+
+3.     Fixed configure.in to correctly handle --enable-rmt
+
+Changes between versions 0.3 and 0.4b1
+======================================
+
+1.     Integrated the changes from 4.4BSD-Lite2
+
+2.     Integrated the patches from Debian and Red Hat
+
+3.     Portability changes: use the __u32, __u16, __s32, and __s16 types
+
+4.     Changed dump to use the Ext2fs library to get block addresses.  This
+       should solve the endianness problem on SparcLinux.
+
+5.     Created a configure.in file (shamelessly stolen from the e2fsprogs
+       distribution's one) to use autoconf
+
+6.     Fixed a few minor bugs
+
+Changes between versions 0.2e and 0.2f
+======================================
+
+1.     Added the creation of named pipes (fifos) in restore.
+
+2.     Added the -N flag in the restore manual page.
+
+3.     Added the file kernel.patch which contains the llseek() optimization
+       patch for 1.2.x kernels.
+
+4.     Fixed a bug in the restoration of symbolic links: owner and group were
+       not restored.
+
+5.     Integrated some changes from FreeBSD 2.2.
+
+6.     Added a call to ftruncate() after restoring each file to restore
+       correctly files ending by a hole.
+
+Changes between versions 0.2d and 0.2e
+======================================
+
+1.     Fixed a bug in the "set owner/mode" process.  Every file was restored
+       with owner = root (0) and group = root/wheel/whatever (0).
+
+Changes between versions 0.2c and 0.2d
+======================================
+
+1.     Dump is now able to backup 2GB+ filesystems.
+
+2.     Dump and restore can now be linked as static binaries.
+
+Changes between versions 0.2b and 0.2c
+======================================
+
+1.     Fixed a bug when dumping ``slow'' (i.e. normal) symbolic links.
+
+Changes between versions 0.2a and 0.2b
+======================================
+
+1.     Really fixed the bug that I should have corrected in 0.2a.
+
+2.     Enabled optimization again.
+
+Changes between versions 0.2 and 0.2a
+=====================================
+
+1.     Disabled the optimization during compilation.
+
+Changes between versions 0.1 and 0.2
+====================================
+
+1.     Fixed a bug in fstab.c which caused a null pointer to be stored in
+       the fs_type field (actually, I modified the file fstab.c to make it
+       use the mntent functions).
+
+2.     Dump and restore now use a 4.3 BSD compatible dump format.  Backups
+       made by dump should be readable by the BSD restore and backups made
+       by the BSD dump should be readable by restore.  Unfortunately, this
+       means that the dump format has changed between version 0.1 and version
+       0.2 :-(
+
+3.     Dump is now able to backup a subtree, it is no longer limited to whole
+       filesystems like the BSD version.
+
+4.     Dump now uses ext2_llseek() so it is able to backup filesystems bigger
+       than 2 GB.
+
+Changes between versions 0.0 and 0.1
+====================================
+
+1.     Now create links rdump and rrestore during the `make install' step.
+
+2.     Linux port specific bugs added to the manual pages
+
+3.     Incorrect estimation of the number of tapes blocks fixed when doing
+       incremental backups.
+
+4.     Better ls-like format in restore in interactive mode.
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..ce4bd12
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,39 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995
+ *
+ */
+
+/*-
+ * 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.
+ */
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..c8caa1f
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,15 @@
+
+       Installation of the dump/restore port should be quite easy, since
+a configure script is now provided.
+
+       Configuration is made by running the configure script.  Run
+`configure --help' to get a list of options.
+
+       After configuring the package, you can type `make' to compile the
+programs, and `make install' to install the binaries in /sbin and the
+manual pages in `/usr/man/man8'.
+
+       You need to have the latest Ext2fs programs installed on your system
+since this version of dump uses the Ext2fs library written by Theodore T'so.
+The Ext2fs programs distribution is available from tsx-11.mit.edu in the
+directory /pub/linux/packages/ext2fs.
diff --git a/KNOWNBUGS b/KNOWNBUGS
new file mode 100644 (file)
index 0000000..61d3a3e
--- /dev/null
+++ b/KNOWNBUGS
@@ -0,0 +1,11 @@
+Known bugs and limitations of the dump/restore port
+===================================================
+
+1.     Multi-volume does not work: restore does not succeeds in
+       resynchronizing itself on a multi-volume dump.  Thus, some files are
+       not restored.
+
+2.     I have tried to minimize changes in the BSD source and some parts
+       may look ugly.  dump/tape.c is really awfull but this is pure BSD
+       code :-)  Maybe, I should replace it with a simpler and cleaner
+       version.
diff --git a/MCONFIG.in b/MCONFIG.in
new file mode 100644 (file)
index 0000000..f3ab85f
--- /dev/null
@@ -0,0 +1,97 @@
+VPATH=         $(srcdir)
+
+top_builddir=  @top_builddir@
+
+AR=            @AR@
+CC=            @CC@
+INSTALL=       @INSTALL@
+LD=            @LD@
+LN_S=          @LN_S@
+MV=            @MV@
+RANLIB=                @RANLIB@
+RM=            @RM@
+
+BINOWNER=      @BINOWNER@
+BINGRP=                @BINGRP@
+BINMODE=       @BINMODE@
+MANOWNER=      @MANOWNER@
+MANGRP=                @MANGRP@
+MANMODE=       @MANMODE@
+
+INSTALLBIN=    $(INSTALL) -o $(BINOWNER) -g $(BINGRP) -m $(BINMODE)
+INSTALLMAN=    $(INSTALL) -o $(MANOWNER) -g $(MANGRP) -m $(MANMODE)
+
+BINDIR=                /sbin
+MANDIR=                /usr/man/man8
+
+#
+# Global include directories
+#
+GINC=          -I/usr/include/bsd -I$(top_builddir) -I$(top_srcdir)/compat/include
+# indicate where the ext2fs library can be found (this is not needed if you
+# have run `make install-libs' in the e2fsprogs source directory).
+#GINC+=                -I/usr/src/e2fsprogs-0.5c/lib
+
+#
+# Global libraries
+#
+# indicate where the ext2fs library can be found (this is not needed if you
+# have run `make install-libs' in the e2fsprogs source directory).
+#GLIBDIR=      -L/usr/src/e2fsprogs-0.5c/lib
+GLIBS=         -lbsd $(GLIBDIR) -L../compat/lib -lcompat -lext2fs -lcom_err
+
+#
+# Definitions (don't change them unless you know what you are doing)
+#
+DEFS=          -DRDUMP -DRRESTORE -D_BSD_SOURCE -D_USE_BSD_SIGNAL -DLINUX_FORK_BUG
+
+all::
+
+#
+# Autoconf magic
+#
+
+$(top_builddir)/config.status: $(top_srcdir)/configure
+       (cd $(top_builddir); ./config.status --recheck)
+
+$(top_builddir)/MCONFIG:       $(top_srcdir)/MCONFIG.in \
+                               $(top_builddir)/config.status
+       (cd $(top_builddir); ./config.status)
+
+$(top_builddir)/config.h:      $(top_srcdir)/config.h.in \
+                               $(top_builddir)/config.status
+       (cd $(top_builddir); ./config.status)
+
+Makefile:                      $(srcdir)/Makefile.in $(top_builddir)/MCONFIG \
+                               $(top_builddir)/config.status
+       (cd $(top_builddir); ./config.status)
+
+$(top_srcdir)/configure:       $(top_srcdir)/configure.in
+       cd $(top_srcdir) && autoconf
+
+$(top_srcdir)/config.h.in:     $(top_srcdir)/configure.in
+       cd $(top_srcdir) && autoheader
+
+#
+# Make depend magic
+#
+
+.depend:                       Makefile $(SRCS) $(top_srcdir)/depfix.sed
+       if test -n "$(SRCS)" ; then \
+               $(CC) -M $(CFLAGS) $(SRCS) | \
+                       sed -f $(top_srcdir)/depfix.sed \
+                           -e 's; $(srcdir)/; $$(srcdir)/;g' \
+                           -e 's; $(top_srcdir)/; $$(top_srcdir)/;g' \
+                           -e 's; $(top_builddir)/; $$(top_builddir)/;g' \
+                           -e 's; \./; ;g' \
+                           -e '/^ *\\$$/d' > .depend; \
+       else :; fi
+
+depend::                       .depend
+       if test -n "$(SRCS)" ; then \
+               sed -e '/^# +++ Dependency line eater +++/,$$d' \
+                       < Makefile | cat - .depend \
+                       > Makefile.new; \
+               $(MV) Makefile Makefile.old; \
+               $(MV) Makefile.new Makefile; \
+       else :; fi
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..68e861e
--- /dev/null
@@ -0,0 +1,23 @@
+srcdir=                @srcdir@
+top_srcdir=    @top_srcdir@
+
+@MCONFIG@
+
+RM=            @RM@
+SUBDIRS=       compat/lib common dump restore @RMTDIR@
+
+all clean install dep depend realclean distclean::
+       for i in $(SUBDIRS); do \
+               (cd $$i && $(MAKE) $@) || exit 1; \
+       done
+
+distclean::
+       $(RM) -f config.status config.log config.cache
+       $(RM) -f MCONFIG Makefile config.h
+
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..24b7fc5
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+
+       This is a test release of dump 0.4.
+
+       See the file CHANGES for a list of changes from the previous version.
+
+       See the file INSTALL for installation instructions.
+
+       If you encounter problems with the dump and restore backup suite,
+please contact me (card@Linux.EU.Org) and send me a detailled bug report.
diff --git a/THANKS b/THANKS
new file mode 100644 (file)
index 0000000..1ffb24a
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,30 @@
+Dump and restore were written by the people of the CSRG at the University
+of California, Berkeley.
+
+They were ported to Linux by Remy Card <card@Linux.EU.Org>, by using
+the Ext2fs library, written by Theodore Ts'o <tytso@mit.edu>.
+
+Kevin Layer <layer@Franz.COM> added the comparison code in restore.
+
+Doug Paul <dbp@dragonsys.com> helped me to make dump able to backup 2GB+
+filesystems.
+
+David Frey <david@eos.lugs.ch> (the Debian dump maintainer) and the people
+from Red Hat Software provided lots of patches.
+
+Thanks to people who reported problems with the port, sent patches, and
+suggested various improvements.
+Here is a partial list of them (if I have forgotten someone, please complain):
+
+Henry Katz             hkatz@hkatz.dialup.access.net
+Klaus Kudielka         kkudielk@cacofonix.nt.tuwien.ac.at
+Florian La Roche       florian@jurix.jura.uni-sb.de
+Greg Lutz              greglutz@ix.netcom.com
+David Miller           davem@caip.rutgers.edu
+David Monro            davidm@cs.su.oz.au
+Lukas Nellen           L.Nellen@ThPhys.Uni-Heidelberg.DE
+Brent Olson            night@halcyon.com
+Bob Snyder             rsnyder@janet.advsys.com
+Stephen Tweedie                sct@dcs.ed.ac.uk
+Daniel Veillard                Daniel.Veillard@imag.fr
+Jason Venner           jason@idiom.com
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..03a2efc
--- /dev/null
+++ b/TODO
@@ -0,0 +1,13 @@
+1.     Fix the multi-volume problem (is there one after all?, I could not
+       reproduce it and some people told me that multi-volume dumps were Ok).
+
+2.     Make dump honor the no-dump attribute on directories.  Currently, it
+       honours it only on files.  This means that a directory flagged with
+       the no-dump attribute will be dumped if it contains a file without
+       the no-dump flag.
+
+3.     Change the way rdump and rrestore call rmt.  Currently, rcmd() is used
+       but it may be better to call rsh.  I have to look at the GNU tar code.
+
+4.     Make dump able to backup several directories and/or files in one
+       invocation, like the SunOS version.
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..761eb13
--- /dev/null
@@ -0,0 +1,5 @@
+/* Define to __s64 if <sys/types.h> does not define */
+#undef quad_t
+
+/* Define to __u64 if <sys/types.h> does not define */
+#undef u_quad_t
diff --git a/common/Makefile.in b/common/Makefile.in
new file mode 100644 (file)
index 0000000..d6a3348
--- /dev/null
@@ -0,0 +1,26 @@
+top_srcdir=    @top_srcdir@
+srcdir=                @srcdir@
+
+@MCONFIG@
+
+INC=           -I$(top_srcdir)/dump
+CFLAGS=                @CCOPTS@ -pipe $(GINC) $(INC) $(DEFS)
+SRCS=          dumprmt.c
+OBJS=          dumprmt.o
+
+all::          $(OBJS)
+
+install::
+
+clean::
+       $(RM) -f \#* *.s *.o *.a *~ core
+
+distclean::    clean
+       $(RM) -f Makefile Makefile.old .depend
+
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+
diff --git a/common/dumprmt.c b/common/dumprmt.c
new file mode 100644 (file)
index 0000000..cdb79c1
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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
+static char sccsid[] = "@(#)dumprmt.c  8.2 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mtio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dinode.h>
+#endif
+#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
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.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/compat/include/bsdcompat.h b/compat/include/bsdcompat.h
new file mode 100644 (file)
index 0000000..c697cab
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+#include <config.h>
+
+#define        __dead          volatile
+
+#ifndef        NBBY
+#define NBBY           8
+#endif
+
+#ifndef        MIN
+#define MIN(a,b)       ((a < b) ? a : b)
+#endif
+
+#define        WINO            ((ino_t)1)
+#define        DEV_BSIZE       512
+#define        DEV_BSHIFT      9
+#define        MAXBSIZE        EXT2_MAX_BLOCK_SIZE
+#define ROOTINO                EXT2_ROOT_INO
+#ifdef EXT2_NODUMP_FL
+#define UF_NODUMP      EXT2_NODUMP_FL
+#endif
+
+#define howmany(x,y)   (((x)+((y)-1))/(y))
+#define roundup(x, y)  ((((x)+((y)-1))/(y))*(y))
+#define powerof2(x)    ((((x)-1)&(x))==0)
+
+#define dbtob(b)       ((unsigned)(b) << DEV_BSHIFT)
+#define fsbtodb(sb,b)  ((int)(((long long)b * EXT2_BLOCK_SIZE(sb->super)) / DEV_BSIZE))
+
+#define        sblock          fs
+#define fs_fsize       fragsize
+#define fs_bsize       blocksize
+#define fs_size                super->s_blocks_count
+
+#define        IFMT            S_IFMT
+#define IFLNK          S_IFLNK
+#define IFREG          S_IFREG
+#define IFDIR          S_IFDIR
+#define IFCHR          S_IFCHR
+#define IFBLK          S_IFBLK
+#define IFSOCK         S_IFSOCK
+#define IFIFO          S_IFIFO
+
+#if 0
+typedef __s64          quad_t;
+typedef __u64          u_quad_t;
+#endif
+
+/*
+ * The BSD dump format reserves 4 bytes for a time_t, but other architectures
+ * (notably axp) have larger time_t.  ctime4() is a modified ctime() which
+ * always accepts short 4-byte times.  time4() is a similarly modified time().
+ */
+#define ctime4(timep) ({ time_t t = *(timep); ctime(&t); })
+#define time4(timep) ({time_t t; t = time(0); if (timep) *timep=t; t; })
+
+/*
+ * This is the ext2_inode structure but the fields have been renamed
+ * to match 4.4BSD's names
+ */
+#define        NDADDR          12
+#define        NIADDR           3
+
+#define NINDIR(fs)     EXT2_ADDR_PER_BLOCK(fs->super)
+
+struct dinode {
+       __u16   di_mode;
+       __u16   di_uid;
+       __u32   di_size;
+       __u32   di_atime;
+       __u32   di_ctime;
+       __u32   di_mtime;
+       __u32   di_dtime;
+       __u16   di_gid;
+       __u16   di_nlink;
+       __u32   di_blocks;
+       __u32   di_flags;
+       __u32   di_reserved1;
+       daddr_t di_db[NDADDR];
+       daddr_t di_ib[NIADDR];
+       __u32   di_gen;
+       __u32   di_file_acl;
+       __u32   di_dir_acl;
+       __u32   di_faddr;
+       __u8    di_frag;
+       __u8    di_fsize;
+       __u16   di_pad1;
+       __u32   di_spare[2];
+};
+
+#define di_rdev                di_db[0]
+/* #define di_ouid             di_uid */
+/* #define di_ogid             di_gid */
+
+/*
+ * This is the ext2_dir_entry structure but the fields have been renamed
+ * to match 4.4BSD's names
+ *
+ * This is the 4.4BSD directory entry structure
+ */
+#define DIRBLKSIZ      DEV_BSIZE
+#define MAXNAMLEN      255
+
+struct direct {
+       __u32   d_ino;
+       __u16   d_reclen;
+#if 1
+       __u8    d_namlen;
+       __u8    d_type;
+#else
+       __u16   d_namlen;
+#endif
+       char            d_name[MAXNAMLEN + 1];
+};
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN      0
+#define DT_FIFO                 1
+#define DT_CHR          2
+#define DT_DIR          4
+#define DT_BLK          6
+#define DT_REG          8
+#define DT_LNK         10
+#define DT_SOCK                12
+#define        DT_WHT          14
+
+/*
+ * Convert between stat structure types and directory types.
+ */
+#define IFTODT(mode)   (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype)        ((dirtype) << 12)
+
+/*
+ * The DIRSIZ macro gives the minimum record length which will hold
+ * the directory entry.  This requires the amount of space in struct direct
+ * without the d_name field, plus enough space for the name with a terminating
+ * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
+ */
+#if    0
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRSIZ(oldfmt, dp) \
+    ((oldfmt) ? \
+    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_type+1 + 3) &~ 3)) : \
+    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)))
+#else
+#define DIRSIZ(oldfmt, dp) \
+    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+#endif
+#else
+
+#define DIRSIZ(oldfmt,dp)      EXT2_DIR_REC_LEN(((dp)->d_namlen & 0xff) + 1)
+
+#endif
+
+/*
+ * This is the old (Net/2) BSD inode structure
+ * copied from the FreeBSD 1.1.5.1 <ufs/dinode.h> include file
+ */
+#define        MAXFASTLINK     (((NDADDR + NIADDR) * sizeof(unsigned long)) - 1)
+
+struct old_bsd_inode {
+       __u16           di_mode;
+       __s16           di_nlink;
+       __u16           di_uid;
+       __u16           di_gid;
+#if    1
+       union {
+               u_quad_t        v;
+               __u32           val[2];
+       }               di_qsize;
+#else
+       u_quad_t        di_size;
+#endif
+       __u32           di_atime;
+       __s32           di_atspare;
+       __u32           di_mtime;
+       __s32           di_mtspare;
+       __u32           di_ctime;
+       __s32           di_ctspare;
+#if    0
+       union {
+               struct {
+                       daddr_t di_udb[NDADDR];
+                       daddr_t di_uib[NIADDR];
+               } di_addr;
+               char    di_usymlink[MAXFASTLINK + 1];
+       }               di_un;
+#else
+       daddr_t         di_db[NDADDR];
+       daddr_t         di_ib[NIADDR];
+#endif
+       __s32           di_flags;
+       __s32           di_blocks;
+       __s32           di_gen;
+       __u32           di_spare[4];
+};
+
+/*
+ * This is the new (4.4) BSD inode structure
+ * copied from the FreeBSD 2.0 <ufs/ufs/dinode.h> include file
+ */
+struct new_bsd_inode {
+       __u16           di_mode;
+       __s16           di_nlink;
+       union {
+               __u16           oldids[2];
+               __u32           inumber;
+       }               di_u;
+       u_quad_t        di_size;
+       struct timeval  di_atime;
+       struct timeval  di_mtime;
+       struct timeval  di_ctime;
+       daddr_t         di_db[NDADDR];
+       daddr_t         di_ib[NIADDR];
+       __u32           di_flags;
+       __s32           di_blocks;
+       __s32           di_gen;
+       __u32           di_uid;
+       __u32           di_gid;
+       __s32           di_spare[2];
+};
+
+#define        di_ouid         di_u.oldids[0]
+#define        di_ogid         di_u.oldids[1]
diff --git a/compat/include/err.h b/compat/include/err.h
new file mode 100644 (file)
index 0000000..6b23d13
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * Copyright (c) 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.
+ *
+ *     @(#)err.h       8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _ERR_H_
+#define        _ERR_H_
+
+#include <sys/cdefs.h>
+
+#include <stdarg.h>
+
+#ifndef        _BSD_VA_LIST_
+#define _BSD_VA_LIST_          va_list
+#endif
+
+#ifndef        __dead
+#define __dead                 volatile
+#endif
+
+__BEGIN_DECLS
+__dead void    err __P((int, const char *, ...));
+__dead void    verr __P((int, const char *, _BSD_VA_LIST_));
+__dead void    errx __P((int, const char *, ...));
+__dead void    verrx __P((int, const char *, _BSD_VA_LIST_));
+void           warn __P((const char *, ...));
+void           vwarn __P((const char *, _BSD_VA_LIST_));
+void           warnx __P((const char *, ...));
+void           vwarnx __P((const char *, _BSD_VA_LIST_));
+__END_DECLS
+
+#endif /* !_ERR_H_ */
diff --git a/compat/include/fstab.h b/compat/include/fstab.h
new file mode 100644 (file)
index 0000000..5b00e7a
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * 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.
+ *
+ *     @(#)fstab.h     8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _FSTAB_H_
+#define _FSTAB_H_
+
+/*
+ * File system table, see fstab(5).
+ *
+ * Used by dump, mount, umount, swapon, fsck, df, ...
+ *
+ * For ufs fs_spec field is the block special name.  Programs that want to
+ * use the character special name must create that name by prepending a 'r'
+ * after the right most slash.  Quota files are always named "quotas", so
+ * if type is "rq", then use concatenation of fs_file and "quotas" to locate
+ * quota file.
+ */
+#define        _PATH_FSTAB     "/etc/fstab"
+#define        FSTAB           "/etc/fstab"    /* deprecated */
+
+#define FSTAB_DEF      "defaults"      /* default mount option */
+#define        FSTAB_RW        "rw"            /* read/write device */
+#define        FSTAB_RQ        "rq"            /* read/write with quotas */
+#define        FSTAB_RO        "ro"            /* read-only device */
+#define        FSTAB_SW        "sw"            /* swap device */
+#define        FSTAB_XX        "ignore"        /* ignore totally */
+
+struct fstab {
+       char    *fs_spec;               /* block special device name */
+       char    *fs_file;               /* file system path prefix */
+       char    *fs_vfstype;            /* File system type, ufs, nfs */
+       char    *fs_mntops;             /* Mount options ala -o */
+       char    *fs_type;               /* FSTAB_* from fs_mntops */
+       int     fs_freq;                /* dump frequency, in days */
+       int     fs_passno;              /* pass number on parallel dump */
+};
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct fstab *getfsent __P((void));
+struct fstab *getfsspec __P((const char *));
+struct fstab *getfsfile __P((const char *));
+int setfsent __P((void));
+void endfsent __P((void));
+__END_DECLS
+
+#endif /* !_FSTAB_H_ */
diff --git a/compat/include/glob.h b/compat/include/glob.h
new file mode 100644 (file)
index 0000000..5cb9c54
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * 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.
+ *
+ *     @(#)glob.h      8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _GLOB_H_
+#define        _GLOB_H_
+
+#include <sys/cdefs.h>
+
+struct stat;
+typedef struct {
+       int gl_pathc;           /* Count of total paths so far. */
+       int gl_matchc;          /* Count of paths matching pattern. */
+       int gl_offs;            /* Reserved at beginning of gl_pathv. */
+       int gl_flags;           /* Copy of flags parameter to glob. */
+       char **gl_pathv;        /* List of paths matching pattern. */
+                               /* Copy of errfunc parameter to glob. */
+       int (*gl_errfunc) __P((const char *, int));
+
+       /*
+        * Alternate filesystem access methods for glob; replacement
+        * versions of closedir(3), readdir(3), opendir(3), stat(2)
+        * and lstat(2).
+        */
+       void (*gl_closedir) __P((void *));
+       struct dirent *(*gl_readdir) __P((void *));     
+       void *(*gl_opendir) __P((const char *));
+       int (*gl_lstat) __P((const char *, struct stat *));
+       int (*gl_stat) __P((const char *, struct stat *));
+} glob_t;
+
+#define        GLOB_APPEND     0x0001  /* Append to output from previous call. */
+#define        GLOB_DOOFFS     0x0002  /* Use gl_offs. */
+#define        GLOB_ERR        0x0004  /* Return on error. */
+#define        GLOB_MARK       0x0008  /* Append / to matching directories. */
+#define        GLOB_NOCHECK    0x0010  /* Return pattern itself if nothing matches. */
+#define        GLOB_NOSORT     0x0020  /* Don't sort. */
+
+/* #ifndef _POSIX_SOURCE */
+#define        GLOB_ALTDIRFUNC 0x0040  /* Use alternately specified directory funcs. */
+#define        GLOB_BRACE      0x0080  /* Expand braces ala csh. */
+#define        GLOB_MAGCHAR    0x0100  /* Pattern had globbing characters. */
+#define        GLOB_NOMAGIC    0x0200  /* GLOB_NOCHECK without magic chars (csh). */
+#define        GLOB_QUOTE      0x0400  /* Quote special chars with \. */
+#define        GLOB_TILDE      0x0800  /* Expand tilde names from the passwd file. */
+/* #endif */
+
+#define        GLOB_NOSPACE    (-1)    /* Malloc call failed. */
+#define        GLOB_ABEND      (-2)    /* Unignored error. */
+
+__BEGIN_DECLS
+int    glob __P((const char *, int, int (*)(const char *, int), glob_t *));
+void   globfree __P((glob_t *));
+__END_DECLS
+
+#endif /* !_GLOB_H_ */
diff --git a/compat/include/pathnames.h b/compat/include/pathnames.h
new file mode 100644 (file)
index 0000000..a4f4267
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * 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       "rmt"
diff --git a/compat/include/protocols/dumprestore.h b/compat/include/protocols/dumprestore.h
new file mode 100644 (file)
index 0000000..ef74c39
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ *     @(#)dumprestore.h       8.2 (Berkeley) 1/21/94
+ */
+
+#ifndef _DUMPRESTORE_H_
+#define _DUMPRESTORE_H_
+
+/*
+ * TP_BSIZE is the size of file blocks on the dump tapes.
+ * Note that TP_BSIZE must be a multiple of DEV_BSIZE.
+ *
+ * NTREC is the number of TP_BSIZE blocks that are written
+ * in each tape record. HIGHDENSITYTREC is the number of
+ * TP_BSIZE blocks that are written in each tape record on
+ * 6250 BPI or higher density tapes.
+ *
+ * TP_NINDIR is the number of indirect pointers in a TS_INODE
+ * or TS_ADDR record. Note that it must be a power of two.
+ */
+#define TP_BSIZE       1024
+#define NTREC          10
+#define HIGHDENSITYTREC        32
+#define TP_NINDIR      (TP_BSIZE/2)
+#define LBLSIZE                16
+#define NAMELEN                64
+
+#define OFS_MAGIC      (int)60011
+#define NFS_MAGIC      (int)60012
+#define CHECKSUM       (int)84446
+
+union u_spcl {
+       char dummy[TP_BSIZE];
+       struct  s_spcl {
+               __s32   c_type;             /* record type (see below) */
+               __u32   c_date;             /* date of this dump */
+               __u32   c_ddate;            /* date of previous dump */
+               __s32   c_volume;           /* dump volume number */
+               daddr_t c_tapea;            /* logical block of this record */
+               ino_t   c_inumber;          /* number of inode */
+               __s32   c_magic;            /* magic number (see above) */
+               __s32   c_checksum;         /* record checksum */
+#ifdef __linux__
+               struct  new_bsd_inode c_dinode;
+#else
+               struct  dinode  c_dinode;   /* ownership and mode of inode */
+#endif
+               __s32   c_count;            /* number of valid c_addr entries */
+               char    c_addr[TP_NINDIR];  /* 1 => data; 0 => hole in inode */
+               char    c_label[LBLSIZE];   /* dump label */
+               __s32   c_level;            /* level of this dump */
+               char    c_filesys[NAMELEN]; /* name of dumpped file system */
+               char    c_dev[NAMELEN];     /* name of dumpped device */
+               char    c_host[NAMELEN];    /* name of dumpped host */
+               __s32   c_flags;            /* additional information */
+               __s32   c_firstrec;         /* first record on volume */
+               __s32   c_spare[32];        /* reserved for future uses */
+       } s_spcl;
+} u_spcl;
+#define spcl u_spcl.s_spcl
+/*
+ * special record types
+ */
+#define TS_TAPE        1       /* dump tape header */
+#define TS_INODE       2       /* beginning of file record */
+#define TS_ADDR        4       /* continuation of file record */
+#define TS_BITS        3       /* map of inodes on tape */
+#define TS_CLRI        6       /* map of inodes deleted since last dump */
+#define TS_END         5       /* end of volume marker */
+
+/*
+ * flag values
+ */
+#define DR_NEWHEADER   0x0001  /* new format tape header */
+#define DR_NEWINODEFMT 0x0002  /* new format inodes on tape */
+
+#define        DUMPOUTFMT      "%-16s %c %s"           /* for printf */
+                                               /* name, level, ctime(date) */
+#define        DUMPINFMT       "%16s %c %[^\n]\n"      /* inverse for scanf */
+
+#endif /* !_DUMPRESTORE_H_ */
diff --git a/compat/include/tzfile.h b/compat/include/tzfile.h
new file mode 100644 (file)
index 0000000..a9f934c
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Arthur David Olson of the National Cancer Institute.
+ *
+ * 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.
+ *
+ *     @(#)tzfile.h    8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _TZFILE_H_
+#define        _TZFILE_H_
+
+/*
+ * Information about time zone files.
+ */
+                       /* Time zone object file directory */
+#define TZDIR          "/usr/share/zoneinfo"
+#define TZDEFAULT      "/etc/localtime"
+#define TZDEFRULES     "posixrules"
+
+/*
+** Each file begins with. . .
+*/
+
+struct tzhead {
+       char    tzh_reserved[24];       /* reserved for future use */
+       char    tzh_ttisstdcnt[4];      /* coded number of trans. time flags */
+       char    tzh_leapcnt[4];         /* coded number of leap seconds */
+       char    tzh_timecnt[4];         /* coded number of transition times */
+       char    tzh_typecnt[4];         /* coded number of local time types */
+       char    tzh_charcnt[4];         /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+**     tzh_timecnt (char [4])s         coded transition times a la time(2)
+**     tzh_timecnt (unsigned char)s    types of local time starting at above
+**     tzh_typecnt repetitions of
+**             one (char [4])          coded GMT offset in seconds
+**             one (unsigned char)     used to set tm_isdst
+**             one (unsigned char)     that's an abbreviation list index
+**     tzh_charcnt (char)s             '\0'-terminated zone abbreviations
+**     tzh_leapcnt repetitions of
+**             one (char [4])          coded leap second transition times
+**             one (char [4])          total correction after above
+**     tzh_ttisstdcnt (char)s          indexed by type; if TRUE, transition
+**                                     time is standard time, if FALSE,
+**                                     transition time is wall clock time
+**                                     if absent, transition times are
+**                                     assumed to be wall clock time
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+/*
+** The TZ_MAX_TIMES value below is enough to handle a bit more than a
+** year's worth of solar time (corrected daily to the nearest second) or
+** 138 years of Pacific Presidential Election time
+** (where there are three time zone transitions every fourth year).
+*/
+#define TZ_MAX_TIMES   370
+
+#define NOSOLAR                        /* 4BSD doesn't currently handle solar time */
+
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES   256     /* Limited by what (unsigned char)'s can hold */
+#else
+#define TZ_MAX_TYPES   10      /* Maximum number of local time types */
+#endif
+
+#define TZ_MAX_CHARS   50      /* Maximum number of abbreviation characters */
+
+#define        TZ_MAX_LEAPS    50      /* Maximum number of leap second corrections */
+
+#define SECSPERMIN     60
+#define MINSPERHOUR    60
+#define HOURSPERDAY    24
+#define DAYSPERWEEK    7
+#define DAYSPERNYEAR   365
+#define DAYSPERLYEAR   366
+#define SECSPERHOUR    (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY     ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR    12
+
+#define TM_SUNDAY      0
+#define TM_MONDAY      1
+#define TM_TUESDAY     2
+#define TM_WEDNESDAY   3
+#define TM_THURSDAY    4
+#define TM_FRIDAY      5
+#define TM_SATURDAY    6
+
+#define TM_JANUARY     0
+#define TM_FEBRUARY    1
+#define TM_MARCH       2
+#define TM_APRIL       3
+#define TM_MAY         4
+#define TM_JUNE                5
+#define TM_JULY                6
+#define TM_AUGUST      7
+#define TM_SEPTEMBER   8
+#define TM_OCTOBER     9
+#define TM_NOVEMBER    10
+#define TM_DECEMBER    11
+
+#define TM_YEAR_BASE   1900
+
+#define EPOCH_YEAR     1970
+#define EPOCH_WDAY     TM_THURSDAY
+
+/*
+** Accurate only for the past couple of centuries;
+** that will probably do.
+*/
+
+#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
+
+#endif /* !_TZFILE_H_ */
diff --git a/compat/lib/Makefile.in b/compat/lib/Makefile.in
new file mode 100644 (file)
index 0000000..c5ba683
--- /dev/null
@@ -0,0 +1,31 @@
+top_srcdir=    @top_srcdir@
+srcdir=                @srcdir@
+
+@MCONFIG@
+
+INC=           -I$(top_srcdir)/compat/include
+CFLAGS=                @CCOPTS@ -pipe $(GINC) $(INC) $(DEFS)
+SRCS=          err.c fstab.c glob.c
+OBJS=          err.o fstab.o glob.o
+LIB=           libcompat.a
+
+all::          $(LIB)
+
+$(LIB):                $(OBJS)
+       $(AR) r $(LIB) $(OBJS)
+       $(RANLIB) $(LIB)
+
+install::
+
+clean::
+       $(RM) -f \#* *.s *.o *.a *~ core
+
+distclean::    clean
+       $(RM) -f Makefile Makefile.old .depend
+
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+
diff --git a/compat/lib/err.c b/compat/lib/err.c
new file mode 100644 (file)
index 0000000..71aaadc
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * Copyright (c) 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)err.c      8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <config.h>
+
+extern char *__progname;               /* Program name, from crt0. */
+
+#ifndef        HAVE_ERR
+__dead void
+#ifdef __STDC__
+err(int eval, const char *fmt, ...)
+#else
+err(eval, fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       verr(eval, fmt, ap);
+       va_end(ap);
+}
+#endif
+
+#ifndef        HAVE_VERR
+__dead void
+verr(eval, fmt, ap)
+       int eval;
+       const char *fmt;
+       va_list ap;
+{
+       int sverrno;
+
+       sverrno = errno;
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL) {
+               (void)vfprintf(stderr, fmt, ap);
+               (void)fprintf(stderr, ": ");
+       }
+       (void)fprintf(stderr, "%s\n", strerror(sverrno));
+       exit(eval);
+}
+#endif
+
+#ifndef        HAVE_ERRX
+__dead void
+#if __STDC__
+errx(int eval, const char *fmt, ...)
+#else
+errx(eval, fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       verrx(eval, fmt, ap);
+       va_end(ap);
+}
+#endif
+
+#ifndef        HAVE_VERRX
+__dead void
+verrx(eval, fmt, ap)
+       int eval;
+       const char *fmt;
+       va_list ap;
+{
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL)
+               (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
+       exit(eval);
+}
+#endif
+
+#ifndef        HAVE_WARN
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarn(fmt, ap);
+       va_end(ap);
+}
+#endif
+
+#ifndef        HAVE_VWARN
+void
+vwarn(fmt, ap)
+       const char *fmt;
+       va_list ap;
+{
+       int sverrno;
+
+       sverrno = errno;
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL) {
+               (void)vfprintf(stderr, fmt, ap);
+               (void)fprintf(stderr, ": ");
+       }
+       (void)fprintf(stderr, "%s\n", strerror(sverrno));
+}
+#endif
+
+#ifndef        HAVE_WARNX
+void
+#ifdef __STDC__
+warnx(const char *fmt, ...)
+#else
+warnx(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarnx(fmt, ap);
+       va_end(ap);
+}
+#endif
+
+#ifndef        HAVE_VWARNX
+void
+vwarnx(fmt, ap)
+       const char *fmt;
+       va_list ap;
+{
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL)
+               (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
+}
+#endif
diff --git a/compat/lib/fstab.c b/compat/lib/fstab.c
new file mode 100644 (file)
index 0000000..0cd2250
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)fstab.c    8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <errno.h>
+#include <fstab.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static FILE *_fs_fp;
+static struct fstab _fs_fstab;
+
+static void error __P((int));
+static int fstabscan __P((void));
+
+static
+int fstabscan()
+{
+       struct mntent   *mnt;
+       register char *cp;
+       int typexx;
+#define        MAXLINELENGTH   1024
+       char subline[MAXLINELENGTH];
+
+       for (;;) {
+               if (!(mnt = getmntent(_fs_fp)))
+                       return 0;
+
+               _fs_fstab.fs_spec = mnt->mnt_fsname;
+               _fs_fstab.fs_file = mnt->mnt_dir;
+               _fs_fstab.fs_vfstype = mnt->mnt_type;
+               _fs_fstab.fs_mntops = mnt->mnt_opts;
+               _fs_fstab.fs_type = FSTAB_RW;   /* rw by default under Linux */
+               _fs_fstab.fs_freq = mnt->mnt_freq;
+               _fs_fstab.fs_passno = mnt->mnt_passno;
+
+               strcpy(subline, _fs_fstab.fs_mntops);
+               for (typexx = 0, cp = strtok(subline, ","); cp;
+                    cp = strtok((char *)NULL, ",")) {
+                       if (!strcmp(cp, FSTAB_RW)) {
+                               _fs_fstab.fs_type = FSTAB_RW;
+                               break;
+                       }
+                       if (!strcmp(cp, FSTAB_RQ)) {
+                               _fs_fstab.fs_type = FSTAB_RQ;
+                               break;
+                       }
+                       if (!strcmp(cp, FSTAB_RO)) {
+                               _fs_fstab.fs_type = FSTAB_RO;
+                               break;
+                       }
+                       if (!strcmp(cp, FSTAB_SW)) {
+                               _fs_fstab.fs_type = FSTAB_SW;
+                               break;
+                       }
+                       if (!strcmp(cp, FSTAB_XX)) {
+                               _fs_fstab.fs_type = FSTAB_XX;
+                               typexx++;
+                               break;
+                       }
+               }
+               if (typexx)
+                       continue;
+
+               return 1;
+       }
+}
+
+struct fstab *
+getfsent()
+{
+       if (!_fs_fp && !setfsent() || !fstabscan())
+               return((struct fstab *)NULL);
+       return(&_fs_fstab);
+}
+
+struct fstab *
+getfsspec(name)
+       register const char *name;
+{
+       if (setfsent())
+               while (fstabscan())
+                       if (!strcmp(_fs_fstab.fs_spec, name))
+                               return(&_fs_fstab);
+       return((struct fstab *)NULL);
+}
+
+struct fstab *
+getfsfile(name)
+       register const char *name;
+{
+       if (setfsent())
+               while (fstabscan())
+                       if (!strcmp(_fs_fstab.fs_file, name))
+                               return(&_fs_fstab);
+       return((struct fstab *)NULL);
+}
+
+int setfsent()
+{
+       if (_fs_fp) {
+               rewind(_fs_fp);
+               return(1);
+       }
+       if ((_fs_fp = setmntent(_PATH_FSTAB, "r")))
+               return(1);
+       error(errno);
+       return(0);
+}
+
+void
+endfsent()
+{
+       if (_fs_fp) {
+               (void)endmntent(_fs_fp);
+               _fs_fp = NULL;
+       }
+}
+
+static
+void error(err)
+       int err;
+{
+       char *p;
+
+       (void)write(STDERR_FILENO, "fstab: ", 7);
+       (void)write(STDERR_FILENO, _PATH_FSTAB, sizeof(_PATH_FSTAB) - 1);
+       (void)write(STDERR_FILENO, ": ", 1);
+       p = strerror(err);
+       (void)write(STDERR_FILENO, p, strlen(p));
+       (void)write(STDERR_FILENO, "\n", 1);
+}
diff --git a/compat/lib/glob.c b/compat/lib/glob.c
new file mode 100644 (file)
index 0000000..ba6f22f
--- /dev/null
@@ -0,0 +1,853 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)glob.c     8.3 (Berkeley) 10/13/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * glob(3) -- a superset of the one defined in POSIX 1003.2.
+ *
+ * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
+ *
+ * Optional extra services, controlled by flags not defined by POSIX:
+ *
+ * GLOB_QUOTE:
+ *     Escaping convention: \ inhibits any special meaning the following
+ *     character might have (except \ at end of string is retained).
+ * GLOB_MAGCHAR:
+ *     Set in gl_flags if pattern contained a globbing character.
+ * GLOB_NOMAGIC:
+ *     Same as GLOB_NOCHECK, but it will only append pattern if it did
+ *     not contain any magic characters.  [Used in csh style globbing]
+ * GLOB_ALTDIRFUNC:
+ *     Use alternately specified directory access functions.
+ * GLOB_TILDE:
+ *     expand ~user/foo to the /home/dir/of/user/foo
+ * GLOB_BRACE:
+ *     expand {1,2}{a,b} to 1a 1b 2a 2b 
+ * gl_matchc:
+ *     Number of matches in the current invocation of glob.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <glob.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define        DOLLAR          '$'
+#define        DOT             '.'
+#define        EOS             '\0'
+#define        LBRACKET        '['
+#define        NOT             '!'
+#define        QUESTION        '?'
+#define        QUOTE           '\\'
+#define        RANGE           '-'
+#define        RBRACKET        ']'
+#define        SEP             '/'
+#define        STAR            '*'
+#define        TILDE           '~'
+#define        UNDERSCORE      '_'
+#define        LBRACE          '{'
+#define        RBRACE          '}'
+#define        SLASH           '/'
+#define        COMMA           ','
+
+#ifndef DEBUG
+
+#define        M_QUOTE         0x8000
+#define        M_PROTECT       0x4000
+#define        M_MASK          0xffff
+#define        M_ASCII         0x00ff
+
+typedef u_short Char;
+
+#else
+
+#define        M_QUOTE         0x80
+#define        M_PROTECT       0x40
+#define        M_MASK          0xff
+#define        M_ASCII         0x7f
+
+typedef char Char;
+
+#endif
+
+
+#define        CHAR(c)         ((Char)((c)&M_ASCII))
+#define        META(c)         ((Char)((c)|M_QUOTE))
+#define        M_ALL           META('*')
+#define        M_END           META(']')
+#define        M_NOT           META('!')
+#define        M_ONE           META('?')
+#define        M_RNG           META('-')
+#define        M_SET           META('[')
+#define        ismeta(c)       (((c)&M_QUOTE) != 0)
+
+
+static int      compare __P((const void *, const void *));
+static void     g_Ctoc __P((const Char *, char *));
+static int      g_lstat __P((Char *, struct stat *, glob_t *));
+static DIR     *g_opendir __P((Char *, glob_t *));
+static Char    *g_strchr __P((Char *, int));
+#ifdef notdef
+static Char    *g_strcat __P((Char *, const Char *));
+#endif
+static int      g_stat __P((Char *, struct stat *, glob_t *));
+static int      glob0 __P((const Char *, glob_t *));
+static int      glob1 __P((Char *, glob_t *));
+static int      glob2 __P((Char *, Char *, Char *, glob_t *));
+static int      glob3 __P((Char *, Char *, Char *, Char *, glob_t *));
+static int      globextend __P((const Char *, glob_t *));
+static const Char *     globtilde __P((const Char *, Char *, glob_t *));
+static int      globexp1 __P((const Char *, glob_t *));
+static int      globexp2 __P((const Char *, const Char *, glob_t *, int *));
+static int      match __P((Char *, Char *, Char *));
+#ifdef DEBUG
+static void     qprintf __P((const char *, Char *));
+#endif
+
+int
+glob(pattern, flags, errfunc, pglob)
+       const char *pattern;
+       int flags, (*errfunc) __P((const char *, int));
+       glob_t *pglob;
+{
+       const u_char *patnext;
+       int c;
+       Char *bufnext, *bufend, patbuf[MAXPATHLEN+1];
+
+       patnext = (u_char *) pattern;
+       if (!(flags & GLOB_APPEND)) {
+               pglob->gl_pathc = 0;
+               pglob->gl_pathv = NULL;
+               if (!(flags & GLOB_DOOFFS))
+                       pglob->gl_offs = 0;
+       }
+       pglob->gl_flags = flags & ~GLOB_MAGCHAR;
+       pglob->gl_errfunc = errfunc;
+       pglob->gl_matchc = 0;
+
+       bufnext = patbuf;
+       bufend = bufnext + MAXPATHLEN;
+       if (flags & GLOB_QUOTE) {
+               /* Protect the quoted characters. */
+               while (bufnext < bufend && (c = *patnext++) != EOS) 
+                       if (c == QUOTE) {
+                               if ((c = *patnext++) == EOS) {
+                                       c = QUOTE;
+                                       --patnext;
+                               }
+                               *bufnext++ = c | M_PROTECT;
+                       }
+                       else
+                               *bufnext++ = c;
+       }
+       else 
+           while (bufnext < bufend && (c = *patnext++) != EOS) 
+                   *bufnext++ = c;
+       *bufnext = EOS;
+
+       if (flags & GLOB_BRACE)
+           return globexp1(patbuf, pglob);
+       else
+           return glob0(patbuf, pglob);
+}
+
+/*
+ * Expand recursively a glob {} pattern. When there is no more expansion
+ * invoke the standard globbing routine to glob the rest of the magic
+ * characters
+ */
+static int globexp1(pattern, pglob)
+       const Char *pattern;
+       glob_t *pglob;
+{
+       const Char* ptr = pattern;
+       int rv;
+
+       /* Protect a single {}, for find(1), like csh */
+       if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
+               return glob0(pattern, pglob);
+
+       while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
+               if (!globexp2(ptr, pattern, pglob, &rv))
+                       return rv;
+
+       return glob0(pattern, pglob);
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes globexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int globexp2(ptr, pattern, pglob, rv)
+       const Char *ptr, *pattern;
+       glob_t *pglob;
+       int *rv;
+{
+       int     i;
+       Char   *lm, *ls;
+       const Char *pe, *pm, *pl;
+       Char    patbuf[MAXPATHLEN + 1];
+
+       /* copy part up to the brace */
+       for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
+               continue;
+       ls = lm;
+
+       /* Find the balanced brace */
+       for (i = 0, pe = ++ptr; *pe; pe++)
+               if (*pe == LBRACKET) {
+                       /* Ignore everything between [] */
+                       for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
+                               continue;
+                       if (*pe == EOS) {
+                               /* 
+                                * We could not find a matching RBRACKET.
+                                * Ignore and just look for RBRACE
+                                */
+                               pe = pm;
+                       }
+               }
+               else if (*pe == LBRACE)
+                       i++;
+               else if (*pe == RBRACE) {
+                       if (i == 0)
+                               break;
+                       i--;
+               }
+
+       /* Non matching braces; just glob the pattern */
+       if (i != 0 || *pe == EOS) {
+               *rv = glob0(patbuf, pglob);
+               return 0;
+       }
+
+       for (i = 0, pl = pm = ptr; pm <= pe; pm++)
+               switch (*pm) {
+               case LBRACKET:
+                       /* Ignore everything between [] */
+                       for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
+                               continue;
+                       if (*pm == EOS) {
+                               /* 
+                                * We could not find a matching RBRACKET.
+                                * Ignore and just look for RBRACE
+                                */
+                               pm = pl;
+                       }
+                       break;
+
+               case LBRACE:
+                       i++;
+                       break;
+
+               case RBRACE:
+                       if (i) {
+                           i--;
+                           break;
+                       }
+                       /* FALLTHROUGH */
+               case COMMA:
+                       if (i && *pm == COMMA)
+                               break;
+                       else {
+                               /* Append the current string */
+                               for (lm = ls; (pl < pm); *lm++ = *pl++)
+                                       continue;
+                               /* 
+                                * Append the rest of the pattern after the
+                                * closing brace
+                                */
+                               for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
+                                       continue;
+
+                               /* Expand the current pattern */
+#ifdef DEBUG
+                               qprintf("globexp2:", patbuf);
+#endif
+                               *rv = globexp1(patbuf, pglob);
+
+                               /* move after the comma, to the next string */
+                               pl = pm + 1;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       *rv = 0;
+       return 0;
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const Char *
+globtilde(pattern, patbuf, pglob)
+       const Char *pattern;
+       Char *patbuf;
+       glob_t *pglob;
+{
+       struct passwd *pwd;
+       char *h;
+       const Char *p;
+       Char *b;
+
+       if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
+               return pattern;
+
+       /* Copy up to the end of the string or / */
+       for (p = pattern + 1, h = (char *) patbuf; *p && *p != SLASH; 
+            *h++ = *p++)
+               continue;
+
+       *h = EOS;
+
+       if (((char *) patbuf)[0] == EOS) {
+               /* 
+                * handle a plain ~ or ~/ by expanding $HOME 
+                * first and then trying the password file
+                */
+               if ((h = getenv("HOME")) == NULL) {
+                       if ((pwd = getpwuid(getuid())) == NULL)
+                               return pattern;
+                       else
+                               h = pwd->pw_dir;
+               }
+       }
+       else {
+               /*
+                * Expand a ~user
+                */
+               if ((pwd = getpwnam((char*) patbuf)) == NULL)
+                       return pattern;
+               else
+                       h = pwd->pw_dir;
+       }
+
+       /* Copy the home directory */
+       for (b = patbuf; *h; *b++ = *h++)
+               continue;
+       
+       /* Append the rest of the pattern */
+       while ((*b++ = *p++) != EOS)
+               continue;
+
+       return patbuf;
+}
+       
+
+/*
+ * The main glob() routine: compiles the pattern (optionally processing
+ * quotes), calls glob1() to do the real pattern matching, and finally
+ * sorts the list (unless unsorted operation is requested).  Returns 0
+ * if things went well, nonzero if errors occurred.  It is not an error
+ * to find no matches.
+ */
+static int
+glob0(pattern, pglob)
+       const Char *pattern;
+       glob_t *pglob;
+{
+       const Char *qpatnext;
+       int c, err, oldpathc;
+       Char *bufnext, patbuf[MAXPATHLEN+1];
+
+       qpatnext = globtilde(pattern, patbuf, pglob);
+       oldpathc = pglob->gl_pathc;
+       bufnext = patbuf;
+
+       /* We don't need to check for buffer overflow any more. */
+       while ((c = *qpatnext++) != EOS) {
+               switch (c) {
+               case LBRACKET:
+                       c = *qpatnext;
+                       if (c == NOT)
+                               ++qpatnext;
+                       if (*qpatnext == EOS ||
+                           g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
+                               *bufnext++ = LBRACKET;
+                               if (c == NOT)
+                                       --qpatnext;
+                               break;
+                       }
+                       *bufnext++ = M_SET;
+                       if (c == NOT)
+                               *bufnext++ = M_NOT;
+                       c = *qpatnext++;
+                       do {
+                               *bufnext++ = CHAR(c);
+                               if (*qpatnext == RANGE &&
+                                   (c = qpatnext[1]) != RBRACKET) {
+                                       *bufnext++ = M_RNG;
+                                       *bufnext++ = CHAR(c);
+                                       qpatnext += 2;
+                               }
+                       } while ((c = *qpatnext++) != RBRACKET);
+                       pglob->gl_flags |= GLOB_MAGCHAR;
+                       *bufnext++ = M_END;
+                       break;
+               case QUESTION:
+                       pglob->gl_flags |= GLOB_MAGCHAR;
+                       *bufnext++ = M_ONE;
+                       break;
+               case STAR:
+                       pglob->gl_flags |= GLOB_MAGCHAR;
+                       /* collapse adjacent stars to one, 
+                        * to avoid exponential behavior
+                        */
+                       if (bufnext == patbuf || bufnext[-1] != M_ALL)
+                           *bufnext++ = M_ALL;
+                       break;
+               default:
+                       *bufnext++ = CHAR(c);
+                       break;
+               }
+       }
+       *bufnext = EOS;
+#ifdef DEBUG
+       qprintf("glob0:", patbuf);
+#endif
+
+       if ((err = glob1(patbuf, pglob)) != 0)
+               return(err);
+
+       /*
+        * If there was no match we are going to append the pattern 
+        * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
+        * and the pattern did not contain any magic characters
+        * GLOB_NOMAGIC is there just for compatibility with csh.
+        */
+       if (pglob->gl_pathc == oldpathc && 
+           ((pglob->gl_flags & GLOB_NOCHECK) || 
+             ((pglob->gl_flags & GLOB_NOMAGIC) &&
+              !(pglob->gl_flags & GLOB_MAGCHAR))))
+               return(globextend(pattern, pglob));
+       else if (!(pglob->gl_flags & GLOB_NOSORT)) 
+               qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
+                   pglob->gl_pathc - oldpathc, sizeof(char *), compare);
+       return(0);
+}
+
+static int
+compare(p, q)
+       const void *p, *q;
+{
+       return(strcmp(*(char **)p, *(char **)q));
+}
+
+static int
+glob1(pattern, pglob)
+       Char *pattern;
+       glob_t *pglob;
+{
+       Char pathbuf[MAXPATHLEN+1];
+
+       /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+       if (*pattern == EOS)
+               return(0);
+       return(glob2(pathbuf, pathbuf, pattern, pglob));
+}
+
+/*
+ * The functions glob2 and glob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+glob2(pathbuf, pathend, pattern, pglob)
+       Char *pathbuf, *pathend, *pattern;
+       glob_t *pglob;
+{
+       struct stat sb;
+       Char *p, *q;
+       int anymeta;
+
+       /*
+        * Loop over pattern segments until end of pattern or until
+        * segment with meta character found.
+        */
+       for (anymeta = 0;;) {
+               if (*pattern == EOS) {          /* End of pattern? */
+                       *pathend = EOS;
+                       if (g_lstat(pathbuf, &sb, pglob))
+                               return(0);
+               
+                       if (((pglob->gl_flags & GLOB_MARK) &&
+                           pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
+                           || (S_ISLNK(sb.st_mode) &&
+                           (g_stat(pathbuf, &sb, pglob) == 0) &&
+                           S_ISDIR(sb.st_mode)))) {
+                               *pathend++ = SEP;
+                               *pathend = EOS;
+                       }
+                       ++pglob->gl_matchc;
+                       return(globextend(pathbuf, pglob));
+               }
+
+               /* Find end of next segment, copy tentatively to pathend. */
+               q = pathend;
+               p = pattern;
+               while (*p != EOS && *p != SEP) {
+                       if (ismeta(*p))
+                               anymeta = 1;
+                       *q++ = *p++;
+               }
+
+               if (!anymeta) {         /* No expansion, do next segment. */
+                       pathend = q;
+                       pattern = p;
+                       while (*pattern == SEP)
+                               *pathend++ = *pattern++;
+               } else                  /* Need expansion, recurse. */
+                       return(glob3(pathbuf, pathend, pattern, p, pglob));
+       }
+       /* NOTREACHED */
+}
+
+static int
+glob3(pathbuf, pathend, pattern, restpattern, pglob)
+       Char *pathbuf, *pathend, *pattern, *restpattern;
+       glob_t *pglob;
+{
+       register struct dirent *dp;
+       DIR *dirp;
+       int err;
+       char buf[MAXPATHLEN];
+
+       /*
+        * The readdirfunc declaration can't be prototyped, because it is
+        * assigned, below, to two functions which are prototyped in glob.h
+        * and dirent.h as taking pointers to differently typed opaque
+        * structures.
+        */
+       struct dirent *(*readdirfunc)();
+
+       *pathend = EOS;
+       errno = 0;
+           
+       if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
+               /* TODO: don't call for ENOENT or ENOTDIR? */
+               if (pglob->gl_errfunc) {
+                       g_Ctoc(pathbuf, buf);
+                       if (pglob->gl_errfunc(buf, errno) ||
+                           pglob->gl_flags & GLOB_ERR)
+                               return (GLOB_ABEND);
+               }
+               return(0);
+       }
+
+       err = 0;
+
+       /* Search directory for matching names. */
+       if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+               readdirfunc = pglob->gl_readdir;
+       else
+               readdirfunc = readdir;
+       while ((dp = (*readdirfunc)(dirp))) {
+               register u_char *sc;
+               register Char *dc;
+
+               /* Initial DOT must be matched literally. */
+               if (dp->d_name[0] == DOT && *pattern != DOT)
+                       continue;
+               for (sc = (u_char *) dp->d_name, dc = pathend; 
+                    (*dc++ = *sc++) != EOS;)
+                       continue;
+               if (!match(pathend, pattern, restpattern)) {
+                       *pathend = EOS;
+                       continue;
+               }
+               err = glob2(pathbuf, --dc, restpattern, pglob);
+               if (err)
+                       break;
+       }
+
+       if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+               (*pglob->gl_closedir)(dirp);
+       else
+               closedir(dirp);
+       return(err);
+}
+
+
+/*
+ * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
+ * add the new item, and update gl_pathc.
+ *
+ * This assumes the BSD realloc, which only copies the block when its size
+ * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
+ * behavior.
+ *
+ * Return 0 if new item added, error code if memory couldn't be allocated.
+ *
+ * Invariant of the glob_t structure:
+ *     Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
+ *     gl_pathv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+globextend(path, pglob)
+       const Char *path;
+       glob_t *pglob;
+{
+       register char **pathv;
+       register int i;
+       u_int newsize;
+       char *copy;
+       const Char *p;
+
+       newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
+       pathv = pglob->gl_pathv ? 
+                   realloc((char *)pglob->gl_pathv, newsize) :
+                   malloc(newsize);
+       if (pathv == NULL)
+               return(GLOB_NOSPACE);
+
+       if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
+               /* first time around -- clear initial gl_offs items */
+               pathv += pglob->gl_offs;
+               for (i = pglob->gl_offs; --i >= 0; )
+                       *--pathv = NULL;
+       }
+       pglob->gl_pathv = pathv;
+
+       for (p = path; *p++;)
+               continue;
+       if ((copy = malloc(p - path)) != NULL) {
+               g_Ctoc(path, copy);
+               pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
+       }
+       pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+       return(copy == NULL ? GLOB_NOSPACE : 0);
+}
+
+
+/*
+ * pattern matching function for filenames.  Each occurrence of the *
+ * pattern causes a recursion level.
+ */
+static int
+match(name, pat, patend)
+       register Char *name, *pat, *patend;
+{
+       int ok, negate_range;
+       Char c, k;
+
+       while (pat < patend) {
+               c = *pat++;
+               switch (c & M_MASK) {
+               case M_ALL:
+                       if (pat == patend)
+                               return(1);
+                       do 
+                           if (match(name, pat, patend))
+                                   return(1);
+                       while (*name++ != EOS);
+                       return(0);
+               case M_ONE:
+                       if (*name++ == EOS)
+                               return(0);
+                       break;
+               case M_SET:
+                       ok = 0;
+                       if ((k = *name++) == EOS)
+                               return(0);
+                       if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
+                               ++pat;
+                       while (((c = *pat++) & M_MASK) != M_END)
+                               if ((*pat & M_MASK) == M_RNG) {
+                                       if (c <= k && k <= pat[1])
+                                               ok = 1;
+                                       pat += 2;
+                               } else if (c == k)
+                                       ok = 1;
+                       if (ok == negate_range)
+                               return(0);
+                       break;
+               default:
+                       if (*name++ != c)
+                               return(0);
+                       break;
+               }
+       }
+       return(*name == EOS);
+}
+
+/* Free allocated data belonging to a glob_t structure. */
+void
+globfree(pglob)
+       glob_t *pglob;
+{
+       register int i;
+       register char **pp;
+
+       if (pglob->gl_pathv != NULL) {
+               pp = pglob->gl_pathv + pglob->gl_offs;
+               for (i = pglob->gl_pathc; i--; ++pp)
+                       if (*pp)
+                               free(*pp);
+               free(pglob->gl_pathv);
+       }
+}
+
+static DIR *
+g_opendir(str, pglob)
+       register Char *str;
+       glob_t *pglob;
+{
+       char buf[MAXPATHLEN];
+
+       if (!*str)
+               strcpy(buf, ".");
+       else
+               g_Ctoc(str, buf);
+
+       if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+               return((*pglob->gl_opendir)(buf));
+
+       return(opendir(buf));
+}
+
+static int
+g_lstat(fn, sb, pglob)
+       register Char *fn;
+       struct stat *sb;
+       glob_t *pglob;
+{
+       char buf[MAXPATHLEN];
+
+       g_Ctoc(fn, buf);
+       if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+               return((*pglob->gl_lstat)(buf, sb));
+       return(lstat(buf, sb));
+}
+
+static int
+g_stat(fn, sb, pglob)
+       register Char *fn;
+       struct stat *sb;
+       glob_t *pglob;
+{
+       char buf[MAXPATHLEN];
+
+       g_Ctoc(fn, buf);
+       if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+               return((*pglob->gl_stat)(buf, sb));
+       return(stat(buf, sb));
+}
+
+static Char *
+g_strchr(str, ch)
+       Char *str;
+       int ch;
+{
+       do {
+               if (*str == ch)
+                       return (str);
+       } while (*str++);
+       return (NULL);
+}
+
+#ifdef notdef
+static Char *
+g_strcat(dst, src)
+       Char *dst;
+       const Char* src;
+{
+       Char *sdst = dst;
+
+       while (*dst++)
+               continue;
+       --dst;
+       while((*dst++ = *src++) != EOS)
+           continue;
+
+       return (sdst);
+}
+#endif
+
+static void
+g_Ctoc(str, buf)
+       register const Char *str;
+       char *buf;
+{
+       register char *dc;
+
+       for (dc = buf; (*dc++ = *str++) != EOS;)
+               continue;
+}
+
+#ifdef DEBUG
+static void 
+qprintf(str, s)
+       const char *str;
+       register Char *s;
+{
+       register Char *p;
+
+       (void)printf("%s:\n", str);
+       for (p = s; *p; p++)
+               (void)printf("%c", CHAR(*p));
+       (void)printf("\n");
+       for (p = s; *p; p++)
+               (void)printf("%c", *p & M_PROTECT ? '"' : ' ');
+       (void)printf("\n");
+       for (p = s; *p; p++)
+               (void)printf("%c", ismeta(*p) ? '_' : ' ');
+       (void)printf("\n");
+}
+#endif
diff --git a/config.guess b/config.guess
new file mode 100755 (executable)
index 0000000..2ff0eba
--- /dev/null
@@ -0,0 +1,565 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+trap 'rm -f dummy.c dummy.o dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    alpha:OSF1:V*:*)
+       # After 1.2, OSF1 uses "V1.3" for uname -r.
+       echo alpha-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^V//'`
+       exit 0 ;;
+    alpha:OSF1:*:*)
+       # 1.2 uses "1.2" for uname -r.
+       echo alpha-dec-osf${UNAME_RELEASE}
+        exit 0 ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit 0 ;;
+    amiga:NetBSD:*:*)
+      echo m68k-cbm-netbsd${UNAME_RELEASE}
+      exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit 0;;
+    Pyramid*:OSx*:*:*)
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit 0 ;;
+    sun4*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    i86pc:SunOS:5.*:*)
+       echo i386-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit 0 ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:NetBSD:*:*)
+       echo m68k-atari-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sun3*:NetBSD:*:*)
+       echo m68k-sun-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:NetBSD:*:*)
+       echo m68k-apple-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    mips:*:4*:UMIPS)
+       echo mips-mips-riscos4sysv
+       exit 0 ;;
+    mips:*:5*:RISCos)
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit 0 ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit 0 ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit 0 ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    AViiON:dgux:*:*)
+       if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
+            -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+       else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+       fi
+       exit 0 ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit 0 ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit 0 ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit 0 ;;
+   ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+       exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i[34]86:AIX:*:*)
+       echo i386-ibm-aix
+       exit 0 ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               sed 's/^                //' << EOF >dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0
+               rm -f dummy.c dummy
+               echo rs6000-ibm-aix3.2.5
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit 0 ;;
+    *:AIX:*:4)
+       if /usr/sbin/lsattr -EHl proc0 | grep POWER >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=4.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC NetBSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to 
+       exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit 0 ;;
+    9000/[3478]??:HP-UX:*:*)
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/7?? | 9000/8?[79] ) HP_ARCH=hppa1.1 ;;
+           9000/8?? )            HP_ARCH=hppa1.0 ;;
+       esac
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    3050*:HI-UX:*:*)
+       sed 's/^        //' << EOF >dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0
+       rm -f dummy.c dummy
+       echo unknown-hitachi-hiuxwe2
+       exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit 0 ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit 0 ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+       echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY*C90:*:*:*)
+       echo c90-cray-unicos${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY-2:*:*:*)
+       echo cray2-cray-unicos
+        exit 0 ;;
+    hp3[0-9][05]:NetBSD:*:*)
+       echo m68k-hp-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    i[34]86:BSD/386:*:* | *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:FreeBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit 0 ;;
+    *:NetBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    *:GNU:*:*)
+       echo `echo ${UNAME_MACHINE}|sed -e 's,/.*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit 0 ;;
+    *:Linux:*:*)
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us.
+       ld_help_string=`ld --help 2>&1`
+       if echo $ld_help_string | grep >/dev/null 2>&1 "supported emulations: elf_i[345]86"; then
+         echo "${UNAME_MACHINE}-unknown-linux" ; exit 0
+       elif echo $ld_help_string | grep >/dev/null 2>&1 "supported emulations: i[345]86linux"; then
+         echo "${UNAME_MACHINE}-unknown-linuxaout" ; exit 0
+       elif echo $ld_help_string | grep >/dev/null 2>&1 "supported emulations: i[345]86coff"; then
+         echo "${UNAME_MACHINE}-unknown-linuxcoff" ; exit 0
+       elif test "${UNAME_MACHINE}" = "alpha" ; then
+         echo alpha-unknown-linux ; exit 0
+       else
+         # Either a pre-BFD a.out linker (linuxoldld) or one that does not give us
+         # useful --help.  Gcc wants to distinguish between linuxoldld and linuxaout.
+         test ! -d /usr/lib/ldscripts/. \
+           && echo "${UNAME_MACHINE}-unknown-linuxoldld" && exit 0
+         # Determine whether the default compiler is a.out or elf
+         cat >dummy.c <<EOF
+main(argc, argv)
+int argc;
+char *argv[];
+{
+#ifdef __ELF__
+  printf ("%s-unknown-linux\n", argv[1]);
+#else
+  printf ("%s-unknown-linuxaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+         ${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy "${UNAME_MACHINE}" && rm dummy.c dummy && exit 0
+         rm -f dummy.c dummy
+       fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.  earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+    i[34]86:DYNIX/ptx:4*:*)
+       echo i386-sequent-sysv4
+       exit 0 ;;
+    i[34]86:*:4.*:* | i[34]86:SYSTEM_V:4.*:*)
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+       else
+               echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}
+       fi
+       exit 0 ;;
+    i[34]86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-unknown-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+               echo ${UNAME_MACHINE}-unknown-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-unknown-sysv32
+       fi
+       exit 0 ;;
+    Intel:Mach:3*:*)
+       echo i386-unknown-mach3
+       exit 0 ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit 0 ;;
+    M680[234]0:*:R3V[567]*:*)
+       test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0)
+        uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4.3 && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m680[234]0:LynxOS:2.[23]*:*)
+       echo m68k-lynx-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit 0 ;;
+    i[34]86:LynxOS:2.[23]*:*)
+       echo i386-lynx-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    TSUNAMI:LynxOS:2.[23]*:*)
+       echo sparc-lynx-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    rs6000:LynxOS:2.[23]*:*)
+       echo rs6000-lynx-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  printf ("%s-next-nextstep%s\n", __ARCHITECTURE__,  version==2 ? "2" : "3");
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-unknown-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+  printf ("vax-dec-bsd\n"); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy && rm dummy.c dummy && exit 0
+rm -f dummy.c dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit 0 ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit 0 ;;
+    c34*)
+       echo c34-convex-bsd
+       exit 0 ;;
+    c38*)
+       echo c38-convex-bsd
+       exit 0 ;;
+    c4*)
+       echo c4-convex-bsd
+       exit 0 ;;
+    esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..817ce66
--- /dev/null
@@ -0,0 +1,37 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define to __s64 if <sys/types.h> does not define */
+#undef quad_t
+
+/* Define to __u64 if <sys/types.h> does not define */
+#undef u_quad_t
+
+/* Define if you have the err function.  */
+#undef HAVE_ERR
+
+/* Define if you have the errx function.  */
+#undef HAVE_ERRX
+
+/* Define if you have the realpath function.  */
+#undef HAVE_REALPATH
+
+/* Define if you have the verr function.  */
+#undef HAVE_VERR
+
+/* Define if you have the verrx function.  */
+#undef HAVE_VERRX
+
+/* Define if you have the vwarn function.  */
+#undef HAVE_VWARN
+
+/* Define if you have the vwarnx function.  */
+#undef HAVE_VWARNX
+
+/* Define if you have the warn function.  */
+#undef HAVE_WARN
+
+/* Define if you have the warnx function.  */
+#undef HAVE_WARNX
diff --git a/config.sub b/config.sub
new file mode 100755 (executable)
index 0000000..93371be
--- /dev/null
@@ -0,0 +1,866 @@
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+#   Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can. 
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+       echo Configuration name missing. 1>&2
+       echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+       echo "or     $0 ALIAS" 1>&2
+       echo where ALIAS is a recognized configuration type. 1>&2
+       exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+       *local*)
+               echo $1
+               exit 0
+               ;;
+       *)
+       ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS (if any).
+basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+if [ $basic_machine != $1 ]
+then os=`echo $1 | sed 's/.*-/-/'`
+else os=; fi
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp )
+               os=
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       tahoe | i[345]86 | i860 | m68k | m68000 | m88k | ns32k | arm \
+               | arme[lb] | pyramid \
+               | tron | a29k | 580 | i960 | h8300 | hppa1.0 | hppa1.1 \
+               | alpha | we32k | ns16k | clipper | sparclite | i370 | sh \
+               | powerpc | powerpcle | sparc64 | 1750a | dsp16xx | mips64 | mipsel \
+               | pdp11 | mips64el | mips64orion | mips64orionel \
+               | sparc)
+               basic_machine=$basic_machine-unknown
+               ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       vax-* | tahoe-* | i[345]86-* | i860-* | m68k-* | m68000-* | m88k-* \
+             | sparc-* | ns32k-* | fx80-* | arm-* | c[123]* \
+             | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* | power-* \
+             | none-* | 580-* | cray2-* | h8300-* | i960-* | xmp-* | ymp-* \
+             | hppa1.0-* | hppa1.1-* | alpha-* | we32k-* | cydra-* | ns16k-* \
+             | pn-* | np1-* | xps100-* | clipper-* | orion-* | sparclite-* \
+             | pdp11-* | sh-* | powerpc-* | powerpcle-* | sparc64-* | mips64-* | mipsel-* \
+             | mips64el-* | mips64orion-* | mips64orionel-*)
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-cbm
+               ;;
+       amigados)
+               basic_machine=m68k-cbm
+               os=-amigados
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-cbm
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       cray2)
+               basic_machine=cray2-cray
+               os=-unicos
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7)
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i[345]86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+               os=-sysv32
+               ;;
+       i[345]86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+               os=-sysv4
+               ;;
+       i[345]86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+               os=-sysv
+               ;;
+       i[345]86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+               os=-solaris2
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+        pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentium | p5 | p6)
+               # We don't have specific support for the Intel Pentium (p6) followon yet, so just call it a Pentium
+               basic_machine=i586-intel
+               ;;
+       pentium-* | p5-* | p6-*)
+               # We don't have specific support for the Intel Pentium (p6) followon yet, so just call it a Pentium
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       k5)
+               # We don't have specific support for AMD's K5 yet, so just call it a Pentium
+               basic_machine=i586-amd
+               ;;
+       nexen)
+               # We don't have specific support for Nexgen yet, so just call it a Pentium
+               basic_machine=i586-nexgen
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=rs6000-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       xmp)
+               basic_machine=xmp-cray
+               os=-unicos
+               ;;
+        xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       mips)
+               basic_machine=mips-mips
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sparc)
+               basic_machine=sparc-sun
+               ;;
+        cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -unixware* | svr4*)
+               os=-sysv4
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[345]* \
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigados* | -msdos* | -newsos* | -unicos* | -aos* \
+             | -nindy* | -vxworks* | -ebmon* | -hms* | -mvs* | -clix* \
+             | -riscos* | -linux* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -netbsd* | -freebsd* | -riscix* \
+             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* )
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+        pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigados
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+        *-gould)
+               os=-sysv
+               ;;
+        *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+        *-sgi)
+               os=-irix
+               ;;
+        *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -lynxos*)
+                               vendor=lynx
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -vxworks*)
+                               vendor=wrs
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..53be0bb
--- /dev/null
+++ b/configure
@@ -0,0 +1,2144 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.12 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+  --enable-dumpdates-patch apply the dumpdates patch from Debian"
+ac_help="$ac_help
+  --enable-debug          include debugging code"
+ac_help="$ac_help
+  --enable-static         link dump and restore statically"
+ac_help="$ac_help
+  --enable-rmt            compile and install rmt"
+ac_help="$ac_help
+  --with-cc=COMPILER      select compiler to use"
+ac_help="$ac_help
+  --with-linker=LINKER    select linker to use"
+ac_help="$ac_help
+  --with-ccopts=CCOPTS    select compiler command line options"
+ac_help="$ac_help
+  --with-ldopts=LDOPTS    select linker command line options"
+ac_help="$ac_help
+  --with-binowner=USER    select owner for binaries"
+ac_help="$ac_help
+  --with-bingrp=GROUP     select group for binaries"
+ac_help="$ac_help
+  --with-binmode=MODE     select mode for binaries"
+ac_help="$ac_help
+  --with-manowner=USER    select owner for manual pages"
+ac_help="$ac_help
+  --with-mangrp=group     select group for manual pages"
+ac_help="$ac_help
+  --with-manmode=MODE     select mode for manual pages"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.12"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=dump/dump.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+MCONFIG=./MCONFIG
+
+
+
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:557: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+       @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
+echo "configure:584: checking whether ln -s works" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  rm -f conftestdata
+if ln -s X conftestdata 2>/dev/null
+then
+  rm -f conftestdata
+  ac_cv_prog_LN_S="ln -s"
+else
+  ac_cv_prog_LN_S=ln
+fi
+fi
+LN_S="$ac_cv_prog_LN_S"
+if test "$ac_cv_prog_LN_S" = "ln -s"; then
+  echo "$ac_t""yes" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:607: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_CP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$CP" in
+  /*)
+  ac_cv_path_CP="$CP" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_CP="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_CP" && ac_cv_path_CP="cp"
+  ;;
+esac
+fi
+CP="$ac_cv_path_CP"
+if test -n "$CP"; then
+  echo "$ac_t""$CP" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "mv", so it can be a program name with args.
+set dummy mv; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:639: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_MV'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$MV" in
+  /*)
+  ac_cv_path_MV="$MV" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_MV="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_MV" && ac_cv_path_MV="mv"
+  ;;
+esac
+fi
+MV="$ac_cv_path_MV"
+if test -n "$MV"; then
+  echo "$ac_t""$MV" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:671: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_RM'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$RM" in
+  /*)
+  ac_cv_path_RM="$RM" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_RM="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm"
+  ;;
+esac
+fi
+RM="$ac_cv_path_RM"
+if test -n "$RM"; then
+  echo "$ac_t""$RM" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+# Make sure we can run config.sub.
+if $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:726: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+  case $nonopt in
+  NONE)
+    if host_alias=`$ac_config_guess`; then :
+    else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+    fi ;;
+  *) host_alias=$nonopt ;;
+  esac ;;
+esac
+
+host=`$ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:747: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+  case $nonopt in
+  NONE) build_alias=$host_alias ;;
+  *) build_alias=$nonopt ;;
+  esac ;;
+esac
+
+build=`$ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+if test $host != $build; then
+  ac_tool_prefix=${host_alias}-
+else
+  ac_tool_prefix=
+fi
+
+# Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:773: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_AR="${ac_tool_prefix}ar"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+AR="$ac_cv_prog_AR"
+if test -n "$AR"; then
+  echo "$ac_t""$AR" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_AR"; then
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:804: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_AR="ar"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_AR" && ac_cv_prog_AR="ar"
+fi
+fi
+AR="$ac_cv_prog_AR"
+if test -n "$AR"; then
+  echo "$ac_t""$AR" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+else
+  AR="ar"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:838: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+  echo "$ac_t""$RANLIB" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_RANLIB"; then
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:869: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_RANLIB="ranlib"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+  echo "$ac_t""$RANLIB" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+else
+  RANLIB=":"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}patch", so it can be a program name with args.
+set dummy ${ac_tool_prefix}patch; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:903: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_PATCH'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$PATCH"; then
+  ac_cv_prog_PATCH="$PATCH" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_PATCH="${ac_tool_prefix}patch"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+PATCH="$ac_cv_prog_PATCH"
+if test -n "$PATCH"; then
+  echo "$ac_t""$PATCH" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_PATCH"; then
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "patch", so it can be a program name with args.
+set dummy patch; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:934: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_PATCH'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$PATCH"; then
+  ac_cv_prog_PATCH="$PATCH" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_PATCH="patch"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_PATCH" && ac_cv_prog_PATCH=":"
+fi
+fi
+PATCH="$ac_cv_prog_PATCH"
+if test -n "$PATCH"; then
+  echo "$ac_t""$PATCH" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+else
+  PATCH=":"
+fi
+fi
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:968: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:997: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  ac_prog_rejected=no
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:1045: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext <<EOF
+#line 1055 "configure"
+#include "confdefs.h"
+main(){return(0);}
+EOF
+if { (eval echo configure:1059: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:1079: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:1084: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:1093: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+  ac_test_CFLAGS="${CFLAGS+set}"
+  ac_save_CFLAGS="$CFLAGS"
+  CFLAGS=
+  echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:1108: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+  if test "$ac_test_CFLAGS" = set; then
+    CFLAGS="$ac_save_CFLAGS"
+  elif test $ac_cv_prog_cc_g = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-O2"
+  fi
+else
+  GCC=
+  test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:1146: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      for ac_prog in ginstall installbsd scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           # OSF/1 installbsd also uses dspmsg, but is usable.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+# Check whether --enable-dumpdates-patch or --disable-dumpdates-patch was given.
+if test "${enable_dumpdates_patch+set}" = set; then
+  enableval="$enable_dumpdates_patch"
+  if test "$enableval" = "yes"
+then
+       if test "$PATCH" = ":"
+       then
+               { echo "configure: error: The patch program was not found on your system" 1>&2; exit 1; }
+       fi
+       (cd $srcdir; $PATCH -p < debian-patch)
+fi
+
+fi
+
+
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+  enableval="$enable_debug"
+  if test "$enableval" = "no"
+then
+       DUMPDEBUG=""
+       RESTOREDEBUG=""
+else
+       DUMPDEBUG="-DFDEBUG -DTDEBUG -DWRITEDEBUG -DDIRDEBUG"
+       RESTOREDEBUG="-DDIRDEBUG"
+fi
+else
+  DUMPDEBUG=""
+RESTOREDEBUG=""
+
+fi
+
+
+
+
+# Check whether --enable-static or --disable-static was given.
+if test "${enable_static+set}" = set; then
+  enableval="$enable_static"
+  if test "$enableval" = "no"
+then
+       STATIC=""
+else
+       STATIC="-static"
+fi
+
+else
+  STATIC=""
+echo "Linking dump and restore dynamically by default"
+
+fi
+
+
+
+# Check whether --enable-rmt or --disable-rmt was given.
+if test "${enable_rmt+set}" = set; then
+  enableval="$enable_rmt"
+  if test "$enableval" = "no"
+then
+       RMTDIR=""
+       RMTMAKEFILE=""
+else
+       RMTDIR="rmt"
+       RMTMAKEFILE="rmt/Makefile"
+fi
+
+else
+  RMTDIR=""
+echo "Not compiling rmt by default"
+
+fi
+
+
+
+# Check whether --with-cc or --without-cc was given.
+if test "${with_cc+set}" = set; then
+  withval="$with_cc"
+  echo "$ac_t""CC=$withval" 1>&6
+CC=$withval
+else
+  if test -z "$CC" ; then CC=cc; fi
+echo "$ac_t""CC defaults to $CC" 1>&6
+fi
+export CC
+
+
+# Check whether --with-linker or --without-linker was given.
+if test "${with_linker+set}" = set; then
+  withval="$with_linker"
+  echo "$ac_t""LD=$withval" 1>&6
+LD=$withval
+else
+  if test -z "$LD" ; then LD=$CC; fi
+echo "$ac_t""LD defaults to $LD" 1>&6
+fi
+export LD
+
+
+# Check whether --with-ccopts or --without-ccopts was given.
+if test "${with_ccopts+set}" = set; then
+  withval="$with_ccopts"
+  echo "$ac_t""CCOPTS is $withval" 1>&6
+CCOPTS=$withval
+CFLAGS="$CFLAGS $withval"
+else
+  CCOPTS=
+fi
+
+
+# Check whether --with-ldopts or --without-ldopts was given.
+if test "${with_ldopts+set}" = set; then
+  withval="$with_ldopts"
+  echo "$ac_t""LDFLAGS is $withval" 1>&6
+LDFLAGS=$withval
+else
+  LDFLAGS=
+fi
+
+
+# Check whether --with-binowner or --without-binowner was given.
+if test "${with_binowner+set}" = set; then
+  withval="$with_binowner"
+  echo "$ac_t""BINOWNER is $withval" 1>&6
+BINOWNER=$withval
+else
+  BINOWNER=root
+echo "BINOWNER defaults to $BINOWNER"
+
+fi
+
+
+# Check whether --with-bingrp or --without-bingrp was given.
+if test "${with_bingrp+set}" = set; then
+  withval="$with_bingrp"
+  echo "$ac_t""BINGRP is $withval" 1>&6
+BINGRP=$withval
+else
+  BINGRP=tty
+echo "BINGRP defaults to $BINGRP"
+
+fi
+
+
+# Check whether --with-binmode or --without-binmode was given.
+if test "${with_binmode+set}" = set; then
+  withval="$with_binmode"
+  echo "$ac_t""BINMODE is $withval" 1>&6
+BINMODE=$withval
+else
+  BINMODE=6555
+echo "BINMODE defaults to $BINMODE"
+
+fi
+
+
+# Check whether --with-manowner or --without-manowner was given.
+if test "${with_manowner+set}" = set; then
+  withval="$with_manowner"
+  echo "$ac_t""MANOWNER is $withval" 1>&6
+MANOWNER=$withval
+else
+  MANOWNER=man
+echo "MANOWNER defaults to $MANOWNER"
+
+fi
+
+
+# Check whether --with-mangrp or --without-mangrp was given.
+if test "${with_mangrp+set}" = set; then
+  withval="$with_mangrp"
+  echo "$ac_t""MANGRP is $withval" 1>&6
+MANGRP=$withval
+else
+  MANGRP=tty
+echo "MANGRP defaults to $MANGRP"
+
+fi
+
+
+# Check whether --with-manmode or --without-manmode was given.
+if test "${with_manmode+set}" = set; then
+  withval="$with_manmode"
+  echo "$ac_t""MANMODE is $withval" 1>&6
+MANMODE=$withval
+else
+  MANMODE=0444
+echo "MANMODE defaults to $MANMODE"
+
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1387: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 1402 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1408: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 1419 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1425: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+ac_safe=`echo "ext2fs/ext2fs.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for ext2fs/ext2fs.h""... $ac_c" 1>&6
+echo "configure:1449: checking for ext2fs/ext2fs.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1454 "configure"
+#include "confdefs.h"
+#include <ext2fs/ext2fs.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1459: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  ext2fs_h=yes
+else
+  echo "$ac_t""no" 1>&6
+ext2fs_h=no
+fi
+
+echo $ac_n "checking for ext2fs_open in -lext2fs""... $ac_c" 1>&6
+echo "configure:1482: checking for ext2fs_open in -lext2fs" >&5
+ac_lib_var=`echo ext2fs'_'ext2fs_open | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lext2fs -lcom_err $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1490 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char ext2fs_open();
+
+int main() {
+ext2fs_open()
+; return 0; }
+EOF
+if { (eval echo configure:1501: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  ext2fs_lib=yes
+else
+  echo "$ac_t""no" 1>&6
+ext2fs_lib=no
+fi
+
+if test "$ext2fs_h" = no -o "$ext2fs_lib" = no; then
+       { echo "configure: error: You need to install the Ext2fs libraries from the E2fsprogs distribution first" 1>&2; exit 1; }
+fi
+
+for ac_func in err errx verr verrx vwarn vwarnx warn warnx realpath
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1529: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1534 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1557: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1583: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1588 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1596: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1613 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1631 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1652 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1663: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for quad_t""... $ac_c" 1>&6
+echo "configure:1687: checking for quad_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_quad_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1692 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "quad_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_quad_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_quad_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_quad_t" 1>&6
+if test $ac_cv_type_quad_t = no; then
+  cat >> confdefs.h <<\EOF
+#define quad_t __s64
+EOF
+
+fi
+
+echo $ac_n "checking for u_quad_t""... $ac_c" 1>&6
+echo "configure:1720: checking for u_quad_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_quad_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1725 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "u_quad_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_u_quad_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_u_quad_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_quad_t" 1>&6
+if test $ac_cv_type_u_quad_t = no; then
+  cat >> confdefs.h <<\EOF
+#define u_quad_t __u64
+EOF
+
+fi
+
+
+top_builddir=`cd .; pwd`
+
+
+test -d compat || mkdir compat
+test -d compat/lib || mkdir compat/lib
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.12"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "MCONFIG Makefile common/Makefile compat/lib/Makefile dump/Makefile restore/Makefile $RMTMAKEFILE config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+/@MCONFIG@/r $MCONFIG
+s%@MCONFIG@%%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@LN_S@%$LN_S%g
+s%@CP@%$CP%g
+s%@MV@%$MV%g
+s%@RM@%$RM%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@AR@%$AR%g
+s%@RANLIB@%$RANLIB%g
+s%@PATCH@%$PATCH%g
+s%@CC@%$CC%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@DUMPDEBUG@%$DUMPDEBUG%g
+s%@RESTOREDEBUG@%$RESTOREDEBUG%g
+s%@STATIC@%$STATIC%g
+s%@RMTDIR@%$RMTDIR%g
+s%@LD@%$LD%g
+s%@CCOPTS@%$CCOPTS%g
+s%@BINOWNER@%$BINOWNER%g
+s%@BINGRP@%$BINGRP%g
+s%@BINMODE@%$BINMODE%g
+s%@MANOWNER@%$MANOWNER%g
+s%@MANGRP@%$MANGRP%g
+s%@MANMODE@%$MANMODE%g
+s%@CPP@%$CPP%g
+s%@top_builddir@%$top_builddir%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"MCONFIG Makefile common/Makefile compat/lib/Makefile dump/Makefile restore/Makefile $RMTMAKEFILE"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..3ef741e
--- /dev/null
@@ -0,0 +1,245 @@
+AC_INIT(dump/dump.h)
+
+MCONFIG=./MCONFIG
+AC_SUBST_FILE(MCONFIG)
+
+AC_CONFIG_HEADER(config.h)
+
+dnl
+dnl Check for programs
+dnl
+AC_PROG_MAKE_SET
+AC_PROG_LN_S
+AC_PATH_PROG(CP, cp, cp)
+AC_PATH_PROG(MV, mv, mv)
+AC_PATH_PROG(RM, rm, rm)
+AC_CHECK_TOOL(AR, ar, ar)
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+AC_CHECK_TOOL(PATCH, patch, :)
+AC_PROG_CC
+AC_PROG_INSTALL
+
+dnl
+dnl Handle --enable-dumpdates-patch
+dnl
+AC_ARG_ENABLE([dumpdates-patch],
+[  --enable-dumpdates-patch apply the dumpdates patch from Debian],
+if test "$enableval" = "yes"
+then
+       if test "$PATCH" = ":"
+       then
+               AC_MSG_ERROR(The patch program was not found on your system)
+       fi
+       (cd $srcdir; $PATCH -p < debian-patch)
+fi
+,
+)
+
+dnl
+dnl Handle --enable-debug
+dnl
+AC_ARG_ENABLE([debug],
+[  --enable-debug          include debugging code],
+if test "$enableval" = "no"
+then
+       DUMPDEBUG=""
+       RESTOREDEBUG=""
+else
+       DUMPDEBUG="-DFDEBUG -DTDEBUG -DWRITEDEBUG -DDIRDEBUG"
+       RESTOREDEBUG="-DDIRDEBUG"
+fi,
+DUMPDEBUG=""
+RESTOREDEBUG=""
+)
+AC_SUBST(DUMPDEBUG)
+AC_SUBST(RESTOREDEBUG)
+
+dnl
+dnl Handle --enable-static
+dnl
+AC_ARG_ENABLE([static],
+[  --enable-static         link dump and restore statically],
+if test "$enableval" = "no"
+then
+       STATIC=""
+else
+       STATIC="-static"
+fi
+,
+STATIC=""
+echo "Linking dump and restore dynamically by default"
+)
+AC_SUBST(STATIC)
+
+dnl
+dnl Handle --enable-rmt
+dnl
+AC_ARG_ENABLE([rmt],
+[  --enable-rmt            compile and install rmt],
+if test "$enableval" = "no"
+then
+       RMTDIR=""
+       RMTMAKEFILE=""
+else
+       RMTDIR="rmt"
+       RMTMAKEFILE="rmt/Makefile"
+fi
+,
+RMTDIR=""
+echo "Not compiling rmt by default"
+)
+AC_SUBST(RMTDIR)
+
+dnl
+dnl set $(CC) from --with-cc=value
+dnl
+AC_ARG_WITH([cc],
+[  --with-cc=COMPILER      select compiler to use],
+AC_MSG_RESULT(CC=$withval)
+CC=$withval,
+if test -z "$CC" ; then CC=cc; fi
+[AC_MSG_RESULT(CC defaults to $CC)])dnl
+export CC
+AC_SUBST([CC])
+
+dnl
+dnl set $(LD) from --with-linker=value
+dnl
+AC_ARG_WITH([linker],
+[  --with-linker=LINKER    select linker to use],
+AC_MSG_RESULT(LD=$withval)
+LD=$withval,
+if test -z "$LD" ; then LD=$CC; fi
+[AC_MSG_RESULT(LD defaults to $LD)])dnl
+export LD
+AC_SUBST([LD])
+
+dnl
+dnl set $(CCOPTS) from --with-ccopts=value
+dnl
+AC_ARG_WITH([ccopts],
+[  --with-ccopts=CCOPTS    select compiler command line options],
+AC_MSG_RESULT(CCOPTS is $withval)
+CCOPTS=$withval
+CFLAGS="$CFLAGS $withval",
+CCOPTS=)dnl
+AC_SUBST(CCOPTS)
+
+dnl
+dnl set $(LDFLAGS) from --with-ldopts=value
+dnl
+AC_ARG_WITH([ldopts],
+[  --with-ldopts=LDOPTS    select linker command line options],
+AC_MSG_RESULT(LDFLAGS is $withval)
+LDFLAGS=$withval,
+LDFLAGS=)dnl
+AC_SUBST(LDFLAGS)
+
+dnl
+dnl set $(BINOWNER) from --with-binowner
+dnl
+AC_ARG_WITH([binowner],
+[  --with-binowner=USER    select owner for binaries],
+AC_MSG_RESULT(BINOWNER is $withval)
+BINOWNER=$withval,
+BINOWNER=root
+echo "BINOWNER defaults to $BINOWNER"
+)dnl
+AC_SUBST(BINOWNER)
+
+dnl
+dnl set $(BINGRP) from --with-bingrp
+dnl
+AC_ARG_WITH([bingrp],
+[  --with-bingrp=GROUP     select group for binaries],
+AC_MSG_RESULT(BINGRP is $withval)
+BINGRP=$withval,
+BINGRP=tty
+echo "BINGRP defaults to $BINGRP"
+)dnl
+AC_SUBST(BINGRP)
+
+dnl
+dnl set $(BINMODE) from --with-binmode
+dnl
+AC_ARG_WITH([binmode],
+[  --with-binmode=MODE     select mode for binaries],
+AC_MSG_RESULT(BINMODE is $withval)
+BINMODE=$withval,
+BINMODE=6555
+echo "BINMODE defaults to $BINMODE"
+)dnl
+AC_SUBST(BINMODE)
+
+dnl
+dnl set $(MANOWNER) from --with-manowner
+dnl
+AC_ARG_WITH([manowner],
+[  --with-manowner=USER    select owner for manual pages],
+AC_MSG_RESULT(MANOWNER is $withval)
+MANOWNER=$withval,
+MANOWNER=man
+echo "MANOWNER defaults to $MANOWNER"
+)dnl
+AC_SUBST(MANOWNER)
+
+dnl
+dnl set $(MANGRP) from --with-mangrp
+dnl
+AC_ARG_WITH([mangrp],
+[  --with-mangrp=group     select group for manual pages],
+AC_MSG_RESULT(MANGRP is $withval)
+MANGRP=$withval,
+MANGRP=tty
+echo "MANGRP defaults to $MANGRP"
+)dnl
+AC_SUBST(MANGRP)
+
+dnl
+dnl set $(MANMODE) from --with-manmode
+dnl
+AC_ARG_WITH([manmode],
+[  --with-manmode=MODE     select mode for manual pages],
+AC_MSG_RESULT(MANMODE is $withval)
+MANMODE=$withval,
+MANMODE=0444
+echo "MANMODE defaults to $MANMODE"
+)dnl
+AC_SUBST(MANMODE)
+
+dnl
+dnl Check for Ext2fs headers and libraries
+dnl
+AC_CHECK_HEADER(ext2fs/ext2fs.h, [ext2fs_h=yes], [ext2fs_h=no])
+AC_CHECK_LIB(ext2fs, ext2fs_open, [ext2fs_lib=yes], [ext2fs_lib=no], [-lcom_err])
+if test "$ext2fs_h" = no -o "$ext2fs_lib" = no; then
+       AC_MSG_ERROR(You need to install the Ext2fs libraries from the E2fsprogs distribution first)
+fi
+
+dnl
+dnl Check for library functions
+dnl
+AC_CHECK_FUNCS(err errx verr verrx vwarn vwarnx warn warnx realpath)
+
+dnl
+dnl Check for types
+dnl
+AC_CHECK_TYPE(quad_t, __s64)
+AC_CHECK_TYPE(u_quad_t, __u64)
+
+dnl
+dnl Compute top_buildir
+dnl
+top_builddir=`cd .; pwd`
+AC_SUBST(top_builddir)
+
+dnl
+dnl Create directories
+dnl
+test -d compat || mkdir compat
+test -d compat/lib || mkdir compat/lib
+
+dnl
+dnl Output files
+dnl
+AC_OUTPUT(MCONFIG Makefile common/Makefile compat/lib/Makefile dump/Makefile restore/Makefile $RMTMAKEFILE)
diff --git a/debian-patch b/debian-patch
new file mode 100644 (file)
index 0000000..b0349f2
--- /dev/null
@@ -0,0 +1,178 @@
+--- dump/dump.h.orig   Sun Dec 15 22:14:30 1996
++++ dump/dump.h        Wed Dec 25 16:11:06 1996
+@@ -40,6 +40,9 @@
+  *    @(#)dump.h      8.2 (Berkeley) 4/28/95
+  */
++/* /etc/dumpdates will not be created, when not already here (Bug#3806).
++   David Frey */
++
+ #define MAXINOPB      (MAXBSIZE / sizeof(struct dinode))
+ #define MAXNINDIR     (MAXBSIZE / sizeof(daddr_t))
+@@ -195,8 +198,8 @@
+ 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  initdumptimes __P((int));
++void  getdumptime __P((int));
+ void  putdumptime __P((void));
+ #define       ITITERATE(i, ddp) \
+       for (ddp = ddatev[i = 0]; i < nddates; ddp = ddatev[++i])
+--- dump/itime.c.orig  Sun Dec 15 22:14:30 1996
++++ dump/itime.c       Wed Dec 25 16:18:15 1996
+@@ -4,6 +4,8 @@
+  *    Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+  *
+  */
++/* /etc/dumpdates will not be created, when not already here (Bug#3806).
++   David Frey */
+ /*-
+  * Copyright (c) 1980, 1993
+@@ -88,7 +90,8 @@
+ static        void readdumptimes __P((FILE *));
+ void
+-initdumptimes()
++initdumptimes(createdumpdates)
++int createdumpdates;
+ {
+       FILE *df;
+@@ -101,22 +104,26 @@
+               /*
+                * 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 */
++              if (createdumpdates) {
++                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 */
++                }
+               }
++      }
++      if (df != NULL) {
++              (void) flock(fileno(df), LOCK_SH);
++              readdumptimes(df);
+               (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
+@@ -148,7 +155,8 @@
+ }
+ void
+-getdumptime()
++getdumptime(createdumpdates)
++int createdumpdates;
+ {
+       register struct dumpdates *ddp;
+       register int i;
+@@ -162,7 +170,9 @@
+       spcl.c_ddate = 0;
+       lastlevel = '0';
+-      initdumptimes();
++      initdumptimes(createdumpdates);
++      if (ddp == NULL)
++              return;
+       /*
+        *      Go find the entry with the same name for a lower increment
+        *      and older date
+--- dump/main.c.orig   Sun Dec 15 22:14:30 1996
++++ dump/main.c        Wed Dec 25 16:09:26 1996
+@@ -4,6 +4,8 @@
+  *    Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+  *
+  */
++/* /etc/dumpdates will not be created, when not already here (Bug#3806).
++   David Frey */
+ /*-
+  * Copyright (c) 1980, 1991, 1993, 1994
+@@ -331,7 +333,7 @@
+       spcl.c_level = level - '0';
+       spcl.c_type = TS_TAPE;
+       if (!Tflag)
+-              getdumptime();          /* /etc/dumpdates snarfed */
++              getdumptime(uflag);             /* /etc/dumpdates snarfed */
+       msg("Date of this level %c dump: %s", level,
+ #ifdef        __linux
+--- dump/optr.c.orig   Sun Dec 15 22:14:30 1996
++++ dump/optr.c        Wed Dec 25 16:09:26 1996
+@@ -4,6 +4,8 @@
+  *    Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+  *
+  */
++/* /etc/dumpdates will not be created, when not already here (Bug#3806).
++   David Frey */
+ /*-
+  * Copyright (c) 1980, 1988, 1993
+@@ -534,22 +536,24 @@
+ {
+       register int i;
+       register struct fstab *dt;
+-      register struct dumpdates *dtwalk;
++      register struct dumpdates *dtwalk=NULL;
+       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
++      initdumptimes(0);       /* /etc/dumpdates input */
++      if (*ddatev != NULL)
++      {
++        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) {
++        lastname = "??";
++        ITITERATE(i, dtwalk) {
+               if (strncmp(lastname, dtwalk->dd_name,
+                   sizeof(dtwalk->dd_name)) == 0)
+                       continue;
+@@ -568,6 +572,7 @@
+                           dt ? dt->fs_file : "",
+                           dtwalk->dd_level,
+                           date);
++        }
+       }
+ }
diff --git a/depfix.sed b/depfix.sed
new file mode 100644 (file)
index 0000000..1bcf112
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Insert the header.....
+#
+1i\
+# +++ Dependency line eater +++\
+# \
+# Makefile dependencies follow.  This must be the last section in\
+# the Makefile.in file\
+#
+
+#
+# Remove line continuations....
+#
+#:FIRST
+#y/    / /
+#s/^ *//
+#/\\$/{
+#N
+#y/    / /
+#s/\\\n */ /
+#bFIRST
+#}
+#s/  */ /g
+
+s;/usr/include/[^ ]* *;;g
+s;/usr/lib/[^ ]* *;;g
+s;/mit/cygnus[^ ]* *;;g
+
+#
+# Now insert a trailing newline...
+#
+$a\
+
diff --git a/dump-0.2.announce b/dump-0.2.announce
new file mode 100644 (file)
index 0000000..9ddec37
--- /dev/null
@@ -0,0 +1,41 @@
+
+       Finally...  The public test version of my port of the 4.4BSD dump
+and restore programs to Linux is now available on tsx-11.mit.edu in the
+directory /pub/linux/packages/ext2fs.
+
+       This port understands the Ext2fs structure and is able to backup
+and restore Linux Ext2 filesystems.  This is a test version and I have only
+made minimal testing so do not rely too much on it for your backups.
+
+       This version of dump contains all the BSD features and also includes
+some enhancements over the previous test versions:
+       - it is possible to backup a subtree instead of a whole filesystem,
+       - some bugs have been fixed :-)
+       - dump is now able to backup filesystems bigger than 2 GB,
+       - the dump format is now compatible with 4.3BSD.  This means that you
+         can restore Linux dumps on BSD based operating systems and you can
+         restore BSD dumps on Linux.  I have made some tests with FreeBSD
+         and SunOS 4.1.3 and this works fine.  I am interested in reports
+         about other BSD based operating systems.
+
+       I am not sure that multi-volume dumps are Ok.  If you use them, please
+send me a bug (or success :-) report.
+
+       The dump format is *not* compatible with the previous test versions
+(versions 0.0 and 0.1), which were distributed as private test versions.  If
+you have backups made by dump 0.0 or 0.1, restore 0.2 will not be able to
+read them.
+
+       You need the latest ext2fs programs source distribution to compile dump
+and restore because I have used Theodore T'so's Ext2fs library.  The latest
+version (0.5b) of these programs is available on tsx-11.mit.edu in the
+directory /pub/linux/packages/ext2fs.  If you want to build ELF binaries, you
+also need to get my ELF patch on ftp.ibp.fr in /pub/linux/ELF/patches.
+
+       I'd like to thank all the people who have used the private test
+versions of dump and restore.  I have appreciated their feedback that has
+helped me to improve dump and restore.
+
+--
+Remy Card
+Remy.Card@freenix.fr
diff --git a/dump-0.2a.announce b/dump-0.2a.announce
new file mode 100644 (file)
index 0000000..130ac8a
--- /dev/null
@@ -0,0 +1,27 @@
+
+       I have received bug reports about dump telling that dump receives
+unexpected signals when dump directories (in pass III).  I have tried to
+reproduce the problem and it appears that this may be caused by an optimization
+problem in some versions of gcc.  Dump works fine when compiled with
+optimization by gcc 2.6.2 but it fails when compiled by gcc 2.6.3:
+       - either it gets an unexpected signal (SIGFPE),
+       - or it issues warnings ``Warning: dumpdirino called with file type
+         040000'' and does not dump directories.
+
+       I am not courageous enough to study the assembly code generated by
+gcc (yet :-), so I have modified the Makefiles to compile dump without
+optimizations.  I have also fixed a few minor bugs and removed some printf's
+that I had added during my tests.  I will study the assembly code generated
+by gcc soon and fix the problem.
+
+       The fixed version (numbered 0.2a) is now available on tsx-11.mit.edu
+in the directory /pub/linux/packages/ext2fs.  Please get it if you are using
+dump and restore.
+
+       Thanks to Daniel Veillard <Daniel.Veillard@imag.fr>, Bob Snyder
+<rsnyder@janet.advsys.com>, and Florian La Roche <rzsfl@rz.uni-sb.de> for
+their reports and their help!
+
+--
+Remy Card
+Remy.Card@freenix.fr
diff --git a/dump-0.2b.announce b/dump-0.2b.announce
new file mode 100644 (file)
index 0000000..b378ec4
--- /dev/null
@@ -0,0 +1,28 @@
+
+       I have received bug reports about dump telling that dump receives
+unexpected signals when dump directories (in pass III).  I have tried to
+reproduce the problem and it appears that this may be caused by an optimization
+problem in some versions of gcc.  Dump works fine when compiled with
+optimization by gcc 2.6.2 but it fails when compiled by gcc 2.6.3:
+       - either it gets an unexpected signal (SIGFPE),
+       - or it issues warnings ``Warning: dumpdirino called with file type
+         040000'' and does not dump directories.
+
+       Finding the problem was not an easy task because the behavior seemed
+to be related to the version of the compiler.  Anyway, I have found it :-)
+There was an incorrect assignement in the function dumpdirino(), which is
+responsible for dumping the contents of directories, and this caused some
+problems when compiled with optimizations by some versions of gcc.  After
+fixing the problem, dump runs fine when compiled by gcc 2.6.3 or gcc 2.7.0.
+
+       The new fixed version (numbered 0.2b) is now available on
+tsx-11.mit.edu in the directory /pub/linux/packages/ext2fs.  Please get it
+if you are using dump and restore.
+
+       Thanks to Daniel Veillard <Daniel.Veillard@imag.fr>, Bob Snyder
+<rsnyder@janet.advsys.com>, and Florian La Roche <rzsfl@rz.uni-sb.de> for
+their reports and their help!
+
+--
+Remy Card
+Remy.Card@freenix.fr
diff --git a/dump-0.2c.announce b/dump-0.2c.announce
new file mode 100644 (file)
index 0000000..d0da6b7
--- /dev/null
@@ -0,0 +1,12 @@
+
+       Yet another bug fix release of dump/restore for Ext2fs is available
+on tsx-11.mit.edu in /pub/linux/packages/ext2fs/dump-0.2c.tar.gz.
+
+       A stupid bug has been fixed.  This bug caused an incorrect dump of
+``slow'' (i.e. normal) symbolic links.  There is no new features (yet :-).
+
+       Thanks to the many people who have sent me success and bug reports!
+
+--
+Remy Card
+Remy.Card@freenix.fr
diff --git a/dump-0.2d.announce b/dump-0.2d.announce
new file mode 100644 (file)
index 0000000..afc10d5
--- /dev/null
@@ -0,0 +1,12 @@
+
+       Ok, I am still trying to saturate comp.os.linux.announce :-)  Yet
+another bug fix release of dump/restore for Ext2fs is available on
+tsx-11.mit.edu in /pub/linux/packages/ext2fs/dump-0.2d.tar.gz.
+
+       Dump is now able to backup 2GB+ filesystems.  Thanks to Doug Paul
+<dbp@dragonsys.com> for the patch.  Dump and restore can now be linked as
+static binaries (you have to edit the file MCONFIG before building them).
+
+--
+Remy Card
+Remy.Card@freenix.fr
diff --git a/dump-0.2e.announce b/dump-0.2e.announce
new file mode 100644 (file)
index 0000000..cdf5c0b
--- /dev/null
@@ -0,0 +1,19 @@
+
+       Dump 0.2e is now available on tsx-11.mit.edu in the directory
+/pub/linux/packages/ext2fs.
+
+       A stupid bug in the "set owner/mode" pass has been fixed.  Files were
+restored with uid=0 and gid=0.  The bug was in restore, not in dump, so this
+means that backups made with dump 0.2d are Ok.  Thanks to Brent Olson
+<night@halcyon.com> for reporting this problem.
+
+       Thanks also to the patience of the testers :-)
+
+       I remind you that dump and restore should be considered as software
+in BETA test.  Don't rely too much on them for your backups...
+
+--
+Remy Card
+Remy.Card@freenix.fr
+
+No to french nuclear experiments!
diff --git a/dump-0.3.announce b/dump-0.3.announce
new file mode 100644 (file)
index 0000000..c5ebc2a
--- /dev/null
@@ -0,0 +1,32 @@
+
+       Ok, I'm back ;-)
+
+       Dump 0.3 is now available on tsx-11.mit.edu in the directory
+/pub/linux/packages/ext2fs.
+
+       Restore is now able to restore named pipes (fifos).  Thanks to
+Jason Venner <jason@idiom.com> for reporting the bug.  I have also fixed
+a bug in the symlink restoration: owner and group were not set by previous
+version.  Thanks to Klaus Kudielka <kkudielk@cacofonix.nt.tuwien.ac.at> for
+reporting it.  Restore is now able to restore correctly files ending with
+a hole (mainly DLL shared libraries under Linux).  Thanks to David Monro
+<davidm@cs.su.oz.au> for sending me a patch much cleaner than mine :-)
+
+       Kevin Layer <layer@Franz.COM> has implemented a verify option in
+restore.  It is now possible to check a dump against the contents of the
+filesystem.  Thank you very much, Kevin!
+
+       Some bug fixes from FreeBSD 2.2-current have also been integrated.
+
+       Last, I have added the file `linux-1.2.x.patch' in the distribution. 
+This patch for 1.2 kernels should be applied if you want to get better
+performances.
+
+       I remind you that dump and restore should be considered as software
+in BETA test.  Don't rely too much on them for your backups...
+
+--
+Remy Card
+Remy.Card@freenix.fr
+
+No to nuclear testings!
diff --git a/dump-0.4b1.announce b/dump-0.4b1.announce
new file mode 100644 (file)
index 0000000..9054672
--- /dev/null
@@ -0,0 +1,35 @@
+
+       A test release of dump 0.4 is now available on tsx-11.mit.edu,
+in the file /pub/linux/ALPHA/ext2fs/dump-0.4b1.tar.gz.
+
+       This new version is based on the latest dump and restore suite
+available from Berkeley (I have integrated the changes from 4.4BSD-Lite2).
+
+       It also includes lots of changes from the Debian 1.2 and Red Hat 4.0
+distributions (I have included most of the patches made by maintainers of
+these distributions).
+
+       Some changes were made to improve the portability of dump and restore:
+       - fixed size types are now used, so that the dump format is the same
+         on every architecture supported by Linux,
+       - dump now uses the Ext2fs library to get the block addresses
+         to solve an endianness problem that was present on SparcLinux.
+
+       Autoconf is now used to configure dump and restore.  I have shamelessly
+adapted the configure script from the one present in the e2fsprogs
+distribution (thanks Ted!).
+
+       Last, a few minor bugs have been fixed.
+
+       This version is a test release.  Use it only for testing :-)  New
+beta versions will probably be released before a stable dump 0.4 version is
+available.
+
+       If you encounter problems with this test release, please send a
+detailled bug report (please look at the file KNOWNBUGS before sending a
+bug report).
+
+--
+Remy Card
+card@Linux.EU.Org
+
diff --git a/dump.lsm b/dump.lsm
new file mode 100644 (file)
index 0000000..6aadaf4
--- /dev/null
+++ b/dump.lsm
@@ -0,0 +1,18 @@
+Begin3
+Title:          dump and restore for Ext2fs
+Version:        0.4b4
+Entered-date:   17JAN97
+Description:    Port of the 4.4BSD dump and restore backup suite
+Keywords:       backup, filesystem, Ext2fs
+Author:         University of California, Berkeley
+Maintained-by:  card@Linux.EU.Org (Remy Card)
+Primary-site:   tsx-11.mit.edu /pub/linux/ALPHA/ext2fs
+                126kB dump-0.4b4.tar.gz
+                627 dump.lsm
+Alternate-site: 
+Original-site:  ftp.freebsd.org /pub/bsd-sources/4.4BSD-Lite2/sbin
+                dump/*
+                restore/*
+Platforms:      linux 2.0.x, e2fsprogs 1.06
+Copying-policy: BSD
+End
diff --git a/dump/Makefile.in b/dump/Makefile.in
new file mode 100644 (file)
index 0000000..71435b0
--- /dev/null
@@ -0,0 +1,49 @@
+top_srcdir=    @top_srcdir@
+srcdir=                @srcdir@
+
+@MCONFIG@
+
+CFLAGS=                @CCOPTS@ -pipe $(GINC) $(INC) $(DEFS) @DUMPDEBUG@
+LDFLAGS:=      $(LDFLAGS) @STATIC@
+LIBS=          $(GLIBS)
+DEPLIBS=       ../compat/lib/libcompat.a
+
+PROG=          dump
+LINKS=         ${BINDIR}/dump ${BINDIR}/rdump
+SRCS=          itime.c main.c optr.c tape.c traverse.c unctime.c
+OBJS=          itime.o main.o optr.o tape.o traverse.o unctime.o \
+               ../common/dumprmt.o
+MAN8=          dump.8
+MLINKS=                $(MANDIR)/dump.8 $(MANDIR)/rdump.8
+
+all::          $(PROG)
+
+$(PROG):       $(OBJS) $(DEPLIBS)
+       $(LD) $(CFLAGS) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install::      $(PROG)
+       $(INSTALLBIN) $(PROG) $(BINDIR)
+       $(INSTALLMAN) $(srcdir)/$(MAN8) $(MANDIR)
+       @set $(LINKS) $(MLINKS); \
+       while test $$# -ge 2; do \
+               l=$(DESTDIR)$$1; \
+               shift; \
+               t=$(DESTDIR)$$1; \
+               shift; \
+               echo $$t -\> $$l; \
+               $(RM) -f $$t; \
+               $(LN_S) $$l $$t; \
+       done; true
+
+clean::
+       $(RM) -f $(PROG) \#* *.s *.o *.a *~ core
+
+distclean::    clean
+       $(RM) -f Makefile Makefile.old .depend
+
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+
diff --git a/dump/dump.8 b/dump/dump.8
new file mode 100644 (file)
index 0000000..fae652b
--- /dev/null
@@ -0,0 +1,364 @@
+.\" 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.3 (Berkeley) 5/1/95
+.\"
+.Dd May 1, 1995
+.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 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 directory
+.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.
+.ne 1i
+.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
+option is mutually exclusive from the
+.Fl u
+option.
+.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
+option 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
+option 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
+key,
+.Nm dump
+interacts with the operator on
+.Em dump'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
+options 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/dump/dump.h b/dump/dump.h
new file mode 100644 (file)
index 0000000..73882dc
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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.2 (Berkeley) 4/28/95
+ */
+
+#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 */
+#ifdef __linux__
+time_t tend_writing;   /* after writing the last tape block */
+ext2_filsys fs;
+#else
+struct fs *sblock;     /* the file system super block */
+char   sblock_buf[MAXBSIZE];
+#endif
+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));
+#ifdef __linux__
+int    mapfilesfromdir __P((ino_t maxino, long *tapesize, char *directory));
+#endif
+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));
+#ifdef __linux__
+void   dumpdirino __P((struct dinode *dp, ino_t ino));
+#endif
+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 */
+#ifdef __linux__
+#define DIALUP "ttyS"                  /* prefix for dialups */
+#else
+#define DIALUP "ttyd"                  /* prefix for dialups */
+#endif
+
+struct fstab *fstabsearch __P((char *key));    /* search fs_file and fs_spec */
+#ifdef __linux__
+struct fstab *fstabsearchdir __P((char *key, char *dir));      /* search fs_file and fs_spec */
+#endif
+
+#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
+
+#ifdef __linux__
+#define        DUMP_CURRENT_REV        0
+#endif
+
+#ifndef        __linux__
+#ifndef        _PATH_UTMP
+#define        _PATH_UTMP      "/etc/utmp"
+#endif
+#ifndef        _PATH_FSTAB
+#define        _PATH_FSTAB     "/etc/fstab"
+#endif
+#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/dump/itime.c b/dump/itime.c
new file mode 100644 (file)
index 0000000..f83459c
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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
+static char sccsid[] = "@(#)itime.c    8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#include <sys/file.h>
+#else
+#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
+#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
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.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;
+#ifdef __linux__
+               if (ddp->dd_ddate <= (time_t)spcl.c_ddate)
+#else
+               if (ddp->dd_ddate <= spcl.c_ddate)
+#endif
+                       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,
+#ifdef __linux__
+               spcl.c_date == 0 ? "the epoch\n" : ctime4(&spcl.c_date));
+#else
+               spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+#endif
+}
+
+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/dump/main.c b/dump/main.c
new file mode 100644 (file)
index 0000000..c09e6b8
--- /dev/null
@@ -0,0 +1,711 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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
+static char sccsid[] = "@(#)main.c     8.6 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <sys/stat.h>
+#include <bsdcompat.h>
+#else
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/inode.h>
+#include <ufs/fs.h>
+#else
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif
+#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>
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#endif
+
+#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) */
+
+#ifdef __linux__
+char   *__progname;
+#endif
+
+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;
+#ifdef __linux__
+       errcode_t retval;
+       char directory[NAME_MAX];
+       char pathname[NAME_MAX];
+#endif
+
+       spcl.c_date = 0;
+#ifdef __linux__
+       (void)time4(&spcl.c_date);
+#else
+       (void)time((time_t *)&spcl.c_date);
+#endif
+
+#ifdef __linux__
+       __progname = argv[0];
+       directory[0] = 0;
+       initialize_ext2_error_table();
+#endif
+
+       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 = '?';
+
+               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);
+#ifdef __linux__
+       } else {
+               /*
+                * The argument was not found in the fstab
+                * assume that this is a subtree and search for it
+                */
+#ifdef HAVE_REALPATH
+               if (realpath(disk, pathname) == NULL)
+#endif
+                       strcpy(pathname, disk);
+               dt = fstabsearchdir(pathname, directory);
+               if (dt != NULL) {
+                       char name[MAXPATHLEN];
+                       (void)sprintf(name, "%s (dir %s)",
+                                     dt->fs_spec, directory);
+                       (void)strncpy(spcl.c_dev, name, NAMELEN);
+                       (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN);
+                       disk = rawname(dt->fs_spec);
+               } else {
+                       (void)strncpy(spcl.c_dev, disk, NAMELEN);
+                       (void)strncpy(spcl.c_filesys, "an unlisted file system",
+                           NAMELEN);
+               }
+       }
+#else
+       } else {
+               (void)strncpy(spcl.c_dev, disk, NAMELEN);
+               (void)strncpy(spcl.c_filesys, "an unlisted file system",
+                   NAMELEN);
+       }
+#endif
+       (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,
+#ifdef __linux
+               spcl.c_date == 0 ? "the epoch\n" : ctime4(&spcl.c_date));
+#else
+               spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+#endif
+       msg("Date of last level %c dump: %s", lastlevel,
+#ifdef __linux__
+               spcl.c_ddate == 0 ? "the epoch\n" : ctime4(&spcl.c_ddate));
+#else
+               spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate));
+#endif
+       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);
+
+#ifdef __linux__
+       retval = ext2fs_open(disk, 0, 0, 0, unix_io_manager, &fs);
+       if (retval) {
+               com_err(disk, retval, "while opening filesystem");
+               if (retval == EXT2_ET_REV_TOO_HIGH)
+                       printf ("Get a newer version of dump!\n");
+               exit(X_ABORT);
+       }
+       if (fs->super->s_rev_level > DUMP_CURRENT_REV) {
+               com_err(disk, retval, "while opening filesystem");
+               printf ("Get a newer version of dump!\n");
+               exit(X_ABORT);
+       }
+       if ((diskfd = open(disk, O_RDONLY)) < 0) {
+               msg("Cannot open %s\n", disk);
+               exit(X_ABORT);
+       }
+       sync();
+       dev_bsize = DEV_BSIZE;
+       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);
+       maxino = fs->super->s_inodes_count;
+#if    0
+       spcl.c_flags |= DR_NEWINODEFMT;
+#endif
+#else  /* __linux __*/
+       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);
+       spcl.c_flags |= DR_NEWINODEFMT;
+#ifdef FS_44INODEFMT
+       if (sblock->fs_inodefmt >= FS_44INODEFMT)
+               spcl.c_flags |= DR_NEWINODEFMT;
+#endif
+       maxino = sblock->fs_ipg * sblock->fs_ncg;
+#endif /* __linux__ */
+       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");
+#ifdef __linux__
+       if (directory[0] == 0)
+               anydirskipped = mapfiles(maxino, &tapesize);
+       else
+               anydirskipped = mapfilesfromdir(maxino, &tapesize, directory);
+#else
+       anydirskipped = mapfiles(maxino, &tapesize);
+#endif
+
+       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;
+#ifdef __linux__
+               (void)dumpdirino(dp, ino);
+#else
+               (void)dumpino(dp, ino);
+#endif
+       }
+
+       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);
+       }
+
+#ifdef __linux__
+       (void)time((time_t *)&(tend_writing));
+#endif
+       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);
+#ifdef __linux__
+       /* report dump performance, avoid division through zero */
+       if (tend_writing - tstart_writing == 0)
+               msg("finished in less than a second\n");
+       else
+               msg("finished in %d seconds, throughput %d KBytes/sec\n",
+                   tend_writing - tstart_writing,
+                   spcl.c_tapea / (tend_writing - tstart_writing));
+#endif
+       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;
+{
+#ifdef __linux__
+       return cp;
+#else  /* __linux__ */
+       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);
+#endif /* __linux__ */
+}
+
+/*
+ * 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/dump/optr.c b/dump/optr.c
new file mode 100644 (file)
index 0000000..4d23242
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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
+static char sccsid[] = "@(#)optr.c     8.2 (Berkeley) 1/6/94";
+#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
+
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <ext2fs/ext2fs.h>
+#include <bsdcompat.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);
+}
+
+#ifdef __linux__
+struct fstab *
+fstabsearchdir(key, directory)
+       char *key;
+       char *directory;
+{
+       register struct pfstab *pf;
+       register struct fstab *fs;
+       register struct fstab *found_fs = NULL;
+       unsigned int size = 0;
+
+       for (pf = table; pf != NULL; pf = pf->pf_next) {
+               fs = pf->pf_fstab;
+               if (strlen(fs->fs_file) > size &&
+                   strlen(key) > strlen(fs->fs_file) + 2 &&
+                   strncmp(fs->fs_file, key, strlen(fs->fs_file)) == 0 &&
+                   (key[strlen(fs->fs_file)] == '/' ||
+                    fs->fs_file[strlen(fs->fs_file) - 1] == '/')) {
+                       found_fs = fs;
+                       size = strlen(fs->fs_file);
+               }
+       }
+       if (found_fs != NULL) {
+               /*
+                * Ok, we have found a fstab entry which matches the argument
+                * We have to split the argument name into:
+                * - a device name (from the fstab entry)
+                * - a directory name on this device
+                */
+               strcpy(directory, key + size);
+       }
+       return(found_fs);
+}
+#endif
+
+/*
+ *     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/dump/tape.c b/dump/tape.c
new file mode 100644 (file)
index 0000000..ce15056
--- /dev/null
@@ -0,0 +1,915 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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
+static char sccsid[] = "@(#)tape.c     8.4 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif
+#endif /* __linux__ */
+
+#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
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#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 int atomic __P((int (*)(), 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])
+#ifdef __linux__
+                   (((long)&buf[reqsiz] + pgoff) &~ pgoff);
+#else
+                   (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
+#endif
+               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 __linux__
+       void    (*interrupt_save)();
+#else  /* __linux__ */
+#ifdef sunos
+       void    (*interrupt_save)();
+#else
+       sig_t   interrupt_save;
+#endif
+#endif /* __linux__ */
+
+       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];
+#ifdef LINUX_FORK_BUG
+       int i, j;
+#else
+       register int i, j;
+#endif
+
+       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 */
+#ifdef LINUX_FORK_BUG
+                       if (atomic(write, cmd[0], (char *) &i, sizeof i)
+                           != sizeof i)
+                               quit("master/slave protocol botched 3\n");
+#endif
+                       doslave(cmd[0], i);
+                       Exit(X_FINOK);
+               }
+       }
+
+#ifdef LINUX_FORK_BUG
+       /*
+        * Wait for all slaves to _actually_ start to circumvent a bug in
+        * Linux kernels >= 2.1.3 where a signal sent to a child that hasn't
+        * returned from fork() causes a SEGV in the child process
+        */
+       for (i = 0; i < SLAVES; i++)
+               if (atomic(read, slaves[i].fd, (char *) &j, sizeof j) != sizeof j)
+                       quit("master/slave protocol botched 4\n");
+#endif
+
+       /* write pid of next slave to each slave */
+       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;
+#ifdef __linux__
+       errcode_t retval;
+#endif
+
+#ifdef TDEBUG
+       msg("slave %d, pid %d\n", slave_number, getpid());
+#endif
+       /*
+        * 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));
+#ifdef __linux__
+       ext2fs_close(fs);
+       retval = ext2fs_open(disk, 0, 0, 0, unix_io_manager, &fs);
+       if (retval)
+               quit("slave couldn't reopen disk: %s\n", strerror(errno));
+#endif /* __linux__ */
+
+       /*
+        * 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
+                       msg("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) 
+                msg("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);
+                       for (;;)
+                               (void) sigpause(0);
+               } 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 int
+atomic(func, fd, buf, count)
+       int (*func)(), fd;
+       char *buf;
+       int count;
+{
+       int got, need = count;
+
+       while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
+               buf += got;
+       return (got < 0 ? got : count - need);
+}
diff --git a/dump/traverse.c b/dump/traverse.c
new file mode 100644 (file)
index 0000000..ccc03f7
--- /dev/null
@@ -0,0 +1,1100 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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
+static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#include <err.h>
+#include <stdlib.h>
+#else  /* __linux__ */
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/fsdir.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif
+#endif /* __linux__ */
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#endif
+
+#include "dump.h"
+
+#define        HASDUMPEDFILE   0x1
+#define        HASSUBDIRS      0x2
+
+#ifdef FS_44INODEFMT
+typedef        quad_t fsizeT;
+#else
+typedef        long fsizeT;
+#endif
+
+#ifndef        __linux__
+static int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size));
+#endif
+static void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size));
+#ifdef __linux__
+static int searchdir __P((struct ext2_dir_entry *dp, int offset,
+                          int blocksize, char *buf, void *private));
+#else
+static int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize));
+#endif
+
+/*
+ * 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;
+#ifdef __linux__
+       if (dp->di_size > fs->blocksize * NDADDR) {
+               /* calculate the number of indirect blocks on the dump tape */
+               blkest +=
+                       howmany(sizeest - NDADDR * fs->blocksize / TP_BSIZE,
+                       TP_NINDIR);
+       }
+#else
+       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);
+       }
+#endif
+       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;
+#ifdef __linux__
+               if (dp->di_nlink == 0 || dp->di_dtime != 0)
+                       continue;
+#endif
+               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);
+}
+
+#ifdef __linux__
+struct mapfile_context {
+       long *tapesize;
+       int anydirskipped;
+};
+
+static int
+mapfilesindir(dirent, offset, blocksize, buf, private)
+       struct ext2_dir_entry *dirent;
+       int offset;
+       int blocksize;
+       char *buf;
+       void *private;
+{
+       register struct dinode *dp;
+       register int mode;
+       ino_t ino;
+       errcode_t retval;
+       struct mapfile_context *mfc;
+
+       ino = dirent->inode;
+       mfc = (struct mapfile_context *)private;
+       dp = getino(ino);
+       if ((mode = (dp->di_mode & IFMT)) != 0 &&
+           dp->di_nlink != 0 && dp->di_dtime == 0) {
+               SETINO(ino, usedinomap);
+               if (mode == IFDIR)
+                       SETINO(ino, dumpdirmap);
+               if (WANTTODUMP(dp)) {
+                       SETINO(ino, dumpinomap);
+                       if (mode != IFREG && mode != IFDIR && mode != IFLNK)
+                               *mfc->tapesize += 1;
+                       else
+                               *mfc->tapesize += blockest(dp);
+               }
+               if (mode == IFDIR) {
+                       mfc->anydirskipped = 1;
+                       if ((dirent->name[0] != '.' || dirent->name_len != 1) &&
+                           (dirent->name[0] != '.' || dirent->name[1] != '.' ||
+                            dirent->name_len != 2)) {
+                       retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
+                                                   mapfilesindir, private);
+                       if (retval)
+                               return retval;
+                       }
+               }
+       }
+       return 0;
+}
+
+/*
+ * 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
+mapfilesfromdir(maxino, tapesize, directory)
+       ino_t maxino;
+       long *tapesize;
+       char *directory;
+{
+       errcode_t retval;
+       struct mapfile_context mfc;
+       ino_t dir_ino;
+       char dir_name [MAXPATHLEN];
+       int i;
+
+       /*
+        * Mark every directory in the path as being dumped
+        */
+       for (i = 0; i < strlen (directory); i++) {
+               if (directory[i] == '/') {
+                       strncpy (dir_name, directory, i);
+                       dir_name[i] = '\0';
+                       retval = ext2fs_namei(fs, ROOTINO, ROOTINO, dir_name,
+                                             &dir_ino);
+                       if (retval) {
+                               com_err(disk, retval, "while translating %s",
+                                       dir_name);
+                               exit(X_ABORT);
+                       }
+/*                     SETINO(dir_ino, dumpinomap); */
+                       SETINO(dir_ino, dumpdirmap);
+               }
+       }
+       /*
+        * Mark the final directory
+        */
+       retval = ext2fs_namei(fs, ROOTINO, ROOTINO, directory, &dir_ino);
+       if (retval) {
+               com_err(disk, retval, "while translating %s", directory);
+               exit(X_ABORT);
+       }
+/*     SETINO(dir_ino, dumpinomap); */
+       SETINO(dir_ino, dumpdirmap);
+
+       mfc.tapesize = tapesize;
+       mfc.anydirskipped = 0;
+       retval = ext2fs_dir_iterate(fs, dir_ino, 0, NULL, mapfilesindir,
+                                   (void *)&mfc);
+
+       if (retval) {
+               com_err(disk, retval, "while mapping files in %s", directory);
+               exit(X_ABORT);
+       }
+       /*
+        * Restore gets very upset if the root is not dumped,
+        * so ensure that it always is dumped.
+        */
+/*     SETINO(ROOTINO, dumpinomap); */
+       SETINO(ROOTINO, dumpdirmap);
+       return (mfc.anydirskipped);
+}
+#endif
+
+/*
+ * 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;
+#ifdef __linux__
+               dp = getino(ino);
+               ret = 0;
+               ext2fs_dir_iterate(fs, ino, 0, NULL, searchdir, (void *) &ret);
+#else  /* __linux__ */
+               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);
+               }
+#endif /* __linux__ */
+               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);
+}
+
+#ifndef        __linux__
+/*
+ * 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);
+}
+#endif /* !__linux__ */
+
+/*
+ * 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.
+ */
+#ifdef __linux__
+static int
+searchdir(dp, offset, blocksize, buf, private)
+       struct ext2_dir_entry *dp;
+       int offset;
+       int blocksize;
+       char *buf;
+       void *private;
+{
+       int *ret = (int *) private;
+
+       if (dp->inode == 0)
+               return 0;
+       if (dp->name[0] == '.') {
+               if (dp->name_len == 1)
+                       return 0;
+               if (dp->name[1] == '.' && dp->name_len == 2)
+                       return 0;
+       }
+       if (TSTINO(dp->inode, dumpinomap)) {
+               *ret |= HASDUMPEDFILE;
+               if (*ret & HASSUBDIRS)
+                       return DIRENT_ABORT;
+       }
+       if (TSTINO(dp->inode, dumpdirmap)) {
+               *ret |= HASSUBDIRS;
+               if (*ret & HASDUMPEDFILE)
+                       return DIRENT_ABORT;
+       }
+       return 0;
+}
+
+#else  /* __linux__ */
+
+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);
+}
+#endif /* __linux__ */
+
+#ifdef __linux__
+
+struct block_context {
+       ino_t   ino;
+       int     *buf;
+       int     cnt;
+       int     max;
+       int     next_block;
+};
+
+/*
+ * Dump a block to the tape
+ */
+static int
+dumponeblock(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
+{
+       struct block_context *p;
+       int i;
+
+       if (blockcnt < NDADDR)
+               return 0;
+       p = (struct block_context *)private;
+       for (i = p->next_block; i < blockcnt; i++) {
+               p->buf[p->cnt++] = 0;
+               if (p->cnt == p->max) {
+                       blksout (p->buf, p->cnt, p->ino);
+                       p->cnt = 0;
+               }
+       }
+       p->buf[p->cnt++] = *blocknr;
+       if (p->cnt == p->max) {
+               blksout (p->buf, p->cnt, p->ino);
+               p->cnt = 0;
+       }
+       p->next_block = blockcnt + 1;
+       return 0;
+}
+#endif
+
+/*
+ * 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];
+       struct old_bsd_inode obi;
+#ifdef __linux__
+       struct block_context bc;
+#endif
+
+       if (newtape) {
+               newtape = 0;
+               dumpmap(dumpinomap, TS_BITS, ino);
+       }
+       CLRINO(ino, dumpinomap);
+#ifdef __linux__
+       memset(&obi, 0, sizeof(obi));
+       obi.di_mode = dp->di_mode;
+       obi.di_uid = dp->di_uid;
+       obi.di_gid = dp->di_gid;
+       obi.di_qsize.v = (u_quad_t)dp->di_size;
+       obi.di_atime = dp->di_atime;
+       obi.di_mtime = dp->di_mtime;
+       obi.di_ctime = dp->di_ctime;
+       obi.di_nlink = dp->di_nlink;
+       obi.di_blocks = dp->di_blocks;
+       obi.di_flags = dp->di_flags;
+       obi.di_gen = dp->di_gen;
+       memmove(&obi.di_db, &dp->di_db, (NDADDR + NIADDR) * sizeof(daddr_t));
+       if (dp->di_file_acl || dp->di_dir_acl)
+               warn("ACLs in inode #%d won't be dumped", ino);
+       memmove(&spcl.c_dinode, &obi, sizeof(obi));
+#else  /* __linux__ */
+       spcl.c_dinode = *dp;
+#endif /* __linux__ */
+       spcl.c_type = TS_INODE;
+       spcl.c_count = 0;
+       switch (dp->di_mode & S_IFMT) {
+
+       case 0:
+               /*
+                * Freed inode.
+                */
+               return;
+
+#ifdef __linux__
+       case S_IFDIR:
+               msg("Warning: dumpino called on a directory (ino %d)\n", ino);
+               return;
+#endif
+
+       case S_IFLNK:
+               /*
+                * Check for short symbolic link.
+                */
+#ifdef __linux__
+               if (dp->di_size > 0 &&
+                   dp->di_size < EXT2_N_BLOCKS * sizeof (daddr_t)) {
+                       spcl.c_addr[0] = 1;
+                       spcl.c_count = 1;
+                       writeheader(ino);
+                       memmove(buf, dp->di_db, (u_long)dp->di_size);
+                       buf[dp->di_size] = '\0';
+                       writerec(buf, 0);
+                       return;
+               }
+#endif /* __linux__ */
+#ifdef FS_44INODEFMT
+               if (dp->di_size > 0 &&
+                   dp->di_size < sblock->fs_maxsymlinklen) {
+                       spcl.c_addr[0] = 1;
+                       spcl.c_count = 1;
+                       writeheader(ino);
+                       memmove(buf, dp->di_shortlink, (u_long)dp->di_size);
+                       buf[dp->di_size] = '\0';
+                       writerec(buf, 0);
+                       return;
+               }
+#endif
+               /* fall through */
+
+#ifndef        __linux__
+       case S_IFDIR:
+#endif
+       case S_IFREG:
+               if (dp->di_size > 0)
+                       break;
+               /* fall through */
+
+       case S_IFIFO:
+       case S_IFSOCK:
+       case S_IFCHR:
+       case S_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)
+#ifdef __linux__
+               cnt = NDADDR * EXT2_FRAGS_PER_BLOCK(fs->super);
+#else
+               cnt = NDADDR * sblock->fs_frag;
+#endif
+       else
+               cnt = howmany(dp->di_size, sblock->fs_fsize);
+       blksout(&dp->di_db[0], cnt, ino);
+       if ((size = ((int)dp->di_size) - NDADDR * sblock->fs_bsize) <= 0)
+               return;
+#ifdef __linux__
+       bc.max = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
+       bc.buf = (int *)malloc (bc.max * sizeof (int));
+       bc.cnt = 0;
+       bc.ino = ino;
+       bc.next_block = NDADDR;
+
+       ext2fs_block_iterate (fs, ino, 0, NULL, dumponeblock, (void *)&bc);
+       if (bc.cnt > 0) {
+               blksout (bc.buf, bc.cnt, bc.ino);
+       }
+#else
+       for (ind_level = 0; ind_level < NIADDR; ind_level++) {
+               dmpindir(ino, dp->di_ib[ind_level], ind_level, &size);
+               if (size <= 0)
+                       return;
+       }
+#endif
+}
+
+#ifdef __linux__
+
+struct convert_dir_context {
+       char *buf;
+       int prev_offset;
+       int offset;
+       int bs;
+};
+
+/*
+ * This function converts an ext2fs directory entry to the BSD format.
+ *
+ * Basically, it adds a null-character at the end of the name, recomputes the
+ * size of the entry, and creates it in a temporary buffer
+ */
+static int
+convert_dir(dirent, offset, blocksize, buf, private)
+       struct ext2_dir_entry *dirent;
+       int offset;
+       int blocksize;
+       char *buf;
+       void *private;
+{
+       struct convert_dir_context *p;
+       struct direct *dp;
+       int reclen;
+
+       p = (struct convert_dir_context *)private;
+
+       reclen = EXT2_DIR_REC_LEN(dirent->name_len + 1);
+       if (((p->offset + reclen - 1) / p->bs) != (p->offset / p->bs)) {
+               dp = (struct direct *)(p->buf + p->prev_offset);
+               dp->d_reclen += p->bs - (p->offset % p->bs);
+               p->offset += p->bs - (p->offset % p->bs);
+       }
+
+       dp = (struct direct *)(p->buf + p->offset);
+       dp->d_ino = dirent->inode;
+       dp->d_reclen = reclen;
+       dp->d_type = 0;
+       dp->d_namlen = dirent->name_len;
+       strncpy(dp->d_name, dirent->name, dirent->name_len);
+       dp->d_name[dp->d_namlen] = '\0';
+       p->prev_offset = p->offset;
+       p->offset += reclen;
+
+       return 0;
+}
+
+/*
+ * Dump pass 3
+ *
+ * Dumps a directory to tape after converting it to the BSD format
+ */
+void
+dumpdirino(dp, ino)
+       register struct dinode *dp;
+       ino_t ino;
+{
+       fsizeT size;
+       char buf[TP_BSIZE];
+       struct old_bsd_inode obi;
+       struct convert_dir_context cdc;
+       errcode_t retval;
+       struct ext2_dir_entry *de;
+       fsizeT dir_size;
+
+       if (newtape) {
+               newtape = 0;
+               dumpmap(dumpinomap, TS_BITS, ino);
+       }
+       CLRINO(ino, dumpinomap);
+
+       /*
+        * Convert the directory to the BSD format
+        */
+       /* Allocate a buffer for the conversion (twice the size of the
+          ext2fs directory to avoid problems ;-) */
+       cdc.buf = (char *)malloc(dp->di_size * 2 * sizeof(char));
+       if (cdc.buf == NULL)
+               err(1, "Cannot allocate buffer to convert directory #%ld\n",
+                   ino);
+       cdc.offset = 0;
+       cdc.prev_offset = 0;
+       cdc.bs = MIN(DIRBLKSIZ, TP_BSIZE);
+       /* Do the conversion */
+       retval = ext2fs_dir_iterate(fs, ino, 0, NULL, convert_dir, (void *)&cdc);
+       if (retval) {
+               com_err(disk, retval, "while converting directory #%ld\n", ino);
+               exit(X_ABORT);
+       }
+       /* Fix the last entry */
+       if ((cdc.offset % cdc.bs) != 0) {
+               de = (struct ext2_dir_entry *)(cdc.buf + cdc.prev_offset);
+               de->rec_len += cdc.bs - (cdc.offset % cdc.bs);
+               cdc.offset += cdc.bs - (cdc.offset % cdc.bs);
+       }
+
+       dir_size = cdc.offset;
+
+#ifdef __linux__
+       memset(&obi, 0, sizeof(obi));
+       obi.di_mode = dp->di_mode;
+       obi.di_uid = dp->di_uid;
+       obi.di_gid = dp->di_gid;
+       obi.di_qsize.v = dir_size; /* (u_quad_t)dp->di_size; */
+       obi.di_atime = dp->di_atime;
+       obi.di_mtime = dp->di_mtime;
+       obi.di_ctime = dp->di_ctime;
+       obi.di_nlink = dp->di_nlink;
+       obi.di_blocks = dp->di_blocks;
+       obi.di_flags = dp->di_flags;
+       obi.di_gen = dp->di_gen;
+       memmove(&obi.di_db, &dp->di_db, (NDADDR + NIADDR) * sizeof(daddr_t));
+       if (dp->di_file_acl || dp->di_dir_acl)
+               warn("ACLs in inode #%d won't be dumped", ino);
+       memmove(&spcl.c_dinode, &obi, sizeof(obi));
+#else  /* __linux__ */
+       spcl.c_dinode = *dp;
+#endif /* __linux__ */
+       spcl.c_type = TS_INODE;
+       spcl.c_count = 0;
+       switch (dp->di_mode & S_IFMT) {
+
+       case 0:
+               /*
+                * Freed inode.
+                */
+               return;
+
+       case S_IFDIR:
+               if (dir_size > 0)
+                       break;
+               msg("Warning: size of directory inode #%d is <= 0 (%d)!\n",
+                       ino, dir_size);
+               return;
+
+       default:
+               msg("Warning: dumpdirino called with file type 0%o (inode #%d)\n",
+                       dp->di_mode & IFMT, ino);
+               return;
+       }
+       for (size = 0; size < dir_size; size += TP_BSIZE) {
+               spcl.c_addr[0] = 1;
+               spcl.c_count = 1;
+               writeheader(ino);
+               memmove(buf, cdc.buf + size, TP_BSIZE);
+               writerec(buf, 0);
+               spcl.c_type = TS_ADDR;
+       }
+
+       (void)free(cdc.buf);
+}
+#endif /* __linux__ */
+
+#ifndef        __linux__
+/*
+ * 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
+#ifdef __linux__
+                       cnt = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
+#else
+                       cnt = NINDIR(sblock) * sblock->fs_frag;
+#endif
+               *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;
+       }
+}
+#endif
+
+/*
+ * 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;
+{
+#ifdef __linux__
+       register __s32 sum, cnt, *lp;
+#else
+       register long sum, cnt, *lp;
+#endif
+
+       spcl.c_inumber = ino;
+       spcl.c_magic = NFS_MAGIC;
+       spcl.c_checksum = 0;
+#ifdef __linux__
+       lp = (__s32 *)&spcl;
+#else
+       lp = (long *)&spcl;
+#endif
+       sum = 0;
+#ifdef __linux__
+       cnt = sizeof(union u_spcl) / (4 * sizeof(__s32));
+#else
+       cnt = sizeof(union u_spcl) / (4 * sizeof(long));
+#endif
+       while (--cnt >= 0) {
+               sum += *lp++;
+               sum += *lp++;
+               sum += *lp++;
+               sum += *lp++;
+       }
+       spcl.c_checksum = CHECKSUM - sum;
+       writerec((char *)&spcl, 1);
+}
+
+#ifdef __linux__
+struct dinode *
+getino(inum)
+       ino_t inum;
+{
+       static struct dinode dinode;
+
+       curino = inum;
+       ext2fs_read_inode(fs, inum, (struct ext2_inode *) &dinode);
+       return &dinode;
+}
+#else  /* __linux__ */
+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]);
+}
+#endif /* __linux__ */
+
+/*
+ * 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:
+#ifdef __linux__
+       if (ext2_llseek(diskfd, ((ext2_loff_t)blkno << dev_bshift), 0) !=
+                       ((ext2_loff_t)blkno << dev_bshift))
+#else
+       if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) == -1)
+#endif
+               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++) {
+#ifdef __linux__
+               if (ext2_llseek(diskfd, ((ext2_loff_t)blkno << dev_bshift), 0) !=
+                               ((ext2_loff_t)blkno << dev_bshift))
+#else
+               if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) == -1)
+#endif
+                       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/dump/unctime.c b/dump/unctime.c
new file mode 100644 (file)
index 0000000..eccc509
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * 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
+static char sccsid[] = "@(#)unctime.c  8.2 (Berkeley) 6/14/94";
+#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);
+}
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..89fc9b0
--- /dev/null
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/linux-1.2.x.patch b/linux-1.2.x.patch
new file mode 100644 (file)
index 0000000..a313a0c
--- /dev/null
@@ -0,0 +1,17 @@
+--- fs/read_write.c.orig       Sun Aug 27 15:41:29 1995
++++ fs/read_write.c    Sun Aug 27 15:42:39 1995
+@@ -112,9 +112,11 @@
+       }
+       if (tmp < 0)
+               return -EINVAL;
+-      file->f_pos = tmp;
+-      file->f_reada = 0;
+-      file->f_version = ++event;
++      if (tmp != file->f_pos) {
++              file->f_pos = tmp;
++              file->f_reada = 0;
++              file->f_version = ++event;
++      }
+       memcpy_tofs(result, &file->f_pos, sizeof(loff_t));
+       return 0;
+ }
diff --git a/restore/Makefile.in b/restore/Makefile.in
new file mode 100644 (file)
index 0000000..dff9e1f
--- /dev/null
@@ -0,0 +1,50 @@
+top_srcdir=    @top_srcdir@
+srcdir=                @srcdir@
+
+@MCONFIG@
+
+CFLAGS=                @CCOPTS@ -pipe $(DEFS) $(GINC) $(INC) @RESTOREDEBUG@
+LDFLAGS:=      $(LDFLAGS) @STATIC@
+LIBS=          $(GLIBS) -le2p
+DEPLIBS=       ../compat/lib/libcompat.a
+
+PROG=          restore
+LINKS=         ${BINDIR}/restore ${BINDIR}/rrestore
+SRCS=          dirs.c interactive.c main.c restore.c symtab.c tape.c \
+               utilities.c
+OBJS=          dirs.o interactive.o main.o restore.o symtab.o tape.o \
+               utilities.o ../common/dumprmt.o
+MAN8=          restore.8
+MLINKS=                $(MANDIR)/restore.8 $(MANDIR)/rrestore.8
+
+all::          $(PROG)
+
+$(PROG):       $(OBJS) $(DEPLIBS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install::      $(PROG)
+       $(INSTALLBIN) $(PROG) $(BINDIR)
+       $(INSTALLMAN) $(srcdir)/$(MAN8) $(MANDIR)
+       @set $(LINKS) $(MLINKS); \
+       while test $$# -ge 2; do \
+               l=$(DESTDIR)$$1; \
+               shift; \
+               t=$(DESTDIR)$$1; \
+               shift; \
+               echo $$t -\> $$l; \
+               $(RM) -f $$t; \
+               $(LN_S) $$l $$t; \
+       done; true
+
+clean::
+       $(RM) -f $(PROG) \#* *.s *.o *.a *~ core
+
+distclean::    clean
+       $(RM) -f Makefile Makefile.old .depend
+
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+
diff --git a/restore/dirs.c b/restore/dirs.c
new file mode 100644 (file)
index 0000000..78285ad
--- /dev/null
@@ -0,0 +1,817 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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 sccsid[] = "@(#)dirs.c     8.7 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#endif /* __linux__ */
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include <endian.h>
+#else
+#include <machine/endian.h>
+#endif
+
+#include "pathnames.h"
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * Symbol table of directories read from tape.
+ */
+#define HASHSIZE       1000
+#define INOHASH(val) (val % HASHSIZE)
+struct inotab {
+       struct  inotab *t_next;
+       ino_t   t_ino;
+       long    t_seekpt;
+       long    t_size;
+};
+static struct inotab *inotab[HASHSIZE];
+
+/*
+ * Information retained about directories.
+ */
+struct modeinfo {
+       ino_t ino;
+       struct timeval timep[2];
+       mode_t mode;
+       uid_t uid;
+       gid_t gid;
+       int flags;
+};
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+#undef DIRBLKSIZ
+#define DIRBLKSIZ 1024
+struct rstdirdesc {
+       int     dd_fd;
+       long    dd_loc;
+       long    dd_size;
+       char    dd_buf[DIRBLKSIZ];
+};
+
+/*
+ * Global variables for this file.
+ */
+static long    seekpt;
+static FILE    *df, *mf;
+static RST_DIR *dirp;
+static char    dirfile[32] = "#";      /* No file */
+static char    modefile[32] = "#";     /* No file */
+static char    dot[2] = ".";           /* So it can be modified */
+
+/*
+ * Format of old style directories.
+ */
+#define ODIRSIZ 14
+struct odirect {
+       u_short d_ino;
+       char    d_name[ODIRSIZ];
+};
+
+#ifdef __linux__
+static struct inotab   *allocinotab __P((ino_t, struct new_bsd_inode *, long));
+#else
+static struct inotab   *allocinotab __P((ino_t, struct dinode *, long));
+#endif
+static void             dcvt __P((struct odirect *, struct direct *));
+static void             flushent __P((void));
+static struct inotab   *inotablookup __P((ino_t));
+static RST_DIR         *opendirfile __P((const char *));
+static void             putdir __P((char *, long));
+static void             putent __P((struct direct *));
+static void             rst_seekdir __P((RST_DIR *, long, long));
+static long             rst_telldir __P((RST_DIR *));
+static struct direct   *searchdir __P((ino_t, char *));
+
+/*
+ *     Extract directory contents, building up a directory structure
+ *     on disk for extraction by name.
+ *     If genmode is requested, save mode, owner, and times for all
+ *     directories on the tape.
+ */
+void
+extractdirs(genmode)
+       int genmode;
+{
+       register int i;
+#ifdef __linux__
+       register struct new_bsd_inode *ip;
+#else
+       register struct dinode *ip;
+#endif
+       struct inotab *itp;
+       struct direct nulldir;
+
+       vprintf(stdout, "Extract directories from tape\n");
+       (void) sprintf(dirfile, "%s/rstdir%d", tmpdir, dumpdate);
+       df = fopen(dirfile, "w");
+       if (df == NULL) {
+               fprintf(stderr,
+                   "restore: %s - cannot create directory temporary\n",
+                   dirfile);
+               fprintf(stderr, "fopen: %s\n", strerror(errno));
+               done(1);
+       }
+       if (genmode != 0) {
+               (void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate);
+               mf = fopen(modefile, "w");
+               if (mf == NULL) {
+                       fprintf(stderr,
+                           "restore: %s - cannot create modefile \n",
+                           modefile);
+                       fprintf(stderr, "fopen: %s\n", strerror(errno));
+                       done(1);
+               }
+       }
+       nulldir.d_ino = 0;
+#ifdef __linux__
+       nulldir.d_type = DT_DIR;
+#endif
+       nulldir.d_namlen = 1;
+       (void) strcpy(nulldir.d_name, "/");
+       nulldir.d_reclen = DIRSIZ(0, &nulldir);
+       for (;;) {
+               curfile.name = "<directory file - name unknown>";
+               curfile.action = USING;
+               ip = curfile.dip;
+               if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
+                       (void) fclose(df);
+                       dirp = opendirfile(dirfile);
+                       if (dirp == NULL)
+                               fprintf(stderr, "opendirfile: %s\n",
+                                   strerror(errno));
+                       if (mf != NULL)
+                               (void) fclose(mf);
+                       i = dirlookup(dot);
+                       if (i == 0)
+                               panic("Root directory is not on tape\n");
+                       return;
+               }
+               itp = allocinotab(curfile.ino, ip, seekpt);
+               getfile(putdir, xtrnull);
+               putent(&nulldir);
+               flushent();
+               itp->t_size = seekpt - itp->t_seekpt;
+       }
+}
+
+/*
+ * skip over all the directories on the tape
+ */
+void
+skipdirs()
+{
+
+       while (curfile.dip && (curfile.dip->di_mode & IFMT) == IFDIR) {
+               skipfile();
+       }
+}
+
+/*
+ *     Recursively find names and inumbers of all files in subtree 
+ *     pname and pass them off to be processed.
+ */
+void
+treescan(pname, ino, todo)
+       char *pname;
+       ino_t ino;
+       long (*todo) __P((char *, ino_t, int));
+{
+       register struct inotab *itp;
+       register struct direct *dp;
+       int namelen;
+       long bpt;
+       char locname[MAXPATHLEN + 1];
+
+       itp = inotablookup(ino);
+       if (itp == NULL) {
+               /*
+                * Pname is name of a simple file or an unchanged directory.
+                */
+               (void) (*todo)(pname, ino, LEAF);
+               return;
+       }
+       /*
+        * Pname is a dumped directory name.
+        */
+       if ((*todo)(pname, ino, NODE) == FAIL)
+               return;
+       /*
+        * begin search through the directory
+        * skipping over "." and ".."
+        */
+       (void) strncpy(locname, pname, MAXPATHLEN);
+       (void) strncat(locname, "/", MAXPATHLEN);
+       namelen = strlen(locname);
+       rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+       dp = rst_readdir(dirp); /* "." */
+       if (dp != NULL && strcmp(dp->d_name, ".") == 0)
+               dp = rst_readdir(dirp); /* ".." */
+       else
+               fprintf(stderr, "Warning: `.' missing from directory %s\n",
+                       pname);
+       if (dp != NULL && strcmp(dp->d_name, "..") == 0)
+               dp = rst_readdir(dirp); /* first real entry */
+       else
+               fprintf(stderr, "Warning: `..' missing from directory %s\n",
+                       pname);
+       bpt = rst_telldir(dirp);
+       /*
+        * a zero inode signals end of directory
+        */
+       while (dp != NULL) {
+               locname[namelen] = '\0';
+               if (namelen + dp->d_namlen >= MAXPATHLEN) {
+                       fprintf(stderr, "%s%s: name exceeds %d char\n",
+                               locname, dp->d_name, MAXPATHLEN);
+               } else {
+                       (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
+                       treescan(locname, dp->d_ino, todo);
+                       rst_seekdir(dirp, bpt, itp->t_seekpt);
+               }
+               dp = rst_readdir(dirp);
+               bpt = rst_telldir(dirp);
+       }
+}
+
+/*
+ * Lookup a pathname which is always assumed to start from the ROOTINO.
+ */
+struct direct *
+pathsearch(pathname)
+       const char *pathname;
+{
+       ino_t ino;
+       struct direct *dp;
+       char *path, *name, buffer[MAXPATHLEN];
+
+       strcpy(buffer, pathname);
+       path = buffer;
+       ino = ROOTINO;
+       while (*path == '/')
+               path++;
+       dp = NULL;
+       while ((name = strsep(&path, "/")) != NULL && *name /* != NULL */) {
+               if ((dp = searchdir(ino, name)) == NULL)
+                       return (NULL);
+               ino = dp->d_ino;
+       }
+       return (dp);
+}
+
+/*
+ * Lookup the requested name in directory inum.
+ * Return its inode number if found, zero if it does not exist.
+ */
+static struct direct *
+searchdir(inum, name)
+       ino_t   inum;
+       char    *name;
+{
+       register struct direct *dp;
+       register struct inotab *itp;
+       int len;
+
+       itp = inotablookup(inum);
+       if (itp == NULL)
+               return (NULL);
+       rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+       len = strlen(name);
+       do {
+               dp = rst_readdir(dirp);
+               if (dp == NULL)
+                       return (NULL);
+       } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
+       return (dp);
+}
+
+/*
+ * Put the directory entries in the directory file
+ */
+static void
+putdir(buf, size)
+       char *buf;
+       long size;
+{
+       struct direct cvtbuf;
+       register struct odirect *odp;
+       struct odirect *eodp;
+       register struct direct *dp;
+       long loc, i;
+
+       if (cvtflag) {
+               eodp = (struct odirect *)&buf[size];
+               for (odp = (struct odirect *)buf; odp < eodp; odp++)
+                       if (odp->d_ino != 0) {
+                               dcvt(odp, &cvtbuf);
+                               putent(&cvtbuf);
+                       }
+       } else {
+               for (loc = 0; loc < size; ) {
+                       dp = (struct direct *)(buf + loc);
+#ifdef DIRDEBUG
+                       printf ("reclen = %d, namlen = %d, type = %d\n",
+                               dp->d_reclen, dp->d_namlen, dp->d_type);
+#endif
+                       if (Bcvt)
+                               swabst((u_char *)"ls", (u_char *) dp);
+                       if (oldinofmt && dp->d_ino != 0) {
+#ifdef __linux__
+                               if (Bcvt)
+                                       swabst((u_char *)"s", (u_char *)&dp->d_namlen);
+#else
+#                              if BYTE_ORDER == BIG_ENDIAN
+                                       if (Bcvt)
+                                               dp->d_namlen = dp->d_type;
+#                              else
+                                       if (!Bcvt)
+                                               dp->d_namlen = dp->d_type;
+#                              endif
+#endif /* __linux__ */
+                               dp->d_type = DT_UNKNOWN;
+                       }
+#ifdef __linux__
+                       /*
+                        * Horrible hack to read FreeBSD 2.0 dumps
+                        */
+                       if (!oldinofmt)
+                               swabst((u_char *)"6bs", (u_char *) dp);
+#endif /* __linux__ */
+#ifdef DIRDEBUG
+                       printf ("reclen = %d, namlen = %d, type = %d\n",
+                               dp->d_reclen, dp->d_namlen, dp->d_type);
+#endif
+                       i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
+                       if ((dp->d_reclen & 0x3) != 0 ||
+                           dp->d_reclen > i ||
+                           dp->d_reclen < DIRSIZ(0, dp) ||
+                           dp->d_namlen > NAME_MAX) {
+                               vprintf(stdout, "Mangled directory: ");
+                               if ((dp->d_reclen & 0x3) != 0)
+                                       vprintf(stdout,
+                                          "reclen not multiple of 4 ");
+                               if (dp->d_reclen < DIRSIZ(0, dp))
+                                       vprintf(stdout,
+                                          "reclen less than DIRSIZ (%d < %d) ",
+                                          dp->d_reclen, DIRSIZ(0, dp));
+                               if (dp->d_namlen > NAME_MAX)
+                                       vprintf(stdout,
+                                          "reclen name too big (%d > %d) ",
+                                          dp->d_namlen, NAME_MAX);
+                               vprintf(stdout, "\n");
+                               loc += i;
+                               continue;
+                       }
+                       loc += dp->d_reclen;
+                       if (dp->d_ino != 0) {
+                               putent(dp);
+                       }
+               }
+       }
+}
+
+/*
+ * These variables are "local" to the following two functions.
+ */
+char dirbuf[DIRBLKSIZ];
+long dirloc = 0;
+long prev = 0;
+
+/*
+ * add a new directory entry to a file.
+ */
+static void
+putent(dp)
+       struct direct *dp;
+{
+       dp->d_reclen = DIRSIZ(0, dp);
+       if (dirloc + dp->d_reclen > DIRBLKSIZ) {
+               ((struct direct *)(dirbuf + prev))->d_reclen =
+                   DIRBLKSIZ - prev;
+               (void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
+               dirloc = 0;
+       }
+       memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
+       prev = dirloc;
+       dirloc += dp->d_reclen;
+}
+
+/*
+ * flush out a directory that is finished.
+ */
+static void
+flushent()
+{
+       ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
+       (void) fwrite(dirbuf, (int)dirloc, 1, df);
+       seekpt = ftell(df);
+       dirloc = 0;
+}
+
+static void
+dcvt(odp, ndp)
+       register struct odirect *odp;
+       register struct direct *ndp;
+{
+
+       memset(ndp, 0, (long)(sizeof *ndp));
+       ndp->d_ino =  odp->d_ino;
+#ifdef __linux__
+       ndp->d_type = DT_UNKNOWN;
+#endif
+       (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
+       ndp->d_namlen = strlen(ndp->d_name);
+       ndp->d_reclen = DIRSIZ(0, ndp);
+}
+
+/*
+ * Seek to an entry in a directory.
+ * Only values returned by rst_telldir should be passed to rst_seekdir.
+ * This routine handles many directories in a single file.
+ * It takes the base of the directory in the file, plus
+ * the desired seek offset into it.
+ */
+static void
+rst_seekdir(dirp, loc, base)
+       register RST_DIR *dirp;
+       long loc, base;
+{
+
+       if (loc == rst_telldir(dirp))
+               return;
+       loc -= base;
+       if (loc < 0)
+               fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc);
+       (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
+       dirp->dd_loc = loc & (DIRBLKSIZ - 1);
+       if (dirp->dd_loc != 0)
+               dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
+}
+
+/*
+ * get next entry in a directory.
+ */
+struct direct *
+rst_readdir(dirp)
+       register RST_DIR *dirp;
+{
+       register struct direct *dp;
+
+       for (;;) {
+               if (dirp->dd_loc == 0) {
+                       dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 
+                           DIRBLKSIZ);
+                       if (dirp->dd_size <= 0) {
+                               dprintf(stderr, "error reading directory\n");
+                               return (NULL);
+                       }
+               }
+               if (dirp->dd_loc >= dirp->dd_size) {
+                       dirp->dd_loc = 0;
+                       continue;
+               }
+               dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
+               if (dp->d_reclen == 0 ||
+                   dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
+                       dprintf(stderr, "corrupted directory: bad reclen %d\n"
+                               "dd_loc = %d, dd_size = %d\n",
+                               dp->d_reclen, dirp->dd_loc, dirp->dd_size);
+                       return (NULL);
+               }
+               dirp->dd_loc += dp->d_reclen;
+               if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
+                       return (NULL);
+               if (dp->d_ino >= maxino) {
+                       dprintf(stderr, "corrupted directory: bad inum %d\n",
+                               dp->d_ino);
+                       continue;
+               }
+               return (dp);
+       }
+}
+
+/*
+ * Simulate the opening of a directory
+ */
+RST_DIR *
+rst_opendir(name)
+       const char *name;
+{
+       struct inotab *itp;
+       RST_DIR *dirp;
+       ino_t ino;
+
+       if ((ino = dirlookup(name)) > 0 &&
+           (itp = inotablookup(ino)) != NULL) {
+               dirp = opendirfile(dirfile);
+               rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+               return (dirp);
+       }
+       return (NULL);
+}
+
+/*
+ * In our case, there is nothing to do when closing a directory.
+ */
+void
+rst_closedir(dirp)
+       RST_DIR *dirp;
+{
+
+       (void)close(dirp->dd_fd);
+       free(dirp);
+       return;
+}
+
+/*
+ * Simulate finding the current offset in the directory.
+ */
+static long
+rst_telldir(dirp)
+       RST_DIR *dirp;
+{
+       return ((long)lseek(dirp->dd_fd,
+           (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
+}
+
+/*
+ * Open a directory file.
+ */
+static RST_DIR *
+opendirfile(name)
+       const char *name;
+{
+       register RST_DIR *dirp;
+       register int fd;
+
+       if ((fd = open(name, O_RDONLY)) == -1)
+               return (NULL);
+       if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
+               (void)close(fd);
+               return (NULL);
+       }
+       dirp->dd_fd = fd;
+       dirp->dd_loc = 0;
+       return (dirp);
+}
+
+/*
+ * Set the mode, owner, and times for all new or changed directories
+ */
+void
+setdirmodes(flags)
+       int flags;
+{
+       FILE *mf;
+       struct modeinfo node;
+       struct entry *ep;
+       char *cp;
+       
+       vprintf(stdout, "Set directory mode, owner, and times.\n");
+       (void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate);
+       mf = fopen(modefile, "r");
+       if (mf == NULL) {
+               fprintf(stderr, "fopen: %s\n", strerror(errno));
+               fprintf(stderr, "cannot open mode file %s\n", modefile);
+               fprintf(stderr, "directory mode, owner, and times not set\n");
+               return;
+       }
+       clearerr(mf);
+       for (;;) {
+               (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
+               if (feof(mf))
+                       break;
+               ep = lookupino(node.ino);
+               if (command == 'i' || command == 'x') {
+                       if (ep == NULL)
+                               continue;
+                       if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
+                               ep->e_flags &= ~NEW;
+                               continue;
+                       }
+                       if (node.ino == ROOTINO &&
+                           reply("set owner/mode for '.'") == FAIL)
+                               continue;
+               }
+               if (ep == NULL) {
+                       panic("cannot find directory inode %d\n", node.ino);
+               } else {
+                       cp = myname(ep);
+                       (void) chown(cp, node.uid, node.gid);
+                       (void) chmod(cp, node.mode);
+#ifdef __linux__
+                       (void) setflags(cp, node.flags);
+#else
+                       (void) chflags(cp, node.flags);
+#endif
+                       utimes(cp, node.timep);
+                       ep->e_flags &= ~NEW;
+               }
+       }
+       if (ferror(mf))
+               panic("error setting directory modes\n");
+       (void) fclose(mf);
+}
+
+/*
+ * Generate a literal copy of a directory.
+ */
+int
+genliteraldir(name, ino)
+       char *name;
+       ino_t ino;
+{
+       register struct inotab *itp;
+       int ofile, dp, i, size;
+       char buf[BUFSIZ];
+
+       itp = inotablookup(ino);
+       if (itp == NULL)
+               panic("Cannot find directory inode %d named %s\n", ino, name);
+       if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
+               fprintf(stderr, "%s: ", name);
+               (void) fflush(stderr);
+               fprintf(stderr, "cannot create file: %s\n", strerror(errno));
+               return (FAIL);
+       }
+       rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+       dp = dup(dirp->dd_fd);
+       for (i = itp->t_size; i > 0; i -= BUFSIZ) {
+               size = i < BUFSIZ ? i : BUFSIZ;
+               if (read(dp, buf, (int) size) == -1) {
+                       fprintf(stderr,
+                               "write error extracting inode %d, name %s\n",
+                               curfile.ino, curfile.name);
+                       fprintf(stderr, "read: %s\n", strerror(errno));
+                       done(1);
+               }
+               if (!Nflag && write(ofile, buf, (int) size) == -1) {
+                       fprintf(stderr,
+                               "write error extracting inode %d, name %s\n",
+                               curfile.ino, curfile.name);
+                       fprintf(stderr, "write: %s\n", strerror(errno));
+                       done(1);
+               }
+       }
+       (void) close(dp);
+       (void) close(ofile);
+       return (GOOD);
+}
+
+/*
+ * Determine the type of an inode
+ */
+int
+inodetype(ino)
+       ino_t ino;
+{
+       struct inotab *itp;
+
+       itp = inotablookup(ino);
+       if (itp == NULL)
+               return (LEAF);
+       return (NODE);
+}
+
+/*
+ * Allocate and initialize a directory inode entry.
+ * If requested, save its pertinent mode, owner, and time info.
+ */
+static struct inotab *
+allocinotab(ino, dip, seekpt)
+       ino_t ino;
+#ifdef __linux__
+       struct new_bsd_inode *dip;
+#else
+       struct dinode *dip;
+#endif
+       long seekpt;
+{
+       register struct inotab  *itp;
+       struct modeinfo node;
+
+       itp = calloc(1, sizeof(struct inotab));
+       if (itp == NULL)
+               panic("no memory directory table\n");
+       itp->t_next = inotab[INOHASH(ino)];
+       inotab[INOHASH(ino)] = itp;
+       itp->t_ino = ino;
+       itp->t_seekpt = seekpt;
+       if (mf == NULL)
+               return (itp);
+       node.ino = ino;
+#ifdef __linux__
+       node.timep[0].tv_sec = dip->di_atime.tv_sec;
+       node.timep[0].tv_usec = dip->di_atime.tv_usec;
+       node.timep[1].tv_sec = dip->di_mtime.tv_sec;
+       node.timep[1].tv_usec = dip->di_mtime.tv_usec;
+#else  /* __linux__ */
+       node.timep[0].tv_sec = dip->di_atime;
+       node.timep[0].tv_usec = dip->di_atimensec / 1000;
+       node.timep[1].tv_sec = dip->di_mtime;
+       node.timep[1].tv_usec = dip->di_mtimensec / 1000;
+#endif /* __linux__ */
+       node.mode = dip->di_mode;
+       node.flags = dip->di_flags;
+       node.uid = dip->di_uid;
+       node.gid = dip->di_gid;
+       (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
+       return (itp);
+}
+
+/*
+ * Look up an inode in the table of directories
+ */
+static struct inotab *
+inotablookup(ino)
+       ino_t   ino;
+{
+       register struct inotab *itp;
+
+       for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
+               if (itp->t_ino == ino)
+                       return (itp);
+       return (NULL);
+}
+
+/*
+ * Clean up and exit
+ */
+__dead void
+done(exitcode)
+       int exitcode;
+{
+
+       closemt();
+       if (modefile[0] != '#')
+               (void) unlink(modefile);
+       if (dirfile[0] != '#')
+               (void) unlink(dirfile);
+       exit(exitcode);
+}
diff --git a/restore/extern.h b/restore/extern.h
new file mode 100644 (file)
index 0000000..54b37ab
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ *     @(#)extern.h    8.2 (Berkeley) 1/7/94
+ */
+
+struct entry   *addentry __P((char *, ino_t, int));
+long            addfile __P((char *, ino_t, int));
+void            badentry __P((struct entry *, char *));
+void            canon __P((char *, char *));
+void            checkrestore __P((void));
+void            closemt __P((void));
+void            createfiles __P((void));
+void            createleaves __P((char *));
+void            compareleaves __P((void));
+void            createlinks __P((void));
+long            deletefile __P((char *, ino_t, int));
+void            deleteino __P((ino_t));
+ino_t           dirlookup __P((const char *));
+__dead void     done __P((int));
+void            dumpsymtable __P((char *, long));
+void            extractdirs __P((int));
+int             extractfile __P((char *));
+void            comparefile __P((char *));
+void            findunreflinks __P((void));
+char           *flagvalues __P((struct entry *));
+void            freeentry __P((struct entry *));
+void            freename __P((char *));
+int             genliteraldir __P((char *, ino_t));
+char           *gentempname __P((struct entry *));
+void            getfile __P((void (*)(char *, long), void (*)(char *, long)));
+void            getvol __P((long));
+void            initsymtable __P((char *));
+int             inodetype __P((ino_t));
+int             linkit __P((char *, char *, int));
+struct entry   *lookupino __P((ino_t));
+struct entry   *lookupname __P((char *));
+long            listfile __P((char *, ino_t, int));
+ino_t           lowerbnd __P((ino_t));
+void            mktempname __P((struct entry *));
+void            moveentry __P((struct entry *, char *));
+void            msg __P((const char *, ...));
+char           *myname __P((struct entry *));
+void            newnode __P((struct entry *));
+void            newtapebuf __P((long));
+long            nodeupdates __P((char *, ino_t, int));
+void            onintr __P((int));
+void            panic __P((const char *, ...));
+void            pathcheck __P((char *));
+struct direct  *pathsearch __P((const char *));
+void            printdumpinfo __P((void));
+void            removeleaf __P((struct entry *));
+void            removenode __P((struct entry *));
+void            removeoldleaves __P((void));
+void            removeoldnodes __P((void));
+void            renameit __P((char *, char *));
+int             reply __P((char *));
+RST_DIR                *rst_opendir __P((const char *));
+struct direct  *rst_readdir __P((RST_DIR *));
+void            rst_closedir __P((RST_DIR *dirp));
+void            runcmdshell __P((void));
+char           *savename __P((char *));
+void            setdirmodes __P((int));
+void            setinput __P((char *));
+void            setup __P((void));
+void            skipdirs __P((void));
+void            skipfile __P((void));
+void            skipmaps __P((void));
+void            swabst __P((u_char *, u_char *));
+void            treescan __P((char *, ino_t, long (*)(char *, ino_t, int)));
+ino_t           upperbnd __P((ino_t));
+long            verifyfile __P((char *, ino_t, int));
+void            xtrnull __P((char *, long));
+
+/* From ../dump/dumprmt.c */
+void           rmtclose __P((void));
+int            rmthost __P((char *));
+int            rmtioctl __P((int, int));
+int            rmtopen __P((char *, int));
+int            rmtread __P((char *, int));
+int            rmtseek __P((int, int));
diff --git a/restore/interactive.c b/restore/interactive.c
new file mode 100644 (file)
index 0000000..c7b730b
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1985, 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
+static char sccsid[] = "@(#)interactive.c      8.5 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#endif /* __linux__ */
+#include <protocols/dumprestore.h>
+
+#include <setjmp.h>
+#include <glob.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#endif
+
+#include "restore.h"
+#include "extern.h"
+
+#define round(a, b) (((a) + (b) - 1) / (b) * (b))
+
+/*
+ * Things to handle interruptions.
+ */
+static int runshell;
+static jmp_buf reset;
+static char *nextarg = NULL;
+
+/*
+ * Structure and routines associated with listing directories.
+ */
+struct afile {
+       ino_t   fnum;           /* inode number of file */
+       char    *fname;         /* file name */
+       short   len;            /* name length */
+       char    prefix;         /* prefix character */
+       char    postfix;        /* postfix character */
+};
+struct arglist {
+       int     freeglob;       /* glob structure needs to be freed */
+       int     argcnt;         /* next globbed argument to return */
+       glob_t  glob;           /* globbing information */
+       char    *cmd;           /* the current command */
+};
+
+static char    *copynext __P((char *, char *));
+static int      fcmp __P((const void *, const void *));
+static void     formatf __P((struct afile *, int));
+static void     getcmd __P((char *, char *, char *, struct arglist *));
+struct dirent  *glob_readdir __P((RST_DIR *dirp));
+static int      glob_stat __P((const char *, struct stat *));
+static void     mkentry __P((char *, struct direct *, struct afile *));
+static void     printlist __P((char *, char *));
+
+/*
+ * Read and execute commands from the terminal.
+ */
+void
+runcmdshell()
+{
+       register struct entry *np;
+       ino_t ino;
+       struct arglist arglist;
+       char curdir[MAXPATHLEN];
+       char name[MAXPATHLEN];
+       char cmd[BUFSIZ];
+
+       arglist.freeglob = 0;
+       arglist.argcnt = 0;
+       arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
+       arglist.glob.gl_opendir = (void *)rst_opendir;
+       arglist.glob.gl_readdir = (void *)glob_readdir;
+       arglist.glob.gl_closedir = (void *)rst_closedir;
+       arglist.glob.gl_lstat = glob_stat;
+       arglist.glob.gl_stat = glob_stat;
+       canon("/", curdir);
+loop:
+       if (setjmp(reset) != 0) {
+               if (arglist.freeglob != 0) {
+                       arglist.freeglob = 0;
+                       arglist.argcnt = 0;
+                       globfree(&arglist.glob);
+               }
+               nextarg = NULL;
+               volno = 0;
+       }
+       runshell = 1;
+       getcmd(curdir, cmd, name, &arglist);
+       switch (cmd[0]) {
+       /*
+        * Add elements to the extraction list.
+        */
+       case 'a':
+               if (strncmp(cmd, "add", strlen(cmd)) != 0)
+                       goto bad;
+               ino = dirlookup(name);
+               if (ino == 0)
+                       break;
+               if (mflag)
+                       pathcheck(name);
+               treescan(name, ino, addfile);
+               break;
+       /*
+        * Change working directory.
+        */
+       case 'c':
+               if (strncmp(cmd, "cd", strlen(cmd)) != 0)
+                       goto bad;
+               ino = dirlookup(name);
+               if (ino == 0)
+                       break;
+               if (inodetype(ino) == LEAF) {
+                       fprintf(stderr, "%s: not a directory\n", name);
+                       break;
+               }
+               (void) strcpy(curdir, name);
+               break;
+       /*
+        * Delete elements from the extraction list.
+        */
+       case 'd':
+               if (strncmp(cmd, "delete", strlen(cmd)) != 0)
+                       goto bad;
+               np = lookupname(name);
+               if (np == NULL || (np->e_flags & NEW) == 0) {
+                       fprintf(stderr, "%s: not on extraction list\n", name);
+                       break;
+               }
+               treescan(name, np->e_ino, deletefile);
+               break;
+       /*
+        * Extract the requested list.
+        */
+       case 'e':
+               if (strncmp(cmd, "extract", strlen(cmd)) != 0)
+                       goto bad;
+               createfiles();
+               createlinks();
+               setdirmodes(0);
+               if (dflag)
+                       checkrestore();
+               volno = 0;
+               break;
+       /*
+        * List available commands.
+        */
+       case 'h':
+               if (strncmp(cmd, "help", strlen(cmd)) != 0)
+                       goto bad;
+       case '?':
+               fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                       "Available commands are:\n",
+                       "\tls [arg] - list directory\n",
+                       "\tcd arg - change directory\n",
+                       "\tpwd - print current directory\n",
+                       "\tadd [arg] - add `arg' to list of",
+                       " files to be extracted\n",
+                       "\tdelete [arg] - delete `arg' from",
+                       " list of files to be extracted\n",
+                       "\textract - extract requested files\n",
+                       "\tsetmodes - set modes of requested directories\n",
+                       "\tquit - immediately exit program\n",
+                       "\twhat - list dump header information\n",
+                       "\tverbose - toggle verbose flag",
+                       " (useful with ``ls'')\n",
+                       "\thelp or `?' - print this list\n",
+                       "If no `arg' is supplied, the current",
+                       " directory is used\n");
+               break;
+       /*
+        * List a directory.
+        */
+       case 'l':
+               if (strncmp(cmd, "ls", strlen(cmd)) != 0)
+                       goto bad;
+               printlist(name, curdir);
+               break;
+       /*
+        * Print current directory.
+        */
+       case 'p':
+               if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
+                       goto bad;
+               if (curdir[1] == '\0')
+                       fprintf(stderr, "/\n");
+               else
+                       fprintf(stderr, "%s\n", &curdir[1]);
+               break;
+       /*
+        * Quit.
+        */
+       case 'q':
+               if (strncmp(cmd, "quit", strlen(cmd)) != 0)
+                       goto bad;
+               return;
+       case 'x':
+               if (strncmp(cmd, "xit", strlen(cmd)) != 0)
+                       goto bad;
+               return;
+       /*
+        * Toggle verbose mode.
+        */
+       case 'v':
+               if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
+                       goto bad;
+               if (vflag) {
+                       fprintf(stderr, "verbose mode off\n");
+                       vflag = 0;
+                       break;
+               }
+               fprintf(stderr, "verbose mode on\n");
+               vflag++;
+               break;
+       /*
+        * Just restore requested directory modes.
+        */
+       case 's':
+               if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
+                       goto bad;
+               setdirmodes(FORCE);
+               break;
+       /*
+        * Print out dump header information.
+        */
+       case 'w':
+               if (strncmp(cmd, "what", strlen(cmd)) != 0)
+                       goto bad;
+               printdumpinfo();
+               break;
+       /*
+        * Turn on debugging.
+        */
+       case 'D':
+               if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
+                       goto bad;
+               if (dflag) {
+                       fprintf(stderr, "debugging mode off\n");
+                       dflag = 0;
+                       break;
+               }
+               fprintf(stderr, "debugging mode on\n");
+               dflag++;
+               break;
+       /*
+        * Unknown command.
+        */
+       default:
+       bad:
+               fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
+               break;
+       }
+       goto loop;
+}
+
+/*
+ * Read and parse an interactive command.
+ * The first word on the line is assigned to "cmd". If
+ * there are no arguments on the command line, then "curdir"
+ * is returned as the argument. If there are arguments
+ * on the line they are returned one at a time on each
+ * successive call to getcmd. Each argument is first assigned
+ * to "name". If it does not start with "/" the pathname in
+ * "curdir" is prepended to it. Finally "canon" is called to
+ * eliminate any embedded ".." components.
+ */
+static void
+getcmd(curdir, cmd, name, ap)
+       char *curdir, *cmd, *name;
+       struct arglist *ap;
+{
+       register char *cp;
+       static char input[BUFSIZ];
+       char output[BUFSIZ];
+#      define rawname input    /* save space by reusing input buffer */
+
+       /*
+        * Check to see if still processing arguments.
+        */
+       if (ap->argcnt > 0)
+               goto retnext;
+       if (nextarg != NULL)
+               goto getnext;
+       /*
+        * Read a command line and trim off trailing white space.
+        */
+       do      {
+               fprintf(stderr, "restore > ");
+               (void) fflush(stderr);
+               (void) fgets(input, BUFSIZ, terminal);
+       } while (!feof(terminal) && input[0] == '\n');
+       if (feof(terminal)) {
+               (void) strcpy(cmd, "quit");
+               return;
+       }
+       for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
+               /* trim off trailing white space and newline */;
+       *++cp = '\0';
+       /*
+        * Copy the command into "cmd".
+        */
+       cp = copynext(input, cmd);
+       ap->cmd = cmd;
+       /*
+        * If no argument, use curdir as the default.
+        */
+       if (*cp == '\0') {
+               (void) strcpy(name, curdir);
+               return;
+       }
+       nextarg = cp;
+       /*
+        * Find the next argument.
+        */
+getnext:
+       cp = copynext(nextarg, rawname);
+       if (*cp == '\0')
+               nextarg = NULL;
+       else
+               nextarg = cp;
+       /*
+        * If it is an absolute pathname, canonicalize it and return it.
+        */
+       if (rawname[0] == '/') {
+               canon(rawname, name);
+       } else {
+               /*
+                * For relative pathnames, prepend the current directory to
+                * it then canonicalize and return it.
+                */
+               (void) strcpy(output, curdir);
+               (void) strcat(output, "/");
+               (void) strcat(output, rawname);
+               canon(output, name);
+       }
+       if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
+               fprintf(stderr, "%s: out of memory\n", ap->cmd);
+       if (ap->glob.gl_pathc == 0)
+               return;
+       ap->freeglob = 1;
+       ap->argcnt = ap->glob.gl_pathc;
+
+retnext:
+       strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]);
+       if (--ap->argcnt == 0) {
+               ap->freeglob = 0;
+               globfree(&ap->glob);
+       }
+#      undef rawname
+}
+
+/*
+ * Strip off the next token of the input.
+ */
+static char *
+copynext(input, output)
+       char *input, *output;
+{
+       register char *cp, *bp;
+       char quote;
+
+       for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
+               /* skip to argument */;
+       bp = output;
+       while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
+               /*
+                * Handle back slashes.
+                */
+               if (*cp == '\\') {
+                       if (*++cp == '\0') {
+                               fprintf(stderr,
+                                       "command lines cannot be continued\n");
+                               continue;
+                       }
+                       *bp++ = *cp++;
+                       continue;
+               }
+               /*
+                * The usual unquoted case.
+                */
+               if (*cp != '\'' && *cp != '"') {
+                       *bp++ = *cp++;
+                       continue;
+               }
+               /*
+                * Handle single and double quotes.
+                */
+               quote = *cp++;
+               while (*cp != quote && *cp != '\0')
+                       *bp++ = *cp++ /* | 0200 */;
+               if (*cp++ == '\0') {
+                       fprintf(stderr, "missing %c\n", quote);
+                       cp--;
+                       continue;
+               }
+       }
+       *bp = '\0';
+       return (cp);
+}
+
+/*
+ * Canonicalize file names to always start with ``./'' and
+ * remove any imbedded "." and ".." components.
+ */
+void
+canon(rawname, canonname)
+       char *rawname, *canonname;
+{
+       register char *cp, *np;
+
+       if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
+               (void) strcpy(canonname, "");
+       else if (rawname[0] == '/')
+               (void) strcpy(canonname, ".");
+       else
+               (void) strcpy(canonname, "./");
+       (void) strcat(canonname, rawname);
+       /*
+        * Eliminate multiple and trailing '/'s
+        */
+       for (cp = np = canonname; *np != '\0'; cp++) {
+               *cp = *np++;
+               while (*cp == '/' && *np == '/')
+                       np++;
+       }
+       *cp = '\0';
+       if (*--cp == '/')
+               *cp = '\0';
+       /*
+        * Eliminate extraneous "." and ".." from pathnames.
+        */
+       for (np = canonname; *np != '\0'; ) {
+               np++;
+               cp = np;
+               while (*np != '/' && *np != '\0')
+                       np++;
+               if (np - cp == 1 && *cp == '.') {
+                       cp--;
+                       (void) strcpy(cp, np);
+                       np = cp;
+               }
+               if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
+                       cp--;
+                       while (cp > &canonname[1] && *--cp != '/')
+                               /* find beginning of name */;
+                       (void) strcpy(cp, np);
+                       np = cp;
+               }
+       }
+}
+
+/*
+ * Do an "ls" style listing of a directory
+ */
+static void
+printlist(name, basename)
+       char *name;
+       char *basename;
+{
+       register struct afile *fp, *list, *listp;
+       register struct direct *dp;
+       struct afile single;
+       RST_DIR *dirp;
+       int entries, len, namelen;
+       char locname[MAXPATHLEN + 1];
+
+       dp = pathsearch(name);
+       if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
+           (!vflag && dp->d_ino == WINO))
+               return;
+       if ((dirp = rst_opendir(name)) == NULL) {
+               entries = 1;
+               list = &single;
+               mkentry(name, dp, list);
+               len = strlen(basename) + 1;
+               if (strlen(name) - len > single.len) {
+                       freename(single.fname);
+                       single.fname = savename(&name[len]);
+                       single.len = strlen(single.fname);
+               }
+       } else {
+               entries = 0;
+/*             while ((dp = rst_readdir(dirp)) && (dp->d_ino != 0)) */
+               while (dp = rst_readdir(dirp))
+                       entries++;
+               rst_closedir(dirp);
+               list = (struct afile *)malloc(entries * sizeof(struct afile));
+               if (list == NULL) {
+                       fprintf(stderr, "ls: out of memory\n");
+                       return;
+               }
+               if ((dirp = rst_opendir(name)) == NULL)
+                       panic("directory reopen failed\n");
+               fprintf(stderr, "%s:\n", name);
+               entries = 0;
+               listp = list;
+               (void) strncpy(locname, name, MAXPATHLEN);
+               (void) strncat(locname, "/", MAXPATHLEN);
+               namelen = strlen(locname);
+               while (dp = rst_readdir(dirp)) {
+                       if (dp == NULL)
+                               break;
+                       if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
+                               continue;
+                       if (!vflag && (dp->d_ino == WINO ||
+                            strcmp(dp->d_name, ".") == 0 ||
+                            strcmp(dp->d_name, "..") == 0))
+                               continue;
+                       locname[namelen] = '\0';
+                       if (namelen + dp->d_namlen >= MAXPATHLEN) {
+                               fprintf(stderr, "%s%s: name exceeds %d char\n",
+                                       locname, dp->d_name, MAXPATHLEN);
+                       } else {
+                               (void) strncat(locname, dp->d_name,
+                                   (int)dp->d_namlen);
+                               mkentry(locname, dp, listp++);
+                               entries++;
+                       }
+               }
+               rst_closedir(dirp);
+               if (entries == 0) {
+                       fprintf(stderr, "\n");
+                       free(list);
+                       return;
+               }
+               qsort((char *)list, entries, sizeof(struct afile), fcmp);
+       }
+       formatf(list, entries);
+       if (dirp != NULL) {
+               for (fp = listp - 1; fp >= list; fp--)
+                       freename(fp->fname);
+               fprintf(stderr, "\n");
+               free(list);
+       }
+}
+
+/*
+ * Read the contents of a directory.
+ */
+static void
+mkentry(name, dp, fp)
+       char *name;
+       struct direct *dp;
+       register struct afile *fp;
+{
+       char *cp;
+       struct entry *np;
+
+       fp->fnum = dp->d_ino;
+       fp->fname = savename(dp->d_name);
+       for (cp = fp->fname; *cp; cp++)
+               if (!vflag && (*cp < ' ' || *cp >= 0177))
+                       *cp = '?';
+       fp->len = cp - fp->fname;
+       if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
+               fp->prefix = '^';
+       else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
+               fp->prefix = '*';
+       else
+               fp->prefix = ' ';
+#if    0
+       if (inodetype(dp->d_ino) == NODE)
+               fp->postfix = '/';
+       else
+               fp->postfix = ' ';
+#else  /* __linux__ */
+       switch(dp->d_type) {
+
+       default:
+               fprintf(stderr, "Warning: undefined file type %d\n",
+                   dp->d_type);
+               /* fall through */
+       case DT_REG:
+               fp->postfix = ' ';
+               break;
+
+       case DT_LNK:
+               fp->postfix = '@';
+               break;
+
+       case DT_FIFO:
+       case DT_SOCK:
+               fp->postfix = '=';
+               break;
+
+       case DT_CHR:
+       case DT_BLK:
+               fp->postfix = '#';
+               break;
+
+       case DT_WHT:
+               fp->postfix = '%';
+               break;
+
+       case DT_UNKNOWN:
+       case DT_DIR:
+               if (inodetype(dp->d_ino) == NODE)
+                       fp->postfix = '/';
+               else
+                       fp->postfix = ' ';
+               break;
+       }
+#endif /* __linux__ */
+       return;
+}
+
+/*
+ * Print out a pretty listing of a directory
+ */
+static void
+formatf(list, nentry)
+       register struct afile *list;
+       int nentry;
+{
+       register struct afile *fp, *endlist;
+       int width, bigino, haveprefix, havepostfix;
+       int i, j, w, precision, columns, lines;
+
+       width = 0;
+       haveprefix = 0;
+       havepostfix = 0;
+       bigino = ROOTINO;
+       endlist = &list[nentry];
+       for (fp = &list[0]; fp < endlist; fp++) {
+               if (bigino < fp->fnum)
+                       bigino = fp->fnum;
+               if (width < fp->len)
+                       width = fp->len;
+               if (fp->prefix != ' ')
+                       haveprefix = 1;
+               if (fp->postfix != ' ')
+                       havepostfix = 1;
+       }
+       if (haveprefix)
+               width++;
+       if (havepostfix)
+               width++;
+       if (vflag) {
+               for (precision = 0, i = bigino; i > 0; i /= 10)
+                       precision++;
+               width += precision + 1;
+       }
+       width++;
+       columns = 81 / width;
+       if (columns == 0)
+               columns = 1;
+       lines = (nentry + columns - 1) / columns;
+       for (i = 0; i < lines; i++) {
+               for (j = 0; j < columns; j++) {
+                       fp = &list[j * lines + i];
+                       if (vflag) {
+                               fprintf(stderr, "%*d ", precision, fp->fnum);
+                               fp->len += precision + 1;
+                       }
+                       if (haveprefix) {
+                               putc(fp->prefix, stderr);
+                               fp->len++;
+                       }
+                       fprintf(stderr, "%s", fp->fname);
+                       if (havepostfix) {
+                               putc(fp->postfix, stderr);
+                               fp->len++;
+                       }
+                       if (fp + lines >= endlist) {
+                               fprintf(stderr, "\n");
+                               break;
+                       }
+                       for (w = fp->len; w < width; w++)
+                               putc(' ', stderr);
+               }
+       }
+}
+
+/*
+ * Skip over directory entries that are not on the tape
+ *
+ * First have to get definition of a dirent.
+ */
+#ifdef __linux__
+struct dirent {
+       off_t           d_off;          /* offset of next disk dir entry */
+        unsigned long   d_fileno;       /* file number of entry */
+        unsigned short  d_reclen;       /* length of this record */
+        unsigned short  d_namlen;       /* length of string in d_name */
+        char            d_name[255+1];  /* name (up to MAXNAMLEN + 1) */
+};
+#else  /* __linux__ */
+#undef DIRBLKSIZ
+#include <dirent.h>
+#undef d_ino
+#endif /* __linux__ */
+
+struct dirent *
+glob_readdir(dirp)
+       RST_DIR *dirp;
+{
+       struct direct *dp;
+       static struct dirent adirent;
+
+       while ((dp = rst_readdir(dirp)) != NULL) {
+               if (!vflag && dp->d_ino == WINO)
+                       continue;
+               if (dflag || TSTINO(dp->d_ino, dumpmap))
+                       break;
+       }
+       if (dp == NULL)
+               return (NULL);
+       adirent.d_fileno = dp->d_ino;
+#ifdef __linux__
+       adirent.d_namlen = dp->d_namlen;
+#else
+       adirent.d_namlen = dp->d_namlen & 0xff;
+#endif
+       memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
+       return (&adirent);
+}
+
+/*
+ * Return st_mode information in response to stat or lstat calls
+ */
+static int
+glob_stat(name, stp)
+       const char *name;
+       struct stat *stp;
+{
+       register struct direct *dp;
+
+       dp = pathsearch(name);
+       if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
+           (!vflag && dp->d_ino == WINO))
+               return (-1);
+       if (inodetype(dp->d_ino) == NODE)
+               stp->st_mode = IFDIR;
+       else
+               stp->st_mode = IFREG;
+       return (0);
+}
+
+/*
+ * Comparison routine for qsort.
+ */
+static int
+fcmp(f1, f2)
+       register const void *f1, *f2;
+{
+       return (strcmp(((struct afile *)f1)->fname,
+           ((struct afile *)f2)->fname));
+}
+
+/*
+ * respond to interrupts
+ */
+void
+onintr(signo)
+       int signo;
+{
+       if (command == 'i' && runshell)
+               longjmp(reset, 1);
+       if (reply("restore interrupted, continue") == FAIL)
+               done(1);
+}
diff --git a/restore/main.c b/restore/main.c
new file mode 100644 (file)
index 0000000..6f4098d
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 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
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     8.6 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif /* __linux__ */
+#include <protocols/dumprestore.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#include <getopt.h>
+#endif
+
+#include "pathnames.h"
+#include "restore.h"
+#include "extern.h"
+
+int    bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0;
+int    hflag = 1, mflag = 1, Nflag = 0;
+char   command = '\0';
+long   dumpnum = 1;
+long   volno = 0;
+long   ntrec;
+char   *dumpmap;
+char   *usedinomap;
+ino_t  maxino;
+time_t dumptime;
+time_t dumpdate;
+FILE   *terminal;
+int    compare_ignore_not_found;
+char   *filesys = NULL;
+char   *tmpdir = _PATH_TMP;
+
+#ifdef __linux__
+char   *__progname;
+#endif
+
+static void obsolete __P((int *, char **[]));
+static void usage __P((void));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch;
+       ino_t ino;
+       char *inputdev = _PATH_DEFTAPE;
+       char *symtbl = "./restoresymtable";
+       char *p, name[MAXPATHLEN];
+
+       if (argc < 2)
+               usage();
+
+#ifdef __linux__
+       __progname = argv[0];
+#endif
+
+       obsolete(&argc, &argv);
+       while ((ch = getopt(argc, argv, "b:CcdD:f:himNRrs:tT:vxy")) != EOF)
+               switch(ch) {
+               case 'b':
+                       /* Change default tape blocksize. */
+                       bflag = 1;
+                       ntrec = strtol(optarg, &p, 10);
+                       if (*p)
+                               errx(1, "illegal blocksize -- %s", optarg);
+                       if (ntrec <= 0)
+                               errx(1, "block size must be greater than 0");
+                       break;
+               case 'c':
+                       cvtflag = 1;
+                       break;
+               case 'D':
+                       filesys = optarg;
+                       break;
+               case 'T':
+                       tmpdir = optarg;
+                       break;
+               case 'd':
+                       dflag = 1;
+                       break;
+               case 'f':
+                       inputdev = optarg;
+                       break;
+               case 'h':
+                       hflag = 0;
+                       break;
+               case 'C':
+               case 'i':
+               case 'R':
+               case 'r':
+               case 't':
+               case 'x':
+                       if (command != '\0')
+                               errx(1,
+                                   "%c and %c options are mutually exclusive",
+                                   ch, command);
+                       command = ch;
+                       break;
+               case 'm':
+                       mflag = 0;
+                       break;
+               case 'N':
+                       Nflag = 1;
+                       break;
+               case 's':
+                       /* Dumpnum (skip to) for multifile dump tapes. */
+                       dumpnum = strtol(optarg, &p, 10);
+                       if (*p)
+                               errx(1, "illegal dump number -- %s", optarg);
+                       if (dumpnum <= 0)
+                               errx(1, "dump number must be greater than 0");
+                       break;
+               case 'v':
+                       vflag = 1;
+                       break;
+               case 'y':
+                       yflag = 1;
+                       break;
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (command == '\0')
+               errx(1, "none of C, i, R, r, t or x options specified");
+
+       if (signal(SIGINT, onintr) == SIG_IGN)
+               (void) signal(SIGINT, SIG_IGN);
+       if (signal(SIGTERM, onintr) == SIG_IGN)
+               (void) signal(SIGTERM, SIG_IGN);
+       setlinebuf(stderr);
+
+       setinput(inputdev);
+
+       if (argc == 0) {
+               argc = 1;
+               *--argv = ".";
+       }
+
+       switch (command) {
+       /*
+        * Compare contents of tape.
+        */
+       case 'C': {
+               struct stat stbuf;
+
+               vprintf(stdout, "Begin compare restore\n");
+               compare_ignore_not_found = 0;
+               setup();
+               printf("filesys = %s\n", filesys);
+               if (stat(filesys, &stbuf) < 0) {
+                       fprintf(stderr, "cannot stat directory %s: %s\n",
+                               filesys, strerror(errno));
+                       exit(1);
+               } else {
+                       if (chdir(filesys) < 0) {
+                               fprintf(stderr, "cannot cd to %s: %s\n",
+                                       filesys, strerror(errno));
+                               exit(1);
+                       }
+               }
+               compare_ignore_not_found = dumptime > 0;
+               initsymtable((char *)0);
+               extractdirs(0);
+               treescan(".", ROOTINO, nodeupdates);
+               compareleaves();
+               checkrestore();
+               break;
+       }
+
+       /*
+        * Interactive mode.
+        */
+       case 'i':
+               setup();
+               extractdirs(1);
+               initsymtable(NULL);
+               runcmdshell();
+               break;
+       /*
+        * Incremental restoration of a file system.
+        */
+       case 'r':
+               setup();
+               if (dumptime > 0) {
+                       /*
+                        * This is an incremental dump tape.
+                        */
+                       vprintf(stdout, "Begin incremental restore\n");
+                       initsymtable(symtbl);
+                       extractdirs(1);
+                       removeoldleaves();
+                       vprintf(stdout, "Calculate node updates.\n");
+                       treescan(".", ROOTINO, nodeupdates);
+                       findunreflinks();
+                       removeoldnodes();
+               } else {
+                       /*
+                        * This is a level zero dump tape.
+                        */
+                       vprintf(stdout, "Begin level 0 restore\n");
+                       initsymtable((char *)0);
+                       extractdirs(1);
+                       vprintf(stdout, "Calculate extraction list.\n");
+                       treescan(".", ROOTINO, nodeupdates);
+               }
+               createleaves(symtbl);
+               createlinks();
+               setdirmodes(FORCE);
+               checkrestore();
+               if (dflag) {
+                       vprintf(stdout, "Verify the directory structure\n");
+                       treescan(".", ROOTINO, verifyfile);
+               }
+               dumpsymtable(symtbl, (long)1);
+               break;
+       /*
+        * Resume an incremental file system restoration.
+        */
+       case 'R':
+               initsymtable(symtbl);
+               skipmaps();
+               skipdirs();
+               createleaves(symtbl);
+               createlinks();
+               setdirmodes(FORCE);
+               checkrestore();
+               dumpsymtable(symtbl, (long)1);
+               break;
+       /*
+        * List contents of tape.
+        */
+       case 't':
+               setup();
+               extractdirs(0);
+               initsymtable((char *)0);
+               while (argc--) {
+                       canon(*argv++, name);
+                       ino = dirlookup(name);
+                       if (ino == 0)
+                               continue;
+                       treescan(name, ino, listfile);
+               }
+               break;
+       /*
+        * Batch extraction of tape contents.
+        */
+       case 'x':
+               setup();
+               extractdirs(1);
+               initsymtable((char *)0);
+               while (argc--) {
+                       canon(*argv++, name);
+                       ino = dirlookup(name);
+                       if (ino == 0)
+                               continue;
+                       if (mflag)
+                               pathcheck(name);
+                       treescan(name, ino, addfile);
+               }
+               createfiles();
+               createlinks();
+               setdirmodes(0);
+               if (dflag)
+                       checkrestore();
+               break;
+       }
+       done(0);
+       /* NOTREACHED */
+}
+
+static void
+usage()
+{
+       (void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n",
+         "restore -i [-chmvy] [-b blocksize] [-f file] [-s fileno]",
+         "restore -r [-cvy] [-b blocksize] [-f file] [-s fileno]",
+         "restore -R [-cvy] [-b blocksize] [-f file] [-s fileno]",
+         "restore -x [-chmvy] [-b blocksize] [-f file] [-s fileno] [file ...]",          "restore -t [-chvy] [-b blocksize] [-f file] [-s fileno] [file ...]");
+       done(1);
+}
+
+/*
+ * 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 'f':
+               case 's':
+                       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/restore/restore.8 b/restore/restore.8
new file mode 100644 (file)
index 0000000..785a6c3
--- /dev/null
@@ -0,0 +1,467 @@
+.\" Copyright (c) 1985, 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.
+.\"
+.\"     @(#)restore.8  8.4 (Berkeley) 5/1/95
+.\"
+.Dd May 1, 1995
+.Dt RESTORE 8
+.Os BSD 4
+.Sh NAME
+.Nm restore
+.Nd "restore files or file systems from backups made with dump"
+.Sh SYNOPSIS
+.Nm restore
+.Fl C
+.Op Fl cvy
+.Op Fl b Ar blocksize
+.Op Fl D Ar filesystem
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op Fl T Ar directory
+.Nm restore
+.Fl i
+.Op Fl chmNvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op Fl T Ar directory
+.Nm restore
+.Fl R
+.Op Fl cNvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op Fl T Ar directory
+.Nm restore
+.Fl r
+.Op Fl cNvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op Fl T Ar directory
+.Nm restore
+.Fl t
+.Op Fl chvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op Fl T Ar directory
+.Op file ...
+.Nm restore
+.Fl x
+.Op Fl chmNvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op Fl T Ar directory
+.Op file ...
+.Pp
+.in -\\n(iSu
+(The
+.Bx 4.3
+option syntax is implemented for backward compatibility, but
+is not documented here.)
+.Sh DESCRIPTION
+The
+.Nm restore
+command performs the inverse function of
+.Xr dump 8 .
+A full backup of a file system may be restored and
+subsequent incremental backups layered on top of it.
+Single files and
+directory subtrees may be restored from full or partial
+backups.
+.Nm Restore
+works across a network;
+to do this see the
+.Fl f
+flag described below.
+Other arguments to the command are file or directory
+names specifying the files that are to be restored.
+Unless the
+.Fl h
+flag is specified (see below),
+the appearance of a directory name refers to
+the files and (recursively) subdirectories of that directory.
+.Pp
+Exactly one of the following flags is required:
+.Bl -tag -width Ds
+.It Fl C
+This mode allows comparison of files from a dump.
+.Nm Restore
+reads the backup and compares its contents with files present on the
+disk.
+It first changes its working directory to the root of the filesystem
+that was dumped and compares the tape with the files in its new
+current directory.
+.It Fl i
+This mode allows interactive restoration of files from a dump.
+After reading in the directory information from the dump,
+.Nm restore
+provides a shell like interface that allows the user to move
+around the directory tree selecting files to be extracted.
+The available commands are given below;
+for those commands that require an argument,
+the default is the current directory.
+.Bl -tag -width Fl
+.It Ic add Op Ar arg
+The current directory or specified argument is added to the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+added to the extraction list
+(unless the
+.Fl h
+flag is specified on the command line).
+Files that are on the extraction list are prepended with a ``*''
+when they are listed by 
+.Ic ls .
+.It Ic \&cd Ar arg
+Change the current working directory to the specified argument.
+.It Ic delete Op Ar arg
+The current directory or specified argument is deleted from the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+deleted from the extraction list
+(unless the
+.Fl h
+flag is specified on the command line).
+The most expedient way to extract most of the files from a directory 
+is to add the directory to the extraction list and then delete
+those files that are not needed.
+.It Ic extract
+All the files that are on the extraction list are extracted
+from the dump.
+.Nm Restore
+will ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.It Ic help
+List a summary of the available commands.
+.It Ic \&ls Op Ar arg
+List the current or specified directory.
+Entries that are directories are appended with a ``/''.
+Entries that have been marked for extraction are prepended with a ``*''.
+If the verbose
+flag is set the inode number of each entry is also listed.
+.It Ic pwd
+Print the full pathname of the current working directory.
+.It Ic quit
+Restore immediately exits,
+even if the extraction list is not empty.
+.It Ic setmodes
+All the directories that have been added to the extraction list
+have their owner, modes, and times set;
+nothing is extracted from the dump.
+This is useful for cleaning up after a restore has been prematurely aborted.
+.It Ic verbose
+The sense of the 
+.Fl v
+flag is toggled.
+When set, the verbose flag causes the 
+.Ic ls
+command to list the inode numbers of all entries.
+It also causes
+.Nm restore
+to print out information about each file as it is extracted.
+.El
+.It Fl R
+.Nm Restore
+requests a particular tape of a multi volume set on which to restart
+a full restore
+(see the
+.Fl r
+flag below).
+This is useful if the restore has been interrupted.
+.It Fl r
+Restore (rebuild a file system).
+The target file system should be made pristine with
+.Xr newfs 8 ,
+mounted and the user
+.Xr cd Ns 'd
+into the pristine file system
+before starting the restoration of the initial level 0 backup. If the
+level 0 restores successfully, the
+.Fl r
+flag may be used to restore
+any necessary incremental backups on top of the level 0.
+The
+.Fl r
+flag precludes an interactive file extraction and can be
+detrimental to one's health if not used carefully (not to mention
+the disk). An example:
+.Bd -literal -offset indent
+newfs /dev/rrp0g eagle
+mount /dev/rp0g /mnt
+cd /mnt
+
+restore rf /dev/rst8
+.Ed
+.Pp
+Note that 
+.Nm restore
+leaves a file 
+.Pa restoresymtable
+in the root directory to pass information between incremental
+restore passes.
+This file should be removed when the last incremental has been
+restored.
+.Pp
+.Nm Restore ,
+in conjunction with
+.Xr newfs 8
+and
+.Xr dump 8 ,
+may be used to modify file system parameters
+such as size or block size.
+.It Fl t
+The names of the specified files are listed if they occur
+on the backup.
+If no file argument is given,
+then the root directory is listed,
+which results in the entire content of the
+backup being listed,
+unless the
+.Fl h
+flag has been specified.
+Note that the
+.Fl t
+flag replaces the function of the old
+.Xr dumpdir 8
+program.
+.ne 1i
+.It Fl x
+The named files are read from the given media.
+If a named file matches a directory whose contents 
+are on the backup
+and the
+.Fl h
+flag is not specified,
+the directory is recursively extracted.
+The owner, modification time,
+and mode are restored (if possible).
+If no file argument is given,
+then the root directory is extracted,
+which results in the entire content of the
+backup being extracted,
+unless the
+.Fl h
+flag has been specified.
+.El
+.Pp
+The following additional options may be specified:
+.Bl -tag -width Ds
+.It Fl b Ar blocksize
+The number of kilobytes per dump record.
+If the
+.Fl b
+option is not specified,
+.Nm restore
+tries to determine the block size dynamically.
+.It Fl c
+Normally,
+.Nm restore
+will try to determine dynamically whether the dump was made from an
+old (pre-4.4) or new format file sytem.  The
+.Fl c
+flag disables this check, and only allows reading a dump in the old
+format.
+.It Fl D Ar filesystem
+The
+.Fl D
+flag allows the user to specify the filesystem name when using
+.Nm restore
+with the
+.Fl C
+option to check the backup.
+.It Fl f Ar file
+Read the backup from
+.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 input).
+If the name of the file is of the form
+.Dq host:file ,
+or
+.Dq user@host:file ,
+.Nm restore
+reads from the named file on the remote host using
+.Xr rmt 8 .
+.Pp
+.It Fl h
+Extract the actual directory, 
+rather than the files that it references.
+This prevents hierarchical restoration of complete subtrees
+from the dump.
+.It Fl m
+Extract by inode numbers rather than by file name.
+This is useful if only a few files are being extracted,
+and one wants to avoid regenerating the complete pathname
+to the file.
+.It Fl N
+The
+.Fl N
+flag causes
+.Nm restore to only print file names. Files are not extracted.
+.It Fl s Ar fileno
+Read from the specified
+.Ar fileno
+on a multi-file tape.
+File numbering starts at 1.
+.It Fl T Ar directory
+The
+.Fl T
+flag allows the user to specify a directory to use for the storage of
+temporary files. The default value is /tmp. This flag is most useful
+when restoring files after having booted from a floppy.  There might be little
+or no space on the floppy filesystem, but another source of space might exist.
+.It Fl v
+Normally
+.Nm restore
+does its work silently.
+The
+.Fl v
+(verbose)
+flag causes it to type the name of each file it treats
+preceded by its file type.
+.It Fl y
+Do not ask the user whether to abort the restore in the event of an error.
+Always try to skip over the bad block(s) and continue.
+.El
+.Sh DIAGNOSTICS
+Complaints if it gets a read error.
+If 
+.Fl y
+has been specified, or the user responds
+.Ql y ,
+.Nm restore
+will attempt to continue the restore.
+.Pp
+If a backup was made using more than one tape volume,
+.Nm restore
+will notify the user when it is time to mount the next volume.
+If the
+.Fl x
+or
+.Fl i
+flag has been specified,
+.Nm restore
+will also ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.Pp
+There are numerous consistency checks that can be listed by
+.Nm restore .
+Most checks are self-explanatory or can ``never happen''.
+Common errors are given below.
+.Pp
+.Bl -tag -width Ds -compact
+.It Converting to new file system format.
+A dump tape created from the old file system has been loaded.
+It is automatically converted to the new file system format.
+.Pp
+.It <filename>: not found on tape
+The specified file name was listed in the tape directory,
+but was not found on the tape.
+This is caused by tape read errors while looking for the file,
+and from using a dump tape created on an active file system.
+.Pp
+.It expected next file <inumber>, got <inumber>
+A file that was not listed in the directory showed up.
+This can occur when using a dump created on an active file system.
+.Pp
+.It Incremental dump too low
+When doing incremental restore,
+a dump that was written before the previous incremental dump,
+or that has too low an incremental level has been loaded.
+.Pp
+.It Incremental dump too high
+When doing incremental restore,
+a dump that does not begin its coverage where the previous incremental 
+dump left off,
+or that has too high an incremental level has been loaded.
+.Pp
+.It Tape read error while restoring <filename>
+.It Tape read error while skipping over inode <inumber>
+.It Tape read error while trying to resynchronize
+A tape (or other media) read error has occurred.
+If a file name is specified,
+then its contents are probably partially wrong.
+If an inode is being skipped or the tape is trying to resynchronize,
+then no extracted files have been corrupted,
+though files may not be found on the tape.
+.Pp
+.It resync restore, skipped <num> blocks
+After a dump read error, 
+.Nm restore
+may have to resynchronize itself.
+This message lists the number of blocks that were skipped over.
+.El
+.Sh FILES
+.Bl -tag -width "./restoresymtable" -compact
+.It Pa /dev/rmt?
+the default tape drive
+.It Pa /tmp/rstdir*
+file containing directories on the tape.
+.It Pa /tmp/rstmode*
+owner, mode, and time stamps for directories.
+.It Pa \&./restoresymtable
+information passed between incremental restores.
+.El
+.Sh SEE ALSO
+.Xr dump 8 ,
+.Xr newfs 8 ,
+.Xr mount 8 ,
+.Xr mkfs 8 ,
+.Xr rmt 8
+.Sh BUGS
+.Nm Restore
+can get confused when doing incremental restores from
+dumps that were made on active file systems.
+.Pp
+A level zero dump must be done after a full restore.
+Because restore runs in user code,
+it has no control over inode allocation;
+thus a full dump must be done to get a new set of directories
+reflecting the new inode numbering,
+even though the contents of the files is unchanged.
+.Sh HISTORY
+The
+.Nm restore
+command appeared in
+.Bx 4.2 .
diff --git a/restore/restore.c b/restore/restore.c
new file mode 100644 (file)
index 0000000..533ae5b
--- /dev/null
@@ -0,0 +1,952 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 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
+static char sccsid[] = "@(#)restore.c  8.3 (Berkeley) 9/13/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#include <sys/time.h>
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#include <ufs/ufs/dinode.h>
+#endif /* __linux__ */
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#endif
+
+#include "restore.h"
+#include "extern.h"
+
+static char *keyval __P((int));
+
+/*
+ * This implements the 't' option.
+ * List entries on the tape.
+ */
+long
+listfile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       long descend = hflag ? GOOD : FAIL;
+
+       if (TSTINO(ino, dumpmap) == 0)
+               return (descend);
+       vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
+       fprintf(stdout, "%10d\t%s\n", ino, name);
+       return (descend);
+}
+
+/*
+ * This implements the 'x' option.
+ * Request that new entries be extracted.
+ */
+long
+addfile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       register struct entry *ep;
+       long descend = hflag ? GOOD : FAIL;
+       char buf[100];
+
+       if (TSTINO(ino, dumpmap) == 0) {
+               dprintf(stdout, "%s: not on the tape\n", name);
+               return (descend);
+       }
+       if (ino == WINO && command == 'i' && !vflag)
+               return (descend);
+       if (!mflag) {
+               (void) sprintf(buf, "./%u", ino);
+               name = buf;
+               if (type == NODE) {
+                       (void) genliteraldir(name, ino);
+                       return (descend);
+               }
+       }
+       ep = lookupino(ino);
+       if (ep != NULL) {
+               if (strcmp(name, myname(ep)) == 0) {
+                       ep->e_flags |= NEW;
+                       return (descend);
+               }
+               type |= LINK;
+       }
+       ep = addentry(name, ino, type);
+       if (type == NODE)
+               newnode(ep);
+       ep->e_flags |= NEW;
+       return (descend);
+}
+
+/*
+ * This is used by the 'i' option to undo previous requests made by addfile.
+ * Delete entries from the request queue.
+ */
+/* ARGSUSED */
+long
+deletefile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       long descend = hflag ? GOOD : FAIL;
+       struct entry *ep;
+
+       if (TSTINO(ino, dumpmap) == 0)
+               return (descend);
+       ep = lookupname(name);
+       if (ep != NULL) {
+               ep->e_flags &= ~NEW;
+               ep->e_flags |= REMOVED;
+               if (ep->e_type != NODE)
+                       freeentry(ep);
+       }
+       return (descend);
+}
+
+/* 
+ * The following four routines implement the incremental
+ * restore algorithm. The first removes old entries, the second
+ * does renames and calculates the extraction list, the third
+ * cleans up link names missed by the first two, and the final
+ * one deletes old directories.
+ *
+ * Directories cannot be immediately deleted, as they may have
+ * other files in them which need to be moved out first. As
+ * directories to be deleted are found, they are put on the 
+ * following deletion list. After all deletions and renames
+ * are done, this list is actually deleted.
+ */
+static struct entry *removelist;
+
+/*
+ *     Remove invalid whiteouts from the old tree.
+ *     Remove unneeded leaves from the old tree.
+ *     Remove directories from the lookup chains.
+ */
+void
+removeoldleaves()
+{
+       register struct entry *ep, *nextep;
+       register ino_t i, mydirino;
+
+       vprintf(stdout, "Mark entries to be removed.\n");
+       if (ep = lookupino(WINO)) {
+               vprintf(stdout, "Delete whiteouts\n");
+               for ( ; ep != NULL; ep = nextep) {
+                       nextep = ep->e_links;
+                       mydirino = ep->e_parent->e_ino;
+                       /*
+                        * We remove all whiteouts that are in directories
+                        * that have been removed or that have been dumped.
+                        */
+                       if (TSTINO(mydirino, usedinomap) &&
+                           !TSTINO(mydirino, dumpmap))
+                               continue;
+#ifdef __linux__
+                       (void)fprintf(stderr, "BUG! Should call delwhiteout\n");
+#else
+                       delwhiteout(ep);
+#endif
+                       freeentry(ep);
+               }
+       }
+       for (i = ROOTINO + 1; i < maxino; i++) {
+               ep = lookupino(i);
+               if (ep == NULL)
+                       continue;
+               if (TSTINO(i, usedinomap))
+                       continue;
+               for ( ; ep != NULL; ep = ep->e_links) {
+                       dprintf(stdout, "%s: REMOVE\n", myname(ep));
+                       if (ep->e_type == LEAF) {
+                               removeleaf(ep);
+                               freeentry(ep);
+                       } else {
+                               mktempname(ep);
+                               deleteino(ep->e_ino);
+                               ep->e_next = removelist;
+                               removelist = ep;
+                       }
+               }
+       }
+}
+
+/*
+ *     For each directory entry on the incremental tape, determine which
+ *     category it falls into as follows:
+ *     KEEP - entries that are to be left alone.
+ *     NEW - new entries to be added.
+ *     EXTRACT - files that must be updated with new contents.
+ *     LINK - new links to be added.
+ *     Renames are done at the same time.
+ */
+long
+nodeupdates(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       register struct entry *ep, *np, *ip;
+       long descend = GOOD;
+       int lookuptype = 0;
+       int key = 0;
+               /* key values */
+#              define ONTAPE   0x1     /* inode is on the tape */
+#              define INOFND   0x2     /* inode already exists */
+#              define NAMEFND  0x4     /* name already exists */
+#              define MODECHG  0x8     /* mode of inode changed */
+
+       /*
+        * This routine is called once for each element in the 
+        * directory hierarchy, with a full path name.
+        * The "type" value is incorrectly specified as LEAF for
+        * directories that are not on the dump tape.
+        *
+        * Check to see if the file is on the tape.
+        */
+       if (TSTINO(ino, dumpmap))
+               key |= ONTAPE;
+       /*
+        * Check to see if the name exists, and if the name is a link.
+        */
+       np = lookupname(name);
+       if (np != NULL) {
+               key |= NAMEFND;
+               ip = lookupino(np->e_ino);
+               if (ip == NULL)
+                       panic("corrupted symbol table\n");
+               if (ip != np)
+                       lookuptype = LINK;
+       }
+       /*
+        * Check to see if the inode exists, and if one of its links
+        * corresponds to the name (if one was found).
+        */
+       ip = lookupino(ino);
+       if (ip != NULL) {
+               key |= INOFND;
+               for (ep = ip->e_links; ep != NULL; ep = ep->e_links) {
+                       if (ep == np) {
+                               ip = ep;
+                               break;
+                       }
+               }
+       }
+       /*
+        * If both a name and an inode are found, but they do not
+        * correspond to the same file, then both the inode that has
+        * been found and the inode corresponding to the name that
+        * has been found need to be renamed. The current pathname
+        * is the new name for the inode that has been found. Since
+        * all files to be deleted have already been removed, the
+        * named file is either a now unneeded link, or it must live
+        * under a new name in this dump level. If it is a link, it
+        * can be removed. If it is not a link, it is given a
+        * temporary name in anticipation that it will be renamed
+        * when it is later found by inode number.
+        */
+       if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
+               if (lookuptype == LINK) {
+                       removeleaf(np);
+                       freeentry(np);
+               } else {
+                       dprintf(stdout, "name/inode conflict, mktempname %s\n",
+                               myname(np));
+                       mktempname(np);
+               }
+               np = NULL;
+               key &= ~NAMEFND;
+       }
+       if ((key & ONTAPE) &&
+         (((key & INOFND) && ip->e_type != type) ||
+          ((key & NAMEFND) && np->e_type != type)))
+               key |= MODECHG;
+
+       /*
+        * Decide on the disposition of the file based on its flags.
+        * Note that we have already handled the case in which
+        * a name and inode are found that correspond to different files.
+        * Thus if both NAMEFND and INOFND are set then ip == np.
+        */
+       switch (key) {
+
+       /*
+        * A previously existing file has been found.
+        * Mark it as KEEP so that other links to the inode can be
+        * detected, and so that it will not be reclaimed by the search
+        * for unreferenced names.
+        */
+       case INOFND|NAMEFND:
+               ip->e_flags |= KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(ip));
+               break;
+
+       /*
+        * A file on the tape has a name which is the same as a name
+        * corresponding to a different file in the previous dump.
+        * Since all files to be deleted have already been removed,
+        * this file is either a now unneeded link, or it must live
+        * under a new name in this dump level. If it is a link, it
+        * can simply be removed. If it is not a link, it is given a
+        * temporary name in anticipation that it will be renamed
+        * when it is later found by inode number (see INOFND case
+        * below). The entry is then treated as a new file.
+        */
+       case ONTAPE|NAMEFND:
+       case ONTAPE|NAMEFND|MODECHG:
+               if (lookuptype == LINK) {
+                       removeleaf(np);
+                       freeentry(np);
+               } else {
+                       mktempname(np);
+               }
+               /* fall through */
+
+       /*
+        * A previously non-existent file.
+        * Add it to the file system, and request its extraction.
+        * If it is a directory, create it immediately.
+        * (Since the name is unused there can be no conflict)
+        */
+       case ONTAPE:
+               ep = addentry(name, ino, type);
+               if (type == NODE)
+                       newnode(ep);
+               ep->e_flags |= NEW|KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(ep));
+               break;
+
+       /*
+        * A file with the same inode number, but a different
+        * name has been found. If the other name has not already
+        * been found (indicated by the KEEP flag, see above) then
+        * this must be a new name for the file, and it is renamed.
+        * If the other name has been found then this must be a
+        * link to the file. Hard links to directories are not
+        * permitted, and are either deleted or converted to
+        * symbolic links. Finally, if the file is on the tape,
+        * a request is made to extract it.
+        */
+       case ONTAPE|INOFND:
+               if (type == LEAF && (ip->e_flags & KEEP) == 0)
+                       ip->e_flags |= EXTRACT;
+               /* fall through */
+       case INOFND:
+               if ((ip->e_flags & KEEP) == 0) {
+                       renameit(myname(ip), name);
+                       moveentry(ip, name);
+                       ip->e_flags |= KEEP;
+                       dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                               flagvalues(ip));
+                       break;
+               }
+               if (ip->e_type == NODE) {
+                       descend = FAIL;
+                       fprintf(stderr,
+                               "deleted hard link %s to directory %s\n",
+                               name, myname(ip));
+                       break;
+               }
+               ep = addentry(name, ino, type|LINK);
+               ep->e_flags |= NEW;
+               dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+                       flagvalues(ep));
+               break;
+
+       /*
+        * A previously known file which is to be updated. If it is a link,
+        * then all names referring to the previous file must be removed
+        * so that the subset of them that remain can be recreated.
+        */
+       case ONTAPE|INOFND|NAMEFND:
+               if (lookuptype == LINK) {
+                       removeleaf(np);
+                       freeentry(np);
+                       ep = addentry(name, ino, type|LINK);
+                       if (type == NODE)
+                               newnode(ep);
+                       ep->e_flags |= NEW|KEEP;
+                       dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+                               flagvalues(ep));
+                       break;
+               }
+               if (type == LEAF && lookuptype != LINK)
+                       np->e_flags |= EXTRACT;
+               np->e_flags |= KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(np));
+               break;
+
+       /*
+        * An inode is being reused in a completely different way.
+        * Normally an extract can simply do an "unlink" followed
+        * by a "creat". Here we must do effectively the same
+        * thing. The complications arise because we cannot really
+        * delete a directory since it may still contain files
+        * that we need to rename, so we delete it from the symbol
+        * table, and put it on the list to be deleted eventually.
+        * Conversely if a directory is to be created, it must be
+        * done immediately, rather than waiting until the 
+        * extraction phase.
+        */
+       case ONTAPE|INOFND|MODECHG:
+       case ONTAPE|INOFND|NAMEFND|MODECHG:
+               if (ip->e_flags & KEEP) {
+                       badentry(ip, "cannot KEEP and change modes");
+                       break;
+               }
+               if (ip->e_type == LEAF) {
+                       /* changing from leaf to node */
+                       removeleaf(ip);
+                       freeentry(ip);
+                       ip = addentry(name, ino, type);
+                       newnode(ip);
+               } else {
+                       /* changing from node to leaf */
+                       if ((ip->e_flags & TMPNAME) == 0)
+                               mktempname(ip);
+                       deleteino(ip->e_ino);
+                       ip->e_next = removelist;
+                       removelist = ip;
+                       ip = addentry(name, ino, type);
+               }
+               ip->e_flags |= NEW|KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(ip));
+               break;
+
+       /*
+        * A hard link to a diirectory that has been removed.
+        * Ignore it.
+        */
+       case NAMEFND:
+               dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
+                       name);
+               descend = FAIL;
+               break;
+
+       /*
+        * If we find a directory entry for a file that is not on
+        * the tape, then we must have found a file that was created
+        * while the dump was in progress. Since we have no contents
+        * for it, we discard the name knowing that it will be on the
+        * next incremental tape.
+        */
+       case NULL:
+               if (compare_ignore_not_found) break;
+               fprintf(stderr, "%s: (inode %d) not found on tape\n",
+                       name, ino);
+               break;
+
+       /*
+        * If any of these arise, something is grievously wrong with
+        * the current state of the symbol table.
+        */
+       case INOFND|NAMEFND|MODECHG:
+       case NAMEFND|MODECHG:
+       case INOFND|MODECHG:
+               fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key),
+                       name);
+               break;
+
+       /*
+        * These states "cannot" arise for any state of the symbol table.
+        */
+       case ONTAPE|MODECHG:
+       case MODECHG:
+       default:
+               panic("[%s] %s: impossible state\n", keyval(key), name);
+               break;
+       }       
+       return (descend);
+}
+
+/*
+ * Calculate the active flags in a key.
+ */
+static char *
+keyval(key)
+       int key;
+{
+       static char keybuf[32];
+
+       (void) strcpy(keybuf, "|NIL");
+       keybuf[0] = '\0';
+       if (key & ONTAPE)
+               (void) strcat(keybuf, "|ONTAPE");
+       if (key & INOFND)
+               (void) strcat(keybuf, "|INOFND");
+       if (key & NAMEFND)
+               (void) strcat(keybuf, "|NAMEFND");
+       if (key & MODECHG)
+               (void) strcat(keybuf, "|MODECHG");
+       return (&keybuf[1]);
+}
+
+/*
+ * Find unreferenced link names.
+ */
+void
+findunreflinks()
+{
+       register struct entry *ep, *np;
+       register ino_t i;
+
+       vprintf(stdout, "Find unreferenced names.\n");
+       for (i = ROOTINO; i < maxino; i++) {
+               ep = lookupino(i);
+               if (ep == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0)
+                       continue;
+               for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
+                       if (np->e_flags == 0) {
+                               dprintf(stdout,
+                                   "%s: remove unreferenced name\n",
+                                   myname(np));
+                               removeleaf(np);
+                               freeentry(np);
+                       }
+               }
+       }
+       /*
+        * Any leaves remaining in removed directories is unreferenced.
+        */
+       for (ep = removelist; ep != NULL; ep = ep->e_next) {
+               for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
+                       if (np->e_type == LEAF) {
+                               if (np->e_flags != 0)
+                                       badentry(np, "unreferenced with flags");
+                               dprintf(stdout,
+                                   "%s: remove unreferenced name\n",
+                                   myname(np));
+                               removeleaf(np);
+                               freeentry(np);
+                       }
+               }
+       }
+}
+
+/*
+ * Remove old nodes (directories).
+ * Note that this routine runs in O(N*D) where:
+ *     N is the number of directory entries to be removed.
+ *     D is the maximum depth of the tree.
+ * If N == D this can be quite slow. If the list were
+ * topologically sorted, the deletion could be done in
+ * time O(N).
+ */
+void
+removeoldnodes()
+{
+       register struct entry *ep, **prev;
+       long change;
+
+       vprintf(stdout, "Remove old nodes (directories).\n");
+       do      {
+               change = 0;
+               prev = &removelist;
+               for (ep = removelist; ep != NULL; ep = *prev) {
+                       if (ep->e_entries != NULL) {
+                               prev = &ep->e_next;
+                               continue;
+                       }
+                       *prev = ep->e_next;
+                       removenode(ep);
+                       freeentry(ep);
+                       change++;
+               }
+       } while (change);
+       for (ep = removelist; ep != NULL; ep = ep->e_next)
+               badentry(ep, "cannot remove, non-empty");
+}
+
+/* Compare the file specified in `ep' (which is on tape) to the */
+/* current copy of this file on disk.  If do_compare is 0, then just */
+/* make our caller think we did it--this is used to handle hard links */
+/* to files and devices. */
+compare_entry(struct entry *ep, int do_compare)
+{
+       if ((ep->e_flags & (NEW|EXTRACT)) == 0)
+               badentry(ep, "unexpected file on tape");
+       if (do_compare) (void) comparefile(myname(ep));
+       ep->e_flags &= ~(NEW|EXTRACT);
+}
+
+/*
+ * This is the routine used to compare files for the 'C' command.
+ */
+void
+compareleaves()
+{
+       register struct entry *ep;
+       ino_t first;
+       long curvol;
+
+       first = lowerbnd(ROOTINO);
+       curvol = volno;
+       while (curfile.ino < maxino) {
+               first = lowerbnd(first);
+               /*
+                * If the next available file is not the one which we
+                * expect then we have missed one or more files. Since
+                * we do not request files that were not on the tape,
+                * the lost files must have been due to a tape read error,
+                * or a file that was removed while the dump was in progress.
+                */
+               while (first < curfile.ino) {
+                       ep = lookupino(first);
+                       if (ep == NULL)
+                               panic("%d: bad first\n", first);
+                       fprintf(stderr, "%s: not found on tape\n", myname(ep));
+                       ep->e_flags &= ~(NEW|EXTRACT);
+                       first = lowerbnd(first);
+               }
+               /*
+                * If we find files on the tape that have no corresponding
+                * directory entries, then we must have found a file that
+                * was created while the dump was in progress. Since we have 
+                * no name for it, we discard it knowing that it will be
+                * on the next incremental tape.
+                */
+               if (first != curfile.ino) {
+                       fprintf(stderr, "expected next file %d, got %d\n",
+                               first, curfile.ino);
+                       skipfile();
+                       goto next;
+               }
+               ep = lookupino(curfile.ino);
+               if (ep == NULL)
+                       panic("unknown file on tape\n");
+               compare_entry(ep, 1);
+               for (ep = ep->e_links; ep != NULL; ep = ep->e_links) {
+                       compare_entry(ep, 0);
+               }
+
+               /*
+                * We checkpoint the restore after every tape reel, so
+                * as to simplify the amount of work re quired by the
+                * 'R' command.
+                */
+       next:
+               if (curvol != volno) {
+                       skipmaps();
+                       curvol = volno;
+               }
+       }
+}
+
+/*
+ * This is the routine used to extract files for the 'r' command.
+ * Extract new leaves.
+ */
+void
+createleaves(symtabfile)
+       char *symtabfile;
+{
+       register struct entry *ep;
+       ino_t first;
+       long curvol;
+
+       if (command == 'R') {
+               vprintf(stdout, "Continue extraction of new leaves\n");
+       } else {
+               vprintf(stdout, "Extract new leaves.\n");
+               dumpsymtable(symtabfile, volno);
+       }
+       first = lowerbnd(ROOTINO);
+       curvol = volno;
+       while (curfile.ino < maxino) {
+               first = lowerbnd(first);
+               /*
+                * If the next available file is not the one which we
+                * expect then we have missed one or more files. Since
+                * we do not request files that were not on the tape,
+                * the lost files must have been due to a tape read error,
+                * or a file that was removed while the dump was in progress.
+                */
+               while (first < curfile.ino) {
+                       ep = lookupino(first);
+                       if (ep == NULL)
+                               panic("%d: bad first\n", first);
+                       fprintf(stderr, "%s: not found on tape\n", myname(ep));
+                       ep->e_flags &= ~(NEW|EXTRACT);
+                       first = lowerbnd(first);
+               }
+               /*
+                * If we find files on the tape that have no corresponding
+                * directory entries, then we must have found a file that
+                * was created while the dump was in progress. Since we have 
+                * no name for it, we discard it knowing that it will be
+                * on the next incremental tape.
+                */
+               if (first != curfile.ino) {
+                       fprintf(stderr, "expected next file %d, got %d\n",
+                               first, curfile.ino);
+                       skipfile();
+                       goto next;
+               }
+               ep = lookupino(curfile.ino);
+               if (ep == NULL)
+                       panic("unknown file on tape\n");
+               if ((ep->e_flags & (NEW|EXTRACT)) == 0)
+                       badentry(ep, "unexpected file on tape");
+               /*
+                * If the file is to be extracted, then the old file must
+                * be removed since its type may change from one leaf type
+                * to another (eg "file" to "character special").
+                */
+               if ((ep->e_flags & EXTRACT) != 0) {
+                       removeleaf(ep);
+                       ep->e_flags &= ~REMOVED;
+               }
+               (void) extractfile(myname(ep));
+               ep->e_flags &= ~(NEW|EXTRACT);
+               /*
+                * We checkpoint the restore after every tape reel, so
+                * as to simplify the amount of work re quired by the
+                * 'R' command.
+                */
+       next:
+               if (curvol != volno) {
+                       dumpsymtable(symtabfile, volno);
+                       skipmaps();
+                       curvol = volno;
+               }
+       }
+}
+
+/*
+ * This is the routine used to extract files for the 'x' and 'i' commands.
+ * Efficiently extract a subset of the files on a tape.
+ */
+void
+createfiles()
+{
+       register ino_t first, next, last;
+       register struct entry *ep;
+       long curvol;
+
+       vprintf(stdout, "Extract requested files\n");
+       curfile.action = SKIP;
+       getvol((long)1);
+       skipmaps();
+       skipdirs();
+       first = lowerbnd(ROOTINO);
+       last = upperbnd(maxino - 1);
+       for (;;) {
+               first = lowerbnd(first);
+               last = upperbnd(last);
+               /*
+                * Check to see if any files remain to be extracted
+                */
+               if (first > last)
+                       return;
+               /*
+                * Reject any volumes with inodes greater
+                * than the last one needed
+                */
+               while (curfile.ino > last) {
+                       curfile.action = SKIP;
+                       getvol((long)0);
+                       skipmaps();
+                       skipdirs();
+               }
+               /*
+                * Decide on the next inode needed.
+                * Skip across the inodes until it is found
+                * or an out of order volume change is encountered
+                */
+               next = lowerbnd(curfile.ino);
+               do      {
+                       curvol = volno;
+                       while (next > curfile.ino && volno == curvol)
+                               skipfile();
+                       skipmaps();
+                       skipdirs();
+               } while (volno == curvol + 1);
+               /*
+                * If volume change out of order occurred the
+                * current state must be recalculated
+                */
+               if (volno != curvol)
+                       continue;
+               /*
+                * If the current inode is greater than the one we were
+                * looking for then we missed the one we were looking for.
+                * Since we only attempt to extract files listed in the
+                * dump map, the lost files must have been due to a tape
+                * read error, or a file that was removed while the dump
+                * was in progress. Thus we report all requested files
+                * between the one we were looking for, and the one we
+                * found as missing, and delete their request flags.
+                */
+               while (next < curfile.ino) {
+                       ep = lookupino(next);
+                       if (ep == NULL)
+                               panic("corrupted symbol table\n");
+                       fprintf(stderr, "%s: not found on tape\n", myname(ep));
+                       ep->e_flags &= ~NEW;
+                       next = lowerbnd(next);
+               }
+               /*
+                * The current inode is the one that we are looking for,
+                * so extract it per its requested name.
+                */
+               if (next == curfile.ino && next <= last) {
+                       ep = lookupino(next);
+                       if (ep == NULL)
+                               panic("corrupted symbol table\n");
+                       (void) extractfile(myname(ep));
+                       ep->e_flags &= ~NEW;
+                       if (volno != curvol)
+                               skipmaps();
+               }
+       }
+}
+
+/*
+ * Add links.
+ */
+void
+createlinks()
+{
+       register struct entry *np, *ep;
+       register ino_t i;
+       char name[BUFSIZ];
+
+       if (ep = lookupino(WINO)) {
+               vprintf(stdout, "Add whiteouts\n");
+               for ( ; ep != NULL; ep = ep->e_links) {
+                       if ((ep->e_flags & NEW) == 0)
+                               continue;
+#ifdef __linux__
+                       (void)fprintf(stderr, "BUG! Should call addwhiteout\n");
+#else
+                       (void) addwhiteout(myname(ep));
+#endif
+                       ep->e_flags &= ~NEW;
+               }
+       }
+       vprintf(stdout, "Add links\n");
+       for (i = ROOTINO; i < maxino; i++) {
+               ep = lookupino(i);
+               if (ep == NULL)
+                       continue;
+               for (np = ep->e_links; np != NULL; np = np->e_links) {
+                       if ((np->e_flags & NEW) == 0)
+                               continue;
+                       (void) strcpy(name, myname(ep));
+                       if (ep->e_type == NODE) {
+                               (void) linkit(name, myname(np), SYMLINK);
+                       } else {
+                               (void) linkit(name, myname(np), HARDLINK);
+                       }
+                       np->e_flags &= ~NEW;
+               }
+       }
+}
+
+/*
+ * Check the symbol table.
+ * We do this to insure that all the requested work was done, and
+ * that no temporary names remain.
+ */
+void
+checkrestore()
+{
+       register struct entry *ep;
+       register ino_t i;
+
+       vprintf(stdout, "Check the symbol table.\n");
+       for (i = WINO; i < maxino; i++) {
+               for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+                       ep->e_flags &= ~KEEP;
+                       if (ep->e_type == NODE)
+                               ep->e_flags &= ~(NEW|EXISTED);
+                       if (ep->e_flags /* != NULL */)
+                               badentry(ep, "incomplete operations");
+               }
+       }
+}
+
+/*
+ * Compare with the directory structure on the tape
+ * A paranoid check that things are as they should be.
+ */
+long
+verifyfile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       struct entry *np, *ep;
+       long descend = GOOD;
+
+       ep = lookupname(name);
+       if (ep == NULL) {
+               fprintf(stderr, "Warning: missing name %s\n", name);
+               return (FAIL);
+       }
+       np = lookupino(ino);
+       if (np != ep)
+               descend = FAIL;
+       for ( ; np != NULL; np = np->e_links)
+               if (np == ep)
+                       break;
+       if (np == NULL)
+               panic("missing inumber %d\n", ino);
+       if (ep->e_type == LEAF && type != LEAF)
+               badentry(ep, "type should be LEAF");
+       return (descend);
+}
diff --git a/restore/restore.h b/restore/restore.h
new file mode 100644 (file)
index 0000000..1cef2d9
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ *     @(#)restore.h   8.3 (Berkeley) 9/13/94
+ */
+
+/*
+ * Flags
+ */
+extern int     cvtflag;        /* convert from old to new tape format */
+extern int     bflag;          /* set input block size */
+extern int     dflag;          /* print out debugging info */
+extern int     hflag;          /* restore heirarchies */
+extern int     mflag;          /* restore by name instead of inode number */
+extern int     Nflag;          /* do not write the disk */
+extern int     vflag;          /* print out actions taken */
+extern int     yflag;          /* always try to recover from tape errors */
+/*
+ * Global variables
+ */
+extern char    *dumpmap;       /* map of inodes on this dump tape */
+extern char    *usedinomap;    /* map of inodes to be deleted */
+extern ino_t   maxino;         /* highest numbered inode in this file system */
+extern long    dumpnum;        /* location of the dump on this tape */
+extern long    volno;          /* current volume being read */
+extern long    ntrec;          /* number of TP_BSIZE records per tape block */
+extern time_t  dumptime;       /* time that this dump begins */
+extern time_t  dumpdate;       /* time that this dump was made */
+extern char    command;        /* opration being performed */
+extern FILE    *terminal;      /* file descriptor for the terminal input */
+extern int     oldinofmt;      /* reading tape with old format inodes */
+extern int     Bcvt;           /* need byte swapping on inodes and dirs */
+extern int     compare_ignore_not_found;
+                               /* used to compare incremental dumps, */
+                               /* so messages about "not found" files */
+                               /* isn't seen. */
+extern char    *filesys;       /* name of dumped filesystem */
+extern char    *tmpdir;        /* name of temp directory */
+
+/*
+ * Each file in the file system is described by one of these entries
+ */
+struct entry {
+       char    *e_name;                /* the current name of this entry */
+       u_char  e_namlen;               /* length of this name */
+       char    e_type;                 /* type of this entry, see below */
+       short   e_flags;                /* status flags, see below */
+       ino_t   e_ino;                  /* inode number in previous file sys */
+       long    e_index;                /* unique index (for dumpped table) */
+       struct  entry *e_parent;        /* pointer to parent directory (..) */
+       struct  entry *e_sibling;       /* next element in this directory (.) */
+       struct  entry *e_links;         /* hard links to this inode */
+       struct  entry *e_entries;       /* for directories, their entries */
+       struct  entry *e_next;          /* hash chain list */
+};
+/* types */
+#define        LEAF 1                  /* non-directory entry */
+#define NODE 2                 /* directory entry */
+#define LINK 4                 /* synthesized type, stripped by addentry */
+/* flags */
+#define EXTRACT                0x0001  /* entry is to be replaced from the tape */
+#define NEW            0x0002  /* a new entry to be extracted */
+#define KEEP           0x0004  /* entry is not to change */
+#define REMOVED                0x0010  /* entry has been removed */
+#define TMPNAME                0x0020  /* entry has been given a temporary name */
+#define EXISTED                0x0040  /* directory already existed during extract */
+
+/*
+ * Constants associated with entry structs
+ */
+#define HARDLINK       1
+#define SYMLINK                2
+#define TMPHDR         "RSTTMP"
+
+/*
+ * The entry describes the next file available on the tape
+ */
+struct context {
+       char    *name;          /* name of file */
+       ino_t   ino;            /* inumber of file */
+#ifdef __linux__
+       struct  new_bsd_inode *dip;     /* pointer to inode */
+#else
+       struct  dinode *dip;    /* pointer to inode */
+#endif
+       char    action;         /* action being taken on this file */
+} curfile;
+/* actions */
+#define        USING   1       /* extracting from the tape */
+#define        SKIP    2       /* skipping */
+#define UNKNOWN 3      /* disposition or starting point is unknown */
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+typedef struct rstdirdesc RST_DIR;
+
+/*
+ * Flags to setdirmodes.
+ */
+#define FORCE  0x0001
+
+/*
+ * Useful macros
+ */
+#define TSTINO(ino, map) \
+       (map[(u_int)((ino) - 1) / NBBY] &  (1 << ((u_int)((ino) - 1) % NBBY)))
+#define        SETINO(ino, map) \
+       map[(u_int)((ino) - 1) / NBBY] |=  1 << ((u_int)((ino) - 1) % NBBY)
+
+#define dprintf                if (dflag) fprintf
+#define vprintf                if (vflag) fprintf
+
+#define GOOD 1
+#define FAIL 0
diff --git a/restore/symtab.c b/restore/symtab.c
new file mode 100644 (file)
index 0000000..d9d9a2e
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 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
+static char sccsid[] = "@(#)symtab.c   8.3 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+/*
+ * These routines maintain the symbol table which tracks the state
+ * of the file system being restored. They provide lookup by either
+ * name or inode number. They also provide for creation, deletion,
+ * and renaming of entries. Because of the dynamic nature of pathnames,
+ * names should not be saved, but always constructed just before they
+ * are needed, by calling "myname".
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#include <sys/time.h>
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#include <ufs/ufs/dinode.h>
+#endif /* __linux__ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#endif
+
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * The following variables define the inode symbol table.
+ * The primary hash table is dynamically allocated based on
+ * the number of inodes in the file system (maxino), scaled by
+ * HASHFACTOR. The variable "entry" points to the hash table;
+ * the variable "entrytblsize" indicates its size (in entries).
+ */
+#define HASHFACTOR 5
+static struct entry **entry;
+static long entrytblsize;
+
+static void             addino __P((ino_t, struct entry *));
+static struct entry    *lookupparent __P((char *));
+static void             removeentry __P((struct entry *));
+
+/*
+ * Look up an entry by inode number
+ */
+struct entry *
+lookupino(inum)
+       ino_t inum;
+{
+       register struct entry *ep;
+
+       if (inum < WINO || inum >= maxino)
+               return (NULL);
+       for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next)
+               if (ep->e_ino == inum)
+                       return (ep);
+       return (NULL);
+}
+
+/*
+ * Add an entry into the entry table
+ */
+static void
+addino(inum, np)
+       ino_t inum;
+       struct entry *np;
+{
+       struct entry **epp;
+
+       if (inum < WINO || inum >= maxino)
+               panic("addino: out of range %d\n", inum);
+       epp = &entry[inum % entrytblsize];
+       np->e_ino = inum;
+       np->e_next = *epp;
+       *epp = np;
+       if (dflag)
+               for (np = np->e_next; np != NULL; np = np->e_next)
+                       if (np->e_ino == inum)
+                               badentry(np, "duplicate inum");
+}
+
+/*
+ * Delete an entry from the entry table
+ */
+void
+deleteino(inum)
+       ino_t inum;
+{
+       register struct entry *next;
+       struct entry **prev;
+
+       if (inum < WINO || inum >= maxino)
+               panic("deleteino: out of range %d\n", inum);
+       prev = &entry[inum % entrytblsize];
+       for (next = *prev; next != NULL; next = next->e_next) {
+               if (next->e_ino == inum) {
+                       next->e_ino = 0;
+                       *prev = next->e_next;
+                       return;
+               }
+               prev = &next->e_next;
+       }
+       panic("deleteino: %d not found\n", inum);
+}
+
+/*
+ * Look up an entry by name
+ */
+struct entry *
+lookupname(name)
+       char *name;
+{
+       register struct entry *ep;
+       register char *np, *cp;
+       char buf[MAXPATHLEN];
+
+       cp = name;
+       for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) {
+               for (np = buf; *cp != '/' && *cp != '\0'; )
+                       *np++ = *cp++;
+               *np = '\0';
+               for ( ; ep != NULL; ep = ep->e_sibling)
+                       if (strcmp(ep->e_name, buf) == 0)
+                               break;
+               if (ep == NULL)
+                       break;
+               if (*cp++ == '\0')
+                       return (ep);
+       }
+       return (NULL);
+}
+
+/*
+ * Look up the parent of a pathname
+ */
+static struct entry *
+lookupparent(name)
+       char *name;
+{
+       struct entry *ep;
+       char *tailindex;
+
+       tailindex = strrchr(name, '/');
+       if (tailindex == NULL)
+               return (NULL);
+       *tailindex = '\0';
+       ep = lookupname(name);
+       *tailindex = '/';
+       if (ep == NULL)
+               return (NULL);
+       if (ep->e_type != NODE)
+               panic("%s is not a directory\n", name);
+       return (ep);
+}
+
+/*
+ * Determine the current pathname of a node or leaf
+ */
+char *
+myname(ep)
+       register struct entry *ep;
+{
+       register char *cp;
+       static char namebuf[MAXPATHLEN];
+
+       for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) {
+               cp -= ep->e_namlen;
+               memmove(cp, ep->e_name, (long)ep->e_namlen);
+               if (ep == lookupino(ROOTINO))
+                       return (cp);
+               *(--cp) = '/';
+               ep = ep->e_parent;
+       }
+       panic("%s: pathname too long\n", cp);
+       return(cp);
+}
+
+/*
+ * Unused symbol table entries are linked together on a freelist
+ * headed by the following pointer.
+ */
+static struct entry *freelist = NULL;
+
+/*
+ * add an entry to the symbol table
+ */
+struct entry *
+addentry(name, inum, type)
+       char *name;
+       ino_t inum;
+       int type;
+{
+       register struct entry *np, *ep;
+
+       if (freelist != NULL) {
+               np = freelist;
+               freelist = np->e_next;
+               memset(np, 0, (long)sizeof(struct entry));
+       } else {
+               np = (struct entry *)calloc(1, sizeof(struct entry));
+               if (np == NULL)
+                       panic("no memory to extend symbol table\n");
+       }
+       np->e_type = type & ~LINK;
+       ep = lookupparent(name);
+       if (ep == NULL) {
+               if (inum != ROOTINO || lookupino(ROOTINO) != NULL)
+                       panic("bad name to addentry %s\n", name);
+               np->e_name = savename(name);
+               np->e_namlen = strlen(name);
+               np->e_parent = np;
+               addino(ROOTINO, np);
+               return (np);
+       }
+       np->e_name = savename(strrchr(name, '/') + 1);
+       np->e_namlen = strlen(np->e_name);
+       np->e_parent = ep;
+       np->e_sibling = ep->e_entries;
+       ep->e_entries = np;
+       if (type & LINK) {
+               ep = lookupino(inum);
+               if (ep == NULL)
+                       panic("link to non-existant name\n");
+               np->e_ino = inum;
+               np->e_links = ep->e_links;
+               ep->e_links = np;
+       } else if (inum != 0) {
+               if (lookupino(inum) != NULL)
+                       panic("duplicate entry\n");
+               addino(inum, np);
+       }
+       return (np);
+}
+
+/*
+ * delete an entry from the symbol table
+ */
+void
+freeentry(ep)
+       register struct entry *ep;
+{
+       register struct entry *np;
+       ino_t inum;
+
+       if (ep->e_flags != REMOVED)
+               badentry(ep, "not marked REMOVED");
+       if (ep->e_type == NODE) {
+               if (ep->e_links != NULL)
+                       badentry(ep, "freeing referenced directory");
+               if (ep->e_entries != NULL)
+                       badentry(ep, "freeing non-empty directory");
+       }
+       if (ep->e_ino != 0) {
+               np = lookupino(ep->e_ino);
+               if (np == NULL)
+                       badentry(ep, "lookupino failed");
+               if (np == ep) {
+                       inum = ep->e_ino;
+                       deleteino(inum);
+                       if (ep->e_links != NULL)
+                               addino(inum, ep->e_links);
+               } else {
+                       for (; np != NULL; np = np->e_links) {
+                               if (np->e_links == ep) {
+                                       np->e_links = ep->e_links;
+                                       break;
+                               }
+                       }
+                       if (np == NULL)
+                               badentry(ep, "link not found");
+               }
+       }
+       removeentry(ep);
+       freename(ep->e_name);
+       ep->e_next = freelist;
+       freelist = ep;
+}
+
+/*
+ * Relocate an entry in the tree structure
+ */
+void
+moveentry(ep, newname)
+       register struct entry *ep;
+       char *newname;
+{
+       struct entry *np;
+       char *cp;
+
+       np = lookupparent(newname);
+       if (np == NULL)
+               badentry(ep, "cannot move ROOT");
+       if (np != ep->e_parent) {
+               removeentry(ep);
+               ep->e_parent = np;
+               ep->e_sibling = np->e_entries;
+               np->e_entries = ep;
+       }
+       cp = strrchr(newname, '/') + 1;
+       freename(ep->e_name);
+       ep->e_name = savename(cp);
+       ep->e_namlen = strlen(cp);
+       if (strcmp(gentempname(ep), ep->e_name) == 0)
+               ep->e_flags |= TMPNAME;
+       else
+               ep->e_flags &= ~TMPNAME;
+}
+
+/*
+ * Remove an entry in the tree structure
+ */
+static void
+removeentry(ep)
+       register struct entry *ep;
+{
+       register struct entry *np;
+
+       np = ep->e_parent;
+       if (np->e_entries == ep) {
+               np->e_entries = ep->e_sibling;
+       } else {
+               for (np = np->e_entries; np != NULL; np = np->e_sibling) {
+                       if (np->e_sibling == ep) {
+                               np->e_sibling = ep->e_sibling;
+                               break;
+                       }
+               }
+               if (np == NULL)
+                       badentry(ep, "cannot find entry in parent list");
+       }
+}
+
+/*
+ * Table of unused string entries, sorted by length.
+ * 
+ * Entries are allocated in STRTBLINCR sized pieces so that names
+ * of similar lengths can use the same entry. The value of STRTBLINCR
+ * is chosen so that every entry has at least enough space to hold
+ * a "struct strtbl" header. Thus every entry can be linked onto an
+ * apprpriate free list.
+ *
+ * NB. The macro "allocsize" below assumes that "struct strhdr"
+ *     has a size that is a power of two.
+ */
+struct strhdr {
+       struct strhdr *next;
+};
+
+#define STRTBLINCR     (sizeof(struct strhdr))
+#define allocsize(size)        (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
+
+static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR];
+
+/*
+ * Allocate space for a name. It first looks to see if it already
+ * has an appropriate sized entry, and if not allocates a new one.
+ */
+char *
+savename(name)
+       char *name;
+{
+       struct strhdr *np;
+       long len;
+       char *cp;
+
+       if (name == NULL)
+               panic("bad name\n");
+       len = strlen(name);
+       np = strtblhdr[len / STRTBLINCR].next;
+       if (np != NULL) {
+               strtblhdr[len / STRTBLINCR].next = np->next;
+               cp = (char *)np;
+       } else {
+               cp = malloc((unsigned)allocsize(len));
+               if (cp == NULL)
+                       panic("no space for string table\n");
+       }
+       (void) strcpy(cp, name);
+       return (cp);
+}
+
+/*
+ * Free space for a name. The resulting entry is linked onto the
+ * appropriate free list.
+ */
+void
+freename(name)
+       char *name;
+{
+       struct strhdr *tp, *np;
+       
+       tp = &strtblhdr[strlen(name) / STRTBLINCR];
+       np = (struct strhdr *)name;
+       np->next = tp->next;
+       tp->next = np;
+}
+
+/*
+ * Useful quantities placed at the end of a dumped symbol table.
+ */
+struct symtableheader {
+       long    volno;
+       long    stringsize;
+       long    entrytblsize;
+       time_t  dumptime;
+       time_t  dumpdate;
+       ino_t   maxino;
+       long    ntrec;
+};
+
+/*
+ * dump a snapshot of the symbol table
+ */
+void
+dumpsymtable(filename, checkpt)
+       char *filename;
+       long checkpt;
+{
+       register struct entry *ep, *tep;
+       register ino_t i;
+       struct entry temp, *tentry;
+       long mynum = 1, stroff = 0;
+       FILE *fd;
+       struct symtableheader hdr;
+
+       vprintf(stdout, "Check pointing the restore\n");
+       if (Nflag)
+               return;
+       if ((fd = fopen(filename, "w")) == NULL) {
+               fprintf(stderr, "fopen: %s\n", strerror(errno));
+               panic("cannot create save file %s for symbol table\n",
+                       filename);
+       }
+       clearerr(fd);
+       /*
+        * Assign indicies to each entry
+        * Write out the string entries
+        */
+       for (i = WINO; i < maxino; i++) {
+               for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+                       ep->e_index = mynum++;
+                       (void) fwrite(ep->e_name, sizeof(char),
+                              (int)allocsize(ep->e_namlen), fd);
+               }
+       }
+       /*
+        * Convert pointers to indexes, and output
+        */
+       tep = &temp;
+       stroff = 0;
+       for (i = WINO; i < maxino; i++) {
+               for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+                       memmove(tep, ep, (long)sizeof(struct entry));
+                       tep->e_name = (char *)stroff;
+                       stroff += allocsize(ep->e_namlen);
+                       tep->e_parent = (struct entry *)ep->e_parent->e_index;
+                       if (ep->e_links != NULL)
+                               tep->e_links =
+                                       (struct entry *)ep->e_links->e_index;
+                       if (ep->e_sibling != NULL)
+                               tep->e_sibling =
+                                       (struct entry *)ep->e_sibling->e_index;
+                       if (ep->e_entries != NULL)
+                               tep->e_entries =
+                                       (struct entry *)ep->e_entries->e_index;
+                       if (ep->e_next != NULL)
+                               tep->e_next =
+                                       (struct entry *)ep->e_next->e_index;
+                       (void) fwrite((char *)tep, sizeof(struct entry), 1, fd);
+               }
+       }
+       /*
+        * Convert entry pointers to indexes, and output
+        */
+       for (i = 0; i < entrytblsize; i++) {
+               if (entry[i] == NULL)
+                       tentry = NULL;
+               else
+                       tentry = (struct entry *)entry[i]->e_index;
+               (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd);
+       }
+       hdr.volno = checkpt;
+       hdr.maxino = maxino;
+       hdr.entrytblsize = entrytblsize;
+       hdr.stringsize = stroff;
+       hdr.dumptime = dumptime;
+       hdr.dumpdate = dumpdate;
+       hdr.ntrec = ntrec;
+       (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd);
+       if (ferror(fd)) {
+               fprintf(stderr, "fwrite: %s\n", strerror(errno));
+               panic("output error to file %s writing symbol table\n",
+                       filename);
+       }
+       (void) fclose(fd);
+}
+
+/*
+ * Initialize a symbol table from a file
+ */
+void
+initsymtable(filename)
+       char *filename;
+{
+       char *base;
+       long tblsize;
+       register struct entry *ep;
+       struct entry *baseep, *lep;
+       struct symtableheader hdr;
+       struct stat stbuf;
+       register long i;
+       int fd;
+
+       vprintf(stdout, "Initialize symbol table.\n");
+       if (filename == NULL) {
+               entrytblsize = maxino / HASHFACTOR;
+               entry = (struct entry **)
+                       calloc((unsigned)entrytblsize, sizeof(struct entry *));
+               if (entry == (struct entry **)NULL)
+                       panic("no memory for entry table\n");
+               ep = addentry(".", ROOTINO, NODE);
+               ep->e_flags |= NEW;
+               return;
+       }
+       if ((fd = open(filename, O_RDONLY, 0)) < 0) {
+               fprintf(stderr, "open: %s\n", strerror(errno));
+               panic("cannot open symbol table file %s\n", filename);
+       }
+       if (fstat(fd, &stbuf) < 0) {
+               fprintf(stderr, "stat: %s\n", strerror(errno));
+               panic("cannot stat symbol table file %s\n", filename);
+       }
+       tblsize = stbuf.st_size - sizeof(struct symtableheader);
+       base = calloc(sizeof(char), (unsigned)tblsize);
+       if (base == NULL)
+               panic("cannot allocate space for symbol table\n");
+       if (read(fd, base, (int)tblsize) < 0 ||
+           read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) {
+               fprintf(stderr, "read: %s\n", strerror(errno));
+               panic("cannot read symbol table file %s\n", filename);
+       }
+       switch (command) {
+       case 'r':
+               /*
+                * For normal continuation, insure that we are using
+                * the next incremental tape
+                */
+               if (hdr.dumpdate != dumptime) {
+                       if (hdr.dumpdate < dumptime)
+                               fprintf(stderr, "Incremental tape too low\n");
+                       else
+                               fprintf(stderr, "Incremental tape too high\n");
+                       done(1);
+               }
+               break;
+       case 'R':
+               /*
+                * For restart, insure that we are using the same tape
+                */
+               curfile.action = SKIP;
+               dumptime = hdr.dumptime;
+               dumpdate = hdr.dumpdate;
+               if (!bflag)
+                       newtapebuf(hdr.ntrec);
+               getvol(hdr.volno);
+               break;
+       default:
+               panic("initsymtable called from command %c\n", command);
+               break;
+       }
+       maxino = hdr.maxino;
+       entrytblsize = hdr.entrytblsize;
+       entry = (struct entry **)
+               (base + tblsize - (entrytblsize * sizeof(struct entry *)));
+       baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry));
+       lep = (struct entry *)entry;
+       for (i = 0; i < entrytblsize; i++) {
+               if (entry[i] == NULL)
+                       continue;
+               entry[i] = &baseep[(long)entry[i]];
+       }
+       for (ep = &baseep[1]; ep < lep; ep++) {
+               ep->e_name = base + (long)ep->e_name;
+               ep->e_parent = &baseep[(long)ep->e_parent];
+               if (ep->e_sibling != NULL)
+                       ep->e_sibling = &baseep[(long)ep->e_sibling];
+               if (ep->e_links != NULL)
+                       ep->e_links = &baseep[(long)ep->e_links];
+               if (ep->e_entries != NULL)
+                       ep->e_entries = &baseep[(long)ep->e_entries];
+               if (ep->e_next != NULL)
+                       ep->e_next = &baseep[(long)ep->e_next];
+       }
+}
diff --git a/restore/tape.c b/restore/tape.c
new file mode 100644 (file)
index 0000000..b00c4bb
--- /dev/null
@@ -0,0 +1,1689 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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 sccsid[] = "@(#)tape.c     8.9 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#include <sys/time.h>
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#include <ufs/ufs/dinode.h>
+#endif /* __linux__ */
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#endif
+
+#include "restore.h"
+#include "extern.h"
+#include "pathnames.h"
+
+static long    fssize = MAXBSIZE;
+static int     mt = -1;
+static int     pipein = 0;
+static char    magtape[BUFSIZ];
+static int     blkcnt;
+static int     numtrec;
+static char    *tapebuf;
+static union   u_spcl endoftapemark;
+static long    blksread;               /* blocks read since last header */
+static long    tpblksread = 0;         /* TP_BSIZE blocks read */
+static long    tapesread;
+static jmp_buf restart;
+static int     gettingfile = 0;        /* restart has a valid frame */
+static char    *host = NULL;
+
+static int     ofile;
+static char    *map;
+static char    lnkbuf[MAXPATHLEN + 1];
+static int     pathlen;
+
+int            oldinofmt;      /* old inode format conversion required */
+int            Bcvt;           /* Swap Bytes (for CCI or sun) */
+static int     Qcvt;           /* Swap quads (for sun) */
+
+#define        FLUSHTAPEBUF()  blkcnt = ntrec + 1
+
+static void     accthdr __P((struct s_spcl *));
+static int      checksum __P((int *));
+static void     findinode __P((struct s_spcl *));
+static void     findtapeblksize __P((void));
+static int      gethead __P((struct s_spcl *));
+static void     readtape __P((char *));
+static void     setdumpnum __P((void));
+static u_long   swabl __P((u_long));
+static u_char  *swablong __P((u_char *, int));
+static u_char  *swabshort __P((u_char *, int));
+static void     terminateinput __P((void));
+static void     xtrfile __P((char *, long));
+static void     xtrlnkfile __P((char *, long));
+static void     xtrlnkskip __P((char *, long));
+static void     xtrmap __P((char *, long));
+static void     xtrmapskip __P((char *, long));
+static void     xtrskip __P((char *, long));
+
+/*
+ * Set up an input source
+ */
+void
+setinput(source)
+       char *source;
+{
+       FLUSHTAPEBUF();
+       if (bflag)
+               newtapebuf(ntrec);
+       else
+               newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
+       terminal = stdin;
+
+#ifdef RRESTORE
+       if (strchr(source, ':')) {
+               host = source;
+               source = strchr(host, ':');
+               *source++ = '\0';
+               if (rmthost(host) == 0)
+                       done(1);
+       } else
+#endif
+       if (strcmp(source, "-") == 0) {
+               /*
+                * Since input is coming from a pipe we must establish
+                * our own connection to the terminal.
+                */
+               terminal = fopen(_PATH_TTY, "r");
+               if (terminal == NULL) {
+                       (void)fprintf(stderr, "cannot open %s: %s\n",
+                           _PATH_TTY, strerror(errno));
+                       terminal = fopen(_PATH_DEVNULL, "r");
+                       if (terminal == NULL) {
+                               (void)fprintf(stderr, "cannot open %s: %s\n",
+                                   _PATH_DEVNULL, strerror(errno));
+                               done(1);
+                       }
+               }
+               pipein++;
+       }
+       setuid(getuid());       /* no longer need or want root privileges */
+       (void) strcpy(magtape, source);
+}
+
+void
+newtapebuf(size)
+       long size;
+{
+       static tapebufsize = -1;
+
+       ntrec = size;
+       if (size <= tapebufsize)
+               return;
+       if (tapebuf != NULL)
+               free(tapebuf);
+       tapebuf = malloc(size * TP_BSIZE);
+       if (tapebuf == NULL) {
+               fprintf(stderr, "Cannot allocate space for tape buffer\n");
+               done(1);
+       }
+       tapebufsize = size;
+}
+
+/*
+ * Verify that the tape drive can be accessed and
+ * that it actually is a dump tape.
+ */
+void
+setup()
+{
+       int i, j, *ip;
+       struct stat stbuf;
+
+       vprintf(stdout, "Verify tape and initialize maps\n");
+#ifdef RRESTORE
+       if (host)
+               mt = rmtopen(magtape, 0);
+       else
+#endif
+       if (pipein)
+               mt = 0;
+       else
+               mt = open(magtape, O_RDONLY, 0);
+       if (mt < 0) {
+               fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
+               done(1);
+       }
+       volno = 1;
+       setdumpnum();
+       FLUSHTAPEBUF();
+       if (!pipein && !bflag)
+               findtapeblksize();
+       if (gethead(&spcl) == FAIL) {
+               blkcnt--; /* push back this block */
+               blksread--;
+               tpblksread--;
+               cvtflag++;
+               if (gethead(&spcl) == FAIL) {
+                       fprintf(stderr, "Tape is not a dump tape\n");
+                       done(1);
+               }
+               fprintf(stderr, "Converting to new file system format.\n");
+       }
+       if (pipein) {
+               endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
+               endoftapemark.s_spcl.c_type = TS_END;
+               ip = (int *)&endoftapemark;
+               j = sizeof(union u_spcl) / sizeof(int);
+               i = 0;
+               do
+                       i += *ip++;
+               while (--j);
+               endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
+       }
+       if (vflag || command == 't' || command == 'C')
+               printdumpinfo();
+       if (filesys == NULL) {
+               filesys = spcl.c_filesys;
+       }
+       dumptime = spcl.c_ddate;
+       dumpdate = spcl.c_date;
+       if (stat(".", &stbuf) < 0) {
+               fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
+               done(1);
+       }
+       if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE)
+               fssize = stbuf.st_blksize;
+       if (((fssize - 1) & fssize) != 0) {
+               fprintf(stderr, "bad block size %d\n", fssize);
+               done(1);
+       }
+       if (spcl.c_volume != 1) {
+               fprintf(stderr, "Tape is not volume 1 of the dump\n");
+               done(1);
+       }
+       if (gethead(&spcl) == FAIL) {
+               dprintf(stdout, "header read failed at %d blocks\n", blksread);
+               panic("no header after volume mark!\n");
+       }
+       findinode(&spcl);
+       if (spcl.c_type != TS_CLRI) {
+               fprintf(stderr, "Cannot find file removal list\n");
+               done(1);
+       }
+       maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
+       dprintf(stdout, "maxino = %d\n", maxino);
+       map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+       if (map == NULL)
+               panic("no memory for active inode map\n");
+       usedinomap = map;
+       curfile.action = USING;
+       getfile(xtrmap, xtrmapskip);
+       if (spcl.c_type != TS_BITS) {
+               fprintf(stderr, "Cannot find file dump list\n");
+               done(1);
+       }
+       map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+       if (map == (char *)NULL)
+               panic("no memory for file dump list\n");
+       dumpmap = map;
+       curfile.action = USING;
+       getfile(xtrmap, xtrmapskip);
+       /*
+        * If there may be whiteout entries on the tape, pretend that the
+        * whiteout inode exists, so that the whiteout entries can be
+        * extracted.
+        */
+       if (oldinofmt == 0)
+               SETINO(WINO, dumpmap);
+}
+
+/*
+ * Prompt user to load a new dump volume.
+ * "Nextvol" is the next suggested volume to use.
+ * This suggested volume is enforced when doing full
+ * or incremental restores, but can be overrridden by
+ * the user when only extracting a subset of the files.
+ */
+void
+getvol(nextvol)
+       long nextvol;
+{
+       long newvol, savecnt, wantnext, i;
+       union u_spcl tmpspcl;
+#      define tmpbuf tmpspcl.s_spcl
+       char buf[TP_BSIZE];
+
+       if (nextvol == 1) {
+               tapesread = 0;
+               gettingfile = 0;
+       }
+       if (pipein) {
+               if (nextvol != 1)
+                       panic("Changing volumes on pipe input?\n");
+               if (volno == 1)
+                       return;
+               goto gethdr;
+       }
+       savecnt = blksread;
+again:
+       if (pipein)
+               done(1); /* pipes do not get a second chance */
+       if (command == 'R' || command == 'r' || curfile.action != SKIP) {
+               newvol = nextvol;
+               wantnext = 1;
+       } else { 
+               newvol = 0;
+               wantnext = 0;
+       }
+       while (newvol <= 0) {
+               if (tapesread == 0) {
+                       fprintf(stderr, "%s%s%s%s%s",
+                           "You have not read any tapes yet.\n",
+                           "Unless you know which volume your",
+                           " file(s) are on you should start\n",
+                           "with the last volume and work",
+                           " towards the first.\n");
+               } else {
+                       fprintf(stderr, "You have read volumes");
+                       strcpy(buf, ": ");
+                       for (i = 1; i < 32; i++)
+                               if (tapesread & (1 << i)) {
+                                       fprintf(stderr, "%s%d", buf, i);
+                                       strcpy(buf, ", ");
+                               }
+                       fprintf(stderr, "\n");
+               }
+               do      {
+                       fprintf(stderr, "Specify next volume #: ");
+                       (void) fflush(stderr);
+                       (void) fgets(buf, BUFSIZ, terminal);
+               } while (!feof(terminal) && buf[0] == '\n');
+               if (feof(terminal))
+                       done(1);
+               newvol = atoi(buf);
+               if (newvol <= 0) {
+                       fprintf(stderr,
+                           "Volume numbers are positive numerics\n");
+               }
+       }
+       if (newvol == volno) {
+               tapesread |= 1 << volno;
+               return;
+       }
+       closemt();
+       fprintf(stderr, "Mount tape volume %d\n", newvol);
+       fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
+       fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
+       (void) fflush(stderr);
+       (void) fgets(buf, BUFSIZ, terminal);
+       if (feof(terminal))
+               done(1);
+       if (!strcmp(buf, "none\n")) {
+               terminateinput();
+               return;
+       }
+       if (buf[0] != '\n') {
+               (void) strcpy(magtape, buf);
+               magtape[strlen(magtape) - 1] = '\0';
+       }
+#ifdef RRESTORE
+       if (host)
+               mt = rmtopen(magtape, 0);
+       else
+#endif
+               mt = open(magtape, O_RDONLY, 0);
+
+       if (mt == -1) {
+               fprintf(stderr, "Cannot open %s\n", magtape);
+               volno = -1;
+               goto again;
+       }
+gethdr:
+       volno = newvol;
+       setdumpnum();
+       FLUSHTAPEBUF();
+       if (gethead(&tmpbuf) == FAIL) {
+               dprintf(stdout, "header read failed at %d blocks\n", blksread);
+               fprintf(stderr, "tape is not dump tape\n");
+               volno = 0;
+               goto again;
+       }
+       if (tmpbuf.c_volume != volno) {
+               fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
+               volno = 0;
+               goto again;
+       }
+       if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
+#ifdef __linux__
+               fprintf(stderr, "Wrong dump date\n\tgot: %s",
+                       ctime4(&tmpbuf.c_date));
+               fprintf(stderr, "\twanted: %s", ctime4(&dumpdate));
+#else
+               fprintf(stderr, "Wrong dump date\n\tgot: %s",
+                       ctime(&tmpbuf.c_date));
+               fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
+#endif
+               volno = 0;
+               goto again;
+       }
+       tapesread |= 1 << volno;
+       blksread = savecnt;
+       /*
+        * If continuing from the previous volume, skip over any
+        * blocks read already at the end of the previous volume.
+        *
+        * If coming to this volume at random, skip to the beginning
+        * of the next record.
+        */
+       dprintf(stdout, "read %ld recs, tape starts with %ld\n", 
+               tpblksread, tmpbuf.c_firstrec);
+       if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
+               if (!wantnext) {
+                       tpblksread = tmpbuf.c_firstrec;
+                       for (i = tmpbuf.c_count; i > 0; i--)
+                               readtape(buf);
+               } else if (tmpbuf.c_firstrec > 0 &&
+                          tmpbuf.c_firstrec < tpblksread - 1) {
+                       /*
+                        * -1 since we've read the volume header
+                        */
+                       i = tpblksread - tmpbuf.c_firstrec - 1;
+                       dprintf(stderr, "Skipping %d duplicate record%s.\n",
+                               i, i > 1 ? "s" : "");
+                       while (--i >= 0)
+                               readtape(buf);
+               }
+       }
+       if (curfile.action == USING) {
+               if (volno == 1)
+                       panic("active file into volume 1\n");
+               return;
+       }
+       /*
+        * Skip up to the beginning of the next record
+        */
+       if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
+               for (i = tmpbuf.c_count; i > 0; i--)
+                       readtape(buf);
+       (void) gethead(&spcl);
+       findinode(&spcl);
+       if (gettingfile) {
+               gettingfile = 0;
+               longjmp(restart, 1);
+       }
+}
+
+/*
+ * Handle unexpected EOF.
+ */
+static void
+terminateinput()
+{
+
+       if (gettingfile && curfile.action == USING) {
+               printf("Warning: %s %s\n",
+                   "End-of-input encountered while extracting", curfile.name);
+       }
+       curfile.name = "<name unknown>";
+       curfile.action = UNKNOWN;
+       curfile.dip = NULL;
+       curfile.ino = maxino;
+       if (gettingfile) {
+               gettingfile = 0;
+               longjmp(restart, 1);
+       }
+}
+
+/*
+ * handle multiple dumps per tape by skipping forward to the
+ * appropriate one.
+ */
+static void
+setdumpnum()
+{
+       struct mtop tcom;
+
+       if (dumpnum == 1 || volno != 1)
+               return;
+       if (pipein) {
+               fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
+               done(1);
+       }
+       tcom.mt_op = MTFSF;
+       tcom.mt_count = dumpnum - 1;
+#ifdef RRESTORE
+       if (host)
+               rmtioctl(MTFSF, dumpnum - 1);
+       else 
+#endif
+               if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
+                       fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
+}
+
+void
+printdumpinfo()
+{
+#ifdef __linux__
+       fprintf(stdout, "Dump   date: %s", ctime4(&spcl.c_date));
+       fprintf(stdout, "Dumped from: %s",
+           (spcl.c_ddate == 0) ? "the epoch\n" : ctime4(&spcl.c_ddate));
+#else
+       fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
+       fprintf(stdout, "Dumped from: %s",
+           (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
+#endif
+       if (spcl.c_host[0] == '\0')
+               return;
+       fprintf(stderr, "Level %d dump of %s on %s:%s\n",
+               spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
+       fprintf(stderr, "Label: %s\n", spcl.c_label);
+}
+
+int
+extractfile(name)
+       char *name;
+{
+       int flags;
+       mode_t mode;
+       struct timeval timep[2];
+       struct entry *ep;
+#ifdef __linux__
+       int err;
+       uid_t uid;
+       gid_t gid;
+#endif
+
+       curfile.name = name;
+       curfile.action = USING;
+#ifdef __linux__
+       timep[0].tv_sec = curfile.dip->di_atime.tv_sec;
+       timep[0].tv_usec = curfile.dip->di_atime.tv_usec;
+       timep[1].tv_sec = curfile.dip->di_mtime.tv_sec;
+       timep[1].tv_usec = curfile.dip->di_mtime.tv_usec;
+#else  /* __linux__ */
+       timep[0].tv_sec = curfile.dip->di_atime;
+       timep[0].tv_usec = curfile.dip->di_atimensec / 1000;
+       timep[1].tv_sec = curfile.dip->di_mtime;
+       timep[1].tv_usec = curfile.dip->di_mtimensec / 1000;
+#endif /* __linux__ */
+       mode = curfile.dip->di_mode;
+       flags = curfile.dip->di_flags;
+       switch (mode & IFMT) {
+
+       default:
+               fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
+               skipfile();
+               return (FAIL);
+
+       case IFSOCK:
+               vprintf(stdout, "skipped socket %s\n", name);
+               skipfile();
+               return (GOOD);
+
+       case IFDIR:
+               if (mflag) {
+                       ep = lookupname(name);
+                       if (ep == NULL || ep->e_flags & EXTRACT)
+                               panic("unextracted directory %s\n", name);
+                       skipfile();
+                       return (GOOD);
+               }
+               vprintf(stdout, "extract file %s\n", name);
+               return (genliteraldir(name, curfile.ino));
+
+       case IFLNK:
+               lnkbuf[0] = '\0';
+               pathlen = 0;
+#ifdef __linux__
+               uid = curfile.dip->di_uid;
+               gid = curfile.dip->di_gid;
+#endif
+               getfile(xtrlnkfile, xtrlnkskip);
+               if (pathlen == 0) {
+                       vprintf(stdout,
+                           "%s: zero length symbolic link (ignored)\n", name);
+                       return (GOOD);
+               }
+#ifdef __linux__
+               err = linkit(lnkbuf, name, SYMLINK);
+               if (err == GOOD)
+                       (void) chown(name, uid, gid);
+               return (err);
+#else
+               return (linkit(lnkbuf, name, SYMLINK));
+#endif
+
+       case IFCHR:
+       case IFBLK:
+               vprintf(stdout, "extract special file %s\n", name);
+               if (Nflag) {
+                       skipfile();
+                       return (GOOD);
+               }
+               if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
+                       fprintf(stderr, "%s: cannot create special file: %s\n",
+                           name, strerror(errno));
+                       skipfile();
+                       return (FAIL);
+               }
+               (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
+               (void) chmod(name, mode);
+#ifdef __linux__
+               (void) fsetflags(name, flags);
+#else
+               (void) chflags(name, flags);
+#endif
+               skipfile();
+               utimes(name, timep);
+               return (GOOD);
+
+       case IFIFO:
+               vprintf(stdout, "extract fifo %s\n", name);
+               if (Nflag) {
+                       skipfile();
+                       return (GOOD);
+               }
+               if (mkfifo(name, mode) < 0) {
+                       fprintf(stderr, "%s: cannot create fifo: %s\n",
+                           name, strerror(errno));
+                       skipfile();
+                       return (FAIL);
+               }
+               (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
+               (void) chmod(name, mode);
+#ifdef __linux__
+               (void) fsetflags(name, flags);
+#else
+               (void) chflags(name, flags);
+#endif
+               skipfile();
+               utimes(name, timep);
+               return (GOOD);
+
+       case IFREG:
+               vprintf(stdout, "extract file %s\n", name);
+               if (Nflag) {
+                       skipfile();
+                       return (GOOD);
+               }
+               if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
+                   0666)) < 0) {
+                       fprintf(stderr, "%s: cannot create file: %s\n",
+                           name, strerror(errno));
+                       skipfile();
+                       return (FAIL);
+               }
+               (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
+               (void) fchmod(ofile, mode);
+#ifdef __linux__
+               (void) fsetflags(ofile, flags);
+#else
+               (void) fchflags(ofile, flags);
+#endif
+               getfile(xtrfile, xtrskip);
+               (void) close(ofile);
+               utimes(name, timep);
+               return (GOOD);
+       }
+       /* NOTREACHED */
+}
+
+/*
+ * skip over bit maps on the tape
+ */
+void
+skipmaps()
+{
+
+       while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
+               skipfile();
+}
+
+/*
+ * skip over a file on the tape
+ */
+void
+skipfile()
+{
+
+       curfile.action = SKIP;
+       getfile(xtrnull, xtrnull);
+}
+
+/*
+ * Extract a file from the tape.
+ * When an allocated block is found it is passed to the fill function;
+ * when an unallocated block (hole) is found, a zeroed buffer is passed
+ * to the skip function.
+ */
+void
+getfile(fill, skip)
+       void    (*fill) __P((char *, long));
+       void    (*skip) __P((char *, long));
+{
+       register int i;
+       int curblk = 0;
+       quad_t size = spcl.c_dinode.di_size;
+       int last_write_was_hole = 0;
+       long origsize = size;
+       static char clearedbuf[MAXBSIZE];
+       char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
+       char junk[TP_BSIZE];
+
+       if (spcl.c_type == TS_END)
+               panic("ran off end of tape\n");
+       if (spcl.c_magic != NFS_MAGIC)
+               panic("not at beginning of a file\n");
+       if (!gettingfile && setjmp(restart) != 0)
+               return;
+       gettingfile++;
+loop:
+       for (i = 0; i < spcl.c_count; i++) {
+               if (spcl.c_addr[i]) {
+                       readtape(&buf[curblk++][0]);
+                       if (curblk == fssize / TP_BSIZE) {
+                               (*fill)((char *)buf, (long)(size > TP_BSIZE ?
+                                    fssize : (curblk - 1) * TP_BSIZE + size));
+                               curblk = 0;
+                               last_write_was_hole = 0;
+                       }
+               } else {
+                       if (curblk > 0) {
+                               (*fill)((char *)buf, (long)(size > TP_BSIZE ?
+                                    curblk * TP_BSIZE :
+                                    (curblk - 1) * TP_BSIZE + size));
+                               curblk = 0;
+                       }
+                       (*skip)(clearedbuf, (long)(size > TP_BSIZE ?
+                               TP_BSIZE : size));
+                       last_write_was_hole = 1;
+               }
+               if ((size -= TP_BSIZE) <= 0) {
+                       for (i++; i < spcl.c_count; i++)
+                               if (spcl.c_addr[i])
+                                       readtape(junk);
+                       break;
+               }
+       }
+       if (gethead(&spcl) == GOOD && size > 0) {
+               if (spcl.c_type == TS_ADDR)
+                       goto loop;
+               dprintf(stdout,
+                       "Missing address (header) block for %s at %d blocks\n",
+                       curfile.name, blksread);
+       }
+       if (curblk > 0) {
+               (*fill)((char *)buf, (curblk * TP_BSIZE) + size);
+               last_write_was_hole = 0;
+       }
+       if (last_write_was_hole) {
+               ftruncate(ofile, origsize);
+       }
+       findinode(&spcl);
+       gettingfile = 0;
+}
+
+/*
+ * Write out the next block of a file.
+ */
+static void
+xtrfile(buf, size)
+       char    *buf;
+       long    size;
+{
+
+       if (Nflag)
+               return;
+       if (write(ofile, buf, (int) size) == -1) {
+               fprintf(stderr,
+                   "write error extracting inode %d, name %s\nwrite: %s\n",
+                       curfile.ino, curfile.name, strerror(errno));
+               done(1);
+       }
+}
+
+/*
+ * Skip over a hole in a file.
+ */
+/* ARGSUSED */
+static void
+xtrskip(buf, size)
+       char *buf;
+       long size;
+{
+
+       if (lseek(ofile, size, SEEK_CUR) == -1) {
+               fprintf(stderr,
+                   "seek error extracting inode %d, name %s\nlseek: %s\n",
+                       curfile.ino, curfile.name, strerror(errno));
+               done(1);
+       }
+}
+
+/*
+ * Collect the next block of a symbolic link.
+ */
+static void
+xtrlnkfile(buf, size)
+       char    *buf;
+       long    size;
+{
+
+       pathlen += size;
+       if (pathlen > MAXPATHLEN) {
+               fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
+                   curfile.name, lnkbuf, buf, pathlen);
+               done(1);
+       }
+       (void) strcat(lnkbuf, buf);
+}
+
+/*
+ * Skip over a hole in a symbolic link (should never happen).
+ */
+/* ARGSUSED */
+static void
+xtrlnkskip(buf, size)
+       char *buf;
+       long size;
+{
+
+       fprintf(stderr, "unallocated block in symbolic link %s\n",
+               curfile.name);
+       done(1);
+}
+
+/*
+ * Collect the next block of a bit map.
+ */
+static void
+xtrmap(buf, size)
+       char    *buf;
+       long    size;
+{
+
+       memmove(map, buf, size);
+       map += size;
+}
+
+/*
+ * Skip over a hole in a bit map (should never happen).
+ */
+/* ARGSUSED */
+static void
+xtrmapskip(buf, size)
+       char *buf;
+       long size;
+{
+
+       panic("hole in map\n");
+       map += size;
+}
+
+/*
+ * Noop, when an extraction function is not needed.
+ */
+/* ARGSUSED */
+void
+xtrnull(buf, size)
+       char *buf;
+       long size;
+{
+
+       return;
+}
+
+int
+do_cmpfiles(int fd_tape, int fd_disk, long size)
+{
+#ifndef BUFSIZE
+#define BUFSIZE 1024
+#endif
+       static char buf_tape[BUFSIZ];
+       static char buf_disk[BUFSIZ];
+       int left = size;
+       int n_tape;
+       int n_disk;
+
+       while (size > 0) {
+               if ((n_tape = read(fd_tape, buf_tape, BUFSIZE)) < 1) {
+                       close(fd_tape), close(fd_disk);
+                       panic("do_cmpfiles: unexpected EOF[1]");
+               }
+               if ((n_disk = read(fd_disk, buf_disk, BUFSIZE)) < 1) {
+                       close(fd_tape), close(fd_disk);
+                       panic("do_cmpfiles: unexpected EOF[2]");
+               }
+               if (n_tape != n_disk) {
+                       close(fd_tape), close(fd_disk);
+                       panic("do_cmpfiles: sizes different!");
+               }
+               if (memcmp(buf_tape, buf_disk, n_tape) != 0) return (1);
+               size -= n_tape;
+       }
+       return (0);
+}
+
+/* for debugging compare problems */
+#undef COMPARE_FAIL_KEEP_FILE
+
+#ifdef COMPARE_FAIL_KEEP_FILE
+/* return true if tapefile should be unlinked after compare */
+int
+#else
+void
+#endif
+cmpfiles(char *tapefile, char *diskfile, struct stat *sbuf_disk)
+{
+       struct stat sbuf_tape;
+       int fd_tape, fd_disk;
+
+       if (stat(tapefile, &sbuf_tape) != 0) {
+               panic("Can't lstat tmp file %s: %s\n", tapefile,
+                     strerror(errno));
+       }
+
+       if (sbuf_disk->st_size != sbuf_tape.st_size) {
+               fprintf(stderr,
+                       "%s: size changed from %d to %d.\n",
+                       diskfile, sbuf_tape.st_size, sbuf_disk->st_size);
+#ifdef COMPARE_FAIL_KEEP_FILE
+               return (0);
+#else
+               return;
+#endif
+       }
+
+       if ((fd_tape = open(tapefile, O_RDONLY)) < 0) {
+               panic("Can't open %s: %s\n", tapefile, strerror(errno));
+       }
+       if ((fd_disk = open(diskfile, O_RDONLY)) < 0) {
+               close(fd_tape);
+               panic("Can't open %s: %s\n", diskfile, strerror(errno));
+       }
+
+       if (do_cmpfiles(fd_tape, fd_disk, sbuf_tape.st_size)) {
+               fprintf(stderr, "%s: tape and disk copies are different\n",
+                       diskfile);
+               close(fd_tape);
+               close(fd_disk);
+#ifdef COMPARE_FAIL_KEEP_FILE
+               /* rename the file to live in /tmp */
+               /* rename `tapefile' to /tmp/<basename of diskfile> */
+               {
+                       char *p = strrchr(diskfile, '/');
+                       char newname[MAXPATHLEN];
+                       if (!p) {
+                               panic("can't find / in %s\n", diskfile);
+                       }
+                       sprintf(newname, "%s/debug/%s", tmpdir, p + 1);
+                       if (rename(tapefile, newname)) {
+                               panic("rename from %s to %s failed: %s\n",
+                                     tapefile, newname,
+                                     strerror(errno));
+                       } else {
+                               fprintf(stderr, "*** %s saved to %s\n",
+                                       tapefile, newname);
+                       }
+               }
+               
+               /* don't unlink the file (it's not there anymore */
+               /* anyway) */
+               return (0);
+#else
+               return;
+#endif
+       }
+       close(fd_tape);
+       close(fd_disk);
+#ifdef COMPARE_FAIL_KEEP_FILE
+       return (1);
+#endif
+}
+
+static char tmpfilename[128];
+
+void
+comparefile(name)
+       char *name;
+{
+       static char *tmpfile = NULL;
+       int mode;
+       struct stat sb, stemp;
+       int r;
+
+       if ((r = lstat(name, &sb)) != 0) {
+               fprintf(stderr, "%s: does not exist (%d, %d).\n", name, r, errno);
+               skipfile();
+               return;
+       }
+
+       curfile.name = name;
+       mode = curfile.dip->di_mode;
+
+       vprintf(stdout, "comparing %s (size: %d, mode: 0%o)\n", name,
+               sb.st_size, mode);
+
+       if (sb.st_mode != mode) {
+               fprintf(stderr, "%s: mode changed from 0%o to 0%o.\n",
+                       name, mode & 07777, sb.st_mode & 07777);
+       }
+       switch (mode & IFMT) {
+       default:
+               skipfile();
+               return;
+
+       case IFSOCK:
+               skipfile();
+               return;
+
+       case IFDIR:
+               skipfile();
+               return;
+
+       case IFLNK: {
+               char lbuf[MAXPATHLEN + 1];
+               int lsize;
+
+               if (!(sb.st_mode & S_IFLNK)) {
+                       fprintf(stderr, "%s: is no longer a symbolic link\n",
+                               name);
+                       return;
+               }
+               lnkbuf[0] = '\0';
+               pathlen = 0;
+               getfile(xtrlnkfile, xtrlnkskip);
+               if (pathlen == 0) {
+                       fprintf(stderr,
+                               "%s: zero length symbolic link (ignored)\n",
+                               name);
+                       return;
+               }
+               if ((lsize = readlink(name, lbuf, MAXPATHLEN)) < 0) {
+                       panic("readlink of %s failed: %s", name,
+                             strerror(errno));
+               }
+               lbuf[lsize] = 0;
+               if (strcmp(lbuf, lnkbuf) != 0) {
+                       fprintf(stderr,
+                               "%s: symbolic link changed from %s to %s.\n",
+                               name, lnkbuf, lbuf);
+                       return;
+               }
+               return;
+       }
+
+       case IFCHR:
+       case IFBLK:
+               if (!(sb.st_mode & (S_IFCHR|S_IFBLK))) {
+                       fprintf(stderr, "%s: no longer a special file\n",
+                               name);
+                       skipfile();
+                       return;
+               }
+
+               if (sb.st_rdev != (int)curfile.dip->di_rdev) {
+                       fprintf(stderr,
+                               "%s: device changed from %d,%d to %d,%d.\n",
+                               name,
+                               ((int)curfile.dip->di_rdev >> 8) & 0xf,
+                               (int)curfile.dip->di_rdev & 0xf,
+                               (sb.st_rdev >> 8) & 0xf,
+                               sb.st_rdev & 0xf);
+               }
+               skipfile();
+               return;
+
+       case IFREG:
+               if (tmpfile == NULL) {
+                       /* argument to mktemp() must not be in RO space: */
+                       sprintf(tmpfilename, "%s/restoreCXXXXXX", tmpdir);
+                       tmpfile = mktemp(&tmpfilename[0]);
+               }
+               if ((stat(tmpfile, &stemp) == 0) && (unlink(tmpfile) != 0)) {
+                       panic("cannot delete tmp file %s: %s\n",
+                             tmpfile, strerror(errno));
+               }
+               if ((ofile = creat(tmpfile, 0600)) < 0) {
+                       panic("cannot create file temp file %s: %s\n",
+                             name, strerror(errno));
+               }
+               getfile(xtrfile, xtrskip);
+               (void) close(ofile);
+#ifdef COMPARE_FAIL_KEEP_FILE
+               if (cmpfiles(tmpfile, name, &sb))
+                       unlink(tmpfile);
+#else
+               cmpfiles(tmpfile, name, &sb);
+               unlink(tmpfile);
+#endif
+               return;
+       }
+       /* NOTREACHED */
+}
+
+/*
+ * Read TP_BSIZE blocks from the input.
+ * Handle read errors, and end of media.
+ */
+static void
+readtape(buf)
+       char *buf;
+{
+       long rd, newvol, i;
+       int cnt, seek_failed;
+
+       if (blkcnt < numtrec) {
+               memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
+               blksread++;
+               tpblksread++;
+               return;
+       }
+       for (i = 0; i < ntrec; i++)
+               ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+       if (numtrec == 0)
+               numtrec = ntrec;
+       cnt = ntrec * TP_BSIZE;
+       rd = 0;
+getmore:
+#ifdef RRESTORE
+       if (host)
+               i = rmtread(&tapebuf[rd], cnt);
+       else
+#endif
+               i = read(mt, &tapebuf[rd], cnt);
+       /*
+        * Check for mid-tape short read error.
+        * If found, skip rest of buffer and start with the next.
+        */
+       if (!pipein && numtrec < ntrec && i > 0) {
+               dprintf(stdout, "mid-media short read error.\n");
+               numtrec = ntrec;
+       }
+       /*
+        * Handle partial block read.
+        */
+       if (pipein && i == 0 && rd > 0)
+               i = rd;
+       else if (i > 0 && i != ntrec * TP_BSIZE) {
+               if (pipein) {
+                       rd += i;
+                       cnt -= i;
+                       if (cnt > 0)
+                               goto getmore;
+                       i = rd;
+               } else {
+                       /*
+                        * Short read. Process the blocks read.
+                        */
+                       if (i % TP_BSIZE != 0)
+                               vprintf(stdout,
+                                   "partial block read: %d should be %d\n",
+                                   i, ntrec * TP_BSIZE);
+                       numtrec = i / TP_BSIZE;
+               }
+       }
+       /*
+        * Handle read error.
+        */
+       if (i < 0) {
+               fprintf(stderr, "Tape read error while ");
+               switch (curfile.action) {
+               default:
+                       fprintf(stderr, "trying to set up tape\n");
+                       break;
+               case UNKNOWN:
+                       fprintf(stderr, "trying to resynchronize\n");
+                       break;
+               case USING:
+                       fprintf(stderr, "restoring %s\n", curfile.name);
+                       break;
+               case SKIP:
+                       fprintf(stderr, "skipping over inode %d\n",
+                               curfile.ino);
+                       break;
+               }
+               if (!yflag && !reply("continue"))
+                       done(1);
+               i = ntrec * TP_BSIZE;
+               memset(tapebuf, 0, i);
+#ifdef RRESTORE
+               if (host)
+                       seek_failed = (rmtseek(i, 1) < 0);
+               else
+#endif
+                       seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
+
+               if (seek_failed) {
+                       fprintf(stderr,
+                           "continuation failed: %s\n", strerror(errno));
+                       done(1);
+               }
+       }
+       /*
+        * Handle end of tape.
+        */
+       if (i == 0) {
+               vprintf(stdout, "End-of-tape encountered\n");
+               if (!pipein) {
+                       newvol = volno + 1;
+                       volno = 0;
+                       numtrec = 0;
+                       getvol(newvol);
+                       readtape(buf);
+                       return;
+               }
+               if (rd % TP_BSIZE != 0)
+                       panic("partial block read: %d should be %d\n",
+                               rd, ntrec * TP_BSIZE);
+               terminateinput();
+               memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
+       }
+       blkcnt = 0;
+       memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
+       blksread++;
+       tpblksread++;
+}
+
+static void
+findtapeblksize()
+{
+       register long i;
+
+       for (i = 0; i < ntrec; i++)
+               ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+       blkcnt = 0;
+#ifdef RRESTORE
+       if (host)
+               i = rmtread(tapebuf, ntrec * TP_BSIZE);
+       else
+#endif
+               i = read(mt, tapebuf, ntrec * TP_BSIZE);
+
+       if (i <= 0) {
+               fprintf(stderr, "tape read error: %s\n", strerror(errno));
+               done(1);
+       }
+       if (i % TP_BSIZE != 0) {
+               fprintf(stderr, "Tape block size (%d) %s (%d)\n",
+                       i, "is not a multiple of dump block size", TP_BSIZE);
+               done(1);
+       }
+       ntrec = i / TP_BSIZE;
+       numtrec = ntrec;
+       vprintf(stdout, "Tape block size is %d\n", ntrec);
+}
+
+void
+closemt()
+{
+
+       if (mt < 0)
+               return;
+#ifdef RRESTORE
+       if (host)
+               rmtclose();
+       else
+#endif
+               (void) close(mt);
+}
+
+/*
+ * Read the next block from the tape.
+ * Check to see if it is one of several vintage headers.
+ * If it is an old style header, convert it to a new style header.
+ * If it is not any valid header, return an error.
+ */
+static int
+gethead(buf)
+       struct s_spcl *buf;
+{
+       long i;
+       union {
+               quad_t  qval;
+               long    val[2];
+       } qcvt;
+       union u_ospcl {
+               char dummy[TP_BSIZE];
+               struct  s_ospcl {
+                       long    c_type;
+                       long    c_date;
+                       long    c_ddate;
+                       long    c_volume;
+                       long    c_tapea;
+                       u_short c_inumber;
+                       long    c_magic;
+                       long    c_checksum;
+                       struct odinode {
+                               unsigned short odi_mode;
+                               u_short odi_nlink;
+                               u_short odi_uid;
+                               u_short odi_gid;
+                               long    odi_size;
+                               long    odi_rdev;
+                               char    odi_addr[36];
+                               long    odi_atime;
+                               long    odi_mtime;
+                               long    odi_ctime;
+                       } c_dinode;
+                       long    c_count;
+                       char    c_addr[256];
+               } s_ospcl;
+       } u_ospcl;
+
+       if (!cvtflag) {
+               readtape((char *)buf);
+               if (buf->c_magic != NFS_MAGIC) {
+                       if (swabl(buf->c_magic) != NFS_MAGIC)
+                               return (FAIL);
+                       if (!Bcvt) {
+                               vprintf(stdout, "Note: Doing Byte swapping\n");
+                               Bcvt = 1;
+                       }
+               }
+               if (checksum((int *)buf) == FAIL)
+                       return (FAIL);
+               if (Bcvt)
+                       swabst((u_char *)"8l4s31l", (u_char *)buf);
+               goto good;
+       }
+       readtape((char *)(&u_ospcl.s_ospcl));
+       memset((char *)buf, 0, (long)TP_BSIZE);
+       buf->c_type = u_ospcl.s_ospcl.c_type;
+       buf->c_date = u_ospcl.s_ospcl.c_date;
+       buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
+       buf->c_volume = u_ospcl.s_ospcl.c_volume;
+       buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
+       buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
+       buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
+       buf->c_magic = u_ospcl.s_ospcl.c_magic;
+       buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
+       buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
+       buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
+       buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
+       buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
+       buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
+#ifdef __linux__
+       buf->c_dinode.di_atime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_atime;
+       buf->c_dinode.di_mtime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime;
+       buf->c_dinode.di_ctime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_ctime;
+#else  /* __linux__ */
+       buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
+       buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
+       buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
+#endif /* __linux__ */
+       buf->c_count = u_ospcl.s_ospcl.c_count;
+       memmove(buf->c_addr, u_ospcl.s_ospcl.c_addr, (long)256);
+       if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
+           checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
+               return(FAIL);
+       buf->c_magic = NFS_MAGIC;
+
+good:
+       if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
+           (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
+               qcvt.qval = buf->c_dinode.di_size;
+               if (qcvt.val[0] || qcvt.val[1]) {
+                       printf("Note: Doing Quad swapping\n");
+                       Qcvt = 1;
+               }
+       }
+       if (Qcvt) {
+               qcvt.qval = buf->c_dinode.di_size;
+               i = qcvt.val[1];
+               qcvt.val[1] = qcvt.val[0];
+               qcvt.val[0] = i;
+               buf->c_dinode.di_size = qcvt.qval;
+       }
+
+       switch (buf->c_type) {
+
+       case TS_CLRI:
+       case TS_BITS:
+               /*
+                * Have to patch up missing information in bit map headers
+                */
+               buf->c_inumber = 0;
+               buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
+               for (i = 0; i < buf->c_count; i++)
+                       buf->c_addr[i]++;
+               break;
+
+       case TS_TAPE:
+               if ((buf->c_flags & DR_NEWINODEFMT) == 0)
+                       oldinofmt = 1;
+               /* fall through */
+       case TS_END:
+               buf->c_inumber = 0;
+               break;
+
+       case TS_INODE:
+       case TS_ADDR:
+               break;
+
+       default:
+               panic("gethead: unknown inode type %d\n", buf->c_type);
+               break;
+       }
+       /*
+        * If we are restoring a filesystem with old format inodes, 
+        * copy the uid/gid to the new location.
+        */
+       if (oldinofmt) {
+               buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
+               buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
+       }
+       if (dflag)
+               accthdr(buf);
+       return(GOOD);
+}
+
+/*
+ * Check that a header is where it belongs and predict the next header
+ */
+static void
+accthdr(header)
+       struct s_spcl *header;
+{
+       static ino_t previno = 0x7fffffff;
+       static int prevtype;
+       static long predict;
+       long blks, i;
+
+       if (header->c_type == TS_TAPE) {
+               fprintf(stderr, "Volume header (%s inode format) ",
+                   oldinofmt ? "old" : "new");
+               if (header->c_firstrec)
+                       fprintf(stderr, "begins with record %d",
+                               header->c_firstrec);
+               fprintf(stderr, "\n");
+               previno = 0x7fffffff;
+               return;
+       }
+       if (previno == 0x7fffffff)
+               goto newcalc;
+       switch (prevtype) {
+       case TS_BITS:
+               fprintf(stderr, "Dumped inodes map header");
+               break;
+       case TS_CLRI:
+               fprintf(stderr, "Used inodes map header");
+               break;
+       case TS_INODE:
+               fprintf(stderr, "File header, ino %d", previno);
+               break;
+       case TS_ADDR:
+               fprintf(stderr, "File continuation header, ino %d", previno);
+               break;
+       case TS_END:
+               fprintf(stderr, "End of tape header");
+               break;
+       }
+       if (predict != blksread - 1)
+               fprintf(stderr, "; predicted %d blocks, got %d blocks",
+                       predict, blksread - 1);
+       fprintf(stderr, "\n");
+newcalc:
+       blks = 0;
+       if (header->c_type != TS_END)
+               for (i = 0; i < header->c_count; i++)
+                       if (header->c_addr[i] != 0)
+                               blks++;
+       predict = blks;
+       blksread = 0;
+       prevtype = header->c_type;
+       previno = header->c_inumber;
+}
+
+/*
+ * Find an inode header.
+ * Complain if had to skip, and complain is set.
+ */
+static void
+findinode(header)
+       struct s_spcl *header;
+{
+       static long skipcnt = 0;
+       long i;
+       char buf[TP_BSIZE];
+
+       curfile.name = "<name unknown>";
+       curfile.action = UNKNOWN;
+       curfile.dip = NULL;
+       curfile.ino = 0;
+       do {
+               if (header->c_magic != NFS_MAGIC) {
+                       skipcnt++;
+                       while (gethead(header) == FAIL ||
+                           header->c_date != dumpdate)
+                               skipcnt++;
+               }
+               switch (header->c_type) {
+
+               case TS_ADDR:
+                       /*
+                        * Skip up to the beginning of the next record
+                        */
+                       for (i = 0; i < header->c_count; i++)
+                               if (header->c_addr[i])
+                                       readtape(buf);
+                       while (gethead(header) == FAIL ||
+                           header->c_date != dumpdate)
+                               skipcnt++;
+                       break;
+
+               case TS_INODE:
+                       curfile.dip = &header->c_dinode;
+                       curfile.ino = header->c_inumber;
+                       break;
+
+               case TS_END:
+                       curfile.ino = maxino;
+                       break;
+
+               case TS_CLRI:
+                       curfile.name = "<file removal list>";
+                       break;
+
+               case TS_BITS:
+                       curfile.name = "<file dump list>";
+                       break;
+
+               case TS_TAPE:
+                       panic("unexpected tape header\n");
+                       /* NOTREACHED */
+
+               default:
+                       panic("unknown tape header type %d\n", spcl.c_type);
+                       /* NOTREACHED */
+
+               }
+       } while (header->c_type == TS_ADDR);
+       if (skipcnt > 0)
+               fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
+       skipcnt = 0;
+}
+
+static int
+checksum(buf)
+       register int *buf;
+{
+       register int i, j;
+
+       j = sizeof(union u_spcl) / sizeof(int);
+       i = 0;
+       if(!Bcvt) {
+               do
+                       i += *buf++;
+               while (--j);
+       } else {
+               /* What happens if we want to read restore tapes
+                       for a 16bit int machine??? */
+               do 
+                       i += swabl(*buf++);
+               while (--j);
+       }
+                       
+       if (i != CHECKSUM) {
+               fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
+                       curfile.ino, curfile.name);
+               return(FAIL);
+       }
+       return(GOOD);
+}
+
+#ifdef RRESTORE
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+msg(const char *fmt, ...)
+#else
+msg(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);
+}
+#endif /* RRESTORE */
+
+static u_char *
+swabshort(sp, n)
+       register u_char *sp;
+       register int n;
+{
+       char c;
+
+       while (--n >= 0) {
+               c = sp[0]; sp[0] = sp[1]; sp[1] = c;
+               sp += 2;
+       }
+       return (sp);
+}
+
+static u_char *
+swablong(sp, n)
+       register u_char *sp;
+       register int n;
+{
+       char c;
+
+       while (--n >= 0) {
+               c = sp[0]; sp[0] = sp[3]; sp[3] = c;
+               c = sp[2]; sp[2] = sp[1]; sp[1] = c;
+               sp += 4;
+       }
+       return (sp);
+}
+
+void
+swabst(cp, sp)
+       register u_char *cp, *sp;
+{
+       int n = 0;
+
+       while (*cp) {
+               switch (*cp) {
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       n = (n * 10) + (*cp++ - '0');
+                       continue;
+               
+               case 's': case 'w': case 'h':
+                       if (n == 0)
+                               n = 1;
+                       sp = swabshort(sp, n);
+                       break;
+
+               case 'l':
+                       if (n == 0)
+                               n = 1;
+                       sp = swablong(sp, n);
+                       break;
+
+               default: /* Any other character, like 'b' counts as byte. */
+                       if (n == 0)
+                               n = 1;
+                       sp += n;
+                       break;
+               }
+               cp++;
+               n = 0;
+       }
+}
+
+static u_long
+swabl(x)
+       u_long x;
+{
+       swabst((u_char *)"l", (u_char *)&x);
+       return (x);
+}
diff --git a/restore/utilities.c b/restore/utilities.c
new file mode 100644 (file)
index 0000000..54b7dc1
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 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
+static char sccsid[] = "@(#)utilities.c        8.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#include <sys/time.h>
+#include <linux/ext2_fs.h>
+#include <bsdcompat.h>
+#else  /* __linux__ */
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#endif /* __linux__ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include <ext2fs/ext2fs.h>
+#endif
+
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * Insure that all the components of a pathname exist.
+ */
+void
+pathcheck(name)
+       char *name;
+{
+       register char *cp;
+       struct entry *ep;
+       char *start;
+
+       start = strrchr(name, '/');
+       if (start == 0)
+               return;
+       for (cp = start; *cp != '\0'; cp++) {
+               if (*cp != '/')
+                       continue;
+               *cp = '\0';
+               ep = lookupname(name);
+               if (ep == NULL) {
+                       /* Safe; we know the pathname exists in the dump. */
+                       ep = addentry(name, pathsearch(name)->d_ino, NODE);
+                       newnode(ep);
+               }
+               ep->e_flags |= NEW|KEEP;
+               *cp = '/';
+       }
+}
+
+/*
+ * Change a name to a unique temporary name.
+ */
+void
+mktempname(ep)
+       register struct entry *ep;
+{
+       char oldname[MAXPATHLEN];
+
+       if (ep->e_flags & TMPNAME)
+               badentry(ep, "mktempname: called with TMPNAME");
+       ep->e_flags |= TMPNAME;
+       (void) strcpy(oldname, myname(ep));
+       freename(ep->e_name);
+       ep->e_name = savename(gentempname(ep));
+       ep->e_namlen = strlen(ep->e_name);
+       renameit(oldname, myname(ep));
+}
+
+/*
+ * Generate a temporary name for an entry.
+ */
+char *
+gentempname(ep)
+       struct entry *ep;
+{
+       static char name[MAXPATHLEN];
+       struct entry *np;
+       long i = 0;
+
+       for (np = lookupino(ep->e_ino);
+           np != NULL && np != ep; np = np->e_links)
+               i++;
+       if (np == NULL)
+               badentry(ep, "not on ino list");
+       (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino);
+       return (name);
+}
+
+/*
+ * Rename a file or directory.
+ */
+void
+renameit(from, to)
+       char *from, *to;
+{
+       if (!Nflag && rename(from, to) < 0) {
+               fprintf(stderr, "warning: cannot rename %s to %s: %s\n",
+                   from, to, strerror(errno));
+               return;
+       }
+       vprintf(stdout, "rename %s to %s\n", from, to);
+}
+
+/*
+ * Create a new node (directory).
+ */
+void
+newnode(np)
+       struct entry *np;
+{
+       char *cp;
+
+       if (np->e_type != NODE)
+               badentry(np, "newnode: not a node");
+       cp = myname(np);
+       if (command == 'C') return;
+       if (!Nflag && mkdir(cp, 0777) < 0) {
+               np->e_flags |= EXISTED;
+               fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+               return;
+       }
+       vprintf(stdout, "Make node %s\n", cp);
+}
+
+/*
+ * Remove an old node (directory).
+ */
+void
+removenode(ep)
+       register struct entry *ep;
+{
+       char *cp;
+
+       if (ep->e_type != NODE)
+               badentry(ep, "removenode: not a node");
+       if (ep->e_entries != NULL)
+               badentry(ep, "removenode: non-empty directory");
+       ep->e_flags |= REMOVED;
+       ep->e_flags &= ~TMPNAME;
+       cp = myname(ep);
+       if (!Nflag && rmdir(cp) < 0) {
+               fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+               return;
+       }
+       vprintf(stdout, "Remove node %s\n", cp);
+}
+
+/*
+ * Remove a leaf.
+ */
+void
+removeleaf(ep)
+       register struct entry *ep;
+{
+       char *cp;
+
+       if (command == 'C') return;
+
+       if (ep->e_type != LEAF)
+               badentry(ep, "removeleaf: not a leaf");
+       ep->e_flags |= REMOVED;
+       ep->e_flags &= ~TMPNAME;
+       cp = myname(ep);
+       if (!Nflag && unlink(cp) < 0) {
+               fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+               return;
+       }
+       vprintf(stdout, "Remove leaf %s\n", cp);
+}
+
+/*
+ * Create a link.
+ */
+int
+linkit(existing, new, type)
+       char *existing, *new;
+       int type;
+{
+
+       if (type == SYMLINK) {
+               if (!Nflag && symlink(existing, new) < 0) {
+                       fprintf(stderr,
+                           "warning: cannot create symbolic link %s->%s: %s\n",
+                           new, existing, strerror(errno));
+                       return (FAIL);
+               }
+       } else if (type == HARDLINK) {
+               if (!Nflag && link(existing, new) < 0) {
+                       fprintf(stderr,
+                           "warning: cannot create hard link %s->%s: %s\n",
+                           new, existing, strerror(errno));
+                       return (FAIL);
+               }
+       } else {
+               panic("linkit: unknown type %d\n", type);
+               return (FAIL);
+       }
+       vprintf(stdout, "Create %s link %s->%s\n",
+               type == SYMLINK ? "symbolic" : "hard", new, existing);
+       return (GOOD);
+}
+
+#ifndef        __linux__
+/*
+ * Create a whiteout
+ */
+int
+addwhiteout(name)
+       char *name;
+{
+
+       if (!Nflag && mknod(name, S_IFWHT, 0) < 0) {
+               fprintf(stderr, "warning: cannot create whiteout %s: %s\n",
+                   name, strerror(errno));
+               return (FAIL);
+       }
+       vprintf(stdout, "Create whiteout %s\n", name);
+       return (GOOD);
+}
+
+/*
+ * Delete a whiteout.
+ */
+void
+delwhiteout(ep)
+       register struct entry *ep;
+{
+       char *name;
+
+       if (ep->e_type != LEAF)
+               badentry(ep, "delwhiteout: not a leaf");
+       ep->e_flags |= REMOVED;
+       ep->e_flags &= ~TMPNAME;
+       name = myname(ep);
+       if (!Nflag && undelete(name) < 0) {
+               fprintf(stderr, "warning: cannot delete whiteout %s: %s\n",
+                   name, strerror(errno));
+               return;
+       }
+       vprintf(stdout, "Delete whiteout %s\n", name);
+}
+#endif
+
+/*
+ * find lowest number file (above "start") that needs to be extracted
+ */
+ino_t
+lowerbnd(start)
+       ino_t start;
+{
+       register struct entry *ep;
+
+       for ( ; start < maxino; start++) {
+               ep = lookupino(start);
+               if (ep == NULL || ep->e_type == NODE)
+                       continue;
+               if (ep->e_flags & (NEW|EXTRACT))
+                       return (start);
+       }
+       return (start);
+}
+
+/*
+ * find highest number file (below "start") that needs to be extracted
+ */
+ino_t
+upperbnd(start)
+       ino_t start;
+{
+       register struct entry *ep;
+
+       for ( ; start > ROOTINO; start--) {
+               ep = lookupino(start);
+               if (ep == NULL || ep->e_type == NODE)
+                       continue;
+               if (ep->e_flags & (NEW|EXTRACT))
+                       return (start);
+       }
+       return (start);
+}
+
+/*
+ * report on a badly formed entry
+ */
+void
+badentry(ep, msg)
+       register struct entry *ep;
+       char *msg;
+{
+
+       fprintf(stderr, "bad entry: %s\n", msg);
+       fprintf(stderr, "name: %s\n", myname(ep));
+       fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
+       if (ep->e_sibling != NULL)
+               fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
+       if (ep->e_entries != NULL)
+               fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
+       if (ep->e_links != NULL)
+               fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
+       if (ep->e_next != NULL)
+               fprintf(stderr,
+                   "next hashchain name: %s\n", myname(ep->e_next));
+       fprintf(stderr, "entry type: %s\n",
+               ep->e_type == NODE ? "NODE" : "LEAF");
+       fprintf(stderr, "inode number: %ld\n", ep->e_ino);
+       panic("flags: %s\n", flagvalues(ep));
+}
+
+/*
+ * Construct a string indicating the active flag bits of an entry.
+ */
+char *
+flagvalues(ep)
+       register struct entry *ep;
+{
+       static char flagbuf[BUFSIZ];
+
+       (void) strcpy(flagbuf, "|NIL");
+       flagbuf[0] = '\0';
+       if (ep->e_flags & REMOVED)
+               (void) strcat(flagbuf, "|REMOVED");
+       if (ep->e_flags & TMPNAME)
+               (void) strcat(flagbuf, "|TMPNAME");
+       if (ep->e_flags & EXTRACT)
+               (void) strcat(flagbuf, "|EXTRACT");
+       if (ep->e_flags & NEW)
+               (void) strcat(flagbuf, "|NEW");
+       if (ep->e_flags & KEEP)
+               (void) strcat(flagbuf, "|KEEP");
+       if (ep->e_flags & EXISTED)
+               (void) strcat(flagbuf, "|EXISTED");
+       return (&flagbuf[1]);
+}
+
+/*
+ * Check to see if a name is on a dump tape.
+ */
+ino_t
+dirlookup(name)
+       const char *name;
+{
+       struct direct *dp;
+       ino_t ino;
+       ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino;
+
+       if (ino == 0 || TSTINO(ino, dumpmap) == 0)
+               fprintf(stderr, "%s is not on the tape\n", name);
+       return (ino);
+}
+
+/*
+ * Elicit a reply.
+ */
+int
+reply(question)
+       char *question;
+{
+       char c;
+
+       do      {
+               fprintf(stderr, "%s? [yn] ", question);
+               (void) fflush(stderr);
+               c = getc(terminal);
+               while (c != '\n' && getc(terminal) != '\n')
+                       if (feof(terminal))
+                               return (FAIL);
+       } while (c != 'y' && c != 'n');
+       if (c == 'y')
+               return (GOOD);
+       return (FAIL);
+}
+
+/*
+ * handle unexpected inconsistencies
+ */
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+panic(const char *fmt, ...)
+#else
+panic(fmt, va_alist)
+       char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+
+       vfprintf(stderr, fmt, ap);
+       if (yflag)
+               return;
+       if (reply("abort") == GOOD) {
+               if (reply("dump core") == GOOD)
+                       abort();
+               done(1);
+       }
+}
diff --git a/rmt/Makefile.in b/rmt/Makefile.in
new file mode 100644 (file)
index 0000000..8cc2286
--- /dev/null
@@ -0,0 +1,41 @@
+top_srcdir=    @top_srcdir@
+srcdir=                @srcdir@
+
+@MCONFIG@
+
+CFLAGS=                @CCOPTS@ -pipe $(GINC) $(INC) $(DEFS)
+LDFLAGS:=      $(LDFLAGS) @STATIC@
+LIBS=          $(GLIBS)
+
+#DEBUG=                -DFDEBUG -DTDEBUG -DWRITEDEBUG
+#INC=          -I../compat/include
+#CFLAGS=               -pipe $(DEFS) $(DEBUG) $(INC) $(GINC) $(OPT)
+#DEPLIBS=      ../compat/lib/libcompat.a
+#LIBS=         $(GLIBS) -L../compat/lib -lcompat
+
+PROG=          rmt
+SRCS=          rmt.c
+OBJS=          rmt.o
+MAN8=          rmt.8
+
+all::          $(PROG)
+
+$(PROG):       $(OBJS) $(DEPLIBS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install::      $(PROG)
+       $(INSTALLBIN) $(PROG) $(BINDIR)
+       $(INSTALLMAN) $(srcdir)/$(MAN8) $(MANDIR)
+
+clean::
+       rm -f $(PROG) \#* *.s *.o *.a *~ core
+
+distclean::    clean
+       rm -f Makefile Makefile.old .depend 
+
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+
diff --git a/rmt/rmt.8 b/rmt/rmt.8
new file mode 100644 (file)
index 0000000..7bee3db
--- /dev/null
+++ b/rmt/rmt.8
@@ -0,0 +1,218 @@
+.\" Copyright (c) 1983, 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.
+.\"
+.\"     @(#)rmt.8      8.3 (Berkeley) 12/11/93
+.\"
+.Dd June 1, 1994
+.Dt RMT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rmt
+.Nd remote magtape protocol module
+.Sh SYNOPSIS
+.Nm rmt
+.Sh DESCRIPTION
+.Nm Rmt
+is a program used by the remote dump and restore programs
+in manipulating a magnetic tape drive through an interprocess
+communication connection.
+.Nm Rmt
+is normally started up with an
+.Xr rexec 3
+or
+.Xr rcmd 3
+call.
+.Pp
+The 
+.Nm rmt
+program accepts requests specific to the manipulation of
+magnetic tapes, performs the commands, then responds with
+a status indication.  All responses are in
+.Tn ASCII
+and in
+one of two forms. 
+Successful commands have responses of:
+.Bd -filled -offset indent
+.Sm off
+.Sy A Ar number No \en
+.Sm on
+.Ed
+.Pp
+.Ar Number
+is an
+.Tn ASCII
+representation of a decimal number.
+Unsuccessful commands are responded to with:
+.Bd -filled -offset indent
+.Sm off
+.Xo Sy E Ar error-number
+.No \en Ar error-message
+.No \en
+.Xc
+.Sm on
+.Ed
+.Pp
+.Ar Error-number
+is one of the possible error
+numbers described in
+.Xr intro 2
+and
+.Ar error-message
+is the corresponding error string as printed
+from a call to
+.Xr perror 3 .
+The protocol is comprised of the
+following commands, which are sent as indicated - no spaces are supplied
+between the command and its arguments, or between its arguments, and
+.Ql \en
+indicates that a newline should be supplied:
+.Bl -tag -width Ds
+.Sm off
+.It Xo Sy \&O Ar device
+.No \en Ar mode No \en
+.Xc
+Open the specified 
+.Ar device
+using the indicated
+.Ar mode .
+.Ar Device
+is a full pathname and
+.Ar mode
+is an
+.Tn ASCII
+representation of a decimal
+number suitable for passing to
+.Xr open 2 .
+If a device had already been opened, it is
+closed before a new open is performed.
+.It Xo Sy C Ar device No \en
+.Xc
+Close the currently open device.  The
+.Ar device
+specified is ignored.
+.It Xo Sy L
+.Ar whence No \en
+.Ar offset No \en
+.Xc
+.Sm on
+Perform an
+.Xr lseek 2
+operation using the specified parameters.
+The response value is that returned from the
+.Xr lseek
+call.
+.Sm off
+.It Sy W Ar count No \en
+.Sm on
+Write data onto the open device.
+.Nm Rmt
+reads
+.Ar count
+bytes from the connection, aborting if
+a premature end-of-file is encountered.
+The response value is that returned from
+the
+.Xr write 2
+call.
+.Sm off
+.It Sy R Ar count No \en
+.Sm on
+Read
+.Ar count
+bytes of data from the open device.
+If
+.Ar count
+exceeds the size of the data buffer (10 kilobytes), it is
+truncated to the data buffer size.
+.Nm rmt
+then performs the requested 
+.Xr read 2
+and responds with 
+.Sm off
+.Sy A Ar count-read No \en
+.Sm on
+if the read was
+successful; otherwise an error in the
+standard format is returned.  If the read
+was successful, the data read is then sent.
+.Sm off
+.It Xo Sy I Ar operation
+.No \en Ar count No \en
+.Xc
+.Sm on
+Perform a
+.Dv MTIOCOP
+.Xr ioctl 2
+command using the specified parameters.
+The parameters are interpreted as the
+.Tn ASCII
+representations of the decimal values
+to place in the 
+.Ar mt_op
+and
+.Ar mt_count
+fields of the structure used in the
+.Xr ioctl
+call.  The return value is the
+.Ar count
+parameter when the operation is successful.
+.ne 1i
+.It Sy S
+Return the status of the open device, as
+obtained with a
+.Dv MTIOCGET
+.Xr ioctl
+call.  If the operation was successful,
+an ``ack'' is sent with the size of the
+status buffer, then the status buffer is
+sent (in binary).
+.El
+.Sm on
+.Pp
+Any other command causes 
+.Nm rmt
+to exit.
+.Sh DIAGNOSTICS
+All responses are of the form described above.
+.Sh SEE ALSO
+.Xr rcmd 3 ,
+.Xr rexec 3 ,
+.Xr mtio 4 ,
+.Xr rdump 8 ,
+.Xr rrestore 8
+.Sh BUGS
+People should be discouraged from using this for a remote
+file access protocol.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/rmt/rmt.c b/rmt/rmt.c
new file mode 100644 (file)
index 0000000..74116b0
--- /dev/null
+++ b/rmt/rmt.c
@@ -0,0 +1,251 @@
+/*
+ *     Ported to Linux's Second Extended File System as part of the
+ *     dump and restore backup suit
+ *     Remy Card <card@Linux.EU.Org>, 1994, 1995, 1996
+ *
+ */
+
+/*
+ * Copyright (c) 1983, 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
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rmt.c      8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * rmt
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mtio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sgtty.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int    tape = -1;
+
+char   *record;
+int    maxrecsize = -1;
+
+#define        SSIZE   64
+char   device[SSIZE];
+char   count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
+
+char   resp[BUFSIZ];
+
+FILE   *debug;
+#define        DEBUG(f)        if (debug) fprintf(debug, f)
+#define        DEBUG1(f,a)     if (debug) fprintf(debug, f, a)
+#define        DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
+
+char   *checkbuf __P((char *, int));
+void    error __P((int));
+void    getstring __P((char *));
+
+int
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       int rval;
+       char c;
+       int n, i, cc;
+
+       argc--, argv++;
+       if (argc > 0) {
+               debug = fopen(*argv, "w");
+               if (debug == 0)
+                       exit(1);
+               (void)setbuf(debug, (char *)0);
+       }
+top:
+       errno = 0;
+       rval = 0;
+       if (read(0, &c, 1) != 1)
+               exit(0);
+       switch (c) {
+
+       case 'O':
+               if (tape >= 0)
+                       (void) close(tape);
+               getstring(device);
+               getstring(mode);
+               DEBUG2("rmtd: O %s %s\n", device, mode);
+               tape = open(device, atoi(mode));
+               if (tape < 0)
+                       goto ioerror;
+               goto respond;
+
+       case 'C':
+               DEBUG("rmtd: C\n");
+               getstring(device);              /* discard */
+               if (close(tape) < 0)
+                       goto ioerror;
+               tape = -1;
+               goto respond;
+
+       case 'L':
+               getstring(count);
+               getstring(pos);
+               DEBUG2("rmtd: L %s %s\n", count, pos);
+               rval = lseek(tape, (off_t)atol(count), atoi(pos));
+               if (rval < 0)
+                       goto ioerror;
+               goto respond;
+
+       case 'W':
+               getstring(count);
+               n = atoi(count);
+               DEBUG1("rmtd: W %s\n", count);
+               record = checkbuf(record, n);
+               for (i = 0; i < n; i += cc) {
+                       cc = read(0, &record[i], n - i);
+                       if (cc <= 0) {
+                               DEBUG("rmtd: premature eof\n");
+                               exit(2);
+                       }
+               }
+               rval = write(tape, record, n);
+               if (rval < 0)
+                       goto ioerror;
+               goto respond;
+
+       case 'R':
+               getstring(count);
+               DEBUG1("rmtd: R %s\n", count);
+               n = atoi(count);
+               record = checkbuf(record, n);
+               rval = read(tape, record, n);
+               if (rval < 0)
+                       goto ioerror;
+               (void)sprintf(resp, "A%d\n", rval);
+               (void)write(1, resp, strlen(resp));
+               (void)write(1, record, rval);
+               goto top;
+
+       case 'I':
+               getstring(op);
+               getstring(count);
+               DEBUG2("rmtd: I %s %s\n", op, count);
+               { struct mtop mtop;
+                 mtop.mt_op = atoi(op);
+                 mtop.mt_count = atoi(count);
+                 if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
+                       goto ioerror;
+                 rval = mtop.mt_count;
+               }
+               goto respond;
+
+       case 'S':               /* status */
+               DEBUG("rmtd: S\n");
+               { struct mtget mtget;
+                 if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
+                       goto ioerror;
+                 rval = sizeof (mtget);
+                 (void)sprintf(resp, "A%d\n", rval);
+                 (void)write(1, resp, strlen(resp));
+                 (void)write(1, (char *)&mtget, sizeof (mtget));
+                 goto top;
+               }
+
+       default:
+               DEBUG1("rmtd: garbage command %c\n", c);
+               exit(3);
+       }
+respond:
+       DEBUG1("rmtd: A %d\n", rval);
+       (void)sprintf(resp, "A%d\n", rval);
+       (void)write(1, resp, strlen(resp));
+       goto top;
+ioerror:
+       error(errno);
+       goto top;
+}
+
+void
+getstring(bp)
+       char *bp;
+{
+       int i;
+       char *cp = bp;
+
+       for (i = 0; i < SSIZE; i++) {
+               if (read(0, cp+i, 1) != 1)
+                       exit(0);
+               if (cp[i] == '\n')
+                       break;
+       }
+       cp[i] = '\0';
+}
+
+char *
+checkbuf(record, size)
+       char *record;
+       int size;
+{
+
+       if (size <= maxrecsize)
+               return (record);
+       if (record != 0)
+               free(record);
+       record = malloc(size);
+       if (record == 0) {
+               DEBUG("rmtd: cannot allocate buffer space\n");
+               exit(4);
+       }
+       maxrecsize = size;
+       while (size > 1024 &&
+              setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
+               size -= 1024;
+       return (record);
+}
+
+void
+error(num)
+       int num;
+{
+
+       DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
+       (void)sprintf(resp, "E%d\n%s\n", num, strerror(num));
+       (void)write(1, resp, strlen(resp));
+}